xref: /titanic_44/usr/src/lib/libpkg/common/pkgserv.c (revision 80e2ca8596e3435bc3b76f3c597833ea0a87f85e)
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 #include <pkglib.h>
28 
29 #include <alloca.h>
30 #include <assert.h>
31 #include <door.h>
32 #include <errno.h>
33 #include <fcntl.h>
34 #include <pthread.h>
35 #include <spawn.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <strings.h>
39 #include <sys/mman.h>
40 #include <sys/param.h>
41 #include <sys/stat.h>
42 #include <sys/wait.h>
43 #include <unistd.h>
44 #include <libintl.h>
45 #include <sys/mnttab.h>
46 #include <sys/mkdev.h>
47 
48 #define	PKGADD_MAX	(512 * 1024)
49 
50 #define	SADM_DIR	"/var/sadm/install"
51 
52 #define	PKGSERV_PATH	"/usr/sadm/install/bin/pkgserv"
53 
54 #define	ERR_PATH_TOO_BIG	"alternate root path is too long"
55 #define	ERR_OPEN_DOOR		"cannot open pkgserv door"
56 #define	ERR_START_SERVER	"cannot start pkgserv daemon: %s"
57 #define	ERR_START_FILTER	"cannot enumerate database entries"
58 #define	ERR_FIND_SADM		"cannot find sadm directory"
59 
60 struct pkg_server {
61 	FILE		*fp;
62 	char		*curbuf;
63 	int		buflen;
64 	int		door;
65 	boolean_t	onetime;
66 };
67 
68 static PKGserver current_server;
69 
70 static start_mode_t defmode = INVALID;
71 static boolean_t registered = B_FALSE;
72 static pid_t master_pid = -1;
73 
74 static void
75 pkgfilename(char path[PATH_MAX], const char *root, const char *sadmdir,
76     const char *file)
77 {
78 	if (snprintf(path, PATH_MAX, "%s%s/%s", root == NULL ? "" : root,
79 	    sadmdir == NULL ? SADM_DIR : sadmdir, file) >= PATH_MAX) {
80 		progerr(gettext(ERR_PATH_TOO_BIG));
81 		exit(99);
82 	}
83 }
84 
85 static void
86 free_xmnt(struct extmnttab *xmnt)
87 {
88 	free(xmnt->mnt_special);
89 	free(xmnt->mnt_mountp);
90 	free(xmnt->mnt_fstype);
91 }
92 
93 static void
94 copy_xmnt(const struct extmnttab *xmnt, struct extmnttab *saved)
95 {
96 
97 	free_xmnt(saved);
98 
99 	/*
100 	 * Copy everything and then strdup the strings we later use and NULL
101 	 * the ones we don't.
102 	 */
103 	*saved = *xmnt;
104 
105 	if (saved->mnt_special != NULL)
106 		saved->mnt_special = strdup(saved->mnt_special);
107 	if (saved->mnt_mountp != NULL)
108 		saved->mnt_mountp = strdup(saved->mnt_mountp);
109 	if (saved->mnt_fstype != NULL)
110 		saved->mnt_fstype = strdup(saved->mnt_fstype);
111 
112 	saved->mnt_mntopts = NULL;
113 	saved->mnt_time = NULL;
114 }
115 
116 static int
117 testdoor(char *path)
118 {
119 	int dir;
120 	int fd;
121 	struct door_info di;
122 	int res;
123 
124 	dir = open(path, O_RDONLY);
125 
126 	if (dir == -1)
127 		return (-1);
128 
129 	fd = openat(dir, PKGDOOR, O_RDWR);
130 	(void) close(dir);
131 	if (fd == -1)
132 		return (-1);
133 
134 	res = door_info(fd, &di);
135 	(void) close(fd);
136 	return (res);
137 }
138 
139 /*
140  * We need to make sure that we can locate the pkgserv and the door;
141  * lofs mounts makes this more difficult: "nosub" mounts don't propagate
142  * the door and doors created in lofs mounts are not propagated back to
143  * the original filesystem.
144  * Here we peel off the lofs mount points until we're
145  *	at /var/sadm/install or
146  *	we find a working door or
147  *	there's nothing more to peel off.
148  * The fullpath parameter is used to return the result (stored in *sadmdir),
149  * root is used but returned in the computed sadmdir and so the caller should
150  * not use "root" any longer or set it to NULL.
151  */
152 static void
153 pkgfindrealsadmdir(char fullpath[PATH_MAX], const char *root,
154     const char **sadmdir)
155 {
156 	struct stat buf;
157 	struct extmnttab xmnt;
158 	FILE *mnttab = NULL;
159 	char temp[PATH_MAX];
160 	struct extmnttab saved = {NULL, NULL, NULL, NULL, NULL, 0, 0};
161 
162 	if (snprintf(temp, PATH_MAX, "%s%s",
163 	    root == NULL ? "" : root,
164 	    *sadmdir == NULL ? SADM_DIR : *sadmdir) >= PATH_MAX) {
165 		progerr(gettext(ERR_PATH_TOO_BIG));
166 		exit(99);
167 	}
168 
169 	if (stat(temp, &buf) != 0) {
170 		progerr(gettext(ERR_FIND_SADM));
171 		exit(99);
172 	}
173 
174 	/*
175 	 * To find the underlying mount point, you will need to
176 	 * search the mnttab and find our mountpoint and the underlying
177 	 * filesystem.
178 	 * To find the mount point: use the longest prefix but limit
179 	 * us to the filesystems with the same major/minor numbers.
180 	 * To find the underlying mount point: find a non-lofs file
181 	 * system or a <mnt> <mnt> entry (fake mountpoint for zones).
182 	 */
183 	for (;;) {
184 		size_t max = 0;
185 
186 		if (realpath(temp, fullpath) == NULL) {
187 			progerr(gettext(ERR_FIND_SADM));
188 			exit(99);
189 		}
190 
191 		if (strcmp(fullpath, SADM_DIR) == 0)
192 			break;
193 
194 		if (testdoor(fullpath) == 0)
195 			break;
196 
197 		if (mnttab == NULL)
198 			mnttab = fopen(MNTTAB, "r");
199 		else
200 			resetmnttab(mnttab);
201 
202 		while (getextmntent(mnttab, &xmnt, 0) == 0) {
203 			size_t len;
204 
205 			if (major(buf.st_dev) != xmnt.mnt_major ||
206 			    minor(buf.st_dev) != xmnt.mnt_minor)
207 				continue;
208 
209 			len = strlen(xmnt.mnt_mountp);
210 			if (len < max)
211 				continue;
212 
213 			if (strncmp(xmnt.mnt_mountp, fullpath, len) == 0 &&
214 			    (len == 1 || fullpath[len] == '/' ||
215 			    fullpath[len] == '\0')) {
216 				max = len;
217 				copy_xmnt(&xmnt, &saved);
218 			}
219 		}
220 		if (strcmp(saved.mnt_fstype, "lofs") != 0 ||
221 		    strcmp(saved.mnt_mountp, saved.mnt_special) == 0) {
222 			break;
223 		}
224 		/* Create a new path in the underlying filesystem. */
225 		if (snprintf(temp, PATH_MAX, "%s%s", saved.mnt_special,
226 		    &fullpath[max]) >= PATH_MAX) {
227 			progerr(gettext(ERR_PATH_TOO_BIG));
228 			exit(99);
229 		}
230 	}
231 
232 	if (mnttab != NULL) {
233 		free_xmnt(&saved);
234 		(void) fclose(mnttab);
235 	}
236 	*sadmdir = fullpath;
237 }
238 
239 static void
240 pkgexit_close(void)
241 {
242 	if (current_server != NULL)
243 		pkgcloseserver(current_server);
244 }
245 
246 static PKGserver
247 pkgopenserver_i(const char *root, const char *sadmdir, boolean_t readonly,
248 	start_mode_t mode)
249 {
250 	PKGserver server;
251 	struct door_info di;
252 	pid_t pid;
253 	int stat;
254 	int first = B_TRUE;
255 	char *cmd[16];
256 	int args;
257 	char pkgdoor[PATH_MAX];
258 	char realsadmdir[PATH_MAX];
259 	extern char **environ;
260 	char *prog;
261 	char pidbuf[12];
262 
263 	if (current_server != NULL)
264 		return (current_server);
265 
266 	if (!registered) {
267 		registered = B_TRUE;
268 		(void) atexit(pkgexit_close);
269 	}
270 	if (readonly) {
271 		int fd;
272 
273 		(void) strcpy(pkgdoor, "/tmp/pkgdoor.XXXXXX");
274 		if ((fd = mkstemp(pkgdoor)) < 0) {
275 			progerr(gettext(ERR_OPEN_DOOR));
276 			return (NULL);
277 		}
278 		(void) close(fd);
279 	} else {
280 		pkgfindrealsadmdir(realsadmdir, root, &sadmdir);
281 		root = NULL;
282 		pkgfilename(pkgdoor, root, sadmdir, PKGDOOR);
283 	}
284 
285 	server = malloc(sizeof (*server));
286 
287 	if (server == NULL)
288 		goto return_null;
289 
290 	server->fp = NULL;
291 	server->onetime = readonly;
292 
293 openserver:
294 	server->door = open(pkgdoor, O_RDWR);
295 
296 	if (server->door >= 0) {
297 		if (door_info(server->door, &di) == 0 && di.di_target >= 0) {
298 			pkgcmd_t n;
299 			n.cmd = PKG_NOP;
300 			server->buflen = 1024;
301 			server->curbuf = malloc(1024);
302 			if (server->curbuf == NULL ||
303 			    pkgcmd(server, &n, sizeof (n), NULL, NULL, NULL)) {
304 				pkgcloseserver(server);
305 				return (NULL);
306 			}
307 			return (current_server = server);
308 		}
309 
310 		(void) close(server->door);
311 	}
312 
313 	if (!first || mode == NEVER)
314 		goto return_null;
315 
316 	first = B_FALSE;
317 
318 	args = 0;
319 	cmd[args++] = strrchr(PKGSERV_PATH, '/') + 1;
320 	if (root != NULL && strcmp(root, "/") != 0) {
321 		cmd[args++] = "-R";
322 		cmd[args++] = (char *)root;
323 	}
324 	if (sadmdir != NULL && strcmp(sadmdir, SADM_DIR) != 0) {
325 		cmd[args++] = "-d";
326 		cmd[args++] = (char *)sadmdir;
327 	}
328 	if (readonly) {
329 		cmd[args++] = "-r";
330 		cmd[args++] = pkgdoor;
331 	}
332 	prog = get_prog_name();
333 	if (prog != NULL) {
334 		cmd[args++] = "-N";
335 		cmd[args++] = prog;
336 	}
337 
338 	switch (mode) {
339 	case FLUSH_LOG:
340 		cmd[args++] = "-e";
341 		break;
342 	case RUN_ONCE:
343 		cmd[args++] = "-o";
344 		break;
345 	case PERMANENT:
346 		cmd[args++] = "-p";
347 		break;
348 	default:
349 		break;
350 	}
351 
352 	if (master_pid != -1) {
353 		cmd[args++] = "-P";
354 		(void) snprintf(pidbuf, sizeof (pidbuf), "%d", master_pid);
355 		cmd[args++] = pidbuf;
356 	}
357 	cmd[args++] = NULL;
358 	assert(args <= sizeof (cmd)/sizeof (char *));
359 
360 	if (posix_spawn(&pid, PKGSERV_PATH, NULL, NULL, cmd, environ) == 0) {
361 		server->onetime |= mode == RUN_ONCE;
362 		while (wait4(pid, &stat, 0, NULL) != -1) {
363 			if (WIFEXITED(stat)) {
364 				int s = WEXITSTATUS(stat);
365 				if (s == 0 || s == 1)
366 					if (mode == FLUSH_LOG)
367 						goto return_null;
368 					else
369 						goto openserver;
370 				if (s == 2)
371 					goto return_null;
372 				break;
373 			} else if (WIFSIGNALED(stat)) {
374 				break;
375 			}
376 		}
377 	}
378 
379 	progerr(gettext(ERR_START_SERVER), strerror(errno));
380 
381 return_null:
382 	if (readonly)
383 		(void) unlink(pkgdoor);
384 	free(server);
385 	return (NULL);
386 }
387 
388 PKGserver
389 pkgopenserver(const char *root, const char *sadmdir, boolean_t ro)
390 {
391 	return (pkgopenserver_i(root, sadmdir, ro, pkgservergetmode()));
392 }
393 
394 start_mode_t
395 pkgparsemode(const char *mode)
396 {
397 	if (strcasecmp(mode, MODE_PERMANENT) == 0) {
398 		return (PERMANENT);
399 	} else if (strncasecmp(mode, MODE_TIMEOUT,
400 	    sizeof (MODE_TIMEOUT) - 1) == 0) {
401 		const char *pidstr = mode + sizeof (MODE_TIMEOUT) - 1;
402 		if (pidstr[0] != '\0') {
403 			master_pid = atoi(pidstr);
404 			if (master_pid <= 1 || kill(master_pid, 0) != 0)
405 				master_pid = -1;
406 		}
407 
408 		return (TIMEOUT);
409 	} else if (strcasecmp(mode, MODE_RUN_ONCE) == 0) {
410 		return (RUN_ONCE);
411 	} else {
412 		progerr(gettext("invalid pkgserver mode: %s"), mode);
413 		exit(99);
414 		/*NOTREACHED*/
415 	}
416 }
417 
418 char *
419 pkgmodeargument(start_mode_t mode)
420 {
421 	static char timebuf[sizeof (PKGSERV_MODE) + sizeof (MODE_TIMEOUT) + 10];
422 
423 	switch (mode) {
424 	case PERMANENT:
425 		return (PKGSERV_MODE MODE_PERMANENT);
426 	case TIMEOUT:
427 		(void) snprintf(timebuf, sizeof (timebuf),
428 		    PKGSERV_MODE MODE_TIMEOUT "%d",
429 		    (master_pid > 1 && kill(master_pid, 0) == 0) ? master_pid :
430 		    getpid());
431 		return (timebuf);
432 	case RUN_ONCE:
433 		return (PKGSERV_MODE MODE_RUN_ONCE);
434 	}
435 	progerr(gettext("Bad pkgserv mode: %d"), (int)mode);
436 	exit(99);
437 }
438 
439 void
440 pkgserversetmode(start_mode_t mode)
441 {
442 	if (mode == DEFAULTMODE || mode == INVALID) {
443 		char *var = getenv(SUNW_PKG_SERVERMODE);
444 
445 		if (var != NULL)
446 			defmode = pkgparsemode(var);
447 		else
448 			defmode = DEFAULTMODE;
449 	} else {
450 		defmode = mode;
451 	}
452 }
453 
454 start_mode_t
455 pkgservergetmode(void)
456 {
457 	if (defmode == INVALID)
458 		pkgserversetmode(DEFAULTMODE);
459 	return (defmode);
460 }
461 
462 void
463 pkgcloseserver(PKGserver server)
464 {
465 
466 	if (server->fp != NULL)
467 		(void) fclose(server->fp);
468 	free(server->curbuf);
469 	if (server->onetime) {
470 		pkgcmd_t cmd;
471 		cmd.cmd = PKG_EXIT;
472 		(void) pkgcmd(server, &cmd, sizeof (cmd), NULL, NULL, NULL);
473 	}
474 	(void) close(server->door);
475 	if (server == current_server)
476 		current_server = NULL;
477 	free(server);
478 }
479 
480 int
481 pkgcmd(PKGserver srv, void *cmd, size_t len, char **result, size_t *rlen,
482     int *fd)
483 {
484 	door_arg_t da;
485 
486 	da.data_ptr = cmd;
487 	da.data_size = len;
488 	da.desc_ptr = NULL;
489 	da.desc_num = 0;
490 	da.rbuf = result == NULL ? NULL : *result;
491 	da.rsize = rlen == NULL ? 0 : *rlen;
492 
493 	if (door_call(srv->door, &da) != 0) {
494 		if (((pkgcmd_t *)cmd)->cmd == PKG_EXIT && errno == EINTR)
495 			return (0);
496 		return (-1);
497 	}
498 
499 	if (da.desc_ptr != NULL) {
500 		int i = 0;
501 		if (fd != NULL)
502 			*fd = da.desc_ptr[i++].d_data.d_desc.d_descriptor;
503 		for (; i < da.desc_num; i++)
504 			(void) close(da.desc_ptr[i].d_data.d_desc.d_descriptor);
505 	}
506 	/* Error return */
507 	if (da.data_size == sizeof (int)) {
508 		int x = *(int *)da.data_ptr;
509 		if (x != 0) {
510 			if (result == NULL || da.rbuf != *result)
511 				(void) munmap(da.rbuf, da.rsize);
512 			return (x);
513 		}
514 	}
515 
516 	/* Other result */
517 	if (result != NULL) {
518 		/* Make sure that the result is at the start of the buffer. */
519 		if (da.data_ptr != NULL && da.rbuf != da.data_ptr)
520 			(void) memmove(da.rbuf, da.data_ptr, da.data_size);
521 		*result = da.rbuf;
522 		*rlen = da.data_size;
523 	} else if (da.rbuf != NULL) {
524 		(void) munmap(da.rbuf, da.rsize);
525 	}
526 	return (0);
527 }
528 
529 /*
530  * Pkgsync:
531  *	If the server is running, make sure that the contents
532  *	file is written.
533  *	If the server is not running, check for the log file;
534  *	if there's a non-empty log file, we need to start the server
535  *	as it will incorporate the log file into the contents file.
536  *	And then check if the door is present.  If it doesn't, we don't
537  *	need to call it.
538  */
539 
540 boolean_t
541 pkgsync_needed(const char *root, const char *sadmdir, boolean_t want_quit)
542 {
543 	struct stat pbuf;
544 	char pkgfile[PATH_MAX];
545 	boolean_t sync_needed, running;
546 	int fd;
547 	struct door_info di;
548 
549 	pkgfilename(pkgfile, root, sadmdir, PKGLOG);
550 
551 	sync_needed = stat(pkgfile, &pbuf) == 0 && pbuf.st_size > 0;
552 
553 	if (!sync_needed && !want_quit)
554 		return (B_FALSE);
555 
556 	pkgfilename(pkgfile, root, sadmdir, PKGDOOR);
557 
558 	/* sync_needed == B_TRUE || want_quit == B_TRUE */
559 	running = B_FALSE;
560 
561 	fd = open(pkgfile, O_RDWR);
562 
563 	if (fd >= 0) {
564 		if (door_info(fd, &di) == 0) {
565 			/* It's mounted, so the server is likely there */
566 			running = B_TRUE;
567 		}
568 		(void) close(fd);
569 	}
570 	return (running || sync_needed);
571 }
572 
573 int
574 pkgsync(const char *root, const char *sadmdir, boolean_t force_quit)
575 {
576 	void *server;
577 	pkgcmd_t cmd;
578 
579 	/* No need to write contents file; don't start if not running */
580 	if (!pkgsync_needed(root, sadmdir, force_quit))
581 		return (0);
582 
583 	server = pkgopenserver_i(root, sadmdir, B_FALSE, FLUSH_LOG);
584 	/*
585 	 * We're assuming that it started the server and exited immediately.
586 	 * If that didn't work, there's nothing we can do.
587 	 */
588 	if (server == NULL)
589 		return (0);
590 
591 	cmd.cmd = force_quit ? PKG_EXIT : PKG_DUMP;
592 
593 	(void) pkgcmd(server, &cmd, sizeof (cmd), NULL, NULL, NULL);
594 	(void) pkgcloseserver(server);
595 	return (0);
596 }
597 
598 int
599 pkgservercommitfile(VFP_T *a_vfp, PKGserver server)
600 {
601 	size_t len = vfpGetModifiedLen(a_vfp);
602 	ssize_t rem = len;
603 	size_t off;
604 	pkgfilter_t *pcmd;
605 	char *map = a_vfp->_vfpStart;
606 
607 	if (len < PKGADD_MAX)
608 		pcmd = alloca(sizeof (*pcmd) + len);
609 	else
610 		pcmd = alloca(sizeof (*pcmd) + PKGADD_MAX);
611 
612 
613 	off = 0;
614 	pcmd->cmd = PKG_ADDLINES;
615 	while (rem > 0) {
616 		char *p = map + off;
617 		len = rem;
618 
619 		if (len >= PKGADD_MAX) {
620 			len = PKGADD_MAX - 1;
621 			while (p[len] != '\n' && len > 0)
622 				len--;
623 			if (p[len] != '\n')
624 				return (-1);
625 			len++;
626 		}
627 		(void) memcpy(&pcmd->buf[0], p, len);
628 		pcmd->len = len;
629 
630 		if (pkgcmd(server, pcmd, sizeof (*pcmd) + len - 1,
631 		    NULL, NULL, NULL) != 0) {
632 			return (-1);
633 		}
634 		rem -= len;
635 		off += len;
636 	}
637 	pcmd->len = 0;
638 	pcmd->cmd = PKG_PKGSYNC;
639 	if (pkgcmd(server, pcmd, sizeof (*pcmd), NULL, NULL, NULL) != 0)
640 		return (-1);
641 
642 	/* Mark it unmodified. */
643 	vfpTruncate(a_vfp);
644 	(void) vfpClearModified(a_vfp);
645 
646 	return (0);
647 }
648 
649 int
650 pkgopenfilter(PKGserver server, const char *filt)
651 {
652 	int fd;
653 	pkgfilter_t *pfcmd;
654 	int clen = filt == NULL ? 0 : strlen(filt);
655 	int len = sizeof (*pfcmd) + clen;
656 
657 	pfcmd = alloca(len);
658 
659 	if (server->fp != NULL) {
660 		(void) fclose(server->fp);
661 		server->fp = NULL;
662 	}
663 
664 	pfcmd->cmd = PKG_FILTER;
665 	pfcmd->len = clen;
666 	if (filt != NULL)
667 		(void) strcpy(pfcmd->buf, filt);
668 
669 	fd = -1;
670 
671 	if (pkgcmd(server, pfcmd, len, NULL, NULL, &fd) != 0 || fd == -1) {
672 		progerr(gettext(ERR_START_FILTER));
673 		return (-1);
674 	}
675 	(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
676 
677 	server->fp = fdopen(fd, "r");
678 	if (server->fp == NULL) {
679 		(void) close(fd);
680 		progerr(gettext(ERR_START_FILTER));
681 		return (-1);
682 	}
683 	return (0);
684 }
685 
686 void
687 pkgclosefilter(PKGserver server)
688 {
689 	if (server->fp != NULL) {
690 		(void) fclose(server->fp);
691 		server->fp = NULL;
692 	}
693 }
694 
695 /*
696  * Report the next entry from the contents file.
697  */
698 char *
699 pkggetentry(PKGserver server, int *len, int *pathlen)
700 {
701 	int num[2];
702 
703 	if (server->fp == NULL)
704 		return (NULL);
705 
706 	if (feof(server->fp) || ferror(server->fp))
707 		return (NULL);
708 
709 	if (fread(num, sizeof (int), 2, server->fp) != 2)
710 		return (NULL);
711 
712 	if (num[0] > server->buflen) {
713 		free(server->curbuf);
714 		server->buflen = num[0];
715 		server->curbuf = malloc(server->buflen);
716 		if (server->curbuf == NULL)
717 			return (NULL);
718 	}
719 	if (fread(server->curbuf, 1, num[0], server->fp) != num[0])
720 		return (NULL);
721 
722 	*len = num[0];
723 	*pathlen = num[1];
724 
725 	return (server->curbuf);
726 }
727 
728 char *
729 pkggetentry_named(PKGserver server, const char *path, int *len, int *pathlen)
730 {
731 	int plen = strlen(path);
732 	pkgfilter_t *pcmd = alloca(sizeof (*pcmd) + plen);
733 	char *result;
734 	unsigned int rlen;
735 
736 	pcmd->cmd = PKG_FINDFILE;
737 	*pathlen = pcmd->len = plen;
738 	(void) memcpy(pcmd->buf, path, pcmd->len + 1);
739 
740 	result = server->curbuf;
741 	rlen = server->buflen;
742 
743 	if (pkgcmd(server, pcmd, sizeof (*pcmd) + pcmd->len,
744 	    &result, &rlen, NULL) != 0) {
745 		return (NULL);
746 	}
747 	if (rlen == 0)
748 		return (NULL);
749 
750 	/* Result too big */
751 	if (result != server->curbuf) {
752 		free(server->curbuf);
753 		server->buflen = rlen;
754 		server->curbuf = malloc(server->buflen);
755 		if (server->curbuf == NULL)
756 			return (NULL);
757 		(void) memcpy(server->curbuf, result, rlen);
758 		(void) munmap(result, rlen);
759 	}
760 	*len = rlen;
761 
762 	return (server->curbuf);
763 }
764