#include "system.h" #include #include #define _RPMSQ_INTERNAL #include #include #include #include #include #include #include #include #include "rpmio/rpmlua.h" #include "lib/rpmscript.h" #include "debug.h" /** * Run internal Lua script. */ static rpmRC runLuaScript(rpmts ts, ARGV_const_t prefixes, const char *sname, rpmlogLvl lvl, ARGV_t * argvp, const char *script, int arg1, int arg2) { rpmRC rc = RPMRC_FAIL; #ifdef WITH_LUA int rootFd = -1; int xx; ARGV_t argv = argvp ? *argvp : NULL; rpmlua lua = NULL; /* Global state. */ rpmluav var; rpmlog(RPMLOG_DEBUG, "%s: running scriptlet.\n", sname); if (!rpmtsChrootDone(ts)) { const char *rootDir = rpmtsRootDir(ts); xx = chdir("/"); rootFd = open(".", O_RDONLY, 0); if (rootFd >= 0) { if (rootDir != NULL && !rstreq(rootDir, "/") && *rootDir == '/') xx = chroot(rootDir); xx = rpmtsSetChrootDone(ts, 1); } } /* Create arg variable */ rpmluaPushTable(lua, "arg"); var = rpmluavNew(); rpmluavSetListMode(var, 1); if (argv) { char **p; for (p = argv; *p; p++) { rpmluavSetValue(var, RPMLUAV_STRING, *p); rpmluaSetVar(lua, var); } } if (arg1 >= 0) { rpmluavSetValueNum(var, arg1); rpmluaSetVar(lua, var); } if (arg2 >= 0) { rpmluavSetValueNum(var, arg2); rpmluaSetVar(lua, var); } var = rpmluavFree(var); rpmluaPop(lua); if (rpmluaRunScript(lua, script, sname) == 0) { rc = RPMRC_OK; } rpmluaDelVar(lua, "arg"); if (rootFd >= 0) { const char *rootDir = rpmtsRootDir(ts); xx = fchdir(rootFd); xx = close(rootFd); if (rootDir != NULL && !rstreq(rootDir, "/") && *rootDir == '/') xx = chroot("."); xx = rpmtsSetChrootDone(ts, 0); } #else rpmlog(lvl, _(" scriptlet support not built in\n")); #endif return rc; } static const char * const SCRIPT_PATH = "PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin"; static void doScriptExec(rpmts ts, ARGV_const_t argv, ARGV_const_t prefixes, FD_t scriptFd, FD_t out) { const char * rootDir; int pipes[2]; int flag; int fdno; int xx; int open_max; (void) signal(SIGPIPE, SIG_DFL); pipes[0] = pipes[1] = 0; /* make stdin inaccessible */ xx = pipe(pipes); xx = close(pipes[1]); xx = dup2(pipes[0], STDIN_FILENO); xx = close(pipes[0]); /* XXX Force FD_CLOEXEC on all inherited fdno's. */ open_max = sysconf(_SC_OPEN_MAX); if (open_max == -1) { open_max = 1024; } for (fdno = 3; fdno < open_max; fdno++) { flag = fcntl(fdno, F_GETFD); if (flag == -1 || (flag & FD_CLOEXEC)) continue; xx = fcntl(fdno, F_SETFD, FD_CLOEXEC); /* XXX W2DO? debug msg for inheirited fdno w/o FD_CLOEXEC */ } if (scriptFd != NULL) { int sfdno = Fileno(scriptFd); int ofdno = Fileno(out); if (sfdno != STDERR_FILENO) xx = dup2(sfdno, STDERR_FILENO); if (ofdno != STDOUT_FILENO) xx = dup2(ofdno, STDOUT_FILENO); /* make sure we don't close stdin/stderr/stdout by mistake! */ if (ofdno > STDERR_FILENO && ofdno != sfdno) xx = Fclose (out); if (sfdno > STDERR_FILENO && ofdno != sfdno) xx = Fclose (scriptFd); } { char *ipath = rpmExpand("%{_install_script_path}", NULL); const char *path = SCRIPT_PATH; if (ipath && ipath[5] != '%') path = ipath; xx = setenv("PATH", path, 1); ipath = _free(ipath); } for (ARGV_const_t pf = prefixes; pf && *pf; pf++) { char *name = NULL; int num = (pf - prefixes); rasprintf(&name, "RPM_INSTALL_PREFIX%d", num); setenv(name, *pf, 1); free(name); /* scripts might still be using the old style prefix */ if (num == 0) { setenv("RPM_INSTALL_PREFIX", *pf, 1); } } rootDir = rpmtsRootDir(ts); if (rootDir != NULL) { /* XXX can't happen */ if (!rpmtsChrootDone(ts) && !(rootDir[0] == '/' && rootDir[1] == '\0')) { xx = chroot(rootDir); } xx = chdir("/"); /* XXX Don't mtrace into children. */ unsetenv("MALLOC_CHECK_"); /* Permit libselinux to do the scriptlet exec. */ if (rpmtsSELinuxEnabled(ts) == 1) { xx = rpm_execcon(0, argv[0], argv, environ); } if (xx == 0) { xx = execv(argv[0], argv); } } _exit(127); /* exit 127 for compatibility with bash(1) */ } /** * Run an external script. */ static rpmRC runExtScript(rpmts ts, ARGV_const_t prefixes, const char *sname, rpmlogLvl lvl, ARGV_t * argvp, const char *script, int arg1, int arg2) { FD_t scriptFd; FD_t out = NULL; char * fn = NULL; int xx; rpmRC rc = RPMRC_FAIL; struct rpmsqElem sq; memset(&sq, 0, sizeof(sq)); sq.reaper = 1; rpmlog(RPMLOG_DEBUG, "%s: scriptlet start\n", sname); if (script) { const char * rootDir = rpmtsRootDir(ts); FD_t fd; fd = rpmMkTempFile((!rpmtsChrootDone(ts) ? rootDir : "/"), &fn); if (fd == NULL || Ferror(fd)) { rpmlog(RPMLOG_ERR, _("Couldn't create temporary file for %s: %s\n"), sname, strerror(errno)); goto exit; } if (rpmIsDebug() && (rstreq(*argvp[0], "/bin/sh") || rstreq(*argvp[0], "/bin/bash"))) { static const char set_x[] = "set -x\n"; xx = Fwrite(set_x, sizeof(set_x[0]), sizeof(set_x)-1, fd); } xx = Fwrite(script, sizeof(script[0]), strlen(script), fd); xx = Fclose(fd); { const char * sn = fn; if (!rpmtsChrootDone(ts) && rootDir != NULL && !(rootDir[0] == '/' && rootDir[1] == '\0')) { sn += strlen(rootDir)-1; } argvAdd(argvp, sn); } if (arg1 >= 0) { argvAddNum(argvp, arg1); } if (arg2 >= 0) { argvAddNum(argvp, arg2); } } scriptFd = rpmtsScriptFd(ts); if (scriptFd != NULL) { if (rpmIsVerbose()) { out = fdDup(Fileno(scriptFd)); } else { out = Fopen("/dev/null", "w.fdio"); if (Ferror(out)) { out = fdDup(Fileno(scriptFd)); } } } else { out = fdDup(STDOUT_FILENO); } if (out == NULL) { rpmlog(RPMLOG_ERR, _("Couldn't duplicate file descriptor: %s: %s\n"), sname, strerror(errno)); goto exit; } xx = rpmsqFork(&sq); if (sq.child == 0) { rpmlog(RPMLOG_DEBUG, "%s: execv(%s) pid %d\n", sname, *argvp[0], (unsigned)getpid()); doScriptExec(ts, *argvp, prefixes, scriptFd, out); } if (sq.child == (pid_t)-1) { rpmlog(RPMLOG_ERR, _("Couldn't fork %s: %s\n"), sname, strerror(errno)); goto exit; } rpmsqWait(&sq); rpmlog(RPMLOG_DEBUG, "%s: waitpid(%d) rc %d status %x\n", sname, (unsigned)sq.child, (unsigned)sq.reaped, sq.status); if (sq.reaped < 0) { rpmlog(lvl, _("%s scriptlet failed, waitpid(%d) rc %d: %s\n"), sname, sq.child, sq.reaped, strerror(errno)); } else if (!WIFEXITED(sq.status) || WEXITSTATUS(sq.status)) { if (WIFSIGNALED(sq.status)) { rpmlog(lvl, _("%s scriptlet failed, signal %d\n"), sname, WTERMSIG(sq.status)); } else { rpmlog(lvl, _("%s scriptlet failed, exit status %d\n"), sname, WEXITSTATUS(sq.status)); } } else { /* if we get this far we're clear */ rc = RPMRC_OK; } exit: if (out) xx = Fclose(out); /* XXX dup'd STDOUT_FILENO */ if (script) { if (!rpmIsDebug()) xx = unlink(fn); fn = _free(fn); } return rc; } rpmRC rpmScriptRun(rpmScript script, int arg1, int arg2, rpmts ts, ARGV_const_t prefixes, int warn_only) { ARGV_t args = NULL; rpmlogLvl lvl = warn_only ? RPMLOG_WARNING : RPMLOG_ERR; rpmRC rc; if (script == NULL) return RPMRC_OK; /* construct a new argv as we can't modify the one from header */ if (script->args) { argvAppend(&args, script->args); } else { argvAdd(&args, "/bin/sh"); } rpmswEnter(rpmtsOp(ts, RPMTS_OP_SCRIPTLETS), 0); if (rstreq(args[0], "")) { rc = runLuaScript(ts, prefixes, script->descr, lvl, &args, script->body, arg1, arg2); } else { rc = runExtScript(ts, prefixes, script->descr, lvl, &args, script->body, arg1, arg2); } rpmswExit(rpmtsOp(ts, RPMTS_OP_SCRIPTLETS), 0); argvFree(args); return rc; } static rpmTag getProgTag(rpmTag scriptTag) { switch (scriptTag) { case RPMTAG_PREIN: return RPMTAG_PREINPROG; case RPMTAG_POSTIN: return RPMTAG_POSTINPROG; case RPMTAG_PREUN: return RPMTAG_PREUNPROG; case RPMTAG_POSTUN: return RPMTAG_POSTUNPROG; case RPMTAG_PRETRANS: return RPMTAG_PRETRANSPROG; case RPMTAG_POSTTRANS: return RPMTAG_POSTTRANSPROG; case RPMTAG_VERIFYSCRIPT: return RPMTAG_VERIFYSCRIPTPROG; default: return RPMTAG_NOT_FOUND; } } static const char * tag2sln(rpmTag tag) { switch ((rpm_tag_t) tag) { case RPMTAG_PRETRANS: return "%pretrans"; case RPMTAG_TRIGGERPREIN: return "%triggerprein"; case RPMTAG_PREIN: return "%pre"; case RPMTAG_POSTIN: return "%post"; case RPMTAG_TRIGGERIN: return "%triggerin"; case RPMTAG_TRIGGERUN: return "%triggerun"; case RPMTAG_PREUN: return "%preun"; case RPMTAG_POSTUN: return "%postun"; case RPMTAG_POSTTRANS: return "%posttrans"; case RPMTAG_TRIGGERPOSTUN: return "%triggerpostun"; case RPMTAG_VERIFYSCRIPT: return "%verify"; default: break; } return "%unknownscript"; } rpmScript rpmScriptFromTag(Header h, rpmTag scriptTag) { rpmScript script = NULL; rpmTag progTag = getProgTag(scriptTag); if (headerIsEntry(h, scriptTag) || headerIsEntry(h, progTag)) { struct rpmtd_s prog; char *nevra = headerGetAsString(h, RPMTAG_NEVRA); script = xcalloc(1, sizeof(*script)); script->tag = scriptTag; script->body = headerGetAsString(h, scriptTag); rasprintf(&script->descr, "%s(%s)", tag2sln(scriptTag), nevra); if (headerGet(h, progTag, &prog, (HEADERGET_ALLOC|HEADERGET_ARGV))) { script->args = prog.data; } free(nevra); } return script; } rpmScript rpmScriptFree(rpmScript script) { if (script) { free(script->args); free(script->body); free(script->descr); free(script); } return NULL; }