How to create a program that reads the last commands exit status? - c

In Linux C, how do you read the last programs exit status.
For example:
true; echo $?
Will show a 0 for success. But I'd like to replace that with a C program:
true; ./echo_exit_status
How/where is the last exit code $? available to a C program?

The last command's exit status is not accessible to a subsequently-executed program (absent insanely-horrible hackery -- attaching to the parent shell with a debugger or somesuch).
You could write a shell function or helper that exported this value into the environment -- but no solution is possible which does not require the shell's involvement.

You'd have to know the pid but otherwise it's http://linux.die.net/man/2/waitpid
More info from the man page
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 be
employed only if WIFEXITED returned true.
Here is an example from the
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
int
main(int argc, char *argv[])
{
pid_t cpid, w;
int status;
cpid = fork();
if (cpid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (cpid == 0) { /* Code executed by child */
printf("Child PID is %ld\n", (long) getpid());
if (argc == 1)
pause(); /* Wait for signals */
_exit(atoi(argv[1]));
} else { /* Code executed by parent */
do {
w = waitpid(cpid, &status, WUNTRACED | WCONTINUED);
if (w == -1) {
perror("waitpid");
exit(EXIT_FAILURE);
}
if (WIFEXITED(status)) {
printf("exited, status=%d\n", WEXITSTATUS(status)); //this line will return the exit status, whether it was 1 or 0
} else if (WIFSIGNALED(status)) {
printf("killed by signal %d\n", WTERMSIG(status));
} else if (WIFSTOPPED(status)) {
printf("stopped by signal %d\n", WSTOPSIG(status));
} else if (WIFCONTINUED(status)) {
printf("continued\n");
}
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
exit(EXIT_SUCCESS);
}
}

Related

Signal handling

I'm currently coding a function that execute external command for an assignment. Here's what I've done, the problem is that the program is taking too long and is interrupted by a SIGALRM.
Thanks for any help!
volatile sig_atomic_t sig = 0;
void ext(int signum){
if(signum==SIGINT || signum==SIGTERM) sig=1;
}
int extern(char **line){
pid_t p;
int status;
struct sigaction as = {0};
as.sa_handler=ext;
if (sigaction(SIGINT, &as, 0)==-1||sigaction(SIGTERM, &as, 0)==-1){
perror("sigaction");
exit(1);
}
switch(p=fork()){
case -1: perror("fork"); exit(1);
case 0 : if(execvp(line[0], line)<0) {perror("exec"); exit(1);} break;
default : //EDIT
if(waitpid(p, &status, 0)>=0){
if (WIFEXITED(status)) return WEXITSTATUS(status);
} else {
perror("wait");
exit(1);
}
break;
}
return 0;
}
Signal dispositions are not carried across a call to execve. They will be reset as soon as execvp executes.
Even if the signal arrives in the child before execpv executes, the parent and the child process have their own copy of
volatile sig_atomic_t sig = 0;
The signal handler in the child changing this value would not cause the parent's copy to change.
Establishing a signal handler is not the correct approach to take here.
Instead, your approach of using waitpid is the correct one, but alongside checking for a normal termination of the program with WIFEXITED(status), you should include another branch that checks WIFSIGNALED(status), which will be true if the child process terminated due to a signal.
WTERMSIG(status) is used to determine which signal terminated the child process.
Here is a general example where the child process randomly exits successfully, or otherwise raises a signal where the default disposition is to terminate the program:
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
int main(void)
{
pid_t child = fork();
if (-1 == child) {
perror("fork");
return EXIT_FAILURE;
}
if (0 == child) {
/* randomly signal or return successfully */
srand((unsigned) time(NULL));
switch (rand() % 4) {
case 1: raise(SIGINT); break;
case 2: raise(SIGTERM); break;
case 3: raise(SIGKILL); break;
}
return EXIT_SUCCESS;
}
int status;
if (-1 == waitpid(child, &status, 0)) {
perror("wait");
return EXIT_FAILURE;
}
if (WIFSIGNALED(status)) {
int sig = WTERMSIG(status);
if (SIGINT == sig || SIGTERM == sig)
printf("Child <%ld> exited by signal SIGINT or SIGTERM.\n", (long) child);
else
printf("Child <%ld> exited by signal #%d.\n", (long) child, sig);
} else if (WIFEXITED(status)) {
printf("Child <%ld> exited normally with status %d.\n",
(long) child,
WEXITSTATUS(status));
}
}
Output from running this program a few times:
Child <41268> exited by signal SIGINT or SIGTERM.
Child <41272> exited by signal SIGINT or SIGTERM.
Child <41276> exited by signal #9.
Child <41280> exited normally with status 0.

waitpid stops waiting after signal is sent

I am currently working on a C project for university. Among other things I should signal the parent process using SIGUSR1.
The problem I'm facing at the moment is that I also need to wait for the child process to terminate so I can safely shut down everything in the end (removing shared Memory etc.).
At the moment I am using sigaction() to respond to the signal and waitpid() to wait for the child to terminate (that was the plan anyways ^^). But when I signal the parent using kill(), waitpid() stops waiting and runs the remainder of the parent even though the child is still running.
I feel like I'm missing something obvious but I can't figure it out.
Any help is greatly appreciated,
stay safe
Tim
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <unistd.h>
void handle_sigusr1(int sig) {
printf("Recieved signal %i.\n", sig);
}
int main() {
pid_t pid;
pid = fork();
if (pid == -1) {
perror("fork:");
return EXIT_FAILURE;
}
else if (pid == 0) {
printf("Hello from the child.\n");
kill(getppid(), SIGUSR1);
sleep(3);
printf("Hello again from the child.\n");
return EXIT_SUCCESS;
}
else {
printf("Hello from the parent.\n");
struct sigaction sa;
sa.sa_handler = &handle_sigusr1;
sigaction(SIGUSR1, &sa, NULL);
int status;
waitpid(pid, &status, 0);
if (WIFEXITED(status))
printf("Exit status: %i\n", WEXITSTATUS(status));
printf("Finished waiting for child.\n");
return EXIT_SUCCESS;
}
}
Output:
Hello from the parent.
Hello from the child.
Recieved signal 10.
Exit status: 0
Finished waiting for child.
tim#schlepptop:signalTest$ Hello again from the child.
PS: WEXITSTATUS(status) is usually 0 but sometimes it's also something like 16 or 128.
Per POSIX waitpid() documentation:
RETURN VALUE
... 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]. ...
You need to check the return value:
int status
do
{
errno = 0;
int rc = waitpid(pid, &status, 0);
if ( rc != -1 )
{
break;
}
}
while ( errno == EINTR );

Check whether Child Process has terminated in C on Unix without blocking

I would like to check whether / when a child process has terminated in C on Unix. It's not supposed to be blocking, rather a short check in a loop.
My code:
pid_t pid = fork();
if (pid > 0)
// Parent Process
while (1) {
// Do a short check whether Child has already terminated if yes break the loop.
// Ik that it's possible to use waitpid(pid, &status, 0) but that blocks the whole loop until the child has terminated
}
if (pid == 0)
printf("child process born");
exit(0);
Thx in advance
The third argument to waitpid is a set of flags. If you pass WNOHANG to this argument, the function will return immediately if no children have yet exited.
You can then check if waitpid returned 0. If so, no child exited and you wait and try again.
while (1) {
pid_t rval = waitpid(pid, &status, WNOHANG);
if (rval == -1) {
perror("waitpid failed");
exit(1);
} else if (rval == 0) {
sleep(1);
} else {
break;
}
}
Traditional way is:
#include <errno.h>
#include <sys/types.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
int exist(pid_t pid) {
return kill(pid, 0) > 0 || errno != ESRCH;
}
int main(int ac, char **av) {
while (--ac > 0) {
pid_t p = strtol(*++av, 0, 0);
printf("%d %s\n", p, exist(p) ? "exists" : "doesn't exist");
}
return 0;
}
It doesn't care about parent : child relation (whereas wait derivatives do), and works even if you don't have permission to affect the process.

Capturing exit status code of child process

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.

Function that returns exit type as string

I want my program to output how the process has exited, as seen in the second line after the user's prompt and input.
shell> wait
shell: process has exited abnormally with signal 11: Segmentation fault.
What function would I use in my printf()? I thought of exit() but that returns void. Could it be strsignal, if so, what would I pass as the int for strsignal? Thank you!
You are correct, it is strsignal(). See this example: https://www.cs.fsu.edu/~baker/opsys/examples/forkexec/print_child_status.c
Relevant portion:
#include <stdio.h>
#include <wait.h>
#include <string.h>
void print_child_status (int status) {
if (WIFEXITED (status)) {
fprintf (stdout, "Child exited with status %d\n", WEXITSTATUS (status));
} else if (WIFSTOPPED (status)) {
fprintf (stdout, "Child stopped by signal %d (%s)\n", WSTOPSIG (status), strsignal (WSTOPSIG (status)));
} else if (WIFSIGNALED (status)) {
fprintf (stdout, "Child killed by signal %d (%s)\n", WTERMSIG (status), strsignal (WTERMSIG (status)));
} else {
fprintf (stdout, "Unknown child status\n");
}
}
You can refer to the code at towards the end in the following link http://linux.die.net/man/2/waitpid
The demonstration of the program:
$ ./a.out &
Child PID is 32360
[1] 32359
$ kill -STOP 32360
stopped by signal 19
$ kill -CONT 32360
continued
$ kill -TERM 32360
killed by signal 15
[1]+ Done ./a.out
$
Source of the program:
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
int
main(int argc, char *argv[])
{
pid_t cpid, w;
int status;
cpid = fork();
if (cpid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (cpid == 0) { /* Code executed by child */
printf("Child PID is %ld\n", (long) getpid());
if (argc == 1)
pause(); /* Wait for signals */
_exit(atoi(argv[1]));
} else { /* Code executed by parent */
do {
w = waitpid(cpid, &status, WUNTRACED | WCONTINUED);
if (w == -1) {
perror("waitpid");
exit(EXIT_FAILURE);
}
if (WIFEXITED(status)) {
printf("exited, status=%d\n", WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
printf("killed by signal %d\n", WTERMSIG(status));
} else if (WIFSTOPPED(status)) {
printf("stopped by signal %d\n", WSTOPSIG(status));
} else if (WIFCONTINUED(status)) {
printf("continued\n");
}
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
exit(EXIT_SUCCESS);
}
}

Resources