#include "system.h" const char *__progname; #include #include "build/buildio.h" #include #include "lib/rpmlead.h" #include /* XXX !HAVE_ERR_H: get from misc */ #include "debug.h" typedef enum injmode_e { INJ_UNKNOWN, INJ_ADD, INJ_DELETE, INJ_MODIFY } injmode_t; injmode_t injmode = INJ_UNKNOWN; typedef struct cmd_s { injmode_t injmode; char * tag; int32_t tagval; int done; int oldcnt; int nvals; char ** vals; } cmd_t; #define MAXCMDS 40 cmd_t *cmds[MAXCMDS]; int ncmds = 0; static const char * pr_injmode(injmode_t injmode) { switch(injmode) { case INJ_ADD: return("add"); case INJ_DELETE: return("delete"); case INJ_MODIFY: return("modify"); case INJ_UNKNOWN: return("unknown"); default: return("???"); } } enum cvtaction {CA_OLD, CA_NEW, CA_OMIT, CA_ERR}; static enum cvtaction convertAMD(enum cvtaction ca, int32_t type, void ** nvalsp, int32_t *ncountp, cmd_t *newc) { int i; if (newc == NULL) return ca; if (!(nvalsp && ncountp)) return CA_ERR; *nvalsp = NULL; *ncountp = 0; switch (ca) { case CA_OLD: case CA_OMIT: case CA_ERR: default: break; case CA_NEW: switch (type) { case RPM_INT32_TYPE: { int32_t *intp = xmalloc(newc->nvals * sizeof(*intp)); for (i = 0; i < newc->nvals; i++) { long ival; char *end; end = NULL; ival = strtol(newc->vals[i], &end, 0); if (end && *end) break; if ((((unsigned long)ival) >> (8*sizeof(*intp))) != 0) break; intp[i] = ival; } if (i < newc->nvals) { ca = CA_ERR; free(intp); break; } *nvalsp = intp; *ncountp = newc->nvals; } break; case RPM_BIN_TYPE: /* icons & signatures */ case RPM_STRING_TYPE: if (newc->nvals != 1) { newc->done = 0; ca = CA_ERR; break; } *nvalsp = xstrdup(newc->vals[0]); *ncountp = newc->nvals; break; case RPM_STRING_ARRAY_TYPE: { const char **av = xmalloc((newc->nvals+1) * sizeof(char *)); for (i = 0; i < newc->nvals; i++) { av[i] = newc->vals[i]; } av[newc->nvals] = NULL; *nvalsp = av; *ncountp = newc->nvals; } break; case RPM_NULL_TYPE: case RPM_CHAR_TYPE: case RPM_INT8_TYPE: /* arch & os */ case RPM_INT16_TYPE: /* file modes & rdevs */ case RPM_I18NSTRING_TYPE: default: /* this conversion cannot be performed (yet) */ newc->done = 0; ca = CA_ERR; break; } break; } return ca; } static enum cvtaction convertExistingAMD(int32_t tag, int32_t type, hPTR_t valsp, int32_t *countp, void ** nvalsp, int32_t *ncountp, cmd_t *cmds[], int ncmds) { cmd_t *newc = NULL; enum cvtaction ca = CA_OLD; int i; if (!((tag >= RPMTAG_NAME && tag < RPMTAG_FIRSTFREE_TAG) || tag >= RPMTAG_EXTERNAL_TAG)) return ca; for (i = 0; i < ncmds; i++) { cmd_t *c; c = cmds[i]; if (tag != c->tagval) continue; if (c->done) continue; switch (c->injmode) { case INJ_ADD: if (ca != CA_OMIT) {/* old tag was deleted, now adding again */ c->done = -1; continue; } ca = CA_NEW; newc = c; c->done = 1; break; case INJ_MODIFY: /* XXX for now, this is delete, then add */ if (ca == CA_OMIT) {/* old tag was deleted, can't modify */ c->done = -1; continue; } ca = CA_NEW; newc = c; c->done = 1; break; case INJ_DELETE: if (ca == CA_OMIT) {/* old tag was deleted, now deleting again */ c->done = -1; continue; } ca = CA_OMIT; newc = c; c->done = 1; break; case INJ_UNKNOWN: default: c->done = -1; break; } } if (newc) { ca = convertAMD(ca, type, nvalsp, ncountp, newc); switch (ca) { case CA_OMIT: case CA_NEW: newc->oldcnt = *countp; break; case CA_OLD: case CA_ERR: break; } } return ca; } static Header headerCopyWithConvert(Header h, cmd_t *cmds[], int ncmds) { int32_t tag, type, count; hPTR_t vals; HeaderIterator headerIter; Header res = headerNew(); headerIter = headerInitIterator(h); while (headerNextIterator(headerIter, &tag, &type, &vals, &count)) { enum cvtaction ca; void *nvals; int32_t ncount; nvals = NULL; ncount = 0; ca = convertExistingAMD(tag, type, &vals, &count, &nvals, &ncount, cmds, ncmds); switch (ca) { case CA_ERR: case CA_OLD: /* copy old tag and values to header */ default: /* Don't copy the old changelog, we'll do that later. */ switch (tag) { case RPMTAG_CHANGELOGTIME: case RPMTAG_CHANGELOGNAME: case RPMTAG_CHANGELOGTEXT: break; default: headerAddEntry(res, tag, type, vals, count); break; } break; case CA_NEW: /* copy new tag and values to header */ headerAddEntry(res, tag, type, nvals, ncount); break; case CA_OMIT: /* delete old tag and values from header */ break; } if (type == RPM_STRING_ARRAY_TYPE || type == RPM_I18NSTRING_TYPE) _free(vals); if (nvals) _free(nvals); } headerFreeIterator(headerIter); return res; } static char * genChangelog(cmd_t *cmds[], int ncmds) { #define MYBUFSIZ (2*BUFSIZ) char *b, *buf = xmalloc(MYBUFSIZ); int i; b = buf; for (i = 0; i < ncmds; i++) { cmd_t *c; if ((c = cmds[i]) == NULL) continue; b += sprintf(b, "- %s tag %s(%d)", pr_injmode(c->injmode), c->tag, c->tagval); if (c->oldcnt || c->nvals) { *b++ = '\t'; *b++ = '('; if (c->oldcnt) b += sprintf(b, "oldcnt %d", c->oldcnt); if (c->oldcnt && c->nvals) { *b++ = ','; *b++ = ' '; } if (c->nvals) b += sprintf(b, "nvals %d", c->nvals); *b++ = ')'; } *b++ = '\n'; } *b = '\0'; return buf; } static int headerInject(Header *hdrp, cmd_t *cmds[], int ncmds) { Header h; int ec = 0; int i; if (!(hdrp && cmds && ncmds > 0)) return -1; h = headerCopyWithConvert(*hdrp, cmds, ncmds); for (i = 0; i < ncmds; i++) { cmd_t *c; int rc; if ((c = cmds[i]) == NULL) continue; rc = headerIsEntry(h, c->tagval); if (!rc && !c->done && c->injmode != INJ_DELETE) { int32_t type, ncount; void *nvals; enum cvtaction ca; type = (c->nvals > 0) ? RPM_STRING_ARRAY_TYPE : RPM_STRING_TYPE; ca = convertAMD(CA_NEW, type, &nvals, &ncount, c); if (ca == CA_NEW) headerAddEntry(h, c->tagval, type, nvals, ncount); rc = headerIsEntry(h, c->tagval); } switch(c->injmode) { case INJ_ADD: if (!(rc && c->done > 0)) { warnx(_("failed to add tag %s"), rpmTagGetName(c->tagval)); ec = 1; } break; case INJ_DELETE: if (!(!rc && c->done > 0)) { warnx(_("failed to delete tag %s"), rpmTagGetName(c->tagval)); ec = 1; } break; case INJ_MODIFY: if (!(rc && c->done > 0)) { warnx(_("failed to modify tag %s"), rpmTagGetName(c->tagval)); ec = 1; } break; case INJ_UNKNOWN: default: ec = 1; break; } /* XXX possibly need strict mode to exit immediately here */ } if (ec == 0 && *hdrp) { static char name[512] = ""; static const char *text = NULL; static int cltags[] = { RPMTAG_CHANGELOGTIME, RPMTAG_CHANGELOGNAME, RPMTAG_CHANGELOGTEXT, 0 }; if (name[0] == '\0') sprintf(name, "rpminject <%s@%s>", getUname(getuid()), buildHost()); if (text == NULL) text = genChangelog(cmds, ncmds); addChangelogEntry(h, *getBuildTime(), name, text); headerCopyTags(*hdrp, h, cltags); headerSort(h); *hdrp = headerFree(*hdrp); *hdrp = h; } else { h = headerFree(h); } return ec; } /* ========================================================================= */ static int rewriteRPM(const char *fni, const char *fno, cmd_t *cmds[], int ncmds) { Header sigs; rpmSpec spec; struct cpioSourceArchive_s csabuf, *csa = &csabuf; int rc; csa->cpioArchiveSize = 0; csa->cpioFdIn = fdNew("init (rewriteRPM)"); csa->cpioList = NULL; /* Read rpm and (partially) recreate spec/pkg control structures */ if ((rc = readRPM(fni, &spec, &sigs, csa)) != 0) return rc; /* Inject new strings into header tags */ if ((rc = headerInject(&spec->packages->header, cmds, ncmds)) != 0) goto exit; /* Rewrite the rpm */ if (headerIsSource(spec->packages->header)) { rc = writeRPM(&spec->packages->header, NULL, fno, csa, spec->passPhrase, &(spec->cookie)); } else { rc = writeRPM(&spec->packages->header, NULL, fno, csa, spec->passPhrase, NULL); } exit: Fclose(csa->cpioFdIn); return rc; } /* ========================================================================= */ static int do_inject(cmd_t *cmds[], int ncmds, const char *argv[]) { const char *arg; int ec = 0; if (argv == NULL || *argv == NULL) { /* XXX generate lead/header to stdout */ return 0; } while ((arg = *argv++) != NULL) { char *fni = xmalloc(strlen(arg) + sizeof("-SAVE")); const char *fno = arg; strcpy(fni, arg); strcat(fni, "-SAVE"); unlink(fni); if (link(fno, fni)) { warn(_("can't link temp input file %s"), fni); ec++; continue; } if (rewriteRPM(fni, fno, cmds, ncmds)) { unlink(fno); if (rename(fni, fno)) warn(_("can't rename %s to %s"), fni, fno); ec++; } if (fni) free(fni); } return ec; } static struct poptOption optionsTable[] = { { "add", 'a', 0, 0, 'a', NULL, NULL }, { "del", 'd', 0, 0, 'd', NULL, NULL }, { "injtags", 'i', 0, 0, 'i', NULL, NULL }, { "modify", 'm', 0, 0, 'm', NULL, NULL }, { "tag", 't', POPT_ARG_STRING, 0, 't', NULL, NULL }, { "value", 'v', POPT_ARG_STRING, 0, 'v', NULL, NULL }, { NULL, 0, 0, 0, 0, NULL, NULL } }; int main(int argc, char *argv[]) { poptContext optCon; const char * optArg; cmd_t *c = NULL; int arg; int ec = 0; injmode_t lastmode = INJ_UNKNOWN; #if HAVE_MCHECK_H && HAVE_MTRACE mtrace(); /* Trace malloc only if MALLOC_TRACE=mtrace-output-file. */ #endif setprogname(argv[0]); /* Retrofit glibc __progname */ #if defined(ENABLE_NLS) (void)setlocale(LC_ALL, "" ); (void)bindtextdomain(PACKAGE, LOCALEDIR); (void)textdomain(PACKAGE); #endif optCon = poptGetContext("rpminject", argc, (const char **) argv, optionsTable, 0); #if RPM_USES_POPTREADDEFAULTCONFIG poptReadDefaultConfig(optCon, 1); #endif while ((arg = poptGetNextOpt(optCon)) > 0) { optArg = poptGetOptArg(optCon); switch (arg) { case 'a': injmode = INJ_ADD; break; case 'd': injmode = INJ_DELETE; break; case 'm': injmode = INJ_MODIFY; break; case 't': if (ncmds == 0 || c == NULL) errx(EXIT_FAILURE, _("missing inject mode before \"--tag %s\""), optArg); if (c->tag) { if (c->injmode != INJ_DELETE && (c->nvals <= 0 || c->vals == NULL)) errx(EXIT_FAILURE, _("add/modify inject mode with \"--tag %s\" needs a value"), c->tag); cmds[ncmds] = c = xcalloc(1, sizeof(cmd_t)); cmds[ncmds]->injmode = cmds[ncmds-1]->injmode; ncmds++; } c->tagval = rpmTagGetValue(optArg); if (!((c->tagval >= RPMTAG_NAME && c->tagval < RPMTAG_FIRSTFREE_TAG) || c->tagval >= RPMTAG_EXTERNAL_TAG)) errx(EXIT_FAILURE, _("unknown rpm tag \"--tag %s\""), optArg); c->tag = xstrdup(optArg); break; case 'v': if (ncmds == 0 || c == NULL) errx(EXIT_FAILURE, _("missing inject mode before \"--value %s\""), optArg); if (c->tag == NULL) errx(EXIT_FAILURE, _("missing tag name before \"--value %s\""), optArg); if (c->nvals == 0 || c->vals == NULL) { c->vals = xcalloc(2, sizeof(char *)); } else { c->vals = xrealloc(c->vals, (c->nvals+2)*sizeof(char *)); } c->vals[c->nvals++] = xstrdup(optArg); c->vals[c->nvals] = NULL; break; case 'i': rpmDisplayQueryTags(stdout); exit(EXIT_SUCCESS); break; default: errx(EXIT_FAILURE, _("unknown popt return (%d)"), arg); break; } if (injmode != lastmode) { cmds[ncmds] = c = xcalloc(1, sizeof(cmd_t)); cmds[ncmds]->injmode = lastmode = injmode; ncmds++; } } /* XXX I don't want to read rpmrc */ addMacro(NULL, "_tmppath", NULL, "/tmp", RMIL_DEFAULT); ec = do_inject(cmds, ncmds, poptGetArgs(optCon)); optCon = poptFreeContext(optCon); return ec; }