Add Book to My BookshelfPurchase This Book Online

Chapter 11 - Processes

UNIX Systems Programming for SVR4
David A. Curry
 Copyright © 1996 O'Reilly & Associates, Inc.

Redirecting Input and Output
One of the most useful features of the UNIX shells, aside from their obvious ability to execute commands, is their ability to redirect input and output. For example, the command
ls > listing
places the output from the ls command into the file listing instead of sending it to the screen. Likewise, the command
a.out < data
tells the a.out command to read its input from the file data instead of from the keyboard. How does the shell arrange for this to work?
Earlier in the chapter, we said that files remain open across a call to exec. Thus, if we can arrange for the standard input (file descriptor 0) and the standard output (file descriptor 1) to refer to the files we want to use for input and output before calling exec, the newly-executed program will read from and write to these files.
In Chapter 3, Low-Level I/O Routines, we described the dup and dup2 functions:
    #include <unitstd.h>
    int dup(int fd);
    int dup2(int fd, int fd2);
As you may recall, dup returns a new file descriptor that references the same file as fd. The new descriptor has the same access mode (read, write, or read/write) and the same read/write offset as the original. The file descriptor returned will be the lowest numbered one available. dup2 causes the file descriptor fd2 to refer to the same file as fd. If fd2 refers to an already-open file, that file is closed first.
 NoteThe bufsplit function is broken in some versions of Solaris 2.4. If this example does not work for you, edit the example program and remove the “#ifdef notdef” and “#endif” to enable the use of a locally-defined version of the function.
Thus, all that is necessary to perform input and output redirection in the shell is to have the shell open the files in question, call dup or dup2 to attach those files to file descriptors 0 and 1, and then execute the command. Example 11-6 shows a rudimentary shell-like program that does just this.
Example 11-6:  shell
#include <sys/types.h>
#include <sys/wait.h>
#include <libgen.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#define NARGS   64
int execute(char **, char *, char *);
int
main(void)
{
    char **cp;
    int n, status;
    char *args[NARGS];
    char command[BUFSIZ];
    char *infile, *outfile;
    /*
     * Set up bufsplit to parse the command line.
     */
    bufsplit(" \t\n", 0, NULL);
    /*
     * Forever...
     */
    for (;;) {
        /*
         * Prompt for a command.
         */
again:  printf("--> ");
        /*
         * Read a command.  If NULL is returned, the
         * user typed CTRL-D, so exit.
         */
        if (fgets(command, sizeof(command), stdin) == NULL) {
            putchar('\n');
            exit(0);
        }
        /*
         * Split the command into words.
         */
        n = bufsplit(command, NARGS, args);
        args[n] = NULL;
        /*
         * Ignore blank lines.
         */
        if (**args == '\0')
            continue;
        /*
         * Find any input and output redirections.
         */
        infile = NULL;
        outfile = NULL;
        for (cp = args; *cp != NULL; cp++) {
            if (strcmp(*cp, "<") == 0) {
                if (*(cp+1) == NULL) {
                    fprintf(stderr, "You must specify ");
                    fprintf(stderr, "an input file.\n");
                    goto again;
                }
                *cp++ = NULL;
                infile = *cp;
            }
            else if (strcmp(*cp, ">") == 0) {
                if (*(cp+1) == NULL) {
                    fprintf(stderr, "You must specify ");
                    fprintf(stderr, "an output file.\n");
                    goto again;
                }
                *cp++ = NULL;
                outfile = *cp;
            }
        }
        /*
         * Execute the command.
         */
        status = execute(args, infile, outfile);
    }
}
/*
* execute - execute a command, possibly with input/output redirection
*/
int
execute(char **args, char *infile, char *outfile)
{
    int status;
    pid_t p, pid;
    int infd, outfd;
    extern int errno;
    sigset_t mask, savemask;
    struct sigaction ignore, saveint, savequit;
    infd = -1;
    outfd = -1;
    /*
     * If an input file was given, open it.
     */
    if (infile != NULL) {
        if ((infd = open(infile, O_RDONLY)) < 0) {
            perror(infile);
            return(-1);
        }
    }
    /*
     * If an output file was given, create it.
     */
    if (outfile != NULL) {
        if ((outfd = creat(outfile, 0666)) < 0) {
            perror(outfile);
            close(infd);
            return(-1);
        }
    }
    /*
     * Set up a sigaction structure to ignore signals.
     */
    sigemptyset(&ignore.sa_mask);
    ignore.sa_handler = SIG_IGN;
    ignore.sa_flags = 0;
    /*
     * Ignore keyboard signals; save old dispositions.
     */
    sigaction(SIGINT, &ignore, &saveint);
    sigaction(SIGQUIT, &ignore, &savequit);
    /*
     * Block SIGCHLD.
     */
    sigemptyset(&mask);
    sigaddset(&mask, SIGCHLD);
    sigprocmask(SIG_BLOCK, &mask, &savemask);
    /*
     * Start a child process.
     */
    if ((pid = fork()) < 0)
        status = -1;
    /*
     * This code executes in the child process.
     */
    if (pid == 0) {
        /*
         * Restore signals to their original dispositions,
         * and restore the signal mask.
         */
        sigaction(SIGINT, &saveint, (struct sigaction *) 0);
        sigaction(SIGQUIT, &savequit, (struct sigaction *) 0);
        sigprocmask(SIG_SETMASK, &savemask, (sigset_t *) 0);
        /*
         * Perform output redirection.
         */
        if (infd > 0)
            dup2(infd, 0);
        if (outfd > 0)
            dup2(outfd, 1);
        /*
         * Execute the command.
         */
        execvp(*args, args);
        perror("exec");
        _exit(127);
    }
    /*
     * Wait for the child process to finish.
     */
    while (waitpid(pid, &status, 0) < 0) {
        /*
         * EINTR (interrupted system call) is okay; otherwise,
         * we got some error that we need to report back.
         */
        if (errno != EINTR) {
            status = -1;
            break;
        }
    }
    /*
     * Restore signals to their original dispositions,
     * and restore the signal mask.
     */
    sigaction(SIGINT, &saveint, (struct sigaction *) 0);
    sigaction(SIGQUIT, &savequit, (struct sigaction *) 0);
    sigprocmask(SIG_SETMASK, &savemask, (sigset_t *) 0);
    /*
     * Close file descriptors.
     */
    close(outfd);
    close(infd);
    /*
     * Return the child process' termination status.
     */
    return(status);
}
/*
* The bufsplit() function on Solaris 2.4 is broken.  Remove the
* "#ifdef notdef" and "#endif" lines to enable this version.
*/
#ifdef notdef
size_t
bufsplit(char *buf, size_t n, char **a)
{
    int i, nsplit;
    static char *splitch = "\t\n";
    if (buf != NULL && n == 0) {
        splitch = buf;
        return(1);
    }
    nsplit = 0;
    while (nsplit < n) {
        a[nsplit++] = buf;
        if ((buf = strpbrk(buf, splitch)) == NULL)
            break;
        *(buf++) = '\0';
        if (*buf == '\0')
            break;
    }
    buf = strrchr(a[nsplit-1], '\0');
    for (i=nsplit; i < n; i++)
        a[i] = buf;
    return(nsplit);
}
#endif
    % shell
    --> ls > listing
    --> cat listing
    Makefile
    fork.c
    forkexec.c
    forkexecwait.c
    listing
    shell.c
    shellcmd.c
    system.c
    --> sort -r < listing > listing2
    --> cat listing2
    system.c
    shellcmd.c
    shell.c
    listing
    forkexecwait.c
    forkexec.c
    fork.c
    Makefile
    --> ^D
Technically, the files could be opened in the child process just as well as in the parent; this would save the parent from having to close them later. However, the method used in the example is preferable, because it does not waste a call to fork if one of the files is inaccessible.

Previous SectionNext Section
Books24x7.com, Inc © 2000 –  Feedback