00001
00005 #include "system.h"
00006 #include <fnmatch.h>
00007 #include <fts.h>
00008
00009 #include <rpmcli.h>
00010
00011 #include "rpmps.h"
00012 #include "rpmdb.h"
00013 #include "rpmds.h"
00014 #include "rpmts.h"
00015
00016 #include "debug.h"
00017
00018 static int _debug = 0;
00019
00020
00021 static int noCache = 0;
00022
00023 static char ** ftsSet;
00024 static int ftsOpts = 0;
00025
00026 const char * bhpath;
00027 int bhpathlen = 0;
00028 int bhlvl = -1;
00029
00030 struct ftsglob_s {
00031 const char ** patterns;
00032 int fnflags;
00033 };
00034
00035 static struct ftsglob_s * bhglobs;
00036 static int nbhglobs = 5;
00037
00038 static int indent = 2;
00039
00040 typedef struct Item_s {
00041 const char * path;
00042 int_32 size;
00043 int_32 mtime;
00044 rpmds this;
00045 Header h;
00046 } * Item;
00047
00048 static Item * items = NULL;
00049 static int nitems = 0;
00050
00051 static inline Item freeItem(Item item) {
00052 if (item != NULL) {
00053 item->path = _free(item->path);
00054 item->this = rpmdsFree(item->this);
00055 item->h = headerFree(item->h);
00056 item = _free(item);
00057 }
00058 return NULL;
00059 }
00060
00061 static inline Item newItem(void) {
00062 Item item = xcalloc(1, sizeof(*item));
00063 return item;
00064 }
00065
00066 static int cmpItem(const void * a, const void * b) {
00067 Item aitem = *(Item *)a;
00068 Item bitem = *(Item *)b;
00069 int rc = strcmp(rpmdsN(aitem->this), rpmdsN(bitem->this));
00070 return rc;
00071 }
00072
00073 static void freeItems(void) {
00074 int i;
00075 for (i = 0; i < nitems; i++)
00076 items[i] = freeItem(items[i]);
00077 items = _free(items);
00078 nitems = 0;
00079 }
00080
00081 static int ftsCachePrint( rpmts ts, FILE * fp)
00082 {
00083 int rc = 0;
00084 int i;
00085
00086 if (fp == NULL) fp = stdout;
00087 for (i = 0; i < nitems; i++) {
00088 Item ip;
00089
00090 ip = items[i];
00091 if (ip == NULL) {
00092 rc = 1;
00093 break;
00094 }
00095
00096 fprintf(fp, "%s\n", ip->path);
00097 }
00098 return rc;
00099 }
00100
00101 static int ftsCacheUpdate(rpmts ts)
00102 {
00103 HGE_t hge = (HGE_t)headerGetEntryMinMemory;
00104 int_32 tid = rpmtsGetTid(ts);
00105 rpmdbMatchIterator mi;
00106 unsigned char * md5;
00107 int rc = 0;
00108 int i;
00109
00110 rc = rpmtsCloseDB(ts);
00111 rc = rpmDefineMacro(NULL, "_dbpath %{_cache_dbpath}", RMIL_CMDLINE);
00112 rc = rpmtsOpenDB(ts, O_RDWR);
00113 if (rc != 0)
00114 return rc;
00115
00116 for (i = 0; i < nitems; i++) {
00117 Item ip;
00118
00119 ip = items[i];
00120 if (ip == NULL) {
00121 rc = 1;
00122 break;
00123 }
00124
00125
00126 if (!hge(ip->h, RPMTAG_SIGMD5, NULL, (void **) &md5, NULL)
00127 || md5 == NULL)
00128 {
00129 rc = 1;
00130 break;
00131 }
00132 mi = rpmtsInitIterator(ts, RPMTAG_SIGMD5, md5, 16);
00133 rc = rpmdbGetIteratorCount(mi);
00134 mi = rpmdbFreeIterator(mi);
00135 if (rc) {
00136 rc = 0;
00137 continue;
00138 }
00139
00140
00141 rc = headerAddOrAppendEntry(ip->h, RPMTAG_CACHECTIME,
00142 RPM_INT32_TYPE, &tid, 1);
00143 if (rc != 1) break;
00144 rc = headerAddOrAppendEntry(ip->h, RPMTAG_CACHEPKGPATH,
00145 RPM_STRING_ARRAY_TYPE, &ip->path, 1);
00146 if (rc != 1) break;
00147 rc = headerAddOrAppendEntry(ip->h, RPMTAG_CACHEPKGSIZE,
00148 RPM_INT32_TYPE, &ip->size, 1);
00149 if (rc != 1) break;
00150 rc = headerAddOrAppendEntry(ip->h, RPMTAG_CACHEPKGMTIME,
00151 RPM_INT32_TYPE, &ip->mtime, 1);
00152 if (rc != 1) break;
00153
00154
00155 rc = rpmdbAdd(rpmtsGetRdb(ts), tid, ip->h, NULL, NULL);
00156 if (rc) break;
00157
00158 }
00159 return rc;
00160 }
00161
00164 static int archOkay( const char * pkgArch)
00165
00166 {
00167 if (pkgArch == NULL) return 0;
00168 return (rpmMachineScore(RPM_MACHTABLE_INSTARCH, pkgArch) ? 1 : 0);
00169 }
00170
00173 static int osOkay( const char * pkgOs)
00174
00175 {
00176 if (pkgOs == NULL) return 0;
00177 return (rpmMachineScore(RPM_MACHTABLE_INSTOS, pkgOs) ? 1 : 0);
00178 }
00179
00180 static int ftsStashLatest(FTSENT * fts, rpmts ts)
00181 {
00182 Header h = NULL;
00183 rpmds add = NULL;
00184 const char * arch;
00185 const char * os;
00186 struct stat sb, * st;
00187 int ec = -1;
00188 int i = 0;
00189
00190 rpmMessage(RPMMESS_DEBUG, "============== %s\n", fts->fts_accpath);
00191
00192
00193 { FD_t fd = Fopen(fts->fts_accpath, "r");
00194 rpmRC rpmrc;
00195 int xx;
00196
00197 if (fd == NULL || Ferror(fd)) {
00198 if (fd) xx = Fclose(fd);
00199 goto exit;
00200 }
00201
00202 rpmrc = rpmReadPackageFile(ts, fd, fts->fts_path, &h);
00203 xx = Fclose(fd);
00204 if (rpmrc != RPMRC_OK || h == NULL)
00205 goto exit;
00206 }
00207
00208 if (!headerGetEntry(h, RPMTAG_ARCH, NULL, (void **) &arch, NULL)
00209 || !headerGetEntry(h, RPMTAG_OS, NULL, (void **) &os, NULL))
00210 goto exit;
00211
00212
00213 if (!archOkay(arch) || !osOkay(os)) {
00214 ec = 0;
00215 goto exit;
00216 }
00217
00218 add = rpmdsThis(h, RPMTAG_REQUIRENAME, (RPMSENSE_EQUAL|RPMSENSE_LESS));
00219
00220 if (items != NULL && nitems > 0) {
00221 Item needle = memset(alloca(sizeof(*needle)), 0, sizeof(*needle));
00222 Item * found, * fneedle = &needle;
00223
00224 needle->this = add;
00225
00226 found = bsearch(fneedle, items, nitems, sizeof(*found), cmpItem);
00227
00228
00229 while (found > items && cmpItem(found-1, fneedle) == 0)
00230 found--;
00231
00232
00233 if (found != NULL)
00234 while (found < (items + nitems) && cmpItem(found, fneedle) == 0) {
00235 ec = rpmdsCompare(needle->this, (*found)->this);
00236 if (ec == 0) {
00237 found++;
00238 continue;
00239 }
00240 i = found - items;
00241 break;
00242 }
00243 }
00244
00245
00246
00247
00248
00249
00250
00251 if (ec == 0) {
00252 goto exit;
00253 } else if (ec == 1) {
00254 items[i] = freeItem(items[i]);
00255 } else {
00256 i = nitems++;
00257 items = xrealloc(items, nitems * sizeof(*items));
00258 }
00259
00260 items[i] = newItem();
00261 items[i]->path = xstrdup(fts->fts_path);
00262 st = fts->fts_statp;
00263 if (st == NULL && Stat(fts->fts_accpath, &sb) == 0)
00264 st = &sb;
00265
00266 if (st != NULL) {
00267 items[i]->size = st->st_size;
00268 items[i]->mtime = st->st_mtime;
00269 }
00270 st = NULL;
00271 items[i]->this = rpmdsThis(h, RPMTAG_PROVIDENAME, RPMSENSE_EQUAL);
00272 items[i]->h = headerLink(h);
00273
00274 if (nitems > 1)
00275 qsort(items, nitems, sizeof(*items), cmpItem);
00276
00277 #if 0
00278 fprintf(stderr, "\t%*s [%d] %s\n",
00279 indent * (fts->fts_level < 0 ? 0 : fts->fts_level), "",
00280 i, fts->fts_name);
00281 #endif
00282
00283 exit:
00284 h = headerFree(h);
00285 add = rpmdsFree(add);
00286 return ec;
00287 }
00288
00289 static const char * ftsInfoStrings[] = {
00290 "UNKNOWN",
00291 "D",
00292 "DC",
00293 "DEFAULT",
00294 "DNR",
00295 "DOT",
00296 "DP",
00297 "ERR",
00298 "F",
00299 "INIT",
00300 "NS",
00301 "NSOK",
00302 "SL",
00303 "SLNONE",
00304 "W",
00305 };
00306
00307 static const char * ftsInfoStr(int fts_info) {
00308 if (!(fts_info >= 1 && fts_info <= 14))
00309 fts_info = 0;
00310 return ftsInfoStrings[ fts_info ];
00311 }
00312
00313 static int ftsPrint(FTS * ftsp, FTSENT * fts, rpmts ts)
00314 {
00315 struct ftsglob_s * bhg;
00316 const char ** patterns;
00317 const char * pattern;
00318 const char * s;
00319 int lvl;
00320 int xx;
00321
00322 switch (fts->fts_info) {
00323 case FTS_D:
00324 if (fts->fts_pathlen < bhpathlen)
00325 break;
00326
00327
00328 if (bhlvl < 0) {
00329 if (fts->fts_pathlen == bhpathlen && !strcmp(fts->fts_path, bhpath))
00330 bhlvl = fts->fts_level;
00331 else
00332 break;
00333 }
00334 lvl = fts->fts_level - bhlvl;
00335
00336 if (lvl < 0)
00337 break;
00338
00339 #if 0
00340 if (_debug)
00341 fprintf(stderr, "FTS_%s\t%*s %s\n", ftsInfoStr(fts->fts_info),
00342 indent * (fts->fts_level < 0 ? 0 : fts->fts_level), "",
00343 fts->fts_name);
00344 #endif
00345
00346
00347 bhg = bhglobs;
00348
00349 if ((patterns = bhg->patterns) != NULL)
00350 while ((pattern = *patterns++) != NULL) {
00351 if (*pattern == '/')
00352 xx = fnmatch(pattern, fts->fts_path, bhg->fnflags);
00353 else
00354 xx = fnmatch(pattern, fts->fts_name, bhg->fnflags);
00355 if (xx == 0)
00356 break;
00357 }
00358
00359
00360 if (lvl == 0 || lvl >= nbhglobs)
00361 break;
00362 bhg += lvl;
00363
00364 if ((patterns = bhg->patterns) != NULL)
00365 while ((pattern = *patterns++) != NULL) {
00366 if (*pattern == '/')
00367 xx = fnmatch(pattern, fts->fts_path, bhg->fnflags);
00368 else
00369 xx = fnmatch(pattern, fts->fts_name, bhg->fnflags);
00370 if (xx == 0)
00371 break;
00372 else
00373 xx = Fts_set(ftsp, fts, FTS_SKIP);
00374 }
00375
00376 break;
00377 case FTS_DP:
00378 #if 0
00379 if (_debug)
00380 fprintf(stderr, "FTS_%s\t%*s %s\n", ftsInfoStr(fts->fts_info),
00381 indent * (fts->fts_level < 0 ? 0 : fts->fts_level), "",
00382 fts->fts_name);
00383 #endif
00384 break;
00385 case FTS_F:
00386 #if 0
00387 if (_debug)
00388 fprintf(stderr, "FTS_%s\t%*s %s\n", ftsInfoStr(fts->fts_info),
00389 indent * (fts->fts_level < 0 ? 0 : fts->fts_level), "",
00390 fts->fts_name);
00391 #endif
00392 if (fts->fts_level >= 0) {
00393
00394 if (!strcmp(fts->fts_parent->fts_name, "SRPMS")) {
00395 xx = Fts_set(ftsp, fts->fts_parent, FTS_SKIP);
00396 break;
00397 }
00398 }
00399
00400
00401 s = fts->fts_name + fts->fts_namelen + 1 - sizeof(".rpm");
00402 if (strcmp(s, ".rpm"))
00403 break;
00404
00405 xx = ftsStashLatest(fts, ts);
00406
00407 break;
00408 case FTS_NS:
00409 case FTS_DNR:
00410 case FTS_ERR:
00411 if (_debug)
00412 fprintf(stderr, "FTS_%s\t%*s %s\n", ftsInfoStr(fts->fts_info),
00413 indent * (fts->fts_level < 0 ? 0 : fts->fts_level), "",
00414 fts->fts_name);
00415 break;
00416 case FTS_DC:
00417 case FTS_DEFAULT:
00418 case FTS_DOT:
00419 case FTS_INIT:
00420 case FTS_NSOK:
00421 case FTS_SL:
00422 case FTS_SLNONE:
00423 case FTS_W:
00424 default:
00425 if (_debug)
00426 fprintf(stderr, "FTS_%s\t%*s %s\n", ftsInfoStr(fts->fts_info),
00427 indent * (fts->fts_level < 0 ? 0 : fts->fts_level), "",
00428 fts->fts_name);
00429 break;
00430 }
00431
00432 return 0;
00433 }
00434
00440 static void initGlobs( rpmts ts, const char ** argv)
00441 {
00442 char buf[BUFSIZ];
00443 int i;
00444
00445 buf[0] = '\0';
00446 if (argv != NULL && * argv != NULL) {
00447 const char * arg;
00448 int single = (Glob_pattern_p(argv[0], 0) && argv[1] == NULL);
00449 char * t;
00450
00451 t = buf;
00452 if (!single)
00453 t = stpcpy(t, "@(");
00454 while ((arg = *argv++) != NULL) {
00455 t = stpcpy(t, arg);
00456 *t++ = '|';
00457 }
00458 t[-1] = (single ? '\0' : ')');
00459 *t = '\0';
00460 }
00461
00462 bhpath = rpmExpand("%{_bhpath}", NULL);
00463 bhpathlen = strlen(bhpath);
00464
00465 ftsSet = xcalloc(2, sizeof(*ftsSet));
00466 ftsSet[0] = rpmExpand("%{_bhpath}", NULL);
00467
00468 nbhglobs = 5;
00469 bhglobs = xcalloc(nbhglobs, sizeof(*bhglobs));
00470 for (i = 0; i < nbhglobs; i++) {
00471 const char * pattern;
00472 const char * macro;
00473
00474 switch (i) {
00475 case 0:
00476 macro = "%{_bhpath}";
00477 break;
00478 case 1:
00479 macro = "%{_bhcoll}";
00480 break;
00481 case 2:
00482 macro = (buf[0] == '\0' ? "%{_bhN}" : buf);
00483 break;
00484 case 3:
00485 macro = "%{_bhVR}";
00486 break;
00487 case 4:
00488 macro = "%{_bhA}";
00489 break;
00490 default:
00491 macro = NULL;
00492 break;
00493 }
00494 bhglobs[i].patterns = xcalloc(2, sizeof(*bhglobs[i].patterns));
00495 if (macro == NULL)
00496 continue;
00497 pattern = rpmExpand(macro, NULL);
00498 if (pattern == NULL || *pattern == '\0') {
00499 pattern = _free(pattern);
00500 continue;
00501 }
00502 bhglobs[i].patterns[0] = pattern;
00503 bhglobs[i].fnflags = (FNM_PATHNAME | FNM_PERIOD | FNM_EXTMATCH);
00504 if (bhglobs[i].patterns[0] != NULL)
00505 rpmMessage(RPMMESS_DEBUG, "\t%d \"%s\"\n",
00506 i, bhglobs[i].patterns[0]);
00507 }
00508 }
00509
00510 static rpmVSFlags vsflags = 0;
00511
00512 static struct poptOption optionsTable[] = {
00513 { "nolegacy", '\0', POPT_BIT_SET, &vsflags, RPMVSF_NEEDPAYLOAD,
00514 N_("don't verify header+payload signature"), NULL },
00515
00516 { "nocache", '\0', POPT_ARG_VAL, &noCache, -1,
00517 N_("don't update cache database, only print package paths"), NULL },
00518
00519 { "comfollow", '\0', POPT_BIT_SET|POPT_ARGFLAG_DOC_HIDDEN,
00520 &ftsOpts, FTS_COMFOLLOW,
00521 N_("follow command line symlinks"), NULL },
00522 { "logical", '\0', POPT_BIT_SET|POPT_ARGFLAG_DOC_HIDDEN,
00523 &ftsOpts, FTS_LOGICAL,
00524 N_("logical walk"), NULL },
00525 { "nochdir", '\0', POPT_BIT_SET|POPT_ARGFLAG_DOC_HIDDEN,
00526 &ftsOpts, FTS_NOCHDIR,
00527 N_("don't change directories"), NULL },
00528 { "nostat", '\0', POPT_BIT_SET|POPT_ARGFLAG_DOC_HIDDEN,
00529 &ftsOpts, FTS_NOSTAT,
00530 N_("don't get stat info"), NULL },
00531 { "physical", '\0', POPT_BIT_SET|POPT_ARGFLAG_DOC_HIDDEN,
00532 &ftsOpts, FTS_PHYSICAL,
00533 N_("physical walk"), NULL },
00534 { "seedot", '\0', POPT_BIT_SET|POPT_ARGFLAG_DOC_HIDDEN,
00535 &ftsOpts, FTS_SEEDOT,
00536 N_("return dot and dot-dot"), NULL },
00537 { "xdev", '\0', POPT_BIT_SET|POPT_ARGFLAG_DOC_HIDDEN,
00538 &ftsOpts, FTS_XDEV,
00539 N_("don't cross devices"), NULL },
00540 { "whiteout", '\0', POPT_BIT_SET|POPT_ARGFLAG_DOC_HIDDEN,
00541 &ftsOpts, FTS_WHITEOUT,
00542 N_("return whiteout information"), NULL },
00543
00544 { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmcliAllPoptTable, 0,
00545 N_("Common options for all rpm modes and executables:"),
00546 NULL },
00547
00548 POPT_AUTOALIAS
00549 POPT_AUTOHELP
00550 POPT_TABLEEND
00551 };
00552
00553 int
00554 main(int argc, char *const argv[])
00555 {
00556 rpmts ts = NULL;
00557 poptContext optCon;
00558 const char * s;
00559 FTS * ftsp;
00560 FTSENT * fts;
00561 int ec = 1;
00562 rpmRC rpmrc;
00563 int xx;
00564
00565 optCon = rpmcliInit(argc, argv, optionsTable);
00566 if (optCon == NULL)
00567 exit(EXIT_FAILURE);
00568
00569
00570 s = rpmExpand("%{?_cache_dbpath}", NULL);
00571 if (!(s && *s))
00572 rpmrc = RPMRC_FAIL;
00573 else
00574 rpmrc = rpmMkdirPath(s, "cache_dbpath");
00575 s = _free(s);
00576 if (rpmrc != RPMRC_OK) {
00577 fprintf(stderr, _("%s: %%{_cache_dbpath} macro is mis-configured.\n"),
00578 __progname);
00579 exit(EXIT_FAILURE);
00580 }
00581
00582 ts = rpmtsCreate();
00583
00584 if (rpmcliQueryFlags & VERIFY_DIGEST)
00585 vsflags |= _RPMVSF_NODIGESTS;
00586 if (rpmcliQueryFlags & VERIFY_SIGNATURE)
00587 vsflags |= _RPMVSF_NOSIGNATURES;
00588 if (rpmcliQueryFlags & VERIFY_HDRCHK)
00589 vsflags |= RPMVSF_NOHDRCHK;
00590 (void) rpmtsSetVSFlags(ts, vsflags);
00591
00592 { int_32 tid = (int_32) time(NULL);
00593 (void) rpmtsSetTid(ts, tid);
00594 }
00595
00596 initGlobs(ts, poptGetArgs(optCon));
00597 if (ftsOpts == 0)
00598 ftsOpts = (FTS_COMFOLLOW | FTS_LOGICAL | FTS_NOSTAT);
00599
00600 if (noCache)
00601 ftsOpts |= FTS_NOSTAT;
00602 else
00603 ftsOpts &= ~FTS_NOSTAT;
00604
00605
00606 ftsp = Fts_open(ftsSet, ftsOpts, NULL);
00607 while((fts = Fts_read(ftsp)) != NULL) {
00608 xx = ftsPrint(ftsp, fts, ts);
00609 }
00610 xx = Fts_close(ftsp);
00611
00612 if (noCache)
00613 ec = ftsCachePrint(ts, stdout);
00614 else
00615 ec = ftsCacheUpdate(ts);
00616 if (ec) {
00617 fprintf(stderr, _("%s: cache operation failed: ec %d.\n"),
00618 __progname, ec);
00619 }
00620
00621 freeItems();
00622
00623 ts = rpmtsFree(ts);
00624
00625 optCon = rpmcliFini(optCon);
00626
00627 return ec;
00628 }