xref: /trueos/usr.bin/csup/main.c (revision 8fe640108653f13042f1b15213769e338aa524f6)
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 
51 int verbose = 1;
52 
53 static void
usage(char * argv0)54 usage(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 
99 int
main(int argc,char * argv[])100 main(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