2 * Copyright 2006 Free Software Foundation, Inc.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 * Reap any leftover children possibly holding file descriptors.
19 * 2006-12-12 Jan Kratochvil <jan.kratochvil@redhat.com>
23 #define _XOPEN_SOURCE 1
24 #define _XOPEN_SOURCE_EXTENDED 1
29 #include <sys/types.h>
40 static const char *progname;
42 static int spawn (char **argv, pid_t *child_pointer)
44 pid_t child, child_got;
47 switch (child = fork ()) {
52 /* Do not setpgrp(2) in the parent process as the process-group
53 is shared for the whole sh(1) pipeline we could be a part
54 of. The process-group is set according to PID of the first
55 command in the pipeline.
56 We would rip even vi(1) in the case of:
57 ./orphanripper sh -c 'sleep 1&' | vi -
59 /* Do not setpgrp(2) as our pty would not be ours and we would
60 get `SIGSTOP' later, particularly after spawning gdb(1). */
61 if (getpid() != setsid ()) {
65 execvp (argv[1], argv + 1);
71 if (child != (child_got = waitpid (child, &status, 0))) {
72 fprintf (stderr, "waitpid(%d)=%d: %m\n", (int) child,
76 if (!WIFEXITED (status)) {
77 fprintf (stderr, "waitpid(2): !WIFEXITED(%d)\n", status);
82 *child_pointer = child;
83 return WEXITSTATUS (status);
86 /* Detected commandline may look weird due to a race:
88 ./orphanripper sh -c 'sleep 1&' &
91 ./orphanripper: Killed -9 orphan PID 29612 (PGID 29611): sleep 1
92 Raced output (sh(1) child still did not update its argv[]):
94 ./orphanripper: Killed -9 orphan PID 29615 (PGID 29614): sh -c sleep 1&
95 We could delay a bit before ripping the children. */
96 static const char *read_cmdline (pid_t pid)
98 char cmdline_fname[32];
99 static char cmdline[LINE_MAX];
104 if (snprintf (cmdline_fname, sizeof (cmdline_fname), "/proc/%d/cmdline",
107 fd = open (cmdline_fname, O_RDONLY);
109 fprintf (stderr, "%s: open (\"%s\"): %m\n", progname,
113 got = read (fd, cmdline, sizeof (cmdline) - 1);
115 fprintf (stderr, "%s: read (\"%s\"): %m\n", progname,
118 fprintf (stderr, "%s: close (\"%s\"): %m\n", progname,
122 /* Convert '\0' argument delimiters to spaces. */
123 for (s = cmdline; s < cmdline + got; s++)
126 /* Trim the trailing spaces (typically single '\0'->' '). */
127 while (s > cmdline && isspace (s[-1]))
133 static void rip_pid (pid_t pid, pid_t pgid_child)
138 /* Don't shoot ourselves. */
141 pgid_pids = getpgid (pid);
142 /* Ignore errors (permissions? untested). */
145 /* Not a process of ours. */
146 if (pgid_pids != pgid_child)
149 cmdline = read_cmdline (pid);
152 fprintf (stderr, "%s: Killed -9 orphan PID %d (PGID %d): %s\n",
153 progname, (int) pid, (int) pgid_pids, cmdline);
154 if (kill (pid, SIGKILL)) {
155 fprintf (stderr, "%s: kill (%d, SIGKILL): %m\n", progname,
159 /* Do not waitpid(2) as it cannot be our direct descendant and it gets
160 cleaned up by init(8). */
163 if (pid != (pid_got = waitpid (pid, NULL, 0))) {
164 fprintf (stderr, "%s: waitpid (%d) != %d: %m\n", progname,
165 (int) pid, (int) pid_got);
171 static void rip (pid_t pgid_child)
174 struct dirent *dirent;
176 dir = opendir ("/proc");
178 perror ("opendir (\"/proc\")");
181 while ((errno = 0, dirent = readdir (dir))) {
184 /* FIXME: POSIX portability. */
185 if (dirent->d_type != DT_DIR)
188 for (cs = dirent->d_name; *cs; cs++)
191 if (cs == dirent->d_name || *cs)
193 rip_pid (atoi (dirent->d_name), pgid_child);
196 perror ("readdir (\"/proc\")");
199 if (closedir (dir)) {
200 perror ("closedir (\"/proc\")");
206 int main (int argc, char **argv)
212 || !strcmp (argv[1], "-h")
213 || !strcmp (argv[1], "--help")) {
214 fputs("Syntax: orphanripper <execvp(3) commandline>\n", stdout);
218 rc = spawn (argv, &child);