/*
* Copyright (C) 2002 Red Hat, Inc.
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see .
*/
#include "config.h"
#include "debug.h"
#include "reaper.hh"
struct _VteReaper {
GObject parent_instance;
};
struct _VteReaperClass {
GObjectClass parent_class;
};
typedef struct _VteReaperClass VteReaperClass;
#define VTE_TYPE_REAPER (vte_reaper_get_type())
#define VTE_REAPER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), VTE_TYPE_REAPER, VteReaper))
#define VTE_REAPER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), VTE_TYPE_REAPER, VteReaperClass))
#define VTE_IS_REAPER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), VTE_TYPE_REAPER))
#define VTE_IS_REAPER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), VTE_TYPE_REAPER))
#define VTE_REAPER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), VTE_TYPE_REAPER, VteReaperClass))
static GType vte_reaper_get_type(void);
G_DEFINE_TYPE(VteReaper, vte_reaper, G_TYPE_OBJECT)
static VteReaper *singleton_reaper = nullptr;
static void
vte_reaper_child_watch_cb(GPid pid,
int status,
gpointer data)
{
_vte_debug_print(VTE_DEBUG_SIGNALS,
"Reaper emitting child-exited signal.\n");
g_signal_emit_by_name(data, "child-exited", pid, status);
g_spawn_close_pid (pid);
}
/*
* vte_reaper_add_child:
* @pid: the ID of a child process which will be monitored
*
* Ensures that child-exited signals will be emitted when @pid exits.
*/
void
vte_reaper_add_child(GPid pid)
{
g_child_watch_add_full(G_PRIORITY_LOW,
pid,
vte_reaper_child_watch_cb,
vte_reaper_ref(),
(GDestroyNotify)g_object_unref);
}
static void
vte_reaper_init(VteReaper *reaper)
{
}
static GObject*
vte_reaper_constructor (GType type,
guint n_construct_properties,
GObjectConstructParam *construct_properties)
{
if (singleton_reaper) {
return (GObject*)g_object_ref (singleton_reaper);
} else {
GObject *obj;
obj = G_OBJECT_CLASS (vte_reaper_parent_class)->constructor (type, n_construct_properties, construct_properties);
singleton_reaper = VTE_REAPER (obj);
return obj;
}
}
static void
vte_reaper_finalize(GObject *reaper)
{
G_OBJECT_CLASS(vte_reaper_parent_class)->finalize(reaper);
singleton_reaper = nullptr;
}
static void
vte_reaper_class_init(VteReaperClass *klass)
{
GObjectClass *gobject_class;
/*
* VteReaper::child-exited:
* @vtereaper: the object which received the signal
* @arg1: the process ID of the exited child
* @arg2: the status of the exited child, as returned by waitpid()
*
* Emitted when the #VteReaper object detects that a child of the
* current process has exited.
*/
g_signal_new(g_intern_static_string("child-exited"),
G_OBJECT_CLASS_TYPE(klass),
G_SIGNAL_RUN_LAST,
0,
nullptr,
nullptr,
g_cclosure_marshal_generic,
G_TYPE_NONE,
2,
G_TYPE_INT, G_TYPE_INT);
gobject_class = G_OBJECT_CLASS(klass);
gobject_class->constructor = vte_reaper_constructor;
gobject_class->finalize = vte_reaper_finalize;
}
/*
* vte_reaper_ref:
*
* Finds the address of the global #VteReaper object, creating the object if
* necessary.
*
* Returns: a reference to the global #VteReaper object, which
* must be unreffed when done with it
*/
VteReaper *
vte_reaper_ref(void)
{
return (VteReaper*)g_object_new(VTE_TYPE_REAPER, nullptr);
}
#ifdef MAIN
#include
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_init();
context = g_main_context_default();
loop = g_main_loop_new(context, FALSE);
reaper = vte_reaper_ref();
g_print("[parent] Forking1.\n");
p = fork();
switch (p) {
case -1:
g_print("[parent] Fork1 failed.\n");
g_assert_not_reached();
break;
case 0:
g_print("[child1] Going to sleep.\n");
sleep(10);
g_print("[child1] Quitting.\n");
_exit(30);
break;
default:
g_print("[parent] Starting to wait for %d.\n", p);
vte_reaper_add_child(p);
child = p;
g_signal_connect(reaper,
"child-exited",
G_CALLBACK(child_exited),
GINT_TO_POINTER(child));
break;
}
g_print("[parent] Forking2.\n");
q = fork();
switch (q) {
case -1:
g_print("[parent] Fork2 failed.\n");
g_assert_not_reached();
break;
case 0:
g_print("[child2] Going to sleep.\n");
sleep(5);
g_print("[child2] Quitting.\n");
_exit(5);
break;
default:
g_print("[parent] Not waiting for %d.\n", q);
break;
}
g_main_loop_run(loop);
g_object_unref(reaper);
return 0;
}
#endif