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