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 (c) 2017 Peter Tribble.
27 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
28 * Use is subject to license terms.
44 #include <sys/param.h>
49 #include <sys/mnttab.h>
50 #include <sys/mkdev.h>
52 #define PKGADD_MAX (512 * 1024)
54 #define SADM_DIR "/var/sadm/install"
56 #define PKGSERV_PATH "/usr/sadm/install/bin/pkgserv"
58 #define ERR_PATH_TOO_BIG "alternate root path is too long"
59 #define ERR_OPEN_DOOR "cannot open pkgserv door"
60 #define ERR_START_SERVER "cannot start pkgserv daemon: %s"
61 #define ERR_START_FILTER "cannot enumerate database entries"
62 #define ERR_FIND_SADM "cannot find sadm directory"
72 static PKGserver current_server
;
74 static start_mode_t defmode
= INVALID
;
75 static boolean_t registered
= B_FALSE
;
76 static pid_t master_pid
= -1;
79 pkgfilename(char path
[PATH_MAX
], const char *root
, const char *sadmdir
,
82 if (snprintf(path
, PATH_MAX
, "%s%s/%s", root
== NULL
? "" : root
,
83 sadmdir
== NULL
? SADM_DIR
: sadmdir
, file
) >= PATH_MAX
) {
84 progerr(gettext(ERR_PATH_TOO_BIG
));
90 free_xmnt(struct extmnttab
*xmnt
)
92 free(xmnt
->mnt_special
);
93 free(xmnt
->mnt_mountp
);
94 free(xmnt
->mnt_fstype
);
98 copy_xmnt(const struct extmnttab
*xmnt
, struct extmnttab
*saved
)
104 * Copy everything and then strdup the strings we later use and NULL
109 if (saved
->mnt_special
!= NULL
)
110 saved
->mnt_special
= strdup(saved
->mnt_special
);
111 if (saved
->mnt_mountp
!= NULL
)
112 saved
->mnt_mountp
= strdup(saved
->mnt_mountp
);
113 if (saved
->mnt_fstype
!= NULL
)
114 saved
->mnt_fstype
= strdup(saved
->mnt_fstype
);
116 saved
->mnt_mntopts
= NULL
;
117 saved
->mnt_time
= NULL
;
128 dir
= open(path
, O_RDONLY
);
133 fd
= openat(dir
, PKGDOOR
, O_RDWR
);
138 res
= door_info(fd
, &di
);
144 * We need to make sure that we can locate the pkgserv and the door;
145 * lofs mounts makes this more difficult: "nosub" mounts don't propagate
146 * the door and doors created in lofs mounts are not propagated back to
147 * the original filesystem.
148 * Here we peel off the lofs mount points until we're
149 * at /var/sadm/install or
150 * we find a working door or
151 * there's nothing more to peel off.
152 * The fullpath parameter is used to return the result (stored in *sadmdir),
153 * root is used but returned in the computed sadmdir and so the caller should
154 * not use "root" any longer or set it to NULL.
157 pkgfindrealsadmdir(char fullpath
[PATH_MAX
], const char *root
,
158 const char **sadmdir
)
161 struct extmnttab xmnt
;
164 struct extmnttab saved
= {NULL
, NULL
, NULL
, NULL
, NULL
, 0, 0};
166 if (snprintf(temp
, PATH_MAX
, "%s%s",
167 root
== NULL
? "" : root
,
168 *sadmdir
== NULL
? SADM_DIR
: *sadmdir
) >= PATH_MAX
) {
169 progerr(gettext(ERR_PATH_TOO_BIG
));
173 if (stat(temp
, &buf
) != 0) {
174 progerr(gettext(ERR_FIND_SADM
));
179 * To find the underlying mount point, you will need to
180 * search the mnttab and find our mountpoint and the underlying
182 * To find the mount point: use the longest prefix but limit
183 * us to the filesystems with the same major/minor numbers.
184 * To find the underlying mount point: find a non-lofs file
185 * system or a <mnt> <mnt> entry (fake mountpoint for zones).
190 if (realpath(temp
, fullpath
) == NULL
) {
191 progerr(gettext(ERR_FIND_SADM
));
195 if (strcmp(fullpath
, SADM_DIR
) == 0)
198 if (testdoor(fullpath
) == 0)
202 mnttab
= fopen(MNTTAB
, "r");
206 while (getextmntent(mnttab
, &xmnt
, 0) == 0) {
209 if (major(buf
.st_dev
) != xmnt
.mnt_major
||
210 minor(buf
.st_dev
) != xmnt
.mnt_minor
)
213 len
= strlen(xmnt
.mnt_mountp
);
217 if (strncmp(xmnt
.mnt_mountp
, fullpath
, len
) == 0 &&
218 (len
== 1 || fullpath
[len
] == '/' ||
219 fullpath
[len
] == '\0')) {
221 copy_xmnt(&xmnt
, &saved
);
224 if (strcmp(saved
.mnt_fstype
, "lofs") != 0 ||
225 strcmp(saved
.mnt_mountp
, saved
.mnt_special
) == 0) {
228 /* Create a new path in the underlying filesystem. */
229 if (snprintf(temp
, PATH_MAX
, "%s%s", saved
.mnt_special
,
230 &fullpath
[max
]) >= PATH_MAX
) {
231 progerr(gettext(ERR_PATH_TOO_BIG
));
236 if (mnttab
!= NULL
) {
238 (void) fclose(mnttab
);
246 if (current_server
!= NULL
)
247 pkgcloseserver(current_server
);
251 pkgopenserver_i(const char *root
, const char *sadmdir
, boolean_t readonly
,
261 char pkgdoor
[PATH_MAX
];
262 char realsadmdir
[PATH_MAX
];
263 extern char **environ
;
267 if (current_server
!= NULL
)
268 return (current_server
);
272 (void) atexit(pkgexit_close
);
277 (void) strcpy(pkgdoor
, "/tmp/pkgdoor.XXXXXX");
278 if ((fd
= mkstemp(pkgdoor
)) < 0) {
279 progerr(gettext(ERR_OPEN_DOOR
));
284 pkgfindrealsadmdir(realsadmdir
, root
, &sadmdir
);
286 pkgfilename(pkgdoor
, root
, sadmdir
, PKGDOOR
);
289 server
= malloc(sizeof (*server
));
295 server
->onetime
= readonly
;
298 server
->door
= open(pkgdoor
, O_RDWR
);
300 if (server
->door
>= 0) {
301 if (door_info(server
->door
, &di
) == 0 && di
.di_target
>= 0) {
304 server
->buflen
= 1024;
305 server
->curbuf
= malloc(1024);
306 if (server
->curbuf
== NULL
||
307 pkgcmd(server
, &n
, sizeof (n
), NULL
, NULL
, NULL
)) {
308 pkgcloseserver(server
);
311 return (current_server
= server
);
314 (void) close(server
->door
);
317 if (!first
|| mode
== NEVER
)
323 cmd
[args
++] = strrchr(PKGSERV_PATH
, '/') + 1;
324 if (root
!= NULL
&& strcmp(root
, "/") != 0) {
326 cmd
[args
++] = (char *)root
;
328 if (sadmdir
!= NULL
&& strcmp(sadmdir
, SADM_DIR
) != 0) {
330 cmd
[args
++] = (char *)sadmdir
;
334 cmd
[args
++] = pkgdoor
;
336 prog
= get_prog_name();
356 if (master_pid
!= -1) {
358 (void) snprintf(pidbuf
, sizeof (pidbuf
), "%d", master_pid
);
359 cmd
[args
++] = pidbuf
;
362 assert(args
<= sizeof (cmd
)/sizeof (char *));
364 if (posix_spawn(&pid
, PKGSERV_PATH
, NULL
, NULL
, cmd
, environ
) == 0) {
365 server
->onetime
|= (mode
== RUN_ONCE
);
366 while (wait4(pid
, &stat
, 0, NULL
) != -1) {
367 if (WIFEXITED(stat
)) {
368 int s
= WEXITSTATUS(stat
);
369 if (s
== 0 || s
== 1)
370 if (mode
== FLUSH_LOG
)
377 } else if (WIFSIGNALED(stat
)) {
383 progerr(gettext(ERR_START_SERVER
), strerror(errno
));
387 (void) unlink(pkgdoor
);
393 pkgopenserver(const char *root
, const char *sadmdir
, boolean_t ro
)
395 return (pkgopenserver_i(root
, sadmdir
, ro
, pkgservergetmode()));
399 pkgparsemode(const char *mode
)
401 if (strcasecmp(mode
, MODE_PERMANENT
) == 0) {
403 } else if (strncasecmp(mode
, MODE_TIMEOUT
,
404 sizeof (MODE_TIMEOUT
) - 1) == 0) {
405 const char *pidstr
= mode
+ sizeof (MODE_TIMEOUT
) - 1;
406 if (pidstr
[0] != '\0') {
407 master_pid
= atoi(pidstr
);
408 if (master_pid
<= 1 || kill(master_pid
, 0) != 0)
413 } else if (strcasecmp(mode
, MODE_RUN_ONCE
) == 0) {
416 progerr(gettext("invalid pkgserver mode: %s"), mode
);
423 pkgmodeargument(start_mode_t mode
)
425 static char timebuf
[sizeof (PKGSERV_MODE
) + sizeof (MODE_TIMEOUT
) + 10];
429 return (PKGSERV_MODE MODE_PERMANENT
);
431 (void) snprintf(timebuf
, sizeof (timebuf
),
432 PKGSERV_MODE MODE_TIMEOUT
"%d",
433 (master_pid
> 1 && kill(master_pid
, 0) == 0) ? master_pid
:
437 return (PKGSERV_MODE MODE_RUN_ONCE
);
439 progerr(gettext("Bad pkgserv mode: %d"), (int)mode
);
445 pkgserversetmode(start_mode_t mode
)
447 if (mode
== DEFAULTMODE
|| mode
== INVALID
) {
448 char *var
= getenv(SUNW_PKG_SERVERMODE
);
451 defmode
= pkgparsemode(var
);
453 defmode
= DEFAULTMODE
;
460 pkgservergetmode(void)
462 if (defmode
== INVALID
)
463 pkgserversetmode(DEFAULTMODE
);
468 pkgcloseserver(PKGserver server
)
471 if (server
->fp
!= NULL
)
472 (void) fclose(server
->fp
);
473 free(server
->curbuf
);
474 if (server
->onetime
) {
477 (void) pkgcmd(server
, &cmd
, sizeof (cmd
), NULL
, NULL
, NULL
);
479 (void) close(server
->door
);
480 if (server
== current_server
)
481 current_server
= NULL
;
486 pkgcmd(PKGserver srv
, void *cmd
, size_t len
, char **result
, size_t *rlen
,
495 da
.rbuf
= result
== NULL
? NULL
: *result
;
496 da
.rsize
= rlen
== NULL
? 0 : *rlen
;
498 if (door_call(srv
->door
, &da
) != 0) {
499 if (((pkgcmd_t
*)cmd
)->cmd
== PKG_EXIT
&& errno
== EINTR
)
504 if (da
.desc_ptr
!= NULL
) {
507 *fd
= da
.desc_ptr
[i
++].d_data
.d_desc
.d_descriptor
;
508 for (; i
< da
.desc_num
; i
++)
509 (void) close(da
.desc_ptr
[i
].d_data
.d_desc
.d_descriptor
);
512 if (da
.data_size
== sizeof (int)) {
514 int x
= *(int *)da
.data_ptr
;
516 if (result
== NULL
|| da
.rbuf
!= *result
)
517 (void) munmap(da
.rbuf
, da
.rsize
);
523 if (result
!= NULL
) {
524 /* Make sure that the result is at the start of the buffer. */
525 if (da
.data_ptr
!= NULL
&& da
.rbuf
!= da
.data_ptr
)
526 (void) memmove(da
.rbuf
, da
.data_ptr
, da
.data_size
);
528 *rlen
= da
.data_size
;
529 } else if (da
.rbuf
!= NULL
) {
530 (void) munmap(da
.rbuf
, da
.rsize
);
537 * If the server is running, make sure that the contents
539 * If the server is not running, check for the log file;
540 * if there's a non-empty log file, we need to start the server
541 * as it will incorporate the log file into the contents file.
542 * And then check if the door is present. If it doesn't, we don't
547 pkgsync_needed(const char *root
, const char *sadmdir
, boolean_t want_quit
)
550 char pkgfile
[PATH_MAX
];
551 boolean_t sync_needed
, running
;
555 pkgfilename(pkgfile
, root
, sadmdir
, PKGLOG
);
557 sync_needed
= stat(pkgfile
, &pbuf
) == 0 && pbuf
.st_size
> 0;
559 if (!sync_needed
&& !want_quit
)
562 pkgfilename(pkgfile
, root
, sadmdir
, PKGDOOR
);
564 /* sync_needed == B_TRUE || want_quit == B_TRUE */
567 fd
= open(pkgfile
, O_RDWR
);
570 if (door_info(fd
, &di
) == 0) {
571 /* It's mounted, so the server is likely there */
576 return (running
|| sync_needed
);
580 pkgsync(const char *root
, const char *sadmdir
, boolean_t force_quit
)
585 /* No need to write contents file; don't start if not running */
586 if (!pkgsync_needed(root
, sadmdir
, force_quit
))
589 server
= pkgopenserver_i(root
, sadmdir
, B_FALSE
, FLUSH_LOG
);
591 * We're assuming that it started the server and exited immediately.
592 * If that didn't work, there's nothing we can do.
597 cmd
.cmd
= force_quit
? PKG_EXIT
: PKG_DUMP
;
599 (void) pkgcmd(server
, &cmd
, sizeof (cmd
), NULL
, NULL
, NULL
);
600 (void) pkgcloseserver(server
);
605 pkgservercommitfile(VFP_T
*a_vfp
, PKGserver server
)
607 size_t len
= vfpGetModifiedLen(a_vfp
);
611 char *map
= a_vfp
->_vfpStart
;
613 if (len
< PKGADD_MAX
)
614 pcmd
= alloca(sizeof (*pcmd
) + len
);
616 pcmd
= alloca(sizeof (*pcmd
) + PKGADD_MAX
);
620 pcmd
->cmd
= PKG_ADDLINES
;
625 if (len
>= PKGADD_MAX
) {
626 len
= PKGADD_MAX
- 1;
627 while (p
[len
] != '\n' && len
> 0)
633 (void) memcpy(&pcmd
->buf
[0], p
, len
);
636 if (pkgcmd(server
, pcmd
, sizeof (*pcmd
) + len
- 1,
637 NULL
, NULL
, NULL
) != 0) {
644 pcmd
->cmd
= PKG_PKGSYNC
;
645 if (pkgcmd(server
, pcmd
, sizeof (*pcmd
), NULL
, NULL
, NULL
) != 0)
648 /* Mark it unmodified. */
650 (void) vfpClearModified(a_vfp
);
656 pkgopenfilter(PKGserver server
, const char *filt
)
660 int clen
= filt
== NULL
? 0 : strlen(filt
);
661 int len
= sizeof (*pfcmd
) + clen
;
665 if (server
->fp
!= NULL
) {
666 (void) fclose(server
->fp
);
670 pfcmd
->cmd
= PKG_FILTER
;
673 (void) strcpy(pfcmd
->buf
, filt
);
677 if (pkgcmd(server
, pfcmd
, len
, NULL
, NULL
, &fd
) != 0 || fd
== -1) {
678 progerr(gettext(ERR_START_FILTER
));
681 (void) fcntl(fd
, F_SETFD
, FD_CLOEXEC
);
683 server
->fp
= fdopen(fd
, "r");
684 if (server
->fp
== NULL
) {
686 progerr(gettext(ERR_START_FILTER
));
693 pkgclosefilter(PKGserver server
)
695 if (server
->fp
!= NULL
) {
696 (void) fclose(server
->fp
);
702 * Report the next entry from the contents file.
705 pkggetentry(PKGserver server
, int *len
, int *pathlen
)
709 if (server
->fp
== NULL
)
712 if (feof(server
->fp
) || ferror(server
->fp
))
715 if (fread(num
, sizeof (int), 2, server
->fp
) != 2)
718 if (num
[0] > server
->buflen
) {
719 free(server
->curbuf
);
720 server
->buflen
= num
[0];
721 server
->curbuf
= malloc(server
->buflen
);
722 if (server
->curbuf
== NULL
)
725 if (fread(server
->curbuf
, 1, num
[0], server
->fp
) != num
[0])
731 return (server
->curbuf
);
735 pkggetentry_named(PKGserver server
, const char *path
, int *len
, int *pathlen
)
737 int plen
= strlen(path
);
738 pkgfilter_t
*pcmd
= alloca(sizeof (*pcmd
) + plen
);
742 pcmd
->cmd
= PKG_FINDFILE
;
743 *pathlen
= pcmd
->len
= plen
;
744 (void) memcpy(pcmd
->buf
, path
, pcmd
->len
+ 1);
746 result
= server
->curbuf
;
747 rlen
= server
->buflen
;
749 if (pkgcmd(server
, pcmd
, sizeof (*pcmd
) + pcmd
->len
,
750 &result
, &rlen
, NULL
) != 0) {
757 if (result
!= server
->curbuf
) {
758 free(server
->curbuf
);
759 server
->buflen
= rlen
;
760 server
->curbuf
= malloc(server
->buflen
);
761 if (server
->curbuf
== NULL
)
763 (void) memcpy(server
->curbuf
, result
, rlen
);
764 (void) munmap(result
, rlen
);
768 return (server
->curbuf
);