1 #define _POSIX_C_SOURCE 200809L
9 #include "lib/jsw/jsw_hlib.h"
10 #include "lib/jsw/jsw_atree.h"
13 * find packages, and lib deps
24 char *localdb, *repodir, *pkgdir;
29 int suppressinstalled;
30 int onlylocalinstalled;
36 char *pathcat(char *dir, char *path) {
37 size_t dirlen = 0, pathlen = 0;
40 /* chop off trailing / on dir */
43 while (dirlen && dir[dirlen-1] == '/') {
49 pathlen = strlen(path);
50 while (*path && *path == '/') {
56 cat = malloc(dirlen + pathlen + 2);
58 strncpy(cat, dir, dirlen);
60 strcpy(cat+dirlen+1, path);
65 char *checkfile(char *pkgstr, char *path, int orgreater) {
69 if (!zpm_open(&pkgfile, path)) {
74 pkgid = zpm_findpkg_range(&pkgfile, pkgstr, 0, 0, 0);
76 pkgid = zpm_findpkg(&pkgfile, pkgstr, NULL);
83 char *checkfileforlib(char *soname, char *path) {
87 if (!zpm_open(&pkgfile, path)) {
91 pkgid = zpm_findlib(&pkgfile, soname, NULL);
93 fprintf(stderr, "sql error: %s\n", pkgfile.errmsg);
100 int find_globs(struct search_ctl *opt) {
110 char *globstr = pathcat(opt->repodir, "*.repo");
111 rv = glob(globstr, globopts, NULL, repos);
114 fprintf(stderr, "glob no mem\n");
117 fprintf(stderr, "glob abort\n");
126 globopts = GLOB_APPEND;
131 char *globstr = pathcat(opt->pkgdir, "*.zpm");
132 rv = glob(globstr, globopts, NULL, repos);
135 fprintf(stderr, "glob no mem\n");
138 fprintf(stderr, "glob abort\n");
154 * find a package and packagefile that supplies a library
156 int find_lib(char *soname, struct search_ctl *opt, struct pkgloc *pkg) {
158 char *installed = 0, *found = 0;
164 installed = zpm_findlib(opt->zpmdb, soname, "status = 'installed'");
166 if (opt->verbose > 1) {
167 fprintf(stderr, "library %s installed via %s\n", soname, installed);
169 /* we're done, the lib is installed */
174 for (i = 0; i < opt->repos.gl_pathc; i++) {
175 if (opt->verbose > 1) {
176 fprintf(stderr, "checking %s for %s\n", opt->repos.gl_pathv[i], soname);
178 found = checkfileforlib(soname, opt->repos.gl_pathv[i]);
180 if (opt->verbose > 1) {
181 fprintf(stderr, "found %s\n", found);
183 rv = zpm_vercmp(found, latest);
187 pkgfile = strdup(opt->repos.gl_pathv[i]);
189 } else if (opt->verbose > 1) {
190 fprintf(stderr, "not found\n");
194 if (latest && pkgfile) {
195 pkg->id = strdup(latest);
203 struct pkgloc *find_atleast_package(char *pkgstr, struct search_ctl *opt, int stopifinstalled) {
205 char *installed = 0, *found = 0;
207 struct pkgloc *pkg = 0;
212 installed = zpm_findpkg_range(opt->zpmdb, pkgstr, 0, "status = 'installed'", 0);
215 pkgfile = opt->zpmdb->path;
216 if (stopifinstalled) {
217 pkg = malloc(sizeof *pkg);
218 pkg->id = strdup(latest);
224 if (!opt->onlylocalinstalled) {
225 found = zpm_findpkg_range(opt->zpmdb, pkgstr, 0, NULL, 0);
227 rv = zpm_vercmp(found, latest);
230 pkgfile = opt->zpmdb->path;
236 for (i = 0; i < opt->repos.gl_pathc; i++) {
237 if (!opt->matchallpkgfile
238 && !strstr(opt->repos.gl_pathv[i], pkgstr)
239 && !strstr(opt->repos.gl_pathv[i], ".repo")
243 found = checkfile(pkgstr, opt->repos.gl_pathv[i], 1);
245 rv = zpm_vercmp(found, latest);
248 pkgfile = strdup(opt->repos.gl_pathv[i]);
253 if (latest && pkgfile) {
254 pkg = malloc(sizeof *pkg);
255 pkg->id = strdup(latest);
259 pkg->installed = !strcmp(latest, installed);
266 struct pkgloc *find_package(char *pkgstr, struct search_ctl *opt) {
268 char *installed = 0, *found = 0;
270 struct pkgloc *pkg = 0;
275 installed = zpm_findpkg(opt->zpmdb, pkgstr, "status = 'installed'");
278 pkgfile = opt->zpmdb->path;
280 if (!opt->onlylocalinstalled) {
281 found = zpm_findpkg(opt->zpmdb, pkgstr, NULL);
283 rv = zpm_vercmp(found, latest);
286 pkgfile = opt->zpmdb->path;
293 zpm_foreach_repo(opt->zpmdb, search_repo, pkg)
298 for (i = 0; i < opt->repos.gl_pathc; i++) {
299 if (!opt->matchallpkgfile
300 && !strstr(opt->repos.gl_pathv[i], pkgstr)
301 && !strstr(opt->repos.gl_pathv[i], ".repo")
305 found = checkfile(pkgstr, opt->repos.gl_pathv[i], 0);
307 rv = zpm_vercmp(found, latest);
310 pkgfile = strdup(opt->repos.gl_pathv[i]);
315 if (latest && pkgfile) {
316 pkg = malloc(sizeof *pkg);
317 pkg->id = strdup(latest);
321 pkg->installed = !strcmp(latest, installed);
328 char *pkglibsneeded = "\
330 select distinct EN.needed as soname, PF.pkgid\
332 join packagefiles_pkgid PF on PF.hash = EN.file\
335 select distinct EL.soname, PF.pkgid\
336 from elflibraries EL\
337 join packagefiles_pkgid PF on PF.hash = EL.file\
339 select distinct PL.soname, PP.soname is not null as selfsatisfied\
341 left join pkgprovides PP on PL.pkgid = PP.pkgid and PL.soname = PP.soname\
345 char *pkgdeps = "select requires from packagedeps_pkgid where pkgid = %Q";
347 static int afind_strcmp(const void *a, const void *b) {
351 void checklibs(struct search_ctl *opts,
352 jsw_hash_t *check, jsw_hash_t *forlibs, jsw_hash_t *nfound) {
353 char *pkgid = 0, *pkgfile = 0;
356 /* checked_libs is a list of checked sonames
357 * checked is a list of checked packages
358 * needed is list of libs for package, or list of package deps
360 jsw_atree_t *needed = 0, *checked, *checked_libs;
364 char *soname, *package;
366 checked = jsw_anew(afind_strcmp, (dup_f)strdup, (rel_f)free);
367 checked_libs = jsw_anew(afind_strcmp, (dup_f)strdup, (rel_f)free);
369 while (jsw_hsize(check) > 0) {
375 pkgid = strdup(jsw_hkey(check));
376 pkgfile = strdup(jsw_hitem(check));
378 if (!pkgid || !pkgfile) {
382 jsw_herase(check, pkgid);
384 if (jsw_afind(checked, pkgid)) {
385 /* already checked this one */
386 /* fprintf(stderr, "already checked %s\n", pkgid); */
390 fprintf(stderr, "checking libs for %s\n", pkgid);
391 /* we do this now so we catch self deps */
392 if (!jsw_ainsert(checked, pkgid)) {
393 fprintf(stderr, "checked insert failed\n");
397 if (!jsw_afind(checked, pkgid)) {
398 /* already checked this one */
399 fprintf(stderr, "checked find failed\n");
403 /* get the libraries and packages needed by this package */
404 if (!zpm_open(&src, pkgfile)) {
405 fprintf(stderr, "can't zpm open %s\n", pkgfile);
410 needed = jsw_anew_str();
411 count = zpm_packages_needed(&src, pkgid, needed);
413 struct pkgloc *pkg = 0;
422 for (package = jsw_atfirst(i, needed); package; package = jsw_atnext(i)) {
423 pkg = find_atleast_package(package, opts, 1);
424 if (!pkg || pkg->installed) {
427 /* look for a package at least that version */
428 /* if package is installed, or already
429 * handled, skip. Otherwise, add it
430 * to the list of installed
441 needed = jsw_anew_str();
442 libs = zpm_libraries_needed(&src, pkgid, needed);
451 for (soname = jsw_atfirst(i, needed); soname; soname = jsw_atnext(i)) {
453 struct pkgloc pkginfo;
455 /* if it's in checked_libs, we've already looked at
457 if (jsw_afind(checked_libs, soname)) {
458 if (opts->verbose > 1) {
459 fprintf(stderr, "already checked %s\n", soname);
464 if (opts->verbose > 1) {
465 fprintf(stderr, "checking for %s\n", soname);
469 /* haven't found this soname */
470 jsw_ainsert(checked_libs, soname);
472 found = find_lib(soname, opts, &pkginfo);
474 if (!jsw_hinsert(nfound, soname, pkgid)) {
475 fprintf(stderr,"insert error\n");
481 /* if not alreacy checked, add to check */
482 if (!jsw_afind(checked, pkginfo.file)) {
483 jsw_hinsert(check,pkginfo.id,pkginfo.file);
485 /* shouldn't be in there already */
486 jsw_hinsert(forlibs, pkginfo.id, pkginfo.file);
490 /* otherwise found == 2, so just continue */
497 void print_pkghash(jsw_hash_t *hash, int json, int idonly) {
498 const char *pkgid, *file;
512 fmt = "\"%s\": \"%s\"";
530 if (jsw_hsize(hash) > 0) {
531 for (jsw_hreset(hash); jsw_hitem(hash); jsw_hnext(hash)) {
532 pkgid = jsw_hkey(hash);
533 file = jsw_hitem(hash);
540 printf(fmt, pkgid, file);
543 if (!json) printf("\n");
548 int main(int ac, char *av[]) {
549 int option, argn, rv = 0;
550 int findlibs = 0, json = 0, idonly = 0;
551 struct pkgloc *found;
552 jsw_hash_t *packages, *check, *forlibs, *nolib;
554 struct search_ctl opt = { 0 };
559 opt.localdb = getenv("ZPMDB");
560 if (!opt.localdb) opt.localdb = "/var/lib/zpm/local.db";
561 opt.pkgdir = getenv("ZPM_PACKAGE_DIR");
562 if (!opt.pkgdir) opt.pkgdir = "/var/lib/zpm/packages";
563 opt.repodir = getenv("ZPM_REPO_DIR");
564 if (!opt.repodir) opt.repodir = "/var/lib/zpm/repo";
566 /* -l also find packages needed for libs
568 * -q quiet - suppress output
570 * -d debug - trace each step
573 * ZPMDB - path to localdb, /var/lib/zpm/local.db
574 * ZPM_REPO_DIR - path to *.repo files, '/var/lib/zpm/repo'
575 * ZPM_PACKAGE_DIRS - : separated paths to *.zpm files,
576 * '/var/lib/zpm/packages'
577 * ZPM_ROOT_DIR :- prepends to above paths
579 * -M (missing) invert the output and only output missing
581 * packages found (suppress with -P)
582 * package strings not found (suppress with -S)
583 * library dependency packages needed and found (suppress with -N)
584 * library dependency packages already installed (suppress with -H)
585 * library dependency packages in install set (suppress with -I)
586 * library dependency sonames not found (suppress with -L)
588 * exit value is 0 if no missing libs and no bad package strings
589 * exit value is non zero otherwise
593 while ((option = getopt(ac, av, "fljqPRDvp:r:d:MiIOn")) != -1) {
595 case 'l': findlibs = 1; break;
596 case 'j': json = 1; break;
597 case 'q': output = 0; break;
598 /* show installed files */
599 case 'i': opt.matchinstalled = 1; break;
600 case 'I': opt.suppressinstalled = 1; break;
601 /* only find localdb pkgs if installed */
602 case 'O': opt.onlylocalinstalled = 1; break;
603 case 'f': opt.foundonly = 1; break;
604 case 'd': opt.localdb = optarg; break;
605 case 'p': opt.pkgdir = optarg; break;
606 case 'r': opt.repodir = optarg; break;
607 case 'P': opt.pkgdir = 0; break;
608 case 'R': opt.repodir = 0; break;
609 case 'D': opt.localdb = 0; break;
610 /* matchallpkgfile means look at
611 * all .zpm files, not just ones
612 * that have the pkgid string prefix
614 case 'M': opt.matchallpkgfile = 1; break;
615 case 'v': opt.verbose++; break;
616 case 'n': idonly = 1; break;
625 if (zpm_open(&localdb, NULL)) {
626 opt.zpmdb = &localdb;
628 fprintf(stderr, "can't open localdb\n");
633 if (!find_globs(&opt)) {
634 fprintf(stderr, "bad globs\n");
638 if (opt.verbose > 1) {
640 fprintf(stderr, "globs:");
641 for (i = 0; i < opt.repos.gl_pathc; i++) {
642 fprintf(stderr, " %s", opt.repos.gl_pathv[i]);
644 fprintf(stderr, "\n");
647 /* direct packages asked for */
648 packages = jsw_hnew(ac,NULL,afind_strcmp,(keydup_f)strdup,
649 (itemdup_f)strdup,free,free);
651 /* packages we need to check for libs */
652 check = jsw_hnew(ac,NULL,afind_strcmp,(keydup_f)strdup,
653 (itemdup_f)strdup,free,free);
655 /* packages we will also need for library dependences */
656 forlibs = jsw_hnew(ac,NULL,afind_strcmp,(keydup_f)strdup,
657 (itemdup_f)strdup,free,free);
659 /* libraries we didn't find */
660 nolib = jsw_hnew(ac,NULL,afind_strcmp,(keydup_f)strdup,
661 (itemdup_f)strdup,free,free);
663 /* packages asked for that we can't find */
664 nfound = jsw_anew(afind_strcmp, (dup_f)strdup, (rel_f)free);
667 for (arg = argn; arg < ac; arg++) {
668 found = find_package(av[arg], &opt);
669 if (found && (opt.matchinstalled || !found->installed)) {
670 if (found->installed && opt.suppressinstalled) {
673 jsw_hinsert(packages, found->id, found->file);
674 jsw_hinsert(check, found->id, found->file);
679 jsw_ainsert(nfound, av[arg]);
684 checklibs(&opt, check, forlibs, nolib);
685 /* remove from forlibs anything already explicitly
691 if (jsw_hsize(packages)) {
692 print_pkghash(packages, json, idonly);
694 if (jsw_hsize(forlibs)) {
695 print_pkghash(forlibs, json, idonly);
699 if (jsw_hsize(nolib) > 0) {
700 for (jsw_hreset(nolib); jsw_hitem(nolib); jsw_hnext(nolib)) {
701 const char *pkgid, *file;
702 pkgid = jsw_hkey(nolib);
703 file = jsw_hitem(nolib);
704 fprintf(stderr, "no lib found %s:%s\n", pkgid, file);
709 if (jsw_asize(nfound) > 0 && !opt.foundonly) {
714 for (pkgstr = jsw_atfirst(i, nfound); pkgstr; pkgstr = jsw_atnext(i)) {
715 fprintf(stderr, "%s: not found\n", pkgstr);