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