/** * The main macro recursion loop. * @todo Dynamically reallocate target buffer. * @param mb macro expansion state * @return 0 on success, 1 on failure */ static int expandMacro(MacroBuf mb) { rpmMacroEntry *mep; rpmMacroEntry me; const char *s = mb->s, *se; const char *f, *fe; const char *g, *ge; size_t fn, gn; char *t = mb->t; /* save expansion pointer for printExpand */ int c; int rc = 0; int negate; const char * lastc; int chkexist; if (++mb->depth > max_macro_depth) { rpmlog(RPMLOG_ERR, _("Recursion depth(%d) greater than max(%d)\n"), mb->depth, max_macro_depth); mb->depth--; mb->expand_trace = 1; return 1; } while (rc == 0 && mb->nb > 0 && (c = *s) != '\0') { s++; /* Copy text until next macro */ switch(c) { case '%': if (*s) { /* Ensure not end-of-string. */ if (*s != '%') break; s++; /* skip first % in %% */ } default: SAVECHAR(mb, c); continue; break; } /* Expand next macro */ f = fe = NULL; g = ge = NULL; if (mb->depth > 1) /* XXX full expansion for outermost level */ t = mb->t; /* save expansion pointer for printExpand */ negate = 0; lastc = NULL; chkexist = 0; switch ((c = *s)) { default: /* %name substitution */ while (strchr("!?", *s) != NULL) { switch(*s++) { case '!': negate = ((negate + 1) % 2); break; case '?': chkexist++; break; } } f = se = s; if (*se == '-') se++; while((c = *se) && (risalnum(c) || c == '_')) se++; /* Recognize non-alnum macros too */ switch (*se) { case '*': se++; if (*se == '*') se++; break; case '#': se++; break; default: break; } fe = se; /* For "%name " macros ... */ if ((c = *fe) && isblank(c)) if ((lastc = strchr(fe,'\n')) == NULL) lastc = strchr(fe, '\0'); break; case '(': /* %(...) shell escape */ if ((se = matchchar(s, c, ')')) == NULL) { rpmlog(RPMLOG_ERR, _("Unterminated %c: %s\n"), (char)c, s); rc = 1; continue; } if (mb->macro_trace) printMacro(mb, s, se+1); s++; /* skip ( */ rc = doShellEscape(mb, s, (se - s)); se++; /* skip ) */ s = se; continue; break; case '{': /* %{...}/%{...:...} substitution */ if ((se = matchchar(s, c, '}')) == NULL) { rpmlog(RPMLOG_ERR, _("Unterminated %c: %s\n"), (char)c, s); rc = 1; continue; } f = s+1;/* skip { */ se++; /* skip } */ while (strchr("!?", *f) != NULL) { switch(*f++) { case '!': negate = ((negate + 1) % 2); break; case '?': chkexist++; break; } } for (fe = f; (c = *fe) && !strchr(" :}", c);) fe++; switch (c) { case ':': g = fe + 1; ge = se - 1; break; case ' ': lastc = se-1; break; default: break; } break; } /* XXX Everything below expects fe > f */ fn = (fe - f); gn = (ge - g); if ((fe - f) <= 0) { /* XXX Process % in unknown context */ c = '%'; /* XXX only need to save % */ SAVECHAR(mb, c); #if 0 rpmlog(RPMLOG_ERR, _("A %% is followed by an unparseable macro\n")); #endif s = se; continue; } if (mb->macro_trace) printMacro(mb, s, se); /* Expand builtin macros */ if (STREQ("global", f, fn)) { s = doDefine(mb, se, RMIL_GLOBAL, 1); continue; } if (STREQ("define", f, fn)) { s = doDefine(mb, se, mb->depth, 0); continue; } if (STREQ("undefine", f, fn)) { s = doUndefine(mb->mc, se); continue; } if (STREQ("echo", f, fn) || STREQ("warn", f, fn) || STREQ("error", f, fn)) { int waserror = 0; if (STREQ("error", f, fn)) waserror = 1; if (g != NULL && g < ge) doOutput(mb, waserror, g, gn); else doOutput(mb, waserror, f, fn); s = se; continue; } if (STREQ("trace", f, fn)) { /* XXX TODO restore expand_trace/macro_trace to 0 on return */ mb->expand_trace = mb->macro_trace = (negate ? 0 : mb->depth); if (mb->depth == 1) { print_macro_trace = mb->macro_trace; print_expand_trace = mb->expand_trace; } s = se; continue; } if (STREQ("dump", f, fn)) { rpmDumpMacroTable(mb->mc, NULL); while (iseol(*se)) se++; s = se; continue; } #ifdef WITH_LUA if (STREQ("lua", f, fn)) { rpmlua lua = NULL; /* Global state. */ const char *ls = s+sizeof("{lua:")-1; const char *lse = se-sizeof("}")+1; char *scriptbuf = (char *)xmalloc((lse-ls)+1); const char *printbuf; memcpy(scriptbuf, ls, lse-ls); scriptbuf[lse-ls] = '\0'; rpmluaSetPrintBuffer(lua, 1); if (rpmluaRunScript(lua, scriptbuf, NULL) == -1) rc = 1; printbuf = rpmluaGetPrintBuffer(lua); if (printbuf) { size_t len = strlen(printbuf); if (len > mb->nb) len = mb->nb; memcpy(mb->t, printbuf, len); mb->t += len; mb->nb -= len; } rpmluaSetPrintBuffer(lua, 0); free(scriptbuf); s = se; continue; } #endif /* XXX necessary but clunky */ if (STREQ("basename", f, fn) || STREQ("suffix", f, fn) || STREQ("expand", f, fn) || STREQ("verbose", f, fn) || STREQ("uncompress", f, fn) || STREQ("url2path", f, fn) || STREQ("u2p", f, fn) || STREQ("getenv", f, fn) || STREQ("S", f, fn) || STREQ("P", f, fn) || STREQ("F", f, fn)) { /* FIX: verbose may be set */ doFoo(mb, negate, f, fn, g, gn); s = se; continue; } /* Expand defined macros */ mep = findEntry(mb->mc, f, fn); me = (mep ? *mep : NULL); /* XXX Special processing for flags */ if (*f == '-') { if (me) me->used++; /* Mark macro as used */ if ((me == NULL && !negate) || /* Without -f, skip %{-f...} */ (me != NULL && negate)) { /* With -f, skip %{!-f...} */ s = se; continue; } if (g && g < ge) { /* Expand X in %{-f:X} */ rc = expandT(mb, g, gn); } else if (me && me->body && *me->body) {/* Expand %{-f}/%{-f*} */ rc = expandT(mb, me->body, strlen(me->body)); } s = se; continue; } /* XXX Special processing for macro existence */ if (chkexist) { if ((me == NULL && !negate) || /* Without -f, skip %{?f...} */ (me != NULL && negate)) { /* With -f, skip %{!?f...} */ s = se; continue; } if (g && g < ge) { /* Expand X in %{?f:X} */ rc = expandT(mb, g, gn); } else if (me && me->body && *me->body) { /* Expand %{?f}/%{?f*} */ rc = expandT(mb, me->body, strlen(me->body)); } s = se; continue; } if (me == NULL) { /* leave unknown %... as is */ #ifndef HACK #if DEAD /* XXX hack to skip over empty arg list */ if (fn == 1 && *f == '*') { s = se; continue; } #endif /* XXX hack to permit non-overloaded %foo to be passed */ c = '%'; /* XXX only need to save % */ SAVECHAR(mb, c); #else rpmlog(RPMLOG_ERR, _("Macro %%%.*s not found, skipping\n"), fn, f); s = se; #endif continue; } /* Setup args for "%name " macros with opts */ if (me && me->opts != NULL) { if (lastc != NULL) { se = grabArgs(mb, me, fe, lastc); } else { addMacro(mb->mc, "**", NULL, "", mb->depth); addMacro(mb->mc, "*", NULL, "", mb->depth); addMacro(mb->mc, "#", NULL, "0", mb->depth); addMacro(mb->mc, "0", NULL, me->name, mb->depth); } } /* Recursively expand body of macro */ if (me->body && *me->body) { mb->s = me->body; rc = expandMacro(mb); if (rc == 0) me->used++; /* Mark macro as used */ } /* Free args for "%name " macros with opts */ if (me->opts != NULL) freeArgs(mb); s = se; } *mb->t = '\0'; mb->s = s; mb->depth--; if (rc != 0 || mb->expand_trace) printExpansion(mb, t, mb->t); return rc; }
static void integrity_check(const char *progname, enum modes progmode_num) { rpmts ts = NULL; rpmlua lua = NULL; char *spec_fn = NULL; char *proc_fn = NULL; char *pkey_fn = NULL; char *spec = NULL; char *proc = NULL; rpmiob spec_iob = NULL; rpmiob proc_iob = NULL; const char *result = NULL; const char *error = NULL; int xx; const char *progmode; int rc = INTEGRITY_ERROR; /* determine paths of integrity checking related files */ spec_fn = rpmExpand("%{?_integrity_spec_cfg}%{!?_integrity_spec_cfg:scripts/integrity.cfg}", NULL); if (spec_fn == NULL || spec_fn[0] == '\0') { integrity_check_message("ERROR: Integrity Configuration Specification file not configured.\n" "rpm: HINT: macro %%{_integrity_spec_cfg} not configured correctly.\n"); goto failure; } proc_fn = rpmExpand("%{?_integrity_proc_lua}%{!?_integrity_proc_lua:scripts/integrity.lua}", NULL); if (proc_fn == NULL || proc_fn[0] == '\0') { integrity_check_message("ERROR: Integrity Validation Processor file not configured.\n" "rpm: HINT: macro %%{_integrity_proc_lua} not configured correctly.\n"); goto failure; } pkey_fn = rpmExpand("%{?_integrity_pkey_pgp}%{!?_integrity_pkey_pgp:scripts/integrity.pgp}", NULL); if (pkey_fn == NULL || pkey_fn[0] == '\0') { integrity_check_message("ERROR: Integrity Autority Public-Key file not configured.\n" "rpm: HINT: macro %%{_integrity_pkey_pgp} not configured correctly.\n"); goto failure; } /* create RPM transaction environment and open RPM database */ ts = rpmtsCreate(); (void)rpmtsOpenDB(ts, O_RDONLY); /* check signature on integrity configuration specification file */ if (rpmnsProbeSignature(ts, spec_fn, NULL, pkey_fn, RPM_INTEGRITY_FP, 0) != RPMRC_OK) { integrity_check_message("ERROR: Integrity Configuration Specification file contains invalid signature.\n" "rpm: HINT: Check file \"%s\".\n", spec_fn); goto failure; } /* check signature on integrity validation processor file */ if (rpmnsProbeSignature(ts, proc_fn, NULL, pkey_fn, RPM_INTEGRITY_FP, 0) != RPMRC_OK) { integrity_check_message("ERROR: Integrity Validation Processor file contains invalid signature.\n" "rpm: HINT: Check file \"%s\".\n", proc_fn); goto failure; } /* load integrity configuration specification file */ xx = rpmiobSlurp(spec_fn, &spec_iob); if (!(xx == 0 && spec_iob != NULL)) { integrity_check_message("ERROR: Unable to load Integrity Configuration Specification file.\n" "rpm: HINT: Check file \"%s\".\n", spec_fn); goto failure; } spec = rpmiobStr(spec_iob); /* load integrity validation processor file */ xx = rpmiobSlurp(proc_fn, &proc_iob); if (!(xx == 0 && proc_iob != NULL)) { integrity_check_message("ERROR: Unable to load Integrity Validation Processor file.\n" "rpm: HINT: Check file \"%s\".\n", proc_fn); goto failure; } proc = rpmiobStr(proc_iob); /* provision program name and mode */ if (progname == NULL || progname[0] == '\0') progname = "rpm"; switch (progmode_num) { case MODE_QUERY: progmode = "query"; break; case MODE_VERIFY: progmode = "verify"; break; case MODE_CHECKSIG: progmode = "checksig"; break; case MODE_RESIGN: progmode = "resign"; break; case MODE_INSTALL: progmode = "install"; break; case MODE_ERASE: progmode = "erase"; break; case MODE_BUILD: progmode = "build"; break; case MODE_REBUILD: progmode = "rebuild"; break; case MODE_RECOMPILE: progmode = "recompile"; break; case MODE_TARBUILD: progmode = "tarbuild"; break; case MODE_REBUILDDB: progmode = "rebuilddb"; break; case MODE_UNKNOWN: progmode = "unknown"; break; default: progmode = "unknown"; break; } /* execute Integrity Validation Processor via Lua glue code */ lua = rpmluaNew(); rpmluaSetPrintBuffer(lua, 1); rpmluaextActivate(lua); lua_getfield(lua->L, LUA_GLOBALSINDEX, "integrity"); lua_getfield(lua->L, -1, "processor"); lua_remove(lua->L, -2); lua_pushstring(lua->L, progname); lua_pushstring(lua->L, progmode); lua_pushstring(lua->L, spec_fn); lua_pushstring(lua->L, spec); lua_pushstring(lua->L, proc_fn); lua_pushstring(lua->L, proc); #ifdef RPM_INTEGRITY_MV lua_pushstring(lua->L, RPM_INTEGRITY_MV); #else lua_pushstring(lua->L, "0"); #endif if (lua_pcall(lua->L, 7, 1, 0) != 0) { error = lua_isstring(lua->L, -1) ? lua_tostring(lua->L, -1) : "unknown error"; lua_pop(lua->L, 1); integrity_check_message("ERROR: Failed to execute Integrity Validation Processor.\n" "rpm: ERROR: Lua: %s.\n" "rpm: HINT: Check file \"%s\".\n", error, proc_fn); goto failure; } /* check Integrity Validation Processor results */ if (!lua_isstring(lua->L, -1)) { integrity_check_message("ERROR: Failed to fetch Integrity Validation Processor results.\n" "rpm: HINT: Check file \"%s\".\n", proc_fn); goto failure; } result = lua_tostring(lua->L, -1); if (strcmp(result, "OK") == 0) rc = INTEGRITY_OK; else if (strncmp(result, "WARNING:", 8) == 0) { rc = INTEGRITY_WARNING; integrity_check_message("%s\n", result); } else { rc = INTEGRITY_ERROR; integrity_check_message("%s\n", result); } /* cleanup processing */ failure: if (lua != NULL) rpmluaFree(lua); if (ts != NULL) (void)rpmtsFree(ts); ts = NULL; if (spec_iob != NULL) spec_iob = rpmiobFree(spec_iob); if (proc_iob != NULL) proc_iob = rpmiobFree(proc_iob); /* final result handling */ if (rc != INTEGRITY_OK) { if (isatty(STDIN_FILENO) || isatty(STDOUT_FILENO)) sleep(4); if (rc == INTEGRITY_ERROR) exit(42); } return; }