From 58c696e88cfeb6d97d836b9f328d683b0e187d65 Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Tue, 2 Dec 2003 17:20:39 +0000 Subject: Initial Import --- src/killjail.c | 387 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 387 insertions(+) create mode 100644 src/killjail.c (limited to 'src/killjail.c') diff --git a/src/killjail.c b/src/killjail.c new file mode 100644 index 0000000..2b4eb7d --- /dev/null +++ b/src/killjail.c @@ -0,0 +1,387 @@ +/* +// AUTHOR +// N. Nielsen +// +// LICENSE +// This software is in the public domain. +// +// The software is provided "as is", without warranty of any kind, +// express or implied, including but not limited to the warranties +// of merchantability, fitness for a particular purpose, and +// noninfringement. In no event shall the author(s) be liable for any +// claim, damages, or other liability, whether in an action of +// contract, tort, or otherwise, arising from, out of, or in connection +// with the software or the use or other dealings in the software. +// +// SUPPORT +// Send bug reports to: +// +// CHANGES +// 0.5.1 kvm_openfiles uses /dev/null +// +// 0.5.3 +// Added quiet option +// Added force option +*/ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "getjail.h" + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + +/* The big long stop process */ +static int stopJail(char* jailName, int force); + +/* Signals the jailer for various requests */ +static int signalJail(char* jailName, int signal); + +static void killProcesses(pid_t* pids, int signal); +static int getJailProcesses(const char* jailName, pid_t* pidJailer, pid_t** pids); + +static void usage(); + + +/* The timeout to wait between kills */ +#define DEFAULT_TIMEOUT 10 +int g_timeout = DEFAULT_TIMEOUT; + +/* To find the jailer process look for this command */ +#define JAILER_COMMAND "jailer" + +/* Supress warnings */ +int g_quiet = 0; + +int main(int argc, char* argv[]) +{ + /* If this gets set then only signal jailer, no kill */ + int signal = 0; + int ch = 0; + int force = 0; + int ret = 0; + + while((ch = getopt(argc, argv, "fhqrt:")) != -1) + { + switch(ch) + { + /* Force jail to shutdown */ + case 'f': + force = 1; + break; + + case 'q': + g_quiet = 1; + break; + + /* Send halt request to jailer */ + case 'h': + signal = SIGQUIT; + break; + + /* Send restart request to jailer */ + case 'r': + signal = SIGHUP; + break; + + /* Timeout to use between kills */ + case 't': + g_timeout = atoi(optarg); + if(g_timeout <= 0) + errx(1, "invalid timeout argument: %s", optarg); + break; + + case '?': + default: + usage(); + } + } + + argc -= optind; + argv += optind; + + /* Make sure we have a jailName */ + if(argc == 0) + usage(); + + /* For each jail */ + while(argc > 0) + { + /* If a signal option was set above then signal, + otherwise kill */ + if(signal == 0) + { + if(stopJail(*argv, force) != 0) + ret = 1; + } + else + { + if(force) + errx(1, "-f option incompatible with -r or -h"); + + if(signalJail(*argv, signal) != 0) + ret = 1; + } + + argc--; + argv++; + } + + return ret; +} + + +int signalJail(char* jailName, int signal) +{ + pid_t jailerPid = 0; + + /* Only ask for jailer pid */ + getJailProcesses(jailName, &jailerPid, NULL); + + if(jailerPid == 0) + { + warnx("%s: jailer not running in jail", jailName); + return 1; + } + + if(kill(jailerPid, signal) < 0) + err(1, "%s: couldn't signal jailer", jailName); + + return 0; +} + + +int stopJail(char* jailName, int force) +{ + pid_t jailerPid = 0; + pid_t* jailProcesses = NULL; + int pass = 0; + int timeout = 0; + int ret = 0; + + /* + * Multiple passes are used to do different things. + * Each time the jails processes are listed. + */ + while(ret == 0 && + getJailProcesses(jailName, &jailerPid, &jailProcesses)) + { + + if(timeout > 0) + { + sleep(1); + timeout--; + } + + else + { + + switch(pass) + { + + /* First pass is killing the jailer */ + case 0: + + if(jailerPid == 0) + { + /* No jailer */ + if(!g_quiet) + warnx("%s: jailer not running in jail", jailName); + } + + else + { + if(kill(jailerPid, SIGTERM) < 0 && errno != ESRCH) + err(1, "%s: couldn't signal jailer:", jailName); + + else + timeout = g_timeout; + } + + break; + + + /* Okay now quit all processes in jail */ + case 1: + + /* If we get here, jailer looks like it's irresponsive */ + if(jailerPid != 0 && !g_quiet) + warnx("%s: jailer (pid %d) won't quit. terminating jail...", jailName, jailerPid); + + + killProcesses(jailProcesses, SIGTERM); + timeout = g_timeout; + break; + + + /* Okay now we force kill the processes if necessary */ + case 2: + + if(force) + { + /* If we get here, jailer looks like it's really irresponsive */ + if(!g_quiet) + warnx("%s: jail won't stop. forcing jail termination...", jailName); + + killProcesses(jailProcesses, SIGKILL); + timeout = g_timeout; + } + + break; + + + /* And if that didn't do it, well then give up */ + case 3: + + if(!g_quiet) + warnx("%s: couldn't stop jail, processes wouldn't die", jailName); + + ret = 1; + break; + + } + + pass++; + } + + if(jailProcesses) + free(jailProcesses); + + } + + if(pass == 0) + { + if(!g_quiet) + warnx("%s: jail not running", jailName); + + ret = 1; + } + + return ret; +} + +void killProcesses(pid_t* pids, int signal) +{ + /* Note that we assume pids is null terminated + this is what getJailProcesses returns */ + + while(*pids) + { + if(kill(*pids, signal) < 0) + { + /* We ignore missing process errors */ + if(errno != ESRCH) + err(1, "couldn't kill process: %d", *pids); + } + + pids++; + } +} + +int getJailProcesses(const char* jailName, pid_t* pidJailer, pid_t** pids) +{ + kvm_t *kd; + struct kinfo_proc* kp; + char errbuf[_POSIX2_LINE_MAX]; + char pidJail[JAIL_BUFF_SIZE]; + int nentries, i, j; + + /* Open the kernel interface */ + kd = kvm_openfiles(_PATH_DEVNULL, _PATH_DEVNULL, _PATH_DEVNULL, + O_RDONLY, errbuf); + if(kd == 0) + errx(1, "%s", errbuf); + + /* Get a process listing */ + if((kp = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nentries)) == 0) + errx(1, "%s", kvm_geterr(kd)); + + /* Allocate memory */ + if(pids) + { + if((*pids = (pid_t*)malloc((nentries + 1) * sizeof(pid_t))) == NULL) + err(1, "out of memory"); + } + + /* Okay now loop and look at each process' jail */ + for(i = 0, j = 0; i < nentries; i++) + { + pid_t pid; + +#if __FreeBSD_version > 500000 + + /* Check the flags first */ + if(!(kp[i].ki_flag & P_JAILED)) + continue; + + pid = kp[i].ki_pid; + +#else + + /* Check the flags first */ + if(!(kp[i].kp_proc.p_flag & P_JAILED)) + continue; + + pid = kp[i].kp_proc.p_pid; + +#endif + + /* Now actually get the jail name */ + if(getpidjail(pid, pidJail) < 0) + continue; + + if(strcmp(pidJail, jailName)) + continue; + + /* Copy the PID over */ + if(pids) + (*pids)[j++] = pid; + + /* If it's the jailer then copy that */ + if(pidJailer) + { +#if __FreeBSD_version > 500000 + if(strstr(kp[i].ki_comm, JAILER_COMMAND)) +#else + if(strstr(kp[i].kp_proc.p_comm, JAILER_COMMAND)) +#endif + *pidJailer = pid; + } + + } + + /* Null terminate pids array */ + if(pids) + (*pids)[j] = 0; + + kvm_close(kd); + + return j == 0 ? 0 : 1; +} + + + +static void usage() +{ + fprintf(stderr, "usage: killjail [ -h | -r ] [ -t timeout ] [ -qf ] jailname ...\n"); + exit(2); +} + -- cgit v1.2.3