summaryrefslogtreecommitdiff
path: root/srcx/jkill.c
diff options
context:
space:
mode:
authorStef Walter <stef@memberwebs.com>2004-05-17 17:52:45 +0000
committerStef Walter <stef@memberwebs.com>2004-05-17 17:52:45 +0000
commit887d8b57c4aa291919c8eec6b2af5a5f5259ac6d (patch)
treeb1bd6b228a39d3f867f882467891e1291c229425 /srcx/jkill.c
parent58c696e88cfeb6d97d836b9f328d683b0e187d65 (diff)
Initial 5.2.x files
Diffstat (limited to 'srcx/jkill.c')
-rw-r--r--srcx/jkill.c283
1 files changed, 283 insertions, 0 deletions
diff --git a/srcx/jkill.c b/srcx/jkill.c
new file mode 100644
index 0000000..46b1815
--- /dev/null
+++ b/srcx/jkill.c
@@ -0,0 +1,283 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+
+#include <paths.h>
+#include <signal.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <err.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <kvm.h>
+#include <limits.h>
+#include <fcntl.h>
+
+#include "util.h"
+
+#ifdef HAVE_CONFIG_H
+#include "../config.h"
+#endif
+
+/* The timeout to wait between kills */
+#define DEFAULT_TIMEOUT 3
+int g_timeout = DEFAULT_TIMEOUT;
+
+/* Supress warnings */
+int g_quiet = 0;
+
+int main(int argc, char* argv[])
+{
+ int ch, r, jid;
+ int ret = 0;
+ int restart = 0;
+ int force = 0;
+ pid_t child;
+
+ while((ch = getopt(argc, argv, "fhqrt:")) != -1)
+ {
+ switch(ch)
+ {
+ case 'f':
+ force = 1;
+ break;
+
+ case 'q':
+ g_quiet = 1;
+ break;
+
+ case 'h':
+ /* dummy for compatibility with killjail */
+ warnx("the '-h' option has been depreciated");
+ break;
+
+ case 'r':
+ restart = 1;
+ break;
+
+ /* Timeout to use between kills */
+ case 't':
+ g_timeout = atoi(optarg);
+ if(g_timeout <= 0)
+ errx(2, "invalid timeout argument: %s", optarg);
+ break;
+
+ case '?':
+ default:
+ usage();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ /* Make sure we have a jail id or name */
+ if(argc == 0)
+ usage();
+
+ /* For each jail */
+ while(argc > 0)
+ {
+ jid = translate_jail(argv[0]);
+ if(jid == -1)
+ {
+ warnx(1, "unknown jail host name: %s", argv[0]);
+ ret = 1;
+ continue;
+ }
+
+ /*
+ * We fork and the child goes into the jail and
+ * does the dirty work.
+ */
+
+ switch((child = fork()))
+ {
+ /* Error condition */
+ case -1:
+ err(1, "couldn't fork child process");
+ break;
+
+ /* The child */
+ case 0:
+ if(jail_attach(jid) == -1)
+ err(1, "couldn't attach to jail");
+
+ r = kill_jail(restart, force, argv[0]);
+ exit(r);
+ break;
+
+ /* The parent */
+ default:
+ if(waitpid(child, &r, options) == -1)
+ err(1, "error waiting for child process");
+
+ if(r != 0)
+ ret = r;
+ break;
+ };
+
+ argc--;
+ argv++;
+ }
+
+ return ret;
+}
+
+int kill_jail(int restart, int force, const char* name)
+{
+ kvm_t* kd = NULL;
+ char errbuf[_POSIX2_LINE_MAX];
+ int pass = 0;
+ int timeout = 0;
+ int ret = 0;
+
+ /* Open the kernel interface */
+ kd = kvm_openfiles(_PATH_DEVNULL, _PATH_DEVNULL, _PATH_DEVNULL,
+ O_RDONLY, errbuf);
+ if(kd == NULL)
+ errx(1, "couldn't connect to kernel: %s", errbuf);
+
+ /*
+ * Multiple passes are used to do different things.
+ * Each time the jails processes are listed.
+ */
+ while(true)
+ {
+ while(timeout > 0)
+ {
+ sleep(1);
+ timeout--;
+
+ if(!check_running_processes(kd))
+ goto done;
+ }
+
+ switch(pass)
+ {
+ /* First pass is an orderly shutdown */
+ case 0:
+
+ /* Check if we have an executable shutdown script */
+ if(check_jail_command(name, "/etc/rc.shutdown"))
+ run_jail_command(name, "/etc/rc.shutdown", NULL, JAIL_OUT_CONSOLE);
+
+ break;
+
+ /* Okay now quit all processes in jail */
+ case 1:
+ kill_jail_processes(kd, 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...", name);
+
+ kill_jail_processes(kd, SIGKILL);
+ timeout = g_timeout;
+ }
+
+ break;
+
+
+ case 3:
+
+ if(check_running_processes(kd))
+ {
+ /* And if that didn't do it, well then give up */
+ if(!g_quiet)
+ warnx("%s: couldn't stop jail, processes wouldn't die", name);
+
+ ret = 1;
+ goto done;
+ }
+
+ else if(restart)
+ {
+ /* Check if we have an executable shutdown script */
+ if(check_jail_command(name, "/etc/rc"))
+ run_jail_command(name, "/etc/rc", NULL, JAIL_OUT_CONSOLE);
+
+ goto done;
+ }
+ }
+
+ pass++;
+
+ if(!check_running_processes(kd))
+ goto done;
+ }
+
+done:
+ if(kd != NULL)
+ kvm_close(kd);
+
+ return ret;
+}
+
+void kill_jail_processes(kvm_t* kd, int sig)
+{
+ struct kinfo_proc* kp;
+ int nentries, i;
+ pid_t cur;
+
+ cur = getpid();
+
+ /* Get a process listing */
+ if((kp = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nentries)) == 0)
+ errx(1, "couldn't list processes: %s", kvm_geterr(kd));
+
+ /* Okay now loop and look at each process' jail */
+ for(i = 0; i < nentries; i++)
+ {
+ if(kp[i].ki_pid == cur)
+ continue;
+
+ if(kill(kp[i].ki_pid, sig) == -1)
+ {
+ if(errno != ESRCH)
+ errx(1, "couldn't signal process: %d", (int)kp[i].ki_pid);
+ }
+ }
+}
+
+int check_running_processes(kvm_t* kd)
+{
+ struct kinfo_proc* kp;
+ int nentries, i;
+ pid_t cur;
+
+ cur = getpid();
+
+ /* Get a process listing */
+ if((kp = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nentries)) == 0)
+ errx(1, "couldn't list processes: %s", kvm_geterr(kd));
+
+ if(&nentries != 1)
+ return 1;
+
+ /* Okay now loop and look at each process' jail */
+ for(i = 0; i < nentries; i++)
+ {
+ if(kp[i].ki_pid != cur)
+ return 1;
+ }
+
+ return 0;
+}
+
+static void usage()
+{
+ fprintf(stderr, "usage: killjail [ -r ] [ -t timeout ] [ -qf ] jailname ...\n");
+ exit(2);
+}