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$ 27156230Smux */ 28156230Smux 29156230Smux#include <sys/file.h> 30156230Smux#include <sys/types.h> 31156230Smux#include <sys/socket.h> 32156230Smux 33156230Smux#include <errno.h> 34156230Smux#include <fcntl.h> 35156230Smux#include <libgen.h> 36156230Smux#include <netdb.h> 37156230Smux#include <stdio.h> 38156230Smux#include <stdlib.h> 39156230Smux#include <string.h> 40156230Smux#include <unistd.h> 41156230Smux 42156230Smux#include "config.h" 43156230Smux#include "fattr.h" 44156230Smux#include "misc.h" 45156230Smux#include "proto.h" 46156230Smux#include "stream.h" 47156230Smux 48156230Smux#define USAGE_OPTFMT " %-12s %s\n" 49156230Smux#define USAGE_OPTFMTSUB " %-14s %s\n", "" 50156230Smux 51156230Smuxint verbose = 1; 52156230Smux 53156230Smuxstatic void 54156230Smuxusage(char *argv0) 55156230Smux{ 56156230Smux 57156230Smux lprintf(-1, "Usage: %s [options] supfile\n", basename(argv0)); 58156230Smux lprintf(-1, " Options:\n"); 59156230Smux lprintf(-1, USAGE_OPTFMT, "-1", "Don't retry automatically on failure " 60156230Smux "(same as \"-r 0\")"); 61156230Smux lprintf(-1, USAGE_OPTFMT, "-4", "Force usage of IPv4 addresses"); 62156230Smux lprintf(-1, USAGE_OPTFMT, "-6", "Force usage of IPv6 addresses"); 63203368Slulf lprintf(-1, USAGE_OPTFMT, "-a", 64203368Slulf "Require server to authenticate itself to us"); 65156230Smux lprintf(-1, USAGE_OPTFMT, "-A addr", 66156230Smux "Bind local socket to a specific address"); 67156230Smux lprintf(-1, USAGE_OPTFMT, "-b base", 68156230Smux "Override supfile's \"base\" directory"); 69156230Smux lprintf(-1, USAGE_OPTFMT, "-c collDir", 70156230Smux "Subdirectory of \"base\" for collections (default \"sup\")"); 71156701Smux lprintf(-1, USAGE_OPTFMT, "-d delLimit", 72156701Smux "Allow at most \"delLimit\" file deletions (default unlimited)"); 73156230Smux lprintf(-1, USAGE_OPTFMT, "-h host", 74156230Smux "Override supfile's \"host\" name"); 75156230Smux lprintf(-1, USAGE_OPTFMT, "-i pattern", 76156230Smux "Include only files/directories matching pattern."); 77156230Smux lprintf(-1, USAGE_OPTFMTSUB, 78156230Smux "May be repeated for an OR operation. Default is"); 79156230Smux lprintf(-1, USAGE_OPTFMTSUB, "to include each entire collection."); 80156701Smux lprintf(-1, USAGE_OPTFMT, "-k", 81156701Smux "Keep bad temporary files when fixups are required"); 82156230Smux lprintf(-1, USAGE_OPTFMT, "-l lockfile", 83156230Smux "Lock file during update; fail if already locked"); 84156230Smux lprintf(-1, USAGE_OPTFMT, "-L n", 85156230Smux "Verbosity level (0..2, default 1)"); 86156230Smux lprintf(-1, USAGE_OPTFMT, "-p port", 87156230Smux "Alternate server port (default 5999)"); 88156230Smux lprintf(-1, USAGE_OPTFMT, "-r n", 89156230Smux "Maximum retries on transient errors (default unlimited)"); 90156230Smux lprintf(-1, USAGE_OPTFMT, "-s", 91156230Smux "Don't stat client files; trust the checkouts file"); 92156230Smux lprintf(-1, USAGE_OPTFMT, "-v", "Print version and exit"); 93156230Smux lprintf(-1, USAGE_OPTFMT, "-z", "Enable compression for all " 94156230Smux "collections"); 95156230Smux lprintf(-1, USAGE_OPTFMT, "-Z", "Disable compression for all " 96156230Smux "collections"); 97156230Smux} 98156230Smux 99156230Smuxint 100156230Smuxmain(int argc, char *argv[]) 101156230Smux{ 102156230Smux struct tm tm; 103156230Smux struct backoff_timer *timer; 104156230Smux struct config *config; 105156230Smux struct coll *override; 106156230Smux struct addrinfo *res; 107156230Smux struct sockaddr *laddr; 108156230Smux socklen_t laddrlen; 109156230Smux struct stream *lock; 110156230Smux char *argv0, *file, *lockfile; 111156230Smux int family, error, lockfd, lflag, overridemask; 112203368Slulf int c, i, deletelim, port, retries, status, reqauth; 113156230Smux time_t nexttry; 114156230Smux 115156230Smux error = 0; 116156230Smux family = PF_UNSPEC; 117156701Smux deletelim = -1; 118156230Smux port = 0; 119156230Smux lflag = 0; 120156230Smux lockfd = 0; 121156230Smux nexttry = 0; 122156230Smux retries = -1; 123156230Smux argv0 = argv[0]; 124156230Smux laddr = NULL; 125156230Smux laddrlen = 0; 126156230Smux lockfile = NULL; 127156230Smux override = coll_new(NULL); 128156230Smux overridemask = 0; 129203368Slulf reqauth = 0; 130156230Smux 131156701Smux while ((c = getopt(argc, argv, 132203368Slulf "146aA:b:c:d:gh:i:kl:L:p:P:r:svzZ")) != -1) { 133156230Smux switch (c) { 134156230Smux case '1': 135156230Smux retries = 0; 136156230Smux break; 137156230Smux case '4': 138156230Smux family = AF_INET; 139156230Smux break; 140156230Smux case '6': 141156230Smux family = AF_INET6; 142156230Smux break; 143203368Slulf case 'a': 144203368Slulf /* Require server authentication */ 145203368Slulf reqauth = 1; 146203368Slulf break; 147156230Smux case 'A': 148156230Smux error = getaddrinfo(optarg, NULL, NULL, &res); 149156230Smux if (error) { 150156230Smux lprintf(-1, "%s: %s\n", optarg, 151156230Smux gai_strerror(error)); 152156230Smux return (1); 153156230Smux } 154156230Smux laddrlen = res->ai_addrlen; 155156230Smux laddr = xmalloc(laddrlen); 156156230Smux memcpy(laddr, res->ai_addr, laddrlen); 157156230Smux freeaddrinfo(res); 158156230Smux break; 159156230Smux case 'b': 160156230Smux if (override->co_base != NULL) 161156230Smux free(override->co_base); 162156230Smux override->co_base = xstrdup(optarg); 163156230Smux break; 164156230Smux case 'c': 165156230Smux override->co_colldir = optarg; 166156230Smux break; 167156701Smux case 'd': 168156701Smux error = asciitoint(optarg, &deletelim, 0); 169156701Smux if (error || deletelim < 0) { 170156701Smux lprintf(-1, "Invalid deletion limit\n"); 171156701Smux usage(argv0); 172156701Smux return (1); 173156701Smux } 174156701Smux break; 175156230Smux case 'g': 176156230Smux /* For compatibility. */ 177156230Smux break; 178156230Smux case 'h': 179156230Smux if (override->co_host != NULL) 180156230Smux free(override->co_host); 181156230Smux override->co_host = xstrdup(optarg); 182156230Smux break; 183156230Smux case 'i': 184156230Smux pattlist_add(override->co_accepts, optarg); 185156230Smux break; 186156701Smux case 'k': 187156701Smux override->co_options |= CO_KEEPBADFILES; 188156701Smux overridemask |= CO_KEEPBADFILES; 189156701Smux break; 190156230Smux case 'l': 191156230Smux lockfile = optarg; 192156230Smux lflag = 1; 193156230Smux lockfd = open(lockfile, 194156230Smux O_CREAT | O_WRONLY | O_TRUNC, 0700); 195156230Smux if (lockfd != -1) { 196156230Smux error = flock(lockfd, LOCK_EX | LOCK_NB); 197156230Smux if (error == -1 && errno == EWOULDBLOCK) { 198156230Smux if (lockfd != -1) 199156230Smux close(lockfd); 200156230Smux lprintf(-1, "\"%s\" is already locked " 201156230Smux "by another process\n", lockfile); 202156230Smux return (1); 203156230Smux } 204156230Smux } 205156230Smux if (lockfd == -1 || error == -1) { 206156230Smux if (lockfd != -1) 207156230Smux close(lockfd); 208156230Smux lprintf(-1, "Error locking \"%s\": %s\n", 209156230Smux lockfile, strerror(errno)); 210156230Smux return (1); 211156230Smux } 212156230Smux lock = stream_open_fd(lockfd, 213156230Smux NULL, stream_write_fd, NULL); 214156230Smux (void)stream_printf(lock, "%10ld\n", (long)getpid()); 215156230Smux stream_close(lock); 216156230Smux break; 217156230Smux case 'L': 218156701Smux error = asciitoint(optarg, &verbose, 0); 219156701Smux if (error) { 220156230Smux lprintf(-1, "Invalid verbosity\n"); 221156230Smux usage(argv0); 222156230Smux return (1); 223156230Smux } 224156230Smux break; 225156230Smux case 'p': 226156230Smux /* Use specified server port. */ 227156701Smux error = asciitoint(optarg, &port, 0); 228156701Smux if (error) { 229156230Smux lprintf(-1, "Invalid server port\n"); 230156230Smux usage(argv0); 231156230Smux return (1); 232156230Smux } 233156701Smux if (port <= 0 || port >= 65536) { 234156701Smux lprintf(-1, "Invalid port %d\n", port); 235156701Smux return (1); 236156701Smux } 237156701Smux if (port < 1024) { 238156701Smux lprintf(-1, "Reserved port %d not permitted\n", 239156701Smux port); 240156701Smux return (1); 241156701Smux } 242156230Smux break; 243156230Smux case 'P': 244156230Smux /* For compatibility. */ 245156230Smux if (strcmp(optarg, "m") != 0) { 246156230Smux lprintf(-1, 247156230Smux "Client only supports multiplexed mode\n"); 248156230Smux return (1); 249156230Smux } 250156230Smux break; 251156230Smux case 'r': 252156701Smux error = asciitoint(optarg, &retries, 0); 253156701Smux if (error || retries < 0) { 254156230Smux lprintf(-1, "Invalid retry limit\n"); 255156230Smux usage(argv0); 256156230Smux return (1); 257156230Smux } 258156230Smux break; 259156230Smux case 's': 260156230Smux override->co_options |= CO_TRUSTSTATUSFILE; 261156230Smux overridemask |= CO_TRUSTSTATUSFILE; 262156230Smux break; 263156230Smux case 'v': 264156230Smux lprintf(0, "CVSup client written in C\n"); 265156230Smux lprintf(0, "Software version: %s\n", PROTO_SWVER); 266156230Smux lprintf(0, "Protocol version: %d.%d\n", 267156230Smux PROTO_MAJ, PROTO_MIN); 268156230Smux return (0); 269156230Smux break; 270156230Smux case 'z': 271156230Smux /* Force compression on all collections. */ 272156230Smux override->co_options |= CO_COMPRESS; 273156230Smux overridemask |= CO_COMPRESS; 274156230Smux break; 275156230Smux case 'Z': 276156230Smux /* Disables compression on all collections. */ 277156230Smux override->co_options &= ~CO_COMPRESS; 278156230Smux overridemask &= ~CO_COMPRESS; 279156230Smux break; 280156230Smux case '?': 281156230Smux default: 282156230Smux usage(argv0); 283156230Smux return (1); 284156230Smux } 285156230Smux } 286156230Smux 287156230Smux argc -= optind; 288156230Smux argv += optind; 289156230Smux 290156230Smux if (argc < 1) { 291156230Smux usage(argv0); 292156230Smux return (1); 293156230Smux } 294156230Smux 295156230Smux file = argv[0]; 296156230Smux lprintf(2, "Parsing supfile \"%s\"\n", file); 297156230Smux config = config_init(file, override, overridemask); 298156230Smux coll_free(override); 299156230Smux if (config == NULL) 300156230Smux return (1); 301156230Smux 302156230Smux if (config_checkcolls(config) == 0) { 303156230Smux lprintf(-1, "No collections selected\n"); 304156230Smux return (1); 305156230Smux } 306156230Smux 307156701Smux if (laddr != NULL) { 308156701Smux config->laddr = laddr; 309156701Smux config->laddrlen = laddrlen; 310156701Smux } 311156701Smux config->deletelim = deletelim; 312203368Slulf config->reqauth = reqauth; 313156230Smux lprintf(2, "Connecting to %s\n", config->host); 314156230Smux 315156230Smux i = 0; 316156230Smux fattr_init(); /* Initialize the fattr API. */ 317156230Smux timer = bt_new(300, 7200, 2.0, 0.1); 318156230Smux for (;;) { 319156230Smux status = proto_connect(config, family, port); 320156230Smux if (status == STATUS_SUCCESS) { 321156230Smux status = proto_run(config); 322156230Smux if (status != STATUS_TRANSIENTFAILURE) 323156230Smux break; 324156230Smux } 325156230Smux if (retries >= 0 && i >= retries) 326156230Smux break; 327156230Smux nexttry = time(0) + bt_get(timer); 328156230Smux localtime_r(&nexttry, &tm); 329156230Smux lprintf(1, "Will retry at %02d:%02d:%02d\n", 330156230Smux tm.tm_hour, tm.tm_min, tm.tm_sec); 331156230Smux bt_pause(timer); 332156230Smux lprintf(1, "Retrying\n"); 333156230Smux i++; 334156230Smux } 335156230Smux bt_free(timer); 336156230Smux fattr_fini(); 337156230Smux if (lflag) { 338156230Smux unlink(lockfile); 339156230Smux flock(lockfd, LOCK_UN); 340156230Smux close(lockfd); 341156230Smux } 342156230Smux config_free(config); 343156230Smux if (status != STATUS_SUCCESS) 344156230Smux return (1); 345156230Smux return (0); 346156230Smux} 347