xref: /illumos-gate/usr/src/lib/libpkg/common/pkgserv.c (revision 2bbdd445a21f9d61f4a0ca0faf05d5ceb2bd91f3)
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 	/*NOTREACHED*/
438 }
439 
440 void
441 pkgserversetmode(start_mode_t mode)
442 {
443 	if (mode == DEFAULTMODE || mode == INVALID) {
444 		char *var = getenv(SUNW_PKG_SERVERMODE);
445 
446 		if (var != NULL)
447 			defmode = pkgparsemode(var);
448 		else
449 			defmode = DEFAULTMODE;
450 	} else {
451 		defmode = mode;
452 	}
453 }
454 
455 start_mode_t
456 pkgservergetmode(void)
457 {
458 	if (defmode == INVALID)
459 		pkgserversetmode(DEFAULTMODE);
460 	return (defmode);
461 }
462 
463 void
464 pkgcloseserver(PKGserver server)
465 {
466 
467 	if (server->fp != NULL)
468 		(void) fclose(server->fp);
469 	free(server->curbuf);
470 	if (server->onetime) {
471 		pkgcmd_t cmd;
472 		cmd.cmd = PKG_EXIT;
473 		(void) pkgcmd(server, &cmd, sizeof (cmd), NULL, NULL, NULL);
474 	}
475 	(void) close(server->door);
476 	if (server == current_server)
477 		current_server = NULL;
478 	free(server);
479 }
480 
481 int
482 pkgcmd(PKGserver srv, void *cmd, size_t len, char **result, size_t *rlen,
483     int *fd)
484 {
485 	door_arg_t da;
486 
487 	da.data_ptr = cmd;
488 	da.data_size = len;
489 	da.desc_ptr = NULL;
490 	da.desc_num = 0;
491 	da.rbuf = result == NULL ? NULL : *result;
492 	da.rsize = rlen == NULL ? 0 : *rlen;
493 
494 	if (door_call(srv->door, &da) != 0) {
495 		if (((pkgcmd_t *)cmd)->cmd == PKG_EXIT && errno == EINTR)
496 			return (0);
497 		return (-1);
498 	}
499 
500 	if (da.desc_ptr != NULL) {
501 		int i = 0;
502 		if (fd != NULL)
503 			*fd = da.desc_ptr[i++].d_data.d_desc.d_descriptor;
504 		for (; i < da.desc_num; i++)
505 			(void) close(da.desc_ptr[i].d_data.d_desc.d_descriptor);
506 	}
507 	/* Error return */
508 	if (da.data_size == sizeof (int)) {
509 		int x = *(int *)da.data_ptr;
510 		if (x != 0) {
511 			if (result == NULL || da.rbuf != *result)
512 				(void) munmap(da.rbuf, da.rsize);
513 			return (x);
514 		}
515 	}
516 
517 	/* Other result */
518 	if (result != NULL) {
519 		/* Make sure that the result is at the start of the buffer. */
520 		if (da.data_ptr != NULL && da.rbuf != da.data_ptr)
521 			(void) memmove(da.rbuf, da.data_ptr, da.data_size);
522 		*result = da.rbuf;
523 		*rlen = da.data_size;
524 	} else if (da.rbuf != NULL) {
525 		(void) munmap(da.rbuf, da.rsize);
526 	}
527 	return (0);
528 }
529 
530 /*
531  * Pkgsync:
532  *	If the server is running, make sure that the contents
533  *	file is written.
534  *	If the server is not running, check for the log file;
535  *	if there's a non-empty log file, we need to start the server
536  *	as it will incorporate the log file into the contents file.
537  *	And then check if the door is present.  If it doesn't, we don't
538  *	need to call it.
539  */
540 
541 boolean_t
542 pkgsync_needed(const char *root, const char *sadmdir, boolean_t want_quit)
543 {
544 	struct stat pbuf;
545 	char pkgfile[PATH_MAX];
546 	boolean_t sync_needed, running;
547 	int fd;
548 	struct door_info di;
549 
550 	pkgfilename(pkgfile, root, sadmdir, PKGLOG);
551 
552 	sync_needed = stat(pkgfile, &pbuf) == 0 && pbuf.st_size > 0;
553 
554 	if (!sync_needed && !want_quit)
555 		return (B_FALSE);
556 
557 	pkgfilename(pkgfile, root, sadmdir, PKGDOOR);
558 
559 	/* sync_needed == B_TRUE || want_quit == B_TRUE */
560 	running = B_FALSE;
561 
562 	fd = open(pkgfile, O_RDWR);
563 
564 	if (fd >= 0) {
565 		if (door_info(fd, &di) == 0) {
566 			/* It's mounted, so the server is likely there */
567 			running = B_TRUE;
568 		}
569 		(void) close(fd);
570 	}
571 	return (running || sync_needed);
572 }
573 
574 int
575 pkgsync(const char *root, const char *sadmdir, boolean_t force_quit)
576 {
577 	void *server;
578 	pkgcmd_t cmd;
579 
580 	/* No need to write contents file; don't start if not running */
581 	if (!pkgsync_needed(root, sadmdir, force_quit))
582 		return (0);
583 
584 	server = pkgopenserver_i(root, sadmdir, B_FALSE, FLUSH_LOG);
585 	/*
586 	 * We're assuming that it started the server and exited immediately.
587 	 * If that didn't work, there's nothing we can do.
588 	 */
589 	if (server == NULL)
590 		return (0);
591 
592 	cmd.cmd = force_quit ? PKG_EXIT : PKG_DUMP;
593 
594 	(void) pkgcmd(server, &cmd, sizeof (cmd), NULL, NULL, NULL);
595 	(void) pkgcloseserver(server);
596 	return (0);
597 }
598 
599 int
600 pkgservercommitfile(VFP_T *a_vfp, PKGserver server)
601 {
602 	size_t len = vfpGetModifiedLen(a_vfp);
603 	ssize_t rem = len;
604 	size_t off;
605 	pkgfilter_t *pcmd;
606 	char *map = a_vfp->_vfpStart;
607 
608 	if (len < PKGADD_MAX)
609 		pcmd = alloca(sizeof (*pcmd) + len);
610 	else
611 		pcmd = alloca(sizeof (*pcmd) + PKGADD_MAX);
612 
613 
614 	off = 0;
615 	pcmd->cmd = PKG_ADDLINES;
616 	while (rem > 0) {
617 		char *p = map + off;
618 		len = rem;
619 
620 		if (len >= PKGADD_MAX) {
621 			len = PKGADD_MAX - 1;
622 			while (p[len] != '\n' && len > 0)
623 				len--;
624 			if (p[len] != '\n')
625 				return (-1);
626 			len++;
627 		}
628 		(void) memcpy(&pcmd->buf[0], p, len);
629 		pcmd->len = len;
630 
631 		if (pkgcmd(server, pcmd, sizeof (*pcmd) + len - 1,
632 		    NULL, NULL, NULL) != 0) {
633 			return (-1);
634 		}
635 		rem -= len;
636 		off += len;
637 	}
638 	pcmd->len = 0;
639 	pcmd->cmd = PKG_PKGSYNC;
640 	if (pkgcmd(server, pcmd, sizeof (*pcmd), NULL, NULL, NULL) != 0)
641 		return (-1);
642 
643 	/* Mark it unmodified. */
644 	vfpTruncate(a_vfp);
645 	(void) vfpClearModified(a_vfp);
646 
647 	return (0);
648 }
649 
650 int
651 pkgopenfilter(PKGserver server, const char *filt)
652 {
653 	int fd;
654 	pkgfilter_t *pfcmd;
655 	int clen = filt == NULL ? 0 : strlen(filt);
656 	int len = sizeof (*pfcmd) + clen;
657 
658 	pfcmd = alloca(len);
659 
660 	if (server->fp != NULL) {
661 		(void) fclose(server->fp);
662 		server->fp = NULL;
663 	}
664 
665 	pfcmd->cmd = PKG_FILTER;
666 	pfcmd->len = clen;
667 	if (filt != NULL)
668 		(void) strcpy(pfcmd->buf, filt);
669 
670 	fd = -1;
671 
672 	if (pkgcmd(server, pfcmd, len, NULL, NULL, &fd) != 0 || fd == -1) {
673 		progerr(gettext(ERR_START_FILTER));
674 		return (-1);
675 	}
676 	(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
677 
678 	server->fp = fdopen(fd, "r");
679 	if (server->fp == NULL) {
680 		(void) close(fd);
681 		progerr(gettext(ERR_START_FILTER));
682 		return (-1);
683 	}
684 	return (0);
685 }
686 
687 void
688 pkgclosefilter(PKGserver server)
689 {
690 	if (server->fp != NULL) {
691 		(void) fclose(server->fp);
692 		server->fp = NULL;
693 	}
694 }
695 
696 /*
697  * Report the next entry from the contents file.
698  */
699 char *
700 pkggetentry(PKGserver server, int *len, int *pathlen)
701 {
702 	int num[2];
703 
704 	if (server->fp == NULL)
705 		return (NULL);
706 
707 	if (feof(server->fp) || ferror(server->fp))
708 		return (NULL);
709 
710 	if (fread(num, sizeof (int), 2, server->fp) != 2)
711 		return (NULL);
712 
713 	if (num[0] > server->buflen) {
714 		free(server->curbuf);
715 		server->buflen = num[0];
716 		server->curbuf = malloc(server->buflen);
717 		if (server->curbuf == NULL)
718 			return (NULL);
719 	}
720 	if (fread(server->curbuf, 1, num[0], server->fp) != num[0])
721 		return (NULL);
722 
723 	*len = num[0];
724 	*pathlen = num[1];
725 
726 	return (server->curbuf);
727 }
728 
729 char *
730 pkggetentry_named(PKGserver server, const char *path, int *len, int *pathlen)
731 {
732 	int plen = strlen(path);
733 	pkgfilter_t *pcmd = alloca(sizeof (*pcmd) + plen);
734 	char *result;
735 	unsigned int rlen;
736 
737 	pcmd->cmd = PKG_FINDFILE;
738 	*pathlen = pcmd->len = plen;
739 	(void) memcpy(pcmd->buf, path, pcmd->len + 1);
740 
741 	result = server->curbuf;
742 	rlen = server->buflen;
743 
744 	if (pkgcmd(server, pcmd, sizeof (*pcmd) + pcmd->len,
745 	    &result, &rlen, NULL) != 0) {
746 		return (NULL);
747 	}
748 	if (rlen == 0)
749 		return (NULL);
750 
751 	/* Result too big */
752 	if (result != server->curbuf) {
753 		free(server->curbuf);
754 		server->buflen = rlen;
755 		server->curbuf = malloc(server->buflen);
756 		if (server->curbuf == NULL)
757 			return (NULL);
758 		(void) memcpy(server->curbuf, result, rlen);
759 		(void) munmap(result, rlen);
760 	}
761 	*len = rlen;
762 
763 	return (server->curbuf);
764 }
765