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
85struct pkgdb_count {
86	size_t files;
87	size_t directories;
88	size_t packages;
89};
90
91static const char Options[] = "C:K:SVbd:qs:v";
92
93int	quiet, verbose;
94
95static void set_unset_variable(char **, Boolean);
96
97/* print usage message and exit */
98void
99usage(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 */
136static int
137add_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
223static void
224delete1pkg(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
232static void
233rebuild(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
259static int
260lspattern(const char *pkg, void *vp)
261{
262	const char *dir = vp;
263	printf("%s/%s\n", dir, pkg);
264	return 0;
265}
266
267static int
268lsbasepattern(const char *pkg, void *vp)
269{
270	puts(pkg);
271	return 0;
272}
273
274static int
275remove_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
289static void
290add_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
317static int
318add_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
343static void
344rebuild_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
352int
353main(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
656struct set_installed_info_arg {
657	char *variable;
658	char *value;
659	int got_match;
660};
661
662static int
663set_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
679static void
680set_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