proto.c revision 186781
1/*- 2 * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 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. 13 * 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 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD: head/contrib/csup/proto.c 186781 2009-01-05 15:18:16Z lulf $ 27 */ 28 29#include <sys/param.h> 30#include <sys/select.h> 31#include <sys/socket.h> 32#include <sys/types.h> 33#include <sys/stat.h> 34 35#include <assert.h> 36#include <err.h> 37#include <errno.h> 38#include <netdb.h> 39#include <pthread.h> 40#include <signal.h> 41#include <stdarg.h> 42#include <stddef.h> 43#include <stdio.h> 44#include <stdlib.h> 45#include <string.h> 46#include <unistd.h> 47 48#include "config.h" 49#include "detailer.h" 50#include "fattr.h" 51#include "fixups.h" 52#include "globtree.h" 53#include "keyword.h" 54#include "lister.h" 55#include "misc.h" 56#include "mux.h" 57#include "proto.h" 58#include "queue.h" 59#include "stream.h" 60#include "threads.h" 61#include "updater.h" 62 63struct killer { 64 pthread_t thread; 65 sigset_t sigset; 66 struct mux *mux; 67 int killedby; 68}; 69 70static void killer_start(struct killer *, struct mux *); 71static void *killer_run(void *); 72static void killer_stop(struct killer *); 73 74static int proto_waitconnect(int); 75static int proto_greet(struct config *); 76static int proto_negproto(struct config *); 77static int proto_login(struct config *); 78static int proto_fileattr(struct config *); 79static int proto_xchgcoll(struct config *); 80static struct mux *proto_mux(struct config *); 81 82static int proto_escape(struct stream *, const char *); 83static void proto_unescape(char *); 84 85static int 86proto_waitconnect(int s) 87{ 88 fd_set readfd; 89 socklen_t len; 90 int error, rv, soerror; 91 92 FD_ZERO(&readfd); 93 FD_SET(s, &readfd); 94 95 do { 96 rv = select(s + 1, &readfd, NULL, NULL, NULL); 97 } while (rv == -1 && errno == EINTR); 98 if (rv == -1) 99 return (-1); 100 /* Check that the connection was really successful. */ 101 len = sizeof(soerror); 102 error = getsockopt(s, SOL_SOCKET, SO_ERROR, &soerror, &len); 103 if (error) { 104 /* We have no choice but faking an error here. */ 105 errno = ECONNREFUSED; 106 return (-1); 107 } 108 if (soerror) { 109 errno = soerror; 110 return (-1); 111 } 112 return (0); 113} 114 115/* Connect to the CVSup server. */ 116int 117proto_connect(struct config *config, int family, uint16_t port) 118{ 119 char addrbuf[NI_MAXHOST]; 120 /* Enough to hold sizeof("cvsup") or any port number. */ 121 char servname[8]; 122 struct addrinfo *res, *ai, hints; 123 int error, opt, s; 124 125 s = -1; 126 if (port != 0) 127 snprintf(servname, sizeof(servname), "%d", port); 128 else { 129 strncpy(servname, "cvsup", sizeof(servname) - 1); 130 servname[sizeof(servname) - 1] = '\0'; 131 } 132 memset(&hints, 0, sizeof(hints)); 133 hints.ai_family = family; 134 hints.ai_socktype = SOCK_STREAM; 135 error = getaddrinfo(config->host, servname, &hints, &res); 136 /* 137 * Try with the hardcoded port number for OSes that don't 138 * have cvsup defined in the /etc/services file. 139 */ 140 if (error == EAI_SERVICE) { 141 strncpy(servname, "5999", sizeof(servname) - 1); 142 servname[sizeof(servname) - 1] = '\0'; 143 error = getaddrinfo(config->host, servname, &hints, &res); 144 } 145 if (error) { 146 lprintf(0, "Name lookup failure for \"%s\": %s\n", config->host, 147 gai_strerror(error)); 148 return (STATUS_TRANSIENTFAILURE); 149 } 150 for (ai = res; ai != NULL; ai = ai->ai_next) { 151 s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 152 if (s != -1) { 153 error = 0; 154 if (config->laddr != NULL) { 155 opt = 1; 156 (void)setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 157 &opt, sizeof(opt)); 158 error = bind(s, config->laddr, 159 config->laddrlen); 160 } 161 if (!error) { 162 error = connect(s, ai->ai_addr, ai->ai_addrlen); 163 if (error && errno == EINTR) 164 error = proto_waitconnect(s); 165 } 166 if (error) 167 close(s); 168 } 169 (void)getnameinfo(ai->ai_addr, ai->ai_addrlen, addrbuf, 170 sizeof(addrbuf), NULL, 0, NI_NUMERICHOST); 171 if (s == -1 || error) { 172 lprintf(0, "Cannot connect to %s: %s\n", addrbuf, 173 strerror(errno)); 174 continue; 175 } 176 lprintf(1, "Connected to %s\n", addrbuf); 177 freeaddrinfo(res); 178 config->socket = s; 179 return (STATUS_SUCCESS); 180 } 181 freeaddrinfo(res); 182 return (STATUS_TRANSIENTFAILURE); 183} 184 185/* Greet the server. */ 186static int 187proto_greet(struct config *config) 188{ 189 char *line, *cmd, *msg, *swver; 190 struct stream *s; 191 192 s = config->server; 193 line = stream_getln(s, NULL); 194 cmd = proto_get_ascii(&line); 195 if (cmd == NULL) 196 goto bad; 197 if (strcmp(cmd, "OK") == 0) { 198 (void)proto_get_ascii(&line); /* major number */ 199 (void)proto_get_ascii(&line); /* minor number */ 200 swver = proto_get_ascii(&line); 201 } else if (strcmp(cmd, "!") == 0) { 202 msg = proto_get_rest(&line); 203 if (msg == NULL) 204 goto bad; 205 lprintf(-1, "Rejected by server: %s\n", msg); 206 return (STATUS_TRANSIENTFAILURE); 207 } else 208 goto bad; 209 lprintf(2, "Server software version: %s\n", 210 swver != NULL ? swver : "."); 211 return (STATUS_SUCCESS); 212bad: 213 lprintf(-1, "Invalid greeting from server\n"); 214 return (STATUS_FAILURE); 215} 216 217/* Negotiate protocol version with the server. */ 218static int 219proto_negproto(struct config *config) 220{ 221 struct stream *s; 222 char *cmd, *line, *msg; 223 int error, maj, min; 224 225 s = config->server; 226 proto_printf(s, "PROTO %d %d %s\n", PROTO_MAJ, PROTO_MIN, PROTO_SWVER); 227 stream_flush(s); 228 line = stream_getln(s, NULL); 229 cmd = proto_get_ascii(&line); 230 if (cmd == NULL || line == NULL) 231 goto bad; 232 if (strcmp(cmd, "!") == 0) { 233 msg = proto_get_rest(&line); 234 lprintf(-1, "Protocol negotiation failed: %s\n", msg); 235 return (1); 236 } else if (strcmp(cmd, "PROTO") != 0) 237 goto bad; 238 error = proto_get_int(&line, &maj, 10); 239 if (!error) 240 error = proto_get_int(&line, &min, 10); 241 if (error) 242 goto bad; 243 if (maj != PROTO_MAJ || min != PROTO_MIN) { 244 lprintf(-1, "Server protocol version %d.%d not supported " 245 "by client\n", maj, min); 246 return (STATUS_FAILURE); 247 } 248 return (STATUS_SUCCESS); 249bad: 250 lprintf(-1, "Invalid PROTO command from server\n"); 251 return (STATUS_FAILURE); 252} 253 254static int 255proto_login(struct config *config) 256{ 257 struct stream *s; 258 char hostbuf[MAXHOSTNAMELEN]; 259 char *line, *login, *host, *cmd, *realm, *challenge, *msg; 260 int error; 261 262 s = config->server; 263 error = gethostname(hostbuf, sizeof(hostbuf)); 264 hostbuf[sizeof(hostbuf) - 1] = '\0'; 265 if (error) 266 host = NULL; 267 else 268 host = hostbuf; 269 login = getlogin(); 270 proto_printf(s, "USER %s %s\n", login != NULL ? login : "?", 271 host != NULL ? host : "?"); 272 stream_flush(s); 273 line = stream_getln(s, NULL); 274 cmd = proto_get_ascii(&line); 275 realm = proto_get_ascii(&line); 276 challenge = proto_get_ascii(&line); 277 if (challenge == NULL || line != NULL) 278 goto bad; 279 if (strcmp(realm, ".") != 0 || strcmp(challenge, ".") != 0) { 280 lprintf(-1, "Authentication required by the server and not " 281 "supported by client\n"); 282 return (STATUS_FAILURE); 283 } 284 proto_printf(s, "AUTHMD5 . . .\n"); 285 stream_flush(s); 286 line = stream_getln(s, NULL); 287 cmd = proto_get_ascii(&line); 288 if (cmd == NULL || line == NULL) 289 goto bad; 290 if (strcmp(cmd, "OK") == 0) 291 return (STATUS_SUCCESS); 292 if (strcmp(cmd, "!") == 0) { 293 msg = proto_get_rest(&line); 294 if (msg == NULL) 295 goto bad; 296 lprintf(-1, "Server error: %s\n", msg); 297 return (STATUS_FAILURE); 298 } 299bad: 300 lprintf(-1, "Invalid server reply to AUTHMD5\n"); 301 return (STATUS_FAILURE); 302} 303 304/* 305 * File attribute support negotiation. 306 */ 307static int 308proto_fileattr(struct config *config) 309{ 310 fattr_support_t support; 311 struct stream *s; 312 char *line, *cmd; 313 int error, i, n, attr; 314 315 s = config->server; 316 lprintf(2, "Negotiating file attribute support\n"); 317 proto_printf(s, "ATTR %d\n", FT_NUMBER); 318 for (i = 0; i < FT_NUMBER; i++) 319 proto_printf(s, "%x\n", fattr_supported(i)); 320 proto_printf(s, ".\n"); 321 stream_flush(s); 322 line = stream_getln(s, NULL); 323 if (line == NULL) 324 goto bad; 325 cmd = proto_get_ascii(&line); 326 error = proto_get_int(&line, &n, 10); 327 if (error || line != NULL || strcmp(cmd, "ATTR") != 0 || n > FT_NUMBER) 328 goto bad; 329 for (i = 0; i < n; i++) { 330 line = stream_getln(s, NULL); 331 if (line == NULL) 332 goto bad; 333 error = proto_get_int(&line, &attr, 16); 334 if (error) 335 goto bad; 336 support[i] = fattr_supported(i) & attr; 337 } 338 for (i = n; i < FT_NUMBER; i++) 339 support[i] = 0; 340 line = stream_getln(s, NULL); 341 if (line == NULL || strcmp(line, ".") != 0) 342 goto bad; 343 memcpy(config->fasupport, support, sizeof(config->fasupport)); 344 return (STATUS_SUCCESS); 345bad: 346 lprintf(-1, "Protocol error negotiating attribute support\n"); 347 return (STATUS_FAILURE); 348} 349 350/* 351 * Exchange collection information. 352 */ 353static int 354proto_xchgcoll(struct config *config) 355{ 356 struct coll *coll; 357 struct stream *s; 358 struct globtree *diraccept, *dirrefuse; 359 struct globtree *fileaccept, *filerefuse; 360 char *line, *cmd, *collname, *pat; 361 char *msg, *release, *ident, *rcskey, *prefix; 362 size_t i, len; 363 int error, flags, options; 364 365 s = config->server; 366 lprintf(2, "Exchanging collection information\n"); 367 STAILQ_FOREACH(coll, &config->colls, co_next) { 368 if (coll->co_options & CO_SKIP) 369 continue; 370 proto_printf(s, "COLL %s %s %o %d\n", coll->co_name, 371 coll->co_release, coll->co_umask, coll->co_options); 372 for (i = 0; i < pattlist_size(coll->co_accepts); i++) { 373 proto_printf(s, "ACC %s\n", 374 pattlist_get(coll->co_accepts, i)); 375 } 376 for (i = 0; i < pattlist_size(coll->co_refusals); i++) { 377 proto_printf(s, "REF %s\n", 378 pattlist_get(coll->co_refusals, i)); 379 } 380 proto_printf(s, ".\n"); 381 } 382 proto_printf(s, ".\n"); 383 stream_flush(s); 384 385 STAILQ_FOREACH(coll, &config->colls, co_next) { 386 if (coll->co_options & CO_SKIP) 387 continue; 388 coll->co_norsync = globtree_false(); 389 line = stream_getln(s, NULL); 390 if (line == NULL) 391 goto bad; 392 cmd = proto_get_ascii(&line); 393 collname = proto_get_ascii(&line); 394 release = proto_get_ascii(&line); 395 error = proto_get_int(&line, &options, 10); 396 if (error || line != NULL) 397 goto bad; 398 if (strcmp(cmd, "COLL") != 0 || 399 strcmp(collname, coll->co_name) != 0 || 400 strcmp(release, coll->co_release) != 0) 401 goto bad; 402 coll->co_options = 403 (coll->co_options | (options & CO_SERVMAYSET)) & 404 ~(~options & CO_SERVMAYCLEAR); 405 while ((line = stream_getln(s, NULL)) != NULL) { 406 if (strcmp(line, ".") == 0) 407 break; 408 cmd = proto_get_ascii(&line); 409 if (cmd == NULL) 410 goto bad; 411 if (strcmp(cmd, "!") == 0) { 412 msg = proto_get_rest(&line); 413 if (msg == NULL) 414 goto bad; 415 lprintf(-1, "Server message: %s\n", msg); 416 } else if (strcmp(cmd, "PRFX") == 0) { 417 prefix = proto_get_ascii(&line); 418 if (prefix == NULL || line != NULL) 419 goto bad; 420 coll->co_cvsroot = xstrdup(prefix); 421 } else if (strcmp(cmd, "KEYALIAS") == 0) { 422 ident = proto_get_ascii(&line); 423 rcskey = proto_get_ascii(&line); 424 if (rcskey == NULL || line != NULL) 425 goto bad; 426 error = keyword_alias(coll->co_keyword, ident, 427 rcskey); 428 if (error) 429 goto bad; 430 } else if (strcmp(cmd, "KEYON") == 0) { 431 ident = proto_get_ascii(&line); 432 if (ident == NULL || line != NULL) 433 goto bad; 434 error = keyword_enable(coll->co_keyword, ident); 435 if (error) 436 goto bad; 437 } else if (strcmp(cmd, "KEYOFF") == 0) { 438 ident = proto_get_ascii(&line); 439 if (ident == NULL || line != NULL) 440 goto bad; 441 error = keyword_disable(coll->co_keyword, 442 ident); 443 if (error) 444 goto bad; 445 } else if (strcmp(cmd, "NORS") == 0) { 446 pat = proto_get_ascii(&line); 447 if (pat == NULL || line != NULL) 448 goto bad; 449 coll->co_norsync = globtree_or(coll->co_norsync, 450 globtree_match(pat, FNM_PATHNAME)); 451 } else if (strcmp(cmd, "RNORS") == 0) { 452 pat = proto_get_ascii(&line); 453 if (pat == NULL || line != NULL) 454 goto bad; 455 coll->co_norsync = globtree_or(coll->co_norsync, 456 globtree_match(pat, FNM_PATHNAME | 457 FNM_LEADING_DIR)); 458 } else 459 goto bad; 460 } 461 if (line == NULL) 462 goto bad; 463 keyword_prepare(coll->co_keyword); 464 465 diraccept = globtree_true(); 466 fileaccept = globtree_true(); 467 dirrefuse = globtree_false(); 468 filerefuse = globtree_false(); 469 470 if (pattlist_size(coll->co_accepts) > 0) { 471 globtree_free(diraccept); 472 globtree_free(fileaccept); 473 diraccept = globtree_false(); 474 fileaccept = globtree_false(); 475 flags = FNM_PATHNAME | FNM_LEADING_DIR | 476 FNM_PREFIX_DIRS; 477 for (i = 0; i < pattlist_size(coll->co_accepts); i++) { 478 pat = pattlist_get(coll->co_accepts, i); 479 diraccept = globtree_or(diraccept, 480 globtree_match(pat, flags)); 481 482 len = strlen(pat); 483 if (coll->co_options & CO_CHECKOUTMODE && 484 (len == 0 || pat[len - 1] != '*')) { 485 /* We must modify the pattern so that it 486 refers to the RCS file, rather than 487 the checked-out file. */ 488 xasprintf(&pat, "%s,v", pat); 489 fileaccept = globtree_or(fileaccept, 490 globtree_match(pat, flags)); 491 free(pat); 492 } else { 493 fileaccept = globtree_or(fileaccept, 494 globtree_match(pat, flags)); 495 } 496 } 497 } 498 499 for (i = 0; i < pattlist_size(coll->co_refusals); i++) { 500 pat = pattlist_get(coll->co_refusals, i); 501 dirrefuse = globtree_or(dirrefuse, 502 globtree_match(pat, 0)); 503 len = strlen(pat); 504 if (coll->co_options & CO_CHECKOUTMODE && 505 (len == 0 || pat[len - 1] != '*')) { 506 /* We must modify the pattern so that it refers 507 to the RCS file, rather than the checked-out 508 file. */ 509 xasprintf(&pat, "%s,v", pat); 510 filerefuse = globtree_or(filerefuse, 511 globtree_match(pat, 0)); 512 free(pat); 513 } else { 514 filerefuse = globtree_or(filerefuse, 515 globtree_match(pat, 0)); 516 } 517 } 518 519 coll->co_dirfilter = globtree_and(diraccept, 520 globtree_not(dirrefuse)); 521 coll->co_filefilter = globtree_and(fileaccept, 522 globtree_not(filerefuse)); 523 524 /* At this point we don't need the pattern lists anymore. */ 525 pattlist_free(coll->co_accepts); 526 pattlist_free(coll->co_refusals); 527 coll->co_accepts = NULL; 528 coll->co_refusals = NULL; 529 530 /* Set up a mask of file attributes that we don't want to sync 531 with the server. */ 532 if (!(coll->co_options & CO_SETOWNER)) 533 coll->co_attrignore |= FA_OWNER | FA_GROUP; 534 if (!(coll->co_options & CO_SETMODE)) 535 coll->co_attrignore |= FA_MODE; 536 if (!(coll->co_options & CO_SETFLAGS)) 537 coll->co_attrignore |= FA_FLAGS; 538 } 539 return (STATUS_SUCCESS); 540bad: 541 lprintf(-1, "Protocol error during collection exchange\n"); 542 return (STATUS_FAILURE); 543} 544 545static struct mux * 546proto_mux(struct config *config) 547{ 548 struct mux *m; 549 struct stream *s, *wr; 550 struct chan *chan0, *chan1; 551 int id; 552 553 s = config->server; 554 lprintf(2, "Establishing multiplexed-mode data connection\n"); 555 proto_printf(s, "MUX\n"); 556 stream_flush(s); 557 m = mux_open(config->socket, &chan0); 558 if (m == NULL) { 559 lprintf(-1, "Cannot open the multiplexer\n"); 560 return (NULL); 561 } 562 id = chan_listen(m); 563 if (id == -1) { 564 lprintf(-1, "ChannelMux.Listen failed: %s\n", strerror(errno)); 565 mux_close(m); 566 return (NULL); 567 } 568 wr = stream_open(chan0, NULL, (stream_writefn_t *)chan_write, NULL); 569 proto_printf(wr, "CHAN %d\n", id); 570 stream_close(wr); 571 chan1 = chan_accept(m, id); 572 if (chan1 == NULL) { 573 lprintf(-1, "ChannelMux.Accept failed: %s\n", strerror(errno)); 574 mux_close(m); 575 return (NULL); 576 } 577 config->chan0 = chan0; 578 config->chan1 = chan1; 579 return (m); 580} 581 582/* 583 * Initializes the connection to the CVSup server, that is handle 584 * the protocol negotiation, logging in, exchanging file attributes 585 * support and collections information, and finally run the update 586 * session. 587 */ 588int 589proto_run(struct config *config) 590{ 591 struct thread_args lister_args; 592 struct thread_args detailer_args; 593 struct thread_args updater_args; 594 struct thread_args *args; 595 struct killer killer; 596 struct threads *workers; 597 struct mux *m; 598 int i, status; 599 600 /* 601 * We pass NULL for the close() function because we'll reuse 602 * the socket after the stream is closed. 603 */ 604 config->server = stream_open_fd(config->socket, stream_read_fd, 605 stream_write_fd, NULL); 606 status = proto_greet(config); 607 if (status == STATUS_SUCCESS) 608 status = proto_negproto(config); 609 if (status == STATUS_SUCCESS) 610 status = proto_login(config); 611 if (status == STATUS_SUCCESS) 612 status = proto_fileattr(config); 613 if (status == STATUS_SUCCESS) 614 status = proto_xchgcoll(config); 615 if (status != STATUS_SUCCESS) 616 return (status); 617 618 /* Multi-threaded action starts here. */ 619 m = proto_mux(config); 620 if (m == NULL) 621 return (STATUS_FAILURE); 622 623 stream_close(config->server); 624 config->server = NULL; 625 config->fixups = fixups_new(); 626 killer_start(&killer, m); 627 628 /* Start the worker threads. */ 629 workers = threads_new(); 630 args = &lister_args; 631 args->config = config; 632 args->status = -1; 633 args->errmsg = NULL; 634 args->rd = NULL; 635 args->wr = stream_open(config->chan0, 636 NULL, (stream_writefn_t *)chan_write, NULL); 637 threads_create(workers, lister, args); 638 639 args = &detailer_args; 640 args->config = config; 641 args->status = -1; 642 args->errmsg = NULL; 643 args->rd = stream_open(config->chan0, 644 (stream_readfn_t *)chan_read, NULL, NULL); 645 args->wr = stream_open(config->chan1, 646 NULL, (stream_writefn_t *)chan_write, NULL); 647 threads_create(workers, detailer, args); 648 649 args = &updater_args; 650 args->config = config; 651 args->status = -1; 652 args->errmsg = NULL; 653 args->rd = stream_open(config->chan1, 654 (stream_readfn_t *)chan_read, NULL, NULL); 655 args->wr = NULL; 656 threads_create(workers, updater, args); 657 658 lprintf(2, "Running\n"); 659 /* Wait for all the worker threads to finish. */ 660 status = STATUS_SUCCESS; 661 for (i = 0; i < 3; i++) { 662 args = threads_wait(workers); 663 if (args->rd != NULL) 664 stream_close(args->rd); 665 if (args->wr != NULL) 666 stream_close(args->wr); 667 if (args->status != STATUS_SUCCESS) { 668 assert(args->errmsg != NULL); 669 if (status == STATUS_SUCCESS) { 670 status = args->status; 671 /* Shutdown the multiplexer to wake up all 672 the other threads. */ 673 mux_shutdown(m, args->errmsg, status); 674 } 675 free(args->errmsg); 676 } 677 } 678 threads_free(workers); 679 if (status == STATUS_SUCCESS) { 680 lprintf(2, "Shutting down connection to server\n"); 681 chan_close(config->chan0); 682 chan_close(config->chan1); 683 chan_wait(config->chan0); 684 chan_wait(config->chan1); 685 mux_shutdown(m, NULL, STATUS_SUCCESS); 686 } 687 killer_stop(&killer); 688 fixups_free(config->fixups); 689 status = mux_close(m); 690 if (status == STATUS_SUCCESS) { 691 lprintf(1, "Finished successfully\n"); 692 } else if (status == STATUS_INTERRUPTED) { 693 lprintf(-1, "Interrupted\n"); 694 if (killer.killedby != -1) 695 kill(getpid(), killer.killedby); 696 } 697 return (status); 698} 699 700/* 701 * Write a string into the stream, escaping characters as needed. 702 * Characters escaped: 703 * 704 * SPACE -> "\_" 705 * TAB -> "\t" 706 * NEWLINE -> "\n" 707 * CR -> "\r" 708 * \ -> "\\" 709 */ 710static int 711proto_escape(struct stream *wr, const char *s) 712{ 713 size_t len; 714 ssize_t n; 715 char c; 716 717 /* Handle characters that need escaping. */ 718 do { 719 len = strcspn(s, " \t\r\n\\"); 720 n = stream_write(wr, s, len); 721 if (n == -1) 722 return (-1); 723 c = s[len]; 724 switch (c) { 725 case ' ': 726 n = stream_write(wr, "\\_", 2); 727 break; 728 case '\t': 729 n = stream_write(wr, "\\t", 2); 730 break; 731 case '\r': 732 n = stream_write(wr, "\\r", 2); 733 break; 734 case '\n': 735 n = stream_write(wr, "\\n", 2); 736 break; 737 case '\\': 738 n = stream_write(wr, "\\\\", 2); 739 break; 740 } 741 if (n == -1) 742 return (-1); 743 s += len + 1; 744 } while (c != '\0'); 745 return (0); 746} 747 748/* 749 * A simple printf() implementation specifically tailored for csup. 750 * List of the supported formats: 751 * 752 * %c Print a char. 753 * %d or %i Print an int as decimal. 754 * %x Print an int as hexadecimal. 755 * %o Print an int as octal. 756 * %t Print a time_t as decimal. 757 * %s Print a char * escaping some characters as needed. 758 * %S Print a char * without escaping. 759 * %f Print an encoded struct fattr *. 760 * %F Print an encoded struct fattr *, specifying the supported 761 * attributes. 762 */ 763int 764proto_printf(struct stream *wr, const char *format, ...) 765{ 766 fattr_support_t *support; 767 long long longval; 768 struct fattr *fa; 769 const char *fmt; 770 va_list ap; 771 char *cp, *s, *attr; 772 ssize_t n; 773 size_t size; 774 off_t off; 775 int rv, val, ignore; 776 char c; 777 778 n = 0; 779 rv = 0; 780 fmt = format; 781 va_start(ap, format); 782 while ((cp = strchr(fmt, '%')) != NULL) { 783 if (cp > fmt) { 784 n = stream_write(wr, fmt, cp - fmt); 785 if (n == -1) 786 return (-1); 787 } 788 if (*++cp == '\0') 789 goto done; 790 switch (*cp) { 791 case 'c': 792 c = va_arg(ap, int); 793 rv = stream_printf(wr, "%c", c); 794 break; 795 case 'd': 796 case 'i': 797 val = va_arg(ap, int); 798 rv = stream_printf(wr, "%d", val); 799 break; 800 case 'x': 801 val = va_arg(ap, int); 802 rv = stream_printf(wr, "%x", val); 803 break; 804 case 'o': 805 val = va_arg(ap, int); 806 rv = stream_printf(wr, "%o", val); 807 break; 808 case 'O': 809 off = va_arg(ap, off_t); 810 rv = stream_printf(wr, "%llu", off); 811 break; 812 case 'S': 813 s = va_arg(ap, char *); 814 assert(s != NULL); 815 rv = stream_printf(wr, "%s", s); 816 break; 817 case 's': 818 s = va_arg(ap, char *); 819 assert(s != NULL); 820 rv = proto_escape(wr, s); 821 break; 822 case 't': 823 longval = (long long)va_arg(ap, time_t); 824 rv = stream_printf(wr, "%lld", longval); 825 break; 826 case 'f': 827 fa = va_arg(ap, struct fattr *); 828 attr = fattr_encode(fa, NULL, 0); 829 rv = proto_escape(wr, attr); 830 free(attr); 831 break; 832 case 'F': 833 fa = va_arg(ap, struct fattr *); 834 support = va_arg(ap, fattr_support_t *); 835 ignore = va_arg(ap, int); 836 attr = fattr_encode(fa, *support, ignore); 837 rv = proto_escape(wr, attr); 838 free(attr); 839 break; 840 case 'z': 841 size = va_arg(ap, size_t); 842 rv = stream_printf(wr, "%zu", size); 843 break; 844 845 case '%': 846 n = stream_write(wr, "%", 1); 847 if (n == -1) 848 return (-1); 849 break; 850 } 851 if (rv == -1) 852 return (-1); 853 fmt = cp + 1; 854 } 855 if (*fmt != '\0') { 856 rv = stream_printf(wr, "%s", fmt); 857 if (rv == -1) 858 return (-1); 859 } 860done: 861 va_end(ap); 862 return (0); 863} 864 865/* 866 * Unescape the string, see proto_escape(). 867 */ 868static void 869proto_unescape(char *s) 870{ 871 char *cp, *cp2; 872 873 cp = s; 874 while ((cp = strchr(cp, '\\')) != NULL) { 875 switch (cp[1]) { 876 case '_': 877 *cp = ' '; 878 break; 879 case 't': 880 *cp = '\t'; 881 break; 882 case 'r': 883 *cp = '\r'; 884 break; 885 case 'n': 886 *cp = '\n'; 887 break; 888 case '\\': 889 *cp = '\\'; 890 break; 891 default: 892 *cp = *(cp + 1); 893 } 894 cp2 = ++cp; 895 while (*cp2 != '\0') { 896 *cp2 = *(cp2 + 1); 897 cp2++; 898 } 899 } 900} 901 902/* 903 * Get an ascii token in the string. 904 */ 905char * 906proto_get_ascii(char **s) 907{ 908 char *ret; 909 910 ret = strsep(s, " "); 911 if (ret == NULL) 912 return (NULL); 913 /* Make sure we disallow 0-length fields. */ 914 if (*ret == '\0') { 915 *s = NULL; 916 return (NULL); 917 } 918 proto_unescape(ret); 919 return (ret); 920} 921 922/* 923 * Get the rest of the string. 924 */ 925char * 926proto_get_rest(char **s) 927{ 928 char *ret; 929 930 if (s == NULL) 931 return (NULL); 932 ret = *s; 933 proto_unescape(ret); 934 *s = NULL; 935 return (ret); 936} 937 938/* 939 * Get an int token. 940 */ 941int 942proto_get_int(char **s, int *val, int base) 943{ 944 char *cp; 945 int error; 946 947 cp = proto_get_ascii(s); 948 if (cp == NULL) 949 return (-1); 950 error = asciitoint(cp, val, base); 951 return (error); 952} 953 954/* 955 * Get a size_t token. 956 */ 957int 958proto_get_sizet(char **s, size_t *val, int base) 959{ 960 unsigned long long tmp; 961 char *cp, *end; 962 963 cp = proto_get_ascii(s); 964 if (cp == NULL) 965 return (-1); 966 errno = 0; 967 tmp = strtoll(cp, &end, base); 968 if (errno || *end != '\0') 969 return (-1); 970 *val = (size_t)tmp; 971 return (0); 972} 973 974/* 975 * Get a time_t token. 976 * 977 * Ideally, we would use an intmax_t and strtoimax() here, but strtoll() 978 * is more portable and 64bits should be enough for a timestamp. 979 */ 980int 981proto_get_time(char **s, time_t *val) 982{ 983 long long tmp; 984 char *cp, *end; 985 986 cp = proto_get_ascii(s); 987 if (cp == NULL) 988 return (-1); 989 errno = 0; 990 tmp = strtoll(cp, &end, 10); 991 if (errno || *end != '\0') 992 return (-1); 993 *val = (time_t)tmp; 994 return (0); 995} 996 997/* Start the killer thread. It is used to protect against some signals 998 during the multi-threaded run so that we can gracefully fail. */ 999static void 1000killer_start(struct killer *k, struct mux *m) 1001{ 1002 int error; 1003 1004 k->mux = m; 1005 k->killedby = -1; 1006 sigemptyset(&k->sigset); 1007 sigaddset(&k->sigset, SIGINT); 1008 sigaddset(&k->sigset, SIGHUP); 1009 sigaddset(&k->sigset, SIGTERM); 1010 sigaddset(&k->sigset, SIGPIPE); 1011 pthread_sigmask(SIG_BLOCK, &k->sigset, NULL); 1012 error = pthread_create(&k->thread, NULL, killer_run, k); 1013 if (error) 1014 err(1, "pthread_create"); 1015} 1016 1017/* The main loop of the killer thread. */ 1018static void * 1019killer_run(void *arg) 1020{ 1021 struct killer *k; 1022 int error, sig, old; 1023 1024 k = arg; 1025again: 1026 error = sigwait(&k->sigset, &sig); 1027 assert(!error); 1028 if (sig == SIGINT || sig == SIGHUP || sig == SIGTERM) { 1029 if (k->killedby == -1) { 1030 k->killedby = sig; 1031 /* Ensure we don't get canceled during the shutdown. */ 1032 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old); 1033 mux_shutdown(k->mux, "Cleaning up ...", 1034 STATUS_INTERRUPTED); 1035 pthread_setcancelstate(old, NULL); 1036 } 1037 } 1038 goto again; 1039} 1040 1041/* Stop the killer thread. */ 1042static void 1043killer_stop(struct killer *k) 1044{ 1045 void *val; 1046 int error; 1047 1048 error = pthread_cancel(k->thread); 1049 assert(!error); 1050 pthread_join(k->thread, &val); 1051 assert(val == PTHREAD_CANCELED); 1052 pthread_sigmask(SIG_UNBLOCK, &k->sigset, NULL); 1053} 1054