154359Sroberto/*
254359Sroberto * ntp_filegen.c,v 3.12 1994/01/25 19:06:11 kardel Exp
354359Sroberto *
454359Sroberto *  implements file generations support for NTP
554359Sroberto *  logfiles and statistic files
654359Sroberto *
754359Sroberto *
854359Sroberto * Copyright (C) 1992, 1996 by Rainer Pruy
9285612Sdelphij * Friedrich-Alexander Universitaet Erlangen-Nuernberg, Germany
1054359Sroberto *
1154359Sroberto * This code may be modified and used freely
1254359Sroberto * provided credits remain intact.
1354359Sroberto */
1454359Sroberto
1554359Sroberto#ifdef HAVE_CONFIG_H
16182007Sroberto# include <config.h>
1754359Sroberto#endif
1854359Sroberto
1954359Sroberto#include <stdio.h>
2054359Sroberto#include <sys/types.h>
2154359Sroberto#include <sys/stat.h>
2254359Sroberto
2354359Sroberto#include "ntpd.h"
2454359Sroberto#include "ntp_io.h"
2554359Sroberto#include "ntp_string.h"
2654359Sroberto#include "ntp_calendar.h"
2754359Sroberto#include "ntp_filegen.h"
2854359Sroberto#include "ntp_stdlib.h"
2954359Sroberto
3054359Sroberto/*
3154359Sroberto * NTP is intended to run long periods of time without restart.
3254359Sroberto * Thus log and statistic files generated by NTP will grow large.
3354359Sroberto *
3454359Sroberto * this set of routines provides a central interface
3554359Sroberto * to generating files using file generations
3654359Sroberto *
3754359Sroberto * the generation of a file is changed according to file generation type
3854359Sroberto */
3954359Sroberto
4054359Sroberto
4154359Sroberto/*
4254359Sroberto * redefine this if your system dislikes filename suffixes like
4354359Sroberto * X.19910101 or X.1992W50 or ....
4454359Sroberto */
4554359Sroberto#define SUFFIX_SEP '.'
4654359Sroberto
47285612Sdelphijstatic	void	filegen_open	(FILEGEN *, u_int32, const time_t*);
48285612Sdelphijstatic	int	valid_fileref	(const char *, const char *);
49285612Sdelphijstatic	void	filegen_init	(const char *, const char *, FILEGEN *);
50285612Sdelphij#ifdef	DEBUG
51285612Sdelphijstatic	void	filegen_uninit		(FILEGEN *);
52285612Sdelphij#endif	/* DEBUG */
53285612Sdelphij
54285612Sdelphij
5554359Sroberto/*
56285612Sdelphij * filegen_init
5754359Sroberto */
5854359Sroberto
59285612Sdelphijstatic void
60285612Sdelphijfilegen_init(
61285612Sdelphij	const char *	dir,
62285612Sdelphij	const char *	fname,
63285612Sdelphij	FILEGEN *	fgp
64285612Sdelphij	)
65285612Sdelphij{
66285612Sdelphij	fgp->fp = NULL;
67285612Sdelphij	fgp->dir = estrdup(dir);
68285612Sdelphij	fgp->fname = estrdup(fname);
69285612Sdelphij	fgp->id_lo = 0;
70285612Sdelphij	fgp->id_hi = 0;
71285612Sdelphij	fgp->type = FILEGEN_DAY;
72285612Sdelphij	fgp->flag = FGEN_FLAG_LINK; /* not yet enabled !!*/
73285612Sdelphij}
7454359Sroberto
75182007Sroberto
7654359Sroberto/*
77285612Sdelphij * filegen_uninit - free memory allocated by filegen_init
78182007Sroberto */
79285612Sdelphij#ifdef DEBUG
80182007Srobertostatic void
81285612Sdelphijfilegen_uninit(
82285612Sdelphij	FILEGEN *fgp
83285612Sdelphij	)
84182007Sroberto{
85285612Sdelphij	free(fgp->dir);
86285612Sdelphij	free(fgp->fname);
87182007Sroberto}
88285612Sdelphij#endif
89182007Sroberto
90182007Sroberto
91182007Sroberto/*
9254359Sroberto * open a file generation according to the current settings of gen
9354359Sroberto * will also provide a link to basename if requested to do so
9454359Sroberto */
9554359Sroberto
9654359Srobertostatic void
9754359Srobertofilegen_open(
98285612Sdelphij	FILEGEN *	gen,
99285612Sdelphij	u_int32		stamp,
100285612Sdelphij	const time_t *	pivot
10154359Sroberto	)
10254359Sroberto{
103285612Sdelphij	char *savename;	/* temp store for name collision handling */
104285612Sdelphij	char *fullname;	/* name with any designation extension */
105285612Sdelphij	char *filename;	/* name without designation extension */
106285612Sdelphij	char *suffix;	/* where to print suffix extension */
107285612Sdelphij	u_int len, suflen;
10854359Sroberto	FILE *fp;
10954359Sroberto	struct calendar cal;
110285612Sdelphij	struct isodate	iso;
11154359Sroberto
112285612Sdelphij	/* get basic filename in buffer, leave room for extensions */
113285612Sdelphij	len = strlen(gen->dir) + strlen(gen->fname) + 65;
114285612Sdelphij	filename = emalloc(len);
115285612Sdelphij	fullname = emalloc(len);
116285612Sdelphij	savename = NULL;
117285612Sdelphij	snprintf(filename, len, "%s%s", gen->dir, gen->fname);
118285612Sdelphij
119285612Sdelphij	/* where to place suffix */
120285612Sdelphij	suflen = strlcpy(fullname, filename, len);
121285612Sdelphij	suffix = fullname + suflen;
122285612Sdelphij	suflen = len - suflen;
123285612Sdelphij
124285612Sdelphij	/* last octet of fullname set to '\0' for truncation check */
125285612Sdelphij	fullname[len - 1] = '\0';
126285612Sdelphij
127285612Sdelphij	switch (gen->type) {
128285612Sdelphij
129285612Sdelphij	default:
130285612Sdelphij		msyslog(LOG_ERR,
131285612Sdelphij			"unsupported file generations type %d for "
132285612Sdelphij			"\"%s\" - reverting to FILEGEN_NONE",
133285612Sdelphij			gen->type, filename);
13454359Sroberto		gen->type = FILEGEN_NONE;
13554359Sroberto		break;
13654359Sroberto
137285612Sdelphij	case FILEGEN_NONE:
138285612Sdelphij		/* no suffix, all set */
13954359Sroberto		break;
140285612Sdelphij
141285612Sdelphij	case FILEGEN_PID:
142285612Sdelphij		gen->id_lo = getpid();
143285612Sdelphij		gen->id_hi = 0;
144285612Sdelphij		snprintf(suffix, suflen, "%c#%ld",
145285612Sdelphij			 SUFFIX_SEP, gen->id_lo);
14654359Sroberto		break;
147285612Sdelphij
148285612Sdelphij	case FILEGEN_DAY:
14954359Sroberto		/*
150285612Sdelphij		 * You can argue here in favor of using MJD, but I
151285612Sdelphij		 * would assume it to be easier for humans to interpret
152285612Sdelphij		 * dates in a format they are used to in everyday life.
15354359Sroberto		 */
154285612Sdelphij		ntpcal_ntp_to_date(&cal, stamp, pivot);
155285612Sdelphij		snprintf(suffix, suflen, "%c%04d%02d%02d",
156285612Sdelphij			 SUFFIX_SEP, cal.year, cal.month, cal.monthday);
157285612Sdelphij		cal.hour = cal.minute = cal.second = 0;
158285612Sdelphij		gen->id_lo = ntpcal_date_to_ntp(&cal);
159285612Sdelphij		gen->id_hi = (u_int32)(gen->id_lo + SECSPERDAY);
16054359Sroberto		break;
16154359Sroberto
162285612Sdelphij	case FILEGEN_WEEK:
163285612Sdelphij		isocal_ntp_to_date(&iso, stamp, pivot);
164285612Sdelphij		snprintf(suffix, suflen, "%c%04dw%02d",
165285612Sdelphij			 SUFFIX_SEP, iso.year, iso.week);
166285612Sdelphij		iso.hour = iso.minute = iso.second = 0;
167285612Sdelphij		iso.weekday = 1;
168285612Sdelphij		gen->id_lo = isocal_date_to_ntp(&iso);
169285612Sdelphij		gen->id_hi = (u_int32)(gen->id_lo + 7 * SECSPERDAY);
17054359Sroberto		break;
17154359Sroberto
172285612Sdelphij	case FILEGEN_MONTH:
173285612Sdelphij		ntpcal_ntp_to_date(&cal, stamp, pivot);
174285612Sdelphij		snprintf(suffix, suflen, "%c%04d%02d",
175285612Sdelphij			 SUFFIX_SEP, cal.year, cal.month);
176285612Sdelphij		cal.hour = cal.minute = cal.second = 0;
177285612Sdelphij		cal.monthday = 1;
178285612Sdelphij		gen->id_lo = ntpcal_date_to_ntp(&cal);
179285612Sdelphij		cal.month++;
180285612Sdelphij		gen->id_hi = ntpcal_date_to_ntp(&cal);
18154359Sroberto		break;
18254359Sroberto
183285612Sdelphij	case FILEGEN_YEAR:
184285612Sdelphij		ntpcal_ntp_to_date(&cal, stamp, pivot);
185285612Sdelphij		snprintf(suffix, suflen, "%c%04d",
186285612Sdelphij			 SUFFIX_SEP, cal.year);
187285612Sdelphij		cal.hour = cal.minute = cal.second = 0;
188285612Sdelphij		cal.month = cal.monthday = 1;
189285612Sdelphij		gen->id_lo = ntpcal_date_to_ntp(&cal);
190285612Sdelphij		cal.year++;
191285612Sdelphij		gen->id_hi = ntpcal_date_to_ntp(&cal);
19254359Sroberto		break;
193285612Sdelphij
194285612Sdelphij	case FILEGEN_AGE:
195285612Sdelphij		gen->id_lo = current_time - (current_time % SECSPERDAY);
196285612Sdelphij		gen->id_hi = gen->id_lo + SECSPERDAY;
197285612Sdelphij		snprintf(suffix, suflen, "%ca%08ld",
198285612Sdelphij			 SUFFIX_SEP, gen->id_lo);
19954359Sroberto	}
20054359Sroberto
201285612Sdelphij	/* check possible truncation */
202285612Sdelphij	if ('\0' != fullname[len - 1]) {
203285612Sdelphij		fullname[len - 1] = '\0';
204285612Sdelphij		msyslog(LOG_ERR, "logfile name truncated: \"%s\"",
205285612Sdelphij			fullname);
206285612Sdelphij	}
207285612Sdelphij
208285612Sdelphij	if (FILEGEN_NONE != gen->type) {
20954359Sroberto		/*
21054359Sroberto		 * check for existence of a file with name 'basename'
21154359Sroberto		 * as we disallow such a file
21254359Sroberto		 * if FGEN_FLAG_LINK is set create a link
21354359Sroberto		 */
21454359Sroberto		struct stat stats;
21554359Sroberto		/*
21654359Sroberto		 * try to resolve name collisions
21754359Sroberto		 */
21854359Sroberto		static u_long conflicts = 0;
21954359Sroberto
22054359Sroberto#ifndef	S_ISREG
22154359Sroberto#define	S_ISREG(mode)	(((mode) & S_IFREG) == S_IFREG)
22254359Sroberto#endif
223285612Sdelphij		if (stat(filename, &stats) == 0) {
22454359Sroberto			/* Hm, file exists... */
22554359Sroberto			if (S_ISREG(stats.st_mode)) {
22654359Sroberto				if (stats.st_nlink <= 1)	{
22754359Sroberto					/*
22854359Sroberto					 * Oh, it is not linked - try to save it
22954359Sroberto					 */
230285612Sdelphij					savename = emalloc(len);
231285612Sdelphij					snprintf(savename, len,
232285612Sdelphij						"%s%c%dC%lu",
233285612Sdelphij						filename, SUFFIX_SEP,
234285612Sdelphij						(int)getpid(), conflicts++);
235285612Sdelphij
236285612Sdelphij					if (rename(filename, savename) != 0)
237285612Sdelphij						msyslog(LOG_ERR,
238285612Sdelphij							"couldn't save %s: %m",
239285612Sdelphij							filename);
24054359Sroberto					free(savename);
24154359Sroberto				} else {
24254359Sroberto					/*
243182007Sroberto					 * there is at least a second link to
244182007Sroberto					 * this file.
24554359Sroberto					 * just remove the conflicting one
24654359Sroberto					 */
24754359Sroberto					if (
24854359Sroberto#if !defined(VMS)
249285612Sdelphij						unlink(filename) != 0
25054359Sroberto#else
251285612Sdelphij						delete(filename) != 0
25254359Sroberto#endif
25354359Sroberto						)
254285612Sdelphij						msyslog(LOG_ERR,
255285612Sdelphij							"couldn't unlink %s: %m",
256285612Sdelphij							filename);
25754359Sroberto				}
25854359Sroberto			} else {
25954359Sroberto				/*
26054359Sroberto				 * Ehh? Not a regular file ?? strange !!!!
26154359Sroberto				 */
262285612Sdelphij				msyslog(LOG_ERR,
263285612Sdelphij					"expected regular file for %s "
264285612Sdelphij					"(found mode 0%lo)",
265285612Sdelphij					filename,
266285612Sdelphij					(unsigned long)stats.st_mode);
26754359Sroberto			}
26854359Sroberto		} else {
26954359Sroberto			/*
27054359Sroberto			 * stat(..) failed, but it is absolutely correct for
27154359Sroberto			 * 'basename' not to exist
27254359Sroberto			 */
273285612Sdelphij			if (ENOENT != errno)
274285612Sdelphij				msyslog(LOG_ERR, "stat(%s) failed: %m",
275285612Sdelphij						 filename);
27654359Sroberto		}
27754359Sroberto	}
27854359Sroberto
27954359Sroberto	/*
28054359Sroberto	 * now, try to open new file generation...
28154359Sroberto	 */
282285612Sdelphij	DPRINTF(4, ("opening filegen (type=%d/stamp=%u) \"%s\"\n",
283285612Sdelphij		    gen->type, stamp, fullname));
284285612Sdelphij
285285612Sdelphij	fp = fopen(fullname, "a");
28654359Sroberto
287285612Sdelphij	if (NULL == fp)	{
28854359Sroberto		/* open failed -- keep previous state
28954359Sroberto		 *
29054359Sroberto		 * If the file was open before keep the previous generation.
29154359Sroberto		 * This will cause output to end up in the 'wrong' file,
292132451Sroberto		 * but I think this is still better than losing output
29354359Sroberto		 *
29454359Sroberto		 * ignore errors due to missing directories
29554359Sroberto		 */
29654359Sroberto
297285612Sdelphij		if (ENOENT != errno)
298285612Sdelphij			msyslog(LOG_ERR, "can't open %s: %m", fullname);
29954359Sroberto	} else {
300285612Sdelphij		if (NULL != gen->fp) {
30154359Sroberto			fclose(gen->fp);
302285612Sdelphij			gen->fp = NULL;
30354359Sroberto		}
30454359Sroberto		gen->fp = fp;
30554359Sroberto
30654359Sroberto		if (gen->flag & FGEN_FLAG_LINK) {
30754359Sroberto			/*
30854359Sroberto			 * need to link file to basename
30954359Sroberto			 * have to use hardlink for now as I want to allow
31054359Sroberto			 * gen->basename spanning directory levels
31154359Sroberto			 * this would make it more complex to get the correct
312285612Sdelphij			 * fullname for symlink
31354359Sroberto			 *
31454359Sroberto			 * Ok, it would just mean taking the part following
31554359Sroberto			 * the last '/' in the name.... Should add it later....
31654359Sroberto			 */
31754359Sroberto
31854359Sroberto			/* Windows NT does not support file links -Greg Schueman 1/18/97 */
31954359Sroberto
320285612Sdelphij#if defined(SYS_WINNT) || defined(SYS_VXWORKS)
32154359Sroberto			SetLastError(0); /* On WinNT, don't support FGEN_FLAG_LINK */
32254359Sroberto#elif defined(VMS)
32354359Sroberto			errno = 0; /* On VMS, don't support FGEN_FLAG_LINK */
32454359Sroberto#else  /* not (VMS) / VXWORKS / WINNT ; DO THE LINK) */
325285612Sdelphij			if (link(fullname, filename) != 0)
326285612Sdelphij				if (EEXIST != errno)
327285612Sdelphij					msyslog(LOG_ERR,
328285612Sdelphij						"can't link(%s, %s): %m",
329285612Sdelphij						fullname, filename);
33054359Sroberto#endif /* SYS_WINNT || VXWORKS */
33154359Sroberto		}		/* flags & FGEN_FLAG_LINK */
33254359Sroberto	}			/* else fp == NULL */
33354359Sroberto
33454359Sroberto	free(filename);
335285612Sdelphij	free(fullname);
33654359Sroberto	return;
33754359Sroberto}
33854359Sroberto
33954359Sroberto/*
34054359Sroberto * this function sets up gen->fp to point to the correct
34154359Sroberto * generation of the file for the time specified by 'now'
34254359Sroberto *
34354359Sroberto * 'now' usually is interpreted as second part of a l_fp as is in the cal...
34454359Sroberto * library routines
34554359Sroberto */
34654359Sroberto
34754359Srobertovoid
34854359Srobertofilegen_setup(
349285612Sdelphij	FILEGEN *	gen,
350285612Sdelphij	u_int32		now
35154359Sroberto	)
35254359Sroberto{
353285612Sdelphij	int	current;
354285612Sdelphij	time_t	pivot;
35554359Sroberto
35654359Sroberto	if (!(gen->flag & FGEN_FLAG_ENABLED)) {
357285612Sdelphij		if (NULL != gen->fp) {
358285612Sdelphij			fclose(gen->fp);
359285612Sdelphij			gen->fp = NULL;
360285612Sdelphij		}
36154359Sroberto		return;
36254359Sroberto	}
363285612Sdelphij
36454359Sroberto	switch (gen->type) {
365285612Sdelphij
366285612Sdelphij	default:
367285612Sdelphij	case FILEGEN_NONE:
368285612Sdelphij		current = TRUE;
36954359Sroberto		break;
370285612Sdelphij
371285612Sdelphij	case FILEGEN_PID:
372285612Sdelphij		current = ((int)gen->id_lo == getpid());
37354359Sroberto		break;
37454359Sroberto
375285612Sdelphij	case FILEGEN_AGE:
376285612Sdelphij		current = (gen->id_lo <= current_time) &&
377285612Sdelphij			  (gen->id_hi > current_time);
37854359Sroberto		break;
37954359Sroberto
380285612Sdelphij	case FILEGEN_DAY:
381285612Sdelphij	case FILEGEN_WEEK:
382285612Sdelphij	case FILEGEN_MONTH:
383285612Sdelphij	case FILEGEN_YEAR:
384285612Sdelphij		current = (gen->id_lo <= now) &&
385285612Sdelphij			  (gen->id_hi > now);
38654359Sroberto		break;
38754359Sroberto	}
38854359Sroberto	/*
38954359Sroberto	 * try to open file if not yet open
39054359Sroberto	 * reopen new file generation file on change of generation id
39154359Sroberto	 */
392285612Sdelphij	if (NULL == gen->fp || !current) {
393285612Sdelphij		DPRINTF(1, ("filegen  %0x %u\n", gen->type, now));
394285612Sdelphij		pivot = time(NULL);
395285612Sdelphij		filegen_open(gen, now, &pivot);
39654359Sroberto	}
39754359Sroberto}
39854359Sroberto
39954359Sroberto
40054359Sroberto/*
40154359Sroberto * change settings for filegen files
40254359Sroberto */
40354359Srobertovoid
40454359Srobertofilegen_config(
405285612Sdelphij	FILEGEN *	gen,
406285612Sdelphij	const char *	dir,
407285612Sdelphij	const char *	fname,
408285612Sdelphij	u_int		type,
409285612Sdelphij	u_int		flag
41054359Sroberto	)
41154359Sroberto{
412285612Sdelphij	int file_existed;
413285612Sdelphij	l_fp now;
414285612Sdelphij
415285612Sdelphij
41654359Sroberto	/*
41754359Sroberto	 * if nothing would be changed...
41854359Sroberto	 */
419285612Sdelphij	if (strcmp(dir, gen->dir) == 0 && strcmp(fname, gen->fname) == 0
420285612Sdelphij	    && type == gen->type && flag == gen->flag)
421285612Sdelphij		return;
422285612Sdelphij
42354359Sroberto	/*
42454359Sroberto	 * validate parameters
42554359Sroberto	 */
426285612Sdelphij	if (!valid_fileref(dir, fname))
427285612Sdelphij		return;
42854359Sroberto
429285612Sdelphij	if (NULL != gen->fp) {
430285612Sdelphij		fclose(gen->fp);
431285612Sdelphij		gen->fp = NULL;
432285612Sdelphij		file_existed = TRUE;
433285612Sdelphij	} else {
434285612Sdelphij		file_existed = FALSE;
435285612Sdelphij	}
43654359Sroberto
437285612Sdelphij	DPRINTF(3, ("configuring filegen:\n"
438285612Sdelphij		    "\tdir:\t%s -> %s\n"
439285612Sdelphij		    "\tfname:\t%s -> %s\n"
440285612Sdelphij		    "\ttype:\t%d -> %d\n"
441285612Sdelphij		    "\tflag: %x -> %x\n",
442285612Sdelphij		    gen->dir, dir,
443285612Sdelphij		    gen->fname, fname,
444285612Sdelphij		    gen->type, type,
445285612Sdelphij		    gen->flag, flag));
446285612Sdelphij
447285612Sdelphij	if (strcmp(gen->dir, dir) != 0) {
448285612Sdelphij		free(gen->dir);
449285612Sdelphij		gen->dir = estrdup(dir);
45054359Sroberto	}
45154359Sroberto
452285612Sdelphij	if (strcmp(gen->fname, fname) != 0) {
453285612Sdelphij		free(gen->fname);
454285612Sdelphij		gen->fname = estrdup(fname);
455285612Sdelphij	}
456285612Sdelphij	gen->type = (u_char)type;
457285612Sdelphij	gen->flag = (u_char)flag;
458285612Sdelphij
45954359Sroberto	/*
46054359Sroberto	 * make filegen use the new settings
46154359Sroberto	 * special action is only required when a generation file
46254359Sroberto	 * is currently open
46354359Sroberto	 * otherwise the new settings will be used anyway at the next open
46454359Sroberto	 */
465285612Sdelphij	if (file_existed) {
46654359Sroberto		get_systime(&now);
46754359Sroberto		filegen_setup(gen, now.l_ui);
46854359Sroberto	}
46954359Sroberto}
47054359Sroberto
47154359Sroberto
47254359Sroberto/*
47354359Sroberto * check whether concatenating prefix and basename
47454359Sroberto * yields a legal filename
47554359Sroberto */
47654359Srobertostatic int
47754359Srobertovalid_fileref(
478285612Sdelphij	const char *	dir,
479285612Sdelphij	const char *	fname
48054359Sroberto	)
48154359Sroberto{
48254359Sroberto	/*
483285612Sdelphij	 * dir cannot be changed dynamically
48454359Sroberto	 * (within the context of filegen)
48554359Sroberto	 * so just reject basenames containing '..'
48654359Sroberto	 *
48754359Sroberto	 * ASSUMPTION:
488285612Sdelphij	 *		file system parts 'below' dir may be
48954359Sroberto	 *		specified without infringement of security
49054359Sroberto	 *
491285612Sdelphij	 *		restricting dir to legal values
49254359Sroberto	 *		has to be ensured by other means
49354359Sroberto	 * (however, it would be possible to perform some checks here...)
49454359Sroberto	 */
495285612Sdelphij	const char *p;
496285612Sdelphij
49754359Sroberto	/*
49854359Sroberto	 * Just to catch, dumb errors opening up the world...
49954359Sroberto	 */
500285612Sdelphij	if (NULL == dir || '\0' == dir[0])
501285612Sdelphij		return FALSE;
50254359Sroberto
503285612Sdelphij	if (NULL == fname)
504285612Sdelphij		return FALSE;
505285612Sdelphij
506285612Sdelphij#ifdef SYS_WINNT
507285612Sdelphij	/*
508285612Sdelphij	 * Windows treats / equivalent to \, reject any / to ensure
509285612Sdelphij	 * check below for DIR_SEP (\ on windows) are adequate.
510285612Sdelphij	 */
511285612Sdelphij	if (strchr(fname, '/')) {
512285612Sdelphij		msyslog(LOG_ERR,
513285612Sdelphij			"filegen filenames must not contain '/': %s",
514285612Sdelphij			fname);
515285612Sdelphij		return FALSE;
51654359Sroberto	}
517285612Sdelphij#endif
518285612Sdelphij
519285612Sdelphij	for (p = fname; p != NULL; p = strchr(p, DIR_SEP)) {
520285612Sdelphij		if ('.' == p[0] && '.' == p[1]
521285612Sdelphij		    && ('\0' == p[2] || DIR_SEP == p[2]))
522285612Sdelphij			return FALSE;
523285612Sdelphij	}
524285612Sdelphij
525285612Sdelphij	return TRUE;
52654359Sroberto}
52754359Sroberto
52854359Sroberto
52954359Sroberto/*
53054359Sroberto * filegen registry
53154359Sroberto */
53254359Sroberto
53354359Srobertostatic struct filegen_entry {
534285612Sdelphij	char *			name;
535285612Sdelphij	FILEGEN *		filegen;
536285612Sdelphij	struct filegen_entry *	next;
53754359Sroberto} *filegen_registry = NULL;
53854359Sroberto
53954359Sroberto
54054359SrobertoFILEGEN *
54154359Srobertofilegen_get(
542285612Sdelphij	const char *	name
54354359Sroberto	)
54454359Sroberto{
54554359Sroberto	struct filegen_entry *f = filegen_registry;
54654359Sroberto
547285612Sdelphij	while (f) {
54854359Sroberto		if (f->name == name || strcmp(name, f->name) == 0) {
549285612Sdelphij			DPRINTF(4, ("filegen_get(%s) = %p\n",
550285612Sdelphij				    name, f->filegen));
55154359Sroberto			return f->filegen;
55254359Sroberto		}
55354359Sroberto		f = f->next;
55454359Sroberto	}
555285612Sdelphij	DPRINTF(4, ("filegen_get(%s) = NULL\n", name));
55654359Sroberto	return NULL;
55754359Sroberto}
55854359Sroberto
559285612Sdelphij
56054359Srobertovoid
56154359Srobertofilegen_register(
562285612Sdelphij	const char *	dir,
563285612Sdelphij	const char *	name,
564285612Sdelphij	FILEGEN *	filegen
56554359Sroberto	)
56654359Sroberto{
567285612Sdelphij	struct filegen_entry **ppfe;
56854359Sroberto
569285612Sdelphij	DPRINTF(4, ("filegen_register(%s, %p)\n", name, filegen));
570182007Sroberto
571285612Sdelphij	filegen_init(dir, name, filegen);
572182007Sroberto
573285612Sdelphij	ppfe = &filegen_registry;
574285612Sdelphij	while (NULL != *ppfe) {
575285612Sdelphij		if ((*ppfe)->name == name
576285612Sdelphij		    || !strcmp((*ppfe)->name, name)) {
577285612Sdelphij
578285612Sdelphij			DPRINTF(5, ("replacing filegen %p\n",
579285612Sdelphij				    (*ppfe)->filegen));
580285612Sdelphij
581285612Sdelphij			(*ppfe)->filegen = filegen;
58254359Sroberto			return;
58354359Sroberto		}
584285612Sdelphij		ppfe = &((*ppfe)->next);
58554359Sroberto	}
58654359Sroberto
587285612Sdelphij	*ppfe = emalloc(sizeof **ppfe);
588285612Sdelphij
589285612Sdelphij	(*ppfe)->next = NULL;
590285612Sdelphij	(*ppfe)->name = estrdup(name);
591285612Sdelphij	(*ppfe)->filegen = filegen;
592285612Sdelphij
593285612Sdelphij	DPRINTF(6, ("adding new filegen\n"));
59454359Sroberto
59554359Sroberto	return;
59654359Sroberto}
59754359Sroberto
598285612Sdelphij
599285612Sdelphij/*
600285612Sdelphij * filegen_statsdir() - reset each filegen entry's dir to statsdir.
601285612Sdelphij */
602285612Sdelphijvoid
603285612Sdelphijfilegen_statsdir(void)
604285612Sdelphij{
605285612Sdelphij	struct filegen_entry *f;
606285612Sdelphij
607285612Sdelphij	for (f = filegen_registry; f != NULL; f = f->next)
608285612Sdelphij		filegen_config(f->filegen, statsdir, f->filegen->fname,
609285612Sdelphij			       f->filegen->type, f->filegen->flag);
610285612Sdelphij}
611285612Sdelphij
612285612Sdelphij
613285612Sdelphij/*
614285612Sdelphij * filegen_unregister frees memory allocated by filegen_register for
615285612Sdelphij * name.
616285612Sdelphij */
617285612Sdelphij#ifdef DEBUG
618285612Sdelphijvoid
61954359Srobertofilegen_unregister(
620285612Sdelphij	const char *name
62154359Sroberto	)
62254359Sroberto{
623285612Sdelphij	struct filegen_entry **	ppfe;
624285612Sdelphij	struct filegen_entry *	pfe;
625285612Sdelphij	FILEGEN *		fg;
626285612Sdelphij
627285612Sdelphij	DPRINTF(4, ("filegen_unregister(%s)\n", name));
62854359Sroberto
629285612Sdelphij	ppfe = &filegen_registry;
630285612Sdelphij
631285612Sdelphij	while (NULL != *ppfe) {
632285612Sdelphij		if ((*ppfe)->name == name
633285612Sdelphij		    || !strcmp((*ppfe)->name, name)) {
634285612Sdelphij			pfe = *ppfe;
635285612Sdelphij			*ppfe = (*ppfe)->next;
636285612Sdelphij			fg = pfe->filegen;
637285612Sdelphij			free(pfe->name);
638285612Sdelphij			free(pfe);
639285612Sdelphij			filegen_uninit(fg);
640285612Sdelphij			break;
64154359Sroberto		}
642285612Sdelphij		ppfe = &((*ppfe)->next);
64354359Sroberto	}
64454359Sroberto}
645285612Sdelphij#endif	/* DEBUG */
646