File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / ntp / ntpd / ntp_filegen.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue May 29 12:08:38 2012 UTC (12 years, 1 month ago) by misho
Branches: ntp, MAIN
CVS tags: v4_2_6p5p0, v4_2_6p5, HEAD
ntp 4.2.6p5

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>