Annotation of embedaddon/ntp/ntpd/ntp_filegen.c, revision 1.1.1.1
1.1 misho 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>