431 lines
11 KiB
C
431 lines
11 KiB
C
#include "system.h"
|
|
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
|
|
#define _RPMSQ_INTERNAL
|
|
#include <rpm/rpmsq.h>
|
|
#include <rpm/rpmts.h>
|
|
#include <rpm/rpmfileutil.h>
|
|
#include <rpm/rpmmacro.h>
|
|
#include <rpm/rpmio.h>
|
|
#include <rpm/rpmlog.h>
|
|
#include <rpm/rpmsw.h>
|
|
#include <rpm/header.h>
|
|
|
|
#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 <lua> 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, _("<lua> 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], "<lua>")) {
|
|
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 rpmTag getFlagTag(rpmTag scriptTag)
|
|
{
|
|
switch (scriptTag) {
|
|
case RPMTAG_PRETRANS: return RPMTAG_PRETRANSFLAGS;
|
|
case RPMTAG_POSTTRANS: return RPMTAG_POSTTRANSFLAGS;
|
|
case RPMTAG_PREUN: return RPMTAG_PREUNFLAGS;
|
|
case RPMTAG_POSTUN: return RPMTAG_POSTUNFLAGS;
|
|
case RPMTAG_PREIN: return RPMTAG_PREINFLAGS;
|
|
case RPMTAG_POSTIN: return RPMTAG_POSTINFLAGS;
|
|
case RPMTAG_VERIFYSCRIPT: return RPMTAG_VERIFYSCRIPTFLAGS;
|
|
case RPMTAG_TRIGGERSCRIPTS: return RPMTAG_TRIGGERSCRIPTFLAGS;
|
|
default:
|
|
break;
|
|
}
|
|
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);
|
|
rpmscriptFlags flags = headerGetNumber(h, getFlagTag(scriptTag));
|
|
|
|
script = xcalloc(1, sizeof(*script));
|
|
script->tag = scriptTag;
|
|
rasprintf(&script->descr, "%s(%s)", tag2sln(scriptTag), nevra);
|
|
script->body = headerGetAsString(h, scriptTag);
|
|
|
|
/* macros need to be expanded before possible queryformat */
|
|
if (script->body && (flags & RPMSCRIPT_EXPAND)) {
|
|
char *body = rpmExpand(script->body, NULL);
|
|
free(script->body);
|
|
script->body = body;
|
|
}
|
|
if (script->body && (flags & RPMSCRIPT_QFORMAT)) {
|
|
/* XXX TODO: handle queryformat errors */
|
|
char *body = headerFormat(h, script->body, NULL);
|
|
free(script->body);
|
|
script->body = body;
|
|
}
|
|
|
|
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;
|
|
}
|