Home | History | Annotate | Line # | Download | only in ntpd
ntp_filegen.c revision 1.1.1.6
      1  1.1.1.6  christos /*	$NetBSD: ntp_filegen.c,v 1.1.1.6 2015/07/10 13:11:04 christos Exp $	*/
      2  1.1.1.6  christos 
      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.1.3  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.1.1.3  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.1.1.3  christos 	const char *	dir,
     64  1.1.1.3  christos 	const char *	fname,
     65      1.1    kardel 	FILEGEN *	fgp
     66      1.1    kardel 	)
     67      1.1    kardel {
     68  1.1.1.3  christos 	fgp->fp = NULL;
     69  1.1.1.3  christos 	fgp->dir = estrdup(dir);
     70  1.1.1.3  christos 	fgp->fname = estrdup(fname);
     71  1.1.1.3  christos 	fgp->id_lo = 0;
     72  1.1.1.3  christos 	fgp->id_hi = 0;
     73  1.1.1.3  christos 	fgp->type = FILEGEN_DAY;
     74  1.1.1.3  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.1.1.3  christos 	FILEGEN *fgp
     85      1.1    kardel 	)
     86      1.1    kardel {
     87  1.1.1.3  christos 	free(fgp->dir);
     88  1.1.1.3  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.1.1.3  christos 	u_int32		stamp,
    102  1.1.1.3  christos 	const time_t *	pivot
    103      1.1    kardel 	)
    104      1.1    kardel {
    105  1.1.1.3  christos 	char *savename;	/* temp store for name collision handling */
    106  1.1.1.3  christos 	char *fullname;	/* name with any designation extension */
    107  1.1.1.3  christos 	char *filename;	/* name without designation extension */
    108  1.1.1.3  christos 	char *suffix;	/* where to print suffix extension */
    109  1.1.1.3  christos 	u_int len, suflen;
    110      1.1    kardel 	FILE *fp;
    111      1.1    kardel 	struct calendar cal;
    112  1.1.1.3  christos 	struct isodate	iso;
    113  1.1.1.3  christos 
    114  1.1.1.3  christos 	/* get basic filename in buffer, leave room for extensions */
    115  1.1.1.3  christos 	len = strlen(gen->dir) + strlen(gen->fname) + 65;
    116  1.1.1.3  christos 	filename = emalloc(len);
    117  1.1.1.3  christos 	fullname = emalloc(len);
    118  1.1.1.3  christos 	savename = NULL;
    119  1.1.1.3  christos 	snprintf(filename, len, "%s%s", gen->dir, gen->fname);
    120  1.1.1.3  christos 
    121  1.1.1.3  christos 	/* where to place suffix */
    122  1.1.1.3  christos 	suflen = strlcpy(fullname, filename, len);
    123  1.1.1.3  christos 	suffix = fullname + suflen;
    124  1.1.1.3  christos 	suflen = len - suflen;
    125  1.1.1.3  christos 
    126  1.1.1.3  christos 	/* last octet of fullname set to '\0' for truncation check */
    127  1.1.1.3  christos 	fullname[len - 1] = '\0';
    128      1.1    kardel 
    129  1.1.1.2    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.1.1.3  christos 			gen->type, filename);
    136      1.1    kardel 		gen->type = FILEGEN_NONE;
    137  1.1.1.3  christos 		break;
    138      1.1    kardel 
    139      1.1    kardel 	case FILEGEN_NONE:
    140  1.1.1.3  christos 		/* no suffix, all set */
    141      1.1    kardel 		break;
    142      1.1    kardel 
    143      1.1    kardel 	case FILEGEN_PID:
    144  1.1.1.3  christos 		gen->id_lo = getpid();
    145  1.1.1.3  christos 		gen->id_hi = 0;
    146  1.1.1.3  christos 		snprintf(suffix, suflen, "%c#%ld",
    147  1.1.1.3  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.1.1.3  christos 		ntpcal_ntp_to_date(&cal, stamp, pivot);
    157  1.1.1.3  christos 		snprintf(suffix, suflen, "%c%04d%02d%02d",
    158  1.1.1.3  christos 			 SUFFIX_SEP, cal.year, cal.month, cal.monthday);
    159  1.1.1.3  christos 		cal.hour = cal.minute = cal.second = 0;
    160  1.1.1.3  christos 		gen->id_lo = ntpcal_date_to_ntp(&cal);
    161  1.1.1.3  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.1.1.3  christos 		isocal_ntp_to_date(&iso, stamp, pivot);
    166  1.1.1.3  christos 		snprintf(suffix, suflen, "%c%04dw%02d",
    167  1.1.1.3  christos 			 SUFFIX_SEP, iso.year, iso.week);
    168  1.1.1.3  christos 		iso.hour = iso.minute = iso.second = 0;
    169  1.1.1.3  christos 		iso.weekday = 1;
    170  1.1.1.3  christos 		gen->id_lo = isocal_date_to_ntp(&iso);
    171  1.1.1.3  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.1.1.3  christos 		ntpcal_ntp_to_date(&cal, stamp, pivot);
    176  1.1.1.3  christos 		snprintf(suffix, suflen, "%c%04d%02d",
    177  1.1.1.3  christos 			 SUFFIX_SEP, cal.year, cal.month);
    178  1.1.1.3  christos 		cal.hour = cal.minute = cal.second = 0;
    179  1.1.1.3  christos 		cal.monthday = 1;
    180  1.1.1.3  christos 		gen->id_lo = ntpcal_date_to_ntp(&cal);
    181  1.1.1.3  christos 		cal.month++;
    182  1.1.1.3  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.1.1.3  christos 		ntpcal_ntp_to_date(&cal, stamp, pivot);
    187  1.1.1.3  christos 		snprintf(suffix, suflen, "%c%04d",
    188  1.1.1.3  christos 			 SUFFIX_SEP, cal.year);
    189  1.1.1.3  christos 		cal.hour = cal.minute = cal.second = 0;
    190  1.1.1.3  christos 		cal.month = cal.monthday = 1;
    191  1.1.1.3  christos 		gen->id_lo = ntpcal_date_to_ntp(&cal);
    192  1.1.1.3  christos 		cal.year++;
    193  1.1.1.3  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.1.1.3  christos 		gen->id_lo = current_time - (current_time % SECSPERDAY);
    198  1.1.1.3  christos 		gen->id_hi = gen->id_lo + SECSPERDAY;
    199  1.1.1.3  christos 		snprintf(suffix, suflen, "%ca%08ld",
    200  1.1.1.3  christos 			 SUFFIX_SEP, gen->id_lo);
    201      1.1    kardel 	}
    202      1.1    kardel 
    203  1.1.1.3  christos 	/* check possible truncation */
    204  1.1.1.3  christos 	if ('\0' != fullname[len - 1]) {
    205  1.1.1.3  christos 		fullname[len - 1] = '\0';
    206  1.1.1.3  christos 		msyslog(LOG_ERR, "logfile name truncated: \"%s\"",
    207  1.1.1.3  christos 			fullname);
    208  1.1.1.3  christos 	}
    209  1.1.1.3  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.1.1.3  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.1.1.3  christos 					savename = emalloc(len);
    233  1.1.1.3  christos 					snprintf(savename, len,
    234      1.1    kardel 						"%s%c%dC%lu",
    235  1.1.1.3  christos 						filename, SUFFIX_SEP,
    236      1.1    kardel 						(int)getpid(), conflicts++);
    237      1.1    kardel 
    238  1.1.1.3  christos 					if (rename(filename, savename) != 0)
    239      1.1    kardel 						msyslog(LOG_ERR,
    240      1.1    kardel 							"couldn't save %s: %m",
    241  1.1.1.3  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.1.1.3  christos 						unlink(filename) != 0
    252      1.1    kardel #else
    253  1.1.1.3  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.1.1.3  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.1.1.3  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.1.1.3  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.1.1.3  christos 	DPRINTF(4, ("opening filegen (type=%d/stamp=%u) \"%s\"\n",
    285  1.1.1.3  christos 		    gen->type, stamp, fullname));
    286      1.1    kardel 
    287  1.1.1.3  christos 	fp = fopen(fullname, "a");
    288  1.1.1.3  christos 
    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.1.1.3  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.1.1.3  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.1.1.3  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.1.1.3  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.1.1.3  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.1.1.3  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.1.1.3  christos 	u_int32		now
    353      1.1    kardel 	)
    354      1.1    kardel {
    355  1.1.1.3  christos 	int	current;
    356  1.1.1.3  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.1.1.3  christos 
    366      1.1    kardel 	switch (gen->type) {
    367      1.1    kardel 
    368  1.1.1.3  christos 	default:
    369      1.1    kardel 	case FILEGEN_NONE:
    370  1.1.1.3  christos 		current = TRUE;
    371      1.1    kardel 		break;
    372      1.1    kardel 
    373      1.1    kardel 	case FILEGEN_PID:
    374  1.1.1.3  christos 		current = ((int)gen->id_lo == getpid());
    375      1.1    kardel 		break;
    376      1.1    kardel 
    377  1.1.1.3  christos 	case FILEGEN_AGE:
    378  1.1.1.3  christos 		current = (gen->id_lo <= current_time) &&
    379  1.1.1.3  christos 			  (gen->id_hi > current_time);
    380      1.1    kardel 		break;
    381      1.1    kardel 
    382  1.1.1.3  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.1.1.3  christos 		current = (gen->id_lo <= now) &&
    387  1.1.1.3  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.1.1.3  christos 	if (NULL == gen->fp || !current) {
    395  1.1.1.3  christos 		DPRINTF(1, ("filegen  %0x %u\n", gen->type, now));
    396  1.1.1.3  christos 		pivot = time(NULL);
    397  1.1.1.3  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.1.1.3  christos 	const char *	dir,
    409  1.1.1.3  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.1.1.3  christos 	int file_existed;
    415  1.1.1.3  christos 	l_fp now;
    416  1.1.1.3  christos 
    417      1.1    kardel 
    418      1.1    kardel 	/*
    419      1.1    kardel 	 * if nothing would be changed...
    420      1.1    kardel 	 */
    421  1.1.1.3  christos 	if (strcmp(dir, gen->dir) == 0 && strcmp(fname, gen->fname) == 0
    422  1.1.1.3  christos 	    && type == gen->type && flag == gen->flag)
    423      1.1    kardel 		return;
    424  1.1.1.3  christos 
    425      1.1    kardel 	/*
    426      1.1    kardel 	 * validate parameters
    427      1.1    kardel 	 */
    428  1.1.1.3  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.1.1.3  christos 		file_existed = TRUE;
    435  1.1.1.3  christos 	} else {
    436  1.1.1.3  christos 		file_existed = FALSE;
    437      1.1    kardel 	}
    438      1.1    kardel 
    439      1.1    kardel 	DPRINTF(3, ("configuring filegen:\n"
    440  1.1.1.3  christos 		    "\tdir:\t%s -> %s\n"
    441  1.1.1.3  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.1.1.3  christos 		    gen->dir, dir,
    445  1.1.1.3  christos 		    gen->fname, fname,
    446      1.1    kardel 		    gen->type, type,
    447      1.1    kardel 		    gen->flag, flag));
    448      1.1    kardel 
    449  1.1.1.3  christos 	if (strcmp(gen->dir, dir) != 0) {
    450  1.1.1.3  christos 		free(gen->dir);
    451  1.1.1.3  christos 		gen->dir = estrdup(dir);
    452  1.1.1.3  christos 	}
    453  1.1.1.3  christos 
    454  1.1.1.3  christos 	if (strcmp(gen->fname, fname) != 0) {
    455  1.1.1.3  christos 		free(gen->fname);
    456  1.1.1.3  christos 		gen->fname = estrdup(fname);
    457      1.1    kardel 	}
    458  1.1.1.2    kardel 	gen->type = (u_char)type;
    459  1.1.1.2    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.1.1.3  christos 	const char *	dir,
    481  1.1.1.3  christos 	const char *	fname
    482      1.1    kardel 	)
    483      1.1    kardel {
    484      1.1    kardel 	/*
    485  1.1.1.3  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.1.1.3  christos 	 *		file system parts 'below' dir may be
    491      1.1    kardel 	 *		specified without infringement of security
    492      1.1    kardel 	 *
    493  1.1.1.3  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.1.1.3  christos 	const char *p;
    498  1.1.1.3  christos 
    499      1.1    kardel 	/*
    500      1.1    kardel 	 * Just to catch, dumb errors opening up the world...
    501      1.1    kardel 	 */
    502  1.1.1.3  christos 	if (NULL == dir || '\0' == dir[0])
    503  1.1.1.3  christos 		return FALSE;
    504      1.1    kardel 
    505  1.1.1.3  christos 	if (NULL == fname)
    506  1.1.1.3  christos 		return FALSE;
    507  1.1.1.3  christos 
    508  1.1.1.3  christos #ifdef SYS_WINNT
    509  1.1.1.3  christos 	/*
    510  1.1.1.3  christos 	 * Windows treats / equivalent to \, reject any / to ensure
    511  1.1.1.3  christos 	 * check below for DIR_SEP (\ on windows) are adequate.
    512  1.1.1.3  christos 	 */
    513  1.1.1.3  christos 	if (strchr(fname, '/')) {
    514  1.1.1.3  christos 		msyslog(LOG_ERR,
    515  1.1.1.3  christos 			"filegen filenames must not contain '/': %s",
    516  1.1.1.3  christos 			fname);
    517  1.1.1.3  christos 		return FALSE;
    518  1.1.1.3  christos 	}
    519  1.1.1.3  christos #endif
    520  1.1.1.3  christos 
    521  1.1.1.3  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.1.1.3  christos 			return FALSE;
    525      1.1    kardel 	}
    526  1.1.1.3  christos 
    527  1.1.1.3  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.1.1.3  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.1.1.3  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.1.1.3  christos  * filegen_statsdir() - reset each filegen entry's dir to statsdir.
    603  1.1.1.3  christos  */
    604  1.1.1.3  christos void
    605  1.1.1.3  christos filegen_statsdir(void)
    606  1.1.1.3  christos {
    607  1.1.1.3  christos 	struct filegen_entry *f;
    608  1.1.1.3  christos 
    609  1.1.1.3  christos 	for (f = filegen_registry; f != NULL; f = f->next)
    610  1.1.1.3  christos 		filegen_config(f->filegen, statsdir, f->filegen->fname,
    611  1.1.1.3  christos 			       f->filegen->type, f->filegen->flag);
    612  1.1.1.3  christos }
    613  1.1.1.3  christos 
    614  1.1.1.3  christos 
    615  1.1.1.3  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.1.1.4  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