Changeset View
Changeset View
Standalone View
Standalone View
contrib/openrc/src/rc/kill_all.c
- This file was added.
Property | Old Value | New Value |
---|---|---|
svn:eol-style | null | native \ No newline at end of property |
svn:keywords | null | FreeBSD=%H \ No newline at end of property |
svn:mime-type | null | text/plain \ No newline at end of property |
/* | |||||
* 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 <limits.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 = NULL; | |||||
FILE *fp; | |||||
char *path = NULL; | |||||
pid_t temp_pid; | |||||
size_t size; | |||||
bool user_process = true; | |||||
while (pid >0 && user_process) { | |||||
if (pid == 2) { | |||||
user_process = false; | |||||
continue; | |||||
} | |||||
xasprintf(&path, "/proc/%d/status", pid); | |||||
fp = fopen(path, "r"); | |||||
free(path); | |||||
/* | |||||
* 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 = NULL; | |||||
if (getline(&buf, &size, fp) != -1) { | |||||
sscanf(buf, "PPid: %d", &temp_pid); | |||||
free(buf); | |||||
} else { | |||||
free(buf); | |||||
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 = NULL; | |||||
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? */ | |||||
if (buf) { | |||||
free(buf); | |||||
buf = NULL; | |||||
} | |||||
xasprintf(&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; | |||||
} |