writing to external program through pipe (C) - c

I wanted to use external program to process data in memory. Like external compressor, encoder, anything to process my data and get the result. I read a lot about pipes and it still din't work. So I ended up with simple program that tries to write to external program through pipe like this, letting it to print to stdout:
stdout
(w) pipeA (r) $prog +---+
+-----------+ /~~~~~~~~~~~\ |{1}|
|[1] [0]| ----> |{0} {1}| ----> | |
+~~> +-----------+ \~~~~~~~~~~~/ | |
| +---+
|
+-+
write() |
+-+
And I still got nowhere.
My code goes like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(void)
{
int pipA[2];
int pid;
char buf_IN[32] = "Hello pipe!\n";
ssize_t n_written;
if ((pipe(pipA) == -1)) {
perror("pipe failed");
exit(1);
}
if ((pid = fork()) < 0) {
perror("fork failed");
exit(2);
}
/*****************************/
if (pid == 0)
{ /* in child */
dup2(0, pipA[0]); // pipA[read(0)-end]->$prog[write{0}-end]
close(pipA[1]); // $prog won't write to this pipe(A)
// external ``$prog''ram
execlp("wc", "wc", (char *) 0); // out should be: ' 1 2 12'
//execlp("base64", "base64", (char *) 0); // out should be: 'SGVsbG8gcGlwZSEK'
;///if we're here something went wrong
perror("execlp() #child failed");
exit(3);
}
else
{ /* in parent */
//dup2(pipA[1], 0); // STDIN -> pipA // that supposed to connect STDIN->pipA; just in case I needed it
close(pipA[0]); // we won't read it, let $prog write to stdout
//perror("execlp() #parent failed");
//exit(4);
n_written = write(pipA[1], buf_IN, strlen(buf_IN));
close(pipA[1]); // I guess this will close the pipe and send child EOF
// base64: read error: Input/output error
// wc: 'standard input': Input/output error
// 0 0 0
}
return 0;
}
Comments show what I'm doing. I have to admit I don't get these dup()s in pipes and that's what I think is causing a problem here but don't know.
Can you help with this, seemingly simple problem? Any help appreciated.

Diagnosis
You have the arguments to dup2() back-to-front. You need:
dup2(pipA[0], 0);
Closing file descriptors
You are not closing enough file descriptors in the child:
Rule of thumb: If you
dup2()
one end of a pipe to standard input or standard output, close both of the
original file descriptors returned by
pipe()
as soon as possible.
In particular, you should close them before using any of the
exec*()
family of functions.
The rule also applies if you duplicate the descriptors with either
dup()
or
fcntl()
with F_DUPFD
Prescription
You have some unused defines and unused variables in your code, too. Shorn of all your comments (but with a few of mine to explain what's happening) and with appropriate fixes in place, I end up with:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(void)
{
int pipA[2];
int pid;
char buf_IN[32] = "Hello pipe!\n";
ssize_t n_written;
if ((pipe(pipA) == -1))
{
perror("pipe failed");
exit(1);
}
if ((pid = fork()) < 0)
{
perror("fork failed");
exit(2);
}
if (pid == 0)
{
/* Child: Connect pipA[0] (read) to standard input 0 */
dup2(pipA[0], 0);
close(pipA[1]); /* Close write end of pipe */
close(pipA[0]); /* Close read end of pipe */
execlp("wc", "wc", (char *)0);
perror("execlp() #child failed");
exit(3);
}
else
{
close(pipA[0]); /* Close read end of pipe */
n_written = write(pipA[1], buf_IN, strlen(buf_IN));
if (n_written != (ssize_t)strlen(buf_IN))
{
perror("short write");
exit(4);
}
close(pipA[1]); /* Close write end of pipe — EOF for child */
}
/* Optionally wait for child to die before exiting */
// #include <sys/wait.h> // With other #include lines
// int corpse;
// int status;
// while ((corpse = wait(&status)) > 0)
// printf("Child %d exited with status 0x%.4X\n", corpse, status);
return 0;
}
When run, that produces:
1 2 12
That looks about right.
Without the wait() loop, it is possible that you'll see the output from wc after the prompt from the shell (so it might look as if the program is waiting for input from you, but in fact, it will be the shell waiting for input); with the waiting loop, you'll get proper separation of output from the shell prompt. You don't have to print anything in the body of the loop, but it is reassuring to do so.

To not make duplicates, nor to play expert in unfamiliar field I post it as answer.
I finished the task
(w) pipeA (r) child (w) pipeB (r)
+-----------+ /~~~~~~~~~~~~~\ +-----------+
+~~~>|[1] [0]| ----> |{0} $prog {1}| ----> |[1] [0]| ~~~+
| +-----------+ \~~~~~~~~~~~~~/ +-----------+ |
| \/
+-+ +--+
write() | | read()
+-+ +--+
dup(pA[0],0) dup(pB[1],1)
close(pA[1]) close(pA[0])
close(pA[0]) close(pA[1])
with second pipe that can be read. All by analogy. If there is something major that is wrong with it or something I should be aware of, say it please.
(Sorry for python style indentations, hope you don't mind)
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
ssize_t read_whole_file(int fildes, const void *buf, size_t nbyte);
int main(void)
{
int pipA[2], pipB[2];
int pid;
char buf_IN[32] = "Hello pipe!\n";
char buf_OUT[1024];
char *bptr;
ssize_t n_written, n_read = 0, a_read = 0, to_read = sizeof(buf_OUT);
if ((pipe(pipA) == -1) || (pipe(pipB) == -1))
{
perror("pipe failed");
exit(1);
}
if ((pid = fork()) < 0)
{
perror("fork failed");
exit(2);
}
if (pid == 0)
{
/* in child */
dup2(pipA[0], 0); // connect pipe A (read end/exit) to stdin (write end/input)
close(pipA[1]); // close unused pipe A end
close(pipA[0]); // close - " - //
;
dup2(pipB[1], 1); // connect stdout (read end/output) to pipe B (write end/entry)
close(pipB[0]); // close unused pipe B ends
close(pipB[1]); // close - " - //
execlp("lzip", "lzip", "-c", (char *)0);
;
perror("execlp() #child failed");
exit(3);
}
else
{
/* in parent */
close(pipA[0]); // close pipe A read end - will only write to this one
n_written = write(pipA[1], buf_IN, strlen(buf_IN));
if (n_written < 0)
perror("error: read_whole_file(pipA[1], ...) failed miserably\n");
close(pipA[1]); // close write end which subsequently signals EOF to child
;
close(pipB[1]); // close pipe B write end - will only read form this one
a_read = read_whole_file(pipB[0], buf_OUT, sizeof(buf_OUT));
if (a_read < 0)
perror("error: read_whole_file(pipB[0], ...) failed miserably\n");
close(pipB[0]); // close read end after reading
;
write(STDOUT_FILENO, buf_OUT, a_read); // dump it to parent's stdout - equivalent of processing data received from external program/plugin
}
return 0;
}
ssize_t read_whole_file(int fildes, const void *buf, size_t nbyte) // read whole file
{
ssize_t n_read, a_read = 0, to_read = nbyte;
char *bptr = (char*)buf;
size_t BUF_SIZE = 4096;
do
{
if(to_read < BUF_SIZE)
BUF_SIZE = to_read;
n_read = read(fildes, bptr, BUF_SIZE);
if (n_read < 0) // recover from temporarily failed read (got it from git wrapper)
{
if ((errno == EINTR) || (errno == EAGAIN) || (errno == EWOULDBLOCK))
continue;
}
bptr += n_read;
to_read -= n_read;
a_read += n_read;
} while ((n_read>0) && (to_read>0));
;
if (n_read < 0)
a_read = n_read;
return a_read;
}
To note not so obvious - I still don't get close(pipA[0]); and close(pipB[1]); in child. Why are they not used anymore?
Also dup2(pipB[1], 1);, I thought it would be other way around but it didn't work so by trial end error I come with this.

Related

pipe function returning random characters

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
int main(void){
//Variables, p[2] for each end of the pipe. nbytes to read pipe return value SUCCESS or FAILURE. pid_t to hold pid of fork process.
// buffer to hold response from the child process.
int p[2], nbytes;
pid_t childpid;
char string[] = "Hello, World!\n";
char buffer[80];
//Declaration of pipe
pipe(p);
//Error handling.
if(((childpid = fork()) == -1) || (pipe(p) == -1))
{
perror("fork");
exit(1);
}
//Child process sends message to paprent.
if(childpid == 0)
{
/* Child process closes up input side of pipe */
close(p[0]);
/* Send "string" through the output side of pipe */
write(p[1], string, (strlen(string)+1));
exit(0);
}
else
{
/* Parent process closes up output side of pipe */
close(p[1]);
/* Read in a string from the pipe */
nbytes = read(p[0], buffer, sizeof(buffer));
printf("Received string: %s", buffer);
}
return(0);
}
Output > Received string: #�=zJ
The point of the exercise is to have a child process send a message through a pipe to the parent process and the parent returns the result. This exact code worked the first time I ran it, but then when I tried to run it a second time it started to return seemingly random characters each time. I tried to copy my buffer to another variable but then it was empty. Is the pipe actually not function the way I think it is? What am I doing wrong?
You first create a pipe with pipe(p); and then you create another with ... || (pipe(p) == -1)) Is that deliberate?
2nd Pipe was causing an issue.
You have:
pipe(p);
//Error handling.
if(((childpid = fork()) == -1) || (pipe(p) == -1))
{
perror("fork");
exit(1);
}
This creates two pipes — one in the line pipe(p); and the second in the condition if(((childpid = fork()) == -1) || (pipe(p) == -1)). This is wasteful at best. Moreover, the second pipe is after the fork(), so the parent and child processes don't access the same pipe any more — you overwrote the one created before the fork() which they do share. Test the result of pipe() before calling fork() and remove the extra condition in the if test:
if (pipe(p) != 0)
{
perror("pipe");
exit(1);
}
if ((childpid = fork()) < 0)
{
perror("fork");
exit(1);
}
Get used to testing for errors and writing appropriate code to handle them. It will be a major part of your life as a C programmer.
Later on in the code, you have:
{
/* Parent process closes up output side of pipe */
close(p[1]);
/* Read in a string from the pipe */
nbytes = read(p[0], buffer, sizeof(buffer));
printf("Received string: %s", buffer);
}
You need to heed the value of nbytes. Since it is an int, you could use:
printf("Received %d bytes: [%.*s]\n", nbytes, nbytes, buffer);
This limits the output to what was read, and reports 0 if that's what it gets. I suppose you should also check for -1 in nbytes before using it in the printf() statement:
if (nbytes < 0)
{
fprintf(stderr, "failed to read from pipe descriptor %d\n", p[0]);
// Or perror("read");
// Should you exit here with a non-zero status?
}
else
printf("Received %d bytes: [%.*s]\n", nbytes, nbytes, buffer);
Note: errors are reported on stderr; perror() does that automatically.
The problem is that you create two pipes when you really only need to check the first for errors:
// Declaration of pipe
if(pipe(p) == -1) { // check for error here
perror("pipe");
exit(1);
}
// Error handling.
if((childpid = fork()) == -1) { // and don't create another pipe here
perror("fork");
exit(1);
}
You should also check the return values from write and read. They may not write or read the full string in one go.

How to handle logic chain of filters of shell commands?

I was reading an Operating Systems book and found this example to work. I understood what ps -elf | less was doing. The | (pipe) works as a bridge between ps -elf and less command and takes the output of ps -elf as input to less.
But going in much deeper in the shell commands I'm trying to understand what would
ps -elf | grep "/usr" | wc –l do?
In this case, there are two | (pipes) and the logic is the same. But I cannot implement it into a working example. If you know how to do it, it would make it very clear for me.
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
int fds[2];
char buf[30];
pid_t pid1, pid2, pid;
int status, i;
/* create a pipe */
if (pipe(fds) == -1) {
perror("pipe");
exit(1);
}
/* fork first child */
if ( (pid1 = fork()) < 0) {
perror("fork");
exit(1);
}
if ( pid1 == 0 ) {
close(1); /* close normal stdout (fd = 1) */
dup2(fds[1], 1); /* make stdout same as fds[1] */
close(fds[0]); /* we don't need the read end -- fds[0] */
if( execlp("ps", "ps", "-elf", (char *) 0) < 0) {
perror("Child");
exit(0);
}
/* control never reaches here */
}
/* fork second child */
if ( (pid2 = fork()) < 0) {
perror("fork");
exit(1);
}
if ( pid2 == 0 ) {
close(0); /* close normal stdin (fd = 0)*/
dup2(fds[0],0); /* make stdin same as fds[0] */
close(fds[1]); /* we don't need the write end -- fds[1]*/
if( execlp("less", "less", (char *) 0) < 0) {
perror("Child");
exit(0);
}
/* control never reaches here */
}
/* parent doesn't need fds - MUST close - WHY? */
close(fds[0]);
close(fds[1]);
/* parent waits for children to complete */
for( i=0; i<2; i++) {
pid = wait(&status);
printf("Parent: Child %d completed with status %d\n", pid, status);
}
}
When creating pipes, you need to start with the last command in the pipeline and work backwards from that. By doing so, each program you start is waiting for output from the prior command in the pipeline. If you start from the front, the first program can finish before you have a chance to start the second one, resulting in the pipe being closed.
In this case, you run less first. That sits waiting for output which hasn't come yet. Then you run ps, and its output gets fed to less.
/* fork first child */
if ( (pid1 = fork()) < 0) {
perror("fork");
exit(1);
}
if ( pid1 == 0 ) {
close(0); /* close normal stdin (fd = 0)*/
dup2(fds[0],0); /* make stdin same as fds[0] */
close(fds[1]); /* we don't need the write end -- fds[1]*/
if( execlp("less", "less", (char *) 0) < 0) {
perror("Child");
exit(0);
}
/* control never reaches here */
}
/* fork second child */
if ( (pid2 = fork()) < 0) {
perror("fork");
exit(1);
}
if ( pid2 == 0 ) {
close(1); /* close normal stdout (fd = 1) */
dup2(fds[1], 1); /* make stdout same as fds[1] */
close(fds[0]); /* we don't need the read end -- fds[0] */
if( execlp("ps", "ps", "-elf", (char *) 0) < 0) {
perror("Child");
exit(0);
}
/* control never reaches here */
}

Sending information with unnamed pipes

My program have to send some bytes of information by using unnamed pipes.
I have a txt file named "input" which is supposed to be read by the program and it's information have to be send and write in another file named "output". Also i must use read(), write(), open() functions.
My code look like this:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <string.h>
#define BUFSIZE 25
int main( int argc, char *argv[] ) {
srand(time(NULL));
pid_t pid;
int mypipefd[2];
int ret;
char buf[BUFSIZE];
int output;
int stream;
int nbytes;
ret = pipe(mypipefd);
if( ret == -1 ) {
perror( "pipe error");
exit(1);
}
pid = fork();
if( pid == -1 ) {
perror( "FORK ERROR...");
exit(2);
}
if( pid == 0 ) {
/* CHILD */
printf(" Child process...\n");
stream = open("input.txt", O_RDONLY);
if (close(mypipefd[0]) == -1 ) {
perror("ERROR CLOSING PIPE");
exit(3);
}
while ( (nbytes = read(stream, buf, BUFSIZE)) > 0 ) {
sleep(rand() %2);
write(mypipefd[1], buf, nbytes );
}
if ( close(stream) == -1 ) {
perror("ERROR CLOSING STREAM");
exit(4);
}
}
else {
/* PARENT */
printf(" Parent process...\n");
output = open("output.txt", O_CREAT | O_WRONLY, 00777);
while ( (nbytes = read(mypipefd[0], buf, BUFSIZE)) > 0 ) {
write(output, buf, nbytes);
}
printf("buf: %s\n", buf);
if (close(output) == -1) {
perror("ERROR CLOSING OUTPUT");
exit(5);
}
if (close(mypipefd[1]) == -1 ) {
perror("ERROR CLOSING PIPE");
exit(6);
}
}
return 0;
}
Unfortunately the code is not working terminal screen
Before I tried while loop and was sending all the information at once, it worked, but output file looked like this output file
while the input file look like this input file
The primary bug was that the parent must do close(mypipefd[1]) before the parent read loop (and not after). This prevented the parent from seeing EOF on the pipe after the child was done writing.
Also, you were missing a waitpid in the parent.
The printf for buf in the parent was in the wrong place [after the read loop]. At that point, buf can't be guaranteed to have the correct data or that it's correctly zero-terminated. That's why stdout had some garbage chars at the end.
So, In addition to outputting to the output file, the loop should output to stdout, but should use fwrite as buf can't be guaranteed to be zero terminated.
I had missed that in my initial post, so I've corrected it.
As per my top comments, the child should loop on a [possible] partial write to the pipe. I coded that.
Here's the version with the bugs annotated and fixed:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#define BUFSIZE 25
int main( int argc, char *argv[] ) {
srand(time(NULL));
pid_t pid;
int mypipefd[2];
int ret;
char buf[BUFSIZE];
int output;
int stream;
int nbytes;
ret = pipe(mypipefd);
if( ret == -1 ) {
perror( "pipe error");
exit(1);
}
pid = fork();
if( pid == -1 ) {
perror( "FORK ERROR...");
exit(2);
}
if( pid == 0 ) {
/* CHILD */
printf(" Child process...\n");
stream = open("input.txt", O_RDONLY);
if (close(mypipefd[0]) == -1 ) {
perror("ERROR CLOSING PIPE");
exit(3);
}
while ( (nbytes = read(stream, buf, BUFSIZE)) > 0 ) {
sleep(rand() %2);
#if 0
write(mypipefd[1], buf, nbytes );
#else
// NOTE: this _should_ work but adds extra at the end
int off;
int wlen;
for (off = 0; nbytes > 0; off += wlen, nbytes -= wlen) {
wlen = write(mypipefd[1], buf + off, nbytes );
if (wlen <= 0)
break;
}
#endif
}
if ( close(stream) == -1 ) {
perror("ERROR CLOSING STREAM");
exit(4);
}
// NOTE/FIX: child must close it's side of the pipe
#if 1
close(mypipefd[1]);
#endif
}
else {
/* PARENT */
printf(" Parent process...\n");
// NOTE/FIX: this must be closed _before_ the read loop -- holding it
// open prevents parent from seeing EOF on pipe
#if 1
if (close(mypipefd[1]) == -1 ) {
perror("ERROR CLOSING PIPE");
exit(6);
}
#endif
#if 1
printf("buf: ");
#endif
output = open("output.txt", O_CREAT | O_WRONLY, 00777);
while ( (nbytes = read(mypipefd[0], buf, BUFSIZE)) > 0 ) {
write(output, buf, nbytes);
#if 1
fwrite(buf,1,nbytes,stdout);
#endif
}
// NOTE/BUG: the buffer at this point will only have the data from
// the _last_ read and may not be null terminated
#if 0
printf("buf: %s\n", buf);
#else
printf("\n");
#endif
if (close(output) == -1) {
perror("ERROR CLOSING OUTPUT");
exit(5);
}
// NOTE/BUG: this must be closed _before_ the parent's read loop
#if 0
if (close(mypipefd[1]) == -1 ) {
perror("ERROR CLOSING PIPE");
exit(6);
}
#endif
// NOTE/FIX: this is missing (prevents orphan/zombie child process)
#if 1
waitpid(pid,NULL,0);
#endif
}
return 0;
}
UPDATE:
but i don't understand what does "for" loop do here
A write to a pipe can generate a "short write" (e.g. you want to write 20 but the return value (i.e. number of bytes actually written) comes back with 15. You have to index into the buffer and write the remaining bytes in subsequent writes.
There is a kernel limit on how many bytes can be written in a single atomic write (e.g.) if you did write(mypipefd[1],buf,10000000), the kernel doesn't have space allocated for such a large write, so it will return the value of what it could append to the pipe buffer [in the kernel].
Also, let's say the kernel pipe buffer can hold 64 bytes. And you write buffers of size 64 to it. Maybe the reader is reading only 32 bytes. So, the first write is fine. Then reader reads out 32 bytes. So, the next write to the pipe of 64, there is only space for 32 bytes, so the write will return 32
Program have to display: "buf: This is ra" then "buf: ndom text"
Okay, I've fixed that
At last, I need to implement error handling everywhere.
I've annotated places where I'd add error and handling, along with some things to look for.
Anyway, here's an updated version. I've left in the // NOTE/* comments but removed the #if/#endif pairs to make an easier read.
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#define BUFSIZE 25
int
main(int argc, char *argv[])
{
srand(time(NULL));
pid_t pid;
int mypipefd[2];
int ret;
char buf[BUFSIZE];
int output;
int stream;
int nbytes;
ret = pipe(mypipefd);
if (ret == -1) {
perror("pipe error");
exit(1);
}
pid = fork();
if (pid == -1) {
perror("FORK ERROR...");
exit(2);
}
if (pid == 0) {
/* CHILD */
printf(" Child process...\n");
stream = open("input.txt", O_RDONLY);
if (close(mypipefd[0]) == -1) {
perror("ERROR CLOSING PIPE");
exit(3);
}
while ((nbytes = read(stream, buf, BUFSIZE)) > 0) {
sleep(rand() % 2);
// NOTE/FIX: writing to pipes _can_ generate a _short_ write. that
// is, (e.g.) if the length given to write is 20, the return value
// may be only 15. this means that the remaining 5 bytes must be
// sent in a second/subsequent write
int off;
int wlen;
for (off = 0; nbytes > 0; off += wlen, nbytes -= wlen) {
wlen = write(mypipefd[1], buf + off, nbytes);
if (wlen < 0) {
perror("ERROR WRITING TO FILE");
exit(3);
}
if (wlen == 0)
break;
}
}
if (close(stream) == -1) {
perror("ERROR CLOSING STREAM");
exit(4);
}
// NOTE/FIX: child must close it's side of the pipe
// NOTE/ERRCODE: check error code here
close(mypipefd[1]);
}
else {
/* PARENT */
printf(" Parent process...\n");
// NOTE/FIX: this must be closed _before_ the read loop -- holding it
// open prevents parent from seeing EOF on pipe
if (close(mypipefd[1]) == -1) {
perror("ERROR CLOSING PIPE");
exit(6);
}
// NOTE/ERRCODE: this should be checked for -1 (i.e. output file
// could not be opened for file permission, etc. or other reasons
// similar to those for the file write below)
output = open("output.txt", O_CREAT | O_WRONLY, 00777);
// NOTE/FIX: we read one less than buffer size to allow for adding an
// artificial zero byte at the end
while ((nbytes = read(mypipefd[0], buf, BUFSIZE - 1)) > 0) {
// NOTE/ERRCODE: error handling _could_ be added here but it would
// be rare (e.g. filesystem has an I/O error because it's full or
// marked R/O because of an I/O error on the underlying disk)
write(output, buf, nbytes);
// write partial buffer to stdout
buf[nbytes] = 0;
printf("buf: %s\n",buf);
}
if (close(output) == -1) {
perror("ERROR CLOSING OUTPUT");
exit(5);
}
// NOTE/FIX: this is missing (prevents orphan/zombie child process)
// NOTE/ERRCODE: yes, this _can_ have an error return but here it's
// unlikely because we _know_ that pid is valid
// what can be done is to do:
// int status;
// waitpid(pid,&status,0)
// then process the return code from the child using the W* macros
// provided (e.g. WIFEXITED, WSTATUS) on status
waitpid(pid, NULL, 0);
}
return 0;
}

C -> Shell - Block on a Write Until Read

I want to send a character from a c program to a shell program. I am using a named pipe to send the letter 'a' whenever it is requsted. I should only have to open the pipe once. Here's an example:
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
int main(){
int fd;
mkfifo("/tmp/test", 0666);
fd = open("/tmp/test", O_WRONLY);
printf("Opened\n");
char * a = "a";
while(1){
printf("Writing to pipe...\n");
write(fd,a,1);
sleep(1);
}
}
And the shell executes this command as many times as it wants...
head -c 1 /tmp/test
The issue is after one head, the c will endlessly stream into the pipe, even if nobody's there.
I noticed that open() blocks until someone is on the other end. How to I tell write() to block until somebody is reading?
I would rather have this feature on write() than read(), as I think there's lots of overhead on opening the file for each request.
Thanks!
UPDATE
This is how I'm handling it in Java, it waits until I have somebody listening on this pipe before it continues on. Maybe just because it's a higher level language.
public static void writeToPipe(int val, String pipename){
try{
pipe_out = new PrintStream("/tmp/" + pipename);
}catch(Exception e){
System.out.println("Could not open a pipe for output!");
e.printStackTrace();
}
try{
pipe_out.println(val);
}catch(Exception e){
System.out.println("Could not write to pipe!");
e.printStackTrace();
}
try{
pipe_out.close();
}catch(Exception e){
System.out.println("Could not close the output pipe!");
e.printStackTrace();
}
}
UPDATE #2 - THIS IS THE SOLUTION
Here is my code based on David's idea, it's rough, but it works. I'm not checking if the named pipe exists and just supressing it from quitting.
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(int argc, char **argv){
mkfifo("/tmp/test", 0666);
while(1){
int fd, status;
if ((fd = open ("/tmp/test", O_WRONLY)) == -1) {
perror ("open failed");
return 1;
}
printf("Opened Pipe\n");
char a = 'a';
int f = fork();
if(f == -1){
perror("fork");
exit(1);
}else if(f == 0){
//This is by the child process
if (write (fd, &a, 1) == -1) {
close(fd);
perror ("open failed");
return 1;
}
}else{
//This is the parent process
int w = waitpid(f, &status, WUNTRACED | WCONTINUED);
if (w == -1){
perror("waitpid");
exit(EXIT_FAILURE);
}
}
}
}
You can do what you are attempting, but understand you must limit your read to one-char on the shell side since there will be no '\n' written to the pipe. Also, you may write many more times than the shell reads. For example, you can add validations as Mr. Pursell suggests to insure your C-program is functioning and blocking on write with something similar to:
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
int main (int argc, char **argv) {
int fd;
errno = 0;
if (mkfifo (argc > 1 ? argv[1] : "/tmp/test", 0666)) {
perror ("mkfifo failed");
return 1;
}
if ((fd = open ("/tmp/test", O_WRONLY)) == -1) {
perror ("open failed");
return 1;
}
printf ("Opened\n");
char a = 'a';
while (1) {
printf ("Writing to pipe...\n");
if (write (fd, &a, 1) == -1) {
perror ("open failed");
return 1;
}
}
return 0;
}
You can test with a simple:
$ declare -i c=0; while test "$c" -lt 10 && read -n 1 ch; do
echo "read: $ch"
((c++))
done </tmp/test
Example Shell Output
read: a
read: a
read: a
read: a
read: a
read: a
read: a
read: a
read: a
read: a
You will write until the fifo buffer is full resulting in more Writing to pipe... than you have read: a.
Rough Example with fork
Continuing from the comments here is a rough example of using fork to spawn child processes to insure your C-program is always blocking on write for the shell. This example is limited to 3 repetitions, but you could just use while (1) for a continual cycle. I also added a quick counter for the write (just for my curiosity) e.g.
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
int crt_fifo_write (char *fname);
int file_exists (char *f);
int main (int argc, char **argv) {
int n = 0;
errno = 0;
while (n < 3) { /* limit example to 3 child processes */
pid_t cpid, w;
int status;
cpid = fork();
if (cpid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (cpid == 0) { /* Code executed by child */
if (!crt_fifo_write (argc > 1 ? argv[1] : "/tmp/test"))
fprintf (stderr, "crt_fifo_write() failed.\n");
}
else { /* Code executed by parent */
do {
w = waitpid (cpid, &status, WUNTRACED | WCONTINUED);
if (w == -1) {
perror("waitpid");
exit(EXIT_FAILURE);
}
if (WIFSIGNALED(status)) /* signal on close of read end */
printf("shell read complete. %s\n",
n < 2 ? "restarting" : "done");
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
}
n++;
}
return 0;
}
/** your write 'a' to the fifo with check for existence & unlink */
int crt_fifo_write (char *fname)
{
int fd, n = 0;
errno = 0;
if (!fname || !*fname) return 0;
if (file_exists (fname))
if (unlink (fname) == -1) {
perror ("fifo exists unlink failed");
return 0;
}
if (mkfifo (fname, 0666)) {
perror ("mkfifo failed");
return 1;
}
if ((fd = open (fname, O_WRONLY)) == -1) {
perror ("open failed");
return 1;
}
printf ("Opened\n");
char a = 'a';
while (write (fd, &a, 1) != -1) {
printf ("%3d - Writing to pipe...\n", n++);
}
return 0;
}
/** atomic test that file exists (1 success, 0 otherwise) */
int file_exists (char *f)
{
errno = 0;
int flags = O_CREAT | O_WRONLY | O_EXCL;
int mode = S_IRUSR | S_IWUSR;
int fd = open (f, flags, mode);
if (fd < 0 && errno == EEXIST)
return 1;
else if (fd) { /* created, like bash touch */
close (fd);
unlink (f);
}
return 0;
}
Example Program Use/Output
$ ./bin/pipe_write_shell_fork
Opened
0 - Writing to pipe...
1 - Writing to pipe...
2 - Writing to pipe...
3 - Writing to pipe...
4 - Writing to pipe...
...
138 - Writing to pipe...
139 - Writing to pipe...
140 - Writing to pipe...
shell read complete. restarting
Opened
0 - Writing to pipe...
1 - Writing to pipe...
2 - Writing to pipe...
3 - Writing to pipe...
4 - Writing to pipe...
...
130 - Writing to pipe...
131 - Writing to pipe...
shell read complete. restarting
Opened
0 - Writing to pipe...
1 - Writing to pipe...
2 - Writing to pipe...
3 - Writing to pipe...
4 - Writing to pipe...
...
144 - Writing to pipe...
145 - Writing to pipe...
shell read complete. done
Example Shell Read/Output
$ declare -i c=0; while test "$c" -lt 10 && read -n 8 ch; do \
echo "read: $ch"; ((c++)); done </tmp/test
read: aaaaaaaa
read: aaaaaaaa
read: aaaaaaaa
read: aaaaaaaa
read: aaaaaaaa
read: aaaaaaaa
read: aaaaaaaa
read: aaaaaaaa
read: aaaaaaaa
read: aaaaaaaa
(repeated 2 more times)
Since you don't bother to check the return of write, you don't even notice that it is failing. (That is, it's not endlessly streaming into the pipe; it's just lying to you and printing "Writing to pipe..." while failing to write to the pipe.)
The only way to block on the write is to fill the pipe. But that's not your problem. The problem is that the pipe has been terminated. If you want to keep that pipe open, you'll need to have some process still alive reading from it. For example, you could do sleep 500 < /tmp/test in some other shell. Or just open the fifo for reading in the program doing the writing. (eg, add the line open("/tmp/fifo", O_RDONLY);)
The more curious issue is; why isn't your program terminating from the SIGPIPE?

How to pipe stdin to a child and execl cat in C

In the code below, I am simply trying to send a file via stdin to a child process which will exec the cat OS command. The code compiles fine. Here is how I call it from the command line:
$ ./uniquify < words.txt
However, when I run it I get a seg fault error. I am really having a hard time understanding how the flow if information is supposed to work through pipes to children. I am trying to make the code as simple as possible, so I can understand it, but it is not yet making sense. Any help would be appreciated.
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#define NUM_CHILDREN 2
int main(int argc, char *argv[])
{
pid_t catPid;
int writeFds[NUM_CHILDREN];
int catFds[2];
int c = 0;
FILE *writeToChildren[NUM_CHILDREN];
//create a pipe
(void) pipe(catFds);
if ((catPid = fork()) < 0) {
perror("cat fork failed");
exit(1);
}
//this is the child case
if (catPid == 0) {
//close the write end of the pipe
close(catFds[1]);
//close stdin?
close(0);
//duplicate the read side of the pipe
dup(catFds[0]);
//exec cat
execl("/bin/cat", "cat", (char *) 0);
perror("***** exec of cat failed");
exit(20);
}
else { //this is the parent case
//close the read end of the pipe
close(catFds[0]);
int p[2];
//create a pipe
pipe(p);
writeToChildren[c] = fdopen(p[1], "w");
} //only the the parent continues from here
//close file descriptor so the cat child can exit
close(catFds[1]);
char words[NUM_CHILDREN][50];
//read through the input file two words at a time
while (fscanf(stdin, "%s %s", words[0], words[1]) != EOF) {
//loop twice passing one of the words to each rev child
for (c = 0; c < NUM_CHILDREN; c++) {
fprintf(writeToChildren[c], "%s\n", words[c]);
}
}
//close all FILEs and fds by sending and EOF
for (c = 0; c < NUM_CHILDREN; c++) {
fclose(writeToChildren[c]);
close(writeFds[c]);
}
int status = 0;
//wait on all children
for (c = 0; c < (NUM_CHILDREN + 1); c++) {
wait(&status);
}
return 0;
}
Since your question seems to be about understanding how pipes and forks work, I hope below programs can help you. Please notice that this is for illustration only. It wouldn't qualify for commercial implementation, but I wanted to keep it short!
You can compile the two programs as follows:
cc pipechild.c -o pipechild
cc pipeparent.c -o pipeparent
Then execute with ./pipeparent
pipeparent.c source
/* pipeparent.c */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define MESSAGE "HELLO!\n"
#define INBUFSIZE 80
#define RD 0 // Read end of pipe
#define WR 1 // Write end of pipe
int main(void)
{
int ptocpipe[2]; // Parent-to-child pipe
int ctoppipe[2]; // Chile-to-parent pipe
pid_t childpid; // Process ID of child
char inbuf[80]; // Input from child
int rd; // read() return
int rdup; // dup():ed stdin for child
int wdup; // dup():ed stdout for child
char *eol; // End of line
// Create pipe for writing to child
if (pipe(ptocpipe) < 0) {
fprintf(stderr, "pipe(ptocpipe) failed!\n");
return 2;
}
// Create pipe for writing back to parent
if (pipe(ctoppipe) < 0) {
fprintf(stderr, "pipe(ctoppipe) failed!\n");
return 2;
}
// Verify that one of the pipes are working by filling it first
// in one end and then reading it from the other. The OS will
// buffer the contents for us. Note, this is not at all necessary,
// it's just to illustrate how it works!
write(ptocpipe[WR], MESSAGE, strlen(MESSAGE));
read(ptocpipe[RD], inbuf, INBUFSIZE);
if (strlen(inbuf) != strlen(MESSAGE)) {
fprintf(stderr, "Failed to flush the toilet!\n");
return 6;
} else {
printf("Wrote to myself: %s", inbuf);
}
// Next, we want to launch some interactive program which
// replies with exactly one line to each line we send to it,
// until it gets tired and returns EOF to us.
// First, we must clone ourselves by using fork(). Then the
// child process must be replaced by the interactive program.
// Problem is: How do we cheat the program to read its stdin
// from us, and send its stdout back to us?
switch (childpid = fork()) {
case -1: // Error
fprintf(stderr, "Parent: fork() failed!\n");
return 3;
case 0: // Child process
// Close the ends we don't need. If not, we might
// write back to ourselves!
close(ptocpipe[WR]);
close(ctoppipe[RD]);
// Close stdin
close(0);
// Create a "new stdin", which WILL be 0 (zero)
if ((rdup = dup(ptocpipe[RD])) < 0) {
fprintf(stderr, "Failed dup(stdin)\n");
return 4;
}
// Close stdout
close(1);
// Create a "new stdout", which WILL be 1 (one)
if ((wdup = dup(ctoppipe[WR])) < 0) {
fprintf(stderr, "Failed dup(stdout)\n");
return 5;
}
// For debugging, verify stdin and stdout
fprintf(stderr, "rdup: %d, wdup %d\n", rdup, wdup);
// Overload current process by the interactive
// child process which we want to execute.
execlp("./pipechild", "pipechild", (char *) NULL);
// Getting here means we failed to launch the child
fprintf(stderr, "Parent: execl() failed!\n");
return 4;
}
// This code is executed by the parent only!
// Close the ends we don't need, to avoid writing back to ourself
close(ptocpipe[RD]);
close(ctoppipe[WR]);
// Write one line to the child and expect a reply, or EOF.
do {
write(ptocpipe[WR], MESSAGE, strlen(MESSAGE));
if ((rd = read(ctoppipe[RD], inbuf, INBUFSIZE)) > 0) {
// Chop off ending EOL
if ((eol = rindex(inbuf, '\n')) != NULL)
*eol = '\0';
printf("Parent: Read \"%s\" from child.\n", inbuf);
}
} while (rd > 0);
fprintf(stderr, "Parent: Child done!\n");
return 0;
}
pipechild.c source
/* pipechild.c
* Note - This is only for illustration purpose!
* To be stable, we should catch/ignore signals,
* and use select() to read.
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <strings.h>
#include <string.h>
#define MAXCOUNT 5 // Maximum input lines toread
#define INBUFSIZE 80 // Buffer size
int main(void)
{
char buff[INBUFSIZE];
int remains = MAXCOUNT;
pid_t mypid;
char *eol;
mypid = getpid(); // Process-ID
fprintf(stderr, "Child %d: Started!\n", mypid);
// For each line read, write one tostdout.
while (fgets(buff, INBUFSIZE, stdin) && remains--) {
// Chop off ending EOL
if ((eol = rindex(buff, '\n')) != NULL)
*eol = '\0';
// Debug to console
fprintf(stderr, "Child %d: I got %s. %d remains.\n",
mypid, buff, 1 + remains);
// Reply to parent
sprintf(buff, "Child %d: %d remains\n", mypid, 1 + remains);
write(1, buff, strlen(buff));
}
fprintf(stderr, "Child %d: I'm done!\n", mypid);
return 0;
}

Resources