diff options
| author | William Hubbs <w.d.hubbs@gmail.com> | 2017-05-19 13:37:30 -0500 | 
|---|---|---|
| committer | William Hubbs <w.d.hubbs@gmail.com> | 2017-05-19 18:13:39 -0500 | 
| commit | 49b8a573a195f4b2cee992cd10678694da0a6f4f (patch) | |
| tree | 63194fbb1766b2e9908f64defa08f7d700f41015 /src | |
| parent | a2055af90054f5125cc07d4851b1dc9d16815e7c (diff) | |
| download | openrc-49b8a573a195f4b2cee992cd10678694da0a6f4f.tar.xz | |
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')
| -rw-r--r-- | src/rc/.gitignore | 1 | ||||
| -rw-r--r-- | src/rc/Makefile | 6 | ||||
| -rw-r--r-- | src/rc/kill_all.c | 250 | 
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; +}  | 
