aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikolay Nechaev <nikolay_nechaev@mail.ru>2023-06-15 09:38:35 +0300
committerGitHub <noreply@github.com>2023-06-15 08:38:35 +0200
commit3ae5f31d04bc78bf404447144841a488cdab06cf (patch)
treed82a01b9a444d33c1cf2a01a50633889dc356ba2
parenta95870120ce30843d95bdc5d87d26973789ed947 (diff)
downloadi3-3ae5f31d04bc78bf404447144841a488cdab06cf.tar.gz
i3-3ae5f31d04bc78bf404447144841a488cdab06cf.zip
Remove the double forking in start_application (#5510)
Having just a single fork is beneficial, as it preserves the approprate parent information for the children of i3, which is useful in some scenarious e.g. when a child wants to do something on the wm's exit (possible via `prctl(PR_SET_PDEATHSIG, ...)`). Moreover, this is a zero-cost benefit: i3 is already using libev with the default loop, which automatically reaps all the zombie children even when there is no corresponding event set. Resolves #5506
-rw-r--r--src/main.c6
-rw-r--r--src/startup.c28
2 files changed, 17 insertions, 17 deletions
diff --git a/src/main.c b/src/main.c
index f679966f..6fae7e41 100644
--- a/src/main.c
+++ b/src/main.c
@@ -579,7 +579,11 @@ int main(int argc, char *argv[]) {
/* Initialize the libev event loop. This needs to be done before loading
* the config file because the parser will install an ev_child watcher
- * for the nagbar when config errors are found. */
+ * for the nagbar when config errors are found.
+ *
+ * Main loop must be ev's default loop because (at the moment of writing)
+ * only the default loop can handle ev_child events and reap zombies
+ * (the start_application routine relies on that too). */
main_loop = EV_DEFAULT;
if (main_loop == NULL)
die("Could not initialize libev. Bad LIBEV_FLAGS?\n");
diff --git a/src/startup.c b/src/startup.c
index 9b0576e8..a34bd663 100644
--- a/src/startup.c
+++ b/src/startup.c
@@ -118,10 +118,9 @@ void startup_sequence_delete(struct Startup_Sequence *sequence) {
}
/*
- * Starts the given application by passing it through a shell. We use double
- * fork to avoid zombie processes. As the started application’s parent exits
- * (immediately), the application is reparented to init (process-id 1), which
- * correctly handles children, so we don’t have to do it :-).
+ * Starts the given application by passing it through a shell. Zombie processes
+ * will be collected by ev in the default loop, we don't have to manually
+ * deal with it.
*
* The shell used to start applications is the system's bourne shell (i.e.,
* /bin/sh).
@@ -173,7 +172,8 @@ void start_application(const char *command, bool no_startup_id) {
LOG("executing: %s\n", command);
if (fork() == 0) {
- /* Child process */
+ /* Child process.
+ * It will be reaped by ev, even though there is no corresponding ev_child */
setsid();
setrlimit(RLIMIT_CORE, &original_rlimit_core);
/* Close all socket activation file descriptors explicitly, we disabled
@@ -186,18 +186,14 @@ void start_application(const char *command, bool no_startup_id) {
unsetenv("LISTEN_PID");
unsetenv("LISTEN_FDS");
signal(SIGPIPE, SIG_DFL);
- if (fork() == 0) {
- /* Setup the environment variable(s) */
- if (!no_startup_id)
- sn_launcher_context_setup_child_process(context);
- setenv("I3SOCK", current_socketpath, 1);
-
- execl(_PATH_BSHELL, _PATH_BSHELL, "-c", command, NULL);
- /* not reached */
- }
- _exit(EXIT_SUCCESS);
+ /* Setup the environment variable(s) */
+ if (!no_startup_id)
+ sn_launcher_context_setup_child_process(context);
+ setenv("I3SOCK", current_socketpath, 1);
+
+ execl(_PATH_BSHELL, _PATH_BSHELL, "-c", command, NULL);
+ /* not reached */
}
- wait(0);
if (!no_startup_id) {
/* Change the pointer of the root window to indicate progress */