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