]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/csup/config.c
This commit was generated by cvs2svn to compensate for changes in r172597,
[FreeBSD/FreeBSD.git] / contrib / csup / config.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/types.h>
30 #include <sys/stat.h>
31
32 #include <errno.h>
33 #include <fcntl.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38
39 #include "config.h"
40 #include "globtree.h"
41 #include "keyword.h"
42 #include "misc.h"
43 #include "parse.h"
44 #include "stream.h"
45 #include "token.h"
46
47 static int               config_parse_refusefiles(struct coll *);
48 static int               config_parse_refusefile(struct coll *, char *);
49
50 extern FILE *yyin;
51
52 /* These are globals because I can't think of a better way with yacc. */
53 static STAILQ_HEAD(, coll) colls;
54 static struct coll *cur_coll;
55 static struct coll *defaults;
56 static struct coll *ovcoll;
57 static int ovmask;
58 static const char *cfgfile;
59
60 /*
61  * Extract all the configuration information from the config
62  * file and some command line parameters.
63  */
64 struct config *
65 config_init(const char *file, struct coll *override, int overridemask)
66 {
67         struct config *config;
68         struct coll *coll;
69         size_t slen;
70         char *prefix;
71         int error;
72         mode_t mask;
73
74         config = xmalloc(sizeof(struct config));
75         memset(config, 0, sizeof(struct config));
76         STAILQ_INIT(&colls);
77
78         defaults = coll_new(NULL);
79         /* Set the default umask. */
80         mask = umask(0);
81         umask(mask);
82         defaults->co_umask = mask;
83         ovcoll = override;
84         ovmask = overridemask;
85
86         /* Extract a list of collections from the configuration file. */
87         cur_coll = coll_new(defaults);
88         yyin = fopen(file, "r");
89         if (yyin == NULL) {
90                 lprintf(-1, "Cannot open \"%s\": %s\n", file, strerror(errno));
91                 goto bad;
92         }
93         cfgfile = file;
94         error = yyparse();
95         fclose(yyin);
96         if (error)
97                 goto bad;
98
99         memcpy(&config->colls, &colls, sizeof(colls));
100         if (STAILQ_EMPTY(&config->colls)) {
101                 lprintf(-1, "Empty supfile\n");
102                 goto bad;
103         }
104
105         /* Fixup the list of collections. */
106         STAILQ_FOREACH(coll, &config->colls, co_next) {
107                 if (coll->co_base == NULL)
108                         coll->co_base = xstrdup("/usr/local/etc/cvsup");
109                 if (coll->co_colldir == NULL)
110                         coll->co_colldir = "sup";
111                 if (coll->co_prefix == NULL) {
112                         coll->co_prefix = xstrdup(coll->co_base);
113                 /*
114                  * If prefix is not an absolute pathname, it is
115                  * interpreted relative to base.
116                  */
117                 } else if (coll->co_prefix[0] != '/') {
118                         slen = strlen(coll->co_base);
119                         if (slen > 0 && coll->co_base[slen - 1] != '/')
120                                 xasprintf(&prefix, "%s/%s", coll->co_base,
121                                     coll->co_prefix);
122                         else
123                                 xasprintf(&prefix, "%s%s", coll->co_base,
124                                     coll->co_prefix);
125                         free(coll->co_prefix);
126                         coll->co_prefix = prefix;
127                 }
128                 coll->co_prefixlen = strlen(coll->co_prefix);
129                 /* Determine whether to checksum RCS files or not. */
130                 if (coll->co_options & CO_EXACTRCS)
131                         coll->co_options |= CO_CHECKRCS;
132                 else
133                         coll->co_options &= ~CO_CHECKRCS;
134                 /* In recent versions, we always try to set the file modes. */
135                 coll->co_options |= CO_SETMODE;
136                 /* XXX We don't support the rsync updating algorithm yet. */
137                 coll->co_options |= CO_NORSYNC;
138                 error = config_parse_refusefiles(coll);
139                 if (error)
140                         goto bad;
141         }
142
143         coll_free(cur_coll);
144         coll_free(defaults);
145         config->host = STAILQ_FIRST(&config->colls)->co_host;
146         return (config);
147 bad:
148         coll_free(cur_coll);
149         coll_free(defaults);
150         config_free(config);
151         return (NULL);
152 }
153
154 int
155 config_checkcolls(struct config *config)
156 {
157         char linkname[4];
158         struct stat sb;
159         struct coll *coll;
160         int error, numvalid, ret;
161
162         numvalid = 0;
163         STAILQ_FOREACH(coll, &config->colls, co_next) {
164                 error = stat(coll->co_prefix, &sb);
165                 if (error || !S_ISDIR(sb.st_mode)) {
166                         /* Skip this collection, and warn about it unless its
167                            prefix is a symbolic link pointing to "SKIP". */
168                         coll->co_options |= CO_SKIP;
169                         ret = readlink(coll->co_prefix, linkname,
170                             sizeof(linkname));
171                         if (ret != 4 || memcmp(linkname, "SKIP", 4) != 0) {
172                                 lprintf(-1,"Nonexistent prefix \"%s\" for "
173                                     "%s/%s\n", coll->co_prefix, coll->co_name,
174                                     coll->co_release);
175                         }
176                         continue;
177                 }
178                 numvalid++;
179         }
180         return (numvalid);
181 }
182
183 static int
184 config_parse_refusefiles(struct coll *coll)
185 {
186         char *collstem, *suffix, *supdir, *path;
187         int error;
188
189         if (coll->co_colldir[0] == '/')
190                 supdir = xstrdup(coll->co_colldir);
191         else
192                 xasprintf(&supdir, "%s/%s", coll->co_base, coll->co_colldir);
193
194         /* First, the global refuse file that applies to all collections. */
195         xasprintf(&path, "%s/refuse", supdir);
196         error = config_parse_refusefile(coll, path);
197         free(path);
198         if (error) {
199                 free(supdir);
200                 return (error);
201         }
202
203         /* Next the per-collection refuse files that applies to all release/tag
204            combinations. */
205         xasprintf(&collstem, "%s/%s/refuse", supdir, coll->co_name);
206         free(supdir);
207         error = config_parse_refusefile(coll, collstem);
208         if (error) {
209                 free(collstem);
210                 return (error);
211         }
212
213         /* Finally, the per-release and per-tag refuse file. */
214         suffix = coll_statussuffix(coll);
215         if (suffix != NULL) {
216                 xasprintf(&path, "%s%s", collstem, suffix);
217                 free(suffix);
218                 error = config_parse_refusefile(coll, path);
219                 free(path);
220         }
221         free(collstem);
222         return (error);
223 }
224
225 /*
226  * Parses a "refuse" file, and records the relevant information in
227  * coll->co_refusals.  If the file does not exist, it is silently
228  * ignored.
229  */
230 static int
231 config_parse_refusefile(struct coll *coll, char *path)
232 {
233         struct stream *rd;
234         char *cp, *line, *pat;
235
236         rd = stream_open_file(path, O_RDONLY);
237         if (rd == NULL)
238                 return (0);
239         while ((line = stream_getln(rd, NULL)) != NULL) {
240                 pat = line;
241                 for (;;) {
242                         /* Trim leading whitespace. */
243                         pat += strspn(pat, " \t");
244                         if (pat[0] == '\0')
245                                 break;
246                         cp = strpbrk(pat, " \t");
247                         if (cp != NULL)
248                                 *cp = '\0';
249                         pattlist_add(coll->co_refusals, pat);
250                         if (cp == NULL)
251                                 break;
252                         pat = cp + 1;
253                 }
254         }
255         if (!stream_eof(rd)) {
256                 stream_close(rd);
257                 lprintf(-1, "Read failure from \"%s\": %s\n", path,
258                     strerror(errno));
259                 return (-1);
260         }
261         stream_close(rd);
262         return (0);
263 }
264
265 void
266 config_free(struct config *config)
267 {
268         struct coll *coll;
269
270         while (!STAILQ_EMPTY(&config->colls)) {
271                 coll = STAILQ_FIRST(&config->colls);
272                 STAILQ_REMOVE_HEAD(&config->colls, co_next);
273                 coll_free(coll);
274         }
275         if (config->server != NULL)
276                 stream_close(config->server);
277         if (config->laddr != NULL)
278                 free(config->laddr);
279         free(config);
280 }
281
282 /* Create a new collection, inheriting options from the default collection. */
283 struct coll *
284 coll_new(struct coll *def)
285 {
286         struct coll *new;
287
288         new = xmalloc(sizeof(struct coll));
289         memset(new, 0, sizeof(struct coll));
290         if (def != NULL) {
291                 new->co_options = def->co_options;
292                 new->co_umask = def->co_umask;
293                 if (def->co_host != NULL)
294                         new->co_host = xstrdup(def->co_host);
295                 if (def->co_base != NULL)
296                         new->co_base = xstrdup(def->co_base);
297                 if (def->co_date != NULL)
298                         new->co_date = xstrdup(def->co_date);
299                 if (def->co_prefix != NULL)
300                         new->co_prefix = xstrdup(def->co_prefix);
301                 if (def->co_release != NULL)
302                         new->co_release = xstrdup(def->co_release);
303                 if (def->co_tag != NULL)
304                         new->co_tag = xstrdup(def->co_tag);
305                 if (def->co_listsuffix != NULL)
306                         new->co_listsuffix = xstrdup(def->co_listsuffix);
307         } else {
308                 new->co_tag = xstrdup(".");
309                 new->co_date = xstrdup(".");
310         }
311         new->co_keyword = keyword_new();
312         new->co_accepts = pattlist_new();
313         new->co_refusals = pattlist_new();
314         new->co_attrignore = FA_DEV | FA_INODE;
315         return (new);
316 }
317
318 void
319 coll_override(struct coll *coll, struct coll *from, int mask)
320 {
321         size_t i;
322         int newoptions, oldoptions;
323
324         newoptions = from->co_options & mask;
325         oldoptions = coll->co_options & (CO_MASK & ~mask);
326
327         if (from->co_release != NULL) {
328                 if (coll->co_release != NULL)
329                         free(coll->co_release);
330                 coll->co_release = xstrdup(from->co_release);
331         }
332         if (from->co_host != NULL) {
333                 if (coll->co_host != NULL)
334                         free(coll->co_host);
335                 coll->co_host = xstrdup(from->co_host);
336         }
337         if (from->co_base != NULL) {
338                 if (coll->co_base != NULL)
339                         free(coll->co_base);
340                 coll->co_base = xstrdup(from->co_base);
341         }
342         if (from->co_colldir != NULL)
343                 coll->co_colldir = from->co_colldir;
344         if (from->co_prefix != NULL) {
345                 if (coll->co_prefix != NULL)
346                         free(coll->co_prefix);
347                 coll->co_prefix = xstrdup(from->co_prefix);
348         }
349         if (newoptions & CO_CHECKOUTMODE) {
350                 if (from->co_tag != NULL) {
351                         if (coll->co_tag != NULL)
352                                 free(coll->co_tag);
353                         coll->co_tag = xstrdup(from->co_tag);
354                 }
355                 if (from->co_date != NULL) {
356                         if (coll->co_date != NULL)
357                                 free(coll->co_date);
358                         coll->co_date = xstrdup(from->co_date);
359                 }
360         }
361         if (from->co_listsuffix != NULL) {
362                 if (coll->co_listsuffix != NULL)
363                         free(coll->co_listsuffix);
364                 coll->co_listsuffix = xstrdup(from->co_listsuffix);
365         }
366         for (i = 0; i < pattlist_size(from->co_accepts); i++) {
367                 pattlist_add(coll->co_accepts,
368                     pattlist_get(from->co_accepts, i));
369         }
370         for (i = 0; i < pattlist_size(from->co_refusals); i++) {
371                 pattlist_add(coll->co_refusals,
372                     pattlist_get(from->co_refusals, i));
373         }
374         coll->co_options = oldoptions | newoptions;
375 }
376
377 char *
378 coll_statussuffix(struct coll *coll)
379 {
380         const char *tag;
381         char *suffix;
382
383         if (coll->co_listsuffix != NULL) {
384                 xasprintf(&suffix, ".%s", coll->co_listsuffix);
385         } else if (coll->co_options & CO_USERELSUFFIX) {
386                 if (coll->co_tag == NULL)
387                         tag = ".";
388                 else
389                         tag = coll->co_tag;
390                 if (coll->co_release != NULL) {
391                         if (coll->co_options & CO_CHECKOUTMODE) {
392                                 xasprintf(&suffix, ".%s:%s",
393                                     coll->co_release, tag);
394                         } else {
395                                 xasprintf(&suffix, ".%s", coll->co_release);
396                         }
397                 } else if (coll->co_options & CO_CHECKOUTMODE) {
398                         xasprintf(&suffix, ":%s", tag);
399                 }
400         } else
401                 suffix = NULL;
402         return (suffix);
403 }
404
405 char *
406 coll_statuspath(struct coll *coll)
407 {
408         char *path, *suffix;
409
410         suffix = coll_statussuffix(coll);
411         if (suffix != NULL) {
412                 if (coll->co_colldir[0] == '/')
413                         xasprintf(&path, "%s/%s/checkouts%s", coll->co_colldir,
414                             coll->co_name, suffix);
415                 else
416                         xasprintf(&path, "%s/%s/%s/checkouts%s", coll->co_base,
417                             coll->co_colldir, coll->co_name, suffix);
418         } else {
419                 if (coll->co_colldir[0] == '/')
420                         xasprintf(&path, "%s/%s/checkouts", coll->co_colldir,
421                             coll->co_name);
422                 else
423                         xasprintf(&path, "%s/%s/%s/checkouts", coll->co_base,
424                             coll->co_colldir, coll->co_name);
425         }
426         free(suffix);
427         return (path);
428 }
429
430 void
431 coll_add(char *name)
432 {
433         struct coll *coll;
434
435         cur_coll->co_name = name;
436         coll_override(cur_coll, ovcoll, ovmask);
437         if (cur_coll->co_release == NULL) {
438                 lprintf(-1, "Release not specified for collection "
439                     "\"%s\"\n", cur_coll->co_name);
440                 exit(1);
441         }
442         if (cur_coll->co_host == NULL) {
443                 lprintf(-1, "Host not specified for collection "
444                     "\"%s\"\n", cur_coll->co_name);
445                 exit(1);
446         }
447         if (!(cur_coll->co_options & CO_CHECKOUTMODE)) {
448                 lprintf(-1, "Client only supports checkout mode\n");
449                 exit(1);
450         }
451         if (!STAILQ_EMPTY(&colls)) {
452                 coll = STAILQ_LAST(&colls, coll, co_next);
453                 if (strcmp(coll->co_host, cur_coll->co_host) != 0) {
454                         lprintf(-1, "All \"host\" fields in the supfile "
455                             "must be the same\n");
456                         exit(1);
457                 }
458         }
459         STAILQ_INSERT_TAIL(&colls, cur_coll, co_next);
460         cur_coll = coll_new(defaults);
461 }
462
463 void
464 coll_free(struct coll *coll)
465 {
466
467         if (coll == NULL)
468                 return;
469         if (coll->co_host != NULL)
470                 free(coll->co_host);
471         if (coll->co_base != NULL)
472                 free(coll->co_base);
473         if (coll->co_date != NULL)
474                 free(coll->co_date);
475         if (coll->co_prefix != NULL)
476                 free(coll->co_prefix);
477         if (coll->co_release != NULL)
478                 free(coll->co_release);
479         if (coll->co_tag != NULL)
480                 free(coll->co_tag);
481         if (coll->co_cvsroot != NULL)
482                 free(coll->co_cvsroot);
483         if (coll->co_name != NULL)
484                 free(coll->co_name);
485         if (coll->co_listsuffix != NULL)
486                 free(coll->co_listsuffix);
487         keyword_free(coll->co_keyword);
488         if (coll->co_dirfilter != NULL)
489                 globtree_free(coll->co_dirfilter);
490         if (coll->co_dirfilter != NULL)
491                 globtree_free(coll->co_filefilter);
492         if (coll->co_norsync != NULL)
493                 globtree_free(coll->co_norsync);
494         if (coll->co_accepts != NULL)
495                 pattlist_free(coll->co_accepts);
496         if (coll->co_refusals != NULL)
497                 pattlist_free(coll->co_refusals);
498         free(coll);
499 }
500
501 void
502 coll_setopt(int opt, char *value)
503 {
504         struct coll *coll;
505         int error, mask;
506
507         coll = cur_coll;
508         switch (opt) {
509         case PT_HOST:
510                 if (coll->co_host != NULL)
511                         free(coll->co_host);
512                 coll->co_host = value;
513                 break;
514         case PT_BASE:
515                 if (coll->co_base != NULL)
516                         free(coll->co_base);
517                 coll->co_base = value;
518                 break;
519         case PT_DATE:
520                 if (coll->co_date != NULL)
521                         free(coll->co_date);
522                 coll->co_date = value;
523                 coll->co_options |= CO_CHECKOUTMODE;
524                 break;
525         case PT_PREFIX:
526                 if (coll->co_prefix != NULL)
527                         free(coll->co_prefix);
528                 coll->co_prefix = value;
529                 break;
530         case PT_RELEASE:
531                 if (coll->co_release != NULL)
532                         free(coll->co_release);
533                 coll->co_release = value;
534                 break;
535         case PT_TAG:
536                 if (coll->co_tag != NULL)
537                         free(coll->co_tag);
538                 coll->co_tag = value;
539                 coll->co_options |= CO_CHECKOUTMODE;
540                 break;
541         case PT_LIST:
542                 if (strchr(value, '/') != NULL) {
543                         lprintf(-1, "Parse error in \"%s\": \"list\" suffix "
544                             "must not contain slashes\n", cfgfile);
545                         exit(1);
546                 }
547                 if (coll->co_listsuffix != NULL)
548                         free(coll->co_listsuffix);
549                 coll->co_listsuffix = value;
550                 break;
551         case PT_UMASK:
552                 error = asciitoint(value, &mask, 8);
553                 free(value);
554                 if (error) {
555                         lprintf(-1, "Parse error in \"%s\": Invalid "
556                             "umask value\n", cfgfile);
557                         exit(1);
558                 }
559                 coll->co_umask = mask;
560                 break;
561         case PT_USE_REL_SUFFIX:
562                 coll->co_options |= CO_USERELSUFFIX;
563                 break;
564         case PT_DELETE:
565                 coll->co_options |= CO_DELETE | CO_EXACTRCS;
566                 break;
567         case PT_COMPRESS:
568                 coll->co_options |= CO_COMPRESS;
569                 break;
570         case PT_NORSYNC:
571                 coll->co_options |= CO_NORSYNC;
572                 break;
573         }
574 }
575
576 /* Set "coll" as being the default collection. */
577 void
578 coll_setdef(void)
579 {
580
581         coll_free(defaults);
582         defaults = cur_coll;
583         cur_coll = coll_new(defaults);
584 }