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