diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/init/deps-exe/s6-linux-init-maker | 1 | ||||
-rw-r--r-- | src/init/s6-linux-init-maker.c | 377 |
2 files changed, 378 insertions, 0 deletions
diff --git a/src/init/deps-exe/s6-linux-init-maker b/src/init/deps-exe/s6-linux-init-maker new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/init/deps-exe/s6-linux-init-maker @@ -0,0 +1 @@ +-lskarnet diff --git a/src/init/s6-linux-init-maker.c b/src/init/s6-linux-init-maker.c new file mode 100644 index 0000000..a965cc0 --- /dev/null +++ b/src/init/s6-linux-init-maker.c @@ -0,0 +1,377 @@ +/* ISC license. */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <errno.h> +#include <pwd.h> +#include <skalibs/config.h> +#include <skalibs/uint64.h> +#include <skalibs/uint.h> +#include <skalibs/gidstuff.h> +#include <skalibs/bytestr.h> +#include <skalibs/allreadwrite.h> +#include <skalibs/buffer.h> +#include <skalibs/strerr2.h> +#include <skalibs/stralloc.h> +#include <skalibs/djbunix.h> +#include <skalibs/sgetopt.h> +#include <skalibs/skamisc.h> +#include <execline/config.h> +#include <s6-portable-utils/config.h> +#include <s6-linux-utils/config.h> +#include <s6/config.h> + +#define USAGE "s6-linux-init-maker [ -c basedir ] [ -l tmpfsdir ] [ -b execline_bindir ] [ -u log_user ] [ -g early_getty_cmd ] [ -2 stage2_script ] [ -3 stage3_script ] [ -p initial_path ] [ -m initial_umask ] [ -t timestamp_style ] [ -d dev_style ] [ -e initial_envvar ... ] dir" +#define dieusage() strerr_dieusage(100, USAGE) +#define dienomem() strerr_diefu1sys(111, "stralloc_catb") ; + +#define CRASH_SCRIPT \ +"#!" EXECLINE_EXTBINPREFIX "execlineb -P\n\n" \ +EXECLINE_EXTBINPREFIX "redirfd -r 0 /dev/console\n" \ +EXECLINE_EXTBINPREFIX "redirfd -w 1 /dev/console\n" \ +EXECLINE_EXTBINPREFIX "fdmove -c 2 1\n" \ +EXECLINE_EXTBINPREFIX "foreground { " \ +S6_PORTABLE_UTILS_EXTBINPREFIX "s6-echo -- " \ +"\"s6-svscan crashed. Dropping to an interactive shell.\" }\n" \ +"/bin/sh -i\n" + +#define BANNER1 "s6-init: stage 1" +#define BANNER2 "s6-init: stage 2" + +static char const *slashrun = "/run" ; +static char const *robase = "/etc/s6" ; +static char const *init_script = "/etc/rc.init" ; +static char const *shutdown_script = "/etc/rc.shutdown" ; +static char const *bindir = "/bin" ; +static char const *initial_path = SKALIBS_DEFAULTPATH ; +static char const *early_getty = 0 ; +static uid_t uncaught_logs_uid = 65534 ; +static gid_t uncaught_logs_gid = 65534 ; +static unsigned int initial_umask = 022 ; +static unsigned int timestamp_style = 1 ; +static unsigned int slashdev_style = 2 ; + +typedef int writetobuf_func_t (buffer *) ; +typedef writetobuf_func_t *writetobuf_func_t_ref ; + +static int early_getty_script (buffer *b) +{ + if (buffer_puts(b, "#!" EXECLINE_EXTBINPREFIX "execlineb -P\n\n") < 0 + || buffer_puts(b, early_getty) < 0 + || buffer_put(b, "\n", 1) < 0) + return 0 ; + return 1 ; +} + +static int s6_svscan_log_script (buffer *b) +{ + unsigned int sabase = satmp.len ; + char fmt[UINT64_FMT] ; + if (buffer_puts(b, + "#!" EXECLINE_EXTBINPREFIX "execlineb -P\n\n" + EXECLINE_EXTBINPREFIX "redirfd -rnb 0 fifo\n" + S6_EXTBINPREFIX "s6-applyuidgid -u ") < 0 + || buffer_put(b, fmt, uint64_fmt(fmt, uncaught_logs_uid)) < 0 + || buffer_puts(b, " -g ") < 0 + || buffer_put(b, fmt, gid_fmt(fmt, uncaught_logs_gid)) < 0 + || buffer_puts(b, " --\n" S6_EXTBINPREFIX "s6-log -bp -- ") < 0 + || buffer_puts(b, timestamp_style & 1 ? "t " : "") < 0 + || buffer_puts(b, timestamp_style & 2 ? "T " : "") < 0) return 0 ; + if (!string_quote(&satmp, slashrun, str_len(slashrun))) return 0 ; + if (buffer_put(b, satmp.s + sabase, satmp.len - sabase) < 0) + { + satmp.len = sabase ; + return 0 ; + } + satmp.len = sabase ; + if (buffer_puts(b, "/uncaught-logs\n") < 0) return 0 ; + return 1 ; +} + +static int finish_script (buffer *b) +{ + unsigned int sabase = satmp.len ; + if (buffer_puts(b, + "#!" EXECLINE_EXTBINPREFIX "execlineb -S0\n\n" + EXECLINE_EXTBINPREFIX "cd /\n" + EXECLINE_EXTBINPREFIX "redirfd -w 2 /dev/console\n" + EXECLINE_EXTBINPREFIX "fdmove -c 1 2\n" + EXECLINE_EXTBINPREFIX "wait { }\n") < 0 + || !string_quote(&satmp, shutdown_script, str_len(shutdown_script))) return 0 ; + if (buffer_put(b, satmp.s + sabase, satmp.len - sabase) < 0) + { + satmp.len = sabase ; + return 0 ; + } + satmp.len = sabase ; + if (buffer_puts(b, " ${@}\n") < 0) return 0 ; + return 1 ; +} + +static void cleanup (char const *base) +{ + int e = errno ; + rm_rf(base) ; + errno = e ; +} + +static void auto_dir (char const *base, char const *dir, uid_t uid, gid_t gid, unsigned int mode) +{ + unsigned int clen = str_len(base) ; + unsigned int dlen = str_len(dir) ; + char fn[clen + dlen + 2] ; + byte_copy(fn, clen, base) ; + fn[clen] = dlen ? '/' : 0 ; + byte_copy(fn + clen + 1, dlen + 1, dir) ; + if (mkdir(fn, mode) < 0 + || ((uid || gid) && (chown(fn, uid, gid) < 0 || chmod(fn, mode) < 0))) + { + cleanup(base) ; + strerr_diefu2sys(111, "mkdir ", fn) ; + } +} + +static void auto_file (char const *base, char const *file, char const *s, unsigned int n, int executable) +{ + unsigned int clen = str_len(base) ; + unsigned int flen = str_len(file) ; + char fn[clen + flen + 2] ; + byte_copy(fn, clen, base) ; + fn[clen] = '/' ; + byte_copy(fn + clen + 1, flen + 1, file) ; + if (!openwritenclose_unsafe(fn, s, n) + || (executable && chmod(fn, 0755) < 0)) + { + cleanup(base) ; + strerr_diefu2sys(111, "write to ", fn) ; + } +} + +static void auto_fifo (char const *base, char const *fifo) +{ + unsigned int baselen = str_len(base) ; + unsigned int fifolen = str_len(fifo) ; + char fn[baselen + fifolen + 2] ; + byte_copy(fn, baselen, base) ; + fn[baselen] = '/' ; + byte_copy(fn + baselen + 1, fifolen + 1, fifo) ; + if (mkfifo(fn, 0600) < 0) + { + cleanup(base) ; + strerr_diefu2sys(111, "mkfifo ", fn) ; + } +} + +static void auto_script (char const *base, char const *file, writetobuf_func_t_ref scriptf) +{ + char buf[4096] ; + buffer b ; + int fd ; + unsigned int baselen = str_len(base) ; + unsigned int filelen = str_len(file) ; + char fn[baselen + filelen + 2] ; + byte_copy(fn, baselen, base) ; + fn[baselen] = '/' ; + byte_copy(fn + baselen + 1, filelen + 1, file) ; + fd = open_trunc(fn) ; + if (fd < 0 || ndelay_off(fd) < 0 || fchmod(fd, 0755) < 0) + { + cleanup(base) ; + strerr_diefu3sys(111, "open ", fn, " for script writing") ; + } + buffer_init(&b, &fd_writesv, fd, buf, 4096) ; + if (!(*scriptf)(&b) || !buffer_flush(&b)) + { + cleanup(base) ; + strerr_diefu2sys(111, "write to ", fn) ; + } + close(fd) ; +} + +static void make_image (char const *base) +{ + auto_dir(base, "run-image", 0, 0, 0755) ; + auto_dir(base, "run-image/uncaught-logs", uncaught_logs_uid, uncaught_logs_gid, 02700) ; + auto_dir(base, "run-image/service", 0, 0, 0755) ; + auto_dir(base, "run-image/service/.s6-svscan", 0, 0, 0755) ; + auto_file(base, "run-image/service/.s6-svscan/crash", CRASH_SCRIPT, sizeof(CRASH_SCRIPT) - 1, 1) ; + auto_script(base, "run-image/service/.s6-svscan/finish", &finish_script) ; + auto_dir(base, "run-image/service/s6-svscan-log", 0, 0, 0755) ; + auto_fifo(base, "run-image/service/s6-svscan-log/fifo") ; + auto_script(base, "run-image/service/s6-svscan-log/run", &s6_svscan_log_script) ; + if (early_getty) + { + auto_dir(base, "run-image/service/early-getty", 0, 0, 0755) ; + auto_script(base, "run-image/service/early-getty/run", &early_getty_script) ; + } +} + +static void make_env (char const *base, char const *modif, unsigned int modiflen) +{ + auto_dir(base, "env", 0, 0, 0755) ; + while (modiflen) + { + unsigned int len = str_len(modif) ; + unsigned int pos = byte_chr(modif, len, '=') ; + char fn[5 + pos] ; + byte_copy(fn, 4, "env/") ; + byte_copy(fn + 4, pos, modif) ; + fn[4 + pos] = 0 ; + + if (pos + 1 < len) auto_file(base, fn, modif + pos + 1, len - pos - 1, 0) ; + else if (pos + 1 == len) auto_file(base, fn, "\n", 1, 0) ; + else auto_file(base, fn, "", 0, 0) ; + modif += len+1 ; modiflen -= len+1 ; + } +} + +static int make_init_script (buffer *b) +{ + unsigned int sabase = satmp.len, pos, pos2 ; + char fmt[UINT_OFMT] ; + if (buffer_puts(b, "#!") < 0 + || buffer_puts(b, bindir) < 0 + || buffer_puts(b, "/execlineb -P\n\n") < 0 + || buffer_puts(b, bindir) < 0 + || buffer_puts(b, "/export PATH ") < 0 + || !string_quote(&satmp, initial_path, str_len(initial_path))) return 0 ; + if (buffer_put(b, satmp.s + sabase, satmp.len - sabase) < 0) goto err ; + satmp.len = sabase ; + if (buffer_put(b, "\n", 1) < 0 + || buffer_puts(b, bindir) < 0 + || buffer_puts(b, "/cd /\n" + EXECLINE_EXTBINPREFIX "umask 0") < 0 + || buffer_put(b, fmt, uint_ofmt(fmt, initial_umask)) < 0 + || buffer_puts(b, "\n" + EXECLINE_EXTBINPREFIX "if { " + S6_PORTABLE_UTILS_EXTBINPREFIX "s6-echo -- \"" BANNER1 "\" }\n" + EXECLINE_EXTBINPREFIX "if { " + S6_LINUX_UTILS_EXTBINPREFIX "s6-mount -nwt tmpfs -o mode=0755 tmpfs ") < 0 + || !string_quote(&satmp, slashrun, str_len(slashrun))) return 0 ; + pos = satmp.len ; + if (buffer_put(b, satmp.s + sabase, pos - sabase) < 0 + || buffer_puts(b, " }\n" + EXECLINE_EXTBINPREFIX "if { " + S6_PORTABLE_UTILS_EXTBINPREFIX "s6-hiercopy ") < 0 + || !string_quote(&satmp, robase, str_len(robase))) return 0 ; + pos2 = satmp.len ; + if (buffer_put(b, satmp.s + pos, pos2 - pos) < 0 + || buffer_puts(b, "/run-image ") < 0 + || buffer_put(b, satmp.s + sabase, pos - sabase) < 0 + || buffer_puts(b, " }\n") < 0) goto err ; + if (slashdev_style == 1) + { + if (buffer_puts(b, + EXECLINE_EXTBINPREFIX "if { " + S6_LINUX_UTILS_EXTBINPREFIX "s6-mount -nt devtmpfs dev /dev }\n") < 0) + goto err ; + } + if (buffer_puts(b, + EXECLINE_EXTBINPREFIX "redirfd -r 0 /dev/null\n" + S6_EXTBINPREFIX "s6-envdir -I -- ") < 0 + || buffer_put(b, satmp.s + pos, pos2 - pos) < 0 + || buffer_puts(b, "/env\n" + EXECLINE_EXTBINPREFIX "background\n{\n " + S6_EXTBINPREFIX "s6-setsid --\n " + EXECLINE_EXTBINPREFIX "redirfd -w 2 ") < 0 + || buffer_put(b, satmp.s + sabase, pos - sabase) < 0 + || buffer_puts(b, "/service/s6-svscan-log/fifo\n " + EXECLINE_EXTBINPREFIX "if { " + S6_PORTABLE_UTILS_EXTBINPREFIX "s6-echo -- \"" BANNER2 "\" }\n " + EXECLINE_EXTBINPREFIX "fdmove -c 1 2\n ") < 0 + || !string_quote(&satmp, init_script, str_len(init_script)) + || buffer_put(b, satmp.s + pos2, satmp.len - pos2) < 0 + || buffer_puts(b, "\n}\n" + EXECLINE_EXTBINPREFIX "unexport !\n" + EXECLINE_EXTBINPREFIX "redirfd -wnb 1 ") < 0 + || buffer_put(b, satmp.s + sabase, pos - sabase) < 0 + || buffer_puts(b, + "/service/s6-svscan-log/fifo\n" + EXECLINE_EXTBINPREFIX "fdmove -c 2 1\n" + EXECLINE_EXTBINPREFIX "cd ") < 0 + || buffer_put(b, satmp.s + sabase, pos - sabase) < 0 + || buffer_puts(b, "/service\n") < 0) goto err ; + if (sizeof(S6_EXTBINPREFIX) > 1) + { + if (buffer_puts(b, EXECLINE_EXTBINPREFIX "exec -a s6-svscan --\n") < 0) + goto err ; + } + if (buffer_puts(b, S6_EXTBINPREFIX "s6-svscan -t0\n") < 0) goto err ; + return 1 ; + err: + satmp.len = sabase ; + return 0 ; +} + +int main (int argc, char const *const *argv) +{ + char const *catchall_user = "nobody" ; + stralloc modif = STRALLOC_ZERO ; + PROG = "s6-linux-init-maker" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "c:l:b:u:g:2:3:p:m:t:d:e:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'c' : robase = l.arg ; break ; + case 'l' : slashrun = l.arg ; break ; + case 'b' : bindir = l.arg ; break ; + case 'u' : catchall_user = l.arg ; break ; + case 'g' : early_getty = l.arg ; break ; + case '2' : init_script = l.arg ; break ; + case '3' : shutdown_script = l.arg ; break ; + case 'p' : initial_path = l.arg ; break ; + case 'm' : if (!uint0_oscan(l.arg, &initial_umask)) dieusage() ; break ; + case 't' : if (!uint0_scan(l.arg, ×tamp_style)) dieusage() ; break ; + case 'd' : if (!uint0_scan(l.arg, &slashdev_style)) dieusage() ; break ; + case 'e' : if (!stralloc_catb(&modif, l.arg, str_len(l.arg) + 1)) dienomem() ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + } + if (!argc) dieusage() ; + + if (robase[0] != '/') + strerr_dief3x(100, "base directory ", robase, " is not absolute") ; + if (slashrun[0] != '/') + strerr_dief3x(100, "tmpfs directory ", slashrun, " is not absolute") ; + if (bindir[0] != '/') + strerr_dief3x(100, "initial location for binaries ", bindir, " is not absolute") ; + if (init_script[0] != '/') + strerr_dief3x(100, "stage 2 script location ", init_script, " is not absolute") ; + if (shutdown_script[0] != '/') + strerr_dief3x(100, "stage 3 script location ", shutdown_script, " is not absolute") ; + if (timestamp_style > 3) + strerr_dief1x(100, "-t timestamp_style must be 0, 1, 2 or 3") ; + if (slashdev_style > 2) + strerr_dief1x(100, "-d dev_style must be 0, 1 or 2") ; + + + { + struct passwd *pw = getpwnam(catchall_user) ; + if (!pw) + { + if (errno) + strerr_diefu2sys(111, "getpwnam for ", catchall_user) ; + else + strerr_dief3x(100, "getpwnam for ", catchall_user, ": no such user") ; + } + uncaught_logs_uid = pw->pw_uid ; + uncaught_logs_gid = pw->pw_gid ; + } + + if (mkdir(argv[0], 0755) < 0) + strerr_diefu2sys(111, "mkdir ", argv[0]) ; + + make_env(argv[0], modif.s, modif.len) ; + stralloc_free(&modif) ; + make_image(argv[0]) ; + if (!make_init_script(buffer_1) || !buffer_flush(buffer_1)) + strerr_diefu1sys(111, "write to stdout") ; + return 0 ; +} |