rpm 5.2.1

- Main Page
- Related Pages
- Modules
- Data Structures
- Files

- File List
- Globals

rpmfc.c
Go to the documentation of this file.
1 #include "system.h"
2
3 #include <signal.h> /* getOutputFrom() */
4
5 #include <rpmio.h>
6 #include <rpmiotypes.h> /* XXX fnpyKey */
7 #include <rpmlog.h>
8 #include <rpmurl.h>
9 #include <rpmmg.h>
10 #include <argv.h>
11 #define _MIRE_INTERNAL
12 #include <mire.h>
13
14 #include <rpmtag.h>
15 #define _RPMEVR_INTERNAL
16 #include <rpmbuild.h>
17
18 #define _RPMNS_INTERNAL
19 #include <rpmns.h>
20
21 #define _RPMFC_INTERNAL
22 #include <rpmfc.h>
23
24 #define _RPMDS_INTERNAL
25 #include <rpmds.h>
26 #include <rpmfi.h>
27
28 #include "debug.h"
29
30 /*@access rpmds @*/
31 /*@access miRE @*/
32
33 /*@unchecked@*/
34 static int _filter_values = 1;
35 /*@unchecked@*/
36 static int _filter_execs = 1;
37
40 static int rpmfcExpandAppend(/*@out@*/ ARGV_t * argvp, const ARGV_t av)
41 /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
42 /*@modifies *argvp, rpmGlobalMacroContext, internalState @*/
43 /*@requires maxRead(argvp) >= 0 @*/
44 {
45 ARGV_t argv = *argvp;
46 int argc = argvCount(argv);
47 int ac = argvCount(av);
48 int i;
49
50 argv = xrealloc(argv, (argc + ac + 1) * sizeof(*argv));
51 for (i = 0; i < ac; i++)
52 argv[argc + i] = rpmExpand(av[i], NULL);
53 argv[argc + ac] = NULL;
54 *argvp = argv;
55 return 0;
56 }
57
68 /*@null@*/
69 static rpmiob getOutputFrom(/*@null@*/ const char * dir, ARGV_t argv,
70 const char * writePtr, size_t writeBytesLeft,
71 int failNonZero)
72 /*@globals h_errno, fileSystem, internalState@*/
73 /*@modifies fileSystem, internalState@*/
74 {
75 pid_t child, reaped;
76 int toProg[2];
77 int fromProg[2];
78 int status;
79 void *oldhandler;
80 rpmiob iob = NULL;
81 int done;
82
83 /*@-type@*/ /* FIX: cast? */
84 oldhandler = signal(SIGPIPE, SIG_IGN);
85 /*@=type@*/
86
87 toProg[0] = toProg[1] = 0;
88 fromProg[0] = fromProg[1] = 0;
89 if (pipe(toProg) < 0 || pipe(fromProg) < 0) {
90 rpmlog(RPMLOG_ERR, _("Couldn't create pipe for %s: %m\n"), argv[0]);
91 return NULL;
92 }
93
94 if (!(child = fork())) {
95 (void) close(toProg[1]);
96 (void) close(fromProg[0]);
97
98 (void) dup2(toProg[0], STDIN_FILENO); /* Make stdin the in pipe */
99 (void) dup2(fromProg[1], STDOUT_FILENO); /* Make stdout the out pipe */
100
101 (void) close(toProg[0]);
102 (void) close(fromProg[1]);
103
104 if (dir) {
105 (void) Chdir(dir);
106 }
107
108 rpmlog(RPMLOG_DEBUG, D_("\texecv(%s) pid %d\n"),
109 argv[0], (unsigned)getpid());
110
111 unsetenv("MALLOC_CHECK_");
112 (void) execvp(argv[0], (char *const *)argv);
113 /* XXX this error message is probably not seen. */
114 rpmlog(RPMLOG_ERR, _("Couldn't exec %s: %s\n"),
115 argv[0], strerror(errno));
116 _exit(EXIT_FAILURE);
117 }
118 if (child < 0) {
119 rpmlog(RPMLOG_ERR, _("Couldn't fork %s: %s\n"),
120 argv[0], strerror(errno));
121 return NULL;
122 }
123
124 (void) close(toProg[0]);
125 (void) close(fromProg[1]);
126
127 /* Do not block reading or writing from/to prog. */
128 (void) fcntl(fromProg[0], F_SETFL, O_NONBLOCK);
129 (void) fcntl(toProg[1], F_SETFL, O_NONBLOCK);
130
131 iob = rpmiobNew(0);
132
133 do {
134 fd_set ibits, obits;
135 struct timeval tv;
136 int nfd;
137 ssize_t nbr;
138 ssize_t nbw;
139 int rc;
140
141 done = 0;
142 top:
143 FD_ZERO(&ibits);
144 FD_ZERO(&obits);
145 if (fromProg[0] >= 0) {
146 FD_SET(fromProg[0], &ibits);
147 }
148 if (toProg[1] >= 0) {
149 FD_SET(toProg[1], &obits);
150 }
151 /* XXX values set to limit spinning with perl doing ~100 forks/sec. */
152 tv.tv_sec = 0;
153 tv.tv_usec = 10000;
154 nfd = ((fromProg[0] > toProg[1]) ? fromProg[0] : toProg[1]);
155 if ((rc = select(nfd, &ibits, &obits, NULL, &tv)) < 0) {
156 if (errno == EINTR)
157 goto top;
158 break;
159 }
160
161 /* Write any data to program */
162 if (toProg[1] >= 0 && FD_ISSET(toProg[1], &obits)) {
163 if (writePtr && writeBytesLeft > 0) {
164 if ((nbw = write(toProg[1], writePtr,
165 ((size_t)1024<writeBytesLeft) ? (size_t)1024 : writeBytesLeft)) < 0)
166 {
167 if (errno != EAGAIN) {
168 perror("getOutputFrom()");
169 exit(EXIT_FAILURE);
170 }
171 nbw = 0;
172 }
173 writeBytesLeft -= nbw;
174 writePtr += nbw;
175 } else if (toProg[1] >= 0) { /* close write fd */
176 (void) close(toProg[1]);
177 toProg[1] = -1;
178 }
179 }
180
181 /* Read any data from prog */
182 { char buf[BUFSIZ+1];
183 while ((nbr = read(fromProg[0], buf, sizeof(buf)-1)) > 0) {
184 buf[nbr] = '\0';
185 iob = rpmiobAppend(iob, buf, 0);
186 }
187 }
188
189 /* terminate on (non-blocking) EOF or error */
190 done = (nbr == 0 || (nbr < 0 && errno != EAGAIN));
191
192 } while (!done);
193
194 /* Clean up */
195 if (toProg[1] >= 0)
196 (void) close(toProg[1]);
197 if (fromProg[0] >= 0)
198 (void) close(fromProg[0]);
199 /*@-type@*/ /* FIX: cast? */
200 (void) signal(SIGPIPE, oldhandler);
201 /*@=type@*/
202
203 /* Collect status from prog */
204 reaped = waitpid(child, &status, 0);
205 rpmlog(RPMLOG_DEBUG, D_("\twaitpid(%d) rc %d status %x\n"),
206 (unsigned)child, (unsigned)reaped, status);
207
208 if (failNonZero && (!WIFEXITED(status) || WEXITSTATUS(status))) {
209 const char *cmd = argvJoin(argv);
210 int rc = (WIFEXITED(status) ? WEXITSTATUS(status) : -1);
211
212 rpmlog(RPMLOG_ERR, _("Command \"%s\" failed, exit(%d)\n"), cmd, rc);
213 cmd = _free(cmd);
214 iob = rpmiobFree(iob);
215 return NULL;
216 }
217 if (writeBytesLeft) {
218 rpmlog(RPMLOG_ERR, _("failed to write all data to %s\n"), argv[0]);
219 iob = rpmiobFree(iob);
220 return NULL;
221 }
222 return iob;
223 }
224
225 int rpmfcExec(ARGV_t av, rpmiob iob_stdin, rpmiob * iob_stdoutp,
226 int failnonzero)
227 {
228 const char * s = NULL;
229 ARGV_t xav = NULL;
230 ARGV_t pav = NULL;
231 int pac = 0;
232 int ec = -1;
233 rpmiob iob = NULL;
234 const char * buf_stdin = NULL;
235 size_t buf_stdin_len = 0;
236 int xx;
237
238 if (iob_stdoutp)
239 *iob_stdoutp = NULL;
240 if (!(av && *av))
241 goto exit;
242
243 /* Find path to executable with (possible) args. */
244 s = rpmExpand(av[0], NULL);
245 if (!(s && *s))
246 goto exit;
247
248 /* Parse args buried within expanded executable. */
249 pac = 0;
250 xx = poptParseArgvString(s, &pac, (const char ***)&pav);
251 if (!(xx == 0 && pac > 0 && pav != NULL))
252 goto exit;
253
254 /* Build argv, appending args to the executable args. */
255 xav = NULL;
256 xx = argvAppend(&xav, pav);
257 if (av[1])
258 xx = rpmfcExpandAppend(&xav, av + 1);
259
260 if (iob_stdin != NULL) {
261 buf_stdin = rpmiobStr(iob_stdin);
262 buf_stdin_len = rpmiobLen(iob_stdin);
263 }
264
265 /* Read output from exec'd helper. */
266 iob = getOutputFrom(NULL, xav, buf_stdin, buf_stdin_len, failnonzero);
267
268 if (iob_stdoutp != NULL) {
269 *iob_stdoutp = iob;
270 iob = NULL; /* XXX don't free */
271 }
272
273 ec = 0;
274
275 exit:
276 iob = rpmiobFree(iob);
277 xav = argvFree(xav);
278 pav = _free(pav); /* XXX popt mallocs in single blob. */
279 s = _free(s);
280 return ec;
281 }
282
285 static int rpmfcSaveArg(/*@out@*/ ARGV_t * argvp, const char * key)
286 /*@modifies *argvp @*/
287 /*@requires maxSet(argvp) >= 0 @*/
288 {
289 int rc = 0;
290
291 if (argvSearch(*argvp, key, NULL) == NULL) {
292 rc = argvAdd(argvp, key);
293 rc = argvSort(*argvp, NULL);
294 }
295 return rc;
296 }
297
300 static char * rpmfcFileDep(/*@returned@*/ char * buf, size_t ix,
301 /*@null@*/ rpmds ds)
302 /*@globals internalState @*/
303 /*@modifies buf, internalState @*/
304 /*@requires maxSet(buf) >= 0 @*/
305 {
306 rpmTag tagN = rpmdsTagN(ds);
307 char deptype = 'X';
308
309 buf[0] = '\0';
310 switch (tagN) {
311 default:
312 assert(0);
313 /*@notreached@*/ break;
314 case RPMTAG_PROVIDENAME:
315 deptype = 'P';
316 break;
317 case RPMTAG_REQUIRENAME:
318 deptype = 'R';
319 break;
320 }
321 /*@-nullpass@*/
322 if (ds != NULL)
323 sprintf(buf, "%08u%c %s %s 0x%08x", (unsigned)ix, deptype,
324 rpmdsN(ds), rpmdsEVR(ds), rpmdsFlags(ds));
325 /*@=nullpass@*/
326 return buf;
327 };
328
329 /*@null@*/
330 static void * rpmfcExpandRegexps(const char * str, int * nmirep)
331 /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
332 /*@modifies *nmirep, rpmGlobalMacroContext, internalState @*/
333 {
334 ARGV_t av = NULL;
335 int ac = 0;
336 miRE mire = NULL;
337 int nmire = 0;
338 const char * s;
339 int xx;
340 int i;
341
342 s = rpmExpand(str, NULL);
343 if (s) {
344 xx = poptParseArgvString(s, &ac, (const char ***)&av);
345 s = _free(s);
346 }
347 if (ac == 0 || av == NULL || *av == NULL)
348 goto exit;
349
350 for (i = 0; i < ac; i++) {
351 xx = mireAppend(RPMMIRE_REGEX, 0, av[i], NULL, &mire, &nmire);
352 /* XXX add REG_NOSUB? better error msg? */
353 if (xx) {
354 rpmlog(RPMLOG_NOTICE,
355 _("Compilation of pattern '%s'"
356 " (expanded from '%s') failed. Skipping ...\n"),
357 av[i], str);
358 nmire--; /* XXX does this actually skip?!? */
359 }
360 }
361 if (nmire == 0)
362 mire = mireFree(mire);
363
364 exit:
365 av = _free(av);
366 if (nmirep)
367 *nmirep = nmire;
368 return mire;
369 }
370
371 static int rpmfcMatchRegexps(void * mires, int nmire,
372 const char * str, char deptype)
373 /*@modifies mires @*/
374 {
375 miRE mire = mires;
376 int xx;
377 int i;
378
379 for (i = 0; i < nmire; i++) {
380 rpmlog(RPMLOG_DEBUG, D_("Checking %c: '%s'\n"), deptype, str);
381 if ((xx = mireRegexec(mire + i, str, 0)) < 0)
382 continue;
383 rpmlog(RPMLOG_NOTICE, _("Skipping %c: '%s'\n"), deptype, str);
384 return 1;
385 }
386 return 0;
387 }
388
389 /*@null@*/
390 static void * rpmfcFreeRegexps(/*@only@*/ void * mires, int nmire)
391 /*@modifies mires @*/
392 {
393 miRE mire = mires;
394 /*@-refcounttrans@*/
395 return mireFreeAll(mire, nmire);
396 /*@=refcounttrans@*/
397 }
398
406 static int rpmfcHelper(rpmfc fc, unsigned char deptype, const char * nsdep)
407 /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
408 /*@modifies fc, rpmGlobalMacroContext, fileSystem, internalState @*/
409 {
410 miRE mire = NULL;
411 int nmire = 0;
412 const char * fn = fc->fn[fc->ix];
413 char buf[BUFSIZ];
414 rpmiob iob_stdout = NULL;
415 rpmiob iob_stdin;
416 const char *av[2];
417 rpmds * depsp, ds;
418 const char * N;
419 const char * EVR;
420 rpmTag tagN;
421 evrFlags Flags;
422 evrFlags dsContext;
423 ARGV_t pav;
424 const char * s;
425 int pac;
426 int xx;
427 int i;
428
429 switch (deptype) {
430 default:
431 return -1;
432 /*@notreached@*/ break;
433 case 'P':
434 if (fc->skipProv)
435 return 0;
436 xx = snprintf(buf, sizeof(buf), "%%{?__%s_provides}", nsdep);
437 depsp = &fc->provides;
438 dsContext = RPMSENSE_FIND_PROVIDES;
439 tagN = RPMTAG_PROVIDENAME;
440 mire = fc->Pmires;
441 nmire = fc->Pnmire;
442 break;
443 case 'R':
444 if (fc->skipReq)
445 return 0;
446 xx = snprintf(buf, sizeof(buf), "%%{?__%s_requires}", nsdep);
447 depsp = &fc->requires;
448 dsContext = RPMSENSE_FIND_REQUIRES;
449 tagN = RPMTAG_REQUIRENAME;
450 mire = fc->Rmires;
451 nmire = fc->Rnmire;
452 break;
453 }
454 buf[sizeof(buf)-1] = '\0';
455 av[0] = buf;
456 av[1] = NULL;
457
458 iob_stdin = rpmiobNew(0);
459 iob_stdin = rpmiobAppend(iob_stdin, fn, 1);
460 iob_stdout = NULL;
461 xx = rpmfcExec(av, iob_stdin, &iob_stdout, 0);
462 iob_stdin = rpmiobFree(iob_stdin);
463
464 if (xx == 0 && iob_stdout != NULL) {
465 pav = NULL;
466 xx = argvSplit(&pav, rpmiobStr(iob_stdout), " \t\n\r");
467 pac = argvCount(pav);
468 if (pav)
469 for (i = 0; i < pac; i++) {
470 N = pav[i];
471 EVR = "";
472 Flags = dsContext;
473 if (pav[i+1] && strchr("=<>", *pav[i+1])) {
474 i++;
475 for (s = pav[i]; *s; s++) {
476 switch(*s) {
477 default:
478 assert(*s != '\0');
479 /*@switchbreak@*/ break;
480 case '=':
481 Flags |= RPMSENSE_EQUAL;
482 /*@switchbreak@*/ break;
483 case '<':
484 Flags |= RPMSENSE_LESS;
485 /*@switchbreak@*/ break;
486 case '>':
487 Flags |= RPMSENSE_GREATER;
488 /*@switchbreak@*/ break;
489 }
490 }
491 i++;
492 EVR = pav[i];
493 assert(EVR != NULL);
494 }
495
496 if (_filter_values && rpmfcMatchRegexps(mire, nmire, N, deptype))
497 continue;
498
499 /* Add tracking dependency for versioned Provides: */
500 if (!fc->tracked && deptype == 'P' && *EVR != '\0') {
501 ds = rpmdsSingle(RPMTAG_REQUIRENAME,
502 "rpmlib(VersionedDependencies)", "3.0.3-1",
503 RPMSENSE_RPMLIB|(RPMSENSE_LESS|RPMSENSE_EQUAL));
504 xx = rpmdsMerge(&fc->requires, ds);
505 (void)rpmdsFree(ds);
506 ds = NULL;
507 fc->tracked = 1;
508 }
509
510 ds = rpmdsSingle(tagN, N, EVR, Flags);
511
512 /* Add to package dependencies. */
513 xx = rpmdsMerge(depsp, ds);
514
515 /* Add to file dependencies. */
516 xx = rpmfcSaveArg(&fc->ddict, rpmfcFileDep(buf, fc->ix, ds));
517
518 (void)rpmdsFree(ds);
519 ds = NULL;
520 }
521
522 pav = argvFree(pav);
523 }
524 iob_stdout = rpmiobFree(iob_stdout);
525
526 return 0;
527 }
528
531 /*@-nullassign@*/
532 /*@unchecked@*/ /*@observer@*/
533 static struct rpmfcTokens_s rpmfcTokens[] = {
534 { "directory", RPMFC_DIRECTORY|RPMFC_INCLUDE },
535
536 { " shared object", RPMFC_LIBRARY },
537 { " executable", RPMFC_EXECUTABLE },
538 { " statically linked", RPMFC_STATIC },
539 { " not stripped", RPMFC_NOTSTRIPPED },
540 { " archive", RPMFC_ARCHIVE },
541
542 { "MIPS, N32 MIPS32", RPMFC_ELFMIPSN32|RPMFC_INCLUDE },
543 { "ELF 32-bit", RPMFC_ELF32|RPMFC_INCLUDE },
544 { "ELF 64-bit", RPMFC_ELF64|RPMFC_INCLUDE },
545
546 { " script", RPMFC_SCRIPT },
547 { " text", RPMFC_TEXT },
548 { " document", RPMFC_DOCUMENT },
549
550 { " compressed", RPMFC_COMPRESSED },
551
552 { "troff or preprocessor input", RPMFC_MANPAGE|RPMFC_INCLUDE },
553 { "GNU Info", RPMFC_MANPAGE|RPMFC_INCLUDE },
554
555 { "perl script text", RPMFC_PERL|RPMFC_INCLUDE },
556 { "Perl5 module source text", RPMFC_PERL|RPMFC_MODULE|RPMFC_INCLUDE },
557
558 { "PHP script text", RPMFC_PHP|RPMFC_INCLUDE },
559
560 /* XXX "a /usr/bin/python -t script text executable" */
561 /* XXX "python 2.3 byte-compiled" */
562 { " /usr/bin/python", RPMFC_PYTHON|RPMFC_INCLUDE },
563 { "python ", RPMFC_PYTHON|RPMFC_INCLUDE },
564
565 { "libtool library ", RPMFC_LIBTOOL|RPMFC_INCLUDE },
566 { "pkgconfig ", RPMFC_PKGCONFIG|RPMFC_INCLUDE },
567
568 { "Bourne ", RPMFC_BOURNE|RPMFC_INCLUDE },
569 { "Bourne-Again ", RPMFC_BOURNE|RPMFC_INCLUDE },
570
571 { "Java ", RPMFC_JAVA|RPMFC_INCLUDE },
572
573 { "Mono/.Net assembly", RPMFC_MONO|RPMFC_INCLUDE },
574
575 { "current ar archive", RPMFC_STATIC|RPMFC_LIBRARY|RPMFC_ARCHIVE|RPMFC_INCLUDE },
576
577 { "Zip archive data", RPMFC_COMPRESSED|RPMFC_ARCHIVE|RPMFC_INCLUDE },
578 { "tar archive", RPMFC_ARCHIVE|RPMFC_INCLUDE },
579 { "cpio archive", RPMFC_ARCHIVE|RPMFC_INCLUDE },
580 { "RPM v3", RPMFC_ARCHIVE|RPMFC_INCLUDE },
581 { "RPM v4", RPMFC_ARCHIVE|RPMFC_INCLUDE },
582
583 { " image", RPMFC_IMAGE|RPMFC_INCLUDE },
584 { " font", RPMFC_FONT|RPMFC_INCLUDE },
585 { " Font", RPMFC_FONT|RPMFC_INCLUDE },
586
587 { " commands", RPMFC_SCRIPT|RPMFC_INCLUDE },
588 { " script", RPMFC_SCRIPT|RPMFC_INCLUDE },
589
590 { "empty", RPMFC_WHITE|RPMFC_INCLUDE },
591
592 { "HTML", RPMFC_WHITE|RPMFC_INCLUDE },
593 { "SGML", RPMFC_WHITE|RPMFC_INCLUDE },
594 { "XML", RPMFC_WHITE|RPMFC_INCLUDE },
595
596 { " program text", RPMFC_WHITE|RPMFC_INCLUDE },
597 { " source", RPMFC_WHITE|RPMFC_INCLUDE },
598 { "GLS_BINARY_LSB_FIRST", RPMFC_WHITE|RPMFC_INCLUDE },
599 { " DB ", RPMFC_WHITE|RPMFC_INCLUDE },
600
601 { "ASCII English text", RPMFC_WHITE|RPMFC_INCLUDE },
602 { "ASCII text", RPMFC_WHITE|RPMFC_INCLUDE },
603 { "ISO-8859 text", RPMFC_WHITE|RPMFC_INCLUDE },
604
605 { "symbolic link to", RPMFC_SYMLINK },
606 { "socket", RPMFC_DEVICE },
607 { "special", RPMFC_DEVICE },
608
609 { "ASCII", RPMFC_WHITE },
610 { "ISO-8859", RPMFC_WHITE },
611
612 { "data", RPMFC_WHITE },
613
614 { "application", RPMFC_WHITE },
615 { "boot", RPMFC_WHITE },
616 { "catalog", RPMFC_WHITE },
617 { "code", RPMFC_WHITE },
618 { "file", RPMFC_WHITE },
619 { "format", RPMFC_WHITE },
620 { "message", RPMFC_WHITE },
621 { "program", RPMFC_WHITE },
622
623 { "broken symbolic link to ", RPMFC_WHITE|RPMFC_ERROR },
624 { "can't read", RPMFC_WHITE|RPMFC_ERROR },
625 { "can't stat", RPMFC_WHITE|RPMFC_ERROR },
626 { "executable, can't read", RPMFC_WHITE|RPMFC_ERROR },
627 { "core file", RPMFC_WHITE|RPMFC_ERROR },
628
629 { NULL, RPMFC_BLACK }
630 };
631 /*@=nullassign@*/
632
633 int rpmfcColoring(const char * fmstr)
634 {
635 rpmfcToken fct;
636 int fcolor = RPMFC_BLACK;
637
638 for (fct = rpmfcTokens; fct->token != NULL; fct++) {
639 if (strstr(fmstr, fct->token) == NULL)
640 continue;
641 fcolor |= fct->colors;
642 if (fcolor & RPMFC_INCLUDE)
643 return fcolor;
644 }
645 return fcolor;
646 }
647
648 void rpmfcPrint(const char * msg, rpmfc fc, FILE * fp)
649 {
650 int fcolor;
651 int ndx;
652 int cx;
653 int dx;
654 size_t fx;
655
656 unsigned nprovides;
657 unsigned nrequires;
658
659 if (fp == NULL) fp = stderr;
660
661 if (msg)
662 fprintf(fp, "===================================== %s\n", msg);
663
664 nprovides = rpmdsCount(fc->provides);
665 nrequires = rpmdsCount(fc->requires);
666
667 if (fc)
668 for (fx = 0; fx < fc->nfiles; fx++) {
669 assert(fx < fc->fcdictx->nvals);
670 cx = fc->fcdictx->vals[fx];
671 assert(fx < fc->fcolor->nvals);
672 fcolor = fc->fcolor->vals[fx];
673
674 fprintf(fp, "%3d %s", (int)fx, fc->fn[fx]);
675 if (fcolor != RPMFC_BLACK)
676 fprintf(fp, "\t0x%x", fc->fcolor->vals[fx]);
677 else
678 fprintf(fp, "\t%s", fc->cdict[cx]);
679 fprintf(fp, "\n");
680
681 if (fc->fddictx == NULL || fc->fddictn == NULL)
682 continue;
683
684 assert(fx < fc->fddictx->nvals);
685 dx = fc->fddictx->vals[fx];
686 assert(fx < fc->fddictn->nvals);
687 ndx = fc->fddictn->vals[fx];
688
689 while (ndx-- > 0) {
690 const char * depval;
691 unsigned char deptype;
692 unsigned ix;
693
694 ix = fc->ddictx->vals[dx++];
695 deptype = ((ix >> 24) & 0xff);
696 ix &= 0x00ffffff;
697 depval = NULL;
698 switch (deptype) {
699 default:
700 assert(depval != NULL);
701 /*@switchbreak@*/ break;
702 case 'P':
703 if (nprovides > 0) {
704 assert(ix < nprovides);
705 (void) rpmdsSetIx(fc->provides, ix-1);
706 if (rpmdsNext(fc->provides) >= 0)
707 depval = rpmdsDNEVR(fc->provides);
708 }
709 /*@switchbreak@*/ break;
710 case 'R':
711 if (nrequires > 0) {
712 assert(ix < nrequires);
713 (void) rpmdsSetIx(fc->requires, ix-1);
714 if (rpmdsNext(fc->requires) >= 0)
715 depval = rpmdsDNEVR(fc->requires);
716 }
717 /*@switchbreak@*/ break;
718 }
719 if (depval)
720 fprintf(fp, "\t%s\n", depval);
721 }
722 }
723 }
724
730 static int rpmfcSCRIPT(rpmfc fc)
731 /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
732 /*@modifies fc, rpmGlobalMacroContext, fileSystem, internalState @*/
733 {
734 const char * fn = fc->fn[fc->ix];
735 const char * bn;
736 rpmds ds;
737 char buf[BUFSIZ];
738 FILE * fp;
739 char * s, * se;
740 int i;
741 int is_executable;
742 int xx;
743
744 /* Extract dependencies only from files with executable bit set. */
745 { struct stat sb, * st = &sb;
746 if (stat(fn, st) != 0)
747 return -1;
748 is_executable = (int)(st->st_mode & (S_IXUSR|S_IXGRP|S_IXOTH));
749 }
750
751 fp = fopen(fn, "r");
752 if (fp == NULL || ferror(fp)) {
753 if (fp) (void) fclose(fp);
754 return -1;
755 }
756
757 /* Look for #! interpreter in first 10 lines. */
758 for (i = 0; i < 10; i++) {
759
760 s = fgets(buf, sizeof(buf) - 1, fp);
761 if (s == NULL || ferror(fp) || feof(fp))
762 break;
763 s[sizeof(buf)-1] = '\0';
764 if (!(s[0] == '#' && s[1] == '!'))
765 continue;
766 s += 2;
767
768 while (*s && strchr(" \t\n\r", *s) != NULL)
769 s++;
770 if (*s == '\0')
771 continue;
772 if (*s != '/')
773 continue;
774
775 for (se = s+1; *se; se++) {
776 if (strchr(" \t\n\r", *se) != NULL)
777 /*@innerbreak@*/ break;
778 }
779 *se = '\0';
780 se++;
781
782 if (!_filter_values
783 || (!fc->skipReq
784 && !rpmfcMatchRegexps(fc->Rmires, fc->Rnmire, s, 'R')))
785 if (is_executable) {
786 /* Add to package requires. */
787 ds = rpmdsSingle(RPMTAG_REQUIRENAME, s, "", RPMSENSE_FIND_REQUIRES);
788 xx = rpmdsMerge(&fc->requires, ds);
789
790 /* Add to file requires. */
791 xx = rpmfcSaveArg(&fc->ddict, rpmfcFileDep(se, fc->ix, ds));
792
793 (void)rpmdsFree(ds);
794 ds = NULL;
795 }
796
797 /* Set color based on interpreter name. */
798 /* XXX magic token should have already done this?!? */
799 /*@-moduncon@*/
800 bn = basename(s);
801 /*@=moduncon@*/
802 if (!strcmp(bn, "perl"))
803 fc->fcolor->vals[fc->ix] |= RPMFC_PERL;
804 else if (!strncmp(bn, "python", sizeof("python")-1))
805 fc->fcolor->vals[fc->ix] |= RPMFC_PYTHON;
806 else if (!strncmp(bn, "php", sizeof("php")-1))
807 fc->fcolor->vals[fc->ix] |= RPMFC_PHP;
808
809 break;
810 }
811
812 (void) fclose(fp);
813
814 if (fc->fcolor->vals[fc->ix] & RPMFC_PERL) {
815 if (strncmp(fn, "/usr/share/doc/", sizeof("/usr/share/doc/")-1)) {
816 if (fc->fcolor->vals[fc->ix] & RPMFC_MODULE)
817 xx = rpmfcHelper(fc, 'P', "perl");
818 if (is_executable || (fc->fcolor->vals[fc->ix] & RPMFC_MODULE))
819 xx = rpmfcHelper(fc, 'R', "perl");
820 }
821 } else
822 if (fc->fcolor->vals[fc->ix] & RPMFC_PYTHON) {
823 xx = rpmfcHelper(fc, 'P', "python");
824 #ifdef NOTYET
825 if (is_executable)
826 #endif
827 xx = rpmfcHelper(fc, 'R', "python");
828 } else
829 if (fc->fcolor->vals[fc->ix] & RPMFC_LIBTOOL) {
830 xx = rpmfcHelper(fc, 'P', "libtool");
831 #ifdef NOTYET
832 if (is_executable)
833 #endif
834 xx = rpmfcHelper(fc, 'R', "libtool");
835 } else
836 if (fc->fcolor->vals[fc->ix] & RPMFC_PKGCONFIG) {
837 xx = rpmfcHelper(fc, 'P', "pkgconfig");
838 #ifdef NOTYET
839 if (is_executable)
840 #endif
841 xx = rpmfcHelper(fc, 'R', "pkgconfig");
842 } else
843 if (fc->fcolor->vals[fc->ix] & RPMFC_BOURNE) {
844 #ifdef NOTYET
845 xx = rpmfcHelper(fc, 'P', "executable");
846 #endif
847 if (is_executable)
848 xx = rpmfcHelper(fc, 'R', "executable");
849 } else
850 if (fc->fcolor->vals[fc->ix] & RPMFC_PHP) {
851 xx = rpmfcHelper(fc, 'P', "php");
852 if (is_executable)
853 xx = rpmfcHelper(fc, 'R', "php");
854 } else
855 if (fc->fcolor->vals[fc->ix] & RPMFC_MONO) {
856 xx = rpmfcHelper(fc, 'P', "mono");
857 if (is_executable)
858 xx = rpmfcHelper(fc, 'R', "mono");
859 }
860 return 0;
861 }
862
869 static int rpmfcMergePR(void * context, rpmds ds)
870 /*@globals fileSystem, internalState @*/
871 /*@modifies ds, fileSystem, internalState @*/
872 {
873 rpmfc fc = context;
874 char buf[BUFSIZ];
875 int rc = 0;
876
877 if (_rpmfc_debug < 0)
878 fprintf(stderr, "*** rpmfcMergePR(%p, %p) %s\n", context, ds, tagName(rpmdsTagN(ds)));
879 switch(rpmdsTagN(ds)) {
880 default:
881 rc = -1;
882 break;
883 case RPMTAG_PROVIDENAME:
884 if (!_filter_values
885 || (!fc->skipProv
886 && !rpmfcMatchRegexps(fc->Pmires, fc->Pnmire, ds->N[0], 'P')))
887 {
888 /* Add to package provides. */
889 rc = rpmdsMerge(&fc->provides, ds);
890
891 /* Add to file dependencies. */
892 buf[0] = '\0';
893 rc = rpmfcSaveArg(&fc->ddict, rpmfcFileDep(buf, fc->ix, ds));
894 }
895 break;
896 case RPMTAG_REQUIRENAME:
897 if (!_filter_values
898 || (!fc->skipReq
899 && !rpmfcMatchRegexps(fc->Rmires, fc->Rnmire, ds->N[0], 'R')))
900 {
901 /* Add to package requires. */
902 rc = rpmdsMerge(&fc->requires, ds);
903
904 /* Add to file dependencies. */
905 buf[0] = '\0';
906 rc = rpmfcSaveArg(&fc->ddict, rpmfcFileDep(buf, fc->ix, ds));
907 }
908 break;
909 }
910 return rc;
911 }
912
918 static int rpmfcELF(rpmfc fc)
919 /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
920 /*@modifies rpmGlobalMacroContext, fileSystem, internalState @*/
921 {
922 const char * fn = fc->fn[fc->ix];
923 int flags = 0;
924
925 if (fc->skipProv)
926 flags |= RPMELF_FLAG_SKIPPROVIDES;
927 if (fc->skipReq)
928 flags |= RPMELF_FLAG_SKIPREQUIRES;
929
930 return rpmdsELF(fn, flags, rpmfcMergePR, fc);
931 }
932
933 typedef struct rpmfcApplyTbl_s {
934 int (*func) (rpmfc fc);
935 int colormask;
936 } * rpmfcApplyTbl;
937
941 /*@-nullassign@*/
942 /*@unchecked@*/
943 static struct rpmfcApplyTbl_s rpmfcApplyTable[] = {
944 { rpmfcELF, RPMFC_ELF },
945 { rpmfcSCRIPT, (RPMFC_SCRIPT|RPMFC_PERL|RPMFC_PYTHON|RPMFC_LIBTOOL|RPMFC_PKGCONFIG|RPMFC_BOURNE|RPMFC_JAVA|RPMFC_PHP|RPMFC_MONO) },
946 { NULL, 0 }
947 };
948 /*@=nullassign@*/
949
950 rpmRC rpmfcApply(rpmfc fc)
951 {
952 rpmfcApplyTbl fcat;
953 const char * s;
954 char * se;
955 rpmds ds;
956 const char * fn;
957 const char * N;
958 const char * EVR;
959 evrFlags Flags;
960 unsigned char deptype;
961 int nddict;
962 int previx;
963 unsigned int val;
964 int dix;
965 int ix;
966 int i;
967 int xx;
968 int skipping;
969
970 miRE mire;
971 int skipProv = fc->skipProv;
972 int skipReq = fc->skipReq;
973 int j;
974
975 if (_filter_execs) {
976 fc->Pnmire = 0;
977 fc->PFnmire = 0;
978 fc->Rnmire = 0;
979 fc->RFnmire = 0;
980
981 fc->PFmires = rpmfcExpandRegexps("%{__noautoprovfiles}", &fc->PFnmire);
982 fc->RFmires = rpmfcExpandRegexps("%{__noautoreqfiles}", &fc->RFnmire);
983 fc->Pmires = rpmfcExpandRegexps("%{__noautoprov}", &fc->Pnmire);
984 fc->Rmires = rpmfcExpandRegexps("%{__noautoreq}", &fc->Rnmire);
985 rpmlog(RPMLOG_DEBUG, D_("%i _noautoprov patterns.\n"), fc->Pnmire);
986 rpmlog(RPMLOG_DEBUG, D_("%i _noautoreq patterns.\n"), fc->Rnmire);
987 }
988
989 /* Make sure something didn't go wrong previously! */
990 assert(fc->fn != NULL);
991 /* Generate package and per-file dependencies. */
992 for (fc->ix = 0; fc->fn[fc->ix] != NULL; fc->ix++) {
993
994 /* XXX Insure that /usr/lib{,64}/python files are marked RPMFC_PYTHON */
995 /* XXX HACK: classification by path is intrinsically stupid. */
996 { fn = strstr(fc->fn[fc->ix], "/usr/lib");
997 if (fn) {
998 fn += sizeof("/usr/lib")-1;
999 if ((fn[0] == '3' && fn[1] == '2') ||
1000 (fn[0] == '6' && fn[1] == '4'))
1001 fn += 2;
1002 if (!strncmp(fn, "/python", sizeof("/python")-1))
1003 fc->fcolor->vals[fc->ix] |= RPMFC_PYTHON;
1004 }
1005 }
1006
1007 if (fc->fcolor->vals[fc->ix])
1008 for (fcat = rpmfcApplyTable; fcat->func != NULL; fcat++) {
1009 if (!(fc->fcolor->vals[fc->ix] & fcat->colormask))
1010 /*@innercontinue@*/ continue;
1011
1012 if (_filter_execs) {
1013 fc->skipProv = skipProv;
1014 fc->skipReq = skipReq;
1015 if ((mire = fc->PFmires) != NULL)
1016 for (j = 0; j < fc->PFnmire; j++, mire++) {
1017 fn = fc->fn[fc->ix] + fc->brlen;
1018 if ((xx = mireRegexec(mire, fn, 0)) < 0)
1019 /*@innercontinue@*/ continue;
1020 rpmlog(RPMLOG_NOTICE, _("skipping %s provides detection\n"),
1021 fn);
1022 fc->skipProv = 1;
1023 /*@innerbreak@*/ break;
1024 }
1025 if ((mire = fc->RFmires) != NULL)
1026 for (j = 0; j < fc->RFnmire; j++, mire++) {
1027 fn = fc->fn[fc->ix] + fc->brlen;
1028 if ((xx = mireRegexec(mire, fn, 0)) < 0)
1029 /*@innercontinue@*/ continue;
1030 rpmlog(RPMLOG_NOTICE, _("skipping %s requires detection\n"),
1031 fn);
1032 fc->skipReq = 1;
1033 /*@innerbreak@*/ break;
1034 }
1035 }
1036
1037 xx = (*fcat->func) (fc);
1038 }
1039 }
1040
1041 if (_filter_execs) {
1042 fc->PFmires = rpmfcFreeRegexps(fc->PFmires, fc->PFnmire);
1043 fc->RFmires = rpmfcFreeRegexps(fc->RFmires, fc->RFnmire);
1044 fc->Pmires = rpmfcFreeRegexps(fc->Pmires, fc->Pnmire);
1045 fc->Rmires = rpmfcFreeRegexps(fc->Rmires, fc->Rnmire);
1046 }
1047 fc->skipProv = skipProv;
1048 fc->skipReq = skipReq;
1049
1050 /* Generate per-file indices into package dependencies. */
1051 nddict = argvCount(fc->ddict);
1052 previx = -1;
1053 for (i = 0; i < nddict; i++) {
1054 s = fc->ddict[i];
1055
1056 /* Parse out (file#,deptype,N,EVR,Flags) */
1057 ix = strtol(s, &se, 10);
1058 assert(se != NULL);
1059 deptype = *se++;
1060 se++;
1061 N = se;
1062 while (*se && *se != ' ')
1063 se++;
1064 *se++ = '\0';
1065 EVR = se;
1066 while (*se && *se != ' ')
1067 se++;
1068 *se++ = '\0';
1069 Flags = strtol(se, NULL, 16);
1070
1071 dix = -1;
1072 skipping = 0;
1073 switch (deptype) {
1074 default:
1075 /*@switchbreak@*/ break;
1076 case 'P':
1077 skipping = fc->skipProv;
1078 ds = rpmdsSingle(RPMTAG_PROVIDENAME, N, EVR, Flags);
1079 dix = rpmdsFind(fc->provides, ds);
1080 (void)rpmdsFree(ds);
1081 ds = NULL;
1082 /*@switchbreak@*/ break;
1083 case 'R':
1084 skipping = fc->skipReq;
1085 ds = rpmdsSingle(RPMTAG_REQUIRENAME, N, EVR, Flags);
1086 dix = rpmdsFind(fc->requires, ds);
1087 (void)rpmdsFree(ds);
1088 ds = NULL;
1089 /*@switchbreak@*/ break;
1090 }
1091
1092 /* XXX assertion incorrect while generating -debuginfo deps. */
1093 #if 0
1094 assert(dix >= 0);
1095 #else
1096 if (dix < 0)
1097 continue;
1098 #endif
1099
1100 val = (deptype << 24) | (dix & 0x00ffffff);
1101 xx = argiAdd(&fc->ddictx, -1, val);
1102
1103 if (previx != ix) {
1104 previx = ix;
1105 xx = argiAdd(&fc->fddictx, ix, argiCount(fc->ddictx)-1);
1106 }
1107 if (fc->fddictn && fc->fddictn->vals && !skipping)
1108 fc->fddictn->vals[ix]++;
1109 }
1110
1111 return RPMRC_OK;
1112 }
1113
1114 rpmRC rpmfcClassify(rpmfc fc, ARGV_t argv, rpmuint16_t * fmode)
1115 {
1116 ARGV_t fcav = NULL;
1117 ARGV_t dav;
1118 rpmmg mg = NULL;
1119 const char * s, * se;
1120 size_t slen;
1121 int fcolor;
1122 int xx;
1123 const char * magicfile = NULL;
1124
1125 if (fc == NULL || argv == NULL)
1126 return RPMRC_OK;
1127
1128 magicfile = rpmExpand("%{?_rpmfc_magic_path}", NULL);
1129 if (magicfile == NULL || *magicfile == '\0')
1130 magicfile = _free(magicfile);
1131 mg = rpmmgNew(magicfile, 0);
1132 assert(mg != NULL); /* XXX figger a proper return path. */
1133
1134 fc->nfiles = argvCount(argv);
1135
1136 /* Initialize the per-file dictionary indices. */
1137 xx = argiAdd(&fc->fddictx, fc->nfiles-1, 0);
1138 xx = argiAdd(&fc->fddictn, fc->nfiles-1, 0);
1139
1140 /* Build (sorted) file class dictionary. */
1141 xx = argvAdd(&fc->cdict, "");
1142 xx = argvAdd(&fc->cdict, "directory");
1143
1144 for (fc->ix = 0; fc->ix < fc->nfiles; fc->ix++) {
1145 const char * ftype;
1146 int freeftype;
1147 rpmuint16_t mode = (fmode ? fmode[fc->ix] : 0);
1148 int urltype;
1149
1150 ftype = ""; freeftype = 0;
1151 urltype = urlPath(argv[fc->ix], &s);
1152 assert(s != NULL && *s == '/');
1153 slen = strlen(s);
1154
1155 switch (mode & S_IFMT) {
1156 case S_IFCHR: ftype = "character special"; /*@switchbreak@*/ break;
1157 case S_IFBLK: ftype = "block special"; /*@switchbreak@*/ break;
1158 #if defined(S_IFIFO)
1159 case S_IFIFO: ftype = "fifo (named pipe)"; /*@switchbreak@*/ break;
1160 #endif
1161 #if defined(S_IFSOCK)
1162 /*@-unrecog@*/
1163 case S_IFSOCK: ftype = "socket"; /*@switchbreak@*/ break;
1164 /*@=unrecog@*/
1165 #endif
1166 case S_IFDIR:
1167 case S_IFLNK:
1168 case S_IFREG:
1169 default:
1170
1171 #define _suffix(_s, _x) \
1172 (slen >= sizeof(_x) && !strcmp((_s)+slen-(sizeof(_x)-1), (_x)))
1173
1174 /* XXX all files with extension ".pm" are perl modules for now. */
1175 if (_suffix(s, ".pm"))
1176 ftype = "Perl5 module source text";
1177
1178 /* XXX all files with extension ".jar" are java archives for now. */
1179 else if (_suffix(s, ".jar"))
1180 ftype = "Java archive file";
1181
1182 /* XXX all files with extension ".class" are java class files for now. */
1183 else if (_suffix(s, ".class"))
1184 ftype = "Java class file";
1185
1186 /* XXX all files with extension ".la" are libtool for now. */
1187 else if (_suffix(s, ".la"))
1188 ftype = "libtool library file";
1189
1190 /* XXX all files with extension ".pc" are pkgconfig for now. */
1191 else if (_suffix(s, ".pc"))
1192 ftype = "pkgconfig file";
1193
1194 /* XXX all files with extension ".php" are PHP for now. */
1195 else if (_suffix(s, ".php"))
1196 ftype = "PHP script text";
1197
1198 /* XXX skip all files in /dev/ which are (or should be) %dev dummies. */
1199 else if (slen >= fc->brlen+sizeof("/dev/") && !strncmp(s+fc->brlen, "/dev/", sizeof("/dev/")-1))
1200 ftype = "";
1201 else if (magicfile) {
1202 ftype = rpmmgFile(mg, s);
1203 assert(ftype != NULL); /* XXX never happens, rpmmgFile() returns "" */
1204 freeftype = 1;
1205 }
1206 /*@switchbreak@*/ break;
1207 }
1208
1209 se = ftype;
1210
1211 if (_rpmfc_debug) /* XXX noisy */
1212 rpmlog(RPMLOG_DEBUG, "%s: %s\n", s, se);
1213
1214 /* Save the path. */
1215 xx = argvAdd(&fc->fn, s);
1216
1217 /* Save the file type string. */
1218 xx = argvAdd(&fcav, se);
1219
1220 /* Add (filtered) entry to sorted class dictionary. */
1221 fcolor = rpmfcColoring(se);
1222 xx = argiAdd(&fc->fcolor, (int)fc->ix, fcolor);
1223
1224 if (fcolor != RPMFC_WHITE && (fcolor & RPMFC_INCLUDE))
1225 xx = rpmfcSaveArg(&fc->cdict, se);
1226
1227 /*@-modobserver -observertrans @*/ /* XXX mixed types in variable */
1228 if (freeftype)
1229 ftype = _free(ftype);
1230 /*@=modobserver =observertrans @*/
1231 }
1232
1233 /* Build per-file class index array. */
1234 fc->fknown = 0;
1235 for (fc->ix = 0; fc->ix < fc->nfiles; fc->ix++) {
1236 se = fcav[fc->ix];
1237 assert(se != NULL);
1238
1239 dav = argvSearch(fc->cdict, se, NULL);
1240 if (dav) {
1241 xx = argiAdd(&fc->fcdictx, (int)fc->ix, (dav - fc->cdict));
1242 fc->fknown++;
1243 } else {
1244 xx = argiAdd(&fc->fcdictx, (int)fc->ix, 0);
1245 fc->fwhite++;
1246 }
1247 }
1248
1249 fcav = argvFree(fcav);
1250
1251 mg = rpmmgFree(mg);
1252 rpmlog(RPMLOG_DEBUG,
1253 D_("categorized %d files into %d classes (using %s).\n"),
1254 fc->nfiles, argvCount(fc->cdict), magicfile);
1255 magicfile = _free(magicfile);
1256
1257 return RPMRC_OK;
1258 }
1259
1262 typedef struct DepMsg_s * DepMsg_t;
1263
1266 struct DepMsg_s {
1267 /*@observer@*/ /*@null@*/
1268 const char * msg;
1269 /*@observer@*/
1270 const char * argv[4];
1271 rpmTag ntag;
1272 rpmTag vtag;
1273 rpmTag ftag;
1274 int mask;
1275 int xor;
1276 };
1277
1280 /*@-nullassign@*/
1281 /*@unchecked@*/
1282 static struct DepMsg_s depMsgs[] = {
1283 { "Provides", { "%{?__find_provides}", NULL, NULL, NULL },
1284 RPMTAG_PROVIDENAME, RPMTAG_PROVIDEVERSION, RPMTAG_PROVIDEFLAGS,
1285 0, -1 },
1286 { "Requires(interp)", { NULL, "interp", NULL, NULL },
1287 RPMTAG_REQUIRENAME, RPMTAG_REQUIREVERSION, RPMTAG_REQUIREFLAGS,
1288 _notpre(RPMSENSE_INTERP), 0 },
1289 { "Requires(rpmlib)", { NULL, "rpmlib", NULL, NULL },
1290 -1, -1, RPMTAG_REQUIREFLAGS,
1291 _notpre(RPMSENSE_RPMLIB), 0 },
1292 { "Requires(verify)", { NULL, "verify", NULL, NULL },
1293 -1, -1, RPMTAG_REQUIREFLAGS,
1294 RPMSENSE_SCRIPT_VERIFY, 0 },
1295 { "Requires(pre)", { NULL, "pre", NULL, NULL },
1296 -1, -1, RPMTAG_REQUIREFLAGS,
1297 _notpre(RPMSENSE_SCRIPT_PRE), 0 },
1298 { "Requires(post)", { NULL, "post", NULL, NULL },
1299 -1, -1, RPMTAG_REQUIREFLAGS,
1300 _notpre(RPMSENSE_SCRIPT_POST), 0 },
1301 { "Requires(preun)", { NULL, "preun", NULL, NULL },
1302 -1, -1, RPMTAG_REQUIREFLAGS,
1303 _notpre(RPMSENSE_SCRIPT_PREUN), 0 },
1304 { "Requires(postun)", { NULL, "postun", NULL, NULL },
1305 -1, -1, RPMTAG_REQUIREFLAGS,
1306 _notpre(RPMSENSE_SCRIPT_POSTUN), 0 },
1307 { "Requires", { "%{?__find_requires}", NULL, NULL, NULL },
1308 -1, -1, RPMTAG_REQUIREFLAGS, /* XXX inherit name/version arrays */
1309 RPMSENSE_FIND_REQUIRES|RPMSENSE_TRIGGERIN|RPMSENSE_TRIGGERUN|RPMSENSE_TRIGGERPOSTUN|RPMSENSE_TRIGGERPREIN, 0 },
1310 { "Conflicts", { "%{?__find_conflicts}", NULL, NULL, NULL },
1311 RPMTAG_CONFLICTNAME, RPMTAG_CONFLICTVERSION, RPMTAG_CONFLICTFLAGS,
1312 0, -1 },
1313 { "Obsoletes", { "%{?__find_obsoletes}", NULL, NULL, NULL },
1314 RPMTAG_OBSOLETENAME, RPMTAG_OBSOLETEVERSION, RPMTAG_OBSOLETEFLAGS,
1315 0, -1 },
1316 { NULL, { NULL, NULL, NULL, NULL }, 0, 0, 0, 0, 0 }
1317 };
1318 /*@=nullassign@*/
1319
1320 /*@unchecked@*/
1321 static DepMsg_t DepMsgs = depMsgs;
1322
1327 static void printDeps(Header h)
1328 /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
1329 /*@modifies h, rpmGlobalMacroContext, fileSystem, internalState @*/
1330 {
1331 DepMsg_t dm;
1332 rpmds ds = NULL;
1333 int flags = 0x2; /* XXX no filtering, !scareMem */
1334 const char * DNEVR;
1335 evrFlags Flags;
1336 int bingo = 0;
1337
1338 for (dm = DepMsgs; dm->msg != NULL; dm++) {
1339 if ((int)dm->ntag != -1) {
1340 (void)rpmdsFree(ds);
1341 ds = NULL;
1342 ds = rpmdsNew(h, dm->ntag, flags);
1343 }
1344 if (dm->ftag == 0)
1345 continue;
1346
1347 ds = rpmdsInit(ds);
1348 if (ds == NULL)
1349 continue; /* XXX can't happen */
1350
1351 bingo = 0;
1352 while (rpmdsNext(ds) >= 0) {
1353
1354 Flags = rpmdsFlags(ds);
1355
1356 if (!((Flags & dm->mask) ^ dm->xor))
1357 /*@innercontinue@*/ continue;
1358 if (bingo == 0) {
1359 rpmlog(RPMLOG_NOTICE, "%s:", (dm->msg ? dm->msg : ""));
1360 bingo = 1;
1361 }
1362 if ((DNEVR = rpmdsDNEVR(ds)) == NULL)
1363 /*@innercontinue@*/ continue; /* XXX can't happen */
1364 rpmlog(RPMLOG_NOTICE, " %s", DNEVR+2);
1365 }
1366 if (bingo)
1367 rpmlog(RPMLOG_NOTICE, "\n");
1368 }
1369 (void)rpmdsFree(ds);
1370 ds = NULL;
1371 }
1372
1375 static rpmRC rpmfcGenerateDependsHelper(const Spec spec, Package pkg, rpmfi fi)
1376 /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
1377 /*@modifies fi, rpmGlobalMacroContext, fileSystem, internalState @*/
1378 {
1379 rpmiob iob_stdin;
1380 rpmiob iob_stdout;
1381 DepMsg_t dm;
1382 int failnonzero = 0;
1383 rpmRC rc = RPMRC_OK;
1384
1385 /*
1386 * Create file manifest buffer to deliver to dependency finder.
1387 */
1388 iob_stdin = rpmiobNew(0);
1389 fi = rpmfiInit(fi, 0);
1390 if (fi != NULL)
1391 while (rpmfiNext(fi) >= 0)
1392 iob_stdin = rpmiobAppend(iob_stdin, rpmfiFN(fi), 1);
1393
1394 for (dm = DepMsgs; dm->msg != NULL; dm++) {
1395 rpmTag tag;
1396 rpmsenseFlags tagflags;
1397 char * s;
1398 int xx;
1399
1400 tag = (dm->ftag > 0) ? dm->ftag : dm->ntag;
1401 tagflags = 0;
1402 s = NULL;
1403
1404 switch(tag) {
1405 case RPMTAG_PROVIDEFLAGS:
1406 if (!pkg->autoProv)
1407 continue;
1408 failnonzero = 1;
1409 tagflags = RPMSENSE_FIND_PROVIDES;
1410 /*@switchbreak@*/ break;
1411 case RPMTAG_REQUIREFLAGS:
1412 if (!pkg->autoReq)
1413 continue;
1414 failnonzero = 0;
1415 tagflags = RPMSENSE_FIND_REQUIRES;
1416 /*@switchbreak@*/ break;
1417 default:
1418 continue;
1419 /*@notreached@*/ /*@switchbreak@*/ break;
1420 }
1421
1422 xx = rpmfcExec(dm->argv, iob_stdin, &iob_stdout, failnonzero);
1423 if (xx == -1)
1424 continue;
1425
1426 s = rpmExpand(dm->argv[0], NULL);
1427 rpmlog(RPMLOG_NOTICE, _("Finding %s: %s\n"), dm->msg,
1428 (s ? s : ""));
1429 s = _free(s);
1430
1431 if (iob_stdout == NULL) {
1432 rpmlog(RPMLOG_ERR, _("Failed to find %s:\n"), dm->msg);
1433 rc = RPMRC_FAIL;
1434 break;
1435 }
1436
1437 /* Parse dependencies into header */
1438 if (spec->_parseRCPOT)
1439 rc = spec->_parseRCPOT(spec, pkg, rpmiobStr(iob_stdout), tag,
1440 0, tagflags);
1441 iob_stdout = rpmiobFree(iob_stdout);
1442
1443 if (rc) {
1444 rpmlog(RPMLOG_ERR, _("Failed to find %s:\n"), dm->msg);
1445 break;
1446 }
1447 }
1448
1449 iob_stdin = rpmiobFree(iob_stdin);
1450
1451 return rc;
1452 }
1453
1456 /*@-nullassign@*/
1457 /*@unchecked@*/
1458 static struct DepMsg_s scriptMsgs[] = {
1459 { "Requires(pre)", { "%{?__scriptlet_requires}", NULL, NULL, NULL },
1460 RPMTAG_PREINPROG, RPMTAG_PREIN, RPMTAG_REQUIREFLAGS,
1461 RPMSENSE_SCRIPT_PRE, 0 },
1462 { "Requires(post)", { "%{?__scriptlet_requires}", NULL, NULL, NULL },
1463 RPMTAG_POSTINPROG, RPMTAG_POSTIN, RPMTAG_REQUIREFLAGS,
1464 RPMSENSE_SCRIPT_POST, 0 },
1465 { "Requires(preun)", { "%{?__scriptlet_requires}", NULL, NULL, NULL },
1466 RPMTAG_PREUNPROG, RPMTAG_PREUN, RPMTAG_REQUIREFLAGS,
1467 RPMSENSE_SCRIPT_PREUN, 0 },
1468 { "Requires(postun)", { "%{?__scriptlet_requires}", NULL, NULL, NULL },
1469 RPMTAG_POSTUNPROG, RPMTAG_POSTUN, RPMTAG_REQUIREFLAGS,
1470 RPMSENSE_SCRIPT_POSTUN, 0 },
1471 { NULL, { NULL, NULL, NULL, NULL }, 0, 0, 0, 0, 0 }
1472 };
1473 /*@=nullassign@*/
1474
1475 /*@unchecked@*/
1476 static DepMsg_t ScriptMsgs = scriptMsgs;
1477
1480 static int rpmfcGenerateScriptletDeps(const Spec spec, Package pkg)
1481 /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
1482 /*@modifies rpmGlobalMacroContext, fileSystem, internalState @*/
1483 {
1484 HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he));
1485 rpmiob iob_stdin = rpmiobNew(0);
1486 rpmiob iob_stdout = NULL;
1487 DepMsg_t dm;
1488 int failnonzero = 0;
1489 int rc = 0;
1490 int xx;
1491
1492 for (dm = ScriptMsgs; dm->msg != NULL; dm++) {
1493 int tag, tagflags;
1494 char * s;
1495
1496 tag = dm->ftag;
1497 tagflags = RPMSENSE_FIND_REQUIRES | dm->mask;
1498
1499 /* Retrieve scriptlet interpreter. */
1500 he->tag = dm->ntag;
1501 xx = headerGet(pkg->header, he, 0);
1502 if (!xx || he->p.str == NULL)
1503 continue;
1504 xx = strcmp(he->p.str, "/bin/sh") && strcmp(he->p.str, "/bin/bash");
1505 he->p.ptr = _free(he->p.ptr);
1506 if (xx)
1507 continue;
1508
1509 /* Retrieve scriptlet body. */
1510 he->tag = dm->vtag;
1511 xx = headerGet(pkg->header, he, 0);
1512 if (!xx || he->p.str == NULL)
1513 continue;
1514 iob_stdin = rpmiobEmpty(iob_stdin);
1515 iob_stdin = rpmiobAppend(iob_stdin, he->p.str, 1);
1516 iob_stdin = rpmiobRTrim(iob_stdin);
1517 he->p.ptr = _free(he->p.ptr);
1518
1519 xx = rpmfcExec(dm->argv, iob_stdin, &iob_stdout, failnonzero);
1520 if (xx == -1)
1521 continue;
1522
1523 /* Parse dependencies into header */
1524 s = rpmiobStr(iob_stdout);
1525 if (s != NULL && *s != '\0') {
1526 char * se = s;
1527 /* XXX Convert "executable(/path/to/file)" to "/path/to/file". */
1528 while ((se = strstr(se, "executable(/")) != NULL) {
1529 /*@-modobserver@*/ /* FIX: rpmiobStr should not be observer */
1530 se = stpcpy(se, " ");
1531 *se = '/'; /* XXX stpcpy truncates the '/' */
1532 /*@=modobserver@*/
1533 se = strchr(se, ')');
1534 if (se == NULL)
1535 /*@innerbreak@*/ break;
1536 *se++ = ' ';
1537 }
1538 if (spec->_parseRCPOT)
1539 rc = spec->_parseRCPOT(spec, pkg, s, tag, 0, tagflags);
1540 }
1541 iob_stdout = rpmiobFree(iob_stdout);
1542
1543 }
1544
1545 iob_stdin = rpmiobFree(iob_stdin);
1546
1547 return rc;
1548 }
1549
1550 rpmRC rpmfcGenerateDepends(void * specp, void * pkgp)
1551 {
1552 HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he));
1553 const Spec spec = specp;
1554 Package pkg = pkgp;
1555 rpmfi fi = pkg->cpioList;
1556 rpmfc fc = NULL;
1557 rpmds ds;
1558 int flags = 0x2; /* XXX no filtering, !scareMem */
1559 ARGV_t av;
1560 rpmuint16_t * fmode;
1561 int ac = rpmfiFC(fi);
1562 char buf[BUFSIZ];
1563 const char * N;
1564 const char * EVR;
1565 int genConfigDeps, internaldeps;
1566 rpmRC rc = RPMRC_OK;
1567 int i;
1568 int xx;
1569
1570 /* Skip packages with no files. */
1571 if (ac <= 0)
1572 return RPMRC_OK;
1573
1574 /* Skip packages that have dependency generation disabled. */
1575 if (! (pkg->autoReq || pkg->autoProv))
1576 return RPMRC_OK;
1577
1578 /* If new-fangled dependency generation is disabled ... */
1579 internaldeps = rpmExpandNumeric("%{?_use_internal_dependency_generator}");
1580 if (internaldeps == 0) {
1581 /* ... then generate dependencies using %{__find_requires} et al. */
1582 rc = rpmfcGenerateDependsHelper(spec, pkg, fi);
1583 printDeps(pkg->header);
1584 return rc;
1585 }
1586
1587 /* Generate scriptlet Dependencies. */
1588 if (internaldeps > 1)
1589 xx = rpmfcGenerateScriptletDeps(spec, pkg);
1590
1591 /* Extract absolute file paths in argv format. */
1592 /* XXX TODO: should use argvFoo ... */
1593 av = xcalloc(ac+1, sizeof(*av));
1594 fmode = xcalloc(ac+1, sizeof(*fmode));
1595
1596 genConfigDeps = 0;
1597 fi = rpmfiInit(fi, 0);
1598 if (fi != NULL)
1599 while ((i = rpmfiNext(fi)) >= 0) {
1600 rpmfileAttrs fileAttrs;
1601
1602 /* Does package have any %config files? */
1603 fileAttrs = rpmfiFFlags(fi);
1604 genConfigDeps |= (fileAttrs & RPMFILE_CONFIG);
1605
1606 av[i] = xstrdup(rpmfiFN(fi));
1607 fmode[i] = rpmfiFMode(fi);
1608 }
1609 av[ac] = NULL;
1610
1611 fc = rpmfcNew();
1612 fc->skipProv = !pkg->autoProv;
1613 fc->skipReq = !pkg->autoReq;
1614 fc->tracked = 0;
1615
1616 { const char * buildRootURL;
1617 const char * buildRoot;
1618 buildRootURL = rpmGenPath(spec->rootURL, "%{?buildroot}", NULL);
1619 (void) urlPath(buildRootURL, &buildRoot);
1620 if (buildRoot && !strcmp(buildRoot, "/")) buildRoot = NULL;
1621 fc->brlen = (buildRoot ? strlen(buildRoot) : 0);
1622 buildRootURL = _free(buildRootURL);
1623 }
1624
1625 /* Copy (and delete) manually generated dependencies to dictionary. */
1626 if (!fc->skipProv) {
1627 ds = rpmdsNew(pkg->header, RPMTAG_PROVIDENAME, flags);
1628 xx = rpmdsMerge(&fc->provides, ds);
1629 (void)rpmdsFree(ds);
1630 ds = NULL;
1631 he->tag = RPMTAG_PROVIDENAME;
1632 xx = headerDel(pkg->header, he, 0);
1633 he->tag = RPMTAG_PROVIDEVERSION;
1634 xx = headerDel(pkg->header, he, 0);
1635 he->tag = RPMTAG_PROVIDEFLAGS;
1636 xx = headerDel(pkg->header, he, 0);
1637
1638 /* Add config dependency, Provides: config(N) = EVR */
1639 if (genConfigDeps) {
1640 N = rpmdsN(pkg->ds);
1641 assert(N != NULL);
1642 EVR = rpmdsEVR(pkg->ds);
1643 assert(EVR != NULL);
1644 sprintf(buf, "config(%s)", N);
1645 ds = rpmdsSingle(RPMTAG_PROVIDENAME, buf, EVR,
1646 (RPMSENSE_EQUAL|RPMSENSE_CONFIG));
1647 xx = rpmdsMerge(&fc->provides, ds);
1648 (void)rpmdsFree(ds);
1649 ds = NULL;
1650 }
1651 }
1652
1653 if (!fc->skipReq) {
1654 ds = rpmdsNew(pkg->header, RPMTAG_REQUIRENAME, flags);
1655 xx = rpmdsMerge(&fc->requires, ds);
1656 (void)rpmdsFree(ds);
1657 ds = NULL;
1658 he->tag = RPMTAG_REQUIRENAME;
1659 xx = headerDel(pkg->header, he, 0);
1660 he->tag = RPMTAG_REQUIREVERSION;
1661 xx = headerDel(pkg->header, he, 0);
1662 he->tag = RPMTAG_REQUIREFLAGS;
1663 xx = headerDel(pkg->header, he, 0);
1664
1665 /* Add config dependency, Requires: config(N) = EVR */
1666 if (genConfigDeps) {
1667 N = rpmdsN(pkg->ds);
1668 assert(N != NULL);
1669 EVR = rpmdsEVR(pkg->ds);
1670 assert(EVR != NULL);
1671 sprintf(buf, "config(%s)", N);
1672 ds = rpmdsSingle(RPMTAG_REQUIRENAME, buf, EVR,
1673 (RPMSENSE_EQUAL|RPMSENSE_CONFIG));
1674 xx = rpmdsMerge(&fc->requires, ds);
1675 (void)rpmdsFree(ds);
1676 ds = NULL;
1677 }
1678 }
1679
1680 /* Build file class dictionary. */
1681 xx = rpmfcClassify(fc, av, fmode);
1682
1683 /* Build file/package dependency dictionary. */
1684 xx = rpmfcApply(fc);
1685
1686 /* Add per-file colors(#files) */
1687 he->tag = RPMTAG_FILECOLORS;
1688 he->t = RPM_UINT32_TYPE;
1689 he->p.ui32p = argiData(fc->fcolor);
1690 he->c = argiCount(fc->fcolor);
1691 assert(ac == (int)he->c);
1692 if (he->p.ptr != NULL && he->c > 0) {
1693 rpmuint32_t * fcolors = he->p.ui32p;
1694
1695 /* XXX Make sure only primary (i.e. Elf32/Elf64) colors are added. */
1696 for (i = 0; i < (int)he->c; i++)
1697 fcolors[i] &= 0x0f;
1698
1699 xx = headerPut(pkg->header, he, 0);
1700 }
1701
1702 /* Add classes(#classes) */
1703 he->tag = RPMTAG_CLASSDICT;
1704 he->t = RPM_STRING_ARRAY_TYPE;
1705 he->p.argv = argvData(fc->cdict);
1706 he->c = argvCount(fc->cdict);
1707 if (he->p.ptr != NULL && he->c > 0) {
1708 xx = headerPut(pkg->header, he, 0);
1709 }
1710
1711 /* Add per-file classes(#files) */
1712 he->tag = RPMTAG_FILECLASS;
1713 he->t = RPM_UINT32_TYPE;
1714 he->p.ui32p = argiData(fc->fcdictx);
1715 he->c = argiCount(fc->fcdictx);
1716 assert(ac == (int)he->c);
1717 if (he->p.ptr != NULL && he->c > 0) {
1718 xx = headerPut(pkg->header, he, 0);
1719 }
1720
1721 /* Add Provides: */
1722 if (fc->provides != NULL && (he->c = rpmdsCount(fc->provides)) > 0
1723 && !fc->skipProv)
1724 {
1725 he->tag = RPMTAG_PROVIDENAME;
1726 he->t = RPM_STRING_ARRAY_TYPE;
1727 he->p.argv = fc->provides->N;
1728 xx = headerPut(pkg->header, he, 0);
1729
1730 /* XXX rpm prior to 3.0.2 did not always supply EVR and Flags. */
1731 /*@-nullpass@*/
1732 he->tag = RPMTAG_PROVIDEVERSION;
1733 he->t = RPM_STRING_ARRAY_TYPE;
1734 he->p.argv = fc->provides->EVR;
1735 assert(he->p.ptr != NULL);
1736 xx = headerPut(pkg->header, he, 0);
1737
1738 he->tag = RPMTAG_PROVIDEFLAGS;
1739 he->t = RPM_UINT32_TYPE;
1740 he->p.ui32p = (rpmuint32_t *) fc->provides->Flags;
1741 assert(he->p.ptr != NULL);
1742 xx = headerPut(pkg->header, he, 0);
1743 /*@=nullpass@*/
1744 }
1745
1746 /* Add Requires: */
1747 if (fc->requires != NULL && (he->c = rpmdsCount(fc->requires)) > 0
1748 && !fc->skipReq)
1749 {
1750 he->tag = RPMTAG_REQUIRENAME;
1751 he->t = RPM_STRING_ARRAY_TYPE;
1752 he->p.argv = fc->requires->N;
1753 assert(he->p.ptr != NULL);
1754 xx = headerPut(pkg->header, he, 0);
1755
1756 /* XXX rpm prior to 3.0.2 did not always supply EVR and Flags. */
1757 /*@-nullpass@*/
1758 he->tag = RPMTAG_REQUIREVERSION;
1759 he->t = RPM_STRING_ARRAY_TYPE;
1760 he->p.argv = fc->requires->EVR;
1761 assert(he->p.ptr != NULL);
1762 xx = headerPut(pkg->header, he, 0);
1763
1764 he->tag = RPMTAG_REQUIREFLAGS;
1765 he->t = RPM_UINT32_TYPE;
1766 he->p.ui32p = (rpmuint32_t *) fc->requires->Flags;
1767 assert(he->p.ptr != NULL);
1768 xx = headerPut(pkg->header, he, 0);
1769 /*@=nullpass@*/
1770 }
1771
1772 /* Add dependency dictionary(#dependencies) */
1773 he->tag = RPMTAG_DEPENDSDICT;
1774 he->t = RPM_UINT32_TYPE;
1775 he->p.ui32p = argiData(fc->ddictx);
1776 he->c = argiCount(fc->ddictx);
1777 if (he->p.ptr != NULL) {
1778 xx = headerPut(pkg->header, he, 0);
1779 }
1780
1781 /* Add per-file dependency (start,number) pairs (#files) */
1782 he->tag = RPMTAG_FILEDEPENDSX;
1783 he->t = RPM_UINT32_TYPE;
1784 he->p.ui32p = argiData(fc->fddictx);
1785 he->c = argiCount(fc->fddictx);
1786 assert(ac == (int)he->c);
1787 if (he->p.ptr != NULL) {
1788 xx = headerPut(pkg->header, he, 0);
1789 }
1790
1791 he->tag = RPMTAG_FILEDEPENDSN;
1792 he->t = RPM_UINT32_TYPE;
1793 he->p.ui32p = argiData(fc->fddictn);
1794 he->c = argiCount(fc->fddictn);
1795 assert(ac == (int)he->c);
1796 if (he->p.ptr != NULL) {
1797 xx = headerPut(pkg->header, he, 0);
1798 }
1799
1800 printDeps(pkg->header);
1801
1802 if (fc != NULL && _rpmfc_debug) {
1803 char msg[BUFSIZ];
1804 sprintf(msg, "final: files %u cdict[%d] %u%% ddictx[%d]", (unsigned int)fc->nfiles, argvCount(fc->cdict), (unsigned int)((100 * fc->fknown)/fc->nfiles), argiCount(fc->ddictx));
1805 rpmfcPrint(msg, fc, NULL);
1806 }
1807
1808 /* Clean up. */
1809 fmode = _free(fmode);
1810 fc = rpmfcFree(fc);
1811 av = argvFree(av);
1812
1813 return rc;
1814 }
1815
1816 /*@-mustmod@*/
1817 static void rpmfcFini(void * _fc)
1818 /*@modifies _fc @*/
1819 {
1820 rpmfc fc = _fc;
1821
1822 fc->fn = argvFree(fc->fn);
1823 fc->fcolor = argiFree(fc->fcolor);
1824 fc->fcdictx = argiFree(fc->fcdictx);
1825 fc->fddictx = argiFree(fc->fddictx);
1826 fc->fddictn = argiFree(fc->fddictn);
1827 fc->cdict = argvFree(fc->cdict);
1828 fc->ddict = argvFree(fc->ddict);
1829 fc->ddictx = argiFree(fc->ddictx);
1830
1831 (void)rpmdsFree(fc->provides);
1832 fc->provides = NULL;
1833 (void)rpmdsFree(fc->requires);
1834 fc->requires = NULL;
1835
1836 fc->iob_java = rpmiobFree(fc->iob_java);
1837 fc->iob_perl = rpmiobFree(fc->iob_perl);
1838 fc->iob_python = rpmiobFree(fc->iob_python);
1839 fc->iob_php = rpmiobFree(fc->iob_php);
1840 }
1841 /*@=mustmod@*/
1842
1843 /*@unchecked@*/ /*@only@*/ /*@null@*/
1844 rpmioPool _rpmfcPool = NULL;
1845
1846 static rpmfc rpmfcGetPool(/*@null@*/ rpmioPool pool)
1847 /*@globals _rpmfcPool, fileSystem, internalState @*/
1848 /*@modifies pool, _rpmfcPool, fileSystem, internalState @*/
1849 {
1850 rpmfc fc;
1851
1852 if (_rpmfcPool == NULL) {
1853 _rpmfcPool = rpmioNewPool("fc", sizeof(*fc), -1, _rpmfc_debug,
1854 NULL, NULL, rpmfcFini);
1855 pool = _rpmfcPool;
1856 }
1857 return (rpmfc) rpmioGetPool(pool, sizeof(*fc));
1858 }
1859
1860 rpmfc rpmfcNew(void)
1861 {
1862 rpmfc fc = rpmfcGetPool(_rpmfcPool);
1863 return rpmfcLink(fc);
1864 }
1865