5 #include <sys/select.h>
11 #include <rpm/header.h>
13 #include <rpm/rpmfc.h>
14 #include <rpm/rpmlog.h>
15 #include <rpm/rpmfileutil.h>
16 #include <rpm/rpmds.h>
17 #include <rpm/rpmfi.h>
18 #include <rpm/rpmstrpool.h>
20 #include "lib/rpmfi_internal.h" /* rpmfiles stuff for now */
21 #include "build/rpmbuild_internal.h"
31 typedef struct rpmfcAttr_s {
33 struct matchRule incl;
34 struct matchRule excl;
52 int nfiles; /*!< no. of files */
53 int fknown; /*!< no. of classified files */
54 int fwhite; /*!< no. of "white" files */
55 int skipProv; /*!< Don't auto-generate Provides:? */
56 int skipReq; /*!< Don't auto-generate Requires:? */
57 char *buildRoot; /*!< (Build) root dir */
58 size_t brlen; /*!< rootDir length */
60 rpmfcAttr *atypes; /*!< known file attribute types */
62 char ** fn; /*!< (no. files) file names */
63 ARGV_t *fattrs; /*!< (no. files) file attribute tokens */
64 rpm_color_t *fcolor;/*!< (no. files) file colors */
65 rpmsid *fcdictx; /*!< (no. files) file class dictionary indices */
66 ARGI_t fddictx; /*!< (no. files) file depends dictionary start */
67 ARGI_t fddictn; /*!< (no. files) file depends dictionary no. entries */
68 ARGI_t ddictx; /*!< (no. dependencies) file->dependency mapping */
69 rpmstrPool cdict; /*!< file class dictionary */
70 rpmfcFileDeps fileDeps; /*!< file dependency mapping */
72 rpmstrPool pool; /*!< general purpose string storage */
75 struct rpmfcTokens_s {
80 static int regMatch(regex_t *reg, const char *val)
82 return (reg && regexec(reg, val, 0, NULL, 0) == 0);
85 static regex_t * regFree(regex_t *reg)
94 static void ruleFree(struct matchRule *rule)
98 argvFree(rule->flags);
101 static char *rpmfcAttrMacroV(const char *arg, va_list args)
105 char *buf = NULL, *obuf;
109 if (arg == NULL || rstreq(arg, ""))
112 va_copy(args2, args);
113 blen = sizeof("%{?_") - 1;
114 for (s = arg; s != NULL; s = va_arg(args, const char *)) {
115 blen += sizeof("_") - 1 + strlen(s);
117 blen += sizeof("}") - 1;
119 buf = xmalloc(blen + 1);
122 pe = stpcpy(pe, "%{?_");
123 for (s = arg; s != NULL; s = va_arg(args2, const char *)) {
131 obuf = rpmExpand(buf, NULL);
134 return rstreq(obuf, "") ? _free(obuf) : obuf;
137 static char *rpmfcAttrMacro(const char *arg, ...)
143 s = rpmfcAttrMacroV(arg, args);
148 static regex_t *rpmfcAttrReg(const char *arg, ...)
155 pattern = rpmfcAttrMacroV(arg, args);
158 reg = xcalloc(1, sizeof(*reg));
159 if (regcomp(reg, pattern, REG_EXTENDED) != 0) {
160 rpmlog(RPMLOG_WARNING, _("Ignoring invalid regex %s\n"), pattern);
168 static rpmfcAttr rpmfcAttrNew(const char *name)
170 rpmfcAttr attr = xcalloc(1, sizeof(*attr));
171 struct matchRule *rules[] = { &attr->incl, &attr->excl, NULL };
173 attr->name = xstrdup(name);
174 for (struct matchRule **rule = rules; rule && *rule; rule++) {
175 const char *prefix = (*rule == &attr->incl) ? NULL : "exclude";
179 flags = rpmfcAttrMacro(name, prefix, "flags", NULL);
181 (*rule)->path = rpmfcAttrReg(name, prefix, "path", NULL);
182 (*rule)->magic = rpmfcAttrReg(name, prefix, "magic", NULL);
184 flags = rpmfcAttrMacro(name, "flags", NULL);
186 (*rule)->path = rpmfcAttrReg(name, "path", NULL);
187 (*rule)->magic = rpmfcAttrReg(name, "magic", NULL);
189 (*rule)->flags = argvSplitString(flags, ",", ARGV_SKIPEMPTY);
190 argvSort((*rule)->flags, NULL);
198 static rpmfcAttr rpmfcAttrFree(rpmfcAttr attr)
201 ruleFree(&attr->incl);
202 ruleFree(&attr->excl);
211 static int rpmfcExpandAppend(ARGV_t * argvp, ARGV_const_t av)
213 ARGV_t argv = *argvp;
214 int argc = argvCount(argv);
215 int ac = argvCount(av);
218 argv = xrealloc(argv, (argc + ac + 1) * sizeof(*argv));
219 for (i = 0; i < ac; i++)
220 argv[argc + i] = rpmExpand(av[i], NULL);
221 argv[argc + ac] = NULL;
226 static rpmds rpmdsSingleNS(rpmstrPool pool,
227 rpmTagVal tagN, const char *namespace,
228 const char * N, const char * EVR, rpmsenseFlags Flags)
232 char *NSN = rpmExpand(namespace, "(", N, ")", NULL);
233 ds = rpmdsSinglePool(pool, tagN, NSN, EVR, Flags);
236 ds = rpmdsSinglePool(pool, tagN, N, EVR, Flags);
241 #define max(x,y) ((x) > (y) ? (x) : (y))
243 /** \ingroup rpmbuild
244 * Return output from helper script.
245 * @todo Use poll(2) rather than select(2), if available.
246 * @param argv program and arguments to run
247 * @param writePtr bytes to feed to script on stdin (or NULL)
248 * @param writeBytesLeft no. of bytes to feed to script on stdin
249 * @param failNonZero is script failure an error?
250 * @param buildRoot buildRoot directory (or NULL)
251 * @return buffered stdout from script, NULL on error
253 static StringBuf getOutputFrom(ARGV_t argv,
254 const char * writePtr, size_t writeBytesLeft,
255 int failNonZero, const char *buildRoot)
258 int toProg[2] = { -1, -1 };
259 int fromProg[2] = { -1, -1 };
263 int ret = 1; /* assume failure */
265 if (pipe(toProg) < 0 || pipe(fromProg) < 0) {
266 rpmlog(RPMLOG_ERR, _("Couldn't create pipe for %s: %m\n"), argv[0]);
275 dup2(toProg[0], STDIN_FILENO); /* Make stdin the in pipe */
278 dup2(fromProg[1], STDOUT_FILENO); /* Make stdout the out pipe */
281 rpmlog(RPMLOG_DEBUG, "\texecv(%s) pid %d\n",
282 argv[0], (unsigned)getpid());
285 setenv("RPM_BUILD_ROOT", buildRoot, 1);
287 unsetenv("MALLOC_CHECK_");
288 execvp(argv[0], (char *const *)argv);
289 rpmlog(RPMLOG_ERR, _("Couldn't exec %s: %s\n"),
290 argv[0], strerror(errno));
294 rpmlog(RPMLOG_ERR, _("Couldn't fork %s: %s\n"),
295 argv[0], strerror(errno));
302 readBuff = newStringBuf();
313 FD_SET(fromProg[0], &ibits);
314 nfd = max(nfd, fromProg[0]);
316 if (writeBytesLeft > 0) {
317 FD_SET(toProg[1], &obits);
318 nfd = max(nfd, toProg[1]);
319 } else if (toProg[1] >= 0) {
320 /* Close write-side pipe to notify child on EOF */
326 iorc = select(nfd + 1, &ibits, &obits, NULL, NULL);
327 } while (iorc == -1 && errno == EINTR);
334 /* Write data to child */
335 if (writeBytesLeft > 0 && FD_ISSET(toProg[1], &obits)) {
336 size_t nb = (1024 < writeBytesLeft) ? 1024 : writeBytesLeft;
338 iorc = write(toProg[1], writePtr, nb);
339 } while (iorc == -1 && errno == EINTR);
345 writeBytesLeft -= iorc;
349 /* Read when we get data back from the child */
350 if (FD_ISSET(fromProg[0], &ibits)) {
352 iorc = read(fromProg[0], buf, sizeof(buf)-1);
353 } while (iorc == -1 && errno == EINTR);
355 if (iorc == 0) break; /* EOF, we're done */
361 appendStringBuf(readBuff, buf);
368 if (fromProg[0] >= 0)
371 /* Collect status from prog */
372 reaped = waitpid(child, &status, 0);
373 rpmlog(RPMLOG_DEBUG, "\twaitpid(%d) rc %d status %x\n",
374 (unsigned)child, (unsigned)reaped, status);
376 if (failNonZero && (!WIFEXITED(status) || WEXITSTATUS(status))) {
377 rpmlog(RPMLOG_ERR, _("%s failed: %x\n"), argv[0], status);
380 if (writeBytesLeft || myerrno) {
381 rpmlog(RPMLOG_ERR, _("failed to write all data to %s: %s\n"),
382 argv[0], strerror(myerrno));
389 readBuff = freeStringBuf(readBuff);
395 int rpmfcExec(ARGV_const_t av, StringBuf sb_stdin, StringBuf * sb_stdoutp,
396 int failnonzero, const char *buildRoot)
404 const char * buf_stdin = NULL;
405 size_t buf_stdin_len = 0;
412 /* Find path to executable with (possible) args. */
413 s = rpmExpand(av[0], NULL);
417 /* Parse args buried within expanded executable. */
418 if (!(poptParseArgvString(s, &pac, (const char ***)&pav) == 0 && pac > 0 && pav != NULL))
421 /* Build argv, appending args to the executable args. */
422 argvAppend(&xav, pav);
424 rpmfcExpandAppend(&xav, av + 1);
426 if (sb_stdin != NULL) {
427 buf_stdin = getStringBuf(sb_stdin);
428 buf_stdin_len = strlen(buf_stdin);
432 char *cmd = argvJoin(xav, " ");
433 rpmlog(RPMLOG_DEBUG, "Executing %s on %s\n", cmd, buf_stdin);
437 /* Read output from exec'd helper. */
438 sb = getOutputFrom(xav, buf_stdin, buf_stdin_len, failnonzero, buildRoot);
440 if (sb_stdoutp != NULL) {
442 sb = NULL; /* XXX don't free */
450 free(pav); /* XXX popt mallocs in single blob. */
455 static void argvAddUniq(ARGV_t * argvp, const char * key)
457 if (argvSearch(*argvp, key, NULL) == NULL) {
459 argvSort(*argvp, NULL);
463 #define hasAttr(_a, _n) (argvSearch((_a), (_n), NULL) != NULL)
465 static void rpmfcAddFileDep(rpmfcFileDeps *fileDeps, rpmds ds, int ix)
467 if (fileDeps->size == fileDeps->alloced) {
468 fileDeps->alloced <<= 2;
469 fileDeps->data = xrealloc(fileDeps->data,
470 fileDeps->alloced * sizeof(fileDeps->data[0]));
472 fileDeps->data[fileDeps->size].fileIx = ix;
473 fileDeps->data[fileDeps->size++].dep = ds;
476 static ARGV_t runCmd(const char *nsdep, const char *depname,
477 const char *buildRoot, const char *fn)
479 ARGV_t output = NULL;
481 char *mname = rstrscat(NULL, "__", nsdep, "_", depname, NULL);
483 rasprintf(&buf, "%%{?%s:%%{%s} %%{?%s_opts}}", mname, mname, mname);
484 if (!rstreq(buf, "")) {
486 StringBuf sb_stdout = NULL;
487 StringBuf sb_stdin = newStringBuf();
490 appendLineStringBuf(sb_stdin, fn);
491 if (rpmfcExec(av, sb_stdin, &sb_stdout, 0, buildRoot) == 0) {
492 argvSplit(&output, getStringBuf(sb_stdout), "\n\r");
496 freeStringBuf(sb_stdin);
497 freeStringBuf(sb_stdout);
504 struct addReqProvDataFc {
506 const char *namespace;
510 static rpmRC addReqProvFc(void *cbdata, rpmTagVal tagN,
511 const char * N, const char * EVR, rpmsenseFlags Flags,
514 struct addReqProvDataFc *data = cbdata;
516 const char *namespace = data->namespace;
517 regex_t *exclude = data->exclude;
519 rpmds ds = rpmdsSingleNS(fc->pool, tagN, namespace, N, EVR, Flags);
520 /* Add to package and file dependencies unless filtered */
521 if (regMatch(exclude, rpmdsDNEVR(ds)+2) == 0)
522 rpmfcAddFileDep(&fc->fileDeps, ds, index);
528 * Run per-interpreter dependency helper.
529 * @param fc file classifier
530 * @param ix file index
531 * @param nsdep class name for interpreter (e.g. "perl")
532 * @param depname "provides" or "requires"
533 * @param dsContext RPMSENSE_FIND_PROVIDES or RPMSENSE_FIND_REQUIRES
534 * @param tagN RPMTAG_PROVIDENAME or RPMTAG_REQUIRENAME
535 * @return 0 on success
537 static int rpmfcHelper(rpmfc fc, int ix,
538 const char *nsdep, const char *depname,
539 rpmsenseFlags dsContext, rpmTagVal tagN)
542 const char * fn = fc->fn[ix];
543 char *namespace = NULL;
546 regex_t *exclude = NULL;
547 regex_t *exclude_from = NULL;
548 regex_t *global_exclude_from = NULL;
550 /* If the entire path is filtered out, there's nothing more to do */
551 exclude_from = rpmfcAttrReg(depname, "exclude", "from", NULL);
552 if (regMatch(exclude_from, fn+fc->brlen))
555 global_exclude_from = rpmfcAttrReg("global", depname, "exclude", "from", NULL);
556 if (regMatch(global_exclude_from, fn+fc->brlen))
559 pav = runCmd(nsdep, depname, fc->buildRoot, fn);
563 pac = argvCount(pav);
564 namespace = rpmfcAttrMacro(nsdep, "namespace", NULL);
565 exclude = rpmfcAttrReg(depname, "exclude", NULL);
567 struct addReqProvDataFc data;
569 data.namespace = namespace;
570 data.exclude = exclude;
572 for (int i = 0; i < pac; i++) {
573 if (parseRCPOT(NULL, fc->pkg, pav[i], tagN, ix, dsContext, addReqProvFc, &data))
582 regFree(exclude_from);
583 regFree(global_exclude_from);
587 /* Only used for elf coloring and controlling RPMTAG_FILECLASS inclusion now */
588 static const struct rpmfcTokens_s rpmfcTokens[] = {
589 { "directory", RPMFC_INCLUDE },
591 { "ELF 32-bit", RPMFC_ELF32|RPMFC_INCLUDE },
592 { "ELF 64-bit", RPMFC_ELF64|RPMFC_INCLUDE },
594 { "troff or preprocessor input", RPMFC_INCLUDE },
595 { "GNU Info", RPMFC_INCLUDE },
597 { "perl ", RPMFC_INCLUDE },
598 { "Perl5 module source text", RPMFC_INCLUDE },
599 { "python ", RPMFC_INCLUDE },
601 { "libtool library ", RPMFC_INCLUDE },
602 { "pkgconfig ", RPMFC_INCLUDE },
604 { "Objective caml ", RPMFC_INCLUDE },
605 { "Mono/.Net assembly", RPMFC_INCLUDE },
607 { "current ar archive", RPMFC_INCLUDE },
608 { "Zip archive data", RPMFC_INCLUDE },
609 { "tar archive", RPMFC_INCLUDE },
610 { "cpio archive", RPMFC_INCLUDE },
611 { "RPM v3", RPMFC_INCLUDE },
612 { "RPM v4", RPMFC_INCLUDE },
614 { " image", RPMFC_INCLUDE },
615 { " font", RPMFC_INCLUDE },
616 { " Font", RPMFC_INCLUDE },
618 { " commands", RPMFC_INCLUDE },
619 { " script", RPMFC_INCLUDE },
621 { "empty", RPMFC_INCLUDE },
623 { "HTML", RPMFC_INCLUDE },
624 { "SGML", RPMFC_INCLUDE },
625 { "XML", RPMFC_INCLUDE },
627 { " source", RPMFC_INCLUDE },
628 { "GLS_BINARY_LSB_FIRST", RPMFC_INCLUDE },
629 { " DB ", RPMFC_INCLUDE },
631 { " text", RPMFC_INCLUDE },
633 { NULL, RPMFC_BLACK }
636 static void argvAddTokens(ARGV_t *argv, const char *tnames)
639 ARGV_t tokens = NULL;
640 argvSplit(&tokens, tnames, ",");
641 for (ARGV_t token = tokens; token && *token; token++)
642 argvAddUniq(argv, *token);
647 static int matches(const struct matchRule *rule,
648 const char *ftype, const char *path, int executable)
650 if (!executable && hasAttr(rule->flags, "exeonly"))
652 if (rule->magic && rule->path && hasAttr(rule->flags, "magic_and_path")) {
653 return (regMatch(rule->magic, ftype) && regMatch(rule->path, path));
655 return (regMatch(rule->magic, ftype) || regMatch(rule->path, path));
659 static void rpmfcAttributes(rpmfc fc, int ix, const char *ftype, const char *fullpath)
661 const char *path = fullpath + fc->brlen;
662 int is_executable = 0;
664 if (stat(fullpath, &st) == 0) {
665 is_executable = (S_ISREG(st.st_mode)) &&
666 (st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH));
669 for (rpmfcAttr *attr = fc->atypes; attr && *attr; attr++) {
670 /* Filter out excludes */
671 if (matches(&(*attr)->excl, ftype, path, is_executable))
674 /* Add attributes on libmagic type & path pattern matches */
675 if (matches(&(*attr)->incl, ftype, path, is_executable))
676 argvAddTokens(&fc->fattrs[ix], (*attr)->name);
680 /* Return color for a given libmagic classification string */
681 static rpm_color_t rpmfcColor(const char * fmstr)
684 rpm_color_t fcolor = RPMFC_BLACK;
686 for (fct = rpmfcTokens; fct->token != NULL; fct++) {
687 if (strstr(fmstr, fct->token) == NULL)
690 fcolor |= fct->colors;
691 if (fcolor & RPMFC_INCLUDE)
698 void rpmfcPrint(const char * msg, rpmfc fc, FILE * fp)
704 if (fp == NULL) fp = stderr;
707 fprintf(fp, "===================================== %s\n", msg);
710 for (fx = 0; fx < fc->nfiles; fx++) {
711 fprintf(fp, "%3d %s", fx, fc->fn[fx]);
713 rpmsid cx = fc->fcdictx[fx] + 1; /* id's are one off */
714 rpm_color_t fcolor = fc->fcolor[fx];
715 ARGV_t fattrs = fc->fattrs[fx];
717 if (fcolor != RPMFC_BLACK)
718 fprintf(fp, "\t0x%x", fc->fcolor[fx]);
720 fprintf(fp, "\t%s", rpmstrPoolStr(fc->cdict, cx));
722 char *attrs = argvJoin(fattrs, ",");
723 fprintf(fp, " [%s]", attrs);
726 fprintf(fp, " [none]");
731 if (fc->fddictx == NULL || fc->fddictn == NULL)
734 assert(fx < fc->fddictx->nvals);
735 dx = fc->fddictx->vals[fx];
736 assert(fx < fc->fddictn->nvals);
737 ndx = fc->fddictn->vals[fx];
741 unsigned char deptype;
745 ix = fc->ddictx->vals[dx++];
746 deptype = ((ix >> 24) & 0xff);
749 ds = rpmfcDependencies(fc, rpmdsDToTagN(deptype));
750 (void) rpmdsSetIx(ds, ix-1);
751 if (rpmdsNext(ds) >= 0)
752 depval = rpmdsDNEVR(ds);
754 fprintf(fp, "\t%s\n", depval);
759 rpmfc rpmfcFree(rpmfc fc)
762 for (rpmfcAttr *attr = fc->atypes; attr && *attr; attr++)
763 rpmfcAttrFree(*attr);
766 for (int i = 0; i < fc->nfiles; i++) {
768 argvFree(fc->fattrs[i]);
775 argiFree(fc->fddictx);
776 argiFree(fc->fddictn);
777 argiFree(fc->ddictx);
779 for (int i = 0; i < fc->fileDeps.size; i++) {
780 rpmdsFree(fc->fileDeps.data[i].dep);
782 free(fc->fileDeps.data);
784 rpmstrPoolFree(fc->cdict);
786 rpmstrPoolFree(fc->pool);
787 memset(fc, 0, sizeof(*fc)); /* trash and burn */
793 rpmfc rpmfcCreate(const char *buildRoot, rpmFlags flags)
795 rpmfc fc = xcalloc(1, sizeof(*fc));
797 fc->buildRoot = xstrdup(buildRoot);
798 fc->brlen = strlen(buildRoot);
800 fc->pool = rpmstrPoolCreate();
801 fc->pkg = xcalloc(1, sizeof(*fc->pkg));
802 fc->fileDeps.alloced = 10;
803 fc->fileDeps.data = xmalloc(fc->fileDeps.alloced *
804 sizeof(fc->fileDeps.data[0]));
810 return rpmfcCreate(NULL, 0);
813 rpmds rpmfcDependencies(rpmfc fc, rpmTagVal tag)
816 return *packageDependencies(fc->pkg, tag);
821 rpmds rpmfcProvides(rpmfc fc)
823 return rpmfcDependencies(fc, RPMTAG_PROVIDENAME);
826 rpmds rpmfcRequires(rpmfc fc)
828 return rpmfcDependencies(fc, RPMTAG_REQUIRENAME);
831 rpmds rpmfcRecommends(rpmfc fc)
833 return rpmfcDependencies(fc, RPMTAG_RECOMMENDNAME);
836 rpmds rpmfcSuggests(rpmfc fc)
838 return rpmfcDependencies(fc, RPMTAG_SUGGESTNAME);
841 rpmds rpmfcSupplements(rpmfc fc)
843 return rpmfcDependencies(fc, RPMTAG_SUPPLEMENTNAME);
846 rpmds rpmfcEnhances(rpmfc fc)
848 return rpmfcDependencies(fc, RPMTAG_ENHANCENAME);
851 rpmds rpmfcConflicts(rpmfc fc)
853 return rpmfcDependencies(fc, RPMTAG_CONFLICTNAME);
856 rpmds rpmfcObsoletes(rpmfc fc)
858 return rpmfcDependencies(fc, RPMTAG_OBSOLETENAME);
862 /* Versioned deps are less than unversioned deps */
863 static int cmpVerDeps(const void *a, const void *b)
865 rpmfcFileDep *fDepA = (rpmfcFileDep *) a;
866 rpmfcFileDep *fDepB = (rpmfcFileDep *) b;
868 int aIsVersioned = rpmdsFlags(fDepA->dep) & RPMSENSE_SENSEMASK ? 1 : 0;
869 int bIsVersioned = rpmdsFlags(fDepB->dep) & RPMSENSE_SENSEMASK ? 1 : 0;
871 return bIsVersioned - aIsVersioned;
875 static int cmpIndexDeps(const void *a, const void *b)
877 rpmfcFileDep *fDepA = (rpmfcFileDep *) a;
878 rpmfcFileDep *fDepB = (rpmfcFileDep *) b;
880 return fDepA->fileIx - fDepB->fileIx;
884 * Remove unversioned deps if corresponding versioned deps exist but only
885 * if the versioned dependency has the same type and the same color as the versioned.
887 static void rpmfcNormalizeFDeps(rpmfc fc)
889 rpmstrPool versionedDeps = rpmstrPoolCreate();
890 rpmfcFileDep *normalizedFDeps = xmalloc(fc->fileDeps.size *
891 sizeof(normalizedFDeps[0]));
895 /* Sort. Versioned dependencies first */
896 qsort(fc->fileDeps.data, fc->fileDeps.size, sizeof(fc->fileDeps.data[0]),
899 for (int i = 0; i < fc->fileDeps.size; i++) {
900 switch (rpmdsTagN(fc->fileDeps.data[i].dep)) {
901 case RPMTAG_REQUIRENAME:
902 case RPMTAG_RECOMMENDNAME:
903 case RPMTAG_SUGGESTNAME:
904 rasprintf(&depStr, "%08x_%c_%s",
905 fc->fcolor[fc->fileDeps.data[i].fileIx],
906 rpmdsD(fc->fileDeps.data[i].dep),
907 rpmdsN(fc->fileDeps.data[i].dep));
909 if (rpmdsFlags(fc->fileDeps.data[i].dep) & RPMSENSE_SENSEMASK) {
910 /* preserve versioned require dependency */
911 normalizedFDeps[ix++] = fc->fileDeps.data[i];
912 rpmstrPoolId(versionedDeps, depStr, 1);
913 } else if (!rpmstrPoolId(versionedDeps, depStr, 0)) {
914 /* preserve unversioned require dep only if versioned dep doesn't exist */
915 normalizedFDeps[ix++] =fc-> fileDeps.data[i];
917 rpmdsFree(fc->fileDeps.data[i].dep);
922 /* Preserve all non-require dependencies */
923 normalizedFDeps[ix++] = fc->fileDeps.data[i];
927 rpmstrPoolFree(versionedDeps);
929 free(fc->fileDeps.data);
930 fc->fileDeps.data = normalizedFDeps;
931 fc->fileDeps.size = ix;
934 static rpmRC rpmfcApplyInternal(rpmfc fc)
942 /* Generate package and per-file dependencies. */
943 for (ix = 0; ix < fc->nfiles && fc->fn[ix] != NULL; ix++) {
944 for (ARGV_t fattr = fc->fattrs[ix]; fattr && *fattr; fattr++) {
946 rpmfcHelper(fc, ix, *fattr, "provides",
947 RPMSENSE_FIND_PROVIDES, RPMTAG_PROVIDENAME);
950 rpmfcHelper(fc, ix, *fattr, "requires",
951 RPMSENSE_FIND_REQUIRES, RPMTAG_REQUIRENAME);
952 rpmfcHelper(fc, ix, *fattr, "recommends",
953 RPMSENSE_FIND_REQUIRES, RPMTAG_RECOMMENDNAME);
954 rpmfcHelper(fc, ix, *fattr, "suggests",
955 RPMSENSE_FIND_REQUIRES, RPMTAG_SUGGESTNAME);
956 rpmfcHelper(fc, ix, *fattr, "supplements",
957 RPMSENSE_FIND_REQUIRES, RPMTAG_SUPPLEMENTNAME);
958 rpmfcHelper(fc, ix, *fattr, "enhances",
959 RPMSENSE_FIND_REQUIRES, RPMTAG_ENHANCENAME);
960 rpmfcHelper(fc, ix, *fattr, "conflicts",
961 RPMSENSE_FIND_REQUIRES, RPMTAG_CONFLICTNAME);
962 rpmfcHelper(fc, ix, *fattr, "obsoletes",
963 RPMSENSE_FIND_REQUIRES, RPMTAG_OBSOLETENAME);
967 /* No more additions after this, freeze pool to minimize memory use */
969 rpmfcNormalizeFDeps(fc);
970 for (int i = 0; i < fc->fileDeps.size; i++) {
971 ds = fc->fileDeps.data[i].dep;
972 rpmdsMerge(packageDependencies(fc->pkg, rpmdsTagN(ds)), ds);
976 qsort(fc->fileDeps.data, fc->fileDeps.size,
977 sizeof(fc->fileDeps.data[0]), cmpIndexDeps);
979 /* Generate per-file indices into package dependencies. */
981 for (int i = 0; i < fc->fileDeps.size; i++) {
982 ds = fc->fileDeps.data[i].dep;
983 ix = fc->fileDeps.data[i].fileIx;
984 dsp = packageDependencies(fc->pkg, rpmdsTagN(ds));
985 dix = rpmdsFind(*dsp, ds);
989 val = (rpmdsD(ds) << 24) | (dix & 0x00ffffff);
990 argiAdd(&fc->ddictx, -1, val);
994 argiAdd(&fc->fddictx, ix, argiCount(fc->ddictx)-1);
996 if (fc->fddictn && fc->fddictn->vals)
997 fc->fddictn->vals[ix]++;
1003 static int initAttrs(rpmfc fc)
1005 ARGV_t files = NULL;
1006 char * attrPath = rpmExpand("%{_fileattrsdir}/*.attr", NULL);
1009 /* Discover known attributes from pathnames + initialize them */
1010 if (rpmGlob(attrPath, NULL, &files) == 0) {
1011 nattrs = argvCount(files);
1012 fc->atypes = xcalloc(nattrs + 1, sizeof(*fc->atypes));
1013 for (int i = 0; i < nattrs; i++) {
1014 char *bn = basename(files[i]);
1015 bn[strlen(bn)-strlen(".attr")] = '\0';
1016 fc->atypes[i] = rpmfcAttrNew(bn);
1018 fc->atypes[nattrs] = NULL;
1025 rpmRC rpmfcClassify(rpmfc fc, ARGV_t argv, rpm_mode_t * fmode)
1027 int msflags = MAGIC_CHECK | MAGIC_COMPRESS | MAGIC_NO_CHECK_TOKENS;
1029 rpmRC rc = RPMRC_FAIL;
1032 rpmlog(RPMLOG_ERR, _("Empty file classifier\n"));
1036 /* It is OK when we have no files to classify. */
1040 if (initAttrs(fc) < 1) {
1041 rpmlog(RPMLOG_ERR, _("No file attributes configured\n"));
1045 fc->nfiles = argvCount(argv);
1046 fc->fn = xcalloc(fc->nfiles, sizeof(*fc->fn));
1047 fc->fattrs = xcalloc(fc->nfiles, sizeof(*fc->fattrs));
1048 fc->fcolor = xcalloc(fc->nfiles, sizeof(*fc->fcolor));
1049 fc->fcdictx = xcalloc(fc->nfiles, sizeof(*fc->fcdictx));
1051 /* Initialize the per-file dictionary indices. */
1052 argiAdd(&fc->fddictx, fc->nfiles-1, 0);
1053 argiAdd(&fc->fddictn, fc->nfiles-1, 0);
1055 /* Build (sorted) file class dictionary. */
1056 fc->cdict = rpmstrPoolCreate();
1058 ms = magic_open(msflags);
1060 rpmlog(RPMLOG_ERR, _("magic_open(0x%x) failed: %s\n"),
1061 msflags, strerror(errno));
1065 if (magic_load(ms, NULL) == -1) {
1066 rpmlog(RPMLOG_ERR, _("magic_load failed: %s\n"), magic_error(ms));
1070 for (int ix = 0; ix < fc->nfiles; ix++) {
1073 const char * s = argv[ix];
1074 size_t slen = strlen(s);
1075 int fcolor = RPMFC_BLACK;
1076 rpm_mode_t mode = (fmode ? fmode[ix] : 0);
1077 int is_executable = (mode & (S_IXUSR|S_IXGRP|S_IXOTH));
1079 switch (mode & S_IFMT) {
1080 case S_IFCHR: ftype = "character special"; break;
1081 case S_IFBLK: ftype = "block special"; break;
1082 case S_IFIFO: ftype = "fifo (named pipe)"; break;
1083 case S_IFSOCK: ftype = "socket"; break;
1084 case S_IFDIR: ftype = "directory"; break;
1088 /* XXX all files with extension ".pm" are perl modules for now. */
1089 if (rpmFileHasSuffix(s, ".pm"))
1090 ftype = "Perl5 module source text";
1092 /* XXX all files with extension ".la" are libtool for now. */
1093 else if (rpmFileHasSuffix(s, ".la"))
1094 ftype = "libtool library file";
1096 /* XXX all files with extension ".pc" are pkgconfig for now. */
1097 else if (rpmFileHasSuffix(s, ".pc"))
1098 ftype = "pkgconfig file";
1100 /* XXX skip all files in /dev/ which are (or should be) %dev dummies. */
1101 else if (slen >= fc->brlen+sizeof("/dev/") && rstreqn(s+fc->brlen, "/dev/", sizeof("/dev/")-1))
1104 ftype = magic_file(ms, s);
1106 if (ftype == NULL) {
1107 rpmlog(is_executable ? RPMLOG_ERR : RPMLOG_WARNING,
1108 _("Recognition of file \"%s\" failed: mode %06o %s\n"),
1109 s, mode, magic_error(ms));
1110 /* only executable files are critical to dep extraction */
1111 if (is_executable) {
1114 /* unrecognized non-executables get treated as "data" */
1119 rpmlog(RPMLOG_DEBUG, "%s: %s\n", s, ftype);
1121 /* Save the path. */
1122 fc->fn[ix] = xstrdup(s);
1124 /* Add (filtered) file coloring */
1125 fcolor |= rpmfcColor(ftype);
1127 /* Add attributes based on file type and/or path */
1128 rpmfcAttributes(fc, ix, ftype, s);
1130 fc->fcolor[ix] = fcolor;
1132 /* Add to file class dictionary and index array */
1133 if (fcolor != RPMFC_WHITE && (fcolor & RPMFC_INCLUDE)) {
1134 ftypeId = rpmstrPoolId(fc->cdict, ftype, 1);
1137 ftypeId = rpmstrPoolId(fc->cdict, "", 1);
1140 /* Pool id's start from 1, for headers we want it from 0 */
1141 fc->fcdictx[ix] = ftypeId - 1;
1146 /* No more additions after this, freeze pool to minimize memory use */
1147 rpmstrPoolFreeze(fc->cdict, 0);
1156 typedef struct DepMsg_s * DepMsg_t;
1162 char * const argv[4];
1172 static struct DepMsg_s depMsgs[] = {
1173 { "Provides", { "%{?__find_provides}", NULL, NULL, NULL },
1174 RPMTAG_PROVIDENAME, RPMTAG_PROVIDEVERSION, RPMTAG_PROVIDEFLAGS,
1176 { "Requires(interp)", { NULL, "interp", NULL, NULL },
1177 RPMTAG_REQUIRENAME, RPMTAG_REQUIREVERSION, RPMTAG_REQUIREFLAGS,
1178 RPMSENSE_INTERP, 0 },
1179 { "Requires(rpmlib)", { NULL, "rpmlib", NULL, NULL },
1180 -1, -1, RPMTAG_REQUIREFLAGS,
1181 RPMSENSE_RPMLIB, 0 },
1182 { "Requires(verify)", { NULL, "verify", NULL, NULL },
1183 -1, -1, RPMTAG_REQUIREFLAGS,
1184 RPMSENSE_SCRIPT_VERIFY, 0 },
1185 { "Requires(pre)", { NULL, "pre", NULL, NULL },
1186 -1, -1, RPMTAG_REQUIREFLAGS,
1187 RPMSENSE_SCRIPT_PRE, 0 },
1188 { "Requires(post)", { NULL, "post", NULL, NULL },
1189 -1, -1, RPMTAG_REQUIREFLAGS,
1190 RPMSENSE_SCRIPT_POST, 0 },
1191 { "Requires(preun)", { NULL, "preun", NULL, NULL },
1192 -1, -1, RPMTAG_REQUIREFLAGS,
1193 RPMSENSE_SCRIPT_PREUN, 0 },
1194 { "Requires(postun)", { NULL, "postun", NULL, NULL },
1195 -1, -1, RPMTAG_REQUIREFLAGS,
1196 RPMSENSE_SCRIPT_POSTUN, 0 },
1197 { "Requires(pretrans)", { NULL, "pretrans", NULL, NULL },
1198 -1, -1, RPMTAG_REQUIREFLAGS,
1199 RPMSENSE_PRETRANS, 0 },
1200 { "Requires(posttrans)", { NULL, "posttrans", NULL, NULL },
1201 -1, -1, RPMTAG_REQUIREFLAGS,
1202 RPMSENSE_POSTTRANS, 0 },
1203 { "Requires", { "%{?__find_requires}", NULL, NULL, NULL },
1204 -1, -1, RPMTAG_REQUIREFLAGS, /* XXX inherit name/version arrays */
1205 RPMSENSE_FIND_REQUIRES|RPMSENSE_TRIGGERIN|RPMSENSE_TRIGGERUN|RPMSENSE_TRIGGERPOSTUN|RPMSENSE_TRIGGERPREIN, 0 },
1206 { "Conflicts", { "%{?__find_conflicts}", NULL, NULL, NULL },
1207 RPMTAG_CONFLICTNAME, RPMTAG_CONFLICTVERSION, RPMTAG_CONFLICTFLAGS,
1209 { "Obsoletes", { "%{?__find_obsoletes}", NULL, NULL, NULL },
1210 RPMTAG_OBSOLETENAME, RPMTAG_OBSOLETEVERSION, RPMTAG_OBSOLETEFLAGS,
1212 { "Recommends", { "%{?__find_recommends}", NULL, NULL, NULL },
1213 RPMTAG_RECOMMENDNAME, RPMTAG_RECOMMENDVERSION, RPMTAG_RECOMMENDFLAGS,
1215 { "Suggests", { "%{?__find_suggests}", NULL, NULL, NULL },
1216 RPMTAG_SUGGESTNAME, RPMTAG_SUGGESTVERSION, RPMTAG_SUGGESTFLAGS,
1218 { "Supplements", { "%{?__find_supplements}", NULL, NULL, NULL },
1219 RPMTAG_SUPPLEMENTNAME, RPMTAG_SUPPLEMENTVERSION, RPMTAG_SUPPLEMENTFLAGS,
1221 { "Enhances", { "%{?__find_enhances}", NULL, NULL, NULL },
1222 RPMTAG_ENHANCENAME, RPMTAG_ENHANCEVERSION, RPMTAG_ENHANCEFLAGS,
1224 { NULL, { NULL, NULL, NULL, NULL }, 0, 0, 0, 0, 0 }
1227 static DepMsg_t DepMsgs = depMsgs;
1231 static void printDeps(rpmfc fc)
1236 rpmsenseFlags Flags;
1239 for (dm = DepMsgs; dm->msg != NULL; dm++) {
1240 if (dm->ntag != -1) {
1241 ds = rpmfcDependencies(fc, dm->ntag);
1248 continue; /* XXX can't happen */
1251 while (rpmdsNext(ds) >= 0) {
1253 Flags = rpmdsFlags(ds);
1255 if (!((Flags & dm->mask) ^ dm->xormask))
1258 rpmlog(RPMLOG_NOTICE, "%s:", (dm->msg ? dm->msg : ""));
1261 if ((DNEVR = rpmdsDNEVR(ds)) == NULL)
1262 continue; /* XXX can't happen */
1263 rpmlog(RPMLOG_NOTICE, " %s", DNEVR+2);
1266 rpmlog(RPMLOG_NOTICE, "\n");
1270 static rpmRC rpmfcApplyExternal(rpmfc fc)
1272 StringBuf sb_stdin = newStringBuf();
1273 rpmRC rc = RPMRC_OK;
1275 /* Create file manifest buffer to deliver to dependency finder. */
1276 for (int i = 0; i < fc->nfiles; i++)
1277 appendLineStringBuf(sb_stdin, fc->fn[i]);
1279 for (DepMsg_t dm = DepMsgs; dm->msg != NULL; dm++) {
1280 rpmTagVal tag = (dm->ftag > 0) ? dm->ftag : dm->ntag;
1281 rpmsenseFlags tagflags;
1283 StringBuf sb_stdout = NULL;
1284 int failnonzero = (tag == RPMTAG_PROVIDEFLAGS);
1287 case RPMTAG_PROVIDEFLAGS:
1290 tagflags = RPMSENSE_FIND_PROVIDES;
1292 case RPMTAG_REQUIREFLAGS:
1293 case RPMTAG_RECOMMENDFLAGS:
1294 case RPMTAG_SUGGESTFLAGS:
1295 case RPMTAG_SUPPLEMENTFLAGS:
1296 case RPMTAG_ENHANCEFLAGS:
1297 case RPMTAG_CONFLICTFLAGS:
1298 case RPMTAG_OBSOLETEFLAGS:
1301 tagflags = RPMSENSE_FIND_REQUIRES;
1308 s = rpmExpand(dm->argv[0], NULL);
1309 rpmlog(RPMLOG_NOTICE, _("Finding %s: %s\n"), dm->msg, s);
1312 if (rpmfcExec(dm->argv, sb_stdin, &sb_stdout,
1313 failnonzero, fc->buildRoot) == -1)
1316 if (sb_stdout == NULL) {
1318 rpmlog(RPMLOG_ERR, _("Failed to find %s:\n"), dm->msg);
1322 /* Parse dependencies into header */
1323 rc = parseRCPOT(NULL, fc->pkg, getStringBuf(sb_stdout), dm->ntag != -1 ? dm->ntag : RPMTAG_REQUIRENAME, 0, tagflags, addReqProvPkg, NULL);
1324 freeStringBuf(sb_stdout);
1327 rpmlog(RPMLOG_ERR, _("Failed to find %s:\n"), dm->msg);
1332 freeStringBuf(sb_stdin);
1337 rpmRC rpmfcApply(rpmfc fc)
1340 /* If new-fangled dependency generation is disabled ... */
1341 if (!rpmExpandNumeric("%{?_use_internal_dependency_generator}")) {
1342 /* ... then generate dependencies using %{__find_requires} et al. */
1343 rpmlog(RPMLOG_WARNING,
1344 _("Deprecated external dependency generator is used!\n"));
1345 rc = rpmfcApplyExternal(fc);
1347 /* ... otherwise generate per-file dependencies */
1348 rc = rpmfcApplyInternal(fc);
1353 rpmRC rpmfcGenerateDepends(const rpmSpec spec, Package pkg)
1355 rpmfi fi = rpmfilesIter(pkg->cpioList, RPMFI_ITER_FWD);
1357 rpm_mode_t * fmode = NULL;
1358 int ac = rpmfiFC(fi);
1359 int genConfigDeps = 0;
1360 rpmRC rc = RPMRC_OK;
1364 /* Skip packages with no files. */
1368 /* Extract absolute file paths in argv format. */
1369 fmode = xcalloc(ac+1, sizeof(*fmode));
1371 fi = rpmfiInit(fi, 0);
1372 while ((idx = rpmfiNext(fi)) >= 0) {
1373 /* Does package have any %config files? */
1374 genConfigDeps |= (rpmfiFFlags(fi) & RPMFILE_CONFIG);
1375 fmode[idx] = rpmfiFMode(fi);
1378 fc = rpmfcCreate(spec->buildRoot, 0);
1381 fc->skipProv = !pkg->autoProv;
1382 fc->skipReq = !pkg->autoReq;
1384 if (!fc->skipProv && genConfigDeps) {
1385 /* Add config dependency, Provides: config(N) = EVR */
1386 rpmds ds = rpmdsSingleNS(fc->pool, RPMTAG_PROVIDENAME, "config",
1387 rpmdsN(pkg->ds), rpmdsEVR(pkg->ds),
1388 (RPMSENSE_EQUAL|RPMSENSE_CONFIG));
1389 rpmdsMerge(packageDependencies(pkg, RPMTAG_PROVIDENAME), ds);
1392 if (!fc->skipReq && genConfigDeps) {
1393 rpmds ds = rpmdsSingleNS(fc->pool, RPMTAG_REQUIRENAME, "config",
1394 rpmdsN(pkg->ds), rpmdsEVR(pkg->ds),
1395 (RPMSENSE_EQUAL|RPMSENSE_CONFIG));
1396 rpmdsMerge(packageDependencies(pkg, RPMTAG_REQUIRENAME), ds);
1400 /* Build file class dictionary. */
1401 rc = rpmfcClassify(fc, pkg->dpaths, fmode);
1402 if ( rc != RPMRC_OK )
1405 /* Build file/package dependency dictionary. */
1406 rc = rpmfcApply(fc);
1410 /* Add per-file colors(#files) */
1411 /* XXX Make sure only primary (i.e. Elf32/Elf64) colors are added. */
1412 for (int i = 0; i < fc->nfiles; i++)
1413 fc->fcolor[i] &= 0x0f;
1414 headerPutUint32(pkg->header, RPMTAG_FILECOLORS, fc->fcolor, fc->nfiles);
1416 /* Add classes(#classes) */
1417 for (rpmsid id = 1; id <= rpmstrPoolNumStr(fc->cdict); id++) {
1418 headerPutString(pkg->header, RPMTAG_CLASSDICT,
1419 rpmstrPoolStr(fc->cdict, id));
1422 /* Add per-file classes(#files) */
1423 headerPutUint32(pkg->header, RPMTAG_FILECLASS, fc->fcdictx, fc->nfiles);
1425 /* Add dependency dictionary(#dependencies) */
1426 if (rpmtdFromArgi(&td, RPMTAG_DEPENDSDICT, fc->ddictx)) {
1427 headerPut(pkg->header, &td, HEADERPUT_DEFAULT);
1429 /* Add per-file dependency (start,number) pairs (#files) */
1430 if (rpmtdFromArgi(&td, RPMTAG_FILEDEPENDSX, fc->fddictx)) {
1431 headerPut(pkg->header, &td, HEADERPUT_DEFAULT);
1434 if (rpmtdFromArgi(&td, RPMTAG_FILEDEPENDSN, fc->fddictn)) {
1435 headerPut(pkg->header, &td, HEADERPUT_DEFAULT);
1442 rasprintf(&msg, "final: files %d cdict[%d] %d%% ddictx[%d]",
1443 fc->nfiles, rpmstrPoolNumStr(fc->cdict),
1444 ((100 * fc->fknown)/fc->nfiles), argiCount(fc->ddictx));
1445 rpmfcPrint(msg, fc, NULL);