Annotation of src/usr.bin/rdist/common.c, Revision 1.39
1.39 ! millert 1: /* $OpenBSD: common.c,v 1.38 2018/09/09 13:53:11 millert Exp $ */
1.3 deraadt 2:
1.1 dm 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.
1.21 millert 15: * 3. Neither the name of the University nor the names of its contributors
1.1 dm 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:
1.36 guenther 32: #include <sys/stat.h>
33: #include <sys/time.h>
34: #include <sys/wait.h>
35:
36: #include <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:
1.20 millert 47: #include "defs.h"
1.1 dm 48:
49: /*
50: * Things common to both the client and server.
51: */
52:
53: /*
54: * Variables common to both client and server
55: */
1.34 deraadt 56: char host[HOST_NAME_MAX+1]; /* Name of this host */
1.29 guenther 57: uid_t userid = (uid_t)-1; /* User's UID */
58: gid_t groupid = (gid_t)-1; /* User's GID */
1.39 ! millert 59: gid_t gidset[NGROUPS_MAX]; /* User's GID list */
! 60: int gidsetlen = 0; /* Number of GIDS in list */
1.1 dm 61: char *homedir = NULL; /* User's $HOME */
62: char *locuser = NULL; /* Local User's name */
63: int isserver = FALSE; /* We're the server */
64: int amchild = 0; /* This PID is a child */
65: int do_fork = 1; /* Fork child process */
66: char *currenthost = NULL; /* Current client hostname */
67: char *progname = NULL; /* Name of this program */
68: int rem_r = -1; /* Client file descriptor */
69: int rem_w = -1; /* Client file descriptor */
1.20 millert 70: volatile sig_atomic_t contimedout = FALSE; /* Connection timed out */
1.1 dm 71: int rtimeout = RTIMEOUT; /* Response time out */
72: jmp_buf finish_jmpbuf; /* Finish() jmp buffer */
1.2 dm 73: int setjmp_ok = FALSE; /* setjmp()/longjmp() status */
1.1 dm 74: char **realargv; /* Real main() argv */
75: int realargc; /* Real main() argc */
76: opt_t options = 0; /* Global install options */
1.20 millert 77: char defowner[64] = "bin"; /* Default owner */
78: char defgroup[64] = "bin"; /* Default group */
79:
80: static int sendcmdmsg(int, char *, size_t);
1.24 krw 81: static ssize_t remread(int, u_char *, size_t);
1.20 millert 82: static int remmore(void);
1.1 dm 83:
84: /*
85: * Front end to write() that handles partial write() requests.
86: */
1.31 guenther 87: ssize_t
88: xwrite(int fd, void *buf, size_t len)
1.1 dm 89: {
1.31 guenther 90: size_t nleft = len;
91: ssize_t nwritten;
1.12 mpech 92: char *ptr = buf;
1.1 dm 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: */
1.20 millert 108: int
109: init(int argc, char **argv, char **envp)
1.1 dm 110: {
1.38 millert 111: struct passwd *pw;
1.12 mpech 112: int i;
1.1 dm 113:
114: /*
115: * Save a copy of our argc and argv before setargs() overwrites them
116: */
117: realargc = argc;
1.37 mmcc 118: realargv = xmalloc(sizeof(char *) * (argc+1));
1.1 dm 119: for (i = 0; i < argc; i++)
1.8 millert 120: realargv[i] = xstrdup(argv[i]);
1.1 dm 121:
122: pw = getpwuid(userid = getuid());
123: if (pw == NULL) {
1.16 deraadt 124: error("Your user id (%u) is not known to this system.",
1.1 dm 125: getuid());
126: return(-1);
127: }
128:
1.20 millert 129: debugmsg(DM_MISC, "UserID = %u pwname = '%s' home = '%s'\n",
1.1 dm 130: userid, pw->pw_name, pw->pw_dir);
1.8 millert 131: homedir = xstrdup(pw->pw_dir);
132: locuser = xstrdup(pw->pw_name);
1.1 dm 133: groupid = pw->pw_gid;
1.39 ! millert 134: gidsetlen = getgroups(NGROUPS_MAX, gidset);
1.1 dm 135: gethostname(host, sizeof(host));
1.20 millert 136: #if 0
1.1 dm 137: if ((cp = strchr(host, '.')) != NULL)
138: *cp = CNULL;
1.20 millert 139: #endif
1.1 dm 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);
147: FLAG_ON(options, DO_NOCHKGROUP);
148: }
149:
150: return(0);
151: }
152:
153: /*
154: * Finish things up before ending.
155: */
1.20 millert 156: void
157: finish(void)
1.1 dm 158: {
159: debugmsg(DM_CALL,
160: "finish() called: do_fork = %d amchild = %d isserver = %d",
161: do_fork, amchild, isserver);
1.20 millert 162: cleanup(0);
1.1 dm 163:
164: /*
165: * There's no valid finish_jmpbuf for the rdist master parent.
166: */
167: if (!do_fork || amchild || isserver) {
1.2 dm 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:
1.1 dm 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: */
1.20 millert 189: void
190: lostconn(void)
1.1 dm 191: {
192: /* Prevent looping */
193: (void) signal(SIGPIPE, SIG_IGN);
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: */
1.20 millert 206: void
207: sighandler(int sig)
1.1 dm 208: {
1.7 millert 209: int save_errno = errno;
210:
1.26 deraadt 211: /* XXX signal race */
1.1 dm 212: debugmsg(DM_CALL, "sighandler() received signal %d\n", sig);
213:
214: switch (sig) {
215: case SIGALRM:
216: contimedout = TRUE;
1.26 deraadt 217: /* XXX signal race */
1.1 dm 218: checkhostname();
219: error("Response time out");
220: finish();
221: break;
222:
223: case SIGPIPE:
1.26 deraadt 224: /* XXX signal race */
1.1 dm 225: lostconn();
226: break;
227:
228: case SIGFPE:
229: debug = !debug;
230: break;
231:
232: case SIGHUP:
233: case SIGINT:
234: case SIGQUIT:
235: case SIGTERM:
1.26 deraadt 236: /* XXX signal race */
1.1 dm 237: finish();
238: break;
239:
240: default:
1.26 deraadt 241: /* XXX signal race */
1.1 dm 242: fatalerr("No signal handler defined for signal %d.", sig);
243: }
1.7 millert 244: errno = save_errno;
1.1 dm 245: }
246:
247: /*
248: * Function to actually send the command char and message to the
249: * remote host.
250: */
1.20 millert 251: static int
252: sendcmdmsg(int cmd, char *msg, size_t msgsize)
1.1 dm 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'))
1.18 millert 263: (void) strlcat(msg + 1, "\n", msgsize - 1);
1.1 dm 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_PROTO, ">>> 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, ...)
1.28 guenther 283: * The fmt may be NULL, in which case there are no args.
1.1 dm 284: */
1.20 millert 285: int
1.28 guenther 286: sendcmd(char cmd, const char *fmt, ...)
1.1 dm 287: {
288: static char buf[BUFSIZ];
289: va_list args;
290:
291: va_start(args, fmt);
292: if (fmt)
1.18 millert 293: (void) vsnprintf(buf + (cmd != C_NONE),
1.20 millert 294: sizeof(buf) - (cmd != C_NONE), fmt, args);
1.1 dm 295: else
296: buf[1] = CNULL;
297: va_end(args);
298:
1.18 millert 299: return(sendcmdmsg(cmd, buf, sizeof(buf)));
1.1 dm 300: }
301:
302: /*
303: * Internal variables and routines for reading lines from the remote.
304: */
305: static u_char rembuf[BUFSIZ];
306: static u_char *remptr;
1.24 krw 307: static ssize_t remleft;
1.1 dm 308:
309: #define remc() (--remleft < 0 ? remmore() : *remptr++)
310:
311: /*
312: * Back end to remote read()
313: */
1.24 krw 314: static ssize_t
315: remread(int fd, u_char *buf, size_t bufsiz)
1.1 dm 316: {
317: return(read(fd, (char *)buf, bufsiz));
318: }
319:
1.20 millert 320: static int
321: remmore(void)
1.1 dm 322: {
323: (void) signal(SIGALRM, 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: */
1.20 millert 346: int
347: remline(u_char *buffer, int space, int doclean)
1.1 dm 348: {
1.12 mpech 349: int c, left = space;
350: u_char *p = buffer;
1.1 dm 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()) < -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;
378:
379: if (debug) {
380: static char mbuf[BUFSIZ];
381:
1.20 millert 382: (void) snprintf(mbuf, sizeof(mbuf),
1.1 dm 383: "<<< Cmd = %c (\\%3.3o) Msg = \"%s\"",
384: buffer[0], buffer[0],
385: buffer + 1);
386:
387: debugmsg(DM_PROTO, "%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; /* truncate */
399: return (space);
400: }
401:
402: /*
403: * Non-line-oriented remote read.
404: */
1.24 krw 405: ssize_t
406: readrem(char *p, ssize_t space)
1.1 dm 407: {
408: if (remleft <= 0) {
409: /*
410: * Set remote time out alarm.
411: */
412: (void) signal(SIGALRM, 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:
1.20 millert 426: memcpy(p, remptr, space);
1.1 dm 427:
428: remptr += space;
429: remleft -= space;
430:
431: return (space);
432: }
433:
434: /*
435: * Get the user name for the uid.
436: */
1.20 millert 437: char *
1.29 guenther 438: getusername(uid_t uid, char *file, opt_t opts)
1.1 dm 439: {
440: static char buf[100];
1.29 guenther 441: static uid_t lastuid = (uid_t)-1;
1.39 ! millert 442: const char *name;
1.1 dm 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)) {
1.20 millert 449: (void) snprintf(buf, sizeof(buf), ":%u", uid);
1.1 dm 450: return(buf);
451: }
452:
453: /*
1.39 ! millert 454: * Try to avoid passwd lookup.
1.1 dm 455: */
1.6 millert 456: if (lastuid == uid && buf[0] != '\0' && buf[0] != ':')
1.1 dm 457: return(buf);
458:
459: lastuid = uid;
460:
1.39 ! millert 461: if ((name = user_from_uid(uid, 1)) == NULL) {
1.20 millert 462: if (IS_ON(opts, DO_DEFOWNER) && !isserver)
463: (void) strlcpy(buf, defowner, sizeof(buf));
464: else {
465: message(MT_WARNING,
466: "%s: No password entry for uid %u", file, uid);
467: (void) snprintf(buf, sizeof(buf), ":%u", uid);
468: }
469: } else {
1.39 ! millert 470: (void) strlcpy(buf, name, sizeof(buf));
1.20 millert 471: }
1.1 dm 472:
473: return(buf);
474: }
475:
476: /*
477: * Get the group name for the gid.
478: */
1.20 millert 479: char *
1.29 guenther 480: getgroupname(gid_t gid, char *file, opt_t opts)
1.1 dm 481: {
482: static char buf[100];
1.29 guenther 483: static gid_t lastgid = (gid_t)-1;
1.39 ! millert 484: const char *name;
1.1 dm 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)) {
1.20 millert 491: (void) snprintf(buf, sizeof(buf), ":%u", gid);
1.1 dm 492: return(buf);
493: }
494:
495: /*
1.39 ! millert 496: * Try to avoid group lookup.
1.1 dm 497: */
1.6 millert 498: if (lastgid == gid && buf[0] != '\0' && buf[0] != ':')
1.1 dm 499: return(buf);
500:
501: lastgid = gid;
502:
1.39 ! millert 503: if ((name = group_from_gid(gid, 1)) == NULL) {
1.20 millert 504: if (IS_ON(opts, DO_DEFGROUP) && !isserver)
505: (void) strlcpy(buf, defgroup, sizeof(buf));
506: else {
507: message(MT_WARNING, "%s: No name for group %u",
508: file, gid);
509: (void) snprintf(buf, sizeof(buf), ":%u", gid);
510: }
1.1 dm 511: } else
1.39 ! millert 512: (void) strlcpy(buf, name, sizeof(buf));
1.1 dm 513:
514: return(buf);
515: }
516:
517: /*
518: * Read a response from the remote host.
519: */
1.20 millert 520: int
521: response(void)
1.1 dm 522: {
523: static u_char resp[BUFSIZ];
524: u_char *s;
525: int n;
526:
527: debugmsg(DM_CALL, "response() start\n");
528:
529: n = remline(s = resp, sizeof(resp), 0);
530:
531: n--;
532: switch (*s++) {
533: case C_ACK:
534: debugmsg(DM_PROTO, "received ACK\n");
535: return(0);
536: case C_LOGMSG:
537: if (n > 0) {
538: message(MT_CHANGE, "%s", s);
539: return(1);
540: }
541: debugmsg(DM_PROTO, "received EMPTY logmsg\n");
542: return(0);
543: case C_NOTEMSG:
544: if (s)
545: message(MT_NOTICE, "%s", s);
546: return(response());
547:
548: default:
549: s--;
550: n++;
551: /* fall into... */
552:
553: case C_ERRMSG: /* Normal error message */
554: if (s)
555: message(MT_NERROR, "%s", s);
556: return(-1);
557:
558: case C_FERRMSG: /* Fatal error message */
559: if (s)
560: message(MT_FERROR, "%s", s);
561: finish();
1.20 millert 562: return(-1);
1.1 dm 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: */
1.20 millert 575: char *
576: exptilde(char *ebuf, char *file, size_t ebufsize)
1.1 dm 577: {
1.38 millert 578: struct passwd *pw;
1.18 millert 579: char *pw_dir, *rest;
1.39 ! millert 580: static char lastuser[_PW_NAME_LEN + 1];
! 581: static char lastdir[PATH_MAX];
1.18 millert 582: size_t len;
1.1 dm 583:
584: if (*file != '~') {
1.18 millert 585: notilde:
586: (void) strlcpy(ebuf, file, ebufsize);
1.1 dm 587: return(ebuf);
588: }
1.38 millert 589: pw_dir = homedir;
1.1 dm 590: if (*++file == CNULL) {
1.18 millert 591: rest = NULL;
1.1 dm 592: } else if (*file == '/') {
1.18 millert 593: rest = file;
1.1 dm 594: } else {
1.18 millert 595: rest = file;
596: while (*rest && *rest != '/')
597: rest++;
598: if (*rest == '/')
599: *rest = CNULL;
1.1 dm 600: else
1.18 millert 601: rest = NULL;
1.38 millert 602: if (strcmp(locuser, file) != 0) {
1.39 ! millert 603: if (strcmp(lastuser, file) != 0) {
! 604: if ((pw = getpwnam(file)) == NULL) {
! 605: error("%s: unknown user name", file);
! 606: if (rest != NULL)
! 607: *rest = '/';
! 608: return(NULL);
! 609: }
! 610: strlcpy(lastuser, pw->pw_name, sizeof(lastuser));
! 611: strlcpy(lastdir, pw->pw_dir, sizeof(lastdir));
1.1 dm 612: }
1.39 ! millert 613: pw_dir = lastdir;
1.1 dm 614: }
1.18 millert 615: if (rest != NULL)
616: *rest = '/';
617: }
618: if ((len = strlcpy(ebuf, pw_dir, ebufsize)) >= ebufsize)
619: goto notilde;
620: pw_dir = ebuf + len;
621: if (rest != NULL) {
622: pw_dir++;
623: if ((len = strlcat(ebuf, rest, ebufsize)) >= ebufsize)
624: goto notilde;
1.1 dm 625: }
1.18 millert 626: return(pw_dir);
1.1 dm 627: }
628:
629:
630:
631: /*
632: * Set access and modify times of a given file
633: */
1.20 millert 634: int
635: setfiletime(char *file, time_t atime, time_t mtime)
1.1 dm 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;
1.33 guenther 642: tv[0].tv_usec = tv[1].tv_usec = 0;
643: return (utimes(file, tv));
1.1 dm 644: } else /* Set to current time */
1.33 guenther 645: return (utimes(file, NULL));
1.1 dm 646: }
647:
648: /*
649: * Get version info
650: */
1.20 millert 651: char *
652: getversion(void)
1.1 dm 653: {
654: static char buff[BUFSIZ];
655:
1.20 millert 656: (void) snprintf(buff, sizeof(buff),
1.1 dm 657: "Version %s.%d (%s) - Protocol Version %d, Release %s, Patch level %d",
658: DISTVERSION, PATCHLEVEL, DISTSTATUS,
659: VERSION, DISTVERSION, PATCHLEVEL);
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: */
1.20 millert 668: void
669: runcommand(char *cmd)
1.1 dm 670: {
1.20 millert 671: ssize_t nread;
672: pid_t pid, wpid;
1.12 mpech 673: char *cp, *s;
1.1 dm 674: char sbuf[BUFSIZ], buf[BUFSIZ];
1.20 millert 675: int fd[2], status;
676:
1.1 dm 677: if (pipe(fd) < 0) {
678: error("pipe of %s failed: %s", cmd, SYSERR);
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, O_RDONLY);
690: (void) dup(fd[PIPE_WRITE]);
691: (void) dup(fd[PIPE_WRITE]);
692: (void) close(fd[PIPE_READ]);
693: (void) close(fd[PIPE_WRITE]);
1.11 deraadt 694: (void) execl(_PATH_BSHELL, "sh", "-c", cmd, (char *)NULL);
1.1 dm 695: _exit(127);
696: }
697: (void) close(fd[PIPE_WRITE]);
698: s = sbuf;
699: *s++ = C_LOGMSG;
1.20 millert 700: while ((nread = read(fd[PIPE_READ], buf, sizeof(buf))) > 0) {
1.1 dm 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;
720: message(MT_INFO, "%s", sbuf+1);
721: }
722: s = &sbuf[1];
1.20 millert 723: } while (--nread);
1.1 dm 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;
731: message(MT_INFO, "%s", sbuf+1);
732: }
733: }
1.20 millert 734: while ((wpid = wait(&status)) != pid && wpid != -1)
1.1 dm 735: ;
1.20 millert 736: if (wpid == -1)
1.1 dm 737: status = -1;
738: (void) close(fd[PIPE_READ]);
739: if (status)
740: error("shell returned %d", status);
741: else if (isserver)
742: ack();
743: }
744:
745: /*
746: * Malloc with error checking
747: */
1.27 guenther 748: void *
1.20 millert 749: xmalloc(size_t amt)
1.1 dm 750: {
1.27 guenther 751: void *ptr;
1.1 dm 752:
1.27 guenther 753: if ((ptr = malloc(amt)) == NULL)
1.25 krw 754: fatalerr("Cannot malloc %zu bytes of memory.", amt);
1.1 dm 755:
1.27 guenther 756: return (ptr);
1.1 dm 757: }
758:
759: /*
760: * realloc with error checking
761: */
1.27 guenther 762: void *
763: xrealloc(void *baseptr, size_t amt)
1.1 dm 764: {
1.27 guenther 765: void *new;
1.1 dm 766:
1.27 guenther 767: if ((new = realloc(baseptr, amt)) == NULL)
1.25 krw 768: fatalerr("Cannot realloc %zu bytes of memory.", amt);
1.1 dm 769:
1.27 guenther 770: return (new);
1.1 dm 771: }
772:
773: /*
774: * calloc with error checking
775: */
1.27 guenther 776: void *
1.20 millert 777: xcalloc(size_t num, size_t esize)
1.1 dm 778: {
1.27 guenther 779: void *ptr;
1.1 dm 780:
1.27 guenther 781: if ((ptr = calloc(num, esize)) == NULL)
1.25 krw 782: fatalerr("Cannot calloc %zu * %zu = %zu bytes of memory.",
1.1 dm 783: num, esize, num * esize);
784:
1.27 guenther 785: return (ptr);
1.8 millert 786: }
787:
788: /*
789: * Strdup with error checking
790: */
1.20 millert 791: char *
792: xstrdup(const char *str)
1.8 millert 793: {
1.20 millert 794: size_t len = strlen(str) + 1;
1.27 guenther 795: char *nstr = xmalloc(len);
1.8 millert 796:
1.27 guenther 797: return (memcpy(nstr, str, len));
1.1 dm 798: }
799:
800: /*
801: * Private version of basename()
802: */
1.20 millert 803: char *
804: xbasename(char *path)
1.1 dm 805: {
1.12 mpech 806: char *cp;
1.1 dm 807:
1.20 millert 808: if ((cp = strrchr(path, '/')) != NULL)
1.1 dm 809: return(cp+1);
810: else
811: return(path);
812: }
813:
814: /*
1.10 provos 815: * Take a colon (':') separated path to a file and
1.1 dm 816: * search until a component of that path is found and
817: * return the found file name.
818: */
1.20 millert 819: char *
820: searchpath(char *path)
1.1 dm 821: {
1.12 mpech 822: char *file;
1.19 millert 823: char *space;
824: int found;
1.1 dm 825: struct stat statbuf;
826:
1.19 millert 827: for (found = 0; !found && (file = strsep(&path, ":")) != NULL; ) {
828: if ((space = strchr(file, ' ')) != NULL)
829: *space = CNULL;
830: found = stat(file, &statbuf) == 0;
831: if (space)
832: *space = ' '; /* Put back what we zapped */
1.1 dm 833: }
1.19 millert 834: return (file);
1.1 dm 835: }