306 lines
6.7 KiB
C
306 lines
6.7 KiB
C
|
/*
|
||
|
* Copyright (C) 2002 Red Hat, Inc.
|
||
|
*
|
||
|
* This is free software; you can redistribute it and/or modify it under
|
||
|
* the terms of the GNU Library General Public License as published by
|
||
|
* the Free Software Foundation; either version 2 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful, but
|
||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
* General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU Library General Public
|
||
|
* License along with this program; if not, write to the Free Software
|
||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||
|
*/
|
||
|
|
||
|
#ident "$Id$"
|
||
|
#include "../config.h"
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/wait.h>
|
||
|
#include <signal.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <unistd.h>
|
||
|
#include <glib.h>
|
||
|
#include <glib-object.h>
|
||
|
#include <gtk/gtk.h>
|
||
|
#include <gtk/gtkmarshal.h>
|
||
|
#include "reaper.h"
|
||
|
|
||
|
#ifdef HAVE_LOCALE_H
|
||
|
#include <locale.h>
|
||
|
#else
|
||
|
#define bindtextdomain(package,dir)
|
||
|
#endif
|
||
|
|
||
|
#ifdef ENABLE_NLS
|
||
|
#include <libintl.h>
|
||
|
#define _(String) dgettext(PACKAGE, String)
|
||
|
#else
|
||
|
#define _(String) String
|
||
|
#endif
|
||
|
|
||
|
static VteReaper *singleton_reaper = NULL;
|
||
|
struct reaper_info {
|
||
|
int signum;
|
||
|
pid_t pid;
|
||
|
int status;
|
||
|
};
|
||
|
|
||
|
static void
|
||
|
vte_reaper_signal_handler(int signum)
|
||
|
{
|
||
|
struct reaper_info info;
|
||
|
int status;
|
||
|
|
||
|
/* This might become more general-purpose in the future, but for now
|
||
|
* just forget about signals other than SIGCHLD. */
|
||
|
info.signum = signum;
|
||
|
if (signum != SIGCHLD) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ((singleton_reaper != NULL) && (singleton_reaper->iopipe[0] != -1)) {
|
||
|
info.pid = waitpid(-1, &status, WNOHANG);
|
||
|
if (info.pid != -1) {
|
||
|
info.status = status;
|
||
|
if (write(singleton_reaper->iopipe[1], "", 0) == 0) {
|
||
|
write(singleton_reaper->iopipe[1],
|
||
|
&info, sizeof(info));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static gboolean
|
||
|
vte_reaper_emit_signal(GIOChannel *channel, GIOCondition condition,
|
||
|
gpointer data)
|
||
|
{
|
||
|
struct reaper_info info;
|
||
|
if (condition != G_IO_IN) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
g_assert(data == singleton_reaper);
|
||
|
read(singleton_reaper->iopipe[0], &info, sizeof(info));
|
||
|
if (info.signum == SIGCHLD) {
|
||
|
g_signal_emit_by_name(data, "child-exited",
|
||
|
info.pid, info.status);
|
||
|
}
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
vte_reaper_channel_destroyed(gpointer data)
|
||
|
{
|
||
|
/* no-op */
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
vte_reaper_init(VteReaper *reaper, gpointer *klass)
|
||
|
{
|
||
|
struct sigaction action;
|
||
|
int ret;
|
||
|
|
||
|
/* Open the signal pipe. */
|
||
|
ret = pipe(reaper->iopipe);
|
||
|
if (ret == -1) {
|
||
|
g_error(_("Error creating signal pipe."));
|
||
|
}
|
||
|
|
||
|
/* Create the channel. */
|
||
|
reaper->channel = g_io_channel_unix_new(reaper->iopipe[0]);
|
||
|
|
||
|
/* Add the channel to the source list. */
|
||
|
g_io_add_watch_full(reaper->channel,
|
||
|
G_PRIORITY_HIGH,
|
||
|
G_IO_IN,
|
||
|
vte_reaper_emit_signal,
|
||
|
reaper,
|
||
|
vte_reaper_channel_destroyed);
|
||
|
|
||
|
/* Set the signal handler. */
|
||
|
action.sa_handler = vte_reaper_signal_handler;
|
||
|
sigemptyset(&action.sa_mask);
|
||
|
action.sa_flags = SA_RESTART | SA_NOCLDSTOP;
|
||
|
sigaction(SIGCHLD, &action, NULL);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
vte_reaper_finalize(GObject *reaper)
|
||
|
{
|
||
|
GObjectClass *object_class;
|
||
|
struct sigaction action, old_action;
|
||
|
|
||
|
/* Reset the signal handler if we still have it hooked. */
|
||
|
action.sa_handler = SIG_DFL;
|
||
|
sigemptyset(&action.sa_mask);
|
||
|
action.sa_flags = SA_RESTART | SA_NOCLDSTOP;
|
||
|
sigaction(SIGCHLD, NULL, &old_action);
|
||
|
if (old_action.sa_handler == vte_reaper_signal_handler) {
|
||
|
sigaction(SIGCHLD, &action, NULL);
|
||
|
}
|
||
|
|
||
|
/* Remove the channel from the source list. */
|
||
|
g_source_remove_by_user_data(reaper);
|
||
|
|
||
|
/* Remove the channel. */
|
||
|
g_io_channel_unref((VTE_REAPER(reaper))->channel);
|
||
|
|
||
|
/* Close the pipes. */
|
||
|
close((VTE_REAPER(reaper))->iopipe[1]);
|
||
|
close((VTE_REAPER(reaper))->iopipe[0]);
|
||
|
|
||
|
/* Call the inherited destructor. */
|
||
|
object_class = g_type_class_peek(G_TYPE_OBJECT);
|
||
|
if (G_OBJECT_CLASS(object_class)->finalize) {
|
||
|
(G_OBJECT_CLASS(object_class))->finalize(reaper);
|
||
|
}
|
||
|
singleton_reaper = NULL;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
vte_reaper_class_init(VteReaperClass *klass, gpointer data)
|
||
|
{
|
||
|
GObjectClass *gobject_class;
|
||
|
|
||
|
klass->child_exited_signal = g_signal_new("child-exited",
|
||
|
G_OBJECT_CLASS_TYPE(klass),
|
||
|
G_SIGNAL_RUN_LAST,
|
||
|
0,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
gtk_marshal_NONE__INT_INT,
|
||
|
G_TYPE_NONE,
|
||
|
2, G_TYPE_INT, G_TYPE_INT);
|
||
|
|
||
|
gobject_class = G_OBJECT_CLASS(klass);
|
||
|
gobject_class->finalize = vte_reaper_finalize;
|
||
|
}
|
||
|
|
||
|
GType
|
||
|
vte_reaper_get_type(void)
|
||
|
{
|
||
|
static GType reaper_type = 0;
|
||
|
static GTypeInfo reaper_type_info = {
|
||
|
sizeof(VteReaperClass),
|
||
|
|
||
|
(GBaseInitFunc)NULL,
|
||
|
(GBaseFinalizeFunc)NULL,
|
||
|
|
||
|
(GClassInitFunc)vte_reaper_class_init,
|
||
|
(GClassFinalizeFunc)NULL,
|
||
|
NULL,
|
||
|
|
||
|
sizeof(VteReaper),
|
||
|
0,
|
||
|
(GInstanceInitFunc) vte_reaper_init,
|
||
|
|
||
|
(const GTypeValueTable *) NULL,
|
||
|
};
|
||
|
if (reaper_type == 0) {
|
||
|
reaper_type = g_type_register_static(G_TYPE_OBJECT,
|
||
|
"VteReaper",
|
||
|
&reaper_type_info,
|
||
|
0);
|
||
|
}
|
||
|
return reaper_type;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* vte_reaper_get:
|
||
|
*
|
||
|
* Finds the address of the global #VteReaper object, creating the object if
|
||
|
* necessary.
|
||
|
*
|
||
|
* Returns: the global #VteReaper object, which should not be unreffed.
|
||
|
*/
|
||
|
VteReaper *
|
||
|
vte_reaper_get(void)
|
||
|
{
|
||
|
if (!VTE_IS_REAPER(singleton_reaper)) {
|
||
|
singleton_reaper = g_object_new(VTE_TYPE_REAPER, NULL);
|
||
|
}
|
||
|
return singleton_reaper;
|
||
|
}
|
||
|
|
||
|
#ifdef REAPER_MAIN
|
||
|
GMainContext *context;
|
||
|
GMainLoop *loop;
|
||
|
pid_t child;
|
||
|
|
||
|
static void
|
||
|
child_exited(GObject *object, int pid, int status, gpointer data)
|
||
|
{
|
||
|
g_print("[parent] Child with pid %d exited with code %d, "
|
||
|
"was waiting for %d.\n", pid, status, GPOINTER_TO_INT(data));
|
||
|
if (child == pid) {
|
||
|
g_print("[parent] Quitting.\n");
|
||
|
g_main_loop_quit(loop);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int
|
||
|
main(int argc, char **argv)
|
||
|
{
|
||
|
VteReaper *reaper;
|
||
|
pid_t p, q;
|
||
|
|
||
|
_vte_debug_parse_string(getenv("VTE_DEBUG_FLAGS"));
|
||
|
|
||
|
g_type_init();
|
||
|
context = g_main_context_default();
|
||
|
loop = g_main_loop_new(context, FALSE);
|
||
|
reaper = vte_reaper_get();
|
||
|
|
||
|
g_print("[parent] Forking.\n");
|
||
|
p = fork();
|
||
|
switch (p) {
|
||
|
case -1:
|
||
|
g_print("[parent] Fork failed.\n");
|
||
|
g_assert_not_reached();
|
||
|
break;
|
||
|
case 0:
|
||
|
g_print("[child] Going to sleep.\n");
|
||
|
sleep(10);
|
||
|
g_print("[child] Quitting.\n");
|
||
|
_exit(30);
|
||
|
break;
|
||
|
default:
|
||
|
g_print("[parent] Starting to wait for %d.\n", p);
|
||
|
child = p;
|
||
|
g_signal_connect(G_OBJECT(reaper),
|
||
|
"child-exited",
|
||
|
G_CALLBACK(child_exited),
|
||
|
GINT_TO_POINTER(child));
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
g_print("[parent] Forking.\n");
|
||
|
q = fork();
|
||
|
switch (q) {
|
||
|
case -1:
|
||
|
g_print("[parent] Fork failed.\n");
|
||
|
g_assert_not_reached();
|
||
|
break;
|
||
|
case 0:
|
||
|
g_print("[child] Going to sleep.\n");
|
||
|
sleep(5);
|
||
|
_exit(5);
|
||
|
break;
|
||
|
default:
|
||
|
g_print("[parent] Not waiting for %d.\n", q);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
|
||
|
g_main_loop_run(loop);
|
||
|
|
||
|
reaper = vte_reaper_get();
|
||
|
g_object_unref(VTE_REAPER(reaper));
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
#endif
|