Wednesday, February 2, 2011

C macro that accepts variable arguments

You must have wondered (at least I did), if there is a way to pass variable arguments to a macro to avoid the usage of va_start, va_arg, va_end etc., in a function

Here's how to do that in C

#define eprintf(...) fprintf (stderr, __VA_ARGS__) 

This kind of macro is called variadic. When the macro is invoked, all the tokens in its argument list after the last named argument (this macro has none), including any commas, become the variable argument. This sequence of tokens replaces the identifier __VA_ARGS__ in the macro body wherever it appears. Thus, we have this expansion:

     eprintf ("%s:%d: ", input_file, lineno)           ==>  fprintf (stderr, "%s:%d: ", input_file, lineno) 

The variable argument is completely macro-expanded before it is inserted into the macro expansion, just like an ordinary argument. You may use the `#' and `##' operators to stringify the variable argument or to paste its leading or trailing token with another token. (But see below for an important special case for `##'.)

If your macro is complicated, you may want a more descriptive name for the variable argument than __VA_ARGS__. CPP permits this, as an extension. You may write an argument name immediately before the `...'; that name is used for the variable argument. The eprintf macro above could be written

     #define eprintf(args...) fprintf (stderr, args) 

using this extension. You cannot use __VA_ARGS__ and this extension in the same macro.

You can have named arguments as well as variable arguments in a variadic macro. We could define eprintf like this, instead:

     #define eprintf(format, ...) fprintf (stderr, format, __VA_ARGS__) 

This formulation looks more descriptive, but unfortunately it is less flexible: you must now supply at least one argument after the format string. In standard C, you cannot omit the comma separating the named argument from the variable arguments. Furthermore, if you leave the variable argument empty, you will get a syntax error, because there will be an extra comma after the format string.

     eprintf("success!\n", );           ==> fprintf(stderr, "success!\n", ); 

GNU CPP has a pair of extensions which deal with this problem. First, you are allowed to leave the variable argument out entirely:

     eprintf ("success!\n")           ==> fprintf(stderr, "success!\n", ); 

Second, the `##' token paste operator has a special meaning when placed between a comma and a variable argument. If you write

     #define eprintf(format, ...) fprintf (stderr, format, ##__VA_ARGS__) 

and the variable argument is left out when the eprintf macro is used, then the comma before the `##' will be deleted. This does not happen if you pass an empty argument, nor does it happen if the token preceding `##' is anything other than a comma.

     eprintf ("success!\n")           ==> fprintf(stderr, "success!\n"); 

The above explanation is ambiguous about the case where the only macro parameter is a variable arguments parameter, as it is meaningless to try to distinguish whether no argument at all is an empty argument or a missing argument. In this case the C99 standard is clear that the comma must remain, however the existing GCC extension used to swallow the comma. So CPP retains the comma when conforming to a specific C standard, and drops it otherwise.

C99 mandates that the only place the identifier __VA_ARGS__ can appear is in the replacement list of a variadic macro. It may not be used as a macro name, macro argument name, or within a different type of macro. It may also be forbidden in open text; the standard is ambiguous. We recommend you avoid using it except for its defined purpose.

Variadic macros are a new feature in C99. GNU CPP has supported them for a long time, but only with a named variable argument (`args...', not `...' and __VA_ARGS__). If you are concerned with portability to previous versions of GCC, you should use only named variable arguments. On the other hand, if you are concerned with portability to other conforming implementations of C99, you should use only __VA_ARGS__.

Previous versions of CPP implemented the comma-deletion extension much more generally. We have restricted it in this release to minimize the differences from C99. To get the same effect with both this and previous versions of GCC, the token preceding the special `##' must be a comma, and there must be white space between that comma and whatever comes immediately before it:

     #define eprintf(format, args...) fprintf (stderr, format , ##args)

Source: http://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html

Finding the IP address of network interfaces in C (linux)

The following program lists the 'up' network interfaces and their IP addresses. If you are behind a firewall, this will list the local IP address

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

int main(void)
{
int fd;
struct if_nameindex *curif, *ifs;
struct ifreq req;

if((fd = socket(PF_INET, SOCK_DGRAM, 0)) != -1) {
ifs = if_nameindex();
if(ifs) {
for(curif = ifs; curif && curif->if_name; curif++) {
strncpy(req.ifr_name, curif->if_name, IFNAMSIZ);
req.ifr_name[IFNAMSIZ] = 0;
if (ioctl(fd, SIOCGIFADDR, &req) < 0)
perror("ioctl");
else
printf("%s: [%s]\n", curif->if_name,
inet_ntoa(((struct sockaddr_in*) &req.ifr_addr)->sin_addr));
}
if_freenameindex(ifs);
if(close(fd)!=0)
perror("close");
} else
perror("if_nameindex");
} else
perror("socket");
return 0;
}

Friday, April 2, 2010

How to enable routing on a linux machine

Ever wondered how to make your linux machine a router? Of course not as powerful as a cisco router ;)

The only requirement is that you have at least two networking interfaces in the machine and one of them is connected to the external network or the Internet

Let us suppose 'eth0' is the interface connected to the external world and 'eth1', 'eth2' etc ., are connected to other machines/switches which would connect to the Internet/external network via this machine

The following script would enable routing on the machine and lets other machines connected to it access machines in the external network

# Delete and flush. Default table is "filter". Others like "nat" must be explicitly stated.
iptables --flush # - Flush all the rules in filter and nat tables
iptables --table nat --flush
iptables --delete-chain # - Delete all chains that are not in default filter and nat table
iptables --table nat --delete-chain

# Set up IP FORWARDing and Masquerading
iptables --table nat --append POSTROUTING --out-interface eth0 -j MASQUERADE
iptables --append FORWARD --in-interface eth1 -j ACCEPT
#Add a similar line as above for each other interfaces which accepts connections

echo 1 > /proc/sys/net/ipv4/ip_forward # - Enables packet forwarding by kernel

Thursday, April 1, 2010

error trying to exec 'cc1': execvp: No such file or directory

I ran into this error when I had more than one version of gcc installed on my machine. I had multiple versions because, compilation of some open source softwares required a gcc of 3.x.x version. I messed things up completely because some other software required gcc-4.x.x as I was playing around with sym links

You could however, run into this problem in several other ways too.

The first thing you would do to fix this problem is to reinstall gcc and g++. On a ubuntu machine, you'd do it by
$apt-get remove gcc g++
$apt-get install gcc g++

If you still face the same problem (as I did), try installing a different version of gcc and g++, say:
$apt-get install gcc-4.1 g++-4.1

If you use other linux distributions, use the corresponding command-line software installers i.e., yum (fedora), yast(suse) etc.,

Fortunately, that did the trick for me.

Wednesday, March 31, 2010

How to undo changes made using ' svn add '

If you want to add a new file/directory to an svn repository, you first specify the new file/directory using the command

$svn add 'file/directory name'

It is important to note that the change does not propagate to the server until you issue an 'svn commit'

For any reason, if you would like to revert the addition of file/directory, just follow these

1. If it is a directory, go to the directory and delete the directory named '.svn'
$ rm -rf .svn
Go to the parent directory and edit the file .svn/entries and delete the entry corresponding to the directory you added using 'svn add'. Be very careful while editing the file as it contains the metadata used by svn in a particular format

2. If it is a file, go to the .svn directory in the directory which contains the file and edit the file entries and remove the entry corresponding to the file you added using 'svn add

I just found this by experimentation. However, I am not aware of any other method to revert the changes. If you know of any, please feel free to leave a comment.

Monday, March 29, 2010

How to compile the linux kernel

Linux newbies often find it difficult to the compile the linux kernel and get it running. without any problems.

I usually follow the instructions at http://www.howtoforge.com/kernel_compilation_ubuntu

If you use other linux distributions please visit http://www.howtoforge.com/howtos/linux/kernel

I am listing out the instructions here for ubuntu distribution in a much simpler way. The tutorial above goes into a lot of detail. We do not usually need all the instructions listed over there. Feel free to leave comments if you have any related question unanswered or if you encounter any problems in the process. I should be able to get back to you as soon as I can.

1. It is always good to run 'sudo apt-get update' once in a while. This updates the list of packages/updates available for ubuntu. It would also be good to run 'sudo apt-get upgrade'
shell commands:
$sudo apt-get update
$sudo apt-get upgrade

2. Install the needed packages now
$apt-get install kernel-package libncurses5-dev fakeroot wget bzip2

3.If you are running recent linux distributions, you will have 2.6 kernel on it. Get the kernel source of the 2.6 kernel version you would like to compile from here
http://www.kernel.org/pub/linux/kernel/v2.6/
Go to the directory /usr/src
$cd /usr/src

Download and unzip the tarball using the command. 'version' is the linux kernel version that you want to compile
$tar -xzvf linux-'version'.tar.gz
or
$tar -xjvf linux-.'version'tar.bz2

$cd linux-'version'



4. Create a .config file

$make menuconfig
Just leave the standard configuration (unless you know if you want a particular feature), press and press 'exit'. This will write the default configuration into the file '.config'.

If you applied any patch to the kernel, edit the '.config' file thus generated and uncomment the related config options and run
$make oldconfig

It will ask you a few questions, reply with 'm' or 'y' accordingly

5. Execute the following two commands to compile the kernel
$make-kpkg clean
$fakeroot make-kpkg --initrd kernel_headers

Now the kernel compilation will start. Be patient as the compilation may take a few hours

6. Once you notice that the compilation was successful, execute the following
$cd /usr/src
$dpkg -i linux-image-'kernel version'.deb
$dpkg -i linux-headers-'kernel version'.deb

7. Go to '/boot' directory to make sure the initrd file exists
$cd /boot
$ls -l initrd.img-'kernel version'

8. Now open the menu.lst file and check if the boot information of the new kernel version is added to the grub
$vi /boot/grub/menu.lst
find an entry for the new kernel that is installed, eg:

title           Ubuntu, kernel 2.6.33
root (hd0,0)
kernel /boot/vmlinuz-2.6.33 root=/dev/sda1 ro quiet splash
initrd /boot/initrd.img-2.6.33
savedefault
boot

title Ubuntu, kernel 2.6.33 (recovery mode)
root (hd0,0)
kernel /boot/vmlinuz-2.6.33 root=/dev/sda1 ro single
initrd /boot/initrd.img-2.6.33
boot
 
9. Now restart the machine and select the kernel version you just installed, when you are on the boot screen
 After the system boots execute
$uname -r
to make sure you booted with the kernel version, you just installed
Congratulations! You have just compiled and installed the linux kernel!