A pipe between two processes is similar to a tubular piece of plumbing. When a UNIX pipe is created, a data pipeline is formed between a writing process and a reading process. The UNIX pipe can become plugged if the reading process does not continue to receive the piped data. Unlike a physical pipe, however, some versions of UNIX insist that the data must flow in one direction: from its source to its destination.
In Chapter 2, "UNIX File System Objects," you read about FIFOs, which are also known as named pipes. This chapter, however, will be concerned with nameless pipes. Unlike FIFOs, nameless pipes are created in the open state and only exist between processes.
The system call that is responsible for creating nameless pipes is the function pipe(2). Its function synopsis is as follows:
#include <unistd.h> int pipe(int fildes[2]);
The pipe(2) call returns one pair of file descriptors that represent both ends of the pipe. When the function is successful, the array fildes[] is populated with two open file descriptors, and the value 0 is returned. Otherwise -1 is returned, and an error code is left in the external variable errno.
Note
The close-on-exec flag is not set on the two file descriptors that are returned by pipe(2).
Systems that only support unidirectional pipes will provide fildes[0] as a file descriptor capable of reading only. The descriptor fildes[1] will be capable of writing only. Data written to fildes[1] can be read at the opposite end of the pipe with file descriptor fildes[0].
Systems that support STREAMS-based pipes allow reading and writing to both ends. Data written to fildes[0] is read via descriptor fildes[1]. Data written to fildes[1] is read via descriptor fildes[0]. In this respect, the STREAMS-based pipe is similar to a connected socket (the curious may read about socketpair(2)).
The following example shows how a pipe is created:
int z; /* General status code */ int fildes[2]; /* Pair of file descriptors */ if ( (z = pipe(&fildes[0])) == -1 ) { perror("pipe(2)"); /* Report the error */ exit(13); ) printf("fildes[0] = %d, for reading ",fildes[0]); printf("fildes[1] = %d, for writing ",fildes[1]);
This example shows how a pipe is created and how its file descriptors are reported (a unidirectional pipe is assumed in this example).
Note
The value st_size returned by fstat(2) is the number of bytes available for reading. For systems that support only unidirectional pipes, the same value st_size is returned for either file descriptor fildes[0] or fildes[1].
For STREAMS-based pipes, the st_size value returned by fstat(2) is the number of bytes available for reading at the specified end of the pipe. Descriptor fildes[0] or fildes[1] specifies which end of the pipe to query.
The creation of a pipe within one process may not appear to be useful. However, when you couple this functionality with the fork(2) system call, which is covered in Chapter 19, "Forked Processes," this becomes a powerful tool.
Because fork(2) is covered in the next chapter, this discussion will now turn to the popen(3) call. The pipe(2) function was introduced here because the popen(3) function calls upon it internally.
Note
FreeBSD release 3.4, UnixWare 7, and Solaris 8 support STREAMS-based pipes (bi-directional).
SGI IRIX 6.5 and HPUX 10.0 and later can be configured to use STREAMS-based (bi-directional) or unidirectional pipes. SGI also permits STREAMS-based pipe support to be chosen at program link time.
Only the unidirectional pipe is supported by Linux and IBM's AIX 4.3.
The C standard I/O library popen(3) makes it easy for the application programmer to open a pipe to an external process. It makes the necessary call to pipe(2) and then calls upon fork(2) to start a new process, which is attached to a pipe. The function synopsis for popen(3) is as follows:
#include <stdio.h> FILE *popen(const char *command, const char *mode); int pclose(FILE *stream);
The popen(3) function arguments are similar to the fopen(3) function except that the first argument is a command rather than a pathname. The argument command must be a command that is acceptable to the UNIX shell.
The second argument mode must be the C string "r" for reading or "w" for writing. No other combination, such as "w+", is acceptable. A popen(3) pipe must be opened for reading from a process or writing to a process, but never for both. When popen(3) is successful, a valid FILE pointer is returned. Otherwise, a null pointer is returned and the error is posted to errno.
Successfully opened pipes must be later closed by a call to pclose(3). The return value for pclose(3) is the termination status of the shell process.
Warning
Calling popen(3) from a set-user-ID program is dangerous. The popen(3) function uses fork(2) and exec(2) to invoke the new shell, and consequently it is possible for a security leak to occur (the current effective user and group ID values are saved by exec(2)). The shell and the commands invoked are subject to environment variable settings such as PATH and SHELL.
The C string given as argument command to popen(3) must be acceptable to the shell. This is because the popen(3) function invokes a shell process first. The entire pipe creation process can be described as follows:
The popen(3) function creates a nameless pipe with a call to pipe(2).
The popen(3) function calls functions fork(2) and execve(2) to start the shell.
The shell interprets your command string that was provided in the call to popen(3).
The command process started by popen(3) is referred to as the child process of your current process. The current process that has called popen(3) is known as the parent process. This terminology helps to identify the process relationships involved.
Because the command string is passed to the shell, you have considerable flexibility in the features at your disposal. This includes the ability to use command lines that use wildcard filenames and shell input and output redirection operators. Additionally, you may use the pipe symbol to create additional pipes to other processes.
Warning
If you write programs that use the popen(3) function and that must be portable to other UNIX operating systems, keep in mind the limitations of the shell. Different shell programs are used on some UNIX platforms, with varying capabilities.
The current process environment is important to the shell that is invoked by the popen(3) call to start your command. This means that any commands that you expect it to invoke are subject to the usual PATH directory searches.
The short program in Listing 18.1 shows a simple program that opens a pipe to the ps(1) command. After the pipe is opened, the program reads from the pipe until end-of-file is reached. All read data is displayed on standard output.
The program begins by opening a read pipe to the command ps -l in line 14. Once the pipe has been opened successfully, the program reads each text line in the loop in lines 24 and 25, until end-of-file is reached. Then the pclose(3) function is called to properly close the pipe p (line 27).
The following FreeBSD compile and run session is provided as follows:
$ make popen cc -c -Wall popen.c cc -o popen popen.o $ ./popen UID PID PPID CPU PRI NI VSZ RSS WCHAN STAT TT TIME COMMAND 1001 7590 1 0 10 0 596 344 wait Is p0 0:00.05 -sh (sh) 1001 7593 7592 1 10 0 596 344 wait Ss p1 0:00.22 -sh (sh) 1001 7813 7593 1 -6 0 780 408 piperd S+ p1 0:00.01 ./popen 1001 7814 7813 1 10 0 496 332 wait S+ p1 0:00.00 sh -c ps -l 1001 7815 7814 1 28 0 376 244 - R+ p1 0:00.00 ps -l $
The last three lines of output show the processes involved (the preceding ones are for the xterm(1) session that was being used). Process 7813 is the process used to execute the program ./popen. However, note how the popen(3) call has created two new processes:
Process 7814 is the shell that has been started to execute the command.
Process 7815 is the command process itself (the ps(1) command).
Although you cannot see the single quotes that were used, you can see how the popen(3) process created the command process using the shell process 7814. If you could see the single quotes, you would see:
sh -c 'ps -l'
This demonstrates the work that the popen(3) function has performed for you by calling upon pipe(2), fork(2), and execve(2). The functions fork(2) and execve(2) are discussed in Chapter 19.
Note
The command-line options for the ps(1) command differ for different UNIX platforms. The examples presented in this chapter assume FreeBSD release 3.4.
When a pipe is being written to by the current process, another process at the other end of the pipe is reading that data from its standard input. To illustrate that procedure, look at the example program provided in Listing 18.2.
This program looks up your user ID in lines 18–21. Then it forms a command to start an email to your current account in lines 26–28. A write pipe is created by calling popen(3) in line 33. The message text lines are written to lines 41–52. The message text is completed by sending end-of-file to the mail(1) command by calling pclose(3) in line 54.
Compiling and running this command under FreeBSD yields the following result:
$ make pmail cc -c -Wall pmail.c cc -o pmail pmail.o $ ./pmail Message sent to ehg $
Checking the mailbox yields results similar to this:
$ mail Mail version 8.1 6/6/93. Type ? for help. "/var/mail/ehg": 1 message 1 new >N 1 ehg Mon Jun 19 23:20 20/588 "A message from process ID 7943" & 1 Message 1: From ehg Mon Jun 19 23:20:24 2000 Date: Mon, 19 Jun 2000 23:20:24 -0400 (EDT) From: "Earl Grey" <ehg> To: ehg Subject: A message from process ID 7943 This is command ./pmail speaking. I am operating in the account for Earl Grey I'd like to operate in root instead. I could do more damage there. :) Sincerely, Process ID 7943 & d 1 & q $
This demonstrated how your C program could write data to another external process through a pipe.
After a pipe is opened for reading or writing, the pipe must be closed by a call to pclose(3). This allows a number of important concluding operations to take place:
The wait(2) function (or equivalent) must be called to pause the execution of the current process until the child process terminates.
Obtain success or failure information from wait(2) about the child process that has terminated.
Destroy the FILE control block.
The wait(2) call is necessary to obtain termination status about the child process. This is fully discussed in the next chapter.
Because popen(3) returns a pointer to FILE, there is a strong urge by programmers to close the open pipe with a call to fclose(3). However, close popen(3) pipes with pclose(3) only. On some platforms, using fclose(3) on a popen(3) FILE stream will cause the program to abort.
Warning
Always use function pclose(3) to close a pipe opened with popen(3). Failure to obey this rule will result in undetected process errors, possible memory leaks and, on some UNIX platforms, aborts.
Furthermore, this practice can result in zombie processes while your program continues to run. For more about zombie processes, see Chapter 19.
When a program has opened a pipe to another process for writing, and that other process has aborted, the read end of the pipe becomes closed. At that point, the pipe is half closed and there is no hope for it to be emptied of data—there is no process reading from it. This causes the UNIX kernel to raise the signal SIGPIPE in the process that is attempting to write to the pipe. This indicates to the writer that the pipe is broken.
The signal SIGPIPE is not always desirable for this purpose. You can elect to ignore the signal SIGPIPE and simply allow the write(2) function to return an error when this condition arises (error code EPIPE).
For example, you could alter Listing 18.2 as follows:
1: /* pmail.c */ 2: 3: #include <stdio.h> 4: #include <stdlib.h> 5: #include <unistd.h> 6: #include <pwd.h> 7: #include <sys/types.h> 8: 9: int 10: main(int argc,char **argv) { 11: struct passwd *pw = 0; /* Password info */ 12: char cmd[256]; /* Command buffer */ 13: FILE *p = 0; /* mailx pipe */ 14: 15: signal(SIGPIPE,SIG_IGN); /* Ignore SIGPIPE */
Line 15 adds a call to signal(3) that requests the action SIG_IGN for signal SIGPIPE. The default action for signal SIGPIPE is to terminate the process. Consequently, you must be prepared for this signal in programs that work with pipes.
Note
EPIPE—Broken pipe This error indicates that the calling process is not able to perform a write(2) (or equivalent) operation on a file descriptor because it is writing to a pipe with no reading processes.
3.147.89.24