Skip to content

Commit 4fb4ab8

Browse files
committed
notify: use double-fork instead of ignoring SIGCHLD
This will allow us to properly check the exit code of the pre-notify script ( #348 ) which would not be possible when ignoring SIGCHLD. Ignoring SIGCHLD causes the kernel to auto-reap children and we cannot access the exit code.
1 parent d062b93 commit 4fb4ab8

File tree

2 files changed

+71
-44
lines changed

2 files changed

+71
-44
lines changed

kill.c

Lines changed: 71 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <sys/syscall.h> /* Definition of SYS_* constants */
1414
#include <time.h>
1515
#include <unistd.h>
16+
#include <wait.h>
1617

1718
#include "globals.h"
1819
#include "kill.h"
@@ -53,55 +54,84 @@ static bool isnumeric(char* str)
5354
}
5455
}
5556

56-
static void notify_dbus(const char* summary, const char* body)
57+
// Spawn a process handling notifications (-n or -N option) in the background
58+
// by double-forking.
59+
// The new process will have init as the parent and init will clean up the zombies.
60+
static void notify_spawn_background(const char* path, char* const argv[], const procinfo_t* victim)
5761
{
58-
int pid = fork();
59-
if (pid > 0) {
60-
// parent
62+
pid_t child_pid = fork();
63+
if (child_pid == 0) {
64+
// Inside child
65+
int grandchild_pid = fork();
66+
if (grandchild_pid == 0) {
67+
// Inside grandchild
68+
if (victim) {
69+
// Pass victim info via env
70+
char pid_str[UID_BUFSIZ] = { 0 };
71+
char uid_str[UID_BUFSIZ] = { 0 };
72+
73+
snprintf(pid_str, UID_BUFSIZ, "%d", victim->pid);
74+
snprintf(uid_str, UID_BUFSIZ, "%d", victim->uid);
75+
76+
setenv("EARLYOOM_PID", pid_str, 1);
77+
setenv("EARLYOOM_UID", uid_str, 1);
78+
setenv("EARLYOOM_NAME", victim->name, 1);
79+
setenv("EARLYOOM_CMDLINE", victim->cmdline, 1);
80+
}
81+
82+
debug("%s: exec %s\n", __func__, path);
83+
execv(path, argv);
84+
warn("%s: exec %s failed: %s\n", __func__, path, strerror(errno));
85+
exit(1);
86+
} else if (grandchild_pid == -1) {
87+
warn("%s: grandchild fork failed: %s\n", __func__, strerror(errno));
88+
}
89+
// Child exits immediately
90+
exit(0);
91+
} else if (child_pid > 0) {
92+
// Inside parent
93+
// Reap the child
94+
int ret = waitpid(child_pid, NULL, 0);
95+
if (ret == -1) {
96+
warn("%s: waitpid: %s\n", __func__, strerror(errno));
97+
}
98+
} else {
99+
warn("%s: child fork failed: %s\n", __func__, strerror(errno));
61100
return;
62101
}
63-
char summary2[1024] = { 0 };
64-
snprintf(summary2, sizeof(summary2), "string:%s", summary);
102+
}
103+
104+
// "-n" option
105+
static void notify_dbus(const char* body)
106+
{
65107
char body2[1024] = "string:";
66108
if (body != NULL) {
67109
snprintf(body2, sizeof(body2), "string:%s", body);
68110
}
69-
const char* dbus_send_path = "/usr/bin/dbus-send";
70-
debug("%s: exec %s\n", __func__, dbus_send_path);
111+
71112
// Complete command line looks like this:
72-
// dbus-send --system / net.nuetzlich.SystemNotifications.Notify 'string:summary text' 'string:and body text'
73-
execl(dbus_send_path, "dbus-send", "--system", "/", "net.nuetzlich.SystemNotifications.Notify",
74-
summary2, body2, NULL);
75-
warn("%s: exec failed: %s\n", __func__, strerror(errno));
76-
exit(1);
113+
// dbus-send --system / net.nuetzlich.SystemNotifications.Notify 'string:earlyoom' 'string:and body text'
114+
char* const argv[] = {
115+
"dbus-send",
116+
"--system",
117+
"/",
118+
"net.nuetzlich.SystemNotifications.Notify",
119+
"string:earlyoom",
120+
body2,
121+
NULL
122+
};
123+
const char* dbus_send_path = "/usr/bin/dbus-send";
124+
notify_spawn_background(dbus_send_path, argv, NULL);
77125
}
78126

79-
static void notify_ext(const char* script, const procinfo_t* victim)
127+
// "-N" option
128+
static void notify_ext(char* const script, const procinfo_t* victim)
80129
{
81-
pid_t pid1 = fork();
82-
83-
if (pid1 == -1) {
84-
warn("notify_ext: fork() returned -1: %s\n", strerror(errno));
85-
return;
86-
} else if (pid1 != 0) {
87-
return;
88-
}
89-
90-
char pid_str[UID_BUFSIZ] = { 0 };
91-
char uid_str[UID_BUFSIZ] = { 0 };
92-
93-
snprintf(pid_str, UID_BUFSIZ, "%d", victim->pid);
94-
snprintf(uid_str, UID_BUFSIZ, "%d", victim->uid);
95-
96-
setenv("EARLYOOM_PID", pid_str, 1);
97-
setenv("EARLYOOM_UID", uid_str, 1);
98-
setenv("EARLYOOM_NAME", victim->name, 1);
99-
setenv("EARLYOOM_CMDLINE", victim->cmdline, 1);
100-
101-
debug("%s: exec %s\n", __func__, script);
102-
execl(script, script, NULL);
103-
warn("%s: exec %s failed: %s\n", __func__, script, strerror(errno));
104-
exit(1);
130+
char* const argv[] = {
131+
script,
132+
NULL
133+
};
134+
notify_spawn_background(script, argv, victim);
105135
}
106136

107137
static void notify_process_killed(const poll_loop_args_t* args, const procinfo_t* victim)
@@ -131,7 +161,7 @@ static void notify_process_killed(const poll_loop_args_t* args, const procinfo_t
131161
char notif_args[PATH_MAX + 1000];
132162
snprintf(notif_args, sizeof(notif_args),
133163
"Low memory! Killing process %d %s", victim->pid, victim->name);
134-
notify_dbus("earlyoom", notif_args);
164+
notify_dbus(notif_args);
135165
}
136166
if (args->notify_ext) {
137167
notify_ext(args->notify_ext, victim);
@@ -523,7 +553,7 @@ void kill_process(const poll_loop_args_t* args, int sig, const procinfo_t* victi
523553
if (victim->pid <= 0) {
524554
warn("Could not find a process to kill. Sleeping 1 second.\n");
525555
if (args->notify) {
526-
notify_dbus("earlyoom", "Error: Could not find a process to kill. Sleeping 1 second.");
556+
notify_dbus("Error: Could not find a process to kill. Sleeping 1 second.");
527557
}
528558
sleep(1);
529559
return;
@@ -560,7 +590,7 @@ void kill_process(const poll_loop_args_t* args, int sig, const procinfo_t* victi
560590
if (res != 0) {
561591
warn("kill failed: %s\n", strerror(saved_errno));
562592
if (args->notify) {
563-
notify_dbus("earlyoom", "Error: Failed to kill process");
593+
notify_dbus("Error: Failed to kill process");
564594
}
565595
// Killing the process may have failed because we are not running as root.
566596
// In that case, trying again in 100ms will just yield the same error.

main.c

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,6 @@ int main(int argc, char* argv[])
126126
* may lag behind stderr */
127127
setlinebuf(stdout);
128128

129-
/* clean up dbus-send zombies */
130-
signal(SIGCHLD, SIG_IGN);
131-
132129
fprintf(stderr, "earlyoom " VERSION "\n");
133130

134131
if (chdir(procdir_path) != 0) {

0 commit comments

Comments
 (0)