|
13 | 13 | #include <sys/syscall.h> /* Definition of SYS_* constants */ |
14 | 14 | #include <time.h> |
15 | 15 | #include <unistd.h> |
| 16 | +#include <wait.h> |
16 | 17 |
|
17 | 18 | #include "globals.h" |
18 | 19 | #include "kill.h" |
@@ -53,55 +54,84 @@ static bool isnumeric(char* str) |
53 | 54 | } |
54 | 55 | } |
55 | 56 |
|
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) |
57 | 61 | { |
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)); |
61 | 100 | return; |
62 | 101 | } |
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 | +{ |
65 | 107 | char body2[1024] = "string:"; |
66 | 108 | if (body != NULL) { |
67 | 109 | snprintf(body2, sizeof(body2), "string:%s", body); |
68 | 110 | } |
69 | | - const char* dbus_send_path = "/usr/bin/dbus-send"; |
70 | | - debug("%s: exec %s\n", __func__, dbus_send_path); |
| 111 | + |
71 | 112 | // 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); |
77 | 125 | } |
78 | 126 |
|
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) |
80 | 129 | { |
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); |
105 | 135 | } |
106 | 136 |
|
107 | 137 | 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 |
131 | 161 | char notif_args[PATH_MAX + 1000]; |
132 | 162 | snprintf(notif_args, sizeof(notif_args), |
133 | 163 | "Low memory! Killing process %d %s", victim->pid, victim->name); |
134 | | - notify_dbus("earlyoom", notif_args); |
| 164 | + notify_dbus(notif_args); |
135 | 165 | } |
136 | 166 | if (args->notify_ext) { |
137 | 167 | 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 |
523 | 553 | if (victim->pid <= 0) { |
524 | 554 | warn("Could not find a process to kill. Sleeping 1 second.\n"); |
525 | 555 | 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."); |
527 | 557 | } |
528 | 558 | sleep(1); |
529 | 559 | return; |
@@ -560,7 +590,7 @@ void kill_process(const poll_loop_args_t* args, int sig, const procinfo_t* victi |
560 | 590 | if (res != 0) { |
561 | 591 | warn("kill failed: %s\n", strerror(saved_errno)); |
562 | 592 | if (args->notify) { |
563 | | - notify_dbus("earlyoom", "Error: Failed to kill process"); |
| 593 | + notify_dbus("Error: Failed to kill process"); |
564 | 594 | } |
565 | 595 | // Killing the process may have failed because we are not running as root. |
566 | 596 | // In that case, trying again in 100ms will just yield the same error. |
|
0 commit comments