main.c revision 156230
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: vendor/csup/dist/contrib/csup/main.c 156230 2006-03-03 04:11:29Z mux $
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");
63156230Smux	lprintf(-1, USAGE_OPTFMT, "-A addr",
64156230Smux	    "Bind local socket to a specific address");
65156230Smux	lprintf(-1, USAGE_OPTFMT, "-b base",
66156230Smux	    "Override supfile's \"base\" directory");
67156230Smux	lprintf(-1, USAGE_OPTFMT, "-c collDir",
68156230Smux	    "Subdirectory of \"base\" for collections (default \"sup\")");
69156230Smux	lprintf(-1, USAGE_OPTFMT, "-h host",
70156230Smux	    "Override supfile's \"host\" name");
71156230Smux	lprintf(-1, USAGE_OPTFMT, "-i pattern",
72156230Smux	    "Include only files/directories matching pattern.");
73156230Smux	lprintf(-1, USAGE_OPTFMTSUB,
74156230Smux	    "May be repeated for an OR operation.  Default is");
75156230Smux	lprintf(-1, USAGE_OPTFMTSUB, "to include each entire collection.");
76156230Smux	lprintf(-1, USAGE_OPTFMT, "-l lockfile",
77156230Smux	    "Lock file during update; fail if already locked");
78156230Smux	lprintf(-1, USAGE_OPTFMT, "-L n",
79156230Smux	    "Verbosity level (0..2, default 1)");
80156230Smux	lprintf(-1, USAGE_OPTFMT, "-p port",
81156230Smux	    "Alternate server port (default 5999)");
82156230Smux	lprintf(-1, USAGE_OPTFMT, "-r n",
83156230Smux	    "Maximum retries on transient errors (default unlimited)");
84156230Smux	lprintf(-1, USAGE_OPTFMT, "-s",
85156230Smux	    "Don't stat client files; trust the checkouts file");
86156230Smux	lprintf(-1, USAGE_OPTFMT, "-v", "Print version and exit");
87156230Smux	lprintf(-1, USAGE_OPTFMT, "-z", "Enable compression for all "
88156230Smux	    "collections");
89156230Smux	lprintf(-1, USAGE_OPTFMT, "-Z", "Disable compression for all "
90156230Smux	    "collections");
91156230Smux}
92156230Smux
93156230Smuxint
94156230Smuxmain(int argc, char *argv[])
95156230Smux{
96156230Smux	struct tm tm;
97156230Smux	struct backoff_timer *timer;
98156230Smux	struct config *config;
99156230Smux	struct coll *override;
100156230Smux	struct addrinfo *res;
101156230Smux	struct sockaddr *laddr;
102156230Smux	socklen_t laddrlen;
103156230Smux	struct stream *lock;
104156230Smux	char *argv0, *file, *lockfile;
105156230Smux	uint16_t port;
106156230Smux	int family, error, lockfd, lflag, overridemask;
107156230Smux	int c, i, retries, status;
108156230Smux	time_t nexttry;
109156230Smux
110156230Smux	error = 0;
111156230Smux	family = PF_UNSPEC;
112156230Smux	port = 0;
113156230Smux	lflag = 0;
114156230Smux	lockfd = 0;
115156230Smux	nexttry = 0;
116156230Smux	retries = -1;
117156230Smux	argv0 = argv[0];
118156230Smux	laddr = NULL;
119156230Smux	laddrlen = 0;
120156230Smux	lockfile = NULL;
121156230Smux	override = coll_new(NULL);
122156230Smux	overridemask = 0;
123156230Smux
124156230Smux	while ((c = getopt(argc, argv, "146A:b:c:gh:i:l:L:p:P:r:svzZ")) != -1) {
125156230Smux		switch (c) {
126156230Smux		case '1':
127156230Smux			retries = 0;
128156230Smux			break;
129156230Smux		case '4':
130156230Smux			family = AF_INET;
131156230Smux			break;
132156230Smux		case '6':
133156230Smux			family = AF_INET6;
134156230Smux			break;
135156230Smux		case 'A':
136156230Smux			error = getaddrinfo(optarg, NULL, NULL, &res);
137156230Smux			if (error) {
138156230Smux				lprintf(-1, "%s: %s\n", optarg,
139156230Smux				    gai_strerror(error));
140156230Smux				return (1);
141156230Smux			}
142156230Smux			laddrlen = res->ai_addrlen;
143156230Smux			laddr = xmalloc(laddrlen);
144156230Smux			memcpy(laddr, res->ai_addr, laddrlen);
145156230Smux			freeaddrinfo(res);
146156230Smux			break;
147156230Smux		case 'b':
148156230Smux			if (override->co_base != NULL)
149156230Smux				free(override->co_base);
150156230Smux			override->co_base = xstrdup(optarg);
151156230Smux			break;
152156230Smux		case 'c':
153156230Smux			override->co_colldir = optarg;
154156230Smux			break;
155156230Smux		case 'g':
156156230Smux			/* For compatibility. */
157156230Smux			break;
158156230Smux		case 'h':
159156230Smux			if (override->co_host != NULL)
160156230Smux				free(override->co_host);
161156230Smux			override->co_host = xstrdup(optarg);
162156230Smux			break;
163156230Smux		case 'i':
164156230Smux			pattlist_add(override->co_accepts, optarg);
165156230Smux			break;
166156230Smux		case 'l':
167156230Smux			lockfile = optarg;
168156230Smux			lflag = 1;
169156230Smux			lockfd = open(lockfile,
170156230Smux			    O_CREAT | O_WRONLY | O_TRUNC, 0700);
171156230Smux			if (lockfd != -1) {
172156230Smux				error = flock(lockfd, LOCK_EX | LOCK_NB);
173156230Smux				if (error == -1 && errno == EWOULDBLOCK) {
174156230Smux					if (lockfd != -1)
175156230Smux						close(lockfd);
176156230Smux					lprintf(-1, "\"%s\" is already locked "
177156230Smux					    "by another process\n", lockfile);
178156230Smux					return (1);
179156230Smux				}
180156230Smux			}
181156230Smux			if (lockfd == -1 || error == -1) {
182156230Smux				if (lockfd != -1)
183156230Smux					close(lockfd);
184156230Smux				lprintf(-1, "Error locking \"%s\": %s\n",
185156230Smux				    lockfile, strerror(errno));
186156230Smux				return (1);
187156230Smux			}
188156230Smux			lock = stream_open_fd(lockfd,
189156230Smux			    NULL, stream_write_fd, NULL);
190156230Smux			(void)stream_printf(lock, "%10ld\n", (long)getpid());
191156230Smux			stream_close(lock);
192156230Smux			break;
193156230Smux		case 'L':
194156230Smux			errno = 0;
195156230Smux			verbose = strtol(optarg, NULL, 0);
196156230Smux			if (errno == EINVAL) {
197156230Smux				lprintf(-1, "Invalid verbosity\n");
198156230Smux				usage(argv0);
199156230Smux				return (1);
200156230Smux			}
201156230Smux			break;
202156230Smux		case 'p':
203156230Smux			/* Use specified server port. */
204156230Smux			errno = 0;
205156230Smux			port = strtol(optarg, NULL, 0);
206156230Smux			if (errno == EINVAL) {
207156230Smux				lprintf(-1, "Invalid server port\n");
208156230Smux				usage(argv0);
209156230Smux				return (1);
210156230Smux			}
211156230Smux			break;
212156230Smux		case 'P':
213156230Smux			/* For compatibility. */
214156230Smux			if (strcmp(optarg, "m") != 0) {
215156230Smux				lprintf(-1,
216156230Smux				    "Client only supports multiplexed mode\n");
217156230Smux				return (1);
218156230Smux			}
219156230Smux			break;
220156230Smux		case 'r':
221156230Smux			errno = 0;
222156230Smux			retries = strtol(optarg, NULL, 0);
223156230Smux			if (errno == EINVAL || retries < 0) {
224156230Smux				lprintf(-1, "Invalid retry limit\n");
225156230Smux				usage(argv0);
226156230Smux				return (1);
227156230Smux			}
228156230Smux			break;
229156230Smux		case 's':
230156230Smux			override->co_options |= CO_TRUSTSTATUSFILE;
231156230Smux			overridemask |= CO_TRUSTSTATUSFILE;
232156230Smux			break;
233156230Smux		case 'v':
234156230Smux			lprintf(0, "CVSup client written in C\n");
235156230Smux			lprintf(0, "Software version: %s\n", PROTO_SWVER);
236156230Smux			lprintf(0, "Protocol version: %d.%d\n",
237156230Smux			    PROTO_MAJ, PROTO_MIN);
238156230Smux			lprintf(0, "http://mu.org/~mux/csup.html\n");
239156230Smux			return (0);
240156230Smux			break;
241156230Smux		case 'z':
242156230Smux			/* Force compression on all collections. */
243156230Smux			override->co_options |= CO_COMPRESS;
244156230Smux			overridemask |= CO_COMPRESS;
245156230Smux			break;
246156230Smux		case 'Z':
247156230Smux			/* Disables compression on all collections. */
248156230Smux			override->co_options &= ~CO_COMPRESS;
249156230Smux			overridemask &= ~CO_COMPRESS;
250156230Smux			break;
251156230Smux		case '?':
252156230Smux		default:
253156230Smux			usage(argv0);
254156230Smux			return (1);
255156230Smux		}
256156230Smux	}
257156230Smux
258156230Smux	argc -= optind;
259156230Smux	argv += optind;
260156230Smux
261156230Smux	if (argc < 1) {
262156230Smux		usage(argv0);
263156230Smux		return (1);
264156230Smux	}
265156230Smux
266156230Smux	file = argv[0];
267156230Smux	lprintf(2, "Parsing supfile \"%s\"\n", file);
268156230Smux	config = config_init(file, override, overridemask);
269156230Smux	coll_free(override);
270156230Smux	if (config == NULL)
271156230Smux		return (1);
272156230Smux
273156230Smux	if (laddr != NULL) {
274156230Smux		config->laddr = laddr;
275156230Smux		config->laddrlen = laddrlen;
276156230Smux	}
277156230Smux	if (config_checkcolls(config) == 0) {
278156230Smux		lprintf(-1, "No collections selected\n");
279156230Smux		return (1);
280156230Smux	}
281156230Smux
282156230Smux	lprintf(2, "Connecting to %s\n", config->host);
283156230Smux
284156230Smux	i = 0;
285156230Smux	fattr_init();	/* Initialize the fattr API. */
286156230Smux	timer = bt_new(300, 7200, 2.0, 0.1);
287156230Smux	for (;;) {
288156230Smux		status = proto_connect(config, family, port);
289156230Smux		if (status == STATUS_SUCCESS) {
290156230Smux			status = proto_run(config);
291156230Smux			if (status != STATUS_TRANSIENTFAILURE)
292156230Smux				break;
293156230Smux		}
294156230Smux		if (retries >= 0 && i >= retries)
295156230Smux			break;
296156230Smux		nexttry = time(0) + bt_get(timer);
297156230Smux		localtime_r(&nexttry, &tm);
298156230Smux		lprintf(1, "Will retry at %02d:%02d:%02d\n",
299156230Smux		    tm.tm_hour, tm.tm_min, tm.tm_sec);
300156230Smux		bt_pause(timer);
301156230Smux		lprintf(1, "Retrying\n");
302156230Smux		i++;
303156230Smux	}
304156230Smux	bt_free(timer);
305156230Smux	fattr_fini();
306156230Smux	if (lflag) {
307156230Smux		unlink(lockfile);
308156230Smux		flock(lockfd, LOCK_UN);
309156230Smux		close(lockfd);
310156230Smux	}
311156230Smux	config_free(config);
312156230Smux	if (status != STATUS_SUCCESS)
313156230Smux		return (1);
314156230Smux	return (0);
315156230Smux}
316