2 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
7 * Copyright (c) 1983 Regents of the University of California.
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.
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.
41 #define RDIST "/usr/ucb/rdist"
44 FILE *lfp
; /* log file for recording files updated */
45 struct subcmd
*subcmds
; /* list of sub-commands for current cmd */
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
,
58 static void closeconn(void);
59 static void doarrow(char **filev
, struct namelist
*files
, char *rhost
,
61 static int makeconn(char *rhost
);
62 static int okname(char *name
);
68 static char *errstring
= "regcmp failed for some unknown reason";
75 recomp
= regcmp(s
, (char *)0);
88 if (regex(recomp
, s
) == NULL
)
96 * Do the commands in cmds (initialized by yyparse).
99 docmds(char **dhosts
, int argc
, char **argv
)
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
);
120 printf("docmds: cmds == NULL\n");
122 printf("docmds: 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)
135 for (cpp
= argv
; *cpp
; cpp
++) {
136 if (c
->c_label
!= NULL
&&
137 strcmp(c
->c_label
, *cpp
) == 0) {
141 for (f
= c
->c_files
; f
!= NULL
; f
= f
->n_next
)
142 if (strcmp(f
->n_name
, *cpp
) == 0)
151 doarrow(cpp
, c
->c_files
, c
->c_name
, c
->c_cmds
);
154 dodcolon(cpp
, c
->c_files
, c
->c_name
, c
->c_cmds
);
157 fatal("illegal command type %d\n", c
->c_type
);
164 * Process commands for sending files to other machines.
167 doarrow(char **filev
, struct namelist
*files
, char *rhost
, struct subcmd
*cmds
)
172 int n
, ddir
, opts
= options
;
175 printf("doarrow(%x, %s, %x)\n", files
, rhost
, cmds
);
178 error("no files to be updated\n");
183 ddir
= files
->n_next
!= NULL
; /* destination is a directory */
185 printf("updating host %s\n", rhost
);
189 (void) signal(SIGPIPE
, lostconn
);
190 if (!makeconn(rhost
))
193 if ((lfp
= fopen(Tmpfile
, "w")) == NULL
) {
194 fatal("cannot open %s\n", Tmpfile
);
198 for (f
= files
; f
!= NULL
; f
= f
->n_next
) {
200 for (cpp
= filev
; *cpp
; cpp
++)
201 if (strcmp(f
->n_name
, *cpp
) == 0)
207 for (sc
= cmds
; sc
!= NULL
; sc
= sc
->sc_next
) {
208 if (sc
->sc_type
!= INSTALL
)
211 install(f
->n_name
, sc
->sc_name
,
212 sc
->sc_name
== NULL
? 0 : ddir
, sc
->sc_options
);
213 opts
= sc
->sc_options
;
216 install(f
->n_name
, NULL
, 0, options
);
220 (void) signal(SIGPIPE
, cleanup
);
224 for (sc
= cmds
; sc
!= NULL
; sc
= sc
->sc_next
)
225 if (sc
->sc_type
== NOTIFY
)
226 notify(Tmpfile
, rhost
, sc
->sc_args
, 0);
228 (void) unlink(Tmpfile
);
229 for (; ihead
!= NULL
; ihead
= ihead
->nextp
) {
231 if ((opts
& IGNLNKS
) || ihead
->count
== 0)
233 log(lfp
, "%s: Warning: missing links\n",
240 init_service(int krb5flag
)
242 boolean_t success
= B_FALSE
;
245 if ((sp
= getservbyname("kshell", "tcp")) == NULL
) {
246 fatal("kshell/tcp: unknown service");
247 (void) fprintf(stderr
,
248 gettext("trying shell/tcp service...\n"));
253 if ((sp
= getservbyname("shell", "tcp")) == NULL
) {
254 fatal("shell/tcp: unknown service");
263 * Create a connection to the rdist server on the machine rhost.
266 makeconn(char *rhost
)
269 static char *cur_host
= NULL
;
270 static int port
= -1;
276 printf("makeconn(%s)\n", rhost
);
278 if (cur_host
!= NULL
&& rem
>= 0) {
279 if (strcmp(cur_host
, rhost
) == 0)
284 cp
= index(rhost
, '@');
289 strncpy(tuser
, rhost
, sizeof (tuser
)-1);
295 else if (!okname(ruser
))
300 printf("updating host %s\n", rhost
);
301 (void) snprintf(buf
, RDIST_BUFSIZ
, "%s%s -Server%s",
302 encrypt_flag
? "-x " : "", RDIST
, qflag
? " -q" : "");
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
);
317 printf("port = %d, luser = %s, ruser = %s\n", ntohs(port
),
319 printf("buf = %s\n", buf
);
324 if (krb5auth_flag
> 0) {
325 if ((encrypt_flag
> 0) && (!krb5_privacy_allowed())) {
326 (void) fprintf(stderr
, gettext("rdist: Encryption "
327 " not supported.\n"));
331 authopts
= AP_OPTS_MUTUAL_REQUIRED
;
333 status
= kcmd(&rem
, &rhost
, port
, user
, ruser
,
334 buf
, 0, "host", krb_realm
, bsd_context
, &auth_context
,
336 0, /* No need for sequence number */
337 0, /* No need for server seq # */
339 1, /* Always set anyport */
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
));
353 /* check NO_TKT_FILE or equivalent... */
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
));
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
);
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"));
379 (void) fprintf(stderr
,
380 gettext("[kcmd ver.2].\n"));
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
);
389 com_err("rdist", status
,
390 "determining subkey for session");
395 "no subkey negotiated for connection");
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
));
417 rem
= rcmd_af(&rhost
, port
, user
, ruser
, buf
, 0, AF_INET6
);
424 if (desread(rem
, cp
, 1, 0) != 1)
428 if (desread(rem
, cp
, 1, 0) != 1)
430 } while (*cp
++ != '\n' && cp
< &buf
[RDIST_BUFSIZ
]);
434 while (*cp
>= '0' && *cp
<= '9')
435 n
= (n
* 10) + (*cp
++ - '0');
436 if (*cp
== '\0' && n
== VERSION
)
438 error("connection failed: version numbers don't match"
439 " (local %d, remote %d)\n", VERSION
, n
);
441 error("connection failed: version numbers don't match\n");
448 * Signal end of previous connection.
454 printf("closeconn()\n");
457 (void) deswrite(rem
, "\2\n", 2, 0);
468 log(lfp
, "rdist: lost connection\n");
482 if (!isalpha(c
) && !isdigit(c
) && c
!= '_' && c
!= '-')
488 error("invalid user name %s\n", name
);
494 extern char target
[], *tp
;
497 * Process commands for comparing files to time stamp files.
500 dodcolon(char **filev
, struct namelist
*files
, char *stamp
, struct subcmd
*cmds
)
505 struct timeval tv
[2];
509 printf("dodcolon()\n");
512 error("no files to be updated\n");
515 if (stat(stamp
, &stb
) < 0) {
516 error("%s: %s\n", stamp
, strerror(errno
));
520 printf("%s: %d\n", stamp
, stb
.st_mtime
);
523 lastmod
= stb
.st_mtime
;
524 if (nflag
|| (options
& VERIFY
))
527 if ((tfp
= fopen(Tmpfile
, "w")) == NULL
) {
528 error("%s: %s\n", stamp
, strerror(errno
));
531 (void) gettimeofday(&tv
[0], (struct timezone
*)NULL
);
533 (void) utimes(stamp
, tv
);
536 for (f
= files
; f
!= NULL
; f
= f
->n_next
) {
538 for (cpp
= filev
; *cpp
; cpp
++)
539 if (strcmp(f
->n_name
, *cpp
) == 0)
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.
566 printf("cmptime(%s)\n", name
);
572 printf("comparing dates: %s\n", name
);
577 * first time cmptime() is called?
580 if (exptilde(target
, RDIST_BUFSIZ
, name
) == NULL
)
586 if (access(name
, 4) < 0 || stat(name
, &stb
) < 0) {
587 error("%s: %s\n", name
, strerror(errno
));
591 switch (stb
.st_mode
& S_IFMT
) {
600 error("%s: not a plain file\n", name
);
604 if (stb
.st_mtime
> lastmod
)
605 log(tfp
, "new: %s\n", name
);
609 rcmptime(struct stat
*st
)
618 printf("rcmptime(%x)\n", st
);
620 if ((d
= opendir(target
)) == NULL
) {
621 error("%s: %s\n", target
, strerror(errno
));
626 while (dp
= readdir(d
)) {
627 if ((strcmp(dp
->d_name
, ".") == 0) ||
628 (strcmp(dp
->d_name
, "..") == 0))
630 if (len
+ 1 + strlen(dp
->d_name
) >= RDIST_BUFSIZ
- 1) {
631 error("%s/%s: Name too long\n", target
, dp
->d_name
);
637 while (*tp
++ = *cp
++)
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
653 notify(char *file
, char *rhost
, struct namelist
*to
, time_t lmod
)
659 if ((options
& VERIFY
) || to
== NULL
)
664 printf("@%s ", rhost
);
670 if ((fd
= open(file
, 0)) < 0) {
671 error("%s: %s\n", file
, strerror(errno
));
674 if (fstat(fd
, &stb
) < 0) {
675 error("%s: %s\n", file
, strerror(errno
));
679 if (stb
.st_size
== 0) {
684 * Create a pipe to mailling program.
686 pf
= popen(MAILCMD
, "w");
688 error("notify: \"%s\" failed\n", MAILCMD
);
693 * Output the proper header information.
695 fprintf(pf
, "From: rdist (Remote distribution program)\n");
697 if (!any('@', to
->n_name
) && rhost
!= NULL
)
698 fprintf(pf
, " %s@%s", to
->n_name
, rhost
);
700 fprintf(pf
, " %s", to
->n_name
);
703 if (!any('@', to
->n_name
) && rhost
!= NULL
)
704 fprintf(pf
, ", %s@%s", to
->n_name
, rhost
);
706 fprintf(pf
, ", %s", to
->n_name
);
711 fprintf(pf
, "Subject: files updated by rdist from %s to %s\n",
714 fprintf(pf
, "Subject: files updated after %s\n", ctime(&lmod
));
717 while ((len
= read(fd
, buf
, RDIST_BUFSIZ
)) > 0)
718 (void) fwrite(buf
, 1, len
, pf
);
724 * Return true if name is in the list.
727 inlist(struct namelist
*list
, char *file
)
731 for (nl
= list
; nl
!= NULL
; nl
= nl
->n_next
)
732 if (strcmp(file
, nl
->n_name
) == 0)
738 * Return TRUE if file is in the exception list.
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
)
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)
759 if (re_exec(file
) > 0)