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