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