Change all internal uses of rpmtsInitIterator() to use DBI tags
[platform/upstream/rpm.git] / lib / query.c
1 /** \ingroup rpmcli
2  * \file lib/query.c
3  * Display tag values from package metadata.
4  */
5
6 #include "system.h"
7
8 #include <errno.h>
9 #include <inttypes.h>
10 #include <ctype.h>
11
12 #include <rpm/rpmcli.h>
13 #include <rpm/header.h>
14 #include <rpm/rpmdb.h>
15 #include <rpm/rpmfi.h>
16 #include <rpm/rpmts.h>
17 #include <rpm/rpmlog.h>
18 #include <rpm/rpmfileutil.h>    /* rpmCleanPath */
19
20 #include "lib/rpmgi.h"
21 #include "lib/manifest.h"
22
23 #include "debug.h"
24
25
26 /**
27  */
28 static void printFileInfo(const char * name,
29                           rpm_loff_t size, unsigned short mode,
30                           unsigned int mtime,
31                           unsigned short rdev, unsigned int nlink,
32                           const char * owner, const char * group,
33                           const char * linkto)
34 {
35     char sizefield[21];
36     char ownerfield[8+1], groupfield[8+1];
37     char timefield[100];
38     time_t when = mtime;  /* important if sizeof(int32_t) ! sizeof(time_t) */
39     struct tm * tm;
40     static time_t now;
41     static struct tm nowtm;
42     char * perms = rpmPermsString(mode);
43     char *link = NULL;
44
45     /* On first call, grab snapshot of now */
46     if (now == 0) {
47         now = time(NULL);
48         tm = localtime(&now);
49         if (tm) nowtm = *tm;    /* structure assignment */
50     }
51
52     rstrlcpy(ownerfield, owner, sizeof(ownerfield));
53     rstrlcpy(groupfield, group, sizeof(groupfield));
54
55     /* this is normally right */
56     snprintf(sizefield, sizeof(sizefield), "%20" PRIu64, size);
57
58     /* this knows too much about dev_t */
59
60     if (S_ISLNK(mode)) {
61         rasprintf(&link, "%s -> %s", name, linkto);
62     } else if (S_ISCHR(mode)) {
63         perms[0] = 'c';
64         snprintf(sizefield, sizeof(sizefield), "%3u, %3u", ((unsigned)(rdev >> 8) & 0xff),
65                         ((unsigned)rdev & 0xff));
66     } else if (S_ISBLK(mode)) {
67         perms[0] = 'b';
68         snprintf(sizefield, sizeof(sizefield), "%3u, %3u", ((unsigned)(rdev >> 8) & 0xff),
69                         ((unsigned)rdev & 0xff));
70     }
71
72     /* Convert file mtime to display format */
73     tm = localtime(&when);
74     timefield[0] = '\0';
75     if (tm != NULL)
76     {   const char *fmt;
77         if (now > when + 6L * 30L * 24L * 60L * 60L ||  /* Old. */
78             now < when - 60L * 60L)                     /* In the future.  */
79         {
80         /* The file is fairly old or in the future.
81          * POSIX says the cutoff is 6 months old;
82          * approximate this by 6*30 days.
83          * Allow a 1 hour slop factor for what is considered "the future",
84          * to allow for NFS server/client clock disagreement.
85          * Show the year instead of the time of day.
86          */        
87             fmt = "%b %e  %Y";
88         } else {
89             fmt = "%b %e %H:%M";
90         }
91         (void)strftime(timefield, sizeof(timefield) - 1, fmt, tm);
92     }
93
94     rpmlog(RPMLOG_NOTICE, "%s %4d %-8s%-8s %10s %s %s\n", perms,
95         (int)nlink, ownerfield, groupfield, sizefield, timefield, 
96         link ? link : name);
97     free(perms);
98     free(link);
99 }
100
101 int showQueryPackage(QVA_t qva, rpmts ts, Header h)
102 {
103     rpmfi fi = NULL;
104     rpmfiFlags fiflags =  (RPMFI_NOHEADER | RPMFI_FLAGS_QUERY);
105     int rc = 0;         /* XXX FIXME: need real return code */
106
107     if (qva->qva_queryFormat != NULL) {
108         const char *errstr;
109         char *str = headerFormat(h, qva->qva_queryFormat, &errstr);
110
111         if ( str != NULL ) {
112             rpmlog(RPMLOG_NOTICE, "%s", str);
113             free(str);
114         } else {
115             rpmlog(RPMLOG_ERR, _("incorrect format: %s\n"), errstr);
116         }
117     }
118
119     if (!(qva->qva_flags & QUERY_FOR_LIST))
120         goto exit;
121
122     if (!(qva->qva_flags & QUERY_FOR_DUMPFILES))
123         fiflags |= RPMFI_NOFILEDIGESTS;
124
125     fi = rpmfiNew(ts, h, RPMTAG_BASENAMES, fiflags);
126     if (rpmfiFC(fi) <= 0) {
127         rpmlog(RPMLOG_NOTICE, _("(contains no files)\n"));
128         goto exit;
129     }
130
131     fi = rpmfiInit(fi, 0);
132     while (rpmfiNext(fi) >= 0) {
133         rpmfileAttrs fflags = rpmfiFFlags(fi);
134         rpm_mode_t fmode = rpmfiFMode(fi);
135         rpm_rdev_t frdev = rpmfiFRdev(fi);
136         rpm_time_t fmtime = rpmfiFMtime(fi);
137         rpmfileState fstate = rpmfiFState(fi);
138         rpm_loff_t fsize = rpmfiFSize(fi);
139         const char *fn = rpmfiFN(fi);
140         const char *fuser = rpmfiFUser(fi);
141         const char *fgroup = rpmfiFGroup(fi);
142         const char *flink = rpmfiFLink(fi);
143         char *buf = NULL;
144
145         /* If querying only docs, skip non-doc files. */
146         if ((qva->qva_flags & QUERY_FOR_DOCS) && !(fflags & RPMFILE_DOC))
147             continue;
148
149         /* If querying only configs, skip non-config files. */
150         if ((qva->qva_flags & QUERY_FOR_CONFIG) && !(fflags & RPMFILE_CONFIG))
151             continue;
152
153         /* If not querying %ghost, skip ghost files. */
154         if ((qva->qva_fflags & RPMFILE_GHOST) && (fflags & RPMFILE_GHOST))
155             continue;
156
157         if (qva->qva_flags & QUERY_FOR_STATE) {
158             switch (fstate) {
159             case RPMFILE_STATE_NORMAL:
160                 rstrcat(&buf, _("normal        "));
161                 break;
162             case RPMFILE_STATE_REPLACED:
163                 rstrcat(&buf, _("replaced      "));
164                 break;
165             case RPMFILE_STATE_NOTINSTALLED:
166                 rstrcat(&buf, _("not installed "));
167                 break;
168             case RPMFILE_STATE_NETSHARED:
169                 rstrcat(&buf, _("net shared    "));
170                 break;
171             case RPMFILE_STATE_WRONGCOLOR:
172                 rstrcat(&buf, _("wrong color   "));
173                 break;
174             case RPMFILE_STATE_MISSING:
175                 rstrcat(&buf, _("(no state)    "));
176                 break;
177             default:
178                 rasprintf(&buf, _("(unknown %3d) "), fstate);
179                 break;
180             }
181         }
182
183         if (qva->qva_flags & QUERY_FOR_DUMPFILES) {
184             char *add, *fdigest;
185             fdigest = rpmfiFDigestHex(fi, NULL);
186             rasprintf(&add, "%s %" PRIu64 " %d %s 0%o ", 
187                       fn, fsize, fmtime, fdigest ? fdigest : "", fmode);
188             rstrcat(&buf, add);
189             free(add);
190             free(fdigest);
191
192             if (fuser && fgroup) {
193                 rasprintf(&add, "%s %s", fuser, fgroup);
194                 rstrcat(&buf, add);
195                 free(add);
196             } else {
197                 rpmlog(RPMLOG_ERR,
198                         _("package has not file owner/group lists\n"));
199             }
200
201             rasprintf(&add, " %s %s %u %s",
202                                  fflags & RPMFILE_CONFIG ? "1" : "0",
203                                  fflags & RPMFILE_DOC ? "1" : "0",
204                                  frdev,
205                                  (flink && *flink ? flink : "X"));
206             rpmlog(RPMLOG_NOTICE, "%s%s\n", buf, add);
207             free(add);
208         } else
209         if (!rpmIsVerbose()) {
210             rpmlog(RPMLOG_NOTICE, "%s%s\n", buf ? buf : "", fn);
211         }
212         else {
213             uint32_t fnlink = rpmfiFNlink(fi);
214
215             /* XXX Adjust directory link count and size for display output. */
216             if (S_ISDIR(fmode)) {
217                 fnlink++;
218                 fsize = 0;
219             }
220
221             if (fuser && fgroup) {
222                 if (buf) {
223                     rpmlog(RPMLOG_NOTICE, "%s", buf);
224                 }
225                 printFileInfo(fn, fsize, fmode, fmtime, frdev, fnlink,
226                                         fuser, fgroup, flink);
227             } else {
228                 rpmlog(RPMLOG_ERR,
229                         _("package has neither file owner or id lists\n"));
230             }
231         }
232         free(buf);
233     }
234
235     rc = 0;
236
237 exit:
238     fi = rpmfiFree(fi);
239     return rc;
240 }
241
242 void rpmDisplayQueryTags(FILE * fp)
243 {
244     static const char * const tagTypeNames[] = {
245         "", "char", "int8", "int16", "int32", "int64",
246         "string", "blob", "argv", "i18nstring"
247     };
248     const char *tname, *sname;
249     rpmtd names = rpmtdNew();
250     (void) rpmTagGetNames(names, 1);
251
252     while ((tname = rpmtdNextString(names))) {
253         sname = tname + strlen("RPMTAG_");
254         if (rpmIsVerbose()) {
255             rpmTagVal tag = rpmTagGetValue(sname);
256             rpmTagType type = rpmTagGetTagType(tag);
257             fprintf(fp, "%-20s %6d", sname, tag);
258             if (type > RPM_NULL_TYPE && type <= RPM_MAX_TYPE)
259                 fprintf(fp, " %s", tagTypeNames[type]);
260         } else {
261             fprintf(fp, "%s", sname);
262         }
263         fprintf(fp, "\n");
264     }
265     rpmtdFreeData(names);
266     rpmtdFree(names);
267 }
268
269 static int rpmgiShowMatches(QVA_t qva, rpmts ts, rpmgi gi)
270 {
271     int ec = 0;
272     Header h;
273
274     while ((h = rpmgiNext(gi)) != NULL) {
275         int rc;
276
277         rpmdbCheckSignals();
278         if ((rc = qva->qva_showPackage(qva, ts, h)) != 0)
279             ec = rc;
280         headerFree(h);
281     }
282     return ec + rpmgiNumErrors(gi);
283 }
284
285 static int rpmcliShowMatches(QVA_t qva, rpmts ts, rpmdbMatchIterator mi)
286 {
287     Header h;
288     int ec = 0;
289
290     if (mi == NULL)
291         return 1;
292
293     while ((h = rpmdbNextIterator(mi)) != NULL) {
294         int rc;
295         rpmdbCheckSignals();
296         if ((rc = qva->qva_showPackage(qva, ts, h)) != 0)
297             ec = rc;
298     }
299     return ec;
300 }
301
302 static rpmdbMatchIterator initQueryIterator(QVA_t qva, rpmts ts, const char * arg)
303 {
304     const char * s;
305     int i;
306     int provides_checked = 0;
307     rpmdbMatchIterator mi = NULL;
308
309     (void) rpmdbCheckSignals();
310
311     if (qva->qva_showPackage == NULL)
312         goto exit;
313
314     switch (qva->qva_source) {
315     case RPMQV_GROUP:
316         mi = rpmtsInitIterator(ts, RPMDBI_GROUP, arg, 0);
317         if (mi == NULL) {
318             rpmlog(RPMLOG_NOTICE,
319                 _("group %s does not contain any packages\n"), arg);
320         }
321         break;
322
323     case RPMQV_TRIGGEREDBY:
324         mi = rpmtsInitIterator(ts, RPMDBI_TRIGGERNAME, arg, 0);
325         if (mi == NULL) {
326             rpmlog(RPMLOG_NOTICE, _("no package triggers %s\n"), arg);
327         }
328         break;
329
330     case RPMQV_PKGID:
331     {   unsigned char MD5[16];
332         unsigned char * t;
333
334         for (i = 0, s = arg; *s && isxdigit(*s); s++, i++)
335             {};
336         if (i != 32) {
337             rpmlog(RPMLOG_ERR, _("malformed %s: %s\n"), "pkgid", arg);
338             goto exit;
339         }
340
341         MD5[0] = '\0';
342         for (i = 0, t = MD5, s = arg; i < 16; i++, t++, s += 2)
343             *t = (rnibble(s[0]) << 4) | rnibble(s[1]);
344         
345         mi = rpmtsInitIterator(ts, RPMDBI_SIGMD5, MD5, sizeof(MD5));
346         if (mi == NULL) {
347             rpmlog(RPMLOG_NOTICE, _("no package matches %s: %s\n"),
348                         "pkgid", arg);
349         }
350     }   break;
351
352     case RPMQV_HDRID:
353         for (i = 0, s = arg; *s && isxdigit(*s); s++, i++)
354             {};
355         if (i != 40) {
356             rpmlog(RPMLOG_ERR, _("malformed %s: %s\n"), "hdrid", arg);
357             goto exit;
358         }
359
360         mi = rpmtsInitIterator(ts, RPMDBI_SHA1HEADER, arg, 0);
361         if (mi == NULL) {
362             rpmlog(RPMLOG_NOTICE, _("no package matches %s: %s\n"),
363                         "hdrid", arg);
364         }
365         break;
366
367     case RPMQV_TID:
368     {   char * end = NULL;
369         rpm_tid_t iid = strtoul(arg, &end, 0);
370
371         if ((*end) || (end == arg) || (iid == UINT_MAX)) {
372             rpmlog(RPMLOG_ERR, _("malformed %s: %s\n"), "tid", arg);
373             goto exit;
374         }
375         mi = rpmtsInitIterator(ts, RPMDBI_INSTALLTID, &iid, sizeof(iid));
376         if (mi == NULL) {
377             rpmlog(RPMLOG_NOTICE, _("no package matches %s: %s\n"),
378                         "tid", arg);
379         }
380     }   break;
381
382     case RPMQV_WHATREQUIRES:
383         mi = rpmtsInitIterator(ts, RPMDBI_REQUIRENAME, arg, 0);
384         if (mi == NULL) {
385             rpmlog(RPMLOG_NOTICE, _("no package requires %s\n"), arg);
386         }
387         break;
388
389     case RPMQV_WHATPROVIDES:
390         if (arg[0] != '/') {
391             provides_checked = 1;
392             mi = rpmtsInitIterator(ts, RPMDBI_PROVIDENAME, arg, 0);
393             if (mi == NULL) {
394                 rpmlog(RPMLOG_NOTICE, _("no package provides %s\n"), arg);
395             }
396             break;
397         }
398     case RPMQV_PATH:
399     {   char * fn;
400
401         for (s = arg; *s != '\0'; s++)
402             if (!(*s == '.' || *s == '/'))
403                 break;
404
405         if (*s == '\0') {
406             char fnbuf[PATH_MAX];
407             fn = realpath(arg, fnbuf);
408             fn = xstrdup( (fn != NULL ? fn : arg) );
409         } else if (*arg != '/') {
410             char *curDir = rpmGetCwd();
411             fn = (char *) rpmGetPath(curDir, "/", arg, NULL);
412             curDir = _free(curDir);
413         } else
414             fn = xstrdup(arg);
415         (void) rpmCleanPath(fn);
416
417         mi = rpmtsInitIterator(ts, RPMDBI_BASENAMES, fn, 0);
418         if (mi == NULL && !provides_checked)
419             mi = rpmtsInitIterator(ts, RPMDBI_PROVIDENAME, fn, 0);
420
421         if (mi == NULL) {
422             struct stat sb;
423             if (lstat(fn, &sb) != 0)
424                 rpmlog(RPMLOG_ERR, _("file %s: %s\n"), fn, strerror(errno));
425             else
426                 rpmlog(RPMLOG_NOTICE,
427                         _("file %s is not owned by any package\n"), fn);
428         }
429
430         fn = _free(fn);
431     }   break;
432
433     case RPMQV_DBOFFSET:
434     {   char * end = NULL;
435         unsigned int recOffset = strtoul(arg, &end, 0);
436
437         if ((*end) || (end == arg) || (recOffset == UINT_MAX)) {
438             rpmlog(RPMLOG_ERR, _("invalid package number: %s\n"), arg);
439             goto exit;
440         }
441         rpmlog(RPMLOG_DEBUG, "package record number: %u\n", recOffset);
442         /* RPMDBI_PACKAGES */
443         mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES, &recOffset, sizeof(recOffset));
444         if (mi == NULL) {
445             rpmlog(RPMLOG_ERR, _("record %u could not be read\n"), recOffset);
446         }
447     }   break;
448
449     case RPMQV_PACKAGE:
450     {
451         int matches = 0;
452         mi = rpmtsInitIterator(ts, RPMDBI_LABEL, arg, 0);
453         while (rpmdbNextIterator(mi) != NULL) {
454             matches++;
455         }
456         mi = rpmdbFreeIterator(mi);
457         if (! matches) {
458             rpmlog(RPMLOG_NOTICE, _("package %s is not installed\n"), arg);
459         } else {
460             mi = rpmtsInitIterator(ts, RPMDBI_LABEL, arg, 0);
461         }
462         break;
463     }
464     default:
465         break;
466     }
467
468 exit:
469     return mi;
470 }
471
472 /*
473  * Initialize db iterator with optional filters.  By default patterns
474  * applied to package name, others can be specified with <tagname>=<pattern>
475  */
476 static rpmdbMatchIterator initFilterIterator(rpmts ts, ARGV_const_t argv)
477 {
478     rpmdbMatchIterator mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES, NULL, 0);
479
480     for (ARGV_const_t arg = argv; arg && *arg != NULL; arg++) {
481         rpmTag tag = RPMTAG_NAME;
482         char a[strlen(*arg)+1], *ae;
483         const char *pat = a;
484
485         strcpy(a, *arg);
486
487         /* Parse for "tag=pattern" args. */
488         if ((ae = strchr(a, '=')) != NULL) {
489             *ae++ = '\0';
490             tag = rpmTagGetValue(a);
491             if (tag == RPMTAG_NOT_FOUND) {
492                 rpmlog(RPMLOG_ERR, _("unknown tag: \"%s\"\n"), a);
493                 mi = rpmdbFreeIterator(mi);
494                 break;
495             }
496             pat = ae;
497         }
498
499         rpmdbSetIteratorRE(mi, tag, RPMMIRE_DEFAULT, pat);
500     }
501
502     return mi;
503 }
504
505 int rpmcliArgIter(rpmts ts, QVA_t qva, ARGV_const_t argv)
506 {
507     int ec = 0;
508
509     switch (qva->qva_source) {
510     case RPMQV_ALL: {
511         rpmdbMatchIterator mi = initFilterIterator(ts, argv);
512         ec = rpmcliShowMatches(qva, ts, mi);
513         mi = rpmdbFreeIterator(mi);
514         break;
515     }
516     case RPMQV_RPM: {
517         rpmgi gi = rpmgiNew(ts, giFlags, argv);
518         ec = rpmgiShowMatches(qva, ts, gi);
519         gi = rpmgiFree(gi);
520         break;
521     }
522     case RPMQV_SPECRPMS:
523     case RPMQV_SPECSRPM:
524         for (ARGV_const_t arg = argv; arg && *arg; arg++) {
525             ec += ((qva->qva_specQuery != NULL)
526                     ? qva->qva_specQuery(ts, qva, *arg) : 1);
527         }
528         break;
529     default:
530         for (ARGV_const_t arg = argv; arg && *arg; arg++) {
531             rpmdbMatchIterator mi = initQueryIterator(qva, ts, *arg);
532             ec += rpmcliShowMatches(qva, ts, mi);
533             rpmdbFreeIterator(mi);
534         }
535         break;
536     }
537
538     return ec;
539 }
540
541 int rpmcliQuery(rpmts ts, QVA_t qva, char * const * argv)
542 {
543     rpmVSFlags vsflags, ovsflags;
544     int ec = 0;
545
546     if (qva->qva_showPackage == NULL)
547         qva->qva_showPackage = showQueryPackage;
548
549     /* If --queryformat unspecified, then set default now. */
550     if (!(qva->qva_flags & _QUERY_FOR_BITS) && qva->qva_queryFormat == NULL) {
551         char * fmt = rpmExpand("%{?_query_all_fmt}\n", NULL);
552         if (fmt == NULL || strlen(fmt) <= 1) {
553             fmt = _free(fmt);
554             fmt = xstrdup("%{nvra}\n");
555         }
556         qva->qva_queryFormat = fmt;
557     }
558
559     vsflags = rpmExpandNumeric("%{?_vsflags_query}");
560     if (rpmcliQueryFlags & VERIFY_DIGEST)
561         vsflags |= _RPMVSF_NODIGESTS;
562     if (rpmcliQueryFlags & VERIFY_SIGNATURE)
563         vsflags |= _RPMVSF_NOSIGNATURES;
564     if (rpmcliQueryFlags & VERIFY_HDRCHK)
565         vsflags |= RPMVSF_NOHDRCHK;
566
567     ovsflags = rpmtsSetVSFlags(ts, vsflags);
568     ec = rpmcliArgIter(ts, qva, argv);
569     vsflags = rpmtsSetVSFlags(ts, ovsflags);
570
571     if (qva->qva_showPackage == showQueryPackage)
572         qva->qva_showPackage = NULL;
573
574     return ec;
575 }