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