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
9290001Sglebius * 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
47290001Sglebiusstatic	void	filegen_open	(FILEGEN *, u_int32, const time_t*);
48290001Sglebiusstatic	int	valid_fileref	(const char *, const char *);
49290001Sglebiusstatic	void	filegen_init	(const char *, const char *, FILEGEN *);
50290001Sglebius#ifdef	DEBUG
51290001Sglebiusstatic	void	filegen_uninit		(FILEGEN *);
52290001Sglebius#endif	/* DEBUG */
53290001Sglebius
54290001Sglebius
5554359Sroberto/*
56290001Sglebius * filegen_init
5754359Sroberto */
5854359Sroberto
59290001Sglebiusstatic void
60290001Sglebiusfilegen_init(
61290001Sglebius	const char *	dir,
62290001Sglebius	const char *	fname,
63290001Sglebius	FILEGEN *	fgp
64290001Sglebius	)
65290001Sglebius{
66290001Sglebius	fgp->fp = NULL;
67290001Sglebius	fgp->dir = estrdup(dir);
68290001Sglebius	fgp->fname = estrdup(fname);
69290001Sglebius	fgp->id_lo = 0;
70290001Sglebius	fgp->id_hi = 0;
71290001Sglebius	fgp->type = FILEGEN_DAY;
72290001Sglebius	fgp->flag = FGEN_FLAG_LINK; /* not yet enabled !!*/
73290001Sglebius}
7454359Sroberto
75182007Sroberto
7654359Sroberto/*
77290001Sglebius * filegen_uninit - free memory allocated by filegen_init
78182007Sroberto */
79290001Sglebius#ifdef DEBUG
80182007Srobertostatic void
81290001Sglebiusfilegen_uninit(
82290001Sglebius	FILEGEN *fgp
83290001Sglebius	)
84182007Sroberto{
85290001Sglebius	free(fgp->dir);
86290001Sglebius	free(fgp->fname);
87182007Sroberto}
88290001Sglebius#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(
98290001Sglebius	FILEGEN *	gen,
99290001Sglebius	u_int32		stamp,
100290001Sglebius	const time_t *	pivot
10154359Sroberto	)
10254359Sroberto{
103290001Sglebius	char *savename;	/* temp store for name collision handling */
104290001Sglebius	char *fullname;	/* name with any designation extension */
105290001Sglebius	char *filename;	/* name without designation extension */
106290001Sglebius	char *suffix;	/* where to print suffix extension */
107290001Sglebius	u_int len, suflen;
10854359Sroberto	FILE *fp;
10954359Sroberto	struct calendar cal;
110290001Sglebius	struct isodate	iso;
11154359Sroberto
112290001Sglebius	/* get basic filename in buffer, leave room for extensions */
113290001Sglebius	len = strlen(gen->dir) + strlen(gen->fname) + 65;
114290001Sglebius	filename = emalloc(len);
115290001Sglebius	fullname = emalloc(len);
116290001Sglebius	savename = NULL;
117290001Sglebius	snprintf(filename, len, "%s%s", gen->dir, gen->fname);
118290001Sglebius
119290001Sglebius	/* where to place suffix */
120290001Sglebius	suflen = strlcpy(fullname, filename, len);
121290001Sglebius	suffix = fullname + suflen;
122290001Sglebius	suflen = len - suflen;
123290001Sglebius
124290001Sglebius	/* last octet of fullname set to '\0' for truncation check */
125290001Sglebius	fullname[len - 1] = '\0';
126290001Sglebius
127290001Sglebius	switch (gen->type) {
128290001Sglebius
129290001Sglebius	default:
130290001Sglebius		msyslog(LOG_ERR,
131290001Sglebius			"unsupported file generations type %d for "
132290001Sglebius			"\"%s\" - reverting to FILEGEN_NONE",
133290001Sglebius			gen->type, filename);
13454359Sroberto		gen->type = FILEGEN_NONE;
13554359Sroberto		break;
13654359Sroberto
137290001Sglebius	case FILEGEN_NONE:
138290001Sglebius		/* no suffix, all set */
13954359Sroberto		break;
140290001Sglebius
141290001Sglebius	case FILEGEN_PID:
142290001Sglebius		gen->id_lo = getpid();
143290001Sglebius		gen->id_hi = 0;
144290001Sglebius		snprintf(suffix, suflen, "%c#%ld",
145290001Sglebius			 SUFFIX_SEP, gen->id_lo);
14654359Sroberto		break;
147290001Sglebius
148290001Sglebius	case FILEGEN_DAY:
14954359Sroberto		/*
150290001Sglebius		 * You can argue here in favor of using MJD, but I
151290001Sglebius		 * would assume it to be easier for humans to interpret
152290001Sglebius		 * dates in a format they are used to in everyday life.
15354359Sroberto		 */
154290001Sglebius		ntpcal_ntp_to_date(&cal, stamp, pivot);
155290001Sglebius		snprintf(suffix, suflen, "%c%04d%02d%02d",
156290001Sglebius			 SUFFIX_SEP, cal.year, cal.month, cal.monthday);
157290001Sglebius		cal.hour = cal.minute = cal.second = 0;
158290001Sglebius		gen->id_lo = ntpcal_date_to_ntp(&cal);
159290001Sglebius		gen->id_hi = (u_int32)(gen->id_lo + SECSPERDAY);
16054359Sroberto		break;
16154359Sroberto
162290001Sglebius	case FILEGEN_WEEK:
163290001Sglebius		isocal_ntp_to_date(&iso, stamp, pivot);
164290001Sglebius		snprintf(suffix, suflen, "%c%04dw%02d",
165290001Sglebius			 SUFFIX_SEP, iso.year, iso.week);
166290001Sglebius		iso.hour = iso.minute = iso.second = 0;
167290001Sglebius		iso.weekday = 1;
168290001Sglebius		gen->id_lo = isocal_date_to_ntp(&iso);
169290001Sglebius		gen->id_hi = (u_int32)(gen->id_lo + 7 * SECSPERDAY);
17054359Sroberto		break;
17154359Sroberto
172290001Sglebius	case FILEGEN_MONTH:
173290001Sglebius		ntpcal_ntp_to_date(&cal, stamp, pivot);
174290001Sglebius		snprintf(suffix, suflen, "%c%04d%02d",
175290001Sglebius			 SUFFIX_SEP, cal.year, cal.month);
176290001Sglebius		cal.hour = cal.minute = cal.second = 0;
177290001Sglebius		cal.monthday = 1;
178290001Sglebius		gen->id_lo = ntpcal_date_to_ntp(&cal);
179290001Sglebius		cal.month++;
180290001Sglebius		gen->id_hi = ntpcal_date_to_ntp(&cal);
18154359Sroberto		break;
18254359Sroberto
183290001Sglebius	case FILEGEN_YEAR:
184290001Sglebius		ntpcal_ntp_to_date(&cal, stamp, pivot);
185290001Sglebius		snprintf(suffix, suflen, "%c%04d",
186290001Sglebius			 SUFFIX_SEP, cal.year);
187290001Sglebius		cal.hour = cal.minute = cal.second = 0;
188290001Sglebius		cal.month = cal.monthday = 1;
189290001Sglebius		gen->id_lo = ntpcal_date_to_ntp(&cal);
190290001Sglebius		cal.year++;
191290001Sglebius		gen->id_hi = ntpcal_date_to_ntp(&cal);
19254359Sroberto		break;
193290001Sglebius
194290001Sglebius	case FILEGEN_AGE:
195290001Sglebius		gen->id_lo = current_time - (current_time % SECSPERDAY);
196290001Sglebius		gen->id_hi = gen->id_lo + SECSPERDAY;
197290001Sglebius		snprintf(suffix, suflen, "%ca%08ld",
198290001Sglebius			 SUFFIX_SEP, gen->id_lo);
19954359Sroberto	}
20054359Sroberto
201290001Sglebius	/* check possible truncation */
202290001Sglebius	if ('\0' != fullname[len - 1]) {
203290001Sglebius		fullname[len - 1] = '\0';
204290001Sglebius		msyslog(LOG_ERR, "logfile name truncated: \"%s\"",
205290001Sglebius			fullname);
206290001Sglebius	}
207290001Sglebius
208290001Sglebius	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
223290001Sglebius		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					 */
230290001Sglebius					savename = emalloc(len);
231290001Sglebius					snprintf(savename, len,
232290001Sglebius						"%s%c%dC%lu",
233290001Sglebius						filename, SUFFIX_SEP,
234290001Sglebius						(int)getpid(), conflicts++);
235290001Sglebius
236290001Sglebius					if (rename(filename, savename) != 0)
237290001Sglebius						msyslog(LOG_ERR,
238290001Sglebius							"couldn't save %s: %m",
239290001Sglebius							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)
249290001Sglebius						unlink(filename) != 0
25054359Sroberto#else
251290001Sglebius						delete(filename) != 0
25254359Sroberto#endif
25354359Sroberto						)
254290001Sglebius						msyslog(LOG_ERR,
255290001Sglebius							"couldn't unlink %s: %m",
256290001Sglebius							filename);
25754359Sroberto				}
25854359Sroberto			} else {
25954359Sroberto				/*
26054359Sroberto				 * Ehh? Not a regular file ?? strange !!!!
26154359Sroberto				 */
262290001Sglebius				msyslog(LOG_ERR,
263290001Sglebius					"expected regular file for %s "
264290001Sglebius					"(found mode 0%lo)",
265290001Sglebius					filename,
266290001Sglebius					(unsigned long)stats.st_mode);
26754359Sroberto			}
26854359Sroberto		} else {
26954359Sroberto			/*
27054359Sroberto			 * stat(..) failed, but it is absolutely correct for
27154359Sroberto			 * 'basename' not to exist
27254359Sroberto			 */
273290001Sglebius			if (ENOENT != errno)
274290001Sglebius				msyslog(LOG_ERR, "stat(%s) failed: %m",
275290001Sglebius						 filename);
27654359Sroberto		}
27754359Sroberto	}
27854359Sroberto
27954359Sroberto	/*
28054359Sroberto	 * now, try to open new file generation...
28154359Sroberto	 */
282290001Sglebius	DPRINTF(4, ("opening filegen (type=%d/stamp=%u) \"%s\"\n",
283290001Sglebius		    gen->type, stamp, fullname));
284290001Sglebius
285290001Sglebius	fp = fopen(fullname, "a");
28654359Sroberto
287290001Sglebius	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
297290001Sglebius		if (ENOENT != errno)
298290001Sglebius			msyslog(LOG_ERR, "can't open %s: %m", fullname);
29954359Sroberto	} else {
300290001Sglebius		if (NULL != gen->fp) {
30154359Sroberto			fclose(gen->fp);
302290001Sglebius			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
312290001Sglebius			 * 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
320290001Sglebius#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) */
325290001Sglebius			if (link(fullname, filename) != 0)
326290001Sglebius				if (EEXIST != errno)
327290001Sglebius					msyslog(LOG_ERR,
328290001Sglebius						"can't link(%s, %s): %m",
329290001Sglebius						fullname, filename);
33054359Sroberto#endif /* SYS_WINNT || VXWORKS */
33154359Sroberto		}		/* flags & FGEN_FLAG_LINK */
33254359Sroberto	}			/* else fp == NULL */
33354359Sroberto
33454359Sroberto	free(filename);
335290001Sglebius	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(
349290001Sglebius	FILEGEN *	gen,
350290001Sglebius	u_int32		now
35154359Sroberto	)
35254359Sroberto{
353290001Sglebius	int	current;
354290001Sglebius	time_t	pivot;
35554359Sroberto
35654359Sroberto	if (!(gen->flag & FGEN_FLAG_ENABLED)) {
357290001Sglebius		if (NULL != gen->fp) {
358290001Sglebius			fclose(gen->fp);
359290001Sglebius			gen->fp = NULL;
360290001Sglebius		}
36154359Sroberto		return;
36254359Sroberto	}
363290001Sglebius
36454359Sroberto	switch (gen->type) {
365290001Sglebius
366290001Sglebius	default:
367290001Sglebius	case FILEGEN_NONE:
368290001Sglebius		current = TRUE;
36954359Sroberto		break;
370290001Sglebius
371290001Sglebius	case FILEGEN_PID:
372290001Sglebius		current = ((int)gen->id_lo == getpid());
37354359Sroberto		break;
37454359Sroberto
375290001Sglebius	case FILEGEN_AGE:
376290001Sglebius		current = (gen->id_lo <= current_time) &&
377290001Sglebius			  (gen->id_hi > current_time);
37854359Sroberto		break;
37954359Sroberto
380290001Sglebius	case FILEGEN_DAY:
381290001Sglebius	case FILEGEN_WEEK:
382290001Sglebius	case FILEGEN_MONTH:
383290001Sglebius	case FILEGEN_YEAR:
384290001Sglebius		current = (gen->id_lo <= now) &&
385290001Sglebius			  (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	 */
392290001Sglebius	if (NULL == gen->fp || !current) {
393290001Sglebius		DPRINTF(1, ("filegen  %0x %u\n", gen->type, now));
394290001Sglebius		pivot = time(NULL);
395290001Sglebius		filegen_open(gen, now, &pivot);
39654359Sroberto	}
39754359Sroberto}
39854359Sroberto
39954359Sroberto
40054359Sroberto/*
40154359Sroberto * change settings for filegen files
40254359Sroberto */
40354359Srobertovoid
40454359Srobertofilegen_config(
405290001Sglebius	FILEGEN *	gen,
406290001Sglebius	const char *	dir,
407290001Sglebius	const char *	fname,
408290001Sglebius	u_int		type,
409290001Sglebius	u_int		flag
41054359Sroberto	)
41154359Sroberto{
412290001Sglebius	int file_existed;
413290001Sglebius	l_fp now;
414290001Sglebius
415290001Sglebius
41654359Sroberto	/*
41754359Sroberto	 * if nothing would be changed...
41854359Sroberto	 */
419290001Sglebius	if (strcmp(dir, gen->dir) == 0 && strcmp(fname, gen->fname) == 0
420290001Sglebius	    && type == gen->type && flag == gen->flag)
421290001Sglebius		return;
422290001Sglebius
42354359Sroberto	/*
42454359Sroberto	 * validate parameters
42554359Sroberto	 */
426290001Sglebius	if (!valid_fileref(dir, fname))
427290001Sglebius		return;
42854359Sroberto
429290001Sglebius	if (NULL != gen->fp) {
430290001Sglebius		fclose(gen->fp);
431290001Sglebius		gen->fp = NULL;
432290001Sglebius		file_existed = TRUE;
433290001Sglebius	} else {
434290001Sglebius		file_existed = FALSE;
435290001Sglebius	}
43654359Sroberto
437290001Sglebius	DPRINTF(3, ("configuring filegen:\n"
438290001Sglebius		    "\tdir:\t%s -> %s\n"
439290001Sglebius		    "\tfname:\t%s -> %s\n"
440290001Sglebius		    "\ttype:\t%d -> %d\n"
441290001Sglebius		    "\tflag: %x -> %x\n",
442290001Sglebius		    gen->dir, dir,
443290001Sglebius		    gen->fname, fname,
444290001Sglebius		    gen->type, type,
445290001Sglebius		    gen->flag, flag));
446290001Sglebius
447290001Sglebius	if (strcmp(gen->dir, dir) != 0) {
448290001Sglebius		free(gen->dir);
449290001Sglebius		gen->dir = estrdup(dir);
45054359Sroberto	}
45154359Sroberto
452290001Sglebius	if (strcmp(gen->fname, fname) != 0) {
453290001Sglebius		free(gen->fname);
454290001Sglebius		gen->fname = estrdup(fname);
455290001Sglebius	}
456290001Sglebius	gen->type = (u_char)type;
457290001Sglebius	gen->flag = (u_char)flag;
458290001Sglebius
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	 */
465290001Sglebius	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(
478290001Sglebius	const char *	dir,
479290001Sglebius	const char *	fname
48054359Sroberto	)
48154359Sroberto{
48254359Sroberto	/*
483290001Sglebius	 * dir cannot be changed dynamically
48454359Sroberto	 * (within the context of filegen)
48554359Sroberto	 * so just reject basenames containing '..'
48654359Sroberto	 *
48754359Sroberto	 * ASSUMPTION:
488290001Sglebius	 *		file system parts 'below' dir may be
48954359Sroberto	 *		specified without infringement of security
49054359Sroberto	 *
491290001Sglebius	 *		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	 */
495290001Sglebius	const char *p;
496290001Sglebius
49754359Sroberto	/*
49854359Sroberto	 * Just to catch, dumb errors opening up the world...
49954359Sroberto	 */
500290001Sglebius	if (NULL == dir || '\0' == dir[0])
501290001Sglebius		return FALSE;
50254359Sroberto
503290001Sglebius	if (NULL == fname)
504290001Sglebius		return FALSE;
505290001Sglebius
506290001Sglebius#ifdef SYS_WINNT
507290001Sglebius	/*
508290001Sglebius	 * Windows treats / equivalent to \, reject any / to ensure
509290001Sglebius	 * check below for DIR_SEP (\ on windows) are adequate.
510290001Sglebius	 */
511290001Sglebius	if (strchr(fname, '/')) {
512290001Sglebius		msyslog(LOG_ERR,
513290001Sglebius			"filegen filenames must not contain '/': %s",
514290001Sglebius			fname);
515290001Sglebius		return FALSE;
51654359Sroberto	}
517290001Sglebius#endif
518290001Sglebius
519290001Sglebius	for (p = fname; p != NULL; p = strchr(p, DIR_SEP)) {
520290001Sglebius		if ('.' == p[0] && '.' == p[1]
521290001Sglebius		    && ('\0' == p[2] || DIR_SEP == p[2]))
522290001Sglebius			return FALSE;
523290001Sglebius	}
524290001Sglebius
525290001Sglebius	return TRUE;
52654359Sroberto}
52754359Sroberto
52854359Sroberto
52954359Sroberto/*
53054359Sroberto * filegen registry
53154359Sroberto */
53254359Sroberto
53354359Srobertostatic struct filegen_entry {
534290001Sglebius	char *			name;
535290001Sglebius	FILEGEN *		filegen;
536290001Sglebius	struct filegen_entry *	next;
53754359Sroberto} *filegen_registry = NULL;
53854359Sroberto
53954359Sroberto
54054359SrobertoFILEGEN *
54154359Srobertofilegen_get(
542290001Sglebius	const char *	name
54354359Sroberto	)
54454359Sroberto{
54554359Sroberto	struct filegen_entry *f = filegen_registry;
54654359Sroberto
547290001Sglebius	while (f) {
54854359Sroberto		if (f->name == name || strcmp(name, f->name) == 0) {
549290001Sglebius			DPRINTF(4, ("filegen_get(%s) = %p\n",
550290001Sglebius				    name, f->filegen));
55154359Sroberto			return f->filegen;
55254359Sroberto		}
55354359Sroberto		f = f->next;
55454359Sroberto	}
555290001Sglebius	DPRINTF(4, ("filegen_get(%s) = NULL\n", name));
55654359Sroberto	return NULL;
55754359Sroberto}
55854359Sroberto
559290001Sglebius
56054359Srobertovoid
56154359Srobertofilegen_register(
562290001Sglebius	const char *	dir,
563290001Sglebius	const char *	name,
564290001Sglebius	FILEGEN *	filegen
56554359Sroberto	)
56654359Sroberto{
567290001Sglebius	struct filegen_entry **ppfe;
56854359Sroberto
569290001Sglebius	DPRINTF(4, ("filegen_register(%s, %p)\n", name, filegen));
570182007Sroberto
571290001Sglebius	filegen_init(dir, name, filegen);
572182007Sroberto
573290001Sglebius	ppfe = &filegen_registry;
574290001Sglebius	while (NULL != *ppfe) {
575290001Sglebius		if ((*ppfe)->name == name
576290001Sglebius		    || !strcmp((*ppfe)->name, name)) {
577290001Sglebius
578290001Sglebius			DPRINTF(5, ("replacing filegen %p\n",
579290001Sglebius				    (*ppfe)->filegen));
580290001Sglebius
581290001Sglebius			(*ppfe)->filegen = filegen;
58254359Sroberto			return;
58354359Sroberto		}
584290001Sglebius		ppfe = &((*ppfe)->next);
58554359Sroberto	}
58654359Sroberto
587290001Sglebius	*ppfe = emalloc(sizeof **ppfe);
588290001Sglebius
589290001Sglebius	(*ppfe)->next = NULL;
590290001Sglebius	(*ppfe)->name = estrdup(name);
591290001Sglebius	(*ppfe)->filegen = filegen;
592290001Sglebius
593290001Sglebius	DPRINTF(6, ("adding new filegen\n"));
59454359Sroberto
59554359Sroberto	return;
59654359Sroberto}
59754359Sroberto
598290001Sglebius
599290001Sglebius/*
600290001Sglebius * filegen_statsdir() - reset each filegen entry's dir to statsdir.
601290001Sglebius */
602290001Sglebiusvoid
603290001Sglebiusfilegen_statsdir(void)
604290001Sglebius{
605290001Sglebius	struct filegen_entry *f;
606290001Sglebius
607290001Sglebius	for (f = filegen_registry; f != NULL; f = f->next)
608290001Sglebius		filegen_config(f->filegen, statsdir, f->filegen->fname,
609290001Sglebius			       f->filegen->type, f->filegen->flag);
610290001Sglebius}
611290001Sglebius
612290001Sglebius
613290001Sglebius/*
614290001Sglebius * filegen_unregister frees memory allocated by filegen_register for
615290001Sglebius * name.
616290001Sglebius */
617290001Sglebius#ifdef DEBUG
618290001Sglebiusvoid
61954359Srobertofilegen_unregister(
620290001Sglebius	const char *name
62154359Sroberto	)
62254359Sroberto{
623290001Sglebius	struct filegen_entry **	ppfe;
624290001Sglebius	struct filegen_entry *	pfe;
625290001Sglebius	FILEGEN *		fg;
626290001Sglebius
627290001Sglebius	DPRINTF(4, ("filegen_unregister(%s)\n", name));
62854359Sroberto
629290001Sglebius	ppfe = &filegen_registry;
630290001Sglebius
631290001Sglebius	while (NULL != *ppfe) {
632290001Sglebius		if ((*ppfe)->name == name
633290001Sglebius		    || !strcmp((*ppfe)->name, name)) {
634290001Sglebius			pfe = *ppfe;
635290001Sglebius			*ppfe = (*ppfe)->next;
636290001Sglebius			fg = pfe->filegen;
637290001Sglebius			free(pfe->name);
638290001Sglebius			free(pfe);
639290001Sglebius			filegen_uninit(fg);
640290001Sglebius			break;
64154359Sroberto		}
642290001Sglebius		ppfe = &((*ppfe)->next);
64354359Sroberto	}
64454359Sroberto}
645290001Sglebius#endif	/* DEBUG */
646