C -> Shell - Block on a Write Until Read - c

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?

Related

Redirect stdin from exec to reading pipe

Basically I'm programming my own xargs implementation for practicing. The main difference with the original xargs is that what I do is to buffer the first 4 lines I get from stdin in the parent process and write it in the pipe I created, so it processes 4 lines at a time instead of each line. Then, in the child process I redirect stdin to the reading pipe so when I call exec it should receive the arguments. After that, when child ends, the parent should do this again until all stdin was read.
So let's say I do cat directories.txt | ./my_xargs ls where directories is a file that has:
/var/
/opt/
/dev/
I should get the result of running ls /var/ /opt/ /dev/. But instead I'm getting as if I've run just ls without parameters.
I tried several things:
Writing a file and redirecting that file descriptor to stdin. Didn't work.
I already checked the pipe is written correctly debugging and also printing the result of reading the pipe instead of calling exec.
closing stdin and opening a new file and write what I read in the child to that file. When I debug I can even see that that file descriptor is 0.
removing the line close(pd[0]); didn't work either.
#ifndef NARGS
#define NARGS 4
#endif
#define LINE_SIZE 1024
#define PATH_MAX 1024
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
typedef enum { false, true } bool;
int
main(int argc, char *argv[])
{
bool eof = false;
int pd[2];
size_t len = 0;
while (!eof) {
if (pipe(pd) == -1) {
fprintf(stderr, "Error creating pipe\n");
}
pid_t pid;
pid = fork();
if (pid == -1) {
fprintf(stderr, "Fork error\n");
}
if (pid == 0) { // child
close(pd[1]); // child doesn't write
dup2(pd[0], 0); // changing reading pipe por stdin
close(pd[0]);
execvp(argv[1], argv + 1);
perror("Exec failed.\n");
} else {
close(pd[0]); // parent doesn't read
char total_params[PATH_MAX] = "";
char* newLine = NULL;
int i = 0;
while (i < NARGS && !eof) {
// Reading stdin
eof = getline(&newLine, &len, stdin) < 1;
// Removing '\n'
if ( i+1 < NARGS) {
newLine[strcspn(newLine, "\n")] = ' ';
} else {
newLine[strcspn(newLine, "\n")] = '\0';
}
strcat(total_params, newLine);
i++;
}
free(newLine);
write(pd[1], &total_params, strlen(total_params));
close(pd[1]);
wait(NULL);
}
}
return 0;
}

writing to external program through pipe (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.

Simple shell with indirect input

I am writing a simple code to implement the indirect input function for a unix/linux shell.
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
extern void error(char* message);
void
cisshRedirectedInput(char* command[], char* inputFile)
{
//Try to implement the RedirectInput from here
pid_t pid;
int status;
int fd;
//For the child process
if ((pid=fork())==0)
{
//Try to input files, failing on an error
fd=open(inputFile,O_RDONLY);//To read input file
if(fd < 0)
{
error("sampleSh: error opening standard input file");
exit(1);
}
//use dup() to copy file
close(1);
if(dup(fd) < 0)
{
error("sampleSh: error duplicating standard input");
perror("dup()");
exit(1);
}
//Close file and exec()
close(fd);
execvp(command[0], command);
//If failure in any case
error("sampleSh: failure to execute command");
exit(1);
}
else
{
/* This is the parent process.
* Wait for the child to terminate.
*/
if(wait(&status) < 0)
{
error("sampleSh: error waiting for child.");
perror("wait");
}
if(status != 0)
error("sampleSh: command exited with nonzero error status.");
}
}
However, after compilation (no error reported), but when I try (fileList created already)
sort -r <fileList
The shell just stuck there without giving me answer, what is the problem please?
The standard input file descriptor is 0 (or STDIN_FILENO), not 1 (or STDOUT_FILENO).
Either use:
int fd = open(inputFile, O_RDONLY);
if (fd < 0) …
close(0);
if (dup(fd) < 0) …
close(fd);
Or:
int fd = open(inputFile, O_RDONLY);
if (fd < 0) …
if (dup2(fd, 0) < 0) …
close(fd);
It is good that your code does the close(fd) after duplicating to a standard I/O descriptor — that is almost always correct. It's also good that you are checking that the key system calls succeed. (There isn't much you can do if close() fails.)
This simple modification of your code (key change: use close(0); instead of close(1);) works for me. Did you null terminate your argument list?
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
static inline void error(char *message)
{
fprintf(stderr, "%s\n", message);
}
void
cisshRedirectedInput(char *command[], char *inputFile);
void
cisshRedirectedInput(char *command[], char *inputFile)
{
// Try to implement the RedirectInput from here
pid_t pid;
int status;
int fd;
// For the child process
if ((pid = fork()) == 0)
{
// Try to input files, failing on an error
fd = open(inputFile, O_RDONLY); // To read input file
if (fd < 0)
{
error("sampleSh: error opening standard input file");
exit(1);
}
// use dup() to copy file
close(0);
if (dup(fd) < 0)
{
error("sampleSh: error duplicating standard input");
perror("dup()");
exit(1);
}
// Close file and exec()
close(fd);
execvp(command[0], command);
// If failure in any case
error("sampleSh: failure to execute command");
exit(1);
}
else
{
/* This is the parent process.
* Wait for the child to terminate.
*/
if (wait(&status) < 0)
{
error("sampleSh: error waiting for child.");
perror("wait");
}
if (status != 0)
error("sampleSh: command exited with nonzero error status.");
}
}
int main(void)
{
char *args[] = { "sort", "-r", 0 };
cisshRedirectedInput(args, "fileList");
return 0;
}
Input file:
bash-assoc-arrays.sh
cissh.c
fileList
kwargs.py
makefile
posixver.h
rangeinc.c
select.c
spc.py
testcsv.py
uncrustify.bug
yield.py
Output:
yield.py
uncrustify.bug
testcsv.py
spc.py
select.c
rangeinc.c
posixver.h
makefile
kwargs.py
fileList
cissh.c
bash-assoc-arrays.sh

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;
}

Send input to a program and get control back

I have been stuck on this for some time.
Let's say I have a C program like the following. I want to be able to send this program some string and get the control after that.
If I do:
--> cat myfile | myprogram
or
--> echo "0123" | myprogram
or
--> myprogram < myfile
I get the ouput (myfile contains "0123")
30 31 32 33
Using the -n option raises a segfault
--> echo -n mystring | ./test
zsh: done echo -n "0123" |
zsh: segmentation fault ./test
I also tried with a named pipe, but it didn't work either.
I would like to be able to do something like
cat myfile | myprogram
and get back the control so that I can type other characters.
1 #include <stdlib.h>
2 #include <stdio.h>
3
4 int main (int argc, char *argv[]) {
6 int i = 0, j;
7 unsigned char buf[512];
8 unsigned char x;
9
10 while ((x = getchar()) != '\n') {
11 buf[i] = x;
12 i++;
13 }
14
16 for (j = 0; j < i; j++) {
17 printf("%x ", buf[j]);
18 }
19 printf ( "\n" );
20
21 return EXIT_SUCCESS;
22 } // end of function main
EDIT:
Below is the wrapper I have come up with.
It does everything I want, except that the output of the child exec-ed file is not properly displayed.
Without the wrapper:
$ bc
bc 1.06.95
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'.
2+2
4
With the wrapper:
$ ./wrapper bc
2+2
enter
4
Deleting the line
dup2(pipefd[0], 0); // Set the read end of the pipe as stdin.
makes the child stdout display correctly, but of course breaks the wrapper.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <assert.h>
int main(int argc, char const *argv[]) {
int cpid;
int pipefd[2];
if (pipe(pipefd) == -1) { perror("pipe.\n"); exit(errno); }
cpid = fork();
if (cpid == -1) { perror("fork."); exit(errno); }
if (cpid) {
// Parent --------------------------------------------------------
int buf_size = 8192;
char buf[buf_size];
size_t file;
// Close the unused read end of the pipe.
close(pipefd[0]);
// Leave a bit of time to the child to display its initial input.
sleep(2);
while (1) {
gets(buf);
if (strcmp("enter", buf) == 0) {
write(pipefd[1], "\n", 1);
} else if (-1 != (file = open(buf, O_RDONLY))) {
// Dump the output of the file to the child's stdin.
char c;
while(read(file, &c, 1) != 0) {
switch(c) {
case '\n':
printf("(skipped \\n)");
break;
default:
printf("%c", c);
write(pipefd[1], &c, 1);
};
}
printf("\n");
} else {
// Dump input to the child's stdin, without trailing '\n'.
for (int i = 0; (buf[i] != 0); i++) {
write(pipefd[1], buf + i, 1);
}
}
}
// Wait for the child to exit.
printf("Waiting for child to exit.\n");
wait(NULL);
} else {
// Child ---------------------------------------------------------
// Close the unused write end of the pipe.
close(pipefd[1]);
// Set the read end of the pipe as stdin.
dup2(pipefd[0], 0); // Set the read end of the pipe as stdin.
char** program_arguments = (char**)(argv + 1);
if (execvp(argv[1], program_arguments) < 0) {
perror("execvp.\n");
exit(errno);
}
}
}
I do not think it is possible to achieve this using named pipes if you can not modify the behavior of the program. Since in essence named pipes are no different then giving the output from standard input with redirection.
I also do not think it is possible if you use pipe or redirection properties of the shell, since always an EOF is sent to your program in this case and you can not ignore EOF since you can not modify the program.
A possible solution is to use a wrapper. The wrapper will first read the prepared input, send them to your program, after the prepared input finishes the wrapper switches to standard input. Actual program just keeps consuming input, it is not aware of the actual source of the data.
Only drawback is, you can not provide prepared input with pipes or redirection, you have to supply a filename. (I'm not sure a named pipe will work or not.) The reason is obvious, if you provide the prepared input to wrapper from standard input then the same problem exists for wrapper. By this way you are just delegating the problem to wrapper, which you can design any way you want.
A possible implementation in C (modified from a similar wrapper I've used, not tested extensively):
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
int main(int argc, char * argv[]) {
char c;
char **pargs ;
char buf[20];
int n;
int pipe_fd[2];
int pid;
pargs = argv+2;
if (pipe(pipe_fd) < 0) {
perror("pipe failed");
exit(errno);
}
if ((pid=fork()) < 0) {
perror ("Fork failed");
exit(errno);
}
if (! pid) {
close(pipe_fd[1]);
dup2(pipe_fd[0],0);
close(pipe_fd[0]);
if (execvp(argv[2],pargs) < 0) {
perror("Exec failed");
exit(errno);
}
} else {
size_t filedesc = open(argv[1],O_RDONLY);
while((n = read(filedesc, buf, 100)) > 0)
write (pipe_fd[1], buf, n);
while((n = read(0, buf, 100)) > 0)
write (pipe_fd[1], buf, n);
}
}
You can run your program with this wrapper as :
./wrapper input.txt myprog possible command line arguments
You can put your initial input into input.txt.
A simpler solution is to reopen the standard input. However if you simply try to open it as if you are opening a file, it does not work. You should open the terminal stream and copy it to standard input of your application. You can do it (again by using a wrapper) with something like:
size_t tty = open("/dev/tty",O_RDONLY);
dup2(tty,0);
Not to mention this second solution is for Linux and not portable.
In this example I use tail -f, not your C program
mkfifo /tmp/pipe # Open Pipe
tail -f /tmp/pipe & # Start your program and put it into the background
Now you also can send data to your program that runs in the background
echo "foobar" > /tmp/pipe
I hope this helps?
You could modify your program to accept 1 null character then continue on...it might work:
replace line 10 with something like
while (TRUE)
{
x = getchar();
if (x == '\n')
break;
if (x == '\0')
{
if (seen)
break;
seen = TRUE;
}
...

Resources