proto.c revision 203368
1156230Smux/*- 2156230Smux * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org> 3156230Smux * All rights reserved. 4156230Smux * 5156230Smux * Redistribution and use in source and binary forms, with or without 6156230Smux * modification, are permitted provided that the following conditions 7156230Smux * are met: 8156230Smux * 1. Redistributions of source code must retain the above copyright 9156230Smux * notice, this list of conditions and the following disclaimer. 10156230Smux * 2. Redistributions in binary form must reproduce the above copyright 11156230Smux * notice, this list of conditions and the following disclaimer in the 12156230Smux * documentation and/or other materials provided with the distribution. 13156230Smux * 14156230Smux * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15156230Smux * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16156230Smux * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17156230Smux * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18156230Smux * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19156230Smux * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20156230Smux * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21156230Smux * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22156230Smux * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23156230Smux * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24156230Smux * SUCH DAMAGE. 25156230Smux * 26156230Smux * $FreeBSD: head/contrib/csup/proto.c 203368 2010-02-02 05:57:42Z lulf $ 27156230Smux */ 28156230Smux 29156230Smux#include <sys/param.h> 30156230Smux#include <sys/select.h> 31156230Smux#include <sys/socket.h> 32156230Smux#include <sys/types.h> 33156230Smux#include <sys/stat.h> 34156230Smux 35156230Smux#include <assert.h> 36156230Smux#include <err.h> 37156230Smux#include <errno.h> 38156230Smux#include <netdb.h> 39156230Smux#include <pthread.h> 40156230Smux#include <signal.h> 41156230Smux#include <stdarg.h> 42156230Smux#include <stddef.h> 43156230Smux#include <stdio.h> 44156230Smux#include <stdlib.h> 45156230Smux#include <string.h> 46156230Smux#include <unistd.h> 47156230Smux 48203368Slulf#include "auth.h" 49156230Smux#include "config.h" 50156230Smux#include "detailer.h" 51156230Smux#include "fattr.h" 52156230Smux#include "fixups.h" 53156230Smux#include "globtree.h" 54156230Smux#include "keyword.h" 55156230Smux#include "lister.h" 56156230Smux#include "misc.h" 57156230Smux#include "mux.h" 58156230Smux#include "proto.h" 59156230Smux#include "queue.h" 60156230Smux#include "stream.h" 61156230Smux#include "threads.h" 62156230Smux#include "updater.h" 63156230Smux 64156230Smuxstruct killer { 65156230Smux pthread_t thread; 66156230Smux sigset_t sigset; 67156230Smux struct mux *mux; 68156230Smux int killedby; 69156230Smux}; 70156230Smux 71156230Smuxstatic void killer_start(struct killer *, struct mux *); 72156230Smuxstatic void *killer_run(void *); 73156230Smuxstatic void killer_stop(struct killer *); 74156230Smux 75156230Smuxstatic int proto_waitconnect(int); 76156230Smuxstatic int proto_greet(struct config *); 77156230Smuxstatic int proto_negproto(struct config *); 78156230Smuxstatic int proto_fileattr(struct config *); 79156230Smuxstatic int proto_xchgcoll(struct config *); 80156230Smuxstatic struct mux *proto_mux(struct config *); 81156230Smux 82156230Smuxstatic int proto_escape(struct stream *, const char *); 83156230Smuxstatic void proto_unescape(char *); 84156230Smux 85156230Smuxstatic int 86156230Smuxproto_waitconnect(int s) 87156230Smux{ 88156230Smux fd_set readfd; 89156230Smux socklen_t len; 90156230Smux int error, rv, soerror; 91156230Smux 92156230Smux FD_ZERO(&readfd); 93156230Smux FD_SET(s, &readfd); 94156230Smux 95156230Smux do { 96156230Smux rv = select(s + 1, &readfd, NULL, NULL, NULL); 97156230Smux } while (rv == -1 && errno == EINTR); 98156230Smux if (rv == -1) 99156230Smux return (-1); 100156230Smux /* Check that the connection was really successful. */ 101156230Smux len = sizeof(soerror); 102156230Smux error = getsockopt(s, SOL_SOCKET, SO_ERROR, &soerror, &len); 103156230Smux if (error) { 104156230Smux /* We have no choice but faking an error here. */ 105156230Smux errno = ECONNREFUSED; 106156230Smux return (-1); 107156230Smux } 108156230Smux if (soerror) { 109156230Smux errno = soerror; 110156230Smux return (-1); 111156230Smux } 112156230Smux return (0); 113156230Smux} 114156230Smux 115156230Smux/* Connect to the CVSup server. */ 116156230Smuxint 117156230Smuxproto_connect(struct config *config, int family, uint16_t port) 118156230Smux{ 119156230Smux char addrbuf[NI_MAXHOST]; 120156230Smux /* Enough to hold sizeof("cvsup") or any port number. */ 121156230Smux char servname[8]; 122156230Smux struct addrinfo *res, *ai, hints; 123156230Smux int error, opt, s; 124156230Smux 125156230Smux s = -1; 126156230Smux if (port != 0) 127156230Smux snprintf(servname, sizeof(servname), "%d", port); 128156230Smux else { 129156230Smux strncpy(servname, "cvsup", sizeof(servname) - 1); 130156230Smux servname[sizeof(servname) - 1] = '\0'; 131156230Smux } 132156230Smux memset(&hints, 0, sizeof(hints)); 133156230Smux hints.ai_family = family; 134156230Smux hints.ai_socktype = SOCK_STREAM; 135156230Smux error = getaddrinfo(config->host, servname, &hints, &res); 136156230Smux /* 137156230Smux * Try with the hardcoded port number for OSes that don't 138156230Smux * have cvsup defined in the /etc/services file. 139156230Smux */ 140156230Smux if (error == EAI_SERVICE) { 141156230Smux strncpy(servname, "5999", sizeof(servname) - 1); 142156230Smux servname[sizeof(servname) - 1] = '\0'; 143156230Smux error = getaddrinfo(config->host, servname, &hints, &res); 144156230Smux } 145156230Smux if (error) { 146156230Smux lprintf(0, "Name lookup failure for \"%s\": %s\n", config->host, 147156230Smux gai_strerror(error)); 148156230Smux return (STATUS_TRANSIENTFAILURE); 149156230Smux } 150156230Smux for (ai = res; ai != NULL; ai = ai->ai_next) { 151156230Smux s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 152156230Smux if (s != -1) { 153156230Smux error = 0; 154156230Smux if (config->laddr != NULL) { 155156230Smux opt = 1; 156156230Smux (void)setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 157156230Smux &opt, sizeof(opt)); 158156230Smux error = bind(s, config->laddr, 159156230Smux config->laddrlen); 160156230Smux } 161156230Smux if (!error) { 162156230Smux error = connect(s, ai->ai_addr, ai->ai_addrlen); 163156230Smux if (error && errno == EINTR) 164156230Smux error = proto_waitconnect(s); 165156230Smux } 166156230Smux if (error) 167156230Smux close(s); 168156230Smux } 169156230Smux (void)getnameinfo(ai->ai_addr, ai->ai_addrlen, addrbuf, 170156230Smux sizeof(addrbuf), NULL, 0, NI_NUMERICHOST); 171156230Smux if (s == -1 || error) { 172156230Smux lprintf(0, "Cannot connect to %s: %s\n", addrbuf, 173156230Smux strerror(errno)); 174156230Smux continue; 175156230Smux } 176156230Smux lprintf(1, "Connected to %s\n", addrbuf); 177156230Smux freeaddrinfo(res); 178156230Smux config->socket = s; 179156230Smux return (STATUS_SUCCESS); 180156230Smux } 181156230Smux freeaddrinfo(res); 182156230Smux return (STATUS_TRANSIENTFAILURE); 183156230Smux} 184156230Smux 185156230Smux/* Greet the server. */ 186156230Smuxstatic int 187156230Smuxproto_greet(struct config *config) 188156230Smux{ 189156230Smux char *line, *cmd, *msg, *swver; 190156230Smux struct stream *s; 191156230Smux 192156230Smux s = config->server; 193156230Smux line = stream_getln(s, NULL); 194156230Smux cmd = proto_get_ascii(&line); 195156230Smux if (cmd == NULL) 196156230Smux goto bad; 197156230Smux if (strcmp(cmd, "OK") == 0) { 198156230Smux (void)proto_get_ascii(&line); /* major number */ 199156230Smux (void)proto_get_ascii(&line); /* minor number */ 200156230Smux swver = proto_get_ascii(&line); 201156230Smux } else if (strcmp(cmd, "!") == 0) { 202156230Smux msg = proto_get_rest(&line); 203156230Smux if (msg == NULL) 204156230Smux goto bad; 205156230Smux lprintf(-1, "Rejected by server: %s\n", msg); 206156230Smux return (STATUS_TRANSIENTFAILURE); 207156230Smux } else 208156230Smux goto bad; 209156230Smux lprintf(2, "Server software version: %s\n", 210156230Smux swver != NULL ? swver : "."); 211156230Smux return (STATUS_SUCCESS); 212156230Smuxbad: 213156230Smux lprintf(-1, "Invalid greeting from server\n"); 214156230Smux return (STATUS_FAILURE); 215156230Smux} 216156230Smux 217156230Smux/* Negotiate protocol version with the server. */ 218156230Smuxstatic int 219156230Smuxproto_negproto(struct config *config) 220156230Smux{ 221156230Smux struct stream *s; 222156230Smux char *cmd, *line, *msg; 223156230Smux int error, maj, min; 224156230Smux 225156230Smux s = config->server; 226156230Smux proto_printf(s, "PROTO %d %d %s\n", PROTO_MAJ, PROTO_MIN, PROTO_SWVER); 227156230Smux stream_flush(s); 228156230Smux line = stream_getln(s, NULL); 229156230Smux cmd = proto_get_ascii(&line); 230156701Smux if (cmd == NULL || line == NULL) 231156230Smux goto bad; 232156230Smux if (strcmp(cmd, "!") == 0) { 233156230Smux msg = proto_get_rest(&line); 234156230Smux lprintf(-1, "Protocol negotiation failed: %s\n", msg); 235156230Smux return (1); 236156230Smux } else if (strcmp(cmd, "PROTO") != 0) 237156230Smux goto bad; 238156230Smux error = proto_get_int(&line, &maj, 10); 239156230Smux if (!error) 240156230Smux error = proto_get_int(&line, &min, 10); 241156230Smux if (error) 242156230Smux goto bad; 243156230Smux if (maj != PROTO_MAJ || min != PROTO_MIN) { 244156230Smux lprintf(-1, "Server protocol version %d.%d not supported " 245156230Smux "by client\n", maj, min); 246156230Smux return (STATUS_FAILURE); 247156230Smux } 248156230Smux return (STATUS_SUCCESS); 249156230Smuxbad: 250156230Smux lprintf(-1, "Invalid PROTO command from server\n"); 251156230Smux return (STATUS_FAILURE); 252156230Smux} 253156230Smux 254156230Smux/* 255156230Smux * File attribute support negotiation. 256156230Smux */ 257156230Smuxstatic int 258156230Smuxproto_fileattr(struct config *config) 259156230Smux{ 260156230Smux fattr_support_t support; 261156230Smux struct stream *s; 262156230Smux char *line, *cmd; 263156230Smux int error, i, n, attr; 264156230Smux 265156230Smux s = config->server; 266156230Smux lprintf(2, "Negotiating file attribute support\n"); 267156230Smux proto_printf(s, "ATTR %d\n", FT_NUMBER); 268156230Smux for (i = 0; i < FT_NUMBER; i++) 269156230Smux proto_printf(s, "%x\n", fattr_supported(i)); 270156230Smux proto_printf(s, ".\n"); 271156230Smux stream_flush(s); 272156230Smux line = stream_getln(s, NULL); 273156230Smux if (line == NULL) 274156230Smux goto bad; 275156230Smux cmd = proto_get_ascii(&line); 276156230Smux error = proto_get_int(&line, &n, 10); 277156230Smux if (error || line != NULL || strcmp(cmd, "ATTR") != 0 || n > FT_NUMBER) 278156230Smux goto bad; 279156230Smux for (i = 0; i < n; i++) { 280156230Smux line = stream_getln(s, NULL); 281156230Smux if (line == NULL) 282156230Smux goto bad; 283156230Smux error = proto_get_int(&line, &attr, 16); 284156230Smux if (error) 285156230Smux goto bad; 286156230Smux support[i] = fattr_supported(i) & attr; 287156230Smux } 288156230Smux for (i = n; i < FT_NUMBER; i++) 289156230Smux support[i] = 0; 290156230Smux line = stream_getln(s, NULL); 291156230Smux if (line == NULL || strcmp(line, ".") != 0) 292156230Smux goto bad; 293156230Smux memcpy(config->fasupport, support, sizeof(config->fasupport)); 294156230Smux return (STATUS_SUCCESS); 295156230Smuxbad: 296156230Smux lprintf(-1, "Protocol error negotiating attribute support\n"); 297156230Smux return (STATUS_FAILURE); 298156230Smux} 299156230Smux 300156230Smux/* 301156230Smux * Exchange collection information. 302156230Smux */ 303156230Smuxstatic int 304156230Smuxproto_xchgcoll(struct config *config) 305156230Smux{ 306156230Smux struct coll *coll; 307156230Smux struct stream *s; 308156230Smux struct globtree *diraccept, *dirrefuse; 309156230Smux struct globtree *fileaccept, *filerefuse; 310156230Smux char *line, *cmd, *collname, *pat; 311156230Smux char *msg, *release, *ident, *rcskey, *prefix; 312156230Smux size_t i, len; 313156230Smux int error, flags, options; 314156230Smux 315156230Smux s = config->server; 316156230Smux lprintf(2, "Exchanging collection information\n"); 317156230Smux STAILQ_FOREACH(coll, &config->colls, co_next) { 318186781Slulf if (coll->co_options & CO_SKIP) 319186781Slulf continue; 320156230Smux proto_printf(s, "COLL %s %s %o %d\n", coll->co_name, 321156230Smux coll->co_release, coll->co_umask, coll->co_options); 322156230Smux for (i = 0; i < pattlist_size(coll->co_accepts); i++) { 323156230Smux proto_printf(s, "ACC %s\n", 324156230Smux pattlist_get(coll->co_accepts, i)); 325156230Smux } 326156230Smux for (i = 0; i < pattlist_size(coll->co_refusals); i++) { 327156230Smux proto_printf(s, "REF %s\n", 328156230Smux pattlist_get(coll->co_refusals, i)); 329156230Smux } 330156230Smux proto_printf(s, ".\n"); 331156230Smux } 332156230Smux proto_printf(s, ".\n"); 333156230Smux stream_flush(s); 334156701Smux 335156230Smux STAILQ_FOREACH(coll, &config->colls, co_next) { 336156230Smux if (coll->co_options & CO_SKIP) 337156230Smux continue; 338156701Smux coll->co_norsync = globtree_false(); 339156230Smux line = stream_getln(s, NULL); 340156230Smux if (line == NULL) 341156230Smux goto bad; 342156230Smux cmd = proto_get_ascii(&line); 343156230Smux collname = proto_get_ascii(&line); 344156230Smux release = proto_get_ascii(&line); 345156230Smux error = proto_get_int(&line, &options, 10); 346156230Smux if (error || line != NULL) 347156230Smux goto bad; 348156230Smux if (strcmp(cmd, "COLL") != 0 || 349156230Smux strcmp(collname, coll->co_name) != 0 || 350156230Smux strcmp(release, coll->co_release) != 0) 351156230Smux goto bad; 352156230Smux coll->co_options = 353156230Smux (coll->co_options | (options & CO_SERVMAYSET)) & 354156230Smux ~(~options & CO_SERVMAYCLEAR); 355156230Smux while ((line = stream_getln(s, NULL)) != NULL) { 356156230Smux if (strcmp(line, ".") == 0) 357156230Smux break; 358156230Smux cmd = proto_get_ascii(&line); 359156230Smux if (cmd == NULL) 360156230Smux goto bad; 361156230Smux if (strcmp(cmd, "!") == 0) { 362156230Smux msg = proto_get_rest(&line); 363156230Smux if (msg == NULL) 364156230Smux goto bad; 365156230Smux lprintf(-1, "Server message: %s\n", msg); 366156230Smux } else if (strcmp(cmd, "PRFX") == 0) { 367156230Smux prefix = proto_get_ascii(&line); 368156230Smux if (prefix == NULL || line != NULL) 369156230Smux goto bad; 370156230Smux coll->co_cvsroot = xstrdup(prefix); 371156230Smux } else if (strcmp(cmd, "KEYALIAS") == 0) { 372156230Smux ident = proto_get_ascii(&line); 373156230Smux rcskey = proto_get_ascii(&line); 374156230Smux if (rcskey == NULL || line != NULL) 375156230Smux goto bad; 376156230Smux error = keyword_alias(coll->co_keyword, ident, 377156230Smux rcskey); 378156230Smux if (error) 379156230Smux goto bad; 380156230Smux } else if (strcmp(cmd, "KEYON") == 0) { 381156230Smux ident = proto_get_ascii(&line); 382156230Smux if (ident == NULL || line != NULL) 383156230Smux goto bad; 384156230Smux error = keyword_enable(coll->co_keyword, ident); 385156230Smux if (error) 386156230Smux goto bad; 387156230Smux } else if (strcmp(cmd, "KEYOFF") == 0) { 388156230Smux ident = proto_get_ascii(&line); 389156230Smux if (ident == NULL || line != NULL) 390156230Smux goto bad; 391156230Smux error = keyword_disable(coll->co_keyword, 392156230Smux ident); 393156230Smux if (error) 394156230Smux goto bad; 395156701Smux } else if (strcmp(cmd, "NORS") == 0) { 396156701Smux pat = proto_get_ascii(&line); 397156701Smux if (pat == NULL || line != NULL) 398156701Smux goto bad; 399156701Smux coll->co_norsync = globtree_or(coll->co_norsync, 400156701Smux globtree_match(pat, FNM_PATHNAME)); 401156701Smux } else if (strcmp(cmd, "RNORS") == 0) { 402156701Smux pat = proto_get_ascii(&line); 403156701Smux if (pat == NULL || line != NULL) 404156701Smux goto bad; 405156701Smux coll->co_norsync = globtree_or(coll->co_norsync, 406156701Smux globtree_match(pat, FNM_PATHNAME | 407156701Smux FNM_LEADING_DIR)); 408156701Smux } else 409156701Smux goto bad; 410156230Smux } 411156230Smux if (line == NULL) 412156230Smux goto bad; 413156230Smux keyword_prepare(coll->co_keyword); 414156230Smux 415156230Smux diraccept = globtree_true(); 416156230Smux fileaccept = globtree_true(); 417156230Smux dirrefuse = globtree_false(); 418156230Smux filerefuse = globtree_false(); 419156230Smux 420156230Smux if (pattlist_size(coll->co_accepts) > 0) { 421156230Smux globtree_free(diraccept); 422156230Smux globtree_free(fileaccept); 423156230Smux diraccept = globtree_false(); 424156230Smux fileaccept = globtree_false(); 425156230Smux flags = FNM_PATHNAME | FNM_LEADING_DIR | 426156230Smux FNM_PREFIX_DIRS; 427156230Smux for (i = 0; i < pattlist_size(coll->co_accepts); i++) { 428156230Smux pat = pattlist_get(coll->co_accepts, i); 429156230Smux diraccept = globtree_or(diraccept, 430156230Smux globtree_match(pat, flags)); 431156230Smux 432156230Smux len = strlen(pat); 433156230Smux if (coll->co_options & CO_CHECKOUTMODE && 434156230Smux (len == 0 || pat[len - 1] != '*')) { 435156230Smux /* We must modify the pattern so that it 436156230Smux refers to the RCS file, rather than 437156230Smux the checked-out file. */ 438156230Smux xasprintf(&pat, "%s,v", pat); 439156230Smux fileaccept = globtree_or(fileaccept, 440156230Smux globtree_match(pat, flags)); 441156230Smux free(pat); 442156230Smux } else { 443156230Smux fileaccept = globtree_or(fileaccept, 444156230Smux globtree_match(pat, flags)); 445156230Smux } 446156230Smux } 447156230Smux } 448156230Smux 449156230Smux for (i = 0; i < pattlist_size(coll->co_refusals); i++) { 450156230Smux pat = pattlist_get(coll->co_refusals, i); 451156230Smux dirrefuse = globtree_or(dirrefuse, 452156230Smux globtree_match(pat, 0)); 453156230Smux len = strlen(pat); 454156230Smux if (coll->co_options & CO_CHECKOUTMODE && 455156230Smux (len == 0 || pat[len - 1] != '*')) { 456156230Smux /* We must modify the pattern so that it refers 457156230Smux to the RCS file, rather than the checked-out 458156230Smux file. */ 459156230Smux xasprintf(&pat, "%s,v", pat); 460156230Smux filerefuse = globtree_or(filerefuse, 461156230Smux globtree_match(pat, 0)); 462156230Smux free(pat); 463156230Smux } else { 464156230Smux filerefuse = globtree_or(filerefuse, 465156230Smux globtree_match(pat, 0)); 466156230Smux } 467156230Smux } 468156230Smux 469156230Smux coll->co_dirfilter = globtree_and(diraccept, 470156230Smux globtree_not(dirrefuse)); 471156230Smux coll->co_filefilter = globtree_and(fileaccept, 472156230Smux globtree_not(filerefuse)); 473156230Smux 474156230Smux /* Set up a mask of file attributes that we don't want to sync 475156230Smux with the server. */ 476156230Smux if (!(coll->co_options & CO_SETOWNER)) 477156230Smux coll->co_attrignore |= FA_OWNER | FA_GROUP; 478156230Smux if (!(coll->co_options & CO_SETMODE)) 479156230Smux coll->co_attrignore |= FA_MODE; 480156230Smux if (!(coll->co_options & CO_SETFLAGS)) 481156230Smux coll->co_attrignore |= FA_FLAGS; 482156230Smux } 483156230Smux return (STATUS_SUCCESS); 484156230Smuxbad: 485156230Smux lprintf(-1, "Protocol error during collection exchange\n"); 486156230Smux return (STATUS_FAILURE); 487156230Smux} 488156230Smux 489156230Smuxstatic struct mux * 490156230Smuxproto_mux(struct config *config) 491156230Smux{ 492156230Smux struct mux *m; 493156230Smux struct stream *s, *wr; 494156230Smux struct chan *chan0, *chan1; 495156230Smux int id; 496156230Smux 497156230Smux s = config->server; 498156230Smux lprintf(2, "Establishing multiplexed-mode data connection\n"); 499156230Smux proto_printf(s, "MUX\n"); 500156230Smux stream_flush(s); 501156230Smux m = mux_open(config->socket, &chan0); 502156230Smux if (m == NULL) { 503156230Smux lprintf(-1, "Cannot open the multiplexer\n"); 504156230Smux return (NULL); 505156230Smux } 506156230Smux id = chan_listen(m); 507156230Smux if (id == -1) { 508156230Smux lprintf(-1, "ChannelMux.Listen failed: %s\n", strerror(errno)); 509156230Smux mux_close(m); 510156230Smux return (NULL); 511156230Smux } 512156230Smux wr = stream_open(chan0, NULL, (stream_writefn_t *)chan_write, NULL); 513156230Smux proto_printf(wr, "CHAN %d\n", id); 514156230Smux stream_close(wr); 515156230Smux chan1 = chan_accept(m, id); 516156230Smux if (chan1 == NULL) { 517156230Smux lprintf(-1, "ChannelMux.Accept failed: %s\n", strerror(errno)); 518156230Smux mux_close(m); 519156230Smux return (NULL); 520156230Smux } 521156230Smux config->chan0 = chan0; 522156230Smux config->chan1 = chan1; 523156230Smux return (m); 524156230Smux} 525156230Smux 526156230Smux/* 527156230Smux * Initializes the connection to the CVSup server, that is handle 528156230Smux * the protocol negotiation, logging in, exchanging file attributes 529156230Smux * support and collections information, and finally run the update 530156230Smux * session. 531156230Smux */ 532156230Smuxint 533156230Smuxproto_run(struct config *config) 534156230Smux{ 535156230Smux struct thread_args lister_args; 536156230Smux struct thread_args detailer_args; 537156230Smux struct thread_args updater_args; 538156230Smux struct thread_args *args; 539156230Smux struct killer killer; 540156230Smux struct threads *workers; 541156230Smux struct mux *m; 542156230Smux int i, status; 543156230Smux 544156230Smux /* 545156230Smux * We pass NULL for the close() function because we'll reuse 546156230Smux * the socket after the stream is closed. 547156230Smux */ 548156230Smux config->server = stream_open_fd(config->socket, stream_read_fd, 549156230Smux stream_write_fd, NULL); 550156230Smux status = proto_greet(config); 551156230Smux if (status == STATUS_SUCCESS) 552156230Smux status = proto_negproto(config); 553156230Smux if (status == STATUS_SUCCESS) 554203368Slulf status = auth_login(config); 555156230Smux if (status == STATUS_SUCCESS) 556156230Smux status = proto_fileattr(config); 557156230Smux if (status == STATUS_SUCCESS) 558156230Smux status = proto_xchgcoll(config); 559156230Smux if (status != STATUS_SUCCESS) 560156230Smux return (status); 561156230Smux 562156230Smux /* Multi-threaded action starts here. */ 563156230Smux m = proto_mux(config); 564156230Smux if (m == NULL) 565156230Smux return (STATUS_FAILURE); 566156230Smux 567156230Smux stream_close(config->server); 568156230Smux config->server = NULL; 569156230Smux config->fixups = fixups_new(); 570156230Smux killer_start(&killer, m); 571156230Smux 572156230Smux /* Start the worker threads. */ 573156230Smux workers = threads_new(); 574156230Smux args = &lister_args; 575156230Smux args->config = config; 576156230Smux args->status = -1; 577156230Smux args->errmsg = NULL; 578156230Smux args->rd = NULL; 579156230Smux args->wr = stream_open(config->chan0, 580156230Smux NULL, (stream_writefn_t *)chan_write, NULL); 581156230Smux threads_create(workers, lister, args); 582156230Smux 583156230Smux args = &detailer_args; 584156230Smux args->config = config; 585156230Smux args->status = -1; 586156230Smux args->errmsg = NULL; 587156230Smux args->rd = stream_open(config->chan0, 588156230Smux (stream_readfn_t *)chan_read, NULL, NULL); 589156230Smux args->wr = stream_open(config->chan1, 590156230Smux NULL, (stream_writefn_t *)chan_write, NULL); 591156230Smux threads_create(workers, detailer, args); 592156230Smux 593156230Smux args = &updater_args; 594156230Smux args->config = config; 595156230Smux args->status = -1; 596156230Smux args->errmsg = NULL; 597156230Smux args->rd = stream_open(config->chan1, 598156230Smux (stream_readfn_t *)chan_read, NULL, NULL); 599156230Smux args->wr = NULL; 600156230Smux threads_create(workers, updater, args); 601156230Smux 602156230Smux lprintf(2, "Running\n"); 603156230Smux /* Wait for all the worker threads to finish. */ 604156230Smux status = STATUS_SUCCESS; 605156230Smux for (i = 0; i < 3; i++) { 606156230Smux args = threads_wait(workers); 607156230Smux if (args->rd != NULL) 608156230Smux stream_close(args->rd); 609156230Smux if (args->wr != NULL) 610156230Smux stream_close(args->wr); 611156230Smux if (args->status != STATUS_SUCCESS) { 612156230Smux assert(args->errmsg != NULL); 613156230Smux if (status == STATUS_SUCCESS) { 614156230Smux status = args->status; 615156230Smux /* Shutdown the multiplexer to wake up all 616156230Smux the other threads. */ 617156230Smux mux_shutdown(m, args->errmsg, status); 618156230Smux } 619156230Smux free(args->errmsg); 620156230Smux } 621156230Smux } 622156230Smux threads_free(workers); 623156230Smux if (status == STATUS_SUCCESS) { 624156230Smux lprintf(2, "Shutting down connection to server\n"); 625156230Smux chan_close(config->chan0); 626156230Smux chan_close(config->chan1); 627156230Smux chan_wait(config->chan0); 628156230Smux chan_wait(config->chan1); 629156230Smux mux_shutdown(m, NULL, STATUS_SUCCESS); 630156230Smux } 631156230Smux killer_stop(&killer); 632156230Smux fixups_free(config->fixups); 633156230Smux status = mux_close(m); 634156230Smux if (status == STATUS_SUCCESS) { 635156230Smux lprintf(1, "Finished successfully\n"); 636156230Smux } else if (status == STATUS_INTERRUPTED) { 637156230Smux lprintf(-1, "Interrupted\n"); 638156230Smux if (killer.killedby != -1) 639156230Smux kill(getpid(), killer.killedby); 640156230Smux } 641156230Smux return (status); 642156230Smux} 643156230Smux 644156230Smux/* 645156230Smux * Write a string into the stream, escaping characters as needed. 646156230Smux * Characters escaped: 647156230Smux * 648156230Smux * SPACE -> "\_" 649156230Smux * TAB -> "\t" 650156230Smux * NEWLINE -> "\n" 651156230Smux * CR -> "\r" 652156230Smux * \ -> "\\" 653156230Smux */ 654156230Smuxstatic int 655156230Smuxproto_escape(struct stream *wr, const char *s) 656156230Smux{ 657156230Smux size_t len; 658156230Smux ssize_t n; 659156230Smux char c; 660156230Smux 661156230Smux /* Handle characters that need escaping. */ 662156230Smux do { 663156230Smux len = strcspn(s, " \t\r\n\\"); 664156230Smux n = stream_write(wr, s, len); 665156230Smux if (n == -1) 666156230Smux return (-1); 667156230Smux c = s[len]; 668156230Smux switch (c) { 669156230Smux case ' ': 670156230Smux n = stream_write(wr, "\\_", 2); 671156230Smux break; 672156230Smux case '\t': 673156230Smux n = stream_write(wr, "\\t", 2); 674156230Smux break; 675156230Smux case '\r': 676156230Smux n = stream_write(wr, "\\r", 2); 677156230Smux break; 678156230Smux case '\n': 679156230Smux n = stream_write(wr, "\\n", 2); 680156230Smux break; 681156230Smux case '\\': 682156230Smux n = stream_write(wr, "\\\\", 2); 683156230Smux break; 684156230Smux } 685156230Smux if (n == -1) 686156230Smux return (-1); 687156230Smux s += len + 1; 688156230Smux } while (c != '\0'); 689156230Smux return (0); 690156230Smux} 691156230Smux 692156230Smux/* 693156230Smux * A simple printf() implementation specifically tailored for csup. 694156230Smux * List of the supported formats: 695156230Smux * 696156230Smux * %c Print a char. 697156230Smux * %d or %i Print an int as decimal. 698156230Smux * %x Print an int as hexadecimal. 699156230Smux * %o Print an int as octal. 700156230Smux * %t Print a time_t as decimal. 701156230Smux * %s Print a char * escaping some characters as needed. 702156230Smux * %S Print a char * without escaping. 703156230Smux * %f Print an encoded struct fattr *. 704156230Smux * %F Print an encoded struct fattr *, specifying the supported 705156230Smux * attributes. 706156230Smux */ 707156230Smuxint 708156230Smuxproto_printf(struct stream *wr, const char *format, ...) 709156230Smux{ 710156230Smux fattr_support_t *support; 711156230Smux long long longval; 712156230Smux struct fattr *fa; 713156230Smux const char *fmt; 714156230Smux va_list ap; 715156230Smux char *cp, *s, *attr; 716156230Smux ssize_t n; 717186781Slulf size_t size; 718186781Slulf off_t off; 719156230Smux int rv, val, ignore; 720156230Smux char c; 721156230Smux 722156230Smux n = 0; 723156230Smux rv = 0; 724156230Smux fmt = format; 725156230Smux va_start(ap, format); 726156230Smux while ((cp = strchr(fmt, '%')) != NULL) { 727156230Smux if (cp > fmt) { 728156230Smux n = stream_write(wr, fmt, cp - fmt); 729156230Smux if (n == -1) 730156230Smux return (-1); 731156230Smux } 732156230Smux if (*++cp == '\0') 733156230Smux goto done; 734156230Smux switch (*cp) { 735156230Smux case 'c': 736156230Smux c = va_arg(ap, int); 737156230Smux rv = stream_printf(wr, "%c", c); 738156230Smux break; 739156230Smux case 'd': 740156230Smux case 'i': 741156230Smux val = va_arg(ap, int); 742156230Smux rv = stream_printf(wr, "%d", val); 743156230Smux break; 744156230Smux case 'x': 745156230Smux val = va_arg(ap, int); 746156230Smux rv = stream_printf(wr, "%x", val); 747156230Smux break; 748156230Smux case 'o': 749156230Smux val = va_arg(ap, int); 750156230Smux rv = stream_printf(wr, "%o", val); 751156230Smux break; 752186781Slulf case 'O': 753186781Slulf off = va_arg(ap, off_t); 754186781Slulf rv = stream_printf(wr, "%llu", off); 755186781Slulf break; 756156230Smux case 'S': 757156230Smux s = va_arg(ap, char *); 758156230Smux assert(s != NULL); 759156230Smux rv = stream_printf(wr, "%s", s); 760156230Smux break; 761156230Smux case 's': 762156230Smux s = va_arg(ap, char *); 763156230Smux assert(s != NULL); 764156230Smux rv = proto_escape(wr, s); 765156230Smux break; 766156230Smux case 't': 767156230Smux longval = (long long)va_arg(ap, time_t); 768156230Smux rv = stream_printf(wr, "%lld", longval); 769156230Smux break; 770156230Smux case 'f': 771156230Smux fa = va_arg(ap, struct fattr *); 772156230Smux attr = fattr_encode(fa, NULL, 0); 773156230Smux rv = proto_escape(wr, attr); 774156230Smux free(attr); 775156230Smux break; 776156230Smux case 'F': 777156230Smux fa = va_arg(ap, struct fattr *); 778156230Smux support = va_arg(ap, fattr_support_t *); 779156230Smux ignore = va_arg(ap, int); 780156230Smux attr = fattr_encode(fa, *support, ignore); 781156230Smux rv = proto_escape(wr, attr); 782156230Smux free(attr); 783156230Smux break; 784186781Slulf case 'z': 785186781Slulf size = va_arg(ap, size_t); 786186781Slulf rv = stream_printf(wr, "%zu", size); 787186781Slulf break; 788186781Slulf 789156230Smux case '%': 790156230Smux n = stream_write(wr, "%", 1); 791156230Smux if (n == -1) 792156230Smux return (-1); 793156230Smux break; 794156230Smux } 795156230Smux if (rv == -1) 796156230Smux return (-1); 797156230Smux fmt = cp + 1; 798156230Smux } 799156230Smux if (*fmt != '\0') { 800156230Smux rv = stream_printf(wr, "%s", fmt); 801156230Smux if (rv == -1) 802156230Smux return (-1); 803156230Smux } 804156230Smuxdone: 805156230Smux va_end(ap); 806156230Smux return (0); 807156230Smux} 808156230Smux 809156230Smux/* 810156230Smux * Unescape the string, see proto_escape(). 811156230Smux */ 812156230Smuxstatic void 813156230Smuxproto_unescape(char *s) 814156230Smux{ 815156230Smux char *cp, *cp2; 816156230Smux 817156230Smux cp = s; 818156230Smux while ((cp = strchr(cp, '\\')) != NULL) { 819156230Smux switch (cp[1]) { 820156230Smux case '_': 821156230Smux *cp = ' '; 822156230Smux break; 823156230Smux case 't': 824156230Smux *cp = '\t'; 825156230Smux break; 826156230Smux case 'r': 827156230Smux *cp = '\r'; 828156230Smux break; 829156230Smux case 'n': 830156230Smux *cp = '\n'; 831156230Smux break; 832156230Smux case '\\': 833156230Smux *cp = '\\'; 834156230Smux break; 835156230Smux default: 836156230Smux *cp = *(cp + 1); 837156230Smux } 838156230Smux cp2 = ++cp; 839156230Smux while (*cp2 != '\0') { 840156230Smux *cp2 = *(cp2 + 1); 841156230Smux cp2++; 842156230Smux } 843156230Smux } 844156230Smux} 845156230Smux 846156230Smux/* 847156230Smux * Get an ascii token in the string. 848156230Smux */ 849156230Smuxchar * 850156230Smuxproto_get_ascii(char **s) 851156230Smux{ 852156230Smux char *ret; 853156230Smux 854156230Smux ret = strsep(s, " "); 855156230Smux if (ret == NULL) 856156230Smux return (NULL); 857156230Smux /* Make sure we disallow 0-length fields. */ 858156230Smux if (*ret == '\0') { 859156230Smux *s = NULL; 860156230Smux return (NULL); 861156230Smux } 862156230Smux proto_unescape(ret); 863156230Smux return (ret); 864156230Smux} 865156230Smux 866156230Smux/* 867156230Smux * Get the rest of the string. 868156230Smux */ 869156230Smuxchar * 870156230Smuxproto_get_rest(char **s) 871156230Smux{ 872156230Smux char *ret; 873156230Smux 874156230Smux if (s == NULL) 875156230Smux return (NULL); 876156230Smux ret = *s; 877156230Smux proto_unescape(ret); 878156230Smux *s = NULL; 879156230Smux return (ret); 880156230Smux} 881156230Smux 882156230Smux/* 883156230Smux * Get an int token. 884156230Smux */ 885156230Smuxint 886156230Smuxproto_get_int(char **s, int *val, int base) 887156230Smux{ 888156701Smux char *cp; 889156701Smux int error; 890156230Smux 891156230Smux cp = proto_get_ascii(s); 892156230Smux if (cp == NULL) 893156230Smux return (-1); 894156701Smux error = asciitoint(cp, val, base); 895156701Smux return (error); 896156230Smux} 897156230Smux 898156230Smux/* 899186781Slulf * Get a size_t token. 900186781Slulf */ 901186781Slulfint 902186781Slulfproto_get_sizet(char **s, size_t *val, int base) 903186781Slulf{ 904186781Slulf unsigned long long tmp; 905186781Slulf char *cp, *end; 906186781Slulf 907186781Slulf cp = proto_get_ascii(s); 908186781Slulf if (cp == NULL) 909186781Slulf return (-1); 910186781Slulf errno = 0; 911186781Slulf tmp = strtoll(cp, &end, base); 912186781Slulf if (errno || *end != '\0') 913186781Slulf return (-1); 914186781Slulf *val = (size_t)tmp; 915186781Slulf return (0); 916186781Slulf} 917186781Slulf 918186781Slulf/* 919156230Smux * Get a time_t token. 920156230Smux * 921156230Smux * Ideally, we would use an intmax_t and strtoimax() here, but strtoll() 922156230Smux * is more portable and 64bits should be enough for a timestamp. 923156230Smux */ 924156230Smuxint 925156230Smuxproto_get_time(char **s, time_t *val) 926156230Smux{ 927156230Smux long long tmp; 928156230Smux char *cp, *end; 929156230Smux 930156230Smux cp = proto_get_ascii(s); 931156230Smux if (cp == NULL) 932156230Smux return (-1); 933156230Smux errno = 0; 934156230Smux tmp = strtoll(cp, &end, 10); 935156230Smux if (errno || *end != '\0') 936156230Smux return (-1); 937156230Smux *val = (time_t)tmp; 938156230Smux return (0); 939156230Smux} 940156230Smux 941156230Smux/* Start the killer thread. It is used to protect against some signals 942156230Smux during the multi-threaded run so that we can gracefully fail. */ 943156230Smuxstatic void 944156230Smuxkiller_start(struct killer *k, struct mux *m) 945156230Smux{ 946156230Smux int error; 947156230Smux 948156230Smux k->mux = m; 949156230Smux k->killedby = -1; 950156230Smux sigemptyset(&k->sigset); 951156230Smux sigaddset(&k->sigset, SIGINT); 952156230Smux sigaddset(&k->sigset, SIGHUP); 953156230Smux sigaddset(&k->sigset, SIGTERM); 954156230Smux sigaddset(&k->sigset, SIGPIPE); 955156230Smux pthread_sigmask(SIG_BLOCK, &k->sigset, NULL); 956156230Smux error = pthread_create(&k->thread, NULL, killer_run, k); 957156230Smux if (error) 958156230Smux err(1, "pthread_create"); 959156230Smux} 960156230Smux 961156230Smux/* The main loop of the killer thread. */ 962156230Smuxstatic void * 963156230Smuxkiller_run(void *arg) 964156230Smux{ 965156230Smux struct killer *k; 966156230Smux int error, sig, old; 967156230Smux 968156230Smux k = arg; 969156230Smuxagain: 970156230Smux error = sigwait(&k->sigset, &sig); 971156230Smux assert(!error); 972156230Smux if (sig == SIGINT || sig == SIGHUP || sig == SIGTERM) { 973156230Smux if (k->killedby == -1) { 974156230Smux k->killedby = sig; 975156230Smux /* Ensure we don't get canceled during the shutdown. */ 976156230Smux pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old); 977156230Smux mux_shutdown(k->mux, "Cleaning up ...", 978156230Smux STATUS_INTERRUPTED); 979156230Smux pthread_setcancelstate(old, NULL); 980156230Smux } 981156230Smux } 982156230Smux goto again; 983156230Smux} 984156230Smux 985156230Smux/* Stop the killer thread. */ 986156230Smuxstatic void 987156230Smuxkiller_stop(struct killer *k) 988156230Smux{ 989156230Smux void *val; 990156230Smux int error; 991156230Smux 992156230Smux error = pthread_cancel(k->thread); 993156230Smux assert(!error); 994156230Smux pthread_join(k->thread, &val); 995156230Smux assert(val == PTHREAD_CANCELED); 996156230Smux pthread_sigmask(SIG_UNBLOCK, &k->sigset, NULL); 997156230Smux} 998