From: lace <> Date: Tue, 10 Jul 2007 14:32:44 +0000 (+0000) Subject: 2006-11-30 X-Git-Url: https://git.jankratochvil.net/?p=nethome.git;a=commitdiff_plain;h=419f1f9c280d7b56293cb17ceab969ed32c9d2e1 2006-11-30 --- diff --git a/src/orphanripper.c b/src/orphanripper.c new file mode 100644 index 0000000..9613b92 --- /dev/null +++ b/src/orphanripper.c @@ -0,0 +1,219 @@ +/* + * Copyright 2006 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Reap any leftover children possibly holding file descriptors. + * 2006-11-30 Jan Kratochvil + */ + + +#define _XOPEN_SOURCE 1 +#define _XOPEN_SOURCE_EXTENDED 1 +#define _BSD_SOURCE 1 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static const char *progname; + +static int spawn (char **argv, pid_t *child_pointer) +{ + pid_t child, child_got; + int status; + + switch (child = fork ()) { + case -1: + perror ("fork(2)"); + exit (EXIT_FAILURE); + case 0: + /* Do not setpgrp(2) in the parent process as the process-group + is shared for the whole sh(1) pipeline we could be a part + of. The process-group is set according to PID of the first + command in the pipeline. + We would rip even vi(1) in the case of: + ./orphanripper sh -c 'sleep 1&' | vi - + */ + if (setpgrp ()) { + perror ("setpgrp"); + exit (EXIT_FAILURE); + } + execvp (argv[1], argv + 1); + perror ("execvp(2)"); + exit (EXIT_FAILURE); + default: + break; + } + if (child != (child_got = waitpid (child, &status, 0))) { + fprintf (stderr, "waitpid(%d)=%d: %m\n", (int) child, + (int) child_got); + exit (EXIT_FAILURE); + } + if (!WIFEXITED (status)) { + fprintf (stderr, "waitpid(2): !WIFEXITED(%d)\n", status); + exit (EXIT_FAILURE); + } + + if (child_pointer) + *child_pointer = child; + return WEXITSTATUS (status); +} + +/* Detected commandline may look weird due to a race: + Original command: + ./orphanripper sh -c 'sleep 1&' & + Correct output: + [1] 29610 + ./orphanripper: Killed -9 orphan PID 29612 (PGID 29611): sleep 1 + Raced output (sh(1) child still did not update its argv[]): + [1] 29613 + ./orphanripper: Killed -9 orphan PID 29615 (PGID 29614): sh -c sleep 1& + We could delay a bit before ripping the children. */ +static const char *read_cmdline (pid_t pid) +{ + char cmdline_fname[32]; + static char cmdline[LINE_MAX]; + int fd; + ssize_t got; + char *s; + + if (snprintf (cmdline_fname, sizeof (cmdline_fname), "/proc/%d/cmdline", + (int) pid) < 0) + return NULL; + fd = open (cmdline_fname, O_RDONLY); + if (fd == -1) { + fprintf (stderr, "%s: open (\"%s\"): %m\n", progname, + cmdline_fname); + return NULL; + } + got = read (fd, cmdline, sizeof (cmdline) - 1); + if (got == -1) + fprintf (stderr, "%s: read (\"%s\"): %m\n", progname, + cmdline_fname); + if (close (fd)) + fprintf (stderr, "%s: close (\"%s\"): %m\n", progname, + cmdline_fname); + if (got < 0) + return NULL; + /* Convert '\0' argument delimiters to spaces. */ + for (s = cmdline; s < cmdline + got; s++) + if (!*s) + *s = ' '; + /* Trim the trailing spaces (typically single '\0'->' '). */ + while (s > cmdline && isspace (s[-1])) + s--; + *s = 0; + return cmdline; +} + +static void rip_pid (pid_t pid, pid_t pgid_child) +{ + pid_t pgid_pids; + const char *cmdline; + + /* Don't shoot ourselves. */ + if (pid == getpid()) + return; + pgid_pids = getpgid (pid); + /* Ignore errors (permissions? untested). */ + if (pgid_pids == -1) + return; + /* Not a process of ours. */ + if (pgid_pids != pgid_child) + return; + + cmdline = read_cmdline (pid); + if (!cmdline) + cmdline = ""; + fprintf (stderr, "%s: Killed -9 orphan PID %d (PGID %d): %s\n", + progname, (int) pid, (int) pgid_pids, cmdline); + if (kill (pid, SIGKILL)) { + fprintf (stderr, "%s: kill (%d, SIGKILL): %m\n", progname, + (int) pid); + return; + } + /* Do not waitpid(2) as it cannot be our direct descendant and it gets + cleaned up by init(8). */ +#if 0 + pid_t pid_got; + if (pid != (pid_got = waitpid (pid, NULL, 0))) { + fprintf (stderr, "%s: waitpid (%d) != %d: %m\n", progname, + (int) pid, (int) pid_got); + return; + } +#endif +} + +static void rip (pid_t pgid_child) +{ + DIR *dir; + struct dirent *dirent; + + dir = opendir ("/proc"); + if (!dir) { + perror ("opendir (\"/proc\")"); + exit (EXIT_FAILURE); + } + while ((errno = 0, dirent = readdir (dir))) { + const char *cs; + + /* FIXME: POSIX portability. */ + if (dirent->d_type != DT_DIR) + continue; + /* Check /^\d+$/: */ + for (cs = dirent->d_name; *cs; cs++) + if (!isdigit (*cs)) + break; + if (cs == dirent->d_name || *cs) + continue; + rip_pid (atoi (dirent->d_name), pgid_child); + } + if (errno) { + perror ("readdir (\"/proc\")"); + exit (EXIT_FAILURE); + } + if (closedir (dir)) { + perror ("closedir (\"/proc\")"); + exit (EXIT_FAILURE); + } + +} + +int main (int argc, char **argv) +{ + int rc; + pid_t child; + + if (argc < 2 + || !strcmp (argv[1], "-h") + || !strcmp (argv[1], "--help")) { + fputs("Syntax: orphanripper \n", stdout); + exit (EXIT_FAILURE); + } + progname = argv[0]; + rc = spawn (argv, &child); + rip (child); + return rc; +}