Main Page   Modules   Data Structures   File List   Data Fields   Globals   Related Pages  

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         headerSort(h);
00427         *hdrp = headerFree(*hdrp);
00428         *hdrp = h;
00429     } else {
00430         h = 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, NULL, fno, (int)lead.type,
00464                 csa, spec->passPhrase, &(spec->cookie));
00465     } else {
00466         rc = writeRPM(&spec->packages->header, NULL, 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 }

Generated on Sun Oct 26 13:02:04 2003 for rpm by doxygen1.2.18