diff options
Diffstat (limited to 'src/kill_all')
-rw-r--r-- | src/kill_all/kill_all.c | 260 | ||||
-rw-r--r-- | src/kill_all/meson.build | 9 |
2 files changed, 269 insertions, 0 deletions
diff --git a/src/kill_all/kill_all.c b/src/kill_all/kill_all.c new file mode 100644 index 00000000..704f30b8 --- /dev/null +++ b/src/kill_all/kill_all.c @@ -0,0 +1,260 @@ +/* + * 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/HEAD/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/HEAD/LICENSE + * This file may not be copied, modified, propagated, or distributed + * except according to the terms contained in the LICENSE file. + */ + + +#include <dirent.h> +#include <errno.h> +#include <getopt.h> +#include <limits.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/wait.h> + +#include "einfo.h" +#include "rc.h" +#include "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; +} diff --git a/src/kill_all/meson.build b/src/kill_all/meson.build new file mode 100644 index 00000000..177b537e --- /dev/null +++ b/src/kill_all/meson.build @@ -0,0 +1,9 @@ +if os == 'Linux' + executable('kill_all', + ['kill_all.c', usage_c, version_h], + c_args : cc_branding_flags, + include_directories: [incdir, einfo_incdir, rc_incdir], + link_with: [libeinfo,librc], + install: true, + install_dir: rc_bindir) +endif |