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