8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / cmd-inet / usr.bin / rdist / docmd.c
blobb45b5f3ae1d91746856fa845899c9be6c1bb47b7
1 /*
2 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
6 /*
7 * Copyright (c) 1983 Regents of the University of California.
8 * All rights reserved.
10 * Redistribution and use in source and binary forms are permitted
11 * provided that the above copyright notice and this paragraph are
12 * duplicated in all such forms and that any documentation,
13 * advertising materials, and other materials related to such
14 * distribution and use acknowledge that the software was developed
15 * by the University of California, Berkeley. The name of the
16 * University may not be used to endorse or promote products derived
17 * from this software without specific prior written permission.
20 #include "defs.h"
21 #include <string.h>
22 #include <setjmp.h>
23 #include <netdb.h>
24 #include <signal.h>
25 #include <krb5defs.h>
27 #ifndef RDIST
28 #ifdef SYSV
30 * Historically, the rdist program has had the following hard-coded
31 * pathname. Some operating systems attempt to "improve" the
32 * directory layout, in the process re-locating the rdist binary
33 * to some other location. However, the first original implementation
34 * sets a standard of sorts. In order to interoperate with other
35 * systems, our implementation must do two things: It must provide
36 * the an rdist binary at the pathname below, and it must use this
37 * pathname when executing rdist on remote systems via the rcmd()
38 * library. Thus the hard-coded path name below can never be changed.
40 #endif /* SYSV */
41 #define RDIST "/usr/ucb/rdist"
42 #endif
44 FILE *lfp; /* log file for recording files updated */
45 struct subcmd *subcmds; /* list of sub-commands for current cmd */
46 jmp_buf env;
48 void cleanup();
49 void lostconn();
50 static int init_service(int);
51 static struct servent *sp;
53 static void notify(char *file, char *rhost, struct namelist *to, time_t lmod);
54 static void rcmptime(struct stat *st);
55 static void cmptime(char *name);
56 static void dodcolon(char **filev, struct namelist *files, char *stamp,
57 struct subcmd *cmds);
58 static void closeconn(void);
59 static void doarrow(char **filev, struct namelist *files, char *rhost,
60 struct subcmd *cmds);
61 static int makeconn(char *rhost);
62 static int okname(char *name);
64 #ifdef SYSV
65 #include <libgen.h>
67 static char *recomp;
68 static char *errstring = "regcmp failed for some unknown reason";
70 char *
71 re_comp(char *s)
73 if ((int)recomp != 0)
74 free(recomp);
75 recomp = regcmp(s, (char *)0);
76 if (recomp == NULL)
77 return (errstring);
78 else
79 return ((char *)0);
83 static int
84 re_exec(char *s)
86 if ((int)recomp == 0)
87 return (-1);
88 if (regex(recomp, s) == NULL)
89 return (0);
90 else
91 return (1);
93 #endif /* SYSV */
96 * Do the commands in cmds (initialized by yyparse).
98 void
99 docmds(char **dhosts, int argc, char **argv)
101 struct cmd *c;
102 struct namelist *f;
103 char **cpp;
104 extern struct cmd *cmds;
106 /* protect backgrounded rdist */
107 if (signal(SIGINT, SIG_IGN) != SIG_IGN)
108 (void) signal(SIGINT, cleanup);
110 /* ... and running via nohup(1) */
111 if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
112 (void) signal(SIGHUP, cleanup);
113 if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
114 (void) signal(SIGQUIT, cleanup);
116 (void) signal(SIGTERM, cleanup);
118 if (debug) {
119 if (!cmds)
120 printf("docmds: cmds == NULL\n");
121 else {
122 printf("docmds: cmds ");
123 prcmd(cmds);
126 for (c = cmds; c != NULL; c = c->c_next) {
127 if (dhosts != NULL && *dhosts != NULL) {
128 for (cpp = dhosts; *cpp; cpp++)
129 if (strcmp(c->c_name, *cpp) == 0)
130 goto fndhost;
131 continue;
133 fndhost:
134 if (argc) {
135 for (cpp = argv; *cpp; cpp++) {
136 if (c->c_label != NULL &&
137 strcmp(c->c_label, *cpp) == 0) {
138 cpp = NULL;
139 goto found;
141 for (f = c->c_files; f != NULL; f = f->n_next)
142 if (strcmp(f->n_name, *cpp) == 0)
143 goto found;
145 continue;
146 } else
147 cpp = NULL;
148 found:
149 switch (c->c_type) {
150 case ARROW:
151 doarrow(cpp, c->c_files, c->c_name, c->c_cmds);
152 break;
153 case DCOLON:
154 dodcolon(cpp, c->c_files, c->c_name, c->c_cmds);
155 break;
156 default:
157 fatal("illegal command type %d\n", c->c_type);
160 closeconn();
164 * Process commands for sending files to other machines.
166 static void
167 doarrow(char **filev, struct namelist *files, char *rhost, struct subcmd *cmds)
169 struct namelist *f;
170 struct subcmd *sc;
171 char **cpp;
172 int n, ddir, opts = options;
174 if (debug)
175 printf("doarrow(%x, %s, %x)\n", files, rhost, cmds);
177 if (files == NULL) {
178 error("no files to be updated\n");
179 return;
182 subcmds = cmds;
183 ddir = files->n_next != NULL; /* destination is a directory */
184 if (nflag)
185 printf("updating host %s\n", rhost);
186 else {
187 if (setjmp(env))
188 goto done;
189 (void) signal(SIGPIPE, lostconn);
190 if (!makeconn(rhost))
191 return;
192 if (!nflag)
193 if ((lfp = fopen(Tmpfile, "w")) == NULL) {
194 fatal("cannot open %s\n", Tmpfile);
195 exit(1);
198 for (f = files; f != NULL; f = f->n_next) {
199 if (filev) {
200 for (cpp = filev; *cpp; cpp++)
201 if (strcmp(f->n_name, *cpp) == 0)
202 goto found;
203 continue;
205 found:
206 n = 0;
207 for (sc = cmds; sc != NULL; sc = sc->sc_next) {
208 if (sc->sc_type != INSTALL)
209 continue;
210 n++;
211 install(f->n_name, sc->sc_name,
212 sc->sc_name == NULL ? 0 : ddir, sc->sc_options);
213 opts = sc->sc_options;
215 if (n == 0)
216 install(f->n_name, NULL, 0, options);
218 done:
219 if (!nflag) {
220 (void) signal(SIGPIPE, cleanup);
221 (void) fclose(lfp);
222 lfp = NULL;
224 for (sc = cmds; sc != NULL; sc = sc->sc_next)
225 if (sc->sc_type == NOTIFY)
226 notify(Tmpfile, rhost, sc->sc_args, 0);
227 if (!nflag) {
228 (void) unlink(Tmpfile);
229 for (; ihead != NULL; ihead = ihead->nextp) {
230 free(ihead);
231 if ((opts & IGNLNKS) || ihead->count == 0)
232 continue;
233 log(lfp, "%s: Warning: missing links\n",
234 ihead->pathname);
239 static int
240 init_service(int krb5flag)
242 boolean_t success = B_FALSE;
244 if (krb5flag > 0) {
245 if ((sp = getservbyname("kshell", "tcp")) == NULL) {
246 fatal("kshell/tcp: unknown service");
247 (void) fprintf(stderr,
248 gettext("trying shell/tcp service...\n"));
249 } else {
250 success = B_TRUE;
252 } else {
253 if ((sp = getservbyname("shell", "tcp")) == NULL) {
254 fatal("shell/tcp: unknown service");
255 exit(1);
256 } else {
257 success = B_TRUE;
260 return (success);
263 * Create a connection to the rdist server on the machine rhost.
265 static int
266 makeconn(char *rhost)
268 char *ruser, *cp;
269 static char *cur_host = NULL;
270 static int port = -1;
271 char tuser[20];
272 int n;
273 extern char user[];
275 if (debug)
276 printf("makeconn(%s)\n", rhost);
278 if (cur_host != NULL && rem >= 0) {
279 if (strcmp(cur_host, rhost) == 0)
280 return (1);
281 closeconn();
283 cur_host = rhost;
284 cp = index(rhost, '@');
285 if (cp != NULL) {
286 char c = *cp;
288 *cp = '\0';
289 strncpy(tuser, rhost, sizeof (tuser)-1);
290 *cp = c;
291 rhost = cp + 1;
292 ruser = tuser;
293 if (*ruser == '\0')
294 ruser = user;
295 else if (!okname(ruser))
296 return (0);
297 } else
298 ruser = user;
299 if (!qflag)
300 printf("updating host %s\n", rhost);
301 (void) snprintf(buf, RDIST_BUFSIZ, "%s%s -Server%s",
302 encrypt_flag ? "-x " : "", RDIST, qflag ? " -q" : "");
303 if (port < 0) {
304 if (debug_port == 0) {
305 if ((retval = (int)init_service(krb5auth_flag)) == 0) {
306 krb5auth_flag = encrypt_flag = 0;
307 (void) init_service(krb5auth_flag);
309 port = sp->s_port;
311 } else {
312 port = debug_port;
316 if (debug) {
317 printf("port = %d, luser = %s, ruser = %s\n", ntohs(port),
318 user, ruser);
319 printf("buf = %s\n", buf);
322 fflush(stdout);
324 if (krb5auth_flag > 0) {
325 if ((encrypt_flag > 0) && (!krb5_privacy_allowed())) {
326 (void) fprintf(stderr, gettext("rdist: Encryption "
327 " not supported.\n"));
328 exit(1);
331 authopts = AP_OPTS_MUTUAL_REQUIRED;
333 status = kcmd(&rem, &rhost, port, user, ruser,
334 buf, 0, "host", krb_realm, bsd_context, &auth_context,
335 &cred,
336 0, /* No need for sequence number */
337 0, /* No need for server seq # */
338 authopts,
339 1, /* Always set anyport */
340 &kcmd_proto);
341 if (status) {
343 * If new protocol requested, we dont
344 * fallback to less secure ones.
346 if (kcmd_proto == KCMD_NEW_PROTOCOL) {
347 (void) fprintf(stderr, gettext("rdist: kcmdv2 "
348 "to host %s failed - %s\n"
349 "Fallback to normal rdist denied."),
350 host, error_message(status));
351 exit(1);
353 /* check NO_TKT_FILE or equivalent... */
354 if (status != -1) {
355 (void) fprintf(stderr, gettext("rdist: "
356 "kcmd to host %s failed - %s\n"
357 "trying normal rdist...\n\n"),
358 host, error_message(status));
359 } else {
360 (void) fprintf(stderr,
361 gettext("trying normal rdist...\n"));
364 * kcmd() failed, so we now fallback to normal rdist
366 krb5auth_flag = encrypt_flag = 0;
367 (void) init_service(krb5auth_flag);
368 port = sp->s_port;
369 goto do_rcmd;
371 #ifdef DEBUG
372 else {
373 (void) fprintf(stderr, gettext("Kerberized rdist "
374 "session, port %d in use "), port);
375 if (kcmd_proto == KCMD_OLD_PROTOCOL)
376 (void) fprintf(stderr,
377 gettext("[kcmd ver.1].\n"));
378 else
379 (void) fprintf(stderr,
380 gettext("[kcmd ver.2].\n"));
382 #endif /* DEBUG */
383 session_key = &cred->keyblock;
385 if (kcmd_proto == KCMD_NEW_PROTOCOL) {
386 status = krb5_auth_con_getlocalsubkey(bsd_context,
387 auth_context, &session_key);
388 if (status) {
389 com_err("rdist", status,
390 "determining subkey for session");
391 exit(1);
393 if (!session_key) {
394 com_err("rdist", 0,
395 "no subkey negotiated for connection");
396 exit(1);
400 eblock.crypto_entry = session_key->enctype;
401 eblock.key = (krb5_keyblock *)session_key;
403 init_encrypt(encrypt_flag, bsd_context, kcmd_proto, &desinbuf,
404 &desoutbuf, CLIENT, &eblock);
407 if (encrypt_flag > 0) {
408 char *s = gettext("This rdist session is using "
409 "encryption for all data transmissions.\r\n");
410 (void) write(2, s, strlen(s));
414 else
415 do_rcmd:
417 rem = rcmd_af(&rhost, port, user, ruser, buf, 0, AF_INET6);
420 if (rem < 0)
421 return (0);
423 cp = buf;
424 if (desread(rem, cp, 1, 0) != 1)
425 lostconn();
426 if (*cp == 'V') {
427 do {
428 if (desread(rem, cp, 1, 0) != 1)
429 lostconn();
430 } while (*cp++ != '\n' && cp < &buf[RDIST_BUFSIZ]);
431 *--cp = '\0';
432 cp = buf;
433 n = 0;
434 while (*cp >= '0' && *cp <= '9')
435 n = (n * 10) + (*cp++ - '0');
436 if (*cp == '\0' && n == VERSION)
437 return (1);
438 error("connection failed: version numbers don't match"
439 " (local %d, remote %d)\n", VERSION, n);
440 } else {
441 error("connection failed: version numbers don't match\n");
443 closeconn();
444 return (0);
448 * Signal end of previous connection.
450 static void
451 closeconn(void)
453 if (debug)
454 printf("closeconn()\n");
456 if (rem >= 0) {
457 (void) deswrite(rem, "\2\n", 2, 0);
458 (void) close(rem);
459 rem = -1;
463 void
464 lostconn(void)
466 if (iamremote)
467 cleanup();
468 log(lfp, "rdist: lost connection\n");
469 longjmp(env, 1);
472 static int
473 okname(char *name)
475 char *cp = name;
476 int c;
478 do {
479 c = *cp;
480 if (c & 0200)
481 goto bad;
482 if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-')
483 goto bad;
484 cp++;
485 } while (*cp);
486 return (1);
487 bad:
488 error("invalid user name %s\n", name);
489 return (0);
492 time_t lastmod;
493 FILE *tfp;
494 extern char target[], *tp;
497 * Process commands for comparing files to time stamp files.
499 static void
500 dodcolon(char **filev, struct namelist *files, char *stamp, struct subcmd *cmds)
502 struct subcmd *sc;
503 struct namelist *f;
504 char **cpp;
505 struct timeval tv[2];
506 struct stat stb;
508 if (debug)
509 printf("dodcolon()\n");
511 if (files == NULL) {
512 error("no files to be updated\n");
513 return;
515 if (stat(stamp, &stb) < 0) {
516 error("%s: %s\n", stamp, strerror(errno));
517 return;
519 if (debug)
520 printf("%s: %d\n", stamp, stb.st_mtime);
522 subcmds = cmds;
523 lastmod = stb.st_mtime;
524 if (nflag || (options & VERIFY))
525 tfp = NULL;
526 else {
527 if ((tfp = fopen(Tmpfile, "w")) == NULL) {
528 error("%s: %s\n", stamp, strerror(errno));
529 return;
531 (void) gettimeofday(&tv[0], (struct timezone *)NULL);
532 tv[1] = tv[0];
533 (void) utimes(stamp, tv);
536 for (f = files; f != NULL; f = f->n_next) {
537 if (filev) {
538 for (cpp = filev; *cpp; cpp++)
539 if (strcmp(f->n_name, *cpp) == 0)
540 goto found;
541 continue;
543 found:
544 tp = NULL;
545 cmptime(f->n_name);
548 if (tfp != NULL)
549 (void) fclose(tfp);
550 for (sc = cmds; sc != NULL; sc = sc->sc_next)
551 if (sc->sc_type == NOTIFY)
552 notify(Tmpfile, NULL, sc->sc_args, lastmod);
553 if (!nflag && !(options & VERIFY))
554 (void) unlink(Tmpfile);
558 * Compare the mtime of file to the list of time stamps.
560 static void
561 cmptime(char *name)
563 struct stat stb;
565 if (debug)
566 printf("cmptime(%s)\n", name);
568 if (except(name))
569 return;
571 if (nflag) {
572 printf("comparing dates: %s\n", name);
573 return;
577 * first time cmptime() is called?
579 if (tp == NULL) {
580 if (exptilde(target, RDIST_BUFSIZ, name) == NULL)
581 return;
582 tp = name = target;
583 while (*tp)
584 tp++;
586 if (access(name, 4) < 0 || stat(name, &stb) < 0) {
587 error("%s: %s\n", name, strerror(errno));
588 return;
591 switch (stb.st_mode & S_IFMT) {
592 case S_IFREG:
593 break;
595 case S_IFDIR:
596 rcmptime(&stb);
597 return;
599 default:
600 error("%s: not a plain file\n", name);
601 return;
604 if (stb.st_mtime > lastmod)
605 log(tfp, "new: %s\n", name);
608 static void
609 rcmptime(struct stat *st)
611 DIR *d;
612 struct dirent *dp;
613 char *cp;
614 char *otp;
615 int len;
617 if (debug)
618 printf("rcmptime(%x)\n", st);
620 if ((d = opendir(target)) == NULL) {
621 error("%s: %s\n", target, strerror(errno));
622 return;
624 otp = tp;
625 len = tp - target;
626 while (dp = readdir(d)) {
627 if ((strcmp(dp->d_name, ".") == 0) ||
628 (strcmp(dp->d_name, "..") == 0))
629 continue;
630 if (len + 1 + strlen(dp->d_name) >= RDIST_BUFSIZ - 1) {
631 error("%s/%s: Name too long\n", target, dp->d_name);
632 continue;
634 tp = otp;
635 *tp++ = '/';
636 cp = dp->d_name;
637 while (*tp++ = *cp++)
639 tp--;
640 cmptime(target);
642 closedir(d);
643 tp = otp;
644 *tp = '\0';
648 * Notify the list of people the changes that were made.
649 * rhost == NULL if we are mailing a list of changes compared to at time
650 * stamp file.
652 static void
653 notify(char *file, char *rhost, struct namelist *to, time_t lmod)
655 int fd, len;
656 FILE *pf, *popen();
657 struct stat stb;
659 if ((options & VERIFY) || to == NULL)
660 return;
661 if (!qflag) {
662 printf("notify ");
663 if (rhost)
664 printf("@%s ", rhost);
665 prnames(to);
667 if (nflag)
668 return;
670 if ((fd = open(file, 0)) < 0) {
671 error("%s: %s\n", file, strerror(errno));
672 return;
674 if (fstat(fd, &stb) < 0) {
675 error("%s: %s\n", file, strerror(errno));
676 (void) close(fd);
677 return;
679 if (stb.st_size == 0) {
680 (void) close(fd);
681 return;
684 * Create a pipe to mailling program.
686 pf = popen(MAILCMD, "w");
687 if (pf == NULL) {
688 error("notify: \"%s\" failed\n", MAILCMD);
689 (void) close(fd);
690 return;
693 * Output the proper header information.
695 fprintf(pf, "From: rdist (Remote distribution program)\n");
696 fprintf(pf, "To:");
697 if (!any('@', to->n_name) && rhost != NULL)
698 fprintf(pf, " %s@%s", to->n_name, rhost);
699 else
700 fprintf(pf, " %s", to->n_name);
701 to = to->n_next;
702 while (to != NULL) {
703 if (!any('@', to->n_name) && rhost != NULL)
704 fprintf(pf, ", %s@%s", to->n_name, rhost);
705 else
706 fprintf(pf, ", %s", to->n_name);
707 to = to->n_next;
709 putc('\n', pf);
710 if (rhost != NULL)
711 fprintf(pf, "Subject: files updated by rdist from %s to %s\n",
712 host, rhost);
713 else
714 fprintf(pf, "Subject: files updated after %s\n", ctime(&lmod));
715 putc('\n', pf);
717 while ((len = read(fd, buf, RDIST_BUFSIZ)) > 0)
718 (void) fwrite(buf, 1, len, pf);
719 (void) close(fd);
720 (void) pclose(pf);
724 * Return true if name is in the list.
727 inlist(struct namelist *list, char *file)
729 struct namelist *nl;
731 for (nl = list; nl != NULL; nl = nl->n_next)
732 if (strcmp(file, nl->n_name) == 0)
733 return (1);
734 return (0);
738 * Return TRUE if file is in the exception list.
741 except(char *file)
743 struct subcmd *sc;
744 struct namelist *nl;
746 if (debug)
747 printf("except(%s)\n", file);
749 for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
750 if (sc->sc_type != EXCEPT && sc->sc_type != PATTERN)
751 continue;
752 for (nl = sc->sc_args; nl != NULL; nl = nl->n_next) {
753 if (sc->sc_type == EXCEPT) {
754 if (strcmp(file, nl->n_name) == 0)
755 return (1);
756 continue;
758 re_comp(nl->n_name);
759 if (re_exec(file) > 0)
760 return (1);
763 return (0);
766 char *
767 colon(char *cp)
769 while (*cp) {
770 if (*cp == ':')
771 return (cp);
772 if (*cp == '/')
773 return (0);
774 cp++;
776 return (0);