#include "zpm.h"
#include "sqlite3.h"
+#include "lib/jsw/jsw_atree.h"
#define DMARK fprintf(stderr, "mark %s %s:%d\n", __FILE__, __func__, __LINE__)
zpm->errmsg = strdup((const char *)sqlite3_errmsg(zpm->db));
}
+char *zpm_findlib(struct zpm *zpm, char *soname, char *where) {
+ char *select = "select pkgid, package, version, release from packagefiles_status join elflibraries on file = hash";
+ char *group = "order by package, version collate vercmp desc, cast(release as integer)";
+ sqlite3_str *sql;
+ char *query, *pkgid = 0;
+
+ /* null pkgstr find "best" package
+ * best is shortest complete package if any are complete
+ * shortest incomplete if any are incomplete
+ * where more than one version is in those sets, best is
+ * latest as determined by vercmp
+ */
+
+ sql = sqlite3_str_new(zpm->db);
+
+ sqlite3_str_appendall(sql, select);
+ sqlite3_str_appendf(sql, " where soname = %Q", soname);
+
+ if (where) {
+ sqlite3_str_appendall(sql, " and ");
+ sqlite3_str_appendall(sql, where);
+ }
+
+ sqlite3_str_appendall(sql, " ");
+ sqlite3_str_appendall(sql, group);
+ sqlite3_str_appendall(sql, " limit 1;");
+
+ if (sqlite3_str_errcode(sql)) {
+ zpm->error = 1;
+ sqlite3_free(sqlite3_str_finish(sql));
+ return 0;
+ }
+
+ query = sqlite3_str_finish(sql);
+
+ pkgid = zpm_db_string(zpm, query);;
+
+ sqlite3_free(query);
+
+ return pkgid;
+}
+
+int zpm_libraries_needed(struct zpm *zpm, char *pkgid, jsw_atree_t *list) {
+ 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\
+ ";
+
+ sqlite3_stmt *st;
+ int count = 0;
+ int rv;
+
+ st = zpm_dbquery(zpm, pkglibsneeded, pkgid);
+
+ while ((rv = sqlite3_step(st)) == SQLITE_ROW) {
+ char *soname;
+ int self;
+
+ soname = (char *)sqlite3_column_text(st, 0);
+ /* TODO check and skip self sats */
+ self = sqlite3_column_int(st,1);
+ if (self) {
+ continue;
+ }
+ count++;
+
+ /* if it's in needed, we've already looked at this one */
+ if (list && !jsw_afind(list, soname)) {
+ jsw_ainsert(list, soname);
+ }
+ }
+
+ sqlite3_finalize(st);
+ return count;
+}
+
char *zpm_findpkg(struct zpm *zpm, char *pkgstr, char *where) {
char *select = "select pkgid, package, version, release from packages_pkgid";
+#if 0
char *group = "group by package having max( version||'-'||release collate vercmp) order by length(package), package, version||'-'||release collate vercmp";
-
-// char *order = "order by package, version collate vercmp, cast(release as integer)";
+#else
+ char *group = "order by package, version collate vercmp desc, cast(release as integer)";
+ //group = "order by package, version , cast(release as integer)";
+#endif
sqlite3_str *sql;
char *query, *pkgid = 0;
--- /dev/null
+#define _POSIX_C_SOURCE 200809L
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <glob.h>
+
+#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;
+};
+
+char *checkfile(char *pkgstr, char *path) {
+ struct zpm pkgfile;
+ char *pkgid = 0;
+
+ if (!zpm_open(&pkgfile, path)) {
+ return NULL;
+ }
+
+ pkgid = zpm_findpkg(&pkgfile, pkgstr, "hash is not 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, "hash is not null");
+ if (pkgfile.error) {
+ fprintf(stderr, "sql error: %s\n", pkgfile.errmsg);
+ }
+ zpm_close(&pkgfile);
+
+ return pkgid;
+}
+
+int find_lib(char *soname, struct pkgloc *pkg) {
+ char *latest = 0;
+ char *installed = 0, *found = 0;
+ char *pkgfile = 0;
+ int rv;
+ struct zpm localdb;
+
+ if (zpm_open(&localdb, NULL)) {
+ installed = zpm_findlib(&localdb, soname, "status = 'installed'");
+ zpm_close(&localdb);
+
+ if (installed) {
+ /* we're done, the lib is installed */
+ return 2;
+ }
+ } else {
+ fprintf(stderr, "can't open localdb\n");
+ }
+
+ glob_t repos;
+ repos.gl_offs = 0;
+ rv = glob("/var/lib/zpm/repos/*.repo", 0, NULL, &repos);
+ switch (rv) {
+ case GLOB_NOSPACE:
+ fprintf(stderr, "glob no mem\n");
+ exit(EXIT_FAILURE); break;
+ case GLOB_ABORTED:
+ fprintf(stderr, "glob abort\n");
+ exit(EXIT_FAILURE); break;
+ case GLOB_NOMATCH:
+ break;
+ case 0:
+ break;
+ default:
+ break;
+ }
+
+ unsigned i;
+ for (i = 0; i < repos.gl_pathc; i++) {
+ found = checkfileforlib(soname, repos.gl_pathv[i]);
+ if (found) {
+ rv = zpm_vercmp(found, latest);
+ if (rv == 1) {
+ latest = found;
+ free(pkgfile);
+ pkgfile = strdup(repos.gl_pathv[i]);
+ }
+ }
+ }
+
+ globfree(&repos);
+ repos.gl_offs = 0;
+
+ char *pkgdir = "/home/zoranix/zrepo/packages";
+ char *pkgglob = 0;
+
+ size_t globlen = strlen(pkgdir)+7;
+ pkgglob = malloc(globlen+1);
+ snprintf(pkgglob, globlen, "%s/*.zpm", pkgdir);
+ rv = glob(pkgglob, 0, NULL, &repos);
+ free(pkgglob);
+
+ switch (rv) {
+ case GLOB_NOSPACE:
+ exit(EXIT_FAILURE); break;
+ case GLOB_ABORTED:
+ exit(EXIT_FAILURE); break;
+ case GLOB_NOMATCH: break;
+ case 0:
+ break;
+ default:
+ break;
+ }
+
+ for (i = 0; i < repos.gl_pathc; i++) {
+ found = checkfileforlib(soname, repos.gl_pathv[i]);
+ if (found) {
+ rv = zpm_vercmp(found, latest);
+ if (rv == 1) {
+ latest = found;
+ free(pkgfile);
+ pkgfile = strdup(repos.gl_pathv[i]);
+ }
+ }
+ }
+
+ if (latest && pkgfile) {
+ pkg->id = strdup(latest);
+ pkg->file = pkgfile;
+ return 1;
+ }
+
+ return 0;
+}
+
+struct pkgloc *find_package(char *pkgstr) {
+ char *latest = 0;
+ char *installed = 0, *found = 0;
+ char *pkgfile;
+ struct pkgloc *pkg = 0;
+ int rv;
+ struct zpm localdb;
+
+ if (zpm_open(&localdb, NULL)) {
+ installed = zpm_findpkg(&localdb, pkgstr, "status = 'installed'");
+ if (installed) {
+ latest = installed;
+ }
+ found = zpm_findpkg(&localdb, pkgstr, NULL);
+ if (found) {
+ rv = zpm_vercmp(found, latest);
+ if (rv == 1) {
+ latest = found;
+ pkgfile = localdb.path;
+ }
+ }
+ zpm_close(&localdb);
+ } else {
+ fprintf(stderr, "can't open localdb\n");
+ }
+
+ glob_t repos;
+ repos.gl_offs = 0;
+ rv = glob("/var/lib/zpm/repos/*.repo", 0, NULL, &repos);
+ switch (rv) {
+ case GLOB_NOSPACE:
+ fprintf(stderr, "glob no mem\n");
+ exit(EXIT_FAILURE); break;
+ case GLOB_ABORTED:
+ fprintf(stderr, "glob abort\n");
+ exit(EXIT_FAILURE); break;
+ case GLOB_NOMATCH:
+ break;
+ case 0:
+ break;
+ default:
+ break;
+ }
+
+ unsigned i;
+ for (i = 0; i < repos.gl_pathc; i++) {
+ found = checkfile(pkgstr, repos.gl_pathv[i]);
+ if (found) {
+ rv = zpm_vercmp(found, latest);
+ if (rv == 1) {
+ latest = found;
+ pkgfile = repos.gl_pathv[i];
+ }
+ }
+ }
+
+ globfree(&repos);
+ repos.gl_offs = 0;
+
+ char *pkgdir = "/home/zoranix/zrepo/packages";
+ char *pkgglob = 0;
+
+ size_t globlen = strlen(pkgdir)+strlen(pkgstr)+7;
+ pkgglob = malloc(globlen+1);
+ snprintf(pkgglob, globlen, "%s/%s*.zpm", pkgdir, pkgstr);
+
+ rv = glob(pkgglob, 0, NULL, &repos);
+ switch (rv) {
+ case GLOB_NOSPACE:
+ exit(EXIT_FAILURE); break;
+ case GLOB_ABORTED:
+ exit(EXIT_FAILURE); break;
+ case GLOB_NOMATCH: break;
+ case 0:
+ break;
+ default:
+ break;
+ }
+
+ for (i = 0; i < repos.gl_pathc; i++) {
+ found = checkfile(pkgstr, repos.gl_pathv[i]);
+ if (found) {
+ rv = zpm_vercmp(found, latest);
+ if (rv == 1) {
+ latest = found;
+ pkgfile = strdup(repos.gl_pathv[i]);
+ }
+ }
+ }
+
+ if (latest && pkgfile) {
+ pkg = malloc(sizeof *pkg);
+ pkg->id = strdup(latest);
+ pkg->file = pkgfile;
+ }
+
+ 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\
+ ";
+
+void checklibs(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((cmp_f)strcmp, (dup_f)strdup, (rel_f)free);
+ checked_libs = jsw_anew((cmp_f)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 */
+ continue;
+ }
+
+ /* we do this now so we catch self deps */
+ jsw_ainsert(checked, pkgid);
+
+ /* 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((cmp_f)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)) {
+ continue;
+ }
+
+ /* haven't found this soname */
+ jsw_ainsert(checked_libs, soname);
+
+ found = find_lib(soname, &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);
+ }
+ /* otherwise found == 2, so just continue */
+ if (found) {
+ free(pkginfo.file);
+ free(pkginfo.id);
+ }
+ }
+ }
+ free(pkgid);
+ free(pkgfile);
+}
+
+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;
+}
+
+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 opt, argn;
+ int findlibs = 0, json = 0;
+ struct pkgloc *found;
+ jsw_hash_t *packages, *check, *forlibs, *nolib;
+ jsw_atree_t *nfound;
+
+ /* -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/repos'
+ * 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 ((opt = getopt(ac, av, "lj")) != -1) {
+ switch (opt) {
+ case 'l': findlibs = 1; break;
+ case 'j': json = 1; break;
+ case 'q': output = 0;
+ default:
+ exit(EXIT_FAILURE);
+ break;
+ }
+ }
+ argn = optind;
+
+ packages = jsw_hnew(ac,NULL,(cmp_f)strcmp,(keydup_f)strdup,
+ (itemdup_f)strdup,free,free);
+ check = jsw_hnew(ac,NULL,(cmp_f)strcmp,(keydup_f)strdup,
+ (itemdup_f)strdup,free,free);
+ forlibs = jsw_hnew(ac,NULL,(cmp_f)strcmp,(keydup_f)strdup,
+ (itemdup_f)strdup,free,free);
+ nolib = jsw_hnew(ac,NULL,(cmp_f)strcmp,(keydup_f)strdup,
+ (itemdup_f)strdup,free,free);
+ nfound = jsw_anew((cmp_f)strcmp, (dup_f)strdup, (rel_f)free);
+
+ int arg;
+ for (arg = argn; arg < ac; arg++) {
+ found = find_package(av[arg]);
+ if (found) {
+ 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(check, forlibs, nolib);
+ }
+
+ if (output) {
+ print_pkghash(packages, json);
+ 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:\n", pkgstr);
+ }
+ }
+
+ return jsw_asize(nfound) > 0 || jsw_hsize(nolib) > 0 ? 1 : 0;
+}