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