0

So I am looking at my professor's code that he handed out to try and give us an idea of how to implement >, <, | support into our unix shell. I ran his code and was amazed at what actually happened.

if( pid == 0 )
{
    close(1);                                   // close
    fd = creat( "userlist", 0644 );             // then open
    execlp( "who", "who", NULL );               // and run
    perror( "execlp" );
    exit(1);
}

This created a userlist file in the directory I was currently in, with the "who" data inside that file. I don't see where any connection between fd, and execlp are being made. How did execlp manage to put the information into userlist? How did execlp even know userlist existed?

Cody
  • 65
  • 3

3 Answers3

1

Read Advanced Linux Programming. It has several chapters related to the issue. And we cannot explain all this in a few sentences. See also the standard stream and process wikipages.

First, all the system calls (see syscalls(2) for a list, and read the documentation of every individual system call that you are using) your program is doing should be tested against failure. But assume they all succeed. After close(1); the file descriptor 1 (STDOUT_FILENO) is free. So creat("userlist",0644) is likely to re-use it, hence fd is 1; you have redirected your stdout to the newline created userlist file.

At last, you are calling execlp(3) which will call execve(2). When successful, your entire process is restarted with the new executable (so a fresh virtual address space is given to it), and its stdout is still the userlist file descriptor. In particular (unless execve fails) the perror call is not reached.

So your code is a bit what a shell running who > userlist is doing; it does a redirection of stdout to userlist and runs the who command.

If you are coding a shell, use strace(1) -notably with -f option- to understand what system calls are done. Try also strace -f /bin/sh -c ls to look into the behavior of a shell. Study also the source code of existing free software shells (e.g. bash and sash).

See also this and the references I gave there.

Community
  • 1
  • 1
Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
0

execlp knowns nothing. Before execing stdout was closed and a file opened, so the descriptor is the one corresponding to stdout (opens always returns the lowest free descriptor). At that point the process has an "stdout" plugged to the file. Then exec is called and this replaces to whole address space, but some properties remains as the descriptors, so know the code of who is executed with an stdout that correspond to the file. This is the way redirections are managed by shells.

Remember that when you use printf (for example) you never specify what stdout exactly is... That can be a file, a terminal, etc.

Jean-Baptiste Yunès
  • 34,548
  • 4
  • 48
  • 69
0

Basile Starynkevitch correctly explained:

After close(1); the file descriptor 1 (STDOUT_FILENO) is free. So creat("userlist",0644) is likely to re-use it…

This is because, as Jean-Baptiste Yunès wrote, "opens always returns the lowest free descriptor".

It should be stressed that the professor's code only likely works; it fails if file descriptor 0 is closed.

Armali
  • 18,255
  • 14
  • 57
  • 171