2 * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 #include <sys/param.h>
30 #include <sys/select.h>
31 #include <sys/socket.h>
32 #include <sys/types.h>
71 static void killer_start(struct killer *, struct mux *);
72 static void *killer_run(void *);
73 static void killer_stop(struct killer *);
75 static int proto_waitconnect(int);
76 static int proto_greet(struct config *);
77 static int proto_negproto(struct config *);
78 static int proto_login(struct config *);
79 static int proto_fileattr(struct config *);
80 static int proto_xchgcoll(struct config *);
81 static struct mux *proto_mux(struct config *);
83 static int proto_escape(struct stream *, const char *);
84 static void proto_unescape(char *);
87 proto_waitconnect(int s)
91 int error, rv, soerror;
97 rv = select(s + 1, &readfd, NULL, NULL, NULL);
98 } while (rv == -1 && errno == EINTR);
101 /* Check that the connection was really successful. */
102 len = sizeof(soerror);
103 error = getsockopt(s, SOL_SOCKET, SO_ERROR, &soerror, &len);
105 /* We have no choice but faking an error here. */
106 errno = ECONNREFUSED;
116 /* Connect to the CVSup server. */
118 proto_connect(struct config *config, int family, uint16_t port)
120 char addrbuf[NI_MAXHOST];
121 /* Enough to hold sizeof("cvsup") or any port number. */
123 struct addrinfo *res, *ai, hints;
128 snprintf(servname, sizeof(servname), "%d", port);
130 strncpy(servname, "cvsup", sizeof(servname) - 1);
131 servname[sizeof(servname) - 1] = '\0';
133 memset(&hints, 0, sizeof(hints));
134 hints.ai_family = family;
135 hints.ai_socktype = SOCK_STREAM;
136 error = getaddrinfo(config->host, servname, &hints, &res);
138 * Try with the hardcoded port number for OSes that don't
139 * have cvsup defined in the /etc/services file.
141 if (error == EAI_SERVICE) {
142 strncpy(servname, "5999", sizeof(servname) - 1);
143 servname[sizeof(servname) - 1] = '\0';
144 error = getaddrinfo(config->host, servname, &hints, &res);
147 lprintf(0, "Name lookup failure for \"%s\": %s\n", config->host,
148 gai_strerror(error));
149 return (STATUS_TRANSIENTFAILURE);
151 for (ai = res; ai != NULL; ai = ai->ai_next) {
152 s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
155 if (config->laddr != NULL) {
157 (void)setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
159 error = bind(s, config->laddr,
163 error = connect(s, ai->ai_addr, ai->ai_addrlen);
164 if (error && errno == EINTR)
165 error = proto_waitconnect(s);
170 (void)getnameinfo(ai->ai_addr, ai->ai_addrlen, addrbuf,
171 sizeof(addrbuf), NULL, 0, NI_NUMERICHOST);
172 if (s == -1 || error) {
173 lprintf(0, "Cannot connect to %s: %s\n", addrbuf,
177 lprintf(1, "Connected to %s\n", addrbuf);
180 return (STATUS_SUCCESS);
183 return (STATUS_TRANSIENTFAILURE);
186 /* Greet the server. */
188 proto_greet(struct config *config)
190 char *line, *cmd, *msg, *swver;
194 line = stream_getln(s, NULL);
195 cmd = proto_get_ascii(&line);
198 if (strcmp(cmd, "OK") == 0) {
199 (void)proto_get_ascii(&line); /* major number */
200 (void)proto_get_ascii(&line); /* minor number */
201 swver = proto_get_ascii(&line);
202 } else if (strcmp(cmd, "!") == 0) {
203 msg = proto_get_rest(&line);
206 lprintf(-1, "Rejected by server: %s\n", msg);
207 return (STATUS_TRANSIENTFAILURE);
210 lprintf(2, "Server software version: %s\n",
211 swver != NULL ? swver : ".");
212 return (STATUS_SUCCESS);
214 lprintf(-1, "Invalid greeting from server\n");
215 return (STATUS_FAILURE);
218 /* Negotiate protocol version with the server. */
220 proto_negproto(struct config *config)
223 char *cmd, *line, *msg;
227 proto_printf(s, "PROTO %d %d %s\n", PROTO_MAJ, PROTO_MIN, PROTO_SWVER);
229 line = stream_getln(s, NULL);
230 cmd = proto_get_ascii(&line);
233 if (strcmp(cmd, "!") == 0) {
234 msg = proto_get_rest(&line);
235 lprintf(-1, "Protocol negotiation failed: %s\n", msg);
237 } else if (strcmp(cmd, "PROTO") != 0)
239 error = proto_get_int(&line, &maj, 10);
241 error = proto_get_int(&line, &min, 10);
244 if (maj != PROTO_MAJ || min != PROTO_MIN) {
245 lprintf(-1, "Server protocol version %d.%d not supported "
246 "by client\n", maj, min);
247 return (STATUS_FAILURE);
249 return (STATUS_SUCCESS);
251 lprintf(-1, "Invalid PROTO command from server\n");
252 return (STATUS_FAILURE);
256 proto_login(struct config *config)
259 char host[MAXHOSTNAMELEN];
260 char *line, *cmd, *realm, *challenge, *msg;
263 gethostname(host, sizeof(host));
264 host[sizeof(host) - 1] = '\0';
265 proto_printf(s, "USER %s %s\n", getlogin(), host);
267 line = stream_getln(s, NULL);
268 cmd = proto_get_ascii(&line);
269 realm = proto_get_ascii(&line);
270 challenge = proto_get_ascii(&line);
271 if (challenge == NULL || line != NULL)
273 if (strcmp(realm, ".") != 0 || strcmp(challenge, ".") != 0) {
274 lprintf(-1, "Authentication required by the server and not "
275 "supported by client\n");
276 return (STATUS_FAILURE);
278 proto_printf(s, "AUTHMD5 . . .\n");
280 line = stream_getln(s, NULL);
281 cmd = proto_get_ascii(&line);
282 if (strcmp(cmd, "OK") == 0)
283 return (STATUS_SUCCESS);
284 if (strcmp(cmd, "!") == 0) {
285 msg = proto_get_rest(&line);
288 lprintf(-1, "Server error: %s\n", msg);
289 return (STATUS_FAILURE);
292 lprintf(-1, "Invalid server reply to AUTHMD5\n");
293 return (STATUS_FAILURE);
297 * File attribute support negotiation.
300 proto_fileattr(struct config *config)
302 fattr_support_t support;
305 int error, i, n, attr;
308 lprintf(2, "Negotiating file attribute support\n");
309 proto_printf(s, "ATTR %d\n", FT_NUMBER);
310 for (i = 0; i < FT_NUMBER; i++)
311 proto_printf(s, "%x\n", fattr_supported(i));
312 proto_printf(s, ".\n");
314 line = stream_getln(s, NULL);
317 cmd = proto_get_ascii(&line);
318 error = proto_get_int(&line, &n, 10);
319 if (error || line != NULL || strcmp(cmd, "ATTR") != 0 || n > FT_NUMBER)
321 for (i = 0; i < n; i++) {
322 line = stream_getln(s, NULL);
325 error = proto_get_int(&line, &attr, 16);
328 support[i] = fattr_supported(i) & attr;
330 for (i = n; i < FT_NUMBER; i++)
332 line = stream_getln(s, NULL);
333 if (line == NULL || strcmp(line, ".") != 0)
335 memcpy(config->fasupport, support, sizeof(config->fasupport));
336 return (STATUS_SUCCESS);
338 lprintf(-1, "Protocol error negotiating attribute support\n");
339 return (STATUS_FAILURE);
343 * Exchange collection information.
346 proto_xchgcoll(struct config *config)
350 struct globtree *diraccept, *dirrefuse;
351 struct globtree *fileaccept, *filerefuse;
352 char *line, *cmd, *collname, *pat;
353 char *msg, *release, *ident, *rcskey, *prefix;
355 int error, flags, options;
358 lprintf(2, "Exchanging collection information\n");
359 STAILQ_FOREACH(coll, &config->colls, co_next) {
360 proto_printf(s, "COLL %s %s %o %d\n", coll->co_name,
361 coll->co_release, coll->co_umask, coll->co_options);
362 for (i = 0; i < pattlist_size(coll->co_accepts); i++) {
363 proto_printf(s, "ACC %s\n",
364 pattlist_get(coll->co_accepts, i));
366 for (i = 0; i < pattlist_size(coll->co_refusals); i++) {
367 proto_printf(s, "REF %s\n",
368 pattlist_get(coll->co_refusals, i));
370 proto_printf(s, ".\n");
372 proto_printf(s, ".\n");
374 STAILQ_FOREACH(coll, &config->colls, co_next) {
375 if (coll->co_options & CO_SKIP)
377 line = stream_getln(s, NULL);
380 cmd = proto_get_ascii(&line);
381 collname = proto_get_ascii(&line);
382 release = proto_get_ascii(&line);
383 error = proto_get_int(&line, &options, 10);
384 if (error || line != NULL)
386 if (strcmp(cmd, "COLL") != 0 ||
387 strcmp(collname, coll->co_name) != 0 ||
388 strcmp(release, coll->co_release) != 0)
391 (coll->co_options | (options & CO_SERVMAYSET)) &
392 ~(~options & CO_SERVMAYCLEAR);
393 while ((line = stream_getln(s, NULL)) != NULL) {
394 if (strcmp(line, ".") == 0)
396 cmd = proto_get_ascii(&line);
399 if (strcmp(cmd, "!") == 0) {
400 msg = proto_get_rest(&line);
403 lprintf(-1, "Server message: %s\n", msg);
404 } else if (strcmp(cmd, "PRFX") == 0) {
405 prefix = proto_get_ascii(&line);
406 if (prefix == NULL || line != NULL)
408 coll->co_cvsroot = xstrdup(prefix);
409 } else if (strcmp(cmd, "KEYALIAS") == 0) {
410 ident = proto_get_ascii(&line);
411 rcskey = proto_get_ascii(&line);
412 if (rcskey == NULL || line != NULL)
414 error = keyword_alias(coll->co_keyword, ident,
418 } else if (strcmp(cmd, "KEYON") == 0) {
419 ident = proto_get_ascii(&line);
420 if (ident == NULL || line != NULL)
422 error = keyword_enable(coll->co_keyword, ident);
425 } else if (strcmp(cmd, "KEYOFF") == 0) {
426 ident = proto_get_ascii(&line);
427 if (ident == NULL || line != NULL)
429 error = keyword_disable(coll->co_keyword,
437 keyword_prepare(coll->co_keyword);
439 diraccept = globtree_true();
440 fileaccept = globtree_true();
441 dirrefuse = globtree_false();
442 filerefuse = globtree_false();
444 if (pattlist_size(coll->co_accepts) > 0) {
445 globtree_free(diraccept);
446 globtree_free(fileaccept);
447 diraccept = globtree_false();
448 fileaccept = globtree_false();
449 flags = FNM_PATHNAME | FNM_LEADING_DIR |
451 for (i = 0; i < pattlist_size(coll->co_accepts); i++) {
452 pat = pattlist_get(coll->co_accepts, i);
453 diraccept = globtree_or(diraccept,
454 globtree_match(pat, flags));
457 if (coll->co_options & CO_CHECKOUTMODE &&
458 (len == 0 || pat[len - 1] != '*')) {
459 /* We must modify the pattern so that it
460 refers to the RCS file, rather than
461 the checked-out file. */
462 xasprintf(&pat, "%s,v", pat);
463 fileaccept = globtree_or(fileaccept,
464 globtree_match(pat, flags));
467 fileaccept = globtree_or(fileaccept,
468 globtree_match(pat, flags));
473 for (i = 0; i < pattlist_size(coll->co_refusals); i++) {
474 pat = pattlist_get(coll->co_refusals, i);
475 dirrefuse = globtree_or(dirrefuse,
476 globtree_match(pat, 0));
478 if (coll->co_options & CO_CHECKOUTMODE &&
479 (len == 0 || pat[len - 1] != '*')) {
480 /* We must modify the pattern so that it refers
481 to the RCS file, rather than the checked-out
483 xasprintf(&pat, "%s,v", pat);
484 filerefuse = globtree_or(filerefuse,
485 globtree_match(pat, 0));
488 filerefuse = globtree_or(filerefuse,
489 globtree_match(pat, 0));
493 coll->co_dirfilter = globtree_and(diraccept,
494 globtree_not(dirrefuse));
495 coll->co_filefilter = globtree_and(fileaccept,
496 globtree_not(filerefuse));
498 /* At this point we don't need the pattern lists anymore. */
499 pattlist_free(coll->co_accepts);
500 pattlist_free(coll->co_refusals);
501 coll->co_accepts = NULL;
502 coll->co_refusals = NULL;
504 /* Set up a mask of file attributes that we don't want to sync
506 if (!(coll->co_options & CO_SETOWNER))
507 coll->co_attrignore |= FA_OWNER | FA_GROUP;
508 if (!(coll->co_options & CO_SETMODE))
509 coll->co_attrignore |= FA_MODE;
510 if (!(coll->co_options & CO_SETFLAGS))
511 coll->co_attrignore |= FA_FLAGS;
513 return (STATUS_SUCCESS);
515 lprintf(-1, "Protocol error during collection exchange\n");
516 return (STATUS_FAILURE);
520 proto_mux(struct config *config)
523 struct stream *s, *wr;
524 struct chan *chan0, *chan1;
528 lprintf(2, "Establishing multiplexed-mode data connection\n");
529 proto_printf(s, "MUX\n");
531 m = mux_open(config->socket, &chan0);
533 lprintf(-1, "Cannot open the multiplexer\n");
538 lprintf(-1, "ChannelMux.Listen failed: %s\n", strerror(errno));
542 wr = stream_open(chan0, NULL, (stream_writefn_t *)chan_write, NULL);
543 proto_printf(wr, "CHAN %d\n", id);
545 chan1 = chan_accept(m, id);
547 lprintf(-1, "ChannelMux.Accept failed: %s\n", strerror(errno));
551 config->chan0 = chan0;
552 config->chan1 = chan1;
557 * Initializes the connection to the CVSup server, that is handle
558 * the protocol negotiation, logging in, exchanging file attributes
559 * support and collections information, and finally run the update
563 proto_run(struct config *config)
565 struct thread_args lister_args;
566 struct thread_args detailer_args;
567 struct thread_args updater_args;
568 struct thread_args *args;
569 struct killer killer;
570 struct threads *workers;
575 * We pass NULL for the close() function because we'll reuse
576 * the socket after the stream is closed.
578 config->server = stream_open_fd(config->socket, stream_read_fd,
579 stream_write_fd, NULL);
580 status = proto_greet(config);
581 if (status == STATUS_SUCCESS)
582 status = proto_negproto(config);
583 if (status == STATUS_SUCCESS)
584 status = proto_login(config);
585 if (status == STATUS_SUCCESS)
586 status = proto_fileattr(config);
587 if (status == STATUS_SUCCESS)
588 status = proto_xchgcoll(config);
589 if (status != STATUS_SUCCESS)
592 /* Multi-threaded action starts here. */
593 m = proto_mux(config);
595 return (STATUS_FAILURE);
597 stream_close(config->server);
598 config->server = NULL;
599 config->fixups = fixups_new();
600 killer_start(&killer, m);
602 /* Start the worker threads. */
603 workers = threads_new();
605 args->config = config;
609 args->wr = stream_open(config->chan0,
610 NULL, (stream_writefn_t *)chan_write, NULL);
611 threads_create(workers, lister, args);
613 args = &detailer_args;
614 args->config = config;
617 args->rd = stream_open(config->chan0,
618 (stream_readfn_t *)chan_read, NULL, NULL);
619 args->wr = stream_open(config->chan1,
620 NULL, (stream_writefn_t *)chan_write, NULL);
621 threads_create(workers, detailer, args);
623 args = &updater_args;
624 args->config = config;
627 args->rd = stream_open(config->chan1,
628 (stream_readfn_t *)chan_read, NULL, NULL);
630 threads_create(workers, updater, args);
632 lprintf(2, "Running\n");
633 /* Wait for all the worker threads to finish. */
634 status = STATUS_SUCCESS;
635 for (i = 0; i < 3; i++) {
636 args = threads_wait(workers);
637 if (args->rd != NULL)
638 stream_close(args->rd);
639 if (args->wr != NULL)
640 stream_close(args->wr);
641 if (args->status != STATUS_SUCCESS) {
642 assert(args->errmsg != NULL);
643 if (status == STATUS_SUCCESS) {
644 status = args->status;
645 /* Shutdown the multiplexer to wake up all
646 the other threads. */
647 mux_shutdown(m, args->errmsg, status);
652 threads_free(workers);
653 if (status == STATUS_SUCCESS) {
654 lprintf(2, "Shutting down connection to server\n");
655 chan_close(config->chan0);
656 chan_close(config->chan1);
657 chan_wait(config->chan0);
658 chan_wait(config->chan1);
659 mux_shutdown(m, NULL, STATUS_SUCCESS);
661 killer_stop(&killer);
662 fixups_free(config->fixups);
663 status = mux_close(m);
664 if (status == STATUS_SUCCESS) {
665 lprintf(1, "Finished successfully\n");
666 } else if (status == STATUS_INTERRUPTED) {
667 lprintf(-1, "Interrupted\n");
668 if (killer.killedby != -1)
669 kill(getpid(), killer.killedby);
675 * Write a string into the stream, escaping characters as needed.
676 * Characters escaped:
685 proto_escape(struct stream *wr, const char *s)
691 /* Handle characters that need escaping. */
693 len = strcspn(s, " \t\r\n\\");
694 n = stream_write(wr, s, len);
700 n = stream_write(wr, "\\_", 2);
703 n = stream_write(wr, "\\t", 2);
706 n = stream_write(wr, "\\r", 2);
709 n = stream_write(wr, "\\n", 2);
712 n = stream_write(wr, "\\\\", 2);
723 * A simple printf() implementation specifically tailored for csup.
724 * List of the supported formats:
727 * %d or %i Print an int as decimal.
728 * %x Print an int as hexadecimal.
729 * %o Print an int as octal.
730 * %t Print a time_t as decimal.
731 * %s Print a char * escaping some characters as needed.
732 * %S Print a char * without escaping.
733 * %f Print an encoded struct fattr *.
734 * %F Print an encoded struct fattr *, specifying the supported
738 proto_printf(struct stream *wr, const char *format, ...)
740 fattr_support_t *support;
753 va_start(ap, format);
754 while ((cp = strchr(fmt, '%')) != NULL) {
756 n = stream_write(wr, fmt, cp - fmt);
765 rv = stream_printf(wr, "%c", c);
769 val = va_arg(ap, int);
770 rv = stream_printf(wr, "%d", val);
773 val = va_arg(ap, int);
774 rv = stream_printf(wr, "%x", val);
777 val = va_arg(ap, int);
778 rv = stream_printf(wr, "%o", val);
781 s = va_arg(ap, char *);
783 rv = stream_printf(wr, "%s", s);
786 s = va_arg(ap, char *);
788 rv = proto_escape(wr, s);
791 longval = (long long)va_arg(ap, time_t);
792 rv = stream_printf(wr, "%lld", longval);
795 fa = va_arg(ap, struct fattr *);
796 attr = fattr_encode(fa, NULL, 0);
797 rv = proto_escape(wr, attr);
801 fa = va_arg(ap, struct fattr *);
802 support = va_arg(ap, fattr_support_t *);
803 ignore = va_arg(ap, int);
804 attr = fattr_encode(fa, *support, ignore);
805 rv = proto_escape(wr, attr);
809 n = stream_write(wr, "%", 1);
819 rv = stream_printf(wr, "%s", fmt);
829 * Unescape the string, see proto_escape().
832 proto_unescape(char *s)
837 while ((cp = strchr(cp, '\\')) != NULL) {
858 while (*cp2 != '\0') {
866 * Get an ascii token in the string.
869 proto_get_ascii(char **s)
873 ret = strsep(s, " ");
876 /* Make sure we disallow 0-length fields. */
886 * Get the rest of the string.
889 proto_get_rest(char **s)
905 proto_get_int(char **s, int *val, int base)
910 cp = proto_get_ascii(s);
914 longval = strtol(cp, &end, base);
915 if (errno || *end != '\0')
917 if (longval > INT_MAX || longval < INT_MIN) {
926 * Get a time_t token.
928 * Ideally, we would use an intmax_t and strtoimax() here, but strtoll()
929 * is more portable and 64bits should be enough for a timestamp.
932 proto_get_time(char **s, time_t *val)
937 cp = proto_get_ascii(s);
941 tmp = strtoll(cp, &end, 10);
942 if (errno || *end != '\0')
948 /* Start the killer thread. It is used to protect against some signals
949 during the multi-threaded run so that we can gracefully fail. */
951 killer_start(struct killer *k, struct mux *m)
957 sigemptyset(&k->sigset);
958 sigaddset(&k->sigset, SIGINT);
959 sigaddset(&k->sigset, SIGHUP);
960 sigaddset(&k->sigset, SIGTERM);
961 sigaddset(&k->sigset, SIGPIPE);
962 pthread_sigmask(SIG_BLOCK, &k->sigset, NULL);
963 error = pthread_create(&k->thread, NULL, killer_run, k);
965 err(1, "pthread_create");
968 /* The main loop of the killer thread. */
970 killer_run(void *arg)
977 error = sigwait(&k->sigset, &sig);
979 if (sig == SIGINT || sig == SIGHUP || sig == SIGTERM) {
980 if (k->killedby == -1) {
982 /* Ensure we don't get canceled during the shutdown. */
983 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old);
984 mux_shutdown(k->mux, "Cleaning up ...",
986 pthread_setcancelstate(old, NULL);
992 /* Stop the killer thread. */
994 killer_stop(struct killer *k)
999 error = pthread_cancel(k->thread);
1001 pthread_join(k->thread, &val);
1002 assert(val == PTHREAD_CANCELED);
1003 pthread_sigmask(SIG_UNBLOCK, &k->sigset, NULL);