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