diff options
Diffstat (limited to 'src/s6-rc/s6-rc-db.c')
-rw-r--r-- | src/s6-rc/s6-rc-db.c | 484 |
1 files changed, 484 insertions, 0 deletions
diff --git a/src/s6-rc/s6-rc-db.c b/src/s6-rc/s6-rc-db.c new file mode 100644 index 0000000..d0bd845 --- /dev/null +++ b/src/s6-rc/s6-rc-db.c @@ -0,0 +1,484 @@ +/* ISC license. */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <unistd.h> +#include <errno.h> +#include <signal.h> +#include <skalibs/uint32.h> +#include <skalibs/uint.h> +#include <skalibs/allreadwrite.h> +#include <skalibs/bytestr.h> +#include <skalibs/cdb.h> +#include <skalibs/sgetopt.h> +#include <skalibs/buffer.h> +#include <skalibs/strerr2.h> +#include <skalibs/environ.h> +#include <skalibs/direntry.h> +#include <skalibs/djbunix.h> +#include <skalibs/unix-transactional.h> +#include <s6/config.h> +#include <s6-rc/s6rc.h> + +#define USAGE "s6-rc-db [ -u | -d ] [ -l live ] [ -c compiled ] command... (use s6-rc-db help for more information)" +#define dieusage() strerr_dieusage(100, USAGE) + +static char const *compiled = 0 ; +static int fdcompiled = -1 ; +static s6rc_db_t *db ; + +static void print_bundle_contents (char const *name) +{ + cdb_t c = CDB_ZERO ; + int fd = open_readatb(fdcompiled, "resolve.cdb") ; + register int r ; + if (fd < 0) strerr_diefu3sys(111, "open ", compiled, "/resolve.cdb") ; + if (!cdb_init_map(&c, fd, 1)) + strerr_diefu3sys(111, "cdb_init ", compiled, "/resolve.cdb") ; + r = cdb_find(&c, name, str_len(name)) ; + if (r < 0) strerr_diefu3sys(111, "read ", compiled, "/resolve.cdb") ; + if (!r) strerr_dief3x(1, name, " is not a valid identifier in ", compiled) ; + if (cdb_datalen(&c) == 4) + { + uint32 x ; + char pack[4] ; + if (cdb_read(&c, pack, 4, cdb_datapos(&c)) < 0) + strerr_diefu3sys(111, "cdb_read ", compiled, "/resolve.cdb") ; + uint32_unpack_big(pack, &x) ; + if (x >= db->nshort + db->nlong) + strerr_dief2x(4, "invalid database in ", compiled) ; + if (!str_diff(name, db->string + db->services[x].name)) + strerr_dief5x(1, "in database ", compiled, ": identifier ", name, " represents an atomic service") ; + if (buffer_puts(buffer_1, db->string + db->services[x].name) < 0) + strerr_diefu1sys(111, "write to stdout") ; + } + else + { + unsigned int len = cdb_datalen(&c) >> 2 ; + char pack[cdb_datalen(&c) + 1] ; + register char const *p = pack ; + if (cdb_datalen(&c) & 3) + strerr_dief2x(4, "invalid database in ", compiled) ; + if (cdb_read(&c, pack, cdb_datalen(&c), cdb_datapos(&c)) < 0) + strerr_diefu3sys(111, "cdb_read ", compiled, "/resolve.cdb") ; + while (len--) + { + uint32 x ; + uint32_unpack_big(p, &x) ; p += 4 ; + if (x >= db->nshort + db->nlong) + strerr_dief2x(4, "invalid database in ", compiled) ; + if (buffer_puts(buffer_1, db->string + db->services[x].name) < 0 + || buffer_put(buffer_1, "\n", 1) < 0) + strerr_diefu1sys(111, "write to stdout") ; + } + } + cdb_free(&c) ; + close(fd) ; + if (!buffer_flush(buffer_1)) + strerr_diefu1sys(111, "write to stdout") ; +} + +static void print_services (unsigned int from, unsigned int to) +{ + for (; from < to ; from++) + { + if (buffer_puts(buffer_1, db->string + db->services[from].name) < 0 + || buffer_put(buffer_1, "\n", 1) < 0) + strerr_diefu1sys(111, "write to stdout") ; + } + if (!buffer_flush(buffer_1)) + strerr_diefu1sys(111, "write to stdout") ; +} + +static void print_all (int bundlesonly) +{ + cdb_t c = CDB_ZERO ; + uint32 kpos ; + int fd = open_readatb(fdcompiled, "resolve.cdb") ; + if (fd < 0) strerr_diefu3sys(111, "open ", compiled, "/resolve.cdb") ; + if (!cdb_init_map(&c, fd, 1)) + strerr_diefu3sys(111, "cdb_init ", compiled, "/resolve.cdb") ; + cdb_traverse_init(&c, &kpos) ; + for (;;) + { + register int r = cdb_nextkey(&c, &kpos) ; + char name[cdb_keylen(&c) + 1] ; + if (r < 0) strerr_diefu3sys(111, "read ", compiled, "/resolve.cdb") ; + if (!r) break ; + if (cdb_read(&c, name, cdb_keylen(&c), cdb_keypos(&c)) < 0) + strerr_diefu3sys(111, "cdb_read ", compiled, "/resolve.cdb") ; + if (bundlesonly && cdb_datalen(&c) == 4) + { + uint32 x ; + char pack[4] ; + if (cdb_read(&c, pack, 4, cdb_datapos(&c)) < 0) + strerr_diefu3sys(111, "cdb_read ", compiled, "/resolve.cdb") ; + uint32_unpack_big(pack, &x) ; + if (x >= db->nshort + db->nlong) + strerr_dief2x(4, "invalid database in ", compiled) ; + if (!byte_diff(name, cdb_keylen(&c), db->string + db->services[x].name) + && !db->string[db->services[x].name + cdb_keylen(&c)]) + continue ; + } + name[cdb_keylen(&c)] = '\n' ; + if (buffer_put(buffer_1, name, cdb_keylen(&c) + 1) < 0) + strerr_diefu1sys(111, "write to stdout") ; + } + cdb_free(&c) ; + close(fd) ; + if (!buffer_flush(buffer_1)) + strerr_diefu1sys(111, "write to stdout") ; +} + +static unsigned int resolve_service (char const *name) +{ + cdb_t c = CDB_ZERO ; + int fd = open_readatb(fdcompiled, "resolve.cdb") ; + uint32 x ; + char pack[4] ; + unsigned int len ; + register int r ; + if (fd < 0) strerr_diefu3sys(111, "open ", compiled, "/resolve.cdb") ; + if (!cdb_init_map(&c, fd, 1)) + strerr_diefu3sys(111, "cdb_init ", compiled, "/resolve.cdb") ; + r = cdb_find(&c, name, str_len(name)) ; + if (r < 0) strerr_diefu3sys(111, "read ", compiled, "/resolve.cdb") ; + if (!r) strerr_dief3x(1, name, " is not a valid identifier in ", compiled) ; + len = cdb_datalen(&c) >> 2 ; + if (cdb_datalen(&c) != 4) return db->nshort + db->nlong ; + if (cdb_read(&c, pack, 4, cdb_datapos(&c)) < 0) + strerr_diefu3sys(111, "cdb_read ", compiled, "/resolve.cdb") ; + uint32_unpack_big(pack, &x) ; + cdb_free(&c) ; + close(fd) ; + if (x >= db->nshort + db->nlong) + strerr_dief2x(4, "invalid database in ", compiled) ; + if (str_diff(name, db->string + db->services[x].name)) + x = db->nshort + db->nlong ; + return x ; +} + +static void print_type (char const *name) +{ + unsigned int n = resolve_service(name) ; + char const *s = n >= db->nshort + db->nlong ? "bundle" : db->services[n].type ? "longrun" : "oneshot" ; + if (buffer_puts(buffer_1, s) < 0 + || buffer_putflush(buffer_1, "\n", 1) < 0) + strerr_diefu1sys(111, "write to stdout") ; +} + +static void print_timeout (char const *name, int h) +{ + unsigned int n = resolve_service(name) ; + unsigned int len ; + char fmt[UINT32_FMT] ; + if (n >= db->nshort + db->nlong) + strerr_dief5x(1, "in database ", compiled, ": identifier ", name, " represents a bundle") ; + len = uint32_fmt(fmt, db->services[n].timeout[h]) ; + fmt[len++] = '\n' ; + if (buffer_putflush(buffer_1, fmt, len) < 0) + strerr_diefu1sys(111, "write to stdout") ; +} + +static void print_servicedir (char const *name) +{ + unsigned int n = resolve_service(name) ; + if (n >= db->nlong) + strerr_dief5x(1, "in database ", compiled, ": identifier ", name, " does not represent a longrun service") ; + if (buffer_puts(buffer_1, db->string + db->services[n].x.longrun.servicedir) < 0 + || buffer_putflush(buffer_1, "\n", 1) < 0) + strerr_diefu1sys(111, "write to stdout") ; +} + +static void print_deps (char const *name, int h) +{ + uint32 const *p ; + unsigned int ndeps ; + unsigned int n = resolve_service(name) ; + if (n >= db->nshort + db->nlong) + strerr_dief5x(1, "in database ", compiled, ": identifier ", name, " represents a bundle") ; + p = db->deps + h * db->ndeps + db->services[n].deps[h] ; + ndeps = db->services[n].ndeps[h] ; + while (ndeps--) + if (buffer_puts(buffer_1, db->string + db->services[*p++].name) < 0 + || buffer_put(buffer_1, "\n", 1) < 0) + strerr_diefu1sys(111, "write to stdout") ; + if (!buffer_flush(buffer_1)) + strerr_diefu1sys(111, "write to stdout") ; +} + +static void print_script (char const *name, int h) +{ + unsigned int argc ; + char *const *argv ; + unsigned int n = resolve_service(name) ; + if (n >= db->nshort + db->nlong) + strerr_dief5x(1, "in database ", compiled, ": identifier ", name, " represents a bundle") ; + if (n < db->nlong) + strerr_dief5x(1, "in database ", compiled, ": identifier ", name, " represents a longrun service") ; + argv = db->argvs + db->services[n].x.oneshot.argv[h] ; + argc = db->services[n].x.oneshot.argc[h] ; + while (argc--) + if (buffer_puts(buffer_1, *argv++) < 0 + || buffer_put(buffer_1, "\0", 1) < 0) + strerr_diefu1sys(111, "write to stdout") ; + if (!buffer_flush(buffer_1)) + strerr_diefu1sys(111, "write to stdout") ; +} + +static inline void print_flags (char const *name) +{ + unsigned int n = resolve_service(name) ; + char fmt[9] = "00000000\n" ; + if (n >= db->nshort + db->nlong) + strerr_dief5x(1, "in database ", compiled, ": identifier ", name, " represents a bundle") ; + uint320_xfmt(fmt, db->services[n].flags, 8) ; + if (buffer_putflush(buffer_1, fmt, 9) < 0) + strerr_diefu1sys(111, "write to stdout") ; +} + +static void print_atomics (char const *const *argv, int h, int doclosure) +{ + unsigned int n = db->nshort + db->nlong ; + cdb_t c = CDB_ZERO ; + int fd = open_readatb(fdcompiled, "resolve.cdb") ; + unsigned char state[n] ; + if (fd < 0) strerr_diefu3sys(111, "open ", compiled, "/resolve.cdb") ; + if (!cdb_init_map(&c, fd, 1)) + strerr_diefu3sys(111, "cdb_init ", compiled, "/resolve.cdb") ; + byte_zero(state, n) ; + for (; *argv ; argv++) + { + register int r = cdb_find(&c, *argv, str_len(*argv)) ; + if (r < 0) strerr_diefu3sys(111, "read ", compiled, "/resolve.cdb") ; + if (!r) strerr_dief3x(1, *argv, " is not a valid identifier in ", compiled) ; + { + unsigned int len = cdb_datalen(&c) >> 2 ; + char pack[cdb_datalen(&c) + 1] ; + register char const *p = pack ; + if (cdb_read(&c, pack, cdb_datalen(&c), cdb_datapos(&c)) < 0) + strerr_diefu3sys(111, "cdb_read ", compiled, "/resolve.cdb") ; + while (len--) + { + uint32 x ; + uint32_unpack_big(p, &x) ; p += 4 ; + if (x >= db->nshort + db->nlong) + strerr_dief2x(4, "invalid database in ", compiled) ; + state[x] |= 1 ; + } + } + } + cdb_free(&c) ; + close(fd) ; + if (doclosure) s6rc_graph_closure(db, state, 0, h) ; + while (n--) if (state[n]) + if (buffer_puts(buffer_1, db->string + db->services[n].name) < 0 + || buffer_put(buffer_1, "\n", 1) < 0) + strerr_diefu1sys(111, "write to stdout") ; + if (!buffer_flush(buffer_1)) + strerr_diefu1sys(111, "write to stdout") ; +} + +static inline void print_help (void) +{ + static char const *help = +"s6-rc-db help\n" +"s6-rc-db check\n" +"s6-rc-db list all|services|oneshots|longruns|bundles\n" +"s6-rc-db type servicename\n" +"s6-rc-db [ -u | -d ] timeout atomicname\n" +"s6-rc-db contents bundlename\n" +"s6-rc-db [ -u | -d ] dependencies servicename\n" +"s6-rc-db servicedir longrunname\n" +"s6-rc-db [ -u | -d ] script oneshotname\n" +"s6-rc-db flags atomicname\n" +"s6-rc-db atomics servicename...\n" +"s6-rc-db [ -u | -d ] all-dependencies servicename...\n" ; + if (buffer_putsflush(buffer_1, help) < 0) + strerr_diefu1sys(111, "write to stdout") ; +} + +static unsigned int lookup (char const *const *table, char const *command) +{ + register unsigned int i = 0 ; + for (; table[i] ; i++) if (!str_diff(command, table[i])) break ; + return i ; +} + +static inline unsigned int parse_command (char const *command) +{ + static char const *const command_table[13] = + { + "help", + "check", + "list", + "type", + "timeout", + "contents", + "dependencies", + "servicedir", + "script", + "flags", + "atomics", + "all-dependencies", + 0 + } ; + register unsigned int i = lookup(command_table, command) ; + if (!command_table[i]) dieusage() ; + return i ; +} + +static inline unsigned int parse_list (char const *command) +{ + static char const *const list_table[6] = + { + "all", + "services", + "oneshots", + "longruns", + "bundles", + 0 + } ; + register unsigned int i = lookup(list_table, command) ; + if (!list_table[i]) dieusage() ; + return i ; +} + +int main (int argc, char const *const *argv) +{ + char const *live = S6RC_LIVE_BASE ; + unsigned int what, subwhat = 0 ; + int up = 1 ; + PROG = "s6-rc-db" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "udl:c:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'l' : live = l.arg ; break ; + case 'c' : compiled = l.arg ; break ; + case 'u' : up = 1 ; break ; + case 'd' : up = 0 ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + } + + if (!argc) dieusage() ; + what = parse_command(argv[0]) ; + if (!what) + { + print_help() ; + return 0 ; + } + if (what >= 2 && argc < 2) dieusage() ; + if (what == 2) subwhat = 1 + parse_list(argv[1]) ; + + { + unsigned int livelen = str_len(live) ; + s6rc_db_t dbblob ; + char compiledblob[compiled ? str_len(compiled) : livelen + 10] ; + db = &dbblob ; + + if (!compiled) + { + byte_copy(compiledblob, livelen, live) ; + byte_copy(compiledblob + livelen, 10, "/compiled") ; + compiled = compiledblob ; + } + + fdcompiled = open_readb(compiled) ; + if (fdcompiled < 0) + strerr_diefu2sys(111, "open ", compiled) ; + + + /* Read the sizes of the compiled db */ + + fdcompiled = open_readb(compiled) ; + if (!s6rc_db_read_sizes(fdcompiled, &dbblob)) + strerr_diefu3sys(111, "read ", compiled, "/n") ; + + + /* Allocate enough stack for the db */ + + { + unsigned int n = dbblob.nshort + dbblob.nlong ; + s6rc_service_t serviceblob[n] ; + char *argvblob[dbblob.nargvs] ; + uint32 depsblob[dbblob.ndeps << 1] ; + char stringblob[dbblob.stringlen] ; + register int r ; + + dbblob.services = serviceblob ; + dbblob.argvs = argvblob ; + dbblob.deps = depsblob ; + dbblob.string = stringblob ; + + + /* Read the db from the file */ + + r = s6rc_db_read(fdcompiled, &dbblob) ; + if (r < 0) strerr_diefu3sys(111, "read ", compiled, "/db") ; + if (!r) strerr_dief3x(4, "invalid service database in ", compiled, "/db") ; + + + /* Perform the action */ + + switch (what) + { + case 1 : /* check */ + { + unsigned int problem, w ; + if (!s6rc_db_check_revdeps(&dbblob)) + strerr_dief3x(4, "invalid service database in ", compiled, ": direct and reverse dependencies are mismatched") ; + w = s6rc_db_check_depcycles(&dbblob, 1, &problem) ; + if (w < n) strerr_dief6x(4, "invalid service database in ", compiled, ": service ", stringblob + serviceblob[w].name, " has a dependency cycle involving ", stringblob + serviceblob[problem].name) ; + break ; + } + case 2 : /* list */ + switch (subwhat) + { + case 1 : print_all(0) ; break ; + case 2 : print_services(0, n) ; break ; + case 3 : print_services(dbblob.nlong, n) ; break ; + case 4 : print_services(0, dbblob.nlong) ; break ; + case 5 : print_all(1) ; break ; + } + break ; + case 3 : /* type */ + print_type(argv[1]) ; + break ; + case 4 : /* timeout */ + print_timeout(argv[1], up) ; + break ; + case 5 : /* contents */ + print_bundle_contents(argv[1]) ; + break ; + case 6 : /* dependencies */ + print_deps(argv[1], up) ; + break ; + case 7 : /* servicedir */ + print_servicedir(argv[1]) ; + break ; + case 8 : /* script */ + print_script(argv[1], up) ; + break ; + case 9 : /* flags */ + print_flags(argv[1]) ; + break ; + case 10 : /* atomics */ + print_atomics(argv + 1, 1, 0) ; + break ; + case 11 : /* all-dependencies */ + print_atomics(argv + 1, up, 1) ; + break ; + } + } + } + return 0 ; +} |