commit:     49b8a573a195f4b2cee992cd10678694da0a6f4f
Author:     William Hubbs <w.d.hubbs <AT> gmail <DOT> com>
AuthorDate: Fri May 19 18:37:30 2017 +0000
Commit:     William Hubbs <williamh <AT> gentoo <DOT> org>
CommitDate: Fri May 19 23:13:39 2017 +0000
URL:        https://gitweb.gentoo.org/proj/openrc.git/commit/?id=49b8a573

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.

 src/rc/.gitignore |   1 +
 src/rc/Makefile   |   6 +-
 src/rc/kill_all.c | 250 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 256 insertions(+), 1 deletion(-)

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;
+}

Reply via email to