-2

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);
John Stawket
  • 17
  • 2
  • 9
  • 2
    For `execvp()`, you need a final NULL pointer — so you need `char *args[5];` and `args[4] = NULL;` (or `= 0`). The `execlp()` is almost OK; strictly, you're supposed to use `(char *)NULL` for the last argument. This might matter on a 64-bit machine where the system provides `#define NULL 0` with no other type. That's a valid null pointer — but only in contexts where it is known that a null pointer is required, and the argument list to `execlp()` isn't one of those contexts. – Jonathan Leffler May 12 '17 at 04:34
  • How do you know it isn't read? – user253751 May 12 '17 at 04:39
  • 1
    Notice that `symlink` is *not* a command (available in your shell). It is a system call. – Basile Starynkevitch May 12 '17 at 04:58
  • 1
    No: it was suggested that you use the `symlink()` system call. `if (symlink(argv[2], argv[3]) != 0) { …report error… }`. – Jonathan Leffler May 12 '17 at 04:58

1 Answers1

2

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

Community
  • 1
  • 1
Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
  • I've edited the question- I don't think my (my University's) version of linux (a very old one) is able to execute the symlink() command. Is there a way to do this with the execlp or execvp? – John Stawket May 12 '17 at 04:51
  • 2
    I'm pretty sure your Linux has the `symlink` syscall. AFAIK it was available even in 1993 Linux kernels and probably even in the first one. You basically can't have a Unix system without it (because it is needed to *implement* the `/bin/ln` command) – Basile Starynkevitch May 12 '17 at 04:54
  • [Here is what I get when I attempt it in the command line.](http://i.imgur.com/qA6tMSE.png) It is an old version of Unix (my apologies) and I don't believe it is included. – John Stawket May 12 '17 at 04:58
  • I did explain that `symlink` is a system call, not a command. – Basile Starynkevitch May 12 '17 at 05:00
  • 1
    Which 'old version of Unix' are you using, @JohnStawket? It would have to be very old (20 years or more) and one of the staider variants even then. POSIX required `symlink()` as a system call from sometime in the 90s. POSIX 1990 did not mandate symbolic links — I checked my paper copy of that standard; POSIX 1997 did. I'm not sure if there was an intermediate version. – Jonathan Leffler May 12 '17 at 05:02
  • @JohnStawket: Where did you got the (wrong) impression that `symlink` is a command? You look confused, and I never wrote that, and the links I have provided don't state anything similar. It is a *system call* (you need to understand what these are) and you can call it as a C function (but I won't work in your shell). – Basile Starynkevitch May 12 '17 at 05:05
  • My apologies, I am a novice at C and linux. However, I did attempt it (my sample code is edited into the original post). Did i implement it incorrectly? When I execute the function, no soft link file is created. Is it necessary that I use fork() and wait()? – John Stawket May 12 '17 at 05:05
  • Yes, you are confused. Take several days to read the links I gave and both *Advanced Linux Programming* and *Operating system : three easy pieces*; they are freely available books that you need to read (and that takes several days). If you insist on using some `exec` you'll better understand how to use `fork` & `waitpid` – Basile Starynkevitch May 12 '17 at 05:06
  • I am sorry, I do not have time as this assignment is due in a few days, and this is the last piece I need. If you could examine my attempt to call symlink() too see if it is incorrect, any help is appreciated. and @jonathan-leffler I am running Solaris 10 s10s_u6wos_07b SPARC – John Stawket May 12 '17 at 05:16
  • 2
    @JohnStawket: Solaris 10 has the `symlink()` system call. Very few if any systems have a `symlink` command — it is not standard and the `ln -s` command suffices. – Jonathan Leffler May 12 '17 at 05:17