Using execvp or execlp with a flag?

1.5k views Asked by At

I'm trying to write a C program to soft-link 2 files on Unix. In Unix, this command would be:

ln -s oldfile newlink  

So I wrote a code to create a char * array of the arguments like so:

char *args[4];
args[0] = "ln";
args[1] = "-s";
args[2] = argv[2]; //argv[2] is the name of the old file
args[3] = argv[3]; //argv[3] is the name of the new soft link file


execvp(args[0], args); 

But the -s flag is not read by the Linux processor. How can I rewrite this in a way that it will handle the -s flag? I tried to do it with execlp as well:

execlp(args[0], args[0], args[1], args[2], args[3], NULL);  

But that also does not work. What am I doing wrong in these lines?

EDIT: I have also attempted the symlink() command, but I don't think my version of linux supports it, unless I am wrong and there is a flaw in my code:

char *args[4];
args[0] = "symlink";
args[1] = argv[2];
args[2] = argv[3];
args[3] = NULL;



execvp(args[0], args);
1

There are 1 answers

10
Basile Starynkevitch On

write a C code to soft-link 2 files on linux

For that specific purpose, you don't need to start a /bin/ln process in your C code. You should instead use the symlink(2) system call (which would be used by the ln process); this is simpler and much faster. Don't forget to check its success. Notice that symlink is a system call (that even old Linux kernels should have) available as a C function, not a command (so indeed you cannot run any symlink command or executable in your shell). As documented, you need to #include <unistd.h> in your C source file. Read also symlink(7).

using the symlink system call

For example, to do the equivalent of ln -s ~/somefile /tmp you would first compute the path (e.g. using snprintf(3)...) corresponding to ~/somefile (by using getenv ...):

char* somefilepath = "somefile";
char oldpathbuf[256];
snprintf(oldpathbuf, sizeof(pathbuf), "%s/%s", getenv("HOME"), somefilepath);
/// missing detecting and handling of errors in above

(and I leave you to handle all the error cases, including lack of space for snprintf, and they are important!)

Then you need to compute the path of the new link (you cannot use symlink system call on directories):

 char newpathbuf[256];
 snprintf(newpathbuf, sizeof(newpathbuf), "/tmp/%s", somefilepath);

(again, handle the errors, and think what would happen if somefilepath starts with ../)

At last, do the system call but check against failure:

 if (symlink(newpathbuf, oldpathbuf)) {
   perror("symlink");
   exit(EXIT_FAILURE);
 }

executing the /bin/ln program

If you insist (wrongly IMHO) on using the execve(2) syscall on /bin/ln or some exec(3) function (which would call execve), be sure to explicitly add a NULL pointer. BTW on success these exec functions don't return, so you probably need to call fork(2) before and to use waitpid(2) after.

Be aware that execvp uses the PATH variable. So passing just ln to it might run (if your user has a weird $PATH setting) something else than /bin/ln (that file path is specified in the Linux FHS and in POSIX) with some unexpected side-effect. But see environ(7).

the -s flag is not read by the Linux processor

There is no "Linux processor" involved. The -s flag is handled by the /bin/ln executable program (whose main function gets the expanded program arguments, and which then calls the symlink system call). You need to understand more the role of unix shells and what globbing is and how a command is expanded by the shell.

useful references to read

I recommend reading Advanced Linux Programming and the intro(2) and syscalls(2) man page.

You probably should read more about Operating Systems and understand the difference between commands and system calls and the role of any Unix shell. I recommend reading the freely available Operating Systems : Three Easy Pieces