Add rpmgiGetFlags() method to avoid rpmgi_internal.h in query
[platform/upstream/rpm.git] / lib / rpmgi.c
1 /** \ingroup rpmio
2  * \file lib/rpmgi.c
3  */
4 #include "system.h"
5
6 #include <rpmlib.h>
7 #include <rpmte.h>              /* XXX rpmElementType */
8
9 #include "lib/rpmgi_internal.h"
10
11 #include <rpmdb.h>
12 #include <rpmmacro.h>           /* XXX rpmExpand */
13 #include <rpmfileutil.h>
14 #include "lib/manifest.h"
15 #include <rpmlog.h>
16
17 #include "debug.h"
18
19
20 int _rpmgi_debug = 0;
21
22 rpmgiFlags giFlags = RPMGI_NONE;
23
24 static int indent = 2;
25
26 static const char * ftsInfoStrings[] = {
27     "UNKNOWN",
28     "D",
29     "DC",
30     "DEFAULT",
31     "DNR",
32     "DOT",
33     "DP",
34     "ERR",
35     "F",
36     "INIT",
37     "NS",
38     "NSOK",
39     "SL",
40     "SLNONE",
41     "W",
42 };
43
44 static const char * ftsInfoStr(int fts_info)
45 {
46
47     if (!(fts_info >= 1 && fts_info <= 14))
48         fts_info = 0;
49     return ftsInfoStrings[ fts_info ];
50 }
51
52 /**
53  * Open a file after macro expanding path.
54  * @todo There are two error messages printed on header, then manifest failures.
55  * @param path          file path
56  * @param fmode         open mode
57  * @return              file handle
58  */
59 static FD_t rpmgiOpen(const char * path, const char * fmode)
60 {
61     const char * fn = rpmExpand(path, NULL);
62     FD_t fd = Fopen(fn, fmode);
63
64     if (fd == NULL || Ferror(fd)) {
65         rpmlog(RPMLOG_ERR, _("open of %s failed: %s\n"), fn, Fstrerror(fd));
66         if (fd != NULL) (void) Fclose(fd);
67         fd = NULL;
68     }
69     fn = _free(fn);
70
71     return fd;
72 }
73
74 /**
75  * Load manifest into iterator arg list.
76  * @param gi            generalized iterator
77  * @param path          file path
78  * @return              RPMRC_OK on success
79  */
80 static rpmRC rpmgiLoadManifest(rpmgi gi, const char * path)
81 {
82     FD_t fd = rpmgiOpen(path, "r.ufdio");
83     rpmRC rpmrc = RPMRC_FAIL;
84
85     if (fd != NULL) {
86         rpmrc = rpmReadPackageManifest(fd, &gi->argc, &gi->argv);
87         (void) Fclose(fd);
88     }
89     return rpmrc;
90 }
91
92 /**
93  * Return header from package.
94  * @param gi            generalized iterator
95  * @param path          file path
96  * @return              header (NULL on failure)
97  */
98 static Header rpmgiReadHeader(rpmgi gi, const char * path)
99 {
100     FD_t fd = rpmgiOpen(path, "r.ufdio");
101     Header h = NULL;
102
103     if (fd != NULL) {
104         /* XXX what if path needs expansion? */
105         rpmRC rpmrc = rpmReadPackageFile(gi->ts, fd, path, &h);
106
107         (void) Fclose(fd);
108
109         switch (rpmrc) {
110         case RPMRC_NOTFOUND:
111             /* XXX Read a package manifest. Restart ftswalk on success. */
112         case RPMRC_FAIL:
113         default:
114             h = headerFree(h);
115             break;
116         case RPMRC_NOTTRUSTED:
117         case RPMRC_NOKEY:
118         case RPMRC_OK:
119             break;
120         }
121     }
122
123     return h;
124 }
125
126 /**
127  * Read next header from package, lazily expanding manifests as found.
128  * @todo An empty file read as manifest truncates argv returning RPMRC_NOTFOUND.
129  * @todo Errors, e.g. non-existent path in manifest, will terminate iteration.
130  * @todo Chained manifests lose an arg someplace.
131  * @param gi            generalized iterator
132  * @return              RPMRC_OK on success
133  */
134 static rpmRC rpmgiLoadReadHeader(rpmgi gi)
135 {
136     rpmRC rpmrc = RPMRC_NOTFOUND;
137     Header h = NULL;
138
139     if (gi->argv != NULL && gi->argv[gi->i] != NULL)
140     do {
141         const char * fn;        /* XXX gi->hdrPath? */
142
143         fn = gi->argv[gi->i];
144         if (!(gi->flags & RPMGI_NOHEADER)) {
145             h = rpmgiReadHeader(gi, fn);
146             if (h != NULL)
147                 rpmrc = RPMRC_OK;
148         } else
149             rpmrc = RPMRC_OK;
150
151         if (rpmrc == RPMRC_OK || gi->flags & RPMGI_NOMANIFEST)
152             break;
153         if (errno == ENOENT) {
154             break;
155         }
156
157         /* Not a header, so try for a manifest. */
158         gi->argv[gi->i] = NULL;         /* Mark the insertion point */
159         rpmrc = rpmgiLoadManifest(gi, fn);
160         if (rpmrc != RPMRC_OK) {
161             gi->argv[gi->i] = fn;       /* Manifest failed, restore fn */
162             break;
163         }
164         fn = _free(fn);
165         rpmrc = RPMRC_NOTFOUND;
166     } while (1);
167
168     if (rpmrc == RPMRC_OK && h != NULL)
169         gi->h = headerLink(h);
170     h = headerFree(h);
171
172     return rpmrc;
173 }
174
175 /**
176  * Filter file tree walk path.
177  * @param gi            generalized iterator
178  * @return              RPMRC_OK on success
179  */
180 static rpmRC rpmgiWalkPathFilter(rpmgi gi)
181 {
182     FTSENT * fts = gi->fts;
183     rpmRC rpmrc = RPMRC_NOTFOUND;
184     const char * s;
185
186 if (_rpmgi_debug < 0)
187 rpmlog(RPMLOG_DEBUG, "FTS_%s\t%*s %s%s\n", ftsInfoStr(fts->fts_info),
188                 indent * (fts->fts_level < 0 ? 0 : fts->fts_level), "",
189                 fts->fts_name,
190         ((fts->fts_info == FTS_D || fts->fts_info == FTS_DP) ? "/" : ""));
191
192     switch (fts->fts_info) {
193     case FTS_D:         /* preorder directory */
194         break;
195     case FTS_DP:        /* postorder directory */
196         break;
197     case FTS_F:         /* regular file */
198         /* Ignore all but *.rpm files. */
199         s = fts->fts_name + fts->fts_namelen + 1 - sizeof(".rpm");
200         if (strcmp(s, ".rpm"))
201             break;
202         rpmrc = RPMRC_OK;
203         break;
204     case FTS_NS:        /* stat(2) failed */
205     case FTS_DNR:       /* unreadable directory */
206     case FTS_ERR:       /* error; errno is set */
207         break;
208     case FTS_DC:        /* directory that causes cycles */
209     case FTS_DEFAULT:   /* none of the above */
210     case FTS_DOT:       /* dot or dot-dot */
211     case FTS_INIT:      /* initialized only */
212     case FTS_NSOK:      /* no stat(2) requested */
213     case FTS_SL:        /* symbolic link */
214     case FTS_SLNONE:    /* symbolic link without target */
215     case FTS_W:         /* whiteout object */
216     default:
217         break;
218     }
219     return rpmrc;
220 }
221
222 /**
223  * Read header from next package, lazily walking file tree.
224  * @param gi            generalized iterator
225  * @return              RPMRC_OK on success
226  */
227 static rpmRC rpmgiWalkReadHeader(rpmgi gi)
228 {
229     rpmRC rpmrc = RPMRC_NOTFOUND;
230
231     if (gi->ftsp != NULL)
232     while ((gi->fts = Fts_read(gi->ftsp)) != NULL) {
233         rpmrc = rpmgiWalkPathFilter(gi);
234         if (rpmrc == RPMRC_OK)
235             break;
236     }
237
238     if (rpmrc == RPMRC_OK) {
239         Header h = NULL;
240         if (!(gi->flags & RPMGI_NOHEADER)) {
241             /* XXX rpmrc = rpmgiLoadReadHeader(gi); */
242             if (gi->fts != NULL)        /* XXX can't happen */
243                 h = rpmgiReadHeader(gi, gi->fts->fts_path);
244         }
245         if (h != NULL)
246             gi->h = headerLink(h);
247         h = headerFree(h);
248     }
249
250     return rpmrc;
251 }
252
253 /**
254  * Append globbed arg list to iterator.
255  * @param gi            generalized iterator
256  * @param argv          arg list to be globbed (or NULL)
257  * @returns             RPMRC_OK on success
258  */
259 static rpmRC rpmgiGlobArgv(rpmgi gi, ARGV_t argv)
260 {
261     const char * arg;
262     rpmRC rpmrc = RPMRC_OK;
263     int ac = 0;
264     int xx;
265
266     /* XXX Expand globs only if requested or for gi specific tags */
267     if ((gi->flags & RPMGI_NOGLOB)
268      || !(gi->tag == RPMDBI_HDLIST || gi->tag == RPMDBI_ARGLIST || gi->tag == RPMDBI_FTSWALK))
269     {
270         if (argv != NULL) {
271             while (argv[ac] != NULL)
272                 ac++;
273 /* XXX argv is not NULL */
274             xx = argvAppend(&gi->argv, argv);
275         }
276         gi->argc = ac;
277         return rpmrc;
278     }
279
280     if (argv != NULL)
281     while ((arg = *argv++) != NULL) {
282         ARGV_t av = NULL;
283
284         xx = rpmGlob(arg, &ac, &av);
285         xx = argvAppend(&gi->argv, av);
286         gi->argc += ac;
287         av = argvFree(av);
288         ac = 0;
289     }
290     return rpmrc;
291 }
292
293 /**
294  * Return rpmdb match iterator with filters (if any) set.
295  * @param gi            generalized iterator
296  * @returns             RPMRC_OK on success
297  */
298 static rpmRC rpmgiInitFilter(rpmgi gi)
299 {
300     rpmRC rpmrc = RPMRC_OK;
301     ARGV_t av;
302     int res = 0;
303
304     gi->mi = rpmtsInitIterator(gi->ts, gi->tag, gi->keyp, gi->keylen);
305
306 if (_rpmgi_debug < 0)
307 fprintf(stderr, "*** gi %p\tmi %p\n", gi, gi->mi);
308
309     if (gi->argv != NULL)
310     for (av = (const char **) gi->argv; *av != NULL; av++) {
311         int tag = RPMTAG_NAME;
312         const char * pat;
313         char * a, * ae;
314
315         pat = a = xstrdup(*av);
316         tag = RPMTAG_NAME;
317
318         /* Parse for "tag=pattern" args. */
319         if ((ae = strchr(a, '=')) != NULL) {
320             *ae++ = '\0';
321             tag = rpmTagGetValue(a);
322             if (tag < 0) {
323                 rpmlog(RPMLOG_NOTICE, _("unknown tag: \"%s\"\n"), a);
324                 res = 1;
325             }
326             pat = ae;
327         }
328
329         if (!res) {
330 if (_rpmgi_debug  < 0)
331 fprintf(stderr, "\tav %p[%ld]: \"%s\" -> %s ~= \"%s\"\n", gi->argv, (long) (av - gi->argv), *av, rpmTagGetName(tag), pat);
332             res = rpmdbSetIteratorRE(gi->mi, tag, RPMMIRE_DEFAULT, pat);
333         }
334         a = _free(a);
335
336         if (res == 0)
337             continue;
338
339         gi->mi = rpmdbFreeIterator(gi->mi);     /* XXX odd side effect? */
340         rpmrc = RPMRC_FAIL;
341         break;
342     }
343
344     return rpmrc;
345 }
346
347 rpmgi XrpmgiUnlink(rpmgi gi, const char * msg, const char * fn, unsigned ln)
348 {
349     if (gi == NULL) return NULL;
350
351 if (_rpmgi_debug && msg != NULL)
352 fprintf(stderr, "--> gi %p -- %d %s at %s:%u\n", gi, gi->nrefs, msg, fn, ln);
353
354     gi->nrefs--;
355     return NULL;
356 }
357
358 rpmgi XrpmgiLink(rpmgi gi, const char * msg, const char * fn, unsigned ln)
359 {
360     if (gi == NULL) return NULL;
361     gi->nrefs++;
362
363 if (_rpmgi_debug && msg != NULL)
364 fprintf(stderr, "--> gi %p ++ %d %s at %s:%u\n", gi, gi->nrefs, msg, fn, ln);
365
366     return gi;
367 }
368
369 rpmgi rpmgiFree(rpmgi gi)
370 {
371     if (gi == NULL)
372         return NULL;
373
374     if (gi->nrefs > 1)
375         return rpmgiUnlink(gi, __FUNCTION__);
376
377     (void) rpmgiUnlink(gi, __FUNCTION__);
378
379
380     gi->hdrPath = _free(gi->hdrPath);
381     gi->h = headerFree(gi->h);
382
383     gi->argv = argvFree(gi->argv);
384
385     if (gi->ftsp != NULL) {
386         int xx;
387         xx = Fts_close(gi->ftsp);
388         gi->ftsp = NULL;
389         gi->fts = NULL;
390     }
391     if (gi->fd != NULL) {
392         (void) Fclose(gi->fd);
393         gi->fd = NULL;
394     }
395     gi->tsi = rpmtsiFree(gi->tsi);
396     gi->mi = rpmdbFreeIterator(gi->mi);
397     gi->ts = rpmtsFree(gi->ts);
398
399     memset(gi, 0, sizeof(*gi));         /* XXX trash and burn */
400     gi = _free(gi);
401     return NULL;
402 }
403
404 rpmgi rpmgiNew(rpmts ts, int tag, const void * keyp, size_t keylen)
405 {
406     rpmgi gi = xcalloc(1, sizeof(*gi));
407
408     if (gi == NULL)
409         return NULL;
410
411     gi->ts = rpmtsLink(ts, __FUNCTION__);
412     gi->tag = tag;
413     gi->keyp = keyp;
414     gi->keylen = keylen;
415
416     gi->flags = 0;
417     gi->active = 0;
418     gi->i = -1;
419     gi->hdrPath = NULL;
420     gi->h = NULL;
421
422     gi->tsi = NULL;
423     gi->mi = NULL;
424     gi->fd = NULL;
425     gi->argv = xcalloc(1, sizeof(*gi->argv));
426     gi->argc = 0;
427     gi->ftsOpts = 0;
428     gi->ftsp = NULL;
429     gi->fts = NULL;
430
431     gi = rpmgiLink(gi, __FUNCTION__);
432
433     return gi;
434 }
435
436 rpmRC rpmgiNext(rpmgi gi)
437 {
438     char hnum[32];
439     rpmRC rpmrc = RPMRC_NOTFOUND;
440     int xx;
441
442     if (gi == NULL)
443         return rpmrc;
444
445     /* Free header from previous iteration. */
446     gi->h = headerFree(gi->h);
447     gi->hdrPath = _free(gi->hdrPath);
448     hnum[0] = '\0';
449
450     if (++gi->i >= 0)
451     switch (gi->tag) {
452     default:
453     case RPMDBI_PACKAGES:
454         if (!gi->active) {
455             rpmrc = rpmgiInitFilter(gi);
456             if (rpmrc != RPMRC_OK) {
457                 gi->mi = rpmdbFreeIterator(gi->mi);     /* XXX unnecessary */
458                 goto enditer;
459             }
460             rpmrc = RPMRC_NOTFOUND;     /* XXX hack */
461             gi->active = 1;
462         }
463         if (gi->mi != NULL) {   /* XXX unnecessary */
464             Header h = rpmdbNextIterator(gi->mi);
465             if (h != NULL) {
466                 if (!(gi->flags & RPMGI_NOHEADER))
467                     gi->h = headerLink(h);
468                 sprintf(hnum, "%u", rpmdbGetIteratorOffset(gi->mi));
469                 gi->hdrPath = rpmExpand("rpmdb h# ", hnum, NULL);
470                 rpmrc = RPMRC_OK;
471                 /* XXX header reference held by iterator, so no headerFree */
472             }
473         }
474         if (rpmrc != RPMRC_OK) {
475             gi->mi = rpmdbFreeIterator(gi->mi);
476             goto enditer;
477         }
478         break;
479     case RPMDBI_ADDED:
480     {   rpmte p;
481
482         if (!gi->active) {
483             gi->tsi = rpmtsiInit(gi->ts);
484             gi->active = 1;
485         }
486         if ((p = rpmtsiNext(gi->tsi, TR_ADDED)) != NULL) {
487             Header h = rpmteHeader(p);
488             if (h != NULL)
489                 if (!(gi->flags & RPMGI_NOHEADER)) {
490                     gi->h = headerLink(h);
491                 sprintf(hnum, "%u", (unsigned)gi->i);
492                 gi->hdrPath = rpmExpand("added h# ", hnum, NULL);
493                 rpmrc = RPMRC_OK;
494                 h = headerFree(h);
495             }
496         }
497         if (rpmrc != RPMRC_OK) {
498             gi->tsi = rpmtsiFree(gi->tsi);
499             goto enditer;
500         }
501     }   break;
502     case RPMDBI_HDLIST:
503         if (!gi->active) {
504             const char * path = "/usr/share/comps/%{_arch}/hdlist";
505             gi->fd = rpmgiOpen(path, "r.ufdio");
506             gi->active = 1;
507         }
508         if (gi->fd != NULL) {
509             Header h = headerRead(gi->fd, HEADER_MAGIC_YES);
510             if (h != NULL) {
511                 if (!(gi->flags & RPMGI_NOHEADER))
512                     gi->h = headerLink(h);
513                 sprintf(hnum, "%u", (unsigned)gi->i);
514                 gi->hdrPath = rpmExpand("hdlist h# ", hnum, NULL);
515                 rpmrc = RPMRC_OK;
516                 h = headerFree(h);
517             }
518         }
519         if (rpmrc != RPMRC_OK) {
520             if (gi->fd != NULL) (void) Fclose(gi->fd);
521             gi->fd = NULL;
522             goto enditer;
523         }
524         break;
525     case RPMDBI_ARGLIST:
526         /* XXX gi->active initialize? */
527 if (_rpmgi_debug  < 0)
528 fprintf(stderr, "*** gi %p\t%p[%d]: %s\n", gi, gi->argv, gi->i, gi->argv[gi->i]);
529         /* Read next header, lazily expanding manifests as found. */
530         rpmrc = rpmgiLoadReadHeader(gi);
531
532         if (rpmrc != RPMRC_OK)  /* XXX check this */
533             goto enditer;
534
535         gi->hdrPath = xstrdup(gi->argv[gi->i]);
536         break;
537     case RPMDBI_FTSWALK:
538         if (gi->argv == NULL)           /* HACK */
539             goto enditer;
540
541         if (!gi->active) {
542             gi->ftsp = Fts_open((char *const *)gi->argv, gi->ftsOpts, NULL);
543             /* XXX NULL with open(2)/malloc(3) errno set */
544             gi->active = 1;
545         }
546
547         /* Read next header, lazily walking file tree. */
548         rpmrc = rpmgiWalkReadHeader(gi);
549
550         if (rpmrc != RPMRC_OK) {
551             xx = Fts_close(gi->ftsp);
552             gi->ftsp = NULL;
553             goto enditer;
554         }
555
556         if (gi->fts != NULL)
557             gi->hdrPath = xstrdup(gi->fts->fts_path);
558         break;
559     }
560
561     if ((gi->flags & RPMGI_TSADD) && gi->h != NULL) {
562         /* XXX rpmgi hack: Save header in transaction element. */
563         xx = rpmtsAddInstallElement(gi->ts, gi->h, (fnpyKey)gi->hdrPath, 2, NULL);
564     }
565
566     return rpmrc;
567
568 enditer:
569     if (gi->flags & RPMGI_TSORDER) {
570         rpmts ts = gi->ts;
571         rpmps ps;
572
573         /* XXX installed database needs close here. */
574         xx = rpmtsCloseDB(ts);
575         xx = rpmtsSetDBMode(ts, -1);    /* XXX disable lazy opens */
576
577         xx = rpmtsCheck(ts);
578
579         /* XXX query/verify will need the glop added to a buffer instead. */
580         ps = rpmtsProblems(ts);
581         if (rpmpsNumProblems(ps) > 0) {
582             /* XXX rpminstall will need RPMLOG_ERR */
583             rpmlog(RPMLOG_INFO, _("Failed dependencies:\n"));
584             if (rpmIsVerbose())
585                 rpmpsPrint(NULL, ps);
586
587
588             if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOSUGGEST))
589                 rpmtsPrintSuggests(ts);
590
591         }
592         ps = rpmpsFree(ps);
593         rpmtsCleanProblems(ts);
594
595         xx = rpmtsOrder(ts);
596
597         gi->tag = RPMDBI_ADDED;                 /* XXX hackery */
598         gi->flags &= ~(RPMGI_TSADD|RPMGI_TSORDER);
599
600     }
601
602     gi->h = headerFree(gi->h);
603     gi->hdrPath = _free(gi->hdrPath);
604     gi->i = -1;
605     gi->active = 0;
606     return rpmrc;
607 }
608
609 const char * rpmgiHdrPath(rpmgi gi)
610 {
611     return (gi != NULL ? gi->hdrPath : NULL);
612 }
613
614 Header rpmgiHeader(rpmgi gi)
615 {
616     return (gi != NULL ? gi->h : NULL);
617 }
618
619 rpmts rpmgiTs(rpmgi gi)
620 {
621     return (gi != NULL ? gi->ts : NULL);
622 }
623
624 rpmRC rpmgiSetArgs(rpmgi gi, ARGV_t argv, int ftsOpts, rpmgiFlags flags)
625 {
626     gi->ftsOpts = ftsOpts;
627     gi->flags = flags;
628     return rpmgiGlobArgv(gi, argv);
629 }
630
631 rpmgiFlags rpmgiGetFlags(rpmgi gi)
632 {
633     return (gi != NULL ? gi->flags : RPMGI_NONE);
634 }