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