I tried popen() and it is working well for output with "r" passed as a second argument; I know you can use "w" as writing mode and it worked for me (the program was just one scanf()). My question is how to use the append ("a") mode. You can both write and read, how do you know when the program is outputting something and when it's requesting for user input?
popen uses a pipe (that's the "p" in "popen") and pipes are unidirectional. You can either read or write from one end of a pipe, not both. To get both read/write access you should use a socketpair instead. I use this in my programs when I want something like popen, but for read/write:
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <unistd.h>
FILE *sopen(const char *program)
{
int fds[2];
pid_t pid;
if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0)
return NULL;
switch(pid=vfork()) {
case -1: /* Error */
close(fds[0]);
close(fds[1]);
return NULL;
case 0: /* child */
close(fds[0]);
dup2(fds[1], 0);
dup2(fds[1], 1);
close(fds[1]);
execl("/bin/sh", "sh", "-c", program, NULL);
_exit(127);
}
/* parent */
close(fds[1]);
return fdopen(fds[0], "r+");
}
Note that since it doesn't return the child's pid, you'll have a a zombie process after the child program exits. (Unless you set up SIGCHLD...)
Related
Here is the full code:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
int main(int argc, char *argv[]) {
char *command, *infile, *outfile;
int wstatus;
command = argv[1];
infile = argv[2];
outfile = argv[3];
if (fork()) {
wait(&wstatus);
printf("Exit status: %d\n", WEXITSTATUS(wstatus));
}
else {
close(0);
open(infile, O_RDONLY);
close(1);
open(outfile, O_CREAT|O_TRUNC|O_WRONLY, 0644);
execlp(command, command, NULL);
}
return 0;
}
This code should fork and exec a command with both stdin and stdout redirection, then wait for it to terminate and printf WEXITSTATUS(wstatus) received. e.g. ./allredir hexdump out_of_ls dump_file.
So, I understand everything before fork(). But I have the following questions:
As far as I understand, fork() clones the process, however I do not understand how it executes the command, because execlp should do that and code never reaches that part.
I do not get how execlp works. Why do we send a command to it twice (execlp(command, command, NULL);)?
How does execlp know where to redirect the output if we do not pass outfile nowhere.
Why do we even need an infile if the command is already passed as the other argument?
Thank you for your answers in advance.
As far as I understand, fork() clones the process, however I do not understand how it executes the command, because execlp should do that
and code never reaches that part.
Fork returns pid of child in parent space and 0 in new process space. The son process makes a call to execlp.
if (fork()) {
/* Parent process waits for child process */
}
else {
/* Son process */
execlp(command, command, NULL);
}
I do not get how execlp works. Why do we send a command to it twice
(execlp(command, command, NULL);)?
Read execlp man page and this thread
The first argument, by convention, should point to the filename
associated with the file being executed.
How does execlp know where to redirect the output if we do not pass outfile nowhere.
Redirection happens before by closing stdin and stdout file descriptors. And redirection happens by opening files which file descriptors will accommodate entries 0 and 1.
else {
/* redirecting stdin */
close(0);
open(infile, O_RDONLY);
/* redirecting stdout */
close(1);
open(outfile, O_CREAT|O_TRUNC|O_WRONLY, 0644);
execlp(command, command, NULL);
}
Why do we even need an infile if the command is already passed as the other argument?
We can't tell what your program does without seeing the argument passed as command.
I have to modify this code. The child process should redirect standard output to text file.
I think that I should do sth with dup2 and exec but I don't know what.
I read this ref and also this.
But it didn't helped me, probably I'm doing sth wrong.
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main ()
{
int fds[2];
pid_t pid;
/* Create a pipe. File descriptors for the two ends of the pipe are placed in fds. */
/* TODO add error handling for system calls like pipe, fork, etc. */
pipe (fds);
/* Fork a child process. */
pid = fork ();
if (pid == (pid_t) 0) {
/* This is the child process. Close our copy of the write end of the file descriptor. */
close (fds[1]);
/* Connect the read end of the pipe to standard input. */
dup2 (fds[0], STDIN_FILENO);
/* Replace the child process with the "sort” program. */
execlp ("sort", "sort", NULL);
} else {
/* This is the parent process. */
FILE* stream;
/* Close our copy of the read end of the file descriptor. */
close (fds[0]);
/* 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;
}
what you have done with dup2 is connecting the parent's stdout to child's stdin, leaving the child's stdout without a redirection. so the childr would print sorted strings to stdout.
what you should do next is to open a textfile , and do a dup2 with it's stdout. such as something before execlp
int outfd=open("/tmp/output",O_WRONLY|O_TRUNC|O_CREAT,0600);
dup2(outfd,STDOUT_FILENO);
execlp ("sort", "sort", NULL);
you will also need to #include <fcntl.h> to have file flags.
I understood how fork and pipes work but i have a doubt regarding the flow of the child and parent process.Since we are using fork the order of execution of parent and child process is undefined but why child process is waiting for stdin from parent process.What happens if child process executes first? it must print empty in console? but it is not happening can i know why?
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main () {
int fds[2]; pid_t pid;
/* File descriptors for the two ends of the pipe are placed in fds. */
pipe (fds);
/* Fork a child process. */
pid = fork ();
if (pid == (pid_t) 0) {
/* Child proces -. Close the write end of the file descriptor. */
close (fds[1]);
/* Connect the read end of the pipe to standard input. */
dup2 (fds[0], STDIN_FILENO);
/* Replace the child process with the “rev” program. */
execlp("rev", "rev", 0); }
else {
/* This is the parent process. */
FILE* stream;
/* Close the read end of the file descriptor. */
close (fds[0]);
/* Convert the write file descriptor to a FILE object */
stream = fdopen (fds[1], "w");
fprintf (stream, ",ereh ot ereht morF\n");
fprintf (stream, ",ereht ot ereh dna\n");
fprintf (stream, ".erehwyreve era sgniht ynnuf\n");
fprintf (stream, "ssueS .rD - \n");
fflush (stream);
close (fds[1]);
/* Wait for the child process to finish. */
waitpid (pid, NULL, 0);
}
return 0;
}
You aren't 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
In this case, the child needs to close fds[1] after duplicating it. Because it is still open, rev will never receive EOF because there is a process (the rev child process) that could, in theory, write to the input.
You should use fclose(stream) instead of close(fds[1]) because the output is buffered and fclose() knows to flush the buffers, but close() hasn't got a clue. However, by using fflush(stream) before the misguided close(), you do avoid problems.
That leads to:
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
int main(void)
{
int fds[2];
pid_t pid;
pipe(fds);
pid = fork();
if (pid == (pid_t)0)
{
close(fds[1]);
dup2(fds[0], STDIN_FILENO);
close(fds[1]);
execlp("rev", "rev", 0);
}
else
{
FILE *stream;
close(fds[0]);
stream = fdopen(fds[1], "w");
fprintf(stream, ",ereh ot ereht morF\n");
fprintf(stream, ",ereht ot ereh dna\n");
fprintf(stream, ".erehwyreve era sgniht ynnuf\n");
fprintf(stream, "ssueS .rD - \n");
fclose(stream);
waitpid(pid, NULL, 0);
}
return 0;
}
which produces the output:
From there to here,
and here to there,
funny things are everywhere.
- Dr. Seuss
I am trying to find out how I can send output of one process into a child process. I have gone down a journey learning of file descriptors and pipes. I think I am almost there but am missing a key component.
This is what I have so far:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
int fd[2];
pid_t sort_pid;
/* Create the pipe */
if(pipe(fd) == -1) {
fprintf(stderr, "Pipe failed\n");
exit(EXIT_FAILURE);
}
/* create child process that will sort */
sort_pid = fork();
if(sort_pid < 0) { // failed to fork
fprintf(stderr, "Child Fork failed\n");
exit(EXIT_FAILURE);
}
else if(sort_pid == 0) { // child process
close(0); // close stdin
dup2(fd[0], 0); // make stdin same as fd[0]
close(fd[1]); // don't need this end of the pipe
execlp("D:/Cygwin/bin/sort", "sort", NULL);
}
else { // parent process
close(1); // close stdout
dup2(fd[1], 1); // make stdout same as fd[1]
close(fd[0]); // don't need this end of the pipe
printf("Hello\n");
printf("Bye\n");
printf("Hi\n");
printf("G'day\n");
printf("It Works!\n");
wait(NULL);
}
return EXIT_SUCCESS;
}
This doesn't work, as it seems to go into an endless loop or something. I tried combinations of the wait() but that doesnt help either.
I am doing this to learn how to apply this idea in my actual program. In my actual program I read files, parse them line by line and save the processed data to a static array of structs. I want to be able to then generate output based on these results and use the fork() and execv() syscalls to sort the output.
This is ultimately for a project in uni.
These are similar examples which I dissected to get to the stage I am at so far:
pipe() and fork() in c
How to call UNIX sort command on data in pipe
Using dup,pipe,fifo to communicate with the child process
Furthermore I read the manual pages on the relevant syscalls to try and understand them. I will admit my knowledge of pipes and using them is still basically nothing, as this is my first every try with them.
Any help is appreciated, even further sources of information I could look into myself. I seem to have exhausted most of the useful stuff a google search give me.
sort will read until it encounters end-of-file. You therefore have to close the write-end of the pipe if you want it to complete. Because of the dup2, you have two copies of the open file description, so you need
close(fd[1]); anytime after the call to dup2
close(1); after you're done writing to (the new) stdout
Make sure to fflush(stdout) before the second of these to ensure that all your data actually made it into the pipe.
(This is a simple example of a deadlock: sort is waiting on the pipe to close, which will happen when the parent exits. But the parent won't exit until it finishes waiting on the child to exit…)
I am trying to prove one of my doubts, that two non-related processes can share the fd of half-duplex pipe and have communication.
I have created two programs for that. But then I had this another question, that what happens to the pipe if process dies?Because my reader got some garbage message, when I printed out the message.
Writer
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main()
{
int fd[2];
char str[] = "hello\n";
if(pipe(fd) < 0)
perror("Pipe creation failed\n");
//Since i am a writer, i should close the reading end as a best practice
close(fd[0]);
/*
The processes need not to be related processes, in order to use the half duplex pipes. fd is just a number/identifier
which can be shared across different processes
*/
printf("Hey there !!! use this file descriptor for reading : %d\n", fd[0]);
//writing message
write(fd[1],str,strlen(str)+1);
return 0;
}
Reader
#include <stdio.h>
#include <unistd.h>
int main()
{
int fd,bytesRead;
char buffer[1024];
printf("please enter the fd :");
scanf("%d",&fd);
bytesRead = read(fd,buffer,1024);
printf("Bytes Read : %d\nMessage : %s\n", bytesRead, buffer);
return 0;
}
You can't do this.
The table of file descriptors is per-process; every process has its own separate set of open file descriptors (note the distinction between open file descriptors, which are per-process, and open file descriptions, which are system-wide, discussed in open(2)). If you want to share a file descriptor between processes, you need to either inherit it over a fork(2) or pass it through a unix(7) domain socket via sendmesg(2) with SCM_RIGHTS in the cmesg(3) header.
(On Linux, you can also pass around paths to /proc/[PID]/fd/..., and other systems may have their own non-portable equivalents).
What is happening in your case is that the read is failing (you're giving it a file descriptor which is not open), leaving your buffer with uninitialized garbage. Since you don't check the return value, you never know that it failed.
In pipe man page,
pipe() creates a pipe, a unidirectional data channel that can be used for interprocess communication. The array pipefd is used to return two file descriptors referring to the ends of the pipe. pipefd[0] refers to the read end of the pipe. pipefd[1] refers to the write end of the pipe. Data written to the write end of the pipe is buffered by the kernel until it is read from the read end of the pipe.
The pipe is mainly used for the related process(parent and child). You are not able to use the pipe for non related process.
In related process, one end is closed. In other end process gets the SIGPIPE signal.
Example program using pipe :-
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int
main(int argc, char *argv[])
{
int pipefd[2];
pid_t cpid;
char buf;
if (argc != 2) {
fprintf(stderr, "Usage: %s <string>\n", argv[0]);
exit(EXIT_FAILURE);
}
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
cpid = fork();
if (cpid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (cpid == 0) { /* Child reads from pipe */
close(pipefd[1]); /* Close unused write end */
while (read(pipefd[0], &buf, 1) > 0)
write(STDOUT_FILENO, &buf, 1);
write(STDOUT_FILENO, "\n", 1);
close(pipefd[0]);
_exit(EXIT_SUCCESS);
} else { /* Parent writes argv[1] to pipe */
close(pipefd[0]); /* Close unused read end */
write(pipefd[1], argv[1], strlen(argv[1]));
close(pipefd[1]); /* Reader will see EOF */
wait(NULL); /* Wait for child */
exit(EXIT_SUCCESS);
}
}
The above program creates a pipe, and then fork(2)s to create a child process; the child inherits a duplicate set of file descriptors that refer to the same pipe. After the fork(2), each process closes the descriptors that it doesn't need for the pipe (see pipe(7)). The parent then writes the string contained in the program's command-line argument to the pipe, and the child reads this string a byte at a time from the pipe and echoes it on standard output.