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