source: trunk/third/xntp/xntpd/ntp_filegen.c @ 10832

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