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