- 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()
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)) {
}
}
+ /* 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);
#include <rpm/rpmlog.h>
#include "rpmio/rpmlua.h"
+#include "lib/rpmscript.h" /* script flags */
#include "debug.h"
/**
*/
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;
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;
rpmTag tag = 0;
rpmsenseFlags tagflags = 0;
rpmTag progtag = 0;
+ rpmTag flagtag = 0;
+ rpmscriptFlags scriptFlags = 0;
int flag = PART_SUBNAME;
Package pkg;
StringBuf sb = NULL;
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}
};
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:
tagflags = 0;
reqtag = RPMTAG_TRIGGERPREIN;
progtag = RPMTAG_TRIGGERSCRIPTPROG;
+ flagtag = RPMTAG_TRIGGERSCRIPTFLAGS;
partname = "%triggerprein";
break;
case PART_TRIGGERIN:
tagflags = 0;
reqtag = RPMTAG_TRIGGERIN;
progtag = RPMTAG_TRIGGERSCRIPTPROG;
+ flagtag = RPMTAG_TRIGGERSCRIPTFLAGS;
partname = "%triggerin";
break;
case PART_TRIGGERUN:
tagflags = 0;
reqtag = RPMTAG_TRIGGERUN;
progtag = RPMTAG_TRIGGERSCRIPTPROG;
+ flagtag = RPMTAG_TRIGGERSCRIPTFLAGS;
partname = "%triggerun";
break;
case PART_TRIGGERPOSTUN:
tagflags = 0;
reqtag = RPMTAG_TRIGGERPOSTUN;
progtag = RPMTAG_TRIGGERSCRIPTPROG;
+ flagtag = RPMTAG_TRIGGERSCRIPTFLAGS;
partname = "%triggerpostun";
break;
}
goto exit;
}
+ scriptFlags |= expand ? RPMSCRIPT_EXPAND : 0;
+ scriptFlags |= qformat ? RPMSCRIPT_QFORMAT : 0;
+
sb = newStringBuf();
if ((rc = readLine(spec, STRIP_NOTHING)) > 0) {
nextPart = PART_NONE;
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)))
if (*p != '\0') {
headerPutString(pkg->header, tag, p);
}
+ if (scriptFlags) {
+ headerPutUint32(pkg->header, flagtag, &scriptFlags, 1);
+ }
if (file) {
switch (parsePart) {
char * fileName;
char * script;
char * prog;
+ uint32_t flags;
struct TriggerFileEntry * next;
};
(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))
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;
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);
}
}
}
( 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 }
};
}
}
+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) {
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;
#include <rpm/rpmtypes.h>
#include <rpm/argv.h>
+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 {
char **args; /* scriptlet call arguments */
char *body; /* script body */
char *descr; /* description for logging */
+ rpmscriptFlags flags; /* flags to control operation */
};
RPM_GNUC_INTERNAL
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;
PLATFORM
POLICIES
POSTIN
+POSTINFLAGS
POSTINPROG
POSTTRANS
+POSTTRANSFLAGS
POSTTRANSPROG
POSTUN
+POSTUNFLAGS
POSTUNPROG
PREFIXES
PREIN
+PREINFLAGS
PREINPROG
PRETRANS
+PRETRANSFLAGS
PRETRANSPROG
PREUN
+PREUNFLAGS
PREUNPROG
PROVIDEFLAGS
PROVIDENAME
TRIGGERFLAGS
TRIGGERINDEX
TRIGGERNAME
+TRIGGERSCRIPTFLAGS
TRIGGERSCRIPTPROG
TRIGGERSCRIPTS
TRIGGERTYPE
VENDOR
VERBOSE
VERIFYSCRIPT
+VERIFYSCRIPTFLAGS
VERIFYSCRIPTPROG
VERSION
XPM