tools/rpminject.c

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

Generated on Fri Oct 12 08:44:55 2007 for rpm by  doxygen 1.5.2