xref: /openbsd/usr.bin/rdist/common.c (revision d9a51c35)
1 /*	$OpenBSD: common.c,v 1.41 2022/12/26 19:16:02 jmc 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.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  */
56 char			host[HOST_NAME_MAX+1];	/* Name of this host */
57 uid_t			userid = (uid_t)-1;	/* User's UID */
58 gid_t			groupid = (gid_t)-1;	/* User's GID */
59 gid_t		        gidset[NGROUPS_MAX];	/* User's GID list */
60 int			gidsetlen = 0;		/* Number of GIDS in list */
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 */
70 volatile sig_atomic_t 	contimedout = FALSE;	/* Connection timed out */
71 int			rtimeout = RTIMEOUT;	/* Response time out */
72 jmp_buf			finish_jmpbuf;		/* Finish() jmp buffer */
73 int			setjmp_ok = FALSE;	/* setjmp()/longjmp() status */
74 char		      **realargv;		/* Real main() argv */
75 int			realargc;		/* Real main() argc */
76 opt_t			options = 0;		/* Global install options */
77 char			defowner[64] = "bin";	/* Default owner */
78 char			defgroup[64] = "bin";	/* Default group */
79 
80 static int sendcmdmsg(int, char *, size_t);
81 static ssize_t remread(int, u_char *, size_t);
82 static int remmore(void);
83 
84 /*
85  * Front end to write() that handles partial write() requests.
86  */
87 ssize_t
xwrite(int fd,void * buf,size_t len)88 xwrite(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  */
108 int
init(int argc,char ** argv,char ** envp)109 init(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) {
124 		error("Your user id (%u) is not known to this system.",
125 		      getuid());
126 		return(-1);
127 	}
128 
129 	debugmsg(DM_MISC, "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_MAX, gidset);
135 	gethostname(host, sizeof(host));
136 #if 0
137 	if ((cp = strchr(host, '.')) != NULL)
138 	    	*cp = CNULL;
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);
147 		FLAG_ON(options, DO_NOCHKGROUP);
148 	}
149 
150 	return(0);
151 }
152 
153 /*
154  * Finish things up before ending.
155  */
156 void
finish(void)157 finish(void)
158 {
159 	debugmsg(DM_CALL,
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("attempting 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  */
189 void
lostconn(void)190 lostconn(void)
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  */
206 void
sighandler(int sig)207 sighandler(int sig)
208 {
209 	int save_errno = errno;
210 
211 	/* XXX signal race */
212 	debugmsg(DM_CALL, "sighandler() received signal %d\n", sig);
213 
214 	switch (sig) {
215 	case SIGALRM:
216 		contimedout = TRUE;
217 		/* XXX signal race */
218 		checkhostname();
219 		error("Response time out");
220 		finish();
221 		break;
222 
223 	case SIGPIPE:
224 		/* XXX signal race */
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:
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 = save_errno;
245 }
246 
247 /*
248  * Function to actually send the command char and message to the
249  * remote host.
250  */
251 static int
sendcmdmsg(int cmd,char * msg,size_t msgsize)252 sendcmdmsg(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_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, ...)
283  * The fmt may be NULL, in which case there are no args.
284  */
285 int
sendcmd(char cmd,const char * fmt,...)286 sendcmd(char cmd, const char *fmt, ...)
287 {
288 	static char buf[BUFSIZ];
289 	va_list args;
290 
291 	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;
297 	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  */
305 static u_char rembuf[BUFSIZ];
306 static u_char *remptr;
307 static ssize_t remleft;
308 
309 #define remc() (--remleft < 0 ? remmore() : *remptr++)
310 
311 /*
312  * Back end to remote read()
313  */
314 static ssize_t
remread(int fd,u_char * buf,size_t bufsiz)315 remread(int fd, u_char *buf, size_t bufsiz)
316 {
317 	return(read(fd, (char *)buf, bufsiz));
318 }
319 
320 static int
remmore(void)321 remmore(void)
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  */
346 int
remline(u_char * buffer,int space,int doclean)347 remline(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()) < -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 
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_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  */
405 ssize_t
readrem(char * p,ssize_t space)406 readrem(char *p, ssize_t space)
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 
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  */
437 char *
getusername(uid_t uid,char * file,opt_t opts)438 getusername(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)) {
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) {
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 {
470 		(void) strlcpy(buf, name, sizeof(buf));
471 	}
472 
473 	return(buf);
474 }
475 
476 /*
477  * Get the group name for the gid.
478  */
479 char *
getgroupname(gid_t gid,char * file,opt_t opts)480 getgroupname(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)) {
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) {
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 		}
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  */
520 int
response(void)521 response(void)
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();
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  */
575 char *
exptilde(char * ebuf,char * file,size_t ebufsize)576 exptilde(char *ebuf, char *file, size_t ebufsize)
577 {
578 	struct passwd *pw;
579 	char *pw_dir, *rest;
580 	static char lastuser[_PW_NAME_LEN + 1];
581 	static char lastdir[PATH_MAX];
582 	size_t len;
583 
584 	if (*file != '~') {
585 notilde:
586 		(void) strlcpy(ebuf, file, ebufsize);
587 		return(ebuf);
588 	}
589 	pw_dir = homedir;
590 	if (*++file == CNULL) {
591 		rest = NULL;
592 	} else if (*file == '/') {
593 		rest = file;
594 	} else {
595 		rest = file;
596 		while (*rest && *rest != '/')
597 			rest++;
598 		if (*rest == '/')
599 			*rest = CNULL;
600 		else
601 			rest = NULL;
602 		if (strcmp(locuser, file) != 0) {
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));
612 			}
613 			pw_dir = lastdir;
614 		}
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;
625 	}
626 	return(pw_dir);
627 }
628 
629 
630 
631 /*
632  * Set access and modify times of a given file
633  */
634 int
setfiletime(char * file,time_t atime,time_t mtime)635 setfiletime(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));
646 }
647 
648 /*
649  * Get version info
650  */
651 char *
getversion(void)652 getversion(void)
653 {
654 	static char buff[BUFSIZ];
655 
656 	(void) snprintf(buff, sizeof(buff),
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  */
668 void
runcommand(char * cmd)669 runcommand(char *cmd)
670 {
671 	ssize_t nread;
672 	pid_t pid, wpid;
673 	char *cp, *s;
674 	char sbuf[BUFSIZ], buf[BUFSIZ];
675 	int fd[2], status;
676 
677 	if (pipe(fd) == -1) {
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]);
694 		(void) execl(_PATH_BSHELL, "sh", "-c", cmd, (char *)NULL);
695 		_exit(127);
696 	}
697 	(void) close(fd[PIPE_WRITE]);
698 	s = sbuf;
699 	*s++ = C_LOGMSG;
700 	while ((nread = read(fd[PIPE_READ], 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;
720 				message(MT_INFO, "%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;
731 			message(MT_INFO, "%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_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  */
748 void *
xmalloc(size_t amt)749 xmalloc(size_t amt)
750 {
751 	void *ptr;
752 
753 	if ((ptr = malloc(amt)) == NULL)
754 		fatalerr("Cannot malloc %zu bytes of memory.", amt);
755 
756 	return (ptr);
757 }
758 
759 /*
760  * realloc with error checking
761  */
762 void *
xrealloc(void * baseptr,size_t amt)763 xrealloc(void *baseptr, size_t amt)
764 {
765 	void *new;
766 
767 	if ((new = realloc(baseptr, amt)) == NULL)
768 		fatalerr("Cannot realloc %zu bytes of memory.", amt);
769 
770 	return (new);
771 }
772 
773 /*
774  * calloc with error checking
775  */
776 void *
xcalloc(size_t num,size_t esize)777 xcalloc(size_t num, size_t esize)
778 {
779 	void *ptr;
780 
781 	if ((ptr = calloc(num, esize)) == NULL)
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  */
791 char *
xstrdup(const char * str)792 xstrdup(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  */
803 char *
xbasename(char * path)804 xbasename(char *path)
805 {
806 	char *cp;
807 
808 	if ((cp = strrchr(path, '/')) != NULL)
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  */
819 char *
searchpath(char * path)820 searchpath(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; ) {
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 */
833 	}
834 	return (file);
835 }
836