]> pd.if.org Git - zpackage/blob - src/search.c
support for package dependencies
[zpackage] / src / search.c
1 #define _POSIX_C_SOURCE 200809L
2 #include <stdlib.h>
3 #include <string.h>
4 #include <unistd.h>
5
6 #include <glob.h>
7
8 #include "zpm.h"
9 #include "lib/jsw/jsw_hlib.h"
10 #include "lib/jsw/jsw_atree.h"
11
12 /*
13  * find packages, and lib deps
14  */
15
16 struct pkgloc {
17         char *id;
18         char *file;
19         int info;
20         int installed;
21 };
22
23 struct search_ctl {
24         char *localdb, *repodir, *pkgdir;
25         struct zpm *zpmdb;
26         glob_t repos;
27         int matchallpkgfile;
28         int matchinstalled;
29         int suppressinstalled;
30         int onlylocalinstalled;
31         int foundonly;
32         int verbose;
33         int dbrepos;
34 };
35
36 char *pathcat(char *dir, char *path) {
37         size_t dirlen = 0, pathlen = 0;
38         char *cat;
39
40         /* chop off trailing / on dir */
41         if (dir) {
42                 dirlen = strlen(dir);
43                 while (dirlen && dir[dirlen-1] == '/') {
44                         dirlen--;
45                 }
46         }
47
48         if (path) {
49                 pathlen = strlen(path);
50                 while (*path && *path == '/') {
51                         path++;
52                         pathlen--;
53                 }
54         }
55
56         cat = malloc(dirlen + pathlen + 2);
57         if (cat) {
58                 strncpy(cat, dir, dirlen);
59                 cat[dirlen] = '/';
60                 strcpy(cat+dirlen+1, path);
61         }
62         return cat;
63 }
64
65 char *checkfile(char *pkgstr, char *path, int orgreater) {
66         struct zpm pkgfile;
67         char *pkgid = 0;
68
69         if (!zpm_open(&pkgfile, path)) {
70                 return NULL;
71         }
72         
73         if (orgreater) {
74                 pkgid = zpm_findpkg_range(&pkgfile, pkgstr, 0, 0, 0);
75         } else {
76                 pkgid = zpm_findpkg(&pkgfile, pkgstr, NULL);
77         }
78         zpm_close(&pkgfile);
79
80         return pkgid;
81 }
82
83 char *checkfileforlib(char *soname, char *path) {
84         struct zpm pkgfile;
85         char *pkgid = 0;
86
87         if (!zpm_open(&pkgfile, path)) {
88                 return NULL;
89         }
90         
91         pkgid = zpm_findlib(&pkgfile, soname, NULL);
92         if (pkgfile.error) {
93                 fprintf(stderr, "sql error: %s\n", pkgfile.errmsg);
94         }
95         zpm_close(&pkgfile);
96
97         return pkgid;
98 }
99
100 int find_globs(struct search_ctl *opt) {
101         glob_t *repos;
102         int rv;
103
104         repos = &opt->repos;
105         repos->gl_offs = 0;
106
107         int globopts = 0;
108
109         if (opt->repodir) {
110                 char *globstr = pathcat(opt->repodir, "*.repo");
111                 rv = glob(globstr, globopts, NULL, repos);
112                 switch (rv) {
113                         case GLOB_NOSPACE:
114                                 fprintf(stderr, "glob no mem\n");
115                                 return 0;
116                         case GLOB_ABORTED:
117                                 fprintf(stderr, "glob abort\n");
118                                 return 0;
119                         case GLOB_NOMATCH:
120                                 break;
121                         case 0:
122                                 break;
123                         default:
124                                 break;
125                 }
126                 globopts = GLOB_APPEND;
127                 free(globstr);
128         }
129
130         if (opt->pkgdir) {
131                 char *globstr = pathcat(opt->pkgdir, "*.zpm");
132                 rv = glob(globstr, globopts, NULL, repos);
133                 switch (rv) {
134                         case GLOB_NOSPACE:
135                                 fprintf(stderr, "glob no mem\n");
136                                 return 0;
137                         case GLOB_ABORTED:
138                                 fprintf(stderr, "glob abort\n");
139                                 return 0;
140                         case GLOB_NOMATCH:
141                                 break;
142                         case 0:
143                                 break;
144                         default:
145                                 break;
146                 }
147                 free(globstr);
148         }
149
150         return 1;
151 }
152
153 /*
154  * find a package and packagefile that supplies a library
155  */
156 int find_lib(char *soname, struct search_ctl *opt, struct pkgloc *pkg) {
157         char *latest = 0;
158         char *installed = 0, *found = 0;
159         char *pkgfile = 0;
160         int rv;
161         unsigned int i;
162
163         if (opt->localdb) {
164                 installed = zpm_findlib(opt->zpmdb, soname, "status = 'installed'");
165                 if (installed) {
166                         if (opt->verbose > 1) {
167                                 fprintf(stderr, "library %s installed via %s\n", soname, installed);
168                         }
169                         /* we're done, the lib is installed */
170                         return 2;
171                 }
172         }
173
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);
177                 }
178                 found = checkfileforlib(soname, opt->repos.gl_pathv[i]);
179                 if (found) {
180                         if (opt->verbose > 1) {
181                                 fprintf(stderr, "found %s\n", found);
182                         }
183                         rv = zpm_vercmp(found, latest);
184                         if (rv == 1) {
185                                 latest = found;
186                                 free(pkgfile);
187                                 pkgfile = strdup(opt->repos.gl_pathv[i]);
188                         }
189                 } else if (opt->verbose > 1) {
190                         fprintf(stderr, "not found\n");
191                 }
192         }
193
194         if (latest && pkgfile) {
195                 pkg->id = strdup(latest);
196                 pkg->file = pkgfile;
197                 return 1;
198         }
199
200         return 0;
201 }
202
203 struct pkgloc *find_atleast_package(char *pkgstr, struct search_ctl *opt, int stopifinstalled) {
204         char *latest = 0;
205         char *installed = 0, *found = 0;
206         char *pkgfile = 0;
207         struct pkgloc *pkg = 0;
208         int rv;
209         unsigned int i;
210
211         if (opt->localdb) {
212                 installed = zpm_findpkg_range(opt->zpmdb, pkgstr, 0, "status = 'installed'", 0);
213                 if (installed) {
214                         latest = installed;
215                         pkgfile = opt->zpmdb->path;
216                         if (stopifinstalled) {
217                                 pkg = malloc(sizeof *pkg);
218                                 pkg->id = strdup(latest);
219                                 pkg->file = pkgfile;
220                                 pkg->installed = 1;
221                                 return pkg;
222                         }
223                 }
224                 if (!opt->onlylocalinstalled) {
225                         found = zpm_findpkg_range(opt->zpmdb, pkgstr, 0, NULL, 0);
226                         if (found) {
227                                 rv = zpm_vercmp(found, latest);
228                                 if (rv == 1) {
229                                         latest = found;
230                                         pkgfile = opt->zpmdb->path;
231                                 }
232                         }
233                 }
234         }
235
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")
240                                 ) {
241                         continue;
242                 }
243                 found = checkfile(pkgstr, opt->repos.gl_pathv[i], 1);
244                 if (found) {
245                         rv = zpm_vercmp(found, latest);
246                         if (rv == 1) {
247                                 latest = found;
248                                 pkgfile = strdup(opt->repos.gl_pathv[i]);
249                         }
250                 }
251         }
252
253         if (latest && pkgfile) {
254                 pkg = malloc(sizeof *pkg);
255                 pkg->id = strdup(latest);
256                 pkg->file = pkgfile;
257                 pkg->installed = 0;
258                 if (installed) {
259                        pkg->installed = !strcmp(latest, installed);
260                 }
261         }
262
263         return pkg;
264 }
265
266 struct pkgloc *find_package(char *pkgstr, struct search_ctl *opt) {
267         char *latest = 0;
268         char *installed = 0, *found = 0;
269         char *pkgfile = 0;
270         struct pkgloc *pkg = 0;
271         int rv;
272         unsigned int i;
273
274         if (opt->localdb) {
275                 installed = zpm_findpkg(opt->zpmdb, pkgstr, "status = 'installed'");
276                 if (installed) {
277                         latest = installed;
278                         pkgfile = opt->zpmdb->path;
279                 }
280                 if (!opt->onlylocalinstalled) {
281                         found = zpm_findpkg(opt->zpmdb, pkgstr, NULL);
282                         if (found) {
283                                 rv = zpm_vercmp(found, latest);
284                                 if (rv == 1) {
285                                         latest = found;
286                                         pkgfile = opt->zpmdb->path;
287                                 }
288                         }
289                 }
290
291 #if 0
292                 if (opt->dbrepos) {
293                         zpm_foreach_repo(opt->zpmdb, search_repo, pkg)
294                 }
295 #endif
296         }
297
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")
302                                 ) {
303                         continue;
304                 }
305                 found = checkfile(pkgstr, opt->repos.gl_pathv[i], 0);
306                 if (found) {
307                         rv = zpm_vercmp(found, latest);
308                         if (rv == 1) {
309                                 latest = found;
310                                 pkgfile = strdup(opt->repos.gl_pathv[i]);
311                         }
312                 }
313         }
314
315         if (latest && pkgfile) {
316                 pkg = malloc(sizeof *pkg);
317                 pkg->id = strdup(latest);
318                 pkg->file = pkgfile;
319                 pkg->installed = 0;
320                 if (installed) {
321                        pkg->installed = !strcmp(latest, installed);
322                 }
323         }
324
325         return pkg;
326 }
327
328 char *pkglibsneeded = "\
329 with pkglibs as (\
330                 select distinct EN.needed as soname, PF.pkgid\
331                 from elfneeded EN\
332                 join packagefiles_pkgid PF on PF.hash = EN.file\
333                 ),\
334      pkgprovides as (\
335                      select distinct EL.soname, PF.pkgid\
336                      from elflibraries EL\
337                      join packagefiles_pkgid PF on PF.hash = EL.file\
338                     )\
339      select distinct PL.soname, PP.soname is not null as selfsatisfied\
340      from pkglibs PL\
341      left join pkgprovides PP on PL.pkgid = PP.pkgid and PL.soname = PP.soname\
342      where PL.pkgid = %Q\
343      ";
344
345 char *pkgdeps = "select requires from packagedeps_pkgid where pkgid = %Q";
346
347 static int afind_strcmp(const void *a, const void *b) {
348         return strcmp(a, b);
349 }
350
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;
354         struct zpm src;
355
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
359          */
360         jsw_atree_t *needed = 0, *checked, *checked_libs;
361
362         int libs, count;
363         jsw_atrav_t *i;
364         char *soname, *package;
365         
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);
368
369         while (jsw_hsize(check) > 0) {
370                 free(pkgid);
371                 free(pkgfile);
372
373                 jsw_hreset(check);
374
375                 pkgid = strdup(jsw_hkey(check));
376                 pkgfile = strdup(jsw_hitem(check));
377
378                 if (!pkgid || !pkgfile) {
379                         break;
380                 }
381
382                 jsw_herase(check, pkgid);
383
384                 if (jsw_afind(checked, pkgid)) {
385                         /* already checked this one */
386                         /* fprintf(stderr, "already checked %s\n", pkgid); */
387                         continue;
388                 }
389
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");
394                         exit(EXIT_FAILURE);
395                 }
396
397                 if (!jsw_afind(checked, pkgid)) {
398                         /* already checked this one */
399                         fprintf(stderr, "checked find failed\n");
400                         exit(EXIT_FAILURE);
401                 }
402
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);
406                         break;
407                 }
408
409                 jsw_adelete(needed);
410                 needed = jsw_anew_str();
411                 count = zpm_packages_needed(&src, pkgid, needed);
412                 if (count) {
413                         struct pkgloc *pkg = 0;
414                         i = jsw_atnew();
415
416                         if (pkg) {
417                                 free(pkg->file);
418                                 free(pkg->id);
419                                 free(pkg);
420                         }
421
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) {
425                                         continue;
426                                 }
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
431                                  */
432                         }
433                         if (pkg) {
434                                 free(pkg->file);
435                                 free(pkg->id);
436                                 free(pkg);
437                         }
438                 }
439
440                 jsw_adelete(needed);
441                 needed = jsw_anew_str();
442                 libs = zpm_libraries_needed(&src, pkgid, needed);
443                 zpm_close(&src);
444
445                 if (libs == 0) {
446                         continue;
447                 }
448
449                 i = jsw_atnew();
450
451                 for (soname = jsw_atfirst(i, needed); soname; soname = jsw_atnext(i)) {
452                         int found;
453                         struct pkgloc pkginfo;
454
455                         /* if it's in checked_libs, we've already looked at
456                          * this one */
457                         if (jsw_afind(checked_libs, soname)) {
458                                 if (opts->verbose > 1) {
459                                         fprintf(stderr, "already checked %s\n", soname);
460                                 }
461                                 continue;
462                         }
463 #if 0
464                         if (opts->verbose > 1) {
465                                 fprintf(stderr, "checking for %s\n", soname);
466                         }
467 #endif
468
469                         /* haven't found this soname */
470                         jsw_ainsert(checked_libs, soname);
471
472                         found = find_lib(soname, opts, &pkginfo);
473                         if (!found) {
474                                 if (!jsw_hinsert(nfound, soname, pkgid)) {
475                                         fprintf(stderr,"insert error\n");
476                                 }
477                                 continue;
478                         }
479
480                         if (found == 1) {
481                                 /* if not alreacy checked, add to check */
482                                 if (!jsw_afind(checked, pkginfo.file)) {
483                                         jsw_hinsert(check,pkginfo.id,pkginfo.file);
484                                 }
485                                 /* shouldn't be in there already */
486                                 jsw_hinsert(forlibs, pkginfo.id, pkginfo.file);
487                                 free(pkginfo.file);
488                                 free(pkginfo.id);
489                         } 
490                         /* otherwise found == 2, so just continue */
491                 }
492         }
493         free(pkgid);
494         free(pkgfile);
495 }
496
497 void print_pkghash(jsw_hash_t *hash, int json, int idonly) {
498         const char *pkgid, *file;
499         char *fmt;
500         char *sep = "\n";
501         int count = 0;
502         char *start = "";
503         char *end = "";
504
505         if (json) {
506                 sep = ", ";
507                 if (idonly) {
508                         fmt = "\"%s\"";
509                         start = "[ ";
510                         end = " ]\n";
511                 } else {
512                         fmt = "\"%s\": \"%s\"";
513                         start = "{ ";
514                         end = " }\n";
515                 }
516         } else {
517                 if (idonly) {
518                         fmt = "%s";
519                         start = "";
520                         end = "";
521                 } else {
522                         fmt = "%s:%s";
523                         start = "";
524                         end = "";
525                 }
526         }
527
528         printf("%s", start);
529
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);
534                         if (count++) {
535                                 printf("%s", sep);
536                         }
537                         if (idonly) {
538                                 printf(fmt, pkgid);
539                         } else {
540                                 printf(fmt, pkgid, file);
541                         }
542                 }
543                 if (!json) printf("\n");
544         }
545         printf("%s", end);
546 }
547
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;
553         jsw_atree_t *nfound;
554         struct search_ctl opt = { 0 };
555         struct zpm localdb;
556
557         opt.dbrepos = 1;
558
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";
565
566         /* -l also find packages needed for libs
567          * -j output json
568          *  -q quiet - suppress output
569          *  -v verbose - 
570          *  -d debug - trace each step
571          *
572          *  environment:
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
578          *
579          *  -M (missing) invert the output and only output missing
580          *  outputs
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)
587          *
588          *  exit value is 0 if no missing libs and no bad package strings
589          *  exit value is non zero otherwise
590          */
591
592         int output = 1;
593         while ((option = getopt(ac, av, "fljqPRDvp:r:d:MiIOn")) != -1) {
594                 switch (option) {
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
613                                    */
614                         case 'M': opt.matchallpkgfile = 1; break;
615                         case 'v': opt.verbose++; break;
616                         case 'n': idonly = 1; break;
617                         default:
618                                   exit(EXIT_FAILURE);
619                                   break;
620                 }
621         }
622         argn = optind;
623
624         if (opt.localdb) {
625                 if (zpm_open(&localdb, NULL)) {
626                         opt.zpmdb = &localdb;
627                 } else {
628                         fprintf(stderr, "can't open localdb\n");
629                         return 2;
630                 }
631         }
632
633         if (!find_globs(&opt)) {
634                 fprintf(stderr, "bad globs\n");
635                 return 3;
636         }
637
638         if (opt.verbose > 1) {
639                 unsigned int i;
640                 fprintf(stderr, "globs:");
641                 for (i = 0; i < opt.repos.gl_pathc; i++) {
642                         fprintf(stderr, " %s", opt.repos.gl_pathv[i]);
643                 }
644                 fprintf(stderr, "\n");
645         }
646
647         /* direct packages asked for */
648         packages = jsw_hnew(ac,NULL,afind_strcmp,(keydup_f)strdup,
649                         (itemdup_f)strdup,free,free);
650
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);
654
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);
658
659         /* libraries we didn't find */
660         nolib = jsw_hnew(ac,NULL,afind_strcmp,(keydup_f)strdup,
661                         (itemdup_f)strdup,free,free);
662
663         /* packages asked for that we can't find */
664         nfound = jsw_anew(afind_strcmp, (dup_f)strdup, (rel_f)free);
665
666         int arg;
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) {
671                                 continue;
672                         }
673                         jsw_hinsert(packages, found->id, found->file);
674                         jsw_hinsert(check, found->id, found->file);
675                         free(found->id);
676                         free(found->file);
677                         free(found);
678                 } else {
679                         jsw_ainsert(nfound, av[arg]);
680                 }
681         }
682
683         if (findlibs) {
684                 checklibs(&opt, check, forlibs, nolib);
685                 /* remove from forlibs anything already explicitly
686                  * in packages
687                  */
688         }
689
690         if (output) {
691                 if (jsw_hsize(packages)) {
692                         print_pkghash(packages, json, idonly);
693                 }
694                 if (jsw_hsize(forlibs)) {
695                         print_pkghash(forlibs, json, idonly);
696                 }
697         }
698
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);
705                 }
706                 rv = 1;
707         }
708
709         if (jsw_asize(nfound) > 0 && !opt.foundonly) {
710                 jsw_atrav_t *i;
711                 i = jsw_atnew();
712                 char *pkgstr;
713
714                 for (pkgstr = jsw_atfirst(i, nfound); pkgstr; pkgstr = jsw_atnext(i)) {
715                         fprintf(stderr, "%s: not found\n", pkgstr);
716                 }
717                 rv = 1;
718         }
719
720         return rv;
721 }