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
pkgfilename(char path[PATH_MAX],const char * root,const char * sadmdir,const char * file)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
free_xmnt(struct extmnttab * xmnt)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
copy_xmnt(const struct extmnttab * xmnt,struct extmnttab * saved)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
testdoor(char * path)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
pkgfindrealsadmdir(char fullpath[PATH_MAX],const char * root,const char ** sadmdir)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
pkgexit_close(void)244 pkgexit_close(void)
245 {
246 if (current_server != NULL)
247 pkgcloseserver(current_server);
248 }
249
250 static PKGserver
pkgopenserver_i(const char * root,const char * sadmdir,boolean_t readonly,start_mode_t mode)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
pkgopenserver(const char * root,const char * sadmdir,boolean_t ro)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
pkgparsemode(const char * mode)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 *
pkgmodeargument(start_mode_t mode)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
pkgserversetmode(start_mode_t mode)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
pkgservergetmode(void)460 pkgservergetmode(void)
461 {
462 if (defmode == INVALID)
463 pkgserversetmode(DEFAULTMODE);
464 return (defmode);
465 }
466
467 void
pkgcloseserver(PKGserver server)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
pkgcmd(PKGserver srv,void * cmd,size_t len,char ** result,size_t * rlen,int * fd)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
pkgsync_needed(const char * root,const char * sadmdir,boolean_t want_quit)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
pkgsync(const char * root,const char * sadmdir,boolean_t force_quit)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
pkgservercommitfile(VFP_T * a_vfp,PKGserver server)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
pkgopenfilter(PKGserver server,const char * filt)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
pkgclosefilter(PKGserver server)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 *
pkggetentry(PKGserver server,int * len,int * pathlen)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 *
pkggetentry_named(PKGserver server,const char * path,int * len,int * pathlen)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