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>
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
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:
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:
00179 case RPM_INT16_TYPE:
00180 case RPM_I18NSTRING_TYPE:
00181 default:
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) {
00216 c->done = -1;
00217 continue;
00218 }
00219 ca = CA_NEW;
00220 newc = c;
00221 c->done = 1;
00222 break;
00223 case INJ_MODIFY:
00224 if (ca == CA_OMIT) {
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) {
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:
00284 default:
00285
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:
00297 headerAddEntry(res, tag, type, nvals, ncount);
00298 break;
00299 case CA_OMIT:
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
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;
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;
00452
00453
00454 if ((rc = readRPM(fni, &spec, &lead, &sigs, csa)) != 0)
00455 return rc;
00456
00457
00458 if ((rc = headerInject(&spec->packages->header, cmds, ncmds)) != 0)
00459 goto exit;
00460
00461
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
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();
00535 #endif
00536
00537 setprogname(argv[0]);
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 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
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 }