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