3 * Display tag values from package metadata.
10 #include <rpm/rpmcli.h>
11 #include <rpm/header.h>
12 #include <rpm/rpmdb.h>
13 #include <rpm/rpmfi.h>
14 #include <rpm/rpmgi.h>
15 #include <rpm/rpmts.h>
16 #include <rpm/rpmlog.h>
17 #include <rpm/rpmfileutil.h> /* rpmCleanPath */
19 #include "lib/manifest.h"
26 static void printFileInfo(const char * name,
27 rpm_loff_t size, unsigned short mode,
29 unsigned short rdev, unsigned int nlink,
30 const char * owner, const char * group,
34 char ownerfield[8+1], groupfield[8+1];
36 time_t when = mtime; /* important if sizeof(int32_t) ! sizeof(time_t) */
39 static struct tm nowtm;
40 char * perms = rpmPermsString(mode);
43 /* On first call, grab snapshot of now */
47 if (tm) nowtm = *tm; /* structure assignment */
50 rstrlcpy(ownerfield, owner, sizeof(ownerfield));
51 rstrlcpy(groupfield, group, sizeof(groupfield));
53 /* this is normally right */
54 snprintf(sizefield, sizeof(sizefield), "%20" PRIu64, size);
56 /* this knows too much about dev_t */
59 rasprintf(&link, "%s -> %s", name, linkto);
60 } else if (S_ISCHR(mode)) {
62 snprintf(sizefield, sizeof(sizefield), "%3u, %3u", ((unsigned)(rdev >> 8) & 0xff),
63 ((unsigned)rdev & 0xff));
64 } else if (S_ISBLK(mode)) {
66 snprintf(sizefield, sizeof(sizefield), "%3u, %3u", ((unsigned)(rdev >> 8) & 0xff),
67 ((unsigned)rdev & 0xff));
70 /* Convert file mtime to display format */
71 tm = localtime(&when);
75 if (now > when + 6L * 30L * 24L * 60L * 60L || /* Old. */
76 now < when - 60L * 60L) /* In the future. */
78 /* The file is fairly old or in the future.
79 * POSIX says the cutoff is 6 months old;
80 * approximate this by 6*30 days.
81 * Allow a 1 hour slop factor for what is considered "the future",
82 * to allow for NFS server/client clock disagreement.
83 * Show the year instead of the time of day.
89 (void)strftime(timefield, sizeof(timefield) - 1, fmt, tm);
92 rpmlog(RPMLOG_NOTICE, "%s %4d %-8s%-8s %10s %s %s\n", perms,
93 (int)nlink, ownerfield, groupfield, sizefield, timefield,
99 int showQueryPackage(QVA_t qva, rpmts ts, Header h)
102 rpmfiFlags fiflags = (RPMFI_NOHEADER | RPMFI_FLAGS_QUERY);
103 int rc = 0; /* XXX FIXME: need real return code */
105 if (qva->qva_queryFormat != NULL) {
107 char *str = headerFormat(h, qva->qva_queryFormat, &errstr);
110 rpmlog(RPMLOG_NOTICE, "%s", str);
113 rpmlog(RPMLOG_ERR, _("incorrect format: %s\n"), errstr);
117 if (!(qva->qva_flags & QUERY_FOR_LIST))
120 if (!(qva->qva_flags & QUERY_FOR_DUMPFILES))
121 fiflags |= RPMFI_NOFILEDIGESTS;
123 fi = rpmfiNew(ts, h, RPMTAG_BASENAMES, fiflags);
124 if (rpmfiFC(fi) <= 0) {
125 rpmlog(RPMLOG_NOTICE, _("(contains no files)\n"));
129 fi = rpmfiInit(fi, 0);
130 while (rpmfiNext(fi) >= 0) {
131 rpmfileAttrs fflags = rpmfiFFlags(fi);
132 rpm_mode_t fmode = rpmfiFMode(fi);
133 rpm_rdev_t frdev = rpmfiFRdev(fi);
134 rpm_time_t fmtime = rpmfiFMtime(fi);
135 rpmfileState fstate = rpmfiFState(fi);
136 rpm_loff_t fsize = rpmfiFSize(fi);
137 const char *fn = rpmfiFN(fi);
138 const char *fuser = rpmfiFUser(fi);
139 const char *fgroup = rpmfiFGroup(fi);
140 const char *flink = rpmfiFLink(fi);
141 uint32_t fnlink = rpmfiFNlink(fi);
144 /* If querying only docs, skip non-doc files. */
145 if ((qva->qva_flags & QUERY_FOR_DOCS) && !(fflags & RPMFILE_DOC))
148 /* If querying only configs, skip non-config files. */
149 if ((qva->qva_flags & QUERY_FOR_CONFIG) && !(fflags & RPMFILE_CONFIG))
152 /* If not querying %ghost, skip ghost files. */
153 if ((qva->qva_fflags & RPMFILE_GHOST) && (fflags & RPMFILE_GHOST))
156 if (qva->qva_flags & QUERY_FOR_STATE) {
158 case RPMFILE_STATE_NORMAL:
159 rstrcat(&buf, _("normal "));
161 case RPMFILE_STATE_REPLACED:
162 rstrcat(&buf, _("replaced "));
164 case RPMFILE_STATE_NOTINSTALLED:
165 rstrcat(&buf, _("not installed "));
167 case RPMFILE_STATE_NETSHARED:
168 rstrcat(&buf, _("net shared "));
170 case RPMFILE_STATE_WRONGCOLOR:
171 rstrcat(&buf, _("wrong color "));
173 case RPMFILE_STATE_MISSING:
174 rstrcat(&buf, _("(no state) "));
177 rasprintf(&buf, _("(unknown %3d) "), fstate);
182 if (qva->qva_flags & QUERY_FOR_DUMPFILES) {
184 fdigest = rpmfiFDigestHex(fi, NULL);
185 rasprintf(&add, "%s %" PRIu64 " %d %s 0%o ",
186 fn, fsize, fmtime, fdigest ? fdigest : "", fmode);
191 if (fuser && fgroup) {
192 rasprintf(&add, "%s %s", fuser, fgroup);
197 _("package has not file owner/group lists\n"));
200 rasprintf(&add, " %s %s %u %s",
201 fflags & RPMFILE_CONFIG ? "1" : "0",
202 fflags & RPMFILE_DOC ? "1" : "0",
204 (flink && *flink ? flink : "X"));
205 rpmlog(RPMLOG_NOTICE, "%s%s\n", buf, add);
208 if (!rpmIsVerbose()) {
209 rpmlog(RPMLOG_NOTICE, "%s%s\n", buf ? buf : "", fn);
213 /* XXX Adjust directory link count and size for display output. */
214 if (S_ISDIR(fmode)) {
219 if (fuser && fgroup) {
221 rpmlog(RPMLOG_NOTICE, "%s", buf);
223 printFileInfo(fn, fsize, fmode, fmtime, frdev, fnlink,
224 fuser, fgroup, flink);
227 _("package has neither file owner or id lists\n"));
240 void rpmDisplayQueryTags(FILE * fp)
242 static const char * const tagTypeNames[] = {
243 "", "char", "int8", "int16", "int32", "int64",
244 "string", "blob", "argv", "i18nstring"
246 const char *tname, *sname;
247 rpmtd names = rpmtdNew();
248 (void) rpmTagGetNames(names, 1);
250 while ((tname = rpmtdNextString(names))) {
251 sname = tname + strlen("RPMTAG_");
252 if (rpmIsVerbose()) {
253 rpmTag tag = rpmTagGetValue(sname);
254 rpmTagType type = rpmTagGetType(tag) & RPM_MASK_TYPE;
255 fprintf(fp, "%-20s %6d", sname, tag);
256 if (type > RPM_NULL_TYPE && type <= RPM_MAX_TYPE)
257 fprintf(fp, " %s", tagTypeNames[type]);
259 fprintf(fp, "%s", sname);
263 rpmtdFreeData(names);
267 static int rpmgiShowMatches(QVA_t qva, rpmts ts)
269 rpmgi gi = qva->qva_gi;
272 while (rpmgiNext(gi) == RPMRC_OK) {
278 if (h == NULL) /* XXX perhaps stricter break instead? */
280 if ((rc = qva->qva_showPackage(qva, ts, h)) != 0)
282 if (qva->qva_source == RPMQV_DBOFFSET)
285 return ec + rpmgiNumErrors(gi);
288 int rpmcliShowMatches(QVA_t qva, rpmts ts)
293 while ((h = rpmdbNextIterator(qva->qva_mi)) != NULL) {
296 if ((rc = qva->qva_showPackage(qva, ts, h)) != 0)
298 if (qva->qva_source == RPMQV_DBOFFSET)
301 qva->qva_mi = rpmdbFreeIterator(qva->qva_mi);
305 int rpmQueryVerify(QVA_t qva, rpmts ts, const char * arg)
310 int provides_checked = 0;
312 (void) rpmdbCheckSignals();
314 if (qva->qva_showPackage == NULL)
317 switch (qva->qva_source) {
322 res = rpmgiShowMatches(qva, ts);
326 res = ((qva->qva_specQuery != NULL)
327 ? qva->qva_specQuery(ts, qva, arg) : 1);
331 qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_GROUP, arg, 0);
332 if (qva->qva_mi == NULL) {
333 rpmlog(RPMLOG_NOTICE,
334 _("group %s does not contain any packages\n"), arg);
337 res = rpmcliShowMatches(qva, ts);
340 case RPMQV_TRIGGEREDBY:
341 qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_TRIGGERNAME, arg, 0);
342 if (qva->qva_mi == NULL) {
343 rpmlog(RPMLOG_NOTICE, _("no package triggers %s\n"), arg);
346 res = rpmcliShowMatches(qva, ts);
350 { unsigned char MD5[16];
353 for (i = 0, s = arg; *s && isxdigit(*s); s++, i++)
356 rpmlog(RPMLOG_ERR, _("malformed %s: %s\n"), "pkgid", arg);
361 for (i = 0, t = MD5, s = arg; i < 16; i++, t++, s += 2)
362 *t = (rnibble(s[0]) << 4) | rnibble(s[1]);
364 qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_SIGMD5, MD5, sizeof(MD5));
365 if (qva->qva_mi == NULL) {
366 rpmlog(RPMLOG_NOTICE, _("no package matches %s: %s\n"),
370 res = rpmcliShowMatches(qva, ts);
374 for (i = 0, s = arg; *s && isxdigit(*s); s++, i++)
377 rpmlog(RPMLOG_ERR, _("malformed %s: %s\n"), "hdrid", arg);
381 qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_SHA1HEADER, arg, 0);
382 if (qva->qva_mi == NULL) {
383 rpmlog(RPMLOG_NOTICE, _("no package matches %s: %s\n"),
387 res = rpmcliShowMatches(qva, ts);
391 { unsigned char *digest, *t;
394 for (i = 0, s = arg; *s && isxdigit(*s); s++, i++)
396 /* XXX dunno the algorithm yet, just check we're in the ballpark */
397 if (i % 32 != 0 || i < 32 || i > 512) {
398 rpmlog(RPMLOG_ERR, _("malformed %s: %s\n"), "fileid", arg);
403 digest = t = xcalloc(diglen, sizeof(*digest));
404 for (i = 0, s = arg; i < diglen; i++, t++, s += 2)
405 *t = (rnibble(s[0]) << 4) | rnibble(s[1]);
407 qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_FILEDIGESTS, digest, diglen);
408 if (qva->qva_mi == NULL) {
409 rpmlog(RPMLOG_NOTICE, _("no package matches %s: %s\n"),
413 res = rpmcliShowMatches(qva, ts);
420 rpm_tid_t iid = strtoul(arg, &end, 0);
422 if ((*end) || (end == arg) || (iid == UINT_MAX)) {
423 rpmlog(RPMLOG_ERR, _("malformed %s: %s\n"), "tid", arg);
426 qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_INSTALLTID, &iid, sizeof(iid));
427 if (qva->qva_mi == NULL) {
428 rpmlog(RPMLOG_NOTICE, _("no package matches %s: %s\n"),
432 res = rpmcliShowMatches(qva, ts);
435 case RPMQV_WHATREQUIRES:
436 qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_REQUIRENAME, arg, 0);
437 if (qva->qva_mi == NULL) {
438 rpmlog(RPMLOG_NOTICE, _("no package requires %s\n"), arg);
441 res = rpmcliShowMatches(qva, ts);
444 case RPMQV_WHATPROVIDES:
446 provides_checked = 1;
447 qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_PROVIDENAME, arg, 0);
448 if (qva->qva_mi == NULL) {
449 rpmlog(RPMLOG_NOTICE, _("no package provides %s\n"), arg);
452 res = rpmcliShowMatches(qva, ts);
458 for (s = arg; *s != '\0'; s++)
459 if (!(*s == '.' || *s == '/'))
463 char fnbuf[PATH_MAX];
464 fn = realpath(arg, fnbuf);
465 fn = xstrdup( (fn != NULL ? fn : arg) );
466 } else if (*arg != '/') {
467 char *curDir = rpmGetCwd();
468 fn = (char *) rpmGetPath(curDir, "/", arg, NULL);
469 curDir = _free(curDir);
472 (void) rpmCleanPath(fn);
474 qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_BASENAMES, fn, 0);
475 if (qva->qva_mi == NULL && !provides_checked)
476 qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_PROVIDENAME, fn, 0);
478 if (qva->qva_mi == NULL) {
480 if (lstat(fn, &sb) != 0)
481 rpmlog(RPMLOG_ERR, _("file %s: %s\n"), fn, strerror(errno));
483 rpmlog(RPMLOG_NOTICE,
484 _("file %s is not owned by any package\n"), fn);
487 res = rpmcliShowMatches(qva, ts);
494 unsigned int recOffset = strtoul(arg, &end, 0);
496 if ((*end) || (end == arg) || (recOffset == UINT_MAX)) {
497 rpmlog(RPMLOG_ERR, _("invalid package number: %s\n"), arg);
500 rpmlog(RPMLOG_DEBUG, "package record number: %u\n", recOffset);
501 /* RPMDBI_PACKAGES */
502 qva->qva_mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES, &recOffset, sizeof(recOffset));
503 if (qva->qva_mi == NULL) {
505 _("record %u could not be read\n"), recOffset);
508 res = rpmcliShowMatches(qva, ts);
514 rpmdbMatchIterator mi;
515 mi = rpmtsInitIterator(ts, RPMDBI_LABEL, arg, 0);
516 while (rpmdbNextIterator(mi) != NULL) {
519 rpmdbFreeIterator(mi);
521 rpmlog(RPMLOG_NOTICE, _("package %s is not installed\n"), arg);
524 qva->qva_mi = rpmtsInitIterator(ts, RPMDBI_LABEL, arg, 0);
525 res = rpmcliShowMatches(qva, ts);
535 static int rpmcliArgIterHelper(rpmts ts, QVA_t qva, rpmTag tag, ARGV_const_t argv, rpmgiFlags gFlgs)
537 rpmRC rpmrc = RPMRC_NOTFOUND;
540 qva->qva_gi = rpmgiNew(ts, tag, NULL, 0);
541 qva->qva_rc = rpmgiSetArgs(qva->qva_gi, argv, ftsOpts, gFlgs);
543 if (qva->qva_gi != NULL && (rpmgiGetFlags(qva->qva_gi) & RPMGI_TSADD)) /* Load the ts with headers. */
544 while ((rpmrc = rpmgiNext(qva->qva_gi)) == RPMRC_OK)
546 if (rpmrc != RPMRC_NOTFOUND) {
547 qva->qva_gi = rpmgiFree(qva->qva_gi);
548 return 1; /* XXX should be no. of failures. */
550 /* FIX: argv can be NULL, cast to pass argv array */
551 ec = rpmQueryVerify(qva, ts, (tag == RPMDBI_PACKAGES)? (const char *) argv : NULL);
553 qva->qva_gi = rpmgiFree(qva->qva_gi);
557 int rpmcliArgIter(rpmts ts, QVA_t qva, ARGV_const_t argv)
561 switch (qva->qva_source) {
563 ec = rpmcliArgIterHelper(ts, qva, RPMDBI_PACKAGES, argv, RPMGI_NONE);
566 ec = rpmcliArgIterHelper(ts, qva, RPMDBI_ARGLIST, argv, giFlags);
569 ec = rpmcliArgIterHelper(ts, qva, RPMDBI_HDLIST, argv, giFlags);
573 ftsOpts = (RPMGI_COMFOLLOW | RPMGI_LOGICAL | RPMGI_NOSTAT);
574 ec = rpmcliArgIterHelper(ts, qva, RPMDBI_FTSWALK, argv, giFlags);
577 qva->qva_gi = rpmgiNew(ts, RPMDBI_ARGLIST, NULL, 0);
578 qva->qva_rc = rpmgiSetArgs(qva->qva_gi, argv, ftsOpts,
579 (giFlags | (RPMGI_NOGLOB|RPMGI_NOHEADER)));
580 while (rpmgiNext(qva->qva_gi) == RPMRC_OK) {
581 ec += rpmQueryVerify(qva, ts, rpmgiHdrPath(qva->qva_gi));
584 qva->qva_gi = rpmgiFree(qva->qva_gi);
591 int rpmcliQuery(rpmts ts, QVA_t qva, char * const * argv)
593 rpmVSFlags vsflags, ovsflags;
596 if (qva->qva_showPackage == NULL)
597 qva->qva_showPackage = showQueryPackage;
599 /* If --queryformat unspecified, then set default now. */
600 if (!(qva->qva_flags & _QUERY_FOR_BITS) && qva->qva_queryFormat == NULL) {
601 char * fmt = rpmExpand("%{?_query_all_fmt}\n", NULL);
602 if (fmt == NULL || strlen(fmt) <= 1) {
604 fmt = xstrdup("%{nvra}\n");
606 qva->qva_queryFormat = fmt;
609 vsflags = rpmExpandNumeric("%{?_vsflags_query}");
610 if (qva->qva_flags & VERIFY_DIGEST)
611 vsflags |= _RPMVSF_NODIGESTS;
612 if (qva->qva_flags & VERIFY_SIGNATURE)
613 vsflags |= _RPMVSF_NOSIGNATURES;
614 if (qva->qva_flags & VERIFY_HDRCHK)
615 vsflags |= RPMVSF_NOHDRCHK;
617 ovsflags = rpmtsSetVSFlags(ts, vsflags);
618 ec = rpmcliArgIter(ts, qva, argv);
619 vsflags = rpmtsSetVSFlags(ts, ovsflags);
621 if (qva->qva_showPackage == showQueryPackage)
622 qva->qva_showPackage = NULL;