#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; }