test_terminal: wait on children to avoid zombies

Unfortunately, there is no clean way I see to hook this up from the pty
object. We can (and will) have more than one pty object opened at a
time, but the semantics of signalfd make it impossible to deliver each
SIGCHLD to its rightful owner without complicating things.

[ From what I tested:
 - If you have two signalfd's listening to the same signal, they will be
   dispatched in some round-robin manner.
 - Also, if more than one child exits before we read signalfd (possibly
   beloging to different terminals), they will be compressed to one
    event. ]

We therefore need to do the reaping from a central location, and need to
remember to copy this snippet over to main.c in the future.

Signed-off-by: Ran Benita <ran234@gmail.com>
Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
This commit is contained in:
Ran Benita 2012-01-10 02:52:42 +02:00 committed by David Herrmann
parent 6ebc7c1892
commit 5cc190d947

View File

@ -36,6 +36,7 @@
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include "eloop.h"
#include "input.h"
@ -48,6 +49,7 @@ struct app {
struct kmscon_eloop *eloop;
struct kmscon_signal *sig_term;
struct kmscon_signal *sig_int;
struct kmscon_signal *sig_chld;
struct kmscon_symbol_table *st;
struct kmscon_font_factory *ff;
struct kmscon_compositor *comp;
@ -63,13 +65,61 @@ static void sig_term(struct kmscon_signal *sig, int signum, void *data)
terminate = 1;
}
static void sig_chld(struct kmscon_signal *sig, int signum, void *data)
{
pid_t pid;
int status;
/*
* If multiple children exit at the same time, signalfd would put them
* all in one event. So we reap in a loop.
*/
while (1) {
pid = waitpid(-1, &status, WNOHANG);
if (pid == -1) {
if (errno != ECHILD)
log_warning("test: cannot wait on child: %m\n");
break;
} else if (pid == 0) {
break;
} else if (WIFEXITED(status)) {
if (WEXITSTATUS(status) != 0)
log_info("test: child %d exited with status "
"%d\n", pid, WEXITSTATUS(status));
else
log_debug("test: child %d exited "
"successfully\n", pid);
} else if (WIFSIGNALED(status)) {
log_debug("test: child %d exited by signal %d\n", pid,
WTERMSIG(status));
}
}
}
static void terminal_closed(struct kmscon_terminal *term, void *data)
{
/*
* Alternativly, we could spwan a new login/shell here, like what
* happens when the user exits the shell in a linux console.
*/
terminate = 1;
#if 0
/*
* Alternativly, we could spwan a new login/shell here, like what
* happens when the user exits the shell in a linux console:
*/
int ret;
struct app *app = data;
if (!app)
goto err_out;
ret = kmscon_terminal_open(app->term, app->eloop,
terminal_closed, app);
if (ret)
goto err_out;
return;
err_out:
#endif
terminate = 1;
}
static void read_input(struct kmscon_input *input,
@ -140,6 +190,7 @@ static void destroy_app(struct app *app)
kmscon_compositor_unref(app->comp);
kmscon_font_factory_unref(app->ff);
kmscon_symbol_table_unref(app->st);
kmscon_eloop_rm_signal(app->sig_chld);
kmscon_eloop_rm_signal(app->sig_int);
kmscon_eloop_rm_signal(app->sig_term);
kmscon_eloop_unref(app->eloop);
@ -163,6 +214,11 @@ static int setup_app(struct app *app)
if (ret)
goto err_loop;
ret = kmscon_eloop_new_signal(app->eloop, &app->sig_chld, SIGCHLD,
sig_chld, NULL);
if (ret)
goto err_loop;
ret = kmscon_symbol_table_new(&app->st);
if (ret)
goto err_loop;
@ -196,7 +252,7 @@ static int setup_app(struct app *app)
goto err_loop;
ret = kmscon_terminal_open(app->term, app->eloop,
terminal_closed, NULL);
terminal_closed, app);
if (ret)
goto err_loop;