8 #include <rpm/rpmfileutil.h>
9 #include <rpm/rpmmacro.h>
10 #include <rpm/rpmio.h>
11 #include <rpm/rpmlog.h>
12 #include <rpm/header.h>
13 #include <rpm/rpmds.h>
15 #include "rpmio/rpmlua.h"
16 #include "lib/rpmscript.h"
18 #include "lib/rpmplugins.h" /* rpm plugins hooks */
22 struct scriptNextFileFunc_s {
23 char *(*func)(void *); /* function producing input for script */
24 void *param; /* parameter for func */
27 typedef struct scriptNextFileFunc_s *scriptNextFileFunc;
30 rpmscriptTypes type; /* script type */
31 rpmTagVal tag; /* script tag */
32 char **args; /* scriptlet call arguments */
33 char *body; /* script body */
34 char *descr; /* description for logging */
35 rpmscriptFlags flags; /* flags to control operation */
36 struct scriptNextFileFunc_s nextFileFunc; /* input function */
48 static const struct scriptInfo_s scriptInfo[] = {
49 { RPMSCRIPT_PREIN, "%prein", 0,
50 RPMTAG_PREIN, RPMTAG_PREINPROG, RPMTAG_PREINFLAGS },
51 { RPMSCRIPT_PREUN, "%preun", 0,
52 RPMTAG_PREUN, RPMTAG_PREUNPROG, RPMTAG_PREUNFLAGS },
53 { RPMSCRIPT_POSTIN, "%post", 0,
54 RPMTAG_POSTIN, RPMTAG_POSTINPROG, RPMTAG_POSTINFLAGS },
55 { RPMSCRIPT_POSTUN, "%postun", 0,
56 RPMTAG_POSTUN, RPMTAG_POSTUNPROG, RPMTAG_POSTUNFLAGS },
57 { RPMSCRIPT_PRETRANS, "%pretrans", 0,
58 RPMTAG_PRETRANS, RPMTAG_PRETRANSPROG, RPMTAG_PRETRANSFLAGS },
59 { RPMSCRIPT_POSTTRANS, "%posttrans", 0,
60 RPMTAG_POSTTRANS, RPMTAG_POSTTRANSPROG, RPMTAG_POSTTRANSFLAGS },
61 { RPMSCRIPT_TRIGGERPREIN, "%triggerprein", RPMSENSE_TRIGGERPREIN,
62 RPMTAG_TRIGGERPREIN, 0, 0 },
63 { RPMSCRIPT_TRIGGERUN, "%triggerun", RPMSENSE_TRIGGERUN,
64 RPMTAG_TRIGGERUN, 0, 0 },
65 { RPMSCRIPT_TRIGGERIN, "%triggerin", RPMSENSE_TRIGGERIN,
66 RPMTAG_TRIGGERIN, 0, 0 },
67 { RPMSCRIPT_TRIGGERPOSTUN, "%triggerpostun", RPMSENSE_TRIGGERPOSTUN,
68 RPMTAG_TRIGGERPOSTUN, 0, 0 },
69 { RPMSCRIPT_VERIFY, "%verify", 0,
70 RPMTAG_VERIFYSCRIPT, RPMTAG_VERIFYSCRIPTPROG, RPMTAG_VERIFYSCRIPTFLAGS},
72 RPMTAG_NOT_FOUND, RPMTAG_NOT_FOUND, RPMTAG_NOT_FOUND }
75 static const struct scriptInfo_s * findTag(rpmTagVal tag)
77 const struct scriptInfo_s * si = scriptInfo;
78 while (si->type && si->tag != tag)
83 * Run internal Lua script.
85 static rpmRC runLuaScript(rpmPlugins plugins, ARGV_const_t prefixes,
86 const char *sname, rpmlogLvl lvl, FD_t scriptFd,
87 ARGV_t * argvp, const char *script, int arg1, int arg2,
88 scriptNextFileFunc nextFileFunc)
90 rpmRC rc = RPMRC_FAIL;
92 ARGV_t argv = argvp ? *argvp : NULL;
93 rpmlua lua = NULL; /* Global state. */
94 rpmluav var = rpmluavNew();
97 rpmlog(RPMLOG_DEBUG, "%s: running <lua> scriptlet.\n", sname);
99 /* Create arg variable */
100 rpmluaPushTable(lua, "arg");
101 rpmluavSetListMode(var, 1);
102 rpmluaSetNextFileFunc(nextFileFunc->func, nextFileFunc->param);
105 for (p = argv; *p; p++) {
106 rpmluavSetValue(var, RPMLUAV_STRING, *p);
107 rpmluaSetVar(lua, var);
111 rpmluavSetValueNum(var, arg1);
112 rpmluaSetVar(lua, var);
115 rpmluavSetValueNum(var, arg2);
116 rpmluaSetVar(lua, var);
120 /* Lua scripts can change our cwd and umask, save and restore */
121 /* XXX TODO: use cwd from chroot state to save unnecessary open here */
122 cwd = open(".", O_RDONLY);
124 mode_t oldmask = umask(0);
126 pid_t pid = getpid();
128 if (chdir("/") == 0 && rpmluaRunScript(lua, script, sname) == 0) {
131 if (pid != getpid()) {
132 /* Terminate child process forked in lua scriptlet */
133 rpmlog(RPMLOG_ERR, _("No exec() called after fork() in lua scriptlet\n"));
136 /* This failing would be fatal, return something different for it... */
138 rpmlog(RPMLOG_ERR, _("Unable to restore current directory: %m"));
145 rpmluaDelVar(lua, "arg");
149 rpmlog(lvl, _("<lua> scriptlet support not built in\n"));
155 static const char * const SCRIPT_PATH = "PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin";
157 static void doScriptExec(ARGV_const_t argv, ARGV_const_t prefixes,
158 FD_t scriptFd, FD_t out)
165 /* SIGPIPE is ignored in rpm, reset to default for the scriptlet */
166 (void) signal(SIGPIPE, SIG_DFL);
168 /* XXX Force FD_CLOEXEC on all inherited fdno's. */
169 open_max = sysconf(_SC_OPEN_MAX);
170 if (open_max == -1) {
173 for (fdno = 3; fdno < open_max; fdno++) {
174 flag = fcntl(fdno, F_GETFD);
175 if (flag == -1 || (flag & FD_CLOEXEC))
177 xx = fcntl(fdno, F_SETFD, FD_CLOEXEC);
178 /* XXX W2DO? debug msg for inheirited fdno w/o FD_CLOEXEC */
181 if (scriptFd != NULL) {
182 int sfdno = Fileno(scriptFd);
183 int ofdno = Fileno(out);
184 if (sfdno != STDERR_FILENO)
185 xx = dup2(sfdno, STDERR_FILENO);
186 if (ofdno != STDOUT_FILENO)
187 xx = dup2(ofdno, STDOUT_FILENO);
188 /* make sure we don't close stdin/stderr/stdout by mistake! */
189 if (ofdno > STDERR_FILENO && ofdno != sfdno)
191 if (sfdno > STDERR_FILENO && ofdno != sfdno)
192 xx = Fclose (scriptFd);
195 { char *ipath = rpmExpand("%{_install_script_path}", NULL);
196 const char *path = SCRIPT_PATH;
198 if (ipath && ipath[5] != '%')
201 xx = setenv("PATH", path, 1);
205 for (ARGV_const_t pf = prefixes; pf && *pf; pf++) {
207 int num = (pf - prefixes);
209 rasprintf(&name, "RPM_INSTALL_PREFIX%d", num);
210 setenv(name, *pf, 1);
213 /* scripts might still be using the old style prefix */
215 setenv("RPM_INSTALL_PREFIX", *pf, 1);
219 if (chdir("/") == 0) {
220 /* XXX Don't mtrace into children. */
221 unsetenv("MALLOC_CHECK_");
224 xx = execv(argv[0], argv);
227 _exit(127); /* exit 127 for compatibility with bash(1) */
230 static char * writeScript(const char *cmd, const char *script)
233 size_t slen = strlen(script);
235 FD_t fd = rpmMkTempFile("/", &fn);
240 if (rpmIsDebug() && (rstreq(cmd, "/bin/sh") || rstreq(cmd, "/bin/bash"))) {
241 static const char set_x[] = "set -x\n";
242 /* Assume failures will be caught by the write below */
243 Fwrite(set_x, sizeof(set_x[0]), sizeof(set_x)-1, fd);
246 ok = (Fwrite(script, sizeof(script[0]), slen, fd) == slen);
249 if (!ok) fn = _free(fn);
255 * Run an external script.
257 static rpmRC runExtScript(rpmPlugins plugins, ARGV_const_t prefixes,
258 const char *sname, rpmlogLvl lvl, FD_t scriptFd,
259 ARGV_t * argvp, const char *script, int arg1, int arg2,
260 scriptNextFileFunc nextFileFunc)
270 rpmRC rc = RPMRC_FAIL;
272 rpmlog(RPMLOG_DEBUG, "%s: scriptlet start\n", sname);
275 fn = writeScript(*argvp[0], script);
278 _("Couldn't create temporary file for %s: %s\n"),
279 sname, strerror(errno));
285 argvAddNum(argvp, arg1);
288 argvAddNum(argvp, arg2);
292 if (pipe(inpipe) < 0) {
294 ("Couldn't create pipe: %s\n"), strerror(errno));
297 in = fdopen(inpipe[1], "w");
300 if (scriptFd != NULL) {
301 if (rpmIsVerbose()) {
302 out = fdDup(Fileno(scriptFd));
304 out = Fopen("/dev/null", "w.fdio");
306 out = fdDup(Fileno(scriptFd));
310 out = fdDup(STDOUT_FILENO);
313 rpmlog(RPMLOG_ERR, _("Couldn't duplicate file descriptor: %s: %s\n"),
314 sname, strerror(errno));
319 if (pid == (pid_t) -1) {
320 rpmlog(RPMLOG_ERR, _("Couldn't fork %s: %s\n"),
321 sname, strerror(errno));
323 } else if (pid == 0) {/* Child */
324 rpmlog(RPMLOG_DEBUG, "%s: execv(%s) pid %d\n",
325 sname, *argvp[0], (unsigned)getpid());
328 dup2(inpipe[0], STDIN_FILENO);
330 /* Run scriptlet post fork hook for all plugins */
331 if (rpmpluginsCallScriptletForkPost(plugins, *argvp[0], RPMSCRIPTLET_FORK | RPMSCRIPTLET_EXEC) != RPMRC_FAIL) {
332 doScriptExec(*argvp, prefixes, scriptFd, out);
334 _exit(126); /* exit 126 for compatibility with bash(1) */
340 if (nextFileFunc->func) {
341 while ((line = nextFileFunc->func(nextFileFunc->param)) != NULL) {
342 size_t size = strlen(line);
344 mline = xstrdup(line);
347 ret_size = fwrite(mline, size + 1, 1, in);
348 mline = _free(mline);
350 if (errno == EPIPE) {
353 rpmlog(RPMLOG_ERR, _("Fwrite failed: %s"), strerror(errno));
364 reaped = waitpid(pid, &status, 0);
365 } while (reaped == -1 && errno == EINTR);
367 rpmlog(RPMLOG_DEBUG, "%s: waitpid(%d) rc %d status %x\n",
368 sname, (unsigned)pid, (unsigned)reaped, status);
371 rpmlog(lvl, _("%s scriptlet failed, waitpid(%d) rc %d: %s\n"),
372 sname, pid, reaped, strerror(errno));
373 } else if (!WIFEXITED(status) || WEXITSTATUS(status)) {
374 if (WIFSIGNALED(status)) {
375 rpmlog(lvl, _("%s scriptlet failed, signal %d\n"),
376 sname, WTERMSIG(status));
378 rpmlog(lvl, _("%s scriptlet failed, exit status %d\n"),
379 sname, WEXITSTATUS(status));
382 /* if we get this far we're clear */
394 Fclose(out); /* XXX dup'd STDOUT_FILENO */
406 rpmRC rpmScriptRun(rpmScript script, int arg1, int arg2, FD_t scriptFd,
407 ARGV_const_t prefixes, int warn_only, rpmPlugins plugins)
410 rpmlogLvl lvl = warn_only ? RPMLOG_WARNING : RPMLOG_ERR;
412 int script_type = RPMSCRIPTLET_FORK | RPMSCRIPTLET_EXEC;
414 if (script == NULL) return RPMRC_OK;
416 /* construct a new argv as we can't modify the one from header */
418 argvAppend(&args, script->args);
420 argvAdd(&args, "/bin/sh");
423 if (rstreq(args[0], "<lua>"))
424 script_type = RPMSCRIPTLET_NONE;
426 /* Run scriptlet pre hook for all plugins */
427 rc = rpmpluginsCallScriptletPre(plugins, script->descr, script_type);
429 if (rc != RPMRC_FAIL) {
430 if (script_type & RPMSCRIPTLET_EXEC) {
431 rc = runExtScript(plugins, prefixes, script->descr, lvl, scriptFd, &args, script->body, arg1, arg2, &script->nextFileFunc);
433 rc = runLuaScript(plugins, prefixes, script->descr, lvl, scriptFd, &args, script->body, arg1, arg2, &script->nextFileFunc);
437 /* Run scriptlet post hook for all plugins */
438 rpmpluginsCallScriptletPost(plugins, script->descr, script_type, rc);
445 static rpmscriptTypes getScriptType(rpmTagVal scriptTag)
447 return findTag(scriptTag)->type;
450 static rpmTagVal getProgTag(rpmTagVal scriptTag)
452 return findTag(scriptTag)->progtag;
455 static rpmTagVal getFlagTag(rpmTagVal scriptTag)
457 return findTag(scriptTag)->flagtag;
460 static const char * tag2sln(rpmTagVal tag)
462 return findTag(tag)->desc;
465 static rpmScript rpmScriptNew(Header h, rpmTagVal tag, const char *body,
466 rpmscriptFlags flags)
468 char *nevra = headerGetAsString(h, RPMTAG_NEVRA);
469 rpmScript script = xcalloc(1, sizeof(*script));
471 script->type = getScriptType(tag);
472 script->flags = flags;
473 script->body = (body != NULL) ? xstrdup(body) : NULL;
474 rasprintf(&script->descr, "%s(%s)", tag2sln(tag), nevra);
476 /* macros need to be expanded before possible queryformat */
477 if (script->body && (script->flags & RPMSCRIPT_FLAG_EXPAND)) {
478 char *body = rpmExpand(script->body, NULL);
482 if (script->body && (script->flags & RPMSCRIPT_FLAG_QFORMAT)) {
483 /* XXX TODO: handle queryformat errors */
484 char *body = headerFormat(h, script->body, NULL);
489 script->nextFileFunc.func = NULL;
490 script->nextFileFunc.param = NULL;
496 void rpmScriptSetNextFileFunc(rpmScript script, char *(*func)(void *),
499 script->nextFileFunc.func = func;
500 script->nextFileFunc.param = param;
503 rpmTagVal triggerDsTag(rpmscriptTriggerModes tm)
505 rpmTagVal tag = RPMTAG_NOT_FOUND;
507 case RPMSCRIPT_NORMALTRIGGER:
508 tag = RPMTAG_TRIGGERNAME;
510 case RPMSCRIPT_FILETRIGGER:
511 tag = RPMTAG_FILETRIGGERNAME;
513 case RPMSCRIPT_TRANSFILETRIGGER:
514 tag = RPMTAG_TRANSFILETRIGGERNAME;
520 rpmscriptTriggerModes triggerMode(rpmTagVal tag)
522 rpmscriptTriggerModes tm = 0;
524 case RPMTAG_TRIGGERNAME:
525 tm = RPMSCRIPT_NORMALTRIGGER;
527 case RPMTAG_FILETRIGGERNAME:
528 tm = RPMSCRIPT_FILETRIGGER;
530 case RPMTAG_TRANSFILETRIGGERNAME:
531 tm = RPMSCRIPT_TRANSFILETRIGGER;
537 rpmTagVal triggertag(rpmsenseFlags sense)
539 rpmTagVal tag = RPMTAG_NOT_FOUND;
541 case RPMSENSE_TRIGGERIN:
542 tag = RPMTAG_TRIGGERIN;
544 case RPMSENSE_TRIGGERUN:
545 tag = RPMTAG_TRIGGERUN;
547 case RPMSENSE_TRIGGERPOSTUN:
548 tag = RPMTAG_TRIGGERPOSTUN;
550 case RPMSENSE_TRIGGERPREIN:
551 tag = RPMTAG_TRIGGERPREIN;
559 rpmScript rpmScriptFromTriggerTag(Header h, rpmTagVal triggerTag,
560 rpmscriptTriggerModes tm, uint32_t ix)
562 rpmScript script = NULL;
563 struct rpmtd_s tscripts, tprogs, tflags;
564 headerGetFlags hgflags = HEADERGET_MINMEM;
567 case RPMSCRIPT_NORMALTRIGGER:
568 headerGet(h, RPMTAG_TRIGGERSCRIPTS, &tscripts, hgflags);
569 headerGet(h, RPMTAG_TRIGGERSCRIPTPROG, &tprogs, hgflags);
570 headerGet(h, RPMTAG_TRIGGERSCRIPTFLAGS, &tflags, hgflags);
572 case RPMSCRIPT_FILETRIGGER:
573 headerGet(h, RPMTAG_FILETRIGGERSCRIPTS, &tscripts, hgflags);
574 headerGet(h, RPMTAG_FILETRIGGERSCRIPTPROG, &tprogs, hgflags);
575 headerGet(h, RPMTAG_FILETRIGGERSCRIPTFLAGS, &tflags, hgflags);
577 case RPMSCRIPT_TRANSFILETRIGGER:
578 headerGet(h, RPMTAG_TRANSFILETRIGGERSCRIPTS, &tscripts, hgflags);
579 headerGet(h, RPMTAG_TRANSFILETRIGGERSCRIPTPROG, &tprogs, hgflags);
580 headerGet(h, RPMTAG_TRANSFILETRIGGERSCRIPTFLAGS, &tflags, hgflags);
584 if (rpmtdSetIndex(&tscripts, ix) >= 0 && rpmtdSetIndex(&tprogs, ix) >= 0) {
585 rpmscriptFlags sflags = 0;
586 const char *prog = rpmtdGetString(&tprogs);
588 if (rpmtdSetIndex(&tflags, ix) >= 0)
589 sflags = rpmtdGetNumber(&tflags);
591 script = rpmScriptNew(h, triggerTag, rpmtdGetString(&tscripts), sflags);
593 /* hack up a hge-style NULL-terminated array */
594 script->args = xmalloc(2 * sizeof(*script->args) + strlen(prog) + 1);
595 script->args[0] = (char *)(script->args + 2);
596 script->args[1] = NULL;
597 strcpy(script->args[0], prog);
600 rpmtdFreeData(&tscripts);
601 rpmtdFreeData(&tprogs);
602 rpmtdFreeData(&tflags);
607 rpmScript rpmScriptFromTag(Header h, rpmTagVal scriptTag)
609 rpmScript script = NULL;
610 rpmTagVal progTag = getProgTag(scriptTag);
612 if (headerIsEntry(h, scriptTag) || headerIsEntry(h, progTag)) {
615 script = rpmScriptNew(h, scriptTag,
616 headerGetString(h, scriptTag),
617 headerGetNumber(h, getFlagTag(scriptTag)));
619 if (headerGet(h, progTag, &prog, (HEADERGET_ALLOC|HEADERGET_ARGV))) {
620 script->args = prog.data;
626 rpmScript rpmScriptFree(rpmScript script)
637 rpmTagVal rpmScriptTag(rpmScript script)
639 return (script != NULL) ? script->tag : RPMTAG_NOT_FOUND;
642 rpmscriptTypes rpmScriptType(rpmScript script)
644 return (script != NULL) ? script->type : 0;