/* * kill_all.c * Sends a signal to all processes on the system. */ /* * Copyright (c) 2017 The OpenRC Authors. * See the Authors file at the top-level directory of this distribution and * https://github.com/OpenRC/openrc/blob/master/AUTHORS * * This file is part of OpenRC. It is subject to the license terms in * the LICENSE file found in the top-level directory of this * distribution and at https://github.com/OpenRC/openrc/blob/master/LICENSE * This file may not be copied, modified, propagated, or distributed * except according to the terms contained in the LICENSE file. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "einfo.h" #include "rc.h" #include "rc-misc.h" #include "_usage.h" const char *applet = NULL; const char *extraopts = "[signal number]"; const char getoptstring[] = "do:" getoptstring_COMMON; const struct option longopts[] = { { "dry-run", 0, NULL, 'd' }, { "omit", 1, NULL, 'o' }, longopts_COMMON }; const char * const longopts_help[] = { "print what would be done", "omit this pid (can be repeated)", longopts_help_COMMON }; const char *usagestring = NULL; static int mount_proc(void) { pid_t pid; pid_t rc; int status; if (exists("/proc/version")) return 0; pid = fork(); switch (pid) { case -1: syslog(LOG_ERR, "Unable to fork"); return -1; break; case 0: /* attempt to mount /proc */ execlp("mount", "mount", "-t", "proc", "proc", "/proc", NULL); syslog(LOG_ERR, "Unable to execute mount"); exit(1); break; default: /* wait for child process */ while ((rc = wait(&status)) != pid) if (rc < 0 && errno == ECHILD) break; if (rc != pid || WEXITSTATUS(status) != 0) syslog(LOG_ERR, "mount returned non-zero exit status"); break; } if (! exists("/proc/version")) { syslog(LOG_ERR, "Could not mount /proc"); return -1; } return 0; } static bool is_user_process(pid_t pid) { char *buf = NULL; FILE *fp; char *path = NULL; pid_t temp_pid; size_t size; bool user_process = true; while (pid >0 && user_process) { if (pid == 2) { user_process = false; continue; } xasprintf(&path, "/proc/%d/status", pid); fp = fopen(path, "r"); free(path); /* * if we could not open the file, the process disappeared, which * leaves us no way to determine for sure whether it was a user * process or kernel thread, so we say it is a kernel thread to * avoid accidentally killing it. */ if (!fp) { user_process = false; continue; } temp_pid = -1; while (! feof(fp)) { buf = NULL; if (getline(&buf, &size, fp) != -1) { sscanf(buf, "PPid: %d", &temp_pid); free(buf); } else { free(buf); break; } } fclose(fp); if (temp_pid == -1) { syslog(LOG_ERR, "Unable to read pid from /proc/%d/status", pid); user_process = false; continue; } pid = temp_pid; } return user_process; } static int signal_processes(int sig, RC_STRINGLIST *omits, bool dryrun) { sigset_t signals; sigset_t oldsigs; DIR *dir; struct dirent *d; char *buf = NULL; pid_t pid; int sendcount = 0; kill(-1, SIGSTOP); sigfillset(&signals); sigemptyset(&oldsigs); sigprocmask(SIG_SETMASK, &signals, &oldsigs); /* * Open the /proc directory. * CWD must be /proc to avoid problems if / is affected by the killing * (i.e. depends on fuse). */ if (chdir("/proc") == -1) { syslog(LOG_ERR, "chdir /proc failed"); sigprocmask(SIG_SETMASK, &oldsigs, NULL); kill(-1, SIGCONT); return -1; } dir = opendir("."); if (!dir) { syslog(LOG_ERR, "cannot opendir(/proc)"); sigprocmask(SIG_SETMASK, &oldsigs, NULL); kill(-1, SIGCONT); return -1; } /* Walk through the directory. */ while ((d = readdir(dir)) != NULL) { /* Is this a process? */ pid = (pid_t) atoi(d->d_name); if (pid == 0) continue; /* Is this a process we have been requested to omit? */ if (buf) { free(buf); buf = NULL; } xasprintf(&buf, "%d", pid); if (rc_stringlist_find(omits, buf)) continue; /* Is this process in our session? */ if (getsid(getpid()) == getsid(pid)) continue; /* Is this a kernel thread? */ if (!is_user_process(pid)) continue; if (dryrun) einfo("Would send signal %d to process %d", sig, pid); else if (kill(pid, sig) == 0) sendcount++; } closedir(dir); sigprocmask(SIG_SETMASK, &oldsigs, NULL); kill(-1, SIGCONT); return sendcount; } int main(int argc, char **argv) { char *arg = NULL; int opt; bool dryrun = false; RC_STRINGLIST *omits = rc_stringlist_new(); int sig = SIGKILL; char *here; char *token; /* Ensure that we are only quiet when explicitly told to be */ unsetenv("EINFO_QUIET"); applet = basename_c(argv[0]); rc_stringlist_addu(omits, "1"); while ((opt = getopt_long(argc, argv, getoptstring, longopts, (int *) 0)) != -1) { switch (opt) { case 'd': dryrun = true; break; case 'o': here = optarg; while ((token = strsep(&here, ",;:"))) { if ((pid_t) atoi(token) > 0) rc_stringlist_addu(omits, token); else { eerror("Invalid omit pid value %s", token); usage(EXIT_FAILURE); } } break; case_RC_COMMON_GETOPT } } if (argc > optind) { arg = argv[optind]; sig = atoi(arg); if (sig <= 0 || sig > 31) { rc_stringlist_free(omits); eerror("Invalid signal %s", arg); usage(EXIT_FAILURE); } } openlog(applet, LOG_CONS|LOG_PID, LOG_DAEMON); if (mount_proc() != 0) { rc_stringlist_free(omits); eerrorx("Unable to mount /proc file system"); } signal_processes(sig, omits, dryrun); rc_stringlist_free(omits); return 0; }