386BSD 0.1 development
[unix-history] / usr / src / usr.bin / rdist / docmd.c
CommitLineData
f79a3fd2
WJ
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
35static 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
42FILE *lfp; /* log file for recording files updated */
43struct subcmd *subcmds; /* list of sub-commands for current cmd */
44jmp_buf env;
45
46void cleanup(), lostconn();
47
48/*
49 * Do the commands in cmds (initialized by yyparse).
50 */
51docmds(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 */
106doarrow(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 }
162done:
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 */
186makeconn(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 */
268closeconn()
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
280void
281lostconn()
282{
283 if (iamremote)
284 cleanup();
285 log(lfp, "rdist: lost connection\n");
286 longjmp(env, 1);
287}
288
289okname(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);
304bad:
305 error("invalid user name %s\n", name);
306 return(0);
307}
308
309time_t lastmod;
310FILE *tfp;
311extern char target[], *tp;
312
313/*
314 * Process commands for comparing files to time stamp files.
315 */
316dodcolon(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 */
381cmptime(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
429rcmptime(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 */
472notify(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 */
549inlist(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 */
564except(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
589char *
590colon(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}