616 lines
14 KiB
C
616 lines
14 KiB
C
#include "system.h"
|
|
|
|
#include "rpmbuild.h"
|
|
#include "buildio.h"
|
|
|
|
#include "header.h"
|
|
#include "rpmlead.h"
|
|
#include "signature.h"
|
|
|
|
#include <err.h> /* XXX !HAVE_ERR_H: get from misc */
|
|
#include "debug.h"
|
|
|
|
typedef enum injmode_e { INJ_UNKNOWN, INJ_ADD, INJ_DELETE, INJ_MODIFY } injmode_t;
|
|
|
|
injmode_t injmode = INJ_UNKNOWN;
|
|
|
|
typedef struct cmd_s {
|
|
injmode_t injmode;
|
|
char * tag;
|
|
int_32 tagval;
|
|
int done;
|
|
int oldcnt;
|
|
int nvals;
|
|
char ** vals;
|
|
} cmd_t;
|
|
|
|
#define MAXCMDS 40
|
|
cmd_t *cmds[MAXCMDS];
|
|
int ncmds = 0;
|
|
|
|
static const char * pr_injmode(injmode_t injmode)
|
|
{
|
|
switch(injmode) {
|
|
case INJ_ADD: return("add");
|
|
case INJ_DELETE: return("delete");
|
|
case INJ_MODIFY: return("modify");
|
|
case INJ_UNKNOWN: return("unknown");
|
|
default: return("???");
|
|
}
|
|
/*@notreached@*/
|
|
}
|
|
|
|
static const char *hdri18ntbl = "HEADER_I18NTABLE";
|
|
|
|
static const char * getTagString(int tval)
|
|
{
|
|
const struct headerTagTableEntry *t;
|
|
|
|
for (t = rpmTagTable; t->name != NULL; t++) {
|
|
if (t->val == tval)
|
|
return t->name;
|
|
}
|
|
if (tval == HEADER_I18NTABLE)
|
|
return hdri18ntbl;
|
|
return NULL;
|
|
}
|
|
|
|
static int getTagVal(const char *tname)
|
|
{
|
|
const struct headerTagTableEntry *t;
|
|
int tval;
|
|
|
|
if (xstrncasecmp("RPMTAG_", tname, sizeof("RPMTAG_"))) {
|
|
char *tagname = alloca(sizeof("RPMTAG_") + strlen(tname));
|
|
sprintf(tagname, "RPMTAG_%s", tname);
|
|
tname = tagname;
|
|
}
|
|
|
|
for (t = rpmTagTable; t->name != NULL; t++) {
|
|
if (!xstrncasecmp(tname, t->name, strlen(t->name)))
|
|
return t->val;
|
|
}
|
|
if (!xstrcasecmp(tname, hdri18ntbl))
|
|
return HEADER_I18NTABLE;
|
|
|
|
tval = atoi(tname);
|
|
return tval;
|
|
}
|
|
|
|
static const struct headerTypeTableEntry {
|
|
char *name;
|
|
int_32 val;
|
|
} rpmTypeTable[] = {
|
|
{"RPM_NULL_TYPE", 0},
|
|
{"RPM_CHAR_TYPE", 1},
|
|
{"RPM_INT8_TYPE", 2},
|
|
{"RPM_INT16_TYPE", 3},
|
|
{"RPM_INT32_TYPE", 4},
|
|
{"RPM_INT64_TYPE", 5},
|
|
{"RPM_STRING_TYPE", 6},
|
|
{"RPM_BIN_TYPE", 7},
|
|
{"RPM_STRING_ARRAY_TYPE", 8},
|
|
{"RPM_I18NSTRING_TYPE", 9},
|
|
{NULL, 0}
|
|
};
|
|
|
|
static char *
|
|
getTypeString(int tval)
|
|
{
|
|
const struct headerTypeTableEntry *t;
|
|
static char buf[128];
|
|
|
|
for (t = rpmTypeTable; t->name != NULL; t++) {
|
|
if (t->val == tval)
|
|
return t->name;
|
|
}
|
|
sprintf(buf, "<RPM_%d_TYPE>", tval);
|
|
return buf;
|
|
}
|
|
|
|
/* ========================================================================= */
|
|
|
|
enum cvtaction {CA_OLD, CA_NEW, CA_OMIT, CA_ERR};
|
|
|
|
static enum cvtaction convertAMD(enum cvtaction ca, int_32 type,
|
|
void ** nvalsp, int_32 *ncountp, cmd_t *newc)
|
|
{
|
|
int i;
|
|
|
|
if (newc == NULL)
|
|
return ca;
|
|
if (!(nvalsp && ncountp))
|
|
return CA_ERR;
|
|
|
|
*nvalsp = NULL;
|
|
*ncountp = 0;
|
|
|
|
switch (ca) {
|
|
case CA_OLD:
|
|
case CA_OMIT:
|
|
case CA_ERR:
|
|
default:
|
|
break;
|
|
case CA_NEW:
|
|
switch (type) {
|
|
case RPM_INT32_TYPE:
|
|
{ int_32 *intp = xmalloc(newc->nvals * sizeof(*intp));
|
|
for (i = 0; i < newc->nvals; i++) {
|
|
long ival;
|
|
char *end;
|
|
end = NULL;
|
|
ival = strtol(newc->vals[i], &end, 0);
|
|
if (end && *end)
|
|
break;
|
|
if ((((unsigned long)ival) >> (8*sizeof(*intp))) != 0)
|
|
break;
|
|
intp[i] = ival;
|
|
}
|
|
if (i < newc->nvals) {
|
|
ca = CA_ERR;
|
|
free(intp);
|
|
break;
|
|
}
|
|
*nvalsp = intp;
|
|
*ncountp = newc->nvals;
|
|
} break;
|
|
case RPM_BIN_TYPE: /* icons & signatures */
|
|
case RPM_STRING_TYPE:
|
|
if (newc->nvals != 1) {
|
|
newc->done = 0;
|
|
ca = CA_ERR;
|
|
break;
|
|
}
|
|
*nvalsp = xstrdup(newc->vals[0]);
|
|
*ncountp = newc->nvals;
|
|
break;
|
|
case RPM_STRING_ARRAY_TYPE:
|
|
{ const char **av = xmalloc((newc->nvals+1) * sizeof(char *));
|
|
for (i = 0; i < newc->nvals; i++) {
|
|
av[i] = newc->vals[i];
|
|
}
|
|
av[newc->nvals] = NULL;
|
|
*nvalsp = av;
|
|
*ncountp = newc->nvals;
|
|
} break;
|
|
case RPM_NULL_TYPE:
|
|
case RPM_CHAR_TYPE:
|
|
case RPM_INT8_TYPE: /* arch & os */
|
|
case RPM_INT16_TYPE: /* file modes & rdevs */
|
|
case RPM_I18NSTRING_TYPE:
|
|
default: /* this conversion cannot be performed (yet) */
|
|
newc->done = 0;
|
|
ca = CA_ERR;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return ca;
|
|
}
|
|
|
|
static enum cvtaction convertExistingAMD(int_32 tag, int_32 type,
|
|
void ** valsp, int_32 *countp, void ** nvalsp, int_32 *ncountp,
|
|
cmd_t *cmds[], int ncmds)
|
|
{
|
|
cmd_t *newc = NULL;
|
|
enum cvtaction ca = CA_OLD;
|
|
int i;
|
|
|
|
if (!((tag >= RPMTAG_NAME && tag < RPMTAG_FIRSTFREE_TAG)
|
|
|| tag >= RPMTAG_EXTERNAL_TAG))
|
|
return ca;
|
|
|
|
for (i = 0; i < ncmds; i++) {
|
|
cmd_t *c;
|
|
c = cmds[i];
|
|
|
|
if (tag != c->tagval)
|
|
continue;
|
|
if (c->done)
|
|
continue;
|
|
|
|
switch (c->injmode) {
|
|
case INJ_ADD:
|
|
if (ca != CA_OMIT) {/* old tag was deleted, now adding again */
|
|
c->done = -1;
|
|
continue;
|
|
}
|
|
ca = CA_NEW;
|
|
newc = c;
|
|
c->done = 1;
|
|
break;
|
|
case INJ_MODIFY: /* XXX for now, this is delete, then add */
|
|
if (ca == CA_OMIT) {/* old tag was deleted, can't modify */
|
|
c->done = -1;
|
|
continue;
|
|
}
|
|
ca = CA_NEW;
|
|
newc = c;
|
|
c->done = 1;
|
|
break;
|
|
case INJ_DELETE:
|
|
if (ca == CA_OMIT) {/* old tag was deleted, now deleting again */
|
|
c->done = -1;
|
|
continue;
|
|
}
|
|
ca = CA_OMIT;
|
|
newc = c;
|
|
c->done = 1;
|
|
break;
|
|
case INJ_UNKNOWN:
|
|
default:
|
|
c->done = -1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (newc) {
|
|
ca = convertAMD(ca, type, nvalsp, ncountp, newc);
|
|
switch (ca) {
|
|
case CA_OMIT:
|
|
case CA_NEW:
|
|
newc->oldcnt = *countp;
|
|
break;
|
|
case CA_OLD:
|
|
case CA_ERR:
|
|
break;
|
|
}
|
|
}
|
|
return ca;
|
|
}
|
|
|
|
static
|
|
Header headerCopyWithConvert(Header h, cmd_t *cmds[], int ncmds)
|
|
{
|
|
int_32 tag, type, count;
|
|
void *vals;
|
|
HeaderIterator headerIter;
|
|
Header res = headerNew();
|
|
|
|
headerIter = headerInitIterator(h);
|
|
|
|
while (headerNextIterator(headerIter, &tag, &type, &vals, &count)) {
|
|
enum cvtaction ca;
|
|
void *nvals;
|
|
int_32 ncount;
|
|
|
|
nvals = NULL;
|
|
ncount = 0;
|
|
ca = convertExistingAMD(tag, type, &vals, &count, &nvals, &ncount, cmds, ncmds);
|
|
switch (ca) {
|
|
case CA_ERR:
|
|
case CA_OLD: /* copy old tag and values to header */
|
|
default:
|
|
/* Don't copy the old changelog, we'll do that later. */
|
|
switch (tag) {
|
|
case RPMTAG_CHANGELOGTIME:
|
|
case RPMTAG_CHANGELOGNAME:
|
|
case RPMTAG_CHANGELOGTEXT:
|
|
break;
|
|
default:
|
|
headerAddEntry(res, tag, type, vals, count);
|
|
break;
|
|
}
|
|
break;
|
|
case CA_NEW: /* copy new tag and values to header */
|
|
headerAddEntry(res, tag, type, nvals, ncount);
|
|
break;
|
|
case CA_OMIT: /* delete old tag and values from header */
|
|
break;
|
|
}
|
|
|
|
if (type == RPM_STRING_ARRAY_TYPE || type == RPM_I18NSTRING_TYPE)
|
|
free(vals);
|
|
if (nvals)
|
|
free(nvals);
|
|
}
|
|
|
|
headerFreeIterator(headerIter);
|
|
|
|
return res;
|
|
}
|
|
|
|
static char * genChangelog(cmd_t *cmds[], int ncmds)
|
|
{
|
|
#define MYBUFSIZ (2*BUFSIZ)
|
|
char *b, *buf = xmalloc(MYBUFSIZ);
|
|
int i;
|
|
|
|
b = buf;
|
|
for (i = 0; i < ncmds; i++) {
|
|
cmd_t *c;
|
|
|
|
if ((c = cmds[i]) == NULL)
|
|
continue;
|
|
|
|
b += sprintf(b, "- %s tag %s(%d)",
|
|
pr_injmode(c->injmode), c->tag, c->tagval);
|
|
|
|
if (c->oldcnt || c->nvals) {
|
|
*b++ = '\t';
|
|
*b++ = '(';
|
|
if (c->oldcnt)
|
|
b += sprintf(b, "oldcnt %d", c->oldcnt);
|
|
if (c->oldcnt && c->nvals) {
|
|
*b++ = ',';
|
|
*b++ = ' ';
|
|
}
|
|
if (c->nvals)
|
|
b += sprintf(b, "nvals %d", c->nvals);
|
|
*b++ = ')';
|
|
}
|
|
*b++ = '\n';
|
|
}
|
|
*b = '\0';
|
|
|
|
return buf;
|
|
}
|
|
|
|
static int
|
|
headerInject(Header *hdrp, cmd_t *cmds[], int ncmds)
|
|
{
|
|
Header h;
|
|
int ec = 0;
|
|
int i;
|
|
|
|
if (!(hdrp && cmds && ncmds > 0))
|
|
return -1;
|
|
|
|
h = headerCopyWithConvert(*hdrp, cmds, ncmds);
|
|
for (i = 0; i < ncmds; i++) {
|
|
cmd_t *c;
|
|
int rc;
|
|
|
|
if ((c = cmds[i]) == NULL)
|
|
continue;
|
|
|
|
rc = headerIsEntry(h, c->tagval);
|
|
if (!rc && !c->done && c->injmode != INJ_DELETE) {
|
|
int_32 type, ncount;
|
|
void *nvals;
|
|
enum cvtaction ca;
|
|
|
|
type = (c->nvals > 0) ? RPM_STRING_ARRAY_TYPE : RPM_STRING_TYPE;
|
|
ca = convertAMD(CA_NEW, type, &nvals, &ncount, c);
|
|
if (ca == CA_NEW)
|
|
headerAddEntry(h, c->tagval, type, nvals, ncount);
|
|
rc = headerIsEntry(h, c->tagval);
|
|
}
|
|
|
|
switch(c->injmode) {
|
|
case INJ_ADD:
|
|
if (!(rc && c->done > 0)) {
|
|
warnx(_("failed to add tag %s"), getTagString(c->tagval));
|
|
ec = 1;
|
|
}
|
|
break;
|
|
case INJ_DELETE:
|
|
if (!(!rc && c->done > 0)) {
|
|
warnx(_("failed to delete tag %s"), getTagString(c->tagval));
|
|
ec = 1;
|
|
}
|
|
break;
|
|
case INJ_MODIFY:
|
|
if (!(rc && c->done > 0)) {
|
|
warnx(_("failed to modify tag %s"), getTagString(c->tagval));
|
|
ec = 1;
|
|
}
|
|
break;
|
|
case INJ_UNKNOWN:
|
|
default:
|
|
ec = 1;
|
|
break;
|
|
}
|
|
|
|
/* XXX possibly need strict mode to exit immediately here */
|
|
}
|
|
|
|
if (ec == 0 && *hdrp) {
|
|
static char name[512] = "";
|
|
static const char *text = NULL;
|
|
static int cltags[] = {
|
|
RPMTAG_CHANGELOGTIME,
|
|
RPMTAG_CHANGELOGNAME,
|
|
RPMTAG_CHANGELOGTEXT,
|
|
0
|
|
};
|
|
|
|
if (name[0] == '\0')
|
|
sprintf(name, "rpminject <%s@%s>", getUname(getuid()), buildHost());
|
|
if (text == NULL)
|
|
text = genChangelog(cmds, ncmds);
|
|
|
|
addChangelogEntry(h, *getBuildTime(), name, text);
|
|
headerCopyTags(*hdrp, h, cltags);
|
|
headerSort(h);
|
|
*hdrp = headerFree(*hdrp);
|
|
*hdrp = h;
|
|
} else {
|
|
h = headerFree(h);
|
|
}
|
|
|
|
return ec;
|
|
}
|
|
|
|
/* ========================================================================= */
|
|
|
|
static int
|
|
rewriteRPM(const char *fni, const char *fno, cmd_t *cmds[], int ncmds)
|
|
{
|
|
struct rpmlead lead; /* XXX FIXME: exorcize lead/arch/os */
|
|
Header sigs;
|
|
Spec spec;
|
|
CSA_t csabuf, *csa = &csabuf;
|
|
int rc;
|
|
|
|
csa->cpioArchiveSize = 0;
|
|
csa->cpioFdIn = fdNew("init (rewriteRPM)");
|
|
csa->cpioList = NULL;
|
|
csa->cpioCount = 0;
|
|
csa->lead = &lead; /* XXX FIXME: exorcize lead/arch/os */
|
|
|
|
/* Read rpm and (partially) recreate spec/pkg control structures */
|
|
if ((rc = readRPM(fni, &spec, &lead, &sigs, csa)) != 0)
|
|
return rc;
|
|
|
|
/* Inject new strings into header tags */
|
|
if ((rc = headerInject(&spec->packages->header, cmds, ncmds)) != 0)
|
|
goto exit;
|
|
|
|
/* Rewrite the rpm */
|
|
if (lead.type == RPMLEAD_SOURCE) {
|
|
rc = writeRPM(&spec->packages->header, NULL, fno, (int)lead.type,
|
|
csa, spec->passPhrase, &(spec->cookie));
|
|
} else {
|
|
rc = writeRPM(&spec->packages->header, NULL, fno, (int)lead.type,
|
|
csa, spec->passPhrase, NULL);
|
|
}
|
|
|
|
exit:
|
|
Fclose(csa->cpioFdIn);
|
|
return rc;
|
|
|
|
}
|
|
|
|
/* ========================================================================= */
|
|
|
|
static int
|
|
do_inject(cmd_t *cmds[], int ncmds, const char *argv[])
|
|
{
|
|
const char *arg;
|
|
int ec = 0;
|
|
|
|
if (argv == NULL || *argv == NULL) {
|
|
/* XXX generate lead/header to stdout */
|
|
return 0;
|
|
}
|
|
|
|
while ((arg = *argv++) != NULL) {
|
|
char *fni = xmalloc(strlen(arg) + sizeof("-SAVE"));
|
|
const char *fno = arg;
|
|
|
|
strcpy(fni, arg);
|
|
strcat(fni, "-SAVE");
|
|
unlink(fni);
|
|
if (link(fno, fni)) {
|
|
warn(_("can't link temp input file %s"), fni);
|
|
ec++;
|
|
continue;
|
|
}
|
|
if (rewriteRPM(fni, fno, cmds, ncmds)) {
|
|
unlink(fno);
|
|
if (rename(fni, fno))
|
|
warn(_("can't rename %s to %s"), fni, fno);
|
|
ec++;
|
|
}
|
|
if (fni) free(fni);
|
|
}
|
|
|
|
return ec;
|
|
}
|
|
|
|
static struct poptOption optionsTable[] = {
|
|
{ "add", 'a', 0, 0, 'a', NULL, NULL },
|
|
{ "del", 'd', 0, 0, 'd', NULL, NULL },
|
|
{ "injtags", 'i', 0, 0, 'i', NULL, NULL },
|
|
{ "modify", 'm', 0, 0, 'm', NULL, NULL },
|
|
{ "tag", 't', POPT_ARG_STRING, 0, 't', NULL, NULL },
|
|
{ "value", 'v', POPT_ARG_STRING, 0, 'v', NULL, NULL },
|
|
{ NULL, 0, 0, 0, 0, NULL, NULL }
|
|
};
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
poptContext optCon;
|
|
char * optArg;
|
|
cmd_t *c = NULL;
|
|
int arg;
|
|
int ec = 0;
|
|
injmode_t lastmode = INJ_UNKNOWN;
|
|
|
|
#if HAVE_MCHECK_H && HAVE_MTRACE
|
|
mtrace(); /* Trace malloc only if MALLOC_TRACE=mtrace-output-file. */
|
|
#endif
|
|
|
|
setprogname(argv[0]); /* Retrofit glibc __progname */
|
|
(void)setlocale(LC_ALL, "" );
|
|
|
|
#ifdef __LCLINT__
|
|
#define LOCALEDIR "/usr/share/locale"
|
|
#endif
|
|
(void)bindtextdomain(PACKAGE, LOCALEDIR);
|
|
(void)textdomain(PACKAGE);
|
|
|
|
optCon = poptGetContext("rpminject", argc, argv, optionsTable, 0);
|
|
poptReadDefaultConfig(optCon, 1);
|
|
|
|
while ((arg = poptGetNextOpt(optCon)) > 0) {
|
|
optArg = poptGetOptArg(optCon);
|
|
switch (arg) {
|
|
case 'a':
|
|
injmode = INJ_ADD;
|
|
break;
|
|
case 'd':
|
|
injmode = INJ_DELETE;
|
|
break;
|
|
case 'm':
|
|
injmode = INJ_MODIFY;
|
|
break;
|
|
case 't':
|
|
if (ncmds == 0 || c == NULL)
|
|
errx(EXIT_FAILURE, _("missing inject mode before \"--tag %s\""), optArg);
|
|
if (c->tag) {
|
|
if (c->injmode != INJ_DELETE &&
|
|
(c->nvals <= 0 || c->vals == NULL))
|
|
errx(EXIT_FAILURE, _("add/modify inject mode with \"--tag %s\" needs a value"), c->tag);
|
|
cmds[ncmds] = c = xcalloc(1, sizeof(cmd_t));
|
|
cmds[ncmds]->injmode = cmds[ncmds-1]->injmode;
|
|
ncmds++;
|
|
}
|
|
c->tagval = getTagVal(optArg);
|
|
if (!((c->tagval >= RPMTAG_NAME && c->tagval < RPMTAG_FIRSTFREE_TAG)
|
|
|| c->tagval >= RPMTAG_EXTERNAL_TAG))
|
|
errx(EXIT_FAILURE, _("unknown rpm tag \"--tag %s\""), optArg);
|
|
c->tag = xstrdup(optArg);
|
|
break;
|
|
case 'v':
|
|
if (ncmds == 0 || c == NULL)
|
|
errx(EXIT_FAILURE, _("missing inject mode before \"--value %s\""), optArg);
|
|
if (c->tag == NULL)
|
|
errx(EXIT_FAILURE, _("missing tag name before \"--value %s\""), optArg);
|
|
if (c->nvals == 0 || c->vals == NULL) {
|
|
c->vals = xcalloc(2, sizeof(char *));
|
|
} else {
|
|
c->vals = xrealloc(c->vals,
|
|
(c->nvals+2)*sizeof(char *));
|
|
}
|
|
c->vals[c->nvals++] = xstrdup(optArg);
|
|
c->vals[c->nvals] = NULL;
|
|
break;
|
|
case 'i':
|
|
rpmDisplayQueryTags(stdout);
|
|
exit(EXIT_SUCCESS);
|
|
break;
|
|
default:
|
|
errx(EXIT_FAILURE, _("unknown popt return (%d)"), arg);
|
|
/*@notreached@*/ break;
|
|
}
|
|
|
|
if (injmode != lastmode) {
|
|
cmds[ncmds] = c = xcalloc(1, sizeof(cmd_t));
|
|
cmds[ncmds]->injmode = lastmode = injmode;
|
|
ncmds++;
|
|
}
|
|
}
|
|
|
|
/* XXX I don't want to read rpmrc */
|
|
addMacro(NULL, "_tmppath", NULL, "/tmp", RMIL_DEFAULT);
|
|
|
|
ec = do_inject(cmds, ncmds, poptGetArgs(optCon));
|
|
|
|
optCon = poptFreeContext(optCon);
|
|
return ec;
|
|
}
|