Bug Summary

File:src/usr.bin/rdist/common.c
Warning:line 623, column 8
Although the value stored to 'len' is used in the enclosing expression, the value is never actually read from 'len'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name common.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.bin/rdist/obj -resource-dir /usr/local/lib/clang/13.0.0 -I . -I /usr/src/usr.bin/rdist -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.bin/rdist/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/usr.bin/rdist/common.c
1/* $OpenBSD: common.c,v 1.40 2019/06/28 13:35:03 deraadt Exp $ */
2
3/*
4 * Copyright (c) 1983 Regents of the University of California.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <sys/stat.h>
33#include <sys/time.h>
34#include <sys/wait.h>
35
36#include <errno(*__errno()).h>
37#include <fcntl.h>
38#include <grp.h>
39#include <limits.h>
40#include <paths.h>
41#include <stdarg.h>
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45#include <unistd.h>
46
47#include "defs.h"
48
49/*
50 * Things common to both the client and server.
51 */
52
53/*
54 * Variables common to both client and server
55 */
56char host[HOST_NAME_MAX255+1]; /* Name of this host */
57uid_t userid = (uid_t)-1; /* User's UID */
58gid_t groupid = (gid_t)-1; /* User's GID */
59gid_t gidset[NGROUPS_MAX16]; /* User's GID list */
60int gidsetlen = 0; /* Number of GIDS in list */
61char *homedir = NULL((void *)0); /* User's $HOME */
62char *locuser = NULL((void *)0); /* Local User's name */
63int isserver = FALSE0; /* We're the server */
64int amchild = 0; /* This PID is a child */
65int do_fork = 1; /* Fork child process */
66char *currenthost = NULL((void *)0); /* Current client hostname */
67char *progname = NULL((void *)0); /* Name of this program */
68int rem_r = -1; /* Client file descriptor */
69int rem_w = -1; /* Client file descriptor */
70volatile sig_atomic_t contimedout = FALSE0; /* Connection timed out */
71int rtimeout = RTIMEOUT900; /* Response time out */
72jmp_buf finish_jmpbuf; /* Finish() jmp buffer */
73int setjmp_ok = FALSE0; /* setjmp()/longjmp() status */
74char **realargv; /* Real main() argv */
75int realargc; /* Real main() argc */
76opt_t options = 0; /* Global install options */
77char defowner[64] = "bin"; /* Default owner */
78char defgroup[64] = "bin"; /* Default group */
79
80static int sendcmdmsg(int, char *, size_t);
81static ssize_t remread(int, u_char *, size_t);
82static int remmore(void);
83
84/*
85 * Front end to write() that handles partial write() requests.
86 */
87ssize_t
88xwrite(int fd, void *buf, size_t len)
89{
90 size_t nleft = len;
91 ssize_t nwritten;
92 char *ptr = buf;
93
94 while (nleft > 0) {
95 if ((nwritten = write(fd, ptr, nleft)) <= 0) {
96 return nwritten;
97 }
98 nleft -= nwritten;
99 ptr += nwritten;
100 }
101
102 return len;
103}
104
105/*
106 * Do run-time initialization
107 */
108int
109init(int argc, char **argv, char **envp)
110{
111 struct passwd *pw;
112 int i;
113
114 /*
115 * Save a copy of our argc and argv before setargs() overwrites them
116 */
117 realargc = argc;
118 realargv = xmalloc(sizeof(char *) * (argc+1));
119 for (i = 0; i < argc; i++)
120 realargv[i] = xstrdup(argv[i]);
121
122 pw = getpwuid(userid = getuid());
123 if (pw == NULL((void *)0)) {
124 error("Your user id (%u) is not known to this system.",
125 getuid());
126 return(-1);
127 }
128
129 debugmsg(DM_MISC0x10, "UserID = %u pwname = '%s' home = '%s'\n",
130 userid, pw->pw_name, pw->pw_dir);
131 homedir = xstrdup(pw->pw_dir);
132 locuser = xstrdup(pw->pw_name);
133 groupid = pw->pw_gid;
134 gidsetlen = getgroups(NGROUPS_MAX16, gidset);
135 gethostname(host, sizeof(host));
136#if 0
137 if ((cp = strchr(host, '.')) != NULL((void *)0))
138 *cp = CNULL'\0';
139#endif
140
141 /*
142 * If we're not root, disable paranoid ownership checks
143 * since normal users cannot chown() files.
144 */
145 if (!isserver && userid != 0) {
146 FLAG_ON(options, DO_NOCHKOWNER)options |= 0x0004000;
147 FLAG_ON(options, DO_NOCHKGROUP)options |= 0x0010000;
148 }
149
150 return(0);
151}
152
153/*
154 * Finish things up before ending.
155 */
156void
157finish(void)
158{
159 debugmsg(DM_CALL0x01,
160 "finish() called: do_fork = %d amchild = %d isserver = %d",
161 do_fork, amchild, isserver);
162 cleanup(0);
163
164 /*
165 * There's no valid finish_jmpbuf for the rdist master parent.
166 */
167 if (!do_fork || amchild || isserver) {
168
169 if (!setjmp_ok) {
170#ifdef DEBUG_SETJMP
171 error("attemping longjmp() without target");
172 abort();
173#else
174 exit(1);
175#endif
176 }
177
178 longjmp(finish_jmpbuf, 1);
179 /*NOTREACHED*/
180 error("Unexpected failure of longjmp() in finish()");
181 exit(2);
182 } else
183 exit(1);
184}
185
186/*
187 * Handle lost connections
188 */
189void
190lostconn(void)
191{
192 /* Prevent looping */
193 (void) signal(SIGPIPE13, SIG_IGN(void (*)(int))1);
194
195 rem_r = rem_w = -1; /* Ensure we don't try to send to server */
196 checkhostname();
197 error("Lost connection to %s",
198 (currenthost) ? currenthost : "(unknown)");
199
200 finish();
201}
202
203/*
204 * General signal handler
205 */
206void
207sighandler(int sig)
208{
209 int save_errno = errno(*__errno());
210
211 /* XXX signal race */
212 debugmsg(DM_CALL0x01, "sighandler() received signal %d\n", sig);
213
214 switch (sig) {
215 case SIGALRM14:
216 contimedout = TRUE1;
217 /* XXX signal race */
218 checkhostname();
219 error("Response time out");
220 finish();
221 break;
222
223 case SIGPIPE13:
224 /* XXX signal race */
225 lostconn();
226 break;
227
228 case SIGFPE8:
229 debug = !debug;
230 break;
231
232 case SIGHUP1:
233 case SIGINT2:
234 case SIGQUIT3:
235 case SIGTERM15:
236 /* XXX signal race */
237 finish();
238 break;
239
240 default:
241 /* XXX signal race */
242 fatalerr("No signal handler defined for signal %d.", sig);
243 }
244 errno(*__errno()) = save_errno;
245}
246
247/*
248 * Function to actually send the command char and message to the
249 * remote host.
250 */
251static int
252sendcmdmsg(int cmd, char *msg, size_t msgsize)
253{
254 int len;
255
256 if (rem_w < 0)
257 return(-1);
258
259 /*
260 * All commands except C_NONE should have a newline
261 */
262 if (cmd != C_NONE'=' && !strchr(msg + 1, '\n'))
263 (void) strlcat(msg + 1, "\n", msgsize - 1);
264
265 if (cmd == C_NONE'=')
266 len = strlen(msg);
267 else {
268 len = strlen(msg + 1) + 1;
269 msg[0] = cmd;
270 }
271
272 debugmsg(DM_PROTO0x02, ">>> Cmd = %c (\\%3.3o) Msg = \"%.*s\"",
273 cmd, cmd,
274 (cmd == C_NONE'=') ? len-1 : len-2,
275 (cmd == C_NONE'=') ? msg : msg + 1);
276
277 return(!(xwrite(rem_w, msg, len) == len));
278}
279
280/*
281 * Send a command message to the remote host.
282 * Called as sendcmd(char cmdchar, char *fmt, arg1, arg2, ...)
283 * The fmt may be NULL, in which case there are no args.
284 */
285int
286sendcmd(char cmd, const char *fmt, ...)
287{
288 static char buf[BUFSIZ1024];
289 va_list args;
290
291 va_start(args, fmt)__builtin_va_start(args, fmt);
292 if (fmt)
293 (void) vsnprintf(buf + (cmd != C_NONE'='),
294 sizeof(buf) - (cmd != C_NONE'='), fmt, args);
295 else
296 buf[1] = CNULL'\0';
297 va_end(args)__builtin_va_end(args);
298
299 return(sendcmdmsg(cmd, buf, sizeof(buf)));
300}
301
302/*
303 * Internal variables and routines for reading lines from the remote.
304 */
305static u_char rembuf[BUFSIZ1024];
306static u_char *remptr;
307static ssize_t remleft;
308
309#define remc()(--remleft < 0 ? remmore() : *remptr++) (--remleft < 0 ? remmore() : *remptr++)
310
311/*
312 * Back end to remote read()
313 */
314static ssize_t
315remread(int fd, u_char *buf, size_t bufsiz)
316{
317 return(read(fd, (char *)buf, bufsiz));
318}
319
320static int
321remmore(void)
322{
323 (void) signal(SIGALRM14, sighandler);
324 (void) alarm(rtimeout);
325
326 remleft = remread(rem_r, rembuf, sizeof(rembuf));
327
328 (void) alarm(0);
329
330 if (remleft < 0)
331 return (-2); /* error */
332 if (remleft == 0)
333 return (-1); /* EOF */
334 remptr = rembuf;
335 remleft--;
336 return (*remptr++);
337}
338
339/*
340 * Read an input line from the remote. Return the number of bytes
341 * stored (equivalent to strlen(p)). If `cleanup' is set, EOF at
342 * the beginning of a line is returned as EOF (-1); other EOFs, or
343 * errors, call cleanup() or lostconn(). In other words, unless
344 * the third argument is nonzero, this routine never returns failure.
345 */
346int
347remline(u_char *buffer, int space, int doclean)
348{
349 int c, left = space;
350 u_char *p = buffer;
351
352 if (rem_r < 0) {
353 error("Cannot read remote input: Remote descriptor not open.");
354 return(-1);
355 }
356
357 while (left > 0) {
358 if ((c = remc()(--remleft < 0 ? remmore() : *remptr++)) < -1) { /* error */
359 if (doclean) {
360 finish();
361 /*NOTREACHED*/
362 }
363 lostconn();
364 /*NOTREACHED*/
365 }
366 if (c == -1) { /* got EOF */
367 if (doclean) {
368 if (left == space)
369 return (-1);/* signal proper EOF */
370 finish(); /* improper EOF */
371 /*NOTREACHED*/
372 }
373 lostconn();
374 /*NOTREACHED*/
375 }
376 if (c == '\n') {
377 *p = CNULL'\0';
378
379 if (debug) {
380 static char mbuf[BUFSIZ1024];
381
382 (void) snprintf(mbuf, sizeof(mbuf),
383 "<<< Cmd = %c (\\%3.3o) Msg = \"%s\"",
384 buffer[0], buffer[0],
385 buffer + 1);
386
387 debugmsg(DM_PROTO0x02, "%s", mbuf);
388 }
389
390 return (space - left);
391 }
392 *p++ = c;
393 left--;
394 }
395
396 /* this will probably blow the entire session */
397 error("remote input line too long");
398 p[-1] = CNULL'\0'; /* truncate */
399 return (space);
400}
401
402/*
403 * Non-line-oriented remote read.
404 */
405ssize_t
406readrem(char *p, ssize_t space)
407{
408 if (remleft <= 0) {
409 /*
410 * Set remote time out alarm.
411 */
412 (void) signal(SIGALRM14, sighandler);
413 (void) alarm(rtimeout);
414
415 remleft = remread(rem_r, rembuf, sizeof(rembuf));
416
417 (void) alarm(0);
418 remptr = rembuf;
419 }
420
421 if (remleft <= 0)
422 return (remleft);
423 if (remleft < space)
424 space = remleft;
425
426 memcpy(p, remptr, space);
427
428 remptr += space;
429 remleft -= space;
430
431 return (space);
432}
433
434/*
435 * Get the user name for the uid.
436 */
437char *
438getusername(uid_t uid, char *file, opt_t opts)
439{
440 static char buf[100];
441 static uid_t lastuid = (uid_t)-1;
442 const char *name;
443
444 /*
445 * The value of opts may have changed so we always
446 * do the opts check.
447 */
448 if (IS_ON(opts, DO_NUMCHKOWNER)(opts & 0x0080000)) {
449 (void) snprintf(buf, sizeof(buf), ":%u", uid);
450 return(buf);
451 }
452
453 /*
454 * Try to avoid passwd lookup.
455 */
456 if (lastuid == uid && buf[0] != '\0' && buf[0] != ':')
457 return(buf);
458
459 lastuid = uid;
460
461 if ((name = user_from_uid(uid, 1)) == NULL((void *)0)) {
462 if (IS_ON(opts, DO_DEFOWNER)(opts & 0x0800000) && !isserver)
463 (void) strlcpy(buf, defowner, sizeof(buf));
464 else {
465 message(MT_WARNING0x0010,
466 "%s: No password entry for uid %u", file, uid);
467 (void) snprintf(buf, sizeof(buf), ":%u", uid);
468 }
469 } else {
470 (void) strlcpy(buf, name, sizeof(buf));
471 }
472
473 return(buf);
474}
475
476/*
477 * Get the group name for the gid.
478 */
479char *
480getgroupname(gid_t gid, char *file, opt_t opts)
481{
482 static char buf[100];
483 static gid_t lastgid = (gid_t)-1;
484 const char *name;
485
486 /*
487 * The value of opts may have changed so we always
488 * do the opts check.
489 */
490 if (IS_ON(opts, DO_NUMCHKGROUP)(opts & 0x0040000)) {
491 (void) snprintf(buf, sizeof(buf), ":%u", gid);
492 return(buf);
493 }
494
495 /*
496 * Try to avoid group lookup.
497 */
498 if (lastgid == gid && buf[0] != '\0' && buf[0] != ':')
499 return(buf);
500
501 lastgid = gid;
502
503 if ((name = group_from_gid(gid, 1)) == NULL((void *)0)) {
504 if (IS_ON(opts, DO_DEFGROUP)(opts & 0x0400000) && !isserver)
505 (void) strlcpy(buf, defgroup, sizeof(buf));
506 else {
507 message(MT_WARNING0x0010, "%s: No name for group %u",
508 file, gid);
509 (void) snprintf(buf, sizeof(buf), ":%u", gid);
510 }
511 } else
512 (void) strlcpy(buf, name, sizeof(buf));
513
514 return(buf);
515}
516
517/*
518 * Read a response from the remote host.
519 */
520int
521response(void)
522{
523 static u_char resp[BUFSIZ1024];
524 u_char *s;
525 int n;
526
527 debugmsg(DM_CALL0x01, "response() start\n");
528
529 n = remline(s = resp, sizeof(resp), 0);
530
531 n--;
532 switch (*s++) {
533 case C_ACK'\5':
534 debugmsg(DM_PROTO0x02, "received ACK\n");
535 return(0);
536 case C_LOGMSG'\4':
537 if (n > 0) {
538 message(MT_CHANGE0x0020, "%s", s);
539 return(1);
540 }
541 debugmsg(DM_PROTO0x02, "received EMPTY logmsg\n");
542 return(0);
543 case C_NOTEMSG'\3':
544 if (s)
545 message(MT_NOTICE0x0100, "%s", s);
546 return(response());
547
548 default:
549 s--;
550 n++;
551 /* fall into... */
552
553 case C_ERRMSG'\1': /* Normal error message */
554 if (s)
555 message(MT_NERROR0x0002, "%s", s);
556 return(-1);
557
558 case C_FERRMSG'\2': /* Fatal error message */
559 if (s)
560 message(MT_FERROR0x0004, "%s", s);
561 finish();
562 return(-1);
563 }
564 /*NOTREACHED*/
565}
566
567/*
568 * This should be in expand.c but the other routines call other modules
569 * that we don't want to load in.
570 *
571 * Expand file names beginning with `~' into the
572 * user's home directory path name. Return a pointer in buf to the
573 * part corresponding to `file'.
574 */
575char *
576exptilde(char *ebuf, char *file, size_t ebufsize)
577{
578 struct passwd *pw;
579 char *pw_dir, *rest;
580 static char lastuser[_PW_NAME_LEN31 + 1];
581 static char lastdir[PATH_MAX1024];
582 size_t len;
583
584 if (*file != '~') {
585notilde:
586 (void) strlcpy(ebuf, file, ebufsize);
587 return(ebuf);
588 }
589 pw_dir = homedir;
590 if (*++file == CNULL'\0') {
591 rest = NULL((void *)0);
592 } else if (*file == '/') {
593 rest = file;
594 } else {
595 rest = file;
596 while (*rest && *rest != '/')
597 rest++;
598 if (*rest == '/')
599 *rest = CNULL'\0';
600 else
601 rest = NULL((void *)0);
602 if (strcmp(locuser, file) != 0) {
603 if (strcmp(lastuser, file) != 0) {
604 if ((pw = getpwnam(file)) == NULL((void *)0)) {
605 error("%s: unknown user name", file);
606 if (rest != NULL((void *)0))
607 *rest = '/';
608 return(NULL((void *)0));
609 }
610 strlcpy(lastuser, pw->pw_name, sizeof(lastuser));
611 strlcpy(lastdir, pw->pw_dir, sizeof(lastdir));
612 }
613 pw_dir = lastdir;
614 }
615 if (rest != NULL((void *)0))
616 *rest = '/';
617 }
618 if ((len = strlcpy(ebuf, pw_dir, ebufsize)) >= ebufsize)
619 goto notilde;
620 pw_dir = ebuf + len;
621 if (rest != NULL((void *)0)) {
622 pw_dir++;
623 if ((len = strlcat(ebuf, rest, ebufsize)) >= ebufsize)
Although the value stored to 'len' is used in the enclosing expression, the value is never actually read from 'len'
624 goto notilde;
625 }
626 return(pw_dir);
627}
628
629
630
631/*
632 * Set access and modify times of a given file
633 */
634int
635setfiletime(char *file, time_t atime, time_t mtime)
636{
637 struct timeval tv[2];
638
639 if (atime != 0 && mtime != 0) {
640 tv[0].tv_sec = atime;
641 tv[1].tv_sec = mtime;
642 tv[0].tv_usec = tv[1].tv_usec = 0;
643 return (utimes(file, tv));
644 } else /* Set to current time */
645 return (utimes(file, NULL((void *)0)));
646}
647
648/*
649 * Get version info
650 */
651char *
652getversion(void)
653{
654 static char buff[BUFSIZ1024];
655
656 (void) snprintf(buff, sizeof(buff),
657 "Version %s.%d (%s) - Protocol Version %d, Release %s, Patch level %d",
658 DISTVERSION"0.92", PATCHLEVEL0, DISTSTATUS"BETA",
659 VERSION6, DISTVERSION"0.92", PATCHLEVEL0);
660
661 return(buff);
662}
663
664/*
665 * Execute a shell command to handle special cases.
666 * This is now common to both server and client
667 */
668void
669runcommand(char *cmd)
670{
671 ssize_t nread;
672 pid_t pid, wpid;
673 char *cp, *s;
674 char sbuf[BUFSIZ1024], buf[BUFSIZ1024];
675 int fd[2], status;
676
677 if (pipe(fd) == -1) {
678 error("pipe of %s failed: %s", cmd, SYSERRstrerror((*__errno())));
679 return;
680 }
681
682 if ((pid = fork()) == 0) {
683 /*
684 * Return everything the shell commands print.
685 */
686 (void) close(0);
687 (void) close(1);
688 (void) close(2);
689 (void) open(_PATH_DEVNULL"/dev/null", O_RDONLY0x0000);
690 (void) dup(fd[PIPE_WRITE1]);
691 (void) dup(fd[PIPE_WRITE1]);
692 (void) close(fd[PIPE_READ0]);
693 (void) close(fd[PIPE_WRITE1]);
694 (void) execl(_PATH_BSHELL"/bin/sh", "sh", "-c", cmd, (char *)NULL((void *)0));
695 _exit(127);
696 }
697 (void) close(fd[PIPE_WRITE1]);
698 s = sbuf;
699 *s++ = C_LOGMSG'\4';
700 while ((nread = read(fd[PIPE_READ0], buf, sizeof(buf))) > 0) {
701 cp = buf;
702 do {
703 *s++ = *cp++;
704 if (cp[-1] != '\n') {
705 if (s < (char *) &sbuf[sizeof(sbuf)-1])
706 continue;
707 *s++ = '\n';
708 }
709 /*
710 * Throw away blank lines.
711 */
712 if (s == &sbuf[2]) {
713 s--;
714 continue;
715 }
716 if (isserver)
717 (void) xwrite(rem_w, sbuf, s - sbuf);
718 else {
719 *s = CNULL'\0';
720 message(MT_INFO0x0040, "%s", sbuf+1);
721 }
722 s = &sbuf[1];
723 } while (--nread);
724 }
725 if (s > (char *) &sbuf[1]) {
726 *s++ = '\n';
727 if (isserver)
728 (void) xwrite(rem_w, sbuf, s - sbuf);
729 else {
730 *s = CNULL'\0';
731 message(MT_INFO0x0040, "%s", sbuf+1);
732 }
733 }
734 while ((wpid = wait(&status)) != pid && wpid != -1)
735 ;
736 if (wpid == -1)
737 status = -1;
738 (void) close(fd[PIPE_READ0]);
739 if (status)
740 error("shell returned %d", status);
741 else if (isserver)
742 ack()(void) sendcmd('\5', ((void *)0));
743}
744
745/*
746 * Malloc with error checking
747 */
748void *
749xmalloc(size_t amt)
750{
751 void *ptr;
752
753 if ((ptr = malloc(amt)) == NULL((void *)0))
754 fatalerr("Cannot malloc %zu bytes of memory.", amt);
755
756 return (ptr);
757}
758
759/*
760 * realloc with error checking
761 */
762void *
763xrealloc(void *baseptr, size_t amt)
764{
765 void *new;
766
767 if ((new = realloc(baseptr, amt)) == NULL((void *)0))
768 fatalerr("Cannot realloc %zu bytes of memory.", amt);
769
770 return (new);
771}
772
773/*
774 * calloc with error checking
775 */
776void *
777xcalloc(size_t num, size_t esize)
778{
779 void *ptr;
780
781 if ((ptr = calloc(num, esize)) == NULL((void *)0))
782 fatalerr("Cannot calloc %zu * %zu = %zu bytes of memory.",
783 num, esize, num * esize);
784
785 return (ptr);
786}
787
788/*
789 * Strdup with error checking
790 */
791char *
792xstrdup(const char *str)
793{
794 size_t len = strlen(str) + 1;
795 char *nstr = xmalloc(len);
796
797 return (memcpy(nstr, str, len));
798}
799
800/*
801 * Private version of basename()
802 */
803char *
804xbasename(char *path)
805{
806 char *cp;
807
808 if ((cp = strrchr(path, '/')) != NULL((void *)0))
809 return(cp+1);
810 else
811 return(path);
812}
813
814/*
815 * Take a colon (':') separated path to a file and
816 * search until a component of that path is found and
817 * return the found file name.
818 */
819char *
820searchpath(char *path)
821{
822 char *file;
823 char *space;
824 int found;
825 struct stat statbuf;
826
827 for (found = 0; !found && (file = strsep(&path, ":")) != NULL((void *)0); ) {
828 if ((space = strchr(file, ' ')) != NULL((void *)0))
829 *space = CNULL'\0';
830 found = stat(file, &statbuf) == 0;
831 if (space)
832 *space = ' '; /* Put back what we zapped */
833 }
834 return (file);
835}