Annotation of src/usr.bin/rdist/common.c, Revision 1.30
1.30 ! guenther 1: /* $OpenBSD: common.c,v 1.29 2014/07/05 06:55:29 guenther 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.20 millert 32: #include "defs.h"
1.1 dm 33:
34: /*
35: * Things common to both the client and server.
36: */
37:
38: #if defined(NEED_UTIME_H)
39: #include <utime.h>
40: #endif /* defined(NEED_UTIME_H) */
1.20 millert 41: #include <sys/wait.h>
1.6 millert 42: #include <sys/socket.h>
1.1 dm 43:
44: /*
45: * Variables common to both client and server
46: */
47: char host[MAXHOSTNAMELEN]; /* Name of this host */
1.29 guenther 48: uid_t userid = (uid_t)-1; /* User's UID */
49: gid_t groupid = (gid_t)-1; /* User's GID */
1.1 dm 50: char *homedir = NULL; /* User's $HOME */
51: char *locuser = NULL; /* Local User's name */
52: int isserver = FALSE; /* We're the server */
53: int amchild = 0; /* This PID is a child */
54: int do_fork = 1; /* Fork child process */
55: char *currenthost = NULL; /* Current client hostname */
56: char *progname = NULL; /* Name of this program */
57: int rem_r = -1; /* Client file descriptor */
58: int rem_w = -1; /* Client file descriptor */
59: struct passwd *pw = NULL; /* Local user's pwd entry */
1.20 millert 60: volatile sig_atomic_t contimedout = FALSE; /* Connection timed out */
1.1 dm 61: int proto_version = -1; /* Protocol version */
62: int rtimeout = RTIMEOUT; /* Response time out */
63: jmp_buf finish_jmpbuf; /* Finish() jmp buffer */
1.2 dm 64: int setjmp_ok = FALSE; /* setjmp()/longjmp() status */
1.1 dm 65: char **realargv; /* Real main() argv */
66: int realargc; /* Real main() argc */
67: opt_t options = 0; /* Global install options */
1.20 millert 68: char defowner[64] = "bin"; /* Default owner */
69: char defgroup[64] = "bin"; /* Default group */
70:
71: static int sendcmdmsg(int, char *, size_t);
1.24 krw 72: static ssize_t remread(int, u_char *, size_t);
1.20 millert 73: static int remmore(void);
1.1 dm 74:
75: /*
76: * Front end to write() that handles partial write() requests.
77: */
1.20 millert 78: WRITE_RETURN_T
79: xwrite(int fd, void *buf, WRITE_AMT_T len)
1.1 dm 80: {
81: WRITE_AMT_T nleft = len;
82: WRITE_RETURN_T nwritten;
1.12 mpech 83: char *ptr = buf;
1.1 dm 84:
85: while (nleft > 0) {
86: if ((nwritten = write(fd, ptr, nleft)) <= 0) {
87: return nwritten;
88: }
89: nleft -= nwritten;
90: ptr += nwritten;
91: }
92:
93: return len;
94: }
95:
96: /*
97: * Do run-time initialization
98: */
1.20 millert 99: int
100: init(int argc, char **argv, char **envp)
1.1 dm 101: {
1.12 mpech 102: int i;
1.1 dm 103:
104: /*
105: * Save a copy of our argc and argv before setargs() overwrites them
106: */
107: realargc = argc;
108: realargv = (char **) xmalloc(sizeof(char *) * (argc+1));
109: for (i = 0; i < argc; i++)
1.8 millert 110: realargv[i] = xstrdup(argv[i]);
1.1 dm 111:
1.6 millert 112: #if defined(SETARGS)
1.1 dm 113: setargs_settup(argc, argv, envp);
114: #endif /* SETARGS */
115:
116: pw = getpwuid(userid = getuid());
117: if (pw == NULL) {
1.16 deraadt 118: error("Your user id (%u) is not known to this system.",
1.1 dm 119: getuid());
120: return(-1);
121: }
122:
1.20 millert 123: debugmsg(DM_MISC, "UserID = %u pwname = '%s' home = '%s'\n",
1.1 dm 124: userid, pw->pw_name, pw->pw_dir);
1.8 millert 125: homedir = xstrdup(pw->pw_dir);
126: locuser = xstrdup(pw->pw_name);
1.1 dm 127: groupid = pw->pw_gid;
128: gethostname(host, sizeof(host));
1.20 millert 129: #if 0
1.1 dm 130: if ((cp = strchr(host, '.')) != NULL)
131: *cp = CNULL;
1.20 millert 132: #endif
1.1 dm 133:
134: /*
135: * If we're not root, disable paranoid ownership checks
136: * since normal users cannot chown() files.
137: */
138: if (!isserver && userid != 0) {
139: FLAG_ON(options, DO_NOCHKOWNER);
140: FLAG_ON(options, DO_NOCHKGROUP);
141: }
142:
143: return(0);
144: }
145:
146: /*
147: * Finish things up before ending.
148: */
1.20 millert 149: void
150: finish(void)
1.1 dm 151: {
152: extern jmp_buf finish_jmpbuf;
153:
154: debugmsg(DM_CALL,
155: "finish() called: do_fork = %d amchild = %d isserver = %d",
156: do_fork, amchild, isserver);
1.20 millert 157: cleanup(0);
1.1 dm 158:
159: /*
160: * There's no valid finish_jmpbuf for the rdist master parent.
161: */
162: if (!do_fork || amchild || isserver) {
1.2 dm 163:
164: if (!setjmp_ok) {
165: #ifdef DEBUG_SETJMP
166: error("attemping longjmp() without target");
167: abort();
168: #else
169: exit(1);
170: #endif
171: }
172:
1.1 dm 173: longjmp(finish_jmpbuf, 1);
174: /*NOTREACHED*/
175: error("Unexpected failure of longjmp() in finish()");
176: exit(2);
177: } else
178: exit(1);
179: }
180:
181: /*
182: * Handle lost connections
183: */
1.20 millert 184: void
185: lostconn(void)
1.1 dm 186: {
187: /* Prevent looping */
188: (void) signal(SIGPIPE, SIG_IGN);
189:
190: rem_r = rem_w = -1; /* Ensure we don't try to send to server */
191: checkhostname();
192: error("Lost connection to %s",
193: (currenthost) ? currenthost : "(unknown)");
194:
195: finish();
196: }
197:
198: /*
199: * General signal handler
200: */
1.20 millert 201: void
202: sighandler(int sig)
1.1 dm 203: {
1.7 millert 204: int save_errno = errno;
205:
1.26 deraadt 206: /* XXX signal race */
1.1 dm 207: debugmsg(DM_CALL, "sighandler() received signal %d\n", sig);
208:
209: switch (sig) {
210: case SIGALRM:
211: contimedout = TRUE;
1.26 deraadt 212: /* XXX signal race */
1.1 dm 213: checkhostname();
214: error("Response time out");
215: finish();
216: break;
217:
218: case SIGPIPE:
1.26 deraadt 219: /* XXX signal race */
1.1 dm 220: lostconn();
221: break;
222:
223: case SIGFPE:
224: debug = !debug;
225: break;
226:
227: case SIGHUP:
228: case SIGINT:
229: case SIGQUIT:
230: case SIGTERM:
1.26 deraadt 231: /* XXX signal race */
1.1 dm 232: finish();
233: break;
234:
235: default:
1.26 deraadt 236: /* XXX signal race */
1.1 dm 237: fatalerr("No signal handler defined for signal %d.", sig);
238: }
1.7 millert 239: errno = save_errno;
1.1 dm 240: }
241:
242: /*
243: * Function to actually send the command char and message to the
244: * remote host.
245: */
1.20 millert 246: static int
247: sendcmdmsg(int cmd, char *msg, size_t msgsize)
1.1 dm 248: {
249: int len;
250:
251: if (rem_w < 0)
252: return(-1);
253:
254: /*
255: * All commands except C_NONE should have a newline
256: */
257: if (cmd != C_NONE && !strchr(msg + 1, '\n'))
1.18 millert 258: (void) strlcat(msg + 1, "\n", msgsize - 1);
1.1 dm 259:
260: if (cmd == C_NONE)
261: len = strlen(msg);
262: else {
263: len = strlen(msg + 1) + 1;
264: msg[0] = cmd;
265: }
266:
267: debugmsg(DM_PROTO, ">>> Cmd = %c (\\%3.3o) Msg = \"%.*s\"",
268: cmd, cmd,
269: (cmd == C_NONE) ? len-1 : len-2,
270: (cmd == C_NONE) ? msg : msg + 1);
271:
272: return(!(xwrite(rem_w, msg, len) == len));
273: }
274:
275: /*
276: * Send a command message to the remote host.
277: * Called as sendcmd(char cmdchar, char *fmt, arg1, arg2, ...)
1.28 guenther 278: * The fmt may be NULL, in which case there are no args.
1.1 dm 279: */
1.20 millert 280: int
1.28 guenther 281: sendcmd(char cmd, const char *fmt, ...)
1.1 dm 282: {
283: static char buf[BUFSIZ];
284: va_list args;
285:
286: va_start(args, fmt);
287: if (fmt)
1.18 millert 288: (void) vsnprintf(buf + (cmd != C_NONE),
1.20 millert 289: sizeof(buf) - (cmd != C_NONE), fmt, args);
1.1 dm 290: else
291: buf[1] = CNULL;
292: va_end(args);
293:
1.18 millert 294: return(sendcmdmsg(cmd, buf, sizeof(buf)));
1.1 dm 295: }
296:
297: /*
298: * Internal variables and routines for reading lines from the remote.
299: */
300: static u_char rembuf[BUFSIZ];
301: static u_char *remptr;
1.24 krw 302: static ssize_t remleft;
1.1 dm 303:
304: #define remc() (--remleft < 0 ? remmore() : *remptr++)
305:
306: /*
307: * Back end to remote read()
308: */
1.24 krw 309: static ssize_t
310: remread(int fd, u_char *buf, size_t bufsiz)
1.1 dm 311: {
312: return(read(fd, (char *)buf, bufsiz));
313: }
314:
1.20 millert 315: static int
316: remmore(void)
1.1 dm 317: {
318: (void) signal(SIGALRM, sighandler);
319: (void) alarm(rtimeout);
320:
321: remleft = remread(rem_r, rembuf, sizeof(rembuf));
322:
323: (void) alarm(0);
324:
325: if (remleft < 0)
326: return (-2); /* error */
327: if (remleft == 0)
328: return (-1); /* EOF */
329: remptr = rembuf;
330: remleft--;
331: return (*remptr++);
332: }
333:
334: /*
335: * Read an input line from the remote. Return the number of bytes
336: * stored (equivalent to strlen(p)). If `cleanup' is set, EOF at
337: * the beginning of a line is returned as EOF (-1); other EOFs, or
338: * errors, call cleanup() or lostconn(). In other words, unless
339: * the third argument is nonzero, this routine never returns failure.
340: */
1.20 millert 341: int
342: remline(u_char *buffer, int space, int doclean)
1.1 dm 343: {
1.12 mpech 344: int c, left = space;
345: u_char *p = buffer;
1.1 dm 346:
347: if (rem_r < 0) {
348: error("Cannot read remote input: Remote descriptor not open.");
349: return(-1);
350: }
351:
352: while (left > 0) {
353: if ((c = remc()) < -1) { /* error */
354: if (doclean) {
355: finish();
356: /*NOTREACHED*/
357: }
358: lostconn();
359: /*NOTREACHED*/
360: }
361: if (c == -1) { /* got EOF */
362: if (doclean) {
363: if (left == space)
364: return (-1);/* signal proper EOF */
365: finish(); /* improper EOF */
366: /*NOTREACHED*/
367: }
368: lostconn();
369: /*NOTREACHED*/
370: }
371: if (c == '\n') {
372: *p = CNULL;
373:
374: if (debug) {
375: static char mbuf[BUFSIZ];
376:
1.20 millert 377: (void) snprintf(mbuf, sizeof(mbuf),
1.1 dm 378: "<<< Cmd = %c (\\%3.3o) Msg = \"%s\"",
379: buffer[0], buffer[0],
380: buffer + 1);
381:
382: debugmsg(DM_PROTO, "%s", mbuf);
383: }
384:
385: return (space - left);
386: }
387: *p++ = c;
388: left--;
389: }
390:
391: /* this will probably blow the entire session */
392: error("remote input line too long");
393: p[-1] = CNULL; /* truncate */
394: return (space);
395: }
396:
397: /*
398: * Non-line-oriented remote read.
399: */
1.24 krw 400: ssize_t
401: readrem(char *p, ssize_t space)
1.1 dm 402: {
403: if (remleft <= 0) {
404: /*
405: * Set remote time out alarm.
406: */
407: (void) signal(SIGALRM, sighandler);
408: (void) alarm(rtimeout);
409:
410: remleft = remread(rem_r, rembuf, sizeof(rembuf));
411:
412: (void) alarm(0);
413: remptr = rembuf;
414: }
415:
416: if (remleft <= 0)
417: return (remleft);
418: if (remleft < space)
419: space = remleft;
420:
1.20 millert 421: memcpy(p, remptr, space);
1.1 dm 422:
423: remptr += space;
424: remleft -= space;
425:
426: return (space);
427: }
428:
429: /*
430: * Get the user name for the uid.
431: */
1.20 millert 432: char *
1.29 guenther 433: getusername(uid_t uid, char *file, opt_t opts)
1.1 dm 434: {
435: static char buf[100];
1.29 guenther 436: static uid_t lastuid = (uid_t)-1;
1.1 dm 437: struct passwd *pwd = NULL;
438:
439: /*
440: * The value of opts may have changed so we always
441: * do the opts check.
442: */
443: if (IS_ON(opts, DO_NUMCHKOWNER)) {
1.20 millert 444: (void) snprintf(buf, sizeof(buf), ":%u", uid);
1.1 dm 445: return(buf);
446: }
447:
448: /*
449: * Try to avoid getpwuid() call.
450: */
1.6 millert 451: if (lastuid == uid && buf[0] != '\0' && buf[0] != ':')
1.1 dm 452: return(buf);
453:
454: lastuid = uid;
455:
456: if ((pwd = getpwuid(uid)) == NULL) {
1.20 millert 457: if (IS_ON(opts, DO_DEFOWNER) && !isserver)
458: (void) strlcpy(buf, defowner, sizeof(buf));
459: else {
460: message(MT_WARNING,
461: "%s: No password entry for uid %u", file, uid);
462: (void) snprintf(buf, sizeof(buf), ":%u", uid);
463: }
464: } else {
465: (void) strlcpy(buf, pwd->pw_name, sizeof(buf));
466: }
1.1 dm 467:
468: return(buf);
469: }
470:
471: /*
472: * Get the group name for the gid.
473: */
1.20 millert 474: char *
1.29 guenther 475: getgroupname(gid_t gid, char *file, opt_t opts)
1.1 dm 476: {
477: static char buf[100];
1.29 guenther 478: static gid_t lastgid = (gid_t)-1;
1.1 dm 479: struct group *grp = NULL;
480:
481: /*
482: * The value of opts may have changed so we always
483: * do the opts check.
484: */
485: if (IS_ON(opts, DO_NUMCHKGROUP)) {
1.20 millert 486: (void) snprintf(buf, sizeof(buf), ":%u", gid);
1.1 dm 487: return(buf);
488: }
489:
490: /*
491: * Try to avoid getgrgid() call.
492: */
1.6 millert 493: if (lastgid == gid && buf[0] != '\0' && buf[0] != ':')
1.1 dm 494: return(buf);
495:
496: lastgid = gid;
497:
498: if ((grp = (struct group *)getgrgid(gid)) == NULL) {
1.20 millert 499: if (IS_ON(opts, DO_DEFGROUP) && !isserver)
500: (void) strlcpy(buf, defgroup, sizeof(buf));
501: else {
502: message(MT_WARNING, "%s: No name for group %u",
503: file, gid);
504: (void) snprintf(buf, sizeof(buf), ":%u", gid);
505: }
1.1 dm 506: } else
1.20 millert 507: (void) strlcpy(buf, grp->gr_name, sizeof(buf));
1.1 dm 508:
509: return(buf);
510: }
511:
512: /*
513: * Read a response from the remote host.
514: */
1.20 millert 515: int
516: response(void)
1.1 dm 517: {
518: static u_char resp[BUFSIZ];
519: u_char *s;
520: int n;
521:
522: debugmsg(DM_CALL, "response() start\n");
523:
524: n = remline(s = resp, sizeof(resp), 0);
525:
526: n--;
527: switch (*s++) {
528: case C_ACK:
529: debugmsg(DM_PROTO, "received ACK\n");
530: return(0);
531: case C_LOGMSG:
532: if (n > 0) {
533: message(MT_CHANGE, "%s", s);
534: return(1);
535: }
536: debugmsg(DM_PROTO, "received EMPTY logmsg\n");
537: return(0);
538: case C_NOTEMSG:
539: if (s)
540: message(MT_NOTICE, "%s", s);
541: return(response());
542:
543: default:
544: s--;
545: n++;
546: /* fall into... */
547:
548: case C_ERRMSG: /* Normal error message */
549: if (s)
550: message(MT_NERROR, "%s", s);
551: return(-1);
552:
553: case C_FERRMSG: /* Fatal error message */
554: if (s)
555: message(MT_FERROR, "%s", s);
556: finish();
1.20 millert 557: return(-1);
1.1 dm 558: }
559: /*NOTREACHED*/
560: }
561:
562: /*
563: * This should be in expand.c but the other routines call other modules
564: * that we don't want to load in.
565: *
566: * Expand file names beginning with `~' into the
567: * user's home directory path name. Return a pointer in buf to the
568: * part corresponding to `file'.
569: */
1.20 millert 570: char *
571: exptilde(char *ebuf, char *file, size_t ebufsize)
1.1 dm 572: {
1.18 millert 573: char *pw_dir, *rest;
574: size_t len;
1.1 dm 575: extern char *homedir;
576:
577: if (*file != '~') {
1.18 millert 578: notilde:
579: (void) strlcpy(ebuf, file, ebufsize);
1.1 dm 580: return(ebuf);
581: }
582: if (*++file == CNULL) {
1.18 millert 583: pw_dir = homedir;
584: rest = NULL;
1.1 dm 585: } else if (*file == '/') {
1.18 millert 586: pw_dir = homedir;
587: rest = file;
1.1 dm 588: } else {
1.18 millert 589: rest = file;
590: while (*rest && *rest != '/')
591: rest++;
592: if (*rest == '/')
593: *rest = CNULL;
1.1 dm 594: else
1.18 millert 595: rest = NULL;
1.1 dm 596: if (pw == NULL || strcmp(pw->pw_name, file) != 0) {
597: if ((pw = getpwnam(file)) == NULL) {
598: error("%s: unknown user name", file);
1.18 millert 599: if (rest != NULL)
600: *rest = '/';
1.1 dm 601: return(NULL);
602: }
603: }
1.18 millert 604: if (rest != NULL)
605: *rest = '/';
606: pw_dir = pw->pw_dir;
607: }
608: if ((len = strlcpy(ebuf, pw_dir, ebufsize)) >= ebufsize)
609: goto notilde;
610: pw_dir = ebuf + len;
611: if (rest != NULL) {
612: pw_dir++;
613: if ((len = strlcat(ebuf, rest, ebufsize)) >= ebufsize)
614: goto notilde;
1.1 dm 615: }
1.18 millert 616: return(pw_dir);
1.1 dm 617: }
618:
619:
620:
621: /*
622: * Set access and modify times of a given file
623: */
1.20 millert 624: int
625: setfiletime(char *file, time_t atime, time_t mtime)
1.1 dm 626: {
627: #if SETFTIME_TYPE == SETFTIME_UTIMES
628: struct timeval tv[2];
629:
630: if (atime != 0 && mtime != 0) {
631: tv[0].tv_sec = atime;
632: tv[1].tv_sec = mtime;
633: tv[0].tv_usec = tv[1].tv_usec = (time_t) 0;
634: return(utimes(file, tv));
635: } else /* Set to current time */
1.6 millert 636: return(utimes(file, NULL));
1.1 dm 637:
638: #endif /* SETFTIME_UTIMES */
639:
640: #if SETFTIME_TYPE == SETFTIME_UTIME
641: struct utimbuf utbuf;
642:
643: if (atime != 0 && mtime != 0) {
644: utbuf.actime = atime;
645: utbuf.modtime = mtime;
646: return(utime(file, &utbuf));
647: } else /* Set to current time */
1.6 millert 648: return(utime(file, NULL));
1.1 dm 649: #endif /* SETFTIME_UTIME */
650:
651: #if !defined(SETFTIME_TYPE)
652: There is no "SETFTIME_TYPE" defined!
653: #endif /* SETFTIME_TYPE */
654: }
655:
656: /*
657: * Get version info
658: */
1.20 millert 659: char *
660: getversion(void)
1.1 dm 661: {
662: static char buff[BUFSIZ];
663:
1.20 millert 664: (void) snprintf(buff, sizeof(buff),
1.1 dm 665: "Version %s.%d (%s) - Protocol Version %d, Release %s, Patch level %d",
666: DISTVERSION, PATCHLEVEL, DISTSTATUS,
667: VERSION, DISTVERSION, PATCHLEVEL);
668:
669: return(buff);
670: }
671:
672: /*
673: * Execute a shell command to handle special cases.
674: * This is now common to both server and client
675: */
1.20 millert 676: void
677: runcommand(char *cmd)
1.1 dm 678: {
1.20 millert 679: ssize_t nread;
680: pid_t pid, wpid;
1.12 mpech 681: char *cp, *s;
1.1 dm 682: char sbuf[BUFSIZ], buf[BUFSIZ];
1.20 millert 683: int fd[2], status;
684:
1.1 dm 685: if (pipe(fd) < 0) {
686: error("pipe of %s failed: %s", cmd, SYSERR);
687: return;
688: }
689:
690: if ((pid = fork()) == 0) {
691: /*
692: * Return everything the shell commands print.
693: */
694: (void) close(0);
695: (void) close(1);
696: (void) close(2);
697: (void) open(_PATH_DEVNULL, O_RDONLY);
698: (void) dup(fd[PIPE_WRITE]);
699: (void) dup(fd[PIPE_WRITE]);
700: (void) close(fd[PIPE_READ]);
701: (void) close(fd[PIPE_WRITE]);
1.11 deraadt 702: (void) execl(_PATH_BSHELL, "sh", "-c", cmd, (char *)NULL);
1.1 dm 703: _exit(127);
704: }
705: (void) close(fd[PIPE_WRITE]);
706: s = sbuf;
707: *s++ = C_LOGMSG;
1.20 millert 708: while ((nread = read(fd[PIPE_READ], buf, sizeof(buf))) > 0) {
1.1 dm 709: cp = buf;
710: do {
711: *s++ = *cp++;
712: if (cp[-1] != '\n') {
713: if (s < (char *) &sbuf[sizeof(sbuf)-1])
714: continue;
715: *s++ = '\n';
716: }
717: /*
718: * Throw away blank lines.
719: */
720: if (s == &sbuf[2]) {
721: s--;
722: continue;
723: }
724: if (isserver)
725: (void) xwrite(rem_w, sbuf, s - sbuf);
726: else {
727: *s = CNULL;
728: message(MT_INFO, "%s", sbuf+1);
729: }
730: s = &sbuf[1];
1.20 millert 731: } while (--nread);
1.1 dm 732: }
733: if (s > (char *) &sbuf[1]) {
734: *s++ = '\n';
735: if (isserver)
736: (void) xwrite(rem_w, sbuf, s - sbuf);
737: else {
738: *s = CNULL;
739: message(MT_INFO, "%s", sbuf+1);
740: }
741: }
1.20 millert 742: while ((wpid = wait(&status)) != pid && wpid != -1)
1.1 dm 743: ;
1.20 millert 744: if (wpid == -1)
1.1 dm 745: status = -1;
746: (void) close(fd[PIPE_READ]);
747: if (status)
748: error("shell returned %d", status);
749: else if (isserver)
750: ack();
751: }
752:
753: /*
754: * Malloc with error checking
755: */
1.27 guenther 756: void *
1.20 millert 757: xmalloc(size_t amt)
1.1 dm 758: {
1.27 guenther 759: void *ptr;
1.1 dm 760:
1.27 guenther 761: if ((ptr = malloc(amt)) == NULL)
1.25 krw 762: fatalerr("Cannot malloc %zu bytes of memory.", amt);
1.1 dm 763:
1.27 guenther 764: return (ptr);
1.1 dm 765: }
766:
767: /*
768: * realloc with error checking
769: */
1.27 guenther 770: void *
771: xrealloc(void *baseptr, size_t amt)
1.1 dm 772: {
1.27 guenther 773: void *new;
1.1 dm 774:
1.27 guenther 775: if ((new = realloc(baseptr, amt)) == NULL)
1.25 krw 776: fatalerr("Cannot realloc %zu bytes of memory.", amt);
1.1 dm 777:
1.27 guenther 778: return (new);
1.1 dm 779: }
780:
781: /*
782: * calloc with error checking
783: */
1.27 guenther 784: void *
1.20 millert 785: xcalloc(size_t num, size_t esize)
1.1 dm 786: {
1.27 guenther 787: void *ptr;
1.1 dm 788:
1.27 guenther 789: if ((ptr = calloc(num, esize)) == NULL)
1.25 krw 790: fatalerr("Cannot calloc %zu * %zu = %zu bytes of memory.",
1.1 dm 791: num, esize, num * esize);
792:
1.27 guenther 793: return (ptr);
1.8 millert 794: }
795:
796: /*
797: * Strdup with error checking
798: */
1.20 millert 799: char *
800: xstrdup(const char *str)
1.8 millert 801: {
1.20 millert 802: size_t len = strlen(str) + 1;
1.27 guenther 803: char *nstr = xmalloc(len);
1.8 millert 804:
1.27 guenther 805: return (memcpy(nstr, str, len));
1.1 dm 806: }
807:
808: /*
809: * Private version of basename()
810: */
1.20 millert 811: char *
812: xbasename(char *path)
1.1 dm 813: {
1.12 mpech 814: char *cp;
1.1 dm 815:
1.20 millert 816: if ((cp = strrchr(path, '/')) != NULL)
1.1 dm 817: return(cp+1);
818: else
819: return(path);
820: }
821:
822: /*
1.10 provos 823: * Take a colon (':') separated path to a file and
1.1 dm 824: * search until a component of that path is found and
825: * return the found file name.
826: */
1.20 millert 827: char *
828: searchpath(char *path)
1.1 dm 829: {
1.12 mpech 830: char *file;
1.19 millert 831: char *space;
832: int found;
1.1 dm 833: struct stat statbuf;
834:
1.19 millert 835: for (found = 0; !found && (file = strsep(&path, ":")) != NULL; ) {
836: if ((space = strchr(file, ' ')) != NULL)
837: *space = CNULL;
838: found = stat(file, &statbuf) == 0;
839: if (space)
840: *space = ' '; /* Put back what we zapped */
1.1 dm 841: }
1.19 millert 842: return (file);
1.1 dm 843: }