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.
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]
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]
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().
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.