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