I want to open a process from C code, and be able to read its standard output and standard error, while being able to write to its standard input.
The closest I get to achieving this is using popen(), but this does not allow you to read the standard error stream. You could add "2>&1" to the command, but this will not make it possible to distinguish between the standard output and error data. Being able to seperate both streams is required for my application.
Python has popen3 and Ruby has Open3 to do these kind of things, but I can't seem to find a way to do it in C. Any help?
#include <unistd.h>
#include <stdio.h>
...
int pipe_err[2], pipe_out[2], pipe_in[2];
if (pipe(pipe_err) || pipe(pipe_out) || pipe(pipe_in)) { // abbreviated error detection
perror("pipe");
scream_and_run_around_frantically();
exit(BAD);
}
pid_t pid = fork();
if (!pid) { // in child
dup2(pipe_err[1], 2);
dup2(pipe_out[1], 1);
dup2(pipe_in[0], 0);
close(pipe_err[0]);
close(pipe_err[1]);
close(pipe_out[0]);
close(pipe_out[1]);
close(pipe_in[0]);
close(pipe_in[1]);
// close any other files that you don't want the new program
// to get access to here unless you know that they have the
// O_CLOEXE bit set
execl(program_path, program_name, arg1, arg2, arg3);
/* only gets here if there is an error executing the program */
} else { // in the parent
if (pid < 0) {
perror("fork");
error_death_plague_stubbed_toe();
exit(BAD);
}
child_err = pipe_err[0];
close(pipe_err[1]);
child_out = pipe_out[0];
close(pipe_out[1]);
child_in = pipe_in[1];
close(pipe_in[0]);
...
You will probably want to have a look at
man 3 exec
This has lots of functions that turn the current program into a new program. They all have different interfaces, but use execve under the hood.
man 2 execve
Also:
man 2 fork
and
man 2 pipe
You may want to consider using execl(). This is what is used internally by popen().
If you're forking the child process, you can duplicate the handle of stderr and use it in the child.
Related
My task is to write a C program that executes the command "ls -l /bin/?? | grep rwxr-xr-x | sort". There are 3 child processes where each of them executes one of the commands separately and sends the result through a pipe to the next child process. I'm using a Swedish modified verision of debian so the error message is in Swedish, but i'll translate the error i get, it's something along the lines of: sort: failed to status -: unknown fileidentifier.
Maybe it's my pipes that do not work as intended, I'm not too sure about the close() commands. I'm pretty sure the error comes from the pipes. Would be grateful if someone could run the program and get the english error message.
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
int main()
{
int ret;
int fds1[2], fds2[2], fds3[2];
char buf[20];
pid_t pid;
///initiating pipes
ret=pipe(fds1);
if(ret == -1){
perror("could not pipe");
exit(1);
}
ret=pipe(fds2);
if( ret == -1){
perror("could not pipe");
exit(1);
}
ret=pipe(fds3);
if (ret == -1){
perror("could not pipe");
exit(1);
}
pid=fork();
if(pid==-1){
fprintf(stderr,"fork failed");
exit(0);
}
if(pid==0){
///CHILD 1
close(1);
dup(fds1[1]);
close(fds1[0]);
close(fds1[1]);
close(0);
execlp("/bin/sh","bin/sh", "ls-l /bin/??", (char *)NULL);
}
else{
wait(0);
}
pid=fork();
if(pid==-1){
fprintf(stderr,"fork failed");
exit(0);
}
if(pid==0){
close(0);
dup(fds1[0]);
close(fds1[0]);
close(fds1[1]);
close(1);
dup(fds2[1]);
close(fds2[0]);
close(fds2[1]);
execlp("/usr/share/grep/", "grep", "rwxr-xr-x", NULL);
}
else{
wait(0);
}
close(fds1[0]);
close(fds1[1]);
pid=fork();
if(pid==-1){
fprintf(stderr,"fork failed");
exit(0);
}
if(pid==0){
close(0);
dup(fds2[0]);
close(fds2[0]);
close(fds2[1]);
execlp("sort", "sort", NULL);
}
else{
wait(0);
}
close(fds2[0]);
close(fds2[1]);
}
Your code has several problems, but before I discuss them, let me introduce you to a flavor of one of my favorite preprocessor macros:
#define DO_OR_DIE(x, s) do { \
if ((x) < 0) { \
perror(s); \
exit(1); \
} \
} while (0)
Using this macro where it is applicable can clarify your code by replacing all the boilerplate error checking. For example, this:
ret=pipe(fds1);
if(ret == -1){
perror("could not pipe");
exit(1);
}
becomes just
DO_OR_DIE(pipe(fds1), "pipe");
That makes it a lot easier to see and focus on the key parts of the code, and it's easier to type, too. As a result, it also reduces the temptation to skip error checks, such as those for your calls to dup().
Now, as to your code. For me, it exhibits not just the one misbehavior you now describe in your question, but three:
It emits an error message "bin/sh: ls-l /bin/??: No such file or directory".
It emits the error message you describe, "sort: stat failed: -: Bad file descriptor".
It does not terminate.
The first error message pertains to multiple problems in the arguments to your first execlp() call. If you want to launch a shell and specify a command for it to run, as opposed to a file from which to read commands, then you must pass the -c option to it. Additionally, you've omitted mandatory whitespace between the ls and its arguments. It looks like you want this:
execlp("/bin/sh","sh", "-c", "ls -l /bin/??", (char *)NULL);
Setting aside the second problem for the moment, let's turn to the failure to terminate. You have several problems in this area, falling into these categories:
Holding pipe ends open where you should ensure them closed
Calling wait() at the wrong points
When you set up a pipe between two processes, you generally want to make sure that there are no open file descriptors on either end of the pipe other than one on the write end held by one process, and one on the read end held by the other process. Each end should be open exactly once, in exactly one process. Since the processes being connected invariably inherit these file descriptors from their parent, it is essential that the parent close its copies (except that the parent will want to keep one open in the event that it itself is one of the communicating processes).
The process on the read end of a pipe will not see EOF on that pipe until all open file descriptors on the write end are closed. Child processes running programs such as grep and sort that read their input to its end will hang indefinitely if the write end of the pipe is not completely closed.
That can be a particularly perverse problem when the child reading the pipe also has a copy of the write end of that pipe, unused, or if one of its siblings does.
Additionally, the whole point of a pipeline is that the processes involved run concurrently. If you wait() after starting one before starting the next, then at minimum you prevent such concurrency. Worse, however, that can also cause your program to hang, because a pipe has finite buffer capacity. If the child is writing output to a pipe, but no one is reading it, then the pipe's buffer can fill to capacity, at which point the child blocks. If the parent is waiting for the child to finish before launching the process that will drain the pipe, then you have a deadlock. Therefore, you should start all the processes in the pipeline first, then wait for them all.
Having fixed such problems in your code, I find that the program emits a different error for me:
execlp: No such file or directory
(The specifics of this message derive from the nature of my fixes.) This should be especially concerning, because if execlp() fails then it returns in the process in which it was called. In your cases, control will then fall right out of your if statement, into the code intended only for the parent to execute. For this reason, it is essential to handle errors from execlp(). At minimum, add a call to exit() or _Exit() immediately after.
But what's failing? Well, it's the grep this time. Note that you specify the command to execute as "/usr/share/grep/" -- that trailing / is erroneous, and the path itself is suspect. On my system, the correct path is /usr/bin/grep, but since we're using execlp, which resolves the executable in the path, we might as well omit the path altogether:
execlp("grep", "grep", "rwxr-xr-x", (char *) NULL);
Et voilĂ ! After making that correction as well, your program runs for me.
Additional advice: do not use dup() when you care what file descriptor number you want the duplicate to have, such as when you're trying to dup onto one of the standard streams. Use dup2() for that, which has the additional advantage that you don't need to close the specified file descriptor first.
I have a front-end X program, in this program , a background program a.out is called. In a.out, there are some printf sentences. I found the standard outputs are saved in ~/.xsession-errors. Is it normal? I don't want to save these printf contents. Is there any method to avoid saving them except deleting printf?
Yes, you can use the freopen(3) function to redirect stdout to some other file, or to the null device if you'd rather not have any output:
// Discard all further output to standard output for the duration of the
// program (or until the next call to freopen()):
stdout = freopen("/dev/null", "w", stdout);
Depending on how the child program is launched, you can also just redirect its stdout stream. If you're launching it with system(3), you can just use shell redirection:
system("./a.out args >/dev/null");
If you're launching it with a fork() and exec() pair, then you can redirect the stdout file descriptor in between the fork() and exec() to avoid changing anything in the parent process:
// Error checking omitted for expository purposes
pid_t pid = fork();
if(pid == 0)
{
// Child process
int fd = open("/dev/null", O_WRONLY);
dup2(fd, STDOUT_FILENO);
close(fd);
execve("./a.out", argv, envp);
}
There are a few possibilities, by no means an exhaustive list:
When you run your child program, do so in such a way that standard output/error is sent to the bitbucket, such as system ("myprog >/dev/nul 2>&1");.
Include in that child program your own printf varargs-type function which basically does nothing. Provided that function is included before any attempt is made to link in the C runtime libraries, it will use your dummy one in preference.
Use freopen to redirect standard output and error to the same bitbucket.
Options 2 and 3 require changes to the child program which may or may not be desirable. The first option can be effected by only changing the parent program.
In a similar situation I had done this:
#define printf(...) ;
I have this code;
pid_t process;
process = fork();
if (process < 0){
//fork error
perror("fork");
exit(EXIT_FAILURE);
}
if (process == 0){
//i try here the execl
execl ("process.c", "process" , n, NULL);
}
else {
wait(NULL);
}
I don't know if this use of fork() and exec() combined is correct. When I try to run the program from the bash I do not receive any result, so I thought it could be a problem in this part of code.
Thanks.
One problem is that
if (process = 0){
should read
if (process == 0){
Otherwise you're assigning zero to process and only calling execl if result is non-zero (i.e. never).
Also, you're trying to exec something called process.c. There's no doubt that one could have an executable called process.c. However, conventionally names ending in .c are given to C source code files. If process.c is indeed a C file, you need to compile and link it first.
Once you've built the executable, you need to either place it somewhere on $PATH or specify its full path to execle(). In many Unix environments placing it in the current directory won't be enough.
Finally, it's unclear what n is in the execle() call, but the name hints at a numeric variable. You need to make sure that it's a string and not, for example, an integer.
Well as per the answers and comments above your code should look somewhat like this
pid_t process;
process = vfork(); //if your sole aim lies in creating a child that will ultimately call exec family functions then its advisable to use vfork
if (process < 0)
{
//fork error
perror("fork");
exit(EXIT_FAILURE);
}
if (process == 0)
{
//i try here the execl
char N[MAX_DIGITS];//A correction here
itoa(n,N);//write this function yourself
execl ("process", "process" , N, NULL);// Here process is the name of the executable N is your original argument
fprintf(stderr,"execl failed\n");//check for error in execl
}
else
{
wait(NULL);
}
Notice the use of vfork instead of fork.Its because it would be much more efficient.The reason could be found here
my question is can I write an integer to pipe ? and how ?
I need to make 3 processes first one generate 2 numbers, second make sum of the numbers, third print the result (USING PIPE)
Thanks all
The complicated part of what you're trying to do is creating the pipeline. You could just have the shell do it for you...
$ ./makenumbers | ./addnumbers | ./printresult
but that's boring, eh? And you have to have three executables. So let's have a look at what those vertical bars are doing at the C level.
You create a pipe with the pipe system call. You reassign standard input/output with dup2. You create new processes with fork, and you wait for them to terminate with waitpid. A program to set the whole thing up would look something like this:
int
main(void)
{
pid_t children[2];
int pipe1[2], pipe2[2];
int status;
pipe(pipe1);
pipe(pipe2);
children[0] = fork();
if (children[0] == 0)
{
/* in child 0 */
dup2(pipe1[1], 1);
generate_two_numbers_and_write_them_to_fd_1();
_exit(0);
}
children[1] = fork();
if (children[1] == 0)
{
/* in child 1 */
dup2(pipe1[0], 0);
dup2(pipe2[1], 1);
read_two_numbers_from_fd_0_add_them_and_write_result_to_fd_1();
_exit(0);
}
/* parent process still */
dup2(pipe2[0], 0);
read_a_number_from_fd_0_and_print_it();
waitpid(children[0], &status, 0);
waitpid(children[1], &status, 0);
return 0;
}
Please note:
I left out all error handling, because that would make the program about three times longer. Your instructor wants you to include error handling.
Similarly, I left out checking the exit status of the children; your instructor also wants you to check that.
You do not need the dup2 calls; you could just pass the pipe fd numbers to the subroutine calls. But if you were exec-ing a new binary in the child, which is more typical, you would need them. You would then also have to worry about making sure all file descriptors numbered 3 and higher were closed.
There is a reason I am using _exit instead of exit. Try to figure out what it is.
You need to use read and write instead of stdio.h calls in the subroutines called from child processes. The reason is related to the reason I am using _exit.
Since a pipe is just a file, you can use the fprintf() function to convert a random number to text and write that to the pipe. For instance:
FILE *pipe = popen("path/to/your/program", "w");
if (pipe != NULL) {
fprintf(pipe, "%d\n", rand());
pclose(pipe);
}
How can I implement chmod command on file by using exec? I would appreciate if anyone can provide me a code.
I'm not going to show you a working model, but execve() works like this:
char *args[] = {"foo", "argument1", "argument2", (char *)NULL};
... handle forking ....
res = execve("/sbin/foo", args, (char *)NULL);
... handle execve() failing ....
The third argument to execve() is left as an exercise for the reader to research, NULL may or may not be suitable for your assignment. Additionally, its up to you to determine what type res should be and what it should equal on success. Notice casting NULL.
The single UNIX specification is usually a good place to start.
From C code, directly calling chmod(2) will almost certainly be a better choice than going through the whole hassle of fork()ing and exec()ing.
Admittedly, most of that hassle is the fork() part, and if your program does not need to do anything else after the exec() call, then just running one of the exec() family functions without forking is reasonably fine (for an exercise in using exec(), that is).
try this: http://support.sas.com/documentation/onlinedoc/sasc/doc/lr2/execve.htm
also see: http://linux.about.com/library/cmd/blcmdl3_execvp.htm
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
main()
{
pid_t pid;
char *parmList[] = {"/bin/chmod", "0700", "/home/op/biaoaai/bead",NULL};
if ((pid = fork()) ==-1) //fork failed
perror("fork error");
else if (pid == 0) { //child (pid==0 if child)
execvp("chmod", parmList);
printf("Return not expected. Must be an execve error.n");
} else { //parent (pid==child process)
//parent specific code goes here
}
}
Edit: I never actually compiled... updated with users working paramList.