Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / external / bsd / ntp / dist / ntpd / ntp_filegen.c
blobf082405e33a5239ca8b64c94c07bfed97421b1bc
1 /* $NetBSD$ */
3 /*
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.
17 #ifdef HAVE_CONFIG_H
18 # include <config.h>
19 #endif
21 #include <stdio.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
25 #include "ntpd.h"
26 #include "ntp_io.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 *);
52 #ifdef DEBUG
53 static void filegen_uninit (FILEGEN *);
54 #endif /* DEBUG */
58 * filegen_init
61 static void
62 filegen_init(
63 const char * prefix,
64 const char * basename,
65 FILEGEN * fgp
68 fgp->fp = NULL;
69 fgp->prefix = prefix; /* Yes, this is TOTALLY lame! */
70 fgp->basename = estrdup(basename);
71 fgp->id = 0;
72 fgp->type = FILEGEN_DAY;
73 fgp->flag = FGEN_FLAG_LINK; /* not yet enabled !!*/
78 * filegen_uninit - free memory allocated by filegen_init
80 #ifdef DEBUG
81 static void
82 filegen_uninit(
83 FILEGEN * fgp
86 free(fgp->basename);
88 #endif
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
96 static void
97 filegen_open(
98 FILEGEN * gen,
99 u_long newid
102 char *filename;
103 char *basename;
104 u_int len;
105 FILE *fp;
106 struct calendar cal;
108 len = strlen(gen->prefix) + strlen(gen->basename) + 1;
109 basename = emalloc(len);
110 snprintf(basename, len, "%s%s", gen->prefix, gen->basename);
112 switch(gen->type) {
114 default:
115 msyslog(LOG_ERR,
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 */
122 case FILEGEN_NONE:
123 filename = estrdup(basename);
124 break;
126 case FILEGEN_PID:
127 filename = emalloc(len + 1 + 1 + 10);
128 snprintf(filename, len + 1 + 1 + 10,
129 "%s%c#%ld",
130 basename, SUFFIX_SEP, newid);
131 break;
133 case FILEGEN_DAY:
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,
142 "%s%c%04d%02d%02d",
143 basename, SUFFIX_SEP,
144 cal.year, cal.month, cal.monthday);
145 break;
147 case FILEGEN_WEEK:
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,
157 "%s%c%04dw%02d",
158 basename, SUFFIX_SEP,
159 cal.year, cal.yearday / 7);
160 break;
162 case FILEGEN_MONTH:
163 caljulian(newid, &cal);
164 filename = emalloc(len + 1 + 4 + 2);
165 snprintf(filename, len + 1 + 4 + 2,
166 "%s%c%04d%02d",
167 basename, SUFFIX_SEP, cal.year, cal.month);
168 break;
170 case FILEGEN_YEAR:
171 caljulian(newid, &cal);
172 filename = emalloc(len + 1 + 4);
173 snprintf(filename, len + 1 + 4,
174 "%s%c%04d",
175 basename, SUFFIX_SEP, cal.year);
176 break;
178 case FILEGEN_AGE:
179 filename = emalloc(len + 1 + 2 + 10);
180 snprintf(filename, len + 1 + 2 + 10,
181 "%s%ca%08ld",
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
191 struct stat stats;
193 * try to resolve name collisions
195 static u_long conflicts = 0;
197 #ifndef S_ISREG
198 #define S_ISREG(mode) (((mode) & S_IFREG) == S_IFREG)
199 #endif
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
207 char *savename;
209 savename = emalloc(len + 1 + 1 + 10 + 10);
210 snprintf(savename, len + 1 + 1 + 10 + 10,
211 "%s%c%dC%lu",
212 basename, SUFFIX_SEP,
213 (int)getpid(), conflicts++);
215 if (rename(basename, savename) != 0)
216 msyslog(LOG_ERR,
217 "couldn't save %s: %m",
218 basename);
219 free(savename);
220 } else {
222 * there is at least a second link to
223 * this file.
224 * just remove the conflicting one
226 if (
227 #if !defined(VMS)
228 unlink(basename) != 0
229 #else
230 delete(basename) != 0
231 #endif
233 msyslog(LOG_ERR,
234 "couldn't unlink %s: %m",
235 basename);
237 } else {
239 * Ehh? Not a regular file ?? strange !!!!
241 msyslog(LOG_ERR,
242 "expected regular file for %s "
243 "(found mode 0%lo)",
244 basename,
245 (unsigned long)stats.st_mode);
247 } else {
249 * stat(..) failed, but it is absolutely correct for
250 * 'basename' not to exist
252 if (ENOENT != errno)
253 msyslog(LOG_ERR, "stat(%s) failed: %m",
254 basename);
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));
266 if (NULL == fp) {
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
276 if (ENOENT != errno)
277 msyslog(LOG_ERR, "can't open %s: %m", filename);
278 } else {
279 if (NULL != gen->fp) {
280 fclose(gen->fp);
281 gen->fp = NULL;
283 gen->fp = fp;
284 gen->id = newid;
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 */
302 #elif defined(VMS)
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)
306 if (EEXIST != errno)
307 msyslog(LOG_ERR,
308 "can't link(%s, %s): %m",
309 filename, basename);
310 #endif /* SYS_WINNT || VXWORKS */
311 } /* flags & FGEN_FLAG_LINK */
312 } /* else fp == NULL */
314 free(basename);
315 free(filename);
316 return;
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...
324 * library routines
327 void
328 filegen_setup(
329 FILEGEN * gen,
330 u_long now
333 u_long new_gen = ~ (u_long) 0;
334 struct calendar cal;
336 if (!(gen->flag & FGEN_FLAG_ENABLED)) {
337 if (NULL != gen->fp) {
338 fclose(gen->fp);
339 gen->fp = NULL;
341 return;
344 switch (gen->type) {
346 case FILEGEN_NONE:
347 if (NULL != gen->fp)
348 return; /* file already open */
349 break;
351 case FILEGEN_PID:
352 new_gen = getpid();
353 break;
355 case FILEGEN_DAY:
356 caljulian(now, &cal);
357 cal.hour = cal.minute = cal.second = 0;
358 new_gen = caltontp(&cal);
359 break;
361 case FILEGEN_WEEK:
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)
366 + 60;
368 * just to be sure -
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);
376 break;
378 case FILEGEN_MONTH:
379 caljulian(now, &cal);
380 cal.yearday = (u_short) (cal.yearday - cal.monthday + 1);
381 cal.monthday = 1;
382 cal.hour = cal.minute = cal.second = 0;
383 new_gen = caltontp(&cal);
384 break;
386 case FILEGEN_YEAR:
387 new_gen = calyearstart(now);
388 break;
390 case FILEGEN_AGE:
391 new_gen = current_time - (current_time % SECSPERDAY);
392 break;
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
411 void
412 filegen_config(
413 FILEGEN * gen,
414 const char * basename,
415 u_int type,
416 u_int flag
419 int file_existed = 0;
420 size_t octets;
423 * if nothing would be changed...
425 if ((strcmp(basename, gen->basename) == 0) && type == gen->type
426 && flag == gen->flag)
427 return;
430 * validate parameters
432 if (!valid_fileref(gen->prefix, basename))
433 return;
435 if (NULL != gen->fp) {
436 fclose(gen->fp);
437 gen->fp = NULL;
438 file_existed = 1;
441 DPRINTF(3, ("configuring filegen:\n"
442 "\tprefix:\t%s\n"
443 "\tbasename:\t%s -> %s\n"
444 "\ttype:\t%d -> %d\n"
445 "\tflag: %x -> %x\n",
446 gen->prefix,
447 gen->basename, basename,
448 gen->type, type,
449 gen->flag, flag));
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
462 * is currently open
463 * otherwise the new settings will be used anyway at the next open
465 if (file_existed) {
466 l_fp now;
468 get_systime(&now);
469 filegen_setup(gen, now.l_ui);
475 * check whether concatenating prefix and basename
476 * yields a legal filename
478 static int
479 valid_fileref(
480 const char * prefix,
481 const char * basename
485 * prefix cannot be changed dynamically
486 * (within the context of filegen)
487 * so just reject basenames containing '..'
489 * ASSUMPTION:
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)
503 return 0;
505 if (NULL == basename)
506 return 0;
508 for (p = basename; p; p = strchr(p, DIR_SEP)) {
509 if ('.' == p[0] && '.' == p[1]
510 && ('\0' == p[2] || DIR_SEP == p[2]))
511 return 0;
514 return 1;
519 * filegen registry
522 static struct filegen_entry {
523 char * name;
524 FILEGEN * filegen;
525 struct filegen_entry * next;
526 } *filegen_registry = NULL;
529 FILEGEN *
530 filegen_get(
531 const char * name
534 struct filegen_entry *f = filegen_registry;
536 while (f) {
537 if (f->name == name || strcmp(name, f->name) == 0) {
538 DPRINTF(4, ("filegen_get(%s) = %p\n",
539 name, f->filegen));
540 return f->filegen;
542 f = f->next;
544 DPRINTF(4, ("filegen_get(%s) = NULL\n", name));
545 return NULL;
549 void
550 filegen_register(
551 const char * prefix,
552 const char * name,
553 FILEGEN * filegen
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",
568 (*ppfe)->filegen));
570 (*ppfe)->filegen = filegen;
571 return;
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"));
584 return;
589 * filegen_unregister frees memory allocated by filegen_register for
590 * name.
592 #ifdef DEBUG
593 void
594 filegen_unregister(
595 char *name
598 struct filegen_entry ** ppfe;
599 struct filegen_entry * pfe;
600 FILEGEN * fg;
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)) {
609 pfe = *ppfe;
610 *ppfe = (*ppfe)->next;
611 fg = pfe->filegen;
612 free(pfe->name);
613 free(pfe);
614 filegen_uninit(fg);
615 break;
617 ppfe = &((*ppfe)->next);
620 #endif /* DEBUG */