162224350SCasper H.S. Dik /*
262224350SCasper H.S. Dik  * CDDL HEADER START
362224350SCasper H.S. Dik  *
462224350SCasper H.S. Dik  * The contents of this file are subject to the terms of the
562224350SCasper H.S. Dik  * Common Development and Distribution License (the "License").
662224350SCasper H.S. Dik  * You may not use this file except in compliance with the License.
762224350SCasper H.S. Dik  *
862224350SCasper H.S. Dik  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
962224350SCasper H.S. Dik  * or http://www.opensolaris.org/os/licensing.
1062224350SCasper H.S. Dik  * See the License for the specific language governing permissions
1162224350SCasper H.S. Dik  * and limitations under the License.
1262224350SCasper H.S. Dik  *
1362224350SCasper H.S. Dik  * When distributing Covered Code, include this CDDL HEADER in each
1462224350SCasper H.S. Dik  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1562224350SCasper H.S. Dik  * If applicable, add the following below this CDDL HEADER, with the
1662224350SCasper H.S. Dik  * fields enclosed by brackets "[]" replaced with your own identifying
1762224350SCasper H.S. Dik  * information: Portions Copyright [yyyy] [name of copyright owner]
1862224350SCasper H.S. Dik  *
1962224350SCasper H.S. Dik  * CDDL HEADER END
2062224350SCasper H.S. Dik  */
2162224350SCasper H.S. Dik 
2262224350SCasper H.S. Dik /*
237706a9bfSCasper H.S. Dik  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
2462224350SCasper H.S. Dik  * Use is subject to license terms.
2562224350SCasper H.S. Dik  */
2662224350SCasper H.S. Dik 
2762224350SCasper H.S. Dik /*
2862224350SCasper H.S. Dik  * The Solaris package installer in-memory database server.
2962224350SCasper H.S. Dik  *
3062224350SCasper H.S. Dik  * We'll keep the contents file as before; but we cache it
3162224350SCasper H.S. Dik  * and we don't write it as often.  Instead, we log all
3262224350SCasper H.S. Dik  * modifications to the log file.
3362224350SCasper H.S. Dik  * Using the contents file and the logfile, the pkgserv can
3462224350SCasper H.S. Dik  * rebuild the up-to-date contents file.
3562224350SCasper H.S. Dik  * The logfile is constructed so that rebuilding the
3662224350SCasper H.S. Dik  * contents file with the logfile is idempotent.
3762224350SCasper H.S. Dik  *
3862224350SCasper H.S. Dik  * The libpkg will start the daemon.
3962224350SCasper H.S. Dik  *
4062224350SCasper H.S. Dik  * The pkgserv will daemonize itself; the parent process
4162224350SCasper H.S. Dik  * waits until the child process has initialized and will
4262224350SCasper H.S. Dik  * start the door server.
4362224350SCasper H.S. Dik  * If any error occurs during start-up, the error messages
4462224350SCasper H.S. Dik  * are printed to stderr and the daemon will exit.
4562224350SCasper H.S. Dik  * After start-up, any further errors are logged to syslog.
4662224350SCasper H.S. Dik  * The parent pkgserv will exit with:
4762224350SCasper H.S. Dik  *	0	- We've started
4862224350SCasper H.S. Dik  *	1	- We couldn't start (locked)
4962224350SCasper H.S. Dik  *	2	- Other problems (error on stderr)
5062224350SCasper H.S. Dik  *     99	- Nothing reported; the caller must report.
5162224350SCasper H.S. Dik  *
5262224350SCasper H.S. Dik  * The daemon will timeout, by default.  It will write the
5362224350SCasper H.S. Dik  * contents file after a first timeout; and after a further
5462224350SCasper H.S. Dik  * timeout, the daemon will exit.
5562224350SCasper H.S. Dik  *
5662224350SCasper H.S. Dik  * The daemon will only timeout if the current "client" has exited;
5762224350SCasper H.S. Dik  * to this end, we always look at the pid of the last caller.
5862224350SCasper H.S. Dik  * If the last client is no longer around, we record the new client.
5962224350SCasper H.S. Dik  * In the typical case of running installf/removef from a post/preinstall
6062224350SCasper H.S. Dik  * script, we continue to follow the pkginstall/pkgremove client's pid.
6162224350SCasper H.S. Dik  *
6262224350SCasper H.S. Dik  * In the particular case of install, we make sure the daemon
6362224350SCasper H.S. Dik  * sticks around.  (Install == install, (live)upgrade, zone install)
6462224350SCasper H.S. Dik  */
6562224350SCasper H.S. Dik 
6662224350SCasper H.S. Dik #ifdef lint
6762224350SCasper H.S. Dik #undef _FILE_OFFSET_BITS
6862224350SCasper H.S. Dik #endif
6962224350SCasper H.S. Dik 
7062224350SCasper H.S. Dik #include <door.h>
7162224350SCasper H.S. Dik #include <errno.h>
7262224350SCasper H.S. Dik #include <fcntl.h>
7362224350SCasper H.S. Dik #include <limits.h>
7462224350SCasper H.S. Dik #include <pthread.h>
7562224350SCasper H.S. Dik #include <signal.h>
7662224350SCasper H.S. Dik #include <stddef.h>
7762224350SCasper H.S. Dik #include <stdio.h>
7862224350SCasper H.S. Dik #include <stdlib.h>
7962224350SCasper H.S. Dik #include <strings.h>
8062224350SCasper H.S. Dik #include <synch.h>
8162224350SCasper H.S. Dik #include <sys/avl.h>
8262224350SCasper H.S. Dik #include <sys/stat.h>
8362224350SCasper H.S. Dik #include <sys/statvfs.h>
8462224350SCasper H.S. Dik #include <sys/mman.h>
8562224350SCasper H.S. Dik #include <sys/time.h>
8662224350SCasper H.S. Dik #include <sys/wait.h>
8762224350SCasper H.S. Dik #include <syslog.h>
8862224350SCasper H.S. Dik #include <limits.h>
8962224350SCasper H.S. Dik #include <thread.h>
9062224350SCasper H.S. Dik #include <ucred.h>
9162224350SCasper H.S. Dik #include <umem.h>
9262224350SCasper H.S. Dik #include <unistd.h>
9362224350SCasper H.S. Dik #include <libintl.h>
9462224350SCasper H.S. Dik #include <locale.h>
9562224350SCasper H.S. Dik 
9662224350SCasper H.S. Dik #include <pkglib.h>
9762224350SCasper H.S. Dik 
9862224350SCasper H.S. Dik #define	SADM_DIR	"/var/sadm/install"
9962224350SCasper H.S. Dik 
10062224350SCasper H.S. Dik #define	LOCK		".pkg.lock"
10162224350SCasper H.S. Dik #define	CLIENTLOCK	".pkg.lock.client"
10262224350SCasper H.S. Dik #define	CONTENTS	"contents"
10362224350SCasper H.S. Dik #define	TCONTENTS	"t.contents"
10462224350SCasper H.S. Dik #define	BADCONTENTS	"contents.badXXXXXX"
10562224350SCasper H.S. Dik 
10662224350SCasper H.S. Dik #define	LLNANOSEC	((int64_t)NANOSEC)
10762224350SCasper H.S. Dik 
10862224350SCasper H.S. Dik #define	DUMPTIMEOUT	60
10962224350SCasper H.S. Dik #define	EXITTIMEOUT	300
11062224350SCasper H.S. Dik 
11162224350SCasper H.S. Dik /*
11262224350SCasper H.S. Dik  * Contents file storage format.  At install time, the amount of memory
11362224350SCasper H.S. Dik  * might be limited, so we make sure that we use as little memory
11462224350SCasper H.S. Dik  * as possible.  The package tools modify the entries; so we install the
11562224350SCasper H.S. Dik  * single lines.  We also remember the length of the path; this is needed
11662224350SCasper H.S. Dik  * for avlcmp and we return it to the tools.  This saves time.
11762224350SCasper H.S. Dik  *
11862224350SCasper H.S. Dik  * All strings are allocated using umem_alloc.
11962224350SCasper H.S. Dik  */
12062224350SCasper H.S. Dik typedef struct pkgentry {
12162224350SCasper H.S. Dik 	char *line;		/* The contents line for the file */
12262224350SCasper H.S. Dik 	avl_node_t avl;		/* The avl header */
12362224350SCasper H.S. Dik 	int pkgoff;		/* Where the packages live; start with SP */
12462224350SCasper H.S. Dik 	int pathlen;		/* The length of the pathname */
12562224350SCasper H.S. Dik 	int len;		/* Length of the line (incl NUL) */
12662224350SCasper H.S. Dik } pkgentry_t;
12762224350SCasper H.S. Dik 
12862224350SCasper H.S. Dik static char IS_ST0[256];
12962224350SCasper H.S. Dik static char IS_ST0Q[256];
13062224350SCasper H.S. Dik 
13162224350SCasper H.S. Dik static void pkg_door_srv(void *, char *, size_t, door_desc_t *, uint_t);
13262224350SCasper H.S. Dik static char *file_find(pkgfilter_t *, int *);
13362224350SCasper H.S. Dik static void parse_contents(void);
13462224350SCasper H.S. Dik static int parse_log(void);
13562224350SCasper H.S. Dik static void pkgdump(void);
13662224350SCasper H.S. Dik static int logflush(void);
13762224350SCasper H.S. Dik static int avlcmp(const void *, const void *);
13862224350SCasper H.S. Dik static void freeentry(pkgentry_t *);
13962224350SCasper H.S. Dik static void swapentry(pkgentry_t *, pkgentry_t *);
14062224350SCasper H.S. Dik static int establish_lock(char *);
14162224350SCasper H.S. Dik static int no_memory_abort(void);
14262224350SCasper H.S. Dik static int pkgfilter(pkgfilter_t *, door_desc_t *);
14362224350SCasper H.S. Dik static int pkgaddlines(pkgfilter_t *);
14462224350SCasper H.S. Dik static void finish(void);
14562224350SCasper H.S. Dik static void signal_handler(int);
14662224350SCasper H.S. Dik static void my_cond_reltimedwait(hrtime_t, int);
14762224350SCasper H.S. Dik static hrtime_t time_since_(hrtime_t);
14862224350SCasper H.S. Dik 
14962224350SCasper H.S. Dik /*
15062224350SCasper H.S. Dik  * Server actions
15162224350SCasper H.S. Dik  *	- set mode (contents file, log file)
15262224350SCasper H.S. Dik  *	- roll log
15362224350SCasper H.S. Dik  *	- remove package
15462224350SCasper H.S. Dik  *	- merge package entries
15562224350SCasper H.S. Dik  */
15662224350SCasper H.S. Dik 
15762224350SCasper H.S. Dik static FILE *log;
15862224350SCasper H.S. Dik static char *door = PKGDOOR;
15962224350SCasper H.S. Dik 
16062224350SCasper H.S. Dik static avl_tree_t listp, *list = &listp;
16162224350SCasper H.S. Dik 
16262224350SCasper H.S. Dik /* Keep the "the last command modified the contents file ... */
16362224350SCasper H.S. Dik static char *ccmnt[2];
16462224350SCasper H.S. Dik static int cind = 0;
16562224350SCasper H.S. Dik 
16662224350SCasper H.S. Dik static mutex_t mtx = DEFAULTMUTEX;
16762224350SCasper H.S. Dik static cond_t cv = DEFAULTCV;
16862224350SCasper H.S. Dik 
16962224350SCasper H.S. Dik static int flushbeforemark = 1;
17062224350SCasper H.S. Dik static int logerrcnt = 0;
17162224350SCasper H.S. Dik static int loglines = 0;
17262224350SCasper H.S. Dik static int suppressed = 0;
17362224350SCasper H.S. Dik static int logcount;
17462224350SCasper H.S. Dik static int ndumps;
17562224350SCasper H.S. Dik static int ncalls;
17662224350SCasper H.S. Dik static int changes;
17762224350SCasper H.S. Dik static hrtime_t lastchange;
17862224350SCasper H.S. Dik static hrtime_t lastcall;
17962224350SCasper H.S. Dik static volatile int want_to_quit;
18062224350SCasper H.S. Dik static boolean_t read_only = B_FALSE;
18162224350SCasper H.S. Dik static boolean_t permanent = B_FALSE;
18262224350SCasper H.S. Dik static boolean_t one_shot = B_FALSE;
18362224350SCasper H.S. Dik static int write_locked;
18462224350SCasper H.S. Dik static pid_t client_pid;
18562224350SCasper H.S. Dik static int verbose = 1;
18662224350SCasper H.S. Dik static hrtime_t dumptimeout = DUMPTIMEOUT;
18762224350SCasper H.S. Dik static boolean_t sync_needed = B_FALSE;
18862224350SCasper H.S. Dik 
18962224350SCasper H.S. Dik static uid_t myuid;
19062224350SCasper H.S. Dik 
19162224350SCasper H.S. Dik static char marker[] = "###Marker\n";
19262224350SCasper H.S. Dik 
19362224350SCasper H.S. Dik static umem_cache_t *ecache;
19462224350SCasper H.S. Dik 
19562224350SCasper H.S. Dik static char pkgdir[PATH_MAX];
19662224350SCasper H.S. Dik 
19762224350SCasper H.S. Dik static void
server_main(int argc,char ** argv)19862224350SCasper H.S. Dik server_main(int argc, char **argv)
19962224350SCasper H.S. Dik {
20062224350SCasper H.S. Dik 	int did;
20162224350SCasper H.S. Dik 	int c;
20262224350SCasper H.S. Dik 	struct statvfs vfsbuf;
20362224350SCasper H.S. Dik 	int imexit = 0;
20462224350SCasper H.S. Dik 	pid_t parent;
20562224350SCasper H.S. Dik 	char *root = NULL;
20662224350SCasper H.S. Dik 	char *sadmdir = NULL;
20762224350SCasper H.S. Dik 	hrtime_t delta;
20862224350SCasper H.S. Dik 	int dir = 0;
20962224350SCasper H.S. Dik 	int dfd;
21062224350SCasper H.S. Dik 
21162224350SCasper H.S. Dik 	(void) set_prog_name("pkgserv");
21262224350SCasper H.S. Dik 
21362224350SCasper H.S. Dik 	openlog("pkgserv", LOG_PID | LOG_ODELAY, LOG_DAEMON);
21462224350SCasper H.S. Dik 
21562224350SCasper H.S. Dik 	while ((c = getopt(argc, argv, "d:eoN:pP:R:r:")) != EOF) {
21662224350SCasper H.S. Dik 		switch (c) {
21762224350SCasper H.S. Dik 		case 'e':
21862224350SCasper H.S. Dik 			imexit = 1;
21962224350SCasper H.S. Dik 			break;
22062224350SCasper H.S. Dik 		case 'd':
22162224350SCasper H.S. Dik 			sadmdir = optarg;
22262224350SCasper H.S. Dik 			if (*sadmdir != '/' || strlen(sadmdir) >= PATH_MAX ||
22362224350SCasper H.S. Dik 			    access(sadmdir, X_OK) != 0)
22462224350SCasper H.S. Dik 				exit(99);
22562224350SCasper H.S. Dik 			break;
22662224350SCasper H.S. Dik 		case 'N':
22762224350SCasper H.S. Dik 			(void) set_prog_name(optarg);
22862224350SCasper H.S. Dik 			break;
22962224350SCasper H.S. Dik 		case 'o':
23062224350SCasper H.S. Dik 			one_shot = B_TRUE;
23162224350SCasper H.S. Dik 			verbose = 0;
23262224350SCasper H.S. Dik 			break;
23362224350SCasper H.S. Dik 		case 'p':
23462224350SCasper H.S. Dik 			/*
23562224350SCasper H.S. Dik 			 * We are updating possibly many zones; so we're not
23662224350SCasper H.S. Dik 			 * dumping based on a short timeout and we will not
23762224350SCasper H.S. Dik 			 * exit.
23862224350SCasper H.S. Dik 			 */
23962224350SCasper H.S. Dik 			permanent = B_TRUE;
24062224350SCasper H.S. Dik 			dumptimeout = 3600;
24162224350SCasper H.S. Dik 			break;
24262224350SCasper H.S. Dik 		case 'P':
24362224350SCasper H.S. Dik 			client_pid = atoi(optarg);
24462224350SCasper H.S. Dik 			break;
24562224350SCasper H.S. Dik 		case 'R':
24662224350SCasper H.S. Dik 			root = optarg;
24762224350SCasper H.S. Dik 			if (*root != '/' || strlen(root) >= PATH_MAX ||
24862224350SCasper H.S. Dik 			    access(root, X_OK) != 0)
24962224350SCasper H.S. Dik 				exit(99);
25062224350SCasper H.S. Dik 			break;
25162224350SCasper H.S. Dik 		case 'r':
25262224350SCasper H.S. Dik 			read_only = B_TRUE;
25362224350SCasper H.S. Dik 			one_shot = B_TRUE;
25462224350SCasper H.S. Dik 			verbose = 0;
25562224350SCasper H.S. Dik 			door = optarg;
25662224350SCasper H.S. Dik 			break;
25762224350SCasper H.S. Dik 		default:
25862224350SCasper H.S. Dik 			exit(99);
25962224350SCasper H.S. Dik 		}
26062224350SCasper H.S. Dik 	}
26162224350SCasper H.S. Dik 
26262224350SCasper H.S. Dik 	if (one_shot && permanent) {
26362224350SCasper H.S. Dik 		progerr(gettext("Incorrect Usage"));
26462224350SCasper H.S. Dik 		exit(99);
26562224350SCasper H.S. Dik 	}
26662224350SCasper H.S. Dik 
26762224350SCasper H.S. Dik 	umem_nofail_callback(no_memory_abort);
26862224350SCasper H.S. Dik 
26962224350SCasper H.S. Dik 	if (root != NULL && strcmp(root, "/") != 0) {
27062224350SCasper H.S. Dik 		if (snprintf(pkgdir, PATH_MAX, "%s%s", root,
27162224350SCasper H.S. Dik 		    sadmdir == NULL ? SADM_DIR : sadmdir) >= PATH_MAX) {
27262224350SCasper H.S. Dik 			exit(99);
27362224350SCasper H.S. Dik 		}
27462224350SCasper H.S. Dik 	} else {
27562224350SCasper H.S. Dik 		if (sadmdir == NULL)
27662224350SCasper H.S. Dik 			(void) strcpy(pkgdir, SADM_DIR);
27762224350SCasper H.S. Dik 		else
27862224350SCasper H.S. Dik 			(void) strcpy(pkgdir, sadmdir);
27962224350SCasper H.S. Dik 	}
28062224350SCasper H.S. Dik 
28162224350SCasper H.S. Dik 	if (chdir(pkgdir) != 0) {
28262224350SCasper H.S. Dik 		progerr(gettext("can't chdir to %s"), pkgdir);
28362224350SCasper H.S. Dik 		exit(2);
28462224350SCasper H.S. Dik 	}
28562224350SCasper H.S. Dik 
28662224350SCasper H.S. Dik 	closefrom(3);
28762224350SCasper H.S. Dik 
28862224350SCasper H.S. Dik 	if (!read_only && establish_lock(LOCK) < 0) {
28962224350SCasper H.S. Dik 		progerr(gettext(
29062224350SCasper H.S. Dik 		    "couldn't lock in %s (server running?): %s"),
29162224350SCasper H.S. Dik 		    pkgdir, strerror(errno));
29262224350SCasper H.S. Dik 		exit(1);
29362224350SCasper H.S. Dik 	}
29462224350SCasper H.S. Dik 
29562224350SCasper H.S. Dik 	did = door_create(pkg_door_srv, 0, DOOR_REFUSE_DESC);
29662224350SCasper H.S. Dik 	if (did == -1) {
29762224350SCasper H.S. Dik 		progerr("door_create: %s", strerror(errno));
29862224350SCasper H.S. Dik 		exit(2);
29962224350SCasper H.S. Dik 	}
30062224350SCasper H.S. Dik 
30162224350SCasper H.S. Dik 	(void) fdetach(door);
30262224350SCasper H.S. Dik 
30362224350SCasper H.S. Dik 	if ((dfd = creat(door, 0644)) < 0 || close(dfd) < 0) {
30462224350SCasper H.S. Dik 		progerr("door_create: %s", strerror(errno));
30562224350SCasper H.S. Dik 		exit(2);
30662224350SCasper H.S. Dik 	}
30762224350SCasper H.S. Dik 
30862224350SCasper H.S. Dik 	(void) mutex_lock(&mtx);
30962224350SCasper H.S. Dik 
31062224350SCasper H.S. Dik 	myuid = geteuid();
31162224350SCasper H.S. Dik 
31262224350SCasper H.S. Dik 	(void) sigset(SIGHUP, signal_handler);
31362224350SCasper H.S. Dik 	(void) sigset(SIGTERM, signal_handler);
31462224350SCasper H.S. Dik 	(void) sigset(SIGINT, signal_handler);
31562224350SCasper H.S. Dik 	(void) sigset(SIGQUIT, signal_handler);
31662224350SCasper H.S. Dik 
31762224350SCasper H.S. Dik 	(void) signal(SIGPIPE, SIG_IGN);
31862224350SCasper H.S. Dik 
31962224350SCasper H.S. Dik 	(void) atexit(finish);
32062224350SCasper H.S. Dik 
32162224350SCasper H.S. Dik 	if (fattach(did, door) != 0) {
32262224350SCasper H.S. Dik 		progerr(gettext("attach door: %s"), strerror(errno));
32362224350SCasper H.S. Dik 		exit(2);
32462224350SCasper H.S. Dik 	}
32562224350SCasper H.S. Dik 	(void) close(did);
32662224350SCasper H.S. Dik 
32762224350SCasper H.S. Dik 	ecache = umem_cache_create("entry", sizeof (pkgentry_t),
32862224350SCasper H.S. Dik 	    sizeof (char *), NULL, NULL, NULL, NULL, NULL, 0);
32962224350SCasper H.S. Dik 
33062224350SCasper H.S. Dik 	avl_create(list, avlcmp, sizeof (pkgentry_t),
33162224350SCasper H.S. Dik 	    offsetof(pkgentry_t, avl));
33262224350SCasper H.S. Dik 
33362224350SCasper H.S. Dik 	IS_ST0['\0'] = 1;
33462224350SCasper H.S. Dik 	IS_ST0[' '] = 1;
33562224350SCasper H.S. Dik 	IS_ST0['\t'] = 1;
33662224350SCasper H.S. Dik 
33762224350SCasper H.S. Dik 	IS_ST0Q['\0'] = 1;
33862224350SCasper H.S. Dik 	IS_ST0Q[' '] = 1;
33962224350SCasper H.S. Dik 	IS_ST0Q['\t'] = 1;
34062224350SCasper H.S. Dik 	IS_ST0Q['='] = 1;
34162224350SCasper H.S. Dik 
34262224350SCasper H.S. Dik 	parse_contents();
34362224350SCasper H.S. Dik 	if (parse_log() > 0)
34462224350SCasper H.S. Dik 		pkgdump();
34562224350SCasper H.S. Dik 
34662224350SCasper H.S. Dik 	if (imexit)
34762224350SCasper H.S. Dik 		exit(0);
34862224350SCasper H.S. Dik 
34962224350SCasper H.S. Dik 	if (statvfs(".", &vfsbuf) != 0) {
35062224350SCasper H.S. Dik 		progerr(gettext("statvfs: %s"), strerror(errno));
35162224350SCasper H.S. Dik 		exit(2);
35262224350SCasper H.S. Dik 	}
35362224350SCasper H.S. Dik 
35462224350SCasper H.S. Dik 	if (strcmp(vfsbuf.f_basetype, "zfs") == 0)
35562224350SCasper H.S. Dik 		flushbeforemark = 0;
35662224350SCasper H.S. Dik 
35762224350SCasper H.S. Dik 	/* We've started, tell the parent */
35862224350SCasper H.S. Dik 	parent = getppid();
35962224350SCasper H.S. Dik 	if (parent != 1)
36062224350SCasper H.S. Dik 		(void) kill(parent, SIGUSR1);
36162224350SCasper H.S. Dik 
36262224350SCasper H.S. Dik 	if (!one_shot) {
36362224350SCasper H.S. Dik 		int fd;
36462224350SCasper H.S. Dik 		(void) setsid();
36562224350SCasper H.S. Dik 		fd = open("/dev/null", O_RDWR, 0);
36662224350SCasper H.S. Dik 		if (fd >= 0) {
36762224350SCasper H.S. Dik 			(void) dup2(fd, STDIN_FILENO);
36862224350SCasper H.S. Dik 			(void) dup2(fd, STDOUT_FILENO);
36962224350SCasper H.S. Dik 			(void) dup2(fd, STDERR_FILENO);
37062224350SCasper H.S. Dik 			if (fd > 2)
37162224350SCasper H.S. Dik 				(void) close(fd);
37262224350SCasper H.S. Dik 		}
37362224350SCasper H.S. Dik 	}
37462224350SCasper H.S. Dik 
37562224350SCasper H.S. Dik 	lastcall = lastchange = gethrtime();
37662224350SCasper H.S. Dik 
37762224350SCasper H.S. Dik 	/*
37862224350SCasper H.S. Dik 	 * Start the main thread, here is where we unlock the mutex.
37962224350SCasper H.S. Dik 	 */
38062224350SCasper H.S. Dik 	for (;;) {
38162224350SCasper H.S. Dik 		if (want_to_quit) {
38262224350SCasper H.S. Dik 			pkgdump();
38362224350SCasper H.S. Dik 			exit(0);
38462224350SCasper H.S. Dik 		}
38562224350SCasper H.S. Dik 		/* Wait forever when root or when there's a running filter */
38662224350SCasper H.S. Dik 		if (write_locked ||
38762224350SCasper H.S. Dik 		    (!one_shot && permanent && dir == changes)) {
38862224350SCasper H.S. Dik 			(void) cond_wait(&cv, &mtx);
38962224350SCasper H.S. Dik 			continue;
39062224350SCasper H.S. Dik 		}
39162224350SCasper H.S. Dik 		delta = time_since_(lastchange);
39262224350SCasper H.S. Dik 		/* Wait until DUMPTIMEOUT after last change before we pkgdump */
39362224350SCasper H.S. Dik 		if (delta < dumptimeout * LLNANOSEC) {
39462224350SCasper H.S. Dik 			my_cond_reltimedwait(delta, dumptimeout);
39562224350SCasper H.S. Dik 			continue;
39662224350SCasper H.S. Dik 		}
39762224350SCasper H.S. Dik 		/* Client still around? Just wait then. */
39862224350SCasper H.S. Dik 		if (client_pid > 1 && kill(client_pid, 0) == 0) {
39962224350SCasper H.S. Dik 			lastchange = lastcall = gethrtime();
40062224350SCasper H.S. Dik 			continue;
40162224350SCasper H.S. Dik 		}
40262224350SCasper H.S. Dik 		/* Wait for another EXITTIMEOUT seconds before we exit */
40362224350SCasper H.S. Dik 		if ((one_shot || !permanent) && dir == changes) {
40462224350SCasper H.S. Dik 			delta = time_since_(lastcall);
40562224350SCasper H.S. Dik 			if (delta < EXITTIMEOUT * LLNANOSEC) {
40662224350SCasper H.S. Dik 				my_cond_reltimedwait(delta, EXITTIMEOUT);
40762224350SCasper H.S. Dik 				continue;
40862224350SCasper H.S. Dik 			}
40962224350SCasper H.S. Dik 			exit(0);
41062224350SCasper H.S. Dik 		}
41162224350SCasper H.S. Dik 		pkgdump();
41262224350SCasper H.S. Dik 		dir = changes;
41362224350SCasper H.S. Dik 	}
41462224350SCasper H.S. Dik 
41562224350SCasper H.S. Dik 	/*NOTREACHED*/
41662224350SCasper H.S. Dik }
41762224350SCasper H.S. Dik 
41862224350SCasper H.S. Dik /*ARGSUSED*/
41962224350SCasper H.S. Dik static void
nothing(int sig)42062224350SCasper H.S. Dik nothing(int sig)
42162224350SCasper H.S. Dik {
42262224350SCasper H.S. Dik }
42362224350SCasper H.S. Dik 
42462224350SCasper H.S. Dik int
main(int argc,char ** argv)42562224350SCasper H.S. Dik main(int argc, char **argv)
42662224350SCasper H.S. Dik {
42762224350SCasper H.S. Dik 	int sig;
42862224350SCasper H.S. Dik 	sigset_t sset;
42962224350SCasper H.S. Dik 	int stat;
43062224350SCasper H.S. Dik 
43162224350SCasper H.S. Dik 	/*
43262224350SCasper H.S. Dik 	 * We're starting the daemon; this process exits when the door
43362224350SCasper H.S. Dik 	 * server is established or when it fails to establish.
43462224350SCasper H.S. Dik 	 * We wait until the child process sends a SIGUSR1 or when it
43562224350SCasper H.S. Dik 	 * exits.
43662224350SCasper H.S. Dik 	 * We keep around who started us and as long as it lives, we don't
43762224350SCasper H.S. Dik 	 * exit.
43862224350SCasper H.S. Dik 	 */
43962224350SCasper H.S. Dik 
44062224350SCasper H.S. Dik 	(void) setlocale(LC_ALL, "");
44162224350SCasper H.S. Dik 	(void) textdomain(TEXT_DOMAIN);
44262224350SCasper H.S. Dik 
44362224350SCasper H.S. Dik 	client_pid = getppid();
44462224350SCasper H.S. Dik 
44562224350SCasper H.S. Dik 	(void) sigemptyset(&sset);
44662224350SCasper H.S. Dik 	(void) sigaddset(&sset, SIGUSR1);
44762224350SCasper H.S. Dik 	(void) sigaddset(&sset, SIGCLD);
44862224350SCasper H.S. Dik 
44962224350SCasper H.S. Dik 	/* We need to catch the SIGCLD before we can sigwait for it. */
45062224350SCasper H.S. Dik 	(void) sigset(SIGCLD, nothing);
45162224350SCasper H.S. Dik 	/* We need to make sure that SIGUSR1 is not ignored. */
45262224350SCasper H.S. Dik 	(void) sigset(SIGUSR1, SIG_DFL);
45362224350SCasper H.S. Dik 	(void) sigprocmask(SIG_BLOCK, &sset, NULL);
45462224350SCasper H.S. Dik 
45562224350SCasper H.S. Dik 	/* We install the contents file readable. */
45662224350SCasper H.S. Dik 	(void) umask(022);
45762224350SCasper H.S. Dik 
45862224350SCasper H.S. Dik 	switch (fork()) {
45962224350SCasper H.S. Dik 	case -1:
46062224350SCasper H.S. Dik 		exit(99);
46162224350SCasper H.S. Dik 		/*NOTREACHED*/
46262224350SCasper H.S. Dik 	case 0:
46362224350SCasper H.S. Dik 		server_main(argc, argv);
46462224350SCasper H.S. Dik 		/*NOTREACHED*/
46562224350SCasper H.S. Dik 	default:
46662224350SCasper H.S. Dik 		/* In the parent */
46762224350SCasper H.S. Dik 		break;
46862224350SCasper H.S. Dik 	}
46962224350SCasper H.S. Dik 
47062224350SCasper H.S. Dik 	for (;;) {
47162224350SCasper H.S. Dik 		sig = sigwait(&sset);
47262224350SCasper H.S. Dik 
47362224350SCasper H.S. Dik 		switch (sig) {
47462224350SCasper H.S. Dik 		case SIGCLD:
47562224350SCasper H.S. Dik 			if (wait(&stat) > 0) {
47662224350SCasper H.S. Dik 				if (WIFEXITED(stat))
47762224350SCasper H.S. Dik 					_exit(WEXITSTATUS(stat));
47862224350SCasper H.S. Dik 				else if (WIFSIGNALED(stat))
47962224350SCasper H.S. Dik 					_exit(99);
48062224350SCasper H.S. Dik 			}
48162224350SCasper H.S. Dik 			break;
48262224350SCasper H.S. Dik 		case SIGUSR1:
48362224350SCasper H.S. Dik 			_exit(0);
48462224350SCasper H.S. Dik 		}
48562224350SCasper H.S. Dik 	}
48662224350SCasper H.S. Dik }
48762224350SCasper H.S. Dik 
48862224350SCasper H.S. Dik /*ARGSUSED*/
48962224350SCasper H.S. Dik static void
pkg_door_srv(void * cookie,char * argp,size_t asz,door_desc_t * dp,uint_t ndesc)49062224350SCasper H.S. Dik pkg_door_srv(void *cookie, char *argp, size_t asz, door_desc_t *dp,
49162224350SCasper H.S. Dik     uint_t ndesc)
49262224350SCasper H.S. Dik {
49362224350SCasper H.S. Dik 	char *p = NULL;
49462224350SCasper H.S. Dik 	pkgcmd_t *pcmd = (pkgcmd_t *)argp;
49562224350SCasper H.S. Dik 	ucred_t *uc = NULL;
49662224350SCasper H.S. Dik 	uid_t caller;
49762224350SCasper H.S. Dik 	pid_t pcaller;
49862224350SCasper H.S. Dik 	door_desc_t ddp;
49962224350SCasper H.S. Dik 	int dnum = 0;
50062224350SCasper H.S. Dik 	int one = 1;
50162224350SCasper H.S. Dik 	int len = -1;
50262224350SCasper H.S. Dik 
50362224350SCasper H.S. Dik 	if (asz < sizeof (pkgcmd_t)) {
50462224350SCasper H.S. Dik 		(void) door_return(NULL, 0, NULL, 0);
50562224350SCasper H.S. Dik 		return;
50662224350SCasper H.S. Dik 	}
50762224350SCasper H.S. Dik 
50862224350SCasper H.S. Dik 	if (door_ucred(&uc) != 0) {
50962224350SCasper H.S. Dik 		(void) door_return(NULL, 0, NULL, 0);
51062224350SCasper H.S. Dik 		return;
51162224350SCasper H.S. Dik 	}
51262224350SCasper H.S. Dik 
51362224350SCasper H.S. Dik 	caller = ucred_geteuid(uc);
51462224350SCasper H.S. Dik 	pcaller = ucred_getpid(uc);
51562224350SCasper H.S. Dik 	ucred_free(uc);
51662224350SCasper H.S. Dik 
51762224350SCasper H.S. Dik 	if (caller != myuid) {
51862224350SCasper H.S. Dik 		(void) door_return(NULL, 0, NULL, 0);
51962224350SCasper H.S. Dik 		return;
52062224350SCasper H.S. Dik 	}
52162224350SCasper H.S. Dik 
52262224350SCasper H.S. Dik 	(void) mutex_lock(&mtx);
52362224350SCasper H.S. Dik 	ncalls++;
52462224350SCasper H.S. Dik 
52562224350SCasper H.S. Dik 	if (pcaller != client_pid && pcaller != -1 &&
52662224350SCasper H.S. Dik 	    (client_pid == 1 || kill(client_pid, 0) != 0)) {
52762224350SCasper H.S. Dik 		client_pid = pcaller;
52862224350SCasper H.S. Dik 	}
52962224350SCasper H.S. Dik 
53062224350SCasper H.S. Dik 	if (PKG_WRITE_COMMAND(pcmd->cmd))
53162224350SCasper H.S. Dik 		while (write_locked > 0)
53262224350SCasper H.S. Dik 			(void) cond_wait(&cv, &mtx);
53362224350SCasper H.S. Dik 
53462224350SCasper H.S. Dik 	switch (pcmd->cmd) {
53562224350SCasper H.S. Dik 	case PKG_FINDFILE:
53662224350SCasper H.S. Dik 		p = file_find((pkgfilter_t *)argp, &len);
53762224350SCasper H.S. Dik 		break;
53862224350SCasper H.S. Dik 	case PKG_DUMP:
53962224350SCasper H.S. Dik 		if (read_only)
54062224350SCasper H.S. Dik 			goto err;
54162224350SCasper H.S. Dik 		if (logcount > 0)
54262224350SCasper H.S. Dik 			pkgdump();
54362224350SCasper H.S. Dik 		break;
54462224350SCasper H.S. Dik 	case PKG_EXIT:
54562224350SCasper H.S. Dik 		if (logcount > 0)
54662224350SCasper H.S. Dik 			pkgdump();
54762224350SCasper H.S. Dik 		exit(0);
54862224350SCasper H.S. Dik 		/*NOTREACHED*/
54962224350SCasper H.S. Dik 	case PKG_PKGSYNC:
55062224350SCasper H.S. Dik 		if (read_only || logflush() != 0)
55162224350SCasper H.S. Dik 			goto err;
55262224350SCasper H.S. Dik 		break;
55362224350SCasper H.S. Dik 	case PKG_FILTER:
55462224350SCasper H.S. Dik 		if (pkgfilter((pkgfilter_t *)argp, &ddp) == 0)
55562224350SCasper H.S. Dik 			dnum = 1;
55662224350SCasper H.S. Dik 		break;
55762224350SCasper H.S. Dik 	case PKG_ADDLINES:
55862224350SCasper H.S. Dik 		if (read_only)
55962224350SCasper H.S. Dik 			goto err;
56062224350SCasper H.S. Dik 		changes++;
56162224350SCasper H.S. Dik 
56262224350SCasper H.S. Dik 		if (pkgaddlines((pkgfilter_t *)argp) != 0)
56362224350SCasper H.S. Dik 			goto err;
56462224350SCasper H.S. Dik 		/* If we've updated the database, tell the dump thread */
56562224350SCasper H.S. Dik 		lastchange = gethrtime();
56662224350SCasper H.S. Dik 		(void) cond_broadcast(&cv);
56762224350SCasper H.S. Dik 		break;
56862224350SCasper H.S. Dik 	case PKG_NOP:
56962224350SCasper H.S. Dik 		/* Do nothing but register the current client's pid. */
57062224350SCasper H.S. Dik 		break;
57162224350SCasper H.S. Dik 	default:
57262224350SCasper H.S. Dik 		goto err;
57362224350SCasper H.S. Dik 	}
57462224350SCasper H.S. Dik 
57562224350SCasper H.S. Dik 	lastcall = gethrtime();
57662224350SCasper H.S. Dik 	(void) mutex_unlock(&mtx);
57762224350SCasper H.S. Dik 	(void) door_return(p, len != -1 ? len : p == NULL ? 0 : strlen(p) + 1,
57862224350SCasper H.S. Dik 	    dnum == 0 ? NULL : &ddp, dnum);
57962224350SCasper H.S. Dik 	return;
58062224350SCasper H.S. Dik 
58162224350SCasper H.S. Dik err:
58262224350SCasper H.S. Dik 	(void) mutex_unlock(&mtx);
583*c57b7750SToomas Soome 	(void) door_return((void *)&one, 4, NULL, 0);
58462224350SCasper H.S. Dik }
58562224350SCasper H.S. Dik 
58662224350SCasper H.S. Dik /*
58762224350SCasper H.S. Dik  * This function returns the length of the string including exactly
58862224350SCasper H.S. Dik  * nf fields.
58962224350SCasper H.S. Dik  */
59062224350SCasper H.S. Dik static ptrdiff_t
fieldoff(char * info,int nf)59162224350SCasper H.S. Dik fieldoff(char *info, int nf)
59262224350SCasper H.S. Dik {
59362224350SCasper H.S. Dik 	char *q = info;
59462224350SCasper H.S. Dik 
59562224350SCasper H.S. Dik 	while (nf > 0) {
59662224350SCasper H.S. Dik 		if (IS_ST0[(unsigned char)*q++]) {
59762224350SCasper H.S. Dik 			if (q[-1] == 0)
59862224350SCasper H.S. Dik 				break;
59962224350SCasper H.S. Dik 			nf--;
60062224350SCasper H.S. Dik 		}
60162224350SCasper H.S. Dik 	}
60262224350SCasper H.S. Dik 	return (q - info - 1);
60362224350SCasper H.S. Dik }
60462224350SCasper H.S. Dik 
60562224350SCasper H.S. Dik /*
60662224350SCasper H.S. Dik  * The buf points into list of \n delimited lines.  We copy it,
60762224350SCasper H.S. Dik  * removing the newline and adding a \0.
60862224350SCasper H.S. Dik  */
60962224350SCasper H.S. Dik static char *
mystrcpy(char * buf,int len)61062224350SCasper H.S. Dik mystrcpy(char *buf, int len)
61162224350SCasper H.S. Dik {
61262224350SCasper H.S. Dik 	char *res = umem_alloc(len, UMEM_NOFAIL);
61362224350SCasper H.S. Dik 
61462224350SCasper H.S. Dik 	(void) memcpy(res, buf, len - 1);
61562224350SCasper H.S. Dik 	res[len - 1] = '\0';
61662224350SCasper H.S. Dik 	return (res);
61762224350SCasper H.S. Dik }
61862224350SCasper H.S. Dik 
61962224350SCasper H.S. Dik /*
62062224350SCasper H.S. Dik  * Entry: a single line without the NEWLINE
62162224350SCasper H.S. Dik  * Return: the package entry with the path determined.
62262224350SCasper H.S. Dik  */
62362224350SCasper H.S. Dik static pkgentry_t *
parse_line(char * buf,int blen,boolean_t full)62462224350SCasper H.S. Dik parse_line(char *buf, int blen, boolean_t full)
62562224350SCasper H.S. Dik {
62662224350SCasper H.S. Dik 	char *t;
62762224350SCasper H.S. Dik 	pkgentry_t *p;
62862224350SCasper H.S. Dik 	int nfields;
62962224350SCasper H.S. Dik 
63062224350SCasper H.S. Dik 	p = umem_cache_alloc(ecache, UMEM_NOFAIL);
63162224350SCasper H.S. Dik 	buf = p->line = mystrcpy(buf, blen + 1);
63262224350SCasper H.S. Dik 	p->len = blen + 1;
63362224350SCasper H.S. Dik 
63462224350SCasper H.S. Dik 	t = buf;
63562224350SCasper H.S. Dik 
63662224350SCasper H.S. Dik 	while (!IS_ST0Q[(unsigned char)*t++])
63762224350SCasper H.S. Dik 		;
63862224350SCasper H.S. Dik 
63962224350SCasper H.S. Dik 	p->pathlen = t - buf - 1;
64062224350SCasper H.S. Dik 	if (p->pathlen == 0 || p->pathlen >= PATH_MAX) {
64162224350SCasper H.S. Dik 		progerr("bad entry read in contents file");
64262224350SCasper H.S. Dik 		logerr("pathname: Unknown");
64362224350SCasper H.S. Dik 		logerr("problem: unable to read pathname field");
64462224350SCasper H.S. Dik 		if (one_shot)
64562224350SCasper H.S. Dik 			exit(2);
64662224350SCasper H.S. Dik 	}
64762224350SCasper H.S. Dik 	if (t[-1] == '=')
64862224350SCasper H.S. Dik 		while (!IS_ST0[(unsigned char)*t++])
64962224350SCasper H.S. Dik 			;
65062224350SCasper H.S. Dik 
65162224350SCasper H.S. Dik 	/* Partial as found in the "-" entries for log */
65262224350SCasper H.S. Dik 	if (t[-1] == '\0') {
65362224350SCasper H.S. Dik 		if (full)
65462224350SCasper H.S. Dik 			goto badline;
65562224350SCasper H.S. Dik 
65662224350SCasper H.S. Dik 		p->pkgoff = -1;
65762224350SCasper H.S. Dik 		return (p);
65862224350SCasper H.S. Dik 	}
65962224350SCasper H.S. Dik 
66062224350SCasper H.S. Dik 	switch (*t) {
66162224350SCasper H.S. Dik 	case '?':
66262224350SCasper H.S. Dik 		nfields = 0;
66362224350SCasper H.S. Dik 		break;
66462224350SCasper H.S. Dik 	case 's':
66562224350SCasper H.S. Dik 	case 'l':
66662224350SCasper H.S. Dik 		/* Fields: class */
66762224350SCasper H.S. Dik 		nfields = 1;
66862224350SCasper H.S. Dik 		break;
66962224350SCasper H.S. Dik 	case 'p':
67062224350SCasper H.S. Dik 	case 'x':
67162224350SCasper H.S. Dik 	case 'd':
67262224350SCasper H.S. Dik 		/* class mode owner group */
67362224350SCasper H.S. Dik 		nfields = 4;
67462224350SCasper H.S. Dik 		break;
67562224350SCasper H.S. Dik 	case 'f':
67662224350SCasper H.S. Dik 	case 'e':
67762224350SCasper H.S. Dik 	case 'v':
67862224350SCasper H.S. Dik 		/* class mode owner group size csum time */
67962224350SCasper H.S. Dik 		nfields = 7;
68062224350SCasper H.S. Dik 		break;
68162224350SCasper H.S. Dik 	case 'c':
68262224350SCasper H.S. Dik 	case 'b':
68362224350SCasper H.S. Dik 		/* class major minor mode owner group */
68462224350SCasper H.S. Dik 		nfields = 6;
68562224350SCasper H.S. Dik 		break;
68662224350SCasper H.S. Dik 	default:
68762224350SCasper H.S. Dik 		progerr("bad entry read in contents file");
68862224350SCasper H.S. Dik 		logerr("pathname: %.*s", p->pathlen, p->line);
68962224350SCasper H.S. Dik 		logerr("problem: unknown ftype");
69062224350SCasper H.S. Dik 		freeentry(p);
69162224350SCasper H.S. Dik 		if (one_shot)
69262224350SCasper H.S. Dik 			exit(2);
69362224350SCasper H.S. Dik 		return (NULL);
69462224350SCasper H.S. Dik 	}
69562224350SCasper H.S. Dik 
69662224350SCasper H.S. Dik 	p->pkgoff = t + fieldoff(t, nfields + 1) - buf;
69762224350SCasper H.S. Dik 
69862224350SCasper H.S. Dik 	if (p->line[p->pkgoff] != '\0' || p->pkgoff == p->len - 1)
69962224350SCasper H.S. Dik 		return (p);
70062224350SCasper H.S. Dik 
70162224350SCasper H.S. Dik badline:
70262224350SCasper H.S. Dik 	progerr(gettext("bad entry read in contents file"));
70362224350SCasper H.S. Dik 	logerr(gettext("pathname: Unknown"));
70462224350SCasper H.S. Dik 	logerr(gettext("problem: unknown ftype"));
70562224350SCasper H.S. Dik 	freeentry(p);
70662224350SCasper H.S. Dik 	if (one_shot)
70762224350SCasper H.S. Dik 		exit(2);
70862224350SCasper H.S. Dik 	return (NULL);
70962224350SCasper H.S. Dik }
71062224350SCasper H.S. Dik 
71162224350SCasper H.S. Dik static void
handle_comments(char * buf,int len)71262224350SCasper H.S. Dik handle_comments(char *buf, int len)
71362224350SCasper H.S. Dik {
71462224350SCasper H.S. Dik 	if (cind >= 2)
71562224350SCasper H.S. Dik 		return;
71662224350SCasper H.S. Dik 
71762224350SCasper H.S. Dik 	if (buf[0] != '#')
71862224350SCasper H.S. Dik 		return;
71962224350SCasper H.S. Dik 
72062224350SCasper H.S. Dik 	if (ccmnt[cind] != NULL)
72162224350SCasper H.S. Dik 		umem_free(ccmnt[cind], strlen(ccmnt[cind]) + 1);
72262224350SCasper H.S. Dik 	ccmnt[cind] = mystrcpy(buf, len);
72362224350SCasper H.S. Dik 	cind++;
72462224350SCasper H.S. Dik }
72562224350SCasper H.S. Dik 
72662224350SCasper H.S. Dik static void
parse_contents(void)72762224350SCasper H.S. Dik parse_contents(void)
72862224350SCasper H.S. Dik {
72962224350SCasper H.S. Dik 	int cnt;
73062224350SCasper H.S. Dik 	pkgentry_t *ent, *e2;
73162224350SCasper H.S. Dik 	avl_index_t where;
73262224350SCasper H.S. Dik 	int num = 0;
73362224350SCasper H.S. Dik 	struct stat stb;
73462224350SCasper H.S. Dik 	ptrdiff_t off;
73562224350SCasper H.S. Dik 	char *p, *q, *map;
73662224350SCasper H.S. Dik 	pkgentry_t *lastentry = NULL;
73762224350SCasper H.S. Dik 	int d;
73862224350SCasper H.S. Dik 	int cntserrs = 0;
73962224350SCasper H.S. Dik 
74062224350SCasper H.S. Dik 	cnt = open(CONTENTS, O_RDONLY);
74162224350SCasper H.S. Dik 
74262224350SCasper H.S. Dik 	cind = 0;
74362224350SCasper H.S. Dik 
74462224350SCasper H.S. Dik 	if (cnt == -1) {
74562224350SCasper H.S. Dik 		if (errno == ENOENT)
74662224350SCasper H.S. Dik 			return;
74762224350SCasper H.S. Dik 		exit(99);
74862224350SCasper H.S. Dik 	}
74962224350SCasper H.S. Dik 
75062224350SCasper H.S. Dik 	if (fstat(cnt, &stb) != 0) {
75162224350SCasper H.S. Dik 		(void) close(cnt);
75262224350SCasper H.S. Dik 		exit(99);
75362224350SCasper H.S. Dik 	}
75462224350SCasper H.S. Dik 	if (stb.st_size == 0) {
75562224350SCasper H.S. Dik 		(void) close(cnt);
75662224350SCasper H.S. Dik 		return;
75762224350SCasper H.S. Dik 	}
75862224350SCasper H.S. Dik 
75962224350SCasper H.S. Dik 	map = mmap(0, stb.st_size, PROT_READ, MAP_PRIVATE, cnt, 0);
76062224350SCasper H.S. Dik 	(void) close(cnt);
76162224350SCasper H.S. Dik 	if (map == (char *)-1)
76262224350SCasper H.S. Dik 		return;
76362224350SCasper H.S. Dik 
76462224350SCasper H.S. Dik 	(void) madvise(map, stb.st_size, MADV_WILLNEED);
76562224350SCasper H.S. Dik 
76662224350SCasper H.S. Dik 	for (off = 0; off < stb.st_size; off += q - p) {
76762224350SCasper H.S. Dik 		p = map + off;
76862224350SCasper H.S. Dik 		q = memchr(p, '\n', stb.st_size - off);
76962224350SCasper H.S. Dik 		if (q == NULL)
77062224350SCasper H.S. Dik 			break;
77162224350SCasper H.S. Dik 
77262224350SCasper H.S. Dik 		q++;
77362224350SCasper H.S. Dik 		num++;
77462224350SCasper H.S. Dik 		if (p[0] == '#' || p[0] == '\n') {
77562224350SCasper H.S. Dik 			handle_comments(p, q - p);
77662224350SCasper H.S. Dik 			continue;
77762224350SCasper H.S. Dik 		}
77862224350SCasper H.S. Dik 		ent = parse_line(p, q - p - 1, B_TRUE);
77962224350SCasper H.S. Dik 
78062224350SCasper H.S. Dik 		if (ent == NULL) {
78162224350SCasper H.S. Dik 			cntserrs++;
78262224350SCasper H.S. Dik 			continue;
78362224350SCasper H.S. Dik 		}
78462224350SCasper H.S. Dik 
78562224350SCasper H.S. Dik 		/*
78662224350SCasper H.S. Dik 		 * We save time by assuming the database is sorted; by
78762224350SCasper H.S. Dik 		 * using avl_insert_here(), building the tree is nearly free.
78862224350SCasper H.S. Dik 		 * lastentry always contains the last entry in the AVL tree.
78962224350SCasper H.S. Dik 		 */
79062224350SCasper H.S. Dik 		if (lastentry == NULL) {
79162224350SCasper H.S. Dik 			avl_add(list, ent);
79262224350SCasper H.S. Dik 			lastentry = ent;
79362224350SCasper H.S. Dik 		} else if ((d = avlcmp(ent, lastentry)) == 1) {
79462224350SCasper H.S. Dik 			avl_insert_here(list, ent, lastentry, AVL_AFTER);
79562224350SCasper H.S. Dik 			lastentry = ent;
79662224350SCasper H.S. Dik 		} else if (d == 0 ||
79762224350SCasper H.S. Dik 		    (e2 = avl_find(list, ent, &where)) != NULL) {
79862224350SCasper H.S. Dik 			/*
79962224350SCasper H.S. Dik 			 * This can only happen if the contents file is bad;
80062224350SCasper H.S. Dik 			 * this can, e.g., happen with the old SQL contents DB,
80162224350SCasper H.S. Dik 			 * it didn't sort properly.  Assume the first one
80262224350SCasper H.S. Dik 			 * is the correct one, but who knows?
80362224350SCasper H.S. Dik 			 */
80462224350SCasper H.S. Dik 			if (d == 0)
80562224350SCasper H.S. Dik 				e2 = lastentry;
80662224350SCasper H.S. Dik 			if (strcmp(ent->line, e2->line) != 0) {
80762224350SCasper H.S. Dik 				progerr(gettext("two entries for %.*s"),
80862224350SCasper H.S. Dik 				    ent->pathlen, ent->line);
80962224350SCasper H.S. Dik 				cntserrs++;
81062224350SCasper H.S. Dik 			}
81162224350SCasper H.S. Dik 			freeentry(ent);
81262224350SCasper H.S. Dik 		} else {
81362224350SCasper H.S. Dik 			/* Out of order: not an error for us, really. */
81462224350SCasper H.S. Dik 			progerr(gettext("bad read of contents file"));
81562224350SCasper H.S. Dik 			logerr(gettext("pathname: Unknown"));
81662224350SCasper H.S. Dik 			logerr(gettext(
81762224350SCasper H.S. Dik 			    "problem: unable to read pathname field"));
81862224350SCasper H.S. Dik 			if (one_shot)
81962224350SCasper H.S. Dik 				exit(2);
82062224350SCasper H.S. Dik 			avl_insert(list, ent, where);
82162224350SCasper H.S. Dik 		}
82262224350SCasper H.S. Dik 	}
82362224350SCasper H.S. Dik 
82462224350SCasper H.S. Dik 	cind = 0;
82562224350SCasper H.S. Dik 
82662224350SCasper H.S. Dik 	(void) munmap(map, stb.st_size);
82762224350SCasper H.S. Dik 
82862224350SCasper H.S. Dik 	/* By default, we ignore bad lines, keep them in a copy. */
82962224350SCasper H.S. Dik 	if (cntserrs > 0 && stb.st_nlink == 1) {
83062224350SCasper H.S. Dik 		char bcf[sizeof (BADCONTENTS)];
83162224350SCasper H.S. Dik 
83262224350SCasper H.S. Dik 		(void) strcpy(bcf, BADCONTENTS);
83362224350SCasper H.S. Dik 		if (mktemp(bcf) != NULL) {
83462224350SCasper H.S. Dik 			(void) link(CONTENTS, bcf);
83562224350SCasper H.S. Dik 			syslog(LOG_WARNING, "A bad contents file was saved: %s",
83662224350SCasper H.S. Dik 			    bcf);
83762224350SCasper H.S. Dik 		}
83862224350SCasper H.S. Dik 	}
83962224350SCasper H.S. Dik }
84062224350SCasper H.S. Dik 
84162224350SCasper H.S. Dik static int
parse_log(void)84262224350SCasper H.S. Dik parse_log(void)
84362224350SCasper H.S. Dik {
84462224350SCasper H.S. Dik 	pkgentry_t *ent, *look;
84562224350SCasper H.S. Dik 	avl_index_t where;
84662224350SCasper H.S. Dik 	int num = 0;
84762224350SCasper H.S. Dik 	int logfd;
84862224350SCasper H.S. Dik 	struct stat stb;
84962224350SCasper H.S. Dik 	int mlen = strlen(marker);
85062224350SCasper H.S. Dik 	off_t realend;
85162224350SCasper H.S. Dik 	ptrdiff_t off;
85262224350SCasper H.S. Dik 	char *p, *q, *map;
85362224350SCasper H.S. Dik 
85462224350SCasper H.S. Dik 	logfd = open(PKGLOG, O_RDONLY);
85562224350SCasper H.S. Dik 
85662224350SCasper H.S. Dik 	if (logfd < 0) {
85762224350SCasper H.S. Dik 		if (errno == ENOENT)
85862224350SCasper H.S. Dik 			return (0);
85962224350SCasper H.S. Dik 		progerr(gettext("cannot read "PKGLOG": %s"), strerror(errno));
86062224350SCasper H.S. Dik 		exit(2);
86162224350SCasper H.S. Dik 	}
86262224350SCasper H.S. Dik 
86362224350SCasper H.S. Dik 	if (fstat(logfd, &stb) != 0) {
86462224350SCasper H.S. Dik 		progerr(gettext("cannot stat "PKGLOG": %s"), strerror(errno));
86562224350SCasper H.S. Dik 		exit(2);
86662224350SCasper H.S. Dik 	}
86762224350SCasper H.S. Dik 
86862224350SCasper H.S. Dik 	if (stb.st_size == 0) {
86962224350SCasper H.S. Dik 		(void) close(logfd);
87062224350SCasper H.S. Dik 		/* Force pkgdump && remove of the logfile. */
87162224350SCasper H.S. Dik 		return (1);
87262224350SCasper H.S. Dik 	}
87362224350SCasper H.S. Dik 
8747706a9bfSCasper H.S. Dik 	map = mmap(0, stb.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE,
87562224350SCasper H.S. Dik 	    logfd, 0);
87662224350SCasper H.S. Dik 	(void) close(logfd);
87762224350SCasper H.S. Dik 	if (map == (char *)-1) {
87862224350SCasper H.S. Dik 		progerr(gettext("Cannot mmap the "PKGLOG": %s"),
87962224350SCasper H.S. Dik 		    strerror(errno));
88062224350SCasper H.S. Dik 		exit(2);
88162224350SCasper H.S. Dik 	}
88262224350SCasper H.S. Dik 
88362224350SCasper H.S. Dik 	cind = 0;
88462224350SCasper H.S. Dik 
88562224350SCasper H.S. Dik 	realend = stb.st_size;
88662224350SCasper H.S. Dik 
88762224350SCasper H.S. Dik 	if (memcmp(map + realend - mlen, marker, mlen) != 0) {
88862224350SCasper H.S. Dik 		progerr(gettext(PKGLOG" is not complete"));
88962224350SCasper H.S. Dik 
8907706a9bfSCasper H.S. Dik 		map[stb.st_size - 1] = '\0'; /* for strstr() */
89162224350SCasper H.S. Dik 		realend = 0;
89262224350SCasper H.S. Dik 		for (p = map; q = strstr(p, marker); ) {
89362224350SCasper H.S. Dik 			if (q == map || q[-1] == '\n')
89462224350SCasper H.S. Dik 				realend = q - map + mlen;
89562224350SCasper H.S. Dik 			p = q + mlen;
89662224350SCasper H.S. Dik 		}
89762224350SCasper H.S. Dik 		progerr(gettext("Ignoring %ld bytes from log"),
89862224350SCasper H.S. Dik 		    (long)(stb.st_size - realend));
89962224350SCasper H.S. Dik 	}
90062224350SCasper H.S. Dik 
90162224350SCasper H.S. Dik 	for (off = 0; off < realend; off += q - p) {
90262224350SCasper H.S. Dik 		p = map + off;
90362224350SCasper H.S. Dik 		q = memchr(p, '\n', realend - off);
90462224350SCasper H.S. Dik 		if (q == NULL)
90562224350SCasper H.S. Dik 			break;
90662224350SCasper H.S. Dik 
90762224350SCasper H.S. Dik 		q++;
90862224350SCasper H.S. Dik 		num++;
90962224350SCasper H.S. Dik 		if (p[0] == '#' || p[0] == '\n') {
91062224350SCasper H.S. Dik 			if (memcmp(marker, p, mlen) == 0)
91162224350SCasper H.S. Dik 				cind = 0;
91262224350SCasper H.S. Dik 			else
91362224350SCasper H.S. Dik 				handle_comments(p, q - p);
91462224350SCasper H.S. Dik 			continue;
91562224350SCasper H.S. Dik 		}
91662224350SCasper H.S. Dik 
91762224350SCasper H.S. Dik 		ent = parse_line(p + 1, q - (p + 1) - 1, p[0] != '-');
91862224350SCasper H.S. Dik 		if (ent == NULL)
91962224350SCasper H.S. Dik 			continue;
92062224350SCasper H.S. Dik 		look = avl_find(list, ent, &where);
92162224350SCasper H.S. Dik 		/*
92262224350SCasper H.S. Dik 		 * The log can be replayed; so any value of "look" is
92362224350SCasper H.S. Dik 		 * not unexpected.
92462224350SCasper H.S. Dik 		 */
92562224350SCasper H.S. Dik 		switch (p[0]) {
92662224350SCasper H.S. Dik 		case '+':
92762224350SCasper H.S. Dik 		case '=':
92862224350SCasper H.S. Dik 			if (look != NULL)
92962224350SCasper H.S. Dik 				swapentry(look, ent);
93062224350SCasper H.S. Dik 			else
93162224350SCasper H.S. Dik 				avl_insert(list, ent, where);
93262224350SCasper H.S. Dik 			break;
93362224350SCasper H.S. Dik 		case '-':
93462224350SCasper H.S. Dik 			if (look != NULL) {
93562224350SCasper H.S. Dik 				avl_remove(list, look);
93662224350SCasper H.S. Dik 				freeentry(look);
93762224350SCasper H.S. Dik 			}
93862224350SCasper H.S. Dik 			freeentry(ent);
93962224350SCasper H.S. Dik 			break;
94062224350SCasper H.S. Dik 		default:
94162224350SCasper H.S. Dik 			freeentry(ent);
94262224350SCasper H.S. Dik 			progerr(gettext("log %d: bad line"), num);
94362224350SCasper H.S. Dik 			break;
94462224350SCasper H.S. Dik 		}
94562224350SCasper H.S. Dik 	}
94662224350SCasper H.S. Dik 	(void) munmap(map, stb.st_size);
94762224350SCasper H.S. Dik 
94862224350SCasper H.S. Dik 	/* Force pkgdump && remove of the logfile if there are no valid mods. */
94962224350SCasper H.S. Dik 	return (num == 0 ? 1 : num);
95062224350SCasper H.S. Dik }
95162224350SCasper H.S. Dik 
95262224350SCasper H.S. Dik static char *
file_find(pkgfilter_t * cmd,int * len)95362224350SCasper H.S. Dik file_find(pkgfilter_t *cmd, int *len)
95462224350SCasper H.S. Dik {
95562224350SCasper H.S. Dik 	pkgentry_t p;
95662224350SCasper H.S. Dik 	pkgentry_t *look;
95762224350SCasper H.S. Dik 
95862224350SCasper H.S. Dik 	p.line = cmd->buf;
95962224350SCasper H.S. Dik 	p.pathlen = cmd->len;
96062224350SCasper H.S. Dik 
96162224350SCasper H.S. Dik 	look = avl_find(list, &p, NULL);
96262224350SCasper H.S. Dik 
96362224350SCasper H.S. Dik 	if (look == NULL)
96462224350SCasper H.S. Dik 		return (NULL);
96562224350SCasper H.S. Dik 
96662224350SCasper H.S. Dik 	*len = look->len;
96762224350SCasper H.S. Dik 	return (look->line);
96862224350SCasper H.S. Dik }
96962224350SCasper H.S. Dik 
97062224350SCasper H.S. Dik static void
pkgdump(void)97162224350SCasper H.S. Dik pkgdump(void)
97262224350SCasper H.S. Dik {
97362224350SCasper H.S. Dik 	FILE *cnts;
97462224350SCasper H.S. Dik 	int err = 0;
97562224350SCasper H.S. Dik 	pkgentry_t *p;
97662224350SCasper H.S. Dik 
97762224350SCasper H.S. Dik 	if (read_only)
97862224350SCasper H.S. Dik 		return;
97962224350SCasper H.S. Dik 
98062224350SCasper H.S. Dik 	/* We cannot dump when the current transaction is not complete. */
98162224350SCasper H.S. Dik 	if (sync_needed)
98262224350SCasper H.S. Dik 		return;
98362224350SCasper H.S. Dik 
98462224350SCasper H.S. Dik 	cnts = fopen(TCONTENTS, "w");
98562224350SCasper H.S. Dik 
98662224350SCasper H.S. Dik 	if (cnts == NULL)
98762224350SCasper H.S. Dik 		exit(99);
98862224350SCasper H.S. Dik 
98962224350SCasper H.S. Dik 	for (p = avl_first(list); p != NULL; p = AVL_NEXT(list, p)) {
99062224350SCasper H.S. Dik 		if (fprintf(cnts, "%s\n", p->line) < 0)
99162224350SCasper H.S. Dik 			err++;
99262224350SCasper H.S. Dik 	}
99362224350SCasper H.S. Dik 
99462224350SCasper H.S. Dik 	if (ccmnt[0] != NULL)
99562224350SCasper H.S. Dik 		(void) fprintf(cnts, "%s\n", ccmnt[0]);
99662224350SCasper H.S. Dik 	if (ccmnt[1] != NULL)
99762224350SCasper H.S. Dik 		(void) fprintf(cnts, "%s\n", ccmnt[1]);
99862224350SCasper H.S. Dik 
99962224350SCasper H.S. Dik 	if (err != 0 || fflush(cnts) == EOF || fsync(fileno(cnts)) != 0 ||
100062224350SCasper H.S. Dik 	    fclose(cnts) == EOF || rename(TCONTENTS, CONTENTS) != 0) {
100162224350SCasper H.S. Dik 		err++;
100262224350SCasper H.S. Dik 	}
100362224350SCasper H.S. Dik 
100462224350SCasper H.S. Dik 	if (err != 0) {
100562224350SCasper H.S. Dik 		progerr("cannot rewrite the contents file");
100662224350SCasper H.S. Dik 		exit(2);
100762224350SCasper H.S. Dik 	}
100862224350SCasper H.S. Dik 
100962224350SCasper H.S. Dik 	(void) fclose(log);
101062224350SCasper H.S. Dik 	(void) unlink(PKGLOG);
101162224350SCasper H.S. Dik 	log = NULL;
101262224350SCasper H.S. Dik 	ndumps++;
101362224350SCasper H.S. Dik 	logcount = 0;
101462224350SCasper H.S. Dik }
101562224350SCasper H.S. Dik 
101662224350SCasper H.S. Dik static void
freeentry(pkgentry_t * p)101762224350SCasper H.S. Dik freeentry(pkgentry_t *p)
101862224350SCasper H.S. Dik {
101962224350SCasper H.S. Dik 	umem_free(p->line, p->len);
102062224350SCasper H.S. Dik 	umem_cache_free(ecache, p);
102162224350SCasper H.S. Dik }
102262224350SCasper H.S. Dik 
102362224350SCasper H.S. Dik static void
swapentry(pkgentry_t * cur,pkgentry_t * new)102462224350SCasper H.S. Dik swapentry(pkgentry_t *cur, pkgentry_t *new)
102562224350SCasper H.S. Dik {
102662224350SCasper H.S. Dik 	if (cur->len == new->len &&
102762224350SCasper H.S. Dik 	    strcmp(cur->line + cur->pathlen,
102862224350SCasper H.S. Dik 	    new->line + new->pathlen) == 0) {
102962224350SCasper H.S. Dik 		suppressed++;
103062224350SCasper H.S. Dik 		freeentry(new);
103162224350SCasper H.S. Dik 		return;
103262224350SCasper H.S. Dik 	}
103362224350SCasper H.S. Dik 
103462224350SCasper H.S. Dik 	/* Free old line */
103562224350SCasper H.S. Dik 	umem_free(cur->line, cur->len);
103662224350SCasper H.S. Dik 
103762224350SCasper H.S. Dik 	/* Copy new value: pathlen is the same and avl is kept */
103862224350SCasper H.S. Dik 	cur->line = new->line;
103962224350SCasper H.S. Dik 	cur->len = new->len;
104062224350SCasper H.S. Dik 	cur->pkgoff = new->pkgoff;
104162224350SCasper H.S. Dik 
104262224350SCasper H.S. Dik 	umem_cache_free(ecache, new);
104362224350SCasper H.S. Dik }
104462224350SCasper H.S. Dik 
104562224350SCasper H.S. Dik static int
logentry(char type,pkgentry_t * p)104662224350SCasper H.S. Dik logentry(char type, pkgentry_t *p)
104762224350SCasper H.S. Dik {
104862224350SCasper H.S. Dik 	int len;
104962224350SCasper H.S. Dik 
105062224350SCasper H.S. Dik 	if (type == '-')
105162224350SCasper H.S. Dik 		len = fprintf(log, "-%.*s\n", p->pathlen, p->line);
105262224350SCasper H.S. Dik 	else
105362224350SCasper H.S. Dik 		len = fprintf(log, "%c%s\n", type, p->line);
105462224350SCasper H.S. Dik 
105562224350SCasper H.S. Dik 	loglines++;
105662224350SCasper H.S. Dik 	if (len < 0) {
105762224350SCasper H.S. Dik 		logerrcnt++;
105862224350SCasper H.S. Dik 		return (-1);
105962224350SCasper H.S. Dik 	}
106062224350SCasper H.S. Dik 	logcount += len;
106162224350SCasper H.S. Dik 	return (0);
106262224350SCasper H.S. Dik }
106362224350SCasper H.S. Dik 
106462224350SCasper H.S. Dik static int
logflush(void)106562224350SCasper H.S. Dik logflush(void)
106662224350SCasper H.S. Dik {
106762224350SCasper H.S. Dik 	int len;
106862224350SCasper H.S. Dik 	static int lastflush;
106962224350SCasper H.S. Dik 
107062224350SCasper H.S. Dik 	if (log == NULL)
107162224350SCasper H.S. Dik 		return (0);
107262224350SCasper H.S. Dik 
107362224350SCasper H.S. Dik 	if (lastflush == logcount)
107462224350SCasper H.S. Dik 		return (0);
107562224350SCasper H.S. Dik 
107662224350SCasper H.S. Dik 	if (cind == 2) {
107762224350SCasper H.S. Dik 		(void) fprintf(log, "%s\n", ccmnt[0]);
107862224350SCasper H.S. Dik 		(void) fprintf(log, "%s\n", ccmnt[1]);
107962224350SCasper H.S. Dik 		cind = 0;
108062224350SCasper H.S. Dik 	}
108162224350SCasper H.S. Dik 
108262224350SCasper H.S. Dik 	/*
108362224350SCasper H.S. Dik 	 * When using zfs, if the mark is there, then so is the rest before
108462224350SCasper H.S. Dik 	 * it.  But with ufs, we need to flush twice.
108562224350SCasper H.S. Dik 	 */
108662224350SCasper H.S. Dik 	if (flushbeforemark) {
108762224350SCasper H.S. Dik 		if (fflush(log) == EOF)
108862224350SCasper H.S. Dik 			logerrcnt++;
108962224350SCasper H.S. Dik 	}
109062224350SCasper H.S. Dik 	/* Anything before the last marker found in the log will be valid */
109162224350SCasper H.S. Dik 	len = fprintf(log, "%s", marker);
109262224350SCasper H.S. Dik 	if (len < 0)
109362224350SCasper H.S. Dik 		logerrcnt++;
109462224350SCasper H.S. Dik 	else
109562224350SCasper H.S. Dik 		logcount += len;
109662224350SCasper H.S. Dik 
109762224350SCasper H.S. Dik 	if (fflush(log) == EOF)
109862224350SCasper H.S. Dik 		logerrcnt++;
109962224350SCasper H.S. Dik 
110062224350SCasper H.S. Dik 	sync_needed = B_FALSE;
110162224350SCasper H.S. Dik 
110262224350SCasper H.S. Dik 	if (logerrcnt > 0 || logcount > MAXLOGFILESIZE)
110362224350SCasper H.S. Dik 		pkgdump();
110462224350SCasper H.S. Dik 
110562224350SCasper H.S. Dik 	if (logerrcnt > 0)
110662224350SCasper H.S. Dik 		return (-1);
110762224350SCasper H.S. Dik 
110862224350SCasper H.S. Dik 	lastflush = logcount;
110962224350SCasper H.S. Dik 
111062224350SCasper H.S. Dik 	return (0);
111162224350SCasper H.S. Dik }
111262224350SCasper H.S. Dik 
111362224350SCasper H.S. Dik static int
avlcmp(const void * ca,const void * cb)111462224350SCasper H.S. Dik avlcmp(const void *ca, const void *cb)
111562224350SCasper H.S. Dik {
111662224350SCasper H.S. Dik 	const pkgentry_t *a = ca;
111762224350SCasper H.S. Dik 	const pkgentry_t *b = cb;
111862224350SCasper H.S. Dik 	int i = memcmp(a->line, b->line,
111962224350SCasper H.S. Dik 	    a->pathlen > b->pathlen ? b->pathlen : a->pathlen);
112062224350SCasper H.S. Dik 
112162224350SCasper H.S. Dik 	if (i < 0)
112262224350SCasper H.S. Dik 		return (-1);
112362224350SCasper H.S. Dik 	else if (i > 0)
112462224350SCasper H.S. Dik 		return (1);
112562224350SCasper H.S. Dik 	else if (a->pathlen == b->pathlen)
112662224350SCasper H.S. Dik 		return (0);
112762224350SCasper H.S. Dik 	else if (a->pathlen > b->pathlen)
112862224350SCasper H.S. Dik 		return (1);
112962224350SCasper H.S. Dik 	else
113062224350SCasper H.S. Dik 		return (-1);
113162224350SCasper H.S. Dik }
113262224350SCasper H.S. Dik 
113362224350SCasper H.S. Dik /*
113462224350SCasper H.S. Dik  * Returns:
113562224350SCasper H.S. Dik  *	0 - if we can get the lock
113662224350SCasper H.S. Dik  *	-1 - we can't lock
113762224350SCasper H.S. Dik  */
113862224350SCasper H.S. Dik 
113962224350SCasper H.S. Dik static int
establish_lock(char * lock)114062224350SCasper H.S. Dik establish_lock(char *lock)
114162224350SCasper H.S. Dik {
114262224350SCasper H.S. Dik 	int fd = open(lock, O_RDWR|O_CREAT, 0644);
114362224350SCasper H.S. Dik 	int i;
114462224350SCasper H.S. Dik 
114562224350SCasper H.S. Dik 	if (fd < 0)
114662224350SCasper H.S. Dik 		return (-1);
114762224350SCasper H.S. Dik 
114862224350SCasper H.S. Dik 	for (i = 0; i < 5; i++) {
114962224350SCasper H.S. Dik 		if (lockf(fd, F_TLOCK, 0) == 0)
115062224350SCasper H.S. Dik 			return (0);
115162224350SCasper H.S. Dik 		(void) sleep(1);
115262224350SCasper H.S. Dik 	}
115362224350SCasper H.S. Dik 
115462224350SCasper H.S. Dik 	(void) close(fd);
115562224350SCasper H.S. Dik 	return (-1);
115662224350SCasper H.S. Dik }
115762224350SCasper H.S. Dik 
115862224350SCasper H.S. Dik static int
no_memory_abort(void)115962224350SCasper H.S. Dik no_memory_abort(void)
116062224350SCasper H.S. Dik {
116162224350SCasper H.S. Dik 	return (UMEM_CALLBACK_EXIT(99));
116262224350SCasper H.S. Dik }
116362224350SCasper H.S. Dik 
116462224350SCasper H.S. Dik /*
116562224350SCasper H.S. Dik  * Dump a part of the contents file in a pipe; grep for the "filter".
116662224350SCasper H.S. Dik  * It doesn't matter if we return too much.
116762224350SCasper H.S. Dik  */
116862224350SCasper H.S. Dik 
116962224350SCasper H.S. Dik static void *
thr_pkgfilter(void * v)117062224350SCasper H.S. Dik thr_pkgfilter(void *v)
117162224350SCasper H.S. Dik {
117262224350SCasper H.S. Dik 	pkgfilter_t *pf = v;
117362224350SCasper H.S. Dik 	pkgentry_t *p;
117462224350SCasper H.S. Dik 	int nums[2];
117562224350SCasper H.S. Dik 	FILE *cnts;
117662224350SCasper H.S. Dik 
117762224350SCasper H.S. Dik 	cnts = fdopen(pf->cmd, "w");
117862224350SCasper H.S. Dik 	if (cnts == NULL)
117962224350SCasper H.S. Dik 		goto free;
118062224350SCasper H.S. Dik 
11817706a9bfSCasper H.S. Dik 	/*
11827706a9bfSCasper H.S. Dik 	 * Remove wild card: don't care about extra matches; make sure
11837706a9bfSCasper H.S. Dik 	 * we remove both the "*" and the "." in front of it.
11847706a9bfSCasper H.S. Dik 	 */
118562224350SCasper H.S. Dik 	if (pf->len > 0) {
118662224350SCasper H.S. Dik 		char *p;
118762224350SCasper H.S. Dik 
118862224350SCasper H.S. Dik 		for (p = pf->buf; *p; p++) {
118962224350SCasper H.S. Dik 			if (*p == '*') {
119062224350SCasper H.S. Dik 				*p = 0;
11917706a9bfSCasper H.S. Dik 				if (p > pf->buf && p[-1] == '.')
11927706a9bfSCasper H.S. Dik 					p[-1] = 0;
119362224350SCasper H.S. Dik 				break;
119462224350SCasper H.S. Dik 			}
119562224350SCasper H.S. Dik 		}
119662224350SCasper H.S. Dik 	}
119762224350SCasper H.S. Dik 
119862224350SCasper H.S. Dik 	/* Disable modifications while the filter is running */
119962224350SCasper H.S. Dik 	(void) mutex_lock(&mtx);
120062224350SCasper H.S. Dik 	write_locked++;
120162224350SCasper H.S. Dik 	(void) mutex_unlock(&mtx);
120262224350SCasper H.S. Dik 	/*
120362224350SCasper H.S. Dik 	 * The protocol for the contents file for the clients:
120462224350SCasper H.S. Dik 	 * <int:len><int:pathlen><line + 0>
120562224350SCasper H.S. Dik 	 */
120662224350SCasper H.S. Dik 
120762224350SCasper H.S. Dik 	for (p = avl_first(list); p != NULL; p = AVL_NEXT(list, p)) {
120862224350SCasper H.S. Dik 		if (pf->len > 0 && strstr(p->line, pf->buf) == NULL)
120962224350SCasper H.S. Dik 			continue;
121062224350SCasper H.S. Dik 
121162224350SCasper H.S. Dik 		nums[0] = p->len;
121262224350SCasper H.S. Dik 		nums[1] = p->pathlen;
121362224350SCasper H.S. Dik 		if (fwrite(nums, sizeof (int), 2, cnts) != 2)
121462224350SCasper H.S. Dik 			break;
121562224350SCasper H.S. Dik 		if (fwrite(p->line, 1, p->len, cnts) != p->len)
121662224350SCasper H.S. Dik 			break;
121762224350SCasper H.S. Dik 	}
121862224350SCasper H.S. Dik 
121962224350SCasper H.S. Dik 	(void) mutex_lock(&mtx);
122062224350SCasper H.S. Dik 	lastcall = gethrtime();
122162224350SCasper H.S. Dik 	write_locked--;
122262224350SCasper H.S. Dik 	(void) cond_broadcast(&cv);
122362224350SCasper H.S. Dik 	(void) mutex_unlock(&mtx);
122462224350SCasper H.S. Dik 	(void) fclose(cnts);
122562224350SCasper H.S. Dik 
122662224350SCasper H.S. Dik free:
122762224350SCasper H.S. Dik 	umem_free(pf, sizeof (pkgfilter_t) + pf->len);
122862224350SCasper H.S. Dik 	return (NULL);
122962224350SCasper H.S. Dik }
123062224350SCasper H.S. Dik 
123162224350SCasper H.S. Dik static hrtime_t
time_since_(hrtime_t last)123262224350SCasper H.S. Dik time_since_(hrtime_t last)
123362224350SCasper H.S. Dik {
123462224350SCasper H.S. Dik 	return (gethrtime() - last);
123562224350SCasper H.S. Dik }
123662224350SCasper H.S. Dik 
123762224350SCasper H.S. Dik static void
my_cond_reltimedwait(hrtime_t delta,int sec)123862224350SCasper H.S. Dik my_cond_reltimedwait(hrtime_t delta, int sec)
123962224350SCasper H.S. Dik {
124062224350SCasper H.S. Dik 	hrtime_t wait = sec * LLNANOSEC - delta;
124162224350SCasper H.S. Dik 	timestruc_t waitfor;
124262224350SCasper H.S. Dik 
124362224350SCasper H.S. Dik 	waitfor.tv_nsec = wait % LLNANOSEC;
124462224350SCasper H.S. Dik 	waitfor.tv_sec = wait / LLNANOSEC;
124562224350SCasper H.S. Dik 	(void) cond_reltimedwait(&cv, &mtx, &waitfor);
124662224350SCasper H.S. Dik }
124762224350SCasper H.S. Dik 
124862224350SCasper H.S. Dik static int
pkgfilter(pkgfilter_t * pf,door_desc_t * dp)124962224350SCasper H.S. Dik pkgfilter(pkgfilter_t *pf, door_desc_t *dp)
125062224350SCasper H.S. Dik {
125162224350SCasper H.S. Dik 
125262224350SCasper H.S. Dik 	int p[2];
125362224350SCasper H.S. Dik 	thread_t tid;
125462224350SCasper H.S. Dik 	pkgfilter_t *cpf;
125562224350SCasper H.S. Dik 
125662224350SCasper H.S. Dik 	if (pipe(p) != 0)
125762224350SCasper H.S. Dik 		return (-1);
125862224350SCasper H.S. Dik 
125962224350SCasper H.S. Dik 	cpf = umem_alloc(sizeof (pkgfilter_t) + pf->len, UMEM_NOFAIL);
126062224350SCasper H.S. Dik 
126162224350SCasper H.S. Dik 	(void) memcpy(cpf, pf, sizeof (pkgfilter_t) + pf->len);
126262224350SCasper H.S. Dik 
126362224350SCasper H.S. Dik 	/* Copy the file descriptor in the command field */
126462224350SCasper H.S. Dik 	cpf->cmd = p[1];
126562224350SCasper H.S. Dik 
1266*c57b7750SToomas Soome 	if (thr_create(NULL, 0, thr_pkgfilter, cpf, THR_DETACHED,
126762224350SCasper H.S. Dik 	    &tid) != 0) {
126862224350SCasper H.S. Dik 		(void) close(p[0]);
126962224350SCasper H.S. Dik 		(void) close(p[1]);
127062224350SCasper H.S. Dik 		umem_free(cpf, sizeof (pkgfilter_t) + pf->len);
127162224350SCasper H.S. Dik 		return (-1);
127262224350SCasper H.S. Dik 	}
127362224350SCasper H.S. Dik 	(void) memset(dp, 0, sizeof (*dp));
127462224350SCasper H.S. Dik 	dp->d_attributes = DOOR_DESCRIPTOR | DOOR_RELEASE;
127562224350SCasper H.S. Dik 	dp->d_data.d_desc.d_descriptor = p[0];
127662224350SCasper H.S. Dik 
127762224350SCasper H.S. Dik 	return (0);
127862224350SCasper H.S. Dik }
127962224350SCasper H.S. Dik 
128062224350SCasper H.S. Dik static int
pkgaddlines(pkgfilter_t * pf)128162224350SCasper H.S. Dik pkgaddlines(pkgfilter_t *pf)
128262224350SCasper H.S. Dik {
128362224350SCasper H.S. Dik 	char *map = pf->buf;
128462224350SCasper H.S. Dik 	int len = pf->len;
128562224350SCasper H.S. Dik 	int off;
128662224350SCasper H.S. Dik 	pkgentry_t *ent, *look;
128762224350SCasper H.S. Dik 	avl_index_t where;
128862224350SCasper H.S. Dik 	char *q, *p;
128962224350SCasper H.S. Dik 	char c;
129062224350SCasper H.S. Dik 	int r = 0;
129162224350SCasper H.S. Dik 
129262224350SCasper H.S. Dik 	if (log == NULL) {
129362224350SCasper H.S. Dik 		log = fopen(PKGLOG, "w");
129462224350SCasper H.S. Dik 		if (log == NULL)
129562224350SCasper H.S. Dik 			return (-1);
129662224350SCasper H.S. Dik 	}
129762224350SCasper H.S. Dik 
129862224350SCasper H.S. Dik 	for (off = 0; off < len; off += q - p) {
129962224350SCasper H.S. Dik 		p = map + off;
130062224350SCasper H.S. Dik 		q = memchr(p, '\n', len - off);
130162224350SCasper H.S. Dik 
130262224350SCasper H.S. Dik 		if (q == NULL)
130362224350SCasper H.S. Dik 			break;
130462224350SCasper H.S. Dik 
130562224350SCasper H.S. Dik 		q++;
130662224350SCasper H.S. Dik 
130762224350SCasper H.S. Dik 		if (p[0] == '#' || p[0] == '\n') {
130862224350SCasper H.S. Dik 			handle_comments(p, q - p);
130962224350SCasper H.S. Dik 			continue;
131062224350SCasper H.S. Dik 		}
131162224350SCasper H.S. Dik 
131262224350SCasper H.S. Dik 		if (*p == '-')
131362224350SCasper H.S. Dik 			ent = parse_line(p + 1, q - (p + 1) - 1, B_FALSE);
131462224350SCasper H.S. Dik 		else
131562224350SCasper H.S. Dik 			ent = parse_line(p, q - p - 1, B_TRUE);
131662224350SCasper H.S. Dik 
131762224350SCasper H.S. Dik 		if (ent == NULL) {
131862224350SCasper H.S. Dik 			r++;
131962224350SCasper H.S. Dik 			continue;
132062224350SCasper H.S. Dik 		}
132162224350SCasper H.S. Dik 
132262224350SCasper H.S. Dik 		look = avl_find(list, ent, &where);
132362224350SCasper H.S. Dik 		if (look != NULL) {
132462224350SCasper H.S. Dik 			c = *p == '-' ? '-' : '=';
132562224350SCasper H.S. Dik 			if (c == '=') {
132662224350SCasper H.S. Dik 				swapentry(look, ent);
132762224350SCasper H.S. Dik 				ent = look;
132862224350SCasper H.S. Dik 			} else {
132962224350SCasper H.S. Dik 				avl_remove(list, look);
133062224350SCasper H.S. Dik 				freeentry(look);
133162224350SCasper H.S. Dik 			}
133262224350SCasper H.S. Dik 		} else if (*p == '-') {
133362224350SCasper H.S. Dik 			/* Remove something which isn't there: no-op */
133462224350SCasper H.S. Dik 			freeentry(ent);
133562224350SCasper H.S. Dik 			continue;
133662224350SCasper H.S. Dik 		} else {
133762224350SCasper H.S. Dik 			avl_insert(list, ent, where);
133862224350SCasper H.S. Dik 			c = '+';
133962224350SCasper H.S. Dik 		}
134062224350SCasper H.S. Dik 
134162224350SCasper H.S. Dik 		sync_needed = B_TRUE;
134262224350SCasper H.S. Dik 		r += logentry(c, ent);
134362224350SCasper H.S. Dik 		if (c == '-')
134462224350SCasper H.S. Dik 			freeentry(ent);
134562224350SCasper H.S. Dik 	}
134662224350SCasper H.S. Dik 
134762224350SCasper H.S. Dik 	return (r);
134862224350SCasper H.S. Dik }
134962224350SCasper H.S. Dik 
135062224350SCasper H.S. Dik static void
finish(void)135162224350SCasper H.S. Dik finish(void)
135262224350SCasper H.S. Dik {
135362224350SCasper H.S. Dik 	if (verbose) {
135462224350SCasper H.S. Dik 		syslog(LOG_DEBUG,
135562224350SCasper H.S. Dik 		    "finished: calls %d, pkgdumps %d, loglines %d "
135662224350SCasper H.S. Dik 		    "(suppressed %d)\n",
135762224350SCasper H.S. Dik 		    ncalls, ndumps, loglines, suppressed);
135862224350SCasper H.S. Dik 	}
135962224350SCasper H.S. Dik 	(void) fdetach(door);
136062224350SCasper H.S. Dik 	if (read_only)
136162224350SCasper H.S. Dik 		(void) unlink(door);
136262224350SCasper H.S. Dik }
136362224350SCasper H.S. Dik 
136462224350SCasper H.S. Dik /*
136562224350SCasper H.S. Dik  * Tell the wait thread to wake up and quit.
136662224350SCasper H.S. Dik  */
136762224350SCasper H.S. Dik /* ARGSUSED */
136862224350SCasper H.S. Dik static void
signal_handler(int sig)136962224350SCasper H.S. Dik signal_handler(int sig)
137062224350SCasper H.S. Dik {
137162224350SCasper H.S. Dik 	if (read_only)
137262224350SCasper H.S. Dik 		exit(0);
137362224350SCasper H.S. Dik 	want_to_quit = 1;
137462224350SCasper H.S. Dik 	(void) cond_broadcast(&cv);
137562224350SCasper H.S. Dik }
1376