]> pd.if.org Git - zpackage/blob - src/search.c
dd324d55a7d1664d0006b283a6f5a6aab292bbd7
[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) {
66         struct zpm pkgfile;
67         char *pkgid = 0;
68
69         if (!zpm_open(&pkgfile, path)) {
70                 return NULL;
71         }
72         
73         pkgid = zpm_findpkg(&pkgfile, pkgstr, NULL);
74         zpm_close(&pkgfile);
75
76         return pkgid;
77 }
78
79 char *checkfileforlib(char *soname, char *path) {
80         struct zpm pkgfile;
81         char *pkgid = 0;
82
83         if (!zpm_open(&pkgfile, path)) {
84                 return NULL;
85         }
86         
87         pkgid = zpm_findlib(&pkgfile, soname, NULL);
88         if (pkgfile.error) {
89                 fprintf(stderr, "sql error: %s\n", pkgfile.errmsg);
90         }
91         zpm_close(&pkgfile);
92
93         return pkgid;
94 }
95
96 int find_globs(struct search_ctl *opt) {
97         glob_t *repos;
98         int rv;
99
100         repos = &opt->repos;
101         repos->gl_offs = 0;
102
103         int globopts = 0;
104
105         if (opt->repodir) {
106                 char *globstr = pathcat(opt->repodir, "*.repo");
107                 rv = glob(globstr, globopts, NULL, repos);
108                 switch (rv) {
109                         case GLOB_NOSPACE:
110                                 fprintf(stderr, "glob no mem\n");
111                                 return 0;
112                         case GLOB_ABORTED:
113                                 fprintf(stderr, "glob abort\n");
114                                 return 0;
115                         case GLOB_NOMATCH:
116                                 break;
117                         case 0:
118                                 break;
119                         default:
120                                 break;
121                 }
122                 globopts = GLOB_APPEND;
123                 free(globstr);
124         }
125
126         if (opt->pkgdir) {
127                 char *globstr = pathcat(opt->pkgdir, "*.zpm");
128                 rv = glob(globstr, globopts, NULL, repos);
129                 switch (rv) {
130                         case GLOB_NOSPACE:
131                                 fprintf(stderr, "glob no mem\n");
132                                 return 0;
133                         case GLOB_ABORTED:
134                                 fprintf(stderr, "glob abort\n");
135                                 return 0;
136                         case GLOB_NOMATCH:
137                                 break;
138                         case 0:
139                                 break;
140                         default:
141                                 break;
142                 }
143                 free(globstr);
144         }
145
146         return 1;
147 }
148
149 int find_lib(char *soname, struct search_ctl *opt, struct pkgloc *pkg) {
150         char *latest = 0;
151         char *installed = 0, *found = 0;
152         char *pkgfile = 0;
153         int rv;
154         unsigned int i;
155
156         if (opt->localdb) {
157                 installed = zpm_findlib(opt->zpmdb, soname, "status = 'installed'");
158                 if (installed) {
159                         if (opt->verbose > 1) {
160                                 fprintf(stderr, "library %s installed via %s\n", soname, installed);
161                         }
162                         /* we're done, the lib is installed */
163                         return 2;
164                 }
165         }
166
167         for (i = 0; i < opt->repos.gl_pathc; i++) {
168                 if (opt->verbose > 1) {
169                         fprintf(stderr, "checking %s for %s\n", opt->repos.gl_pathv[i], soname);
170                 }
171                 found = checkfileforlib(soname, opt->repos.gl_pathv[i]);
172                 if (found) {
173                         if (opt->verbose > 1) {
174                                 fprintf(stderr, "found %s\n", found);
175                         }
176                         rv = zpm_vercmp(found, latest);
177                         if (rv == 1) {
178                                 latest = found;
179                                 free(pkgfile);
180                                 pkgfile = strdup(opt->repos.gl_pathv[i]);
181                         }
182                 } else if (opt->verbose > 1) {
183                         fprintf(stderr, "not found\n");
184                 }
185         }
186
187         if (latest && pkgfile) {
188                 pkg->id = strdup(latest);
189                 pkg->file = pkgfile;
190                 return 1;
191         }
192
193         return 0;
194 }
195
196 struct pkgloc *find_package(char *pkgstr, struct search_ctl *opt) {
197         char *latest = 0;
198         char *installed = 0, *found = 0;
199         char *pkgfile = 0;
200         struct pkgloc *pkg = 0;
201         int rv;
202         unsigned int i;
203
204         if (opt->localdb) {
205                 installed = zpm_findpkg(opt->zpmdb, pkgstr, "status = 'installed'");
206                 if (installed) {
207                         latest = installed;
208                         pkgfile = opt->zpmdb->path;
209                 }
210                 if (!opt->onlylocalinstalled) {
211                         found = zpm_findpkg(opt->zpmdb, pkgstr, NULL);
212                         if (found) {
213                                 rv = zpm_vercmp(found, latest);
214                                 if (rv == 1) {
215                                         latest = found;
216                                         pkgfile = opt->zpmdb->path;
217                                 }
218                         }
219                 }
220
221 #if 0
222                 if (opt->dbrepos) {
223                         zpm_foreach_repo(opt->zpmdb, search_repo, pkg)
224                 }
225 #endif
226         }
227
228         for (i = 0; i < opt->repos.gl_pathc; i++) {
229                 if (!opt->matchallpkgfile
230                                 && !strstr(opt->repos.gl_pathv[i], pkgstr)
231                                 && !strstr(opt->repos.gl_pathv[i], ".repo")
232                                 ) {
233                         continue;
234                 }
235                 found = checkfile(pkgstr, opt->repos.gl_pathv[i]);
236                 if (found) {
237                         rv = zpm_vercmp(found, latest);
238                         if (rv == 1) {
239                                 latest = found;
240                                 pkgfile = strdup(opt->repos.gl_pathv[i]);
241                         }
242                 }
243         }
244
245         if (latest && pkgfile) {
246                 pkg = malloc(sizeof *pkg);
247                 pkg->id = strdup(latest);
248                 pkg->file = pkgfile;
249                 pkg->installed = 0;
250                 if (installed) {
251                        pkg->installed = !strcmp(latest, installed);
252                 }
253         }
254
255         return pkg;
256 }
257
258 char *pkglibsneeded = "\
259 with pkglibs as (\
260                 select distinct EN.needed as soname, PF.pkgid\
261                 from elfneeded EN\
262                 join packagefiles_pkgid PF on PF.hash = EN.file\
263                 ),\
264      pkgprovides as (\
265                      select distinct EL.soname, PF.pkgid\
266                      from elflibraries EL\
267                      join packagefiles_pkgid PF on PF.hash = EL.file\
268                     )\
269      select distinct PL.soname, PP.soname is not null as selfsatisfied\
270      from pkglibs PL\
271      left join pkgprovides PP on PL.pkgid = PP.pkgid and PL.soname = PP.soname\
272      where PL.pkgid = %Q\
273      ";
274
275 static int afind_strcmp(const void *a, const void *b) {
276         return strcmp(a, b);
277 }
278
279 void checklibs(struct search_ctl *opts,
280                 jsw_hash_t *check, jsw_hash_t *forlibs, jsw_hash_t *nfound) {
281         char *pkgid = 0, *pkgfile = 0;
282         struct zpm src;
283
284         /* checked_libs is a list of checked sonames
285          * checked is a list of checked packages
286          * needed is list of libs for package
287          */
288         jsw_atree_t *needed = 0, *checked, *checked_libs;
289
290         int libs;
291         jsw_atrav_t *i;
292         char *soname;
293         
294         checked = jsw_anew(afind_strcmp, (dup_f)strdup, (rel_f)free);
295         checked_libs = jsw_anew(afind_strcmp, (dup_f)strdup, (rel_f)free);
296
297         while (jsw_hsize(check) > 0) {
298                 free(pkgid);
299                 free(pkgfile);
300
301                 jsw_hreset(check);
302
303                 pkgid = strdup(jsw_hkey(check));
304                 pkgfile = strdup(jsw_hitem(check));
305
306                 if (!pkgid || !pkgfile) {
307                         break;
308                 }
309
310                 jsw_herase(check, pkgid);
311
312                 if (jsw_afind(checked, pkgid)) {
313                         /* already checked this one */
314                         /* fprintf(stderr, "already checked %s\n", pkgid); */
315                         continue;
316                 }
317
318                 fprintf(stderr, "checking libs for %s\n", pkgid);
319                 /* we do this now so we catch self deps */
320                 if (!jsw_ainsert(checked, pkgid)) {
321                         fprintf(stderr, "checked insert failed\n");
322                         exit(EXIT_FAILURE);
323                 }
324
325                 if (!jsw_afind(checked, pkgid)) {
326                         /* already checked this one */
327                         fprintf(stderr, "checked find failed\n");
328                         exit(EXIT_FAILURE);
329                 }
330
331                 /* get the libraries needed by this package */
332                 if (!zpm_open(&src, pkgfile)) {
333                         fprintf(stderr, "can't zpm open %s\n", pkgfile);
334                         break;
335                 }
336
337                 if (needed) jsw_adelete(needed);
338                 needed = jsw_anew(afind_strcmp, (dup_f)strdup, (rel_f)free);
339                 libs = zpm_libraries_needed(&src, pkgid, needed);
340                 zpm_close(&src);
341
342                 if (libs == 0) {
343                         continue;
344                 }
345
346                 i = jsw_atnew();
347
348                 for (soname = jsw_atfirst(i, needed); soname; soname = jsw_atnext(i)) {
349                         int found;
350                         struct pkgloc pkginfo;
351
352                         /* if it's in checked_libs, we've already looked at this one */
353                         if (jsw_afind(checked_libs, soname)) {
354                                 if (opts->verbose > 1) {
355                                         fprintf(stderr, "already checked %s\n", soname);
356                                 }
357                                 continue;
358                         }
359 #if 0
360                         if (opts->verbose > 1) {
361                                 fprintf(stderr, "checking for %s\n", soname);
362                         }
363 #endif
364
365                         /* haven't found this soname */
366                         jsw_ainsert(checked_libs, soname);
367
368                         found = find_lib(soname, opts, &pkginfo);
369                         if (!found) {
370                                 if (!jsw_hinsert(nfound, soname, pkgid)) {
371                                         fprintf(stderr,"insert error\n");
372                                 }
373                                 continue;
374                         }
375
376                         if (found == 1) {
377                                 /* if not alreacy checked, add to check */
378                                 if (!jsw_afind(checked, pkginfo.file)) {
379                                         jsw_hinsert(check,pkginfo.id,pkginfo.file);
380                                 }
381                                 /* shouldn't be in there already */
382                                 jsw_hinsert(forlibs, pkginfo.id, pkginfo.file);
383                                 free(pkginfo.file);
384                                 free(pkginfo.id);
385                         } 
386                         /* otherwise found == 2, so just continue */
387                 }
388         }
389         free(pkgid);
390         free(pkgfile);
391 }
392
393 void print_pkghash(jsw_hash_t *hash, int json, int idonly) {
394         const char *pkgid, *file;
395         char *fmt;
396         char *sep = "\n";
397         int count = 0;
398         char *start = "";
399         char *end = "";
400
401         if (json) {
402                 sep = ", ";
403                 if (idonly) {
404                         fmt = "\"%s\"";
405                         start = "[ ";
406                         end = " ]\n";
407                 } else {
408                         fmt = "\"%s\": \"%s\"";
409                         start = "{ ";
410                         end = " }\n";
411                 }
412         } else {
413                 if (idonly) {
414                         fmt = "%s";
415                         start = "";
416                         end = "";
417                 } else {
418                         fmt = "%s:%s";
419                         start = "";
420                         end = "";
421                 }
422         }
423
424         printf("%s", start);
425
426         if (jsw_hsize(hash) > 0) {
427                 for (jsw_hreset(hash); jsw_hitem(hash); jsw_hnext(hash)) {
428                         pkgid = jsw_hkey(hash);
429                         file = jsw_hitem(hash);
430                         if (count++) {
431                                 printf("%s", sep);
432                         }
433                         if (idonly) {
434                                 printf(fmt, pkgid);
435                         } else {
436                                 printf(fmt, pkgid, file);
437                         }
438                 }
439                 if (!json) printf("\n");
440         }
441         printf("%s", end);
442 }
443
444 int main(int ac, char *av[]) {
445         int option, argn, rv = 0;
446         int findlibs = 0, json = 0, idonly = 0;
447         struct pkgloc *found;
448         jsw_hash_t *packages, *check, *forlibs, *nolib;
449         jsw_atree_t *nfound;
450         struct search_ctl opt = { 0 };
451         struct zpm localdb;
452
453         opt.dbrepos = 1;
454
455         opt.localdb = getenv("ZPMDB");
456         if (!opt.localdb) opt.localdb = "/var/lib/zpm/local.db";
457         opt.pkgdir = getenv("ZPM_PACKAGE_DIR");
458         if (!opt.pkgdir) opt.pkgdir = "/var/lib/zpm/packages";
459         opt.repodir = getenv("ZPM_REPO_DIR");
460         if (!opt.repodir) opt.repodir = "/var/lib/zpm/repo";
461
462
463         /* -l also find packages needed for libs
464          * -j output json
465          *  -q quiet - suppress output
466          *  -v verbose - 
467          *  -d debug - trace each step
468          *
469          *  environment:
470          *  ZPMDB - path to localdb, /var/lib/zpm/local.db
471          *  ZPM_REPO_DIR - path to *.repo files, '/var/lib/zpm/repo'
472          *  ZPM_PACKAGE_DIRS - : separated paths to *.zpm files,
473          *  '/var/lib/zpm/packages'
474          *  ZPM_ROOT_DIR :- prepends to above paths
475          *
476          *  -M (missing) invert the output and only output missing
477          *  outputs
478          *  packages found (suppress with -P)
479          *  package strings not found (suppress with -S)
480          *  library dependency packages needed and found (suppress with -N)
481          *  library dependency packages already installed (suppress with -H)
482          *  library dependency packages in install set (suppress with -I)
483          *  library dependency sonames not found (suppress with -L)
484          *
485          *  exit value is 0 if no missing libs and no bad package strings
486          *  exit value is non zero otherwise
487          */
488
489         int output = 1;
490         while ((option = getopt(ac, av, "fljqPRDvp:r:d:MiIOn")) != -1) {
491                 switch (option) {
492                         case 'l': findlibs = 1; break;
493                         case 'j': json = 1; break;
494                         case 'q': output = 0; break;
495                                   /* show installed files */
496                         case 'i': opt.matchinstalled = 1; break;
497                         case 'I': opt.suppressinstalled = 1; break;
498                                   /* only find localdb pkgs if installed */
499                         case 'O': opt.onlylocalinstalled = 1; break;
500                         case 'f': opt.foundonly = 1; break;
501                         case 'd': opt.localdb = optarg; break;
502                         case 'p': opt.pkgdir = optarg; break;
503                         case 'r': opt.repodir = optarg; break;
504                         case 'P': opt.pkgdir = 0; break;
505                         case 'R': opt.repodir = 0; break;
506                         case 'D': opt.localdb = 0; break;
507                                   /* matchallpkgfile means look at
508                                    * all .zpm files, not just ones
509                                    * that have the pkgid string prefix
510                                    */
511                         case 'M': opt.matchallpkgfile = 1; break;
512                         case 'v': opt.verbose++; break;
513                         case 'n': idonly = 1; break;
514                         default:
515                                   exit(EXIT_FAILURE);
516                                   break;
517                 }
518         }
519         argn = optind;
520
521         if (opt.localdb) {
522                 if (zpm_open(&localdb, NULL)) {
523                         opt.zpmdb = &localdb;
524                 } else {
525                         fprintf(stderr, "can't open localdb\n");
526                         return 2;
527                 }
528         }
529
530         if (!find_globs(&opt)) {
531                 fprintf(stderr, "bad globs\n");
532                 return 3;
533         }
534
535         if (opt.verbose > 1) {
536                 unsigned int i;
537                 fprintf(stderr, "globs:");
538                 for (i = 0; i < opt.repos.gl_pathc; i++) {
539                         fprintf(stderr, " %s", opt.repos.gl_pathv[i]);
540                 }
541                 fprintf(stderr, "\n");
542         }
543
544         /* direct packages asked for */
545         packages = jsw_hnew(ac,NULL,afind_strcmp,(keydup_f)strdup,
546                         (itemdup_f)strdup,free,free);
547
548         /* packages we need to check for libs */
549         check = jsw_hnew(ac,NULL,afind_strcmp,(keydup_f)strdup,
550                         (itemdup_f)strdup,free,free);
551
552         /* packages we will also need for library dependences */
553         forlibs = jsw_hnew(ac,NULL,afind_strcmp,(keydup_f)strdup,
554                         (itemdup_f)strdup,free,free);
555
556         /* libraries we didn't find */
557         nolib = jsw_hnew(ac,NULL,afind_strcmp,(keydup_f)strdup,
558                         (itemdup_f)strdup,free,free);
559
560         /* packages asked for that we can't find */
561         nfound = jsw_anew(afind_strcmp, (dup_f)strdup, (rel_f)free);
562
563         int arg;
564         for (arg = argn; arg < ac; arg++) {
565                 found = find_package(av[arg], &opt);
566                 if (found && (opt.matchinstalled || !found->installed)) {
567                         if (found->installed && opt.suppressinstalled) {
568                                 continue;
569                         }
570                         jsw_hinsert(packages, found->id, found->file);
571                         jsw_hinsert(check, found->id, found->file);
572                         free(found->id);
573                         free(found->file);
574                         free(found);
575                 } else {
576                         jsw_ainsert(nfound, av[arg]);
577                 }
578         }
579
580         if (findlibs) {
581                 checklibs(&opt, check, forlibs, nolib);
582                 /* remove from forlibs anything already explicitly
583                  * in packages
584                  */
585         }
586
587         if (output) {
588                 if (jsw_hsize(packages)) {
589                         print_pkghash(packages, json, idonly);
590                 }
591                 if (jsw_hsize(forlibs)) {
592                         print_pkghash(forlibs, json, idonly);
593                 }
594         }
595
596         if (jsw_hsize(nolib) > 0) {
597                 for (jsw_hreset(nolib); jsw_hitem(nolib); jsw_hnext(nolib)) {
598                         const char *pkgid, *file;
599                         pkgid = jsw_hkey(nolib);
600                         file = jsw_hitem(nolib);
601                         fprintf(stderr, "no lib found %s:%s\n", pkgid, file);
602                 }
603                 rv = 1;
604         }
605
606         if (jsw_asize(nfound) > 0 && !opt.foundonly) {
607                 jsw_atrav_t *i;
608                 i = jsw_atnew();
609                 char *pkgstr;
610
611                 for (pkgstr = jsw_atfirst(i, nfound); pkgstr; pkgstr = jsw_atnext(i)) {
612                         fprintf(stderr, "%s: not found\n", pkgstr);
613                 }
614                 rv = 1;
615         }
616
617         return rv;
618 }