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