Section 10.5. Simple Children

   


10.5. Simple Children

Although fork(), exec(), and wait() allow programs to make full use of the Linux process model, many applications do not need that much control over their children. There are two library functions that make it easier to use child processes: system() and popen().

10.5.1. Running and Waiting with system()

Programs regularly want to run another program and wait for it to finish before continuing. The system() function allows a program to do this easily.

 int system(const char * cmd); 


system() forks a child process that exec()s/bin/sh, which then runs cmd. The original process waits for the child shell to exit and returns the same status code that wait() does.[13] If you do not need the shell to hang around (which is rarely necessary), cmd should contain a preceding "exec", which causes the shell to exec() cmd rather than run cmd as a subprocess.

[13] In the process, system() blocks SIGCHLD, which will cause a SIGCHLD signal to be delivered to the program immediately before system() returns (but after system() has called wait() on the process it spawned), so programs that use signal handlers need to be careful to handle this possibly spurious signal. The system() function also ignores SIGINT and SIGQUIT, which means that a tight loop calling system() repeatedly could be uninterruptible by everything except SIGSTOP and SIGKILL.

As cmd is run through the /bin/sh shell, normal shell rules for command expansion apply. Here is an example of system() that displays all the C source files in the current directory.

 #include <stdlib.h> #include <sys/wait.h> int main() {     int result;     result = system("exec ls *.c");     if (!WIFEXITED(result))         printf("(abnormal exit)\n");     exit(0); } 


The system() command should be used very carefully in programs that run with special permissions. As the system shell provides many powerful features and is strongly influenced by environment variables, system() provides many potential security weaknesses for intruders to exploit. As long as the application is not a system daemon or a setuid/setgid program, however, system() is perfectly safe.

10.5.2. Reading or Writing from a Process

Although system() displays the command's output on standard output and allows the child to read from standard input, this is not always ideal. Often, a process wants to read the output from a process or send it text on standard input. popen() makes it easy for a process to do this.[14]

[14] While popen() makes this easy, it has some hidden behaviors that are not immediately obvious. It creates a child process that might terminate before pclose() is called, which would cause the wait() functions to return status on the process. When that process ends, it also causes a SIGCHLD to be generated, which could confuse a naively written signal handler.

 FILE * popen(const char * cmd, const char * mode); 


The cmd is run through the shell, just as with system(). The mode should be "r" if the parent wants to read the command's output and "w" to write to the child's standard input. Note that you cannot do both with popen(); two processes reading from and writing to each other is complex[15] and beyond popen()'s abilities.[16]

[15] This type of processing often results in deadlocks, in which process A is waiting for process B to do something, while process B is waiting for process A, resulting in nothing at all getting done.

[16] If you find yourself needing to do this, start the child with fork() and exec() and use poll() to read to and write from the child process. A program called expect is designed to do this.

popen() returns a FILE * (as defined by the ANSI/ISO standard I/O library), which can be read from and written to just like any other stdio stream,[17] or NULL if the operation fails. When the parent process is finished, it should use pclose() to close the stream and terminate the child process if it is still running. Like system(), pclose() returns the child's status from wait4().

[17] For information on reading and writing from stdio streams, consult [Kernighan, 1988].

 int pclose(FILE * stream); 


Here is a simple calculator program that uses the bc program to do all of the real work. It is important to flush the popen() ed stream after writing to it to prevent stdio buffering from delaying output (see [Kernighan, 1988] for details on buffering in the ANSI/ISO C stdio library functions).

  1: /* calc.c */  2:  3: /* This is a very simple calculator which uses the external bc  4:    command to do everything. It opens a pipe to bc, reads a command  5:    in, passes it to bc, and exits. */  6: #include <stdio.h>  7: #include <sys/wait.h>  8: #include <unistd.h>  9: 10: int main(void) { 11:     char buf[1024]; 12:     FILE * bc; 13:     int result; 14: 15:     /* open a pipe to bc, and exit if we fail */ 16:     bc = popen("bc", "w"); 17:     if (!bc) { 18:        perror("popen"); 19:        return 1; 20:    } 21: 22:    /* prompt for an expression, and read it in */ 23:    printf("expr: "); fflush(stdout); 24:    fgets(buf, sizeof(buf), stdin); 25: 26:    /* send the expression to bc for evaluation */ 27:    fprintf(bc, "%s\n", buf); 28:    fflush(bc); 29: 30:    /* close the pipe to bc, and wait for it to exit */ 31:    result = pclose(bc); 32: 33:    if (!WIFEXITED(result)) 34:        printf("(abnormal exit)\n"); 35: 36:    return 0; 37: } 


Like system(), popen() runs commands through the system shell and should be used very cautiously by programs that run with root credentials.


       
    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