xref: /titanic_50/usr/src/cmd/svr4pkg/pkgserv/pkgserv.c (revision 62224350e5355e6834f7deb9d8a7d062a50cb7c2)
1*62224350SCasper H.S. Dik /*
2*62224350SCasper H.S. Dik  * CDDL HEADER START
3*62224350SCasper H.S. Dik  *
4*62224350SCasper H.S. Dik  * The contents of this file are subject to the terms of the
5*62224350SCasper H.S. Dik  * Common Development and Distribution License (the "License").
6*62224350SCasper H.S. Dik  * You may not use this file except in compliance with the License.
7*62224350SCasper H.S. Dik  *
8*62224350SCasper H.S. Dik  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*62224350SCasper H.S. Dik  * or http://www.opensolaris.org/os/licensing.
10*62224350SCasper H.S. Dik  * See the License for the specific language governing permissions
11*62224350SCasper H.S. Dik  * and limitations under the License.
12*62224350SCasper H.S. Dik  *
13*62224350SCasper H.S. Dik  * When distributing Covered Code, include this CDDL HEADER in each
14*62224350SCasper H.S. Dik  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*62224350SCasper H.S. Dik  * If applicable, add the following below this CDDL HEADER, with the
16*62224350SCasper H.S. Dik  * fields enclosed by brackets "[]" replaced with your own identifying
17*62224350SCasper H.S. Dik  * information: Portions Copyright [yyyy] [name of copyright owner]
18*62224350SCasper H.S. Dik  *
19*62224350SCasper H.S. Dik  * CDDL HEADER END
20*62224350SCasper H.S. Dik  */
21*62224350SCasper H.S. Dik 
22*62224350SCasper H.S. Dik /*
23*62224350SCasper H.S. Dik  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24*62224350SCasper H.S. Dik  * Use is subject to license terms.
25*62224350SCasper H.S. Dik  */
26*62224350SCasper H.S. Dik 
27*62224350SCasper H.S. Dik /*
28*62224350SCasper H.S. Dik  * The Solaris package installer in-memory database server.
29*62224350SCasper H.S. Dik  *
30*62224350SCasper H.S. Dik  * We'll keep the contents file as before; but we cache it
31*62224350SCasper H.S. Dik  * and we don't write it as often.  Instead, we log all
32*62224350SCasper H.S. Dik  * modifications to the log file.
33*62224350SCasper H.S. Dik  * Using the contents file and the logfile, the pkgserv can
34*62224350SCasper H.S. Dik  * rebuild the up-to-date contents file.
35*62224350SCasper H.S. Dik  * The logfile is constructed so that rebuilding the
36*62224350SCasper H.S. Dik  * contents file with the logfile is idempotent.
37*62224350SCasper H.S. Dik  *
38*62224350SCasper H.S. Dik  * The libpkg will start the daemon.
39*62224350SCasper H.S. Dik  *
40*62224350SCasper H.S. Dik  * The pkgserv will daemonize itself; the parent process
41*62224350SCasper H.S. Dik  * waits until the child process has initialized and will
42*62224350SCasper H.S. Dik  * start the door server.
43*62224350SCasper H.S. Dik  * If any error occurs during start-up, the error messages
44*62224350SCasper H.S. Dik  * are printed to stderr and the daemon will exit.
45*62224350SCasper H.S. Dik  * After start-up, any further errors are logged to syslog.
46*62224350SCasper H.S. Dik  * The parent pkgserv will exit with:
47*62224350SCasper H.S. Dik  *	0	- We've started
48*62224350SCasper H.S. Dik  *	1	- We couldn't start (locked)
49*62224350SCasper H.S. Dik  *	2	- Other problems (error on stderr)
50*62224350SCasper H.S. Dik  *     99	- Nothing reported; the caller must report.
51*62224350SCasper H.S. Dik  *
52*62224350SCasper H.S. Dik  * The daemon will timeout, by default.  It will write the
53*62224350SCasper H.S. Dik  * contents file after a first timeout; and after a further
54*62224350SCasper H.S. Dik  * timeout, the daemon will exit.
55*62224350SCasper H.S. Dik  *
56*62224350SCasper H.S. Dik  * The daemon will only timeout if the current "client" has exited;
57*62224350SCasper H.S. Dik  * to this end, we always look at the pid of the last caller.
58*62224350SCasper H.S. Dik  * If the last client is no longer around, we record the new client.
59*62224350SCasper H.S. Dik  * In the typical case of running installf/removef from a post/preinstall
60*62224350SCasper H.S. Dik  * script, we continue to follow the pkginstall/pkgremove client's pid.
61*62224350SCasper H.S. Dik  *
62*62224350SCasper H.S. Dik  * In the particular case of install, we make sure the daemon
63*62224350SCasper H.S. Dik  * sticks around.  (Install == install, (live)upgrade, zone install)
64*62224350SCasper H.S. Dik  */
65*62224350SCasper H.S. Dik 
66*62224350SCasper H.S. Dik #ifdef lint
67*62224350SCasper H.S. Dik #undef _FILE_OFFSET_BITS
68*62224350SCasper H.S. Dik #endif
69*62224350SCasper H.S. Dik 
70*62224350SCasper H.S. Dik #include <door.h>
71*62224350SCasper H.S. Dik #include <errno.h>
72*62224350SCasper H.S. Dik #include <fcntl.h>
73*62224350SCasper H.S. Dik #include <limits.h>
74*62224350SCasper H.S. Dik #include <pthread.h>
75*62224350SCasper H.S. Dik #include <signal.h>
76*62224350SCasper H.S. Dik #include <stddef.h>
77*62224350SCasper H.S. Dik #include <stdio.h>
78*62224350SCasper H.S. Dik #include <stdlib.h>
79*62224350SCasper H.S. Dik #include <strings.h>
80*62224350SCasper H.S. Dik #include <synch.h>
81*62224350SCasper H.S. Dik #include <sys/avl.h>
82*62224350SCasper H.S. Dik #include <sys/stat.h>
83*62224350SCasper H.S. Dik #include <sys/statvfs.h>
84*62224350SCasper H.S. Dik #include <sys/mman.h>
85*62224350SCasper H.S. Dik #include <sys/time.h>
86*62224350SCasper H.S. Dik #include <sys/wait.h>
87*62224350SCasper H.S. Dik #include <syslog.h>
88*62224350SCasper H.S. Dik #include <limits.h>
89*62224350SCasper H.S. Dik #include <thread.h>
90*62224350SCasper H.S. Dik #include <ucred.h>
91*62224350SCasper H.S. Dik #include <umem.h>
92*62224350SCasper H.S. Dik #include <unistd.h>
93*62224350SCasper H.S. Dik #include <libintl.h>
94*62224350SCasper H.S. Dik #include <locale.h>
95*62224350SCasper H.S. Dik 
96*62224350SCasper H.S. Dik #include <pkglib.h>
97*62224350SCasper H.S. Dik 
98*62224350SCasper H.S. Dik #define	SADM_DIR	"/var/sadm/install"
99*62224350SCasper H.S. Dik 
100*62224350SCasper H.S. Dik #define	LOCK		".pkg.lock"
101*62224350SCasper H.S. Dik #define	CLIENTLOCK	".pkg.lock.client"
102*62224350SCasper H.S. Dik #define	CONTENTS	"contents"
103*62224350SCasper H.S. Dik #define	TCONTENTS	"t.contents"
104*62224350SCasper H.S. Dik #define	BADCONTENTS	"contents.badXXXXXX"
105*62224350SCasper H.S. Dik 
106*62224350SCasper H.S. Dik #define	LLNANOSEC	((int64_t)NANOSEC)
107*62224350SCasper H.S. Dik 
108*62224350SCasper H.S. Dik #define	DUMPTIMEOUT	60
109*62224350SCasper H.S. Dik #define	EXITTIMEOUT	300
110*62224350SCasper H.S. Dik 
111*62224350SCasper H.S. Dik /*
112*62224350SCasper H.S. Dik  * Contents file storage format.  At install time, the amount of memory
113*62224350SCasper H.S. Dik  * might be limited, so we make sure that we use as little memory
114*62224350SCasper H.S. Dik  * as possible.  The package tools modify the entries; so we install the
115*62224350SCasper H.S. Dik  * single lines.  We also remember the length of the path; this is needed
116*62224350SCasper H.S. Dik  * for avlcmp and we return it to the tools.  This saves time.
117*62224350SCasper H.S. Dik  *
118*62224350SCasper H.S. Dik  * All strings are allocated using umem_alloc.
119*62224350SCasper H.S. Dik  */
120*62224350SCasper H.S. Dik typedef struct pkgentry {
121*62224350SCasper H.S. Dik 	char *line;		/* The contents line for the file */
122*62224350SCasper H.S. Dik 	avl_node_t avl;		/* The avl header */
123*62224350SCasper H.S. Dik 	int pkgoff;		/* Where the packages live; start with SP */
124*62224350SCasper H.S. Dik 	int pathlen;		/* The length of the pathname */
125*62224350SCasper H.S. Dik 	int len;		/* Length of the line (incl NUL) */
126*62224350SCasper H.S. Dik } pkgentry_t;
127*62224350SCasper H.S. Dik 
128*62224350SCasper H.S. Dik static char IS_ST0[256];
129*62224350SCasper H.S. Dik static char IS_ST0Q[256];
130*62224350SCasper H.S. Dik 
131*62224350SCasper H.S. Dik static void pkg_door_srv(void *, char *, size_t, door_desc_t *, uint_t);
132*62224350SCasper H.S. Dik static char *file_find(pkgfilter_t *, int *);
133*62224350SCasper H.S. Dik static void parse_contents(void);
134*62224350SCasper H.S. Dik static int parse_log(void);
135*62224350SCasper H.S. Dik static void pkgdump(void);
136*62224350SCasper H.S. Dik static int logflush(void);
137*62224350SCasper H.S. Dik static int avlcmp(const void *, const void *);
138*62224350SCasper H.S. Dik static void freeentry(pkgentry_t *);
139*62224350SCasper H.S. Dik static void swapentry(pkgentry_t *, pkgentry_t *);
140*62224350SCasper H.S. Dik static int establish_lock(char *);
141*62224350SCasper H.S. Dik static int no_memory_abort(void);
142*62224350SCasper H.S. Dik static int pkgfilter(pkgfilter_t *, door_desc_t *);
143*62224350SCasper H.S. Dik static int pkgaddlines(pkgfilter_t *);
144*62224350SCasper H.S. Dik static void finish(void);
145*62224350SCasper H.S. Dik static void signal_handler(int);
146*62224350SCasper H.S. Dik static void my_cond_reltimedwait(hrtime_t, int);
147*62224350SCasper H.S. Dik static hrtime_t time_since_(hrtime_t);
148*62224350SCasper H.S. Dik 
149*62224350SCasper H.S. Dik /*
150*62224350SCasper H.S. Dik  * Server actions
151*62224350SCasper H.S. Dik  *	- set mode (contents file, log file)
152*62224350SCasper H.S. Dik  *	- roll log
153*62224350SCasper H.S. Dik  *	- remove package
154*62224350SCasper H.S. Dik  *	- merge package entries
155*62224350SCasper H.S. Dik  */
156*62224350SCasper H.S. Dik 
157*62224350SCasper H.S. Dik static FILE *log;
158*62224350SCasper H.S. Dik static char *door = PKGDOOR;
159*62224350SCasper H.S. Dik 
160*62224350SCasper H.S. Dik static avl_tree_t listp, *list = &listp;
161*62224350SCasper H.S. Dik 
162*62224350SCasper H.S. Dik /* Keep the "the last command modified the contents file ... */
163*62224350SCasper H.S. Dik static char *ccmnt[2];
164*62224350SCasper H.S. Dik static int cind = 0;
165*62224350SCasper H.S. Dik 
166*62224350SCasper H.S. Dik static mutex_t mtx = DEFAULTMUTEX;
167*62224350SCasper H.S. Dik static cond_t cv = DEFAULTCV;
168*62224350SCasper H.S. Dik 
169*62224350SCasper H.S. Dik static int flushbeforemark = 1;
170*62224350SCasper H.S. Dik static int logerrcnt = 0;
171*62224350SCasper H.S. Dik static int loglines = 0;
172*62224350SCasper H.S. Dik static int suppressed = 0;
173*62224350SCasper H.S. Dik static int logcount;
174*62224350SCasper H.S. Dik static int ndumps;
175*62224350SCasper H.S. Dik static int ncalls;
176*62224350SCasper H.S. Dik static int changes;
177*62224350SCasper H.S. Dik static hrtime_t lastchange;
178*62224350SCasper H.S. Dik static hrtime_t lastcall;
179*62224350SCasper H.S. Dik static volatile int want_to_quit;
180*62224350SCasper H.S. Dik static boolean_t read_only = B_FALSE;
181*62224350SCasper H.S. Dik static boolean_t permanent = B_FALSE;
182*62224350SCasper H.S. Dik static boolean_t one_shot = B_FALSE;
183*62224350SCasper H.S. Dik static int write_locked;
184*62224350SCasper H.S. Dik static pid_t client_pid;
185*62224350SCasper H.S. Dik static int verbose = 1;
186*62224350SCasper H.S. Dik static hrtime_t dumptimeout = DUMPTIMEOUT;
187*62224350SCasper H.S. Dik static boolean_t sync_needed = B_FALSE;
188*62224350SCasper H.S. Dik 
189*62224350SCasper H.S. Dik static uid_t myuid;
190*62224350SCasper H.S. Dik 
191*62224350SCasper H.S. Dik static char marker[] = "###Marker\n";
192*62224350SCasper H.S. Dik 
193*62224350SCasper H.S. Dik static umem_cache_t *ecache;
194*62224350SCasper H.S. Dik 
195*62224350SCasper H.S. Dik static char pkgdir[PATH_MAX];
196*62224350SCasper H.S. Dik 
197*62224350SCasper H.S. Dik static void
198*62224350SCasper H.S. Dik server_main(int argc, char **argv)
199*62224350SCasper H.S. Dik {
200*62224350SCasper H.S. Dik 	int did;
201*62224350SCasper H.S. Dik 	int c;
202*62224350SCasper H.S. Dik 	struct statvfs vfsbuf;
203*62224350SCasper H.S. Dik 	int imexit = 0;
204*62224350SCasper H.S. Dik 	pid_t parent;
205*62224350SCasper H.S. Dik 	char *root = NULL;
206*62224350SCasper H.S. Dik 	char *sadmdir = NULL;
207*62224350SCasper H.S. Dik 	hrtime_t delta;
208*62224350SCasper H.S. Dik 	int dir = 0;
209*62224350SCasper H.S. Dik 	int dfd;
210*62224350SCasper H.S. Dik 
211*62224350SCasper H.S. Dik 	(void) set_prog_name("pkgserv");
212*62224350SCasper H.S. Dik 
213*62224350SCasper H.S. Dik 	openlog("pkgserv", LOG_PID | LOG_ODELAY, LOG_DAEMON);
214*62224350SCasper H.S. Dik 
215*62224350SCasper H.S. Dik 	while ((c = getopt(argc, argv, "d:eoN:pP:R:r:")) != EOF) {
216*62224350SCasper H.S. Dik 		switch (c) {
217*62224350SCasper H.S. Dik 		case 'e':
218*62224350SCasper H.S. Dik 			imexit = 1;
219*62224350SCasper H.S. Dik 			break;
220*62224350SCasper H.S. Dik 		case 'd':
221*62224350SCasper H.S. Dik 			sadmdir = optarg;
222*62224350SCasper H.S. Dik 			if (*sadmdir != '/' || strlen(sadmdir) >= PATH_MAX ||
223*62224350SCasper H.S. Dik 			    access(sadmdir, X_OK) != 0)
224*62224350SCasper H.S. Dik 				exit(99);
225*62224350SCasper H.S. Dik 			break;
226*62224350SCasper H.S. Dik 		case 'N':
227*62224350SCasper H.S. Dik 			(void) set_prog_name(optarg);
228*62224350SCasper H.S. Dik 			break;
229*62224350SCasper H.S. Dik 		case 'o':
230*62224350SCasper H.S. Dik 			one_shot = B_TRUE;
231*62224350SCasper H.S. Dik 			verbose = 0;
232*62224350SCasper H.S. Dik 			break;
233*62224350SCasper H.S. Dik 		case 'p':
234*62224350SCasper H.S. Dik 			/*
235*62224350SCasper H.S. Dik 			 * We are updating possibly many zones; so we're not
236*62224350SCasper H.S. Dik 			 * dumping based on a short timeout and we will not
237*62224350SCasper H.S. Dik 			 * exit.
238*62224350SCasper H.S. Dik 			 */
239*62224350SCasper H.S. Dik 			permanent = B_TRUE;
240*62224350SCasper H.S. Dik 			dumptimeout = 3600;
241*62224350SCasper H.S. Dik 			break;
242*62224350SCasper H.S. Dik 		case 'P':
243*62224350SCasper H.S. Dik 			client_pid = atoi(optarg);
244*62224350SCasper H.S. Dik 			break;
245*62224350SCasper H.S. Dik 		case 'R':
246*62224350SCasper H.S. Dik 			root = optarg;
247*62224350SCasper H.S. Dik 			if (*root != '/' || strlen(root) >= PATH_MAX ||
248*62224350SCasper H.S. Dik 			    access(root, X_OK) != 0)
249*62224350SCasper H.S. Dik 				exit(99);
250*62224350SCasper H.S. Dik 			break;
251*62224350SCasper H.S. Dik 		case 'r':
252*62224350SCasper H.S. Dik 			read_only = B_TRUE;
253*62224350SCasper H.S. Dik 			one_shot = B_TRUE;
254*62224350SCasper H.S. Dik 			verbose = 0;
255*62224350SCasper H.S. Dik 			door = optarg;
256*62224350SCasper H.S. Dik 			break;
257*62224350SCasper H.S. Dik 		default:
258*62224350SCasper H.S. Dik 			exit(99);
259*62224350SCasper H.S. Dik 		}
260*62224350SCasper H.S. Dik 	}
261*62224350SCasper H.S. Dik 
262*62224350SCasper H.S. Dik 	if (one_shot && permanent) {
263*62224350SCasper H.S. Dik 		progerr(gettext("Incorrect Usage"));
264*62224350SCasper H.S. Dik 		exit(99);
265*62224350SCasper H.S. Dik 	}
266*62224350SCasper H.S. Dik 
267*62224350SCasper H.S. Dik 	umem_nofail_callback(no_memory_abort);
268*62224350SCasper H.S. Dik 
269*62224350SCasper H.S. Dik 	if (root != NULL && strcmp(root, "/") != 0) {
270*62224350SCasper H.S. Dik 		if (snprintf(pkgdir, PATH_MAX, "%s%s", root,
271*62224350SCasper H.S. Dik 		    sadmdir == NULL ? SADM_DIR : sadmdir) >= PATH_MAX) {
272*62224350SCasper H.S. Dik 			exit(99);
273*62224350SCasper H.S. Dik 		}
274*62224350SCasper H.S. Dik 	} else {
275*62224350SCasper H.S. Dik 		if (sadmdir == NULL)
276*62224350SCasper H.S. Dik 			(void) strcpy(pkgdir, SADM_DIR);
277*62224350SCasper H.S. Dik 		else
278*62224350SCasper H.S. Dik 			(void) strcpy(pkgdir, sadmdir);
279*62224350SCasper H.S. Dik 	}
280*62224350SCasper H.S. Dik 
281*62224350SCasper H.S. Dik 	if (chdir(pkgdir) != 0) {
282*62224350SCasper H.S. Dik 		progerr(gettext("can't chdir to %s"), pkgdir);
283*62224350SCasper H.S. Dik 		exit(2);
284*62224350SCasper H.S. Dik 	}
285*62224350SCasper H.S. Dik 
286*62224350SCasper H.S. Dik 	closefrom(3);
287*62224350SCasper H.S. Dik 
288*62224350SCasper H.S. Dik 	if (!read_only && establish_lock(LOCK) < 0) {
289*62224350SCasper H.S. Dik 		progerr(gettext(
290*62224350SCasper H.S. Dik 		    "couldn't lock in %s (server running?): %s"),
291*62224350SCasper H.S. Dik 		    pkgdir, strerror(errno));
292*62224350SCasper H.S. Dik 		exit(1);
293*62224350SCasper H.S. Dik 	}
294*62224350SCasper H.S. Dik 
295*62224350SCasper H.S. Dik 	did = door_create(pkg_door_srv, 0, DOOR_REFUSE_DESC);
296*62224350SCasper H.S. Dik 	if (did == -1) {
297*62224350SCasper H.S. Dik 		progerr("door_create: %s", strerror(errno));
298*62224350SCasper H.S. Dik 		exit(2);
299*62224350SCasper H.S. Dik 	}
300*62224350SCasper H.S. Dik 
301*62224350SCasper H.S. Dik 	(void) fdetach(door);
302*62224350SCasper H.S. Dik 
303*62224350SCasper H.S. Dik 	if ((dfd = creat(door, 0644)) < 0 || close(dfd) < 0) {
304*62224350SCasper H.S. Dik 		progerr("door_create: %s", strerror(errno));
305*62224350SCasper H.S. Dik 		exit(2);
306*62224350SCasper H.S. Dik 	}
307*62224350SCasper H.S. Dik 
308*62224350SCasper H.S. Dik 	(void) mutex_lock(&mtx);
309*62224350SCasper H.S. Dik 
310*62224350SCasper H.S. Dik 	myuid = geteuid();
311*62224350SCasper H.S. Dik 
312*62224350SCasper H.S. Dik 	(void) sigset(SIGHUP, signal_handler);
313*62224350SCasper H.S. Dik 	(void) sigset(SIGTERM, signal_handler);
314*62224350SCasper H.S. Dik 	(void) sigset(SIGINT, signal_handler);
315*62224350SCasper H.S. Dik 	(void) sigset(SIGQUIT, signal_handler);
316*62224350SCasper H.S. Dik 
317*62224350SCasper H.S. Dik 	(void) signal(SIGPIPE, SIG_IGN);
318*62224350SCasper H.S. Dik 
319*62224350SCasper H.S. Dik 	(void) atexit(finish);
320*62224350SCasper H.S. Dik 
321*62224350SCasper H.S. Dik 	if (fattach(did, door) != 0) {
322*62224350SCasper H.S. Dik 		progerr(gettext("attach door: %s"), strerror(errno));
323*62224350SCasper H.S. Dik 		exit(2);
324*62224350SCasper H.S. Dik 	}
325*62224350SCasper H.S. Dik 	(void) close(did);
326*62224350SCasper H.S. Dik 
327*62224350SCasper H.S. Dik 	ecache = umem_cache_create("entry", sizeof (pkgentry_t),
328*62224350SCasper H.S. Dik 	    sizeof (char *), NULL, NULL, NULL, NULL, NULL, 0);
329*62224350SCasper H.S. Dik 
330*62224350SCasper H.S. Dik 	avl_create(list, avlcmp, sizeof (pkgentry_t),
331*62224350SCasper H.S. Dik 	    offsetof(pkgentry_t, avl));
332*62224350SCasper H.S. Dik 
333*62224350SCasper H.S. Dik 	IS_ST0['\0'] = 1;
334*62224350SCasper H.S. Dik 	IS_ST0[' '] = 1;
335*62224350SCasper H.S. Dik 	IS_ST0['\t'] = 1;
336*62224350SCasper H.S. Dik 
337*62224350SCasper H.S. Dik 	IS_ST0Q['\0'] = 1;
338*62224350SCasper H.S. Dik 	IS_ST0Q[' '] = 1;
339*62224350SCasper H.S. Dik 	IS_ST0Q['\t'] = 1;
340*62224350SCasper H.S. Dik 	IS_ST0Q['='] = 1;
341*62224350SCasper H.S. Dik 
342*62224350SCasper H.S. Dik 	parse_contents();
343*62224350SCasper H.S. Dik 	if (parse_log() > 0)
344*62224350SCasper H.S. Dik 		pkgdump();
345*62224350SCasper H.S. Dik 
346*62224350SCasper H.S. Dik 	if (imexit)
347*62224350SCasper H.S. Dik 		exit(0);
348*62224350SCasper H.S. Dik 
349*62224350SCasper H.S. Dik 	if (statvfs(".", &vfsbuf) != 0) {
350*62224350SCasper H.S. Dik 		progerr(gettext("statvfs: %s"), strerror(errno));
351*62224350SCasper H.S. Dik 		exit(2);
352*62224350SCasper H.S. Dik 	}
353*62224350SCasper H.S. Dik 
354*62224350SCasper H.S. Dik 	if (strcmp(vfsbuf.f_basetype, "zfs") == 0)
355*62224350SCasper H.S. Dik 		flushbeforemark = 0;
356*62224350SCasper H.S. Dik 
357*62224350SCasper H.S. Dik 	/* We've started, tell the parent */
358*62224350SCasper H.S. Dik 	parent = getppid();
359*62224350SCasper H.S. Dik 	if (parent != 1)
360*62224350SCasper H.S. Dik 		(void) kill(parent, SIGUSR1);
361*62224350SCasper H.S. Dik 
362*62224350SCasper H.S. Dik 	if (!one_shot) {
363*62224350SCasper H.S. Dik 		int fd;
364*62224350SCasper H.S. Dik 		(void) setsid();
365*62224350SCasper H.S. Dik 		fd = open("/dev/null", O_RDWR, 0);
366*62224350SCasper H.S. Dik 		if (fd >= 0) {
367*62224350SCasper H.S. Dik 			(void) dup2(fd, STDIN_FILENO);
368*62224350SCasper H.S. Dik 			(void) dup2(fd, STDOUT_FILENO);
369*62224350SCasper H.S. Dik 			(void) dup2(fd, STDERR_FILENO);
370*62224350SCasper H.S. Dik 			if (fd > 2)
371*62224350SCasper H.S. Dik 				(void) close(fd);
372*62224350SCasper H.S. Dik 		}
373*62224350SCasper H.S. Dik 	}
374*62224350SCasper H.S. Dik 
375*62224350SCasper H.S. Dik 	lastcall = lastchange = gethrtime();
376*62224350SCasper H.S. Dik 
377*62224350SCasper H.S. Dik 	/*
378*62224350SCasper H.S. Dik 	 * Start the main thread, here is where we unlock the mutex.
379*62224350SCasper H.S. Dik 	 */
380*62224350SCasper H.S. Dik 	for (;;) {
381*62224350SCasper H.S. Dik 		if (want_to_quit) {
382*62224350SCasper H.S. Dik 			pkgdump();
383*62224350SCasper H.S. Dik 			exit(0);
384*62224350SCasper H.S. Dik 		}
385*62224350SCasper H.S. Dik 		/* Wait forever when root or when there's a running filter */
386*62224350SCasper H.S. Dik 		if (write_locked ||
387*62224350SCasper H.S. Dik 		    (!one_shot && permanent && dir == changes)) {
388*62224350SCasper H.S. Dik 			(void) cond_wait(&cv, &mtx);
389*62224350SCasper H.S. Dik 			continue;
390*62224350SCasper H.S. Dik 		}
391*62224350SCasper H.S. Dik 		delta = time_since_(lastchange);
392*62224350SCasper H.S. Dik 		/* Wait until DUMPTIMEOUT after last change before we pkgdump */
393*62224350SCasper H.S. Dik 		if (delta < dumptimeout * LLNANOSEC) {
394*62224350SCasper H.S. Dik 			my_cond_reltimedwait(delta, dumptimeout);
395*62224350SCasper H.S. Dik 			continue;
396*62224350SCasper H.S. Dik 		}
397*62224350SCasper H.S. Dik 		/* Client still around? Just wait then. */
398*62224350SCasper H.S. Dik 		if (client_pid > 1 && kill(client_pid, 0) == 0) {
399*62224350SCasper H.S. Dik 			lastchange = lastcall = gethrtime();
400*62224350SCasper H.S. Dik 			continue;
401*62224350SCasper H.S. Dik 		}
402*62224350SCasper H.S. Dik 		/* Wait for another EXITTIMEOUT seconds before we exit */
403*62224350SCasper H.S. Dik 		if ((one_shot || !permanent) && dir == changes) {
404*62224350SCasper H.S. Dik 			delta = time_since_(lastcall);
405*62224350SCasper H.S. Dik 			if (delta < EXITTIMEOUT * LLNANOSEC) {
406*62224350SCasper H.S. Dik 				my_cond_reltimedwait(delta, EXITTIMEOUT);
407*62224350SCasper H.S. Dik 				continue;
408*62224350SCasper H.S. Dik 			}
409*62224350SCasper H.S. Dik 			exit(0);
410*62224350SCasper H.S. Dik 		}
411*62224350SCasper H.S. Dik 		pkgdump();
412*62224350SCasper H.S. Dik 		dir = changes;
413*62224350SCasper H.S. Dik 	}
414*62224350SCasper H.S. Dik 
415*62224350SCasper H.S. Dik 	/*NOTREACHED*/
416*62224350SCasper H.S. Dik }
417*62224350SCasper H.S. Dik 
418*62224350SCasper H.S. Dik /*ARGSUSED*/
419*62224350SCasper H.S. Dik static void
420*62224350SCasper H.S. Dik nothing(int sig)
421*62224350SCasper H.S. Dik {
422*62224350SCasper H.S. Dik }
423*62224350SCasper H.S. Dik 
424*62224350SCasper H.S. Dik int
425*62224350SCasper H.S. Dik main(int argc, char **argv)
426*62224350SCasper H.S. Dik {
427*62224350SCasper H.S. Dik 	int sig;
428*62224350SCasper H.S. Dik 	sigset_t sset;
429*62224350SCasper H.S. Dik 	int stat;
430*62224350SCasper H.S. Dik 
431*62224350SCasper H.S. Dik 	/*
432*62224350SCasper H.S. Dik 	 * We're starting the daemon; this process exits when the door
433*62224350SCasper H.S. Dik 	 * server is established or when it fails to establish.
434*62224350SCasper H.S. Dik 	 * We wait until the child process sends a SIGUSR1 or when it
435*62224350SCasper H.S. Dik 	 * exits.
436*62224350SCasper H.S. Dik 	 * We keep around who started us and as long as it lives, we don't
437*62224350SCasper H.S. Dik 	 * exit.
438*62224350SCasper H.S. Dik 	 */
439*62224350SCasper H.S. Dik 
440*62224350SCasper H.S. Dik 	(void) setlocale(LC_ALL, "");
441*62224350SCasper H.S. Dik 	(void) textdomain(TEXT_DOMAIN);
442*62224350SCasper H.S. Dik 
443*62224350SCasper H.S. Dik 	client_pid = getppid();
444*62224350SCasper H.S. Dik 
445*62224350SCasper H.S. Dik 	(void) sigemptyset(&sset);
446*62224350SCasper H.S. Dik 	(void) sigaddset(&sset, SIGUSR1);
447*62224350SCasper H.S. Dik 	(void) sigaddset(&sset, SIGCLD);
448*62224350SCasper H.S. Dik 
449*62224350SCasper H.S. Dik 	/* We need to catch the SIGCLD before we can sigwait for it. */
450*62224350SCasper H.S. Dik 	(void) sigset(SIGCLD, nothing);
451*62224350SCasper H.S. Dik 	/* We need to make sure that SIGUSR1 is not ignored. */
452*62224350SCasper H.S. Dik 	(void) sigset(SIGUSR1, SIG_DFL);
453*62224350SCasper H.S. Dik 	(void) sigprocmask(SIG_BLOCK, &sset, NULL);
454*62224350SCasper H.S. Dik 
455*62224350SCasper H.S. Dik 	/* We install the contents file readable. */
456*62224350SCasper H.S. Dik 	(void) umask(022);
457*62224350SCasper H.S. Dik 
458*62224350SCasper H.S. Dik 	switch (fork()) {
459*62224350SCasper H.S. Dik 	case -1:
460*62224350SCasper H.S. Dik 		exit(99);
461*62224350SCasper H.S. Dik 		/*NOTREACHED*/
462*62224350SCasper H.S. Dik 	case 0:
463*62224350SCasper H.S. Dik 		server_main(argc, argv);
464*62224350SCasper H.S. Dik 		/*NOTREACHED*/
465*62224350SCasper H.S. Dik 	default:
466*62224350SCasper H.S. Dik 		/* In the parent */
467*62224350SCasper H.S. Dik 		break;
468*62224350SCasper H.S. Dik 	}
469*62224350SCasper H.S. Dik 
470*62224350SCasper H.S. Dik 	for (;;) {
471*62224350SCasper H.S. Dik 		sig = sigwait(&sset);
472*62224350SCasper H.S. Dik 
473*62224350SCasper H.S. Dik 		switch (sig) {
474*62224350SCasper H.S. Dik 		case SIGCLD:
475*62224350SCasper H.S. Dik 			if (wait(&stat) > 0) {
476*62224350SCasper H.S. Dik 				if (WIFEXITED(stat))
477*62224350SCasper H.S. Dik 					_exit(WEXITSTATUS(stat));
478*62224350SCasper H.S. Dik 				else if (WIFSIGNALED(stat))
479*62224350SCasper H.S. Dik 					_exit(99);
480*62224350SCasper H.S. Dik 			}
481*62224350SCasper H.S. Dik 			break;
482*62224350SCasper H.S. Dik 		case SIGUSR1:
483*62224350SCasper H.S. Dik 			_exit(0);
484*62224350SCasper H.S. Dik 		}
485*62224350SCasper H.S. Dik 	}
486*62224350SCasper H.S. Dik }
487*62224350SCasper H.S. Dik 
488*62224350SCasper H.S. Dik /*ARGSUSED*/
489*62224350SCasper H.S. Dik static void
490*62224350SCasper H.S. Dik pkg_door_srv(void *cookie, char *argp, size_t asz, door_desc_t *dp,
491*62224350SCasper H.S. Dik     uint_t ndesc)
492*62224350SCasper H.S. Dik {
493*62224350SCasper H.S. Dik 	char *p = NULL;
494*62224350SCasper H.S. Dik 	pkgcmd_t *pcmd = (pkgcmd_t *)argp;
495*62224350SCasper H.S. Dik 	ucred_t *uc = NULL;
496*62224350SCasper H.S. Dik 	uid_t caller;
497*62224350SCasper H.S. Dik 	pid_t pcaller;
498*62224350SCasper H.S. Dik 	door_desc_t ddp;
499*62224350SCasper H.S. Dik 	int dnum = 0;
500*62224350SCasper H.S. Dik 	int one = 1;
501*62224350SCasper H.S. Dik 	int len = -1;
502*62224350SCasper H.S. Dik 
503*62224350SCasper H.S. Dik 	if (asz < sizeof (pkgcmd_t)) {
504*62224350SCasper H.S. Dik 		(void) door_return(NULL, 0, NULL, 0);
505*62224350SCasper H.S. Dik 		return;
506*62224350SCasper H.S. Dik 	}
507*62224350SCasper H.S. Dik 
508*62224350SCasper H.S. Dik 	if (door_ucred(&uc) != 0) {
509*62224350SCasper H.S. Dik 		(void) door_return(NULL, 0, NULL, 0);
510*62224350SCasper H.S. Dik 		return;
511*62224350SCasper H.S. Dik 	}
512*62224350SCasper H.S. Dik 
513*62224350SCasper H.S. Dik 	caller = ucred_geteuid(uc);
514*62224350SCasper H.S. Dik 	pcaller = ucred_getpid(uc);
515*62224350SCasper H.S. Dik 	ucred_free(uc);
516*62224350SCasper H.S. Dik 
517*62224350SCasper H.S. Dik 	if (caller != myuid) {
518*62224350SCasper H.S. Dik 		(void) door_return(NULL, 0, NULL, 0);
519*62224350SCasper H.S. Dik 		return;
520*62224350SCasper H.S. Dik 	}
521*62224350SCasper H.S. Dik 
522*62224350SCasper H.S. Dik 	(void) mutex_lock(&mtx);
523*62224350SCasper H.S. Dik 	ncalls++;
524*62224350SCasper H.S. Dik 
525*62224350SCasper H.S. Dik 	if (pcaller != client_pid && pcaller != -1 &&
526*62224350SCasper H.S. Dik 	    (client_pid == 1 || kill(client_pid, 0) != 0)) {
527*62224350SCasper H.S. Dik 		client_pid = pcaller;
528*62224350SCasper H.S. Dik 	}
529*62224350SCasper H.S. Dik 
530*62224350SCasper H.S. Dik 	if (PKG_WRITE_COMMAND(pcmd->cmd))
531*62224350SCasper H.S. Dik 		while (write_locked > 0)
532*62224350SCasper H.S. Dik 			(void) cond_wait(&cv, &mtx);
533*62224350SCasper H.S. Dik 
534*62224350SCasper H.S. Dik 	switch (pcmd->cmd) {
535*62224350SCasper H.S. Dik 	case PKG_FINDFILE:
536*62224350SCasper H.S. Dik 		p = file_find((pkgfilter_t *)argp, &len);
537*62224350SCasper H.S. Dik 		break;
538*62224350SCasper H.S. Dik 	case PKG_DUMP:
539*62224350SCasper H.S. Dik 		if (read_only)
540*62224350SCasper H.S. Dik 			goto err;
541*62224350SCasper H.S. Dik 		if (logcount > 0)
542*62224350SCasper H.S. Dik 			pkgdump();
543*62224350SCasper H.S. Dik 		break;
544*62224350SCasper H.S. Dik 	case PKG_EXIT:
545*62224350SCasper H.S. Dik 		if (logcount > 0)
546*62224350SCasper H.S. Dik 			pkgdump();
547*62224350SCasper H.S. Dik 		exit(0);
548*62224350SCasper H.S. Dik 		/*NOTREACHED*/
549*62224350SCasper H.S. Dik 	case PKG_PKGSYNC:
550*62224350SCasper H.S. Dik 		if (read_only || logflush() != 0)
551*62224350SCasper H.S. Dik 			goto err;
552*62224350SCasper H.S. Dik 		break;
553*62224350SCasper H.S. Dik 	case PKG_FILTER:
554*62224350SCasper H.S. Dik 		if (pkgfilter((pkgfilter_t *)argp, &ddp) == 0)
555*62224350SCasper H.S. Dik 			dnum = 1;
556*62224350SCasper H.S. Dik 		break;
557*62224350SCasper H.S. Dik 	case PKG_ADDLINES:
558*62224350SCasper H.S. Dik 		if (read_only)
559*62224350SCasper H.S. Dik 			goto err;
560*62224350SCasper H.S. Dik 		changes++;
561*62224350SCasper H.S. Dik 
562*62224350SCasper H.S. Dik 		if (pkgaddlines((pkgfilter_t *)argp) != 0)
563*62224350SCasper H.S. Dik 			goto err;
564*62224350SCasper H.S. Dik 		/* If we've updated the database, tell the dump thread */
565*62224350SCasper H.S. Dik 		lastchange = gethrtime();
566*62224350SCasper H.S. Dik 		(void) cond_broadcast(&cv);
567*62224350SCasper H.S. Dik 		break;
568*62224350SCasper H.S. Dik 	case PKG_NOP:
569*62224350SCasper H.S. Dik 		/* Do nothing but register the current client's pid. */
570*62224350SCasper H.S. Dik 		break;
571*62224350SCasper H.S. Dik 	default:
572*62224350SCasper H.S. Dik 		goto err;
573*62224350SCasper H.S. Dik 	}
574*62224350SCasper H.S. Dik 
575*62224350SCasper H.S. Dik 	lastcall = gethrtime();
576*62224350SCasper H.S. Dik 	(void) mutex_unlock(&mtx);
577*62224350SCasper H.S. Dik 	(void) door_return(p, len != -1 ? len : p == NULL ? 0 : strlen(p) + 1,
578*62224350SCasper H.S. Dik 	    dnum == 0 ? NULL : &ddp, dnum);
579*62224350SCasper H.S. Dik 	return;
580*62224350SCasper H.S. Dik 
581*62224350SCasper H.S. Dik err:
582*62224350SCasper H.S. Dik 	(void) mutex_unlock(&mtx);
583*62224350SCasper H.S. Dik 	(void) door_return((void *)&one, 4, NULL, NULL);
584*62224350SCasper H.S. Dik }
585*62224350SCasper H.S. Dik 
586*62224350SCasper H.S. Dik /*
587*62224350SCasper H.S. Dik  * This function returns the length of the string including exactly
588*62224350SCasper H.S. Dik  * nf fields.
589*62224350SCasper H.S. Dik  */
590*62224350SCasper H.S. Dik static ptrdiff_t
591*62224350SCasper H.S. Dik fieldoff(char *info, int nf)
592*62224350SCasper H.S. Dik {
593*62224350SCasper H.S. Dik 	char *q = info;
594*62224350SCasper H.S. Dik 
595*62224350SCasper H.S. Dik 	while (nf > 0) {
596*62224350SCasper H.S. Dik 		if (IS_ST0[(unsigned char)*q++]) {
597*62224350SCasper H.S. Dik 			if (q[-1] == 0)
598*62224350SCasper H.S. Dik 				break;
599*62224350SCasper H.S. Dik 			nf--;
600*62224350SCasper H.S. Dik 		}
601*62224350SCasper H.S. Dik 	}
602*62224350SCasper H.S. Dik 	return (q - info - 1);
603*62224350SCasper H.S. Dik }
604*62224350SCasper H.S. Dik 
605*62224350SCasper H.S. Dik /*
606*62224350SCasper H.S. Dik  * The buf points into list of \n delimited lines.  We copy it,
607*62224350SCasper H.S. Dik  * removing the newline and adding a \0.
608*62224350SCasper H.S. Dik  */
609*62224350SCasper H.S. Dik static char *
610*62224350SCasper H.S. Dik mystrcpy(char *buf, int len)
611*62224350SCasper H.S. Dik {
612*62224350SCasper H.S. Dik 	char *res = umem_alloc(len, UMEM_NOFAIL);
613*62224350SCasper H.S. Dik 
614*62224350SCasper H.S. Dik 	(void) memcpy(res, buf, len - 1);
615*62224350SCasper H.S. Dik 	res[len - 1] = '\0';
616*62224350SCasper H.S. Dik 	return (res);
617*62224350SCasper H.S. Dik }
618*62224350SCasper H.S. Dik 
619*62224350SCasper H.S. Dik /*
620*62224350SCasper H.S. Dik  * Entry: a single line without the NEWLINE
621*62224350SCasper H.S. Dik  * Return: the package entry with the path determined.
622*62224350SCasper H.S. Dik  */
623*62224350SCasper H.S. Dik static pkgentry_t *
624*62224350SCasper H.S. Dik parse_line(char *buf, int blen, boolean_t full)
625*62224350SCasper H.S. Dik {
626*62224350SCasper H.S. Dik 	char *t;
627*62224350SCasper H.S. Dik 	pkgentry_t *p;
628*62224350SCasper H.S. Dik 	int nfields;
629*62224350SCasper H.S. Dik 
630*62224350SCasper H.S. Dik 	p = umem_cache_alloc(ecache, UMEM_NOFAIL);
631*62224350SCasper H.S. Dik 	buf = p->line = mystrcpy(buf, blen + 1);
632*62224350SCasper H.S. Dik 	p->len = blen + 1;
633*62224350SCasper H.S. Dik 
634*62224350SCasper H.S. Dik 	t = buf;
635*62224350SCasper H.S. Dik 
636*62224350SCasper H.S. Dik 	while (!IS_ST0Q[(unsigned char)*t++])
637*62224350SCasper H.S. Dik 		;
638*62224350SCasper H.S. Dik 
639*62224350SCasper H.S. Dik 	p->pathlen = t - buf - 1;
640*62224350SCasper H.S. Dik 	if (p->pathlen == 0 || p->pathlen >= PATH_MAX) {
641*62224350SCasper H.S. Dik 		progerr("bad entry read in contents file");
642*62224350SCasper H.S. Dik 		logerr("pathname: Unknown");
643*62224350SCasper H.S. Dik 		logerr("problem: unable to read pathname field");
644*62224350SCasper H.S. Dik 		if (one_shot)
645*62224350SCasper H.S. Dik 			exit(2);
646*62224350SCasper H.S. Dik 	}
647*62224350SCasper H.S. Dik 	if (t[-1] == '=')
648*62224350SCasper H.S. Dik 		while (!IS_ST0[(unsigned char)*t++])
649*62224350SCasper H.S. Dik 			;
650*62224350SCasper H.S. Dik 
651*62224350SCasper H.S. Dik 	/* Partial as found in the "-" entries for log */
652*62224350SCasper H.S. Dik 	if (t[-1] == '\0') {
653*62224350SCasper H.S. Dik 		if (full)
654*62224350SCasper H.S. Dik 			goto badline;
655*62224350SCasper H.S. Dik 
656*62224350SCasper H.S. Dik 		p->pkgoff = -1;
657*62224350SCasper H.S. Dik 		return (p);
658*62224350SCasper H.S. Dik 	}
659*62224350SCasper H.S. Dik 
660*62224350SCasper H.S. Dik 	switch (*t) {
661*62224350SCasper H.S. Dik 	case '?':
662*62224350SCasper H.S. Dik 		nfields = 0;
663*62224350SCasper H.S. Dik 		break;
664*62224350SCasper H.S. Dik 	case 's':
665*62224350SCasper H.S. Dik 	case 'l':
666*62224350SCasper H.S. Dik 		/* Fields: class */
667*62224350SCasper H.S. Dik 		nfields = 1;
668*62224350SCasper H.S. Dik 		break;
669*62224350SCasper H.S. Dik 	case 'p':
670*62224350SCasper H.S. Dik 	case 'x':
671*62224350SCasper H.S. Dik 	case 'd':
672*62224350SCasper H.S. Dik 		/* class mode owner group */
673*62224350SCasper H.S. Dik 		nfields = 4;
674*62224350SCasper H.S. Dik 		break;
675*62224350SCasper H.S. Dik 	case 'f':
676*62224350SCasper H.S. Dik 	case 'e':
677*62224350SCasper H.S. Dik 	case 'v':
678*62224350SCasper H.S. Dik 		/* class mode owner group size csum time */
679*62224350SCasper H.S. Dik 		nfields = 7;
680*62224350SCasper H.S. Dik 		break;
681*62224350SCasper H.S. Dik 	case 'c':
682*62224350SCasper H.S. Dik 	case 'b':
683*62224350SCasper H.S. Dik 		/* class major minor mode owner group */
684*62224350SCasper H.S. Dik 		nfields = 6;
685*62224350SCasper H.S. Dik 		break;
686*62224350SCasper H.S. Dik 	default:
687*62224350SCasper H.S. Dik 		progerr("bad entry read in contents file");
688*62224350SCasper H.S. Dik 		logerr("pathname: %.*s", p->pathlen, p->line);
689*62224350SCasper H.S. Dik 		logerr("problem: unknown ftype");
690*62224350SCasper H.S. Dik 		freeentry(p);
691*62224350SCasper H.S. Dik 		if (one_shot)
692*62224350SCasper H.S. Dik 			exit(2);
693*62224350SCasper H.S. Dik 		return (NULL);
694*62224350SCasper H.S. Dik 	}
695*62224350SCasper H.S. Dik 
696*62224350SCasper H.S. Dik 	p->pkgoff = t + fieldoff(t, nfields + 1) - buf;
697*62224350SCasper H.S. Dik 
698*62224350SCasper H.S. Dik 	if (p->line[p->pkgoff] != '\0' || p->pkgoff == p->len - 1)
699*62224350SCasper H.S. Dik 		return (p);
700*62224350SCasper H.S. Dik 
701*62224350SCasper H.S. Dik badline:
702*62224350SCasper H.S. Dik 	progerr(gettext("bad entry read in contents file"));
703*62224350SCasper H.S. Dik 	logerr(gettext("pathname: Unknown"));
704*62224350SCasper H.S. Dik 	logerr(gettext("problem: unknown ftype"));
705*62224350SCasper H.S. Dik 	freeentry(p);
706*62224350SCasper H.S. Dik 	if (one_shot)
707*62224350SCasper H.S. Dik 		exit(2);
708*62224350SCasper H.S. Dik 	return (NULL);
709*62224350SCasper H.S. Dik }
710*62224350SCasper H.S. Dik 
711*62224350SCasper H.S. Dik static void
712*62224350SCasper H.S. Dik handle_comments(char *buf, int len)
713*62224350SCasper H.S. Dik {
714*62224350SCasper H.S. Dik 	if (cind >= 2)
715*62224350SCasper H.S. Dik 		return;
716*62224350SCasper H.S. Dik 
717*62224350SCasper H.S. Dik 	if (buf[0] != '#')
718*62224350SCasper H.S. Dik 		return;
719*62224350SCasper H.S. Dik 
720*62224350SCasper H.S. Dik 	if (ccmnt[cind] != NULL)
721*62224350SCasper H.S. Dik 		umem_free(ccmnt[cind], strlen(ccmnt[cind]) + 1);
722*62224350SCasper H.S. Dik 	ccmnt[cind] = mystrcpy(buf, len);
723*62224350SCasper H.S. Dik 	cind++;
724*62224350SCasper H.S. Dik }
725*62224350SCasper H.S. Dik 
726*62224350SCasper H.S. Dik static void
727*62224350SCasper H.S. Dik parse_contents(void)
728*62224350SCasper H.S. Dik {
729*62224350SCasper H.S. Dik 	int cnt;
730*62224350SCasper H.S. Dik 	pkgentry_t *ent, *e2;
731*62224350SCasper H.S. Dik 	avl_index_t where;
732*62224350SCasper H.S. Dik 	int num = 0;
733*62224350SCasper H.S. Dik 	struct stat stb;
734*62224350SCasper H.S. Dik 	ptrdiff_t off;
735*62224350SCasper H.S. Dik 	char *p, *q, *map;
736*62224350SCasper H.S. Dik 	pkgentry_t *lastentry = NULL;
737*62224350SCasper H.S. Dik 	int d;
738*62224350SCasper H.S. Dik 	int cntserrs = 0;
739*62224350SCasper H.S. Dik 
740*62224350SCasper H.S. Dik 	cnt = open(CONTENTS, O_RDONLY);
741*62224350SCasper H.S. Dik 
742*62224350SCasper H.S. Dik 	cind = 0;
743*62224350SCasper H.S. Dik 
744*62224350SCasper H.S. Dik 	if (cnt == -1) {
745*62224350SCasper H.S. Dik 		if (errno == ENOENT)
746*62224350SCasper H.S. Dik 			return;
747*62224350SCasper H.S. Dik 		exit(99);
748*62224350SCasper H.S. Dik 	}
749*62224350SCasper H.S. Dik 
750*62224350SCasper H.S. Dik 	if (fstat(cnt, &stb) != 0) {
751*62224350SCasper H.S. Dik 		(void) close(cnt);
752*62224350SCasper H.S. Dik 		exit(99);
753*62224350SCasper H.S. Dik 	}
754*62224350SCasper H.S. Dik 	if (stb.st_size == 0) {
755*62224350SCasper H.S. Dik 		(void) close(cnt);
756*62224350SCasper H.S. Dik 		return;
757*62224350SCasper H.S. Dik 	}
758*62224350SCasper H.S. Dik 
759*62224350SCasper H.S. Dik 	map = mmap(0, stb.st_size, PROT_READ, MAP_PRIVATE, cnt, 0);
760*62224350SCasper H.S. Dik 	(void) close(cnt);
761*62224350SCasper H.S. Dik 	if (map == (char *)-1)
762*62224350SCasper H.S. Dik 		return;
763*62224350SCasper H.S. Dik 
764*62224350SCasper H.S. Dik 	(void) madvise(map, stb.st_size, MADV_WILLNEED);
765*62224350SCasper H.S. Dik 
766*62224350SCasper H.S. Dik 	for (off = 0; off < stb.st_size; off += q - p) {
767*62224350SCasper H.S. Dik 		p = map + off;
768*62224350SCasper H.S. Dik 		q = memchr(p, '\n', stb.st_size - off);
769*62224350SCasper H.S. Dik 		if (q == NULL)
770*62224350SCasper H.S. Dik 			break;
771*62224350SCasper H.S. Dik 
772*62224350SCasper H.S. Dik 		q++;
773*62224350SCasper H.S. Dik 		num++;
774*62224350SCasper H.S. Dik 		if (p[0] == '#' || p[0] == '\n') {
775*62224350SCasper H.S. Dik 			handle_comments(p, q - p);
776*62224350SCasper H.S. Dik 			continue;
777*62224350SCasper H.S. Dik 		}
778*62224350SCasper H.S. Dik 		ent = parse_line(p, q - p - 1, B_TRUE);
779*62224350SCasper H.S. Dik 
780*62224350SCasper H.S. Dik 		if (ent == NULL) {
781*62224350SCasper H.S. Dik 			cntserrs++;
782*62224350SCasper H.S. Dik 			continue;
783*62224350SCasper H.S. Dik 		}
784*62224350SCasper H.S. Dik 
785*62224350SCasper H.S. Dik 		/*
786*62224350SCasper H.S. Dik 		 * We save time by assuming the database is sorted; by
787*62224350SCasper H.S. Dik 		 * using avl_insert_here(), building the tree is nearly free.
788*62224350SCasper H.S. Dik 		 * lastentry always contains the last entry in the AVL tree.
789*62224350SCasper H.S. Dik 		 */
790*62224350SCasper H.S. Dik 		if (lastentry == NULL) {
791*62224350SCasper H.S. Dik 			avl_add(list, ent);
792*62224350SCasper H.S. Dik 			lastentry = ent;
793*62224350SCasper H.S. Dik 		} else if ((d = avlcmp(ent, lastentry)) == 1) {
794*62224350SCasper H.S. Dik 			avl_insert_here(list, ent, lastentry, AVL_AFTER);
795*62224350SCasper H.S. Dik 			lastentry = ent;
796*62224350SCasper H.S. Dik 		} else if (d == 0 ||
797*62224350SCasper H.S. Dik 		    (e2 = avl_find(list, ent, &where)) != NULL) {
798*62224350SCasper H.S. Dik 			/*
799*62224350SCasper H.S. Dik 			 * This can only happen if the contents file is bad;
800*62224350SCasper H.S. Dik 			 * this can, e.g., happen with the old SQL contents DB,
801*62224350SCasper H.S. Dik 			 * it didn't sort properly.  Assume the first one
802*62224350SCasper H.S. Dik 			 * is the correct one, but who knows?
803*62224350SCasper H.S. Dik 			 */
804*62224350SCasper H.S. Dik 			if (d == 0)
805*62224350SCasper H.S. Dik 				e2 = lastentry;
806*62224350SCasper H.S. Dik 			if (strcmp(ent->line, e2->line) != 0) {
807*62224350SCasper H.S. Dik 				progerr(gettext("two entries for %.*s"),
808*62224350SCasper H.S. Dik 				    ent->pathlen, ent->line);
809*62224350SCasper H.S. Dik 				cntserrs++;
810*62224350SCasper H.S. Dik 			}
811*62224350SCasper H.S. Dik 			freeentry(ent);
812*62224350SCasper H.S. Dik 		} else {
813*62224350SCasper H.S. Dik 			/* Out of order: not an error for us, really. */
814*62224350SCasper H.S. Dik 			progerr(gettext("bad read of contents file"));
815*62224350SCasper H.S. Dik 			logerr(gettext("pathname: Unknown"));
816*62224350SCasper H.S. Dik 			logerr(gettext(
817*62224350SCasper H.S. Dik 			    "problem: unable to read pathname field"));
818*62224350SCasper H.S. Dik 			if (one_shot)
819*62224350SCasper H.S. Dik 				exit(2);
820*62224350SCasper H.S. Dik 			avl_insert(list, ent, where);
821*62224350SCasper H.S. Dik 		}
822*62224350SCasper H.S. Dik 	}
823*62224350SCasper H.S. Dik 
824*62224350SCasper H.S. Dik 	cind = 0;
825*62224350SCasper H.S. Dik 
826*62224350SCasper H.S. Dik 	(void) munmap(map, stb.st_size);
827*62224350SCasper H.S. Dik 
828*62224350SCasper H.S. Dik 	/* By default, we ignore bad lines, keep them in a copy. */
829*62224350SCasper H.S. Dik 	if (cntserrs > 0 && stb.st_nlink == 1) {
830*62224350SCasper H.S. Dik 		char bcf[sizeof (BADCONTENTS)];
831*62224350SCasper H.S. Dik 
832*62224350SCasper H.S. Dik 		(void) strcpy(bcf, BADCONTENTS);
833*62224350SCasper H.S. Dik 		if (mktemp(bcf) != NULL) {
834*62224350SCasper H.S. Dik 			(void) link(CONTENTS, bcf);
835*62224350SCasper H.S. Dik 			syslog(LOG_WARNING, "A bad contents file was saved: %s",
836*62224350SCasper H.S. Dik 			    bcf);
837*62224350SCasper H.S. Dik 		}
838*62224350SCasper H.S. Dik 	}
839*62224350SCasper H.S. Dik }
840*62224350SCasper H.S. Dik 
841*62224350SCasper H.S. Dik static int
842*62224350SCasper H.S. Dik parse_log(void)
843*62224350SCasper H.S. Dik {
844*62224350SCasper H.S. Dik 	pkgentry_t *ent, *look;
845*62224350SCasper H.S. Dik 	avl_index_t where;
846*62224350SCasper H.S. Dik 	int num = 0;
847*62224350SCasper H.S. Dik 	int logfd;
848*62224350SCasper H.S. Dik 	struct stat stb;
849*62224350SCasper H.S. Dik 	int mlen = strlen(marker);
850*62224350SCasper H.S. Dik 	off_t realend;
851*62224350SCasper H.S. Dik 	ptrdiff_t off;
852*62224350SCasper H.S. Dik 	char *p, *q, *map;
853*62224350SCasper H.S. Dik 
854*62224350SCasper H.S. Dik 	logfd = open(PKGLOG, O_RDONLY);
855*62224350SCasper H.S. Dik 
856*62224350SCasper H.S. Dik 	if (logfd < 0) {
857*62224350SCasper H.S. Dik 		if (errno == ENOENT)
858*62224350SCasper H.S. Dik 			return (0);
859*62224350SCasper H.S. Dik 		progerr(gettext("cannot read "PKGLOG": %s"), strerror(errno));
860*62224350SCasper H.S. Dik 		exit(2);
861*62224350SCasper H.S. Dik 	}
862*62224350SCasper H.S. Dik 
863*62224350SCasper H.S. Dik 	if (fstat(logfd, &stb) != 0) {
864*62224350SCasper H.S. Dik 		progerr(gettext("cannot stat "PKGLOG": %s"), strerror(errno));
865*62224350SCasper H.S. Dik 		exit(2);
866*62224350SCasper H.S. Dik 	}
867*62224350SCasper H.S. Dik 
868*62224350SCasper H.S. Dik 	if (stb.st_size == 0) {
869*62224350SCasper H.S. Dik 		(void) close(logfd);
870*62224350SCasper H.S. Dik 		/* Force pkgdump && remove of the logfile. */
871*62224350SCasper H.S. Dik 		return (1);
872*62224350SCasper H.S. Dik 	}
873*62224350SCasper H.S. Dik 
874*62224350SCasper H.S. Dik 	/* We're making sure that we end with a NUL or more for strstr() */
875*62224350SCasper H.S. Dik 	map = mmap(0, stb.st_size + getpagesize(), PROT_READ, MAP_PRIVATE,
876*62224350SCasper H.S. Dik 	    logfd, 0);
877*62224350SCasper H.S. Dik 	(void) close(logfd);
878*62224350SCasper H.S. Dik 	if (map == (char *)-1) {
879*62224350SCasper H.S. Dik 		progerr(gettext("Cannot mmap the "PKGLOG": %s"),
880*62224350SCasper H.S. Dik 		    strerror(errno));
881*62224350SCasper H.S. Dik 		exit(2);
882*62224350SCasper H.S. Dik 	}
883*62224350SCasper H.S. Dik 
884*62224350SCasper H.S. Dik 	cind = 0;
885*62224350SCasper H.S. Dik 
886*62224350SCasper H.S. Dik 	realend = stb.st_size;
887*62224350SCasper H.S. Dik 
888*62224350SCasper H.S. Dik 	if (memcmp(map + realend - mlen, marker, mlen) != 0) {
889*62224350SCasper H.S. Dik 		progerr(gettext(PKGLOG" is not complete"));
890*62224350SCasper H.S. Dik 
891*62224350SCasper H.S. Dik 		realend = 0;
892*62224350SCasper H.S. Dik 		for (p = map; q = strstr(p, marker); ) {
893*62224350SCasper H.S. Dik 			if (q == map || q[-1] == '\n')
894*62224350SCasper H.S. Dik 				realend = q - map + mlen;
895*62224350SCasper H.S. Dik 			p = q + mlen;
896*62224350SCasper H.S. Dik 		}
897*62224350SCasper H.S. Dik 		progerr(gettext("Ignoring %ld bytes from log"),
898*62224350SCasper H.S. Dik 		    (long)(stb.st_size - realend));
899*62224350SCasper H.S. Dik 	}
900*62224350SCasper H.S. Dik 
901*62224350SCasper H.S. Dik 	for (off = 0; off < realend; off += q - p) {
902*62224350SCasper H.S. Dik 		p = map + off;
903*62224350SCasper H.S. Dik 		q = memchr(p, '\n', realend - off);
904*62224350SCasper H.S. Dik 		if (q == NULL)
905*62224350SCasper H.S. Dik 			break;
906*62224350SCasper H.S. Dik 
907*62224350SCasper H.S. Dik 		q++;
908*62224350SCasper H.S. Dik 		num++;
909*62224350SCasper H.S. Dik 		if (p[0] == '#' || p[0] == '\n') {
910*62224350SCasper H.S. Dik 			if (memcmp(marker, p, mlen) == 0)
911*62224350SCasper H.S. Dik 				cind = 0;
912*62224350SCasper H.S. Dik 			else
913*62224350SCasper H.S. Dik 				handle_comments(p, q - p);
914*62224350SCasper H.S. Dik 			continue;
915*62224350SCasper H.S. Dik 		}
916*62224350SCasper H.S. Dik 
917*62224350SCasper H.S. Dik 		ent = parse_line(p + 1, q - (p + 1) - 1, p[0] != '-');
918*62224350SCasper H.S. Dik 		if (ent == NULL)
919*62224350SCasper H.S. Dik 			continue;
920*62224350SCasper H.S. Dik 		look = avl_find(list, ent, &where);
921*62224350SCasper H.S. Dik 		/*
922*62224350SCasper H.S. Dik 		 * The log can be replayed; so any value of "look" is
923*62224350SCasper H.S. Dik 		 * not unexpected.
924*62224350SCasper H.S. Dik 		 */
925*62224350SCasper H.S. Dik 		switch (p[0]) {
926*62224350SCasper H.S. Dik 		case '+':
927*62224350SCasper H.S. Dik 		case '=':
928*62224350SCasper H.S. Dik 			if (look != NULL)
929*62224350SCasper H.S. Dik 				swapentry(look, ent);
930*62224350SCasper H.S. Dik 			else
931*62224350SCasper H.S. Dik 				avl_insert(list, ent, where);
932*62224350SCasper H.S. Dik 			break;
933*62224350SCasper H.S. Dik 		case '-':
934*62224350SCasper H.S. Dik 			if (look != NULL) {
935*62224350SCasper H.S. Dik 				avl_remove(list, look);
936*62224350SCasper H.S. Dik 				freeentry(look);
937*62224350SCasper H.S. Dik 			}
938*62224350SCasper H.S. Dik 			freeentry(ent);
939*62224350SCasper H.S. Dik 			break;
940*62224350SCasper H.S. Dik 		default:
941*62224350SCasper H.S. Dik 			freeentry(ent);
942*62224350SCasper H.S. Dik 			progerr(gettext("log %d: bad line"), num);
943*62224350SCasper H.S. Dik 			break;
944*62224350SCasper H.S. Dik 		}
945*62224350SCasper H.S. Dik 	}
946*62224350SCasper H.S. Dik 	(void) munmap(map, stb.st_size);
947*62224350SCasper H.S. Dik 
948*62224350SCasper H.S. Dik 	/* Force pkgdump && remove of the logfile if there are no valid mods. */
949*62224350SCasper H.S. Dik 	return (num == 0 ? 1 : num);
950*62224350SCasper H.S. Dik }
951*62224350SCasper H.S. Dik 
952*62224350SCasper H.S. Dik static char *
953*62224350SCasper H.S. Dik file_find(pkgfilter_t *cmd, int *len)
954*62224350SCasper H.S. Dik {
955*62224350SCasper H.S. Dik 	pkgentry_t p;
956*62224350SCasper H.S. Dik 	pkgentry_t *look;
957*62224350SCasper H.S. Dik 
958*62224350SCasper H.S. Dik 	p.line = cmd->buf;
959*62224350SCasper H.S. Dik 	p.pathlen = cmd->len;
960*62224350SCasper H.S. Dik 
961*62224350SCasper H.S. Dik 	look = avl_find(list, &p, NULL);
962*62224350SCasper H.S. Dik 
963*62224350SCasper H.S. Dik 	if (look == NULL)
964*62224350SCasper H.S. Dik 		return (NULL);
965*62224350SCasper H.S. Dik 
966*62224350SCasper H.S. Dik 	*len = look->len;
967*62224350SCasper H.S. Dik 	return (look->line);
968*62224350SCasper H.S. Dik }
969*62224350SCasper H.S. Dik 
970*62224350SCasper H.S. Dik static void
971*62224350SCasper H.S. Dik pkgdump(void)
972*62224350SCasper H.S. Dik {
973*62224350SCasper H.S. Dik 	FILE *cnts;
974*62224350SCasper H.S. Dik 	int err = 0;
975*62224350SCasper H.S. Dik 	pkgentry_t *p;
976*62224350SCasper H.S. Dik 
977*62224350SCasper H.S. Dik 	if (read_only)
978*62224350SCasper H.S. Dik 		return;
979*62224350SCasper H.S. Dik 
980*62224350SCasper H.S. Dik 	/* We cannot dump when the current transaction is not complete. */
981*62224350SCasper H.S. Dik 	if (sync_needed)
982*62224350SCasper H.S. Dik 		return;
983*62224350SCasper H.S. Dik 
984*62224350SCasper H.S. Dik 	cnts = fopen(TCONTENTS, "w");
985*62224350SCasper H.S. Dik 
986*62224350SCasper H.S. Dik 	if (cnts == NULL)
987*62224350SCasper H.S. Dik 		exit(99);
988*62224350SCasper H.S. Dik 
989*62224350SCasper H.S. Dik 	for (p = avl_first(list); p != NULL; p = AVL_NEXT(list, p)) {
990*62224350SCasper H.S. Dik 		if (fprintf(cnts, "%s\n", p->line) < 0)
991*62224350SCasper H.S. Dik 			err++;
992*62224350SCasper H.S. Dik 	}
993*62224350SCasper H.S. Dik 
994*62224350SCasper H.S. Dik 	if (ccmnt[0] != NULL)
995*62224350SCasper H.S. Dik 		(void) fprintf(cnts, "%s\n", ccmnt[0]);
996*62224350SCasper H.S. Dik 	if (ccmnt[1] != NULL)
997*62224350SCasper H.S. Dik 		(void) fprintf(cnts, "%s\n", ccmnt[1]);
998*62224350SCasper H.S. Dik 
999*62224350SCasper H.S. Dik 	if (err != 0 || fflush(cnts) == EOF || fsync(fileno(cnts)) != 0 ||
1000*62224350SCasper H.S. Dik 	    fclose(cnts) == EOF || rename(TCONTENTS, CONTENTS) != 0) {
1001*62224350SCasper H.S. Dik 		err++;
1002*62224350SCasper H.S. Dik 	}
1003*62224350SCasper H.S. Dik 
1004*62224350SCasper H.S. Dik 	if (err != 0) {
1005*62224350SCasper H.S. Dik 		progerr("cannot rewrite the contents file");
1006*62224350SCasper H.S. Dik 		exit(2);
1007*62224350SCasper H.S. Dik 	}
1008*62224350SCasper H.S. Dik 
1009*62224350SCasper H.S. Dik 	(void) fclose(log);
1010*62224350SCasper H.S. Dik 	(void) unlink(PKGLOG);
1011*62224350SCasper H.S. Dik 	log = NULL;
1012*62224350SCasper H.S. Dik 	ndumps++;
1013*62224350SCasper H.S. Dik 	logcount = 0;
1014*62224350SCasper H.S. Dik }
1015*62224350SCasper H.S. Dik 
1016*62224350SCasper H.S. Dik static void
1017*62224350SCasper H.S. Dik freeentry(pkgentry_t *p)
1018*62224350SCasper H.S. Dik {
1019*62224350SCasper H.S. Dik 	umem_free(p->line, p->len);
1020*62224350SCasper H.S. Dik 	umem_cache_free(ecache, p);
1021*62224350SCasper H.S. Dik }
1022*62224350SCasper H.S. Dik 
1023*62224350SCasper H.S. Dik static void
1024*62224350SCasper H.S. Dik swapentry(pkgentry_t *cur, pkgentry_t *new)
1025*62224350SCasper H.S. Dik {
1026*62224350SCasper H.S. Dik 	if (cur->len == new->len &&
1027*62224350SCasper H.S. Dik 	    strcmp(cur->line + cur->pathlen,
1028*62224350SCasper H.S. Dik 	    new->line + new->pathlen) == 0) {
1029*62224350SCasper H.S. Dik 		suppressed++;
1030*62224350SCasper H.S. Dik 		freeentry(new);
1031*62224350SCasper H.S. Dik 		return;
1032*62224350SCasper H.S. Dik 	}
1033*62224350SCasper H.S. Dik 
1034*62224350SCasper H.S. Dik 	/* Free old line */
1035*62224350SCasper H.S. Dik 	umem_free(cur->line, cur->len);
1036*62224350SCasper H.S. Dik 
1037*62224350SCasper H.S. Dik 	/* Copy new value: pathlen is the same and avl is kept */
1038*62224350SCasper H.S. Dik 	cur->line = new->line;
1039*62224350SCasper H.S. Dik 	cur->len = new->len;
1040*62224350SCasper H.S. Dik 	cur->pkgoff = new->pkgoff;
1041*62224350SCasper H.S. Dik 
1042*62224350SCasper H.S. Dik 	umem_cache_free(ecache, new);
1043*62224350SCasper H.S. Dik }
1044*62224350SCasper H.S. Dik 
1045*62224350SCasper H.S. Dik static int
1046*62224350SCasper H.S. Dik logentry(char type, pkgentry_t *p)
1047*62224350SCasper H.S. Dik {
1048*62224350SCasper H.S. Dik 	int len;
1049*62224350SCasper H.S. Dik 
1050*62224350SCasper H.S. Dik 	if (type == '-')
1051*62224350SCasper H.S. Dik 		len = fprintf(log, "-%.*s\n", p->pathlen, p->line);
1052*62224350SCasper H.S. Dik 	else
1053*62224350SCasper H.S. Dik 		len = fprintf(log, "%c%s\n", type, p->line);
1054*62224350SCasper H.S. Dik 
1055*62224350SCasper H.S. Dik 	loglines++;
1056*62224350SCasper H.S. Dik 	if (len < 0) {
1057*62224350SCasper H.S. Dik 		logerrcnt++;
1058*62224350SCasper H.S. Dik 		return (-1);
1059*62224350SCasper H.S. Dik 	}
1060*62224350SCasper H.S. Dik 	logcount += len;
1061*62224350SCasper H.S. Dik 	return (0);
1062*62224350SCasper H.S. Dik }
1063*62224350SCasper H.S. Dik 
1064*62224350SCasper H.S. Dik static int
1065*62224350SCasper H.S. Dik logflush(void)
1066*62224350SCasper H.S. Dik {
1067*62224350SCasper H.S. Dik 	int len;
1068*62224350SCasper H.S. Dik 	static int lastflush;
1069*62224350SCasper H.S. Dik 
1070*62224350SCasper H.S. Dik 	if (log == NULL)
1071*62224350SCasper H.S. Dik 		return (0);
1072*62224350SCasper H.S. Dik 
1073*62224350SCasper H.S. Dik 	if (lastflush == logcount)
1074*62224350SCasper H.S. Dik 		return (0);
1075*62224350SCasper H.S. Dik 
1076*62224350SCasper H.S. Dik 	if (cind == 2) {
1077*62224350SCasper H.S. Dik 		(void) fprintf(log, "%s\n", ccmnt[0]);
1078*62224350SCasper H.S. Dik 		(void) fprintf(log, "%s\n", ccmnt[1]);
1079*62224350SCasper H.S. Dik 		cind = 0;
1080*62224350SCasper H.S. Dik 	}
1081*62224350SCasper H.S. Dik 
1082*62224350SCasper H.S. Dik 	/*
1083*62224350SCasper H.S. Dik 	 * When using zfs, if the mark is there, then so is the rest before
1084*62224350SCasper H.S. Dik 	 * it.  But with ufs, we need to flush twice.
1085*62224350SCasper H.S. Dik 	 */
1086*62224350SCasper H.S. Dik 	if (flushbeforemark) {
1087*62224350SCasper H.S. Dik 		if (fflush(log) == EOF)
1088*62224350SCasper H.S. Dik 			logerrcnt++;
1089*62224350SCasper H.S. Dik 	}
1090*62224350SCasper H.S. Dik 	/* Anything before the last marker found in the log will be valid */
1091*62224350SCasper H.S. Dik 	len = fprintf(log, "%s", marker);
1092*62224350SCasper H.S. Dik 	if (len < 0)
1093*62224350SCasper H.S. Dik 		logerrcnt++;
1094*62224350SCasper H.S. Dik 	else
1095*62224350SCasper H.S. Dik 		logcount += len;
1096*62224350SCasper H.S. Dik 
1097*62224350SCasper H.S. Dik 	if (fflush(log) == EOF)
1098*62224350SCasper H.S. Dik 		logerrcnt++;
1099*62224350SCasper H.S. Dik 
1100*62224350SCasper H.S. Dik 	sync_needed = B_FALSE;
1101*62224350SCasper H.S. Dik 
1102*62224350SCasper H.S. Dik 	if (logerrcnt > 0 || logcount > MAXLOGFILESIZE)
1103*62224350SCasper H.S. Dik 		pkgdump();
1104*62224350SCasper H.S. Dik 
1105*62224350SCasper H.S. Dik 	if (logerrcnt > 0)
1106*62224350SCasper H.S. Dik 		return (-1);
1107*62224350SCasper H.S. Dik 
1108*62224350SCasper H.S. Dik 	lastflush = logcount;
1109*62224350SCasper H.S. Dik 
1110*62224350SCasper H.S. Dik 	return (0);
1111*62224350SCasper H.S. Dik }
1112*62224350SCasper H.S. Dik 
1113*62224350SCasper H.S. Dik static int
1114*62224350SCasper H.S. Dik avlcmp(const void *ca, const void *cb)
1115*62224350SCasper H.S. Dik {
1116*62224350SCasper H.S. Dik 	const pkgentry_t *a = ca;
1117*62224350SCasper H.S. Dik 	const pkgentry_t *b = cb;
1118*62224350SCasper H.S. Dik 	int i = memcmp(a->line, b->line,
1119*62224350SCasper H.S. Dik 	    a->pathlen > b->pathlen ? b->pathlen : a->pathlen);
1120*62224350SCasper H.S. Dik 
1121*62224350SCasper H.S. Dik 	if (i < 0)
1122*62224350SCasper H.S. Dik 		return (-1);
1123*62224350SCasper H.S. Dik 	else if (i > 0)
1124*62224350SCasper H.S. Dik 		return (1);
1125*62224350SCasper H.S. Dik 	else if (a->pathlen == b->pathlen)
1126*62224350SCasper H.S. Dik 		return (0);
1127*62224350SCasper H.S. Dik 	else if (a->pathlen > b->pathlen)
1128*62224350SCasper H.S. Dik 		return (1);
1129*62224350SCasper H.S. Dik 	else
1130*62224350SCasper H.S. Dik 		return (-1);
1131*62224350SCasper H.S. Dik }
1132*62224350SCasper H.S. Dik 
1133*62224350SCasper H.S. Dik /*
1134*62224350SCasper H.S. Dik  * Returns:
1135*62224350SCasper H.S. Dik  *	0 - if we can get the lock
1136*62224350SCasper H.S. Dik  *	-1 - we can't lock
1137*62224350SCasper H.S. Dik  */
1138*62224350SCasper H.S. Dik 
1139*62224350SCasper H.S. Dik static int
1140*62224350SCasper H.S. Dik establish_lock(char *lock)
1141*62224350SCasper H.S. Dik {
1142*62224350SCasper H.S. Dik 	int fd = open(lock, O_RDWR|O_CREAT, 0644);
1143*62224350SCasper H.S. Dik 	int i;
1144*62224350SCasper H.S. Dik 
1145*62224350SCasper H.S. Dik 	if (fd < 0)
1146*62224350SCasper H.S. Dik 		return (-1);
1147*62224350SCasper H.S. Dik 
1148*62224350SCasper H.S. Dik 	for (i = 0; i < 5; i++) {
1149*62224350SCasper H.S. Dik 		if (lockf(fd, F_TLOCK, 0) == 0)
1150*62224350SCasper H.S. Dik 			return (0);
1151*62224350SCasper H.S. Dik 		(void) sleep(1);
1152*62224350SCasper H.S. Dik 	}
1153*62224350SCasper H.S. Dik 
1154*62224350SCasper H.S. Dik 	(void) close(fd);
1155*62224350SCasper H.S. Dik 	return (-1);
1156*62224350SCasper H.S. Dik }
1157*62224350SCasper H.S. Dik 
1158*62224350SCasper H.S. Dik static int
1159*62224350SCasper H.S. Dik no_memory_abort(void)
1160*62224350SCasper H.S. Dik {
1161*62224350SCasper H.S. Dik 	return (UMEM_CALLBACK_EXIT(99));
1162*62224350SCasper H.S. Dik }
1163*62224350SCasper H.S. Dik 
1164*62224350SCasper H.S. Dik /*
1165*62224350SCasper H.S. Dik  * Dump a part of the contents file in a pipe; grep for the "filter".
1166*62224350SCasper H.S. Dik  * It doesn't matter if we return too much.
1167*62224350SCasper H.S. Dik  */
1168*62224350SCasper H.S. Dik 
1169*62224350SCasper H.S. Dik static void *
1170*62224350SCasper H.S. Dik thr_pkgfilter(void *v)
1171*62224350SCasper H.S. Dik {
1172*62224350SCasper H.S. Dik 	pkgfilter_t *pf = v;
1173*62224350SCasper H.S. Dik 	pkgentry_t *p;
1174*62224350SCasper H.S. Dik 	int nums[2];
1175*62224350SCasper H.S. Dik 	FILE *cnts;
1176*62224350SCasper H.S. Dik 
1177*62224350SCasper H.S. Dik 	cnts = fdopen(pf->cmd, "w");
1178*62224350SCasper H.S. Dik 	if (cnts == NULL)
1179*62224350SCasper H.S. Dik 		goto free;
1180*62224350SCasper H.S. Dik 
1181*62224350SCasper H.S. Dik 	/* Remove wild card: don't care about extra matches */
1182*62224350SCasper H.S. Dik 	if (pf->len > 0) {
1183*62224350SCasper H.S. Dik 		char *p;
1184*62224350SCasper H.S. Dik 
1185*62224350SCasper H.S. Dik 		for (p = pf->buf; *p; p++) {
1186*62224350SCasper H.S. Dik 			if (*p == '*') {
1187*62224350SCasper H.S. Dik 				*p = 0;
1188*62224350SCasper H.S. Dik 				break;
1189*62224350SCasper H.S. Dik 			}
1190*62224350SCasper H.S. Dik 		}
1191*62224350SCasper H.S. Dik 	}
1192*62224350SCasper H.S. Dik 
1193*62224350SCasper H.S. Dik 	/* Disable modifications while the filter is running */
1194*62224350SCasper H.S. Dik 	(void) mutex_lock(&mtx);
1195*62224350SCasper H.S. Dik 	write_locked++;
1196*62224350SCasper H.S. Dik 	(void) mutex_unlock(&mtx);
1197*62224350SCasper H.S. Dik 	/*
1198*62224350SCasper H.S. Dik 	 * The protocol for the contents file for the clients:
1199*62224350SCasper H.S. Dik 	 * <int:len><int:pathlen><line + 0>
1200*62224350SCasper H.S. Dik 	 */
1201*62224350SCasper H.S. Dik 
1202*62224350SCasper H.S. Dik 	for (p = avl_first(list); p != NULL; p = AVL_NEXT(list, p)) {
1203*62224350SCasper H.S. Dik 		if (pf->len > 0 && strstr(p->line, pf->buf) == NULL)
1204*62224350SCasper H.S. Dik 			continue;
1205*62224350SCasper H.S. Dik 
1206*62224350SCasper H.S. Dik 		nums[0] = p->len;
1207*62224350SCasper H.S. Dik 		nums[1] = p->pathlen;
1208*62224350SCasper H.S. Dik 		if (fwrite(nums, sizeof (int), 2, cnts) != 2)
1209*62224350SCasper H.S. Dik 			break;
1210*62224350SCasper H.S. Dik 		if (fwrite(p->line, 1, p->len, cnts) != p->len)
1211*62224350SCasper H.S. Dik 			break;
1212*62224350SCasper H.S. Dik 	}
1213*62224350SCasper H.S. Dik 
1214*62224350SCasper H.S. Dik 	(void) mutex_lock(&mtx);
1215*62224350SCasper H.S. Dik 	lastcall = gethrtime();
1216*62224350SCasper H.S. Dik 	write_locked--;
1217*62224350SCasper H.S. Dik 	(void) cond_broadcast(&cv);
1218*62224350SCasper H.S. Dik 	(void) mutex_unlock(&mtx);
1219*62224350SCasper H.S. Dik 	(void) fclose(cnts);
1220*62224350SCasper H.S. Dik 
1221*62224350SCasper H.S. Dik free:
1222*62224350SCasper H.S. Dik 	umem_free(pf, sizeof (pkgfilter_t) + pf->len);
1223*62224350SCasper H.S. Dik 	return (NULL);
1224*62224350SCasper H.S. Dik }
1225*62224350SCasper H.S. Dik 
1226*62224350SCasper H.S. Dik static hrtime_t
1227*62224350SCasper H.S. Dik time_since_(hrtime_t last)
1228*62224350SCasper H.S. Dik {
1229*62224350SCasper H.S. Dik 	return (gethrtime() - last);
1230*62224350SCasper H.S. Dik }
1231*62224350SCasper H.S. Dik 
1232*62224350SCasper H.S. Dik static void
1233*62224350SCasper H.S. Dik my_cond_reltimedwait(hrtime_t delta, int sec)
1234*62224350SCasper H.S. Dik {
1235*62224350SCasper H.S. Dik 	hrtime_t wait = sec * LLNANOSEC - delta;
1236*62224350SCasper H.S. Dik 	timestruc_t waitfor;
1237*62224350SCasper H.S. Dik 
1238*62224350SCasper H.S. Dik 	waitfor.tv_nsec = wait % LLNANOSEC;
1239*62224350SCasper H.S. Dik 	waitfor.tv_sec = wait / LLNANOSEC;
1240*62224350SCasper H.S. Dik 	(void) cond_reltimedwait(&cv, &mtx, &waitfor);
1241*62224350SCasper H.S. Dik }
1242*62224350SCasper H.S. Dik 
1243*62224350SCasper H.S. Dik static int
1244*62224350SCasper H.S. Dik pkgfilter(pkgfilter_t *pf, door_desc_t *dp)
1245*62224350SCasper H.S. Dik {
1246*62224350SCasper H.S. Dik 
1247*62224350SCasper H.S. Dik 	int p[2];
1248*62224350SCasper H.S. Dik 	thread_t tid;
1249*62224350SCasper H.S. Dik 	pkgfilter_t *cpf;
1250*62224350SCasper H.S. Dik 
1251*62224350SCasper H.S. Dik 	if (pipe(p) != 0)
1252*62224350SCasper H.S. Dik 		return (-1);
1253*62224350SCasper H.S. Dik 
1254*62224350SCasper H.S. Dik 	cpf = umem_alloc(sizeof (pkgfilter_t) + pf->len, UMEM_NOFAIL);
1255*62224350SCasper H.S. Dik 
1256*62224350SCasper H.S. Dik 	(void) memcpy(cpf, pf, sizeof (pkgfilter_t) + pf->len);
1257*62224350SCasper H.S. Dik 
1258*62224350SCasper H.S. Dik 	/* Copy the file descriptor in the command field */
1259*62224350SCasper H.S. Dik 	cpf->cmd = p[1];
1260*62224350SCasper H.S. Dik 
1261*62224350SCasper H.S. Dik 	if (thr_create(NULL, NULL, thr_pkgfilter, cpf, THR_DETACHED,
1262*62224350SCasper H.S. Dik 	    &tid) != 0) {
1263*62224350SCasper H.S. Dik 		(void) close(p[0]);
1264*62224350SCasper H.S. Dik 		(void) close(p[1]);
1265*62224350SCasper H.S. Dik 		umem_free(cpf, sizeof (pkgfilter_t) + pf->len);
1266*62224350SCasper H.S. Dik 		return (-1);
1267*62224350SCasper H.S. Dik 	}
1268*62224350SCasper H.S. Dik 	(void) memset(dp, 0, sizeof (*dp));
1269*62224350SCasper H.S. Dik 	dp->d_attributes = DOOR_DESCRIPTOR | DOOR_RELEASE;
1270*62224350SCasper H.S. Dik 	dp->d_data.d_desc.d_descriptor = p[0];
1271*62224350SCasper H.S. Dik 
1272*62224350SCasper H.S. Dik 	return (0);
1273*62224350SCasper H.S. Dik }
1274*62224350SCasper H.S. Dik 
1275*62224350SCasper H.S. Dik static int
1276*62224350SCasper H.S. Dik pkgaddlines(pkgfilter_t *pf)
1277*62224350SCasper H.S. Dik {
1278*62224350SCasper H.S. Dik 	char *map = pf->buf;
1279*62224350SCasper H.S. Dik 	int len = pf->len;
1280*62224350SCasper H.S. Dik 	int off;
1281*62224350SCasper H.S. Dik 	pkgentry_t *ent, *look;
1282*62224350SCasper H.S. Dik 	avl_index_t where;
1283*62224350SCasper H.S. Dik 	char *q, *p;
1284*62224350SCasper H.S. Dik 	char c;
1285*62224350SCasper H.S. Dik 	int r = 0;
1286*62224350SCasper H.S. Dik 
1287*62224350SCasper H.S. Dik 	if (log == NULL) {
1288*62224350SCasper H.S. Dik 		log = fopen(PKGLOG, "w");
1289*62224350SCasper H.S. Dik 		if (log == NULL)
1290*62224350SCasper H.S. Dik 			return (-1);
1291*62224350SCasper H.S. Dik 	}
1292*62224350SCasper H.S. Dik 
1293*62224350SCasper H.S. Dik 	for (off = 0; off < len; off += q - p) {
1294*62224350SCasper H.S. Dik 		p = map + off;
1295*62224350SCasper H.S. Dik 		q = memchr(p, '\n', len - off);
1296*62224350SCasper H.S. Dik 
1297*62224350SCasper H.S. Dik 		if (q == NULL)
1298*62224350SCasper H.S. Dik 			break;
1299*62224350SCasper H.S. Dik 
1300*62224350SCasper H.S. Dik 		q++;
1301*62224350SCasper H.S. Dik 
1302*62224350SCasper H.S. Dik 		if (p[0] == '#' || p[0] == '\n') {
1303*62224350SCasper H.S. Dik 			handle_comments(p, q - p);
1304*62224350SCasper H.S. Dik 			continue;
1305*62224350SCasper H.S. Dik 		}
1306*62224350SCasper H.S. Dik 
1307*62224350SCasper H.S. Dik 		if (*p == '-')
1308*62224350SCasper H.S. Dik 			ent = parse_line(p + 1, q - (p + 1) - 1, B_FALSE);
1309*62224350SCasper H.S. Dik 		else
1310*62224350SCasper H.S. Dik 			ent = parse_line(p, q - p - 1, B_TRUE);
1311*62224350SCasper H.S. Dik 
1312*62224350SCasper H.S. Dik 		if (ent == NULL) {
1313*62224350SCasper H.S. Dik 			r++;
1314*62224350SCasper H.S. Dik 			continue;
1315*62224350SCasper H.S. Dik 		}
1316*62224350SCasper H.S. Dik 
1317*62224350SCasper H.S. Dik 		look = avl_find(list, ent, &where);
1318*62224350SCasper H.S. Dik 		if (look != NULL) {
1319*62224350SCasper H.S. Dik 			c = *p == '-' ? '-' : '=';
1320*62224350SCasper H.S. Dik 			if (c == '=') {
1321*62224350SCasper H.S. Dik 				swapentry(look, ent);
1322*62224350SCasper H.S. Dik 				ent = look;
1323*62224350SCasper H.S. Dik 			} else {
1324*62224350SCasper H.S. Dik 				avl_remove(list, look);
1325*62224350SCasper H.S. Dik 				freeentry(look);
1326*62224350SCasper H.S. Dik 			}
1327*62224350SCasper H.S. Dik 		} else if (*p == '-') {
1328*62224350SCasper H.S. Dik 			/* Remove something which isn't there: no-op */
1329*62224350SCasper H.S. Dik 			freeentry(ent);
1330*62224350SCasper H.S. Dik 			continue;
1331*62224350SCasper H.S. Dik 		} else {
1332*62224350SCasper H.S. Dik 			avl_insert(list, ent, where);
1333*62224350SCasper H.S. Dik 			c = '+';
1334*62224350SCasper H.S. Dik 		}
1335*62224350SCasper H.S. Dik 
1336*62224350SCasper H.S. Dik 		sync_needed = B_TRUE;
1337*62224350SCasper H.S. Dik 		r += logentry(c, ent);
1338*62224350SCasper H.S. Dik 		if (c == '-')
1339*62224350SCasper H.S. Dik 			freeentry(ent);
1340*62224350SCasper H.S. Dik 	}
1341*62224350SCasper H.S. Dik 
1342*62224350SCasper H.S. Dik 	return (r);
1343*62224350SCasper H.S. Dik }
1344*62224350SCasper H.S. Dik 
1345*62224350SCasper H.S. Dik static void
1346*62224350SCasper H.S. Dik finish(void)
1347*62224350SCasper H.S. Dik {
1348*62224350SCasper H.S. Dik 	if (verbose) {
1349*62224350SCasper H.S. Dik 		syslog(LOG_DEBUG,
1350*62224350SCasper H.S. Dik 		    "finished: calls %d, pkgdumps %d, loglines %d "
1351*62224350SCasper H.S. Dik 		    "(suppressed %d)\n",
1352*62224350SCasper H.S. Dik 		    ncalls, ndumps, loglines, suppressed);
1353*62224350SCasper H.S. Dik 	}
1354*62224350SCasper H.S. Dik 	(void) fdetach(door);
1355*62224350SCasper H.S. Dik 	if (read_only)
1356*62224350SCasper H.S. Dik 		(void) unlink(door);
1357*62224350SCasper H.S. Dik }
1358*62224350SCasper H.S. Dik 
1359*62224350SCasper H.S. Dik /*
1360*62224350SCasper H.S. Dik  * Tell the wait thread to wake up and quit.
1361*62224350SCasper H.S. Dik  */
1362*62224350SCasper H.S. Dik /* ARGSUSED */
1363*62224350SCasper H.S. Dik static void
1364*62224350SCasper H.S. Dik signal_handler(int sig)
1365*62224350SCasper H.S. Dik {
1366*62224350SCasper H.S. Dik 	if (read_only)
1367*62224350SCasper H.S. Dik 		exit(0);
1368*62224350SCasper H.S. Dik 	want_to_quit = 1;
1369*62224350SCasper H.S. Dik 	(void) cond_broadcast(&cv);
1370*62224350SCasper H.S. Dik }
1371