X-Git-Url: https://pd.if.org/git/?a=blobdiff_plain;f=src%2Fscript.c;fp=src%2Fscript.c;h=7c03b241792ae1f91ea504215b1400526580c71b;hb=5dd3c3e64a9574112dda77a5afc167f5daa53fd8;hp=0000000000000000000000000000000000000000;hpb=bd21f0a1265b43ad5f05353a39db31c16826f05c;p=zpackage diff --git a/src/script.c b/src/script.c new file mode 100644 index 0000000..7c03b24 --- /dev/null +++ b/src/script.c @@ -0,0 +1,348 @@ +#define _POSIX_C_SOURCE 200809L + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "zpm.h" + +/* + * -f package file, otherwise localdb + * -p phase, defaults to 'configure' + * -s script name, defaults to /var/tmp/zpm-script + * -r chroot to directory + * -o script output, /var/tmp/zpm-script.out, '-' for stdout + * + * arg is package id + */ + +void usage(void) { + fprintf(stderr, "usage: zpm script ...\n"); +} + +int setdir(char *rootdir) { + if (chdir(rootdir ? rootdir : "/") == -1) { + perror("can not chdir to rootdir"); + return 0; + } +#if 0 + if (rootdir && strcmp(rootdir, "/")) { + if (geteuid() == 0) { + /* chroot is deprecated, and not in posix. need to use + * OS/kernel specific code. + */ + fprintf(stderr, "support for chroot equivalent not supported on this OS\n"); + } else { + fprintf(stderr, "unable to chroot as non root user\n"); + } + } +#endif + return 1; +} + + +int run(char *program, char **args, char *output, int *status) { + /* if stdout is a terminal, leave stdout where it goes, + * if it's not a terminal, redirect stdout to output. + * in either case, send stderr to output, unless null + * if output is null or "-", just run the program + */ + int interactive = 0; + pid_t pid; + int rv; + + errno = 0; + + interactive = isatty(1); + + pid = fork(); + + if (pid == -1) { + *status = 1; + return -1; + } + + if (pid == 0) { + /* child */ + if (output && strcmp(output, "-") != 0) { + close(2); + rv = open(output, O_NOFOLLOW|O_TRUNC|O_CREAT|O_WRONLY, + 0600); + if (rv == -1) { + perror("cannot open output file"); + } + if (!interactive) { + rv = dup2(2,1); + if (rv == -1) { + perror("unable to redirect stdout"); + exit(EXIT_FAILURE); + } + } + } + + rv = execvp(program, args); + if (rv == -1) { + perror("cannot exec"); + exit(EXIT_FAILURE); + } + } + + pid = wait(status); + if (pid == -1) { + perror("error waiting for child"); + return -2; + } + + if (WIFEXITED(*status)) { + return WEXITSTATUS(*status); + } + + return -3; +} + +#define RUN 1 +#define SET 2 +#define LIST 3 + +#define SOFT 1 +#define HARD 2 + +static int list_scripts(void *ud, const char *pkg, const char *stage, + const char *hash) { + printf("%s %s %.8s\n", pkg, stage, hash); + return 0; +} + +int main(int ac, char **av){ + struct zpm zpm; + int rv; + int status; + int required = 0; + int fail = 0; + char *pkgstr; + int opt; + + char hash[ZPM_HASH_STRLEN+1]; + char *args[4]; + char *pkgid; + + char *rootdir = 0; + char *db = "/var/lib/zpm/local.db"; + char *script = "/var/tmp/zpm-script"; + char *output = "/var/tmp/zpm-script.out"; + char *phase = 0; + char *envvar = 0; + int quiet = 0; + int scriptishash = 0; + int mode = RUN; + + if (getenv("ZPMDB")) { + db = getenv("ZPMDB"); + } + /* ZPM_PACKAGE_FILE ? */ + + rootdir = getenv("ZPM_ROOT_DIR"); + + envvar = getenv("ZPM_SCRIPT_FILE"); + if (envvar) { + script = envvar; + } + + envvar = getenv("ZPM_SCRIPT_OUTPUT"); + if (envvar) { + output = envvar; + } + + /* run, set, show, hash */ + /* set -S, if -H set the hash, output hash, unless quiet + * show: -o, or stdout, + * All modes: [-f pkgfile] default to zpmdb + * All modes: [-p phase], default to configure + * All modes: [-R rootdir], chdir, attempt to chroot + * All modes: [-N], required, error if no such script + * + * run: zpm-script -r -p foo + * set: zpm-script -s + * set: zpm-script -s -h + * show: zpm-script -l [-a] [-o outfile] + * show hash: zpm-script -lh + */ + while ((opt = getopt(ac, av, "f:p:rslRFho:S:q")) != -1) { + switch (opt) { + case 'f': db = optarg; break; + case 'p': phase = optarg; break; + case 'r': mode = RUN; break; + case 's': mode = SET; break; + case 'l': mode = LIST; break; + case 'R': rootdir = optarg; break; + case 'F': required = 1; break; + + case 'h': scriptishash = 1; break; + case 'o': output = optarg; break; + case 'S': script = optarg; break; + case 'q': quiet = 1; break; + + default: + usage(); + exit(EXIT_FAILURE); + break; + } + } + int argn = optind; + + if (argn >= ac) { + usage(); + exit(EXIT_FAILURE); + } + + if (!zpm_open(&zpm, db)) { + fprintf(stderr, "unable to open zpm database: %s\n", db); + if (zpm.errmsg) { + fprintf(stderr, "error detail: %s\n", zpm.errmsg); + } + exit(EXIT_FAILURE); + } + + /* first non option arg is always a package id */ + pkgstr = av[argn]; + pkgid = zpm_findpkg(&zpm, pkgstr, NULL); + + if (!pkgid) { + fprintf(stderr, "no package for %s\n", pkgstr); + zpm_close(&zpm); + exit(EXIT_FAILURE); + } + + argn++; + + if (mode == SET) { + char *sethash = 0; + char *setscript = 0; + int rv = 0; + + if (!phase) { + phase = "configure"; + } + + if (argn < ac) { + if (scriptishash) { + sethash = av[argn]; + } else { + setscript = av[argn]; + } + } + + if (setscript) { + rv = zpm_import(&zpm,setscript,0,hash); + if (!rv) { + fprintf(stderr, "unable to import %s\n", + setscript); + fail = HARD; + } else { + sethash = hash; + } + } + + if (!fail && !zpm_script_set(&zpm,pkgid,phase,sethash)) { + fprintf(stderr, "unable to set %s script hash %s\n", + pkgid, + sethash ? sethash : "null"); + fprintf(stderr, "zpm error: %s\n", zpm.errmsg); + fail = HARD; + } + } else if (mode == LIST) { + if (!phase) { + zpm_foreach_script(&zpm, pkgid, phase, 0, list_scripts); + } else if (!zpm_script_hash(&zpm, pkgid, phase, hash)) { + fail = SOFT; + } else if (scriptishash) { + if (!quiet) { + printf("%s\n", hash); + } + } else { + if (!output) { + output = "-"; + } + if (!zpm_extract(&zpm, hash, output, 0700)) { + fail = HARD; + } + } + } else { + /* run a script */ + if (!phase) { + phase = "configure"; + } + + if (!output) { + output = "/var/tmp/zpm-script.out"; + } + + if (!script) { + script = "/var/tmp/zpm-script"; + } + + /* since the script file name doesn't really + * mean anything, pass in the phase as arg 0 + */ + /* TODO sanitize environment ? */ + args[0] = phase; + args[1] = pkgid; + args[2] = 0; + args[3] = 0; + + if (argn <= ac) { + args[2] = av[argn]; + } + + if (!zpm_script_hash(&zpm, pkgid, phase, hash)) { + fail = SOFT; + goto cleanup; + } + + if (!setdir(rootdir)) { + fail = HARD; + goto cleanup; + } + + if (!zpm_extract(&zpm, hash, script, 0700)) { + fprintf(stderr, "unable to extract script"); + fail = HARD; + zpm_note_add(&zpm, pkgid, NULL, hash, "unable to extract %s script", phase); + goto cleanup; + } + + rv = run(script, args, output, &status); + if (rv) { + fprintf(stderr, "package %s script failed with code %d\n", + pkgid, rv); + + zpm_import(&zpm, output, 0, hash); + zpm_note_add(&zpm, pkgid, NULL, hash, "package %s script failed with code %d", phase, rv); + + fail = HARD; + } + + /* TODO log output */ + if (script) { + unlink(script); + } + if (output) { + unlink(output); + } + } + +cleanup: + free(pkgid); + zpm_close(&zpm); + + return (fail == HARD || (required && fail)) ? EXIT_FAILURE : 0; +}