PageRenderTime 50ms CodeModel.GetById 18ms app.highlight 27ms RepoModel.GetById 1ms app.codeStats 0ms

/usr.bin/csup/config.c

https://bitbucket.org/freebsd/freebsd-head/
C | 579 lines | 483 code | 41 blank | 55 comment | 171 complexity | 1282a2d41de7a9488c17933047e7e0b7 MD5 | raw file
  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
 47static int		 config_parse_refusefiles(struct coll *);
 48static int		 config_parse_refusefile(struct coll *, char *);
 49
 50extern FILE *yyin;
 51
 52/* These are globals because I can't think of a better way with yacc. */
 53static STAILQ_HEAD(, coll) colls;
 54static struct coll *cur_coll;
 55static struct coll *defaults;
 56static struct coll *ovcoll;
 57static int ovmask;
 58static const char *cfgfile;
 59
 60/*
 61 * Extract all the configuration information from the config
 62 * file and some command line parameters.
 63 */
 64struct config *
 65config_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		coll->co_options |= CO_NORSYNC;
137		error = config_parse_refusefiles(coll);
138		if (error)
139			goto bad;
140	}
141
142	coll_free(cur_coll);
143	coll_free(defaults);
144	config->host = STAILQ_FIRST(&config->colls)->co_host;
145	return (config);
146bad:
147	coll_free(cur_coll);
148	coll_free(defaults);
149	config_free(config);
150	return (NULL);
151}
152
153int
154config_checkcolls(struct config *config)
155{
156	char linkname[4];
157	struct stat sb;
158	struct coll *coll;
159	int error, numvalid, ret;
160
161	numvalid = 0;
162	STAILQ_FOREACH(coll, &config->colls, co_next) {
163		error = stat(coll->co_prefix, &sb);
164		if (error || !S_ISDIR(sb.st_mode)) {
165			/* Skip this collection, and warn about it unless its
166			   prefix is a symbolic link pointing to "SKIP". */
167			coll->co_options |= CO_SKIP;
168			ret = readlink(coll->co_prefix, linkname,
169			    sizeof(linkname));
170			if (ret != 4 || memcmp(linkname, "SKIP", 4) != 0) {
171				lprintf(-1,"Nonexistent prefix \"%s\" for "
172				    "%s/%s\n", coll->co_prefix, coll->co_name,
173				    coll->co_release);
174			}
175			continue;
176		}
177		numvalid++;
178	}
179	return (numvalid);
180}
181
182static int
183config_parse_refusefiles(struct coll *coll)
184{
185	char *collstem, *suffix, *supdir, *path;
186	int error;
187
188	if (coll->co_colldir[0] == '/')
189		supdir = xstrdup(coll->co_colldir);
190	else
191		xasprintf(&supdir, "%s/%s", coll->co_base, coll->co_colldir);
192
193	/* First, the global refuse file that applies to all collections. */
194	xasprintf(&path, "%s/refuse", supdir);
195	error = config_parse_refusefile(coll, path);
196	free(path);
197	if (error) {
198		free(supdir);
199		return (error);
200	}
201
202	/* Next the per-collection refuse files that applies to all release/tag
203	   combinations. */
204	xasprintf(&collstem, "%s/%s/refuse", supdir, coll->co_name);
205	free(supdir);
206	error = config_parse_refusefile(coll, collstem);
207	if (error) {
208		free(collstem);
209		return (error);
210	}
211
212	/* Finally, the per-release and per-tag refuse file. */
213	suffix = coll_statussuffix(coll);
214	if (suffix != NULL) {
215		xasprintf(&path, "%s%s", collstem, suffix);
216		free(suffix);
217		error = config_parse_refusefile(coll, path);
218		free(path);
219	}
220	free(collstem);
221	return (error);
222}
223
224/*
225 * Parses a "refuse" file, and records the relevant information in
226 * coll->co_refusals.  If the file does not exist, it is silently
227 * ignored.
228 */
229static int
230config_parse_refusefile(struct coll *coll, char *path)
231{
232	struct stream *rd;
233	char *cp, *line, *pat;
234
235	rd = stream_open_file(path, O_RDONLY);
236	if (rd == NULL)
237		return (0);
238	while ((line = stream_getln(rd, NULL)) != NULL) {
239		pat = line;
240		for (;;) {
241			/* Trim leading whitespace. */
242			pat += strspn(pat, " \t");
243			if (pat[0] == '\0')
244				break;
245			cp = strpbrk(pat, " \t");
246			if (cp != NULL)
247				*cp = '\0';
248			pattlist_add(coll->co_refusals, pat);
249			if (cp == NULL)
250				break;
251			pat = cp + 1;
252		}
253	}
254	if (!stream_eof(rd)) {
255		stream_close(rd);
256		lprintf(-1, "Read failure from \"%s\": %s\n", path,
257		    strerror(errno));
258		return (-1);
259	}
260	stream_close(rd);
261	return (0);
262}
263
264void
265config_free(struct config *config)
266{
267	struct coll *coll;
268
269	while (!STAILQ_EMPTY(&config->colls)) {
270		coll = STAILQ_FIRST(&config->colls);
271		STAILQ_REMOVE_HEAD(&config->colls, co_next);
272		coll_free(coll);
273	}
274	if (config->server != NULL)
275		stream_close(config->server);
276	if (config->laddr != NULL)
277		free(config->laddr);
278	free(config);
279}
280
281/* Create a new collection, inheriting options from the default collection. */
282struct coll *
283coll_new(struct coll *def)
284{
285	struct coll *new;
286
287	new = xmalloc(sizeof(struct coll));
288	memset(new, 0, sizeof(struct coll));
289	if (def != NULL) {
290		new->co_options = def->co_options;
291		new->co_umask = def->co_umask;
292		if (def->co_host != NULL)
293			new->co_host = xstrdup(def->co_host);
294		if (def->co_base != NULL)
295			new->co_base = xstrdup(def->co_base);
296		if (def->co_date != NULL)
297			new->co_date = xstrdup(def->co_date);
298		if (def->co_prefix != NULL)
299			new->co_prefix = xstrdup(def->co_prefix);
300		if (def->co_release != NULL)
301			new->co_release = xstrdup(def->co_release);
302		if (def->co_tag != NULL)
303			new->co_tag = xstrdup(def->co_tag);
304		if (def->co_listsuffix != NULL)
305			new->co_listsuffix = xstrdup(def->co_listsuffix);
306	} else {
307		new->co_tag = xstrdup(".");
308		new->co_date = xstrdup(".");
309	}
310	new->co_keyword = keyword_new();
311	new->co_accepts = pattlist_new();
312	new->co_refusals = pattlist_new();
313	new->co_attrignore = FA_DEV | FA_INODE;
314	return (new);
315}
316
317void
318coll_override(struct coll *coll, struct coll *from, int mask)
319{
320	size_t i;
321	int newoptions, oldoptions;
322
323	newoptions = from->co_options & mask;
324	oldoptions = coll->co_options & (CO_MASK & ~mask);
325
326	if (from->co_release != NULL) {
327		if (coll->co_release != NULL)
328			free(coll->co_release);
329		coll->co_release = xstrdup(from->co_release);
330	}
331	if (from->co_host != NULL) {
332		if (coll->co_host != NULL)
333			free(coll->co_host);
334		coll->co_host = xstrdup(from->co_host);
335	}
336	if (from->co_base != NULL) {
337		if (coll->co_base != NULL)
338			free(coll->co_base);
339		coll->co_base = xstrdup(from->co_base);
340	}
341	if (from->co_colldir != NULL)
342		coll->co_colldir = from->co_colldir;
343	if (from->co_prefix != NULL) {
344		if (coll->co_prefix != NULL)
345			free(coll->co_prefix);
346		coll->co_prefix = xstrdup(from->co_prefix);
347	}
348	if (newoptions & CO_CHECKOUTMODE) {
349		if (from->co_tag != NULL) {
350			if (coll->co_tag != NULL)
351				free(coll->co_tag);
352			coll->co_tag = xstrdup(from->co_tag);
353		}
354		if (from->co_date != NULL) {
355			if (coll->co_date != NULL)
356				free(coll->co_date);
357			coll->co_date = xstrdup(from->co_date);
358		}
359	}
360	if (from->co_listsuffix != NULL) {
361		if (coll->co_listsuffix != NULL)
362			free(coll->co_listsuffix);
363		coll->co_listsuffix = xstrdup(from->co_listsuffix);
364	}
365	for (i = 0; i < pattlist_size(from->co_accepts); i++) {
366		pattlist_add(coll->co_accepts,
367		    pattlist_get(from->co_accepts, i));
368	}
369	for (i = 0; i < pattlist_size(from->co_refusals); i++) {
370		pattlist_add(coll->co_refusals,
371		    pattlist_get(from->co_refusals, i));
372	}
373	coll->co_options = oldoptions | newoptions;
374}
375
376char *
377coll_statussuffix(struct coll *coll)
378{
379	const char *tag;
380	char *suffix;
381
382	if (coll->co_listsuffix != NULL) {
383		xasprintf(&suffix, ".%s", coll->co_listsuffix);
384	} else if (coll->co_options & CO_USERELSUFFIX) {
385		if (coll->co_tag == NULL)
386			tag = ".";
387		else
388			tag = coll->co_tag;
389		if (coll->co_release != NULL) {
390			if (coll->co_options & CO_CHECKOUTMODE) {
391				xasprintf(&suffix, ".%s:%s",
392				    coll->co_release, tag);
393			} else {
394				xasprintf(&suffix, ".%s", coll->co_release);
395			}
396		} else if (coll->co_options & CO_CHECKOUTMODE) {
397			xasprintf(&suffix, ":%s", tag);
398		}
399	} else
400		suffix = NULL;
401	return (suffix);
402}
403
404char *
405coll_statuspath(struct coll *coll)
406{
407	char *path, *suffix;
408
409	suffix = coll_statussuffix(coll);
410	if (suffix != NULL) {
411		if (coll->co_colldir[0] == '/')
412			xasprintf(&path, "%s/%s/checkouts%s", coll->co_colldir,
413			    coll->co_name, suffix);
414		else
415			xasprintf(&path, "%s/%s/%s/checkouts%s", coll->co_base,
416			    coll->co_colldir, coll->co_name, suffix);
417	} else {
418		if (coll->co_colldir[0] == '/')
419			xasprintf(&path, "%s/%s/checkouts", coll->co_colldir,
420			    coll->co_name);
421		else
422			xasprintf(&path, "%s/%s/%s/checkouts", coll->co_base,
423			    coll->co_colldir, coll->co_name);
424	}
425	free(suffix);
426	return (path);
427}
428
429void
430coll_add(char *name)
431{
432	struct coll *coll;
433
434	cur_coll->co_name = name;
435	coll_override(cur_coll, ovcoll, ovmask);
436	if (cur_coll->co_release == NULL) {
437		lprintf(-1, "Release not specified for collection "
438		    "\"%s\"\n", cur_coll->co_name);
439		exit(1);
440	}
441	if (cur_coll->co_host == NULL) {
442		lprintf(-1, "Host not specified for collection "
443		    "\"%s\"\n", cur_coll->co_name);
444		exit(1);
445	}
446	if (!STAILQ_EMPTY(&colls)) {
447		coll = STAILQ_LAST(&colls, coll, co_next);
448		if (strcmp(coll->co_host, cur_coll->co_host) != 0) {
449			lprintf(-1, "All \"host\" fields in the supfile "
450			    "must be the same\n");
451			exit(1);
452		}
453	}
454	STAILQ_INSERT_TAIL(&colls, cur_coll, co_next);
455	cur_coll = coll_new(defaults);
456}
457
458void
459coll_free(struct coll *coll)
460{
461
462	if (coll == NULL)
463		return;
464	if (coll->co_host != NULL)
465		free(coll->co_host);
466	if (coll->co_base != NULL)
467		free(coll->co_base);
468	if (coll->co_date != NULL)
469		free(coll->co_date);
470	if (coll->co_prefix != NULL)
471		free(coll->co_prefix);
472	if (coll->co_release != NULL)
473		free(coll->co_release);
474	if (coll->co_tag != NULL)
475		free(coll->co_tag);
476	if (coll->co_cvsroot != NULL)
477		free(coll->co_cvsroot);
478	if (coll->co_name != NULL)
479		free(coll->co_name);
480	if (coll->co_listsuffix != NULL)
481		free(coll->co_listsuffix);
482	keyword_free(coll->co_keyword);
483	if (coll->co_dirfilter != NULL)
484		globtree_free(coll->co_dirfilter);
485	if (coll->co_dirfilter != NULL)
486		globtree_free(coll->co_filefilter);
487	if (coll->co_norsync != NULL)
488		globtree_free(coll->co_norsync);
489	if (coll->co_accepts != NULL)
490		pattlist_free(coll->co_accepts);
491	if (coll->co_refusals != NULL)
492		pattlist_free(coll->co_refusals);
493	free(coll);
494}
495
496void
497coll_setopt(int opt, char *value)
498{
499	struct coll *coll;
500	int error, mask;
501
502	coll = cur_coll;
503	switch (opt) {
504	case PT_HOST:
505		if (coll->co_host != NULL)
506			free(coll->co_host);
507		coll->co_host = value;
508		break;
509	case PT_BASE:
510		if (coll->co_base != NULL)
511			free(coll->co_base);
512		coll->co_base = value;
513		break;
514	case PT_DATE:
515		if (coll->co_date != NULL)
516			free(coll->co_date);
517		coll->co_date = value;
518		coll->co_options |= CO_CHECKOUTMODE;
519		break;
520	case PT_PREFIX:
521		if (coll->co_prefix != NULL)
522			free(coll->co_prefix);
523		coll->co_prefix = value;
524		break;
525	case PT_RELEASE:
526		if (coll->co_release != NULL)
527			free(coll->co_release);
528		coll->co_release = value;
529		break;
530	case PT_TAG:
531		if (coll->co_tag != NULL)
532			free(coll->co_tag);
533		coll->co_tag = value;
534		coll->co_options |= CO_CHECKOUTMODE;
535		break;
536	case PT_LIST:
537		if (strchr(value, '/') != NULL) {
538			lprintf(-1, "Parse error in \"%s\": \"list\" suffix "
539			    "must not contain slashes\n", cfgfile);
540			exit(1);
541		}
542		if (coll->co_listsuffix != NULL)
543			free(coll->co_listsuffix);
544		coll->co_listsuffix = value;
545		break;
546	case PT_UMASK:
547		error = asciitoint(value, &mask, 8);
548		free(value);
549		if (error) {
550			lprintf(-1, "Parse error in \"%s\": Invalid "
551			    "umask value\n", cfgfile);
552			exit(1);
553		}
554		coll->co_umask = mask;
555		break;
556	case PT_USE_REL_SUFFIX:
557		coll->co_options |= CO_USERELSUFFIX;
558		break;
559	case PT_DELETE:
560		coll->co_options |= CO_DELETE | CO_EXACTRCS;
561		break;
562	case PT_COMPRESS:
563		coll->co_options |= CO_COMPRESS;
564		break;
565	case PT_NORSYNC:
566		coll->co_options |= CO_NORSYNC;
567		break;
568	}
569}
570
571/* Set "coll" as being the default collection. */
572void
573coll_setdef(void)
574{
575
576	coll_free(defaults);
577	defaults = cur_coll;
578	cur_coll = coll_new(defaults);
579}