xref: /illumos-gate/usr/src/lib/libpkg/common/pkgserv.c (revision 23f76dc290ca84b3df56bf58be0a4b8e3a7e38ab)
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