]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - usr.bin/csup/main.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / usr.bin / csup / main.c
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
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
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 }