# tools/rpminject.c

Go to the documentation of this file.

    00001 #include "system.h"
    00002
    00003 #include "rpmbuild.h"
    00004 #include "buildio.h"
    00005
    00006 #include "header.h"
    00007 #include "rpmlead.h"
    00008 #include "signature.h"
    00009
    00010 #include <err.h>        /* XXX !HAVE_ERR_H: get from misc */
    00011 #include "debug.h"
    00012
    00013 typedef enum injmode_e { INJ_UNKNOWN, INJ_ADD, INJ_DELETE, INJ_MODIFY } injmode_t;
    00014
    00015 injmode_t injmode = INJ_UNKNOWN;
    00016
    00017 typedef struct cmd_s {
    00018     injmode_t   injmode;
    00019     char *      tag;
    00020     int_32      tagval;
    00021     int         done;
    00022     int         oldcnt;
    00023     int         nvals;
    00024     char **     vals;
    00025 } cmd_t;
    00026
    00027 #define MAXCMDS 40
    00028 cmd_t *cmds[MAXCMDS];
    00029 int ncmds = 0;
    00030
    00031 static const char * pr_injmode(injmode_t injmode)
    00032 {
    00033     switch(injmode) {
    00034     case INJ_ADD:       return("add");
    00035     case INJ_DELETE:    return("delete");
    00036     case INJ_MODIFY:    return("modify");
    00037     case INJ_UNKNOWN:   return("unknown");
    00038     default:            return("???");
    00039     }
    00040     /*@notreached@*/
    00041 }
    00042
    00043 static const char *hdri18ntbl = "HEADER_I18NTABLE";
    00044
    00045 static const char * getTagString(int tval)
    00046 {
    00047     const struct headerTagTableEntry *t;
    00048
    00049     for (t = rpmTagTable; t->name != NULL; t++) {
    00050         if (t->val == tval)
    00051             return t->name;
    00052     }
    00053     if (tval == HEADER_I18NTABLE)
    00054         return hdri18ntbl;
    00055     return NULL;
    00056 }
    00057
    00058 static int getTagVal(const char *tname)
    00059 {
    00060     const struct headerTagTableEntry *t;
    00061     int tval;
    00062
    00063     if (xstrncasecmp("RPMTAG_", tname, sizeof("RPMTAG_"))) {
    00064         char *tagname = alloca(sizeof("RPMTAG_") + strlen(tname));
    00065         sprintf(tagname, "RPMTAG_%s", tname);
    00066         tname = tagname;
    00067     }
    00068
    00069     for (t = rpmTagTable; t->name != NULL; t++) {
    00070         if (!xstrncasecmp(tname, t->name, strlen(t->name)))
    00071             return t->val;
    00072     }
    00073     if (!xstrcasecmp(tname, hdri18ntbl))
    00074         return HEADER_I18NTABLE;
    00075
    00076     tval = atoi(tname);
    00077     return tval;
    00078 }
    00079
    00080 static const struct headerTypeTableEntry {
    00081     char *name;
    00082     int_32 val;
    00083 } rpmTypeTable[] = {
    00084     {"RPM_NULL_TYPE",   0},
    00085     {"RPM_CHAR_TYPE",   1},
    00086     {"RPM_INT8_TYPE",   2},
    00087     {"RPM_INT16_TYPE",  3},
    00088     {"RPM_INT32_TYPE",  4},
    00089     {"RPM_INT64_TYPE",  5},
    00090     {"RPM_STRING_TYPE", 6},
    00091     {"RPM_BIN_TYPE",    7},
    00092     {"RPM_STRING_ARRAY_TYPE",   8},
    00093     {"RPM_I18NSTRING_TYPE",     9},
    00094     {NULL,      0}
    00095 };
    00096
    00097 static char *
    00098 getTypeString(int tval)
    00099 {
    00100     const struct headerTypeTableEntry *t;
    00101     static char buf[128];
    00102
    00103     for (t = rpmTypeTable; t->name != NULL; t++) {
    00104         if (t->val == tval)
    00105             return t->name;
    00106     }
    00107     sprintf(buf, "<RPM_%d_TYPE>", tval);
    00108     return buf;
    00109 }
    00110
    00111 /* ========================================================================= */
    00112
    00113 enum cvtaction {CA_OLD, CA_NEW, CA_OMIT, CA_ERR};
    00114
    00115 static enum cvtaction convertAMD(enum cvtaction ca, int_32 type,
    00116         void ** nvalsp, int_32 *ncountp, cmd_t *newc)
    00117 {
    00118     int i;
    00119
    00120     if (newc == NULL)
    00121         return ca;
    00122     if (!(nvalsp && ncountp))
    00123         return CA_ERR;
    00124
    00125     *nvalsp = NULL;
    00126     *ncountp = 0;
    00127
    00128     switch (ca) {
    00129     case CA_OLD:
    00130     case CA_OMIT:
    00131     case CA_ERR:
    00132     default:
    00133         break;
    00134     case CA_NEW:
    00135         switch (type) {
    00136         case RPM_INT32_TYPE:
    00137         {   int_32 *intp = xmalloc(newc->nvals * sizeof(*intp));
    00138             for (i = 0; i < newc->nvals; i++) {
    00139                 long ival;
    00140                 char *end;
    00141                 end = NULL;
    00142                 ival = strtol(newc->vals[i], &end, 0);
    00143                 if (end && *end)
    00144                     break;
    00145                 if ((((unsigned long)ival) >> (8*sizeof(*intp))) != 0)
    00146                     break;
    00147                 intp[i] = ival;
    00148             }
    00149             if (i < newc->nvals) {
    00150                 ca = CA_ERR;
    00151                 free(intp);
    00152                 break;
    00153             }
    00154             *nvalsp = intp;
    00155             *ncountp = newc->nvals;
    00156         }   break;
    00157         case RPM_BIN_TYPE:      /* icons & signatures */
    00158         case RPM_STRING_TYPE:
    00159             if (newc->nvals != 1) {
    00160                 newc->done = 0;
    00161                 ca = CA_ERR;
    00162                 break;
    00163             }
    00164             *nvalsp = xstrdup(newc->vals[0]);
    00165             *ncountp = newc->nvals;
    00166             break;
    00167         case RPM_STRING_ARRAY_TYPE:
    00168         {   const char **av = xmalloc((newc->nvals+1) * sizeof(char *));
    00169             for (i = 0; i < newc->nvals; i++) {
    00170                 av[i] = newc->vals[i];
    00171             }
    00172             av[newc->nvals] = NULL;
    00173             *nvalsp = av;
    00174             *ncountp = newc->nvals;
    00175         }   break;
    00176         case RPM_NULL_TYPE:
    00177         case RPM_CHAR_TYPE:
    00178         case RPM_INT8_TYPE:     /* arch & os */
    00179         case RPM_INT16_TYPE:    /* file modes & rdevs */
    00180         case RPM_I18NSTRING_TYPE:
    00181         default:        /* this conversion cannot be performed (yet) */
    00182             newc->done = 0;
    00183             ca = CA_ERR;
    00184             break;
    00185         }
    00186         break;
    00187     }
    00188
    00189     return ca;
    00190 }
    00191
    00192 static enum cvtaction convertExistingAMD(int_32 tag, int_32 type,
    00193         void ** valsp, int_32 *countp, void ** nvalsp, int_32 *ncountp,
    00194         cmd_t *cmds[], int ncmds)
    00195 {
    00196     cmd_t *newc = NULL;
    00197     enum cvtaction ca = CA_OLD;
    00198     int i;
    00199
    00200     if (!((tag >= RPMTAG_NAME && tag < RPMTAG_FIRSTFREE_TAG)
    00201         || tag >= RPMTAG_EXTERNAL_TAG))
    00202         return ca;
    00203
    00204     for (i = 0; i < ncmds; i++) {
    00205         cmd_t *c;
    00206         c = cmds[i];
    00207
    00208         if (tag != c->tagval)
    00209             continue;
    00210         if (c->done)
    00211             continue;
    00212
    00213         switch (c->injmode) {
    00214         case INJ_ADD:
    00215             if (ca != CA_OMIT) {/* old tag was deleted, now adding again */
    00216                 c->done = -1;
    00217                 continue;
    00218             }
    00219             ca = CA_NEW;
    00220             newc = c;
    00221             c->done = 1;
    00222             break;
    00223         case INJ_MODIFY:        /* XXX for now, this is delete, then add */
    00224             if (ca == CA_OMIT) {/* old tag was deleted, can't modify */
    00225                 c->done = -1;
    00226                 continue;
    00227             }
    00228             ca = CA_NEW;
    00229             newc = c;
    00230             c->done = 1;
    00231             break;
    00232         case INJ_DELETE:
    00233             if (ca == CA_OMIT)  {/* old tag was deleted, now deleting again */
    00234                 c->done = -1;
    00235                 continue;
    00236             }
    00237             ca = CA_OMIT;
    00238             newc = c;
    00239             c->done = 1;
    00240             break;
    00241         case INJ_UNKNOWN:
    00242         default:
    00243             c->done = -1;
    00244             break;
    00245         }
    00246     }
    00247
    00248     if (newc) {
    00249         ca = convertAMD(ca, type, nvalsp, ncountp, newc);
    00250         switch (ca) {
    00251         case CA_OMIT:
    00252         case CA_NEW:
    00253             newc->oldcnt = *countp;
    00254             break;
    00255         case CA_OLD:
    00256         case CA_ERR:
    00257             break;
    00258         }
    00259     }
    00260     return ca;
    00261 }
    00262
    00263 static
    00264 Header headerCopyWithConvert(Header h, cmd_t *cmds[], int ncmds)
    00265 {
    00266     int_32 tag, type, count;
    00267     void *vals;
    00268     HeaderIterator headerIter;
    00269     Header res = headerNew();
    00270
    00271     headerIter = headerInitIterator(h);
    00272
    00273     while (headerNextIterator(headerIter, &tag, &type, &vals, &count)) {
    00274         enum cvtaction ca;
    00275         void *nvals;
    00276         int_32 ncount;
    00277
    00278         nvals = NULL;
    00279         ncount = 0;
    00280         ca = convertExistingAMD(tag, type, &vals, &count, &nvals, &ncount, cmds, ncmds);
    00281         switch (ca) {
    00282         case CA_ERR:
    00283         case CA_OLD:            /* copy old tag and values to header */
    00284         default:
    00285             /* Don't copy the old changelog, we'll do that later. */
    00286             switch (tag) {
    00287             case RPMTAG_CHANGELOGTIME:
    00288             case RPMTAG_CHANGELOGNAME:
    00289             case RPMTAG_CHANGELOGTEXT:
    00290                 break;
    00291             default:
    00292                 headerAddEntry(res, tag, type, vals, count);
    00293                 break;
    00294             }
    00295             break;
    00296         case CA_NEW:            /* copy new tag and values to header */
    00297             headerAddEntry(res, tag, type, nvals, ncount);
    00298             break;
    00299         case CA_OMIT:           /* delete old tag and values from header */
    00300             break;
    00301         }
    00302
    00303         if (type == RPM_STRING_ARRAY_TYPE || type == RPM_I18NSTRING_TYPE)
    00304             free(vals);
    00305         if (nvals)
    00306             free(nvals);
    00307     }
    00308
    00309     headerFreeIterator(headerIter);
    00310
    00311     return res;
    00312 }
    00313
    00314 static char * genChangelog(cmd_t *cmds[], int ncmds)
    00315 {
    00316 #define MYBUFSIZ (2*BUFSIZ)
    00317     char *b, *buf = xmalloc(MYBUFSIZ);
    00318     int i;
    00319
    00320     b = buf;
    00321     for (i = 0; i < ncmds; i++) {
    00322         cmd_t *c;
    00323
    00324         if ((c = cmds[i]) == NULL)
    00325             continue;
    00326
    00327         b += sprintf(b, "- %s tag %s(%d)",
    00328                 pr_injmode(c->injmode), c->tag, c->tagval);
    00329
    00330         if (c->oldcnt || c->nvals) {
    00331             *b++ = '\t';
    00332             *b++ = '(';
    00333             if (c->oldcnt)
    00334                 b += sprintf(b, "oldcnt %d", c->oldcnt);
    00335             if (c->oldcnt && c->nvals) {
    00336                 *b++ = ',';
    00337                 *b++ = ' ';
    00338             }
    00339             if (c->nvals)
    00340                 b += sprintf(b, "nvals %d", c->nvals);
    00341             *b++ = ')';
    00342         }
    00343         *b++ = '\n';
    00344     }
    00345     *b = '\0';
    00346
    00347     return buf;
    00348 }
    00349
    00350 static int
    00351 headerInject(Header *hdrp, cmd_t *cmds[], int ncmds)
    00352 {
    00353     Header h;
    00354     int ec = 0;
    00355     int i;
    00356
    00357     if (!(hdrp && cmds && ncmds > 0))
    00358         return -1;
    00359
    00360     h = headerCopyWithConvert(*hdrp, cmds, ncmds);
    00361     for (i = 0; i < ncmds; i++) {
    00362         cmd_t *c;
    00363         int rc;
    00364
    00365         if ((c = cmds[i]) == NULL)
    00366             continue;
    00367
    00368         rc = headerIsEntry(h, c->tagval);
    00369         if (!rc && !c->done && c->injmode != INJ_DELETE) {
    00370             int_32 type, ncount;
    00371             void *nvals;
    00372             enum cvtaction ca;
    00373
    00374             type = (c->nvals > 0) ? RPM_STRING_ARRAY_TYPE : RPM_STRING_TYPE;
    00375             ca = convertAMD(CA_NEW, type, &nvals, &ncount, c);
    00376             if (ca == CA_NEW)
    00377                 headerAddEntry(h, c->tagval, type, nvals, ncount);
    00378             rc = headerIsEntry(h, c->tagval);
    00379         }
    00380
    00381         switch(c->injmode) {
    00382         case INJ_ADD:
    00383             if (!(rc && c->done > 0)) {
    00384                 warnx(_("failed to add tag %s"), getTagString(c->tagval));
    00385                 ec = 1;
    00386             }
    00387             break;
    00388         case INJ_DELETE:
    00389             if (!(!rc && c->done > 0)) {
    00390                 warnx(_("failed to delete tag %s"), getTagString(c->tagval));
    00391                 ec = 1;
    00392             }
    00393             break;
    00394         case INJ_MODIFY:
    00395             if (!(rc && c->done > 0)) {
    00396                 warnx(_("failed to modify tag %s"), getTagString(c->tagval));
    00397                 ec = 1;
    00398             }
    00399             break;
    00400         case INJ_UNKNOWN:
    00401         default:
    00402             ec = 1;
    00403             break;
    00404         }
    00405
    00406         /* XXX possibly need strict mode to exit immediately here */
    00407     }
    00408
    00409     if (ec == 0 && *hdrp) {
    00410         static char name[512] = "";
    00411         static const char *text = NULL;
    00412         static int cltags[] = {
    00413             RPMTAG_CHANGELOGTIME,
    00414             RPMTAG_CHANGELOGNAME,
    00415             RPMTAG_CHANGELOGTEXT,
    00416             0
    00417         };
    00418
    00419         if (name[0] == '\0')
    00420             sprintf(name, "rpminject <%s@%s>", getUname(getuid()), buildHost());
    00421         if (text == NULL)
    00422             text = genChangelog(cmds, ncmds);
    00423
    00424         addChangelogEntry(h, *getBuildTime(), name, text);
    00425         headerCopyTags(*hdrp, h, cltags);
    00426         headerFree(*hdrp);
    00427         headerSort(h);
    00428         *hdrp = h;
    00429     } else {
    00430         headerFree(h);
    00431     }
    00432
    00433     return ec;
    00434 }
    00435
    00436 /* ========================================================================= */
    00437
    00438 static int
    00439 rewriteRPM(const char *fni, const char *fno, cmd_t *cmds[], int ncmds)
    00440 {
    00441     struct rpmlead lead;        /* XXX FIXME: exorcize lead/arch/os */
    00442     Header sigs;
    00443     Spec spec;
    00444     CSA_t csabuf, *csa = &csabuf;
    00445     int rc;
    00446
    00447     csa->cpioArchiveSize = 0;
    00448     csa->cpioFdIn = fdNew("init (rewriteRPM)");
    00449     csa->cpioList = NULL;
    00450     csa->cpioCount = 0;
    00451     csa->lead = &lead;          /* XXX FIXME: exorcize lead/arch/os */
    00452
    00453     /* Read rpm and (partially) recreate spec/pkg control structures */
    00454     if ((rc = readRPM(fni, &spec, &lead, &sigs, csa)) != 0)
    00455         return rc;
    00456
    00457     /* Inject new strings into header tags */
    00458     if ((rc = headerInject(&spec->packages->header, cmds, ncmds)) != 0)
    00459         goto exit;
    00460
    00461     /* Rewrite the rpm */
    00462     if (lead.type == RPMLEAD_SOURCE) {
    00463         rc = writeRPM(&spec->packages->header, fno, (int)lead.type,
    00464                 csa, spec->passPhrase, &(spec->cookie));
    00465     } else {
    00466         rc = writeRPM(&spec->packages->header, fno, (int)lead.type,
    00467                 csa, spec->passPhrase, NULL);
    00468     }
    00469
    00470 exit:
    00471     Fclose(csa->cpioFdIn);
    00472     return rc;
    00473
    00474 }
    00475
    00476 /* ========================================================================= */
    00477
    00478 static int
    00479 do_inject(cmd_t *cmds[], int ncmds, const char *argv[])
    00480 {
    00481     const char *arg;
    00482     int ec = 0;
    00483
    00484     if (argv == NULL || *argv == NULL) {
    00485         /* XXX generate lead/header to stdout */
    00486         return 0;
    00487     }
    00488
    00489     while ((arg = *argv++) != NULL) {
    00490         char *fni = xmalloc(strlen(arg) + sizeof("-SAVE"));
    00491         const char *fno = arg;
    00492
    00493         strcpy(fni, arg);
    00494         strcat(fni, "-SAVE");
    00495         unlink(fni);
    00496         if (link(fno, fni)) {
    00497             warn(_("can't link temp input file %s"), fni);
    00498             ec++;
    00499             continue;
    00500         }
    00501         if (rewriteRPM(fni, fno, cmds, ncmds)) {
    00502             unlink(fno);
    00503             if (rename(fni, fno))
    00504                 warn(_("can't rename %s to %s"), fni, fno);
    00505             ec++;
    00506         }
    00507         if (fni) free(fni);
    00508     }
    00509
    00510     return ec;
    00511 }
    00512
    00513 static struct poptOption optionsTable[] = {
    00514  { "add",       'a', 0, 0, 'a',                 NULL, NULL },
    00515  { "del",       'd', 0, 0, 'd',                 NULL, NULL },
    00516  { "injtags",   'i', 0, 0, 'i',                 NULL, NULL },
    00517  { "modify",    'm', 0, 0, 'm',                 NULL, NULL },
    00518  { "tag",       't', POPT_ARG_STRING, 0, 't',   NULL, NULL },
    00519  { "value",     'v', POPT_ARG_STRING, 0, 'v',   NULL, NULL },
    00520  { NULL,        0, 0, 0, 0,                     NULL, NULL }
    00521 };
    00522
    00523 int
    00524 main(int argc, char *argv[])
    00525 {
    00526     poptContext optCon;
    00527     char * optArg;
    00528     cmd_t *c = NULL;
    00529     int arg;
    00530     int ec = 0;
    00531     injmode_t lastmode = INJ_UNKNOWN;
    00532
    00533 #if HAVE_MCHECK_H && HAVE_MTRACE
    00534     mtrace();  /* Trace malloc only if MALLOC_TRACE=mtrace-output-file. */
    00535 #endif
    00536
    00537     setprogname(argv[0]);       /* Retrofit glibc __progname */
    00538     (void)setlocale(LC_ALL, "" );
    00539
    00540 #ifdef  __LCLINT__
    00541 #define LOCALEDIR       "/usr/share/locale"
    00542 #endif
    00543     (void)bindtextdomain(PACKAGE, LOCALEDIR);
    00544     (void)textdomain(PACKAGE);
    00545
    00546     optCon = poptGetContext("rpminject", argc, argv, optionsTable, 0);
    00547     poptReadDefaultConfig(optCon, 1);
    00548
    00549     while ((arg = poptGetNextOpt(optCon)) > 0) {
    00550         optArg = poptGetOptArg(optCon);
    00551         switch (arg) {
    00552         case 'a':
    00553             injmode = INJ_ADD;
    00554             break;
    00555         case 'd':
    00556             injmode = INJ_DELETE;
    00557             break;
    00558         case 'm':
    00559             injmode = INJ_MODIFY;
    00560             break;
    00561         case 't':
    00562             if (ncmds == 0 || c == NULL)
    00563                 errx(EXIT_FAILURE, _("missing inject mode before \"--tag %s\""), optArg);
    00564             if (c->tag) {
    00565                 if (c->injmode != INJ_DELETE &&
    00566                   (c->nvals <= 0 || c->vals == NULL))
    00567                     errx(EXIT_FAILURE, _("add/modify inject mode with \"--tag %s\" needs a value"), c->tag);
    00568                 cmds[ncmds] = c = xcalloc(1, sizeof(cmd_t));
    00569                 cmds[ncmds]->injmode = cmds[ncmds-1]->injmode;
    00570                 ncmds++;
    00571             }
    00572             c->tagval = getTagVal(optArg);
    00573             if (!((c->tagval >= RPMTAG_NAME && c->tagval < RPMTAG_FIRSTFREE_TAG)
    00574                 || c->tagval >= RPMTAG_EXTERNAL_TAG))
    00575                 errx(EXIT_FAILURE, _("unknown rpm tag \"--tag %s\""), optArg);
    00576             c->tag = xstrdup(optArg);
    00577             break;
    00578         case 'v':
    00579             if (ncmds == 0 || c == NULL)
    00580                 errx(EXIT_FAILURE, _("missing inject mode before \"--value %s\""), optArg);
    00581             if (c->tag == NULL)
    00582                 errx(EXIT_FAILURE, _("missing tag name before \"--value %s\""), optArg);
    00583             if (c->nvals == 0 || c->vals == NULL) {
    00584                 c->vals = xcalloc(2, sizeof(char *));
    00585             } else {
    00586                 c->vals = xrealloc(c->vals,
    00587                                 (c->nvals+2)*sizeof(char *));
    00588             }
    00589             c->vals[c->nvals++] = xstrdup(optArg);
    00590             c->vals[c->nvals] = NULL;
    00591             break;
    00592         case 'i':
    00593             rpmDisplayQueryTags(stdout);
    00594             exit(EXIT_SUCCESS);
    00595             break;
    00596         default:
    00597             errx(EXIT_FAILURE, _("unknown popt return (%d)"), arg);
    00598             /*@notreached@*/ break;
    00599         }
    00600
    00601         if (injmode != lastmode) {
    00602             cmds[ncmds] = c = xcalloc(1, sizeof(cmd_t));
    00603             cmds[ncmds]->injmode = lastmode = injmode;
    00604             ncmds++;
    00605         }
    00606     }
    00607
    00608     /* XXX I don't want to read rpmrc */
    00609     addMacro(NULL, "_tmppath", NULL, "/tmp", RMIL_DEFAULT);
    00610
    00611     ec = do_inject(cmds, ncmds, poptGetArgs(optCon));
    00612
    00613     optCon = poptFreeContext(optCon);
    00614     return ec;
    00615 }