1 #define _POSIX_C_SOURCE 200809L
11 #include "lib/jsw/jsw_hlib.h"
12 #include "lib/jsw/jsw_atree.h"
15 * find packages, and lib deps
26 char *localdb, *repodir, *pkgdir;
31 int suppressinstalled;
32 int onlylocalinstalled;
38 void trace(int level, struct search_ctl *ctl, char *fmt, ...) {
41 if (level <= ctl->verbose) {
43 vfprintf(stderr, fmt, ap);
48 char *pathcat(char *dir, char *path) {
49 size_t dirlen = 0, pathlen = 0;
52 /* chop off trailing / on dir */
55 while (dirlen && dir[dirlen-1] == '/') {
61 pathlen = strlen(path);
62 while (*path && *path == '/') {
68 cat = malloc(dirlen + pathlen + 2);
70 strncpy(cat, dir, dirlen);
72 strcpy(cat+dirlen+1, path);
77 char *checkfile(char *pkgstr, char *path, int orgreater) {
81 if (!zpm_open(&pkgfile, path)) {
86 pkgid = zpm_findpkg_range(&pkgfile, pkgstr, 0, 0, 0);
88 pkgid = zpm_findpkg(&pkgfile, pkgstr, NULL);
95 char *checkfileforlib(char *soname, char *path) {
99 if (!zpm_open(&pkgfile, path)) {
103 pkgid = zpm_findlib(&pkgfile, soname, NULL);
105 fprintf(stderr, "sql error: %s\n", pkgfile.errmsg);
112 int find_globs(struct search_ctl *opt) {
122 char *globstr = pathcat(opt->repodir, "*.repo");
123 rv = glob(globstr, globopts, NULL, repos);
126 fprintf(stderr, "glob no mem\n");
129 fprintf(stderr, "glob abort\n");
138 globopts = GLOB_APPEND;
143 char *globstr = pathcat(opt->pkgdir, "*.zpm");
144 rv = glob(globstr, globopts, NULL, repos);
147 fprintf(stderr, "glob no mem\n");
150 fprintf(stderr, "glob abort\n");
166 * find a package and packagefile that supplies a library
168 int find_lib(char *soname, struct search_ctl *opt, struct pkgloc *pkg) {
170 char *installed = 0, *found = 0;
176 installed = zpm_findlib(opt->zpmdb, soname, "status = 'installed'");
178 if (opt->verbose > 1) {
179 fprintf(stderr, "library %s installed via %s\n", soname, installed);
181 /* we're done, the lib is installed */
186 for (i = 0; i < opt->repos.gl_pathc; i++) {
187 if (opt->verbose > 1) {
188 fprintf(stderr, "checking %s for %s\n", opt->repos.gl_pathv[i], soname);
190 found = checkfileforlib(soname, opt->repos.gl_pathv[i]);
192 if (opt->verbose > 1) {
193 fprintf(stderr, "found %s\n", found);
195 rv = zpm_vercmp(found, latest);
199 pkgfile = strdup(opt->repos.gl_pathv[i]);
201 } else if (opt->verbose > 1) {
202 fprintf(stderr, "not found\n");
206 if (latest && pkgfile) {
207 pkg->id = strdup(latest);
215 struct pkgloc *find_atleast_package(char *pkgstr, struct search_ctl *opt, int stopifinstalled) {
217 char *installed = 0, *found = 0;
219 struct pkgloc *pkg = 0;
224 installed = zpm_findpkg_range(opt->zpmdb, pkgstr, 0, "status = 'installed'", 0);
227 pkgfile = opt->zpmdb->path;
228 if (stopifinstalled) {
229 pkg = malloc(sizeof *pkg);
230 pkg->id = strdup(latest);
236 if (!opt->onlylocalinstalled) {
237 found = zpm_findpkg_range(opt->zpmdb, pkgstr, 0, NULL, 0);
239 rv = zpm_vercmp(found, latest);
242 pkgfile = opt->zpmdb->path;
248 for (i = 0; i < opt->repos.gl_pathc; i++) {
249 if (!opt->matchallpkgfile
250 && !strstr(opt->repos.gl_pathv[i], pkgstr)
251 && !strstr(opt->repos.gl_pathv[i], ".repo")
255 found = checkfile(pkgstr, opt->repos.gl_pathv[i], 1);
257 rv = zpm_vercmp(found, latest);
260 pkgfile = strdup(opt->repos.gl_pathv[i]);
265 if (latest && pkgfile) {
266 pkg = malloc(sizeof *pkg);
267 pkg->id = strdup(latest);
271 pkg->installed = !strcmp(latest, installed);
278 struct pkgloc *find_package(char *pkgstr, struct search_ctl *opt) {
280 char *installed = 0, *found = 0;
282 struct pkgloc *pkg = 0;
287 trace(1, opt, "checking local database for installed %s\n", pkgstr);
288 installed = zpm_findpkg(opt->zpmdb, pkgstr, "status = 'installed'");
290 trace(1, opt, "found installed %s as %s\n", pkgstr, installed);
292 pkgfile = opt->zpmdb->path;
295 if (!opt->onlylocalinstalled) {
296 trace(1, opt, "checking local database for any %s\n", pkgstr);
297 found = zpm_findpkg(opt->zpmdb, pkgstr, NULL);
299 trace(1, opt, "found %s as %s\n", pkgstr, installed);
300 rv = zpm_vercmp(found, latest);
303 pkgfile = opt->zpmdb->path;
310 zpm_foreach_repo(opt->zpmdb, search_repo, pkg)
315 trace(1, opt, "checking repositories\n");
316 for (i = 0; i < opt->repos.gl_pathc; i++) {
317 trace(1, opt, "checking repo/pkg %s\n", opt->repos.gl_pathv[i]);
318 if (!opt->matchallpkgfile
319 && !strstr(opt->repos.gl_pathv[i], pkgstr)
320 && !strstr(opt->repos.gl_pathv[i], ".repo")
322 trace(1, opt, "skipping repo/pkg %s\n", opt->repos.gl_pathv[i]);
325 found = checkfile(pkgstr, opt->repos.gl_pathv[i], 0);
326 trace(1, opt, "found %s\n", found);
328 trace(1, opt, "comparing found %s to latest (%s)\n", found, latest);
329 rv = zpm_vercmp(found, latest);
332 pkgfile = strdup(opt->repos.gl_pathv[i]);
334 trace(1, opt, "latest %s\n", latest);
338 if (latest && pkgfile) {
339 pkg = malloc(sizeof *pkg);
340 pkg->id = strdup(latest);
344 pkg->installed = !strcmp(latest, installed);
346 trace(1, opt, "set up pkgloc = {%s, %s, %d}\n", pkg->id, pkg->file, pkg->installed);
352 char *pkglibsneeded = "\
354 select distinct EN.needed as soname, PF.pkgid\
356 join packagefiles_pkgid PF on PF.hash = EN.file\
359 select distinct EL.soname, PF.pkgid\
360 from elflibraries EL\
361 join packagefiles_pkgid PF on PF.hash = EL.file\
363 select distinct PL.soname, PP.soname is not null as selfsatisfied\
365 left join pkgprovides PP on PL.pkgid = PP.pkgid and PL.soname = PP.soname\
369 char *pkgdeps = "select requires from packagedeps_pkgid where pkgid = %Q";
371 static int afind_strcmp(const void *a, const void *b) {
375 void checklibs(struct search_ctl *opts,
376 jsw_hash_t *check, jsw_hash_t *forlibs, jsw_hash_t *nfound) {
377 char *pkgid = 0, *pkgfile = 0;
380 /* checked_libs is a list of checked sonames
381 * checked is a list of checked packages
382 * needed is list of libs for package, or list of package deps
384 jsw_atree_t *needed = 0, *checked, *checked_libs;
388 char *soname, *package;
390 checked = jsw_anew(afind_strcmp, (dup_f)strdup, (rel_f)free);
391 checked_libs = jsw_anew(afind_strcmp, (dup_f)strdup, (rel_f)free);
393 while (jsw_hsize(check) > 0) {
399 pkgid = strdup(jsw_hkey(check));
400 pkgfile = strdup(jsw_hitem(check));
402 if (!pkgid || !pkgfile) {
406 jsw_herase(check, pkgid);
408 if (jsw_afind(checked, pkgid)) {
409 /* already checked this one */
410 /* fprintf(stderr, "already checked %s\n", pkgid); */
414 fprintf(stderr, "checking libs for %s\n", pkgid);
415 /* we do this now so we catch self deps */
416 if (!jsw_ainsert(checked, pkgid)) {
417 fprintf(stderr, "checked insert failed\n");
421 if (!jsw_afind(checked, pkgid)) {
422 /* already checked this one */
423 fprintf(stderr, "checked find failed\n");
427 /* get the libraries and packages needed by this package */
428 if (!zpm_open(&src, pkgfile)) {
429 fprintf(stderr, "can't zpm open %s\n", pkgfile);
434 needed = jsw_anew_str();
435 count = zpm_packages_needed(&src, pkgid, needed);
437 struct pkgloc *pkg = 0;
446 for (package = jsw_atfirst(i, needed); package; package = jsw_atnext(i)) {
447 pkg = find_atleast_package(package, opts, 1);
448 if (!pkg || pkg->installed) {
451 /* look for a package at least that version */
452 /* if package is installed, or already
453 * handled, skip. Otherwise, add it
454 * to the list of installed
465 needed = jsw_anew_str();
466 libs = zpm_libraries_needed(&src, pkgid, needed);
475 for (soname = jsw_atfirst(i, needed); soname; soname = jsw_atnext(i)) {
477 struct pkgloc pkginfo;
479 /* if it's in checked_libs, we've already looked at
481 if (jsw_afind(checked_libs, soname)) {
482 if (opts->verbose > 1) {
483 fprintf(stderr, "already checked %s\n", soname);
488 if (opts->verbose > 1) {
489 fprintf(stderr, "checking for %s\n", soname);
493 /* haven't found this soname */
494 jsw_ainsert(checked_libs, soname);
496 found = find_lib(soname, opts, &pkginfo);
498 if (!jsw_hinsert(nfound, soname, pkgid)) {
499 fprintf(stderr,"insert error\n");
505 /* if not alreacy checked, add to check */
506 if (!jsw_afind(checked, pkginfo.file)) {
507 jsw_hinsert(check,pkginfo.id,pkginfo.file);
509 /* shouldn't be in there already */
510 jsw_hinsert(forlibs, pkginfo.id, pkginfo.file);
514 /* otherwise found == 2, so just continue */
521 void print_pkghash(jsw_hash_t *hash, int json, int idonly) {
522 const char *pkgid, *file;
536 fmt = "\"%s\": \"%s\"";
554 if (jsw_hsize(hash) > 0) {
555 for (jsw_hreset(hash); jsw_hitem(hash); jsw_hnext(hash)) {
556 pkgid = jsw_hkey(hash);
557 file = jsw_hitem(hash);
564 printf(fmt, pkgid, file);
567 if (!json) printf("\n");
572 int main(int ac, char *av[]) {
573 int option, argn, rv = 0;
574 int findlibs = 0, json = 0, idonly = 0;
575 struct pkgloc *found;
576 jsw_hash_t *packages, *check, *forlibs, *nolib;
578 struct search_ctl opt = { 0 };
583 opt.localdb = getenv("ZPMDB");
584 if (!opt.localdb) opt.localdb = "/var/lib/zpm/local.db";
585 opt.pkgdir = getenv("ZPM_PACKAGE_DIR");
586 if (!opt.pkgdir) opt.pkgdir = "/var/lib/zpm/packages";
587 opt.repodir = getenv("ZPM_REPO_DIR");
588 if (!opt.repodir) opt.repodir = "/var/lib/zpm/repo";
590 /* -l also find packages needed for libs
592 * -q quiet - suppress output
594 * -d debug - trace each step
597 * ZPMDB - path to localdb, /var/lib/zpm/local.db
598 * ZPM_REPO_DIR - path to *.repo files, '/var/lib/zpm/repo'
599 * ZPM_PACKAGE_DIRS - : separated paths to *.zpm files,
600 * '/var/lib/zpm/packages'
601 * ZPM_ROOT_DIR :- prepends to above paths
603 * -M (missing) invert the output and only output missing
605 * packages found (suppress with -P)
606 * package strings not found (suppress with -S)
607 * library dependency packages needed and found (suppress with -N)
608 * library dependency packages already installed (suppress with -H)
609 * library dependency packages in install set (suppress with -I)
610 * library dependency sonames not found (suppress with -L)
612 * exit value is 0 if no missing libs and no bad package strings
613 * exit value is non zero otherwise
617 while ((option = getopt(ac, av, "fljqPRDvp:r:d:MiIOn")) != -1) {
619 case 'l': findlibs = 1; break;
620 case 'j': json = 1; break;
621 case 'q': output = 0; break;
622 /* show installed files */
623 case 'i': opt.matchinstalled = 1; break;
624 case 'I': opt.suppressinstalled = 1; break;
625 /* only find localdb pkgs if installed */
626 case 'O': opt.onlylocalinstalled = 1; break;
627 case 'f': opt.foundonly = 1; break;
628 case 'd': opt.localdb = optarg; break;
629 case 'p': opt.pkgdir = optarg; break;
630 case 'r': opt.repodir = optarg; break;
631 case 'P': opt.pkgdir = 0; break;
632 case 'R': opt.repodir = 0; break;
633 case 'D': opt.localdb = 0; break;
634 /* matchallpkgfile means look at
635 * all .zpm files, not just ones
636 * that have the pkgid string prefix
638 case 'M': opt.matchallpkgfile = 1; break;
639 case 'v': opt.verbose++; break;
640 case 'n': idonly = 1; break;
649 if (zpm_open(&localdb, NULL)) {
650 opt.zpmdb = &localdb;
652 fprintf(stderr, "can't open localdb\n");
657 if (!find_globs(&opt)) {
658 fprintf(stderr, "bad globs\n");
662 if (opt.verbose > 1) {
664 fprintf(stderr, "globs:");
665 for (i = 0; i < opt.repos.gl_pathc; i++) {
666 fprintf(stderr, " %s", opt.repos.gl_pathv[i]);
668 fprintf(stderr, "\n");
671 /* direct packages asked for */
672 packages = jsw_hnew(ac,NULL,afind_strcmp,(keydup_f)strdup,
673 (itemdup_f)strdup,free,free);
675 /* packages we need to check for libs */
676 check = jsw_hnew(ac,NULL,afind_strcmp,(keydup_f)strdup,
677 (itemdup_f)strdup,free,free);
679 /* packages we will also need for library dependences */
680 forlibs = jsw_hnew(ac,NULL,afind_strcmp,(keydup_f)strdup,
681 (itemdup_f)strdup,free,free);
683 /* libraries we didn't find */
684 nolib = jsw_hnew(ac,NULL,afind_strcmp,(keydup_f)strdup,
685 (itemdup_f)strdup,free,free);
687 /* packages asked for that we can't find */
688 nfound = jsw_anew(afind_strcmp, (dup_f)strdup, (rel_f)free);
691 for (arg = argn; arg < ac; arg++) {
692 found = find_package(av[arg], &opt);
693 if (found && (opt.matchinstalled || !found->installed)) {
694 if (found->installed && opt.suppressinstalled) {
697 jsw_hinsert(packages, found->id, found->file);
698 jsw_hinsert(check, found->id, found->file);
703 jsw_ainsert(nfound, av[arg]);
708 checklibs(&opt, check, forlibs, nolib);
709 /* remove from forlibs anything already explicitly
715 if (jsw_hsize(packages)) {
716 print_pkghash(packages, json, idonly);
718 if (jsw_hsize(forlibs)) {
719 print_pkghash(forlibs, json, idonly);
723 if (jsw_hsize(nolib) > 0) {
724 for (jsw_hreset(nolib); jsw_hitem(nolib); jsw_hnext(nolib)) {
725 const char *pkgid, *file;
726 pkgid = jsw_hkey(nolib);
727 file = jsw_hitem(nolib);
728 fprintf(stderr, "no lib found %s:%s\n", pkgid, file);
733 if (jsw_asize(nfound) > 0 && !opt.foundonly) {
738 for (pkgstr = jsw_atfirst(i, nfound); pkgstr; pkgstr = jsw_atnext(i)) {
739 fprintf(stderr, "%s: not found\n", pkgstr);