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
server_main(int argc,char ** argv)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
nothing(int sig)420 nothing(int sig)
421 {
422 }
423
424 int
main(int argc,char ** argv)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
pkg_door_srv(void * cookie,char * argp,size_t asz,door_desc_t * dp,uint_t ndesc)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, 0);
584 }
585
586 /*
587 * This function returns the length of the string including exactly
588 * nf fields.
589 */
590 static ptrdiff_t
fieldoff(char * info,int nf)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 *
mystrcpy(char * buf,int len)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 *
parse_line(char * buf,int blen,boolean_t full)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
handle_comments(char * buf,int len)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
parse_contents(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
parse_log(void)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 *
file_find(pkgfilter_t * cmd,int * len)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
pkgdump(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
freeentry(pkgentry_t * p)1017 freeentry(pkgentry_t *p)
1018 {
1019 umem_free(p->line, p->len);
1020 umem_cache_free(ecache, p);
1021 }
1022
1023 static void
swapentry(pkgentry_t * cur,pkgentry_t * new)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
logentry(char type,pkgentry_t * p)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
logflush(void)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
avlcmp(const void * ca,const void * cb)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
establish_lock(char * lock)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
no_memory_abort(void)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 *
thr_pkgfilter(void * v)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
time_since_(hrtime_t last)1232 time_since_(hrtime_t last)
1233 {
1234 return (gethrtime() - last);
1235 }
1236
1237 static void
my_cond_reltimedwait(hrtime_t delta,int sec)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
pkgfilter(pkgfilter_t * pf,door_desc_t * dp)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, 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
1280 static int
pkgaddlines(pkgfilter_t * pf)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
finish(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
signal_handler(int sig)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