Under Solaris, it is very more easy to remove than to add. The network interface can be found thanks to the couple (name, inet address):
N.B.: All checks have been removed for better legibility, but it is very important to check returned code after each request.
// We consider the existence of the variables (unsigned char *) "networkInterfaceName", "inetAddress" which could be respectively "eth0" and "192.168.80.1" for instance.
int socketDescriptor;
struct sockaddr_in *addr;
struct lifreq lifr;
sd = socket(AF_INET, SOCK_DGRAM, 0);
memset(&lifr, 0, sizeof(lifr));
addr = (struct sockaddr_in *) &(lifr.lifr_addr);
strncpy(lifr.lifr_name, networkInterfaceName, sizeof(lifr.lifr_name));
addr->sin_family = AF_INET;
addr->sin_addr.s_addr = inet_addr(inetAddress);
ioctl(socketDescriptor, SIOCLIFREMOVEIF, (caddr_t) &lifr)
close(socketDescriptor);
sharing knowledge earned into design/development/technical delicate and/or difficult situations ...
Labels
Gnu/Linux
(95)
Administration
(83)
StorageHardware
(17)
Programming
(16)
WebBrowser
(15)
General
(11)
GNU/Bash
(7)
Solaris
(7)
Virtualization
(7)
C
(6)
Domotics
(6)
Musics
(5)
Raspberry
(5)
Desktop
(4)
Java
(4)
VersionControlSystems
(4)
ArtificialIntelligence
(2)
Optimization
(2)
multimedia
(2)
Arduino
(1)
Electronics
(1)
LTS
(1)
MacOS
(1)
Mechanics
(1)
Processing
(1)
Robotics
(1)
Ubuntu
(1)
Upgrade
(1)
ez-robot
(1)
23 September 2007
Remove network interface under GNU/Linux
It is enough to switch down the corresponding alias network interface:
N.B.: All checks have been removed for better legibility, but it is very important to check returned code after each request.
// We consider the existence of the variable (unsigned char *) "aliasInterfaceName" ("eth0:myAlias" for instance).
int sd;
struct ifreq ifr;
sd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
memset(&ifr, 0, sizeof(struct ifreq));
strncpy(ifr.lifr_name, aliasInterfaceName, sizeof(ifr.ifr_name));
ifr.ifr_flags = ~IFF_UP;
ioctl(sd, SIOCSIFFLAGS, &ifr);
close(sd);
N.B.: All checks have been removed for better legibility, but it is very important to check returned code after each request.
// We consider the existence of the variable (unsigned char *) "aliasInterfaceName" ("eth0:myAlias" for instance).
int sd;
struct ifreq ifr;
sd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
memset(&ifr, 0, sizeof(struct ifreq));
strncpy(ifr.lifr_name, aliasInterfaceName, sizeof(ifr.ifr_name));
ifr.ifr_flags = ~IFF_UP;
ioctl(sd, SIOCSIFFLAGS, &ifr);
close(sd);
Add network interface under Solaris
Under Solairs, it is not trivial to add a network interface, there is no alias, and it seems to not be possible to specify the additional network interface name (so it must be got and kept).
N.B.: All checks have been removed for better legibility, but it is very important to check returned code after each request.
// We consider the existence of the variables (unsigned char *) "networkInterfaceName", "inetAddress", "broadcastAddress" and "netmaskAddress" which could be respectively "eth0", "192.168.80.1", "192.168.80.255" and "255.255.255.0" for instance.
int sd;
struct sockaddr_in *addr;
struct lifreq lifr, lifrBroadcast, lifrNetmask, lifrFlags;
const char *aliasName;
// Resets structure.
memset(&lifr, 0, sizeof(lifr));
strncpy(lifr.lifr_name, networkInterfaceName, sizeof(lifr.lifr_name));
sd = socket(AF_INET, SOCK_DGRAM, 0);
ioctl(sd, SIOCLIFADDIF, (caddr_t) &lifr);
// Very important to get the alias name to update network interface parameters.
aliasName = lifr.lifr_name;
// Manages broadcast.
memset(&lifrBroadcast, 0, sizeof(lifrBroadcast));
addr = (struct sockaddr_in *) &(lifrBroadcast.lifr_broadaddr);
strncpy(lifrBroadcast.lifr_name, aliasName, sizeof(lifrBroadcast.lifr_name));
addr->sin_family = AF_INET;
addr->sin_addr.s_addr = inet_addr(broadcastAddress);
ioctl(sd, SIOCSLIFBRDADDR, (caddr_t) &lifrBroadcast);
// Manages netmask.
memset(&lifrNetmask, 0, sizeof(lifrNetmask));
addr = (struct sockaddr_in *) &(lifrNetmask.lifr_addr);
strncpy(lifrNetmask.lifr_name, aliasName, sizeof(lifrNetmask.lifr_name));
addr->sin_family = AF_INET;
addr->sin_addr.s_addr = inet_addr(netmaskAddress);
ioctl(sd, SIOCSLIFNETMASK, (caddr_t) &lifrNetmask);
// Defines the address of the new interface.
addr = (struct sockaddr_in *) &(lifr.lifr_addr);
addr->sin_family = AF_INET;
addr->sin_addr.s_addr = inet_addr(inetAddress);
ioctl(sd, SIOCSLIFADDR, (caddr_t) &lifr);
// Sets the new interface UP.
memset(&lifrFlags, 0, sizeof(lifrFlags));
strncpy(lifrFlags.lifr_name, aliasName, sizeof(lifrFlags.lifr_name));
ioctl(sd, SIOCGLIFFLAGS, (caddr_t) &lifrFlags)
lifrFlags.lifr_flags |= IFF_UP;
ioctl(sd, SIOCSLIFFLAGS, (caddr_t) &lifrFlags);
close(sd);
N.B.: All checks have been removed for better legibility, but it is very important to check returned code after each request.
// We consider the existence of the variables (unsigned char *) "networkInterfaceName", "inetAddress", "broadcastAddress" and "netmaskAddress" which could be respectively "eth0", "192.168.80.1", "192.168.80.255" and "255.255.255.0" for instance.
int sd;
struct sockaddr_in *addr;
struct lifreq lifr, lifrBroadcast, lifrNetmask, lifrFlags;
const char *aliasName;
// Resets structure.
memset(&lifr, 0, sizeof(lifr));
strncpy(lifr.lifr_name, networkInterfaceName, sizeof(lifr.lifr_name));
sd = socket(AF_INET, SOCK_DGRAM, 0);
ioctl(sd, SIOCLIFADDIF, (caddr_t) &lifr);
// Very important to get the alias name to update network interface parameters.
aliasName = lifr.lifr_name;
// Manages broadcast.
memset(&lifrBroadcast, 0, sizeof(lifrBroadcast));
addr = (struct sockaddr_in *) &(lifrBroadcast.lifr_broadaddr);
strncpy(lifrBroadcast.lifr_name, aliasName, sizeof(lifrBroadcast.lifr_name));
addr->sin_family = AF_INET;
addr->sin_addr.s_addr = inet_addr(broadcastAddress);
ioctl(sd, SIOCSLIFBRDADDR, (caddr_t) &lifrBroadcast);
// Manages netmask.
memset(&lifrNetmask, 0, sizeof(lifrNetmask));
addr = (struct sockaddr_in *) &(lifrNetmask.lifr_addr);
strncpy(lifrNetmask.lifr_name, aliasName, sizeof(lifrNetmask.lifr_name));
addr->sin_family = AF_INET;
addr->sin_addr.s_addr = inet_addr(netmaskAddress);
ioctl(sd, SIOCSLIFNETMASK, (caddr_t) &lifrNetmask);
// Defines the address of the new interface.
addr = (struct sockaddr_in *) &(lifr.lifr_addr);
addr->sin_family = AF_INET;
addr->sin_addr.s_addr = inet_addr(inetAddress);
ioctl(sd, SIOCSLIFADDR, (caddr_t) &lifr);
// Sets the new interface UP.
memset(&lifrFlags, 0, sizeof(lifrFlags));
strncpy(lifrFlags.lifr_name, aliasName, sizeof(lifrFlags.lifr_name));
ioctl(sd, SIOCGLIFFLAGS, (caddr_t) &lifrFlags)
lifrFlags.lifr_flags |= IFF_UP;
ioctl(sd, SIOCSLIFFLAGS, (caddr_t) &lifrFlags);
close(sd);
Add network interface under GNU/Linux
It is not so hard to perform it under GNU/Linux with alias.
The aim, is to add an alias to an existing network interface with the wished inet, broadcast and netmask addresses:
N.B.: All checks have been removed for better legibility, but it is very important to check returned code after each request.
// We consider the existence of the variables (unsigned char *) "aliasInterfaceName", "inetAddress", "broadcastAddress" and "netmaskAddress" which could be respectively "eth0:myAlias"; "192.168.80.1", "192.168.80.255" and "255.255.255.0" for instance.
int sd;
struct sockaddr_in *addr;
struct ifreq ifr, ifrBroadcast, ifrNetmask;
sd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
// Add alias.
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.lifr_name, aliasInterfaceName, sizeof(ifr.ifr_name));
addr = (struct sockaddr_in *) &(ifr.ifr_addr);
addr->sin_family=AF_INET;
addr->sin_addr.s_addr=inet_addr(inetAddress);
ioctl(sd, SIOCSIFADDR, &ifr);
// Manages broadcast.
memset(&ifrBroadcast, 0, sizeof(ifrBroadcast));
strncpy(ifrBroadcast.lifr_name, aliasInterfaceName, sizeof(ifrBroadcast.ifr_name));
addr = (struct sockaddr_in *) &(ifrBroadcast.ifr_addr);
addr->sin_family=AF_INET;
addr->sin_addr.s_addr=inet_addr(broadcastAddress);
ioctl(sd, SIOCSIFBRDADDR, &ifrBroadcast)
// Manages netmask.
memset(&ifrNetmask, 0, sizeof(ifrNetmask));
strncpy(ifrNetmask.lifr_name, aliasInterfaceName, sizeof(ifrNetmask.ifr_name));
addr = (struct sockaddr_in *) &(ifrNetmask.ifr_addr);
addr->sin_family=AF_INET;
addr->sin_addr.s_addr=inet_addr(netmaskAddress);
ioctl(sd, SIOCSIFNETMASK, &ifrNetmask);
close(sd);
The aim, is to add an alias to an existing network interface with the wished inet, broadcast and netmask addresses:
N.B.: All checks have been removed for better legibility, but it is very important to check returned code after each request.
// We consider the existence of the variables (unsigned char *) "aliasInterfaceName", "inetAddress", "broadcastAddress" and "netmaskAddress" which could be respectively "eth0:myAlias"; "192.168.80.1", "192.168.80.255" and "255.255.255.0" for instance.
int sd;
struct sockaddr_in *addr;
struct ifreq ifr, ifrBroadcast, ifrNetmask;
sd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
// Add alias.
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.lifr_name, aliasInterfaceName, sizeof(ifr.ifr_name));
addr = (struct sockaddr_in *) &(ifr.ifr_addr);
addr->sin_family=AF_INET;
addr->sin_addr.s_addr=inet_addr(inetAddress);
ioctl(sd, SIOCSIFADDR, &ifr);
// Manages broadcast.
memset(&ifrBroadcast, 0, sizeof(ifrBroadcast));
strncpy(ifrBroadcast.lifr_name, aliasInterfaceName, sizeof(ifrBroadcast.ifr_name));
addr = (struct sockaddr_in *) &(ifrBroadcast.ifr_addr);
addr->sin_family=AF_INET;
addr->sin_addr.s_addr=inet_addr(broadcastAddress);
ioctl(sd, SIOCSIFBRDADDR, &ifrBroadcast)
// Manages netmask.
memset(&ifrNetmask, 0, sizeof(ifrNetmask));
strncpy(ifrNetmask.lifr_name, aliasInterfaceName, sizeof(ifrNetmask.ifr_name));
addr = (struct sockaddr_in *) &(ifrNetmask.ifr_addr);
addr->sin_family=AF_INET;
addr->sin_addr.s_addr=inet_addr(netmaskAddress);
ioctl(sd, SIOCSIFNETMASK, &ifrNetmask);
close(sd);
Get MAC address under GNU/Linux or Solaris
This is a way to get MAC address of a network interface in C language.
Under GNU/Linux it can be done thanks to a RAW socket:
N.B.: All checks have been removed for better legibility, but it is very important to check returned code after each request.
// Consider a variable "networkInterfaceName" of type char * is defined ("eth0" for instance).
int sd;
struct ifreq ifr;
const unsigned char *hardwareAddress;
sd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
memset(&ifr, 0, sizeof(ifr));
setNameInIfreq(&ifr, networkInterfaceName);
hardwareAddress = (unsigned char *) &ifr.ifr_hwaddr.sa_data;
close(sd);
Under Solaris, it begins to be less easy, we need to use DLPI code:
// Consider a variable "devicePath" of type char * is defined ("/dev/eth0" for instance).
int sd;
dl_phys_addr_req_t dlpareq;
dl_phys_addr_ack_t *dlpaack;
struct strbuf msg;
char buf[128];
int flags = 0;
const unsigned char *hardwareAddress;
sd = open(devicePath, O_RDWR));
dlpareq.dl_primitive = DL_PHYS_ADDR_REQ;
dlpareq.dl_addr_type = DL_CURR_PHYS_ADDR;
msg.buf = (char *)&dlpareq;
msg.len = DL_PHYS_ADDR_REQ_SIZE;
putmsg(sd, &msg, NULL, 0);
dlpaack = (dl_phys_addr_ack_t *)buf;
msg.buf = (char *)buf;
msg.len = 0;
msg.maxlen = sizeof (buf);
getmsg(sd, &msg, NULL, &flags);
memcpy(hardwareAddress, &buf[dlpaack->dl_addr_offset], dlpaack->dl_addr_length);
close(sd);
Under GNU/Linux it can be done thanks to a RAW socket:
N.B.: All checks have been removed for better legibility, but it is very important to check returned code after each request.
// Consider a variable "networkInterfaceName" of type char * is defined ("eth0" for instance).
int sd;
struct ifreq ifr;
const unsigned char *hardwareAddress;
sd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
memset(&ifr, 0, sizeof(ifr));
setNameInIfreq(&ifr, networkInterfaceName);
hardwareAddress = (unsigned char *) &ifr.ifr_hwaddr.sa_data;
close(sd);
Under Solaris, it begins to be less easy, we need to use DLPI code:
// Consider a variable "devicePath" of type char * is defined ("/dev/eth0" for instance).
int sd;
dl_phys_addr_req_t dlpareq;
dl_phys_addr_ack_t *dlpaack;
struct strbuf msg;
char buf[128];
int flags = 0;
const unsigned char *hardwareAddress;
sd = open(devicePath, O_RDWR));
dlpareq.dl_primitive = DL_PHYS_ADDR_REQ;
dlpareq.dl_addr_type = DL_CURR_PHYS_ADDR;
msg.buf = (char *)&dlpareq;
msg.len = DL_PHYS_ADDR_REQ_SIZE;
putmsg(sd, &msg, NULL, 0);
dlpaack = (dl_phys_addr_ack_t *)buf;
msg.buf = (char *)buf;
msg.len = 0;
msg.maxlen = sizeof (buf);
getmsg(sd, &msg, NULL, &flags);
memcpy(hardwareAddress, &buf[dlpaack->dl_addr_offset], dlpaack->dl_addr_length);
close(sd);
22 September 2007
Computer science Experience Knowledge SHAring
For more understanding, the blog is now named "Computer science Experience Knowledge SHAring", which could be shortly called CEKSHA.
15 September 2007
Upgrade GNU/Linux Fedora (with yum)
Upgrade from Fedora core N to Fedora core N+1 can be done thanks to yum, although it is not really recommended.
I have successfully performed such upgrade from core 3 (perhaps core 2, I'm not sure), to core 4, then core 5 and finally core 6. Upgrade from Fedora core 6 to Fedora 7 fails with this way because of great distribution architecture update (core and extras merged).
This is steps which can be followed to perform such upgrade:
- open a terminal in run level 3,
- temporary set default run level to 3 (edit /etc/inittab file) to avoid potential problem with card detection or something else during next boot,
- ensure to have the last version of yum thanks to yum upgrade yum,
- clean the yum meta-data to avoid some rare conflict yum clean all,
- upgrade the release thanks to fedora-release (and fedora-release-notes from Fedora core 6) package(s)
For instance, from core 5 to core 6 : rpm -Uvh fedora-release fedora-release-notes
- finally upgrade the full system, ensuring to keep information about all progress into a file, thanks to yum upgrade 2>&1 |tee /tmp/systemUpgrade.log
- fix potential little problem like missing symbolic link (I had one about i686-redhat-linux-gnu one time),
- think to upgrade depositories if needed (livna, dries, dag ...),
- update the /etc/grub.conf or /etc/lilo.conf file according to your environment if you want to ensure the default use of a kernel,
- reboot.
I have successfully performed such upgrade from core 3 (perhaps core 2, I'm not sure), to core 4, then core 5 and finally core 6. Upgrade from Fedora core 6 to Fedora 7 fails with this way because of great distribution architecture update (core and extras merged).
This is steps which can be followed to perform such upgrade:
- open a terminal in run level 3,
- temporary set default run level to 3 (edit /etc/inittab file) to avoid potential problem with card detection or something else during next boot,
- ensure to have the last version of yum thanks to yum upgrade yum,
- clean the yum meta-data to avoid some rare conflict yum clean all,
- upgrade the release thanks to fedora-release (and fedora-release-notes from Fedora core 6) package(s)
For instance, from core 5 to core 6 : rpm -Uvh fedora-release fedora-release-notes
- finally upgrade the full system, ensuring to keep information about all progress into a file, thanks to yum upgrade 2>&1 |tee /tmp/systemUpgrade.log
- fix potential little problem like missing symbolic link (I had one about i686-redhat-linux-gnu one time),
- think to upgrade depositories if needed (livna, dries, dag ...),
- update the /etc/grub.conf or /etc/lilo.conf file according to your environment if you want to ensure the default use of a kernel,
- reboot.
8 September 2007
How to choose a *nix shell
Today, there is very lots of shells like sh, bash, csh, tcsh, ksh, zsh, ash ...
For various reasons, it makes sens to choose one, a time for all, although it is not so easy.
Personally, I have first chosen tcsh for its functionalities and its legibility very near the C language. On the first hand, it was not a bad choice because thanks to it, I had done all I had needed, on the other hand I had faced two issues.
The first was the fact that function does not exists under tcsh, and that alias become dirty very quickly.
The second, the more important, is this is not installed by default on all *nix operating systems.
The wish to create something which can be used on lots of operating system, particularly on GNU/Linux, Solaris, and Cygwin which was my first shell multi-OS need, my second and definitive choice has been for GNU/Bash. It resolves the two points and it perfectly fits my needs.
To lead me on this choice, I have read lots of articles and Web Site, and I have concluded that GNU/Bash is now embedded as installation base on almost all operating systems. In the case it would not, it can be used as sh compatible.
It is why, I think it is the best choice in most cases. If you have not made your choice, you might make this one too, it will be a guarantee of perenity and should answer to all your needs.
For various reasons, it makes sens to choose one, a time for all, although it is not so easy.
Personally, I have first chosen tcsh for its functionalities and its legibility very near the C language. On the first hand, it was not a bad choice because thanks to it, I had done all I had needed, on the other hand I had faced two issues.
The first was the fact that function does not exists under tcsh, and that alias become dirty very quickly.
The second, the more important, is this is not installed by default on all *nix operating systems.
The wish to create something which can be used on lots of operating system, particularly on GNU/Linux, Solaris, and Cygwin which was my first shell multi-OS need, my second and definitive choice has been for GNU/Bash. It resolves the two points and it perfectly fits my needs.
To lead me on this choice, I have read lots of articles and Web Site, and I have concluded that GNU/Bash is now embedded as installation base on almost all operating systems. In the case it would not, it can be used as sh compatible.
It is why, I think it is the best choice in most cases. If you have not made your choice, you might make this one too, it will be a guarantee of perenity and should answer to all your needs.
4 September 2007
Compile OpenVPN and TUN under Solaris
I've recently worked on Solaris and need to compile the source code of openVPN and TUN.
At first, it seemed easy but because of some compatibility issues, it was not.
You can follow those steps to get openvpn executable under Solaris:
- download the openVPN source code and uncompress it under [OPENVPN_DIRECTORY],
- download the TUN source code and uncompress it under [TUNE_DIRECTORY],
- copy the [TUNE_DIRECTORY]/solaris/if_tun.h file to [OPENVPN_DIRECTORY],
- edit the [OPENVPN_DIRECTORY]/tun.c file and add the #include "if_tun.h" line after the #include "tun.h" one,
- under [OPENVPN_DIRECTORY], execute ./configure --disable-lzo,
- ensure there is a make executable into your path, for instance create a symbolic link executing ln -s /usr/sfw/bin/gmake /usr/bin/make,
- then execute make under [OPENVPN_DIRECTORY],
- finally the openvpn executable is ready for Solaris.
Then, you can follow those steps to get the tun functionality under Solaris:
- create the subdirectory [TUNE_DIRECTORY] /solaris/sys,
- download the dditypes.h file and put it under [TUNE_DIRECTORY] /solaris/sys (it fixes an incompatibility issue),
- ensure there is a ld executable into your path, for instance create a symbolic link executing ln -s /usr/sfw/bin/gld /usr/bin/ld,
- execute make install under [TUNE_DIRECTORY],
- finally the tun functionality is ready for Solaris.
It is now possible to use openVPN and TUN under Solaris.
At first, it seemed easy but because of some compatibility issues, it was not.
You can follow those steps to get openvpn executable under Solaris:
- download the openVPN source code and uncompress it under [OPENVPN_DIRECTORY],
- download the TUN source code and uncompress it under [TUNE_DIRECTORY],
- copy the [TUNE_DIRECTORY]/solaris/if_tun.h file to [OPENVPN_DIRECTORY],
- edit the [OPENVPN_DIRECTORY]/tun.c file and add the #include "if_tun.h" line after the #include "tun.h" one,
- under [OPENVPN_DIRECTORY], execute ./configure --disable-lzo,
- ensure there is a make executable into your path, for instance create a symbolic link executing ln -s /usr/sfw/bin/gmake /usr/bin/make,
- then execute make under [OPENVPN_DIRECTORY],
- finally the openvpn executable is ready for Solaris.
Then, you can follow those steps to get the tun functionality under Solaris:
- create the subdirectory [TUNE_DIRECTORY] /solaris/sys,
- download the dditypes.h file and put it under [TUNE_DIRECTORY] /solaris/sys (it fixes an incompatibility issue),
- ensure there is a ld executable into your path, for instance create a symbolic link executing ln -s /usr/sfw/bin/gld /usr/bin/ld,
- execute make install under [TUNE_DIRECTORY],
- finally the tun functionality is ready for Solaris.
It is now possible to use openVPN and TUN under Solaris.
1 September 2007
Create a full and safe emails server under GNU/Linux
Not so easy ? right.
Anyway, almost one year ago, I've successfully followed a very interesting How To Forge Tutorial which allows me to have such a fully functional emails server. Because it is for my own purpose, there is only few users using it, and I so don't know how must it is scalable.
This emails server is very interesting thanks to its completeness and security. The system is compounded of :
- postfix for the MTA layer,
- courier-imap for mailboxes,
- clamAV, openssl, openldap, cyrus-sasl and courier-authlib-mysql for authentication and anti-virus,
- mysql for users configuration and administration,
- spamassassin, dcc, razor, pyzor and amavis-new for spamfilters.
It works perfectly under Fedora core 5, and Fedora core 6 with my additional instructions.
During my documentation research in 2006, I have read articles enough to think that this system should work on several others GNU/Linux distributions, although packages name/configuration may differ.
Anyway, almost one year ago, I've successfully followed a very interesting How To Forge Tutorial which allows me to have such a fully functional emails server. Because it is for my own purpose, there is only few users using it, and I so don't know how must it is scalable.
This emails server is very interesting thanks to its completeness and security. The system is compounded of :
- postfix for the MTA layer,
- courier-imap for mailboxes,
- clamAV, openssl, openldap, cyrus-sasl and courier-authlib-mysql for authentication and anti-virus,
- mysql for users configuration and administration,
- spamassassin, dcc, razor, pyzor and amavis-new for spamfilters.
It works perfectly under Fedora core 5, and Fedora core 6 with my additional instructions.
During my documentation research in 2006, I have read articles enough to think that this system should work on several others GNU/Linux distributions, although packages name/configuration may differ.
Subscribe to:
Posts (Atom)