Home | History | Annotate | Line # | Download | only in veriexecgen
veriexecgen.c revision 1.18.4.2
      1  1.18.4.2    martin /* $NetBSD: veriexecgen.c,v 1.18.4.2 2020/04/13 08:06:07 martin Exp $ */
      2       1.1      elad 
      3       1.1      elad /*-
      4       1.1      elad  * Copyright (c) 2006 The NetBSD Foundation, Inc.
      5       1.1      elad  * All rights reserved.
      6       1.1      elad  *
      7       1.1      elad  * This code is derived from software contributed to The NetBSD Foundation
      8       1.1      elad  * by Matt Fleming.
      9       1.1      elad  *
     10       1.1      elad  * Redistribution and use in source and binary forms, with or without
     11       1.1      elad  * modification, are permitted provided that the following conditions
     12       1.1      elad  * are met:
     13       1.1      elad  * 1. Redistributions of source code must retain the above copyright
     14       1.1      elad  *    notice, this list of conditions and the following disclaimer.
     15       1.1      elad  * 2. Redistributions in binary form must reproduce the above copyright
     16       1.1      elad  *    notice, this list of conditions and the following disclaimer in the
     17       1.1      elad  *    documentation and/or other materials provided with the distribution.
     18       1.1      elad  *
     19       1.1      elad  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20       1.1      elad  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21       1.1      elad  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22       1.1      elad  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23       1.1      elad  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24       1.1      elad  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25       1.1      elad  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26       1.1      elad  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27       1.1      elad  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28       1.1      elad  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29       1.1      elad  * POSSIBILITY OF SUCH DAMAGE.
     30       1.1      elad  */
     31      1.11       agc #if HAVE_NBTOOL_CONFIG_H
     32      1.11       agc #include "nbtool_config.h"
     33      1.11       agc #endif
     34      1.11       agc 
     35      1.11       agc #include <sys/cdefs.h>
     36      1.11       agc 
     37      1.11       agc #ifndef lint
     38      1.11       agc #ifdef __RCSID
     39  1.18.4.2    martin __RCSID("$NetBSD: veriexecgen.c,v 1.18.4.2 2020/04/13 08:06:07 martin Exp $");
     40      1.11       agc #endif
     41      1.11       agc #endif /* not lint */
     42      1.11       agc 
     43       1.1      elad #include <sys/param.h>
     44       1.1      elad #include <sys/types.h>
     45       1.1      elad #include <sys/queue.h>
     46       1.1      elad #include <sys/stat.h>
     47       1.1      elad #include <sys/dirent.h>
     48       1.1      elad #include <sys/verified_exec.h>
     49       1.1      elad 
     50       1.1      elad #include <err.h>
     51       1.1      elad #include <errno.h>
     52       1.1      elad #include <fts.h>
     53       1.1      elad #include <stdio.h>
     54       1.1      elad #include <stdlib.h>
     55       1.1      elad #include <string.h>
     56       1.1      elad #include <time.h>
     57       1.1      elad #include <unistd.h>
     58       1.1      elad #include <util.h>
     59       1.1      elad 
     60       1.8  christos #include <sha2.h>
     61       1.1      elad 
     62       1.1      elad #define IS_EXEC(mode) ((mode) & (S_IXUSR | S_IXGRP | S_IXOTH))
     63       1.1      elad 
     64       1.1      elad #define DEFAULT_DBFILE  "/etc/signatures"
     65       1.1      elad #define DEFAULT_HASH    "sha256"
     66       1.1      elad #define DEFAULT_SYSPATHS { "/bin", "/sbin", "/usr/bin", "/usr/sbin", \
     67       1.6      elad 			   "/lib", "/usr/lib", "/libexec", "/usr/libexec", \
     68       1.6      elad 			   NULL }
     69       1.1      elad 
     70      1.11       agc /* this struct defines a hash algorithm */
     71      1.11       agc typedef struct hash_t {
     72      1.11       agc 	const char	*hashname;	/* algorithm name */
     73      1.11       agc 	char		*(*filefunc)(const char *, char *); /* function */
     74      1.11       agc } hash_t;
     75      1.11       agc 
     76      1.11       agc /* this struct encapsulates various diverse options and arguments */
     77      1.11       agc typedef struct veriexecgen_t {
     78      1.11       agc 	int	 all_files;	/* scan also for non-executable files */
     79      1.11       agc 	int	 append_output;	/* append output to existing sigs file */
     80      1.11       agc 	char	*dbfile;	/* name of signatures database file */
     81      1.11       agc 	int	 exit_on_error;	/* exit if we can't create a hash */
     82      1.11       agc 	char	*prefix;	/* any prefix to be discarded on output */
     83      1.11       agc 	int	 recursive_scan;/* perform scan for files recursively */
     84      1.11       agc 	int	 scan_system_dirs;	/* just scan system directories */
     85      1.11       agc 	int	 verbose;	/* verbosity level */
     86      1.15      elad 	int	 stamp;		/* put a timestamp */
     87  1.18.4.2    martin 	FILE	*from_file;	/* read from a file or stdin */
     88  1.18.4.2    martin 	char	*from_filename;
     89      1.11       agc } veriexecgen_t;
     90      1.11       agc 
     91      1.10       agc /* this struct describes a directory entry to generate a hash for */
     92       1.1      elad struct fentry {
     93      1.10       agc 	char filename[MAXPATHLEN];	/* name of entry */
     94      1.10       agc 	char *hash_val;			/* its associated hash value */
     95      1.10       agc 	int flags;			/* any associated flags */
     96      1.10       agc 	TAILQ_ENTRY(fentry) f;		/* its place in the queue */
     97       1.1      elad };
     98       1.1      elad TAILQ_HEAD(, fentry) fehead;
     99       1.1      elad 
    100      1.11       agc /* define the possible hash algorithms */
    101      1.11       agc static hash_t	 hashes[] = {
    102       1.1      elad 	{ "SHA256", SHA256_File },
    103       1.1      elad 	{ "SHA384", SHA384_File },
    104       1.1      elad 	{ "SHA512", SHA512_File },
    105       1.1      elad 	{ NULL, NULL },
    106       1.1      elad };
    107       1.1      elad 
    108      1.10       agc static int Fflag;
    109       1.9       agc 
    110      1.10       agc static int	make_immutable;	/* set immutable flag on signatures file */
    111       1.9       agc 
    112       1.9       agc /* warn about a problem - exit if exit_on_error is set */
    113       1.9       agc static void
    114      1.11       agc gripe(veriexecgen_t *vp, const char *fmt, const char *filename)
    115       1.9       agc {
    116       1.9       agc 	warn(fmt, filename);
    117      1.11       agc 	if (vp->exit_on_error) {
    118       1.9       agc 		/* error out on problematic files */
    119       1.9       agc 		exit(EXIT_FAILURE);
    120       1.9       agc 	}
    121       1.9       agc }
    122       1.1      elad 
    123      1.10       agc /* print usage message */
    124       1.1      elad static void
    125       1.1      elad usage(void)
    126       1.1      elad {
    127       1.1      elad 	(void)fprintf(stderr,
    128  1.18.4.2    martin 	    "usage:  %s [-AaDrSTvW] [-d dir] [-f file] [-o fingerprintdb] [-p prefix]\n"
    129      1.12       wiz 	    "\t\t    [-t algorithm]\n"
    130      1.12       wiz 	    "\t%s [-h]\n", getprogname(), getprogname());
    131       1.1      elad }
    132       1.1      elad 
    133      1.10       agc /* tell people what we're doing - scan dirs, fingerprint etc */
    134       1.1      elad static void
    135      1.11       agc banner(veriexecgen_t *vp, hash_t *hash_type, char **search_path)
    136       1.1      elad {
    137       1.1      elad 	int j;
    138       1.1      elad 
    139       1.1      elad 	(void)printf("Fingerprinting ");
    140       1.1      elad 
    141  1.18.4.2    martin 	if (search_path) {
    142  1.18.4.2    martin 		for (j = 0; search_path[j] != NULL; j++)
    143  1.18.4.2    martin 			(void)printf("%s ", search_path[j]);
    144  1.18.4.2    martin 	} else if (vp->from_file == stdin) {
    145  1.18.4.2    martin 		(void)printf("files from stdin ");
    146  1.18.4.2    martin 	} else {
    147  1.18.4.2    martin 		(void)printf("files from %s ",
    148  1.18.4.2    martin 			vp->from_filename ? vp->from_filename : "???");
    149  1.18.4.2    martin 	}
    150       1.1      elad 
    151       1.1      elad 	(void)printf("(%s) (%s) using %s\n",
    152      1.11       agc 	    vp->all_files ? "all files" : "executables only",
    153      1.11       agc 	    vp->recursive_scan ? "recursive" : "non-recursive",
    154       1.1      elad 	    hash_type->hashname);
    155       1.1      elad }
    156       1.1      elad 
    157      1.10       agc /* find a hash algorithm, given its name */
    158      1.11       agc static hash_t *
    159       1.1      elad find_hash(char *hash_type)
    160       1.1      elad {
    161      1.11       agc 	hash_t *hash;
    162       1.1      elad 
    163       1.1      elad 	for (hash = hashes; hash->hashname != NULL; hash++)
    164       1.1      elad 		if (strcasecmp(hash_type, hash->hashname) == 0)
    165       1.1      elad 			return hash;
    166       1.1      elad 	return NULL;
    167       1.1      elad }
    168       1.1      elad 
    169      1.10       agc /* perform the hashing operation on `filename' */
    170       1.1      elad static char *
    171      1.11       agc do_hash(char *filename, hash_t * h)
    172       1.1      elad {
    173       1.1      elad 	return h->filefunc(filename, NULL);
    174       1.1      elad }
    175       1.1      elad 
    176      1.10       agc /* return flags for `path' */
    177       1.1      elad static int
    178       1.1      elad figure_flags(char *path, mode_t mode)
    179       1.1      elad {
    180       1.1      elad #ifdef notyet
    181       1.1      elad 	if (Fflag) {
    182       1.1      elad 		/* Try to figure out right flag(s). */
    183       1.1      elad 		return VERIEXEC_DIRECT;
    184      1.10       agc 	}
    185       1.1      elad #endif /* notyet */
    186       1.1      elad 
    187      1.10       agc 	return (IS_EXEC(mode)) ? 0 : VERIEXEC_FILE;
    188       1.1      elad }
    189       1.1      elad 
    190      1.10       agc /* check to see that we don't have a duplicate entry */
    191       1.1      elad static int
    192       1.1      elad check_dup(char *filename)
    193       1.1      elad {
    194       1.1      elad 	struct fentry *lwalk;
    195       1.1      elad 
    196       1.1      elad 	TAILQ_FOREACH(lwalk, &fehead, f) {
    197      1.15      elad 		if (strcmp(lwalk->filename, filename) == 0)
    198       1.1      elad 			return 1;
    199       1.1      elad 	}
    200       1.1      elad 
    201       1.1      elad 	return 0;
    202       1.1      elad }
    203       1.1      elad 
    204      1.10       agc /* add a new entry to the list for `file' */
    205       1.1      elad static void
    206  1.18.4.2    martin add_new_path_entry(veriexecgen_t *vp, const char *file, hash_t *hash)
    207  1.18.4.2    martin {
    208  1.18.4.2    martin 	struct stat sb;
    209  1.18.4.2    martin 	struct fentry *e;
    210  1.18.4.2    martin 
    211  1.18.4.2    martin 	if (stat(file, &sb) == -1) {
    212  1.18.4.2    martin 		gripe(vp, "Cannot stat file `%s'", file);
    213  1.18.4.2    martin 		return;
    214  1.18.4.2    martin 	}
    215  1.18.4.2    martin 
    216  1.18.4.2    martin 	if (!vp->all_files && !IS_EXEC(sb.st_mode))
    217  1.18.4.2    martin 		return;
    218  1.18.4.2    martin 
    219  1.18.4.2    martin 	e = ecalloc(1UL, sizeof(*e));
    220  1.18.4.2    martin 
    221  1.18.4.2    martin 	if (realpath(file, e->filename) == NULL) {
    222  1.18.4.2    martin 		gripe(vp, "Cannot find absolute path `%s'", file);
    223  1.18.4.2    martin 		return;
    224  1.18.4.2    martin 	}
    225  1.18.4.2    martin 	if (check_dup(e->filename)) {
    226  1.18.4.2    martin 		free(e);
    227  1.18.4.2    martin 		return;
    228  1.18.4.2    martin 	}
    229  1.18.4.2    martin 	if ((e->hash_val = do_hash(e->filename, hash)) == NULL) {
    230  1.18.4.2    martin 		gripe(vp, "Cannot calculate hash `%s'", e->filename);
    231  1.18.4.2    martin 		return;
    232  1.18.4.2    martin 	}
    233  1.18.4.2    martin 	e->flags = figure_flags(e->filename, sb.st_mode);
    234  1.18.4.2    martin 
    235  1.18.4.2    martin 	TAILQ_INSERT_TAIL(&fehead, e, f);
    236  1.18.4.2    martin }
    237  1.18.4.2    martin 
    238  1.18.4.2    martin /* add a new entry to the list for `file' */
    239  1.18.4.2    martin static void
    240  1.18.4.2    martin add_new_ftsent_entry(veriexecgen_t *vp, FTSENT *file, hash_t *hash)
    241       1.1      elad {
    242       1.1      elad 	struct fentry *e;
    243       1.1      elad 	struct stat sb;
    244       1.1      elad 
    245       1.1      elad 	if (file->fts_info == FTS_SL) {
    246      1.10       agc 		/* we have a symbolic link */
    247       1.9       agc 		if (stat(file->fts_path, &sb) == -1) {
    248      1.11       agc 			gripe(vp, "Cannot stat symlink `%s'", file->fts_path);
    249       1.9       agc 			return;
    250       1.9       agc 		}
    251       1.1      elad 	} else
    252       1.1      elad 		sb = *file->fts_statp;
    253       1.1      elad 
    254  1.18.4.1  christos 	if (!vp->all_files && !IS_EXEC(sb.st_mode))
    255       1.1      elad 		return;
    256       1.1      elad 
    257       1.1      elad 	e = ecalloc(1UL, sizeof(*e));
    258       1.1      elad 
    259       1.9       agc 	if (realpath(file->fts_accpath, e->filename) == NULL) {
    260      1.11       agc 		gripe(vp, "Cannot find absolute path `%s'", file->fts_accpath);
    261       1.9       agc 		return;
    262       1.9       agc 	}
    263       1.1      elad 	if (check_dup(e->filename)) {
    264       1.1      elad 		free(e);
    265       1.1      elad 		return;
    266       1.1      elad 	}
    267       1.9       agc 	if ((e->hash_val = do_hash(e->filename, hash)) == NULL) {
    268      1.11       agc 		gripe(vp, "Cannot calculate hash `%s'", e->filename);
    269       1.9       agc 		return;
    270       1.9       agc 	}
    271       1.1      elad 	e->flags = figure_flags(e->filename, sb.st_mode);
    272       1.1      elad 
    273       1.1      elad 	TAILQ_INSERT_TAIL(&fehead, e, f);
    274       1.1      elad }
    275       1.1      elad 
    276      1.10       agc /* walk through a directory */
    277       1.1      elad static void
    278      1.11       agc walk_dir(veriexecgen_t *vp, char **search_path, hash_t *hash)
    279       1.1      elad {
    280       1.1      elad 	FTS *fh;
    281       1.1      elad 	FTSENT *file;
    282       1.1      elad 
    283       1.9       agc 	if ((fh = fts_open(search_path, FTS_PHYSICAL, NULL)) == NULL) {
    284      1.11       agc 		gripe(vp, "fts_open `%s'", (const char *)search_path);
    285       1.9       agc 		return;
    286       1.9       agc 	}
    287       1.1      elad 
    288       1.1      elad 	while ((file = fts_read(fh)) != NULL) {
    289      1.11       agc 		if (!vp->recursive_scan && file->fts_level > 1) {
    290       1.1      elad 			fts_set(fh, file, FTS_SKIP);
    291       1.1      elad 			continue;
    292       1.1      elad 		}
    293       1.1      elad 
    294       1.1      elad 		switch (file->fts_info) {
    295       1.1      elad 		case FTS_D:
    296       1.1      elad 		case FTS_DC:
    297       1.1      elad 		case FTS_DP:
    298       1.1      elad 			continue;
    299       1.1      elad 		default:
    300       1.1      elad 			break;
    301       1.1      elad 		}
    302       1.1      elad 
    303       1.1      elad 		if (file->fts_errno) {
    304      1.11       agc 			if (vp->exit_on_error) {
    305      1.10       agc 				errx(EXIT_FAILURE, "%s: %s", file->fts_path,
    306      1.10       agc 				    strerror(file->fts_errno));
    307      1.10       agc 			}
    308      1.10       agc 		} else {
    309  1.18.4.2    martin 			add_new_ftsent_entry(vp, file, hash);
    310       1.1      elad 		}
    311       1.1      elad 	}
    312       1.1      elad 
    313       1.1      elad 	fts_close(fh);
    314       1.1      elad }
    315       1.1      elad 
    316  1.18.4.2    martin /* read files from `file' */
    317  1.18.4.2    martin static void
    318  1.18.4.2    martin read_from_file(veriexecgen_t *vp, hash_t *hash, FILE *file)
    319  1.18.4.2    martin {
    320  1.18.4.2    martin 	char *line = NULL;
    321  1.18.4.2    martin 	size_t linesize = 0;
    322  1.18.4.2    martin 	ssize_t linelen;
    323  1.18.4.2    martin 
    324  1.18.4.2    martin 	while ((linelen = getline(&line, &linesize, file)) != -1) {
    325  1.18.4.2    martin 		if (linelen > 0 && line[linelen - 1] == '\n')
    326  1.18.4.2    martin 			line[linelen - 1] = '\0';
    327  1.18.4.2    martin 		add_new_path_entry(vp, line, hash);
    328  1.18.4.2    martin 	}
    329  1.18.4.2    martin 
    330  1.18.4.2    martin 	if (ferror(stdin)) {
    331  1.18.4.2    martin 		gripe(vp, "Error reading from stdin `%s'", strerror(errno));
    332  1.18.4.2    martin 		return;
    333  1.18.4.2    martin 	}
    334  1.18.4.2    martin }
    335  1.18.4.2    martin 
    336      1.10       agc /* return a string representation of the flags */
    337       1.1      elad static char *
    338       1.1      elad flags2str(int flags)
    339       1.1      elad {
    340      1.15      elad 	return (flags == 0) ? "" : "file, indirect";
    341      1.15      elad }
    342      1.15      elad 
    343      1.15      elad static char *
    344      1.15      elad escape(const char *s)
    345      1.15      elad {
    346      1.15      elad 	char *q, *p;
    347      1.15      elad 	size_t len;
    348      1.15      elad 
    349      1.15      elad 	len = strlen(s);
    350      1.15      elad 	if (len >= MAXPATHLEN)
    351      1.15      elad 		return (NULL);
    352      1.15      elad 
    353      1.15      elad 	len *= 2;
    354      1.15      elad 	q = p = calloc(1, len + 1);
    355      1.15      elad 
    356      1.15      elad 	while (*s) {
    357      1.15      elad 		if (*s == ' ' || *s == '\t')
    358      1.15      elad 			*p++ = '\\';
    359      1.15      elad 
    360      1.15      elad 		*p++ = *s++;
    361      1.15      elad 	}
    362      1.15      elad 
    363      1.15      elad 	return (q);
    364       1.1      elad }
    365       1.1      elad 
    366      1.10       agc /* store the list in the signatures file */
    367       1.1      elad static void
    368      1.11       agc store_entries(veriexecgen_t *vp, hash_t *hash)
    369       1.1      elad {
    370       1.1      elad 	FILE *fp;
    371       1.1      elad 	int move = 1;
    372       1.1      elad 	char old_dbfile[MAXPATHLEN];
    373       1.1      elad 	time_t ct;
    374       1.1      elad 	struct stat sb;
    375       1.1      elad 	struct fentry  *e;
    376      1.11       agc 	int	prefixc;
    377       1.1      elad 
    378      1.11       agc 	if (stat(vp->dbfile, &sb) != 0) {
    379       1.1      elad 		if (errno == ENOENT)
    380       1.1      elad 			move = 0;
    381       1.1      elad 		else
    382      1.11       agc 			err(EXIT_FAILURE, "could not stat %s", vp->dbfile);
    383       1.1      elad 	}
    384      1.11       agc 	if (move && !vp->append_output) {
    385      1.11       agc 		if (vp->verbose)
    386       1.1      elad 			(void)printf("\nBacking up existing fingerprint file "
    387      1.11       agc 			    "to \"%s.old\"\n\n", vp->dbfile);
    388       1.1      elad 
    389      1.15      elad 		if (snprintf(old_dbfile, sizeof(old_dbfile), "%s.old",
    390      1.15      elad 		    vp->dbfile) < strlen(vp->dbfile) + 4) {
    391      1.10       agc 			err(EXIT_FAILURE, "%s", old_dbfile);
    392       1.1      elad 		}
    393      1.11       agc 		if (rename(vp->dbfile, old_dbfile) == -1)
    394      1.10       agc 			err(EXIT_FAILURE, "could not rename file");
    395       1.1      elad 	}
    396       1.1      elad 
    397      1.11       agc 	prefixc = (vp->prefix == NULL) ? -1 : strlen(vp->prefix);
    398      1.11       agc 
    399      1.11       agc 	fp = efopen(vp->dbfile, vp->append_output ? "a" : "w+");
    400       1.1      elad 
    401      1.15      elad 	if (vp->stamp) {
    402      1.15      elad 		time(&ct);
    403      1.15      elad 		(void)fprintf(fp, "# Generated by %s, %.24s\n",
    404      1.15      elad 			getlogin(), ctime(&ct));
    405      1.15      elad 	}
    406       1.1      elad 
    407       1.1      elad 	TAILQ_FOREACH(e, &fehead, f) {
    408      1.15      elad 		char *file;
    409      1.15      elad 
    410      1.11       agc 		if (vp->verbose)
    411       1.1      elad 			(void)printf("Adding %s.\n", e->filename);
    412       1.1      elad 
    413      1.15      elad 		file = (prefixc < 0) ? e->filename : &e->filename[prefixc];
    414      1.15      elad 		file = escape(file);
    415      1.15      elad 
    416      1.15      elad 		(void)fprintf(fp, "%s %s %s %s\n", file, hash->hashname,
    417      1.15      elad 		    e->hash_val, flags2str(e->flags));
    418      1.11       agc 
    419      1.15      elad 		free(file);
    420       1.1      elad 	}
    421       1.1      elad 
    422       1.1      elad 	(void)fclose(fp);
    423       1.1      elad 
    424      1.11       agc 	if (vp->verbose) {
    425      1.10       agc 		(void)printf("\n\n"
    426      1.10       agc "#############################################################\n"
    427      1.10       agc "  PLEASE VERIFY CONTENTS OF %s AND FINE-TUNE THE\n"
    428      1.10       agc "  FLAGS WHERE APPROPRIATE AFTER READING veriexecctl(8)\n"
    429      1.10       agc "#############################################################\n",
    430      1.11       agc 			vp->dbfile);
    431      1.10       agc 	}
    432       1.1      elad }
    433       1.1      elad 
    434       1.1      elad int
    435       1.1      elad main(int argc, char **argv)
    436       1.1      elad {
    437       1.1      elad 	int ch, total = 0;
    438       1.1      elad 	char **search_path = NULL;
    439      1.11       agc 	hash_t *hash = NULL;
    440      1.11       agc 	veriexecgen_t	v;
    441       1.1      elad 
    442      1.11       agc 	(void) memset(&v, 0x0, sizeof(v));
    443      1.10       agc 	make_immutable = 0;
    444      1.10       agc 	Fflag = 0;
    445       1.1      elad 
    446       1.9       agc 	/* error out if we have a dangling symlink or other fs problem */
    447      1.11       agc 	v.exit_on_error = 1;
    448       1.9       agc 
    449  1.18.4.2    martin 	while ((ch = getopt(argc, argv, "AaDd:f:ho:p:rSTt:vW")) != -1) {
    450       1.1      elad 		switch (ch) {
    451       1.1      elad 		case 'A':
    452      1.11       agc 			v.append_output = 1;
    453       1.1      elad 			break;
    454       1.1      elad 		case 'a':
    455      1.11       agc 			v.all_files = 1;
    456       1.1      elad 			break;
    457       1.1      elad 		case 'D':
    458      1.11       agc 			v.scan_system_dirs = 1;
    459       1.1      elad 			break;
    460       1.1      elad 		case 'd':
    461       1.1      elad 			search_path = erealloc(search_path, sizeof(char *) *
    462       1.1      elad 			    (total + 1));
    463       1.1      elad 			search_path[total] = optarg;
    464       1.1      elad 			search_path[++total] = NULL;
    465       1.1      elad 			break;
    466       1.1      elad #ifdef notyet
    467       1.1      elad 		case 'F':
    468       1.1      elad 			Fflag = 1;
    469       1.1      elad 			break;
    470       1.1      elad #endif /* notyet */
    471  1.18.4.2    martin 		case 'f':
    472  1.18.4.2    martin 			if (strcmp(optarg, "-") == 0) {
    473  1.18.4.2    martin 				v.from_file = stdin;
    474  1.18.4.2    martin 				v.from_filename = NULL;
    475  1.18.4.2    martin 			} else {
    476  1.18.4.2    martin 				v.from_file = fopen(optarg, "r");
    477  1.18.4.2    martin 				if (v.from_file == NULL) {
    478  1.18.4.2    martin 					errx(EXIT_FAILURE,
    479  1.18.4.2    martin 						"Error opening file %s",
    480  1.18.4.2    martin 						optarg);
    481  1.18.4.2    martin 				}
    482  1.18.4.2    martin 				v.from_filename = strdup(optarg);
    483  1.18.4.2    martin 			}
    484  1.18.4.2    martin 			break;
    485       1.1      elad 		case 'h':
    486       1.1      elad 			usage();
    487      1.10       agc 			return EXIT_SUCCESS;
    488       1.1      elad 		case 'o':
    489      1.11       agc 			v.dbfile = optarg;
    490      1.11       agc 			break;
    491      1.11       agc 		case 'p':
    492      1.11       agc 			v.prefix = optarg;
    493       1.1      elad 			break;
    494       1.1      elad 		case 'r':
    495      1.11       agc 			v.recursive_scan = 1;
    496       1.1      elad 			break;
    497       1.3      elad 		case 'S':
    498      1.10       agc 			make_immutable = 1;
    499       1.3      elad 			break;
    500      1.15      elad 		case 'T':
    501      1.15      elad 			v.stamp = 1;
    502      1.15      elad 			break;
    503       1.1      elad 		case 't':
    504      1.10       agc 			if ((hash = find_hash(optarg)) == NULL) {
    505      1.10       agc 				errx(EXIT_FAILURE,
    506      1.10       agc 					"No such hash algorithm (%s)",
    507      1.10       agc 					optarg);
    508      1.10       agc 			}
    509       1.1      elad 			break;
    510       1.1      elad 		case 'v':
    511      1.11       agc 			v.verbose = 1;
    512       1.1      elad 			break;
    513       1.9       agc 		case 'W':
    514      1.11       agc 			v.exit_on_error = 0;
    515       1.9       agc 			break;
    516       1.1      elad 		default:
    517       1.1      elad 			usage();
    518      1.10       agc 			return EXIT_FAILURE;
    519       1.1      elad 		}
    520       1.1      elad 	}
    521       1.1      elad 
    522      1.11       agc 	if (v.dbfile == NULL)
    523      1.11       agc 		v.dbfile = DEFAULT_DBFILE;
    524       1.1      elad 
    525       1.1      elad 	if (hash == NULL) {
    526       1.1      elad 		if ((hash = find_hash(DEFAULT_HASH)) == NULL)
    527      1.10       agc 			errx(EXIT_FAILURE, "No hash algorithm");
    528       1.1      elad 	}
    529       1.1      elad 
    530       1.1      elad 	TAILQ_INIT(&fehead);
    531       1.1      elad 
    532  1.18.4.2    martin 	if (search_path == NULL && !v.from_file)
    533      1.11       agc 		v.scan_system_dirs = 1;
    534       1.1      elad 
    535      1.11       agc 	if (v.scan_system_dirs) {
    536       1.1      elad 		char *sys_paths[] = DEFAULT_SYSPATHS;
    537       1.1      elad 
    538      1.11       agc 		if (v.verbose)
    539      1.11       agc 			banner(&v, hash, sys_paths);
    540      1.11       agc 		walk_dir(&v, sys_paths, hash);
    541       1.1      elad 	}
    542       1.1      elad 
    543       1.1      elad 	if (search_path != NULL) {
    544      1.11       agc 		if (v.verbose)
    545      1.11       agc 			banner(&v, hash, search_path);
    546      1.11       agc 		walk_dir(&v, search_path, hash);
    547       1.1      elad 	}
    548       1.1      elad 
    549  1.18.4.2    martin 	if (v.from_file) {
    550  1.18.4.2    martin 		if (v.verbose)
    551  1.18.4.2    martin 			banner(&v, hash, NULL);
    552  1.18.4.2    martin 		read_from_file(&v, hash, v.from_file);
    553  1.18.4.2    martin 	}
    554  1.18.4.2    martin 
    555      1.11       agc 	store_entries(&v, hash);
    556       1.1      elad 
    557      1.11       agc 	if (make_immutable && chflags(v.dbfile, SF_IMMUTABLE) != 0)
    558      1.10       agc 		err(EXIT_FAILURE, "Can't set immutable flag");
    559       1.3      elad 
    560  1.18.4.2    martin 	if (v.from_file && v.from_file != stdin) {
    561  1.18.4.2    martin 		fclose(v.from_file);
    562  1.18.4.2    martin 		free(v.from_filename);
    563  1.18.4.2    martin 	}
    564  1.18.4.2    martin 
    565      1.10       agc 	return EXIT_SUCCESS;
    566       1.1      elad }
    567