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$ 27 */ 28 29#include <sys/file.h> 30#include <sys/types.h> 31#include <sys/socket.h> 32 33#include <errno.h> 34#include <fcntl.h> 35#include <libgen.h> 36#include <netdb.h> 37#include <stdio.h> 38#include <stdlib.h> 39#include <string.h> 40#include <unistd.h> 41 42#include "config.h" 43#include "fattr.h" 44#include "misc.h" 45#include "proto.h" 46#include "stream.h" 47 48#define USAGE_OPTFMT " %-12s %s\n" 49#define USAGE_OPTFMTSUB " %-14s %s\n", "" 50 51int verbose = 1; 52 53static void 54usage(char *argv0) 55{ 56 57 lprintf(-1, "Usage: %s [options] supfile\n", basename(argv0)); 58 lprintf(-1, " Options:\n"); 59 lprintf(-1, USAGE_OPTFMT, "-1", "Don't retry automatically on failure " 60 "(same as \"-r 0\")"); 61 lprintf(-1, USAGE_OPTFMT, "-4", "Force usage of IPv4 addresses"); 62 lprintf(-1, USAGE_OPTFMT, "-6", "Force usage of IPv6 addresses"); 63 lprintf(-1, USAGE_OPTFMT, "-a", 64 "Require server to authenticate itself to us"); 65 lprintf(-1, USAGE_OPTFMT, "-A addr", 66 "Bind local socket to a specific address"); 67 lprintf(-1, USAGE_OPTFMT, "-b base", 68 "Override supfile's \"base\" directory"); 69 lprintf(-1, USAGE_OPTFMT, "-c collDir", 70 "Subdirectory of \"base\" for collections (default \"sup\")"); 71 lprintf(-1, USAGE_OPTFMT, "-d delLimit", 72 "Allow at most \"delLimit\" file deletions (default unlimited)"); 73 lprintf(-1, USAGE_OPTFMT, "-h host", 74 "Override supfile's \"host\" name"); 75 lprintf(-1, USAGE_OPTFMT, "-i pattern", 76 "Include only files/directories matching pattern."); 77 lprintf(-1, USAGE_OPTFMTSUB, 78 "May be repeated for an OR operation. Default is"); 79 lprintf(-1, USAGE_OPTFMTSUB, "to include each entire collection."); 80 lprintf(-1, USAGE_OPTFMT, "-k", 81 "Keep bad temporary files when fixups are required"); 82 lprintf(-1, USAGE_OPTFMT, "-l lockfile", 83 "Lock file during update; fail if already locked"); 84 lprintf(-1, USAGE_OPTFMT, "-L n", 85 "Verbosity level (0..2, default 1)"); 86 lprintf(-1, USAGE_OPTFMT, "-p port", 87 "Alternate server port (default 5999)"); 88 lprintf(-1, USAGE_OPTFMT, "-r n", 89 "Maximum retries on transient errors (default unlimited)"); 90 lprintf(-1, USAGE_OPTFMT, "-s", 91 "Don't stat client files; trust the checkouts file"); 92 lprintf(-1, USAGE_OPTFMT, "-v", "Print version and exit"); 93 lprintf(-1, USAGE_OPTFMT, "-z", "Enable compression for all " 94 "collections"); 95 lprintf(-1, USAGE_OPTFMT, "-Z", "Disable compression for all " 96 "collections"); 97} 98 99int 100main(int argc, char *argv[]) 101{ 102 struct tm tm; 103 struct backoff_timer *timer; 104 struct config *config; 105 struct coll *override; 106 struct addrinfo *res; 107 struct sockaddr *laddr; 108 socklen_t laddrlen; 109 struct stream *lock; 110 char *argv0, *file, *lockfile; 111 int family, error, lockfd, lflag, overridemask; 112 int c, i, deletelim, port, retries, status, reqauth; 113 time_t nexttry; 114 115 error = 0; 116 family = PF_UNSPEC; 117 deletelim = -1; 118 port = 0; 119 lflag = 0; 120 lockfd = 0; 121 nexttry = 0; 122 retries = -1; 123 argv0 = argv[0]; 124 laddr = NULL; 125 laddrlen = 0; 126 lockfile = NULL; 127 override = coll_new(NULL); 128 overridemask = 0; 129 reqauth = 0; 130 131 while ((c = getopt(argc, argv, 132 "146aA:b:c:d:gh:i:kl:L:p:P:r:svzZ")) != -1) { 133 switch (c) { 134 case '1': 135 retries = 0; 136 break; 137 case '4': 138 family = AF_INET; 139 break; 140 case '6': 141 family = AF_INET6; 142 break; 143 case 'a': 144 /* Require server authentication */ 145 reqauth = 1; 146 break; 147 case 'A': 148 error = getaddrinfo(optarg, NULL, NULL, &res); 149 if (error) { 150 lprintf(-1, "%s: %s\n", optarg, 151 gai_strerror(error)); 152 return (1); 153 } 154 laddrlen = res->ai_addrlen; 155 laddr = xmalloc(laddrlen); 156 memcpy(laddr, res->ai_addr, laddrlen); 157 freeaddrinfo(res); 158 break; 159 case 'b': 160 if (override->co_base != NULL) 161 free(override->co_base); 162 override->co_base = xstrdup(optarg); 163 break; 164 case 'c': 165 override->co_colldir = optarg; 166 break; 167 case 'd': 168 error = asciitoint(optarg, &deletelim, 0); 169 if (error || deletelim < 0) { 170 lprintf(-1, "Invalid deletion limit\n"); 171 usage(argv0); 172 return (1); 173 } 174 break; 175 case 'g': 176 /* For compatibility. */ 177 break; 178 case 'h': 179 if (override->co_host != NULL) 180 free(override->co_host); 181 override->co_host = xstrdup(optarg); 182 break; 183 case 'i': 184 pattlist_add(override->co_accepts, optarg); 185 break; 186 case 'k': 187 override->co_options |= CO_KEEPBADFILES; 188 overridemask |= CO_KEEPBADFILES; 189 break; 190 case 'l': 191 lockfile = optarg; 192 lflag = 1; 193 lockfd = open(lockfile, 194 O_CREAT | O_WRONLY | O_TRUNC, 0700); 195 if (lockfd != -1) { 196 error = flock(lockfd, LOCK_EX | LOCK_NB); 197 if (error == -1 && errno == EWOULDBLOCK) { 198 if (lockfd != -1) 199 close(lockfd); 200 lprintf(-1, "\"%s\" is already locked " 201 "by another process\n", lockfile); 202 return (1); 203 } 204 } 205 if (lockfd == -1 || error == -1) { 206 if (lockfd != -1) 207 close(lockfd); 208 lprintf(-1, "Error locking \"%s\": %s\n", 209 lockfile, strerror(errno)); 210 return (1); 211 } 212 lock = stream_open_fd(lockfd, 213 NULL, stream_write_fd, NULL); 214 (void)stream_printf(lock, "%10ld\n", (long)getpid()); 215 stream_close(lock); 216 break; 217 case 'L': 218 error = asciitoint(optarg, &verbose, 0); 219 if (error) { 220 lprintf(-1, "Invalid verbosity\n"); 221 usage(argv0); 222 return (1); 223 } 224 break; 225 case 'p': 226 /* Use specified server port. */ 227 error = asciitoint(optarg, &port, 0); 228 if (error) { 229 lprintf(-1, "Invalid server port\n"); 230 usage(argv0); 231 return (1); 232 } 233 if (port <= 0 || port >= 65536) { 234 lprintf(-1, "Invalid port %d\n", port); 235 return (1); 236 } 237 if (port < 1024) { 238 lprintf(-1, "Reserved port %d not permitted\n", 239 port); 240 return (1); 241 } 242 break; 243 case 'P': 244 /* For compatibility. */ 245 if (strcmp(optarg, "m") != 0) { 246 lprintf(-1, 247 "Client only supports multiplexed mode\n"); 248 return (1); 249 } 250 break; 251 case 'r': 252 error = asciitoint(optarg, &retries, 0); 253 if (error || retries < 0) { 254 lprintf(-1, "Invalid retry limit\n"); 255 usage(argv0); 256 return (1); 257 } 258 break; 259 case 's': 260 override->co_options |= CO_TRUSTSTATUSFILE; 261 overridemask |= CO_TRUSTSTATUSFILE; 262 break; 263 case 'v': 264 lprintf(0, "CVSup client written in C\n"); 265 lprintf(0, "Software version: %s\n", PROTO_SWVER); 266 lprintf(0, "Protocol version: %d.%d\n", 267 PROTO_MAJ, PROTO_MIN); 268 return (0); 269 break; 270 case 'z': 271 /* Force compression on all collections. */ 272 override->co_options |= CO_COMPRESS; 273 overridemask |= CO_COMPRESS; 274 break; 275 case 'Z': 276 /* Disables compression on all collections. */ 277 override->co_options &= ~CO_COMPRESS; 278 overridemask &= ~CO_COMPRESS; 279 break; 280 case '?': 281 default: 282 usage(argv0); 283 return (1); 284 } 285 } 286 287 argc -= optind; 288 argv += optind; 289 290 if (argc < 1) { 291 usage(argv0); 292 return (1); 293 } 294 295 file = argv[0]; 296 lprintf(2, "Parsing supfile \"%s\"\n", file); 297 config = config_init(file, override, overridemask); 298 coll_free(override); 299 if (config == NULL) 300 return (1); 301 302 if (config_checkcolls(config) == 0) { 303 lprintf(-1, "No collections selected\n"); 304 return (1); 305 } 306 307 if (laddr != NULL) { 308 config->laddr = laddr; 309 config->laddrlen = laddrlen; 310 } 311 config->deletelim = deletelim; 312 config->reqauth = reqauth; 313 lprintf(2, "Connecting to %s\n", config->host); 314 315 i = 0; 316 fattr_init(); /* Initialize the fattr API. */ 317 timer = bt_new(300, 7200, 2.0, 0.1); 318 for (;;) { 319 status = proto_connect(config, family, port); 320 if (status == STATUS_SUCCESS) { 321 status = proto_run(config); 322 if (status != STATUS_TRANSIENTFAILURE) 323 break; 324 } 325 if (retries >= 0 && i >= retries) 326 break; 327 nexttry = time(0) + bt_get(timer); 328 localtime_r(&nexttry, &tm); 329 lprintf(1, "Will retry at %02d:%02d:%02d\n", 330 tm.tm_hour, tm.tm_min, tm.tm_sec); 331 bt_pause(timer); 332 lprintf(1, "Retrying\n"); 333 i++; 334 } 335 bt_free(timer); 336 fattr_fini(); 337 if (lflag) { 338 unlink(lockfile); 339 flock(lockfd, LOCK_UN); 340 close(lockfd); 341 } 342 config_free(config); 343 if (status != STATUS_SUCCESS) 344 return (1); 345 return (0); 346} 347