Main Page Modules Data Structures File List Data Fields Globals Related Pages

# tools/rpmcache.c

Go to the documentation of this file.

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