main.c revision 1.7
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
91struct 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 */
101struct pkgbase_entry {
102	char *pkgbase;
103	char *pkgname;
104	SLIST_ENTRY(pkgbase_entry) entries;
105};
106SLIST_HEAD(pkgbase_entry_head, pkgbase_entry);
107
108/*
109 * A hashed list of +REQUIRED_BY entries.
110 */
111struct reqd_by_entry {
112	char *pkgname;
113	SLIST_ENTRY(reqd_by_entry) entries;
114};
115SLIST_HEAD(reqd_by_entry_head, reqd_by_entry);
116
117/*
118 * A hashed list of packages that contain +REQUIRED_BY entries.
119 */
120struct 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};
125SLIST_HEAD(pkg_reqd_by_head, pkg_reqd_by);
126
127static const char Options[] = "C:K:SVbd:qs:v";
128
129int	quiet, verbose;
130
131static void set_unset_variable(char **, Boolean);
132static void digest_input(char **);
133
134/* print usage message and exit */
135void
136usage(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 */
172static int
173add_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
259static void
260rebuild(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
286static int
287lspattern(const char *pkg, void *vp)
288{
289	const char *dir = vp;
290	printf("%s/%s\n", dir, pkg);
291	return 0;
292}
293
294static int
295lsbasepattern(const char *pkg, void *vp)
296{
297	puts(pkg);
298	return 0;
299}
300
301static int
302remove_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
316static void
317add_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
385static int
386add_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 */
416static int
417check_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
445static void
446check_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
455static void
456rebuild_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
507int
508main(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
814struct set_installed_info_arg {
815	char *variable;
816	char *value;
817	int got_match;
818};
819
820static int
821set_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
837static void
838set_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
914static void
915digest_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