xref: /onnv-gate/usr/src/cmd/svr4pkg/pkgserv/pkgserv.c (revision 11770:02193b138d85)
19869SCasper.Dik@Sun.COM /*
29869SCasper.Dik@Sun.COM  * CDDL HEADER START
39869SCasper.Dik@Sun.COM  *
49869SCasper.Dik@Sun.COM  * The contents of this file are subject to the terms of the
59869SCasper.Dik@Sun.COM  * Common Development and Distribution License (the "License").
69869SCasper.Dik@Sun.COM  * You may not use this file except in compliance with the License.
79869SCasper.Dik@Sun.COM  *
89869SCasper.Dik@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
99869SCasper.Dik@Sun.COM  * or http://www.opensolaris.org/os/licensing.
109869SCasper.Dik@Sun.COM  * See the License for the specific language governing permissions
119869SCasper.Dik@Sun.COM  * and limitations under the License.
129869SCasper.Dik@Sun.COM  *
139869SCasper.Dik@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
149869SCasper.Dik@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
159869SCasper.Dik@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
169869SCasper.Dik@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
179869SCasper.Dik@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
189869SCasper.Dik@Sun.COM  *
199869SCasper.Dik@Sun.COM  * CDDL HEADER END
209869SCasper.Dik@Sun.COM  */
219869SCasper.Dik@Sun.COM 
229869SCasper.Dik@Sun.COM /*
23*11770SCasper.Dik@Sun.COM  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
249869SCasper.Dik@Sun.COM  * Use is subject to license terms.
259869SCasper.Dik@Sun.COM  */
269869SCasper.Dik@Sun.COM 
279869SCasper.Dik@Sun.COM /*
289869SCasper.Dik@Sun.COM  * The Solaris package installer in-memory database server.
299869SCasper.Dik@Sun.COM  *
309869SCasper.Dik@Sun.COM  * We'll keep the contents file as before; but we cache it
319869SCasper.Dik@Sun.COM  * and we don't write it as often.  Instead, we log all
329869SCasper.Dik@Sun.COM  * modifications to the log file.
339869SCasper.Dik@Sun.COM  * Using the contents file and the logfile, the pkgserv can
349869SCasper.Dik@Sun.COM  * rebuild the up-to-date contents file.
359869SCasper.Dik@Sun.COM  * The logfile is constructed so that rebuilding the
369869SCasper.Dik@Sun.COM  * contents file with the logfile is idempotent.
379869SCasper.Dik@Sun.COM  *
389869SCasper.Dik@Sun.COM  * The libpkg will start the daemon.
399869SCasper.Dik@Sun.COM  *
409869SCasper.Dik@Sun.COM  * The pkgserv will daemonize itself; the parent process
419869SCasper.Dik@Sun.COM  * waits until the child process has initialized and will
429869SCasper.Dik@Sun.COM  * start the door server.
439869SCasper.Dik@Sun.COM  * If any error occurs during start-up, the error messages
449869SCasper.Dik@Sun.COM  * are printed to stderr and the daemon will exit.
459869SCasper.Dik@Sun.COM  * After start-up, any further errors are logged to syslog.
469869SCasper.Dik@Sun.COM  * The parent pkgserv will exit with:
479869SCasper.Dik@Sun.COM  *	0	- We've started
489869SCasper.Dik@Sun.COM  *	1	- We couldn't start (locked)
499869SCasper.Dik@Sun.COM  *	2	- Other problems (error on stderr)
509869SCasper.Dik@Sun.COM  *     99	- Nothing reported; the caller must report.
519869SCasper.Dik@Sun.COM  *
529869SCasper.Dik@Sun.COM  * The daemon will timeout, by default.  It will write the
539869SCasper.Dik@Sun.COM  * contents file after a first timeout; and after a further
549869SCasper.Dik@Sun.COM  * timeout, the daemon will exit.
559869SCasper.Dik@Sun.COM  *
569869SCasper.Dik@Sun.COM  * The daemon will only timeout if the current "client" has exited;
579869SCasper.Dik@Sun.COM  * to this end, we always look at the pid of the last caller.
589869SCasper.Dik@Sun.COM  * If the last client is no longer around, we record the new client.
599869SCasper.Dik@Sun.COM  * In the typical case of running installf/removef from a post/preinstall
609869SCasper.Dik@Sun.COM  * script, we continue to follow the pkginstall/pkgremove client's pid.
619869SCasper.Dik@Sun.COM  *
629869SCasper.Dik@Sun.COM  * In the particular case of install, we make sure the daemon
639869SCasper.Dik@Sun.COM  * sticks around.  (Install == install, (live)upgrade, zone install)
649869SCasper.Dik@Sun.COM  */
659869SCasper.Dik@Sun.COM 
669869SCasper.Dik@Sun.COM #ifdef lint
679869SCasper.Dik@Sun.COM #undef _FILE_OFFSET_BITS
689869SCasper.Dik@Sun.COM #endif
699869SCasper.Dik@Sun.COM 
709869SCasper.Dik@Sun.COM #include <door.h>
719869SCasper.Dik@Sun.COM #include <errno.h>
729869SCasper.Dik@Sun.COM #include <fcntl.h>
739869SCasper.Dik@Sun.COM #include <limits.h>
749869SCasper.Dik@Sun.COM #include <pthread.h>
759869SCasper.Dik@Sun.COM #include <signal.h>
769869SCasper.Dik@Sun.COM #include <stddef.h>
779869SCasper.Dik@Sun.COM #include <stdio.h>
789869SCasper.Dik@Sun.COM #include <stdlib.h>
799869SCasper.Dik@Sun.COM #include <strings.h>
809869SCasper.Dik@Sun.COM #include <synch.h>
819869SCasper.Dik@Sun.COM #include <sys/avl.h>
829869SCasper.Dik@Sun.COM #include <sys/stat.h>
839869SCasper.Dik@Sun.COM #include <sys/statvfs.h>
849869SCasper.Dik@Sun.COM #include <sys/mman.h>
859869SCasper.Dik@Sun.COM #include <sys/time.h>
869869SCasper.Dik@Sun.COM #include <sys/wait.h>
879869SCasper.Dik@Sun.COM #include <syslog.h>
889869SCasper.Dik@Sun.COM #include <limits.h>
899869SCasper.Dik@Sun.COM #include <thread.h>
909869SCasper.Dik@Sun.COM #include <ucred.h>
919869SCasper.Dik@Sun.COM #include <umem.h>
929869SCasper.Dik@Sun.COM #include <unistd.h>
939869SCasper.Dik@Sun.COM #include <libintl.h>
949869SCasper.Dik@Sun.COM #include <locale.h>
959869SCasper.Dik@Sun.COM 
969869SCasper.Dik@Sun.COM #include <pkglib.h>
979869SCasper.Dik@Sun.COM 
989869SCasper.Dik@Sun.COM #define	SADM_DIR	"/var/sadm/install"
999869SCasper.Dik@Sun.COM 
1009869SCasper.Dik@Sun.COM #define	LOCK		".pkg.lock"
1019869SCasper.Dik@Sun.COM #define	CLIENTLOCK	".pkg.lock.client"
1029869SCasper.Dik@Sun.COM #define	CONTENTS	"contents"
1039869SCasper.Dik@Sun.COM #define	TCONTENTS	"t.contents"
1049869SCasper.Dik@Sun.COM #define	BADCONTENTS	"contents.badXXXXXX"
1059869SCasper.Dik@Sun.COM 
1069869SCasper.Dik@Sun.COM #define	LLNANOSEC	((int64_t)NANOSEC)
1079869SCasper.Dik@Sun.COM 
1089869SCasper.Dik@Sun.COM #define	DUMPTIMEOUT	60
1099869SCasper.Dik@Sun.COM #define	EXITTIMEOUT	300
1109869SCasper.Dik@Sun.COM 
1119869SCasper.Dik@Sun.COM /*
1129869SCasper.Dik@Sun.COM  * Contents file storage format.  At install time, the amount of memory
1139869SCasper.Dik@Sun.COM  * might be limited, so we make sure that we use as little memory
1149869SCasper.Dik@Sun.COM  * as possible.  The package tools modify the entries; so we install the
1159869SCasper.Dik@Sun.COM  * single lines.  We also remember the length of the path; this is needed
1169869SCasper.Dik@Sun.COM  * for avlcmp and we return it to the tools.  This saves time.
1179869SCasper.Dik@Sun.COM  *
1189869SCasper.Dik@Sun.COM  * All strings are allocated using umem_alloc.
1199869SCasper.Dik@Sun.COM  */
1209869SCasper.Dik@Sun.COM typedef struct pkgentry {
1219869SCasper.Dik@Sun.COM 	char *line;		/* The contents line for the file */
1229869SCasper.Dik@Sun.COM 	avl_node_t avl;		/* The avl header */
1239869SCasper.Dik@Sun.COM 	int pkgoff;		/* Where the packages live; start with SP */
1249869SCasper.Dik@Sun.COM 	int pathlen;		/* The length of the pathname */
1259869SCasper.Dik@Sun.COM 	int len;		/* Length of the line (incl NUL) */
1269869SCasper.Dik@Sun.COM } pkgentry_t;
1279869SCasper.Dik@Sun.COM 
1289869SCasper.Dik@Sun.COM static char IS_ST0[256];
1299869SCasper.Dik@Sun.COM static char IS_ST0Q[256];
1309869SCasper.Dik@Sun.COM 
1319869SCasper.Dik@Sun.COM static void pkg_door_srv(void *, char *, size_t, door_desc_t *, uint_t);
1329869SCasper.Dik@Sun.COM static char *file_find(pkgfilter_t *, int *);
1339869SCasper.Dik@Sun.COM static void parse_contents(void);
1349869SCasper.Dik@Sun.COM static int parse_log(void);
1359869SCasper.Dik@Sun.COM static void pkgdump(void);
1369869SCasper.Dik@Sun.COM static int logflush(void);
1379869SCasper.Dik@Sun.COM static int avlcmp(const void *, const void *);
1389869SCasper.Dik@Sun.COM static void freeentry(pkgentry_t *);
1399869SCasper.Dik@Sun.COM static void swapentry(pkgentry_t *, pkgentry_t *);
1409869SCasper.Dik@Sun.COM static int establish_lock(char *);
1419869SCasper.Dik@Sun.COM static int no_memory_abort(void);
1429869SCasper.Dik@Sun.COM static int pkgfilter(pkgfilter_t *, door_desc_t *);
1439869SCasper.Dik@Sun.COM static int pkgaddlines(pkgfilter_t *);
1449869SCasper.Dik@Sun.COM static void finish(void);
1459869SCasper.Dik@Sun.COM static void signal_handler(int);
1469869SCasper.Dik@Sun.COM static void my_cond_reltimedwait(hrtime_t, int);
1479869SCasper.Dik@Sun.COM static hrtime_t time_since_(hrtime_t);
1489869SCasper.Dik@Sun.COM 
1499869SCasper.Dik@Sun.COM /*
1509869SCasper.Dik@Sun.COM  * Server actions
1519869SCasper.Dik@Sun.COM  *	- set mode (contents file, log file)
1529869SCasper.Dik@Sun.COM  *	- roll log
1539869SCasper.Dik@Sun.COM  *	- remove package
1549869SCasper.Dik@Sun.COM  *	- merge package entries
1559869SCasper.Dik@Sun.COM  */
1569869SCasper.Dik@Sun.COM 
1579869SCasper.Dik@Sun.COM static FILE *log;
1589869SCasper.Dik@Sun.COM static char *door = PKGDOOR;
1599869SCasper.Dik@Sun.COM 
1609869SCasper.Dik@Sun.COM static avl_tree_t listp, *list = &listp;
1619869SCasper.Dik@Sun.COM 
1629869SCasper.Dik@Sun.COM /* Keep the "the last command modified the contents file ... */
1639869SCasper.Dik@Sun.COM static char *ccmnt[2];
1649869SCasper.Dik@Sun.COM static int cind = 0;
1659869SCasper.Dik@Sun.COM 
1669869SCasper.Dik@Sun.COM static mutex_t mtx = DEFAULTMUTEX;
1679869SCasper.Dik@Sun.COM static cond_t cv = DEFAULTCV;
1689869SCasper.Dik@Sun.COM 
1699869SCasper.Dik@Sun.COM static int flushbeforemark = 1;
1709869SCasper.Dik@Sun.COM static int logerrcnt = 0;
1719869SCasper.Dik@Sun.COM static int loglines = 0;
1729869SCasper.Dik@Sun.COM static int suppressed = 0;
1739869SCasper.Dik@Sun.COM static int logcount;
1749869SCasper.Dik@Sun.COM static int ndumps;
1759869SCasper.Dik@Sun.COM static int ncalls;
1769869SCasper.Dik@Sun.COM static int changes;
1779869SCasper.Dik@Sun.COM static hrtime_t lastchange;
1789869SCasper.Dik@Sun.COM static hrtime_t lastcall;
1799869SCasper.Dik@Sun.COM static volatile int want_to_quit;
1809869SCasper.Dik@Sun.COM static boolean_t read_only = B_FALSE;
1819869SCasper.Dik@Sun.COM static boolean_t permanent = B_FALSE;
1829869SCasper.Dik@Sun.COM static boolean_t one_shot = B_FALSE;
1839869SCasper.Dik@Sun.COM static int write_locked;
1849869SCasper.Dik@Sun.COM static pid_t client_pid;
1859869SCasper.Dik@Sun.COM static int verbose = 1;
1869869SCasper.Dik@Sun.COM static hrtime_t dumptimeout = DUMPTIMEOUT;
1879869SCasper.Dik@Sun.COM static boolean_t sync_needed = B_FALSE;
1889869SCasper.Dik@Sun.COM 
1899869SCasper.Dik@Sun.COM static uid_t myuid;
1909869SCasper.Dik@Sun.COM 
1919869SCasper.Dik@Sun.COM static char marker[] = "###Marker\n";
1929869SCasper.Dik@Sun.COM 
1939869SCasper.Dik@Sun.COM static umem_cache_t *ecache;
1949869SCasper.Dik@Sun.COM 
1959869SCasper.Dik@Sun.COM static char pkgdir[PATH_MAX];
1969869SCasper.Dik@Sun.COM 
1979869SCasper.Dik@Sun.COM static void
server_main(int argc,char ** argv)1989869SCasper.Dik@Sun.COM server_main(int argc, char **argv)
1999869SCasper.Dik@Sun.COM {
2009869SCasper.Dik@Sun.COM 	int did;
2019869SCasper.Dik@Sun.COM 	int c;
2029869SCasper.Dik@Sun.COM 	struct statvfs vfsbuf;
2039869SCasper.Dik@Sun.COM 	int imexit = 0;
2049869SCasper.Dik@Sun.COM 	pid_t parent;
2059869SCasper.Dik@Sun.COM 	char *root = NULL;
2069869SCasper.Dik@Sun.COM 	char *sadmdir = NULL;
2079869SCasper.Dik@Sun.COM 	hrtime_t delta;
2089869SCasper.Dik@Sun.COM 	int dir = 0;
2099869SCasper.Dik@Sun.COM 	int dfd;
2109869SCasper.Dik@Sun.COM 
2119869SCasper.Dik@Sun.COM 	(void) set_prog_name("pkgserv");
2129869SCasper.Dik@Sun.COM 
2139869SCasper.Dik@Sun.COM 	openlog("pkgserv", LOG_PID | LOG_ODELAY, LOG_DAEMON);
2149869SCasper.Dik@Sun.COM 
2159869SCasper.Dik@Sun.COM 	while ((c = getopt(argc, argv, "d:eoN:pP:R:r:")) != EOF) {
2169869SCasper.Dik@Sun.COM 		switch (c) {
2179869SCasper.Dik@Sun.COM 		case 'e':
2189869SCasper.Dik@Sun.COM 			imexit = 1;
2199869SCasper.Dik@Sun.COM 			break;
2209869SCasper.Dik@Sun.COM 		case 'd':
2219869SCasper.Dik@Sun.COM 			sadmdir = optarg;
2229869SCasper.Dik@Sun.COM 			if (*sadmdir != '/' || strlen(sadmdir) >= PATH_MAX ||
2239869SCasper.Dik@Sun.COM 			    access(sadmdir, X_OK) != 0)
2249869SCasper.Dik@Sun.COM 				exit(99);
2259869SCasper.Dik@Sun.COM 			break;
2269869SCasper.Dik@Sun.COM 		case 'N':
2279869SCasper.Dik@Sun.COM 			(void) set_prog_name(optarg);
2289869SCasper.Dik@Sun.COM 			break;
2299869SCasper.Dik@Sun.COM 		case 'o':
2309869SCasper.Dik@Sun.COM 			one_shot = B_TRUE;
2319869SCasper.Dik@Sun.COM 			verbose = 0;
2329869SCasper.Dik@Sun.COM 			break;
2339869SCasper.Dik@Sun.COM 		case 'p':
2349869SCasper.Dik@Sun.COM 			/*
2359869SCasper.Dik@Sun.COM 			 * We are updating possibly many zones; so we're not
2369869SCasper.Dik@Sun.COM 			 * dumping based on a short timeout and we will not
2379869SCasper.Dik@Sun.COM 			 * exit.
2389869SCasper.Dik@Sun.COM 			 */
2399869SCasper.Dik@Sun.COM 			permanent = B_TRUE;
2409869SCasper.Dik@Sun.COM 			dumptimeout = 3600;
2419869SCasper.Dik@Sun.COM 			break;
2429869SCasper.Dik@Sun.COM 		case 'P':
2439869SCasper.Dik@Sun.COM 			client_pid = atoi(optarg);
2449869SCasper.Dik@Sun.COM 			break;
2459869SCasper.Dik@Sun.COM 		case 'R':
2469869SCasper.Dik@Sun.COM 			root = optarg;
2479869SCasper.Dik@Sun.COM 			if (*root != '/' || strlen(root) >= PATH_MAX ||
2489869SCasper.Dik@Sun.COM 			    access(root, X_OK) != 0)
2499869SCasper.Dik@Sun.COM 				exit(99);
2509869SCasper.Dik@Sun.COM 			break;
2519869SCasper.Dik@Sun.COM 		case 'r':
2529869SCasper.Dik@Sun.COM 			read_only = B_TRUE;
2539869SCasper.Dik@Sun.COM 			one_shot = B_TRUE;
2549869SCasper.Dik@Sun.COM 			verbose = 0;
2559869SCasper.Dik@Sun.COM 			door = optarg;
2569869SCasper.Dik@Sun.COM 			break;
2579869SCasper.Dik@Sun.COM 		default:
2589869SCasper.Dik@Sun.COM 			exit(99);
2599869SCasper.Dik@Sun.COM 		}
2609869SCasper.Dik@Sun.COM 	}
2619869SCasper.Dik@Sun.COM 
2629869SCasper.Dik@Sun.COM 	if (one_shot && permanent) {
2639869SCasper.Dik@Sun.COM 		progerr(gettext("Incorrect Usage"));
2649869SCasper.Dik@Sun.COM 		exit(99);
2659869SCasper.Dik@Sun.COM 	}
2669869SCasper.Dik@Sun.COM 
2679869SCasper.Dik@Sun.COM 	umem_nofail_callback(no_memory_abort);
2689869SCasper.Dik@Sun.COM 
2699869SCasper.Dik@Sun.COM 	if (root != NULL && strcmp(root, "/") != 0) {
2709869SCasper.Dik@Sun.COM 		if (snprintf(pkgdir, PATH_MAX, "%s%s", root,
2719869SCasper.Dik@Sun.COM 		    sadmdir == NULL ? SADM_DIR : sadmdir) >= PATH_MAX) {
2729869SCasper.Dik@Sun.COM 			exit(99);
2739869SCasper.Dik@Sun.COM 		}
2749869SCasper.Dik@Sun.COM 	} else {
2759869SCasper.Dik@Sun.COM 		if (sadmdir == NULL)
2769869SCasper.Dik@Sun.COM 			(void) strcpy(pkgdir, SADM_DIR);
2779869SCasper.Dik@Sun.COM 		else
2789869SCasper.Dik@Sun.COM 			(void) strcpy(pkgdir, sadmdir);
2799869SCasper.Dik@Sun.COM 	}
2809869SCasper.Dik@Sun.COM 
2819869SCasper.Dik@Sun.COM 	if (chdir(pkgdir) != 0) {
2829869SCasper.Dik@Sun.COM 		progerr(gettext("can't chdir to %s"), pkgdir);
2839869SCasper.Dik@Sun.COM 		exit(2);
2849869SCasper.Dik@Sun.COM 	}
2859869SCasper.Dik@Sun.COM 
2869869SCasper.Dik@Sun.COM 	closefrom(3);
2879869SCasper.Dik@Sun.COM 
2889869SCasper.Dik@Sun.COM 	if (!read_only && establish_lock(LOCK) < 0) {
2899869SCasper.Dik@Sun.COM 		progerr(gettext(
2909869SCasper.Dik@Sun.COM 		    "couldn't lock in %s (server running?): %s"),
2919869SCasper.Dik@Sun.COM 		    pkgdir, strerror(errno));
2929869SCasper.Dik@Sun.COM 		exit(1);
2939869SCasper.Dik@Sun.COM 	}
2949869SCasper.Dik@Sun.COM 
2959869SCasper.Dik@Sun.COM 	did = door_create(pkg_door_srv, 0, DOOR_REFUSE_DESC);
2969869SCasper.Dik@Sun.COM 	if (did == -1) {
2979869SCasper.Dik@Sun.COM 		progerr("door_create: %s", strerror(errno));
2989869SCasper.Dik@Sun.COM 		exit(2);
2999869SCasper.Dik@Sun.COM 	}
3009869SCasper.Dik@Sun.COM 
3019869SCasper.Dik@Sun.COM 	(void) fdetach(door);
3029869SCasper.Dik@Sun.COM 
3039869SCasper.Dik@Sun.COM 	if ((dfd = creat(door, 0644)) < 0 || close(dfd) < 0) {
3049869SCasper.Dik@Sun.COM 		progerr("door_create: %s", strerror(errno));
3059869SCasper.Dik@Sun.COM 		exit(2);
3069869SCasper.Dik@Sun.COM 	}
3079869SCasper.Dik@Sun.COM 
3089869SCasper.Dik@Sun.COM 	(void) mutex_lock(&mtx);
3099869SCasper.Dik@Sun.COM 
3109869SCasper.Dik@Sun.COM 	myuid = geteuid();
3119869SCasper.Dik@Sun.COM 
3129869SCasper.Dik@Sun.COM 	(void) sigset(SIGHUP, signal_handler);
3139869SCasper.Dik@Sun.COM 	(void) sigset(SIGTERM, signal_handler);
3149869SCasper.Dik@Sun.COM 	(void) sigset(SIGINT, signal_handler);
3159869SCasper.Dik@Sun.COM 	(void) sigset(SIGQUIT, signal_handler);
3169869SCasper.Dik@Sun.COM 
3179869SCasper.Dik@Sun.COM 	(void) signal(SIGPIPE, SIG_IGN);
3189869SCasper.Dik@Sun.COM 
3199869SCasper.Dik@Sun.COM 	(void) atexit(finish);
3209869SCasper.Dik@Sun.COM 
3219869SCasper.Dik@Sun.COM 	if (fattach(did, door) != 0) {
3229869SCasper.Dik@Sun.COM 		progerr(gettext("attach door: %s"), strerror(errno));
3239869SCasper.Dik@Sun.COM 		exit(2);
3249869SCasper.Dik@Sun.COM 	}
3259869SCasper.Dik@Sun.COM 	(void) close(did);
3269869SCasper.Dik@Sun.COM 
3279869SCasper.Dik@Sun.COM 	ecache = umem_cache_create("entry", sizeof (pkgentry_t),
3289869SCasper.Dik@Sun.COM 	    sizeof (char *), NULL, NULL, NULL, NULL, NULL, 0);
3299869SCasper.Dik@Sun.COM 
3309869SCasper.Dik@Sun.COM 	avl_create(list, avlcmp, sizeof (pkgentry_t),
3319869SCasper.Dik@Sun.COM 	    offsetof(pkgentry_t, avl));
3329869SCasper.Dik@Sun.COM 
3339869SCasper.Dik@Sun.COM 	IS_ST0['\0'] = 1;
3349869SCasper.Dik@Sun.COM 	IS_ST0[' '] = 1;
3359869SCasper.Dik@Sun.COM 	IS_ST0['\t'] = 1;
3369869SCasper.Dik@Sun.COM 
3379869SCasper.Dik@Sun.COM 	IS_ST0Q['\0'] = 1;
3389869SCasper.Dik@Sun.COM 	IS_ST0Q[' '] = 1;
3399869SCasper.Dik@Sun.COM 	IS_ST0Q['\t'] = 1;
3409869SCasper.Dik@Sun.COM 	IS_ST0Q['='] = 1;
3419869SCasper.Dik@Sun.COM 
3429869SCasper.Dik@Sun.COM 	parse_contents();
3439869SCasper.Dik@Sun.COM 	if (parse_log() > 0)
3449869SCasper.Dik@Sun.COM 		pkgdump();
3459869SCasper.Dik@Sun.COM 
3469869SCasper.Dik@Sun.COM 	if (imexit)
3479869SCasper.Dik@Sun.COM 		exit(0);
3489869SCasper.Dik@Sun.COM 
3499869SCasper.Dik@Sun.COM 	if (statvfs(".", &vfsbuf) != 0) {
3509869SCasper.Dik@Sun.COM 		progerr(gettext("statvfs: %s"), strerror(errno));
3519869SCasper.Dik@Sun.COM 		exit(2);
3529869SCasper.Dik@Sun.COM 	}
3539869SCasper.Dik@Sun.COM 
3549869SCasper.Dik@Sun.COM 	if (strcmp(vfsbuf.f_basetype, "zfs") == 0)
3559869SCasper.Dik@Sun.COM 		flushbeforemark = 0;
3569869SCasper.Dik@Sun.COM 
3579869SCasper.Dik@Sun.COM 	/* We've started, tell the parent */
3589869SCasper.Dik@Sun.COM 	parent = getppid();
3599869SCasper.Dik@Sun.COM 	if (parent != 1)
3609869SCasper.Dik@Sun.COM 		(void) kill(parent, SIGUSR1);
3619869SCasper.Dik@Sun.COM 
3629869SCasper.Dik@Sun.COM 	if (!one_shot) {
3639869SCasper.Dik@Sun.COM 		int fd;
3649869SCasper.Dik@Sun.COM 		(void) setsid();
3659869SCasper.Dik@Sun.COM 		fd = open("/dev/null", O_RDWR, 0);
3669869SCasper.Dik@Sun.COM 		if (fd >= 0) {
3679869SCasper.Dik@Sun.COM 			(void) dup2(fd, STDIN_FILENO);
3689869SCasper.Dik@Sun.COM 			(void) dup2(fd, STDOUT_FILENO);
3699869SCasper.Dik@Sun.COM 			(void) dup2(fd, STDERR_FILENO);
3709869SCasper.Dik@Sun.COM 			if (fd > 2)
3719869SCasper.Dik@Sun.COM 				(void) close(fd);
3729869SCasper.Dik@Sun.COM 		}
3739869SCasper.Dik@Sun.COM 	}
3749869SCasper.Dik@Sun.COM 
3759869SCasper.Dik@Sun.COM 	lastcall = lastchange = gethrtime();
3769869SCasper.Dik@Sun.COM 
3779869SCasper.Dik@Sun.COM 	/*
3789869SCasper.Dik@Sun.COM 	 * Start the main thread, here is where we unlock the mutex.
3799869SCasper.Dik@Sun.COM 	 */
3809869SCasper.Dik@Sun.COM 	for (;;) {
3819869SCasper.Dik@Sun.COM 		if (want_to_quit) {
3829869SCasper.Dik@Sun.COM 			pkgdump();
3839869SCasper.Dik@Sun.COM 			exit(0);
3849869SCasper.Dik@Sun.COM 		}
3859869SCasper.Dik@Sun.COM 		/* Wait forever when root or when there's a running filter */
3869869SCasper.Dik@Sun.COM 		if (write_locked ||
3879869SCasper.Dik@Sun.COM 		    (!one_shot && permanent && dir == changes)) {
3889869SCasper.Dik@Sun.COM 			(void) cond_wait(&cv, &mtx);
3899869SCasper.Dik@Sun.COM 			continue;
3909869SCasper.Dik@Sun.COM 		}
3919869SCasper.Dik@Sun.COM 		delta = time_since_(lastchange);
3929869SCasper.Dik@Sun.COM 		/* Wait until DUMPTIMEOUT after last change before we pkgdump */
3939869SCasper.Dik@Sun.COM 		if (delta < dumptimeout * LLNANOSEC) {
3949869SCasper.Dik@Sun.COM 			my_cond_reltimedwait(delta, dumptimeout);
3959869SCasper.Dik@Sun.COM 			continue;
3969869SCasper.Dik@Sun.COM 		}
3979869SCasper.Dik@Sun.COM 		/* Client still around? Just wait then. */
3989869SCasper.Dik@Sun.COM 		if (client_pid > 1 && kill(client_pid, 0) == 0) {
3999869SCasper.Dik@Sun.COM 			lastchange = lastcall = gethrtime();
4009869SCasper.Dik@Sun.COM 			continue;
4019869SCasper.Dik@Sun.COM 		}
4029869SCasper.Dik@Sun.COM 		/* Wait for another EXITTIMEOUT seconds before we exit */
4039869SCasper.Dik@Sun.COM 		if ((one_shot || !permanent) && dir == changes) {
4049869SCasper.Dik@Sun.COM 			delta = time_since_(lastcall);
4059869SCasper.Dik@Sun.COM 			if (delta < EXITTIMEOUT * LLNANOSEC) {
4069869SCasper.Dik@Sun.COM 				my_cond_reltimedwait(delta, EXITTIMEOUT);
4079869SCasper.Dik@Sun.COM 				continue;
4089869SCasper.Dik@Sun.COM 			}
4099869SCasper.Dik@Sun.COM 			exit(0);
4109869SCasper.Dik@Sun.COM 		}
4119869SCasper.Dik@Sun.COM 		pkgdump();
4129869SCasper.Dik@Sun.COM 		dir = changes;
4139869SCasper.Dik@Sun.COM 	}
4149869SCasper.Dik@Sun.COM 
4159869SCasper.Dik@Sun.COM 	/*NOTREACHED*/
4169869SCasper.Dik@Sun.COM }
4179869SCasper.Dik@Sun.COM 
4189869SCasper.Dik@Sun.COM /*ARGSUSED*/
4199869SCasper.Dik@Sun.COM static void
nothing(int sig)4209869SCasper.Dik@Sun.COM nothing(int sig)
4219869SCasper.Dik@Sun.COM {
4229869SCasper.Dik@Sun.COM }
4239869SCasper.Dik@Sun.COM 
4249869SCasper.Dik@Sun.COM int
main(int argc,char ** argv)4259869SCasper.Dik@Sun.COM main(int argc, char **argv)
4269869SCasper.Dik@Sun.COM {
4279869SCasper.Dik@Sun.COM 	int sig;
4289869SCasper.Dik@Sun.COM 	sigset_t sset;
4299869SCasper.Dik@Sun.COM 	int stat;
4309869SCasper.Dik@Sun.COM 
4319869SCasper.Dik@Sun.COM 	/*
4329869SCasper.Dik@Sun.COM 	 * We're starting the daemon; this process exits when the door
4339869SCasper.Dik@Sun.COM 	 * server is established or when it fails to establish.
4349869SCasper.Dik@Sun.COM 	 * We wait until the child process sends a SIGUSR1 or when it
4359869SCasper.Dik@Sun.COM 	 * exits.
4369869SCasper.Dik@Sun.COM 	 * We keep around who started us and as long as it lives, we don't
4379869SCasper.Dik@Sun.COM 	 * exit.
4389869SCasper.Dik@Sun.COM 	 */
4399869SCasper.Dik@Sun.COM 
4409869SCasper.Dik@Sun.COM 	(void) setlocale(LC_ALL, "");
4419869SCasper.Dik@Sun.COM 	(void) textdomain(TEXT_DOMAIN);
4429869SCasper.Dik@Sun.COM 
4439869SCasper.Dik@Sun.COM 	client_pid = getppid();
4449869SCasper.Dik@Sun.COM 
4459869SCasper.Dik@Sun.COM 	(void) sigemptyset(&sset);
4469869SCasper.Dik@Sun.COM 	(void) sigaddset(&sset, SIGUSR1);
4479869SCasper.Dik@Sun.COM 	(void) sigaddset(&sset, SIGCLD);
4489869SCasper.Dik@Sun.COM 
4499869SCasper.Dik@Sun.COM 	/* We need to catch the SIGCLD before we can sigwait for it. */
4509869SCasper.Dik@Sun.COM 	(void) sigset(SIGCLD, nothing);
4519869SCasper.Dik@Sun.COM 	/* We need to make sure that SIGUSR1 is not ignored. */
4529869SCasper.Dik@Sun.COM 	(void) sigset(SIGUSR1, SIG_DFL);
4539869SCasper.Dik@Sun.COM 	(void) sigprocmask(SIG_BLOCK, &sset, NULL);
4549869SCasper.Dik@Sun.COM 
4559869SCasper.Dik@Sun.COM 	/* We install the contents file readable. */
4569869SCasper.Dik@Sun.COM 	(void) umask(022);
4579869SCasper.Dik@Sun.COM 
4589869SCasper.Dik@Sun.COM 	switch (fork()) {
4599869SCasper.Dik@Sun.COM 	case -1:
4609869SCasper.Dik@Sun.COM 		exit(99);
4619869SCasper.Dik@Sun.COM 		/*NOTREACHED*/
4629869SCasper.Dik@Sun.COM 	case 0:
4639869SCasper.Dik@Sun.COM 		server_main(argc, argv);
4649869SCasper.Dik@Sun.COM 		/*NOTREACHED*/
4659869SCasper.Dik@Sun.COM 	default:
4669869SCasper.Dik@Sun.COM 		/* In the parent */
4679869SCasper.Dik@Sun.COM 		break;
4689869SCasper.Dik@Sun.COM 	}
4699869SCasper.Dik@Sun.COM 
4709869SCasper.Dik@Sun.COM 	for (;;) {
4719869SCasper.Dik@Sun.COM 		sig = sigwait(&sset);
4729869SCasper.Dik@Sun.COM 
4739869SCasper.Dik@Sun.COM 		switch (sig) {
4749869SCasper.Dik@Sun.COM 		case SIGCLD:
4759869SCasper.Dik@Sun.COM 			if (wait(&stat) > 0) {
4769869SCasper.Dik@Sun.COM 				if (WIFEXITED(stat))
4779869SCasper.Dik@Sun.COM 					_exit(WEXITSTATUS(stat));
4789869SCasper.Dik@Sun.COM 				else if (WIFSIGNALED(stat))
4799869SCasper.Dik@Sun.COM 					_exit(99);
4809869SCasper.Dik@Sun.COM 			}
4819869SCasper.Dik@Sun.COM 			break;
4829869SCasper.Dik@Sun.COM 		case SIGUSR1:
4839869SCasper.Dik@Sun.COM 			_exit(0);
4849869SCasper.Dik@Sun.COM 		}
4859869SCasper.Dik@Sun.COM 	}
4869869SCasper.Dik@Sun.COM }
4879869SCasper.Dik@Sun.COM 
4889869SCasper.Dik@Sun.COM /*ARGSUSED*/
4899869SCasper.Dik@Sun.COM static void
pkg_door_srv(void * cookie,char * argp,size_t asz,door_desc_t * dp,uint_t ndesc)4909869SCasper.Dik@Sun.COM pkg_door_srv(void *cookie, char *argp, size_t asz, door_desc_t *dp,
4919869SCasper.Dik@Sun.COM     uint_t ndesc)
4929869SCasper.Dik@Sun.COM {
4939869SCasper.Dik@Sun.COM 	char *p = NULL;
4949869SCasper.Dik@Sun.COM 	pkgcmd_t *pcmd = (pkgcmd_t *)argp;
4959869SCasper.Dik@Sun.COM 	ucred_t *uc = NULL;
4969869SCasper.Dik@Sun.COM 	uid_t caller;
4979869SCasper.Dik@Sun.COM 	pid_t pcaller;
4989869SCasper.Dik@Sun.COM 	door_desc_t ddp;
4999869SCasper.Dik@Sun.COM 	int dnum = 0;
5009869SCasper.Dik@Sun.COM 	int one = 1;
5019869SCasper.Dik@Sun.COM 	int len = -1;
5029869SCasper.Dik@Sun.COM 
5039869SCasper.Dik@Sun.COM 	if (asz < sizeof (pkgcmd_t)) {
5049869SCasper.Dik@Sun.COM 		(void) door_return(NULL, 0, NULL, 0);
5059869SCasper.Dik@Sun.COM 		return;
5069869SCasper.Dik@Sun.COM 	}
5079869SCasper.Dik@Sun.COM 
5089869SCasper.Dik@Sun.COM 	if (door_ucred(&uc) != 0) {
5099869SCasper.Dik@Sun.COM 		(void) door_return(NULL, 0, NULL, 0);
5109869SCasper.Dik@Sun.COM 		return;
5119869SCasper.Dik@Sun.COM 	}
5129869SCasper.Dik@Sun.COM 
5139869SCasper.Dik@Sun.COM 	caller = ucred_geteuid(uc);
5149869SCasper.Dik@Sun.COM 	pcaller = ucred_getpid(uc);
5159869SCasper.Dik@Sun.COM 	ucred_free(uc);
5169869SCasper.Dik@Sun.COM 
5179869SCasper.Dik@Sun.COM 	if (caller != myuid) {
5189869SCasper.Dik@Sun.COM 		(void) door_return(NULL, 0, NULL, 0);
5199869SCasper.Dik@Sun.COM 		return;
5209869SCasper.Dik@Sun.COM 	}
5219869SCasper.Dik@Sun.COM 
5229869SCasper.Dik@Sun.COM 	(void) mutex_lock(&mtx);
5239869SCasper.Dik@Sun.COM 	ncalls++;
5249869SCasper.Dik@Sun.COM 
5259869SCasper.Dik@Sun.COM 	if (pcaller != client_pid && pcaller != -1 &&
5269869SCasper.Dik@Sun.COM 	    (client_pid == 1 || kill(client_pid, 0) != 0)) {
5279869SCasper.Dik@Sun.COM 		client_pid = pcaller;
5289869SCasper.Dik@Sun.COM 	}
5299869SCasper.Dik@Sun.COM 
5309869SCasper.Dik@Sun.COM 	if (PKG_WRITE_COMMAND(pcmd->cmd))
5319869SCasper.Dik@Sun.COM 		while (write_locked > 0)
5329869SCasper.Dik@Sun.COM 			(void) cond_wait(&cv, &mtx);
5339869SCasper.Dik@Sun.COM 
5349869SCasper.Dik@Sun.COM 	switch (pcmd->cmd) {
5359869SCasper.Dik@Sun.COM 	case PKG_FINDFILE:
5369869SCasper.Dik@Sun.COM 		p = file_find((pkgfilter_t *)argp, &len);
5379869SCasper.Dik@Sun.COM 		break;
5389869SCasper.Dik@Sun.COM 	case PKG_DUMP:
5399869SCasper.Dik@Sun.COM 		if (read_only)
5409869SCasper.Dik@Sun.COM 			goto err;
5419869SCasper.Dik@Sun.COM 		if (logcount > 0)
5429869SCasper.Dik@Sun.COM 			pkgdump();
5439869SCasper.Dik@Sun.COM 		break;
5449869SCasper.Dik@Sun.COM 	case PKG_EXIT:
5459869SCasper.Dik@Sun.COM 		if (logcount > 0)
5469869SCasper.Dik@Sun.COM 			pkgdump();
5479869SCasper.Dik@Sun.COM 		exit(0);
5489869SCasper.Dik@Sun.COM 		/*NOTREACHED*/
5499869SCasper.Dik@Sun.COM 	case PKG_PKGSYNC:
5509869SCasper.Dik@Sun.COM 		if (read_only || logflush() != 0)
5519869SCasper.Dik@Sun.COM 			goto err;
5529869SCasper.Dik@Sun.COM 		break;
5539869SCasper.Dik@Sun.COM 	case PKG_FILTER:
5549869SCasper.Dik@Sun.COM 		if (pkgfilter((pkgfilter_t *)argp, &ddp) == 0)
5559869SCasper.Dik@Sun.COM 			dnum = 1;
5569869SCasper.Dik@Sun.COM 		break;
5579869SCasper.Dik@Sun.COM 	case PKG_ADDLINES:
5589869SCasper.Dik@Sun.COM 		if (read_only)
5599869SCasper.Dik@Sun.COM 			goto err;
5609869SCasper.Dik@Sun.COM 		changes++;
5619869SCasper.Dik@Sun.COM 
5629869SCasper.Dik@Sun.COM 		if (pkgaddlines((pkgfilter_t *)argp) != 0)
5639869SCasper.Dik@Sun.COM 			goto err;
5649869SCasper.Dik@Sun.COM 		/* If we've updated the database, tell the dump thread */
5659869SCasper.Dik@Sun.COM 		lastchange = gethrtime();
5669869SCasper.Dik@Sun.COM 		(void) cond_broadcast(&cv);
5679869SCasper.Dik@Sun.COM 		break;
5689869SCasper.Dik@Sun.COM 	case PKG_NOP:
5699869SCasper.Dik@Sun.COM 		/* Do nothing but register the current client's pid. */
5709869SCasper.Dik@Sun.COM 		break;
5719869SCasper.Dik@Sun.COM 	default:
5729869SCasper.Dik@Sun.COM 		goto err;
5739869SCasper.Dik@Sun.COM 	}
5749869SCasper.Dik@Sun.COM 
5759869SCasper.Dik@Sun.COM 	lastcall = gethrtime();
5769869SCasper.Dik@Sun.COM 	(void) mutex_unlock(&mtx);
5779869SCasper.Dik@Sun.COM 	(void) door_return(p, len != -1 ? len : p == NULL ? 0 : strlen(p) + 1,
5789869SCasper.Dik@Sun.COM 	    dnum == 0 ? NULL : &ddp, dnum);
5799869SCasper.Dik@Sun.COM 	return;
5809869SCasper.Dik@Sun.COM 
5819869SCasper.Dik@Sun.COM err:
5829869SCasper.Dik@Sun.COM 	(void) mutex_unlock(&mtx);
5839869SCasper.Dik@Sun.COM 	(void) door_return((void *)&one, 4, NULL, NULL);
5849869SCasper.Dik@Sun.COM }
5859869SCasper.Dik@Sun.COM 
5869869SCasper.Dik@Sun.COM /*
5879869SCasper.Dik@Sun.COM  * This function returns the length of the string including exactly
5889869SCasper.Dik@Sun.COM  * nf fields.
5899869SCasper.Dik@Sun.COM  */
5909869SCasper.Dik@Sun.COM static ptrdiff_t
fieldoff(char * info,int nf)5919869SCasper.Dik@Sun.COM fieldoff(char *info, int nf)
5929869SCasper.Dik@Sun.COM {
5939869SCasper.Dik@Sun.COM 	char *q = info;
5949869SCasper.Dik@Sun.COM 
5959869SCasper.Dik@Sun.COM 	while (nf > 0) {
5969869SCasper.Dik@Sun.COM 		if (IS_ST0[(unsigned char)*q++]) {
5979869SCasper.Dik@Sun.COM 			if (q[-1] == 0)
5989869SCasper.Dik@Sun.COM 				break;
5999869SCasper.Dik@Sun.COM 			nf--;
6009869SCasper.Dik@Sun.COM 		}
6019869SCasper.Dik@Sun.COM 	}
6029869SCasper.Dik@Sun.COM 	return (q - info - 1);
6039869SCasper.Dik@Sun.COM }
6049869SCasper.Dik@Sun.COM 
6059869SCasper.Dik@Sun.COM /*
6069869SCasper.Dik@Sun.COM  * The buf points into list of \n delimited lines.  We copy it,
6079869SCasper.Dik@Sun.COM  * removing the newline and adding a \0.
6089869SCasper.Dik@Sun.COM  */
6099869SCasper.Dik@Sun.COM static char *
mystrcpy(char * buf,int len)6109869SCasper.Dik@Sun.COM mystrcpy(char *buf, int len)
6119869SCasper.Dik@Sun.COM {
6129869SCasper.Dik@Sun.COM 	char *res = umem_alloc(len, UMEM_NOFAIL);
6139869SCasper.Dik@Sun.COM 
6149869SCasper.Dik@Sun.COM 	(void) memcpy(res, buf, len - 1);
6159869SCasper.Dik@Sun.COM 	res[len - 1] = '\0';
6169869SCasper.Dik@Sun.COM 	return (res);
6179869SCasper.Dik@Sun.COM }
6189869SCasper.Dik@Sun.COM 
6199869SCasper.Dik@Sun.COM /*
6209869SCasper.Dik@Sun.COM  * Entry: a single line without the NEWLINE
6219869SCasper.Dik@Sun.COM  * Return: the package entry with the path determined.
6229869SCasper.Dik@Sun.COM  */
6239869SCasper.Dik@Sun.COM static pkgentry_t *
parse_line(char * buf,int blen,boolean_t full)6249869SCasper.Dik@Sun.COM parse_line(char *buf, int blen, boolean_t full)
6259869SCasper.Dik@Sun.COM {
6269869SCasper.Dik@Sun.COM 	char *t;
6279869SCasper.Dik@Sun.COM 	pkgentry_t *p;
6289869SCasper.Dik@Sun.COM 	int nfields;
6299869SCasper.Dik@Sun.COM 
6309869SCasper.Dik@Sun.COM 	p = umem_cache_alloc(ecache, UMEM_NOFAIL);
6319869SCasper.Dik@Sun.COM 	buf = p->line = mystrcpy(buf, blen + 1);
6329869SCasper.Dik@Sun.COM 	p->len = blen + 1;
6339869SCasper.Dik@Sun.COM 
6349869SCasper.Dik@Sun.COM 	t = buf;
6359869SCasper.Dik@Sun.COM 
6369869SCasper.Dik@Sun.COM 	while (!IS_ST0Q[(unsigned char)*t++])
6379869SCasper.Dik@Sun.COM 		;
6389869SCasper.Dik@Sun.COM 
6399869SCasper.Dik@Sun.COM 	p->pathlen = t - buf - 1;
6409869SCasper.Dik@Sun.COM 	if (p->pathlen == 0 || p->pathlen >= PATH_MAX) {
6419869SCasper.Dik@Sun.COM 		progerr("bad entry read in contents file");
6429869SCasper.Dik@Sun.COM 		logerr("pathname: Unknown");
6439869SCasper.Dik@Sun.COM 		logerr("problem: unable to read pathname field");
6449869SCasper.Dik@Sun.COM 		if (one_shot)
6459869SCasper.Dik@Sun.COM 			exit(2);
6469869SCasper.Dik@Sun.COM 	}
6479869SCasper.Dik@Sun.COM 	if (t[-1] == '=')
6489869SCasper.Dik@Sun.COM 		while (!IS_ST0[(unsigned char)*t++])
6499869SCasper.Dik@Sun.COM 			;
6509869SCasper.Dik@Sun.COM 
6519869SCasper.Dik@Sun.COM 	/* Partial as found in the "-" entries for log */
6529869SCasper.Dik@Sun.COM 	if (t[-1] == '\0') {
6539869SCasper.Dik@Sun.COM 		if (full)
6549869SCasper.Dik@Sun.COM 			goto badline;
6559869SCasper.Dik@Sun.COM 
6569869SCasper.Dik@Sun.COM 		p->pkgoff = -1;
6579869SCasper.Dik@Sun.COM 		return (p);
6589869SCasper.Dik@Sun.COM 	}
6599869SCasper.Dik@Sun.COM 
6609869SCasper.Dik@Sun.COM 	switch (*t) {
6619869SCasper.Dik@Sun.COM 	case '?':
6629869SCasper.Dik@Sun.COM 		nfields = 0;
6639869SCasper.Dik@Sun.COM 		break;
6649869SCasper.Dik@Sun.COM 	case 's':
6659869SCasper.Dik@Sun.COM 	case 'l':
6669869SCasper.Dik@Sun.COM 		/* Fields: class */
6679869SCasper.Dik@Sun.COM 		nfields = 1;
6689869SCasper.Dik@Sun.COM 		break;
6699869SCasper.Dik@Sun.COM 	case 'p':
6709869SCasper.Dik@Sun.COM 	case 'x':
6719869SCasper.Dik@Sun.COM 	case 'd':
6729869SCasper.Dik@Sun.COM 		/* class mode owner group */
6739869SCasper.Dik@Sun.COM 		nfields = 4;
6749869SCasper.Dik@Sun.COM 		break;
6759869SCasper.Dik@Sun.COM 	case 'f':
6769869SCasper.Dik@Sun.COM 	case 'e':
6779869SCasper.Dik@Sun.COM 	case 'v':
6789869SCasper.Dik@Sun.COM 		/* class mode owner group size csum time */
6799869SCasper.Dik@Sun.COM 		nfields = 7;
6809869SCasper.Dik@Sun.COM 		break;
6819869SCasper.Dik@Sun.COM 	case 'c':
6829869SCasper.Dik@Sun.COM 	case 'b':
6839869SCasper.Dik@Sun.COM 		/* class major minor mode owner group */
6849869SCasper.Dik@Sun.COM 		nfields = 6;
6859869SCasper.Dik@Sun.COM 		break;
6869869SCasper.Dik@Sun.COM 	default:
6879869SCasper.Dik@Sun.COM 		progerr("bad entry read in contents file");
6889869SCasper.Dik@Sun.COM 		logerr("pathname: %.*s", p->pathlen, p->line);
6899869SCasper.Dik@Sun.COM 		logerr("problem: unknown ftype");
6909869SCasper.Dik@Sun.COM 		freeentry(p);
6919869SCasper.Dik@Sun.COM 		if (one_shot)
6929869SCasper.Dik@Sun.COM 			exit(2);
6939869SCasper.Dik@Sun.COM 		return (NULL);
6949869SCasper.Dik@Sun.COM 	}
6959869SCasper.Dik@Sun.COM 
6969869SCasper.Dik@Sun.COM 	p->pkgoff = t + fieldoff(t, nfields + 1) - buf;
6979869SCasper.Dik@Sun.COM 
6989869SCasper.Dik@Sun.COM 	if (p->line[p->pkgoff] != '\0' || p->pkgoff == p->len - 1)
6999869SCasper.Dik@Sun.COM 		return (p);
7009869SCasper.Dik@Sun.COM 
7019869SCasper.Dik@Sun.COM badline:
7029869SCasper.Dik@Sun.COM 	progerr(gettext("bad entry read in contents file"));
7039869SCasper.Dik@Sun.COM 	logerr(gettext("pathname: Unknown"));
7049869SCasper.Dik@Sun.COM 	logerr(gettext("problem: unknown ftype"));
7059869SCasper.Dik@Sun.COM 	freeentry(p);
7069869SCasper.Dik@Sun.COM 	if (one_shot)
7079869SCasper.Dik@Sun.COM 		exit(2);
7089869SCasper.Dik@Sun.COM 	return (NULL);
7099869SCasper.Dik@Sun.COM }
7109869SCasper.Dik@Sun.COM 
7119869SCasper.Dik@Sun.COM static void
handle_comments(char * buf,int len)7129869SCasper.Dik@Sun.COM handle_comments(char *buf, int len)
7139869SCasper.Dik@Sun.COM {
7149869SCasper.Dik@Sun.COM 	if (cind >= 2)
7159869SCasper.Dik@Sun.COM 		return;
7169869SCasper.Dik@Sun.COM 
7179869SCasper.Dik@Sun.COM 	if (buf[0] != '#')
7189869SCasper.Dik@Sun.COM 		return;
7199869SCasper.Dik@Sun.COM 
7209869SCasper.Dik@Sun.COM 	if (ccmnt[cind] != NULL)
7219869SCasper.Dik@Sun.COM 		umem_free(ccmnt[cind], strlen(ccmnt[cind]) + 1);
7229869SCasper.Dik@Sun.COM 	ccmnt[cind] = mystrcpy(buf, len);
7239869SCasper.Dik@Sun.COM 	cind++;
7249869SCasper.Dik@Sun.COM }
7259869SCasper.Dik@Sun.COM 
7269869SCasper.Dik@Sun.COM static void
parse_contents(void)7279869SCasper.Dik@Sun.COM parse_contents(void)
7289869SCasper.Dik@Sun.COM {
7299869SCasper.Dik@Sun.COM 	int cnt;
7309869SCasper.Dik@Sun.COM 	pkgentry_t *ent, *e2;
7319869SCasper.Dik@Sun.COM 	avl_index_t where;
7329869SCasper.Dik@Sun.COM 	int num = 0;
7339869SCasper.Dik@Sun.COM 	struct stat stb;
7349869SCasper.Dik@Sun.COM 	ptrdiff_t off;
7359869SCasper.Dik@Sun.COM 	char *p, *q, *map;
7369869SCasper.Dik@Sun.COM 	pkgentry_t *lastentry = NULL;
7379869SCasper.Dik@Sun.COM 	int d;
7389869SCasper.Dik@Sun.COM 	int cntserrs = 0;
7399869SCasper.Dik@Sun.COM 
7409869SCasper.Dik@Sun.COM 	cnt = open(CONTENTS, O_RDONLY);
7419869SCasper.Dik@Sun.COM 
7429869SCasper.Dik@Sun.COM 	cind = 0;
7439869SCasper.Dik@Sun.COM 
7449869SCasper.Dik@Sun.COM 	if (cnt == -1) {
7459869SCasper.Dik@Sun.COM 		if (errno == ENOENT)
7469869SCasper.Dik@Sun.COM 			return;
7479869SCasper.Dik@Sun.COM 		exit(99);
7489869SCasper.Dik@Sun.COM 	}
7499869SCasper.Dik@Sun.COM 
7509869SCasper.Dik@Sun.COM 	if (fstat(cnt, &stb) != 0) {
7519869SCasper.Dik@Sun.COM 		(void) close(cnt);
7529869SCasper.Dik@Sun.COM 		exit(99);
7539869SCasper.Dik@Sun.COM 	}
7549869SCasper.Dik@Sun.COM 	if (stb.st_size == 0) {
7559869SCasper.Dik@Sun.COM 		(void) close(cnt);
7569869SCasper.Dik@Sun.COM 		return;
7579869SCasper.Dik@Sun.COM 	}
7589869SCasper.Dik@Sun.COM 
7599869SCasper.Dik@Sun.COM 	map = mmap(0, stb.st_size, PROT_READ, MAP_PRIVATE, cnt, 0);
7609869SCasper.Dik@Sun.COM 	(void) close(cnt);
7619869SCasper.Dik@Sun.COM 	if (map == (char *)-1)
7629869SCasper.Dik@Sun.COM 		return;
7639869SCasper.Dik@Sun.COM 
7649869SCasper.Dik@Sun.COM 	(void) madvise(map, stb.st_size, MADV_WILLNEED);
7659869SCasper.Dik@Sun.COM 
7669869SCasper.Dik@Sun.COM 	for (off = 0; off < stb.st_size; off += q - p) {
7679869SCasper.Dik@Sun.COM 		p = map + off;
7689869SCasper.Dik@Sun.COM 		q = memchr(p, '\n', stb.st_size - off);
7699869SCasper.Dik@Sun.COM 		if (q == NULL)
7709869SCasper.Dik@Sun.COM 			break;
7719869SCasper.Dik@Sun.COM 
7729869SCasper.Dik@Sun.COM 		q++;
7739869SCasper.Dik@Sun.COM 		num++;
7749869SCasper.Dik@Sun.COM 		if (p[0] == '#' || p[0] == '\n') {
7759869SCasper.Dik@Sun.COM 			handle_comments(p, q - p);
7769869SCasper.Dik@Sun.COM 			continue;
7779869SCasper.Dik@Sun.COM 		}
7789869SCasper.Dik@Sun.COM 		ent = parse_line(p, q - p - 1, B_TRUE);
7799869SCasper.Dik@Sun.COM 
7809869SCasper.Dik@Sun.COM 		if (ent == NULL) {
7819869SCasper.Dik@Sun.COM 			cntserrs++;
7829869SCasper.Dik@Sun.COM 			continue;
7839869SCasper.Dik@Sun.COM 		}
7849869SCasper.Dik@Sun.COM 
7859869SCasper.Dik@Sun.COM 		/*
7869869SCasper.Dik@Sun.COM 		 * We save time by assuming the database is sorted; by
7879869SCasper.Dik@Sun.COM 		 * using avl_insert_here(), building the tree is nearly free.
7889869SCasper.Dik@Sun.COM 		 * lastentry always contains the last entry in the AVL tree.
7899869SCasper.Dik@Sun.COM 		 */
7909869SCasper.Dik@Sun.COM 		if (lastentry == NULL) {
7919869SCasper.Dik@Sun.COM 			avl_add(list, ent);
7929869SCasper.Dik@Sun.COM 			lastentry = ent;
7939869SCasper.Dik@Sun.COM 		} else if ((d = avlcmp(ent, lastentry)) == 1) {
7949869SCasper.Dik@Sun.COM 			avl_insert_here(list, ent, lastentry, AVL_AFTER);
7959869SCasper.Dik@Sun.COM 			lastentry = ent;
7969869SCasper.Dik@Sun.COM 		} else if (d == 0 ||
7979869SCasper.Dik@Sun.COM 		    (e2 = avl_find(list, ent, &where)) != NULL) {
7989869SCasper.Dik@Sun.COM 			/*
7999869SCasper.Dik@Sun.COM 			 * This can only happen if the contents file is bad;
8009869SCasper.Dik@Sun.COM 			 * this can, e.g., happen with the old SQL contents DB,
8019869SCasper.Dik@Sun.COM 			 * it didn't sort properly.  Assume the first one
8029869SCasper.Dik@Sun.COM 			 * is the correct one, but who knows?
8039869SCasper.Dik@Sun.COM 			 */
8049869SCasper.Dik@Sun.COM 			if (d == 0)
8059869SCasper.Dik@Sun.COM 				e2 = lastentry;
8069869SCasper.Dik@Sun.COM 			if (strcmp(ent->line, e2->line) != 0) {
8079869SCasper.Dik@Sun.COM 				progerr(gettext("two entries for %.*s"),
8089869SCasper.Dik@Sun.COM 				    ent->pathlen, ent->line);
8099869SCasper.Dik@Sun.COM 				cntserrs++;
8109869SCasper.Dik@Sun.COM 			}
8119869SCasper.Dik@Sun.COM 			freeentry(ent);
8129869SCasper.Dik@Sun.COM 		} else {
8139869SCasper.Dik@Sun.COM 			/* Out of order: not an error for us, really. */
8149869SCasper.Dik@Sun.COM 			progerr(gettext("bad read of contents file"));
8159869SCasper.Dik@Sun.COM 			logerr(gettext("pathname: Unknown"));
8169869SCasper.Dik@Sun.COM 			logerr(gettext(
8179869SCasper.Dik@Sun.COM 			    "problem: unable to read pathname field"));
8189869SCasper.Dik@Sun.COM 			if (one_shot)
8199869SCasper.Dik@Sun.COM 				exit(2);
8209869SCasper.Dik@Sun.COM 			avl_insert(list, ent, where);
8219869SCasper.Dik@Sun.COM 		}
8229869SCasper.Dik@Sun.COM 	}
8239869SCasper.Dik@Sun.COM 
8249869SCasper.Dik@Sun.COM 	cind = 0;
8259869SCasper.Dik@Sun.COM 
8269869SCasper.Dik@Sun.COM 	(void) munmap(map, stb.st_size);
8279869SCasper.Dik@Sun.COM 
8289869SCasper.Dik@Sun.COM 	/* By default, we ignore bad lines, keep them in a copy. */
8299869SCasper.Dik@Sun.COM 	if (cntserrs > 0 && stb.st_nlink == 1) {
8309869SCasper.Dik@Sun.COM 		char bcf[sizeof (BADCONTENTS)];
8319869SCasper.Dik@Sun.COM 
8329869SCasper.Dik@Sun.COM 		(void) strcpy(bcf, BADCONTENTS);
8339869SCasper.Dik@Sun.COM 		if (mktemp(bcf) != NULL) {
8349869SCasper.Dik@Sun.COM 			(void) link(CONTENTS, bcf);
8359869SCasper.Dik@Sun.COM 			syslog(LOG_WARNING, "A bad contents file was saved: %s",
8369869SCasper.Dik@Sun.COM 			    bcf);
8379869SCasper.Dik@Sun.COM 		}
8389869SCasper.Dik@Sun.COM 	}
8399869SCasper.Dik@Sun.COM }
8409869SCasper.Dik@Sun.COM 
8419869SCasper.Dik@Sun.COM static int
parse_log(void)8429869SCasper.Dik@Sun.COM parse_log(void)
8439869SCasper.Dik@Sun.COM {
8449869SCasper.Dik@Sun.COM 	pkgentry_t *ent, *look;
8459869SCasper.Dik@Sun.COM 	avl_index_t where;
8469869SCasper.Dik@Sun.COM 	int num = 0;
8479869SCasper.Dik@Sun.COM 	int logfd;
8489869SCasper.Dik@Sun.COM 	struct stat stb;
8499869SCasper.Dik@Sun.COM 	int mlen = strlen(marker);
8509869SCasper.Dik@Sun.COM 	off_t realend;
8519869SCasper.Dik@Sun.COM 	ptrdiff_t off;
8529869SCasper.Dik@Sun.COM 	char *p, *q, *map;
8539869SCasper.Dik@Sun.COM 
8549869SCasper.Dik@Sun.COM 	logfd = open(PKGLOG, O_RDONLY);
8559869SCasper.Dik@Sun.COM 
8569869SCasper.Dik@Sun.COM 	if (logfd < 0) {
8579869SCasper.Dik@Sun.COM 		if (errno == ENOENT)
8589869SCasper.Dik@Sun.COM 			return (0);
8599869SCasper.Dik@Sun.COM 		progerr(gettext("cannot read "PKGLOG": %s"), strerror(errno));
8609869SCasper.Dik@Sun.COM 		exit(2);
8619869SCasper.Dik@Sun.COM 	}
8629869SCasper.Dik@Sun.COM 
8639869SCasper.Dik@Sun.COM 	if (fstat(logfd, &stb) != 0) {
8649869SCasper.Dik@Sun.COM 		progerr(gettext("cannot stat "PKGLOG": %s"), strerror(errno));
8659869SCasper.Dik@Sun.COM 		exit(2);
8669869SCasper.Dik@Sun.COM 	}
8679869SCasper.Dik@Sun.COM 
8689869SCasper.Dik@Sun.COM 	if (stb.st_size == 0) {
8699869SCasper.Dik@Sun.COM 		(void) close(logfd);
8709869SCasper.Dik@Sun.COM 		/* Force pkgdump && remove of the logfile. */
8719869SCasper.Dik@Sun.COM 		return (1);
8729869SCasper.Dik@Sun.COM 	}
8739869SCasper.Dik@Sun.COM 
874*11770SCasper.Dik@Sun.COM 	map = mmap(0, stb.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE,
8759869SCasper.Dik@Sun.COM 	    logfd, 0);
8769869SCasper.Dik@Sun.COM 	(void) close(logfd);
8779869SCasper.Dik@Sun.COM 	if (map == (char *)-1) {
8789869SCasper.Dik@Sun.COM 		progerr(gettext("Cannot mmap the "PKGLOG": %s"),
8799869SCasper.Dik@Sun.COM 		    strerror(errno));
8809869SCasper.Dik@Sun.COM 		exit(2);
8819869SCasper.Dik@Sun.COM 	}
8829869SCasper.Dik@Sun.COM 
8839869SCasper.Dik@Sun.COM 	cind = 0;
8849869SCasper.Dik@Sun.COM 
8859869SCasper.Dik@Sun.COM 	realend = stb.st_size;
8869869SCasper.Dik@Sun.COM 
8879869SCasper.Dik@Sun.COM 	if (memcmp(map + realend - mlen, marker, mlen) != 0) {
8889869SCasper.Dik@Sun.COM 		progerr(gettext(PKGLOG" is not complete"));
8899869SCasper.Dik@Sun.COM 
890*11770SCasper.Dik@Sun.COM 		map[stb.st_size - 1] = '\0'; /* for strstr() */
8919869SCasper.Dik@Sun.COM 		realend = 0;
8929869SCasper.Dik@Sun.COM 		for (p = map; q = strstr(p, marker); ) {
8939869SCasper.Dik@Sun.COM 			if (q == map || q[-1] == '\n')
8949869SCasper.Dik@Sun.COM 				realend = q - map + mlen;
8959869SCasper.Dik@Sun.COM 			p = q + mlen;
8969869SCasper.Dik@Sun.COM 		}
8979869SCasper.Dik@Sun.COM 		progerr(gettext("Ignoring %ld bytes from log"),
8989869SCasper.Dik@Sun.COM 		    (long)(stb.st_size - realend));
8999869SCasper.Dik@Sun.COM 	}
9009869SCasper.Dik@Sun.COM 
9019869SCasper.Dik@Sun.COM 	for (off = 0; off < realend; off += q - p) {
9029869SCasper.Dik@Sun.COM 		p = map + off;
9039869SCasper.Dik@Sun.COM 		q = memchr(p, '\n', realend - off);
9049869SCasper.Dik@Sun.COM 		if (q == NULL)
9059869SCasper.Dik@Sun.COM 			break;
9069869SCasper.Dik@Sun.COM 
9079869SCasper.Dik@Sun.COM 		q++;
9089869SCasper.Dik@Sun.COM 		num++;
9099869SCasper.Dik@Sun.COM 		if (p[0] == '#' || p[0] == '\n') {
9109869SCasper.Dik@Sun.COM 			if (memcmp(marker, p, mlen) == 0)
9119869SCasper.Dik@Sun.COM 				cind = 0;
9129869SCasper.Dik@Sun.COM 			else
9139869SCasper.Dik@Sun.COM 				handle_comments(p, q - p);
9149869SCasper.Dik@Sun.COM 			continue;
9159869SCasper.Dik@Sun.COM 		}
9169869SCasper.Dik@Sun.COM 
9179869SCasper.Dik@Sun.COM 		ent = parse_line(p + 1, q - (p + 1) - 1, p[0] != '-');
9189869SCasper.Dik@Sun.COM 		if (ent == NULL)
9199869SCasper.Dik@Sun.COM 			continue;
9209869SCasper.Dik@Sun.COM 		look = avl_find(list, ent, &where);
9219869SCasper.Dik@Sun.COM 		/*
9229869SCasper.Dik@Sun.COM 		 * The log can be replayed; so any value of "look" is
9239869SCasper.Dik@Sun.COM 		 * not unexpected.
9249869SCasper.Dik@Sun.COM 		 */
9259869SCasper.Dik@Sun.COM 		switch (p[0]) {
9269869SCasper.Dik@Sun.COM 		case '+':
9279869SCasper.Dik@Sun.COM 		case '=':
9289869SCasper.Dik@Sun.COM 			if (look != NULL)
9299869SCasper.Dik@Sun.COM 				swapentry(look, ent);
9309869SCasper.Dik@Sun.COM 			else
9319869SCasper.Dik@Sun.COM 				avl_insert(list, ent, where);
9329869SCasper.Dik@Sun.COM 			break;
9339869SCasper.Dik@Sun.COM 		case '-':
9349869SCasper.Dik@Sun.COM 			if (look != NULL) {
9359869SCasper.Dik@Sun.COM 				avl_remove(list, look);
9369869SCasper.Dik@Sun.COM 				freeentry(look);
9379869SCasper.Dik@Sun.COM 			}
9389869SCasper.Dik@Sun.COM 			freeentry(ent);
9399869SCasper.Dik@Sun.COM 			break;
9409869SCasper.Dik@Sun.COM 		default:
9419869SCasper.Dik@Sun.COM 			freeentry(ent);
9429869SCasper.Dik@Sun.COM 			progerr(gettext("log %d: bad line"), num);
9439869SCasper.Dik@Sun.COM 			break;
9449869SCasper.Dik@Sun.COM 		}
9459869SCasper.Dik@Sun.COM 	}
9469869SCasper.Dik@Sun.COM 	(void) munmap(map, stb.st_size);
9479869SCasper.Dik@Sun.COM 
9489869SCasper.Dik@Sun.COM 	/* Force pkgdump && remove of the logfile if there are no valid mods. */
9499869SCasper.Dik@Sun.COM 	return (num == 0 ? 1 : num);
9509869SCasper.Dik@Sun.COM }
9519869SCasper.Dik@Sun.COM 
9529869SCasper.Dik@Sun.COM static char *
file_find(pkgfilter_t * cmd,int * len)9539869SCasper.Dik@Sun.COM file_find(pkgfilter_t *cmd, int *len)
9549869SCasper.Dik@Sun.COM {
9559869SCasper.Dik@Sun.COM 	pkgentry_t p;
9569869SCasper.Dik@Sun.COM 	pkgentry_t *look;
9579869SCasper.Dik@Sun.COM 
9589869SCasper.Dik@Sun.COM 	p.line = cmd->buf;
9599869SCasper.Dik@Sun.COM 	p.pathlen = cmd->len;
9609869SCasper.Dik@Sun.COM 
9619869SCasper.Dik@Sun.COM 	look = avl_find(list, &p, NULL);
9629869SCasper.Dik@Sun.COM 
9639869SCasper.Dik@Sun.COM 	if (look == NULL)
9649869SCasper.Dik@Sun.COM 		return (NULL);
9659869SCasper.Dik@Sun.COM 
9669869SCasper.Dik@Sun.COM 	*len = look->len;
9679869SCasper.Dik@Sun.COM 	return (look->line);
9689869SCasper.Dik@Sun.COM }
9699869SCasper.Dik@Sun.COM 
9709869SCasper.Dik@Sun.COM static void
pkgdump(void)9719869SCasper.Dik@Sun.COM pkgdump(void)
9729869SCasper.Dik@Sun.COM {
9739869SCasper.Dik@Sun.COM 	FILE *cnts;
9749869SCasper.Dik@Sun.COM 	int err = 0;
9759869SCasper.Dik@Sun.COM 	pkgentry_t *p;
9769869SCasper.Dik@Sun.COM 
9779869SCasper.Dik@Sun.COM 	if (read_only)
9789869SCasper.Dik@Sun.COM 		return;
9799869SCasper.Dik@Sun.COM 
9809869SCasper.Dik@Sun.COM 	/* We cannot dump when the current transaction is not complete. */
9819869SCasper.Dik@Sun.COM 	if (sync_needed)
9829869SCasper.Dik@Sun.COM 		return;
9839869SCasper.Dik@Sun.COM 
9849869SCasper.Dik@Sun.COM 	cnts = fopen(TCONTENTS, "w");
9859869SCasper.Dik@Sun.COM 
9869869SCasper.Dik@Sun.COM 	if (cnts == NULL)
9879869SCasper.Dik@Sun.COM 		exit(99);
9889869SCasper.Dik@Sun.COM 
9899869SCasper.Dik@Sun.COM 	for (p = avl_first(list); p != NULL; p = AVL_NEXT(list, p)) {
9909869SCasper.Dik@Sun.COM 		if (fprintf(cnts, "%s\n", p->line) < 0)
9919869SCasper.Dik@Sun.COM 			err++;
9929869SCasper.Dik@Sun.COM 	}
9939869SCasper.Dik@Sun.COM 
9949869SCasper.Dik@Sun.COM 	if (ccmnt[0] != NULL)
9959869SCasper.Dik@Sun.COM 		(void) fprintf(cnts, "%s\n", ccmnt[0]);
9969869SCasper.Dik@Sun.COM 	if (ccmnt[1] != NULL)
9979869SCasper.Dik@Sun.COM 		(void) fprintf(cnts, "%s\n", ccmnt[1]);
9989869SCasper.Dik@Sun.COM 
9999869SCasper.Dik@Sun.COM 	if (err != 0 || fflush(cnts) == EOF || fsync(fileno(cnts)) != 0 ||
10009869SCasper.Dik@Sun.COM 	    fclose(cnts) == EOF || rename(TCONTENTS, CONTENTS) != 0) {
10019869SCasper.Dik@Sun.COM 		err++;
10029869SCasper.Dik@Sun.COM 	}
10039869SCasper.Dik@Sun.COM 
10049869SCasper.Dik@Sun.COM 	if (err != 0) {
10059869SCasper.Dik@Sun.COM 		progerr("cannot rewrite the contents file");
10069869SCasper.Dik@Sun.COM 		exit(2);
10079869SCasper.Dik@Sun.COM 	}
10089869SCasper.Dik@Sun.COM 
10099869SCasper.Dik@Sun.COM 	(void) fclose(log);
10109869SCasper.Dik@Sun.COM 	(void) unlink(PKGLOG);
10119869SCasper.Dik@Sun.COM 	log = NULL;
10129869SCasper.Dik@Sun.COM 	ndumps++;
10139869SCasper.Dik@Sun.COM 	logcount = 0;
10149869SCasper.Dik@Sun.COM }
10159869SCasper.Dik@Sun.COM 
10169869SCasper.Dik@Sun.COM static void
freeentry(pkgentry_t * p)10179869SCasper.Dik@Sun.COM freeentry(pkgentry_t *p)
10189869SCasper.Dik@Sun.COM {
10199869SCasper.Dik@Sun.COM 	umem_free(p->line, p->len);
10209869SCasper.Dik@Sun.COM 	umem_cache_free(ecache, p);
10219869SCasper.Dik@Sun.COM }
10229869SCasper.Dik@Sun.COM 
10239869SCasper.Dik@Sun.COM static void
swapentry(pkgentry_t * cur,pkgentry_t * new)10249869SCasper.Dik@Sun.COM swapentry(pkgentry_t *cur, pkgentry_t *new)
10259869SCasper.Dik@Sun.COM {
10269869SCasper.Dik@Sun.COM 	if (cur->len == new->len &&
10279869SCasper.Dik@Sun.COM 	    strcmp(cur->line + cur->pathlen,
10289869SCasper.Dik@Sun.COM 	    new->line + new->pathlen) == 0) {
10299869SCasper.Dik@Sun.COM 		suppressed++;
10309869SCasper.Dik@Sun.COM 		freeentry(new);
10319869SCasper.Dik@Sun.COM 		return;
10329869SCasper.Dik@Sun.COM 	}
10339869SCasper.Dik@Sun.COM 
10349869SCasper.Dik@Sun.COM 	/* Free old line */
10359869SCasper.Dik@Sun.COM 	umem_free(cur->line, cur->len);
10369869SCasper.Dik@Sun.COM 
10379869SCasper.Dik@Sun.COM 	/* Copy new value: pathlen is the same and avl is kept */
10389869SCasper.Dik@Sun.COM 	cur->line = new->line;
10399869SCasper.Dik@Sun.COM 	cur->len = new->len;
10409869SCasper.Dik@Sun.COM 	cur->pkgoff = new->pkgoff;
10419869SCasper.Dik@Sun.COM 
10429869SCasper.Dik@Sun.COM 	umem_cache_free(ecache, new);
10439869SCasper.Dik@Sun.COM }
10449869SCasper.Dik@Sun.COM 
10459869SCasper.Dik@Sun.COM static int
logentry(char type,pkgentry_t * p)10469869SCasper.Dik@Sun.COM logentry(char type, pkgentry_t *p)
10479869SCasper.Dik@Sun.COM {
10489869SCasper.Dik@Sun.COM 	int len;
10499869SCasper.Dik@Sun.COM 
10509869SCasper.Dik@Sun.COM 	if (type == '-')
10519869SCasper.Dik@Sun.COM 		len = fprintf(log, "-%.*s\n", p->pathlen, p->line);
10529869SCasper.Dik@Sun.COM 	else
10539869SCasper.Dik@Sun.COM 		len = fprintf(log, "%c%s\n", type, p->line);
10549869SCasper.Dik@Sun.COM 
10559869SCasper.Dik@Sun.COM 	loglines++;
10569869SCasper.Dik@Sun.COM 	if (len < 0) {
10579869SCasper.Dik@Sun.COM 		logerrcnt++;
10589869SCasper.Dik@Sun.COM 		return (-1);
10599869SCasper.Dik@Sun.COM 	}
10609869SCasper.Dik@Sun.COM 	logcount += len;
10619869SCasper.Dik@Sun.COM 	return (0);
10629869SCasper.Dik@Sun.COM }
10639869SCasper.Dik@Sun.COM 
10649869SCasper.Dik@Sun.COM static int
logflush(void)10659869SCasper.Dik@Sun.COM logflush(void)
10669869SCasper.Dik@Sun.COM {
10679869SCasper.Dik@Sun.COM 	int len;
10689869SCasper.Dik@Sun.COM 	static int lastflush;
10699869SCasper.Dik@Sun.COM 
10709869SCasper.Dik@Sun.COM 	if (log == NULL)
10719869SCasper.Dik@Sun.COM 		return (0);
10729869SCasper.Dik@Sun.COM 
10739869SCasper.Dik@Sun.COM 	if (lastflush == logcount)
10749869SCasper.Dik@Sun.COM 		return (0);
10759869SCasper.Dik@Sun.COM 
10769869SCasper.Dik@Sun.COM 	if (cind == 2) {
10779869SCasper.Dik@Sun.COM 		(void) fprintf(log, "%s\n", ccmnt[0]);
10789869SCasper.Dik@Sun.COM 		(void) fprintf(log, "%s\n", ccmnt[1]);
10799869SCasper.Dik@Sun.COM 		cind = 0;
10809869SCasper.Dik@Sun.COM 	}
10819869SCasper.Dik@Sun.COM 
10829869SCasper.Dik@Sun.COM 	/*
10839869SCasper.Dik@Sun.COM 	 * When using zfs, if the mark is there, then so is the rest before
10849869SCasper.Dik@Sun.COM 	 * it.  But with ufs, we need to flush twice.
10859869SCasper.Dik@Sun.COM 	 */
10869869SCasper.Dik@Sun.COM 	if (flushbeforemark) {
10879869SCasper.Dik@Sun.COM 		if (fflush(log) == EOF)
10889869SCasper.Dik@Sun.COM 			logerrcnt++;
10899869SCasper.Dik@Sun.COM 	}
10909869SCasper.Dik@Sun.COM 	/* Anything before the last marker found in the log will be valid */
10919869SCasper.Dik@Sun.COM 	len = fprintf(log, "%s", marker);
10929869SCasper.Dik@Sun.COM 	if (len < 0)
10939869SCasper.Dik@Sun.COM 		logerrcnt++;
10949869SCasper.Dik@Sun.COM 	else
10959869SCasper.Dik@Sun.COM 		logcount += len;
10969869SCasper.Dik@Sun.COM 
10979869SCasper.Dik@Sun.COM 	if (fflush(log) == EOF)
10989869SCasper.Dik@Sun.COM 		logerrcnt++;
10999869SCasper.Dik@Sun.COM 
11009869SCasper.Dik@Sun.COM 	sync_needed = B_FALSE;
11019869SCasper.Dik@Sun.COM 
11029869SCasper.Dik@Sun.COM 	if (logerrcnt > 0 || logcount > MAXLOGFILESIZE)
11039869SCasper.Dik@Sun.COM 		pkgdump();
11049869SCasper.Dik@Sun.COM 
11059869SCasper.Dik@Sun.COM 	if (logerrcnt > 0)
11069869SCasper.Dik@Sun.COM 		return (-1);
11079869SCasper.Dik@Sun.COM 
11089869SCasper.Dik@Sun.COM 	lastflush = logcount;
11099869SCasper.Dik@Sun.COM 
11109869SCasper.Dik@Sun.COM 	return (0);
11119869SCasper.Dik@Sun.COM }
11129869SCasper.Dik@Sun.COM 
11139869SCasper.Dik@Sun.COM static int
avlcmp(const void * ca,const void * cb)11149869SCasper.Dik@Sun.COM avlcmp(const void *ca, const void *cb)
11159869SCasper.Dik@Sun.COM {
11169869SCasper.Dik@Sun.COM 	const pkgentry_t *a = ca;
11179869SCasper.Dik@Sun.COM 	const pkgentry_t *b = cb;
11189869SCasper.Dik@Sun.COM 	int i = memcmp(a->line, b->line,
11199869SCasper.Dik@Sun.COM 	    a->pathlen > b->pathlen ? b->pathlen : a->pathlen);
11209869SCasper.Dik@Sun.COM 
11219869SCasper.Dik@Sun.COM 	if (i < 0)
11229869SCasper.Dik@Sun.COM 		return (-1);
11239869SCasper.Dik@Sun.COM 	else if (i > 0)
11249869SCasper.Dik@Sun.COM 		return (1);
11259869SCasper.Dik@Sun.COM 	else if (a->pathlen == b->pathlen)
11269869SCasper.Dik@Sun.COM 		return (0);
11279869SCasper.Dik@Sun.COM 	else if (a->pathlen > b->pathlen)
11289869SCasper.Dik@Sun.COM 		return (1);
11299869SCasper.Dik@Sun.COM 	else
11309869SCasper.Dik@Sun.COM 		return (-1);
11319869SCasper.Dik@Sun.COM }
11329869SCasper.Dik@Sun.COM 
11339869SCasper.Dik@Sun.COM /*
11349869SCasper.Dik@Sun.COM  * Returns:
11359869SCasper.Dik@Sun.COM  *	0 - if we can get the lock
11369869SCasper.Dik@Sun.COM  *	-1 - we can't lock
11379869SCasper.Dik@Sun.COM  */
11389869SCasper.Dik@Sun.COM 
11399869SCasper.Dik@Sun.COM static int
establish_lock(char * lock)11409869SCasper.Dik@Sun.COM establish_lock(char *lock)
11419869SCasper.Dik@Sun.COM {
11429869SCasper.Dik@Sun.COM 	int fd = open(lock, O_RDWR|O_CREAT, 0644);
11439869SCasper.Dik@Sun.COM 	int i;
11449869SCasper.Dik@Sun.COM 
11459869SCasper.Dik@Sun.COM 	if (fd < 0)
11469869SCasper.Dik@Sun.COM 		return (-1);
11479869SCasper.Dik@Sun.COM 
11489869SCasper.Dik@Sun.COM 	for (i = 0; i < 5; i++) {
11499869SCasper.Dik@Sun.COM 		if (lockf(fd, F_TLOCK, 0) == 0)
11509869SCasper.Dik@Sun.COM 			return (0);
11519869SCasper.Dik@Sun.COM 		(void) sleep(1);
11529869SCasper.Dik@Sun.COM 	}
11539869SCasper.Dik@Sun.COM 
11549869SCasper.Dik@Sun.COM 	(void) close(fd);
11559869SCasper.Dik@Sun.COM 	return (-1);
11569869SCasper.Dik@Sun.COM }
11579869SCasper.Dik@Sun.COM 
11589869SCasper.Dik@Sun.COM static int
no_memory_abort(void)11599869SCasper.Dik@Sun.COM no_memory_abort(void)
11609869SCasper.Dik@Sun.COM {
11619869SCasper.Dik@Sun.COM 	return (UMEM_CALLBACK_EXIT(99));
11629869SCasper.Dik@Sun.COM }
11639869SCasper.Dik@Sun.COM 
11649869SCasper.Dik@Sun.COM /*
11659869SCasper.Dik@Sun.COM  * Dump a part of the contents file in a pipe; grep for the "filter".
11669869SCasper.Dik@Sun.COM  * It doesn't matter if we return too much.
11679869SCasper.Dik@Sun.COM  */
11689869SCasper.Dik@Sun.COM 
11699869SCasper.Dik@Sun.COM static void *
thr_pkgfilter(void * v)11709869SCasper.Dik@Sun.COM thr_pkgfilter(void *v)
11719869SCasper.Dik@Sun.COM {
11729869SCasper.Dik@Sun.COM 	pkgfilter_t *pf = v;
11739869SCasper.Dik@Sun.COM 	pkgentry_t *p;
11749869SCasper.Dik@Sun.COM 	int nums[2];
11759869SCasper.Dik@Sun.COM 	FILE *cnts;
11769869SCasper.Dik@Sun.COM 
11779869SCasper.Dik@Sun.COM 	cnts = fdopen(pf->cmd, "w");
11789869SCasper.Dik@Sun.COM 	if (cnts == NULL)
11799869SCasper.Dik@Sun.COM 		goto free;
11809869SCasper.Dik@Sun.COM 
1181*11770SCasper.Dik@Sun.COM 	/*
1182*11770SCasper.Dik@Sun.COM 	 * Remove wild card: don't care about extra matches; make sure
1183*11770SCasper.Dik@Sun.COM 	 * we remove both the "*" and the "." in front of it.
1184*11770SCasper.Dik@Sun.COM 	 */
11859869SCasper.Dik@Sun.COM 	if (pf->len > 0) {
11869869SCasper.Dik@Sun.COM 		char *p;
11879869SCasper.Dik@Sun.COM 
11889869SCasper.Dik@Sun.COM 		for (p = pf->buf; *p; p++) {
11899869SCasper.Dik@Sun.COM 			if (*p == '*') {
11909869SCasper.Dik@Sun.COM 				*p = 0;
1191*11770SCasper.Dik@Sun.COM 				if (p > pf->buf && p[-1] == '.')
1192*11770SCasper.Dik@Sun.COM 					p[-1] = 0;
11939869SCasper.Dik@Sun.COM 				break;
11949869SCasper.Dik@Sun.COM 			}
11959869SCasper.Dik@Sun.COM 		}
11969869SCasper.Dik@Sun.COM 	}
11979869SCasper.Dik@Sun.COM 
11989869SCasper.Dik@Sun.COM 	/* Disable modifications while the filter is running */
11999869SCasper.Dik@Sun.COM 	(void) mutex_lock(&mtx);
12009869SCasper.Dik@Sun.COM 	write_locked++;
12019869SCasper.Dik@Sun.COM 	(void) mutex_unlock(&mtx);
12029869SCasper.Dik@Sun.COM 	/*
12039869SCasper.Dik@Sun.COM 	 * The protocol for the contents file for the clients:
12049869SCasper.Dik@Sun.COM 	 * <int:len><int:pathlen><line + 0>
12059869SCasper.Dik@Sun.COM 	 */
12069869SCasper.Dik@Sun.COM 
12079869SCasper.Dik@Sun.COM 	for (p = avl_first(list); p != NULL; p = AVL_NEXT(list, p)) {
12089869SCasper.Dik@Sun.COM 		if (pf->len > 0 && strstr(p->line, pf->buf) == NULL)
12099869SCasper.Dik@Sun.COM 			continue;
12109869SCasper.Dik@Sun.COM 
12119869SCasper.Dik@Sun.COM 		nums[0] = p->len;
12129869SCasper.Dik@Sun.COM 		nums[1] = p->pathlen;
12139869SCasper.Dik@Sun.COM 		if (fwrite(nums, sizeof (int), 2, cnts) != 2)
12149869SCasper.Dik@Sun.COM 			break;
12159869SCasper.Dik@Sun.COM 		if (fwrite(p->line, 1, p->len, cnts) != p->len)
12169869SCasper.Dik@Sun.COM 			break;
12179869SCasper.Dik@Sun.COM 	}
12189869SCasper.Dik@Sun.COM 
12199869SCasper.Dik@Sun.COM 	(void) mutex_lock(&mtx);
12209869SCasper.Dik@Sun.COM 	lastcall = gethrtime();
12219869SCasper.Dik@Sun.COM 	write_locked--;
12229869SCasper.Dik@Sun.COM 	(void) cond_broadcast(&cv);
12239869SCasper.Dik@Sun.COM 	(void) mutex_unlock(&mtx);
12249869SCasper.Dik@Sun.COM 	(void) fclose(cnts);
12259869SCasper.Dik@Sun.COM 
12269869SCasper.Dik@Sun.COM free:
12279869SCasper.Dik@Sun.COM 	umem_free(pf, sizeof (pkgfilter_t) + pf->len);
12289869SCasper.Dik@Sun.COM 	return (NULL);
12299869SCasper.Dik@Sun.COM }
12309869SCasper.Dik@Sun.COM 
12319869SCasper.Dik@Sun.COM static hrtime_t
time_since_(hrtime_t last)12329869SCasper.Dik@Sun.COM time_since_(hrtime_t last)
12339869SCasper.Dik@Sun.COM {
12349869SCasper.Dik@Sun.COM 	return (gethrtime() - last);
12359869SCasper.Dik@Sun.COM }
12369869SCasper.Dik@Sun.COM 
12379869SCasper.Dik@Sun.COM static void
my_cond_reltimedwait(hrtime_t delta,int sec)12389869SCasper.Dik@Sun.COM my_cond_reltimedwait(hrtime_t delta, int sec)
12399869SCasper.Dik@Sun.COM {
12409869SCasper.Dik@Sun.COM 	hrtime_t wait = sec * LLNANOSEC - delta;
12419869SCasper.Dik@Sun.COM 	timestruc_t waitfor;
12429869SCasper.Dik@Sun.COM 
12439869SCasper.Dik@Sun.COM 	waitfor.tv_nsec = wait % LLNANOSEC;
12449869SCasper.Dik@Sun.COM 	waitfor.tv_sec = wait / LLNANOSEC;
12459869SCasper.Dik@Sun.COM 	(void) cond_reltimedwait(&cv, &mtx, &waitfor);
12469869SCasper.Dik@Sun.COM }
12479869SCasper.Dik@Sun.COM 
12489869SCasper.Dik@Sun.COM static int
pkgfilter(pkgfilter_t * pf,door_desc_t * dp)12499869SCasper.Dik@Sun.COM pkgfilter(pkgfilter_t *pf, door_desc_t *dp)
12509869SCasper.Dik@Sun.COM {
12519869SCasper.Dik@Sun.COM 
12529869SCasper.Dik@Sun.COM 	int p[2];
12539869SCasper.Dik@Sun.COM 	thread_t tid;
12549869SCasper.Dik@Sun.COM 	pkgfilter_t *cpf;
12559869SCasper.Dik@Sun.COM 
12569869SCasper.Dik@Sun.COM 	if (pipe(p) != 0)
12579869SCasper.Dik@Sun.COM 		return (-1);
12589869SCasper.Dik@Sun.COM 
12599869SCasper.Dik@Sun.COM 	cpf = umem_alloc(sizeof (pkgfilter_t) + pf->len, UMEM_NOFAIL);
12609869SCasper.Dik@Sun.COM 
12619869SCasper.Dik@Sun.COM 	(void) memcpy(cpf, pf, sizeof (pkgfilter_t) + pf->len);
12629869SCasper.Dik@Sun.COM 
12639869SCasper.Dik@Sun.COM 	/* Copy the file descriptor in the command field */
12649869SCasper.Dik@Sun.COM 	cpf->cmd = p[1];
12659869SCasper.Dik@Sun.COM 
12669869SCasper.Dik@Sun.COM 	if (thr_create(NULL, NULL, thr_pkgfilter, cpf, THR_DETACHED,
12679869SCasper.Dik@Sun.COM 	    &tid) != 0) {
12689869SCasper.Dik@Sun.COM 		(void) close(p[0]);
12699869SCasper.Dik@Sun.COM 		(void) close(p[1]);
12709869SCasper.Dik@Sun.COM 		umem_free(cpf, sizeof (pkgfilter_t) + pf->len);
12719869SCasper.Dik@Sun.COM 		return (-1);
12729869SCasper.Dik@Sun.COM 	}
12739869SCasper.Dik@Sun.COM 	(void) memset(dp, 0, sizeof (*dp));
12749869SCasper.Dik@Sun.COM 	dp->d_attributes = DOOR_DESCRIPTOR | DOOR_RELEASE;
12759869SCasper.Dik@Sun.COM 	dp->d_data.d_desc.d_descriptor = p[0];
12769869SCasper.Dik@Sun.COM 
12779869SCasper.Dik@Sun.COM 	return (0);
12789869SCasper.Dik@Sun.COM }
12799869SCasper.Dik@Sun.COM 
12809869SCasper.Dik@Sun.COM static int
pkgaddlines(pkgfilter_t * pf)12819869SCasper.Dik@Sun.COM pkgaddlines(pkgfilter_t *pf)
12829869SCasper.Dik@Sun.COM {
12839869SCasper.Dik@Sun.COM 	char *map = pf->buf;
12849869SCasper.Dik@Sun.COM 	int len = pf->len;
12859869SCasper.Dik@Sun.COM 	int off;
12869869SCasper.Dik@Sun.COM 	pkgentry_t *ent, *look;
12879869SCasper.Dik@Sun.COM 	avl_index_t where;
12889869SCasper.Dik@Sun.COM 	char *q, *p;
12899869SCasper.Dik@Sun.COM 	char c;
12909869SCasper.Dik@Sun.COM 	int r = 0;
12919869SCasper.Dik@Sun.COM 
12929869SCasper.Dik@Sun.COM 	if (log == NULL) {
12939869SCasper.Dik@Sun.COM 		log = fopen(PKGLOG, "w");
12949869SCasper.Dik@Sun.COM 		if (log == NULL)
12959869SCasper.Dik@Sun.COM 			return (-1);
12969869SCasper.Dik@Sun.COM 	}
12979869SCasper.Dik@Sun.COM 
12989869SCasper.Dik@Sun.COM 	for (off = 0; off < len; off += q - p) {
12999869SCasper.Dik@Sun.COM 		p = map + off;
13009869SCasper.Dik@Sun.COM 		q = memchr(p, '\n', len - off);
13019869SCasper.Dik@Sun.COM 
13029869SCasper.Dik@Sun.COM 		if (q == NULL)
13039869SCasper.Dik@Sun.COM 			break;
13049869SCasper.Dik@Sun.COM 
13059869SCasper.Dik@Sun.COM 		q++;
13069869SCasper.Dik@Sun.COM 
13079869SCasper.Dik@Sun.COM 		if (p[0] == '#' || p[0] == '\n') {
13089869SCasper.Dik@Sun.COM 			handle_comments(p, q - p);
13099869SCasper.Dik@Sun.COM 			continue;
13109869SCasper.Dik@Sun.COM 		}
13119869SCasper.Dik@Sun.COM 
13129869SCasper.Dik@Sun.COM 		if (*p == '-')
13139869SCasper.Dik@Sun.COM 			ent = parse_line(p + 1, q - (p + 1) - 1, B_FALSE);
13149869SCasper.Dik@Sun.COM 		else
13159869SCasper.Dik@Sun.COM 			ent = parse_line(p, q - p - 1, B_TRUE);
13169869SCasper.Dik@Sun.COM 
13179869SCasper.Dik@Sun.COM 		if (ent == NULL) {
13189869SCasper.Dik@Sun.COM 			r++;
13199869SCasper.Dik@Sun.COM 			continue;
13209869SCasper.Dik@Sun.COM 		}
13219869SCasper.Dik@Sun.COM 
13229869SCasper.Dik@Sun.COM 		look = avl_find(list, ent, &where);
13239869SCasper.Dik@Sun.COM 		if (look != NULL) {
13249869SCasper.Dik@Sun.COM 			c = *p == '-' ? '-' : '=';
13259869SCasper.Dik@Sun.COM 			if (c == '=') {
13269869SCasper.Dik@Sun.COM 				swapentry(look, ent);
13279869SCasper.Dik@Sun.COM 				ent = look;
13289869SCasper.Dik@Sun.COM 			} else {
13299869SCasper.Dik@Sun.COM 				avl_remove(list, look);
13309869SCasper.Dik@Sun.COM 				freeentry(look);
13319869SCasper.Dik@Sun.COM 			}
13329869SCasper.Dik@Sun.COM 		} else if (*p == '-') {
13339869SCasper.Dik@Sun.COM 			/* Remove something which isn't there: no-op */
13349869SCasper.Dik@Sun.COM 			freeentry(ent);
13359869SCasper.Dik@Sun.COM 			continue;
13369869SCasper.Dik@Sun.COM 		} else {
13379869SCasper.Dik@Sun.COM 			avl_insert(list, ent, where);
13389869SCasper.Dik@Sun.COM 			c = '+';
13399869SCasper.Dik@Sun.COM 		}
13409869SCasper.Dik@Sun.COM 
13419869SCasper.Dik@Sun.COM 		sync_needed = B_TRUE;
13429869SCasper.Dik@Sun.COM 		r += logentry(c, ent);
13439869SCasper.Dik@Sun.COM 		if (c == '-')
13449869SCasper.Dik@Sun.COM 			freeentry(ent);
13459869SCasper.Dik@Sun.COM 	}
13469869SCasper.Dik@Sun.COM 
13479869SCasper.Dik@Sun.COM 	return (r);
13489869SCasper.Dik@Sun.COM }
13499869SCasper.Dik@Sun.COM 
13509869SCasper.Dik@Sun.COM static void
finish(void)13519869SCasper.Dik@Sun.COM finish(void)
13529869SCasper.Dik@Sun.COM {
13539869SCasper.Dik@Sun.COM 	if (verbose) {
13549869SCasper.Dik@Sun.COM 		syslog(LOG_DEBUG,
13559869SCasper.Dik@Sun.COM 		    "finished: calls %d, pkgdumps %d, loglines %d "
13569869SCasper.Dik@Sun.COM 		    "(suppressed %d)\n",
13579869SCasper.Dik@Sun.COM 		    ncalls, ndumps, loglines, suppressed);
13589869SCasper.Dik@Sun.COM 	}
13599869SCasper.Dik@Sun.COM 	(void) fdetach(door);
13609869SCasper.Dik@Sun.COM 	if (read_only)
13619869SCasper.Dik@Sun.COM 		(void) unlink(door);
13629869SCasper.Dik@Sun.COM }
13639869SCasper.Dik@Sun.COM 
13649869SCasper.Dik@Sun.COM /*
13659869SCasper.Dik@Sun.COM  * Tell the wait thread to wake up and quit.
13669869SCasper.Dik@Sun.COM  */
13679869SCasper.Dik@Sun.COM /* ARGSUSED */
13689869SCasper.Dik@Sun.COM static void
signal_handler(int sig)13699869SCasper.Dik@Sun.COM signal_handler(int sig)
13709869SCasper.Dik@Sun.COM {
13719869SCasper.Dik@Sun.COM 	if (read_only)
13729869SCasper.Dik@Sun.COM 		exit(0);
13739869SCasper.Dik@Sun.COM 	want_to_quit = 1;
13749869SCasper.Dik@Sun.COM 	(void) cond_broadcast(&cv);
13759869SCasper.Dik@Sun.COM }
1376