2 * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
29 #include <sys/types.h>
47 static int config_parse_refusefiles(struct coll *);
48 static int config_parse_refusefile(struct coll *, char *);
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;
58 static const char *cfgfile;
61 * Extract all the configuration information from the config
62 * file and some command line parameters.
65 config_init(const char *file, struct coll *override, int overridemask)
67 struct config *config;
74 config = xmalloc(sizeof(struct config));
75 memset(config, 0, sizeof(struct config));
78 defaults = coll_new(NULL);
79 /* Set the default umask. */
82 defaults->co_umask = mask;
84 ovmask = overridemask;
86 /* Extract a list of collections from the configuration file. */
87 cur_coll = coll_new(defaults);
88 yyin = fopen(file, "r");
90 lprintf(-1, "Cannot open \"%s\": %s\n", file, strerror(errno));
99 memcpy(&config->colls, &colls, sizeof(colls));
100 if (STAILQ_EMPTY(&config->colls)) {
101 lprintf(-1, "Empty supfile\n");
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);
114 * If prefix is not an absolute pathname, it is
115 * interpreted relative to base.
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,
123 xasprintf(&prefix, "%s%s", coll->co_base,
125 free(coll->co_prefix);
126 coll->co_prefix = prefix;
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;
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);
145 config->host = STAILQ_FIRST(&config->colls)->co_host;
155 config_checkcolls(struct config *config)
160 int error, numvalid, ret;
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,
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,
184 config_parse_refusefiles(struct coll *coll)
186 char *collstem, *suffix, *supdir, *path;
189 if (coll->co_colldir[0] == '/')
190 supdir = xstrdup(coll->co_colldir);
192 xasprintf(&supdir, "%s/%s", coll->co_base, coll->co_colldir);
194 /* First, the global refuse file that applies to all collections. */
195 xasprintf(&path, "%s/refuse", supdir);
196 error = config_parse_refusefile(coll, path);
203 /* Next the per-collection refuse files that applies to all release/tag
205 xasprintf(&collstem, "%s/%s/refuse", supdir, coll->co_name);
207 error = config_parse_refusefile(coll, collstem);
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);
218 error = config_parse_refusefile(coll, path);
226 * Parses a "refuse" file, and records the relevant information in
227 * coll->co_refusals. If the file does not exist, it is silently
231 config_parse_refusefile(struct coll *coll, char *path)
234 char *cp, *line, *pat;
236 rd = stream_open_file(path, O_RDONLY);
239 while ((line = stream_getln(rd, NULL)) != NULL) {
242 /* Trim leading whitespace. */
243 pat += strspn(pat, " \t");
246 cp = strpbrk(pat, " \t");
249 pattlist_add(coll->co_refusals, pat);
255 if (!stream_eof(rd)) {
257 lprintf(-1, "Read failure from \"%s\": %s\n", path,
266 config_free(struct config *config)
270 while (!STAILQ_EMPTY(&config->colls)) {
271 coll = STAILQ_FIRST(&config->colls);
272 STAILQ_REMOVE_HEAD(&config->colls, co_next);
275 if (config->server != NULL)
276 stream_close(config->server);
277 if (config->laddr != NULL)
282 /* Create a new collection, inheriting options from the default collection. */
284 coll_new(struct coll *def)
288 new = xmalloc(sizeof(struct coll));
289 memset(new, 0, sizeof(struct coll));
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);
308 new->co_tag = xstrdup(".");
309 new->co_date = xstrdup(".");
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;
319 coll_override(struct coll *coll, struct coll *from, int mask)
322 int newoptions, oldoptions;
324 newoptions = from->co_options & mask;
325 oldoptions = coll->co_options & (CO_MASK & ~mask);
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);
332 if (from->co_host != NULL) {
333 if (coll->co_host != NULL)
335 coll->co_host = xstrdup(from->co_host);
337 if (from->co_base != NULL) {
338 if (coll->co_base != NULL)
340 coll->co_base = xstrdup(from->co_base);
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);
349 if (newoptions & CO_CHECKOUTMODE) {
350 if (from->co_tag != NULL) {
351 if (coll->co_tag != NULL)
353 coll->co_tag = xstrdup(from->co_tag);
355 if (from->co_date != NULL) {
356 if (coll->co_date != NULL)
358 coll->co_date = xstrdup(from->co_date);
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);
366 for (i = 0; i < pattlist_size(from->co_accepts); i++) {
367 pattlist_add(coll->co_accepts,
368 pattlist_get(from->co_accepts, i));
370 for (i = 0; i < pattlist_size(from->co_refusals); i++) {
371 pattlist_add(coll->co_refusals,
372 pattlist_get(from->co_refusals, i));
374 coll->co_options = oldoptions | newoptions;
378 coll_statussuffix(struct coll *coll)
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)
390 if (coll->co_release != NULL) {
391 if (coll->co_options & CO_CHECKOUTMODE) {
392 xasprintf(&suffix, ".%s:%s",
393 coll->co_release, tag);
395 xasprintf(&suffix, ".%s", coll->co_release);
397 } else if (coll->co_options & CO_CHECKOUTMODE) {
398 xasprintf(&suffix, ":%s", tag);
406 coll_statuspath(struct coll *coll)
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);
416 xasprintf(&path, "%s/%s/%s/checkouts%s", coll->co_base,
417 coll->co_colldir, coll->co_name, suffix);
419 if (coll->co_colldir[0] == '/')
420 xasprintf(&path, "%s/%s/checkouts", coll->co_colldir,
423 xasprintf(&path, "%s/%s/%s/checkouts", coll->co_base,
424 coll->co_colldir, coll->co_name);
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);
442 if (cur_coll->co_host == NULL) {
443 lprintf(-1, "Host not specified for collection "
444 "\"%s\"\n", cur_coll->co_name);
447 if (!(cur_coll->co_options & CO_CHECKOUTMODE)) {
448 lprintf(-1, "Client only supports checkout mode\n");
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");
459 STAILQ_INSERT_TAIL(&colls, cur_coll, co_next);
460 cur_coll = coll_new(defaults);
464 coll_free(struct coll *coll)
469 if (coll->co_host != NULL)
471 if (coll->co_base != NULL)
473 if (coll->co_date != NULL)
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)
481 if (coll->co_cvsroot != NULL)
482 free(coll->co_cvsroot);
483 if (coll->co_name != NULL)
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);
502 coll_setopt(int opt, char *value)
510 if (coll->co_host != NULL)
512 coll->co_host = value;
515 if (coll->co_base != NULL)
517 coll->co_base = value;
520 if (coll->co_date != NULL)
522 coll->co_date = value;
523 coll->co_options |= CO_CHECKOUTMODE;
526 if (coll->co_prefix != NULL)
527 free(coll->co_prefix);
528 coll->co_prefix = value;
531 if (coll->co_release != NULL)
532 free(coll->co_release);
533 coll->co_release = value;
536 if (coll->co_tag != NULL)
538 coll->co_tag = value;
539 coll->co_options |= CO_CHECKOUTMODE;
542 if (strchr(value, '/') != NULL) {
543 lprintf(-1, "Parse error in \"%s\": \"list\" suffix "
544 "must not contain slashes\n", cfgfile);
547 if (coll->co_listsuffix != NULL)
548 free(coll->co_listsuffix);
549 coll->co_listsuffix = value;
552 error = asciitoint(value, &mask, 8);
555 lprintf(-1, "Parse error in \"%s\": Invalid "
556 "umask value\n", cfgfile);
559 coll->co_umask = mask;
561 case PT_USE_REL_SUFFIX:
562 coll->co_options |= CO_USERELSUFFIX;
565 coll->co_options |= CO_DELETE | CO_EXACTRCS;
568 coll->co_options |= CO_COMPRESS;
571 coll->co_options |= CO_NORSYNC;
576 /* Set "coll" as being the default collection. */
583 cur_coll = coll_new(defaults);