6f2d593a7a1a1bcbe1ddb7e3ccbbfed9bf4750e0
[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     char * perms = rpmPermsString(mode);
42     char *link = NULL;
43
44     /* On first call, grab snapshot of now */
45     if (now == 0)
46         now = time(NULL);
47
48     rstrlcpy(ownerfield, owner, sizeof(ownerfield));
49     rstrlcpy(groupfield, group, sizeof(groupfield));
50
51     /* this is normally right */
52     snprintf(sizefield, sizeof(sizefield), "%20" PRIu64, size);
53
54     /* this knows too much about dev_t */
55
56     if (S_ISLNK(mode)) {
57         rasprintf(&link, "%s -> %s", name, linkto);
58     } else if (S_ISCHR(mode)) {
59         perms[0] = 'c';
60         snprintf(sizefield, sizeof(sizefield), "%3u, %3u", ((unsigned)(rdev >> 8) & 0xff),
61                         ((unsigned)rdev & 0xff));
62     } else if (S_ISBLK(mode)) {
63         perms[0] = 'b';
64         snprintf(sizefield, sizeof(sizefield), "%3u, %3u", ((unsigned)(rdev >> 8) & 0xff),
65                         ((unsigned)rdev & 0xff));
66     }
67
68     /* Convert file mtime to display format */
69     tm = localtime(&when);
70     timefield[0] = '\0';
71     if (tm != NULL)
72     {   const char *fmt;
73         if (now > when + 6L * 30L * 24L * 60L * 60L ||  /* Old. */
74             now < when - 60L * 60L)                     /* In the future.  */
75         {
76         /* The file is fairly old or in the future.
77          * POSIX says the cutoff is 6 months old;
78          * approximate this by 6*30 days.
79          * Allow a 1 hour slop factor for what is considered "the future",
80          * to allow for NFS server/client clock disagreement.
81          * Show the year instead of the time of day.
82          */        
83             fmt = "%b %e  %Y";
84         } else {
85             fmt = "%b %e %H:%M";
86         }
87         (void)strftime(timefield, sizeof(timefield) - 1, fmt, tm);
88     }
89
90     rpmlog(RPMLOG_NOTICE, "%s %4d %-8s%-8s %10s %s %s\n", perms,
91         (int)nlink, ownerfield, groupfield, sizefield, timefield, 
92         link ? link : name);
93     free(perms);
94     free(link);
95 }
96
97 int showQueryPackage(QVA_t qva, rpmts ts, Header h)
98 {
99     rpmfi fi = NULL;
100     rpmfiFlags fiflags =  (RPMFI_NOHEADER | RPMFI_FLAGS_QUERY);
101     int rc = 0;         /* XXX FIXME: need real return code */
102
103     if (qva->qva_queryFormat != NULL) {
104         const char *errstr;
105         char *str = headerFormat(h, qva->qva_queryFormat, &errstr);
106
107         if ( str != NULL ) {
108             rpmlog(RPMLOG_NOTICE, "%s", str);
109             free(str);
110         } else {
111             rpmlog(RPMLOG_ERR, _("incorrect format: %s\n"), errstr);
112         }
113     }
114
115     if (!(qva->qva_flags & QUERY_FOR_LIST))
116         goto exit;
117
118     if (!(qva->qva_flags & QUERY_FOR_DUMPFILES))
119         fiflags |= RPMFI_NOFILEDIGESTS;
120
121     fi = rpmfiNew(ts, h, RPMTAG_BASENAMES, fiflags);
122     if (rpmfiFC(fi) <= 0) {
123         rpmlog(RPMLOG_NOTICE, _("(contains no files)\n"));
124         goto exit;
125     }
126
127     fi = rpmfiInit(fi, 0);
128     while (rpmfiNext(fi) >= 0) {
129         rpmfileAttrs fflags = rpmfiFFlags(fi);
130         rpm_mode_t fmode = rpmfiFMode(fi);
131         rpm_rdev_t frdev = rpmfiFRdev(fi);
132         rpm_time_t fmtime = rpmfiFMtime(fi);
133         rpmfileState fstate = rpmfiFState(fi);
134         rpm_loff_t fsize = rpmfiFSize(fi);
135         const char *fn = rpmfiFN(fi);
136         const char *fuser = rpmfiFUser(fi);
137         const char *fgroup = rpmfiFGroup(fi);
138         const char *flink = rpmfiFLink(fi);
139         char *buf = NULL;
140
141         /* If querying only docs, skip non-doc files. */
142         if ((qva->qva_flags & QUERY_FOR_DOCS) && !(fflags & RPMFILE_DOC))
143             continue;
144
145         /* If querying only configs, skip non-config files. */
146         if ((qva->qva_flags & QUERY_FOR_CONFIG) && !(fflags & RPMFILE_CONFIG))
147             continue;
148
149         /* If querying only licenses, skip non-license files. */
150         if ((qva->qva_flags & QUERY_FOR_LICENSE) && !(fflags & RPMFILE_LICENSE))
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     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     rpmdbMatchIterator mi = NULL;
307
308     (void) rpmdbCheckSignals();
309
310     if (qva->qva_showPackage == NULL)
311         goto exit;
312
313     switch (qva->qva_source) {
314     case RPMQV_GROUP:
315         mi = rpmtsInitIterator(ts, RPMDBI_GROUP, arg, 0);
316         if (mi == NULL) {
317             rpmlog(RPMLOG_NOTICE,
318                 _("group %s does not contain any packages\n"), arg);
319         }
320         break;
321
322     case RPMQV_TRIGGEREDBY:
323         mi = rpmtsInitIterator(ts, RPMDBI_TRIGGERNAME, arg, 0);
324         if (mi == NULL) {
325             rpmlog(RPMLOG_NOTICE, _("no package triggers %s\n"), arg);
326         }
327         break;
328
329     case RPMQV_PKGID:
330     {   unsigned char MD5[16];
331         unsigned char * t;
332
333         for (i = 0, s = arg; *s && isxdigit(*s); s++, i++)
334             {};
335         if (i != 32) {
336             rpmlog(RPMLOG_ERR, _("malformed %s: %s\n"), "pkgid", arg);
337             goto exit;
338         }
339
340         MD5[0] = '\0';
341         for (i = 0, t = MD5, s = arg; i < 16; i++, t++, s += 2)
342             *t = (rnibble(s[0]) << 4) | rnibble(s[1]);
343         
344         mi = rpmtsInitIterator(ts, RPMDBI_SIGMD5, MD5, sizeof(MD5));
345         if (mi == NULL) {
346             rpmlog(RPMLOG_NOTICE, _("no package matches %s: %s\n"),
347                         "pkgid", arg);
348         }
349     }   break;
350
351     case RPMQV_HDRID:
352         for (i = 0, s = arg; *s && isxdigit(*s); s++, i++)
353             {};
354         if (i != 40) {
355             rpmlog(RPMLOG_ERR, _("malformed %s: %s\n"), "hdrid", arg);
356             goto exit;
357         }
358
359         mi = rpmtsInitIterator(ts, RPMDBI_SHA1HEADER, arg, 0);
360         if (mi == NULL) {
361             rpmlog(RPMLOG_NOTICE, _("no package matches %s: %s\n"),
362                         "hdrid", arg);
363         }
364         break;
365
366     case RPMQV_TID:
367     {   char * end = NULL;
368         rpm_tid_t iid = strtoul(arg, &end, 0);
369
370         if ((*end) || (end == arg) || (iid == UINT_MAX)) {
371             rpmlog(RPMLOG_ERR, _("malformed %s: %s\n"), "tid", arg);
372             goto exit;
373         }
374         mi = rpmtsInitIterator(ts, RPMDBI_INSTALLTID, &iid, sizeof(iid));
375         if (mi == NULL) {
376             rpmlog(RPMLOG_NOTICE, _("no package matches %s: %s\n"),
377                         "tid", arg);
378         }
379     }   break;
380
381     case RPMQV_WHATREQUIRES:
382         mi = rpmtsInitIterator(ts, RPMDBI_REQUIRENAME, arg, 0);
383         if (mi == NULL) {
384             rpmlog(RPMLOG_NOTICE, _("no package requires %s\n"), arg);
385         }
386         break;
387
388     case RPMQV_WHATPROVIDES:
389         if (arg[0] != '/' && arg[0] != '.') {
390             mi = rpmtsInitIterator(ts, RPMDBI_PROVIDENAME, arg, 0);
391             if (mi == NULL) {
392                 rpmlog(RPMLOG_NOTICE, _("no package provides %s\n"), arg);
393             }
394             break;
395         }
396         /* fallthrough on absolute and relative paths */
397     case RPMQV_PATH:
398     {   char * fn;
399
400         for (s = arg; *s != '\0'; s++)
401             if (!(*s == '.' || *s == '/'))
402                 break;
403
404         if (*s == '\0') {
405             char fnbuf[PATH_MAX];
406             fn = realpath(arg, fnbuf);
407             fn = xstrdup( (fn != NULL ? fn : arg) );
408         } else if (*arg != '/') {
409             char *curDir = rpmGetCwd();
410             fn = (char *) rpmGetPath(curDir, "/", arg, NULL);
411             free(curDir);
412         } else
413             fn = xstrdup(arg);
414         (void) rpmCleanPath(fn);
415
416         /* XXX Add a switch to enable former BASENAMES behavior? */
417         mi = rpmtsInitIterator(ts, RPMDBI_INSTFILENAMES, fn, 0);
418         if (mi == NULL)
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         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         rpmTagVal 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         rpmdbFreeIterator(mi);
514         break;
515     }
516     case RPMQV_RPM: {
517         rpmgi gi = rpmgiNew(ts, giFlags, argv);
518         ec = rpmgiShowMatches(qva, ts, gi);
519         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             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     rpmtsSetVSFlags(ts, ovsflags);
570
571     if (qva->qva_showPackage == showQueryPackage)
572         qva->qva_showPackage = NULL;
573
574     return ec;
575 }