Home | History | Annotate | Download | only in rdist
      1 /*
      2  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
      3  * Use is subject to license terms.
      4  */
      5 
      6 /*
      7  * Copyright (c) 1983 Regents of the University of California.
      8  * All rights reserved.
      9  *
     10  * Redistribution and use in source and binary forms are permitted
     11  * provided that the above copyright notice and this paragraph are
     12  * duplicated in all such forms and that any documentation,
     13  * advertising materials, and other materials related to such
     14  * distribution and use acknowledge that the software was developed
     15  * by the University of California, Berkeley.  The name of the
     16  * University may not be used to endorse or promote products derived
     17  * from this software without specific prior written permission.
     18  */
     19 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     20 
     21 #include "defs.h"
     22 #include <string.h>
     23 #include <setjmp.h>
     24 #include <netdb.h>
     25 #include <signal.h>
     26 #include <krb5defs.h>
     27 
     28 #ifndef RDIST
     29 #ifdef SYSV
     30 /*
     31  * Historically, the rdist program has had the following hard-coded
     32  * pathname.  Some operating systems attempt to "improve" the
     33  * directory layout, in the process re-locating the rdist binary
     34  * to some other location.  However, the first original implementation
     35  * sets a standard of sorts.  In order to interoperate with other
     36  * systems, our implementation must do two things: It must provide
     37  * the an rdist binary at the pathname below, and it must use this
     38  * pathname when executing rdist on remote systems via the rcmd()
     39  * library.  Thus the hard-coded path name below can never be changed.
     40  */
     41 #endif /* SYSV */
     42 #define	RDIST "/usr/ucb/rdist"
     43 #endif
     44 
     45 FILE	*lfp;			/* log file for recording files updated */
     46 struct	subcmd *subcmds;	/* list of sub-commands for current cmd */
     47 jmp_buf	env;
     48 
     49 void	cleanup();
     50 void	lostconn();
     51 static int	init_service(int);
     52 static struct servent *sp;
     53 
     54 static void notify(char *file, char *rhost, struct namelist *to, time_t lmod);
     55 static void rcmptime(struct stat *st);
     56 static void cmptime(char *name);
     57 static void dodcolon(char **filev, struct namelist *files, char *stamp,
     58     struct subcmd *cmds);
     59 static void closeconn(void);
     60 static void doarrow(char **filev, struct namelist *files, char *rhost,
     61     struct subcmd *cmds);
     62 static int makeconn(char *rhost);
     63 static int okname(register char *name);
     64 
     65 #ifdef SYSV
     66 #include <libgen.h>
     67 
     68 static char *recomp;
     69 static char *errstring = "regcmp failed for some unknown reason";
     70 
     71 char *
     72 re_comp(s)
     73 char *s;
     74 {
     75 	if ((int)recomp != 0)
     76 		free(recomp);
     77 	recomp = regcmp(s, (char *)0);
     78 	if (recomp == NULL)
     79 		return (errstring);
     80 	else
     81 		return ((char *)0);
     82 }
     83 
     84 
     85 static int
     86 re_exec(s)
     87 char *s;
     88 {
     89 	if ((int)recomp == 0)
     90 		return (-1);
     91 	if (regex(recomp, s) == NULL)
     92 		return (0);
     93 	else
     94 		return (1);
     95 }
     96 #endif /* SYSV */
     97 
     98 /*
     99  * Do the commands in cmds (initialized by yyparse).
    100  */
    101 void
    102 docmds(dhosts, argc, argv)
    103 	char **dhosts;
    104 	int argc;
    105 	char **argv;
    106 {
    107 	register struct cmd *c;
    108 	register struct namelist *f;
    109 	register char **cpp;
    110 	extern struct cmd *cmds;
    111 
    112 	/* protect backgrounded rdist */
    113 	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
    114 		(void) signal(SIGINT, cleanup);
    115 
    116 	/* ... and running via nohup(1) */
    117 	if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
    118 		(void) signal(SIGHUP, cleanup);
    119 	if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
    120 		(void) signal(SIGQUIT, cleanup);
    121 
    122 	(void) signal(SIGTERM, cleanup);
    123 
    124 if (debug)
    125 	if (!cmds)
    126 		printf("docmds:  cmds == NULL\n");
    127 	else {
    128 		printf("docmds:  cmds ");
    129 		prcmd(cmds);
    130 	}
    131 	for (c = cmds; c != NULL; c = c->c_next) {
    132 		if (dhosts != NULL && *dhosts != NULL) {
    133 			for (cpp = dhosts; *cpp; cpp++)
    134 				if (strcmp(c->c_name, *cpp) == 0)
    135 					goto fndhost;
    136 			continue;
    137 		}
    138 	fndhost:
    139 		if (argc) {
    140 			for (cpp = argv; *cpp; cpp++) {
    141 				if (c->c_label != NULL &&
    142 				    strcmp(c->c_label, *cpp) == 0) {
    143 					cpp = NULL;
    144 					goto found;
    145 				}
    146 				for (f = c->c_files; f != NULL; f = f->n_next)
    147 					if (strcmp(f->n_name, *cpp) == 0)
    148 						goto found;
    149 			}
    150 			continue;
    151 		} else
    152 			cpp = NULL;
    153 	found:
    154 		switch (c->c_type) {
    155 		case ARROW:
    156 			doarrow(cpp, c->c_files, c->c_name, c->c_cmds);
    157 			break;
    158 		case DCOLON:
    159 			dodcolon(cpp, c->c_files, c->c_name, c->c_cmds);
    160 			break;
    161 		default:
    162 			fatal("illegal command type %d\n", c->c_type);
    163 		}
    164 	}
    165 	closeconn();
    166 }
    167 
    168 /*
    169  * Process commands for sending files to other machines.
    170  */
    171 static void
    172 doarrow(filev, files, rhost, cmds)
    173 	char **filev;
    174 	struct namelist *files;
    175 	char *rhost;
    176 	struct subcmd *cmds;
    177 {
    178 	register struct namelist *f;
    179 	register struct subcmd *sc;
    180 	register char **cpp;
    181 	int n, ddir, opts = options;
    182 
    183 	if (debug)
    184 		printf("doarrow(%x, %s, %x)\n", files, rhost, cmds);
    185 
    186 	if (files == NULL) {
    187 		error("no files to be updated\n");
    188 		return;
    189 	}
    190 
    191 	subcmds = cmds;
    192 	ddir = files->n_next != NULL;	/* destination is a directory */
    193 	if (nflag)
    194 		printf("updating host %s\n", rhost);
    195 	else {
    196 		if (setjmp(env))
    197 			goto done;
    198 		(void) signal(SIGPIPE, lostconn);
    199 		if (!makeconn(rhost))
    200 			return;
    201 		if (!nflag)
    202 			if ((lfp = fopen(Tmpfile, "w")) == NULL) {
    203 				fatal("cannot open %s\n", Tmpfile);
    204 				exit(1);
    205 			}
    206 	}
    207 	for (f = files; f != NULL; f = f->n_next) {
    208 		if (filev) {
    209 			for (cpp = filev; *cpp; cpp++)
    210 				if (strcmp(f->n_name, *cpp) == 0)
    211 					goto found;
    212 			continue;
    213 		}
    214 	found:
    215 		n = 0;
    216 		for (sc = cmds; sc != NULL; sc = sc->sc_next) {
    217 			if (sc->sc_type != INSTALL)
    218 				continue;
    219 			n++;
    220 			install(f->n_name, sc->sc_name,
    221 				sc->sc_name == NULL ? 0 : ddir, sc->sc_options);
    222 			opts = sc->sc_options;
    223 		}
    224 		if (n == 0)
    225 			install(f->n_name, NULL, 0, options);
    226 	}
    227 done:
    228 	if (!nflag) {
    229 		(void) signal(SIGPIPE, cleanup);
    230 		(void) fclose(lfp);
    231 		lfp = NULL;
    232 	}
    233 	for (sc = cmds; sc != NULL; sc = sc->sc_next)
    234 		if (sc->sc_type == NOTIFY)
    235 			notify(Tmpfile, rhost, sc->sc_args, 0);
    236 	if (!nflag) {
    237 		(void) unlink(Tmpfile);
    238 		for (; ihead != NULL; ihead = ihead->nextp) {
    239 			free(ihead);
    240 			if ((opts & IGNLNKS) || ihead->count == 0)
    241 				continue;
    242 			log(lfp, "%s: Warning: missing links\n",
    243 				ihead->pathname);
    244 		}
    245 	}
    246 }
    247 
    248 static int
    249 init_service(int krb5flag)
    250 {
    251 	boolean_t success = B_FALSE;
    252 
    253 	if (krb5flag > 0) {
    254 		if ((sp = getservbyname("kshell", "tcp")) == NULL) {
    255 			fatal("kshell/tcp: unknown service");
    256 			(void) fprintf(stderr,
    257 				gettext("trying shell/tcp service...\n"));
    258 		} else {
    259 			success = B_TRUE;
    260 		}
    261 	} else {
    262 		if ((sp = getservbyname("shell", "tcp")) == NULL) {
    263 			fatal("shell/tcp: unknown service");
    264 			exit(1);
    265 		} else {
    266 			success = B_TRUE;
    267 		}
    268 	}
    269 	return (success);
    270 }
    271 /*
    272  * Create a connection to the rdist server on the machine rhost.
    273  */
    274 static int
    275 makeconn(rhost)
    276 	char *rhost;
    277 {
    278 	register char *ruser, *cp;
    279 	static char *cur_host = NULL;
    280 	static int port = -1;
    281 	char tuser[20];
    282 	int n;
    283 	extern char user[];
    284 
    285 	if (debug)
    286 		printf("makeconn(%s)\n", rhost);
    287 
    288 	if (cur_host != NULL && rem >= 0) {
    289 		if (strcmp(cur_host, rhost) == 0)
    290 			return (1);
    291 		closeconn();
    292 	}
    293 	cur_host = rhost;
    294 	cp = index(rhost, '@');
    295 	if (cp != NULL) {
    296 		char c = *cp;
    297 
    298 		*cp = '\0';
    299 		strncpy(tuser, rhost, sizeof (tuser)-1);
    300 		*cp = c;
    301 		rhost = cp + 1;
    302 		ruser = tuser;
    303 		if (*ruser == '\0')
    304 			ruser = user;
    305 		else if (!okname(ruser))
    306 			return (0);
    307 	} else
    308 		ruser = user;
    309 	if (!qflag)
    310 		printf("updating host %s\n", rhost);
    311 	(void) snprintf(buf, RDIST_BUFSIZ, "%s%s -Server%s",
    312 			encrypt_flag ? "-x " : "", RDIST, qflag ? " -q" : "");
    313 	if (port < 0) {
    314 		if (debug_port == 0) {
    315 			if ((retval = (int)init_service(krb5auth_flag)) == 0) {
    316 				krb5auth_flag = encrypt_flag = 0;
    317 				(void) init_service(krb5auth_flag);
    318 			}
    319 			port = sp->s_port;
    320 
    321 		} else {
    322 			port = debug_port;
    323 		}
    324 	}
    325 
    326 	if (debug) {
    327 		printf("port = %d, luser = %s, ruser = %s\n", ntohs(port),
    328 			user, ruser);
    329 		printf("buf = %s\n", buf);
    330 	}
    331 
    332 	fflush(stdout);
    333 
    334 	if (krb5auth_flag > 0) {
    335 		if ((encrypt_flag > 0) && (!krb5_privacy_allowed())) {
    336 			(void) fprintf(stderr, gettext("rdist: Encryption "
    337 					" not supported.\n"));
    338 			exit(1);
    339 		}
    340 
    341 		authopts = AP_OPTS_MUTUAL_REQUIRED;
    342 
    343 		status = kcmd(&rem, &rhost, port,
    344 				user, ruser,
    345 				buf, 0, "host", krb_realm,
    346 				bsd_context,
    347 				&auth_context,
    348 				&cred,
    349 				0,	/* No need for sequence number */
    350 				0,	/* No need for server seq # */
    351 				authopts,
    352 				1,	/* Always set anyport */
    353 				&kcmd_proto);
    354 		if (status) {
    355 			/*
    356 			 * If new protocol requested, we dont
    357 			 * fallback to less secure ones.
    358 			 */
    359 			if (kcmd_proto == KCMD_NEW_PROTOCOL) {
    360 				(void) fprintf(stderr, gettext("rdist: kcmdv2 "
    361 					"to host %s failed - %s\n"
    362 					"Fallback to normal rdist denied."),
    363 					host, error_message(status));
    364 				exit(1);
    365 			}
    366 			/* check NO_TKT_FILE or equivalent... */
    367 			if (status != -1) {
    368 				(void) fprintf(stderr, gettext("rdist: "
    369 				"kcmd to host %s failed - %s\n"
    370 				"trying normal rdist...\n\n"),
    371 				host, error_message(status));
    372 			} else {
    373 				(void) fprintf(stderr,
    374 					gettext("trying normal rdist...\n"));
    375 			}
    376 			/*
    377 			 * kcmd() failed, so we now fallback to normal rdist
    378 			 */
    379 			krb5auth_flag = encrypt_flag = 0;
    380 			(void) init_service(krb5auth_flag);
    381 			port = sp->s_port;
    382 			goto do_rcmd;
    383 		}
    384 #ifdef DEBUG
    385 		else {
    386 			(void) fprintf(stderr, gettext("Kerberized rdist "
    387 					"session, port %d in use "), port);
    388 			if (kcmd_proto == KCMD_OLD_PROTOCOL)
    389 				(void) fprintf(stderr,
    390 						gettext("[kcmd ver.1].\n"));
    391 			else
    392 				(void) fprintf(stderr,
    393 						gettext("[kcmd ver.2].\n"));
    394 		}
    395 #endif /* DEBUG */
    396 		session_key = &cred->keyblock;
    397 
    398 		if (kcmd_proto == KCMD_NEW_PROTOCOL) {
    399 			status = krb5_auth_con_getlocalsubkey(bsd_context,
    400 							    auth_context,
    401 							    &session_key);
    402 			if (status) {
    403 				com_err("rdist", status,
    404 					"determining subkey for session");
    405 				exit(1);
    406 			}
    407 			if (!session_key) {
    408 				com_err("rdist", 0,
    409 				"no subkey negotiated for connection");
    410 				exit(1);
    411 			}
    412 		}
    413 
    414 		eblock.crypto_entry = session_key->enctype;
    415 		eblock.key = (krb5_keyblock *)session_key;
    416 
    417 		init_encrypt(encrypt_flag, bsd_context, kcmd_proto, &desinbuf,
    418 				&desoutbuf, CLIENT, &eblock);
    419 
    420 
    421 		if (encrypt_flag > 0) {
    422 			char *s = gettext("This rdist session is using "
    423 				"encryption for all data transmissions.\r\n");
    424 			(void) write(2, s, strlen(s));
    425 		}
    426 
    427 	}
    428 	else
    429 do_rcmd:
    430 	{
    431 		rem = rcmd_af(&rhost, port, user, ruser, buf, 0, AF_INET6);
    432 	}
    433 
    434 	if (rem < 0)
    435 		return (0);
    436 
    437 	cp = buf;
    438 	if (desread(rem, cp, 1, 0) != 1)
    439 		lostconn();
    440 	if (*cp == 'V') {
    441 		do {
    442 			if (desread(rem, cp, 1, 0) != 1)
    443 				lostconn();
    444 		} while (*cp++ != '\n' && cp < &buf[RDIST_BUFSIZ]);
    445 		*--cp = '\0';
    446 		cp = buf;
    447 		n = 0;
    448 		while (*cp >= '0' && *cp <= '9')
    449 			n = (n * 10) + (*cp++ - '0');
    450 		if (*cp == '\0' && n == VERSION)
    451 			return (1);
    452 		error("connection failed: version numbers don't match"
    453 		    " (local %d, remote %d)\n", VERSION, n);
    454 	} else {
    455 		error("connection failed: version numbers don't match\n");
    456 	}
    457 	closeconn();
    458 	return (0);
    459 }
    460 
    461 /*
    462  * Signal end of previous connection.
    463  */
    464 static void
    465 closeconn(void)
    466 {
    467 	if (debug)
    468 		printf("closeconn()\n");
    469 
    470 	if (rem >= 0) {
    471 		(void) deswrite(rem, "\2\n", 2, 0);
    472 		(void) close(rem);
    473 		rem = -1;
    474 	}
    475 }
    476 
    477 void
    478 lostconn()
    479 {
    480 	if (iamremote)
    481 		cleanup();
    482 	log(lfp, "rdist: lost connection\n");
    483 	longjmp(env, 1);
    484 }
    485 
    486 static int
    487 okname(name)
    488 	register char *name;
    489 {
    490 	register char *cp = name;
    491 	register int c;
    492 
    493 	do {
    494 		c = *cp;
    495 		if (c & 0200)
    496 			goto bad;
    497 		if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-')
    498 			goto bad;
    499 		cp++;
    500 	} while (*cp);
    501 	return (1);
    502 bad:
    503 	error("invalid user name %s\n", name);
    504 	return (0);
    505 }
    506 
    507 time_t	lastmod;
    508 FILE	*tfp;
    509 extern	char target[], *tp;
    510 
    511 /*
    512  * Process commands for comparing files to time stamp files.
    513  */
    514 static void
    515 dodcolon(filev, files, stamp, cmds)
    516 	char **filev;
    517 	struct namelist *files;
    518 	char *stamp;
    519 	struct subcmd *cmds;
    520 {
    521 	register struct subcmd *sc;
    522 	register struct namelist *f;
    523 	register char **cpp;
    524 	struct timeval tv[2];
    525 	struct stat stb;
    526 
    527 	if (debug)
    528 		printf("dodcolon()\n");
    529 
    530 	if (files == NULL) {
    531 		error("no files to be updated\n");
    532 		return;
    533 	}
    534 	if (stat(stamp, &stb) < 0) {
    535 		error("%s: %s\n", stamp, strerror(errno));
    536 		return;
    537 	}
    538 	if (debug)
    539 		printf("%s: %d\n", stamp, stb.st_mtime);
    540 
    541 	subcmds = cmds;
    542 	lastmod = stb.st_mtime;
    543 	if (nflag || (options & VERIFY))
    544 		tfp = NULL;
    545 	else {
    546 		if ((tfp = fopen(Tmpfile, "w")) == NULL) {
    547 			error("%s: %s\n", stamp, strerror(errno));
    548 			return;
    549 		}
    550 		(void) gettimeofday(&tv[0], (struct timezone *)NULL);
    551 		tv[1] = tv[0];
    552 		(void) utimes(stamp, tv);
    553 	}
    554 
    555 	for (f = files; f != NULL; f = f->n_next) {
    556 		if (filev) {
    557 			for (cpp = filev; *cpp; cpp++)
    558 				if (strcmp(f->n_name, *cpp) == 0)
    559 					goto found;
    560 			continue;
    561 		}
    562 	found:
    563 		tp = NULL;
    564 		cmptime(f->n_name);
    565 	}
    566 
    567 	if (tfp != NULL)
    568 		(void) fclose(tfp);
    569 	for (sc = cmds; sc != NULL; sc = sc->sc_next)
    570 		if (sc->sc_type == NOTIFY)
    571 			notify(Tmpfile, NULL, sc->sc_args, lastmod);
    572 	if (!nflag && !(options & VERIFY))
    573 		(void) unlink(Tmpfile);
    574 }
    575 
    576 /*
    577  * Compare the mtime of file to the list of time stamps.
    578  */
    579 static void
    580 cmptime(name)
    581 	char *name;
    582 {
    583 	struct stat stb;
    584 
    585 	if (debug)
    586 		printf("cmptime(%s)\n", name);
    587 
    588 	if (except(name))
    589 		return;
    590 
    591 	if (nflag) {
    592 		printf("comparing dates: %s\n", name);
    593 		return;
    594 	}
    595 
    596 	/*
    597 	 * first time cmptime() is called?
    598 	 */
    599 	if (tp == NULL) {
    600 		if (exptilde(target, RDIST_BUFSIZ, name) == NULL)
    601 			return;
    602 		tp = name = target;
    603 		while (*tp)
    604 			tp++;
    605 	}
    606 	if (access(name, 4) < 0 || stat(name, &stb) < 0) {
    607 		error("%s: %s\n", name, strerror(errno));
    608 		return;
    609 	}
    610 
    611 	switch (stb.st_mode & S_IFMT) {
    612 	case S_IFREG:
    613 		break;
    614 
    615 	case S_IFDIR:
    616 		rcmptime(&stb);
    617 		return;
    618 
    619 	default:
    620 		error("%s: not a plain file\n", name);
    621 		return;
    622 	}
    623 
    624 	if (stb.st_mtime > lastmod)
    625 		log(tfp, "new: %s\n", name);
    626 }
    627 
    628 static void
    629 rcmptime(st)
    630 	struct stat *st;
    631 {
    632 	register DIR *d;
    633 	register struct dirent *dp;
    634 	register char *cp;
    635 	char *otp;
    636 	int len;
    637 
    638 	if (debug)
    639 		printf("rcmptime(%x)\n", st);
    640 
    641 	if ((d = opendir(target)) == NULL) {
    642 		error("%s: %s\n", target, strerror(errno));
    643 		return;
    644 	}
    645 	otp = tp;
    646 	len = tp - target;
    647 	while (dp = readdir(d)) {
    648 		if ((strcmp(dp->d_name, ".") == 0) ||
    649 		    (strcmp(dp->d_name, "..") == 0))
    650 			continue;
    651 		if (len + 1 + strlen(dp->d_name) >= RDIST_BUFSIZ - 1) {
    652 			error("%s/%s: Name too long\n", target, dp->d_name);
    653 			continue;
    654 		}
    655 		tp = otp;
    656 		*tp++ = '/';
    657 		cp = dp->d_name;
    658 		while (*tp++ = *cp++)
    659 			;
    660 		tp--;
    661 		cmptime(target);
    662 	}
    663 	closedir(d);
    664 	tp = otp;
    665 	*tp = '\0';
    666 }
    667 
    668 /*
    669  * Notify the list of people the changes that were made.
    670  * rhost == NULL if we are mailing a list of changes compared to at time
    671  * stamp file.
    672  */
    673 static void
    674 notify(file, rhost, to, lmod)
    675 	char *file, *rhost;
    676 	register struct namelist *to;
    677 	time_t lmod;
    678 {
    679 	register int fd, len;
    680 	FILE *pf, *popen();
    681 	struct stat stb;
    682 
    683 	if ((options & VERIFY) || to == NULL)
    684 		return;
    685 	if (!qflag) {
    686 		printf("notify ");
    687 		if (rhost)
    688 			printf("@%s ", rhost);
    689 		prnames(to);
    690 	}
    691 	if (nflag)
    692 		return;
    693 
    694 	if ((fd = open(file, 0)) < 0) {
    695 		error("%s: %s\n", file, strerror(errno));
    696 		return;
    697 	}
    698 	if (fstat(fd, &stb) < 0) {
    699 		error("%s: %s\n", file, strerror(errno));
    700 		(void) close(fd);
    701 		return;
    702 	}
    703 	if (stb.st_size == 0) {
    704 		(void) close(fd);
    705 		return;
    706 	}
    707 	/*
    708 	 * Create a pipe to mailling program.
    709 	 */
    710 	pf = popen(MAILCMD, "w");
    711 	if (pf == NULL) {
    712 		error("notify: \"%s\" failed\n", MAILCMD);
    713 		(void) close(fd);
    714 		return;
    715 	}
    716 	/*
    717 	 * Output the proper header information.
    718 	 */
    719 	fprintf(pf, "From: rdist (Remote distribution program)\n");
    720 	fprintf(pf, "To:");
    721 	if (!any('@', to->n_name) && rhost != NULL)
    722 		fprintf(pf, " %s@%s", to->n_name, rhost);
    723 	else
    724 		fprintf(pf, " %s", to->n_name);
    725 	to = to->n_next;
    726 	while (to != NULL) {
    727 		if (!any('@', to->n_name) && rhost != NULL)
    728 			fprintf(pf, ", %s@%s", to->n_name, rhost);
    729 		else
    730 			fprintf(pf, ", %s", to->n_name);
    731 		to = to->n_next;
    732 	}
    733 	putc('\n', pf);
    734 	if (rhost != NULL)
    735 		fprintf(pf, "Subject: files updated by rdist from %s to %s\n",
    736 			host, rhost);
    737 	else
    738 		fprintf(pf, "Subject: files updated after %s\n", ctime(&lmod));
    739 	putc('\n', pf);
    740 
    741 	while ((len = read(fd, buf, RDIST_BUFSIZ)) > 0)
    742 		(void) fwrite(buf, 1, len, pf);
    743 	(void) close(fd);
    744 	(void) pclose(pf);
    745 }
    746 
    747 /*
    748  * Return true if name is in the list.
    749  */
    750 int
    751 inlist(list, file)
    752 	struct namelist *list;
    753 	char *file;
    754 {
    755 	register struct namelist *nl;
    756 
    757 	for (nl = list; nl != NULL; nl = nl->n_next)
    758 		if (strcmp(file, nl->n_name) == 0)
    759 			return (1);
    760 	return (0);
    761 }
    762 
    763 /*
    764  * Return TRUE if file is in the exception list.
    765  */
    766 int
    767 except(file)
    768 	char *file;
    769 {
    770 	register struct	subcmd *sc;
    771 	register struct	namelist *nl;
    772 
    773 	if (debug)
    774 		printf("except(%s)\n", file);
    775 
    776 	for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
    777 		if (sc->sc_type != EXCEPT && sc->sc_type != PATTERN)
    778 			continue;
    779 		for (nl = sc->sc_args; nl != NULL; nl = nl->n_next) {
    780 			if (sc->sc_type == EXCEPT) {
    781 				if (strcmp(file, nl->n_name) == 0)
    782 					return (1);
    783 				continue;
    784 			}
    785 			re_comp(nl->n_name);
    786 			if (re_exec(file) > 0)
    787 				return (1);
    788 		}
    789 	}
    790 	return (0);
    791 }
    792 
    793 char *
    794 colon(cp)
    795 	register char *cp;
    796 {
    797 
    798 	while (*cp) {
    799 		if (*cp == ':')
    800 			return (cp);
    801 		if (*cp == '/')
    802 			return (0);
    803 		cp++;
    804 	}
    805 	return (0);
    806 }
    807