summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--srcx/jails.c77
-rw-r--r--srcx/jid.c42
-rw-r--r--srcx/jkill.c283
-rw-r--r--srcx/jps.c132
-rw-r--r--srcx/jps.c~362
-rw-r--r--srcx/jstart.c107
-rw-r--r--srcx/util.c366
-rw-r--r--srcx/util.h19
8 files changed, 1388 insertions, 0 deletions
diff --git a/srcx/jails.c b/srcx/jails.c
new file mode 100644
index 0000000..aac6a31
--- /dev/null
+++ b/srcx/jails.c
@@ -0,0 +1,77 @@
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <kvm.h>
+#include <paths.h>
+
+#include "util.h"
+
+#ifdef HAVE_CONFIG_H
+#include "../config.h"
+#endif
+
+static void usage();
+static void list_jails();
+
+int main(int argc, char* argv[])
+{
+ if(argc > 1)
+ usage();
+
+ if(in_jail())
+ errx(1, "can't run from inside jail");
+
+ list_jails();
+ return 0;
+}
+
+static void usage()
+{
+ fprintf(stderr, "usage: jails \n");
+ exit(2);
+}
+
+static void list_jails()
+{
+ struct xprison* sxp;
+ struct xprison* xp;
+ size_t len, i;
+
+ /* ... otherwise it's a name */
+
+ if(sysctlbyname("security.jail.list", NULL, &len, NULL, 0) == -1)
+ err(1, "couldn't list jails");
+
+retry:
+
+ if(len <= 0)
+ return;
+
+ sxp = xp = calloc(len, 1);
+ if(sxp == NULL)
+ err(1, "out of memory");
+
+ if(sysctlbyname("security.jail.list", xp, &len, NULL, 0) == -1)
+ {
+ if(errno == ENOMEM)
+ {
+ free(sxp);
+ goto retry;
+ }
+
+ err(1, "couldn't list jails");
+ }
+
+ if(len < sizeof(*xp) || len % sizeof(*xp) || xp->pr_version != XPRISON_VERSION)
+ errx(1, "kernel and userland out of sync");
+
+ jid = -1;
+
+ for(i = 0; i < (len / sizeof(*xp)); i++)
+ printf("%s\n", xp->pr_host);
+
+ free(sxp);
+}
+
diff --git a/srcx/jid.c b/srcx/jid.c
new file mode 100644
index 0000000..a201822
--- /dev/null
+++ b/srcx/jid.c
@@ -0,0 +1,42 @@
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "util.h"
+
+#ifdef HAVE_CONFIG_H
+#include "../config.h"
+#endif
+
+static void usage();
+
+int main(int argc, char* argv[])
+{
+ int i;
+ int jid = 0;
+
+ /* Remove the program name */
+ argc--;
+ argv++;
+
+ if(argc != 1)
+ usage();
+
+ if(in_jail())
+ errx(1, "can't run from inside jail");
+
+ jid = translate_jail_id(argv[0]);
+ if(jid == -1)
+ errx(1, "unknown jail host name: %s", argv[0]);
+
+ printf("%d \n", (int)jid);
+ return 0;
+}
+
+static void usage()
+{
+ fprintf(stderr, "usage: jid hostname \n");
+ exit(2);
+}
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);
+}
diff --git a/srcx/jps.c b/srcx/jps.c
new file mode 100644
index 0000000..e9a6ec8
--- /dev/null
+++ b/srcx/jps.c
@@ -0,0 +1,132 @@
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <kvm.h>
+#include <paths.h>
+
+#include "util.h"
+
+#ifdef HAVE_CONFIG_H
+#include "../config.h"
+#endif
+
+static void usage();
+static void printJailIds();
+static void runJailPs(int argc, char* argv[]);
+
+int main(int argc, char* argv[])
+{
+ int ch = 0;
+ int simple = 0;
+ int jid = 0;
+
+ while((ch = getopt(argc, argv, "x")) != -1)
+ {
+ switch(ch)
+ {
+ case 'x':
+ simple = 1;
+ break;
+
+ case '?':
+ default:
+ usage();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ /* Make sure we have a jail name or id */
+ if(argc == 0)
+ usage();
+
+ if(running_in_jail())
+ errx(1, "can't run from inside jail");
+
+ /* Translate the jail name into an id if neccessary */
+ name = argv[0];
+ jid = translate_jail_name(argv[0]);
+ if(jid == -1)
+ errx(1, "unknown jail host name: %s", argv[0]);
+
+ argc--;
+ argv++;
+
+ /* Go into the jail */
+ if(jail_attach(jid) == -1)
+ err(1, "couldn't attach to jail");
+
+ if(simple)
+ {
+ if(argc > 0)
+ usage();
+
+ print_jail_ids();
+ }
+
+ else
+ {
+ /* This function never returns */
+ run_jail_ps(argc, argv);
+ }
+
+ return 0;
+}
+
+static void usage()
+{
+ fprintf(stderr, "usage: jps [-x] jail [ ps_options ... ]\n");
+ exit(2);
+}
+
+static void run_jail_ps(int argc, char* argv[])
+{
+ char* args[];
+ int i;
+
+ if(!check_jail_command(NULL, "/bin/ps"))
+ exit(1);
+
+ /*
+ * TODO: We need to purge down the environment here.
+ * If the jail is in any way malicious or compromised
+ * then it could have replaced /bin/ps which we run...
+ */
+
+ args = alloca(sizeof(char*) * (argc + 2));
+ args[0] = "ps";
+
+ for(i = 0; i < argc; i++)
+ args[i + 1] = argv[i];
+
+ args[i + 1] = NULL;
+
+ run_jail_command(NULL, "/bin/ps", args, 0);
+}
+
+static void print_jail_ids()
+{
+ kvm_t* kd;
+ struct kinfo_proc* kp;
+ char errbuf[_POSIX2_LINE_MAX];
+
+ /* Open 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);
+
+ /* Get all processes and print the pids */
+ if((kp = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nentries)) == 0)
+ errx(1, "couldn't list processes: %s", kvm_geterr(kd));
+
+ for(i = 0; i < nentries; i++)
+ printf("%d ", (int)(kp[i].ki_pid));
+
+ fputc(stdout, '\n');
+ kvm_close(kd);
+}
diff --git a/srcx/jps.c~ b/srcx/jps.c~
new file mode 100644
index 0000000..852fc64
--- /dev/null
+++ b/srcx/jps.c~
@@ -0,0 +1,362 @@
+
+#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 "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);
+}
+
diff --git a/srcx/jstart.c b/srcx/jstart.c
new file mode 100644
index 0000000..76bdaae
--- /dev/null
+++ b/srcx/jstart.c
@@ -0,0 +1,107 @@
+
+/* A lot of code from jail.c in */
+/* TODO: Attribute properly */
+
+#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
+
+int main(int argc, char* argv[])
+{
+#include <signal.h>
+ int ch, jid;
+ struct jail j;
+ int printjid = 0;
+ int console = 0;
+ struct in_addr in;
+
+ while((ch = getopt(argc, argv, "ic")) != -1)
+ {
+ switch(ch)
+ {
+ case 'i':
+ printjid = 1;
+ break;
+
+ case 'c':
+ console = 1;
+ break;
+
+ case '?':
+ default:
+ usage();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if(argc != 3)
+ usage();
+
+ if(getuid() != 0)
+ errx(1, "must be run as root");
+
+ if(chdir(argv[0]) != 0)
+ err(1, "couldn't change to jail directory: %s", argv[0]);
+
+ if(inet_aton(argv[2], &in) != 0)
+ errx(1, "invalid ip address: %s", argv[2]);
+
+ memset(&j, 0, sizeof(j));
+ j.version = 0;
+ j.path = argv[0];
+#include <signal.h>
+ j.hostname = argv[1];
+ j.ip_number = ntohl(in.s_addr);
+
+ /* Here's where we actually go into the jail */
+ jid = jail(&j);
+ if(jid == -1)
+ err(1, "couldn't create jail");
+
+ if(console)
+ {
+
+
+ }
+
+ if(printjid)
+ {
+ printf("%d\n", jid);
+ fflush(stdout);
+ }
+
+ if(!check_jail_command("/etc/rc"))
+ exit(1);
+
+ run_jail_command("/etc/rc");
+ return 0;
+}
+
+static void usage()
+{
+ fprintf(stderr, "usage: jstart [-ic] path hostname ip-number\n");
+ exit(2);
+}
diff --git a/srcx/util.c b/srcx/util.c
new file mode 100644
index 0000000..6c1d811
--- /dev/null
+++ b/srcx/util.c
@@ -0,0 +1,366 @@
+
+#include <sys/types.h>
+#include <sys/user.h>
+#include <sys/proc.h>
+#include <sys/param.h>
+#include <sys/jail.h>
+#include <sys/sysctl.h>
+#include <sys/file.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <kvm.h>
+#include <paths.h>
+
+int translate_jail_name(const char* str)
+{
+ struct xprison* sxp;
+ struct xprison* xp;
+ size_t len, i;
+ char* e;
+ int jid;
+
+ jid = strtol(str, &e, 10);
+
+ /* If it was all a number ... */
+ if(!*e)
+ {
+ if(jid > 0)
+ return jid;
+
+ errx(1, "invalid jail id: %s", str);
+ }
+
+ /* ... otherwise it's a name */
+
+ if(sysctlbyname("security.jail.list", NULL, &len, NULL, 0) == -1)
+ err(1, "couldn't list jails");
+
+retry:
+
+ if(len <= 0)
+ return -1;
+
+ sxp = xp = calloc(len, 1);
+ if(sxp == NULL)
+ err(1, "out of memory");
+
+ if(sysctlbyname("security.jail.list", xp, &len, NULL, 0) == -1)
+ {
+ if(errno == ENOMEM)
+ {
+ free(sxp);
+ goto retry;
+ }
+
+ err(1, "couldn't list jails");
+ }
+
+ if(len < sizeof(*xp) || len % sizeof(*xp) || xp->pr_version != XPRISON_VERSION)
+ errx(1, "kernel and userland out of sync");
+
+ jid = -1;
+
+ for(i = 0; i < (len / sizeof(*xp)); i++)
+ {
+ if(strcmp(xp->pr_host, str) == 0)
+ {
+ jid = xp->pr_id;
+ break;
+ }
+ }
+
+ free(sxp);
+ return -1;
+}
+
+/*
+ * in_jail
+ * This code was written by James E. Quick mailto:jq@quick.com
+ * The code may be freely re-used under the terms of the BSD copyright,
+ * as long as this comment remains intact.
+ */
+
+int running_in_jail()
+{
+ int count;
+ kvm_t* kd = 0;
+ struct kinfo_proc* kp;
+ char errbuf[_POSIX2_LINE_MAX];
+ int result = -1;
+
+ kd = kvm_openfiles(_PATH_DEVNULL, _PATH_DEVNULL, _PATH_DEVNULL,
+ O_RDONLY, errbuf);
+ if(kd == NULL)
+ errx(1, "couldn't connect to kernel: %s", errbuf);
+
+ kp = kvm_getprocs(kd, KERN_PROC_PID, getpid(), &count);
+ if(kp == NULL)
+ errx(1, "couldn't list processes: %s", kvm_geterr(kd));
+
+ result = (kp->ki_flag & P_JAILED) ? 1 : 0;
+ kvm_close(kd);
+
+ return result;
+}
+
+
+int check_jail_command(const char* jail, const char* cmd)
+{
+ struct stat sb;
+
+ if(stat(cmd, &sb) == -1)
+ {
+ if(errno == EACCESS || errno == ELOOP || errno == ENAMETOOLONG ||
+ errno == ENOENT || errno == ENOTDIR)
+ {
+ warn("%s%scan't execute in jail: %s", , jail ? jail : "",
+ jail ? jail : ": ", cmd);
+ return 0;
+ }
+
+ err(1, "%s%scouldn't stat file: %s", , jail ? jail : "",
+ jail ? jail : ": ", cmd);
+ }
+
+ if(!(sb.st_mode & S_IFREG))
+ {
+ warnx("%s%snot a regular file: %s", , jail ? jail : "",
+ jail ? jail : ": ", cmd);
+ return 0;
+ }
+
+ if(!(sb.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
+ {
+ warnx("%s%snot executable: %s", , jail ? jail : "",
+ jail ? jail : ": ", cmd);
+ return 0;
+ }
+
+ if(sb.st_uid != 0)
+ {
+ warnx("%s%snot owned by root: %s", jail ? jail : "",
+ jail ? jail : ": ", cmd);
+ return 0;
+ }
+
+ return 1;
+}
+
+int run_simple_command(const char* jail, const char* cmd, char* args[])
+{
+ pid_t pid;
+ int status;
+
+ switch((pid = fork()))
+ {
+ case -1:
+ err(1, "couldn't fork child process");
+ break;
+
+ /* This is the child here */
+ case 0:
+ if(args)
+ exect(cmd, args, env);
+ else
+ execle(cmd, cmd, NULL, env);
+
+ exit(errno);
+ break;
+
+ /* This is the parent process */
+ default:
+
+ /* If the processes exited then break out */
+ if(waitpid(pid, &status, 0) == pid)
+ break;
+
+ /* Return any status codes */
+ if(status != 0)
+ {
+ warnx("%s%serror executing: %s: %s", jail ? jail : "",
+ jail ? jail : ": ", cmd, strerror(status));
+ return 0;
+ }
+
+ break;
+ }
+
+ return 1;
+}
+
+int run_dup_command(const char* jail, const char* cmd, char* argv[] args, int opts)
+{
+ int outpipe[2];
+ pid_t pid;
+
+ /*
+ * Special function to duplicate output of a command to two
+ * files.
+ *
+ * NOTE: Yes, I know this may seem like overkill, but system,
+ * popen and all those guys would hang with certain rc scripts.
+ * Those which opened a daemon in the background ('&') but still
+ * kept their output going to the same stdin/stdout handles.
+ */
+
+ /* Create a pipe for the child process */
+ if(pipe(outpipe) < 0)
+ return -1;
+
+ switch(pid = fork())
+ {
+ case -1:
+ err(1, "couldn't fork child process");
+ break;
+
+ /* This is the child here */
+ case 0:
+ {
+ /* Fix up our end of the pipe */
+ if(dup2(outpipe[WRITE_END], STDOUT) < 0 ||
+ dup2(outpipe[WRITE_END], STDERR) < 0)
+ exit(errno);
+
+ /* Okay, now run whatever command it was */
+ if(args)
+ exect(cmd, args, env);
+ else
+ execle(cmd, cmd, NULL, env);
+
+ /* In case it returns then have to do this to get
+ children to disconnect from stdout */
+ fflush(stdout);
+ fclose(stdout);
+ close(outpipe[WRITE_END]);
+
+ exit(errno);
+ }
+ break;
+
+
+ /* And this is the parent */
+ default:
+ {
+ int console = -1;
+ int ret;
+ int status = 0;
+ fd_set readmask;
+ struct timeval timeout = { 0, 10000 };
+
+ FD_ZERO(&readmask);
+
+ /* Open the console file and write the header */
+ if(opts & JAIL_OUT_CONSOLE)
+ console = open(_PATH_CONSOLE, O_WRONLY | O_APPEND);
+
+ /* No blocking on the child processes pipe */
+ fcntl(outpipe[READ_END], F_SETFL, fcntl(outpipe[READ_END], F_GETFL, 0) | O_NONBLOCK);
+
+ /* Loop until the process dies or no more output */
+ while(1)
+ {
+ FD_SET(outpipe[READ_END], &readmask);
+
+ if(select(FD_SETSIZE, &readmask, NULL, NULL, &timeout) == -1)
+ err(1, "couldn't select");
+
+ if(FD_ISSET(outpipe[READ_END], &readmask))
+ {
+ /* Read text */
+ while((ret = read(outpipe[READ_END], buff, 256)) > 0)
+ {
+ if(opts & JAIL_OUT_STDOUT)
+ write(STDOUT, buff, ret);
+
+ if(opts & JAIL_OUT_STDERR)
+ write(STDERR, buff, ret);
+
+ if(console != -1)
+ write(console, buff, ret);
+ }
+ }
+
+ /* If the processes exited then break out */
+ if(waitpid(pid, &status, WNOHANG) == pid)
+ break;
+
+ /* Or if there's an error or end of file */
+ if(ret == -1 && errno != EAGAIN || ret == 0)
+ break;
+ }
+
+ /* Return any status codes */
+ if(status != 0)
+ {
+ warnx("%s%serror executing: %s: %s", jail ? jail : "",
+ jail ? jail : ": ", cmd, strerror(status));
+ return 0;
+ }
+
+ /* Clean up */
+ close(outpipe[READ_END]);
+
+ if(console != -1)
+ close(console);
+ }
+ break;
+ }
+
+ return 1;
+}
+
+#define MAKE_ENV_VAR(v, n) \
+{ \
+ char* t = getenv(n); \
+ t = t ? t : ""; \
+ (v) = alloca(strlen(n) + 2 + strlen(t)); \
+ sprintf((v), "%s=%s", (n), (v)); \
+}
+
+/* read & write ends of a pipe */
+#define READ_END 0
+#define WRITE_END 1
+
+/* pre-set file descriptors */
+#define STDIN 0
+#define STDOUT 1
+#define STDERR 2
+
+int run_jail_command(const char* jail, const char* cmd, char* args[], int opts)
+{
+ char* env[5];
+
+ /* Prepare an environment for the cmd */
+ env[0] = "PATH=" _PATH_STDPATH;
+ env[1] = NULL;
+
+ /*
+ * If we're outputing to the console then we need a
+ * few more environement variables. Processes like
+ * 'ps' depend on them for formatting.
+ */
+ if(!opts || opts & JAIL_OUT_STDOUT ||
+ opts & JAIL_OUT_STDERR)
+ {
+ /* TODO: For fun debugging. Remove later */
+ t = getenv(VAR);
+ t = t ? t : NULL;
+ env[1] = alloca(sizeof("TERM") + 2 + strlen(t));
+ sprintf(env[1], "%s=%s", "TERM", t);
+
+ MAKE_ENV_VAR(env[1], "TERM");
+ MAKE_ENV_VAR(env[2], "COLUMNS");
+ MAKE_ENV_VAR(env[3], "LINES");
+ env[4] = NULL;
+ }
+
+ if(opts)
+ return run_dup_command(jail, cmd, args, opts);
+ else
+ return run_simple_command(jail, cmd, args);
+}
diff --git a/srcx/util.h b/srcx/util.h
new file mode 100644
index 0000000..1ac02c5
--- /dev/null
+++ b/srcx/util.h
@@ -0,0 +1,19 @@
+#ifndef __UTIL_H__
+#define __UTIL_H__
+
+int translate_jail_name(const char* str);
+int running_in_jail();
+
+/* Output stuff to the jail console if available */
+#define JAIL_OUT_CONSOLE 1 << 1
+
+/* Output to stdout */
+#define JAIL_OUT_STDOUT 1 << 2
+
+/* Output to stderr */
+#define JAIL_OUT_STDERR 1 << 3
+
+int run_jail_command(const char* jail, const char* script, int opts);
+int check_jail_command(const char* jail, const char* script);
+
+#endif __UTIL_H__