Reap children even across a restart

In Unix, so that a process can learn about the exit status of the child
processes that it started, children become zombie processes until the
parents collects their exit information. We use glib both for starting
and for collecting processes. However, when awesome is restarted, the
new instance inherits children, but does not know about them and does
not inherit them.

Fix this by explicitly tracking a list of running child processes and by
serialising them across a restart via an environment variable. The new
awesome instance can then watch for these child processes, but besides
that it ignores them and does not use their exit status in any way.

Thanks to Colin Walters for the hint with serialising the list of processes.

Fixes: https://github.com/awesomeWM/awesome/issues/1193
Signed-off-by: Uli Schlachter <psychon@znc.in>
This commit is contained in:
Uli Schlachter 2017-06-01 15:47:24 +02:00
parent 0707f52ebf
commit e54361a374
3 changed files with 64 additions and 1 deletions

View file

@ -115,6 +115,8 @@ awesome_atexit(bool restart)
/* Disconnect *after* closing lua */
xcb_cursor_context_free(globalconf.cursor_ctx);
xcb_disconnect(globalconf.connection);
spawn_before_exit(restart);
}
/** Restore the client order after a restart */

62
spawn.c
View file

@ -62,6 +62,12 @@
/** 20 seconds timeout */
#define AWESOME_SPAWN_TIMEOUT 20.0
static int
compare_pids(const void *a, const void *b)
{
return *(GPid *) a - *(GPid *)b;
}
/** Wrapper for unrefing startup sequence.
*/
static inline void
@ -73,7 +79,11 @@ a_sn_startup_sequence_unref(SnStartupSequence **sss)
DO_ARRAY(SnStartupSequence *, SnStartupSequence, a_sn_startup_sequence_unref)
/** The array of startup sequence running */
SnStartupSequence_array_t sn_waits;
static SnStartupSequence_array_t sn_waits;
DO_BARRAY(GPid, pid, DO_NOTHING, compare_pids)
static pid_array_t running_children;
/** Remove a SnStartupSequence pointer from an array and forget about it.
* \param s The startup sequence to found, remove and unref.
@ -256,6 +266,20 @@ spawn_start_notify(client_t *c, const char * startup_id)
}
}
static void
remove_running_child(GPid pid, gint status, gpointer user_data)
{
(void) status;
(void) user_data;
GPid *pid_in_array = pid_array_lookup(&running_children, &pid);
if (pid_in_array != NULL) {
pid_array_remove(&running_children, pid_in_array);
} else {
warn("(Partially) unknown child %d exited?!", (int) pid);
}
}
/** Initialize program spawner.
*/
void
@ -267,6 +291,40 @@ spawn_init(void)
globalconf.default_screen,
spawn_monitor_event,
NULL, NULL);
const char* children = getenv("AWESOME_RUNNING_CHILDREN");
while (children != NULL) {
int pid, length;
if (sscanf(children, "%d%n", &pid, &length) != 1)
break;
children += length;
if (*children == ',')
children++;
pid_array_insert(&running_children, pid);
g_child_watch_add((GPid) pid, remove_running_child, NULL);
}
unsetenv("AWESOME_RUNNING_CHILDREN");
}
/** Called right before exit, serialise state in case of a restart.
*/
void
spawn_before_exit(bool restart)
{
if (!restart)
return;
buffer_t buffer;
buffer_init(&buffer);
foreach(pid, running_children) {
if(buffer.len != 0)
buffer_addc(&buffer, ',');
buffer_addf(&buffer, "%d", (int) *pid);
}
if(buffer.len != 0)
setenv("AWESOME_RUNNING_CHILDREN", buffer.s, 1);
buffer_wipe(&buffer);
}
static gboolean
@ -345,6 +403,7 @@ child_exit_callback(GPid pid, gint status, gpointer user_data)
{
lua_State *L = globalconf_get_lua_State();
int exit_callback = GPOINTER_TO_INT(user_data);
remove_running_child(pid, status, user_data);
/* 'Decode' the exit status */
if (WIFEXITED(status)) {
@ -458,6 +517,7 @@ luaA_spawn(lua_State *L)
int exit_callback = LUA_REFNIL;
/* Only do this down here to avoid leaks in case of errors */
luaA_registerfct(L, 6, &exit_callback);
pid_array_insert(&running_children, pid);
g_child_watch_add(pid, child_exit_callback, GINT_TO_POINTER(exit_callback));
}

View file

@ -27,6 +27,7 @@
#include <lua.h>
void spawn_init(void);
void spawn_before_exit(bool);
void spawn_start_notify(client_t *, const char *);
int luaA_spawn(lua_State *);