Section 11.7. Adding Redirection to ladsh

   


11.7. Adding Redirection to ladsh

Now that we have covered the basics of file manipulation, we can teach ladsh to redirect input and output through files and pipes. ladsh2.c, which we present here, handles pipes (denoted by a | in ladsh commands, just as in most shells) and input and output redirection for arbitrary file descriptors. We show only the modified pieces of code here full source to ladsh2.c is available from http://ladweb.net/lad/src/. The changes to parseCommand() are a simple exercise in string parsing, so we do not bother discussing them here.

11.7.1. The Data Structures

Although ladsh1.c included the concept of a job as multiple processes (presumably tied together by pipes), it did not provide a way of specifying which files to use for a child's input and output. To allow for this, new data structures are introduced and existing ones modified.

 24:                        REDIRECT_APPEND }; 25: 26: struct redirectionSpecifier { 27:     enum redirectionType type;  /* type of redirection */ 28:     int fd;                 /* file descriptor being redirected */ 29:     char * filename;        /* file to redirect fd to */ 30: }; 31: 32: struct childProgram { 33:     pid_t pid;              /* 0 if exited */ 34:     char ** argv;           /* program name and arguments */ 35:     int numRedirections;    /* elements in redirection array */ 36:     struct redirectionSpecifier * redirections; /* I/O redirs */ 37: }; 


struct redirectionSpecifier tells ladsh2.c how to set up a single file descriptor. It contains an enum redirectionType that tells us whether this redirection is an input redirection, an output redirection that should be appended to an already existing file, or an output redirection that replaces any existing file. It also includes the file descriptor that is being redirected, as well as the name of the file involved. Each child program (struct childProgram) now specifies an arbitrary number of redirections for itself.

These new data structures are not involved in setting up pipes between processes. As a job is defined as multiple child processes with pipes tying them together, there is no need for more explicit information describing the pipes. Figure 11.1 shows how these new data structures would look for the command tail < input-file | sort > output-file.

Figure 11.1. Job Data Structures for ladsh2.c


11.7.2. Changing the Code

Once parseCommand() has set up the data structures properly, running the commands in the proper sequence is easy enough, as long as you watch the details. First of all, we added a loop to runCommand() to start the child processes, because there could now be multiple children. Before entering the loop, we set up nextin and nextout, which are the file descriptors to use for the standard input and the standard output of the next process we start. To begin with, we use the same stdin and stdout as the shell.

Now we take a look at what happens inside the loop. The basic idea is as follows:

1.

If this is the final process in the job, make sure nextout points at stdout. Otherwise, we need to connect the output of this job to the input side of an unnamed pipe.

2.

Fork the new process. Inside the child, redirect stdin and stdout as specified by nextout, nextin, and any file redirections that were specified.

3.

Back in the parent, we close the nextin and nextout used by the just-started child (unless they are the shell's own stdin or stdout).

4.

Now set up the next process in the job to receive its input from output of the process we just created (through nextin).

Here is how these ideas translate into C:

 365:     nextin = 0, nextout = 1; 366:     for (i = 0; i < newJob.numProgs; i++) { 367:         if ((i + 1) < newJob.numProgs) { 368:             pipe(pipefds); 369:             nextout = pipefds[1]; 370:         } else { 371:             nextout = 1; 372:         } 373: 374:         if (!(newJob.progs[i].pid = fork())) { 375:             if (nextin != 0) { 376:                 dup2(nextin, 0); 377:                 close(nextin); 378:             } 379: 380:             if (nextout != 1) { 381:                 dup2(nextout, 1); 382:                 close(nextout); 383:             } 384: 385:             /* explicit redirections override pipes */ 386:             setupRedirections(newJob.progs + i); 387: 388:             execvp(newJob.progs[i].argv[0], newJob.progs[i].argv); 389:             fprintf(stderr, "exec() of %s failed: %s\n", 390:                     newJob.progs[i].argv[0], 391:                     strerror(errno)); 392:             exit(1); 393:         } 394: 395:         /* put our child in the process group whose leader is the 396:            first process in this pipe */ 397:         setpgid(newJob.progs[i].pid, newJob.progs[0].pid); 398: 399:         if (nextin != 0) close(nextin); 400:         if (nextout != 1) close(nextout); 401: 402:         /* If there isn't another process, nextin is garbage 403:            but it doesn't matter */ 404:         nextin = pipefds[0]; 


The only other code added to ladsh2.c to allow redirection was setupRedirections(), the source of which appears unchanged in all subsequent versions of ladsh. Its job is to process the struct redirectionSpecifier specifiers for a child job and modify the child's file descriptors as appropriate. We recommend reading over the function as it appears in Appendix B to ensure you understand its implementation.


       
    top
     


    Linux Application Development
    Linux Application Development (paperback) (2nd Edition)
    ISBN: 0321563220
    EAN: 2147483647
    Year: 2003
    Pages: 168

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net