upgrade rpm version to 4.14.1
[platform/upstream/rpm.git] / lib / rpmscript.c
index e5ce8da..cc98c48 100644 (file)
 #include "system.h"
 
+#include <sys/types.h>
+#include <sys/wait.h>
 #include <errno.h>
 #include <unistd.h>
 
-#define _RPMSQ_INTERNAL
-#include <rpm/rpmsq.h>
 #include <rpm/rpmfileutil.h>
 #include <rpm/rpmmacro.h>
 #include <rpm/rpmio.h>
 #include <rpm/rpmlog.h>
 #include <rpm/header.h>
+#include <rpm/rpmds.h>
 
 #include "rpmio/rpmlua.h"
 #include "lib/rpmscript.h"
 
+#include "lib/rpmplugins.h"     /* rpm plugins hooks */
+
 #include "debug.h"
 
+struct scriptNextFileFunc_s {
+    char *(*func)(void *);     /* function producing input for script */
+    void *param;               /* parameter for func */
+};
+
+typedef struct scriptNextFileFunc_s *scriptNextFileFunc;
+
+struct rpmScript_s {
+    rpmscriptTypes type;       /* script type */
+    rpmTagVal tag;             /* script tag */
+    char **args;               /* scriptlet call arguments */
+    char *body;                        /* script body */
+    char *descr;               /* description for logging */
+    rpmscriptFlags flags;      /* flags to control operation */
+    struct scriptNextFileFunc_s nextFileFunc;  /* input function */
+};
+
+struct scriptInfo_s {
+    rpmscriptTypes type;
+    const char *desc;
+    rpmsenseFlags sense;
+    rpmTagVal tag;
+    rpmTagVal progtag;
+    rpmTagVal flagtag;
+};
+
+static const struct scriptInfo_s scriptInfo[] = {
+    { RPMSCRIPT_PREIN, "%prein", 0,
+       RPMTAG_PREIN, RPMTAG_PREINPROG, RPMTAG_PREINFLAGS },
+    { RPMSCRIPT_PREUN, "%preun", 0,
+       RPMTAG_PREUN, RPMTAG_PREUNPROG, RPMTAG_PREUNFLAGS },
+    { RPMSCRIPT_POSTIN, "%post", 0,
+       RPMTAG_POSTIN, RPMTAG_POSTINPROG, RPMTAG_POSTINFLAGS },
+    { RPMSCRIPT_POSTUN, "%postun", 0,
+       RPMTAG_POSTUN, RPMTAG_POSTUNPROG, RPMTAG_POSTUNFLAGS },
+    { RPMSCRIPT_PRETRANS, "%pretrans", 0,
+       RPMTAG_PRETRANS, RPMTAG_PRETRANSPROG, RPMTAG_PRETRANSFLAGS },
+    { RPMSCRIPT_POSTTRANS, "%posttrans", 0,
+       RPMTAG_POSTTRANS, RPMTAG_POSTTRANSPROG, RPMTAG_POSTTRANSFLAGS },
+    { RPMSCRIPT_TRIGGERPREIN, "%triggerprein", RPMSENSE_TRIGGERPREIN,
+       RPMTAG_TRIGGERPREIN, 0, 0 },
+    { RPMSCRIPT_TRIGGERUN, "%triggerun", RPMSENSE_TRIGGERUN,
+       RPMTAG_TRIGGERUN, 0, 0 },
+    { RPMSCRIPT_TRIGGERIN, "%triggerin", RPMSENSE_TRIGGERIN,
+       RPMTAG_TRIGGERIN, 0, 0 },
+    { RPMSCRIPT_TRIGGERPOSTUN, "%triggerpostun", RPMSENSE_TRIGGERPOSTUN,
+       RPMTAG_TRIGGERPOSTUN, 0, 0 },
+    { RPMSCRIPT_VERIFY, "%verify", 0,
+       RPMTAG_VERIFYSCRIPT, RPMTAG_VERIFYSCRIPTPROG, RPMTAG_VERIFYSCRIPTFLAGS},
+    { 0, "unknown", 0,
+       RPMTAG_NOT_FOUND, RPMTAG_NOT_FOUND, RPMTAG_NOT_FOUND }
+};
+
+static const struct scriptInfo_s * findTag(rpmTagVal tag)
+{
+    const struct scriptInfo_s * si = scriptInfo;
+    while (si->type && si->tag != tag)
+       si++;
+    return si;
+}
 /**
  * Run internal Lua script.
  */
-static rpmRC runLuaScript(int selinux, ARGV_const_t prefixes,
+static rpmRC runLuaScript(rpmPlugins plugins, ARGV_const_t prefixes,
                   const char *sname, rpmlogLvl lvl, FD_t scriptFd,
-                  ARGV_t * argvp, const char *script, int arg1, int arg2)
+                  ARGV_t * argvp, const char *script, int arg1, int arg2,
+                  scriptNextFileFunc nextFileFunc)
 {
     rpmRC rc = RPMRC_FAIL;
 #ifdef WITH_LUA
     ARGV_t argv = argvp ? *argvp : NULL;
     rpmlua lua = NULL; /* Global state. */
-    rpmluav var;
+    rpmluav var = rpmluavNew();
     int cwd = -1;
 
     rpmlog(RPMLOG_DEBUG, "%s: running <lua> scriptlet.\n", sname);
 
     /* Create arg variable */
     rpmluaPushTable(lua, "arg");
-    var = rpmluavNew();
     rpmluavSetListMode(var, 1);
+    rpmluaSetNextFileFunc(nextFileFunc->func, nextFileFunc->param);
     if (argv) {
        char **p;
        for (p = argv; *p; p++) {
@@ -51,27 +115,35 @@ static rpmRC runLuaScript(int selinux, ARGV_const_t prefixes,
        rpmluavSetValueNum(var, arg2);
        rpmluaSetVar(lua, var);
     }
-    var = rpmluavFree(var);
     rpmluaPop(lua);
 
     /* Lua scripts can change our cwd and umask, save and restore */
     /* XXX TODO: use cwd from chroot state to save unnecessary open here */
     cwd = open(".", O_RDONLY);
     if (cwd != -1) {
-       int xx;
        mode_t oldmask = umask(0);
        umask(oldmask);
+       pid_t pid = getpid();
 
        if (chdir("/") == 0 && rpmluaRunScript(lua, script, sname) == 0) {
            rc = RPMRC_OK;
        }
-       /* XXX no way to return error from restore meaningfully atm */
-       xx = fchdir(cwd);
+       if (pid != getpid()) {
+           /* Terminate child process forked in lua scriptlet */
+           rpmlog(RPMLOG_ERR, _("No exec() called after fork() in lua scriptlet\n"));
+           _exit(EXIT_FAILURE);
+       }
+       /* This failing would be fatal, return something different for it... */
+       if (fchdir(cwd)) {
+           rpmlog(RPMLOG_ERR, _("Unable to restore current directory: %m"));
+           rc = RPMRC_NOTFOUND;
+       }
        close(cwd);
        umask(oldmask);
     }
 
     rpmluaDelVar(lua, "arg");
+    rpmluavFree(var);
 
 #else
     rpmlog(lvl, _("<lua> scriptlet support not built in\n"));
@@ -82,22 +154,16 @@ static rpmRC runLuaScript(int selinux, ARGV_const_t prefixes,
 
 static const char * const SCRIPT_PATH = "PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin";
 
-static void doScriptExec(int selinux, ARGV_const_t argv, ARGV_const_t prefixes,
+static void doScriptExec(ARGV_const_t argv, ARGV_const_t prefixes,
                        FD_t scriptFd, FD_t out)
 {
-    int pipes[2];
     int flag;
     int fdno;
     int xx;
     int open_max;
 
+    /* SIGPIPE is ignored in rpm, reset to default for the scriptlet */
     (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);
@@ -133,7 +199,7 @@ static void doScriptExec(int selinux, ARGV_const_t argv, ARGV_const_t prefixes,
            path = ipath;
 
        xx = setenv("PATH", path, 1);
-       ipath = _free(ipath);
+       free(ipath);
     }
 
     for (ARGV_const_t pf = prefixes; pf && *pf; pf++) {
@@ -154,11 +220,6 @@ static void doScriptExec(int selinux, ARGV_const_t argv, ARGV_const_t prefixes,
        /* XXX Don't mtrace into children. */
        unsetenv("MALLOC_CHECK_");
 
-       /* Permit libselinux to do the scriptlet exec. */
-       if (selinux == 1) {     
-           xx = rpm_execcon(0, argv[0], argv, environ);
-       }
-
        if (xx == 0) {
            xx = execv(argv[0], argv);
        }
@@ -193,18 +254,20 @@ exit:
 /**
  * Run an external script.
  */
-static rpmRC runExtScript(int selinux, ARGV_const_t prefixes,
+static rpmRC runExtScript(rpmPlugins plugins, ARGV_const_t prefixes,
                   const char *sname, rpmlogLvl lvl, FD_t scriptFd,
-                  ARGV_t * argvp, const char *script, int arg1, int arg2)
+                  ARGV_t * argvp, const char *script, int arg1, int arg2,
+                  scriptNextFileFunc nextFileFunc)
 {
     FD_t out = NULL;
     char * fn = NULL;
-    int xx;
+    pid_t pid, reaped;
+    int status;
+    int inpipe[2];
+    FILE *in = NULL;
+    const char *line;
+    char *mline = NULL;
     rpmRC rc = RPMRC_FAIL;
-    struct rpmsqElem sq;
-
-    memset(&sq, 0, sizeof(sq));
-    sq.reaper = 1;
 
     rpmlog(RPMLOG_DEBUG, "%s: scriptlet start\n", sname);
 
@@ -226,6 +289,14 @@ static rpmRC runExtScript(int selinux, ARGV_const_t prefixes,
        }
     }
 
+    if (pipe(inpipe) < 0) {
+       rpmlog(RPMLOG_ERR,
+               ("Couldn't create pipe: %s\n"), strerror(errno));
+       goto exit;
+    }
+    in = fdopen(inpipe[1], "w");
+    inpipe[1] = 0;
+
     if (scriptFd != NULL) {
        if (rpmIsVerbose()) {
            out = fdDup(Fileno(scriptFd));
@@ -244,33 +315,68 @@ static rpmRC runExtScript(int selinux, ARGV_const_t prefixes,
        goto exit;
     }
 
-    xx = rpmsqFork(&sq);
-    if (sq.child == 0) {
+    pid = fork();
+    if (pid == (pid_t) -1) {
+       rpmlog(RPMLOG_ERR, _("Couldn't fork %s: %s\n"),
+               sname, strerror(errno));
+       goto exit;
+    } else if (pid == 0) {/* Child */
        rpmlog(RPMLOG_DEBUG, "%s: execv(%s) pid %d\n",
               sname, *argvp[0], (unsigned)getpid());
-       doScriptExec(selinux, *argvp, prefixes, scriptFd, out);
-    }
 
-    if (sq.child == (pid_t)-1) {
-       rpmlog(RPMLOG_ERR, _("Couldn't fork %s: %s\n"), sname, strerror(errno));
-       goto exit;
+       fclose(in);
+       dup2(inpipe[0], STDIN_FILENO);
+
+       /* Run scriptlet post fork hook for all plugins */
+       if (rpmpluginsCallScriptletForkPost(plugins, *argvp[0], RPMSCRIPTLET_FORK | RPMSCRIPTLET_EXEC) != RPMRC_FAIL) {
+           doScriptExec(*argvp, prefixes, scriptFd, out);
+       } else {
+           _exit(126); /* exit 126 for compatibility with bash(1) */
+       }
+    }
+    close(inpipe[0]);
+    inpipe[0] = 0;
+
+    if (nextFileFunc->func) {
+       while ((line = nextFileFunc->func(nextFileFunc->param)) != NULL) {
+           size_t size = strlen(line);
+           size_t ret_size;
+           mline = xstrdup(line);
+           mline[size] = '\n';
+
+           ret_size = fwrite(mline, size + 1, 1, in);
+           mline = _free(mline);
+           if (ret_size != 1) {
+               if (errno == EPIPE) {
+                   break;
+               } else {
+                   rpmlog(RPMLOG_ERR, _("Fwrite failed: %s"), strerror(errno));
+                   rc = RPMRC_FAIL;
+                   goto exit;
+               }
+           }
+       }
     }
+    fclose(in);
+    in = NULL;
 
-    rpmsqWait(&sq);
+    do {
+       reaped = waitpid(pid, &status, 0);
+    } while (reaped == -1 && errno == EINTR);
 
     rpmlog(RPMLOG_DEBUG, "%s: waitpid(%d) rc %d status %x\n",
-          sname, (unsigned)sq.child, (unsigned)sq.reaped, sq.status);
+          sname, (unsigned)pid, (unsigned)reaped, status);
 
-    if (sq.reaped < 0) {
+    if (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)) {
+                sname, pid, reaped, strerror(errno));
+    } else if (!WIFEXITED(status) || WEXITSTATUS(status)) {
+       if (WIFSIGNALED(status)) {
            rpmlog(lvl, _("%s scriptlet failed, signal %d\n"),
-                   sname, WTERMSIG(sq.status));
+                   sname, WTERMSIG(status));
        } else {
            rpmlog(lvl, _("%s scriptlet failed, exit status %d\n"),
-                  sname, WEXITSTATUS(sq.status));
+                  sname, WEXITSTATUS(status));
        }
     } else {
        /* if we get this far we're clear */
@@ -278,23 +384,32 @@ static rpmRC runExtScript(int selinux, ARGV_const_t prefixes,
     }
 
 exit:
+    if (in)
+       fclose(in);
+
+    if (inpipe[0])
+       close(inpipe[0]);
+
     if (out)
-       xx = Fclose(out);       /* XXX dup'd STDOUT_FILENO */
+       Fclose(out);    /* XXX dup'd STDOUT_FILENO */
 
-    if (script) {
+    if (fn) {
        if (!rpmIsDebug())
-           xx = unlink(fn);
-       fn = _free(fn);
+           unlink(fn);
+       free(fn);
     }
+    free(mline);
+
     return rc;
 }
 
 rpmRC rpmScriptRun(rpmScript script, int arg1, int arg2, FD_t scriptFd,
-                  ARGV_const_t prefixes, int warn_only, int selinux)
+                  ARGV_const_t prefixes, int warn_only, rpmPlugins plugins)
 {
     ARGV_t args = NULL;
     rpmlogLvl lvl = warn_only ? RPMLOG_WARNING : RPMLOG_ERR;
     rpmRC rc;
+    int script_type = RPMSCRIPTLET_FORK | RPMSCRIPTLET_EXEC;
 
     if (script == NULL) return RPMRC_OK;
 
@@ -304,65 +419,189 @@ rpmRC rpmScriptRun(rpmScript script, int arg1, int arg2, FD_t scriptFd,
     } else {
        argvAdd(&args, "/bin/sh");
     }
+    
+    if (rstreq(args[0], "<lua>"))
+       script_type = RPMSCRIPTLET_NONE;
 
-    if (rstreq(args[0], "<lua>")) {
-       rc = runLuaScript(selinux, prefixes, script->descr, lvl, scriptFd, &args, script->body, arg1, arg2);
-    } else {
-       rc = runExtScript(selinux, prefixes, script->descr, lvl, scriptFd, &args, script->body, arg1, arg2);
+    /* Run scriptlet pre hook for all plugins */
+    rc = rpmpluginsCallScriptletPre(plugins, script->descr, script_type);
+
+    if (rc != RPMRC_FAIL) {
+       if (script_type & RPMSCRIPTLET_EXEC) {
+           rc = runExtScript(plugins, prefixes, script->descr, lvl, scriptFd, &args, script->body, arg1, arg2, &script->nextFileFunc);
+       } else {
+           rc = runLuaScript(plugins, prefixes, script->descr, lvl, scriptFd, &args, script->body, arg1, arg2, &script->nextFileFunc);
+       }
     }
+
+    /* Run scriptlet post hook for all plugins */
+    rpmpluginsCallScriptletPost(plugins, script->descr, script_type, rc);
+
     argvFree(args);
 
     return rc;
 }
 
+static rpmscriptTypes getScriptType(rpmTagVal scriptTag)
+{
+    return findTag(scriptTag)->type;
+}
+
 static rpmTagVal getProgTag(rpmTagVal 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;
-    }
+    return findTag(scriptTag)->progtag;
 }
 
 static rpmTagVal getFlagTag(rpmTagVal 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;
+    return findTag(scriptTag)->flagtag;
 }
 
 static const char * tag2sln(rpmTagVal tag)
 {
+    return findTag(tag)->desc;
+}
+
+static rpmScript rpmScriptNew(Header h, rpmTagVal tag, const char *body,
+                             rpmscriptFlags flags)
+{
+    char *nevra = headerGetAsString(h, RPMTAG_NEVRA);
+    rpmScript script = xcalloc(1, sizeof(*script));
+    script->tag = tag;
+    script->type = getScriptType(tag);
+    script->flags = flags;
+    script->body = (body != NULL) ? xstrdup(body) : NULL;
+    rasprintf(&script->descr, "%s(%s)", tag2sln(tag), nevra);
+
+    /* macros need to be expanded before possible queryformat */
+    if (script->body && (script->flags & RPMSCRIPT_FLAG_EXPAND)) {
+       char *body = rpmExpand(script->body, NULL);
+       free(script->body);
+       script->body = body;
+    }
+    if (script->body && (script->flags & RPMSCRIPT_FLAG_QFORMAT)) {
+       /* XXX TODO: handle queryformat errors */
+       char *body = headerFormat(h, script->body, NULL);
+       free(script->body);
+       script->body = body;
+    }
+
+    script->nextFileFunc.func = NULL;
+    script->nextFileFunc.param = NULL;
+
+    free(nevra);
+    return script;
+}
+
+void rpmScriptSetNextFileFunc(rpmScript script, char *(*func)(void *),
+                           void *param)
+{
+    script->nextFileFunc.func = func;
+    script->nextFileFunc.param = param;
+}
+
+rpmTagVal triggerDsTag(rpmscriptTriggerModes tm)
+{
+    rpmTagVal tag = RPMTAG_NOT_FOUND;
+    switch (tm) {
+    case RPMSCRIPT_NORMALTRIGGER:
+       tag = RPMTAG_TRIGGERNAME;
+       break;
+    case RPMSCRIPT_FILETRIGGER:
+       tag = RPMTAG_FILETRIGGERNAME;
+       break;
+    case RPMSCRIPT_TRANSFILETRIGGER:
+       tag = RPMTAG_TRANSFILETRIGGERNAME;
+       break;
+    }
+    return tag;
+}
+
+rpmscriptTriggerModes triggerMode(rpmTagVal tag)
+{
+    rpmscriptTriggerModes tm = 0;
     switch (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;
+    case RPMTAG_TRIGGERNAME:
+       tm = RPMSCRIPT_NORMALTRIGGER;
+       break;
+    case RPMTAG_FILETRIGGERNAME:
+       tm = RPMSCRIPT_FILETRIGGER;
+       break;
+    case RPMTAG_TRANSFILETRIGGERNAME:
+       tm = RPMSCRIPT_TRANSFILETRIGGER;
+       break;
+    }
+    return tm;
+}
+
+rpmTagVal triggertag(rpmsenseFlags sense)
+{
+    rpmTagVal tag = RPMTAG_NOT_FOUND;
+    switch (sense) {
+    case RPMSENSE_TRIGGERIN:
+       tag = RPMTAG_TRIGGERIN;
+       break;
+    case RPMSENSE_TRIGGERUN:
+       tag = RPMTAG_TRIGGERUN;
+       break;
+    case RPMSENSE_TRIGGERPOSTUN:
+       tag = RPMTAG_TRIGGERPOSTUN;
+       break;
+    case RPMSENSE_TRIGGERPREIN:
+       tag = RPMTAG_TRIGGERPREIN;
+       break;
+    default:
+       break;
+    }
+    return tag;
+}
+
+rpmScript rpmScriptFromTriggerTag(Header h, rpmTagVal triggerTag,
+                           rpmscriptTriggerModes tm, uint32_t ix)
+{
+    rpmScript script = NULL;
+    struct rpmtd_s tscripts, tprogs, tflags;
+    headerGetFlags hgflags = HEADERGET_MINMEM;
+
+    switch (tm) {
+       case RPMSCRIPT_NORMALTRIGGER:
+           headerGet(h, RPMTAG_TRIGGERSCRIPTS, &tscripts, hgflags);
+           headerGet(h, RPMTAG_TRIGGERSCRIPTPROG, &tprogs, hgflags);
+           headerGet(h, RPMTAG_TRIGGERSCRIPTFLAGS, &tflags, hgflags);
+           break;
+       case RPMSCRIPT_FILETRIGGER:
+           headerGet(h, RPMTAG_FILETRIGGERSCRIPTS, &tscripts, hgflags);
+           headerGet(h, RPMTAG_FILETRIGGERSCRIPTPROG, &tprogs, hgflags);
+           headerGet(h, RPMTAG_FILETRIGGERSCRIPTFLAGS, &tflags, hgflags);
+           break;
+       case RPMSCRIPT_TRANSFILETRIGGER:
+           headerGet(h, RPMTAG_TRANSFILETRIGGERSCRIPTS, &tscripts, hgflags);
+           headerGet(h, RPMTAG_TRANSFILETRIGGERSCRIPTPROG, &tprogs, hgflags);
+           headerGet(h, RPMTAG_TRANSFILETRIGGERSCRIPTFLAGS, &tflags, hgflags);
+           break;
     }
-    return "%unknownscript";
+
+    if (rpmtdSetIndex(&tscripts, ix) >= 0 && rpmtdSetIndex(&tprogs, ix) >= 0) {
+       rpmscriptFlags sflags = 0;
+       const char *prog = rpmtdGetString(&tprogs);
+
+       if (rpmtdSetIndex(&tflags, ix) >= 0)
+           sflags = rpmtdGetNumber(&tflags);
+
+       script = rpmScriptNew(h, triggerTag, rpmtdGetString(&tscripts), sflags);
+
+       /* hack up a hge-style NULL-terminated array */
+       script->args = xmalloc(2 * sizeof(*script->args) + strlen(prog) + 1);
+       script->args[0] = (char *)(script->args + 2);
+       script->args[1] = NULL;
+       strcpy(script->args[0], prog);
+    }
+
+    rpmtdFreeData(&tscripts);
+    rpmtdFreeData(&tprogs);
+    rpmtdFreeData(&tflags);
+
+    return script;
 }
 
 rpmScript rpmScriptFromTag(Header h, rpmTagVal scriptTag)
@@ -372,31 +611,14 @@ rpmScript rpmScriptFromTag(Header h, rpmTagVal 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;
-       }
+
+       script = rpmScriptNew(h, scriptTag,
+                             headerGetString(h, scriptTag),
+                             headerGetNumber(h, getFlagTag(scriptTag)));
 
        if (headerGet(h, progTag, &prog, (HEADERGET_ALLOC|HEADERGET_ARGV))) {
            script->args = prog.data;
        }
-       free(nevra);
     }
     return script;
 }
@@ -411,3 +633,13 @@ rpmScript rpmScriptFree(rpmScript script)
     }
     return NULL;
 }
+
+rpmTagVal rpmScriptTag(rpmScript script)
+{
+    return (script != NULL) ? script->tag : RPMTAG_NOT_FOUND;
+}
+
+rpmscriptTypes rpmScriptType(rpmScript script)
+{
+    return (script != NULL) ? script->type : 0;
+}