xref: /netbsd-src/external/bsd/pkg_install/dist/admin/main.c (revision 1b2611421270f128b12bc59de682905e00d5645e)
1 /*	$NetBSD: main.c,v 1.7 2024/06/11 09:26:57 wiz Exp $	*/
2 
3 #ifdef HAVE_NBTOOL_CONFIG_H
4 #include "nbtool_config.h"
5 #else
6 #if HAVE_CONFIG_H
7 #include "config.h"
8 #endif
9 #include <nbcompat.h>
10 #if HAVE_SYS_CDEFS_H
11 #include <sys/cdefs.h>
12 #endif
13 #endif
14 __RCSID("$NetBSD: main.c,v 1.7 2024/06/11 09:26:57 wiz Exp $");
15 
16 /*-
17  * Copyright (c) 1999-2019 The NetBSD Foundation, Inc.
18  * All rights reserved.
19  *
20  * This code is derived from software contributed to The NetBSD Foundation
21  * by Hubert Feyrer <hubert@feyrer.de> and
22  * by Joerg Sonnenberger <joerg@NetBSD.org>.
23  *
24  * Redistribution and use in source and binary forms, with or without
25  * modification, are permitted provided that the following conditions
26  * are met:
27  * 1. Redistributions of source code must retain the above copyright
28  *    notice, this list of conditions and the following disclaimer.
29  * 2. Redistributions in binary form must reproduce the above copyright
30  *    notice, this list of conditions and the following disclaimer in the
31  *    documentation and/or other materials provided with the distribution.
32  *
33  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
34  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
35  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
36  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
37  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
38  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
39  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
40  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
41  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
42  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
43  * POSSIBILITY OF SUCH DAMAGE.
44  */
45 
46 #if HAVE_SYS_TYPES_H
47 #include <sys/types.h>
48 #endif
49 #if HAVE_SYS_STAT_H
50 #include <sys/stat.h>
51 #endif
52 #if HAVE_DIRENT_H
53 #include <dirent.h>
54 #endif
55 #if HAVE_ERR_H
56 #include <err.h>
57 #endif
58 #if HAVE_ERRNO_H
59 #include <errno.h>
60 #endif
61 #if HAVE_FCNTL_H
62 #include <fcntl.h>
63 #endif
64 #ifndef NETBSD
65 #include <nbcompat/md5.h>
66 #include <nbcompat/sha2.h>
67 #else
68 #include <md5.h>
69 #include <sha2.h>
70 #endif
71 #if HAVE_LIMITS_H
72 #include <limits.h>
73 #endif
74 #if HAVE_STDIO_H
75 #include <stdio.h>
76 #endif
77 #if HAVE_STRING_H
78 #include <string.h>
79 #endif
80 
81 #ifndef BOOTSTRAP
82 #include <archive.h>
83 #include <fetch.h>
84 #endif
85 
86 #include "admin.h"
87 #include "lib.h"
88 
89 #define DEFAULT_SFX	".t[bg]z"	/* default suffix for ls{all,best} */
90 
91 struct pkgdb_count {
92 	size_t files;
93 	size_t directories;
94 	size_t packages;
95 };
96 
97 /*
98  * A simple list of pkgname/pkgbase entries in the pkgdb to verify there are
99  * no duplicate entries.
100  */
101 struct pkgbase_entry {
102 	char *pkgbase;
103 	char *pkgname;
104 	SLIST_ENTRY(pkgbase_entry) entries;
105 };
106 SLIST_HEAD(pkgbase_entry_head, pkgbase_entry);
107 
108 /*
109  * A hashed list of +REQUIRED_BY entries.
110  */
111 struct reqd_by_entry {
112 	char *pkgname;
113 	SLIST_ENTRY(reqd_by_entry) entries;
114 };
115 SLIST_HEAD(reqd_by_entry_head, reqd_by_entry);
116 
117 /*
118  * A hashed list of packages that contain +REQUIRED_BY entries.
119  */
120 struct pkg_reqd_by {
121 	char *pkgname;
122 	struct reqd_by_entry_head required_by[PKG_HASH_SIZE];
123 	SLIST_ENTRY(pkg_reqd_by) entries;
124 };
125 SLIST_HEAD(pkg_reqd_by_head, pkg_reqd_by);
126 
127 static const char Options[] = "C:K:SVbd:qs:v";
128 
129 int	quiet, verbose;
130 
131 static void set_unset_variable(char **, Boolean);
132 static void digest_input(char **);
133 
134 /* print usage message and exit */
135 void
usage(void)136 usage(void)
137 {
138 	(void) fprintf(stderr, "usage: %s [-bqSVv] [-C config] [-d lsdir] [-K pkg_dbdir] [-s sfx] command [args ...]\n"
139 	    "Where 'commands' and 'args' are:\n"
140 	    " rebuild                     - rebuild pkgdb from +CONTENTS files\n"
141 	    " rebuild-tree                - rebuild +REQUIRED_BY files from forward deps\n"
142 	    " check [pkg ...]             - check md5 checksum of installed files\n"
143 	    " add pkg ...                 - add pkg files to database\n"
144 	    " set variable=value pkg ...  - set installation variable for package\n"
145 	    " unset variable pkg ...      - unset installation variable for package\n"
146 	    " lsall /path/to/pkgpattern   - list all pkgs matching the pattern\n"
147 	    " lsbest /path/to/pkgpattern  - list pkgs matching the pattern best\n"
148 	    " dump                        - dump database\n"
149 	    " pmatch pattern pkg          - returns true if pkg matches pattern, otherwise false\n"
150 	    " fetch-pkg-vulnerabilities [-s] - fetch new vulnerability file\n"
151 	    " check-pkg-vulnerabilities [-s] <file> - check syntax and checksums of the vulnerability file\n"
152 	    " audit [-eis] [-t type] ...       - check installed packages for vulnerabilities\n"
153 	    " audit-pkg [-eis] [-t type] ...   - check listed packages for vulnerabilities\n"
154 	    " audit-batch [-eis] [-t type] ... - check packages in listed files for vulnerabilities\n"
155 	    " audit-history [-t type] ...     - print all advisories for package names\n"
156 	    " check-license <condition>       - check if condition is acceptable\n"
157 	    " check-single-license <license>  - check if license is acceptable\n"
158 	    " config-var name                 - print current value of the configuration variable\n"
159 	    " check-signature ...             - verify the signature of packages\n"
160 	    " x509-sign-package pkg spkg key cert  - create X509 signature\n"
161 	    " gpg-sign-package pkg spkg       - create GPG signature\n",
162 	    getprogname());
163 	exit(EXIT_FAILURE);
164 }
165 
166 /*
167  * add1pkg(<pkg>)
168  *	adds the files listed in the +CONTENTS of <pkg> into the
169  *	pkgdb.byfile.db database file in the current package dbdir.  It
170  *	returns the number of files added to the database file.
171  */
172 static int
add_pkg(const char * pkgdir,void * vp)173 add_pkg(const char *pkgdir, void *vp)
174 {
175 	FILE	       *f;
176 	plist_t	       *p;
177 	package_t	Plist;
178 	char 	       *contents;
179 	char *PkgName, *dirp;
180 	char 		file[MaxPathSize];
181 	struct pkgdb_count *count;
182 
183 	if (!pkgdb_open(ReadWrite))
184 		err(EXIT_FAILURE, "cannot open pkgdb");
185 
186 	count = vp;
187 	++count->packages;
188 
189 	contents = pkgdb_pkg_file(pkgdir, CONTENTS_FNAME);
190 	if ((f = fopen(contents, "r")) == NULL)
191 		errx(EXIT_FAILURE, "%s: can't open `%s'", pkgdir, CONTENTS_FNAME);
192 	free(contents);
193 
194 	read_plist(&Plist, f);
195 	if ((p = find_plist(&Plist, PLIST_NAME)) == NULL) {
196 		errx(EXIT_FAILURE, "Package `%s' has no @name, aborting.", pkgdir);
197 	}
198 
199 	PkgName = p->name;
200 	dirp = NULL;
201 	for (p = Plist.head; p; p = p->next) {
202 		switch(p->type) {
203 		case PLIST_FILE:
204 			if (dirp == NULL) {
205 				errx(EXIT_FAILURE, "@cwd not yet found, please send-pr!");
206 			}
207 			(void) snprintf(file, sizeof(file), "%s/%s", dirp, p->name);
208 			if (!(isfile(file) || islinktodir(file))) {
209 				if (isbrokenlink(file)) {
210 					warnx("%s: Symlink `%s' exists and is in %s but target does not exist!",
211 						PkgName, file, CONTENTS_FNAME);
212 				} else {
213 					warnx("%s: File `%s' is in %s but not on filesystem!",
214 						PkgName, file, CONTENTS_FNAME);
215 				}
216 			} else {
217 				pkgdb_store(file, PkgName);
218 				++count->files;
219 			}
220 			break;
221 		case PLIST_PKGDIR:
222 			add_pkgdir(PkgName, dirp, p->name);
223 			++count->directories;
224 			break;
225 		case PLIST_CWD:
226 			if (strcmp(p->name, ".") != 0)
227 				dirp = p->name;
228 			else
229 				dirp = pkgdb_pkg_dir(pkgdir);
230 			break;
231 		case PLIST_IGNORE:
232 			p = p->next;
233 			break;
234 		case PLIST_SHOW_ALL:
235 		case PLIST_SRC:
236 		case PLIST_CMD:
237 		case PLIST_CHMOD:
238 		case PLIST_CHOWN:
239 		case PLIST_CHGRP:
240 		case PLIST_COMMENT:
241 		case PLIST_NAME:
242 		case PLIST_UNEXEC:
243 		case PLIST_DISPLAY:
244 		case PLIST_PKGDEP:
245 		case PLIST_DIR_RM:
246 		case PLIST_OPTION:
247 		case PLIST_PKGCFL:
248 		case PLIST_BLDDEP:
249 			break;
250 		}
251 	}
252 	free_plist(&Plist);
253 	fclose(f);
254 	pkgdb_close();
255 
256 	return 0;
257 }
258 
259 static void
rebuild(void)260 rebuild(void)
261 {
262 	char *cachename;
263 	struct pkgdb_count count;
264 
265 	count.files = 0;
266 	count.directories = 0;
267 	count.packages = 0;
268 
269 	cachename = pkgdb_get_database();
270 	if (unlink(cachename) != 0 && errno != ENOENT)
271 		err(EXIT_FAILURE, "unlink %s", cachename);
272 
273 	setbuf(stdout, NULL);
274 
275 	iterate_pkg_db(add_pkg, &count);
276 
277 	printf("\n");
278 	printf("Stored %" PRIzu " file%s and %" PRIzu " explicit director%s"
279 	    " from %"PRIzu " package%s in %s.\n",
280 	    count.files, count.files == 1 ? "" : "s",
281 	    count.directories, count.directories == 1 ? "y" : "ies",
282 	    count.packages, count.packages == 1 ? "" : "s",
283 	    cachename);
284 }
285 
286 static int
lspattern(const char * pkg,void * vp)287 lspattern(const char *pkg, void *vp)
288 {
289 	const char *dir = vp;
290 	printf("%s/%s\n", dir, pkg);
291 	return 0;
292 }
293 
294 static int
lsbasepattern(const char * pkg,void * vp)295 lsbasepattern(const char *pkg, void *vp)
296 {
297 	puts(pkg);
298 	return 0;
299 }
300 
301 static int
remove_required_by(const char * pkgname,void * cookie)302 remove_required_by(const char *pkgname, void *cookie)
303 {
304 	char *path;
305 
306 	path = pkgdb_pkg_file(pkgname, REQUIRED_BY_FNAME);
307 
308 	if (unlink(path) == -1 && errno != ENOENT)
309 		err(EXIT_FAILURE, "Cannot remove %s", path);
310 
311 	free(path);
312 
313 	return 0;
314 }
315 
316 static void
add_required_by(const char * pattern,const char * pkgname,struct pkg_reqd_by_head * hash)317 add_required_by(const char *pattern, const char *pkgname, struct pkg_reqd_by_head *hash)
318 {
319 	struct pkg_reqd_by_head *phead;
320 	struct pkg_reqd_by *pkg;
321 	struct reqd_by_entry_head *ehead;
322 	struct reqd_by_entry *entry;
323 	char *best_installed;
324 	int i;
325 
326 	best_installed = find_best_matching_installed_pkg(pattern, 1);
327 	if (best_installed == NULL) {
328 		warnx("Dependency %s of %s unresolved", pattern, pkgname);
329 		return;
330 	}
331 
332 	/*
333 	 * Find correct reqd_by head based on hash of best_installed, which is
334 	 * the package in question that we are adding +REQUIRED_BY entries for.
335 	 */
336 	phead = &hash[PKG_HASH_ENTRY(best_installed)];
337 
338 	/*
339 	 * Look for an existing entry in this hash list.
340 	 */
341 	SLIST_FOREACH(pkg, phead, entries) {
342 		if (strcmp(pkg->pkgname, best_installed) == 0) {
343 
344 			/*
345 			 * Found an entry, now see if it already has a
346 			 * +REQUIRED_BY entry recorded for this pkgname,
347 			 * and if not then add it.
348 			 */
349 			ehead = &pkg->required_by[PKG_HASH_ENTRY(pkgname)];
350 			SLIST_FOREACH(entry, ehead, entries) {
351 				if (strcmp(entry->pkgname, pkgname) == 0)
352 					break;
353 			}
354 
355 			if (entry == NULL) {
356 				entry = xmalloc(sizeof(*entry));
357 				entry->pkgname = xstrdup(pkgname);
358 				SLIST_INSERT_HEAD(ehead, entry, entries);
359 			}
360 
361 			break;
362 		}
363 	}
364 
365 	/*
366 	 * Create new package containing its first +REQUIRED_BY entry.
367 	 */
368 	if (pkg == NULL) {
369 		pkg = xmalloc(sizeof(*pkg));
370 		pkg->pkgname = xstrdup(best_installed);
371 		for (i = 0; i < PKG_HASH_SIZE; i++)
372 		       SLIST_INIT(&pkg->required_by[i]);
373 
374 		ehead = &pkg->required_by[PKG_HASH_ENTRY(pkgname)];
375 		entry = xmalloc(sizeof(*entry));
376 		entry->pkgname = xstrdup(pkgname);
377 		SLIST_INSERT_HEAD(ehead, entry, entries);
378 
379 		SLIST_INSERT_HEAD(phead, pkg, entries);
380 	}
381 
382 	free(best_installed);
383 }
384 
385 static int
add_depends_of(const char * pkgname,void * cookie)386 add_depends_of(const char *pkgname, void *cookie)
387 {
388 	FILE *fp;
389 	struct pkg_reqd_by_head *h = cookie;
390 	plist_t *p;
391 	package_t plist;
392 	char *path;
393 
394 	path = pkgdb_pkg_file(pkgname, CONTENTS_FNAME);
395 	if ((fp = fopen(path, "r")) == NULL)
396 		errx(EXIT_FAILURE, "Cannot read %s of package %s",
397 		    CONTENTS_FNAME, pkgname);
398 	free(path);
399 	read_plist(&plist, fp);
400 	fclose(fp);
401 
402 	for (p = plist.head; p; p = p->next) {
403 		if (p->type == PLIST_PKGDEP)
404 			add_required_by(p->name, pkgname, h);
405 	}
406 
407 	free_plist(&plist);
408 
409 	return 0;
410 }
411 
412 /*
413  * It is a fatal error if the pkgdb contains multiple entries with the same
414  * PKGBASE, usually caused by inserting directories manually into the pkgdb.
415  */
416 static int
check_duplicate_pkgbase(const char * pkgname,void * cookie)417 check_duplicate_pkgbase(const char *pkgname, void *cookie)
418 {
419 	struct pkgbase_entry_head *head = cookie;
420 	struct pkgbase_entry *pkg, *pkgiter;
421 	char *p;
422 
423 	if ((p = strrchr(pkgname, '-')) == NULL) {
424 		errx(EXIT_FAILURE, "entry '%s' in pkgdb is not a valid package name.",
425 		    pkgname);
426 	}
427 
428 	pkg = xmalloc(sizeof(*pkg));
429 	pkg->pkgname = xstrdup(pkgname);
430 	*p = '\0';
431 	pkg->pkgbase = xstrdup(pkgname);
432 
433 	SLIST_FOREACH(pkgiter, head, entries) {
434 		if (strcmp(pkg->pkgbase, pkgiter->pkgbase) == 0) {
435 			errx(EXIT_FAILURE, "corrupt pkgdb, duplicate PKGBASE entries:\n"
436 			    "\t%s\n\t%s", pkg->pkgname, pkgiter->pkgname);
437 		}
438 	}
439 
440 	SLIST_INSERT_HEAD(head, pkg, entries);
441 
442 	return 0;
443 }
444 
445 static void
check_pkgdb(void)446 check_pkgdb(void)
447 {
448 	struct pkgbase_entry_head pbhead;
449 
450 	SLIST_INIT(&pbhead);
451 	if (iterate_pkg_db(check_duplicate_pkgbase, &pbhead) == -1)
452 		errx(EXIT_FAILURE, "cannot iterate pkgdb");
453 }
454 
455 static void
rebuild_tree(void)456 rebuild_tree(void)
457 {
458 	FILE *fp;
459 	struct pkg_reqd_by_head pkgs[PKG_HASH_SIZE];
460 	struct pkg_reqd_by *p;
461 	struct reqd_by_entry *e;
462 	int fd, i, j;
463 	char *path;
464 
465 	for (i = 0; i < PKG_HASH_SIZE; i++)
466 		SLIST_INIT(&pkgs[i]);
467 
468 	/*
469 	 * First, calculate all of the +REQUIRED_BY entries and store in our
470 	 * pkgs hashed list.
471 	 */
472 	if (iterate_pkg_db(add_depends_of, &pkgs) == -1)
473 		errx(EXIT_FAILURE, "cannot iterate pkgdb");
474 
475 	/*
476 	 * Now we can remove all existing +REQUIRED_BY files.
477 	 */
478 	if (iterate_pkg_db(remove_required_by, NULL) == -1)
479 		errx(EXIT_FAILURE, "cannot iterate pkgdb");
480 
481 	/*
482 	 * Finally, write out all the new +REQUIRED_BY files.
483 	 */
484 	for (i = 0; i < PKG_HASH_SIZE; i++) {
485 		SLIST_FOREACH(p, &pkgs[i], entries) {
486 			path = pkgdb_pkg_file(p->pkgname, REQUIRED_BY_FNAME);
487 
488 			if ((fd = open(path, O_WRONLY | O_APPEND | O_CREAT,
489 			    0644)) == -1)
490 				errx(EXIT_FAILURE, "cannot write to %s", path);
491 
492 			if ((fp = fdopen(fd, "a")) == NULL)
493 				errx(EXIT_FAILURE, "cannot open %s", path);
494 
495 			for (j = 0; j < PKG_HASH_SIZE; j++) {
496 				SLIST_FOREACH(e, &p->required_by[j], entries)
497 					fprintf(fp, "%s\n", e->pkgname);
498 			}
499 			if (fclose(fp) == EOF) {
500 				remove(path);
501 				errx(EXIT_FAILURE, "cannot close %s", path);
502 			}
503 		}
504 	}
505 }
506 
507 int
main(int argc,char * argv[])508 main(int argc, char *argv[])
509 {
510 	Boolean		 use_default_sfx = TRUE;
511 	Boolean 	 show_basename_only = FALSE;
512 	char		 lsdir[MaxPathSize];
513 	char		 sfx[MaxPathSize];
514 	char		*lsdirp = NULL;
515 	int		 ch;
516 
517 	setprogname(argv[0]);
518 
519 	if (argc < 2)
520 		usage();
521 
522 	while ((ch = getopt(argc, argv, Options)) != -1)
523 		switch (ch) {
524 		case 'C':
525 			config_file = optarg;
526 			break;
527 
528 		case 'K':
529 			pkgdb_set_dir(optarg, 3);
530 			break;
531 
532 		case 'S':
533 			sfx[0] = 0x0;
534 			use_default_sfx = FALSE;
535 			break;
536 
537 		case 'V':
538 			show_version();
539 			/* NOTREACHED */
540 
541 		case 'b':
542 			show_basename_only = TRUE;
543 			break;
544 
545 		case 'd':
546 			(void) strlcpy(lsdir, optarg, sizeof(lsdir));
547 			lsdirp = lsdir;
548 			break;
549 
550 		case 'q':
551 			quiet = 1;
552 			break;
553 
554 		case 's':
555 			(void) strlcpy(sfx, optarg, sizeof(sfx));
556 			use_default_sfx = FALSE;
557 			break;
558 
559 		case 'v':
560 			++verbose;
561 			break;
562 
563 		default:
564 			usage();
565 			/* NOTREACHED */
566 		}
567 
568 	argc -= optind;
569 	argv += optind;
570 
571 	if (argc <= 0) {
572 		usage();
573 	}
574 
575 	/*
576 	 * config-var is reading the config file implicitly,
577 	 * so skip it here.
578 	 */
579 	if (strcasecmp(argv[0], "config-var") != 0)
580 		pkg_install_config();
581 
582 	if (use_default_sfx)
583 		(void) strlcpy(sfx, DEFAULT_SFX, sizeof(sfx));
584 
585 	if (strcasecmp(argv[0], "pmatch") == 0) {
586 
587 		char *pattern, *pkg;
588 
589 		argv++;		/* "pmatch" */
590 
591 		if (argv[0] == NULL || argv[1] == NULL) {
592 			usage();
593 		}
594 
595 		pattern = argv[0];
596 		pkg = argv[1];
597 
598 		if (pkg_match(pattern, pkg)){
599 			return 0;
600 		} else {
601 			return 1;
602 		}
603 
604 	} else if (strcasecmp(argv[0], "rebuild") == 0) {
605 
606 		check_pkgdb();
607 		rebuild();
608 		if (!quiet) {
609 			printf("Done.\n");
610 		}
611 
612 	} else if (strcasecmp(argv[0], "rebuild-tree") == 0) {
613 
614 		check_pkgdb();
615 		rebuild_tree();
616 		if (!quiet) {
617 			printf("Done.\n");
618 		}
619 
620 	} else if (strcasecmp(argv[0], "check") == 0) {
621 		argv++;		/* "check" */
622 
623 		check_pkgdb();
624 		check(argv);
625 
626 		if (!quiet) {
627 			printf("Done.\n");
628 		}
629 
630 	} else if (strcasecmp(argv[0], "lsall") == 0) {
631 		argv++;		/* "lsall" */
632 
633 		while (*argv != NULL) {
634 			/* args specified */
635 			int     rc;
636 			const char *basep, *dir;
637 
638 			dir = lsdirp ? lsdirp : dirname_of(*argv);
639 			basep = basename_of(*argv);
640 
641 			if (show_basename_only)
642 				rc = match_local_files(dir, use_default_sfx, 1, basep, lsbasepattern, NULL);
643 			else
644 				rc = match_local_files(dir, use_default_sfx, 1, basep, lspattern, __UNCONST(dir));
645 			if (rc == -1)
646 				errx(EXIT_FAILURE, "Error from match_local_files(\"%s\", \"%s\", ...)",
647 				     dir, basep);
648 
649 			argv++;
650 		}
651 
652 	} else if (strcasecmp(argv[0], "lsbest") == 0) {
653 		argv++;		/* "lsbest" */
654 
655 		while (*argv != NULL) {
656 			/* args specified */
657 			const char *basep, *dir;
658 			char *p;
659 
660 			dir = lsdirp ? lsdirp : dirname_of(*argv);
661 			basep = basename_of(*argv);
662 
663 			p = find_best_matching_file(dir, basep, use_default_sfx, 1);
664 
665 			if (p) {
666 				if (show_basename_only)
667 					printf("%s\n", p);
668 				else
669 					printf("%s/%s\n", dir, p);
670 				free(p);
671 			}
672 
673 			argv++;
674 		}
675 	} else if (strcasecmp(argv[0], "list") == 0 ||
676 	    strcasecmp(argv[0], "dump") == 0) {
677 
678 		pkgdb_dump();
679 
680 	} else if (strcasecmp(argv[0], "add") == 0) {
681 		struct pkgdb_count count;
682 
683 		count.files = 0;
684 		count.directories = 0;
685 		count.packages = 0;
686 
687 		for (++argv; *argv != NULL; ++argv)
688 			add_pkg(*argv, &count);
689 	} else if (strcasecmp(argv[0], "set") == 0) {
690 		argv++;		/* "set" */
691 		set_unset_variable(argv, FALSE);
692 	} else if (strcasecmp(argv[0], "unset") == 0) {
693 		argv++;		/* "unset" */
694 		set_unset_variable(argv, TRUE);
695 	} else if (strcasecmp(argv[0], "digest") == 0) {
696 		argv++;		/* "digest" */
697 		digest_input(argv);
698 	} else if (strcasecmp(argv[0], "config-var") == 0) {
699 		argv++;
700 		if (argv == NULL || argv[1] != NULL)
701 			errx(EXIT_FAILURE, "config-var takes exactly one argument");
702 		pkg_install_show_variable(argv[0]);
703 	} else if (strcasecmp(argv[0], "check-license") == 0) {
704 		if (argv[1] == NULL)
705 			errx(EXIT_FAILURE, "check-license takes exactly one argument");
706 
707 		load_license_lists();
708 
709 		switch (acceptable_pkg_license(argv[1])) {
710 		case 0:
711 			puts("no");
712 			return 0;
713 		case 1:
714 			puts("yes");
715 			return 0;
716 		case -1:
717 			errx(EXIT_FAILURE, "invalid license condition");
718 		}
719 	} else if (strcasecmp(argv[0], "check-single-license") == 0) {
720 		if (argv[1] == NULL)
721 			errx(EXIT_FAILURE, "check-license takes exactly one argument");
722 		load_license_lists();
723 
724 		switch (acceptable_license(argv[1])) {
725 		case 0:
726 			puts("no");
727 			return 0;
728 		case 1:
729 			puts("yes");
730 			return 0;
731 		case -1:
732 			errx(EXIT_FAILURE, "invalid license");
733 		}
734 	}
735 #ifndef BOOTSTRAP
736 	else if (strcasecmp(argv[0], "findbest") == 0) {
737 		struct url *url;
738 		char *output;
739 		int rc;
740 
741 		process_pkg_path();
742 
743 		rc = 0;
744 		for (++argv; *argv != NULL; ++argv) {
745 			url = find_best_package(NULL, *argv, 1);
746 			if (url == NULL) {
747 				rc = 1;
748 				continue;
749 			}
750 			output = fetchStringifyURL(url);
751 			puts(output);
752 			fetchFreeURL(url);
753 			free(output);
754 		}
755 
756 		return rc;
757 	} else if (strcasecmp(argv[0], "fetch-pkg-vulnerabilities") == 0) {
758 		fetch_pkg_vulnerabilities(--argc, ++argv);
759 	} else if (strcasecmp(argv[0], "check-pkg-vulnerabilities") == 0) {
760 		check_pkg_vulnerabilities(--argc, ++argv);
761 	} else if (strcasecmp(argv[0], "audit") == 0) {
762 		audit_pkgdb(--argc, ++argv);
763 	} else if (strcasecmp(argv[0], "audit-pkg") == 0) {
764 		audit_pkg(--argc, ++argv);
765 	} else if (strcasecmp(argv[0], "audit-batch") == 0) {
766 		audit_batch(--argc, ++argv);
767 	} else if (strcasecmp(argv[0], "audit-history") == 0) {
768 		audit_history(--argc, ++argv);
769 	} else if (strcasecmp(argv[0], "check-signature") == 0) {
770 		struct archive *pkg;
771 		int rc;
772 
773 		rc = 0;
774 		for (--argc, ++argv; argc > 0; --argc, ++argv) {
775 			char *archive_name;
776 
777 			pkg = open_archive(*argv, &archive_name);
778 			if (pkg == NULL) {
779 				warnx("%s could not be opened", *argv);
780 				continue;
781 			}
782 			if (pkg_full_signature_check(archive_name, &pkg))
783 				rc = 1;
784 			free(archive_name);
785 			if (pkg != NULL)
786 				archive_read_free(pkg);
787 		}
788 		return rc;
789 	} else if (strcasecmp(argv[0], "x509-sign-package") == 0) {
790 #ifdef HAVE_SSL
791 		--argc;
792 		++argv;
793 		if (argc != 4)
794 			errx(EXIT_FAILURE, "x509-sign-package takes exactly four arguments");
795 		pkg_sign_x509(argv[0], argv[1], argv[2], argv[3]);
796 #else
797 		errx(EXIT_FAILURE, "OpenSSL support is not included");
798 #endif
799 	} else if (strcasecmp(argv[0], "gpg-sign-package") == 0) {
800 		--argc;
801 		++argv;
802 		if (argc != 2)
803 			errx(EXIT_FAILURE, "gpg-sign-package takes exactly two arguments");
804 		pkg_sign_gpg(argv[0], argv[1]);
805 	}
806 #endif
807 	else {
808 		usage();
809 	}
810 
811 	return 0;
812 }
813 
814 struct set_installed_info_arg {
815 	char *variable;
816 	char *value;
817 	int got_match;
818 };
819 
820 static int
set_installed_info_var(const char * name,void * cookie)821 set_installed_info_var(const char *name, void *cookie)
822 {
823 	struct set_installed_info_arg *arg = cookie;
824 	char *filename;
825 	int retval;
826 
827 	filename = pkgdb_pkg_file(name, INSTALLED_INFO_FNAME);
828 
829 	retval = var_set(filename, arg->variable, arg->value);
830 
831 	free(filename);
832 	arg->got_match = 1;
833 
834 	return retval;
835 }
836 
837 static void
set_unset_variable(char ** argv,Boolean unset)838 set_unset_variable(char **argv, Boolean unset)
839 {
840 	struct set_installed_info_arg arg;
841 	char *eq;
842 	char *variable;
843 	int ret = 0;
844 
845 	if (argv[0] == NULL || argv[1] == NULL)
846 		usage();
847 
848 	variable = NULL;
849 
850 	if (unset) {
851 		arg.variable = argv[0];
852 		arg.value = NULL;
853 	} else {
854 		eq = NULL;
855 		if ((eq=strchr(argv[0], '=')) == NULL)
856 			usage();
857 
858 		variable = xmalloc(eq-argv[0]+1);
859 		strlcpy(variable, argv[0], eq-argv[0]+1);
860 
861 		arg.variable = variable;
862 		arg.value = eq+1;
863 
864 		if (strcmp(variable, AUTOMATIC_VARNAME) == 0 &&
865 		    strcasecmp(arg.value, "yes") != 0 &&
866 		    strcasecmp(arg.value, "no") != 0) {
867 			errx(EXIT_FAILURE,
868 			     "unknown value `%s' for " AUTOMATIC_VARNAME,
869 			     arg.value);
870 		}
871 	}
872 	if (strpbrk(arg.variable, "ABCDEFGHIJKLMNOPQRSTUVWXYZ") != NULL) {
873 		free(variable);
874 		errx(EXIT_FAILURE,
875 		     "variable name must not contain uppercase letters");
876 	}
877 
878 	argv++;
879 	while (*argv != NULL) {
880 		arg.got_match = 0;
881 		if (match_installed_pkgs(*argv, set_installed_info_var, &arg) == -1)
882 			errx(EXIT_FAILURE, "Cannot process pkdbdb");
883 		if (arg.got_match == 0) {
884 			char *pattern;
885 
886 			if (ispkgpattern(*argv)) {
887 				warnx("no matching pkg for `%s'", *argv);
888 				ret++;
889 			} else {
890 				pattern = xasprintf("%s-[0-9]*", *argv);
891 
892 				if (match_installed_pkgs(pattern, set_installed_info_var, &arg) == -1)
893 					errx(EXIT_FAILURE, "Cannot process pkdbdb");
894 
895 				if (arg.got_match == 0) {
896 					warnx("cannot find package %s", *argv);
897 					++ret;
898 				}
899 				free(pattern);
900 			}
901 		}
902 
903 		argv++;
904 	}
905 
906 	if (ret > 0)
907 		exit(EXIT_FAILURE);
908 
909 	free(variable);
910 
911 	return;
912 }
913 
914 static void
digest_input(char ** argv)915 digest_input(char **argv)
916 {
917 	char digest[SHA256_DIGEST_STRING_LENGTH];
918 	int failures = 0;
919 
920 	while (*argv != NULL) {
921 		if (SHA256_File(*argv, digest)) {
922 			puts(digest);
923 		} else {
924 			warn("cannot process %s", *argv);
925 			++failures;
926 		}
927 		argv++;
928 	}
929 	if (failures)
930 		exit(EXIT_FAILURE);
931 }
932