From 381f46674a3749e89daaba4fe7c81a5c08f748d8 Mon Sep 17 00:00:00 2001 From: Nathan Wagner Date: Thu, 7 Mar 2019 20:03:25 +0000 Subject: [PATCH] expand script functionality --- doc/zpm-script.8 | 57 +++++++++++++++++++++++++++-- src/script.c | 95 +++++++++++++++++++++++++++++++++++------------- 2 files changed, 122 insertions(+), 30 deletions(-) diff --git a/doc/zpm-script.8 b/doc/zpm-script.8 index bf5bbb7..97d1a9a 100644 --- a/doc/zpm-script.8 +++ b/doc/zpm-script.8 @@ -1,10 +1,10 @@ -.TH zpm-script 8 2019-02-15 "ZPM 0.3" +.TH zpm-script 8 2019-03-06 "ZPM 0.7.0" .SH NAME zpm-script \- run, set, or list package scripts .SH SYNOPSIS .B zpm script [ -.B -Fhq +.B -Fhql ] [ .BI -f " pkgfile" @@ -22,6 +22,9 @@ zpm-script \- run, set, or list package scripts .BI -S " script" ] .I package +[ +.I arg +] .SH DESCRIPTION \fBzpm-script\fR runs, sets, or lists package scripts for a package. The package is a package @@ -38,8 +41,11 @@ to /var/tmp/zpm-script.out, or as specified by the -o option. The script itself will be extracted to /var/tmp/zpm-script, set as executable, and then run. The script is run with one or two arguments. The first argument is the package id being installed, removed, or configured. -For a package version update (or downgrade) the second argument is -the previous version of the package. +If given \fIarg\fR is passed to the script as a second argument. +For a package version update (or downgrade) this is the previous version of the +package. +.PP If the script fails (i.e. any non-zero exit status), the output will +be imported into the database and a note will be added. .TP An example script .EX @@ -53,12 +59,40 @@ Set mode will set the script for a given package and phase to the path given as an additional argument. If the -h is given, the script is interpreted as a hash to be set directly, and nothing is taken from the filesystem. +.SS List Mode +If a phase is given with \-p the script will be printed to stdout (or +wherever \-o specifies). If no phase is given, a list of scripts for +the package will be printed to stdout. .SH OPTIONS .TP +.B \-l +Run in list mode. +.TP +.B \-r +Run in run mode. This is the default. +.TP +.B \-s +Run in set mode. +.TP .BI \-p phase Specify the script phase. This defaults to all phases for list mode, and configure for running or setting scripts. .TP +.B \-n +Do not create notes. Overrides other note options. +.TP +.B \-N +Create notes only if the database is writable, otherwise send +output to stdout. +.TP +.B \-a +Make a note for all output, regardless of the exit status. +.TP +.B \-e +Allow empty notes to be created for the \-a option. +Normally if the output is empty and the script succeeded, the \-a option +will not cause a note to be created. +.TP .B \-f specify the package file to find packages in .TP @@ -68,6 +102,18 @@ than a path to a script. .TP .B \-q Run quietly. +.TP +.B \-v +Send any output to stdout. +.TP +.BI \-R " rootdir" +The current directory will be changed to \fIrootdir\fR before +running the script. Defaults to '/'. No chroot is done +before running the script, so scripts should be aware of this +and generally use relative paths. If neccessary and possible +the script itself can chroot. Calling chroot(2) requires superuser +privileges, so it would be impossible to run the scripts as a non +root user if chroot was attempted. .SH EXAMPLES .TP zpm script -f foo-1.0-1.zpm foo @@ -77,6 +123,9 @@ file. zpm script foo Run the configure script for the (most recent) foo package found in the local database. +.TP +zpm script -l foo +List all scripts for the foo package. .SH EXIT STATUS 0 on success non zero on failure .SH FILES diff --git a/src/script.c b/src/script.c index 7c03b24..f0b173f 100644 --- a/src/script.c +++ b/src/script.c @@ -125,7 +125,7 @@ static int list_scripts(void *ud, const char *pkg, const char *stage, int main(int ac, char **av){ struct zpm zpm; int rv; - int status; + int status = 0; int required = 0; int fail = 0; char *pkgstr; @@ -137,12 +137,13 @@ int main(int ac, char **av){ char *rootdir = 0; char *db = "/var/lib/zpm/local.db"; - char *script = "/var/tmp/zpm-script"; - char *output = "/var/tmp/zpm-script.out"; + char *script = 0; + char *output = 0; char *phase = 0; - char *envvar = 0; - int quiet = 0; - int scriptishash = 0; + int quiet = 0, verbose = 0; + int allowempty = 0, notealways = 0, makenote = 0, showoutput = 0; + int scriptishash = 0, notenever = 0, noteifwritable = 0; + size_t outsize; int mode = RUN; if (getenv("ZPMDB")) { @@ -151,16 +152,8 @@ int main(int ac, char **av){ /* 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; - } + script = getenv("ZPM_SCRIPT_FILE"); + output = getenv("ZPM_SCRIPT_OUTPUT"); /* run, set, show, hash */ /* set -S, if -H set the hash, output hash, unless quiet @@ -176,7 +169,7 @@ int main(int ac, char **av){ * show: zpm-script -l [-a] [-o outfile] * show hash: zpm-script -lh */ - while ((opt = getopt(ac, av, "f:p:rslRFho:S:q")) != -1) { + while ((opt = getopt(ac, av, "f:p:rslRFho:S:qv")) != -1) { switch (opt) { case 'f': db = optarg; break; case 'p': phase = optarg; break; @@ -185,11 +178,15 @@ int main(int ac, char **av){ case 'l': mode = LIST; break; case 'R': rootdir = optarg; break; case 'F': required = 1; break; - + case 'a': notealways = 1; break; + case 'n': notenever = 1; break; + case 'N': noteifwritable = 1; break; + case 'e': allowempty = 1; break; case 'h': scriptishash = 1; break; case 'o': output = optarg; break; case 'S': script = optarg; break; case 'q': quiet = 1; break; + case 'v': verbose = 1; break; default: usage(); @@ -305,33 +302,79 @@ int main(int ac, char **av){ if (!zpm_script_hash(&zpm, pkgid, phase, hash)) { fail = SOFT; + status = EXIT_FAILURE; goto cleanup; } if (!setdir(rootdir)) { fail = HARD; + status = EXIT_FAILURE; goto cleanup; } if (!zpm_extract(&zpm, hash, script, 0700)) { fprintf(stderr, "unable to extract script"); fail = HARD; + status = EXIT_FAILURE; 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); + status = WEXITSTATUS(status); + if (status != 0) { fail = HARD; } - /* TODO log output */ + if (zpm_readonly(&zpm) && noteifwritable) { + notenever = 1; + } + + if (output && !notenever) { + struct stat outstat; + stat(output, &outstat); + outsize = outstat.st_size; + if (notealways || (outsize && status && (outsize || allowempty))) { + makenote = 1; + } + } + + if (makenote) { + char note[1024]; + + zpm_import(&zpm, output, 0, hash); + + if (status) { + sprintf(note, "%.64s script failed with code %d", + phase, status); + + fail = HARD; + } else { + sprintf(note, "%.64s script succeeded", phase); + } + zpm_note_add(&zpm, pkgid, NULL, hash, note); + } + + if (output && showoutput) { + char buf[4096]; + int out = open(output, O_RDONLY); + ssize_t bytes; + if (out == -1) { + perror("can't open output file"); + } else { + while ((bytes = read(out, buf, sizeof buf)) > 0) { + write(1, buf, bytes); + } + if (bytes == -1) { + perror("output file read error"); + } + } + if (out != -1) { + close(out); + } + } + if (script) { unlink(script); } @@ -344,5 +387,5 @@ cleanup: free(pkgid); zpm_close(&zpm); - return (fail == HARD || (required && fail)) ? EXIT_FAILURE : 0; + return (fail == HARD || (required && fail)) ? status : 0; } -- 2.40.0