Greg Briggs' Technical ArticlesArticle Index

Howto change a UNIX process and child process name by modifying argv[0]

By Greg Briggs

Table of Contents


Basic solution for forking child

Suppose you have a program that forks into multiple threads or something. Suppose you want to change the argv[0] for each child so you can tell them apart in `ps`, but you didn't know how to. The code to do this is below:

 int argv0size = strlen(argv[0]); //Take note of how many chars have been allocated
 ...
 strncpy(argv[0],"main-thread-name",argv0size); //replace argv[0][0..argv0size]
 ...
 fork(); //make child process
 ...
 strncpy(argv[0],"child-thread-name",argv0size); //replace argv[0][0..argv0size]

Hope this is useful. There is a catch, which is that "ps" and "top" can show you either the "command line" or the "program name", and we can only modify the command line. If you are running top, you can hit the "c" key to switch between which one it shows. On my Linux box, "ps u" shows the command line, but "ps" shows the program name. However, on my Solaris 8 machine, both ps and top refuse to show you the new argv[0], so this technique probably only works on Linux (anyone try any other OSes?).

You'll notice that we save the size of argv[0] at the beginning. This is because its size has already been allocated for us, but if we give the parent thread a short name, we still would like the child thread to take full advantage of the space allocated, rather than assuming the current strlen(argv[0]) is the size limit. We can't just allocate a new string, as assigning argv[0] = "newstring" will not be visible to ps and top.

Here is a test program that renames its child:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>

int main(int argc, char **argv) {
        int argv0size = strlen(argv[0]);

        sleep(3);

        strncpy(argv[0],"main",argv0size);

        sleep(3);
        pid_t p = fork();
        if(p) {
                strncpy(argv[0],"parent",argv0size);
        } else {
                strncpy(argv[0],"child",argv0size);
        }
        if(argc>1){
                /* The point of this is to show that the child thread still has
                 * the full original size of argv[0] available for use, even
                 * though we reduced the size of argv[0] prior to the fork */
                printf("argv[0] at 0x%lX and argv[1] at 0x%lX (difference %d bytes)\n",
                        argv[0],argv[1],argv[1]-argv[0]);
        }
        sleep(3);

        if(!p) _exit(0); //child exits here
        waitpid(p);
        exit(EXIT_SUCCESS); //parent exits here
}

If you run the program with an argument, then it will demonstrate the allocation for argv[0]. It will also show you that both processes after the fork still think they are modifying the same memory address, but it is actually different copies of that memory.

Additional solution for when the child will exec/execvp

Thanks to Jerome for providing this additional example.

"My problem in a nut shell is to start child process that have different command line in a ps listing with the undesired lose of certain resources incurred with using exec()."

"The eventual implementation will be a utility method."

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <alloca.h>

int     argsLength;
char ** args;

int
child(void)
{
        int ii;

        fprintf(stderr,"I am child starting\n");
        args[0] = "tt -worker";

        fprintf(stderr,"I am child sleeping\n");
        for(ii = 60;ii = sleep(ii);) ;

        fprintf(stderr,"I am child terminating\n");
        return(0);
} // int child(void)

int
parent(pid_t pid)
{
        int ii;

        fprintf(stderr,"child %p running\n",pid);

        fprintf(stderr,"parent is sleeping\n");
        for(ii = 60;ii = sleep(ii);) ;

        fprintf(stderr,"parent is terminating\n");
        return(0);
} // int parent(void)

int
main(int argc,char *argv[])
{
        pid_t   pid                     = -1;
        int             exitCode;
        char    buf[4 * 1024];
        int             fds[2];
        char *  bp;
        int             ii;

        sscanf(basename(argv[0]),"%s",buf); strcat(buf," -ipl ");
        if (strncmp(argv[0],buf,strlen(buf))) {
                char *newArgv[2] = {0};
                
                for(*buf=0,bp=buf,ii=1;ii < argc;ii++)
                        bp += sprintf(bp,"%s ",argv[ii]);

                fprintf(stderr,"starting\n");
                ii = strlen(argv[0]) + strlen(buf) + 40;
                bp = alloca(ii);
                sprintf(bp,"%s -ipl %-*.*s",argv[0],ii,ii,buf);
                newArgv[0] = bp;

                execvp(argv[0],newArgv);

                exit(99);

        } else {
                char *cmd = alloca(strlen(argv[0]));
                char *arg = alloca(strlen(argv[0]));

                sscanf(argv[0],"%s -ipl %[^\n]",cmd,arg);
                for(bp=arg + strlen(arg) -1;bp > arg;bp--) {
                        if (' ' == *bp) *bp = 0;
                        else                    break;
                } // for(bp=arg + strlen(arg) -1;bp > arg;bp--)

                sprintf(argv[0],"%s %s",cmd,arg);
                fprintf(stderr,"restarting\n");
        } // if ((argv[1]) && 79 > strlen(argv[1]))

        argsLength      = argc;
        args            = argv;
        pid = fork();
        if (0 > pid) {
                perror("fork");
                exit(1);
        } // if (0 > pid)

        if (!(pid)) {
                char *cmd = alloca(strlen(argv[0]));
                char *arg = alloca(strlen(argv[0]));

                sscanf(argv[0],"%s %[^\n]",cmd,arg);
                sprintf(argv[0],"%s -child -a -b -etc %s",cmd,arg);
                child();
        } else {
                char *cmd = alloca(strlen(argv[0]));
                char *arg = alloca(strlen(argv[0]));

                sscanf(argv[0],"%s %[^\n]",cmd,arg);
                sprintf(argv[0],"%s -parent %s",cmd,arg);
        } // if (!(pid))
        parent(pid);
        wait(&exitCode);

        return(exitCode);
} // int main(int argc,char *argv[])

Read more computer-related articles

© 2002-2009 Greg Briggs except where attributed otherwise