Home | History | Annotate | Line # | Download | only in rdist
docmd.c revision 1.1.1.1.2.1
      1 /*
      2  * Copyright (c) 1983 Regents of the University of California.
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  * 1. Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  * 2. Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in the
     12  *    documentation and/or other materials provided with the distribution.
     13  * 3. All advertising materials mentioning features or use of this software
     14  *    must display the following acknowledgement:
     15  *	This product includes software developed by the University of
     16  *	California, Berkeley and its contributors.
     17  * 4. Neither the name of the University nor the names of its contributors
     18  *    may be used to endorse or promote products derived from this software
     19  *    without specific prior written permission.
     20  *
     21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     31  * SUCH DAMAGE.
     32  */
     33 
     34 #ifndef lint
     35 static char sccsid[] = "@(#)docmd.c	5.8 (Berkeley) 3/1/91";
     36 #endif /* not lint */
     37 
     38 #include "defs.h"
     39 #include <setjmp.h>
     40 #include <netdb.h>
     41 
     42 FILE	*lfp;			/* log file for recording files updated */
     43 struct	subcmd *subcmds;	/* list of sub-commands for current cmd */
     44 jmp_buf	env;
     45 
     46 void cleanup(), lostconn();
     47 
     48 /*
     49  * Do the commands in cmds (initialized by yyparse).
     50  */
     51 docmds(dhosts, argc, argv)
     52 	char **dhosts;
     53 	int argc;
     54 	char **argv;
     55 {
     56 	register struct cmd *c;
     57 	register struct namelist *f;
     58 	register char **cpp;
     59 	extern struct cmd *cmds;
     60 
     61 	signal(SIGHUP, cleanup);
     62 	signal(SIGINT, cleanup);
     63 	signal(SIGQUIT, cleanup);
     64 	signal(SIGTERM, cleanup);
     65 
     66 	for (c = cmds; c != NULL; c = c->c_next) {
     67 		if (dhosts != NULL && *dhosts != NULL) {
     68 			for (cpp = dhosts; *cpp; cpp++)
     69 				if (strcmp(c->c_name, *cpp) == 0)
     70 					goto fndhost;
     71 			continue;
     72 		}
     73 	fndhost:
     74 		if (argc) {
     75 			for (cpp = argv; *cpp; cpp++) {
     76 				if (c->c_label != NULL &&
     77 				    strcmp(c->c_label, *cpp) == 0) {
     78 					cpp = NULL;
     79 					goto found;
     80 				}
     81 				for (f = c->c_files; f != NULL; f = f->n_next)
     82 					if (strcmp(f->n_name, *cpp) == 0)
     83 						goto found;
     84 			}
     85 			continue;
     86 		} else
     87 			cpp = NULL;
     88 	found:
     89 		switch (c->c_type) {
     90 		case ARROW:
     91 			doarrow(cpp, c->c_files, c->c_name, c->c_cmds);
     92 			break;
     93 		case DCOLON:
     94 			dodcolon(cpp, c->c_files, c->c_name, c->c_cmds);
     95 			break;
     96 		default:
     97 			fatal("illegal command type %d\n", c->c_type);
     98 		}
     99 	}
    100 	closeconn();
    101 }
    102 
    103 /*
    104  * Process commands for sending files to other machines.
    105  */
    106 doarrow(filev, files, rhost, cmds)
    107 	char **filev;
    108 	struct namelist *files;
    109 	char *rhost;
    110 	struct subcmd *cmds;
    111 {
    112 	register struct namelist *f;
    113 	register struct subcmd *sc;
    114 	register char **cpp;
    115 	int n, ddir, opts = options;
    116 
    117 	if (debug)
    118 		printf("doarrow(%x, %s, %x)\n", files, rhost, cmds);
    119 
    120 	if (files == NULL) {
    121 		error("no files to be updated\n");
    122 		return;
    123 	}
    124 
    125 	subcmds = cmds;
    126 	ddir = files->n_next != NULL;	/* destination is a directory */
    127 	if (nflag)
    128 		printf("updating host %s\n", rhost);
    129 	else {
    130 		if (setjmp(env))
    131 			goto done;
    132 		signal(SIGPIPE, lostconn);
    133 		if (!makeconn(rhost))
    134 			return;
    135 		if ((lfp = fopen(tempfile, "w")) == NULL) {
    136 			fatal("cannot open %s\n", tempfile);
    137 			exit(1);
    138 		}
    139 	}
    140 	for (f = files; f != NULL; f = f->n_next) {
    141 		if (filev) {
    142 			for (cpp = filev; *cpp; cpp++)
    143 				if (strcmp(f->n_name, *cpp) == 0)
    144 					goto found;
    145 			if (!nflag)
    146 				(void) fclose(lfp);
    147 			continue;
    148 		}
    149 	found:
    150 		n = 0;
    151 		for (sc = cmds; sc != NULL; sc = sc->sc_next) {
    152 			if (sc->sc_type != INSTALL)
    153 				continue;
    154 			n++;
    155 			install(f->n_name, sc->sc_name,
    156 				sc->sc_name == NULL ? 0 : ddir, sc->sc_options);
    157 			opts = sc->sc_options;
    158 		}
    159 		if (n == 0)
    160 			install(f->n_name, NULL, 0, options);
    161 	}
    162 done:
    163 	if (!nflag) {
    164 		(void) signal(SIGPIPE, cleanup);
    165 		(void) fclose(lfp);
    166 		lfp = NULL;
    167 	}
    168 	for (sc = cmds; sc != NULL; sc = sc->sc_next)
    169 		if (sc->sc_type == NOTIFY)
    170 			notify(tempfile, rhost, sc->sc_args, 0);
    171 	if (!nflag) {
    172 		(void) unlink(tempfile);
    173 		for (; ihead != NULL; ihead = ihead->nextp) {
    174 			free(ihead);
    175 			if ((opts & IGNLNKS) || ihead->count == 0)
    176 				continue;
    177 			log(lfp, "%s: Warning: missing links\n",
    178 				ihead->pathname);
    179 		}
    180 	}
    181 }
    182 
    183 /*
    184  * Create a connection to the rdist server on the machine rhost.
    185  */
    186 makeconn(rhost)
    187 	char *rhost;
    188 {
    189 	register char *ruser, *cp;
    190 	static char *cur_host = NULL;
    191 	static int port = -1;
    192 	char tuser[20];
    193 	int n;
    194 	extern char user[];
    195 	extern int userid;
    196 
    197 	if (debug)
    198 		printf("makeconn(%s)\n", rhost);
    199 
    200 	if (cur_host != NULL && rem >= 0) {
    201 		if (strcmp(cur_host, rhost) == 0)
    202 			return(1);
    203 		closeconn();
    204 	}
    205 	cur_host = rhost;
    206 	cp = index(rhost, '@');
    207 	if (cp != NULL) {
    208 		char c = *cp;
    209 
    210 		*cp = '\0';
    211 		strncpy(tuser, rhost, sizeof(tuser)-1);
    212 		*cp = c;
    213 		rhost = cp + 1;
    214 		ruser = tuser;
    215 		if (*ruser == '\0')
    216 			ruser = user;
    217 		else if (!okname(ruser))
    218 			return(0);
    219 	} else
    220 		ruser = user;
    221 	if (!qflag)
    222 		printf("updating host %s\n", rhost);
    223 	(void) sprintf(buf, "%s -Server%s", _PATH_RDIST, qflag ? " -q" : "");
    224 	if (port < 0) {
    225 		struct servent *sp;
    226 
    227 		if ((sp = getservbyname("shell", "tcp")) == NULL)
    228 			fatal("shell/tcp: unknown service");
    229 		port = sp->s_port;
    230 	}
    231 
    232 	if (debug) {
    233 		printf("port = %d, luser = %s, ruser = %s\n", ntohs(port), user, ruser);
    234 		printf("buf = %s\n", buf);
    235 	}
    236 
    237 	fflush(stdout);
    238 	setreuid(userid, 0);
    239 	rem = rcmd(&rhost, port, user, ruser, buf, 0);
    240 	setreuid(0, userid);
    241 	if (rem < 0)
    242 		return(0);
    243 	cp = buf;
    244 	if (read(rem, cp, 1) != 1)
    245 		lostconn();
    246 	if (*cp == 'V') {
    247 		do {
    248 			if (read(rem, cp, 1) != 1)
    249 				lostconn();
    250 		} while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
    251 		*--cp = '\0';
    252 		cp = buf;
    253 		n = 0;
    254 		while (*cp >= '0' && *cp <= '9')
    255 			n = (n * 10) + (*cp++ - '0');
    256 		if (*cp == '\0' && n == VERSION)
    257 			return(1);
    258 		error("connection failed: version numbers don't match (local %d, remote %d)\n", VERSION, n);
    259 	} else
    260 		error("connection failed: version numbers don't match\n");
    261 	closeconn();
    262 	return(0);
    263 }
    264 
    265 /*
    266  * Signal end of previous connection.
    267  */
    268 closeconn()
    269 {
    270 	if (debug)
    271 		printf("closeconn()\n");
    272 
    273 	if (rem >= 0) {
    274 		(void) write(rem, "\2\n", 2);
    275 		(void) close(rem);
    276 		rem = -1;
    277 	}
    278 }
    279 
    280 void
    281 lostconn()
    282 {
    283 	if (iamremote)
    284 		cleanup();
    285 	log(lfp, "rdist: lost connection\n");
    286 	longjmp(env, 1);
    287 }
    288 
    289 okname(name)
    290 	register char *name;
    291 {
    292 	register char *cp = name;
    293 	register int c;
    294 
    295 	do {
    296 		c = *cp;
    297 		if (c & 0200)
    298 			goto bad;
    299 		if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-')
    300 			goto bad;
    301 		cp++;
    302 	} while (*cp);
    303 	return(1);
    304 bad:
    305 	error("invalid user name %s\n", name);
    306 	return(0);
    307 }
    308 
    309 time_t	lastmod;
    310 FILE	*tfp;
    311 extern	char target[], *tp;
    312 
    313 /*
    314  * Process commands for comparing files to time stamp files.
    315  */
    316 dodcolon(filev, files, stamp, cmds)
    317 	char **filev;
    318 	struct namelist *files;
    319 	char *stamp;
    320 	struct subcmd *cmds;
    321 {
    322 	register struct subcmd *sc;
    323 	register struct namelist *f;
    324 	register char **cpp;
    325 	struct timeval tv[2];
    326 	struct timezone tz;
    327 	struct stat stb;
    328 
    329 	if (debug)
    330 		printf("dodcolon()\n");
    331 
    332 	if (files == NULL) {
    333 		error("no files to be updated\n");
    334 		return;
    335 	}
    336 	if (stat(stamp, &stb) < 0) {
    337 		error("%s: %s\n", stamp, strerror(errno));
    338 		return;
    339 	}
    340 	if (debug)
    341 		printf("%s: %d\n", stamp, stb.st_mtime);
    342 
    343 	subcmds = cmds;
    344 	lastmod = stb.st_mtime;
    345 	if (nflag || (options & VERIFY))
    346 		tfp = NULL;
    347 	else {
    348 		if ((tfp = fopen(tempfile, "w")) == NULL) {
    349 			error("%s: %s\n", stamp, strerror(errno));
    350 			return;
    351 		}
    352 		(void) gettimeofday(&tv[0], &tz);
    353 		tv[1] = tv[0];
    354 		(void) utimes(stamp, tv);
    355 	}
    356 
    357 	for (f = files; f != NULL; f = f->n_next) {
    358 		if (filev) {
    359 			for (cpp = filev; *cpp; cpp++)
    360 				if (strcmp(f->n_name, *cpp) == 0)
    361 					goto found;
    362 			continue;
    363 		}
    364 	found:
    365 		tp = NULL;
    366 		cmptime(f->n_name);
    367 	}
    368 
    369 	if (tfp != NULL)
    370 		(void) fclose(tfp);
    371 	for (sc = cmds; sc != NULL; sc = sc->sc_next)
    372 		if (sc->sc_type == NOTIFY)
    373 			notify(tempfile, NULL, sc->sc_args, lastmod);
    374 	if (!nflag && !(options & VERIFY))
    375 		(void) unlink(tempfile);
    376 }
    377 
    378 /*
    379  * Compare the mtime of file to the list of time stamps.
    380  */
    381 cmptime(name)
    382 	char *name;
    383 {
    384 	struct stat stb;
    385 
    386 	if (debug)
    387 		printf("cmptime(%s)\n", name);
    388 
    389 	if (except(name))
    390 		return;
    391 
    392 	if (nflag) {
    393 		printf("comparing dates: %s\n", name);
    394 		return;
    395 	}
    396 
    397 	/*
    398 	 * first time cmptime() is called?
    399 	 */
    400 	if (tp == NULL) {
    401 		if (exptilde(target, name) == NULL)
    402 			return;
    403 		tp = name = target;
    404 		while (*tp)
    405 			tp++;
    406 	}
    407 	if (access(name, 4) < 0 || stat(name, &stb) < 0) {
    408 		error("%s: %s\n", name, strerror(errno));
    409 		return;
    410 	}
    411 
    412 	switch (stb.st_mode & S_IFMT) {
    413 	case S_IFREG:
    414 		break;
    415 
    416 	case S_IFDIR:
    417 		rcmptime(&stb);
    418 		return;
    419 
    420 	default:
    421 		error("%s: not a plain file\n", name);
    422 		return;
    423 	}
    424 
    425 	if (stb.st_mtime > lastmod)
    426 		log(tfp, "new: %s\n", name);
    427 }
    428 
    429 rcmptime(st)
    430 	struct stat *st;
    431 {
    432 	register DIR *d;
    433 	register struct direct *dp;
    434 	register char *cp;
    435 	char *otp;
    436 	int len;
    437 
    438 	if (debug)
    439 		printf("rcmptime(%x)\n", st);
    440 
    441 	if ((d = opendir(target)) == NULL) {
    442 		error("%s: %s\n", target, strerror(errno));
    443 		return;
    444 	}
    445 	otp = tp;
    446 	len = tp - target;
    447 	while (dp = readdir(d)) {
    448 		if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
    449 			continue;
    450 		if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
    451 			error("%s/%s: Name too long\n", target, dp->d_name);
    452 			continue;
    453 		}
    454 		tp = otp;
    455 		*tp++ = '/';
    456 		cp = dp->d_name;
    457 		while (*tp++ = *cp++)
    458 			;
    459 		tp--;
    460 		cmptime(target);
    461 	}
    462 	closedir(d);
    463 	tp = otp;
    464 	*tp = '\0';
    465 }
    466 
    467 /*
    468  * Notify the list of people the changes that were made.
    469  * rhost == NULL if we are mailing a list of changes compared to at time
    470  * stamp file.
    471  */
    472 notify(file, rhost, to, lmod)
    473 	char *file, *rhost;
    474 	register struct namelist *to;
    475 	time_t lmod;
    476 {
    477 	register int fd, len;
    478 	FILE *pf, *popen();
    479 	struct stat stb;
    480 
    481 	if ((options & VERIFY) || to == NULL)
    482 		return;
    483 	if (!qflag) {
    484 		printf("notify ");
    485 		if (rhost)
    486 			printf("@%s ", rhost);
    487 		prnames(to);
    488 	}
    489 	if (nflag)
    490 		return;
    491 
    492 	if ((fd = open(file, 0)) < 0) {
    493 		error("%s: %s\n", file, strerror(errno));
    494 		return;
    495 	}
    496 	if (fstat(fd, &stb) < 0) {
    497 		error("%s: %s\n", file, strerror(errno));
    498 		(void) close(fd);
    499 		return;
    500 	}
    501 	if (stb.st_size == 0) {
    502 		(void) close(fd);
    503 		return;
    504 	}
    505 	/*
    506 	 * Create a pipe to mailling program.
    507 	 */
    508 	(void)sprintf(buf, "%s -oi -t", _PATH_SENDMAIL);
    509 	pf = popen(buf, "w");
    510 	if (pf == NULL) {
    511 		error("notify: \"%s\" failed\n", _PATH_SENDMAIL);
    512 		(void) close(fd);
    513 		return;
    514 	}
    515 	/*
    516 	 * Output the proper header information.
    517 	 */
    518 	fprintf(pf, "From: rdist (Remote distribution program)\n");
    519 	fprintf(pf, "To:");
    520 	if (!any('@', to->n_name) && rhost != NULL)
    521 		fprintf(pf, " %s@%s", to->n_name, rhost);
    522 	else
    523 		fprintf(pf, " %s", to->n_name);
    524 	to = to->n_next;
    525 	while (to != NULL) {
    526 		if (!any('@', to->n_name) && rhost != NULL)
    527 			fprintf(pf, ", %s@%s", to->n_name, rhost);
    528 		else
    529 			fprintf(pf, ", %s", to->n_name);
    530 		to = to->n_next;
    531 	}
    532 	putc('\n', pf);
    533 	if (rhost != NULL)
    534 		fprintf(pf, "Subject: files updated by rdist from %s to %s\n",
    535 			host, rhost);
    536 	else
    537 		fprintf(pf, "Subject: files updated after %s\n", ctime(&lmod));
    538 	putc('\n', pf);
    539 
    540 	while ((len = read(fd, buf, BUFSIZ)) > 0)
    541 		(void) fwrite(buf, 1, len, pf);
    542 	(void) close(fd);
    543 	(void) pclose(pf);
    544 }
    545 
    546 /*
    547  * Return true if name is in the list.
    548  */
    549 inlist(list, file)
    550 	struct namelist *list;
    551 	char *file;
    552 {
    553 	register struct namelist *nl;
    554 
    555 	for (nl = list; nl != NULL; nl = nl->n_next)
    556 		if (!strcmp(file, nl->n_name))
    557 			return(1);
    558 	return(0);
    559 }
    560 
    561 /*
    562  * Return TRUE if file is in the exception list.
    563  */
    564 except(file)
    565 	char *file;
    566 {
    567 	register struct	subcmd *sc;
    568 	register struct	namelist *nl;
    569 
    570 	if (debug)
    571 		printf("except(%s)\n", file);
    572 
    573 	for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
    574 		if (sc->sc_type != EXCEPT && sc->sc_type != PATTERN)
    575 			continue;
    576 		for (nl = sc->sc_args; nl != NULL; nl = nl->n_next) {
    577 			if (sc->sc_type == EXCEPT) {
    578 				if (!strcmp(file, nl->n_name))
    579 					return(1);
    580 				continue;
    581 			}
    582 			if (regexec(regcomp(nl->n_name), file) > 0)
    583 				return(1);
    584 		}
    585 	}
    586 	return(0);
    587 }
    588 
    589 char *
    590 colon(cp)
    591 	register char *cp;
    592 {
    593 
    594 	while (*cp) {
    595 		if (*cp == ':')
    596 			return(cp);
    597 		if (*cp == '/')
    598 			return(0);
    599 		cp++;
    600 	}
    601 	return(0);
    602 }
    603