diff options
author | William Hubbs <w.d.hubbs@gmail.com> | 2017-05-19 13:37:30 -0500 |
---|---|---|
committer | William Hubbs <w.d.hubbs@gmail.com> | 2017-05-19 18:13:39 -0500 |
commit | 49b8a573a195f4b2cee992cd10678694da0a6f4f (patch) | |
tree | 63194fbb1766b2e9908f64defa08f7d700f41015 /src | |
parent | a2055af90054f5125cc07d4851b1dc9d16815e7c (diff) |
add kill_all helper
This is similar to the sysvinit killall5 utility. It should only be used
in service scripts, so it will not be installed in the path.
This closes #129.
Diffstat (limited to 'src')
-rw-r--r-- | src/rc/.gitignore | 1 | ||||
-rw-r--r-- | src/rc/Makefile | 6 | ||||
-rw-r--r-- | src/rc/kill_all.c | 250 |
3 files changed, 256 insertions, 1 deletions
diff --git a/src/rc/.gitignore b/src/rc/.gitignore index c3e7b3f6..91d57075 100644 --- a/src/rc/.gitignore +++ b/src/rc/.gitignore @@ -62,3 +62,4 @@ openrc openrc-init openrc-run openrc-shutdown +kill_all diff --git a/src/rc/Makefile b/src/rc/Makefile index 2bc03f76..5874ed17 100644 --- a/src/rc/Makefile +++ b/src/rc/Makefile @@ -14,7 +14,7 @@ SRCS+= rc-selinux.c endif ifeq (${OS},Linux) -SRCS+= openrc-init.c openrc-shutdown.c +SRCS+= kill_all.c openrc-init.c openrc-shutdown.c endif CLEANFILES= version.h rc-selinux.o @@ -44,6 +44,7 @@ RC_SBINPROGS= mark_service_starting mark_service_started \ rc-abort swclock ifeq (${OS},Linux) +RC_BINPROGS+= kill_all SBINPROGS+= openrc-init openrc-shutdown endif @@ -99,6 +100,9 @@ checkpath: rc-selinux.o endif ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} +kill_all: kill_all.o _usage.o + ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} + einfon einfo ewarnn ewarn eerrorn eerror ebegin eend ewend \ eindent eoutdent esyslog eval_ecolors ewaitfile \ veinfo vewarn vebegin veend vewend veindent veoutdent: do_e.o rc-misc.o diff --git a/src/rc/kill_all.c b/src/rc/kill_all.c new file mode 100644 index 00000000..a4b15c87 --- /dev/null +++ b/src/rc/kill_all.c @@ -0,0 +1,250 @@ +/* + * 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 <dirent.h> +#include <errno.h> +#include <getopt.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 "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 */ + execl("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[PATH_MAX+1]; + FILE *fp; + char path[PATH_MAX+1]; + pid_t temp_pid; + bool user_process = true; + + while (pid >0 && user_process) { + if (pid == 2) { + user_process = false; + continue; + } + snprintf(path, sizeof(path), "/proc/%d/status", pid); + fp = fopen(path, "r"); + /* + * 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[0] = 0; + if (fgets(buf, sizeof(buf), fp)) + sscanf(buf, "PPid: %d", &temp_pid); + else + 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[PATH_MAX+1]; + 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? */ + sprintf(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; +} |