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