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