4 * ntp_filegen.c,v 3.12 1994/01/25 19:06:11 kardel Exp
6 * implements file generations support for NTP
7 * logfiles and statistic files
10 * Copyright (C) 1992, 1996 by Rainer Pruy
11 * Friedrich-Alexander Universität Erlangen-Nürnberg, Germany
13 * This code may be modified and used freely
14 * provided credits remain intact.
22 #include <sys/types.h>
27 #include "ntp_string.h"
28 #include "ntp_calendar.h"
29 #include "ntp_filegen.h"
30 #include "ntp_stdlib.h"
33 * NTP is intended to run long periods of time without restart.
34 * Thus log and statistic files generated by NTP will grow large.
36 * this set of routines provides a central interface
37 * to generating files using file generations
39 * the generation of a file is changed according to file generation type
44 * redefine this if your system dislikes filename suffixes like
45 * X.19910101 or X.1992W50 or ....
47 #define SUFFIX_SEP '.'
49 static void filegen_open (FILEGEN
*, u_long
);
50 static int valid_fileref (const char *, const char *);
51 static void filegen_init (const char *, const char *, FILEGEN
*);
53 static void filegen_uninit (FILEGEN
*);
64 const char * basename
,
69 fgp
->prefix
= prefix
; /* Yes, this is TOTALLY lame! */
70 fgp
->basename
= estrdup(basename
);
72 fgp
->type
= FILEGEN_DAY
;
73 fgp
->flag
= FGEN_FLAG_LINK
; /* not yet enabled !!*/
78 * filegen_uninit - free memory allocated by filegen_init
92 * open a file generation according to the current settings of gen
93 * will also provide a link to basename if requested to do so
108 len
= strlen(gen
->prefix
) + strlen(gen
->basename
) + 1;
109 basename
= emalloc(len
);
110 snprintf(basename
, len
, "%s%s", gen
->prefix
, gen
->basename
);
116 "unsupported file generations type %d for "
117 "\"%s\" - reverting to FILEGEN_NONE",
118 gen
->type
, basename
);
119 gen
->type
= FILEGEN_NONE
;
120 /* fall through to FILEGEN_NONE */
123 filename
= estrdup(basename
);
127 filename
= emalloc(len
+ 1 + 1 + 10);
128 snprintf(filename
, len
+ 1 + 1 + 10,
130 basename
, SUFFIX_SEP
, newid
);
135 * You can argue here in favor of using MJD, but I
136 * would assume it to be easier for humans to interpret
137 * dates in a format they are used to in everyday life.
139 caljulian(newid
, &cal
);
140 filename
= emalloc(len
+ 1 + 4 + 2 + 2);
141 snprintf(filename
, len
+ 1 + 4 + 2 + 2,
143 basename
, SUFFIX_SEP
,
144 cal
.year
, cal
.month
, cal
.monthday
);
149 * This is still a hack
150 * - the term week is not correlated to week as it is used
151 * normally - it just refers to a period of 7 days
152 * starting at Jan 1 - 'weeks' are counted starting from zero
154 caljulian(newid
, &cal
);
155 filename
= emalloc(len
+ 1 + 4 + 1 + 2);
156 snprintf(filename
, len
+ 1 + 4 + 1 + 2,
158 basename
, SUFFIX_SEP
,
159 cal
.year
, cal
.yearday
/ 7);
163 caljulian(newid
, &cal
);
164 filename
= emalloc(len
+ 1 + 4 + 2);
165 snprintf(filename
, len
+ 1 + 4 + 2,
167 basename
, SUFFIX_SEP
, cal
.year
, cal
.month
);
171 caljulian(newid
, &cal
);
172 filename
= emalloc(len
+ 1 + 4);
173 snprintf(filename
, len
+ 1 + 4,
175 basename
, SUFFIX_SEP
, cal
.year
);
179 filename
= emalloc(len
+ 1 + 2 + 10);
180 snprintf(filename
, len
+ 1 + 2 + 10,
182 basename
, SUFFIX_SEP
, newid
);
185 if (FILEGEN_NONE
!= gen
->type
) {
187 * check for existence of a file with name 'basename'
188 * as we disallow such a file
189 * if FGEN_FLAG_LINK is set create a link
193 * try to resolve name collisions
195 static u_long conflicts
= 0;
198 #define S_ISREG(mode) (((mode) & S_IFREG) == S_IFREG)
200 if (stat(basename
, &stats
) == 0) {
201 /* Hm, file exists... */
202 if (S_ISREG(stats
.st_mode
)) {
203 if (stats
.st_nlink
<= 1) {
205 * Oh, it is not linked - try to save it
209 savename
= emalloc(len
+ 1 + 1 + 10 + 10);
210 snprintf(savename
, len
+ 1 + 1 + 10 + 10,
212 basename
, SUFFIX_SEP
,
213 (int)getpid(), conflicts
++);
215 if (rename(basename
, savename
) != 0)
217 "couldn't save %s: %m",
222 * there is at least a second link to
224 * just remove the conflicting one
228 unlink(basename
) != 0
230 delete(basename
) != 0
234 "couldn't unlink %s: %m",
239 * Ehh? Not a regular file ?? strange !!!!
242 "expected regular file for %s "
245 (unsigned long)stats
.st_mode
);
249 * stat(..) failed, but it is absolutely correct for
250 * 'basename' not to exist
253 msyslog(LOG_ERR
, "stat(%s) failed: %m",
259 * now, try to open new file generation...
261 fp
= fopen(filename
, "a");
263 DPRINTF(4, ("opening filegen (type=%d/id=%lu) \"%s\"\n",
264 gen
->type
, newid
, filename
));
267 /* open failed -- keep previous state
269 * If the file was open before keep the previous generation.
270 * This will cause output to end up in the 'wrong' file,
271 * but I think this is still better than losing output
273 * ignore errors due to missing directories
277 msyslog(LOG_ERR
, "can't open %s: %m", filename
);
279 if (NULL
!= gen
->fp
) {
286 if (gen
->flag
& FGEN_FLAG_LINK
) {
288 * need to link file to basename
289 * have to use hardlink for now as I want to allow
290 * gen->basename spanning directory levels
291 * this would make it more complex to get the correct
292 * filename for symlink
294 * Ok, it would just mean taking the part following
295 * the last '/' in the name.... Should add it later....
298 /* Windows NT does not support file links -Greg Schueman 1/18/97 */
300 #if defined SYS_WINNT || defined SYS_VXWORKS
301 SetLastError(0); /* On WinNT, don't support FGEN_FLAG_LINK */
303 errno
= 0; /* On VMS, don't support FGEN_FLAG_LINK */
304 #else /* not (VMS) / VXWORKS / WINNT ; DO THE LINK) */
305 if (link(filename
, basename
) != 0)
308 "can't link(%s, %s): %m",
310 #endif /* SYS_WINNT || VXWORKS */
311 } /* flags & FGEN_FLAG_LINK */
312 } /* else fp == NULL */
320 * this function sets up gen->fp to point to the correct
321 * generation of the file for the time specified by 'now'
323 * 'now' usually is interpreted as second part of a l_fp as is in the cal...
333 u_long new_gen
= ~ (u_long
) 0;
336 if (!(gen
->flag
& FGEN_FLAG_ENABLED
)) {
337 if (NULL
!= gen
->fp
) {
348 return; /* file already open */
356 caljulian(now
, &cal
);
357 cal
.hour
= cal
.minute
= cal
.second
= 0;
358 new_gen
= caltontp(&cal
);
362 /* Would be nice to have a calweekstart() routine */
363 /* so just use a hack ... */
364 /* just round time to integral 7 day period for actual year */
365 new_gen
= now
- (now
- calyearstart(now
)) % TIMES7(SECSPERDAY
)
369 * the computation above would fail in the presence of leap seconds
370 * so at least carry the date to the next day (+60 (seconds))
371 * and go back to the start of the day via calendar computations
373 caljulian(new_gen
, &cal
);
374 cal
.hour
= cal
.minute
= cal
.second
= 0;
375 new_gen
= caltontp(&cal
);
379 caljulian(now
, &cal
);
380 cal
.yearday
= (u_short
) (cal
.yearday
- cal
.monthday
+ 1);
382 cal
.hour
= cal
.minute
= cal
.second
= 0;
383 new_gen
= caltontp(&cal
);
387 new_gen
= calyearstart(now
);
391 new_gen
= current_time
- (current_time
% SECSPERDAY
);
395 * try to open file if not yet open
396 * reopen new file generation file on change of generation id
398 if (NULL
== gen
->fp
|| gen
->id
!= new_gen
) {
400 DPRINTF(1, ("filegen %0x %lu %lu %lu\n",
401 gen
->type
, now
, gen
->id
, new_gen
));
403 filegen_open(gen
, new_gen
);
409 * change settings for filegen files
414 const char * basename
,
419 int file_existed
= 0;
423 * if nothing would be changed...
425 if ((strcmp(basename
, gen
->basename
) == 0) && type
== gen
->type
426 && flag
== gen
->flag
)
430 * validate parameters
432 if (!valid_fileref(gen
->prefix
, basename
))
435 if (NULL
!= gen
->fp
) {
441 DPRINTF(3, ("configuring filegen:\n"
443 "\tbasename:\t%s -> %s\n"
444 "\ttype:\t%d -> %d\n"
445 "\tflag: %x -> %x\n",
447 gen
->basename
, basename
,
451 if (strcmp(gen
->basename
, basename
) != 0) {
452 octets
= strlen(basename
) + 1;
453 gen
->basename
= erealloc(gen
->basename
, octets
);
454 memcpy(gen
->basename
, basename
, octets
);
456 gen
->type
= (u_char
) type
;
457 gen
->flag
= (u_char
) flag
;
460 * make filegen use the new settings
461 * special action is only required when a generation file
463 * otherwise the new settings will be used anyway at the next open
469 filegen_setup(gen
, now
.l_ui
);
475 * check whether concatenating prefix and basename
476 * yields a legal filename
481 const char * basename
485 * prefix cannot be changed dynamically
486 * (within the context of filegen)
487 * so just reject basenames containing '..'
490 * file system parts 'below' prefix may be
491 * specified without infringement of security
493 * restricting prefix to legal values
494 * has to be ensured by other means
495 * (however, it would be possible to perform some checks here...)
497 register const char *p
= basename
;
500 * Just to catch, dumb errors opening up the world...
502 if (NULL
== prefix
|| '\0' == *prefix
)
505 if (NULL
== basename
)
508 for (p
= basename
; p
; p
= strchr(p
, DIR_SEP
)) {
509 if ('.' == p
[0] && '.' == p
[1]
510 && ('\0' == p
[2] || DIR_SEP
== p
[2]))
522 static struct filegen_entry
{
525 struct filegen_entry
* next
;
526 } *filegen_registry
= NULL
;
534 struct filegen_entry
*f
= filegen_registry
;
537 if (f
->name
== name
|| strcmp(name
, f
->name
) == 0) {
538 DPRINTF(4, ("filegen_get(%s) = %p\n",
544 DPRINTF(4, ("filegen_get(%s) = NULL\n", name
));
556 struct filegen_entry
**ppfe
;
558 DPRINTF(4, ("filegen_register(%s, %p)\n", name
, filegen
));
560 filegen_init(prefix
, name
, filegen
);
562 ppfe
= &filegen_registry
;
563 while (NULL
!= *ppfe
) {
564 if ((*ppfe
)->name
== name
565 || !strcmp((*ppfe
)->name
, name
)) {
567 DPRINTF(5, ("replacing filegen %p\n",
570 (*ppfe
)->filegen
= filegen
;
573 ppfe
= &((*ppfe
)->next
);
576 *ppfe
= emalloc(sizeof **ppfe
);
578 (*ppfe
)->next
= NULL
;
579 (*ppfe
)->name
= estrdup(name
);
580 (*ppfe
)->filegen
= filegen
;
582 DPRINTF(6, ("adding new filegen\n"));
589 * filegen_unregister frees memory allocated by filegen_register for
598 struct filegen_entry
** ppfe
;
599 struct filegen_entry
* pfe
;
602 DPRINTF(4, ("filegen_unregister(%s)\n", name
));
604 ppfe
= &filegen_registry
;
606 while (NULL
!= *ppfe
) {
607 if ((*ppfe
)->name
== name
608 || !strcmp((*ppfe
)->name
, name
)) {
610 *ppfe
= (*ppfe
)->next
;
617 ppfe
= &((*ppfe
)->next
);