1 /* $NetBSD: ntp_filegen.c,v 1.2 2003/12/04 16:23:37 drochner Exp $ */
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 '.'
52 #define FGEN_AGE_SECS (24*60*60) /* life time of FILEGEN_AGE in seconds */
54 static void filegen_open
P((FILEGEN
*, u_long
));
55 static int valid_fileref
P((char *, char *));
57 static FILEGEN
*filegen_unregister
P((char *));
60 static void filegen_init
P((char *, const char *, FILEGEN
*));
67 filegen_init(char *prefix
, const char *basename
, FILEGEN
*fp
)
70 fp
->prefix
= prefix
; /* Yes, this is TOTALLY lame! */
71 fp
->basename
= (char*)emalloc(strlen(basename
) + 1);
72 strcpy(fp
->basename
, basename
);
74 fp
->type
= FILEGEN_DAY
;
75 fp
->flag
= FGEN_FLAG_LINK
; /* not yet enabled !!*/
80 * open a file generation according to the current settings of gen
81 * will also provide a link to basename if requested to do so
96 len
= strlen(gen
->prefix
) + strlen(gen
->basename
) + 1;
97 basename
= (char*)emalloc(len
);
98 sprintf(basename
, "%s%s", gen
->prefix
, gen
->basename
);
102 msyslog(LOG_ERR
, "unsupported file generations type %d for \"%s\" - reverting to FILEGEN_NONE",
103 gen
->type
, basename
);
104 gen
->type
= FILEGEN_NONE
;
108 filename
= (char*)emalloc(len
);
109 sprintf(filename
,"%s", basename
);
113 filename
= (char*)emalloc(len
+ 1 + 1 + 10);
114 sprintf(filename
,"%s%c#%ld", basename
, SUFFIX_SEP
, newid
);
118 /* You can argue here in favor of using MJD, but
119 * I would assume it to be easier for humans to interpret dates
120 * in a format they are used to in everyday life.
122 caljulian(newid
,&cal
);
123 filename
= (char*)emalloc(len
+ 1 + 4 + 2 + 2);
124 sprintf(filename
, "%s%c%04d%02d%02d",
125 basename
, SUFFIX_SEP
, cal
.year
, cal
.month
, cal
.monthday
);
130 * This is still a hack
131 * - the term week is not correlated to week as it is used
132 * normally - it just refers to a period of 7 days
133 * starting at Jan 1 - 'weeks' are counted starting from zero
135 caljulian(newid
,&cal
);
136 filename
= (char*)emalloc(len
+ 1 + 4 + 1 + 2);
137 sprintf(filename
, "%s%c%04dw%02d",
138 basename
, SUFFIX_SEP
, cal
.year
, cal
.yearday
/ 7);
142 caljulian(newid
,&cal
);
143 filename
= (char*)emalloc(len
+ 1 + 4 + 2);
144 sprintf(filename
, "%s%c%04d%02d",
145 basename
, SUFFIX_SEP
, cal
.year
, cal
.month
);
149 caljulian(newid
,&cal
);
150 filename
= (char*)emalloc(len
+ 1 + 4);
151 sprintf(filename
, "%s%c%04d", basename
, SUFFIX_SEP
, cal
.year
);
155 filename
= (char*)emalloc(len
+ 1 + 2 + 10);
156 sprintf(filename
, "%s%ca%08ld", basename
, SUFFIX_SEP
, newid
);
160 if (gen
->type
!= FILEGEN_NONE
) {
162 * check for existence of a file with name 'basename'
163 * as we disallow such a file
164 * if FGEN_FLAG_LINK is set create a link
168 * try to resolve name collisions
170 static u_long conflicts
= 0;
173 #define S_ISREG(mode) (((mode) & S_IFREG) == S_IFREG)
175 if (stat(basename
, &stats
) == 0) {
176 /* Hm, file exists... */
177 if (S_ISREG(stats
.st_mode
)) {
178 if (stats
.st_nlink
<= 1) {
180 * Oh, it is not linked - try to save it
182 char *savename
= (char*)emalloc(len
+ 1 + 1 + 10 + 10);
183 sprintf(savename
, "%s%c%dC%lu",
187 (u_long
)conflicts
++);
188 if (rename(basename
, savename
) != 0)
189 msyslog(LOG_ERR
," couldn't save %s: %m", basename
);
193 * there is at least a second link to
195 * just remove the conflicting one
199 unlink(basename
) != 0
201 delete(basename
) != 0
204 msyslog(LOG_ERR
, "couldn't unlink %s: %m", basename
);
208 * Ehh? Not a regular file ?? strange !!!!
210 msyslog(LOG_ERR
, "expected regular file for %s (found mode 0%lo)",
211 basename
, (unsigned long)stats
.st_mode
);
215 * stat(..) failed, but it is absolutely correct for
216 * 'basename' not to exist
219 msyslog(LOG_ERR
,"stat(%s) failed: %m", basename
);
224 * now, try to open new file generation...
226 fp
= fopen(filename
, "a");
230 printf("opening filegen (type=%d/id=%lu) \"%s\"\n",
231 gen
->type
, (u_long
)newid
, filename
);
235 /* open failed -- keep previous state
237 * If the file was open before keep the previous generation.
238 * This will cause output to end up in the 'wrong' file,
239 * but I think this is still better than losing output
241 * ignore errors due to missing directories
245 msyslog(LOG_ERR
, "can't open %s: %m", filename
);
247 if (gen
->fp
!= NULL
) {
253 if (gen
->flag
& FGEN_FLAG_LINK
) {
255 * need to link file to basename
256 * have to use hardlink for now as I want to allow
257 * gen->basename spanning directory levels
258 * this would make it more complex to get the correct
259 * filename for symlink
261 * Ok, it would just mean taking the part following
262 * the last '/' in the name.... Should add it later....
265 /* Windows NT does not support file links -Greg Schueman 1/18/97 */
267 #if defined SYS_WINNT || defined SYS_VXWORKS
268 SetLastError(0); /* On WinNT, don't support FGEN_FLAG_LINK */
270 errno
= 0; /* On VMS, don't support FGEN_FLAG_LINK */
271 #else /* not (VMS) / VXWORKS / WINNT ; DO THE LINK) */
272 if (link(filename
, basename
) != 0)
274 msyslog(LOG_ERR
, "can't link(%s, %s): %m", filename
, basename
);
275 #endif /* SYS_WINNT || VXWORKS */
276 } /* flags & FGEN_FLAG_LINK */
277 } /* else fp == NULL */
285 * this function sets up gen->fp to point to the correct
286 * generation of the file for the time specified by 'now'
288 * 'now' usually is interpreted as second part of a l_fp as is in the cal...
298 u_long new_gen
= ~ (u_long
) 0;
301 if (!(gen
->flag
& FGEN_FLAG_ENABLED
)) {
309 if (gen
->fp
!= NULL
) return; /* file already open */
317 caljulian(now
, &cal
);
318 cal
.hour
= cal
.minute
= cal
.second
= 0;
319 new_gen
= caltontp(&cal
);
323 /* Would be nice to have a calweekstart() routine */
324 /* so just use a hack ... */
325 /* just round time to integral 7 day period for actual year */
326 new_gen
= now
- (now
- calyearstart(now
)) % TIMES7(SECSPERDAY
)
330 * the computation above would fail in the presence of leap seconds
331 * so at least carry the date to the next day (+60 (seconds))
332 * and go back to the start of the day via calendar computations
334 caljulian(new_gen
, &cal
);
335 cal
.hour
= cal
.minute
= cal
.second
= 0;
336 new_gen
= caltontp(&cal
);
340 caljulian(now
, &cal
);
341 cal
.yearday
= (u_short
) (cal
.yearday
- cal
.monthday
+ 1);
343 cal
.hour
= cal
.minute
= cal
.second
= 0;
344 new_gen
= caltontp(&cal
);
348 new_gen
= calyearstart(now
);
352 new_gen
= current_time
- (current_time
% FGEN_AGE_SECS
);
356 * try to open file if not yet open
357 * reopen new file generation file on change of generation id
359 if (gen
->fp
== NULL
|| gen
->id
!= new_gen
) {
362 printf("filegen %0x %lu %lu %lu\n", gen
->type
, now
,
365 filegen_open(gen
, new_gen
);
371 * change settings for filegen files
382 * if nothing would be changed...
384 if ((basename
== gen
->basename
|| strcmp(basename
,gen
->basename
) == 0) &&
390 * validate parameters
392 if (!valid_fileref(gen
->prefix
,basename
))
400 printf("configuring filegen:\n\tprefix:\t%s\n\tbasename:\t%s -> %s\n\ttype:\t%d -> %d\n\tflag: %x -> %x\n",
401 gen
->prefix
, gen
->basename
, basename
, gen
->type
, type
, gen
->flag
, flag
);
403 if (gen
->basename
!= basename
|| strcmp(gen
->basename
, basename
) != 0) {
405 gen
->basename
= (char*)emalloc(strlen(basename
) + 1);
406 strcpy(gen
->basename
, basename
);
408 gen
->type
= (u_char
) type
;
409 gen
->flag
= (u_char
) flag
;
412 * make filegen use the new settings
413 * special action is only required when a generation file
415 * otherwise the new settings will be used anyway at the next open
417 if (gen
->fp
!= NULL
) {
421 filegen_setup(gen
, now
.l_ui
);
427 * check whether concatenating prefix and basename
428 * yields a legal filename
437 * prefix cannot be changed dynamically
438 * (within the context of filegen)
439 * so just reject basenames containing '..'
442 * file system parts 'below' prefix may be
443 * specified without infringement of security
445 * restricing prefix to legal values
446 * has to be ensured by other means
447 * (however, it would be possible to perform some checks here...)
449 register char *p
= basename
;
452 * Just to catch, dumb errors opening up the world...
454 if (prefix
== NULL
|| *prefix
== '\0')
457 if (basename
== NULL
)
460 for (p
= basename
; p
; p
= strchr(p
, '/')) {
461 if (*p
== '.' && *(p
+1) == '.' && (*(p
+2) == '\0' || *(p
+2) == '/'))
473 static struct filegen_entry
{
476 struct filegen_entry
*next
;
477 } *filegen_registry
= NULL
;
485 struct filegen_entry
*f
= filegen_registry
;
488 if (f
->name
== name
|| strcmp(name
, f
->name
) == 0) {
489 #ifdef XXX /* this gives the Alpha compiler fits */
491 printf("filegen_get(\"%s\") = %x\n", name
,
500 printf("filegen_get(\"%s\") = NULL\n", name
);
512 struct filegen_entry
**f
= &filegen_registry
;
514 #ifdef XXX /* this gives the Alpha compiler fits */
516 printf("filegen_register(\"%s\",%x)\n", name
, (u_int
)filegen
);
519 filegen_init(prefix
, name
, filegen
);
522 if ((*f
)->name
== name
|| strcmp(name
, (*f
)->name
) == 0) {
523 #ifdef XXX /* this gives the Alpha compiler fits */
525 printf("replacing filegen %x\n", (u_int
)(*f
)->filegen
);
528 (*f
)->filegen
= filegen
;
534 *f
= (struct filegen_entry
*) emalloc(sizeof(struct filegen_entry
));
537 (*f
)->name
= (char*)emalloc(strlen(name
) + 1);
538 strcpy((*f
)->name
, name
);
539 (*f
)->filegen
= filegen
;
542 printf("adding new filegen\n");
556 struct filegen_entry
**f
= &filegen_registry
;
560 printf("filegen_unregister(\"%s\")\n", name
);
564 if (strcmp((*f
)->name
,name
) == 0) {
565 struct filegen_entry
*ff
= *f
;