aboutsummaryrefslogtreecommitdiff
path: root/src/rc
diff options
context:
space:
mode:
authorWilliam Hubbs <w.d.hubbs@gmail.com>2017-05-19 13:37:30 -0500
committerWilliam Hubbs <w.d.hubbs@gmail.com>2017-05-19 18:13:39 -0500
commit49b8a573a195f4b2cee992cd10678694da0a6f4f (patch)
tree63194fbb1766b2e9908f64defa08f7d700f41015 /src/rc
parenta2055af90054f5125cc07d4851b1dc9d16815e7c (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/rc')
-rw-r--r--src/rc/.gitignore1
-rw-r--r--src/rc/Makefile6
-rw-r--r--src/rc/kill_all.c250
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;
+}