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