Pipes

System Commands and
Interprocess Communication
chroot
• int chroot(const char *path);
• chroot changes the root directory to that
specified in path.
• This directory will be used for path names
beginning with /.
• The root directory is inherited by all children
of the current process.
• Why might you want to do this before the
exec for a CGI program?
Pipes
• A special file that can store a limited amount of data
in a first-in-first-out (FIFO) manner
• The include file <limits.h> or <sys/param.h> contains
a defined constant PIPE_BUF that specifies the
maximum number of bytes a pipe may hold
• One process will write to the pipe (as if it were a file),
while another process will read from the pipe
• The system provides the synchronization between
writing and reading processes
Pipes
• By default, if a writing process attempts to write to a
full pipe, the system will automatically block the
process until the pipe is able to receive the data
• Likewise, if a read is attempted on an empty pipe, the
process will block until data is available
• In addition, the process will block if a specified pipe
has been opened for reading, but another process
has not opened the pipe for writing
• Data is written to the pipe and read from the pipe
using the unbuffered I/O write and read system calls
Read and Write
Include File(s)
<unistd.h>
Summary
ssize_t write( int fildes,
const void *buf,
size_t nbyte );
Success
Failure
Sets errno
Return
Number of bytes written
-1
Yes
Include File(s)
<sys/types>
<sys/uio.h>
<unistd.h>
Summary
Return
ssize_t read( int fildes,
const void *buf,
size_t nbyte );
Success
Failure
Sets errno
Number of bytes read
-1
Yes
PIPES
• A call to the pipe() function returns a pair of file
descriptors.
• One of these descriptors is connected to the write
end of the pipe, and the other is connected to the
read end.
• int pipe( int fildes[2] );
pipe() Example
#include
#include
#include
#include
<stdio.h>
<stdlib.h>
<sys/types.h>
<unistd.h>
int main() {
int pfds[2];
char buf[30];
pipe(pfds);
if (fork()==0) {
printf(" CHILD: writing to the pipe\n");
write(pfds[1], "test", 5); //close pfds[0]
printf(" CHILD: exiting\n");
exit(0);
} else {
printf("PARENT: reading from pipe\n");
read(pfds[0], buf, 5); //close pfds[1]
printf("PARENT: read \"%s\"\n", buf);
wait(NULL);
}
}
popen() Example
#include <stdio.h>
#include <unistd.h>
int main ()
{
// creates a pipe and changes it to a file pointer
// only works one way.
FILE* stream = popen (“sort”, “w”);
fprintf (stream, “This is a test.\n”);
fprintf (stream, “Hello, world.\n”);
fprintf (stream, “My dog has fleas.\n”);
fprintf (stream, “This program is great.\n”);
fprintf (stream, “One fish, two fish.\n”);
return pclose (stream);
}
dup and dup2
• The dup system call duplicates an original open file descriptor
– int dup(int fd)
– The new descriptor, fildes, references the system file table entry for
the next available non-negative file descriptor.
• The dup2 system call duplicates a file descriptor (fd1) onto a
specific file descriptor (fd2)
– int dup2(int fd1, int fd2)
– if fd2 is already open, it will be closed first
• The new descriptor will
– Share the same file pointer(offset)
– Have the same access mode as the original and
– Will be set to remain open across an exec call
dup2
#include <fcntl.h>
#include <unistd.h>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
main()
{
int fd;
fd = open("tmp360", O_CREAT|O_WRONLY, 00777);
dup2(fd, 1);
close(fd);
cout << "what is going on?\n";
cerr << "1111111\n";
}
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main ()
{
int fds[2];
pid_t pid;
pipe (fds);
/* Create a pipe. File descriptors for the two ends of the pipe are placed in fds. */
pid = fork ();
if (pid == (pid_t) 0)
{
close (fds[1]);
/* Fork a child process. */
dup2 (fds[0], STDIN_FILENO);
execlp ("sort", "sort", 0);
/* This is the child process. Close our copy of the write end of the file descriptor. */
/* Connect the read end of the pipe to standard input. */
/* Replace the child process with the sort program. */
}
else
{
/* This is the parent process. */
FILE* stream;
close (fds[0]);
/* Close our copy of the read end of the file descriptor. */
/* Convert the write file descriptor to a FILE object, and write to it. */
stream = fdopen (fds[1], "w");
fprintf (stream, "This is a test.\n");
fprintf (stream, "Hello, world.\n");
fprintf (stream, "My dog has fleas.\n");
fprintf (stream, "This program is great.\n");
fprintf (stream, "One fish, two fish.\n");
fflush (stream);
close (fds[1]);
/* Wait for the child process to finish. */
waitpid (pid, NULL, 0);
}
return 0;
}
Web Servers & Pipes
use dup2 to redirect
stdin and stdout back
to the web server
Client
Web
Server
stdin
CGI programs are defined
to only read from stdin and
write to stdout
stdout
CGI
program
Steps for CGI launching
•
Create two pipes using the pipe system call
– childin and childout
•
fork
– now both the parent and child have the pipes
•
Child
–
–
–
–
–
•
use dup2 to dup the pipe onto stdin and stdout appropriately
dup2( childin[0], 0);// Change stdin to come from the parent
dup2(childout[1], 1);// Change stdout to go back to the parent
close the unused parts of childin and childout
exec the CGI process – It will inherit the new stdin and stdout
Parent
–
–
–
write the body of the HTTP message to childin[1] (the write side of childin)
read from childout[0] (the read side of childout) and send it back to client on the socket
You will know how much to read based on the Content-Length header coming back
from the child process
FIFO
• A FIFO is sometimes known as a named pipe. That is, it's like a
pipe, except that it has a name!
• In this case, the name is that of a file that multiple processes
can open() and read and write to.
• Has to be open at both ends simultaneously before you can
proceed to do any input or output operations on it .
– int mkfifo(const char *path,mode_t mode);
– mkfifo("/tmp/namedpipe" , 0666);
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main ()
{
int pid;
char *namedpipe = “/tmp/MyNamedPipe”;
mkfifo(namedpipe, 0660);
/* Create a fifo. */
pid = fork ();
if (pid == (pid_t) 0)
{
/* This is the child process. */
FILE *input;
char str[256];
/* Fork a child process. */
input = fopen(namedpipe, "r");
while(fgets(str, 255, input))
{
printf("CHILD: %s", str);
}
fclose(input);
/* Open the fifo just like any FILE and read from it */
}
else
{
/* This is the parent process. */
FILE* stream;
stream = fopen (namedpipe, "w");
/* Open the fifo just like any FILE and write to it */
fprintf (stream, "This is a test.\n");
fprintf (stream, "Hello, world.\n");
fprintf (stream, "My dog has fleas.\n");
fprintf (stream, "This program is great.\n");
fprintf (stream, "One fish, two fish.\n");
fflush (stream);
fclose (stream);
waitpid (pid, NULL, 0);
}
return 0;
}
/* Wait for the child process to finish. */