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.
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.
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]
23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
28 * The Solaris package installer in-memory database server.
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.
38 * The libpkg will start the daemon.
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:
48 * 1 - We couldn't start (locked)
49 * 2 - Other problems (error on stderr)
50 * 99 - Nothing reported; the caller must report.
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.
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.
62 * In the particular case of install, we make sure the daemon
63 * sticks around. (Install == install, (live)upgrade, zone install)
67 #undef _FILE_OFFSET_BITS
83 #include <sys/statvfs.h>
98 #define SADM_DIR "/var/sadm/install"
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"
106 #define LLNANOSEC ((int64_t)NANOSEC)
108 #define DUMPTIMEOUT 60
109 #define EXITTIMEOUT 300
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.
118 * All strings are allocated using umem_alloc.
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) */
128 static char IS_ST0
[256];
129 static char IS_ST0Q
[256];
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
);
151 * - set mode (contents file, log file)
154 * - merge package entries
158 static char *door
= PKGDOOR
;
160 static avl_tree_t listp
, *list
= &listp
;
162 /* Keep the "the last command modified the contents file ... */
163 static char *ccmnt
[2];
166 static mutex_t mtx
= DEFAULTMUTEX
;
167 static cond_t cv
= DEFAULTCV
;
169 static int flushbeforemark
= 1;
170 static int logerrcnt
= 0;
171 static int loglines
= 0;
172 static int suppressed
= 0;
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
;
191 static char marker
[] = "###Marker\n";
193 static umem_cache_t
*ecache
;
195 static char pkgdir
[PATH_MAX
];
198 server_main(int argc
, char **argv
)
202 struct statvfs vfsbuf
;
206 char *sadmdir
= NULL
;
211 (void) set_prog_name("pkgserv");
213 openlog("pkgserv", LOG_PID
| LOG_ODELAY
, LOG_DAEMON
);
215 while ((c
= getopt(argc
, argv
, "d:eoN:pP:R:r:")) != EOF
) {
222 if (*sadmdir
!= '/' || strlen(sadmdir
) >= PATH_MAX
||
223 access(sadmdir
, X_OK
) != 0)
227 (void) set_prog_name(optarg
);
235 * We are updating possibly many zones; so we're not
236 * dumping based on a short timeout and we will not
243 client_pid
= atoi(optarg
);
247 if (*root
!= '/' || strlen(root
) >= PATH_MAX
||
248 access(root
, X_OK
) != 0)
262 if (one_shot
&& permanent
) {
263 progerr(gettext("Incorrect Usage"));
267 umem_nofail_callback(no_memory_abort
);
269 if (root
!= NULL
&& strcmp(root
, "/") != 0) {
270 if (snprintf(pkgdir
, PATH_MAX
, "%s%s", root
,
271 sadmdir
== NULL
? SADM_DIR
: sadmdir
) >= PATH_MAX
) {
276 (void) strcpy(pkgdir
, SADM_DIR
);
278 (void) strcpy(pkgdir
, sadmdir
);
281 if (chdir(pkgdir
) != 0) {
282 progerr(gettext("can't chdir to %s"), pkgdir
);
288 if (!read_only
&& establish_lock(LOCK
) < 0) {
290 "couldn't lock in %s (server running?): %s"),
291 pkgdir
, strerror(errno
));
295 did
= door_create(pkg_door_srv
, 0, DOOR_REFUSE_DESC
);
297 progerr("door_create: %s", strerror(errno
));
301 (void) fdetach(door
);
303 if ((dfd
= creat(door
, 0644)) < 0 || close(dfd
) < 0) {
304 progerr("door_create: %s", strerror(errno
));
308 (void) mutex_lock(&mtx
);
312 (void) sigset(SIGHUP
, signal_handler
);
313 (void) sigset(SIGTERM
, signal_handler
);
314 (void) sigset(SIGINT
, signal_handler
);
315 (void) sigset(SIGQUIT
, signal_handler
);
317 (void) signal(SIGPIPE
, SIG_IGN
);
319 (void) atexit(finish
);
321 if (fattach(did
, door
) != 0) {
322 progerr(gettext("attach door: %s"), strerror(errno
));
327 ecache
= umem_cache_create("entry", sizeof (pkgentry_t
),
328 sizeof (char *), NULL
, NULL
, NULL
, NULL
, NULL
, 0);
330 avl_create(list
, avlcmp
, sizeof (pkgentry_t
),
331 offsetof(pkgentry_t
, avl
));
349 if (statvfs(".", &vfsbuf
) != 0) {
350 progerr(gettext("statvfs: %s"), strerror(errno
));
354 if (strcmp(vfsbuf
.f_basetype
, "zfs") == 0)
357 /* We've started, tell the parent */
360 (void) kill(parent
, SIGUSR1
);
365 fd
= open("/dev/null", O_RDWR
, 0);
367 (void) dup2(fd
, STDIN_FILENO
);
368 (void) dup2(fd
, STDOUT_FILENO
);
369 (void) dup2(fd
, STDERR_FILENO
);
375 lastcall
= lastchange
= gethrtime();
378 * Start the main thread, here is where we unlock the mutex.
385 /* Wait forever when root or when there's a running filter */
387 (!one_shot
&& permanent
&& dir
== changes
)) {
388 (void) cond_wait(&cv
, &mtx
);
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
);
397 /* Client still around? Just wait then. */
398 if (client_pid
> 1 && kill(client_pid
, 0) == 0) {
399 lastchange
= lastcall
= gethrtime();
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
);
425 main(int argc
, char **argv
)
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
436 * We keep around who started us and as long as it lives, we don't
440 (void) setlocale(LC_ALL
, "");
441 (void) textdomain(TEXT_DOMAIN
);
443 client_pid
= getppid();
445 (void) sigemptyset(&sset
);
446 (void) sigaddset(&sset
, SIGUSR1
);
447 (void) sigaddset(&sset
, SIGCLD
);
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
);
455 /* We install the contents file readable. */
463 server_main(argc
, argv
);
471 sig
= sigwait(&sset
);
475 if (wait(&stat
) > 0) {
477 _exit(WEXITSTATUS(stat
));
478 else if (WIFSIGNALED(stat
))
490 pkg_door_srv(void *cookie
, char *argp
, size_t asz
, door_desc_t
*dp
,
494 pkgcmd_t
*pcmd
= (pkgcmd_t
*)argp
;
503 if (asz
< sizeof (pkgcmd_t
)) {
504 (void) door_return(NULL
, 0, NULL
, 0);
508 if (door_ucred(&uc
) != 0) {
509 (void) door_return(NULL
, 0, NULL
, 0);
513 caller
= ucred_geteuid(uc
);
514 pcaller
= ucred_getpid(uc
);
517 if (caller
!= myuid
) {
518 (void) door_return(NULL
, 0, NULL
, 0);
522 (void) mutex_lock(&mtx
);
525 if (pcaller
!= client_pid
&& pcaller
!= -1 &&
526 (client_pid
== 1 || kill(client_pid
, 0) != 0)) {
527 client_pid
= pcaller
;
530 if (PKG_WRITE_COMMAND(pcmd
->cmd
))
531 while (write_locked
> 0)
532 (void) cond_wait(&cv
, &mtx
);
536 p
= file_find((pkgfilter_t
*)argp
, &len
);
550 if (read_only
|| logflush() != 0)
554 if (pkgfilter((pkgfilter_t
*)argp
, &ddp
) == 0)
562 if (pkgaddlines((pkgfilter_t
*)argp
) != 0)
564 /* If we've updated the database, tell the dump thread */
565 lastchange
= gethrtime();
566 (void) cond_broadcast(&cv
);
569 /* Do nothing but register the current client's pid. */
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
);
582 (void) mutex_unlock(&mtx
);
583 (void) door_return((void *)&one
, 4, NULL
, NULL
);
587 * This function returns the length of the string including exactly
591 fieldoff(char *info
, int nf
)
596 if (IS_ST0
[(unsigned char)*q
++]) {
602 return (q
- info
- 1);
606 * The buf points into list of \n delimited lines. We copy it,
607 * removing the newline and adding a \0.
610 mystrcpy(char *buf
, int len
)
612 char *res
= umem_alloc(len
, UMEM_NOFAIL
);
614 (void) memcpy(res
, buf
, len
- 1);
620 * Entry: a single line without the NEWLINE
621 * Return: the package entry with the path determined.
624 parse_line(char *buf
, int blen
, boolean_t full
)
630 p
= umem_cache_alloc(ecache
, UMEM_NOFAIL
);
631 buf
= p
->line
= mystrcpy(buf
, blen
+ 1);
636 while (!IS_ST0Q
[(unsigned char)*t
++])
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");
648 while (!IS_ST0
[(unsigned char)*t
++])
651 /* Partial as found in the "-" entries for log */
672 /* class mode owner group */
678 /* class mode owner group size csum time */
683 /* class major minor mode owner group */
687 progerr("bad entry read in contents file");
688 logerr("pathname: %.*s", p
->pathlen
, p
->line
);
689 logerr("problem: unknown ftype");
696 p
->pkgoff
= t
+ fieldoff(t
, nfields
+ 1) - buf
;
698 if (p
->line
[p
->pkgoff
] != '\0' || p
->pkgoff
== p
->len
- 1)
702 progerr(gettext("bad entry read in contents file"));
703 logerr(gettext("pathname: Unknown"));
704 logerr(gettext("problem: unknown ftype"));
712 handle_comments(char *buf
, int len
)
720 if (ccmnt
[cind
] != NULL
)
721 umem_free(ccmnt
[cind
], strlen(ccmnt
[cind
]) + 1);
722 ccmnt
[cind
] = mystrcpy(buf
, len
);
730 pkgentry_t
*ent
, *e2
;
736 pkgentry_t
*lastentry
= NULL
;
740 cnt
= open(CONTENTS
, O_RDONLY
);
750 if (fstat(cnt
, &stb
) != 0) {
754 if (stb
.st_size
== 0) {
759 map
= mmap(0, stb
.st_size
, PROT_READ
, MAP_PRIVATE
, cnt
, 0);
761 if (map
== (char *)-1)
764 (void) madvise(map
, stb
.st_size
, MADV_WILLNEED
);
766 for (off
= 0; off
< stb
.st_size
; off
+= q
- p
) {
768 q
= memchr(p
, '\n', stb
.st_size
- off
);
774 if (p
[0] == '#' || p
[0] == '\n') {
775 handle_comments(p
, q
- p
);
778 ent
= parse_line(p
, q
- p
- 1, B_TRUE
);
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.
790 if (lastentry
== NULL
) {
793 } else if ((d
= avlcmp(ent
, lastentry
)) == 1) {
794 avl_insert_here(list
, ent
, lastentry
, AVL_AFTER
);
797 (e2
= avl_find(list
, ent
, &where
)) != NULL
) {
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?
806 if (strcmp(ent
->line
, e2
->line
) != 0) {
807 progerr(gettext("two entries for %.*s"),
808 ent
->pathlen
, ent
->line
);
813 /* Out of order: not an error for us, really. */
814 progerr(gettext("bad read of contents file"));
815 logerr(gettext("pathname: Unknown"));
817 "problem: unable to read pathname field"));
820 avl_insert(list
, ent
, where
);
826 (void) munmap(map
, stb
.st_size
);
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
)];
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",
844 pkgentry_t
*ent
, *look
;
849 int mlen
= strlen(marker
);
854 logfd
= open(PKGLOG
, O_RDONLY
);
859 progerr(gettext("cannot read "PKGLOG
": %s"), strerror(errno
));
863 if (fstat(logfd
, &stb
) != 0) {
864 progerr(gettext("cannot stat "PKGLOG
": %s"), strerror(errno
));
868 if (stb
.st_size
== 0) {
870 /* Force pkgdump && remove of the logfile. */
874 map
= mmap(0, stb
.st_size
, PROT_READ
|PROT_WRITE
, MAP_PRIVATE
,
877 if (map
== (char *)-1) {
878 progerr(gettext("Cannot mmap the "PKGLOG
": %s"),
885 realend
= stb
.st_size
;
887 if (memcmp(map
+ realend
- mlen
, marker
, mlen
) != 0) {
888 progerr(gettext(PKGLOG
" is not complete"));
890 map
[stb
.st_size
- 1] = '\0'; /* for strstr() */
892 for (p
= map
; q
= strstr(p
, marker
); ) {
893 if (q
== map
|| q
[-1] == '\n')
894 realend
= q
- map
+ mlen
;
897 progerr(gettext("Ignoring %ld bytes from log"),
898 (long)(stb
.st_size
- realend
));
901 for (off
= 0; off
< realend
; off
+= q
- p
) {
903 q
= memchr(p
, '\n', realend
- off
);
909 if (p
[0] == '#' || p
[0] == '\n') {
910 if (memcmp(marker
, p
, mlen
) == 0)
913 handle_comments(p
, q
- p
);
917 ent
= parse_line(p
+ 1, q
- (p
+ 1) - 1, p
[0] != '-');
920 look
= avl_find(list
, ent
, &where
);
922 * The log can be replayed; so any value of "look" is
929 swapentry(look
, ent
);
931 avl_insert(list
, ent
, where
);
935 avl_remove(list
, look
);
942 progerr(gettext("log %d: bad line"), num
);
946 (void) munmap(map
, stb
.st_size
);
948 /* Force pkgdump && remove of the logfile if there are no valid mods. */
949 return (num
== 0 ? 1 : num
);
953 file_find(pkgfilter_t
*cmd
, int *len
)
959 p
.pathlen
= cmd
->len
;
961 look
= avl_find(list
, &p
, NULL
);
980 /* We cannot dump when the current transaction is not complete. */
984 cnts
= fopen(TCONTENTS
, "w");
989 for (p
= avl_first(list
); p
!= NULL
; p
= AVL_NEXT(list
, p
)) {
990 if (fprintf(cnts
, "%s\n", p
->line
) < 0)
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]);
999 if (err
!= 0 || fflush(cnts
) == EOF
|| fsync(fileno(cnts
)) != 0 ||
1000 fclose(cnts
) == EOF
|| rename(TCONTENTS
, CONTENTS
) != 0) {
1005 progerr("cannot rewrite the contents file");
1010 (void) unlink(PKGLOG
);
1017 freeentry(pkgentry_t
*p
)
1019 umem_free(p
->line
, p
->len
);
1020 umem_cache_free(ecache
, p
);
1024 swapentry(pkgentry_t
*cur
, pkgentry_t
*new)
1026 if (cur
->len
== new->len
&&
1027 strcmp(cur
->line
+ cur
->pathlen
,
1028 new->line
+ new->pathlen
) == 0) {
1035 umem_free(cur
->line
, cur
->len
);
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
;
1042 umem_cache_free(ecache
, new);
1046 logentry(char type
, pkgentry_t
*p
)
1051 len
= fprintf(log
, "-%.*s\n", p
->pathlen
, p
->line
);
1053 len
= fprintf(log
, "%c%s\n", type
, p
->line
);
1068 static int lastflush
;
1073 if (lastflush
== logcount
)
1077 (void) fprintf(log
, "%s\n", ccmnt
[0]);
1078 (void) fprintf(log
, "%s\n", ccmnt
[1]);
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.
1086 if (flushbeforemark
) {
1087 if (fflush(log
) == EOF
)
1090 /* Anything before the last marker found in the log will be valid */
1091 len
= fprintf(log
, "%s", marker
);
1097 if (fflush(log
) == EOF
)
1100 sync_needed
= B_FALSE
;
1102 if (logerrcnt
> 0 || logcount
> MAXLOGFILESIZE
)
1108 lastflush
= logcount
;
1114 avlcmp(const void *ca
, const void *cb
)
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
);
1125 else if (a
->pathlen
== b
->pathlen
)
1127 else if (a
->pathlen
> b
->pathlen
)
1135 * 0 - if we can get the lock
1136 * -1 - we can't lock
1140 establish_lock(char *lock
)
1142 int fd
= open(lock
, O_RDWR
|O_CREAT
, 0644);
1148 for (i
= 0; i
< 5; i
++) {
1149 if (lockf(fd
, F_TLOCK
, 0) == 0)
1159 no_memory_abort(void)
1161 return (UMEM_CALLBACK_EXIT(99));
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.
1170 thr_pkgfilter(void *v
)
1172 pkgfilter_t
*pf
= v
;
1177 cnts
= fdopen(pf
->cmd
, "w");
1182 * Remove wild card: don't care about extra matches; make sure
1183 * we remove both the "*" and the "." in front of it.
1188 for (p
= pf
->buf
; *p
; p
++) {
1191 if (p
> pf
->buf
&& p
[-1] == '.')
1198 /* Disable modifications while the filter is running */
1199 (void) mutex_lock(&mtx
);
1201 (void) mutex_unlock(&mtx
);
1203 * The protocol for the contents file for the clients:
1204 * <int:len><int:pathlen><line + 0>
1207 for (p
= avl_first(list
); p
!= NULL
; p
= AVL_NEXT(list
, p
)) {
1208 if (pf
->len
> 0 && strstr(p
->line
, pf
->buf
) == NULL
)
1212 nums
[1] = p
->pathlen
;
1213 if (fwrite(nums
, sizeof (int), 2, cnts
) != 2)
1215 if (fwrite(p
->line
, 1, p
->len
, cnts
) != p
->len
)
1219 (void) mutex_lock(&mtx
);
1220 lastcall
= gethrtime();
1222 (void) cond_broadcast(&cv
);
1223 (void) mutex_unlock(&mtx
);
1224 (void) fclose(cnts
);
1227 umem_free(pf
, sizeof (pkgfilter_t
) + pf
->len
);
1232 time_since_(hrtime_t last
)
1234 return (gethrtime() - last
);
1238 my_cond_reltimedwait(hrtime_t delta
, int sec
)
1240 hrtime_t wait
= sec
* LLNANOSEC
- delta
;
1241 timestruc_t waitfor
;
1243 waitfor
.tv_nsec
= wait
% LLNANOSEC
;
1244 waitfor
.tv_sec
= wait
/ LLNANOSEC
;
1245 (void) cond_reltimedwait(&cv
, &mtx
, &waitfor
);
1249 pkgfilter(pkgfilter_t
*pf
, door_desc_t
*dp
)
1259 cpf
= umem_alloc(sizeof (pkgfilter_t
) + pf
->len
, UMEM_NOFAIL
);
1261 (void) memcpy(cpf
, pf
, sizeof (pkgfilter_t
) + pf
->len
);
1263 /* Copy the file descriptor in the command field */
1266 if (thr_create(NULL
, NULL
, thr_pkgfilter
, cpf
, THR_DETACHED
,
1270 umem_free(cpf
, sizeof (pkgfilter_t
) + pf
->len
);
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];
1281 pkgaddlines(pkgfilter_t
*pf
)
1283 char *map
= pf
->buf
;
1286 pkgentry_t
*ent
, *look
;
1293 log
= fopen(PKGLOG
, "w");
1298 for (off
= 0; off
< len
; off
+= q
- p
) {
1300 q
= memchr(p
, '\n', len
- off
);
1307 if (p
[0] == '#' || p
[0] == '\n') {
1308 handle_comments(p
, q
- p
);
1313 ent
= parse_line(p
+ 1, q
- (p
+ 1) - 1, B_FALSE
);
1315 ent
= parse_line(p
, q
- p
- 1, B_TRUE
);
1322 look
= avl_find(list
, ent
, &where
);
1324 c
= *p
== '-' ? '-' : '=';
1326 swapentry(look
, ent
);
1329 avl_remove(list
, look
);
1332 } else if (*p
== '-') {
1333 /* Remove something which isn't there: no-op */
1337 avl_insert(list
, ent
, where
);
1341 sync_needed
= B_TRUE
;
1342 r
+= logentry(c
, ent
);
1355 "finished: calls %d, pkgdumps %d, loglines %d "
1356 "(suppressed %d)\n",
1357 ncalls
, ndumps
, loglines
, suppressed
);
1359 (void) fdetach(door
);
1361 (void) unlink(door
);
1365 * Tell the wait thread to wake up and quit.
1369 signal_handler(int sig
)
1374 (void) cond_broadcast(&cv
);