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