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 */
120typedef 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
128static char IS_ST0[256];
129static char IS_ST0Q[256];
130
131static void pkg_door_srv(void *, char *, size_t, door_desc_t *, uint_t);
132static char *file_find(pkgfilter_t *, int *);
133static void parse_contents(void);
134static int parse_log(void);
135static void pkgdump(void);
136static int logflush(void);
137static int avlcmp(const void *, const void *);
138static void freeentry(pkgentry_t *);
139static void swapentry(pkgentry_t *, pkgentry_t *);
140static int establish_lock(char *);
141static int no_memory_abort(void);
142static int pkgfilter(pkgfilter_t *, door_desc_t *);
143static int pkgaddlines(pkgfilter_t *);
144static void finish(void);
145static void signal_handler(int);
146static void my_cond_reltimedwait(hrtime_t, int);
147static 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
157static FILE *log;
158static char *door = PKGDOOR;
159
160static avl_tree_t listp, *list = &listp;
161
162/* Keep the "the last command modified the contents file ... */
163static char *ccmnt[2];
164static int cind = 0;
165
166static mutex_t mtx = DEFAULTMUTEX;
167static cond_t cv = DEFAULTCV;
168
169static int flushbeforemark = 1;
170static int logerrcnt = 0;
171static int loglines = 0;
172static int suppressed = 0;
173static int logcount;
174static int ndumps;
175static int ncalls;
176static int changes;
177static hrtime_t lastchange;
178static hrtime_t lastcall;
179static volatile int want_to_quit;
180static boolean_t read_only = B_FALSE;
181static boolean_t permanent = B_FALSE;
182static boolean_t one_shot = B_FALSE;
183static int write_locked;
184static pid_t client_pid;
185static int verbose = 1;
186static hrtime_t dumptimeout = DUMPTIMEOUT;
187static boolean_t sync_needed = B_FALSE;
188
189static uid_t myuid;
190
191static char marker[] = "###Marker\n";
192
193static umem_cache_t *ecache;
194
195static char pkgdir[PATH_MAX];
196
197static void
198server_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*/
419static void
420nothing(int sig)
421{
422}
423
424int
425main(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*/
489static void
490pkg_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
581err:
582	(void) mutex_unlock(&mtx);
583	(void) door_return((void *)&one, 4, NULL, 0);
584}
585
586/*
587 * This function returns the length of the string including exactly
588 * nf fields.
589 */
590static ptrdiff_t
591fieldoff(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 */
609static char *
610mystrcpy(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 */
623static pkgentry_t *
624parse_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
701badline:
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
711static void
712handle_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
726static void
727parse_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
841static int
842parse_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
952static char *
953file_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
970static void
971pkgdump(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
1016static void
1017freeentry(pkgentry_t *p)
1018{
1019	umem_free(p->line, p->len);
1020	umem_cache_free(ecache, p);
1021}
1022
1023static void
1024swapentry(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
1045static int
1046logentry(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
1064static int
1065logflush(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
1113static int
1114avlcmp(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
1139static int
1140establish_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
1158static int
1159no_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
1169static void *
1170thr_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
1226free:
1227	umem_free(pf, sizeof (pkgfilter_t) + pf->len);
1228	return (NULL);
1229}
1230
1231static hrtime_t
1232time_since_(hrtime_t last)
1233{
1234	return (gethrtime() - last);
1235}
1236
1237static void
1238my_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
1248static int
1249pkgfilter(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, 0, 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
1280static int
1281pkgaddlines(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
1350static void
1351finish(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 */
1368static void
1369signal_handler(int sig)
1370{
1371	if (read_only)
1372		exit(0);
1373	want_to_quit = 1;
1374	(void) cond_broadcast(&cv);
1375}
1376