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:
parent
a2055af900
commit
49b8a573a1
1
src/rc/.gitignore
vendored
1
src/rc/.gitignore
vendored
@ -62,3 +62,4 @@ openrc
|
|||||||
openrc-init
|
openrc-init
|
||||||
openrc-run
|
openrc-run
|
||||||
openrc-shutdown
|
openrc-shutdown
|
||||||
|
kill_all
|
||||||
|
@ -14,7 +14,7 @@ SRCS+= rc-selinux.c
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq (${OS},Linux)
|
ifeq (${OS},Linux)
|
||||||
SRCS+= openrc-init.c openrc-shutdown.c
|
SRCS+= kill_all.c openrc-init.c openrc-shutdown.c
|
||||||
endif
|
endif
|
||||||
|
|
||||||
CLEANFILES= version.h rc-selinux.o
|
CLEANFILES= version.h rc-selinux.o
|
||||||
@ -44,6 +44,7 @@ RC_SBINPROGS= mark_service_starting mark_service_started \
|
|||||||
rc-abort swclock
|
rc-abort swclock
|
||||||
|
|
||||||
ifeq (${OS},Linux)
|
ifeq (${OS},Linux)
|
||||||
|
RC_BINPROGS+= kill_all
|
||||||
SBINPROGS+= openrc-init openrc-shutdown
|
SBINPROGS+= openrc-init openrc-shutdown
|
||||||
endif
|
endif
|
||||||
|
|
||||||
@ -99,6 +100,9 @@ checkpath: rc-selinux.o
|
|||||||
endif
|
endif
|
||||||
${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD}
|
${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 \
|
einfon einfo ewarnn ewarn eerrorn eerror ebegin eend ewend \
|
||||||
eindent eoutdent esyslog eval_ecolors ewaitfile \
|
eindent eoutdent esyslog eval_ecolors ewaitfile \
|
||||||
veinfo vewarn vebegin veend vewend veindent veoutdent: do_e.o rc-misc.o
|
veinfo vewarn vebegin veend vewend veindent veoutdent: do_e.o rc-misc.o
|
||||||
|
250
src/rc/kill_all.c
Normal file
250
src/rc/kill_all.c
Normal 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;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user