I'm trying to solve a problem I've got where a child process runs execvp() and needs to let the parent know if it returns. So, after the execvp() returns (because there's been an error), how can I tell the parent that this particular event has happened so it can handle it.
There's one method of writing a string of text through the pipe I'm using and then reading that from the parent.. but it seems a bit sloppy. Is there a better way?
Thanks!
Edit: Here is some code I'm trying where I can't seem to get the read to return.
int errorPipe[2];
signal( SIGPIPE, SIG_IGN );
int oldflags = fcntl (errorPipe[0], F_GETFD, 0);
oldflags |= FD_CLOEXEC;
fcntl (errorPipe[0], F_SETFD, oldflags);
oldflags = fcntl (errorPipe[1], F_GETFD, 0);
oldflags |= FD_CLOEXEC;
fcntl (errorPipe[1], F_SETFD, oldflags);
pipe( errorPipe );
// in the child..
char *error_message = "exec failed";
write( errorPipe[1], error_message, strlen(error_message)+1 );
exit(-1);
// in the parent
printf("read gives: %d\n", read(errorPipe[0], error_read, MAX_LINE_LENGTH) );
The easiest way is a pipe with the FD_CLOEXEC flag set, as then you can detect a successful exec as easily as a failure. In the event of a failure, I'd write whole the error message back to the parent over the pipe, but you could just write the status code or anything else that is meaningful. (Definitely write something though; nothing written has got to be a sign of a successful start of the other executable.)
[EDIT]: How to make use of this:
If the parent needs to wait until it knows whether the child successfully ran execve() (the unlying syscall) then it should do a blocking read() on the pipe. A zero result from that indicates success. (Make sure you've got SIGPIPE ignored.)
If the parent has some kind of event handling framework based on non-blocking IO and select() (or poll() or kqueue() or …) then wait for the pipe to become readable before trying to read the message (which will be zero-length if the child did the execve() correctly).
execvp() never returns, except when it fails to even start the executable at all. If it can start the executable, it will not return, no matter what the executable does (i.e. regardless to whether the executable succeeds at its task or not).
Your parent process will receive a SIGCHLD signal, for which you can install a signal handler.
Or you can wait(2) for the child process.
int child_pid = fork();
if (child_pid == 0) {
execvp("/path/to/executable", ...);
exit(123); /* this happens only if execvp() fails to invoke executable */
}
/* ... */
int status = 0;
int exit_pid = waitpid(-1, &status, WNOHANG);
if (exit_pid == child_pid && WIFEXITED(status)) {
if (WEXITSTATUS(status) == 0) {
/* child process exited fine */
} else if (WEXITSTATUS(status) == 123)
/* execvp() itself failed */
} else {
/* executed child process failed */
}
}
Cache the pid for the (child) process for which you want to examine the status in the parent.
Add a handler for SIGCHLD in the parent. In the child call exit with some status value of your choosing to denote that execvp failed. On receiving the signal in the parent you now have 2 options
a) Call waitpid with a pid of -1 (i.e. wait for any child), examine the return value, if that matches your cached pid, examine the status using macros like WEXITSTATUS.
b) Call waitpid with your cached pid , then on return examine the exit status.
To make this robust you should call WIFEXITED(status) before examining the exit status via WEXITSTATUS. WIFEXITED returns true if the child terminated normally i.e. by calling exit or _exit and not as a result of seg fault, un handled signal etc.
Also see man wait(2).
Related
I have a small utility that need to use fork() and wait(), however, I am facing an issue that the child process is not being terminated after the parent program is running. Any idea how I can fix it?
int test(void)
{
pid_t PID;
PID = fork();
if (PID == 0) {
sprintf(execmd, "/root/test);
system(execmd);
sprintf(filename, "test_results.txt);
FILE *fp = fopen(filename, "r");
fscanf (fp, "%s%s%s%s%s", &A, &B, &C, &D, &E);
printf ("A=%s B=%s C=%s D=%s E=%s\n", A, B, C, D, E);
fclose (fp);
}
else // *** Parent Process ***
{
int status;
wait(&status);
}
return 0;
}
At the very beginning: your code should not compile at all, as you do not close your strings:
sprintf(execmd, "/root/test);
system(execmd); // ^ missing quote!
(By the way, why don't you simply call system("/root/test");? At least from the code shown, I do not see any reason why you would need a copy...)
Then see wait documentation:
The wait() function shall suspend execution of the calling thread until status information for one of the terminated child processes of the calling process is available, or until delivery of a signal whose action is either to execute a signal-catching function or to terminate the process. If more than one thread is suspended in wait() or waitpid() awaiting termination of the same process, exactly one thread shall return the process status at the time of the target process termination. If status information is available prior to the call to wait(), return shall be immediate.
Return Value
If wait() or waitpid() returns because the status of a child process is available, these functions shall return a value equal to the process ID of the child process for which status is reported. If wait() or waitpid() returns due to the delivery of a signal to the calling process, -1 shall be returned and errno set to [EINTR]. If waitpid() was invoked with WNOHANG set in options, it has at least one child process specified by pid for which status is not available, and status is not available for any process specified by pid, 0 is returned. Otherwise, (pid_t)-1 shall be returned, and errno set to indicate the error.
So - if wait returns, you first should check if the process id was actually returned, otherwise you might re-enter into wait.
Then, if wait does not return, your child process is still running. I don't see why your process should be blocked because of the file handling (well, you do not perform any error checking, though, but that's a different matter unrelated to your problem), so most likely your child process is caught in the call to system.
What you now need is a timeout mechanism. My proposition is now as follows:
create a pipe for self-triggering events
install a signal handler for SIGCHLD using sigaction
in the signal handler, do the wait and write a single byte to your pipe
in your main function, where you currently wait for your child process, you would now first select or poll for your pipe with a timeout. If timeout occurs, send a signal to your child process (you could do that twice, first sending SIGTERM to allow your child to terminate gracefully, and if that does not help, send SIGKILL afterwards.
Alternative: If on linux, have a look at signalfd - you are not portable then, but get the same work done easier (select/poll for your fd and on success, you can call wait in the main function again).
Additionally, I recommend to re-structure your program a little: system will internally call fork and execve again, so you actually create another child process.
So I'd rather do it this way:
// preparations as described above
if (PID == 0)
{
execl("/root/test", ""); // won't return unless on error!
// some error handling?
return -1;
}
// parent process
if(PID < 0)
{
// error, the child process could not be created!
return -1;
}
// select/poll
if(timeout)
{
kill(PID, SIGTERM);
// select/poll
if(timeout)
kill(PID, SIGKILL);
}
//***************************************************
// if using signalfd:
int status;
wait(&status);
// yet to be done: check return value, status, errno
// you might need a loop...
// otherwise, prefer doing this in the signal handler
// however, the following out put MUST NOT be done there, as
// file handling is not async safe!
// just set some global flag there if the process terminated successfully!
//***************************************************
if(child_was_successful)
{
FILE* fp = fopen("test_results.txt", "r");
if(fp) // only if successful!
{
// missing declarations (presumably global), adding them here:
char A[32], B[32], C[32], D[32], E[32];
// some fixes:
// 1. checking return value
// 2. arrays already decay to pointers when being passed
// -> you do not need to take the address of again!
// 3. adding max length to your arrays prevents fscanf
// from writing beyond your array boundaries
if(fscanf (fp, "%31s%31s%31s%31s%31s", A, B, C, D, E) == 5)
// ^ no ampersand!
// ^ 31: need to leave space for terminating 0 character
{
printf ("A=%s B=%s C=%s D=%s E=%s\n", A, B, C, D, E);
}
fclose (fp);
}
}
I am doing a simple server/client program in C which listens on a network interface and accepts clients. Each client is handled in a forked process.
The goal I have is to let the parent process know, once a client has disconnected from the child process.
Currently my main loop looks like this:
for (;;) {
/* 1. [network] Wait for new connection... (BLOCKING CALL) */
fd_listen[client] = accept(fd_listen[server], (struct sockaddr *)&cli_addr, &clilen);
if (fd_listen[client] < 0) {
perror("ERROR on accept");
exit(1);
}
/* 2. [process] Call socketpair */
if ( socketpair(AF_LOCAL, SOCK_STREAM, 0, fd_comm) != 0 ) {
perror("ERROR on socketpair");
exit(1);
}
/* 3. [process] Call fork */
pid = fork();
if (pid < 0) {
perror("ERROR on fork");
exit(1);
}
/* 3.1 [process] Inside the Child */
if (pid == 0) {
printf("[child] num of clients: %d\n", num_client+1);
printf("[child] pid: %ld\n", (long) getpid());
close(fd_comm[parent]); // Close the parent socket file descriptor
close(fd_listen[server]); // Close the server socket file descriptor
// Tasks that the child process should be doing for the connected client
child_processing(fd_listen[client]);
exit(0);
}
/* 3.2 [process] Inside the Parent */
else {
num_client++;
close(fd_comm[child]); // Close the child socket file descriptor
close(fd_listen[client]); // Close the client socket file descriptor
printf("[parent] num of clients: %d\n", num_client);
while ( (w = waitpid(-1, &status, WNOHANG)) > 0) {
printf("[EXIT] child %d terminated\n", w);
num_client--;
}
}
}/* end of while */
It all works well, the only problem I have is (probably) due to the blocking accept call.
When I connect to the above server, a new child process is created and child_processing is called.
However when I disconnect with that client, the main parent process does not know about it and does NOT output printf("[EXIT] child %d terminated\n", w);
But, when I connect with a second client after the first client has disconnected, the main loop is able to finally process the while ( (w = waitpid(-1, &status, WNOHANG)) > 0) part and tell me that the first client has disconnected.
If there will be only ever one client connecting and disconnecting afterwards, my main parent process will never be able to tell if it has disconnected or not.
Is there any way to tell the parent process that my client already left?
UPDATE
As I am a real beginner with c, it would be nice if you provide some short snippets to your answer so I can actually understand it :-)
Your waitpid usage is not correct. You have a non-blocking call so if the child is not finished then then the call gets 0:
waitpid(): on success, returns the process ID of the child whose state
has changed; if WNOHANG was specified and one or more child(ren)
specified by pid exist, but have not yet changed state, then 0 is
returned. On error, -1 is returned.
So your are going immediately out of the while loop. Of course this can be catched later when the first children terminates and a second one lets you process the waitpid again.
As you need to have a non-blocking call to wait I can suggest you not to manage termination directly but through SIGCHLD signal that will let you catch termination of any children and then appropriately call waitpid in the handler:
void handler(int signal) {
while (waitpid(...)) { // find an adequate condition and paramters for your needs
}
...
struct sigaction act;
act.sa_flag = 0;
sigemptyset(&(act.sa_mask));
act.sa_handler = handler;
sigaction(SIGCHLD,&act,NULL);
... // now ready to receive SIGCHLD when at least a children changes its state
If I understand correctly, you want to be able to servicve multiple clients at once, and therefore your waitpid call is correct in that it does not block if no child has terminated.
However, the problem you then have is that you need to be able to process asynchronous child termination while waiting for new clients via accept.
Assuming that you're dealing with a POSIXy system, merely having a SIGCHLD handler established and having the signal unmasked (via sigprocmask, though IIRC it is unmasked by default), should be enough to cause accept to fail with EINTR if a child terminates while you are waiting for a new client to connect - and you can then handle EINTR appropriately.
The reason for this is that a SIGCHLD signal will be automatically sent to the parent process when a child process terminates. In general, system calls such as accept will return an error of EINTR ("interrupted") if a signal is received while they are waiting.
However, there would still be a race condition, where a child terminates just before you call accept (i.e. in between where already have waitpid and accept). There are two main possibilities to overcome this:
Do all the child termination processing in your SIGCHLD handler, instead of the main loop. This may not be feasible, however, since there are significant limits to what you are allowed to do within a signal handler. You may not call printf for example (though you may use write).
I do not suggest you go down this path, although it may seem simpler at first it is the least flexible option and may prove unworkable later.
Write to one end of a non-blocking pipe in your SIGCHLD signal handler. Within the main loop, instead of calling accept directly, use poll (or select) to look for readiness on both the socket and the read end of the pipe, and handle each appropriately.
On Linux (and OpenBSD, I'm not sure about others) you can use ppoll (man page) to avoid the need to create a pipe (and in this case you should leave the signal masked, and have it unmasked during the poll operation; if ppoll fails with EINTR, you know that a signal was received, and you should call waitpid). You still need to set a signal handler for SIGCHLD, but it doesn't need to do anything.
Another option on Linux is to use signalfd (man page) to avoid both the need to create a pipe and set up a signal handler (I think). You should mask the SIGCHLD signal (using sigprocmask) if you use this. When poll (or equivalent) indicates that the signalfd is active, read the signal data from it (which clears the signal) and then call waitpid to reap the child.
On various BSD systems you can use kqueue (OpenBSD man page) instead of poll and watch for signals without needing to establish a signal handler.
On other POSIX systems you may be able to use pselect (documentation) in a similar way to ppoll as described above.
There is also the option of using a library such as libevent to abstract away the OS-specifics.
The Glibc manual has an example of using select. Consult the manual pages for poll, ppoll, pselect for more information about those functions. There is an online book on using Libevent.
Rough example for using select, borrowed from Glibc documentation (and modified):
/* Set up a pipe and set signal handler for SIGCHLD */
int pipefd[2]; /* must be a global variable */
pipe(pipefd); /* TODO check for error return */
fcntl(pipefd[1], F_SETFL, O_NONBLOCK); /* set write end non-blocking */
/* signal handler */
void sigchld_handler(int signum)
{
char a = 0; /* write anything, doesn't matter what */
write(pipefd[1], &a, 1);
}
/* set up signal handler */
signal(SIGCHLD, sigchld_handler);
Where you currently have accept, you need to check status of the server socket and the read end of the pipe:
fd_set set, outset;
struct timeval timeout;
/* Initialize the file descriptor set. */
FD_ZERO (&set);
FD_SET (fdlisten[server], &set);
FD_SET (pipefds[0], &set);
FD_ZERO(&outset);
for (;;) {
select (FD_SETSIZE, &set, NULL, &outset, NULL /* no timeout */));
/* TODO check for error return.
EINTR should just continue the loop. */
if (FD_ISSET(fdlisten[server], &outset)) {
/* now do accept() etc */
}
if (FD_ISSET(pipefds[0], &outset)) {
/* now do waitpid(), and read a byte from the pipe */
}
}
Using other mechanisms is generally simpler, so I leave those as an exercise :)
So, I'm exiting from the child thread back to the parent. I am using the _exit() system call. I was wondering a few things. One was what parameter for the _exit for my child. Here is the code that my child process is executing:
printf("\n****Child process.****\n\nSquence: ");
do{
//Print the integer in the sequence.
printf("%d\t",inputInteger);
if((inputInteger%2) == 0){
//printf("inputInteger = %d\n", inputInteger);
inputInteger = inputInteger / 2;
}else{
inputInteger = 3*inputInteger +1;
//printf("%d\t",inputInteger);
}
}while(inputInteger != 1);
//Makes sure we print off 1!
printf("%d\n\n", inputInteger);
//Properly exit
_exit(status);
I use status because back in my parent thread I use it in the waitpid() system call. Here is the code for parent process that is executed after the child is completed.
waitpid_check = waitpid(processID, &status, 0);
printf("\n****Parent process.****\n");
if(waitpid_check == -1){
printf("Error in waitpid.\n");
exit(EXIT_FAILURE);
}
if(WIFEXITED(status)){
printf("Child process terminated normally!\n");
}
Here I'm using waitpid() system call that ensures that the child was exited, then use status to check if it was exited properly. I was wondering if I was going about this in the right way of creating the child and exiting it.
Then I was also wondering if I was correctly checking the exiting of the child in the parent.
Thanks for your help!
From the waitpid linux manual.
"If status is not NULL, wait() and waitpid() store status information in the int to which
it points."
You don't need the return value of wait paid to check if the child failed. You need to check to value of status. There are a handful of macros to check status.
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.
As for whether or not you are exiting the child process right that really depends. You would exit like you would in any other program since when you fork a process you are really just duplicating an address space and the child when run as its own independent program (of course with the same open FD's, already declared values etc as parent). Below is typical implementation for this problem (although NULL is being passed to the wait instead of a status so I think you are doing it right.)
/* fork a child process */
pid = fork();
if (pid < 0) { /* error occurred */
fprintf(stderr, "Fork Failed\n");
return 1;
}
else if (pid == 0) { /* child process */
printf("I am the child %d\n",pid);
execlp("/bin/ls","ls",NULL);
}
else { /* parent process */
/* parent will wait for the child to complete */
printf("I am the parent %d\n",pid);
wait(NULL);
printf("Child Complete\n");
}
return 0;
I'd love to help but I'm really rusty on these calls. If you've read through the documentation on these API calls and you're checking everywhere for error returns, then you should be in good shape.
The idea seems good at a high level.
One thing to keep in mind is you might want to surround the meat of your child method in a try/catch. With threads, you often don't want an exception to mess up your main flow.
You won't have that problem with multiple processes, but think about whether you want _exit to be called in the face of an exception, and how to communicate (to the parent or to the user) that an exception occurred.
I want to send exit status 0 from the execve process to the process which started it.
Because on Success execve never returns, So I am not able to do anything after that. But I want if execve ran successful.
You can verify that exec succeeded using FD_CLOEXEC:
create a pipe.
fork.
in the child, close the read end of the pipe and set the FD_CLOEXEC flag on the write end of the pipe. Then proceed with exec. If exec succeeds, the pipe will be automatically closed due to FD_CLOEXEC. If exec fails, write a byte to the pipe and exit.
in the parent, close the write end of the pipe and read from the read end. If you read 0 bytes (EOF), it means that exec succeeded. If you read a byte in the pipe, it means that exec failed.
The data written over the pipe in case of failure can be used to transmit error information, such as value of errno after exec.
The code, with error checking omitted for brevity, would look like this:
int pipe_fds[2];
pipe(pipe_fds);
if (!fork()) {
close(pipe_fds[0]);
fcntl(pipe_fds[1], F_SETFD, F_CLOEXEC);
execve(...);
write(pipe_fds[1], "", 1);
_exit(1);
}
else {
int n;
char out;
close(pipe_fds[1]);
n = read(pipe_fds[0], &out, 1);
if (n == 0)
printf("exec successful\n");
else
printf("exec failed\n");
close(pipe_fds[0]);
}
Note that this technique can be unsafe if other threads may exec their own processes in parallel. The issue is that there is a delay between when the pipe is created and when the close-on-exec flag gets set. If an unrelated thread forks and execs during that critical window, the child will inherit pipe_fds[1] and won't close it, causing the parent to hang. This can be fixed using the Linux-specific pipe2 call which allows atomically creating the pipe with the close-on-exec flag set.
to wait you process launched by fork() finish, you can use wait():
/* parent */
int status;
while (wait(&status) != uproc.pid) {
printf("waiting for child to exit");
}
and based on this question
The exit status of the child is provided by the wait function, in the status variable.
You get the exit status by using the WEXITSTATUS macro, but only if the program exited normally (i.e. called exit or returned from its main function):
if (WIFEXITED(status))
printf("Child exit status: %d\n", WEXITSTATUS(status));
else
printf("Child exited abnormally\n");
I am trying to create a simple client/server program that allows the client to connect to the server using a TCP socket and then allows the user to issue system calls form the client side to the server side and return the reply to the user. For example:
Client issues: ls
Server will find ls in /usr/bin or w/e and then execute it using execve()
I will also have something liks lls, or lmkdir, ect..which will issue the system calls on the client side.
The problem is my execve() is not appearing to run correctly because 'ls' or any other command is not actually being called. I have done this same kind of program before with only a local side (no server or anything) and execve() worked fine. Here is some code:
pid = fork();
if(pid){ // Child
printf("child wait");
pid = wait(&status);
printf("Child dead\n");
}else{ // Parent
if(execPath){
execve(execPath, arglist, env);
printf("Command Complete\n");
}
}
For some reason the printfs in the child section of the PID statement are not executing at all. I do not think the system is actually ever forking a process. Is there something special I would have to do to make this work since it is a client/server type of program or should it work exactly the same?
Thanks
exactly, execve does not fork. It replaces current image with the one specified as its argument and starts from its start (i.e. main()). It never returns to your origial program.
You probably want to use system() in your use case.
There are several problems in the code:
fork() returns pid for the parent and zero for the child. So parent runs the true branch of the if. And child runs the else branch. Swap those comments.
The stdout is line buffered. Add new line (\n) to printf which is before the wait. Or else you don't see the printout before waiting is done and 2nd printf is under call.
Be sure that child will exit also in error cases, or else the child will run the code of parent, and parent is still waiting exit of the child.
execve does not return if it success. It will return, if it fails.
So, fixed code could be something like that:
pid = fork();
if(pid){ // Parent
printf("child wait\n");
pid = waitpid(pid, &status, 0);
printf("Child dead\n");
}else{ // Child
if(execPath){
execve(execPath, arglist, env);
printf("execve failed!\n");
}
_exit(1);
}
Or you could use system(3).
Since the child process has not spawned any children of its own, the wait() call is unlikely to return without some other external event (like a signal interrupting the call). You should have the parent wait on the child process instead.
Note that fork() may fail, and you should account for that. Also note that if execve succeeds, it won't return. So, the print statement after it should indicate failure if it is to print anything at all.
Using system() probably would not save you the fork, since you are likely to want the output of the command to be directed to the socket associated with the connected client. But, your code is missing the steps that would allow the output to flow to the client.
switch ((pid = fork())) {
case -1: /* todo: handle error */
break;
case 0: /* child */
dup2(socket, 0); /* todo: check return value */
dup2(socket, 1); /* todo: check return value */
dup2(socket, 2); /* todo: check return value */
close(socket); /* todo: check return value */
execve(...);
/* todo: handle failure */
exit(EXIT_FAILURE);
default: /* parent */
if (pid != waitpid(pid, 0, 0)) {
/* todo: handle error */
}
}