Fix mdoc(7)/man(7) mix up.
[netbsd-mini2440.git] / usr.bin / rdist / docmd.c
blob7e1425836cfa346506a195fa778dfd983c4f1aeb
1 /* $NetBSD: docmd.c,v 1.27 2006/12/18 15:14:42 christos Exp $ */
3 /*
4 * Copyright (c) 1983, 1993
5 * The Regents of the University of California. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
32 #include <sys/cdefs.h>
33 #ifndef lint
34 #if 0
35 static char sccsid[] = "@(#)docmd.c 8.1 (Berkeley) 6/9/93";
36 #else
37 __RCSID("$NetBSD: docmd.c,v 1.27 2006/12/18 15:14:42 christos Exp $");
38 #endif
39 #endif /* not lint */
41 #include <sys/types.h>
42 #include <sys/ioctl.h>
44 #include <errno.h>
45 #include <netdb.h>
46 #include <regex.h>
47 #include <setjmp.h>
48 #include <fcntl.h>
50 #include "defs.h"
52 FILE *lfp; /* log file for recording files updated */
53 struct subcmd *subcmds; /* list of sub-commands for current cmd */
54 jmp_buf env;
56 static int remerr = -1; /* Remote stderr */
58 static int makeconn(char *);
59 static int okname(char *);
60 static void closeconn(void);
61 static void cmptime(char *);
62 static void doarrow(char **,
63 struct namelist *, char *, struct subcmd *);
64 static void dodcolon(char **,
65 struct namelist *, char *, struct subcmd *);
66 static void notify(char *, char *, struct namelist *, time_t);
67 static void rcmptime(struct stat *);
70 * Do the commands in cmds (initialized by yyparse).
72 void
73 docmds(char **dhosts, int argc, char **argv)
75 struct cmd *c;
76 struct namelist *f;
77 char **cpp;
78 extern struct cmd *cmds;
80 signal(SIGHUP, cleanup);
81 signal(SIGINT, cleanup);
82 signal(SIGQUIT, cleanup);
83 signal(SIGTERM, cleanup);
85 for (c = cmds; c != NULL; c = c->c_next) {
86 if (dhosts != NULL && *dhosts != NULL) {
87 for (cpp = dhosts; *cpp; cpp++)
88 if (strcmp(c->c_name, *cpp) == 0)
89 goto fndhost;
90 continue;
92 fndhost:
93 if (argc) {
94 for (cpp = argv; *cpp; cpp++) {
95 if (c->c_label != NULL &&
96 strcmp(c->c_label, *cpp) == 0) {
97 cpp = NULL;
98 goto found;
100 for (f = c->c_files; f != NULL; f = f->n_next)
101 if (strcmp(f->n_name, *cpp) == 0)
102 goto found;
104 continue;
105 } else
106 cpp = NULL;
107 found:
108 switch (c->c_type) {
109 case ARROW:
110 doarrow(cpp, c->c_files, c->c_name, c->c_cmds);
111 break;
112 case DCOLON:
113 dodcolon(cpp, c->c_files, c->c_name, c->c_cmds);
114 break;
115 default:
116 fatal("illegal command type %d\n", c->c_type);
119 closeconn();
123 * Process commands for sending files to other machines.
125 static void
126 doarrow(char **filev, struct namelist *files, char *rhost, struct subcmd *cmds)
128 struct namelist *f;
129 struct subcmd *sc;
130 char **cpp;
131 int n;
132 int volatile ddir;
133 int volatile opts;
135 opts = options;
136 if (debug)
137 printf("doarrow(%lx, %s, %lx)\n",
138 (long)files, rhost, (long)cmds);
140 if (files == NULL) {
141 error("no files to be updated\n");
142 return;
145 subcmds = cmds;
146 ddir = files->n_next != NULL; /* destination is a directory */
147 if (nflag)
148 printf("updating host %s\n", rhost);
149 else {
150 if (setjmp(env))
151 goto done;
152 signal(SIGPIPE, lostconn);
153 if (!makeconn(rhost))
154 return;
155 if ((lfp = fopen(tempfile, "w")) == NULL) {
156 fatal("cannot open %s\n", tempfile);
157 exit(1);
160 for (f = files; f != NULL; f = f->n_next) {
161 if (filev) {
162 for (cpp = filev; *cpp; cpp++)
163 if (strcmp(f->n_name, *cpp) == 0)
164 goto found;
165 if (!nflag && lfp)
166 (void) fclose(lfp);
167 continue;
169 found:
170 n = 0;
171 for (sc = cmds; sc != NULL; sc = sc->sc_next) {
172 if (sc->sc_type != INSTALL)
173 continue;
174 n++;
175 install(f->n_name, sc->sc_name,
176 sc->sc_name == NULL ? 0 : ddir, sc->sc_options);
177 opts = sc->sc_options;
179 if (n == 0)
180 install(f->n_name, NULL, 0, options);
182 done:
183 if (!nflag) {
184 (void) signal(SIGPIPE, cleanup);
185 if (lfp)
186 (void) fclose(lfp);
187 lfp = NULL;
189 for (sc = cmds; sc != NULL; sc = sc->sc_next)
190 if (sc->sc_type == NOTIFY)
191 notify(tempfile, rhost, sc->sc_args, 0);
192 if (!nflag) {
193 for (; ihead != NULL; ihead = ihead->nextp) {
194 free(ihead);
195 if ((opts & IGNLNKS) || ihead->count == 0)
196 continue;
197 if (lfp)
198 dolog(lfp, "%s: Warning: missing links\n",
199 ihead->pathname);
205 * Create a connection to the rdist server on the machine rhost.
207 static int
208 makeconn(char *rhost)
210 char *ruser, *cp;
211 static char *cur_host = NULL;
212 static int port = -1;
213 char tuser[20];
214 int n;
215 extern char user[];
217 if (debug)
218 printf("makeconn(%s)\n", rhost);
220 if (cur_host != NULL && rem >= 0) {
221 if (strcmp(cur_host, rhost) == 0)
222 return(1);
223 closeconn();
225 cur_host = rhost;
226 cp = strchr(rhost, '@');
227 if (cp != NULL) {
228 char c = *cp;
230 *cp = '\0';
231 if (strlcpy(tuser, rhost, sizeof(tuser)) >= sizeof(tuser)) {
232 *cp = c;
233 return(0);
235 *cp = c;
236 rhost = cp + 1;
237 ruser = tuser;
238 if (*ruser == '\0')
239 ruser = user;
240 else if (!okname(ruser))
241 return(0);
242 } else
243 ruser = user;
244 if (!qflag)
245 printf("updating host %s\n", rhost);
246 (void) snprintf(buf, sizeof(buf), "%s -Server%s", _PATH_RDIST,
247 qflag ? " -q" : "");
248 if (port < 0) {
249 struct servent *sp;
251 if ((sp = getservbyname("shell", "tcp")) == NULL)
252 fatal("shell/tcp: unknown service");
253 port = sp->s_port;
256 if (debug) {
257 printf("port = %d, luser = %s, ruser = %s\n", ntohs(port), user, ruser);
258 printf("buf = %s\n", buf);
261 fflush(stdout);
262 seteuid(0);
263 rem = rcmd(&rhost, port, user, ruser, buf, &remerr);
264 seteuid(userid);
265 if (rem < 0)
266 return(0);
267 cp = buf;
268 if (read(rem, cp, 1) != 1)
269 lostconn(0);
270 if (*cp == 'V') {
271 do {
272 if (read(rem, cp, 1) != 1)
273 lostconn(0);
274 } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
275 *--cp = '\0';
276 cp = buf;
277 n = 0;
278 while (*cp >= '0' && *cp <= '9')
279 n = (n * 10) + (*cp++ - '0');
280 if (*cp == '\0' && n == VERSION)
281 return(1);
282 error("connection failed: version numbers don't match (local %d, remote %d)\n", VERSION, n);
283 } else {
284 error("connection failed: version numbers don't match\n");
285 error("got unexpected input:");
286 do {
287 error("%c", *cp);
288 } while (*cp != '\n' && read(rem, cp, 1) == 1);
290 closeconn();
291 return(0);
295 * Signal end of previous connection.
297 static void
298 closeconn(void)
300 if (debug)
301 printf("closeconn()\n");
303 if (rem >= 0) {
304 if (write(rem, "\2\n", 2) < 0 && debug)
305 printf("write failed on fd %d: %s\n", rem,
306 strerror(errno));
307 (void) close(rem);
308 (void) close(remerr);
309 rem = -1;
310 remerr = -1;
314 void
315 /*ARGSUSED*/
316 lostconn(int signo __unused)
318 char lcbuf[BUFSIZ];
319 int nr = -1;
321 if (remerr != -1)
322 if (ioctl(remerr, FIONREAD, &nr) != -1) {
323 if (nr >= (int)sizeof(lcbuf))
324 nr = sizeof(lcbuf) - 1;
325 if ((nr = read(remerr, lcbuf, nr)) > 0) {
326 lcbuf[nr] = '\0';
327 if (lcbuf[nr - 1] == '\n')
328 lcbuf[--nr] = '\0';
332 if (nr <= 0)
333 (void) strlcpy(lcbuf, "lost connection", sizeof(lcbuf));
335 if (iamremote)
336 cleanup(0);
337 if (lfp)
338 dolog(lfp, "rdist: %s\n", lcbuf);
339 else
340 error("%s\n", lcbuf);
341 longjmp(env, 1);
344 static int
345 okname(char *name)
347 char *cp = name;
348 int c;
350 do {
351 c = *cp;
352 if (c & 0200)
353 goto bad;
354 if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-')
355 goto bad;
356 cp++;
357 } while (*cp);
358 return(1);
359 bad:
360 error("invalid user name %s\n", name);
361 return(0);
364 time_t lastmod;
365 FILE *tfp;
366 extern char target[], *tp;
369 * Process commands for comparing files to time stamp files.
371 static void
372 dodcolon(char **filev, struct namelist *files, char *stamp, struct subcmd *cmds)
374 struct subcmd *sc;
375 struct namelist *f;
376 char **cpp;
377 struct timeval tv[2];
378 struct stat stb;
380 if (debug)
381 printf("dodcolon()\n");
383 if (files == NULL) {
384 error("no files to be updated\n");
385 return;
387 if (stat(stamp, &stb) < 0) {
388 error("%s: %s\n", stamp, strerror(errno));
389 return;
391 if (debug)
392 printf("%s: %lu\n", stamp, (u_long)stb.st_mtime);
394 subcmds = cmds;
395 lastmod = stb.st_mtime;
396 if (nflag || (options & VERIFY))
397 tfp = NULL;
398 else {
399 if ((tfp = fopen(tempfile, "w")) == NULL) {
400 error("%s: %s\n", tempfile, strerror(errno));
401 return;
403 (void) gettimeofday(&tv[0], (struct timezone *)0);
404 tv[1] = tv[0];
405 (void) utimes(stamp, tv);
408 for (f = files; f != NULL; f = f->n_next) {
409 if (filev) {
410 for (cpp = filev; *cpp; cpp++)
411 if (strcmp(f->n_name, *cpp) == 0)
412 goto found;
413 continue;
415 found:
416 tp = NULL;
417 cmptime(f->n_name);
420 if (tfp != NULL)
421 (void) fclose(tfp);
422 for (sc = cmds; sc != NULL; sc = sc->sc_next)
423 if (sc->sc_type == NOTIFY)
424 notify(tempfile, NULL, sc->sc_args, lastmod);
428 * Compare the mtime of file to the list of time stamps.
430 static void
431 cmptime(char *name)
433 struct stat stb;
435 if (debug)
436 printf("cmptime(%s)\n", name);
438 if (except(name))
439 return;
441 if (nflag) {
442 printf("comparing dates: %s\n", name);
443 return;
447 * first time cmptime() is called?
449 if (tp == NULL) {
450 if (exptilde(target, name) == NULL)
451 return;
452 tp = name = target;
453 while (*tp)
454 tp++;
456 if (access(name, 4) < 0 || stat(name, &stb) < 0) {
457 error("%s: %s\n", name, strerror(errno));
458 return;
461 switch (stb.st_mode & S_IFMT) {
462 case S_IFREG:
463 break;
465 case S_IFDIR:
466 rcmptime(&stb);
467 return;
469 default:
470 error("%s: not a plain file\n", name);
471 return;
474 if (stb.st_mtime > lastmod)
475 dolog(tfp, "new: %s\n", name);
478 static void
479 rcmptime(struct stat *st)
481 DIR *d;
482 struct dirent *dp;
483 char *cp;
484 char *otp;
485 int len;
487 if (debug)
488 printf("rcmptime(%lx)\n", (long)st);
490 if ((d = opendir(target)) == NULL) {
491 error("%s: %s\n", target, strerror(errno));
492 return;
494 otp = tp;
495 len = tp - target;
496 while ((dp = readdir(d)) != NULL) {
497 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
498 continue;
499 if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
500 error("%s/%s: Name too long\n", target, dp->d_name);
501 continue;
503 tp = otp;
504 *tp++ = '/';
505 cp = dp->d_name;
506 while ((*tp++ = *cp++) != 0)
508 tp--;
509 cmptime(target);
511 closedir(d);
512 tp = otp;
513 *tp = '\0';
517 * Notify the list of people the changes that were made.
518 * rhost == NULL if we are mailing a list of changes compared to at time
519 * stamp file.
521 static void
522 notify(char *file, char *rhost, struct namelist *to, time_t lmod)
524 int fd, len;
525 struct stat stb;
526 FILE *pf;
527 char *cp, *nrhost = rhost;
529 if ((options & VERIFY) || to == NULL)
530 return;
532 /* strip any leading user@ prefix from rhost */
533 if (rhost && (cp = strchr(rhost, '@')) != NULL)
534 nrhost = cp + 1;
536 if (!qflag) {
537 printf("notify ");
538 if (rhost)
539 printf("@%s ", nrhost);
540 prnames(to);
542 if (nflag)
543 return;
545 if ((fd = open(file, 0)) < 0) {
546 error("%s: %s\n", file, strerror(errno));
547 return;
549 if (fstat(fd, &stb) < 0) {
550 error("%s: %s\n", file, strerror(errno));
551 (void) close(fd);
552 return;
554 if (stb.st_size == 0) {
555 (void) close(fd);
556 return;
559 * Create a pipe to mailling program.
561 (void)snprintf(buf, sizeof(buf), "%s -oi -t", _PATH_SENDMAIL);
562 pf = popen(buf, "w");
563 if (pf == NULL) {
564 error("notify: \"%s\" failed\n", _PATH_SENDMAIL);
565 (void) close(fd);
566 return;
569 * Output the proper header information.
571 fprintf(pf, "From: rdist (Remote distribution program)\n");
572 fprintf(pf, "To:");
573 if (!any('@', to->n_name) && rhost != NULL)
574 fprintf(pf, " %s@%s", to->n_name, nrhost);
575 else
576 fprintf(pf, " %s", to->n_name);
577 to = to->n_next;
578 while (to != NULL) {
579 if (!any('@', to->n_name) && rhost != NULL)
580 fprintf(pf, ", %s@%s", to->n_name, nrhost);
581 else
582 fprintf(pf, ", %s", to->n_name);
583 to = to->n_next;
585 putc('\n', pf);
586 if (rhost != NULL)
587 fprintf(pf, "Subject: files updated by rdist from %s to %s\n",
588 host, rhost);
589 else
590 fprintf(pf, "Subject: files updated after %s\n", ctime(&lmod));
591 putc('\n', pf);
593 while ((len = read(fd, buf, BUFSIZ)) > 0)
594 if (fwrite(buf, 1, len, pf) < 1)
595 error("%s: %s\n", file, strerror(errno));
596 (void) close(fd);
597 (void) pclose(pf);
601 * Return true if name is in the list.
604 inlist(struct namelist *list, char *file)
606 struct namelist *nl;
608 for (nl = list; nl != NULL; nl = nl->n_next)
609 if (!strcmp(file, nl->n_name))
610 return(1);
611 return(0);
615 * Return TRUE if file is in the exception list.
618 except(char *file)
620 struct subcmd *sc;
621 struct namelist *nl;
622 int err;
623 regex_t s;
625 if (debug)
626 printf("except(%s)\n", file);
628 for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
629 if (sc->sc_type != EXCEPT && sc->sc_type != PATTERN)
630 continue;
631 for (nl = sc->sc_args; nl != NULL; nl = nl->n_next) {
632 if (sc->sc_type == EXCEPT) {
633 if (!strcmp(file, nl->n_name))
634 return(1);
635 continue;
637 if ((err = regcomp(&s, nl->n_name, 0)) != 0) {
638 char ebuf[BUFSIZ];
639 (void) regerror(err, &s, ebuf, sizeof(ebuf));
640 error("%s: %s\n", nl->n_name, ebuf);
642 if (regexec(&s, file, 0, NULL, 0) == 0) {
643 regfree(&s);
644 return(1);
646 regfree(&s);
649 return(0);
652 char *
653 colon(char *cp)
656 while (*cp) {
657 if (*cp == ':')
658 return(cp);
659 if (*cp == '/')
660 return(0);
661 cp++;
663 return(0);