I'm currently writing a simple shell in C and i'm facing issues with signals.
For example when I launch my program, I type ping command then CTRL-Z I want that the child process (the ping command) to be paused and then to come back when I use fg.
I think that I need to store as a global variable the child pid of the one that execute the ping command.
I already checked other posts to resolve the problem by my own but I can't get it working.
This is the code to execute a command (multiple commands with |) and where I store the child pid.
int exec_proc(int input, int output, char** command) {
pid_t runner;
runner = fork();
f_pid = runner;
if (runner == 0) {
// Use input for stdin
if (input != 0) {
dup2(input, 0);
close(input);
}
// Use output for stdout
if (output != 1) {
dup2(output, 1);
close(output);
}
// Return command code
execvp(command[0], command);
}
// An error occured
return -1;
This is my handler c file.
pid_t f_pid;
/**
* Handles every handler !
*/
void handlerManager()
{
signal(SIGINT,INTHandler);
signal(SIGTSTP,TSTPHandler);
signal(SIGCONT,CONTHandler);
}
/**
* Handler for CTRL-C
* #param sig
*/
void INTHandler(int sig)
{
printf("\nDo you really want to quit ? [y/n] \n");
int answer = getchar();
if(toupper(answer) == 'Y')
kill(f_pid,SIGINT);
}
/**
* Handler for CTRL-Z (processus sleep)
* #param sig
*/
void TSTPHandler(int sig)
{
printf("\nGoing to sleep! \n");
printf("%d", f_pid);
kill(f_pid,SIGTSTP);
}
/**
* Handler to reset a processus
* #param sig
*/
void CONTHandler(int sig)
{
printf("\nHey i'm awake\n");
kill(f_pid,SIGCONT);
}`
When I print the pid I am getting the right PID.
Finally this where I call my handler Manager.
int main() {
char* line;
char** args[MAX_ARG_SIZE] = {NULL};
int status;
handlerManager();
do {
fflush(stdin);
prompt();
line = readline();
char linecpy[strlen(line)];
strcpy(linecpy, line);
splitBy(line, " ", args);
status = exec(args, linecpy);
switch (status) {
case EMPTY_LINE:
break;
}
} while (status);
return 0;
Thank you in advance and sorry for my english.
Doing proper job-control signal handling lifts your project a bit beyond what I would characterize as a "simple" shell. The GLIBC manual has a whole multi-part section on implementing a job-control shell, and it sounds like much of it would apply to your project.
A key aspect that you seem to be disregarding is managing process groups and which of those has control of the terminal. The way you're doing things now, your shell's child processes will belong to the same process group as the shell itself, and that will present a problem when a signal, such as SIGSTP, is sent to the child's whole process group.
To avoid such problems, your shell should make new child processes be process-group leaders of their own process groups (via setpgid()). When such process groups are meant to initially be in the foreground, your shell should make them terminal's controlling process group (tcsetpgrp()).
There's more to it than that, of course, but that should get you going in the right direction.
The exec_proc file should have
pid_t f_pid;
as a global variable (reverse the two lines)
pid_t runner;
int exec_proc(int input, int output, char** command) {
The handler C file needs to access the global variable declared in the other file via extern
extern pid_t f_pid;
This way, both object files share the same variable f_pid.
edit -----
Try to change the handler TSTP, and add the following ALRM
void TSTPHandler(int sig)
{
signal(SIGTSTP,SIG_DFL); // <+++++
printf("\nGoing to sleep! \n");
printf("%d", f_pid);
kill(f_pid,SIGTSTP);
kill(getpid(),SIGTSTP); // <+++++
alarm(1); // <+++++
}
void ALRMHandler(int sig) // <+++++
{
signal(SIGTSTP,TSTPHandler);
}
Add Alarm signal
void handlerManager()
{
signal(SIGINT,INTHandler);
signal(SIGTSTP,TSTPHandler);
signal(SIGCONT,CONTHandler);
signal(SIGALRM,ALRMHandler); // <+++++
}
Works on my Linux box. When a stop occurs
disable TSTP handling
do the current stuff
kill the main process!
start an alarm to set the TSTP handling back (adding that to the handler seems to create some trouble...)
Note that alarm() takes seconds, setitimer() goes down to the microsecond (in theory).
Related
I would need some help with some C code.
Basically I have n processes which execute some code. Once they're almost done, I'd like the "Manager Process" (which is the main function) to send to each of the n processes an int variable, which may be different for every process.
My idea was to signal(handler_function, SIGALRM) once all processes started. When process is almost done, it uses kill(getpid(), SIGSTOP) in order to wait for the Manager Process.
After SIM_TIME seconds passed, handler_function sends int variable on a Message Queue then uses kill(process_pid, SIGCONT) in order to wake up waiting processes. Those processes, after being woken up should receive that int variable from Message Queue, print it and simply terminate, letting Manager Process take control again.
Here's some code:
/**
* Child Process creation using fork() system call
* Parent Process allocates and initializes necessary variables in shared memory
* Child Process executes Student Process code defined in childProcess function
*/
pid_t runChild(int index, int (*func)(int index))
{
pid_t pid;
pid = fork();
if (pid == -1)
{
printf(RED "Fork ERROR!\n" RESET);
exit(EXIT_FAILURE);
}
else if (pid == 0)
{
int res = func(index);
return getpid();
}
else
{
/*INSIGNIFICANT CODE*/
currentStudent = createStudent(pid);
currentStudent->status = FREE;
students[index] = *currentStudent;
currentGroup = createGroup(index);
addMember(currentStudent, currentGroup);
currentGroup->closed = FALSE;
groups[index] = *currentGroup;
return pid;
}
}
Code executed by each Process
/**
* Student Process Code
* Each Student executes this code
*/
int childProcess(int index)
{
/*NOTICE: showing only relevant part of code*/
printf("Process Index %d has almost done, waiting for manager!\n", index);
/* PROGRAM GETS STUCK HERE!*/
kill(getpid(), SIGSTOP);
/* mex variable is already defines, it's a struct implementing Message Queue message struct*/
receiveMessage(mexId, mex, getpid());
printf(GREEN "Student %d has received variable %d\n" RESET, getpid(), mex->variable);
}
Handler Function:
* Handler function
* Will be launched when SIM_TIME is reached
*/
void end_handler(int sig)
{
if (sig == SIGALRM)
{
usleep(150000);
printf(RED "Time's UP!\n" RESET);
printGroups();
for(int i = 0; i < POP_SIZE; i++){
mex->mtype = childPids[i];
mex->variable = generateInt(18, 30);
sendMessage(mexId, mex);
//childPids is an array containing PIDs of all previously launched processes
kill(childPids[i], SIGCONT);
}
}
I hope my code is understandable.
I have an issue though, Using provided code the entire program gets stuck at kill(getpid(), SIGSTOP) system call.
I also tried to launch ps in terminal and no active processes are detected.
I think handler_function doesn't send kill(childPids[i], SIGCONT) system call for some reason.
Any idea how to solve this problem?
Thank you
You might want to start by reading the manual page for mq_overview (man mq_overview). It provides a portable and flexible communication mechanism between processes which permits sync and async mechanisms to communicate.
In your approach, there is a general problem of “how does one process know if another is waiting”. If the process hasn’t stopped itself, the SIGCONT is ignored, and when it subsequently suspends itself, nobody will continue it.
In contrast, message-based communication between the two can be viewed as a little language. For simple exchanges (such as yours), the completeness of the grammar can be readily hand checked. For more complex ones, state machines or even nested state machines can be constructed to analyze their behaviour.
Like many others, I'm trying to simulate a shell. I've gotten to use the execvp properly on a string coming from the user. The string is parsed and an array of strings is generated (each word has its array, split on the space character), including a NULL at the very end.
When I find that the last word entered by the user is &, I set a flag up to notify my shell that the command is to be executed in the background while letting the user input another command right away. The "background-executed" command sees its & replaced by a NULL character within the array of strings passed to execvp.
As it is, I've been trying to use a pthread to run the process in the background, but it's acting somewhat weird: the command passed to execvp through the thread's function requires me to press two times ENTER after sending the command.
Here is my simplified main function that is to simulate a shell:
int main (void) {
fprintf (stdout, "%% ");
bool running = true;
while(running) {
/* Ask for an instruction and parses it. */
char** args = query_and_split_input();
/* Executing the commands. */
if (args == NULL) { // error while reading input
running = false;
} else {
printf("shell processing new command\n");
int count = count_words(args);
split_line* line = form_split_line(args, count);
Expression* ast = parse_line(line, 0, line->size - 1);
if(line->thread_flag) {
pthread_t cmd_thr;
/* Setting up the content of the thread. */
thread_data_t thr_data;
thr_data.ast = *ast;
thr_data.line = *line;
/* Executing the thread. */
int thr_err;
if ((thr_err = pthread_create(&cmd_thr, NULL, thr_func, &thr_data))) {
fprintf(stderr, "error: pthread_create, rc: %d\n", thr_err);
return EXIT_FAILURE;
}
printf("thread has been created.\n");
} else {
run_shell(args);
}
free(line);
printf("done running shell on one command\n");
}
}
/* We're all done here. See you! */
printf("Bye!\n");
exit (0);
}
Here is my thread's function:
void *thr_func(void *arg) {
thread_data_t *data = (thread_data_t *)arg;
data->line.content[data->line.size-1] = NULL; // to replace the trailing '&' from the command
run_shell(data->line.content);
printf("thread should have ran the command\n");
pthread_exit(NULL);
}
And the actual line that runs a command:
void run_shell(char** args) {
/* Forking. */
int status;
pid_t pid; /* Right here, the created THREAD somehow awaits a second 'ENTER' before going on and executing the next instruction that forks the process. This is the subject of my first question. */
pid = fork();
if (pid < 0) {
fprintf(stderr, "fork failed");
} else if (pid == 0) { // child
printf("Child executing the command.\n");
/* Executing the commands. */
execvp(args[0], args);
/* Child process failed. */
printf("execvp didn't finish properly: running exit on child process\n");
exit(-1);
} else { // back in parent
waitpid(-1, &status, 0); // wait for child to finish
if (WIFEXITED(status)) { printf("OK: Child exited with exit status %d.\n", WEXITSTATUS(status)); }
else { printf("ERROR: Child has not terminated correctly. Status is: %d\n", status); }
free(args);
printf("Terminating parent of the child.\n");
}
}
So basically, as an example, what run_shell(args) receives is either ["echo","bob","is","great",NULL] (in the case of a sequential execution) or ["echo","bob","is","great",NULL,NULL] (in the case of a command to be executed in the background).
I've left the printf traces since it might help you understand the execution flow.
If I input echo bob is great, the output (printf traces) is:
shell processing new command
Child executing the command.
bob is great
OK: Child exited with exit status 0.
Terminating parent of the child.
done running shell on one command
However, if I input echo bob is great &, the output is:
shell processing new command
thread has been created.
done running shell on one command
And then I actually need to press ENTER again to obtain the following output:
Child executing the command.
bob is great
OK: Child exited with exit status 0.
Terminating parent of the child.
thread should have ran the command
(On that last execution, I also get traces of my function that queries and parses the input of the user, but that seemed irrelevant so I abstracted this whole part.)
So my questions are:
How comes the created thread awaits a second ENTER before running the execvp ? (thr_func stops executing run_shell and awaits the second ENTER right before the pid = fork(); instruction)
Do I have the right approach to solve the problem at hand? (Trying to execute a shell command in the background.)
You cannot use a thread to simulate a process. Well, strictly you can, but there's no use on doing that. The problem is that all the threads belonging to a process share the same virtual address space. There's no reason to create a thread, as you finally need to fork() to create a new process (you'll need this for reasons explained below), so why to create two threads of execution if one of them will be stopped all the time just waiting for the subprocess to finish. There's no use on this schema.
The need of a fork() system call comes historically to make a simple call to create a new process (with different virtual memory map) to allow for a new program to be able to be executed. You need to create a new, complete process before calling exec(2) system call, because the process address space will be overwritten by the text and data segments of the new program. If you do this in a thread, you'll be overwriting the whole process address space (this is the shell) and killing all the threads you can have running on behalf of that process. The schema to follow is (pseudocode):
/* create pipes for redirection here, before fork()ing, so they are available
* in the parent process and the child process */
int fds[2];
if (pipe(fds) < 0) { /* error */
... /* do error treatment */
}
pid_t child_pid = fork();
switch(child_pid) {
case -1: /* fork failed for some reason, no subprocess created */
...
break;
case 0: /* this code is executed in the childd process, do redirections
* here on pipes acquired ***before*** the fork() call */
if (dup2(0 /* or 1, or 2... */, fds[0 /* or 1, or 2... */]) < 0) { /* error */
... /* do error management, considering you are in a different process now */
}
execvpe(argc, argv, envp);
... /* do error management, as execvpe failed (exec* is non-returning if ok) */
break; /* or exit(2) or whatever */
default: /* we are the parent, use the return value to track the child */
save_child_pid(child_pid);
... /* close the unused file descriptors */
close(fds[1 /* or 0, or 2, ... */]);
... /* more bookkeeping */
/* next depends on if you have to wait for the child or not */
wait*(...); /* wait has several flavours */
} /* switch */
Exec and fork system calls are separated by two reasons:
you need to be able to do housekeeping between both calls to execute the actual redirections in the child before exec().
there was a time when unix was not multitasking or protected, and the exec call just replaced all the memory in the system with the new program to execute (including kernel code, to cope with the fact that an unprotected system could be corrupted by the executing program) This was common in old operating systems and I've seen it on systems like CP/M or TRS-DOS. The implementation in unix conserved almost all the semantics of exec() call and added with fork() the unavailable functionality only. This was good, as it allowed both, parent and child processes to do the necessary bookkeeping when the time for pipes came.
Only if you need a different thread to communicate with each child is when you probably can use a different thread to do the task. But think that a thread shares all the virtual space with the parent (case we can talk about a parent/child relationship between threads) and if you do an exec call you'll get that virtual space overwritten for the whole process (all threads there)
I was wondering what exactly happens after the execution of a handler on a signal in C, more specifically on the SIGCHLD signal.
I'm actually building a shell and I need to do this:
User enters a command with "&" as the last argument
A new process is created with fork
The parent process returns to the main function and wait for a new input from the user
The child process executes in the background
The child process ends and SIGCHLD is triggered
The parent process handles the death of his child
My code do all of these points, but behaves in a strange way at the very end when the handler function returns.
My implementation
Main loop
int
main()
{
// SIGNALS HANDLING :
sigaction(SIGTERM, NULL, NULL);
sigaction(SIGQUIT, NULL, NULL);
// Current location
char* path = malloc(PATHMAX);
int argc;
char** argv;
// main loop
while(1)
{
getcwd(path, PATHMAX);
printf("%s$ ",path);
argc = 0;
// Parsing
argv = malloc(MAX_INPUT); // user's input is limited to 100000 characters
getParameters(argv, &argc);
// First we check if the user wants to execute the task in background
if ( strcmp(argv[argc-1],"&") == 0 ) {
// builtin functions can't be executed in background:
int isExit = (strcmp(argv[0],"exit") == 0) ? 1 : 0;
int isCd = (strcmp(argv[0],"cd") == 0) ? 1 : 0;
if(isExit || isCd) {
printf("Built-in functions can't be executed in background. \n");
} else {
// Execute the job in background
// First we delete the last arg
argv[argc-1] = NULL;
argc--;
execBackground(argv,argc);
}
} else {
// Execute built-in functions
if(!(exeBuiltin(argv, argc))) {
// Execute jobs if no builtin functions were executed
exeJob(argv,argc);
}
}
free(argv);
}
return 0;
}
User's input processing
// max 100000 characters
char input[MAX_INPUT];
// read keyboard inputs
fgets(input,MAX_INPUT,stdin);
Built-in functions (cd and exit)
int exeBuiltin(char** argv, int argc) {
// execute builtin functions if it exists
char* builtin[2];
builtin[0] = "exit";
builtin[1] = "cd";
// run through the arguments
for(int i = 0; i < argc; i++) {
// exit
if (strcmp(argv[i],builtin[0]) == 0) {
exitBuiltin(EXIT_SUCCESS);
} else if (strcmp(argv[i],builtin[1]) == 0) {
// cd
if (argc >= 2) {
cdBuiltin(argv[i+1]);
return 1;
}
}
}
return 0;
}
static void cdBuiltin(char* path) {
if(chdir(path) == -1) {
printf("%s\n",strerror(errno));
};
}
static void exitBuiltin(int signal) {
exit(signal);
}
Link between signal and handler:
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sa.sa_handler = child_handler;
sigaction(SIGCHLD, &sa, NULL);
Handler:
static void child_handler(int sig)
{
int status;
/* kills process
pid is a global variable that is set when I call fork() */
if(waitpid(pid, &status, 0) != -1) {
if (status == 0) // Verify child process terminated without error.
{
// success
printf("\nBackground job exited with code 0\n");
}
if (status == 1)
{
// error
printf("\nBackground job exited\n");
}
}
return;
}
The problem
My problem occurs when I call something like:
sudo apt-get update &
cd ../
Then when the sudo command terminates (the handler is called and prints "Background job exited with code 0"), even though the command "cd ../" is already executed it gets executed again.
The ouput looks like:
$ /home/ubuntu/workspace$ sudo apt-get update &
$ /home/ubuntu/workspace$ cd ../
$ /home/ubuntu$
Background job exited with code 0
$ /home$
Additional information:
cd is a builtin function which only does: chdir(path) with a path given, and the path is simply output with a printf("%s$",path) at the beginning of every loop in the main (after a command has been called).
The question
In order to understand what the real problem is, I believe that I should understand what happens at the end of my child_handler function.
If I'm at a certain point in the code with the main process, say waiting on the user input, and the background process dies, child_handler is called and then what? Does it change anything for the main process or is it still a the same point in the code, waiting for an input?
Thanks for you help.
One definite problem is that you are calling printf in your signal handler, and printf is not async-safe, so if the SIGCHLD happens while in any library function (such as fgets waiting to read a line of input perhaps?), you get undefined behavior. Perhaps corrupting the stdio file buffers and causing it to act as if the input was duplicated.
You need to very careful how you write the code in your signal handler, making sure it cannot call (directly or indirectly) and non-async-safe library function, and ensuring that all the code is itself async-safe with respect to any side effects in other parts of your program.
As #ChrisDodd said, the problem was that a system call (fgets), was interrupted by the death of the background process and caused undefined behaviour.
Therefore, I solved my problem by adding the following flag to my handler:
sa.sa_flags = SA_RESTART;
As the documentation says it will:
Provide behavior compatible with BSD signal semantics by making certain system calls restartable across signals.
Thus no more issue with fgets.
I got a problem in C when I try to pause an execution of a system() call.
A thread calls some application (e.g. some benchmark) repeatedly. Whenever it gets a signal SIGUSR1, the execution shall be paused and resumed on receiving SIGUSR2.
The source looks like this:
#include <signal.h>
#include <pthread.h>
void* run_app(sigset_t* signalsBetweenControllerandLoad)
{
/* assign handler */
signal(SIGUSR1, pausesignal_handler)
signal(SIGUSR2, pausesignal_handler)
pthread_sigmask(SIG_UNBLOCK, signalsBetweenControllerandLoad, NULL))
/* call application repeatedly */
while(1) {
system(SOMECOMMAND);
}
return(0);
}
static void pausesignal_handler(int signo)
{
int caughtSignal;
caughtSignal = 0;
/* when SIGUSR1 is received, wait until SIGUSR2 to continue execution */
if (signo == SIGUSR1) {
signal(signo, pausesignal_handler);
while (caughtSignal != SIGUSR2) {
sigwait (signalsBetweenControllerandLoad, &caughtSignal);
}
}
}
When I use some commands (e.g. a for loop as below that makes some computations) instead of system(SOMECOMMAND) this code works. But a program called by system() is not paused when the handler is active.
int i;
for(i=0;i<10;i++) {
sleep(1);
printf("Just a text");
}
Is there a way to pause the execution of the system() command by using thread signals? And is there even a way to stop the application called by system without needing to wait until the program is finished?
Thank you very much in advance!
system runs the command in a separate process, which doesn't even share address space with the invoking program, never mind signal handlers. The process which called system is sitting in a waitpid (or equivalent), so pausing and unpausing it will have little effect (except that if it is paused, it won't return to the loop to call system again.)
In short, there is no way to use signals sent to the parent process to pause an executable being run in a child, for example with the system() call or with fork()/exec().
If the executable itself implements the feature (which is unlikely, unless you wrote it yourself), you could deliver the signal to that process, not the one which called system.
Alternatively, you could send the SIGSTOP signal to the executable's process, which will unconditionally suspend execution. To do that, you'll need to know its pid, which suggests the use of the fork()/exec()/waitpid() sequence -- a little more work than system(), but cleaner, safer, and generally more efficient -- and you'll need to deal with a couple of issues:
A process cannot block or trap SIGSTOP, but it can trap SIGCONT so the sequence is not necessarily 100% transparent.
Particular care needs to be taken if the stopped process is the terminal's controlling process, since when it is resumed with SIGCONT it will need to reacquire the terminal. Furthermore, if the application has placed the terminal in a non-standard state -- for example, by using the readline or curses libraries which typically put the terminal into raw mode and disable echoing -- then the terminal may be rendered unusable.
Your process will receive a SIGCHLD signal as a result of the child processed being stopped. So you need to handle that correctly.
I want to present you my (shortened) resulting code after the help of #rici. Again, thank you very much.
Shortly described, the code forks a new process (calling fork) and executes there a command with exec. The parent then catches user defined signals SIGNAL_PAUSE and SIGNAL_RESUME and forwards signals to the forked child accordingly. Whenever the command finishes - catched by waitpid - the parent forks again and restarts the load.
This gets repeated until SIGNAL_STOP is sent where the child gets a SIGINT and gets cancelled.
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#define SIGNAL_PAUSE (SIGUSR1)
#define SIGNAL_RESUME (SIGUSR2)
#define SIGNAL_STOP (SIGSYS)
/* File scoped functions */
static void pausesignal_handler(int signo);
static void stopsignal_handler(int signo);
void send_signal_to_load_child(int signo);
/*Set file scope variables as handlers can only have signal-number as argument */
sigset_t* signalsBetweenControllerandLoad;
int restart_benchmark;
pid_t child_pid;
void* Load(char* load_arguments[MAX_NR_LOAD_ARGS], sigset_t* signalsToCatch) {
int load_ID;
pid_t p;
signalsBetweenControllerandLoad = signalsToCatch;
/* set signal handlers to catch signals from controller */
signal(SIGNAL_PAUSE, pausesignal_handler)
signal(SIGNAL_RESUME, pausesignal_handler)
signal(SIGNAL_STOP, stopsignal_handler)
pthread_sigmask(SIG_UNBLOCK, signalsBetweenControllerandLoad[load_ID], NULL)
/* Keep restarting benchmark until Stop signal was received */
restart_benchmark[load_ID] = 1;
/* execute benchmark, repeat until stop signal received */
while(restart_benchmark[load_ID])
{
if (child_pid == 0) {
if ((p = fork()) == 0) {
execv(load_arguments[0],load_arguments);
exit(0);
}
}
/* Parent process: Wait until child with benchmark finished and restart it */
if (p>0) {
child_pid = p; /* Make PID available for helper functions */
wait(child_pid); /* Wait until child finished */
child_pid = 0; /* Reset PID when benchmark finished */
}
}
return(0);
}
static void pausesignal_handler(int signo) {
static double elapsedTime;
int caughtSignal;
caughtSignal = 0;
if (signo == SIGNAL_PAUSE) {
send_signal_to_load_child(SIGSTOP);
printf("Load Paused, waiting for resume signal\n");
while (restart_benchmark == 1 && caughtSignal != SIGNAL_RESUME) {
sigwait (signalsBetweenControllerandLoad, &caughtSignal);
if (caughtSignal == SIGNAL_STOP) {
printf("Load caught stop signal when waiting for resume\n");
stopsignal_handler(caughtSignal);
} else if (caughtSignal != SIGNAL_RESUME) {
printf("Load caught signal %d which is not Resume (%d), keep waiting...\n",caughtSignal,SIGNAL_RESUME);
}
}
if (restart_benchmark[load_ID]) {
send_signal_to_load_child(SIGCONT, load_ID);
printf("Load resumed\n");
}
} else {
printf("Load caught unexpected signal %d.\n",signo);
}
/* reassign signals for compatibility reasons */
signal(SIGNAL_PAUSE, pausesignal_handler);
signal(SIGNAL_RESUME, pausesignal_handler);
}
static void stopsignal_handler(int signo) {
double elapsedTime;
signal(SIGNAL_STOP, stopsignal_handler);
if (signo == SIGNAL_STOP) {
restart_benchmark = 0;
send_signal_to_load_child(SIGINT);
printf("Load stopped.\n");
} else {
printf("catched unexpected stop-signal %d\n",signo);
}
}
void send_signal_to_load_child(int signo) {
int dest_pid;
dest_pid = child_pid;
printf("Error sending %d to Child: PID not set.\n",signo);
kill(dest_pid, signo);
}
EDIT:
I am trying to write a simple smoketest, where all options and reasonable parameters are tested.
I used popen() to execute the program that should be tested.
Using this approach does not work, because if the process dies with a signal (SIGINT, SIGSEGV...) the pipe from popen() does not tell me what happend.
Writing a signal handler did not help since popen creates a new process that receives the signals but not my smoketest.
Thanks to the answers i used pipe(), fork() and execv() to create my own popen()-version.
When the program now segfaults there is the problem that the pipe is useless (a read caused weird behavior -> blocked the process until i send a sigkill to the parent!)
To avoid this i tried different things and my solution is the following (it is simple but it took me a while to figure it out). so here is my example-code:
static int child_dead = 0;
void sigaction_sigchld(int signal) { /* Child died */
child_dead = 1;
}
int main(int argc, char *argv[], char *env[])
{
char *crashing_program = "/program_path/and_name";
int ret;
int byte;
pid = fork();
if(pid == 0) /* Child */
{
execve(crashing_program, argv, env);
/* if execve returns that it mus have failed! */
fprintf(stderr, "Exec failed\n");
_exit(-1);
} else /* Parent */
{
if(!child_dead)
{
byte = read(pipe_out[1], line, BUFFSIZE);
if(!byte){
perror("Smoketest:Line:xxx");
} else
{
fprintf(stdout, line);
}
}
wait(&child_status);
/*
check if the child died with SIGSEGV etc
*/
}
This seems to work fine as long as i only have one child at a time which is sufficient for me though. I anyone has a better idea or any tipps for me i would be glad to update this entry.
Last but not least: Of course using this method it is probably impossible to do any cleanup.
Cheers.
See the documentation for waitpid(2). There are a bunch of macros you can use to test how the child process was terminated. In particular, you can use WIFSIGNALED() and WTERMSIG() to test if the child process was terminated by a signal, and if so, which signal:
int status = pclose(...);
if (WIFSIGNALED(status))
{
// It was terminated by a signal
if (WTERMSIG(status) == SIGSEGV)
{
// It was terminated by a segfault
}
}
Edit: As stated in the comments, you'd rather make use of fork and exec, then use waitpid(2) to correctly update status.