Home | History | Annotate | Line # | Download | only in ntpd
ntp_filegen.c revision 1.2
      1  1.2  christos /*	$NetBSD: ntp_filegen.c,v 1.2 2010/12/04 23:08:35 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.1    kardel  * Friedrich-Alexander Universitt Erlangen-Nrnberg, 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.1    kardel static	void	filegen_open	(FILEGEN *, u_long);
     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.1    kardel 	const char *	prefix,
     64  1.1    kardel 	const char *	basename,
     65  1.1    kardel 	FILEGEN *	fgp
     66  1.1    kardel 	)
     67  1.1    kardel {
     68  1.1    kardel 	fgp->fp       = NULL;
     69  1.1    kardel 	fgp->prefix   = prefix;		/* Yes, this is TOTALLY lame! */
     70  1.1    kardel 	fgp->basename = estrdup(basename);
     71  1.1    kardel 	fgp->id       = 0;
     72  1.1    kardel 	fgp->type     = FILEGEN_DAY;
     73  1.1    kardel 	fgp->flag     = FGEN_FLAG_LINK; /* not yet enabled !!*/
     74  1.1    kardel }
     75  1.1    kardel 
     76  1.1    kardel 
     77  1.1    kardel /*
     78  1.1    kardel  * filegen_uninit - free memory allocated by filegen_init
     79  1.1    kardel  */
     80  1.1    kardel #ifdef DEBUG
     81  1.1    kardel static void
     82  1.1    kardel filegen_uninit(
     83  1.1    kardel 	FILEGEN *	fgp
     84  1.1    kardel 	)
     85  1.1    kardel {
     86  1.1    kardel 	free(fgp->basename);
     87  1.1    kardel }
     88  1.1    kardel #endif
     89  1.1    kardel 
     90  1.1    kardel 
     91  1.1    kardel /*
     92  1.1    kardel  * open a file generation according to the current settings of gen
     93  1.1    kardel  * will also provide a link to basename if requested to do so
     94  1.1    kardel  */
     95  1.1    kardel 
     96  1.1    kardel static void
     97  1.1    kardel filegen_open(
     98  1.1    kardel 	FILEGEN *	gen,
     99  1.1    kardel 	u_long		newid
    100  1.1    kardel 	)
    101  1.1    kardel {
    102  1.1    kardel 	char *filename;
    103  1.1    kardel 	char *basename;
    104  1.1    kardel 	u_int len;
    105  1.1    kardel 	FILE *fp;
    106  1.1    kardel 	struct calendar cal;
    107  1.1    kardel 
    108  1.1    kardel 	len = strlen(gen->prefix) + strlen(gen->basename) + 1;
    109  1.1    kardel 	basename = emalloc(len);
    110  1.1    kardel 	snprintf(basename, len, "%s%s", gen->prefix, gen->basename);
    111  1.1    kardel 
    112  1.1    kardel 	switch(gen->type) {
    113  1.1    kardel 
    114  1.1    kardel 	default:
    115  1.1    kardel 		msyslog(LOG_ERR,
    116  1.1    kardel 			"unsupported file generations type %d for "
    117  1.1    kardel 			"\"%s\" - reverting to FILEGEN_NONE",
    118  1.1    kardel 			gen->type, basename);
    119  1.1    kardel 		gen->type = FILEGEN_NONE;
    120  1.1    kardel 		/* fall through to FILEGEN_NONE */
    121  1.1    kardel 
    122  1.1    kardel 	case FILEGEN_NONE:
    123  1.1    kardel 		filename = estrdup(basename);
    124  1.1    kardel 		break;
    125  1.1    kardel 
    126  1.1    kardel 	case FILEGEN_PID:
    127  1.1    kardel 		filename = emalloc(len + 1 + 1 + 10);
    128  1.1    kardel 		snprintf(filename, len + 1 + 1 + 10,
    129  1.1    kardel 			 "%s%c#%ld",
    130  1.1    kardel 			 basename, SUFFIX_SEP, newid);
    131  1.1    kardel 		break;
    132  1.1    kardel 
    133  1.1    kardel 	case FILEGEN_DAY:
    134  1.1    kardel 		/*
    135  1.1    kardel 		 * You can argue here in favor of using MJD, but I
    136  1.1    kardel 		 * would assume it to be easier for humans to interpret
    137  1.1    kardel 		 * dates in a format they are used to in everyday life.
    138  1.1    kardel 		 */
    139  1.1    kardel 		caljulian(newid, &cal);
    140  1.1    kardel 		filename = emalloc(len + 1 + 4 + 2 + 2);
    141  1.1    kardel 		snprintf(filename, len + 1 + 4 + 2 + 2,
    142  1.1    kardel 			 "%s%c%04d%02d%02d",
    143  1.1    kardel 			 basename, SUFFIX_SEP,
    144  1.1    kardel 			 cal.year, cal.month, cal.monthday);
    145  1.1    kardel 		break;
    146  1.1    kardel 
    147  1.1    kardel 	case FILEGEN_WEEK:
    148  1.1    kardel 		/*
    149  1.1    kardel 		 * This is still a hack
    150  1.1    kardel 		 * - the term week is not correlated to week as it is used
    151  1.1    kardel 		 *   normally - it just refers to a period of 7 days
    152  1.1    kardel 		 *   starting at Jan 1 - 'weeks' are counted starting from zero
    153  1.1    kardel 		 */
    154  1.1    kardel 		caljulian(newid, &cal);
    155  1.1    kardel 		filename = emalloc(len + 1 + 4 + 1 + 2);
    156  1.1    kardel 		snprintf(filename, len + 1 + 4 + 1 + 2,
    157  1.1    kardel 			 "%s%c%04dw%02d",
    158  1.1    kardel 			 basename, SUFFIX_SEP,
    159  1.1    kardel 			 cal.year, cal.yearday / 7);
    160  1.1    kardel 		break;
    161  1.1    kardel 
    162  1.1    kardel 	case FILEGEN_MONTH:
    163  1.1    kardel 		caljulian(newid, &cal);
    164  1.1    kardel 		filename = emalloc(len + 1 + 4 + 2);
    165  1.1    kardel 		snprintf(filename, len + 1 + 4 + 2,
    166  1.1    kardel 			 "%s%c%04d%02d",
    167  1.1    kardel 			 basename, SUFFIX_SEP, cal.year, cal.month);
    168  1.1    kardel 		break;
    169  1.1    kardel 
    170  1.1    kardel 	case FILEGEN_YEAR:
    171  1.1    kardel 		caljulian(newid, &cal);
    172  1.1    kardel 		filename = emalloc(len + 1 + 4);
    173  1.1    kardel 		snprintf(filename, len + 1 + 4,
    174  1.1    kardel 			 "%s%c%04d",
    175  1.1    kardel 			 basename, SUFFIX_SEP, cal.year);
    176  1.1    kardel 		break;
    177  1.1    kardel 
    178  1.1    kardel 	case FILEGEN_AGE:
    179  1.1    kardel 		filename = emalloc(len + 1 + 2 + 10);
    180  1.1    kardel 		snprintf(filename, len + 1 + 2 + 10,
    181  1.1    kardel 			 "%s%ca%08ld",
    182  1.1    kardel 			 basename, SUFFIX_SEP, newid);
    183  1.1    kardel 	}
    184  1.1    kardel 
    185  1.1    kardel 	if (FILEGEN_NONE != gen->type) {
    186  1.1    kardel 		/*
    187  1.1    kardel 		 * check for existence of a file with name 'basename'
    188  1.1    kardel 		 * as we disallow such a file
    189  1.1    kardel 		 * if FGEN_FLAG_LINK is set create a link
    190  1.1    kardel 		 */
    191  1.1    kardel 		struct stat stats;
    192  1.1    kardel 		/*
    193  1.1    kardel 		 * try to resolve name collisions
    194  1.1    kardel 		 */
    195  1.1    kardel 		static u_long conflicts = 0;
    196  1.1    kardel 
    197  1.1    kardel #ifndef	S_ISREG
    198  1.1    kardel #define	S_ISREG(mode)	(((mode) & S_IFREG) == S_IFREG)
    199  1.1    kardel #endif
    200  1.1    kardel 		if (stat(basename, &stats) == 0) {
    201  1.1    kardel 			/* Hm, file exists... */
    202  1.1    kardel 			if (S_ISREG(stats.st_mode)) {
    203  1.1    kardel 				if (stats.st_nlink <= 1)	{
    204  1.1    kardel 					/*
    205  1.1    kardel 					 * Oh, it is not linked - try to save it
    206  1.1    kardel 					 */
    207  1.1    kardel 					char *savename;
    208  1.1    kardel 
    209  1.1    kardel 					savename = emalloc(len + 1 + 1 + 10 + 10);
    210  1.1    kardel 					snprintf(savename, len + 1 + 1 + 10 + 10,
    211  1.1    kardel 						"%s%c%dC%lu",
    212  1.1    kardel 						basename, SUFFIX_SEP,
    213  1.1    kardel 						(int)getpid(), conflicts++);
    214  1.1    kardel 
    215  1.1    kardel 					if (rename(basename, savename) != 0)
    216  1.1    kardel 						msyslog(LOG_ERR,
    217  1.1    kardel 							"couldn't save %s: %m",
    218  1.1    kardel 							basename);
    219  1.1    kardel 					free(savename);
    220  1.1    kardel 				} else {
    221  1.1    kardel 					/*
    222  1.1    kardel 					 * there is at least a second link to
    223  1.1    kardel 					 * this file.
    224  1.1    kardel 					 * just remove the conflicting one
    225  1.1    kardel 					 */
    226  1.1    kardel 					if (
    227  1.1    kardel #if !defined(VMS)
    228  1.1    kardel 						unlink(basename) != 0
    229  1.1    kardel #else
    230  1.1    kardel 						delete(basename) != 0
    231  1.1    kardel #endif
    232  1.1    kardel 						)
    233  1.1    kardel 						msyslog(LOG_ERR,
    234  1.1    kardel 							"couldn't unlink %s: %m",
    235  1.1    kardel 							basename);
    236  1.1    kardel 				}
    237  1.1    kardel 			} else {
    238  1.1    kardel 				/*
    239  1.1    kardel 				 * Ehh? Not a regular file ?? strange !!!!
    240  1.1    kardel 				 */
    241  1.1    kardel 				msyslog(LOG_ERR,
    242  1.1    kardel 					"expected regular file for %s "
    243  1.1    kardel 					"(found mode 0%lo)",
    244  1.1    kardel 					basename,
    245  1.1    kardel 					(unsigned long)stats.st_mode);
    246  1.1    kardel 			}
    247  1.1    kardel 		} else {
    248  1.1    kardel 			/*
    249  1.1    kardel 			 * stat(..) failed, but it is absolutely correct for
    250  1.1    kardel 			 * 'basename' not to exist
    251  1.1    kardel 			 */
    252  1.1    kardel 			if (ENOENT != errno)
    253  1.1    kardel 				msyslog(LOG_ERR, "stat(%s) failed: %m",
    254  1.1    kardel 						 basename);
    255  1.1    kardel 		}
    256  1.1    kardel 	}
    257  1.1    kardel 
    258  1.1    kardel 	/*
    259  1.1    kardel 	 * now, try to open new file generation...
    260  1.1    kardel 	 */
    261  1.1    kardel 	fp = fopen(filename, "a");
    262  1.1    kardel 
    263  1.1    kardel 	DPRINTF(4, ("opening filegen (type=%d/id=%lu) \"%s\"\n",
    264  1.1    kardel 		    gen->type, newid, filename));
    265  1.1    kardel 
    266  1.1    kardel 	if (NULL == fp)	{
    267  1.1    kardel 		/* open failed -- keep previous state
    268  1.1    kardel 		 *
    269  1.1    kardel 		 * If the file was open before keep the previous generation.
    270  1.1    kardel 		 * This will cause output to end up in the 'wrong' file,
    271  1.1    kardel 		 * but I think this is still better than losing output
    272  1.1    kardel 		 *
    273  1.1    kardel 		 * ignore errors due to missing directories
    274  1.1    kardel 		 */
    275  1.1    kardel 
    276  1.1    kardel 		if (ENOENT != errno)
    277  1.1    kardel 			msyslog(LOG_ERR, "can't open %s: %m", filename);
    278  1.1    kardel 	} else {
    279  1.1    kardel 		if (NULL != gen->fp) {
    280  1.1    kardel 			fclose(gen->fp);
    281  1.1    kardel 			gen->fp = NULL;
    282  1.1    kardel 		}
    283  1.1    kardel 		gen->fp = fp;
    284  1.1    kardel 		gen->id = newid;
    285  1.1    kardel 
    286  1.1    kardel 		if (gen->flag & FGEN_FLAG_LINK) {
    287  1.1    kardel 			/*
    288  1.1    kardel 			 * need to link file to basename
    289  1.1    kardel 			 * have to use hardlink for now as I want to allow
    290  1.1    kardel 			 * gen->basename spanning directory levels
    291  1.1    kardel 			 * this would make it more complex to get the correct
    292  1.1    kardel 			 * filename for symlink
    293  1.1    kardel 			 *
    294  1.1    kardel 			 * Ok, it would just mean taking the part following
    295  1.1    kardel 			 * the last '/' in the name.... Should add it later....
    296  1.1    kardel 			 */
    297  1.1    kardel 
    298  1.1    kardel 			/* Windows NT does not support file links -Greg Schueman 1/18/97 */
    299  1.1    kardel 
    300  1.1    kardel #if defined SYS_WINNT || defined SYS_VXWORKS
    301  1.1    kardel 			SetLastError(0); /* On WinNT, don't support FGEN_FLAG_LINK */
    302  1.1    kardel #elif defined(VMS)
    303  1.1    kardel 			errno = 0; /* On VMS, don't support FGEN_FLAG_LINK */
    304  1.1    kardel #else  /* not (VMS) / VXWORKS / WINNT ; DO THE LINK) */
    305  1.1    kardel 			if (link(filename, basename) != 0)
    306  1.1    kardel 				if (EEXIST != errno)
    307  1.1    kardel 					msyslog(LOG_ERR,
    308  1.1    kardel 						"can't link(%s, %s): %m",
    309  1.1    kardel 						filename, basename);
    310  1.1    kardel #endif /* SYS_WINNT || VXWORKS */
    311  1.1    kardel 		}		/* flags & FGEN_FLAG_LINK */
    312  1.1    kardel 	}			/* else fp == NULL */
    313  1.1    kardel 
    314  1.1    kardel 	free(basename);
    315  1.1    kardel 	free(filename);
    316  1.1    kardel 	return;
    317  1.1    kardel }
    318  1.1    kardel 
    319  1.1    kardel /*
    320  1.1    kardel  * this function sets up gen->fp to point to the correct
    321  1.1    kardel  * generation of the file for the time specified by 'now'
    322  1.1    kardel  *
    323  1.1    kardel  * 'now' usually is interpreted as second part of a l_fp as is in the cal...
    324  1.1    kardel  * library routines
    325  1.1    kardel  */
    326  1.1    kardel 
    327  1.1    kardel void
    328  1.1    kardel filegen_setup(
    329  1.1    kardel 	FILEGEN *	gen,
    330  1.1    kardel 	u_long		now
    331  1.1    kardel 	)
    332  1.1    kardel {
    333  1.1    kardel 	u_long new_gen = ~ (u_long) 0;
    334  1.1    kardel 	struct calendar cal;
    335  1.1    kardel 
    336  1.1    kardel 	if (!(gen->flag & FGEN_FLAG_ENABLED)) {
    337  1.1    kardel 		if (NULL != gen->fp) {
    338  1.1    kardel 			fclose(gen->fp);
    339  1.1    kardel 			gen->fp = NULL;
    340  1.1    kardel 		}
    341  1.1    kardel 		return;
    342  1.1    kardel 	}
    343  1.1    kardel 
    344  1.1    kardel 	switch (gen->type) {
    345  1.1    kardel 
    346  1.1    kardel 	case FILEGEN_NONE:
    347  1.1    kardel 		if (NULL != gen->fp)
    348  1.1    kardel 			return; /* file already open */
    349  1.1    kardel 		break;
    350  1.1    kardel 
    351  1.1    kardel 	case FILEGEN_PID:
    352  1.1    kardel 		new_gen = getpid();
    353  1.1    kardel 		break;
    354  1.1    kardel 
    355  1.1    kardel 	case FILEGEN_DAY:
    356  1.1    kardel 		caljulian(now, &cal);
    357  1.1    kardel 		cal.hour = cal.minute = cal.second = 0;
    358  1.1    kardel 		new_gen = caltontp(&cal);
    359  1.1    kardel 		break;
    360  1.1    kardel 
    361  1.1    kardel 	case FILEGEN_WEEK:
    362  1.1    kardel 		/* Would be nice to have a calweekstart() routine */
    363  1.1    kardel 		/* so just use a hack ... */
    364  1.1    kardel 		/* just round time to integral 7 day period for actual year  */
    365  1.1    kardel 		new_gen = now - (now - calyearstart(now)) % TIMES7(SECSPERDAY)
    366  1.1    kardel 			+ 60;
    367  1.1    kardel 		/*
    368  1.1    kardel 		 * just to be sure -
    369  1.1    kardel 		 * the computation above would fail in the presence of leap seconds
    370  1.1    kardel 		 * so at least carry the date to the next day (+60 (seconds))
    371  1.1    kardel 		 * and go back to the start of the day via calendar computations
    372  1.1    kardel 		 */
    373  1.1    kardel 		caljulian(new_gen, &cal);
    374  1.1    kardel 		cal.hour = cal.minute = cal.second = 0;
    375  1.1    kardel 		new_gen = caltontp(&cal);
    376  1.1    kardel 		break;
    377  1.1    kardel 
    378  1.1    kardel 	case FILEGEN_MONTH:
    379  1.1    kardel 		caljulian(now, &cal);
    380  1.1    kardel 		cal.yearday = (u_short) (cal.yearday - cal.monthday + 1);
    381  1.1    kardel 		cal.monthday = 1;
    382  1.1    kardel 		cal.hour = cal.minute = cal.second = 0;
    383  1.1    kardel 		new_gen = caltontp(&cal);
    384  1.1    kardel 		break;
    385  1.1    kardel 
    386  1.1    kardel 	case FILEGEN_YEAR:
    387  1.1    kardel 		new_gen = calyearstart(now);
    388  1.1    kardel 		break;
    389  1.1    kardel 
    390  1.1    kardel 	case FILEGEN_AGE:
    391  1.1    kardel 		new_gen = current_time - (current_time % SECSPERDAY);
    392  1.1    kardel 		break;
    393  1.1    kardel 	}
    394  1.1    kardel 	/*
    395  1.1    kardel 	 * try to open file if not yet open
    396  1.1    kardel 	 * reopen new file generation file on change of generation id
    397  1.1    kardel 	 */
    398  1.1    kardel 	if (NULL == gen->fp || gen->id != new_gen) {
    399  1.1    kardel 
    400  1.1    kardel 		DPRINTF(1, ("filegen  %0x %lu %lu %lu\n",
    401  1.1    kardel 			    gen->type, now, gen->id, new_gen));
    402  1.1    kardel 
    403  1.1    kardel 		filegen_open(gen, new_gen);
    404  1.1    kardel 	}
    405  1.1    kardel }
    406  1.1    kardel 
    407  1.1    kardel 
    408  1.1    kardel /*
    409  1.1    kardel  * change settings for filegen files
    410  1.1    kardel  */
    411  1.1    kardel void
    412  1.1    kardel filegen_config(
    413  1.1    kardel 	FILEGEN *	gen,
    414  1.1    kardel 	const char *	basename,
    415  1.1    kardel 	u_int		type,
    416  1.1    kardel 	u_int		flag
    417  1.1    kardel 	)
    418  1.1    kardel {
    419  1.1    kardel 	int file_existed = 0;
    420  1.1    kardel 	size_t octets;
    421  1.1    kardel 
    422  1.1    kardel 	/*
    423  1.1    kardel 	 * if nothing would be changed...
    424  1.1    kardel 	 */
    425  1.1    kardel 	if ((strcmp(basename, gen->basename) == 0) && type == gen->type
    426  1.1    kardel 	    && flag == gen->flag)
    427  1.1    kardel 		return;
    428  1.1    kardel 
    429  1.1    kardel 	/*
    430  1.1    kardel 	 * validate parameters
    431  1.1    kardel 	 */
    432  1.1    kardel 	if (!valid_fileref(gen->prefix, basename))
    433  1.1    kardel 		return;
    434  1.1    kardel 
    435  1.1    kardel 	if (NULL != gen->fp) {
    436  1.1    kardel 		fclose(gen->fp);
    437  1.1    kardel 		gen->fp = NULL;
    438  1.1    kardel 		file_existed = 1;
    439  1.1    kardel 	}
    440  1.1    kardel 
    441  1.1    kardel 	DPRINTF(3, ("configuring filegen:\n"
    442  1.1    kardel 		    "\tprefix:\t%s\n"
    443  1.1    kardel 		    "\tbasename:\t%s -> %s\n"
    444  1.1    kardel 		    "\ttype:\t%d -> %d\n"
    445  1.1    kardel 		    "\tflag: %x -> %x\n",
    446  1.1    kardel 		    gen->prefix,
    447  1.1    kardel 		    gen->basename, basename,
    448  1.1    kardel 		    gen->type, type,
    449  1.1    kardel 		    gen->flag, flag));
    450  1.1    kardel 
    451  1.1    kardel 	if (strcmp(gen->basename, basename) != 0) {
    452  1.1    kardel 		octets = strlen(basename) + 1;
    453  1.1    kardel 		gen->basename = erealloc(gen->basename, octets);
    454  1.1    kardel 		memcpy(gen->basename, basename, octets);
    455  1.1    kardel 	}
    456  1.1    kardel 	gen->type = (u_char) type;
    457  1.1    kardel 	gen->flag = (u_char) flag;
    458  1.1    kardel 
    459  1.1    kardel 	/*
    460  1.1    kardel 	 * make filegen use the new settings
    461  1.1    kardel 	 * special action is only required when a generation file
    462  1.1    kardel 	 * is currently open
    463  1.1    kardel 	 * otherwise the new settings will be used anyway at the next open
    464  1.1    kardel 	 */
    465  1.1    kardel 	if (file_existed) {
    466  1.1    kardel 		l_fp now;
    467  1.1    kardel 
    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.1    kardel 	const char *	prefix,
    481  1.1    kardel 	const char *	basename
    482  1.1    kardel 	)
    483  1.1    kardel {
    484  1.1    kardel 	/*
    485  1.1    kardel 	 * prefix 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.1    kardel 	 * 		file system parts 'below' prefix may be
    491  1.1    kardel 	 *		specified without infringement of security
    492  1.1    kardel 	 *
    493  1.1    kardel 	 *		restricting prefix 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.1    kardel 	register const char *p = basename;
    498  1.1    kardel 
    499  1.1    kardel 	/*
    500  1.1    kardel 	 * Just to catch, dumb errors opening up the world...
    501  1.1    kardel 	 */
    502  1.1    kardel 	if (NULL == prefix || '\0' == *prefix)
    503  1.1    kardel 		return 0;
    504  1.1    kardel 
    505  1.1    kardel 	if (NULL == basename)
    506  1.1    kardel 		return 0;
    507  1.1    kardel 
    508  1.1    kardel 	for (p = basename; p; p = strchr(p, DIR_SEP)) {
    509  1.1    kardel 		if ('.' == p[0] && '.' == p[1]
    510  1.1    kardel 		    && ('\0' == p[2] || DIR_SEP == p[2]))
    511  1.1    kardel 			return 0;
    512  1.1    kardel 	}
    513  1.1    kardel 
    514  1.1    kardel 	return 1;
    515  1.1    kardel }
    516  1.1    kardel 
    517  1.1    kardel 
    518  1.1    kardel /*
    519  1.1    kardel  * filegen registry
    520  1.1    kardel  */
    521  1.1    kardel 
    522  1.1    kardel static struct filegen_entry {
    523  1.1    kardel 	char *			name;
    524  1.1    kardel 	FILEGEN *		filegen;
    525  1.1    kardel 	struct filegen_entry *	next;
    526  1.1    kardel } *filegen_registry = NULL;
    527  1.1    kardel 
    528  1.1    kardel 
    529  1.1    kardel FILEGEN *
    530  1.1    kardel filegen_get(
    531  1.1    kardel 	const char *	name
    532  1.1    kardel 	)
    533  1.1    kardel {
    534  1.1    kardel 	struct filegen_entry *f = filegen_registry;
    535  1.1    kardel 
    536  1.1    kardel 	while (f) {
    537  1.1    kardel 		if (f->name == name || strcmp(name, f->name) == 0) {
    538  1.1    kardel 			DPRINTF(4, ("filegen_get(%s) = %p\n",
    539  1.1    kardel 				    name, f->filegen));
    540  1.1    kardel 			return f->filegen;
    541  1.1    kardel 		}
    542  1.1    kardel 		f = f->next;
    543  1.1    kardel 	}
    544  1.1    kardel 	DPRINTF(4, ("filegen_get(%s) = NULL\n", name));
    545  1.1    kardel 	return NULL;
    546  1.1    kardel }
    547  1.1    kardel 
    548  1.1    kardel 
    549  1.1    kardel void
    550  1.1    kardel filegen_register(
    551  1.1    kardel 	const char *	prefix,
    552  1.1    kardel 	const char *	name,
    553  1.1    kardel 	FILEGEN *	filegen
    554  1.1    kardel 	)
    555  1.1    kardel {
    556  1.1    kardel 	struct filegen_entry **ppfe;
    557  1.1    kardel 
    558  1.1    kardel 	DPRINTF(4, ("filegen_register(%s, %p)\n", name, filegen));
    559  1.1    kardel 
    560  1.1    kardel 	filegen_init(prefix, name, filegen);
    561  1.1    kardel 
    562  1.1    kardel 	ppfe = &filegen_registry;
    563  1.1    kardel 	while (NULL != *ppfe) {
    564  1.1    kardel 		if ((*ppfe)->name == name
    565  1.1    kardel 		    || !strcmp((*ppfe)->name, name)) {
    566  1.1    kardel 
    567  1.1    kardel 			DPRINTF(5, ("replacing filegen %p\n",
    568  1.1    kardel 				    (*ppfe)->filegen));
    569  1.1    kardel 
    570  1.1    kardel 			(*ppfe)->filegen = filegen;
    571  1.1    kardel 			return;
    572  1.1    kardel 		}
    573  1.1    kardel 		ppfe = &((*ppfe)->next);
    574  1.1    kardel 	}
    575  1.1    kardel 
    576  1.1    kardel 	*ppfe = emalloc(sizeof **ppfe);
    577  1.1    kardel 
    578  1.1    kardel 	(*ppfe)->next = NULL;
    579  1.1    kardel 	(*ppfe)->name = estrdup(name);
    580  1.1    kardel 	(*ppfe)->filegen = filegen;
    581  1.1    kardel 
    582  1.1    kardel 	DPRINTF(6, ("adding new filegen\n"));
    583  1.1    kardel 
    584  1.1    kardel 	return;
    585  1.1    kardel }
    586  1.1    kardel 
    587  1.1    kardel 
    588  1.1    kardel /*
    589  1.1    kardel  * filegen_unregister frees memory allocated by filegen_register for
    590  1.1    kardel  * name.
    591  1.1    kardel  */
    592  1.1    kardel #ifdef DEBUG
    593  1.1    kardel void
    594  1.1    kardel filegen_unregister(
    595  1.2  christos 	const char *name
    596  1.1    kardel 	)
    597  1.1    kardel {
    598  1.1    kardel 	struct filegen_entry **	ppfe;
    599  1.1    kardel 	struct filegen_entry *	pfe;
    600  1.1    kardel 	FILEGEN *		fg;
    601  1.1    kardel 
    602  1.1    kardel 	DPRINTF(4, ("filegen_unregister(%s)\n", name));
    603  1.1    kardel 
    604  1.1    kardel 	ppfe = &filegen_registry;
    605  1.1    kardel 
    606  1.1    kardel 	while (NULL != *ppfe) {
    607  1.1    kardel 		if ((*ppfe)->name == name
    608  1.1    kardel 		    || !strcmp((*ppfe)->name, name)) {
    609  1.1    kardel 			pfe = *ppfe;
    610  1.1    kardel 			*ppfe = (*ppfe)->next;
    611  1.1    kardel 			fg = pfe->filegen;
    612  1.1    kardel 			free(pfe->name);
    613  1.1    kardel 			free(pfe);
    614  1.1    kardel 			filegen_uninit(fg);
    615  1.1    kardel 			break;
    616  1.1    kardel 		}
    617  1.1    kardel 		ppfe = &((*ppfe)->next);
    618  1.1    kardel 	}
    619  1.1    kardel }
    620  1.1    kardel #endif	/* DEBUG */
    621