From e70562b7b93e79372e230738038c62cf80436088 Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Wed, 19 May 2004 17:27:00 +0000 Subject: Proper preparing of kvm for jail --- srcx/jkill.c | 26 ++- srcx/jps.c | 35 ++-- srcx/util.c | 657 +++++++++++++++++++++++++++++++---------------------------- srcx/util.h | 10 +- 4 files changed, 392 insertions(+), 336 deletions(-) diff --git a/srcx/jkill.c b/srcx/jkill.c index 4311ed8..953095e 100644 --- a/srcx/jkill.c +++ b/srcx/jkill.c @@ -84,15 +84,17 @@ static void usage_jail(const char* name); int main(int argc, char* argv[]) { - int r, jid; - int ret = 0; + struct xprison* sxp = NULL; + struct xprison* xp = NULL; + size_t len; + int jid, r, ret = 0; pid_t child; /* * When running in a jail we do things slightly * differently, and accept different args */ - if(running_in_jail()) + if(running_in_jail() != 0) { parse_jail_opts(argc, argv); @@ -114,17 +116,23 @@ int main(int argc, char* argv[]) argc -= optind; argv += optind; + len = get_jail_sysctl(&sxp); + /* For each jail */ for(; argc > 0; argc--, argv++) { - jid = translate_jail_name(argv[0]); - if(jid == -1) + xp = find_jail(str, len, sxp); + + if(xp == NULL) { warnx("unknown jail host name: %s", argv[0]); ret = 1; continue; } + /* This makes sure we can use kvm funcs in jail */ + kvm_prepare_jail(xp); + /* * We fork and the child goes into the jail and * does the dirty work. Unless in debug mode where @@ -141,6 +149,11 @@ int main(int argc, char* argv[]) /* The child */ case 0: #endif + jid = xp->pri_id; + + /* Always free jail info before going into jail */ + free_jail_sysctl(len, sxp); + if(jail_attach(jid) == -1) err(1, "couldn't attach to jail"); @@ -164,6 +177,7 @@ int main(int argc, char* argv[]) argv++; } + free_jail_sysctl(len, sxp); return ret; } } @@ -304,7 +318,7 @@ static int kill_jail(const char* jail) int cmdargs = JAIL_RUN_CONSOLE; /* Open the kernel interface */ - kd = open_kvm_handle(jail, errbuf); + kd = kvm_openfiles(_PATH_DEVNULL, _PATH_DEVNULL, NULL, O_RDONLY, errbuf); if(kd == NULL) errx(1, "couldn't connect to kernel: %s", errbuf); diff --git a/srcx/jps.c b/srcx/jps.c index 8b12b4a..867364d 100644 --- a/srcx/jps.c +++ b/srcx/jps.c @@ -61,9 +61,11 @@ static void run_jail_ps(int argc, char* argv[]); int main(int argc, char* argv[]) { - int ch = 0; + struct xprison* sxp = NULL; + struct xprison* xp = NULL; + size_t len; + int jid, ch = 0; int simple = 0; - int jid = 0; while((ch = getopt(argc, argv, "i")) != -1) { @@ -86,19 +88,29 @@ int main(int argc, char* argv[]) if(argc == 0) usage(); - if(running_in_jail()) + if(running_in_jail() != 0) errx(1, "can't run from inside jail"); /* Translate the jail name into an id if neccessary */ - jid = translate_jail_name(argv[0]); - if(jid == -1) + len = get_jail_sysctl(&sxp); + xp = find_jail(argv[0], len, sxp); + + if(xp == NULL) errx(1, "unknown jail host name: %s", argv[0]); argc--; argv++; + /* This makes sure we can use kvm funcs in jail */ + kvm_prepare_jail(xp); + + jid = xp->pr_id; + + /* Always free jail info before going into jail */ + free_jail_sysctl(len, sxp); + /* Go into the jail */ - if(jail_attach(jid) == -1) + if(jail_attach(xp->pr_id) == -1) err(1, "couldn't attach to jail"); if(simple) @@ -126,21 +138,12 @@ static void usage() static void run_jail_ps(int argc, char* argv[]) { - char errbuf[_POSIX2_LINE_MAX]; char** args; - kvm_t* kd; int i; if(!check_jail_command(NULL, "/bin/ps")) exit(1); - /* Make sure we can use kvm functionality here */ - kd = open_kvm_handle(NULL, errbuf); - if(kd == NULL) - errx(1, "couldn't connect to kernel: %s", errbuf); - - kvm_close(kd); - /* * TODO: We need to purge down the environment here. * If the jail is in any way malicious or compromised @@ -166,7 +169,7 @@ static void print_jail_ids() char errbuf[_POSIX2_LINE_MAX]; /* Open kernel interface */ - kd = open_kvm_handle(NULL, errbuf); + kd = kvm_openfiles(_PATH_DEVNULL, _PATH_DEVNULL, NULL, O_RDONLY, errbuf); if(kd == NULL) errx(1, "couldn't connect to kernel: %s", errbuf); diff --git a/srcx/util.c b/srcx/util.c index 31ee3bd..137ca24 100644 --- a/srcx/util.c +++ b/srcx/util.c @@ -61,149 +61,178 @@ extern char** environ; size_t get_jail_sysctl(struct xprison** ret) { - struct xprison* xp; - size_t len; - *ret = NULL; + struct xprison* xp; + size_t len; + *ret = NULL; - if(sysctlbyname("security.jail.list", NULL, &len, NULL, 0) == -1) - err(1, "couldn't list jails"); + if(sysctlbyname("security.jail.list", NULL, &len, NULL, 0) == -1) + err(1, "couldn't list jails"); retry: - if(len <= 0) - return 0; + if(len <= 0) + return 0; - xp = calloc(len, 1); - if(xp == NULL) - err(1, "out of memory"); + xp = calloc(len, 1); + if(xp == NULL) + err(1, "out of memory"); - if(sysctlbyname("security.jail.list", xp, &len, NULL, 0) == -1) - { - if(errno == ENOMEM) + if(sysctlbyname("security.jail.list", xp, &len, NULL, 0) == -1) { - free(xp); - goto retry; + if(errno == ENOMEM) + { + free(xp); + goto retry; + } + + err(1, "couldn't list jails"); } - 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"); - if(len < sizeof(*xp) || len % sizeof(*xp) || xp->pr_version != XPRISON_VERSION) - errx(1, "kernel and userland out of sync"); + *ret = xp; + return len / sizeof(*xp); +} - *ret = xp; - return len / sizeof(*xp); +void free_jail_sysctl(size_t len, struct xprison* xp) +{ + if(len == 0 || xp == NULL) + return; + + /* + * An extra precaution to prevent leakage of information + * into the jail. + */ + + memset(xp, 0, sizeof(struct xprison) * len); + free(xp); } -int translate_jail_name(const char* str) +struct xprison* find_jail(const char* str, size_t len, struct xprison* xp) { - struct xprison* xp = NULL; - size_t len, i; - char* e; - int jid = -1; + int jid; + size_t i; + char* e; + + if(len == 0 || xp == NULL) + return NULL; - len = get_jail_sysctl(&xp); - if(len == 0) - goto done; + jid = strtol(str, &e, 10); - jid = strtol(str, &e, 10); + /* If it was all a number ... */ + if(!*e) + { + if(jid <= 0) + errx(1, "invalid jail id: %s", str); - /* If it was all a number ... */ - if(!*e) - { - if(jid <= 0) - errx(1, "invalid jail id: %s", str); + /* Validate the number */ + for(i = 0; i < len; i++) + { + if(jid == xp[i].pr_id) + return &(xp[i]); + } + } - /* Validate the number */ for(i = 0; i < len; i++) { - if(jid == xp[i].pr_id) - goto done; + if(strcmp(xp[i].pr_host, str) == 0) + return &(xp[i]); } - jid = -1; - } + return NULL; +} - jid = -1; +int translate_jail_name(const char* str) +{ + struct xprison* sxp = NULL; + struct xprison* xp = NULL; + size_t len; + int jid = -1; - for(i = 0; i < len; i++) - { - if(strcmp(xp[i].pr_host, str) == 0) + len = get_jail_sysctl(&sxp); + if(sxp) { - jid = xp[i].pr_id; - break; - } - } + xp = find_jail(str, sxp); + if(xp != NULL) + jid = xp->pr_id; -done: - if(xp) - free(xp); + free(sxp); + } - return jid; + return jid; } -kvm_t* open_kvm_handle(const char* jail, char* errbuf) +int kvm_prepare_jail(struct xprison* xp) { - /* - * Basically the kvm routines won't work in a jail unless there's - * a /dev/null device for us to use as the file names. If it's - * missing we have to create it. - */ - - struct stat sb; - int nodir = 0; - int nonull = 0; - - if(stat(_PATH_DEVNULL, &sb) == -1) - { - if(errno == ENOTDIR) - { - nodir = 1; - nonull = 1; - } + /* + * Basically the kvm routines won't work in a jail unless there's + * a /dev/null device for us to use as the file names. If it's + * missing we have to create it. + */ + + struct stat sb; + char* path; + int nodir = 0; + int nonull = 0; + + path = (char*)alloca(strlen(_PATH_DEVNULL) + 2 + strlen(xp->pr_path)); - else if(errno == ENOENT) + strcpy(path, xp->pr_path); + strcat(path, _PATH_DEVNULL); + + if(stat(path, &sb) == -1) { - nonull = 1; + if(errno == ENOTDIR) + { + nodir = 1; + nonull = 1; + } + + else if(errno == ENOENT) + { + nonull = 1; + } + + else + { + err(1, "couldn't stat file: %s", path); + } } - else + if(nodir) { - err(1, "%s%scouldn't stat file: %s", jail ? jail : "", - jail ? ": " : "", _PATH_DEVNULL); - } - } + strcpy(path, xp->pr_path); + strcat(path, _PATH_DEV); - if(nodir) - { - warnx("%s%sthe %s directory doesn't exist in jail. creating...", - jail ? jail : "", jail ? ": " : "", _PATH_DEV); + warnx("the %s directory doesn't exist. creating...", path); - if(mkdir(_PATH_DEV, 0) == -1 || - chmod(_PATH_DEV, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1) - { - warn("%s%scouldn't create %s directory", - jail ? jail : "", jail ? ": " : "", _PATH_DEV); - nonull = 0; + if(mkdir(path, 0) == -1 || + chmod(path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1) + { + warn("couldn't create %s directory", path); + return -1; + } } - } - if(nonull) - { - mode_t mode = 0666 | S_IFCHR; - dev_t dev = makedev(2, 2); + if(nonull) + { + mode_t mode = 0666 | S_IFCHR; + dev_t dev = makedev(2, 2); + + strcpy(path, xp->pr_path); + strcat(path, _PATH_DEVNULL); - warnx("%s%sthe %s device doesn't exist in jail. creating...", - jail ? jail : "", jail ? ": " : "", _PATH_DEVNULL); + warnx("the %s device doesn't exist in jail. creating...", path); - if(mknod(_PATH_DEVNULL, mode, dev) == -1) - { - warn("%s%scouldn't create %s device", - jail ? jail : "", jail ? ": " : "", _PATH_DEVNULL); + if(mknod(path, mode, dev) == -1) + { + warn("couldn't create %s device", path); + return -1; + } } - } - return kvm_openfiles(_PATH_DEVNULL, _PATH_DEVNULL, NULL, O_RDONLY, errbuf); + return 0; } /* @@ -215,119 +244,120 @@ kvm_t* open_kvm_handle(const char* jail, char* errbuf) int running_in_jail() { - int count; - kvm_t* kd = 0; - struct kinfo_proc* kp; - char errbuf[_POSIX2_LINE_MAX]; - int result = -1; + int count; + kvm_t* kd = 0; + struct kinfo_proc* kp; + int result = -1; - kd = open_kvm_handle(NULL, errbuf); - if(kd == NULL) - errx(1, "couldn't connect to kernel: %s", errbuf); + kd = kvm_open(_PATH_DEVNULL, _PATH_DEVNULL, NULL, O_RDONLY, NULL); + if(kd == NULL) + return -1; - kp = kvm_getprocs(kd, KERN_PROC_PID, getpid(), &count); - if(kp == NULL) - errx(1, "couldn't list processes: %s", kvm_geterr(kd)); + kp = kvm_getprocs(kd, KERN_PROC_PID, getpid(), &count); - result = (kp->ki_flag & P_JAILED) ? 1 : 0; - kvm_close(kd); + if(kp == NULL) + result = -1; + else + result = (kp->ki_flag & P_JAILED) ? 1 : 0; + + kvm_close(kd); - return result; + return result; } int check_jail_command(const char* jail, const char* cmd) { - struct stat sb; + struct stat sb; - if(stat(cmd, &sb) == -1) - { - if(errno == EACCES || errno == ELOOP || errno == ENAMETOOLONG || - errno == ENOENT || errno == ENOTDIR) + if(stat(cmd, &sb) == -1) { - warn("%s%scan't execute in jail: %s", jail ? jail : "", - jail ? ": " : "", cmd); - return 0; - } + if(errno == EACCES || errno == ELOOP || errno == ENAMETOOLONG || + errno == ENOENT || errno == ENOTDIR) + { + warn("%s%scan't execute in jail: %s", jail ? jail : "", + jail ? ": " : "", cmd); + return 0; + } - err(1, "%s%scouldn't stat file: %s", jail ? jail : "", - jail ? ": " : "", cmd); - } + err(1, "%s%scouldn't stat file: %s", jail ? jail : "", + jail ? ": " : "", cmd); + } - if(!(sb.st_mode & S_IFREG)) - { - warnx("%s%snot a regular file: %s", jail ? jail : "", + if(!(sb.st_mode & S_IFREG)) + { + warnx("%s%snot a regular file: %s", jail ? jail : "", jail ? ": " : "", cmd); - return 0; - } + return 0; + } - if(sb.st_uid != 0) - { - warnx("%s%snot owned by root: %s", jail ? jail : "", + if(sb.st_uid != 0) + { + warnx("%s%snot owned by root: %s", jail ? jail : "", jail ? ": " : "", cmd); - return 0; - } + return 0; + } - return 1; + return 1; } int run_overlay_command(const char* jail, const char* cmd, char* env[], char* args[]) { - if(args) - execve(cmd, args, env); - else - execle(cmd, cmd, NULL, env); + if(args) + execve(cmd, args, env); + else + execle(cmd, cmd, NULL, env); - warn("%s%serror executing: %s: %s", jail ? jail : "", + warn("%s%serror executing: %s: %s", jail ? jail : "", jail ? ": " : "", cmd); - return 0; + return 0; } int run_simple_command(const char* jail, const char* cmd, char* env[], char* args[], int opts) { - pid_t pid; - int status = 0; + pid_t pid; + int status = 0; - if(opts & JAIL_RUN_NOFORK) - return run_overlay_command(jail, cmd, env, args); + if(opts & JAIL_RUN_NOFORK) + return run_overlay_command(jail, cmd, env, args); - switch((pid = fork())) - { - case -1: - err(1, "couldn't fork child process"); - break; + switch((pid = fork())) + { + case -1: + err(1, "couldn't fork child process"); + break; - /* This is the child here */ - case 0: - if(args) - execve(cmd, args, env); - else - execle(cmd, cmd, NULL, env); + /* This is the child here */ + case 0: + if(args) + execve(cmd, args, env); + else + execle(cmd, cmd, NULL, env); - exit(errno); - break; + exit(errno); + break; - /* This is the parent process */ - default: + /* This is the parent process */ + default: - /* If the processes exited then break out */ - if(waitpid(pid, &status, 0) == -1) - err(1, "couldn't wait on child process"); + /* If the processes exited then break out */ + if(waitpid(pid, &status, 0) == -1) + err(1, "couldn't wait on child process"); - /* Return any status codes */ - if(WEXITSTATUS(status) != 0) - { - warnx("%s%serror executing: %s: %s", jail ? jail : "", - jail ? ": " : "", cmd, strerror(WEXITSTATUS(status))); - return 0; - } + /* Return any status codes */ + if(WEXITSTATUS(status) != 0) + { + warnx("%s%serror executing: %s: %s", jail ? jail : "", + jail ? ": " : "", cmd, strerror(WEXITSTATUS(status))); + return 0; + } - break; - } + break; + } - return 1; + return 1; } /* read & write ends of a pipe */ @@ -342,155 +372,162 @@ int run_simple_command(const char* jail, const char* cmd, char* env[], int run_dup_command(const char* jail, const char* cmd, char* env[], char* 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: + 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()) { - /* Fix up our end of the pipe */ - if(dup2(outpipe[WRITE_END], STDOUT) < 0 || - dup2(outpipe[WRITE_END], STDERR) < 0) - exit(errno); + case -1: + err(1, "couldn't fork child process"); + break; - /* Okay, now run whatever command it was */ - if(args) - execve(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; - char buff[256]; - struct timeval timeout = { 0, 10000 }; - - FD_ZERO(&readmask); - - /* Open the console file and write the header */ - if(opts & JAIL_RUN_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); + /* 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) + execve(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; - if(select(FD_SETSIZE, &readmask, NULL, NULL, &timeout) == -1) - err(1, "couldn't select"); - if(FD_ISSET(outpipe[READ_END], &readmask)) + /* And this is the parent */ + default: { - /* Read text */ - while((ret = read(outpipe[READ_END], buff, 256)) > 0) - { - if(opts & JAIL_RUN_STDOUT) - write(STDOUT, buff, ret); - - if(opts & JAIL_RUN_STDERR) - write(STDERR, buff, ret); + int console = -1; + int ret; + int status = 0; + int waited = 0; + fd_set readmask; + char buff[256]; + struct timeval timeout = { 0, 10000 }; + + FD_ZERO(&readmask); + + /* Open the console file and write the header */ + if(opts & JAIL_RUN_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_RUN_STDOUT) + write(STDOUT, buff, ret); + + if(opts & JAIL_RUN_STDERR) + write(STDERR, buff, ret); + + if(console != -1) + write(console, buff, ret); + } + } + + /* Or if there's an error or end of file */ + if(ret == -1 && errno != EAGAIN || ret == 0) + break; + + /* If the processes exited then break out */ + if(waitpid(pid, &status, WNOHANG) == pid) + { + waited = 1; + break; + } + } + + if(!waited) + waitpid(pid, &status, 0); + + /* Return any status codes */ + if(WEXITSTATUS(status) != 0) + { + warnx("%s%serror executing: %s: %s", jail ? jail : "", + jail ? ": " : "", cmd, strerror(WEXITSTATUS(status))); + return 0; + } + + /* Clean up */ + close(outpipe[READ_END]); if(console != -1) - write(console, buff, ret); - } + close(console); } - - /* 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(WEXITSTATUS(status) != 0) - { - warnx("%s%serror executing: %s: %s", jail ? jail : "", - jail ? ": " : "", cmd, strerror(WEXITSTATUS(status))); - return 0; - } - - /* Clean up */ - close(outpipe[READ_END]); - - if(console != -1) - close(console); + break; } - break; - } - return 1; + return 1; } int run_jail_command(const char* jail, const char* cmd, char* args[], int opts) { - char* env[5]; - char* t; - int j; - - memset(env, 0, sizeof(env)); - -#define MAKE_ENV_VAR(n) \ - t = getenv(n); \ - if(t != NULL) \ - { \ - env[j] = alloca(strlen(n) + 2 + strlen(t)); \ - sprintf(env[j], "%s=%s", (char*)(n), t); \ - j++; \ - } - - /* Prepare an environment for the cmd */ - env[0] = "PATH=" _PATH_STDPATH; - j = 1; - - MAKE_ENV_VAR("TERM"); - MAKE_ENV_VAR("COLUMNS"); - MAKE_ENV_VAR("LINES"); - - if(opts & JAIL_RUN_OUTPUT) - return run_dup_command(jail, cmd, env, args, opts); - else - return run_simple_command(jail, cmd, env, args, opts); + char* env[5]; + char* t; + int j; + + memset(env, 0, sizeof(env)); + +#define MAKE_ENV_VAR(n) \ + t = getenv(n); \ + if(t != NULL) \ + { \ + env[j] = alloca(strlen(n) + 2 + strlen(t)); \ + sprintf(env[j], "%s=%s", (char*)(n), t); \ + j++; \ + } + + /* Prepare an environment for the cmd */ + env[0] = "PATH=" _PATH_STDPATH; + j = 1; + + MAKE_ENV_VAR("TERM"); + MAKE_ENV_VAR("COLUMNS"); + MAKE_ENV_VAR("LINES"); + + if(opts & JAIL_RUN_OUTPUT) + return run_dup_command(jail, cmd, env, args, opts); + else + return run_simple_command(jail, cmd, env, args, opts); } diff --git a/srcx/util.h b/srcx/util.h index fed9261..0696438 100644 --- a/srcx/util.h +++ b/srcx/util.h @@ -39,14 +39,16 @@ #ifndef __UTIL_H__ #define __UTIL_H__ -#include -#include +struct xprison; int translate_jail_name(const char* str); int running_in_jail(); -kvm_t* open_kvm_handle(const char* jail, char* errbuf); -size_t get_jail_sysctl(struct xprison** ret); +size_t get_jail_sysctl(struct xprison** xp); +void free_jail_sysctl(size_t len, struct xprison* xp); + +int kvm_prepare_jail(struct xprison* xp); +struct xprison* find_jail(const char* str, size_t len, struct xprison* xp); #define JAIL_RUN_CONSOLE 0x00000001 /* Output stuff to the jail console if available */ #define JAIL_RUN_STDOUT 0x00000002 /* Output to stdout */ -- cgit v1.2.3