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