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