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