From d370816ba508a33cc39252cbb9ba85dcd99504d5 Mon Sep 17 00:00:00 2001 From: Panu Matilainen Date: Thu, 11 Mar 2010 12:06:49 +0200 Subject: [PATCH] Support run-time macro and queryformat expansion on scriptlets - Add per-scriptlet type flag tags to control special behavior. - Add rpmlib dependency on scriptlet expansion - if a package relies on scriptlet expansion it cannot be correctly installed with a version of rpm that doesn't support it. - Expansion is always an opt-in behavior, enabled with -q and/or -e argument in spec. We can't just blindly expand even macros as there's no telling %{} constructs might mean in whatever language is used for the script. - Queryformat expansion requires great care with strange and ugly escapes when writing scriptlets, but OTOH it permits access arbitrary header data at runtime, which has previously been completely impossible. - The handling of these expansions needs unifying for all scriptlet types, the trigger scriptlet handling is uuugly. Macro expansion could be transparently done from rpmScriptRun(), but because of their similar syntax, macro expansion needs to happen before query format expansion, and we dont have the header available in rpmScriptrun() --- build/pack.c | 13 +++++++++++++ build/parseScript.c | 33 +++++++++++++++++++++++++++++++-- build/rpmspec.h | 1 + lib/psm.c | 24 +++++++++++++++++++++--- lib/rpmds.c | 3 +++ lib/rpmscript.c | 32 +++++++++++++++++++++++++++++++- lib/rpmscript.h | 7 +++++++ lib/rpmtag.h | 8 ++++++++ tests/rpmgeneral.at | 8 ++++++++ 9 files changed, 123 insertions(+), 6 deletions(-) diff --git a/build/pack.c b/build/pack.c index 1857257..fa947bf 100644 --- a/build/pack.c +++ b/build/pack.c @@ -185,6 +185,7 @@ static int addFileToArrayTag(rpmSpec spec, const char *file, Header h, rpmTag ta static rpmRC processScriptFiles(rpmSpec spec, Package pkg) { struct TriggerFileEntry *p; + int addflags = 0; if (pkg->preInFile) { if (addFileToTag(spec, pkg->preInFile, pkg->header, RPMTAG_PREIN)) { @@ -237,8 +238,20 @@ static rpmRC processScriptFiles(rpmSpec spec, Package pkg) } } + /* if any trigger has flags, we need to add flags entry for all of them */ + for (p = pkg->triggerFiles; p != NULL; p = p->next) { + if (p->flags) { + addflags = 1; + break; + } + } + for (p = pkg->triggerFiles; p != NULL; p = p->next) { headerPutString(pkg->header, RPMTAG_TRIGGERSCRIPTPROG, p->prog); + if (addflags) { + headerPutUint32(pkg->header, RPMTAG_TRIGGERSCRIPTFLAGS, + &p->flags, 1); + } if (p->script) { headerPutString(pkg->header, RPMTAG_TRIGGERSCRIPTS, p->script); diff --git a/build/parseScript.c b/build/parseScript.c index c424951..32793c5 100644 --- a/build/parseScript.c +++ b/build/parseScript.c @@ -10,6 +10,7 @@ #include #include "rpmio/rpmlua.h" +#include "lib/rpmscript.h" /* script flags */ #include "debug.h" @@ -17,7 +18,7 @@ /** */ static int addTriggerIndex(Package pkg, const char *file, - const char *script, const char *prog) + const char *script, const char *prog, rpmscriptFlags flags) { struct TriggerFileEntry *tfe; struct TriggerFileEntry *list = pkg->triggerFiles; @@ -37,6 +38,7 @@ static int addTriggerIndex(Package pkg, const char *file, tfe->fileName = (file) ? xstrdup(file) : NULL; tfe->script = (script && *script != '\0') ? xstrdup(script) : NULL; tfe->prog = xstrdup(prog); + tfe->flags = flags; tfe->index = index; tfe->next = NULL; @@ -70,6 +72,8 @@ int parseScript(rpmSpec spec, int parsePart) rpmTag tag = 0; rpmsenseFlags tagflags = 0; rpmTag progtag = 0; + rpmTag flagtag = 0; + rpmscriptFlags scriptFlags = 0; int flag = PART_SUBNAME; Package pkg; StringBuf sb = NULL; @@ -85,10 +89,14 @@ int parseScript(rpmSpec spec, int parsePart) const char *name = NULL; const char *prog = "/bin/sh"; const char *file = NULL; + int expand = 0; + int qformat = 1; struct poptOption optionsTable[] = { { NULL, 'p', POPT_ARG_STRING, &prog, 'p', NULL, NULL}, { NULL, 'n', POPT_ARG_STRING, &name, 'n', NULL, NULL}, { NULL, 'f', POPT_ARG_STRING, &file, 'f', NULL, NULL}, + { NULL, 'e', POPT_ARG_NONE, &expand, 'e', NULL, NULL}, + { NULL, 'q', POPT_ARG_NONE, &qformat, 'q', NULL, NULL}, { 0, 0, 0, 0, 0, NULL, NULL} }; @@ -97,42 +105,49 @@ int parseScript(rpmSpec spec, int parsePart) tag = RPMTAG_PREIN; tagflags = RPMSENSE_SCRIPT_PRE; progtag = RPMTAG_PREINPROG; + flagtag = RPMTAG_PREINFLAGS; partname = "%pre"; break; case PART_POST: tag = RPMTAG_POSTIN; tagflags = RPMSENSE_SCRIPT_POST; progtag = RPMTAG_POSTINPROG; + flagtag = RPMTAG_POSTINFLAGS; partname = "%post"; break; case PART_PREUN: tag = RPMTAG_PREUN; tagflags = RPMSENSE_SCRIPT_PREUN; progtag = RPMTAG_PREUNPROG; + flagtag = RPMTAG_PREUNFLAGS; partname = "%preun"; break; case PART_POSTUN: tag = RPMTAG_POSTUN; tagflags = RPMSENSE_SCRIPT_POSTUN; progtag = RPMTAG_POSTUNPROG; + flagtag = RPMTAG_POSTUNFLAGS; partname = "%postun"; break; case PART_PRETRANS: tag = RPMTAG_PRETRANS; tagflags = 0; progtag = RPMTAG_PRETRANSPROG; + flagtag = RPMTAG_PRETRANSFLAGS; partname = "%pretrans"; break; case PART_POSTTRANS: tag = RPMTAG_POSTTRANS; tagflags = 0; progtag = RPMTAG_POSTTRANSPROG; + flagtag = RPMTAG_POSTTRANSFLAGS; partname = "%posttrans"; break; case PART_VERIFYSCRIPT: tag = RPMTAG_VERIFYSCRIPT; tagflags = RPMSENSE_SCRIPT_VERIFY; progtag = RPMTAG_VERIFYSCRIPTPROG; + flagtag = RPMTAG_VERIFYSCRIPTFLAGS; partname = "%verifyscript"; break; case PART_TRIGGERPREIN: @@ -140,6 +155,7 @@ int parseScript(rpmSpec spec, int parsePart) tagflags = 0; reqtag = RPMTAG_TRIGGERPREIN; progtag = RPMTAG_TRIGGERSCRIPTPROG; + flagtag = RPMTAG_TRIGGERSCRIPTFLAGS; partname = "%triggerprein"; break; case PART_TRIGGERIN: @@ -147,6 +163,7 @@ int parseScript(rpmSpec spec, int parsePart) tagflags = 0; reqtag = RPMTAG_TRIGGERIN; progtag = RPMTAG_TRIGGERSCRIPTPROG; + flagtag = RPMTAG_TRIGGERSCRIPTFLAGS; partname = "%triggerin"; break; case PART_TRIGGERUN: @@ -154,6 +171,7 @@ int parseScript(rpmSpec spec, int parsePart) tagflags = 0; reqtag = RPMTAG_TRIGGERUN; progtag = RPMTAG_TRIGGERSCRIPTPROG; + flagtag = RPMTAG_TRIGGERSCRIPTFLAGS; partname = "%triggerun"; break; case PART_TRIGGERPOSTUN: @@ -161,6 +179,7 @@ int parseScript(rpmSpec spec, int parsePart) tagflags = 0; reqtag = RPMTAG_TRIGGERPOSTUN; progtag = RPMTAG_TRIGGERSCRIPTPROG; + flagtag = RPMTAG_TRIGGERSCRIPTFLAGS; partname = "%triggerpostun"; break; } @@ -247,6 +266,9 @@ int parseScript(rpmSpec spec, int parsePart) goto exit; } + scriptFlags |= expand ? RPMSCRIPT_EXPAND : 0; + scriptFlags |= qformat ? RPMSCRIPT_QFORMAT : 0; + sb = newStringBuf(); if ((rc = readLine(spec, STRIP_NOTHING)) > 0) { nextPart = PART_NONE; @@ -286,11 +308,15 @@ int parseScript(rpmSpec spec, int parsePart) progArgv[0], NULL, (tagflags | RPMSENSE_INTERP), 0); } + if (scriptFlags) { + rpmlibNeedsFeature(pkg->header, "ScriptletExpansion", "4.9.0-1"); + } + /* Trigger script insertion is always delayed in order to */ /* get the index right. */ if (tag == RPMTAG_TRIGGERSCRIPTS) { /* Add file/index/prog triple to the trigger file list */ - index = addTriggerIndex(pkg, file, p, progArgv[0]); + index = addTriggerIndex(pkg, file, p, progArgv[0], scriptFlags); /* Generate the trigger tags */ if ((rc = parseRCPOT(spec, pkg, reqargs, reqtag, index, tagflags))) @@ -319,6 +345,9 @@ int parseScript(rpmSpec spec, int parsePart) if (*p != '\0') { headerPutString(pkg->header, tag, p); } + if (scriptFlags) { + headerPutUint32(pkg->header, flagtag, &scriptFlags, 1); + } if (file) { switch (parsePart) { diff --git a/build/rpmspec.h b/build/rpmspec.h index c944677..e3eac6c 100644 --- a/build/rpmspec.h +++ b/build/rpmspec.h @@ -24,6 +24,7 @@ struct TriggerFileEntry { char * fileName; char * script; char * prog; + uint32_t flags; struct TriggerFileEntry * next; }; diff --git a/lib/psm.c b/lib/psm.c index 78fe1eb..9f3388f 100644 --- a/lib/psm.c +++ b/lib/psm.c @@ -479,7 +479,7 @@ static rpmRC handleOneTrigger(const rpmpsm psm, (void) rpmdsSetNoPromote(trigger, 1); while ((i = rpmdsNext(trigger)) >= 0) { - struct rpmtd_s tscripts, tprogs, tindexes; + struct rpmtd_s tscripts, tprogs, tindexes, tflags; headerGetFlags hgflags = HEADERGET_MINMEM; if (!(rpmdsFlags(trigger) & psm->sense)) @@ -502,8 +502,12 @@ static rpmRC handleOneTrigger(const rpmpsm psm, char ** triggerScripts = tscripts.data; char ** triggerProgs = tprogs.data; uint32_t * triggerIndices = tindexes.data; + uint32_t * triggerFlags = NULL; uint32_t ix = triggerIndices[i]; + headerGet(trigH, RPMTAG_TRIGGERSCRIPTFLAGS, &tflags, hgflags); + triggerFlags = tflags.data; + if (arg1 < 0) { /* XXX W2DO? fails as "execution of script failed" */ rc = RPMRC_FAIL; @@ -511,17 +515,31 @@ static rpmRC handleOneTrigger(const rpmpsm psm, arg1 += psm->countCorrection; if (triggersAlreadyRun == NULL || triggersAlreadyRun[ix] == 0) { - /* construct script manually, this must not be freed */ + /* XXX TODO add rpmScript API to handle this, ugh */ + char *macro = NULL; + char *qformat = NULL; char *args[2] = { triggerProgs[ix], NULL }; struct rpmScript_s script = { .tag = triggertag(psm->sense), .body = triggerScripts[ix], + .flags = triggerFlags[ix], .args = args }; - + + if (script.flags & RPMSCRIPT_EXPAND) { + macro = rpmExpand(script.body, NULL); + script.body = macro; + } + if (script.flags & RPMSCRIPT_QFORMAT) { + qformat = headerFormat(trigH, script.body, NULL); + script.body = qformat; + } + rc = runScript(psm, pfx.data, &script, arg1, arg2); if (triggersAlreadyRun != NULL) triggersAlreadyRun[ix] = 1; + free(macro); + free(qformat); } } } diff --git a/lib/rpmds.c b/lib/rpmds.c index 344eeca..e8240fd 100644 --- a/lib/rpmds.c +++ b/lib/rpmds.c @@ -961,6 +961,9 @@ static const struct rpmlibProvides_s rpmlibProvides[] = { ( RPMSENSE_EQUAL), N_("support for POSIX.1e file capabilities") }, #endif + { "rpmlib(ScriptletExpansion)", "4.9.0-1", + ( RPMSENSE_EQUAL), + N_("package scriptlets can be expanded at install time.") }, { NULL, NULL, 0, NULL } }; diff --git a/lib/rpmscript.c b/lib/rpmscript.c index 4cde9bb..980f5fe 100644 --- a/lib/rpmscript.c +++ b/lib/rpmscript.c @@ -346,6 +346,23 @@ static rpmTag getProgTag(rpmTag scriptTag) } } +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) { @@ -373,11 +390,24 @@ rpmScript rpmScriptFromTag(Header h, rpmTag 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; - script->body = headerGetAsString(h, scriptTag); rasprintf(&script->descr, "%s(%s)", tag2sln(scriptTag), nevra); + script->body = headerGetAsString(h, scriptTag); + /* macros need to be expanded before possible queryformat */ + if (flags & RPMSCRIPT_EXPAND) { + char *body = rpmExpand(script->body, NULL); + free(script->body); + script->body = body; + } + if (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; diff --git a/lib/rpmscript.h b/lib/rpmscript.h index 8df2cc3..f7574ba 100644 --- a/lib/rpmscript.h +++ b/lib/rpmscript.h @@ -4,6 +4,12 @@ #include #include +typedef enum rpmscriptFlags_e { + RPMSCRIPT_NONE = 0, + RPMSCRIPT_EXPAND = (1 << 0), /* macro expansion */ + RPMSCRIPT_QFORMAT = (1 << 1), /* header queryformat expansion */ +} rpmscriptFlags; + typedef struct rpmScript_s * rpmScript; struct rpmScript_s { @@ -11,6 +17,7 @@ struct rpmScript_s { char **args; /* scriptlet call arguments */ char *body; /* script body */ char *descr; /* description for logging */ + rpmscriptFlags flags; /* flags to control operation */ }; RPM_GNUC_INTERNAL diff --git a/lib/rpmtag.h b/lib/rpmtag.h index ef43c5b..5f68971 100644 --- a/lib/rpmtag.h +++ b/lib/rpmtag.h @@ -292,6 +292,14 @@ typedef enum rpmTag_e { RPMTAG_HEADERCOLOR = 5017, /* i extension */ RPMTAG_VERBOSE = 5018, /* i extension */ RPMTAG_EPOCHNUM = 5019, /* i extension */ + RPMTAG_PREINFLAGS = 5020, /* i */ + RPMTAG_POSTINFLAGS = 5021, /* i */ + RPMTAG_PREUNFLAGS = 5022, /* i */ + RPMTAG_POSTUNFLAGS = 5023, /* i */ + RPMTAG_PRETRANSFLAGS = 5024, /* i */ + RPMTAG_POSTTRANSFLAGS = 5025, /* i */ + RPMTAG_VERIFYSCRIPTFLAGS = 5026, /* i */ + RPMTAG_TRIGGERSCRIPTFLAGS = 5027, /* i[] */ RPMTAG_FIRSTFREE_TAG /*!< internal */ } rpmTag; diff --git a/tests/rpmgeneral.at b/tests/rpmgeneral.at index 114a8c1..73644c2 100644 --- a/tests/rpmgeneral.at +++ b/tests/rpmgeneral.at @@ -179,17 +179,23 @@ PKGID PLATFORM POLICIES POSTIN +POSTINFLAGS POSTINPROG POSTTRANS +POSTTRANSFLAGS POSTTRANSPROG POSTUN +POSTUNFLAGS POSTUNPROG PREFIXES PREIN +PREINFLAGS PREINPROG PRETRANS +PRETRANSFLAGS PRETRANSPROG PREUN +PREUNFLAGS PREUNPROG PROVIDEFLAGS PROVIDENAME @@ -222,6 +228,7 @@ TRIGGERCONDS TRIGGERFLAGS TRIGGERINDEX TRIGGERNAME +TRIGGERSCRIPTFLAGS TRIGGERSCRIPTPROG TRIGGERSCRIPTS TRIGGERTYPE @@ -231,6 +238,7 @@ V VENDOR VERBOSE VERIFYSCRIPT +VERIFYSCRIPTFLAGS VERIFYSCRIPTPROG VERSION XPM -- 2.7.4