Capturing exit status code of child process - c

I have a function that forks a process, duplicates file descriptors for input and output buffers, and then runs execl on a command passed in via a string called cmd:
static pid_t
c2b_popen4(const char* cmd, int pin[2], int pout[2], int perr[2], int flags)
{
pid_t ret = fork();
if (ret < 0) {
fprintf(stderr, "fork() failed!\n");
return ret;
}
else if (ret == 0) {
/*
Assign file descriptors to child pipes (not shown)...
*/
execl("/bin/sh", "/bin/sh", "-c", cmd, NULL);
fprintf(stderr, "execl() failed!\n");
exit(EXIT_FAILURE);
}
else {
/*
Close parent read and write pipes (not shown)...
*/
return ret;
}
return ret;
}
Each of the cmd instances process my data correctly, so long as my test inputs are correct.
When bad data is passed to a child process, my parent program will run to completion and exit with a non-error status code of 0.
If I deliberately put in bad input — to purposefully try to get one of the cmd instances to fail in an expected way — I'd like to know how to capture the exit status of that cmd so that I can issue the correct error status code from the parent program, before termination.
How is this generally done?

You can get the exit status of the child via the first argument of wait(), or the second argument of waitpid(), and then using the macros WIFEXITED and WEXITSTATUS with it.
For instance:
pid_t ret = c2b_popen4("myprog", pin, pout, perr, 0);
if ( ret > 0 ) {
int status;
if ( waitpid(ret, &status, 0) == -1 ) {
perror("waitpid() failed");
exit(EXIT_FAILURE);
}
if ( WIFEXITED(status) ) {
int es = WEXITSTATUS(status);
printf("Exit status was %d\n", es);
}
}
A simplified working example:
failprog.c:
int main(void) {
return 53;
}
shellex.c:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(void)
{
pid_t p = fork();
if ( p == -1 ) {
perror("fork failed");
return EXIT_FAILURE;
}
else if ( p == 0 ) {
execl("/bin/sh", "bin/sh", "-c", "./failprog", "NULL");
return EXIT_FAILURE;
}
int status;
if ( waitpid(p, &status, 0) == -1 ) {
perror("waitpid failed");
return EXIT_FAILURE;
}
if ( WIFEXITED(status) ) {
const int es = WEXITSTATUS(status);
printf("exit status was %d\n", es);
}
return EXIT_SUCCESS;
}
Output:
paul#thoth:~/src/sandbox$ ./shellex
exit status was 53
paul#thoth:~/src/sandbox$
waitpid() will block until the process with the supplied process ID exits. Since you're calling your function with a popen() name and passing pipes to it, presumably your child process doesn't terminate quickly, so that probably wouldn't be the right place to check it, if the call succeeded. You can pass WNOHANG as the third parameter to waitpid() to check if the process has terminated, and to return 0 if the child has not yet exited, but you have to be careful about when you do this, since you get no guarantees about which process will run when. If you call waitpid() with WNOHANG immediately after returning from c2b_popen4(), it may return 0 before your child process has had a chance to execute and terminate with an error code, and make it look as if the execution was successful when it's just about to not be successful.
If the process does die immediately, you'll have problems reading from and writing to your pipes, so one option would be to check waitpid() if you get an error from the first attempt to do that, to check if the read() or write() is failing because your child process died. If that turns out to be true, you can retrieve the exit status and exit your overall program then.
There are other possible strategies, including catching the SIGCHLD signal, since that'll be raised whenever one of your child processes dies. It would be OK, for instance, to call _exit() right from your signal handler, after waiting for the child process (calling waitpid() in a signal handler is also safe) and getting its exit status.

Related

prctl(PR_SET_PDEATHSIG) race condition

As I understand, the best way to achieve terminating a child process when its parent dies is via prctl(PR_SET_PDEATHSIG) (at least on Linux): How to make child process die after parent exits?
There is one caveat to this mentioned in man prctl:
This value is cleared for the child of a fork(2) and (since Linux 2.4.36 / 2.6.23) when executing a set-user-ID or set-group-ID binary, or a binary that has associated capabilities (see capabilities(7)). This value is preserved across execve(2).
So, the following code has a race condition:
parent.c:
#include <unistd.h>
int main(int argc, char **argv) {
int f = fork();
if (fork() == 0) {
execl("./child", "child", NULL, NULL);
}
return 0;
}
child.c:
#include <sys/prctl.h>
#include <signal.h>
int main(int argc, char **argv) {
prctl(PR_SET_PDEATHSIG, SIGKILL); // ignore error checking for now
// ...
return 0;
}
Namely, the parent count die before prctl() is executed in the child (and thus the child will not receive the SIGKILL). The proper way to address this is to prctl() in the parent before the exec():
parent.c:
#include <unistd.h>
#include <sys/prctl.h>
#include <signal.h>
int main(int argc, char **argv) {
int f = fork();
if (fork() == 0) {
prctl(PR_SET_PDEATHSIG, SIGKILL); // ignore error checking for now
execl("./child", "child", NULL, NULL);
}
return 0;
}
child.c:
int main(int argc, char **argv) {
// ...
return 0;
}
However, if ./child is a setuid/setgid binary, then this trick to avoid the race condition doesn't work (exec()ing the setuid/setgid binary causes the PDEATHSIG to be lost as per the man page quoted above), and it seems like you are forced to employ the first (racy) solution.
Is there any way if child is a setuid/setgid binary to prctl(PR_SET_PDEATH_SIG) in a non-racy way?
It is much more common to have the parent process set up a pipe. Parent process keeps the write end open (pipefd[1]), closing the read end (pipefd[0]). Child process closes the write end (pipefd[1]), and sets the read end (pipefd[1]) nonblocking.
This way, the child process can use read(pipefd[0], buffer, 1) to check if the parent process is still alive. If the parent is still running, it will return -1 with errno == EAGAIN (or errno == EINTR).
Now, in Linux, the child process can also set the read end async, in which case it will be sent a signal (SIGIO by default) when the parent process exits:
fcntl(pipefd[0], F_SETSIG, desired_signal);
fcntl(pipefd[0], F_SETOWN, getpid());
fcntl(pipefd[0], F_SETFL, O_NONBLOCK | O_ASYNC);
Use a siginfo handler for desired_signal. If info->si_code == POLL_IN && info->si_fd == pipefd[0], the parent process either exited or wrote something to the pipe. Because read() is async-signal safe, and the pipe is nonblocking, you can use read(pipefd[0], &buffer, sizeof buffer) in the signal handler whether the parent wrote something, or if parent exited (closed the pipe). In the latter case, the read() will return 0.
As far as I can see, this approach has no race conditions (if you use a realtime signal, so that the signal is not lost because an user-sent one is already pending), although it is very Linux-specific. After setting the signal handler, and at any point during the lifetime of the child process, the child can always explicitly check if the parent is still alive, without affecting the signal generation.
So, to recap, in pseudocode:
Construct pipe
Fork child process
Child process:
Close write end of pipe
Install pipe signal handler (say, SIGRTMIN+0)
Set read end of pipe to generate pipe signal (F_SETSIG)
Set own PID as read end owner (F_SETOWN)
Set read end of pipe nonblocking and async (F_SETFL, O_NONBLOCK | O_ASYNC)
If read(pipefd[0], buffer, sizeof buffer) == 0,
the parent process has already exited.
Continue with normal work.
Child process pipe signal handler:
If siginfo->si_code == POLL_IN and siginfo->si_fd == pipefd[0],
parent process has exited.
To immediately die, use e.g. raise(SIGKILL).
Parent process:
Close read end of pipe
Continue with normal work.
I do not expect you to believe my word.
Below is a crude example program you can use to check this behaviour yourself. It is long, but only because I wanted it to be easy to see what is happening at runtime. To implement this in a normal program, you only need a couple of dozen lines of code. example.c:
#define _GNU_SOURCE
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
static volatile sig_atomic_t done = 0;
static void handle_done(int signum)
{
if (!done)
done = signum;
}
static int install_done(const int signum)
{
struct sigaction act;
memset(&act, 0, sizeof act);
sigemptyset(&act.sa_mask);
act.sa_handler = handle_done;
act.sa_flags = 0;
if (sigaction(signum, &act, NULL) == -1)
return errno;
return 0;
}
static int deathfd = -1;
static void death(int signum, siginfo_t *info, void *context)
{
if (info->si_code == POLL_IN && info->si_fd == deathfd)
raise(SIGTERM);
}
static int install_death(const int signum)
{
struct sigaction act;
memset(&act, 0, sizeof act);
sigemptyset(&act.sa_mask);
act.sa_sigaction = death;
act.sa_flags = SA_SIGINFO;
if (sigaction(signum, &act, NULL) == -1)
return errno;
return 0;
}
int main(void)
{
pid_t child, p;
int pipefd[2], status;
char buffer[8];
if (install_done(SIGINT)) {
fprintf(stderr, "Cannot set SIGINT handler: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
if (pipe(pipefd) == -1) {
fprintf(stderr, "Cannot create control pipe: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
child = fork();
if (child == (pid_t)-1) {
fprintf(stderr, "Cannot fork child process: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
if (!child) {
/*
* Child process.
*/
/* Close write end of pipe. */
deathfd = pipefd[0];
close(pipefd[1]);
/* Set a SIGHUP signal handler. */
if (install_death(SIGHUP)) {
fprintf(stderr, "Child process: cannot set SIGHUP handler: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
/* Set SIGTERM signal handler. */
if (install_done(SIGTERM)) {
fprintf(stderr, "Child process: cannot set SIGTERM handler: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
/* We want a SIGHUP instead of SIGIO. */
fcntl(deathfd, F_SETSIG, SIGHUP);
/* We want the SIGHUP delivered when deathfd closes. */
fcntl(deathfd, F_SETOWN, getpid());
/* Make the deathfd (read end of pipe) nonblocking and async. */
fcntl(deathfd, F_SETFL, O_NONBLOCK | O_ASYNC);
/* Check if the parent process is dead. */
if (read(deathfd, buffer, sizeof buffer) == 0) {
printf("Child process (%ld): Parent process is already dead.\n", (long)getpid());
return EXIT_FAILURE;
}
while (1) {
status = __atomic_fetch_and(&done, 0, __ATOMIC_SEQ_CST);
if (status == SIGINT)
printf("Child process (%ld): SIGINT caught and ignored.\n", (long)getpid());
else
if (status)
break;
printf("Child process (%ld): Tick.\n", (long)getpid());
fflush(stdout);
sleep(1);
status = __atomic_fetch_and(&done, 0, __ATOMIC_SEQ_CST);
if (status == SIGINT)
printf("Child process (%ld): SIGINT caught and ignored.\n", (long)getpid());
else
if (status)
break;
printf("Child process (%ld): Tock.\n", (long)getpid());
fflush(stdout);
sleep(1);
}
printf("Child process (%ld): Exited due to %s.\n", (long)getpid(),
(status == SIGINT) ? "SIGINT" :
(status == SIGHUP) ? "SIGHUP" :
(status == SIGTERM) ? "SIGTERM" : "Unknown signal.\n");
fflush(stdout);
return EXIT_SUCCESS;
}
/*
* Parent process.
*/
/* Close read end of pipe. */
close(pipefd[0]);
while (!done) {
fprintf(stderr, "Parent process (%ld): Tick.\n", (long)getpid());
fflush(stderr);
sleep(1);
fprintf(stderr, "Parent process (%ld): Tock.\n", (long)getpid());
fflush(stderr);
sleep(1);
/* Try reaping the child process. */
p = waitpid(child, &status, WNOHANG);
if (p == child || (p == (pid_t)-1 && errno == ECHILD)) {
if (p == child && WIFSIGNALED(status))
fprintf(stderr, "Child process died from %s. Parent will now exit, too.\n",
(WTERMSIG(status) == SIGINT) ? "SIGINT" :
(WTERMSIG(status) == SIGHUP) ? "SIGHUP" :
(WTERMSIG(status) == SIGTERM) ? "SIGTERM" : "an unknown signal");
else
fprintf(stderr, "Child process has exited, so the parent will too.\n");
fflush(stderr);
break;
}
}
if (done) {
fprintf(stderr, "Parent process (%ld): Exited due to %s.\n", (long)getpid(),
(done == SIGINT) ? "SIGINT" :
(done == SIGHUP) ? "SIGHUP" : "Unknown signal.\n");
fflush(stderr);
}
/* Never reached! */
return EXIT_SUCCESS;
}
Compile and run the above using e.g.
gcc -Wall -O2 example.c -o example
./example
The parent process will print to standard output, and the child process to standard error. The parent process will exit if you press Ctrl+C; the child process will ignore that signal. The child process uses SIGHUP instead of SIGIO (although a realtime signal, say SIGRTMIN+0, would be safer); if generated by the parent process exiting, the SIGHUP signal handler will raise SIGTERM in the child.
To make the termination causes easy to see, the child catches SIGTERM, and exits the next iteration (a second later). If so desired, the handler can use e.g. raise(SIGKILL) to terminate itself immediately.
Both parent and child processes show their process IDs, so you can easily send a SIGINT/SIGHUP/SIGTERM signal from another terminal window. (The child process ignores SIGINT and SIGHUP sent from outside the process.)
Your last code snippet still contains a race condition:
int main(int argc, char **argv) {
int f = fork();
if (fork() == 0) {
// <- !!!race time!!!
prctl(PR_SET_PDEATHSIG, SIGKILL); // ignore error checking for now
execl("./child", "child", NULL, NULL);
}
return 0;
}
Meaning that in the child, after the fork, until the prctl() has visible effects (think: returns), there is a time-window where the parent may exit.
To fix this race you have to save the PID of the parent before the fork and check it after the prctl() call, e.g.:
pid_t ppid_before_fork = getpid();
pid_t pid = fork();
if (pid == -1) { perror(0); exit(1); }
if (pid) {
; // continue parent execution
} else {
int r = prctl(PR_SET_PDEATHSIG, SIGTERM);
if (r == -1) { perror(0); exit(1); }
// test in case the original parent exited just
// before the prctl() call
if (getppid() != ppid_before_fork)
exit(1);
// continue child execution ...
(see also)
Regarding executing a setuid/setgid program: You can then pass the ppid_before_fork by other means (e.g. in the argument or environment vector) and execute the prctl() (including the comparison) after the exec, i.e. inside the execed binary.
I don't know this for sure, but clearing the parent death signal on execve when invoking a set-id binary looks like an intentional restriction for security reasons. I'm not sure why, considering that you can use kill to send signals to setuid programs that share your real user ID, but they wouldn't have bothered making that change in 2.6.23 if there wasn't a reason to disallow it.
Since you control the code of the set-id child, here is a kludge: make the call to prctl, then immediately afterward, call getppid and see if it returns 1. If it does, then either the process was started directly by init (which is not as rare as it used to be) or the process was reparented to init before it had a chance to call prctl, which means its original parent is dead and it should exit.
(This is a kludge because I know of no way to rule out the possibility that the process was started directly by init. init never exits, so you have one case where it should exit and one case where it shouldn't and no way to tell which. But if you know from the larger design that the process will not be started directly by init, it should be reliable.)
(You must call getppid after prctl, or you have only narrowed the race window, not eliminated it.)

waitpid with execl used in child returns -1 with ECHILD?

When do I need to use waitpid if I am using execl in a child process which may take time to finish?
When I use waitpid in the parent, it tells me that the child is running as the return value from waitpid is 0. However, if I call waitpid after some time in another function, it returns -1 with errno set to ECHILD. When should I use waitpid if I am not sure whether the child has completed or not?
//pid_t Checksum_pid = fork();
Checksum_pid = fork();
if (Checksum_pid == 0)
{
execl(path, name, argument as needed, ,NULL);
exit(EXIT_SUCCESS);
}
else if (Checksum_pid > 0)
{
pid_t returnValue = waitpid(Checksum_pid, &childStatus, WNOHANG);
if ( returnValue > 0)
{
if (WIFEXITED(childStatus))
{
printf("Exit Code: _ WEXITSTATUS(childStatus)") ;
}
}
else if ( returnValue == 0)
{
//Send positive response with routine status running (0x03)
printf("Child process still running") ;
}
else
{
if ( errno == ECHILD )
{
printf(" Error ECHILD!!") ;
}
else if ( errno == EINTR )
{
// other real errors handled here.
printf(" Error EINTR!!") ;
}
else
{
printf("Error EINVAL!!") ;
}
}
}
else
{
/* Fork failed. */
printf("Fork Failed") ;
}
If the signal SIGCHLD is ignored (which is the default) and you skip WNOHANG as mentioned by Jonathan, waitpid will hang until the child exits and then fail with code ECHILD. I encountered this myself in a scenario where the parent process simply should wait for the child to finish and it seemed a bit overkill to register a handler for SIGCHLD. The natural follow-up question would be "Is it is OK to treat an ECHILD as an expected event in this case, or am I always supposed to write a handler for SIGCHLD?",
but for that I don't have an answer.
From the documentation for waitpid(2):
ECHILD
(for waitpid() or waitid()) The process specified by pid (waitpid())
or idtype and id (waitid()) does not exist or is not a child of the
calling process. (This can happen for one's own child if the action
for SIGCHLD is set to SIG_IGN. See also the Linux Notes section about
threads.)
and further down
POSIX.1-2001 specifies that if the disposition of SIGCHLD is set to
SIG_IGN or the SA_NOCLDWAIT flag is set for SIGCHLD (see
sigaction(2)), then children that terminate do not become zombies and
a call to wait() or waitpid() will block until all children have
terminated, and then fail with errno set to ECHILD. (The original
POSIX standard left the behavior of setting SIGCHLD to SIG_IGN
unspecified. Note that even though the default disposition of SIGCHLD
is "ignore", explicitly setting the disposition to SIG_IGN results in
different treatment of zombie process children.) Linux 2.6 conforms to
this specification. However, Linux 2.4 (and earlier) does not: if a
wait() or waitpid() call is made while SIGCHLD is being ignored, the
call behaves just as though SIGCHLD were not being ignored, that is,
the call blocks until the next child terminates and then returns the
process ID and status of that child.
See also this answer.
If you wait with WNOHANG, then you won't get any useful status unless your child has already died — and it may not even have got going yet (it could still be waiting for the executable to be loaded from disk when the parent executes the waitpid()).
If you want to know that the child has finished, don't use WNOHANG — use 0 for the third argument unless you want to know about untraced or continued processes. This will wait until the process identified by Checksum_pid exits, or the system loses track of it by some mysterious means (which I've never seen happen).
This code produces Exit Code: 0 as the output:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
int main(void)
{
pid_t Checksum_pid = fork();
if (Checksum_pid < 0)
printf("Fork Failed\n");
else if (Checksum_pid == 0)
{
execl("/bin/sleep", "/bin/sleep", "2", NULL);
exit(EXIT_FAILURE);
}
else
{
int childStatus;
pid_t returnValue = waitpid(Checksum_pid, &childStatus, 0);
if (returnValue > 0)
{
if (WIFEXITED(childStatus))
printf("Exit Code: %d\n", WEXITSTATUS(childStatus));
else
printf("Exit Status: 0x%.4X\n", childStatus);
}
else if (returnValue == 0)
printf("Child process still running\n");
else
{
if (errno == ECHILD)
printf(" Error ECHILD!!\n");
else if (errno == EINTR)
printf(" Error EINTR!!\n");
else
printf("Error EINVAL!!\n");
}
}
return 0;
}
It is very similar to your code; I've merely moved the check for fork() failing to the top. I'd still prefer to get rid of the 'bushy trees' of if statements, but it isn't critical. What happens when you run this code? What do you have to change to get the ECHILD error (can you change it so that you get the ECHILD error)?
When you've managed to get code based off this to reproduce the problem, we can work out why you get that behaviour.
Tested on Mac OS X 10.9.2 Mavericks with GCC 4.8.2, and also Ubuntu 13.10 with GCC 4.8.1 (I needed to add -D_XOPEN_SOURCE=700 to get it to compile with my stringent compilation flags; Mac OS X managed without that), but I don't expect to get different results elsewhere.
int start(int Area)
{
int childStatus;
pid_t Checksum_pid = fork();
if(Checksum_pid < 0)
{
/* Fork failed. */
printf(" Fork Failed") ;
}
else if (Checksum_pid == 0)
{
for (int i = 0; i<5; i++)
{
if ( g_area[i] == Area)
{
execl(ScriptPath, ScriptName, NULL);
}
}
exit(EXIT_FAILURE);
}
else
{
/* This is the parent process. */
pid_t returnValue = waitpid(Checksum_pid, &childStatus, 0);
if ( returnValue > 0)
{
// Check if child ended normally
printf("WAITPID Successful") ;
if (WIFEXITED(childStatus))
{
printf("Exit Code: _ WEXITSTATUS(childStatus)");
}
}
else if ( returnValue == 0)
{
printf("Child process still running") ;
}
else
{
if ( errno == ECHILD )
{
printf("Error ECHILD!!") ;
}
else if ( errno == EINTR )
{
printf("Error EINTR!!") ;
}
else
{
printf(" Error EINVAL!!") ;
}
retCode = FAILED;
}
}
}

whether a function returned in the child process can be captured in the parent process

I'm currently implementing the && function in a shell using C. For example, if we input cmd1 && cmd2, then cmd2 executes only when cmd1 exits successfully. I'm thinking about:
int main() {
int i;
char **args;
while(1) {
printf("yongfeng's shell:~$ ");
args = get_line();
if (strcmp(args[0], "exit") == 0) exit(0); /* if it's built-in command exit, exit the shell */
if('&&') parse_out_two_commands: cmd1, cmd2;
if (execute(cmd1) != -1) /* if cmd1 successfully executed */
execute(cmd2); /* then execute the second cmd */
}
}
int execute(char **args){
int pid;
int status; /* location to store the termination status of the terminated process */
char **cmd; /* pure command without special charactors */
if(pid=fork() < 0){ //fork a child process, if pid<0, fork fails
perror("Error: forking failed");
return -1;
}
/* child */
else if(pid==0){ /* child process, in which command is going to be executed */
cmd = parse_out(args);
/* codes handleing I/O redirection */
if(execvp(*cmd, cmd) < 0){ /* execute command */
perror("execution error");
return -1;
}
return 0;
}
/* parent */
else{ /* parent process is going to wait for child or not, depends on whether there's '&' at the end of the command */
if(strcmp(args[sizeof(args)],'&') == 0){
/* handle signals */
}
else if (pid = waitpid(pid, &status, 0) == -1) perror("wait error");
}
}
So I'm using another function int execute(char ** args) to do the actual work. Its return type is int because I wan to know whether the command exits successfully. But I'm not sure here whether the parent process can get the return value from the child since they're two different processes.
Or should I decide whether to execute the second command in the child process, by forking another process to run it? Thanks a lot.
Change:
if(pid=fork() < 0){ //fork a child process, if pid<0, fork fails
to:
if((pid=fork()) < 0){ //fork a child process, if pid<0, fork fails
You're setting pid to the result of fork() < 0, not setting it to the PID of the child. So unless there's an error in fork(), this sets pid to 0 in both the parent and child, so they both think they're the child.
Regarding the return value of the execute() function: It will return in both the parent and child. In each process, it will return whatever was specified in the return statement in the corresponding branch of the if in execute(). Note that it execve() is successful, the child never returns, because it's no longer running this program, it's running the program that was exec'ed.
If the child wants to send success or failure information to the parent, it does this using its exit status, by calling exit(0) to indicate success, and exit(some-nonzero-value) to indicate failure. The parent can get the exit status using waitpid, and then return a success or failure indication from execute().

System call fork() and execv function

I'm trying to run two executables consecutively using this c code:
#include <stdio.h>
#include <unistd.h>
int main (int argc, char *argv[])
{
fork();
execv("./prcs1", &argv[1]); // GIVE ADDRESS OF 2nd element as starting point to skip source.txt
fork();
execv("./prcs2", argv);
printf("EXECV Failed\n");
}
The program exits after the first execv() call despite the fork, it never gets to the second execv(). I've tried calling wait() after the first fork but I'm not sure that's what it's missing.
Any ideas why control doesn't return to the parent after the child exits?
You need to understand how fork and execv work together.
fork() makes a duplicate of the current process, returning 0 to child, childpid to parent
fork() can fail, and returns -1 on failure, check for that
execv() replaces the duplicated parent process with a new process
typical fork/exec pairing replaces the child process with a new process
often you fork more than one child, and want them to run simultaneously,
however, you asked for them to run consecutively, that is one after another
thus, you need to wait for the first to complete before starting the second
thus you need to use some variant of wait(), example below uses waitpid() to wait for specific child
You need stdlib for exit (in case execv fails), and errno, to print the reason,
//I'm trying to run two executables consecutively using this c code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
You may want to examine the reason your child exited (core dump, signal, normal exit), thus I have added this function,
#include <sys/types.h>
#include <sys/wait.h>
//WIFEXITED(status) returns true if the child terminated normally, that is, by calling exit(3) or _exit(2), or by returning from main().
//WEXITSTATUS(status) returns the exit status of the child. This consists of the least significant 8 bits of the status argument that the child specified in a call to exit(3) or _exit(2) or as the argument for a return statement in main(). This macro should only be employed if WIFEXITED returned true.
//WIFSIGNALED(status) returns true if the child process was terminated by a signal.
//WTERMSIG(status) returns the number of the signal that caused the child process to terminate. This macro should only be employed if WIFSIGNALED returned true.
//WCOREDUMP(status) returns true if the child produced a core dump. This macro should only be employed if WIFSIGNALED returned true. This macro is not specified in POSIX.1-2001 and is not available on some UNIX implementations (e.g., AIX, SunOS). Only use this enclosed in #ifdef WCOREDUMP ... #endif.
//WIFSTOPPED(status) returns true if the child process was stopped by delivery of a signal; this is only possible if the call was done using WUNTRACED or when the child is being traced (see ptrace(2)).
//WSTOPSIG(status) returns the number of the signal which caused the child to stop. This macro should only be employed if WIFSTOPPED returned true.
//WIFCONTINUED(status) (since Linux 2.6.10) returns true if the child process was resumed by delivery of SIGCONT.
int
exitreason(pid_t cid, int status)
{
if( WIFEXITED(status) )
{
printf("child %d terminated normally, that is, by calling exit(3) or _exit(2), or by returning from main().\n",cid);
if( WEXITSTATUS(status) )
{
printf("child %d exit status %d. This consists of the least significant 8 bits of the status argument that the child specified in a call to exit(3) or _exit(2) or as the argument for a return statement in main().\n",cid,WEXITSTATUS(status));
}
}
if( WIFSIGNALED(status) )
{
printf("child %d process was terminated by a signal.\n",cid);
if( WTERMSIG(status) )
{
printf("child %d signal %d that caused the child process to terminate.\n",cid,WTERMSIG(status));
}
if( WCOREDUMP(status) )
{
printf("child %d produced a core dump. WCOREDUMP() is not specified in POSIX.1-2001 and is not available on some UNIX implementations (e.g., AIX, SunOS). Only use this enclosed in #ifdef WCOREDUMP ... #endif.\n",cid);
}
}
if( WIFSTOPPED(status) )
{
printf("child %d process was stopped by delivery of a signal; this is only possible if the call was done using WUNTRACED or when the child is being traced (see ptrace(2)).\n",cid);
if( WSTOPSIG(status) )
{
printf("child %d number of the signal which caused the child to stop.\n",cid);
}
}
if( WIFCONTINUED(status) )
{
printf("child %d process was resumed by delivery of SIGCONT.\n");
}
}
And here is your program annotated with comments explaining which sections of code are processed by the parent, and which by the child(ren).
int main (int argc, char *argv[])
{
char proc1[] = "/bin/echo"; //"./prcs1";
char proc2[] = "/bin/echo"; //"./prcs2";
pid_t cid1, cid2, cidX;
int status=0;
int waitoptions = 0;
//WNOHANG return immediately if no child has exited.
//WUNTRACED also return if a child has stopped (but not traced via ptrace(2)). Status for traced children which have stopped is provided even if this option is not specified.
//WCONTINUED also return if a stopped child has been resumed by delivery of SIGCONT.
int res;
if( (cid1 = fork()) == 0 ) //child1
{
printf("in child1\n");
if( (res = execv(proc1, &argv[1])) < 0 ) // GIVE ADDRESS OF 2nd element as starting point to skip source.txt
{
printf("error: child1: %d exec failed %d\n", cid1, errno);
printf("error: cannot execv %s\n",proc1);
exit(91); //must exit child
}
}
else if( cid1 > 0 ) //cid>0, parent, waitfor child
{
cidX = waitpid(cid1, &status, waitoptions);
printf("child1: %d res %d\n", cid1, res);
exitreason(cid1, status);
}
else //cid1 < 0, error
{
printf("error: child1 fork failed\n");
}
if( (cid2 = fork()) == 0 ) //child2
{
printf("in child2\n");
if( (res = execv(proc2, &argv[1])) < 0 ) // GIVE ADDRESS OF 2nd element as starting point to skip source.txt
{
printf("error: child2: %d exec failed %d\n", cid2, errno);
printf("error: cannot execv %s\n",proc2);
exit(92); //must exit child
}
}
else if( cid2 > 0 ) //cid>0, parent, waitfor child
{
cidX = waitpid(cid2, &status, waitoptions);
printf("child2: %d res %d\n", cid2, res);
exitreason(cid2, status);
}
else //cid2 < 0, error
{
printf("error: child2 fork failed\n");
}
}
You have a couple of problems. First, if you only want to run two programs, you only need to call fork() once. Then run one program in the parent process and one in the child. Second, you're constructing the argv array to be passed to execv incorrectly. The first entry should be the executable name. Do something like:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main(int argc, char **argv)
{
pid_t i = fork();
if (i == 0)
{
execv("./prcs1", (char *[]){ "./prcs1", argv[1], NULL });
_exit(1);
}
else if (i > 0)
{
execv("./prcs2", (char *[]){ "./prcs2", argv[0], NULL });
_exit(2);
}
else
{
perror("fork failed");
_exit(3);
}
}
Note that this example does no error checking.
You haven't had much reading on fork() I guess.
when you call fork(), it creates a child process which will run the same code from fork.
fork() returns three kind of values
negative which shows an error
positive which says you are in parent process and value shows childprosess ID
zero which says you are in child process.
your code should look like this.
#include <stdio.h>
#include <unistd.h>
int main (int argc, char *argv[])
{
int ret = fork();
if(ret==0)
{
//child process
execv("./prcs1", &argv[1]); // GIVE ADDRESS OF 2nd element as starting point to skip source.txt
printf("EXECV Failed from child\n");
}
else if(ret>0)
{
//parent process
execv("./prcs2", argv);
printf("EXECV Failed from parent\n");
}
else
{
//you will come here only if fork() fails.
printf("forkFailed\n");
}
return 0;
}
The exec family will only return if the call fails.
Since you do not check the return value of fork you will call execv in parent and child process.
Check the return value: if it is 0 you are in the child process, if it is greater than zero then you are in the parent process. Less than zero means the fork failed.

Questions about using forkpty to create pseudo terminal to ssh in C

pid = forkpty (&pty,0,0,0);
if (pid == 0) {
execl ("/usr/bin/ssh", "ssh", hostname, NULL);
exit (0);
} else if (pid > 0) {
ssh_pid = pid;
ssh_pty = pty;
if(child_ssh_success()) {
get_user_input();
send_user_input_to_child_ssh_and_child_forword_it_to_remote_server();
get_remote_server_response_from_child();
display_response_to_stdout();
}
}
How can I tell whether ssh success or not?
How can parent know that child has successfully ssh-ed to remote server, so parent can send something to remote server?
Use the function waitpid in the parent ( ie inside your else if ( pid>0) condition ) to get the status of the child
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *stat_loc, int options);
Quoting IBM DeveloperWorks
stat_loc
A pointer to an integer where the wait function will return the status
of the child process. If the waitpid function returns because a
process has exited, the return value is equal to the pid of the
exiting process. For this, if the value of stat_loc is not NULL,
information is stored in the location pointed to by stat_loc. If
status is returned from a terminated child process that returned a
value of 0, the value stored at the location pointed to by stat_loc is
0. If the return is greater than 0, you can evaluate this information using the following macros: WEXITSTATUS WIFEXITED WIFSIGNALED
WTERMSIG.
So if stat_loc is zero, you may assume that SSH terminated normally
EDIT1
If you dont want the parent to be blocked, then you need to set up a signal handler for SIGCHLD and do the same waitpid there. This time as the child has already terminated, waitpid will return immediately
Something on these lines
pid_t pid;
int main ()
{
struct sigaction action;
memset (&action, 0, sizeof(action));
action.sa_handler = sigchld_handler;
if (sigaction(SIGCHLD, &action, 0))
{
perror ("sigaction");
return 1;
}
pid = forkpty (&pty,0,0,0);
if (pid == 0)
{
execl ("/usr/bin/ssh", "ssh", hostname, NULL);
exit (0);
}
else if (pid > 0)
{
ssh_pid = pid;
ssh_pty = pty;
}
}
/* SIGCHLD handler. */
static void sigchld_handler (int sig)
{
int chld_state;
while (waitpid(pid,&child_state,options) > 0)
{
if (WIFEXITED(chld_state))
{
printf("Child exited with RC=%d\n",WEXITSTATUS(chld_state));
}
}
}
Make pid global so that you can access it from sigchld_handler too. Generally ssh returns 0 for SUCCESS and 255 ( or some other positive value ) for failure, though I am not cent percent sure on this.
EDIT2
From our discussion, I see you want to execute commands as well on the remote server. I suggest you run ssh this
ssh root#remoteserver.com 'ls -l'
You can pass these arguments of ssh via execl() and as explained in EDIT1, you can check for its return value to verify whether everything went fine. Also, since you are doing ssh from code, you may not want to enter password manually. Here is how you do password less login

Resources