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.
This commit is contained in:
William Hubbs 2017-05-19 13:37:30 -05:00
parent a2055af900
commit 49b8a573a1
3 changed files with 256 additions and 1 deletions

1
src/rc/.gitignore vendored
View File

@ -62,3 +62,4 @@ openrc
openrc-init
openrc-run
openrc-shutdown
kill_all

View File

@ -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

250
src/rc/kill_all.c Normal file
View File

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