]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - contrib/csup/main.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / contrib / 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 addr",
64             "Bind local socket to a specific address");
65         lprintf(-1, USAGE_OPTFMT, "-b base",
66             "Override supfile's \"base\" directory");
67         lprintf(-1, USAGE_OPTFMT, "-c collDir",
68             "Subdirectory of \"base\" for collections (default \"sup\")");
69         lprintf(-1, USAGE_OPTFMT, "-d delLimit",
70             "Allow at most \"delLimit\" file deletions (default unlimited)");
71         lprintf(-1, USAGE_OPTFMT, "-h host",
72             "Override supfile's \"host\" name");
73         lprintf(-1, USAGE_OPTFMT, "-i pattern",
74             "Include only files/directories matching pattern.");
75         lprintf(-1, USAGE_OPTFMTSUB,
76             "May be repeated for an OR operation.  Default is");
77         lprintf(-1, USAGE_OPTFMTSUB, "to include each entire collection.");
78         lprintf(-1, USAGE_OPTFMT, "-k",
79             "Keep bad temporary files when fixups are required");
80         lprintf(-1, USAGE_OPTFMT, "-l lockfile",
81             "Lock file during update; fail if already locked");
82         lprintf(-1, USAGE_OPTFMT, "-L n",
83             "Verbosity level (0..2, default 1)");
84         lprintf(-1, USAGE_OPTFMT, "-p port",
85             "Alternate server port (default 5999)");
86         lprintf(-1, USAGE_OPTFMT, "-r n",
87             "Maximum retries on transient errors (default unlimited)");
88         lprintf(-1, USAGE_OPTFMT, "-s",
89             "Don't stat client files; trust the checkouts file");
90         lprintf(-1, USAGE_OPTFMT, "-v", "Print version and exit");
91         lprintf(-1, USAGE_OPTFMT, "-z", "Enable compression for all "
92             "collections");
93         lprintf(-1, USAGE_OPTFMT, "-Z", "Disable compression for all "
94             "collections");
95 }
96
97 int
98 main(int argc, char *argv[])
99 {
100         struct tm tm;
101         struct backoff_timer *timer;
102         struct config *config;
103         struct coll *override;
104         struct addrinfo *res;
105         struct sockaddr *laddr;
106         socklen_t laddrlen;
107         struct stream *lock;
108         char *argv0, *file, *lockfile;
109         int family, error, lockfd, lflag, overridemask;
110         int c, i, deletelim, port, retries, status;
111         time_t nexttry;
112
113         error = 0;
114         family = PF_UNSPEC;
115         deletelim = -1;
116         port = 0;
117         lflag = 0;
118         lockfd = 0;
119         nexttry = 0;
120         retries = -1;
121         argv0 = argv[0];
122         laddr = NULL;
123         laddrlen = 0;
124         lockfile = NULL;
125         override = coll_new(NULL);
126         overridemask = 0;
127
128         while ((c = getopt(argc, argv,
129             "146A:b:c:d:gh:i:kl:L:p:P:r:svzZ")) != -1) {
130                 switch (c) {
131                 case '1':
132                         retries = 0;
133                         break;
134                 case '4':
135                         family = AF_INET;
136                         break;
137                 case '6':
138                         family = AF_INET6;
139                         break;
140                 case 'A':
141                         error = getaddrinfo(optarg, NULL, NULL, &res);
142                         if (error) {
143                                 lprintf(-1, "%s: %s\n", optarg,
144                                     gai_strerror(error));
145                                 return (1);
146                         }
147                         laddrlen = res->ai_addrlen;
148                         laddr = xmalloc(laddrlen);
149                         memcpy(laddr, res->ai_addr, laddrlen);
150                         freeaddrinfo(res);
151                         break;
152                 case 'b':
153                         if (override->co_base != NULL)
154                                 free(override->co_base);
155                         override->co_base = xstrdup(optarg);
156                         break;
157                 case 'c':
158                         override->co_colldir = optarg;
159                         break;
160                 case 'd':
161                         error = asciitoint(optarg, &deletelim, 0);
162                         if (error || deletelim < 0) {
163                                 lprintf(-1, "Invalid deletion limit\n");
164                                 usage(argv0);
165                                 return (1);
166                         }
167                         break;
168                 case 'g':
169                         /* For compatibility. */
170                         break;
171                 case 'h':
172                         if (override->co_host != NULL)
173                                 free(override->co_host);
174                         override->co_host = xstrdup(optarg);
175                         break;
176                 case 'i':
177                         pattlist_add(override->co_accepts, optarg);
178                         break;
179                 case 'k':
180                         override->co_options |= CO_KEEPBADFILES;
181                         overridemask |= CO_KEEPBADFILES;
182                         break;
183                 case 'l':
184                         lockfile = optarg;
185                         lflag = 1;
186                         lockfd = open(lockfile,
187                             O_CREAT | O_WRONLY | O_TRUNC, 0700);
188                         if (lockfd != -1) {
189                                 error = flock(lockfd, LOCK_EX | LOCK_NB);
190                                 if (error == -1 && errno == EWOULDBLOCK) {
191                                         if (lockfd != -1)
192                                                 close(lockfd);
193                                         lprintf(-1, "\"%s\" is already locked "
194                                             "by another process\n", lockfile);
195                                         return (1);
196                                 }
197                         }
198                         if (lockfd == -1 || error == -1) {
199                                 if (lockfd != -1)
200                                         close(lockfd);
201                                 lprintf(-1, "Error locking \"%s\": %s\n",
202                                     lockfile, strerror(errno));
203                                 return (1);
204                         }
205                         lock = stream_open_fd(lockfd,
206                             NULL, stream_write_fd, NULL);
207                         (void)stream_printf(lock, "%10ld\n", (long)getpid());
208                         stream_close(lock);
209                         break;
210                 case 'L':
211                         error = asciitoint(optarg, &verbose, 0);
212                         if (error) {
213                                 lprintf(-1, "Invalid verbosity\n");
214                                 usage(argv0);
215                                 return (1);
216                         }
217                         break;
218                 case 'p':
219                         /* Use specified server port. */
220                         error = asciitoint(optarg, &port, 0);
221                         if (error) {
222                                 lprintf(-1, "Invalid server port\n");
223                                 usage(argv0);
224                                 return (1);
225                         }
226                         if (port <= 0 || port >= 65536) {
227                                 lprintf(-1, "Invalid port %d\n", port);
228                                 return (1);
229                         }
230                         if (port < 1024) {
231                                 lprintf(-1, "Reserved port %d not permitted\n",
232                                     port);
233                                 return (1);
234                         }
235                         break;
236                 case 'P':
237                         /* For compatibility. */
238                         if (strcmp(optarg, "m") != 0) {
239                                 lprintf(-1,
240                                     "Client only supports multiplexed mode\n");
241                                 return (1);
242                         }
243                         break;
244                 case 'r':
245                         error = asciitoint(optarg, &retries, 0);
246                         if (error || retries < 0) {
247                                 lprintf(-1, "Invalid retry limit\n");
248                                 usage(argv0);
249                                 return (1);
250                         }
251                         break;
252                 case 's':
253                         override->co_options |= CO_TRUSTSTATUSFILE;
254                         overridemask |= CO_TRUSTSTATUSFILE;
255                         break;
256                 case 'v':
257                         lprintf(0, "CVSup client written in C\n");
258                         lprintf(0, "Software version: %s\n", PROTO_SWVER);
259                         lprintf(0, "Protocol version: %d.%d\n",
260                             PROTO_MAJ, PROTO_MIN);
261                         lprintf(0, "http://mu.org/~mux/csup.html\n");
262                         return (0);
263                         break;
264                 case 'z':
265                         /* Force compression on all collections. */
266                         override->co_options |= CO_COMPRESS;
267                         overridemask |= CO_COMPRESS;
268                         break;
269                 case 'Z':
270                         /* Disables compression on all collections. */
271                         override->co_options &= ~CO_COMPRESS;
272                         overridemask &= ~CO_COMPRESS;
273                         break;
274                 case '?':
275                 default:
276                         usage(argv0);
277                         return (1);
278                 }
279         }
280
281         argc -= optind;
282         argv += optind;
283
284         if (argc < 1) {
285                 usage(argv0);
286                 return (1);
287         }
288
289         file = argv[0];
290         lprintf(2, "Parsing supfile \"%s\"\n", file);
291         config = config_init(file, override, overridemask);
292         coll_free(override);
293         if (config == NULL)
294                 return (1);
295
296         if (config_checkcolls(config) == 0) {
297                 lprintf(-1, "No collections selected\n");
298                 return (1);
299         }
300
301         if (laddr != NULL) {
302                 config->laddr = laddr;
303                 config->laddrlen = laddrlen;
304         }
305         config->deletelim = deletelim;
306         lprintf(2, "Connecting to %s\n", config->host);
307
308         i = 0;
309         fattr_init();   /* Initialize the fattr API. */
310         timer = bt_new(300, 7200, 2.0, 0.1);
311         for (;;) {
312                 status = proto_connect(config, family, port);
313                 if (status == STATUS_SUCCESS) {
314                         status = proto_run(config);
315                         if (status != STATUS_TRANSIENTFAILURE)
316                                 break;
317                 }
318                 if (retries >= 0 && i >= retries)
319                         break;
320                 nexttry = time(0) + bt_get(timer);
321                 localtime_r(&nexttry, &tm);
322                 lprintf(1, "Will retry at %02d:%02d:%02d\n",
323                     tm.tm_hour, tm.tm_min, tm.tm_sec);
324                 bt_pause(timer);
325                 lprintf(1, "Retrying\n");
326                 i++;
327         }
328         bt_free(timer);
329         fattr_fini();
330         if (lflag) {
331                 unlink(lockfile);
332                 flock(lockfd, LOCK_UN);
333                 close(lockfd);
334         }
335         config_free(config);
336         if (status != STATUS_SUCCESS)
337                 return (1);
338         return (0);
339 }