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