#define _POSIX_C_SOURCE 200809L #include #include #include #include #include "zpm.h" #include "lib/jsw/jsw_hlib.h" #include "lib/jsw/jsw_atree.h" /* * find packages, and lib deps */ struct pkgloc { char *id; char *file; int info; int installed; }; struct search_ctl { char *localdb, *repodir, *pkgdir; struct zpm *zpmdb; glob_t repos; int matchallpkgfile; int matchinstalled; int suppressinstalled; int onlylocalinstalled; int verbose; int dbrepos; }; char *pathcat(char *dir, char *path) { size_t dirlen = 0, pathlen = 0; char *cat; /* chop off trailing / on dir */ if (dir) { dirlen = strlen(dir); while (dirlen && dir[dirlen-1] == '/') { dirlen--; } } if (path) { pathlen = strlen(path); while (*path && *path == '/') { path++; pathlen--; } } cat = malloc(dirlen + pathlen + 2); if (cat) { strncpy(cat, dir, dirlen); cat[dirlen] = '/'; strcpy(cat+dirlen+1, path); } return cat; } char *checkfile(char *pkgstr, char *path) { struct zpm pkgfile; char *pkgid = 0; if (!zpm_open(&pkgfile, path)) { return NULL; } pkgid = zpm_findpkg(&pkgfile, pkgstr, NULL); zpm_close(&pkgfile); return pkgid; } char *checkfileforlib(char *soname, char *path) { struct zpm pkgfile; char *pkgid = 0; if (!zpm_open(&pkgfile, path)) { return NULL; } pkgid = zpm_findlib(&pkgfile, soname, NULL); if (pkgfile.error) { fprintf(stderr, "sql error: %s\n", pkgfile.errmsg); } zpm_close(&pkgfile); return pkgid; } int find_globs(struct search_ctl *opt) { glob_t *repos; int rv; repos = &opt->repos; repos->gl_offs = 0; int globopts = 0; if (opt->repodir) { char *globstr = pathcat(opt->repodir, "*.repo"); rv = glob(globstr, globopts, NULL, repos); switch (rv) { case GLOB_NOSPACE: fprintf(stderr, "glob no mem\n"); return 0; case GLOB_ABORTED: fprintf(stderr, "glob abort\n"); return 0; case GLOB_NOMATCH: break; case 0: break; default: break; } globopts = GLOB_APPEND; free(globstr); } if (opt->pkgdir) { char *globstr = pathcat(opt->pkgdir, "*.zpm"); rv = glob(globstr, globopts, NULL, repos); switch (rv) { case GLOB_NOSPACE: fprintf(stderr, "glob no mem\n"); return 0; case GLOB_ABORTED: fprintf(stderr, "glob abort\n"); return 0; case GLOB_NOMATCH: break; case 0: break; default: break; } free(globstr); } return 1; } int find_lib(char *soname, struct search_ctl *opt, struct pkgloc *pkg) { char *latest = 0; char *installed = 0, *found = 0; char *pkgfile = 0; int rv; unsigned int i; if (opt->localdb) { installed = zpm_findlib(opt->zpmdb, soname, "status = 'installed'"); if (installed) { if (opt->verbose > 1) { fprintf(stderr, "library %s installed via %s\n", soname, installed); } /* we're done, the lib is installed */ return 2; } } for (i = 0; i < opt->repos.gl_pathc; i++) { if (opt->verbose > 1) { fprintf(stderr, "checking %s for %s\n", opt->repos.gl_pathv[i], soname); } found = checkfileforlib(soname, opt->repos.gl_pathv[i]); if (found) { if (opt->verbose > 1) { fprintf(stderr, "found %s\n", found); } rv = zpm_vercmp(found, latest); if (rv == 1) { latest = found; free(pkgfile); pkgfile = strdup(opt->repos.gl_pathv[i]); } } else if (opt->verbose > 1) { fprintf(stderr, "not found\n"); } } if (latest && pkgfile) { pkg->id = strdup(latest); pkg->file = pkgfile; return 1; } return 0; } struct pkgloc *find_package(char *pkgstr, struct search_ctl *opt) { char *latest = 0; char *installed = 0, *found = 0; char *pkgfile = 0; struct pkgloc *pkg = 0; int rv; unsigned int i; if (opt->localdb) { installed = zpm_findpkg(opt->zpmdb, pkgstr, "status = 'installed'"); if (installed) { latest = installed; pkgfile = opt->zpmdb->path; } if (!opt->onlylocalinstalled) { found = zpm_findpkg(opt->zpmdb, pkgstr, NULL); if (found) { rv = zpm_vercmp(found, latest); if (rv == 1) { latest = found; pkgfile = opt->zpmdb->path; } } } #if 0 if (opt->dbrepos) { zpm_foreach_repo(opt->zpmdb, search_repo, pkg) } #endif } for (i = 0; i < opt->repos.gl_pathc; i++) { if (!opt->matchallpkgfile && !strstr(opt->repos.gl_pathv[i], pkgstr) && !strstr(opt->repos.gl_pathv[i], ".repo") ) { continue; } found = checkfile(pkgstr, opt->repos.gl_pathv[i]); if (found) { rv = zpm_vercmp(found, latest); if (rv == 1) { latest = found; pkgfile = strdup(opt->repos.gl_pathv[i]); } } } if (latest && pkgfile) { pkg = malloc(sizeof *pkg); pkg->id = strdup(latest); pkg->file = pkgfile; pkg->installed = 0; if (installed) { pkg->installed = !strcmp(latest, installed); } } return pkg; } char *pkglibsneeded = "\ with pkglibs as (\ select distinct EN.needed as soname, PF.pkgid\ from elfneeded EN\ join packagefiles_pkgid PF on PF.hash = EN.file\ ),\ pkgprovides as (\ select distinct EL.soname, PF.pkgid\ from elflibraries EL\ join packagefiles_pkgid PF on PF.hash = EL.file\ )\ select distinct PL.soname, PP.soname is not null as selfsatisfied\ from pkglibs PL\ left join pkgprovides PP on PL.pkgid = PP.pkgid and PL.soname = PP.soname\ where PL.pkgid = %Q\ "; static int afind_strcmp(const void *a, const void *b) { return strcmp(a, b); } void checklibs(struct search_ctl *opts, jsw_hash_t *check, jsw_hash_t *forlibs, jsw_hash_t *nfound) { char *pkgid = 0, *pkgfile = 0; struct zpm src; /* checked_libs is a list of checked sonames * checked is a list of checked packages * needed is list of libs for package */ jsw_atree_t *needed = 0, *checked, *checked_libs; int libs; jsw_atrav_t *i; char *soname; checked = jsw_anew(afind_strcmp, (dup_f)strdup, (rel_f)free); checked_libs = jsw_anew(afind_strcmp, (dup_f)strdup, (rel_f)free); while (jsw_hsize(check) > 0) { free(pkgid); free(pkgfile); jsw_hreset(check); pkgid = strdup(jsw_hkey(check)); pkgfile = strdup(jsw_hitem(check)); if (!pkgid || !pkgfile) { break; } jsw_herase(check, pkgid); if (jsw_afind(checked, pkgid)) { /* already checked this one */ /* fprintf(stderr, "already checked %s\n", pkgid); */ continue; } fprintf(stderr, "checking libs for %s\n", pkgid); /* we do this now so we catch self deps */ if (!jsw_ainsert(checked, pkgid)) { fprintf(stderr, "checked insert failed\n"); exit(EXIT_FAILURE); } if (!jsw_afind(checked, pkgid)) { /* already checked this one */ fprintf(stderr, "checked find failed\n"); exit(EXIT_FAILURE); } /* get the libraries needed by this package */ if (!zpm_open(&src, pkgfile)) { fprintf(stderr, "can't zpm open %s\n", pkgfile); break; } if (needed) jsw_adelete(needed); needed = jsw_anew(afind_strcmp, (dup_f)strdup, (rel_f)free); libs = zpm_libraries_needed(&src, pkgid, needed); zpm_close(&src); if (libs == 0) { continue; } i = jsw_atnew(); for (soname = jsw_atfirst(i, needed); soname; soname = jsw_atnext(i)) { int found; struct pkgloc pkginfo; /* if it's in checked_libs, we've already looked at this one */ if (jsw_afind(checked_libs, soname)) { if (opts->verbose > 1) { fprintf(stderr, "already checked %s\n", soname); } continue; } #if 0 if (opts->verbose > 1) { fprintf(stderr, "checking for %s\n", soname); } #endif /* haven't found this soname */ jsw_ainsert(checked_libs, soname); found = find_lib(soname, opts, &pkginfo); if (!found) { if (!jsw_hinsert(nfound, soname, pkgid)) { fprintf(stderr,"insert error\n"); } continue; } if (found == 1) { /* if not alreacy checked, add to check */ if (!jsw_afind(checked, pkginfo.file)) { jsw_hinsert(check,pkginfo.id,pkginfo.file); } /* shouldn't be in there already */ jsw_hinsert(forlibs, pkginfo.id, pkginfo.file); free(pkginfo.file); free(pkginfo.id); } /* otherwise found == 2, so just continue */ } } free(pkgid); free(pkgfile); } void print_pkghash(jsw_hash_t *hash, int json) { const char *pkgid, *file; char *fmt; char *sep; int count = 0; fmt = json ? "\"%s\": \"%s\"" : "%s:%s"; sep = json ? ", " : "\n"; if (json) { printf("{ "); } if (jsw_hsize(hash) > 0) { for (jsw_hreset(hash); jsw_hitem(hash); jsw_hnext(hash)) { pkgid = jsw_hkey(hash); file = jsw_hitem(hash); if (count++) { printf("%s", sep); } printf(fmt, pkgid, file); } } if (json) printf(" }"); printf("\n"); } int main(int ac, char *av[]) { int option, argn; int findlibs = 0, json = 0; struct pkgloc *found; jsw_hash_t *packages, *check, *forlibs, *nolib; jsw_atree_t *nfound; struct search_ctl opt = { 0 }; struct zpm localdb; opt.dbrepos = 1; opt.localdb = getenv("ZPMDB"); if (!opt.localdb) opt.localdb = "/var/lib/zpm/local.db"; opt.pkgdir = getenv("ZPM_PACKAGE_DIR"); if (!opt.pkgdir) opt.pkgdir = "/var/lib/zpm/packages"; opt.repodir = getenv("ZPM_REPO_DIR"); if (!opt.repodir) opt.repodir = "/var/lib/zpm/repo"; /* -l also find packages needed for libs * -j output json * -q quiet - suppress output * -v verbose - * -d debug - trace each step * * environment: * ZPMDB - path to localdb, /var/lib/zpm/local.db * ZPM_REPO_DIR - path to *.repo files, '/var/lib/zpm/repo' * ZPM_PACKAGE_DIRS - : separated paths to *.zpm files, * '/var/lib/zpm/packages' * ZPM_ROOT_DIR :- prepends to above paths * * -M (missing) invert the output and only output missing * outputs * packages found (suppress with -P) * package strings not found (suppress with -S) * library dependency packages needed and found (suppress with -N) * library dependency packages already installed (suppress with -H) * library dependency packages in install set (suppress with -I) * library dependency sonames not found (suppress with -L) * * exit value is 0 if no missing libs and no bad package strings * exit value is non zero otherwise */ int output = 1; while ((option = getopt(ac, av, "ljqPRDvp:r:d:MiIO")) != -1) { switch (option) { case 'l': findlibs = 1; break; case 'j': json = 1; break; case 'q': output = 0; break; /* show installed files */ case 'i': opt.matchinstalled = 1; break; case 'I': opt.suppressinstalled = 1; break; /* only find localdb pkgs if installed */ case 'O': opt.onlylocalinstalled = 1; break; case 'd': opt.localdb = optarg; break; case 'p': opt.pkgdir = optarg; break; case 'r': opt.repodir = optarg; break; case 'P': opt.pkgdir = 0; break; case 'R': opt.repodir = 0; break; case 'D': opt.localdb = 0; break; /* matchallpkgfile means look at * all .zpm files, not just ones * that have the pkgid string prefix */ case 'M': opt.matchallpkgfile = 1; break; case 'v': opt.verbose++; break; default: exit(EXIT_FAILURE); break; } } argn = optind; if (opt.localdb) { if (zpm_open(&localdb, NULL)) { opt.zpmdb = &localdb; } else { fprintf(stderr, "can't open localdb\n"); return 2; } } if (!find_globs(&opt)) { fprintf(stderr, "bad globs\n"); return 3; } if (opt.verbose > 1) { unsigned int i; fprintf(stderr, "globs:"); for (i = 0; i < opt.repos.gl_pathc; i++) { fprintf(stderr, " %s", opt.repos.gl_pathv[i]); } fprintf(stderr, "\n"); } /* direct packages asked for */ packages = jsw_hnew(ac,NULL,afind_strcmp,(keydup_f)strdup, (itemdup_f)strdup,free,free); /* packages we need to check for libs */ check = jsw_hnew(ac,NULL,afind_strcmp,(keydup_f)strdup, (itemdup_f)strdup,free,free); /* packages we will also need for library dependences */ forlibs = jsw_hnew(ac,NULL,afind_strcmp,(keydup_f)strdup, (itemdup_f)strdup,free,free); /* libraries we didn't find */ nolib = jsw_hnew(ac,NULL,afind_strcmp,(keydup_f)strdup, (itemdup_f)strdup,free,free); /* packages asked for that we can't find */ nfound = jsw_anew(afind_strcmp, (dup_f)strdup, (rel_f)free); int arg; for (arg = argn; arg < ac; arg++) { found = find_package(av[arg], &opt); if (found && (opt.matchinstalled || !found->installed)) { if (found->installed && opt.suppressinstalled) { continue; } jsw_hinsert(packages, found->id, found->file); jsw_hinsert(check, found->id, found->file); free(found->id); free(found->file); free(found); } else { jsw_ainsert(nfound, av[arg]); } } if (findlibs) { checklibs(&opt, check, forlibs, nolib); /* remove from forlibs anything already explicitly * in packages */ } if (output) { if (jsw_hsize(packages)) { print_pkghash(packages, json); } if (jsw_hsize(forlibs)) { print_pkghash(forlibs, json); } } if (jsw_hsize(nolib) > 0) { for (jsw_hreset(nolib); jsw_hitem(nolib); jsw_hnext(nolib)) { const char *pkgid, *file; pkgid = jsw_hkey(nolib); file = jsw_hitem(nolib); fprintf(stderr, "no lib found %s:%s\n", pkgid, file); } } if (jsw_asize(nfound) > 0) { jsw_atrav_t *i; i = jsw_atnew(); char *pkgstr; for (pkgstr = jsw_atfirst(i, nfound); pkgstr; pkgstr = jsw_atnext(i)) { fprintf(stderr, "%s: not found\n", pkgstr); } } return jsw_asize(nfound) > 0 || jsw_hsize(nolib) > 0 ? 1 : 0; }