Home | History | Annotate | Line # | Download | only in ntpd
      1  1.9  christos /*	$NetBSD: ntp_filegen.c,v 1.9 2020/05/25 20:47:25 christos Exp $	*/
      2  1.1    kardel 
      3  1.1    kardel /*
      4  1.1    kardel  * ntp_filegen.c,v 3.12 1994/01/25 19:06:11 kardel Exp
      5  1.1    kardel  *
      6  1.1    kardel  *  implements file generations support for NTP
      7  1.1    kardel  *  logfiles and statistic files
      8  1.1    kardel  *
      9  1.1    kardel  *
     10  1.1    kardel  * Copyright (C) 1992, 1996 by Rainer Pruy
     11  1.4  christos  * Friedrich-Alexander Universitaet Erlangen-Nuernberg, Germany
     12  1.1    kardel  *
     13  1.1    kardel  * This code may be modified and used freely
     14  1.1    kardel  * provided credits remain intact.
     15  1.1    kardel  */
     16  1.1    kardel 
     17  1.1    kardel #ifdef HAVE_CONFIG_H
     18  1.1    kardel # include <config.h>
     19  1.1    kardel #endif
     20  1.1    kardel 
     21  1.1    kardel #include <stdio.h>
     22  1.1    kardel #include <sys/types.h>
     23  1.1    kardel #include <sys/stat.h>
     24  1.1    kardel 
     25  1.1    kardel #include "ntpd.h"
     26  1.1    kardel #include "ntp_io.h"
     27  1.1    kardel #include "ntp_string.h"
     28  1.1    kardel #include "ntp_calendar.h"
     29  1.1    kardel #include "ntp_filegen.h"
     30  1.1    kardel #include "ntp_stdlib.h"
     31  1.1    kardel 
     32  1.1    kardel /*
     33  1.1    kardel  * NTP is intended to run long periods of time without restart.
     34  1.1    kardel  * Thus log and statistic files generated by NTP will grow large.
     35  1.1    kardel  *
     36  1.1    kardel  * this set of routines provides a central interface
     37  1.1    kardel  * to generating files using file generations
     38  1.1    kardel  *
     39  1.1    kardel  * the generation of a file is changed according to file generation type
     40  1.1    kardel  */
     41  1.1    kardel 
     42  1.1    kardel 
     43  1.1    kardel /*
     44  1.1    kardel  * redefine this if your system dislikes filename suffixes like
     45  1.1    kardel  * X.19910101 or X.1992W50 or ....
     46  1.1    kardel  */
     47  1.1    kardel #define SUFFIX_SEP '.'
     48  1.1    kardel 
     49  1.4  christos static	void	filegen_open	(FILEGEN *, u_int32, const time_t*);
     50  1.1    kardel static	int	valid_fileref	(const char *, const char *);
     51  1.1    kardel static	void	filegen_init	(const char *, const char *, FILEGEN *);
     52  1.1    kardel #ifdef	DEBUG
     53  1.1    kardel static	void	filegen_uninit		(FILEGEN *);
     54  1.1    kardel #endif	/* DEBUG */
     55  1.1    kardel 
     56  1.1    kardel 
     57  1.1    kardel /*
     58  1.1    kardel  * filegen_init
     59  1.1    kardel  */
     60  1.1    kardel 
     61  1.1    kardel static void
     62  1.1    kardel filegen_init(
     63  1.4  christos 	const char *	dir,
     64  1.4  christos 	const char *	fname,
     65  1.1    kardel 	FILEGEN *	fgp
     66  1.1    kardel 	)
     67  1.1    kardel {
     68  1.4  christos 	fgp->fp = NULL;
     69  1.4  christos 	fgp->dir = estrdup(dir);
     70  1.4  christos 	fgp->fname = estrdup(fname);
     71  1.4  christos 	fgp->id_lo = 0;
     72  1.4  christos 	fgp->id_hi = 0;
     73  1.4  christos 	fgp->type = FILEGEN_DAY;
     74  1.4  christos 	fgp->flag = FGEN_FLAG_LINK; /* not yet enabled !!*/
     75  1.1    kardel }
     76  1.1    kardel 
     77  1.1    kardel 
     78  1.1    kardel /*
     79  1.1    kardel  * filegen_uninit - free memory allocated by filegen_init
     80  1.1    kardel  */
     81  1.1    kardel #ifdef DEBUG
     82  1.1    kardel static void
     83  1.1    kardel filegen_uninit(
     84  1.4  christos 	FILEGEN *fgp
     85  1.1    kardel 	)
     86  1.1    kardel {
     87  1.4  christos 	free(fgp->dir);
     88  1.4  christos 	free(fgp->fname);
     89  1.1    kardel }
     90  1.1    kardel #endif
     91  1.1    kardel 
     92  1.1    kardel 
     93  1.1    kardel /*
     94  1.1    kardel  * open a file generation according to the current settings of gen
     95  1.1    kardel  * will also provide a link to basename if requested to do so
     96  1.1    kardel  */
     97  1.1    kardel 
     98  1.1    kardel static void
     99  1.1    kardel filegen_open(
    100  1.1    kardel 	FILEGEN *	gen,
    101  1.4  christos 	u_int32		stamp,
    102  1.4  christos 	const time_t *	pivot
    103  1.1    kardel 	)
    104  1.1    kardel {
    105  1.4  christos 	char *savename;	/* temp store for name collision handling */
    106  1.4  christos 	char *fullname;	/* name with any designation extension */
    107  1.4  christos 	char *filename;	/* name without designation extension */
    108  1.4  christos 	char *suffix;	/* where to print suffix extension */
    109  1.4  christos 	u_int len, suflen;
    110  1.1    kardel 	FILE *fp;
    111  1.1    kardel 	struct calendar cal;
    112  1.4  christos 	struct isodate	iso;
    113  1.4  christos 
    114  1.4  christos 	/* get basic filename in buffer, leave room for extensions */
    115  1.4  christos 	len = strlen(gen->dir) + strlen(gen->fname) + 65;
    116  1.4  christos 	filename = emalloc(len);
    117  1.4  christos 	fullname = emalloc(len);
    118  1.4  christos 	savename = NULL;
    119  1.4  christos 	snprintf(filename, len, "%s%s", gen->dir, gen->fname);
    120  1.4  christos 
    121  1.4  christos 	/* where to place suffix */
    122  1.4  christos 	suflen = strlcpy(fullname, filename, len);
    123  1.4  christos 	suffix = fullname + suflen;
    124  1.4  christos 	suflen = len - suflen;
    125  1.4  christos 
    126  1.4  christos 	/* last octet of fullname set to '\0' for truncation check */
    127  1.4  christos 	fullname[len - 1] = '\0';
    128  1.1    kardel 
    129  1.3    kardel 	switch (gen->type) {
    130  1.1    kardel 
    131  1.1    kardel 	default:
    132  1.1    kardel 		msyslog(LOG_ERR,
    133  1.1    kardel 			"unsupported file generations type %d for "
    134  1.1    kardel 			"\"%s\" - reverting to FILEGEN_NONE",
    135  1.4  christos 			gen->type, filename);
    136  1.1    kardel 		gen->type = FILEGEN_NONE;
    137  1.4  christos 		break;
    138  1.1    kardel 
    139  1.1    kardel 	case FILEGEN_NONE:
    140  1.4  christos 		/* no suffix, all set */
    141  1.1    kardel 		break;
    142  1.1    kardel 
    143  1.1    kardel 	case FILEGEN_PID:
    144  1.4  christos 		gen->id_lo = getpid();
    145  1.4  christos 		gen->id_hi = 0;
    146  1.4  christos 		snprintf(suffix, suflen, "%c#%ld",
    147  1.4  christos 			 SUFFIX_SEP, gen->id_lo);
    148  1.1    kardel 		break;
    149  1.1    kardel 
    150  1.1    kardel 	case FILEGEN_DAY:
    151  1.1    kardel 		/*
    152  1.1    kardel 		 * You can argue here in favor of using MJD, but I
    153  1.1    kardel 		 * would assume it to be easier for humans to interpret
    154  1.1    kardel 		 * dates in a format they are used to in everyday life.
    155  1.1    kardel 		 */
    156  1.4  christos 		ntpcal_ntp_to_date(&cal, stamp, pivot);
    157  1.4  christos 		snprintf(suffix, suflen, "%c%04d%02d%02d",
    158  1.4  christos 			 SUFFIX_SEP, cal.year, cal.month, cal.monthday);
    159  1.4  christos 		cal.hour = cal.minute = cal.second = 0;
    160  1.4  christos 		gen->id_lo = ntpcal_date_to_ntp(&cal);
    161  1.4  christos 		gen->id_hi = (u_int32)(gen->id_lo + SECSPERDAY);
    162  1.1    kardel 		break;
    163  1.1    kardel 
    164  1.1    kardel 	case FILEGEN_WEEK:
    165  1.4  christos 		isocal_ntp_to_date(&iso, stamp, pivot);
    166  1.4  christos 		snprintf(suffix, suflen, "%c%04dw%02d",
    167  1.4  christos 			 SUFFIX_SEP, iso.year, iso.week);
    168  1.4  christos 		iso.hour = iso.minute = iso.second = 0;
    169  1.4  christos 		iso.weekday = 1;
    170  1.4  christos 		gen->id_lo = isocal_date_to_ntp(&iso);
    171  1.4  christos 		gen->id_hi = (u_int32)(gen->id_lo + 7 * SECSPERDAY);
    172  1.1    kardel 		break;
    173  1.1    kardel 
    174  1.1    kardel 	case FILEGEN_MONTH:
    175  1.4  christos 		ntpcal_ntp_to_date(&cal, stamp, pivot);
    176  1.4  christos 		snprintf(suffix, suflen, "%c%04d%02d",
    177  1.4  christos 			 SUFFIX_SEP, cal.year, cal.month);
    178  1.4  christos 		cal.hour = cal.minute = cal.second = 0;
    179  1.4  christos 		cal.monthday = 1;
    180  1.4  christos 		gen->id_lo = ntpcal_date_to_ntp(&cal);
    181  1.4  christos 		cal.month++;
    182  1.4  christos 		gen->id_hi = ntpcal_date_to_ntp(&cal);
    183  1.1    kardel 		break;
    184  1.1    kardel 
    185  1.1    kardel 	case FILEGEN_YEAR:
    186  1.4  christos 		ntpcal_ntp_to_date(&cal, stamp, pivot);
    187  1.4  christos 		snprintf(suffix, suflen, "%c%04d",
    188  1.4  christos 			 SUFFIX_SEP, cal.year);
    189  1.4  christos 		cal.hour = cal.minute = cal.second = 0;
    190  1.4  christos 		cal.month = cal.monthday = 1;
    191  1.4  christos 		gen->id_lo = ntpcal_date_to_ntp(&cal);
    192  1.4  christos 		cal.year++;
    193  1.4  christos 		gen->id_hi = ntpcal_date_to_ntp(&cal);
    194  1.1    kardel 		break;
    195  1.1    kardel 
    196  1.1    kardel 	case FILEGEN_AGE:
    197  1.4  christos 		gen->id_lo = current_time - (current_time % SECSPERDAY);
    198  1.4  christos 		gen->id_hi = gen->id_lo + SECSPERDAY;
    199  1.4  christos 		snprintf(suffix, suflen, "%ca%08ld",
    200  1.4  christos 			 SUFFIX_SEP, gen->id_lo);
    201  1.1    kardel 	}
    202  1.1    kardel 
    203  1.4  christos 	/* check possible truncation */
    204  1.4  christos 	if ('\0' != fullname[len - 1]) {
    205  1.4  christos 		fullname[len - 1] = '\0';
    206  1.4  christos 		msyslog(LOG_ERR, "logfile name truncated: \"%s\"",
    207  1.4  christos 			fullname);
    208  1.4  christos 	}
    209  1.4  christos 
    210  1.1    kardel 	if (FILEGEN_NONE != gen->type) {
    211  1.1    kardel 		/*
    212  1.1    kardel 		 * check for existence of a file with name 'basename'
    213  1.1    kardel 		 * as we disallow such a file
    214  1.1    kardel 		 * if FGEN_FLAG_LINK is set create a link
    215  1.1    kardel 		 */
    216  1.1    kardel 		struct stat stats;
    217  1.1    kardel 		/*
    218  1.1    kardel 		 * try to resolve name collisions
    219  1.1    kardel 		 */
    220  1.1    kardel 		static u_long conflicts = 0;
    221  1.1    kardel 
    222  1.1    kardel #ifndef	S_ISREG
    223  1.1    kardel #define	S_ISREG(mode)	(((mode) & S_IFREG) == S_IFREG)
    224  1.1    kardel #endif
    225  1.4  christos 		if (stat(filename, &stats) == 0) {
    226  1.1    kardel 			/* Hm, file exists... */
    227  1.1    kardel 			if (S_ISREG(stats.st_mode)) {
    228  1.1    kardel 				if (stats.st_nlink <= 1)	{
    229  1.1    kardel 					/*
    230  1.1    kardel 					 * Oh, it is not linked - try to save it
    231  1.1    kardel 					 */
    232  1.4  christos 					savename = emalloc(len);
    233  1.4  christos 					snprintf(savename, len,
    234  1.1    kardel 						"%s%c%dC%lu",
    235  1.4  christos 						filename, SUFFIX_SEP,
    236  1.1    kardel 						(int)getpid(), conflicts++);
    237  1.1    kardel 
    238  1.4  christos 					if (rename(filename, savename) != 0)
    239  1.1    kardel 						msyslog(LOG_ERR,
    240  1.1    kardel 							"couldn't save %s: %m",
    241  1.4  christos 							filename);
    242  1.1    kardel 					free(savename);
    243  1.1    kardel 				} else {
    244  1.1    kardel 					/*
    245  1.1    kardel 					 * there is at least a second link to
    246  1.1    kardel 					 * this file.
    247  1.1    kardel 					 * just remove the conflicting one
    248  1.1    kardel 					 */
    249  1.1    kardel 					if (
    250  1.1    kardel #if !defined(VMS)
    251  1.4  christos 						unlink(filename) != 0
    252  1.1    kardel #else
    253  1.4  christos 						delete(filename) != 0
    254  1.1    kardel #endif
    255  1.1    kardel 						)
    256  1.1    kardel 						msyslog(LOG_ERR,
    257  1.1    kardel 							"couldn't unlink %s: %m",
    258  1.4  christos 							filename);
    259  1.1    kardel 				}
    260  1.1    kardel 			} else {
    261  1.1    kardel 				/*
    262  1.1    kardel 				 * Ehh? Not a regular file ?? strange !!!!
    263  1.1    kardel 				 */
    264  1.1    kardel 				msyslog(LOG_ERR,
    265  1.1    kardel 					"expected regular file for %s "
    266  1.1    kardel 					"(found mode 0%lo)",
    267  1.4  christos 					filename,
    268  1.1    kardel 					(unsigned long)stats.st_mode);
    269  1.1    kardel 			}
    270  1.1    kardel 		} else {
    271  1.1    kardel 			/*
    272  1.1    kardel 			 * stat(..) failed, but it is absolutely correct for
    273  1.1    kardel 			 * 'basename' not to exist
    274  1.1    kardel 			 */
    275  1.1    kardel 			if (ENOENT != errno)
    276  1.1    kardel 				msyslog(LOG_ERR, "stat(%s) failed: %m",
    277  1.4  christos 						 filename);
    278  1.1    kardel 		}
    279  1.1    kardel 	}
    280  1.1    kardel 
    281  1.1    kardel 	/*
    282  1.1    kardel 	 * now, try to open new file generation...
    283  1.1    kardel 	 */
    284  1.4  christos 	DPRINTF(4, ("opening filegen (type=%d/stamp=%u) \"%s\"\n",
    285  1.4  christos 		    gen->type, stamp, fullname));
    286  1.4  christos 
    287  1.4  christos 	fp = fopen(fullname, "a");
    288  1.1    kardel 
    289  1.1    kardel 	if (NULL == fp)	{
    290  1.1    kardel 		/* open failed -- keep previous state
    291  1.1    kardel 		 *
    292  1.1    kardel 		 * If the file was open before keep the previous generation.
    293  1.1    kardel 		 * This will cause output to end up in the 'wrong' file,
    294  1.1    kardel 		 * but I think this is still better than losing output
    295  1.1    kardel 		 *
    296  1.1    kardel 		 * ignore errors due to missing directories
    297  1.1    kardel 		 */
    298  1.1    kardel 
    299  1.1    kardel 		if (ENOENT != errno)
    300  1.4  christos 			msyslog(LOG_ERR, "can't open %s: %m", fullname);
    301  1.1    kardel 	} else {
    302  1.1    kardel 		if (NULL != gen->fp) {
    303  1.1    kardel 			fclose(gen->fp);
    304  1.1    kardel 			gen->fp = NULL;
    305  1.1    kardel 		}
    306  1.1    kardel 		gen->fp = fp;
    307  1.1    kardel 
    308  1.1    kardel 		if (gen->flag & FGEN_FLAG_LINK) {
    309  1.1    kardel 			/*
    310  1.1    kardel 			 * need to link file to basename
    311  1.1    kardel 			 * have to use hardlink for now as I want to allow
    312  1.1    kardel 			 * gen->basename spanning directory levels
    313  1.1    kardel 			 * this would make it more complex to get the correct
    314  1.4  christos 			 * fullname for symlink
    315  1.1    kardel 			 *
    316  1.1    kardel 			 * Ok, it would just mean taking the part following
    317  1.1    kardel 			 * the last '/' in the name.... Should add it later....
    318  1.1    kardel 			 */
    319  1.1    kardel 
    320  1.1    kardel 			/* Windows NT does not support file links -Greg Schueman 1/18/97 */
    321  1.1    kardel 
    322  1.4  christos #if defined(SYS_WINNT) || defined(SYS_VXWORKS)
    323  1.1    kardel 			SetLastError(0); /* On WinNT, don't support FGEN_FLAG_LINK */
    324  1.1    kardel #elif defined(VMS)
    325  1.1    kardel 			errno = 0; /* On VMS, don't support FGEN_FLAG_LINK */
    326  1.1    kardel #else  /* not (VMS) / VXWORKS / WINNT ; DO THE LINK) */
    327  1.4  christos 			if (link(fullname, filename) != 0)
    328  1.1    kardel 				if (EEXIST != errno)
    329  1.1    kardel 					msyslog(LOG_ERR,
    330  1.1    kardel 						"can't link(%s, %s): %m",
    331  1.4  christos 						fullname, filename);
    332  1.1    kardel #endif /* SYS_WINNT || VXWORKS */
    333  1.1    kardel 		}		/* flags & FGEN_FLAG_LINK */
    334  1.1    kardel 	}			/* else fp == NULL */
    335  1.1    kardel 
    336  1.1    kardel 	free(filename);
    337  1.4  christos 	free(fullname);
    338  1.1    kardel 	return;
    339  1.1    kardel }
    340  1.1    kardel 
    341  1.1    kardel /*
    342  1.1    kardel  * this function sets up gen->fp to point to the correct
    343  1.1    kardel  * generation of the file for the time specified by 'now'
    344  1.1    kardel  *
    345  1.1    kardel  * 'now' usually is interpreted as second part of a l_fp as is in the cal...
    346  1.1    kardel  * library routines
    347  1.1    kardel  */
    348  1.1    kardel 
    349  1.1    kardel void
    350  1.1    kardel filegen_setup(
    351  1.1    kardel 	FILEGEN *	gen,
    352  1.4  christos 	u_int32		now
    353  1.1    kardel 	)
    354  1.1    kardel {
    355  1.4  christos 	int	current;
    356  1.4  christos 	time_t	pivot;
    357  1.1    kardel 
    358  1.1    kardel 	if (!(gen->flag & FGEN_FLAG_ENABLED)) {
    359  1.1    kardel 		if (NULL != gen->fp) {
    360  1.1    kardel 			fclose(gen->fp);
    361  1.1    kardel 			gen->fp = NULL;
    362  1.1    kardel 		}
    363  1.1    kardel 		return;
    364  1.1    kardel 	}
    365  1.4  christos 
    366  1.1    kardel 	switch (gen->type) {
    367  1.1    kardel 
    368  1.4  christos 	default:
    369  1.1    kardel 	case FILEGEN_NONE:
    370  1.4  christos 		current = TRUE;
    371  1.1    kardel 		break;
    372  1.1    kardel 
    373  1.1    kardel 	case FILEGEN_PID:
    374  1.4  christos 		current = ((int)gen->id_lo == getpid());
    375  1.1    kardel 		break;
    376  1.1    kardel 
    377  1.4  christos 	case FILEGEN_AGE:
    378  1.4  christos 		current = (gen->id_lo <= current_time) &&
    379  1.4  christos 			  (gen->id_hi > current_time);
    380  1.1    kardel 		break;
    381  1.1    kardel 
    382  1.4  christos 	case FILEGEN_DAY:
    383  1.1    kardel 	case FILEGEN_WEEK:
    384  1.1    kardel 	case FILEGEN_MONTH:
    385  1.1    kardel 	case FILEGEN_YEAR:
    386  1.4  christos 		current = (gen->id_lo <= now) &&
    387  1.4  christos 			  (gen->id_hi > now);
    388  1.1    kardel 		break;
    389  1.1    kardel 	}
    390  1.1    kardel 	/*
    391  1.1    kardel 	 * try to open file if not yet open
    392  1.1    kardel 	 * reopen new file generation file on change of generation id
    393  1.1    kardel 	 */
    394  1.4  christos 	if (NULL == gen->fp || !current) {
    395  1.4  christos 		DPRINTF(1, ("filegen  %0x %u\n", gen->type, now));
    396  1.4  christos 		pivot = time(NULL);
    397  1.4  christos 		filegen_open(gen, now, &pivot);
    398  1.1    kardel 	}
    399  1.1    kardel }
    400  1.1    kardel 
    401  1.1    kardel 
    402  1.1    kardel /*
    403  1.1    kardel  * change settings for filegen files
    404  1.1    kardel  */
    405  1.1    kardel void
    406  1.1    kardel filegen_config(
    407  1.1    kardel 	FILEGEN *	gen,
    408  1.4  christos 	const char *	dir,
    409  1.4  christos 	const char *	fname,
    410  1.1    kardel 	u_int		type,
    411  1.1    kardel 	u_int		flag
    412  1.1    kardel 	)
    413  1.1    kardel {
    414  1.4  christos 	int file_existed;
    415  1.4  christos 	l_fp now;
    416  1.4  christos 
    417  1.1    kardel 
    418  1.1    kardel 	/*
    419  1.1    kardel 	 * if nothing would be changed...
    420  1.1    kardel 	 */
    421  1.4  christos 	if (strcmp(dir, gen->dir) == 0 && strcmp(fname, gen->fname) == 0
    422  1.4  christos 	    && type == gen->type && flag == gen->flag)
    423  1.1    kardel 		return;
    424  1.4  christos 
    425  1.1    kardel 	/*
    426  1.1    kardel 	 * validate parameters
    427  1.1    kardel 	 */
    428  1.4  christos 	if (!valid_fileref(dir, fname))
    429  1.1    kardel 		return;
    430  1.1    kardel 
    431  1.1    kardel 	if (NULL != gen->fp) {
    432  1.1    kardel 		fclose(gen->fp);
    433  1.1    kardel 		gen->fp = NULL;
    434  1.4  christos 		file_existed = TRUE;
    435  1.4  christos 	} else {
    436  1.4  christos 		file_existed = FALSE;
    437  1.1    kardel 	}
    438  1.1    kardel 
    439  1.1    kardel 	DPRINTF(3, ("configuring filegen:\n"
    440  1.4  christos 		    "\tdir:\t%s -> %s\n"
    441  1.4  christos 		    "\tfname:\t%s -> %s\n"
    442  1.1    kardel 		    "\ttype:\t%d -> %d\n"
    443  1.1    kardel 		    "\tflag: %x -> %x\n",
    444  1.4  christos 		    gen->dir, dir,
    445  1.4  christos 		    gen->fname, fname,
    446  1.1    kardel 		    gen->type, type,
    447  1.1    kardel 		    gen->flag, flag));
    448  1.1    kardel 
    449  1.4  christos 	if (strcmp(gen->dir, dir) != 0) {
    450  1.4  christos 		free(gen->dir);
    451  1.4  christos 		gen->dir = estrdup(dir);
    452  1.4  christos 	}
    453  1.4  christos 
    454  1.4  christos 	if (strcmp(gen->fname, fname) != 0) {
    455  1.4  christos 		free(gen->fname);
    456  1.4  christos 		gen->fname = estrdup(fname);
    457  1.1    kardel 	}
    458  1.3    kardel 	gen->type = (u_char)type;
    459  1.3    kardel 	gen->flag = (u_char)flag;
    460  1.1    kardel 
    461  1.1    kardel 	/*
    462  1.1    kardel 	 * make filegen use the new settings
    463  1.1    kardel 	 * special action is only required when a generation file
    464  1.1    kardel 	 * is currently open
    465  1.1    kardel 	 * otherwise the new settings will be used anyway at the next open
    466  1.1    kardel 	 */
    467  1.1    kardel 	if (file_existed) {
    468  1.1    kardel 		get_systime(&now);
    469  1.1    kardel 		filegen_setup(gen, now.l_ui);
    470  1.1    kardel 	}
    471  1.1    kardel }
    472  1.1    kardel 
    473  1.1    kardel 
    474  1.1    kardel /*
    475  1.1    kardel  * check whether concatenating prefix and basename
    476  1.1    kardel  * yields a legal filename
    477  1.1    kardel  */
    478  1.1    kardel static int
    479  1.1    kardel valid_fileref(
    480  1.4  christos 	const char *	dir,
    481  1.4  christos 	const char *	fname
    482  1.1    kardel 	)
    483  1.1    kardel {
    484  1.1    kardel 	/*
    485  1.4  christos 	 * dir cannot be changed dynamically
    486  1.1    kardel 	 * (within the context of filegen)
    487  1.1    kardel 	 * so just reject basenames containing '..'
    488  1.1    kardel 	 *
    489  1.1    kardel 	 * ASSUMPTION:
    490  1.4  christos 	 *		file system parts 'below' dir may be
    491  1.1    kardel 	 *		specified without infringement of security
    492  1.1    kardel 	 *
    493  1.4  christos 	 *		restricting dir to legal values
    494  1.1    kardel 	 *		has to be ensured by other means
    495  1.1    kardel 	 * (however, it would be possible to perform some checks here...)
    496  1.1    kardel 	 */
    497  1.4  christos 	const char *p;
    498  1.4  christos 
    499  1.1    kardel 	/*
    500  1.1    kardel 	 * Just to catch, dumb errors opening up the world...
    501  1.1    kardel 	 */
    502  1.4  christos 	if (NULL == dir || '\0' == dir[0])
    503  1.4  christos 		return FALSE;
    504  1.1    kardel 
    505  1.4  christos 	if (NULL == fname)
    506  1.4  christos 		return FALSE;
    507  1.4  christos 
    508  1.4  christos #ifdef SYS_WINNT
    509  1.4  christos 	/*
    510  1.4  christos 	 * Windows treats / equivalent to \, reject any / to ensure
    511  1.4  christos 	 * check below for DIR_SEP (\ on windows) are adequate.
    512  1.4  christos 	 */
    513  1.4  christos 	if (strchr(fname, '/')) {
    514  1.4  christos 		msyslog(LOG_ERR,
    515  1.4  christos 			"filegen filenames must not contain '/': %s",
    516  1.4  christos 			fname);
    517  1.4  christos 		return FALSE;
    518  1.4  christos 	}
    519  1.4  christos #endif
    520  1.4  christos 
    521  1.4  christos 	for (p = fname; p != NULL; p = strchr(p, DIR_SEP)) {
    522  1.1    kardel 		if ('.' == p[0] && '.' == p[1]
    523  1.1    kardel 		    && ('\0' == p[2] || DIR_SEP == p[2]))
    524  1.4  christos 			return FALSE;
    525  1.1    kardel 	}
    526  1.4  christos 
    527  1.4  christos 	return TRUE;
    528  1.1    kardel }
    529  1.1    kardel 
    530  1.1    kardel 
    531  1.1    kardel /*
    532  1.1    kardel  * filegen registry
    533  1.1    kardel  */
    534  1.1    kardel 
    535  1.1    kardel static struct filegen_entry {
    536  1.1    kardel 	char *			name;
    537  1.1    kardel 	FILEGEN *		filegen;
    538  1.1    kardel 	struct filegen_entry *	next;
    539  1.1    kardel } *filegen_registry = NULL;
    540  1.1    kardel 
    541  1.1    kardel 
    542  1.1    kardel FILEGEN *
    543  1.1    kardel filegen_get(
    544  1.1    kardel 	const char *	name
    545  1.1    kardel 	)
    546  1.1    kardel {
    547  1.1    kardel 	struct filegen_entry *f = filegen_registry;
    548  1.1    kardel 
    549  1.1    kardel 	while (f) {
    550  1.1    kardel 		if (f->name == name || strcmp(name, f->name) == 0) {
    551  1.1    kardel 			DPRINTF(4, ("filegen_get(%s) = %p\n",
    552  1.1    kardel 				    name, f->filegen));
    553  1.1    kardel 			return f->filegen;
    554  1.1    kardel 		}
    555  1.1    kardel 		f = f->next;
    556  1.1    kardel 	}
    557  1.1    kardel 	DPRINTF(4, ("filegen_get(%s) = NULL\n", name));
    558  1.1    kardel 	return NULL;
    559  1.1    kardel }
    560  1.1    kardel 
    561  1.1    kardel 
    562  1.1    kardel void
    563  1.1    kardel filegen_register(
    564  1.4  christos 	const char *	dir,
    565  1.1    kardel 	const char *	name,
    566  1.1    kardel 	FILEGEN *	filegen
    567  1.1    kardel 	)
    568  1.1    kardel {
    569  1.1    kardel 	struct filegen_entry **ppfe;
    570  1.1    kardel 
    571  1.1    kardel 	DPRINTF(4, ("filegen_register(%s, %p)\n", name, filegen));
    572  1.1    kardel 
    573  1.4  christos 	filegen_init(dir, name, filegen);
    574  1.1    kardel 
    575  1.1    kardel 	ppfe = &filegen_registry;
    576  1.1    kardel 	while (NULL != *ppfe) {
    577  1.1    kardel 		if ((*ppfe)->name == name
    578  1.1    kardel 		    || !strcmp((*ppfe)->name, name)) {
    579  1.1    kardel 
    580  1.1    kardel 			DPRINTF(5, ("replacing filegen %p\n",
    581  1.1    kardel 				    (*ppfe)->filegen));
    582  1.1    kardel 
    583  1.1    kardel 			(*ppfe)->filegen = filegen;
    584  1.1    kardel 			return;
    585  1.1    kardel 		}
    586  1.1    kardel 		ppfe = &((*ppfe)->next);
    587  1.1    kardel 	}
    588  1.1    kardel 
    589  1.1    kardel 	*ppfe = emalloc(sizeof **ppfe);
    590  1.1    kardel 
    591  1.1    kardel 	(*ppfe)->next = NULL;
    592  1.1    kardel 	(*ppfe)->name = estrdup(name);
    593  1.1    kardel 	(*ppfe)->filegen = filegen;
    594  1.1    kardel 
    595  1.1    kardel 	DPRINTF(6, ("adding new filegen\n"));
    596  1.1    kardel 
    597  1.1    kardel 	return;
    598  1.1    kardel }
    599  1.1    kardel 
    600  1.1    kardel 
    601  1.1    kardel /*
    602  1.4  christos  * filegen_statsdir() - reset each filegen entry's dir to statsdir.
    603  1.4  christos  */
    604  1.4  christos void
    605  1.4  christos filegen_statsdir(void)
    606  1.4  christos {
    607  1.4  christos 	struct filegen_entry *f;
    608  1.4  christos 
    609  1.4  christos 	for (f = filegen_registry; f != NULL; f = f->next)
    610  1.4  christos 		filegen_config(f->filegen, statsdir, f->filegen->fname,
    611  1.4  christos 			       f->filegen->type, f->filegen->flag);
    612  1.4  christos }
    613  1.4  christos 
    614  1.4  christos 
    615  1.4  christos /*
    616  1.1    kardel  * filegen_unregister frees memory allocated by filegen_register for
    617  1.1    kardel  * name.
    618  1.1    kardel  */
    619  1.1    kardel #ifdef DEBUG
    620  1.1    kardel void
    621  1.1    kardel filegen_unregister(
    622  1.2  christos 	const char *name
    623  1.1    kardel 	)
    624  1.1    kardel {
    625  1.1    kardel 	struct filegen_entry **	ppfe;
    626  1.1    kardel 	struct filegen_entry *	pfe;
    627  1.1    kardel 	FILEGEN *		fg;
    628  1.1    kardel 
    629  1.1    kardel 	DPRINTF(4, ("filegen_unregister(%s)\n", name));
    630  1.1    kardel 
    631  1.1    kardel 	ppfe = &filegen_registry;
    632  1.1    kardel 
    633  1.1    kardel 	while (NULL != *ppfe) {
    634  1.1    kardel 		if ((*ppfe)->name == name
    635  1.1    kardel 		    || !strcmp((*ppfe)->name, name)) {
    636  1.1    kardel 			pfe = *ppfe;
    637  1.1    kardel 			*ppfe = (*ppfe)->next;
    638  1.1    kardel 			fg = pfe->filegen;
    639  1.1    kardel 			free(pfe->name);
    640  1.1    kardel 			free(pfe);
    641  1.1    kardel 			filegen_uninit(fg);
    642  1.1    kardel 			break;
    643  1.1    kardel 		}
    644  1.1    kardel 		ppfe = &((*ppfe)->next);
    645  1.1    kardel 	}
    646  1.1    kardel }
    647  1.1    kardel #endif	/* DEBUG */
    648