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