ntp_filegen.c revision 1.1.1.5 1 1.1 kardel /*
2 1.1 kardel * ntp_filegen.c,v 3.12 1994/01/25 19:06:11 kardel Exp
3 1.1 kardel *
4 1.1 kardel * implements file generations support for NTP
5 1.1 kardel * logfiles and statistic files
6 1.1 kardel *
7 1.1 kardel *
8 1.1 kardel * Copyright (C) 1992, 1996 by Rainer Pruy
9 1.1.1.3 christos * Friedrich-Alexander Universitaet Erlangen-Nuernberg, Germany
10 1.1 kardel *
11 1.1 kardel * This code may be modified and used freely
12 1.1 kardel * provided credits remain intact.
13 1.1 kardel */
14 1.1 kardel
15 1.1 kardel #ifdef HAVE_CONFIG_H
16 1.1 kardel # include <config.h>
17 1.1 kardel #endif
18 1.1 kardel
19 1.1 kardel #include <stdio.h>
20 1.1 kardel #include <sys/types.h>
21 1.1 kardel #include <sys/stat.h>
22 1.1 kardel
23 1.1 kardel #include "ntpd.h"
24 1.1 kardel #include "ntp_io.h"
25 1.1 kardel #include "ntp_string.h"
26 1.1 kardel #include "ntp_calendar.h"
27 1.1 kardel #include "ntp_filegen.h"
28 1.1 kardel #include "ntp_stdlib.h"
29 1.1 kardel
30 1.1 kardel /*
31 1.1 kardel * NTP is intended to run long periods of time without restart.
32 1.1 kardel * Thus log and statistic files generated by NTP will grow large.
33 1.1 kardel *
34 1.1 kardel * this set of routines provides a central interface
35 1.1 kardel * to generating files using file generations
36 1.1 kardel *
37 1.1 kardel * the generation of a file is changed according to file generation type
38 1.1 kardel */
39 1.1 kardel
40 1.1 kardel
41 1.1 kardel /*
42 1.1 kardel * redefine this if your system dislikes filename suffixes like
43 1.1 kardel * X.19910101 or X.1992W50 or ....
44 1.1 kardel */
45 1.1 kardel #define SUFFIX_SEP '.'
46 1.1 kardel
47 1.1.1.3 christos static void filegen_open (FILEGEN *, u_int32, const time_t*);
48 1.1 kardel static int valid_fileref (const char *, const char *);
49 1.1 kardel static void filegen_init (const char *, const char *, FILEGEN *);
50 1.1 kardel #ifdef DEBUG
51 1.1 kardel static void filegen_uninit (FILEGEN *);
52 1.1 kardel #endif /* DEBUG */
53 1.1 kardel
54 1.1 kardel
55 1.1 kardel /*
56 1.1 kardel * filegen_init
57 1.1 kardel */
58 1.1 kardel
59 1.1 kardel static void
60 1.1 kardel filegen_init(
61 1.1.1.3 christos const char * dir,
62 1.1.1.3 christos const char * fname,
63 1.1 kardel FILEGEN * fgp
64 1.1 kardel )
65 1.1 kardel {
66 1.1.1.3 christos fgp->fp = NULL;
67 1.1.1.3 christos fgp->dir = estrdup(dir);
68 1.1.1.3 christos fgp->fname = estrdup(fname);
69 1.1.1.3 christos fgp->id_lo = 0;
70 1.1.1.3 christos fgp->id_hi = 0;
71 1.1.1.3 christos fgp->type = FILEGEN_DAY;
72 1.1.1.3 christos fgp->flag = FGEN_FLAG_LINK; /* not yet enabled !!*/
73 1.1 kardel }
74 1.1 kardel
75 1.1 kardel
76 1.1 kardel /*
77 1.1 kardel * filegen_uninit - free memory allocated by filegen_init
78 1.1 kardel */
79 1.1 kardel #ifdef DEBUG
80 1.1 kardel static void
81 1.1 kardel filegen_uninit(
82 1.1.1.3 christos FILEGEN *fgp
83 1.1 kardel )
84 1.1 kardel {
85 1.1.1.3 christos free(fgp->dir);
86 1.1.1.3 christos free(fgp->fname);
87 1.1 kardel }
88 1.1 kardel #endif
89 1.1 kardel
90 1.1 kardel
91 1.1 kardel /*
92 1.1 kardel * open a file generation according to the current settings of gen
93 1.1 kardel * will also provide a link to basename if requested to do so
94 1.1 kardel */
95 1.1 kardel
96 1.1 kardel static void
97 1.1 kardel filegen_open(
98 1.1 kardel FILEGEN * gen,
99 1.1.1.3 christos u_int32 stamp,
100 1.1.1.3 christos const time_t * pivot
101 1.1 kardel )
102 1.1 kardel {
103 1.1.1.3 christos char *savename; /* temp store for name collision handling */
104 1.1.1.3 christos char *fullname; /* name with any designation extension */
105 1.1.1.3 christos char *filename; /* name without designation extension */
106 1.1.1.3 christos char *suffix; /* where to print suffix extension */
107 1.1.1.3 christos u_int len, suflen;
108 1.1 kardel FILE *fp;
109 1.1 kardel struct calendar cal;
110 1.1.1.3 christos struct isodate iso;
111 1.1.1.3 christos
112 1.1.1.3 christos /* get basic filename in buffer, leave room for extensions */
113 1.1.1.3 christos len = strlen(gen->dir) + strlen(gen->fname) + 65;
114 1.1.1.3 christos filename = emalloc(len);
115 1.1.1.3 christos fullname = emalloc(len);
116 1.1.1.3 christos savename = NULL;
117 1.1.1.3 christos snprintf(filename, len, "%s%s", gen->dir, gen->fname);
118 1.1.1.3 christos
119 1.1.1.3 christos /* where to place suffix */
120 1.1.1.3 christos suflen = strlcpy(fullname, filename, len);
121 1.1.1.3 christos suffix = fullname + suflen;
122 1.1.1.3 christos suflen = len - suflen;
123 1.1.1.3 christos
124 1.1.1.3 christos /* last octet of fullname set to '\0' for truncation check */
125 1.1.1.3 christos fullname[len - 1] = '\0';
126 1.1 kardel
127 1.1.1.2 kardel switch (gen->type) {
128 1.1 kardel
129 1.1 kardel default:
130 1.1 kardel msyslog(LOG_ERR,
131 1.1 kardel "unsupported file generations type %d for "
132 1.1 kardel "\"%s\" - reverting to FILEGEN_NONE",
133 1.1.1.3 christos gen->type, filename);
134 1.1 kardel gen->type = FILEGEN_NONE;
135 1.1.1.3 christos break;
136 1.1 kardel
137 1.1 kardel case FILEGEN_NONE:
138 1.1.1.3 christos /* no suffix, all set */
139 1.1 kardel break;
140 1.1 kardel
141 1.1 kardel case FILEGEN_PID:
142 1.1.1.3 christos gen->id_lo = getpid();
143 1.1.1.3 christos gen->id_hi = 0;
144 1.1.1.3 christos snprintf(suffix, suflen, "%c#%ld",
145 1.1.1.3 christos SUFFIX_SEP, gen->id_lo);
146 1.1 kardel break;
147 1.1 kardel
148 1.1 kardel case FILEGEN_DAY:
149 1.1 kardel /*
150 1.1 kardel * You can argue here in favor of using MJD, but I
151 1.1 kardel * would assume it to be easier for humans to interpret
152 1.1 kardel * dates in a format they are used to in everyday life.
153 1.1 kardel */
154 1.1.1.3 christos ntpcal_ntp_to_date(&cal, stamp, pivot);
155 1.1.1.3 christos snprintf(suffix, suflen, "%c%04d%02d%02d",
156 1.1.1.3 christos SUFFIX_SEP, cal.year, cal.month, cal.monthday);
157 1.1.1.3 christos cal.hour = cal.minute = cal.second = 0;
158 1.1.1.3 christos gen->id_lo = ntpcal_date_to_ntp(&cal);
159 1.1.1.3 christos gen->id_hi = (u_int32)(gen->id_lo + SECSPERDAY);
160 1.1 kardel break;
161 1.1 kardel
162 1.1 kardel case FILEGEN_WEEK:
163 1.1.1.3 christos isocal_ntp_to_date(&iso, stamp, pivot);
164 1.1.1.3 christos snprintf(suffix, suflen, "%c%04dw%02d",
165 1.1.1.3 christos SUFFIX_SEP, iso.year, iso.week);
166 1.1.1.3 christos iso.hour = iso.minute = iso.second = 0;
167 1.1.1.3 christos iso.weekday = 1;
168 1.1.1.3 christos gen->id_lo = isocal_date_to_ntp(&iso);
169 1.1.1.3 christos gen->id_hi = (u_int32)(gen->id_lo + 7 * SECSPERDAY);
170 1.1 kardel break;
171 1.1 kardel
172 1.1 kardel case FILEGEN_MONTH:
173 1.1.1.3 christos ntpcal_ntp_to_date(&cal, stamp, pivot);
174 1.1.1.3 christos snprintf(suffix, suflen, "%c%04d%02d",
175 1.1.1.3 christos SUFFIX_SEP, cal.year, cal.month);
176 1.1.1.3 christos cal.hour = cal.minute = cal.second = 0;
177 1.1.1.3 christos cal.monthday = 1;
178 1.1.1.3 christos gen->id_lo = ntpcal_date_to_ntp(&cal);
179 1.1.1.3 christos cal.month++;
180 1.1.1.3 christos gen->id_hi = ntpcal_date_to_ntp(&cal);
181 1.1 kardel break;
182 1.1 kardel
183 1.1 kardel case FILEGEN_YEAR:
184 1.1.1.3 christos ntpcal_ntp_to_date(&cal, stamp, pivot);
185 1.1.1.3 christos snprintf(suffix, suflen, "%c%04d",
186 1.1.1.3 christos SUFFIX_SEP, cal.year);
187 1.1.1.3 christos cal.hour = cal.minute = cal.second = 0;
188 1.1.1.3 christos cal.month = cal.monthday = 1;
189 1.1.1.3 christos gen->id_lo = ntpcal_date_to_ntp(&cal);
190 1.1.1.3 christos cal.year++;
191 1.1.1.3 christos gen->id_hi = ntpcal_date_to_ntp(&cal);
192 1.1 kardel break;
193 1.1 kardel
194 1.1 kardel case FILEGEN_AGE:
195 1.1.1.3 christos gen->id_lo = current_time - (current_time % SECSPERDAY);
196 1.1.1.3 christos gen->id_hi = gen->id_lo + SECSPERDAY;
197 1.1.1.3 christos snprintf(suffix, suflen, "%ca%08ld",
198 1.1.1.3 christos SUFFIX_SEP, gen->id_lo);
199 1.1 kardel }
200 1.1 kardel
201 1.1.1.3 christos /* check possible truncation */
202 1.1.1.3 christos if ('\0' != fullname[len - 1]) {
203 1.1.1.3 christos fullname[len - 1] = '\0';
204 1.1.1.3 christos msyslog(LOG_ERR, "logfile name truncated: \"%s\"",
205 1.1.1.3 christos fullname);
206 1.1.1.3 christos }
207 1.1.1.3 christos
208 1.1 kardel if (FILEGEN_NONE != gen->type) {
209 1.1 kardel /*
210 1.1 kardel * check for existence of a file with name 'basename'
211 1.1 kardel * as we disallow such a file
212 1.1 kardel * if FGEN_FLAG_LINK is set create a link
213 1.1 kardel */
214 1.1 kardel struct stat stats;
215 1.1 kardel /*
216 1.1 kardel * try to resolve name collisions
217 1.1 kardel */
218 1.1 kardel static u_long conflicts = 0;
219 1.1 kardel
220 1.1 kardel #ifndef S_ISREG
221 1.1 kardel #define S_ISREG(mode) (((mode) & S_IFREG) == S_IFREG)
222 1.1 kardel #endif
223 1.1.1.3 christos if (stat(filename, &stats) == 0) {
224 1.1 kardel /* Hm, file exists... */
225 1.1 kardel if (S_ISREG(stats.st_mode)) {
226 1.1 kardel if (stats.st_nlink <= 1) {
227 1.1 kardel /*
228 1.1 kardel * Oh, it is not linked - try to save it
229 1.1 kardel */
230 1.1.1.3 christos savename = emalloc(len);
231 1.1.1.3 christos snprintf(savename, len,
232 1.1 kardel "%s%c%dC%lu",
233 1.1.1.3 christos filename, SUFFIX_SEP,
234 1.1 kardel (int)getpid(), conflicts++);
235 1.1 kardel
236 1.1.1.3 christos if (rename(filename, savename) != 0)
237 1.1 kardel msyslog(LOG_ERR,
238 1.1 kardel "couldn't save %s: %m",
239 1.1.1.3 christos filename);
240 1.1 kardel free(savename);
241 1.1 kardel } else {
242 1.1 kardel /*
243 1.1 kardel * there is at least a second link to
244 1.1 kardel * this file.
245 1.1 kardel * just remove the conflicting one
246 1.1 kardel */
247 1.1 kardel if (
248 1.1 kardel #if !defined(VMS)
249 1.1.1.3 christos unlink(filename) != 0
250 1.1 kardel #else
251 1.1.1.3 christos delete(filename) != 0
252 1.1 kardel #endif
253 1.1 kardel )
254 1.1 kardel msyslog(LOG_ERR,
255 1.1 kardel "couldn't unlink %s: %m",
256 1.1.1.3 christos filename);
257 1.1 kardel }
258 1.1 kardel } else {
259 1.1 kardel /*
260 1.1 kardel * Ehh? Not a regular file ?? strange !!!!
261 1.1 kardel */
262 1.1 kardel msyslog(LOG_ERR,
263 1.1 kardel "expected regular file for %s "
264 1.1 kardel "(found mode 0%lo)",
265 1.1.1.3 christos filename,
266 1.1 kardel (unsigned long)stats.st_mode);
267 1.1 kardel }
268 1.1 kardel } else {
269 1.1 kardel /*
270 1.1 kardel * stat(..) failed, but it is absolutely correct for
271 1.1 kardel * 'basename' not to exist
272 1.1 kardel */
273 1.1 kardel if (ENOENT != errno)
274 1.1 kardel msyslog(LOG_ERR, "stat(%s) failed: %m",
275 1.1.1.3 christos filename);
276 1.1 kardel }
277 1.1 kardel }
278 1.1 kardel
279 1.1 kardel /*
280 1.1 kardel * now, try to open new file generation...
281 1.1 kardel */
282 1.1.1.3 christos DPRINTF(4, ("opening filegen (type=%d/stamp=%u) \"%s\"\n",
283 1.1.1.3 christos gen->type, stamp, fullname));
284 1.1 kardel
285 1.1.1.3 christos fp = fopen(fullname, "a");
286 1.1.1.3 christos
287 1.1 kardel if (NULL == fp) {
288 1.1 kardel /* open failed -- keep previous state
289 1.1 kardel *
290 1.1 kardel * If the file was open before keep the previous generation.
291 1.1 kardel * This will cause output to end up in the 'wrong' file,
292 1.1 kardel * but I think this is still better than losing output
293 1.1 kardel *
294 1.1 kardel * ignore errors due to missing directories
295 1.1 kardel */
296 1.1 kardel
297 1.1 kardel if (ENOENT != errno)
298 1.1.1.3 christos msyslog(LOG_ERR, "can't open %s: %m", fullname);
299 1.1 kardel } else {
300 1.1 kardel if (NULL != gen->fp) {
301 1.1 kardel fclose(gen->fp);
302 1.1 kardel gen->fp = NULL;
303 1.1 kardel }
304 1.1 kardel gen->fp = fp;
305 1.1 kardel
306 1.1 kardel if (gen->flag & FGEN_FLAG_LINK) {
307 1.1 kardel /*
308 1.1 kardel * need to link file to basename
309 1.1 kardel * have to use hardlink for now as I want to allow
310 1.1 kardel * gen->basename spanning directory levels
311 1.1 kardel * this would make it more complex to get the correct
312 1.1.1.3 christos * fullname for symlink
313 1.1 kardel *
314 1.1 kardel * Ok, it would just mean taking the part following
315 1.1 kardel * the last '/' in the name.... Should add it later....
316 1.1 kardel */
317 1.1 kardel
318 1.1 kardel /* Windows NT does not support file links -Greg Schueman 1/18/97 */
319 1.1 kardel
320 1.1.1.3 christos #if defined(SYS_WINNT) || defined(SYS_VXWORKS)
321 1.1 kardel SetLastError(0); /* On WinNT, don't support FGEN_FLAG_LINK */
322 1.1 kardel #elif defined(VMS)
323 1.1 kardel errno = 0; /* On VMS, don't support FGEN_FLAG_LINK */
324 1.1 kardel #else /* not (VMS) / VXWORKS / WINNT ; DO THE LINK) */
325 1.1.1.3 christos if (link(fullname, filename) != 0)
326 1.1 kardel if (EEXIST != errno)
327 1.1 kardel msyslog(LOG_ERR,
328 1.1 kardel "can't link(%s, %s): %m",
329 1.1.1.3 christos fullname, filename);
330 1.1 kardel #endif /* SYS_WINNT || VXWORKS */
331 1.1 kardel } /* flags & FGEN_FLAG_LINK */
332 1.1 kardel } /* else fp == NULL */
333 1.1 kardel
334 1.1 kardel free(filename);
335 1.1.1.3 christos free(fullname);
336 1.1 kardel return;
337 1.1 kardel }
338 1.1 kardel
339 1.1 kardel /*
340 1.1 kardel * this function sets up gen->fp to point to the correct
341 1.1 kardel * generation of the file for the time specified by 'now'
342 1.1 kardel *
343 1.1 kardel * 'now' usually is interpreted as second part of a l_fp as is in the cal...
344 1.1 kardel * library routines
345 1.1 kardel */
346 1.1 kardel
347 1.1 kardel void
348 1.1 kardel filegen_setup(
349 1.1 kardel FILEGEN * gen,
350 1.1.1.3 christos u_int32 now
351 1.1 kardel )
352 1.1 kardel {
353 1.1.1.3 christos int current;
354 1.1.1.3 christos time_t pivot;
355 1.1 kardel
356 1.1 kardel if (!(gen->flag & FGEN_FLAG_ENABLED)) {
357 1.1 kardel if (NULL != gen->fp) {
358 1.1 kardel fclose(gen->fp);
359 1.1 kardel gen->fp = NULL;
360 1.1 kardel }
361 1.1 kardel return;
362 1.1 kardel }
363 1.1.1.3 christos
364 1.1 kardel switch (gen->type) {
365 1.1 kardel
366 1.1.1.3 christos default:
367 1.1 kardel case FILEGEN_NONE:
368 1.1.1.3 christos current = TRUE;
369 1.1 kardel break;
370 1.1 kardel
371 1.1 kardel case FILEGEN_PID:
372 1.1.1.3 christos current = ((int)gen->id_lo == getpid());
373 1.1 kardel break;
374 1.1 kardel
375 1.1.1.3 christos case FILEGEN_AGE:
376 1.1.1.3 christos current = (gen->id_lo <= current_time) &&
377 1.1.1.3 christos (gen->id_hi > current_time);
378 1.1 kardel break;
379 1.1 kardel
380 1.1.1.3 christos case FILEGEN_DAY:
381 1.1 kardel case FILEGEN_WEEK:
382 1.1 kardel case FILEGEN_MONTH:
383 1.1 kardel case FILEGEN_YEAR:
384 1.1.1.3 christos current = (gen->id_lo <= now) &&
385 1.1.1.3 christos (gen->id_hi > now);
386 1.1 kardel break;
387 1.1 kardel }
388 1.1 kardel /*
389 1.1 kardel * try to open file if not yet open
390 1.1 kardel * reopen new file generation file on change of generation id
391 1.1 kardel */
392 1.1.1.3 christos if (NULL == gen->fp || !current) {
393 1.1.1.3 christos DPRINTF(1, ("filegen %0x %u\n", gen->type, now));
394 1.1.1.3 christos pivot = time(NULL);
395 1.1.1.3 christos filegen_open(gen, now, &pivot);
396 1.1 kardel }
397 1.1 kardel }
398 1.1 kardel
399 1.1 kardel
400 1.1 kardel /*
401 1.1 kardel * change settings for filegen files
402 1.1 kardel */
403 1.1 kardel void
404 1.1 kardel filegen_config(
405 1.1 kardel FILEGEN * gen,
406 1.1.1.3 christos const char * dir,
407 1.1.1.3 christos const char * fname,
408 1.1 kardel u_int type,
409 1.1 kardel u_int flag
410 1.1 kardel )
411 1.1 kardel {
412 1.1.1.3 christos int file_existed;
413 1.1.1.3 christos l_fp now;
414 1.1.1.3 christos
415 1.1 kardel
416 1.1 kardel /*
417 1.1 kardel * if nothing would be changed...
418 1.1 kardel */
419 1.1.1.3 christos if (strcmp(dir, gen->dir) == 0 && strcmp(fname, gen->fname) == 0
420 1.1.1.3 christos && type == gen->type && flag == gen->flag)
421 1.1 kardel return;
422 1.1.1.3 christos
423 1.1 kardel /*
424 1.1 kardel * validate parameters
425 1.1 kardel */
426 1.1.1.3 christos if (!valid_fileref(dir, fname))
427 1.1 kardel return;
428 1.1 kardel
429 1.1 kardel if (NULL != gen->fp) {
430 1.1 kardel fclose(gen->fp);
431 1.1 kardel gen->fp = NULL;
432 1.1.1.3 christos file_existed = TRUE;
433 1.1.1.3 christos } else {
434 1.1.1.3 christos file_existed = FALSE;
435 1.1 kardel }
436 1.1 kardel
437 1.1 kardel DPRINTF(3, ("configuring filegen:\n"
438 1.1.1.3 christos "\tdir:\t%s -> %s\n"
439 1.1.1.3 christos "\tfname:\t%s -> %s\n"
440 1.1 kardel "\ttype:\t%d -> %d\n"
441 1.1 kardel "\tflag: %x -> %x\n",
442 1.1.1.3 christos gen->dir, dir,
443 1.1.1.3 christos gen->fname, fname,
444 1.1 kardel gen->type, type,
445 1.1 kardel gen->flag, flag));
446 1.1 kardel
447 1.1.1.3 christos if (strcmp(gen->dir, dir) != 0) {
448 1.1.1.3 christos free(gen->dir);
449 1.1.1.3 christos gen->dir = estrdup(dir);
450 1.1.1.3 christos }
451 1.1.1.3 christos
452 1.1.1.3 christos if (strcmp(gen->fname, fname) != 0) {
453 1.1.1.3 christos free(gen->fname);
454 1.1.1.3 christos gen->fname = estrdup(fname);
455 1.1 kardel }
456 1.1.1.2 kardel gen->type = (u_char)type;
457 1.1.1.2 kardel gen->flag = (u_char)flag;
458 1.1 kardel
459 1.1 kardel /*
460 1.1 kardel * make filegen use the new settings
461 1.1 kardel * special action is only required when a generation file
462 1.1 kardel * is currently open
463 1.1 kardel * otherwise the new settings will be used anyway at the next open
464 1.1 kardel */
465 1.1 kardel if (file_existed) {
466 1.1 kardel get_systime(&now);
467 1.1 kardel filegen_setup(gen, now.l_ui);
468 1.1 kardel }
469 1.1 kardel }
470 1.1 kardel
471 1.1 kardel
472 1.1 kardel /*
473 1.1 kardel * check whether concatenating prefix and basename
474 1.1 kardel * yields a legal filename
475 1.1 kardel */
476 1.1 kardel static int
477 1.1 kardel valid_fileref(
478 1.1.1.3 christos const char * dir,
479 1.1.1.3 christos const char * fname
480 1.1 kardel )
481 1.1 kardel {
482 1.1 kardel /*
483 1.1.1.3 christos * dir cannot be changed dynamically
484 1.1 kardel * (within the context of filegen)
485 1.1 kardel * so just reject basenames containing '..'
486 1.1 kardel *
487 1.1 kardel * ASSUMPTION:
488 1.1.1.3 christos * file system parts 'below' dir may be
489 1.1 kardel * specified without infringement of security
490 1.1 kardel *
491 1.1.1.3 christos * restricting dir to legal values
492 1.1 kardel * has to be ensured by other means
493 1.1 kardel * (however, it would be possible to perform some checks here...)
494 1.1 kardel */
495 1.1.1.3 christos const char *p;
496 1.1.1.3 christos
497 1.1 kardel /*
498 1.1 kardel * Just to catch, dumb errors opening up the world...
499 1.1 kardel */
500 1.1.1.3 christos if (NULL == dir || '\0' == dir[0])
501 1.1.1.3 christos return FALSE;
502 1.1 kardel
503 1.1.1.3 christos if (NULL == fname)
504 1.1.1.3 christos return FALSE;
505 1.1.1.3 christos
506 1.1.1.3 christos #ifdef SYS_WINNT
507 1.1.1.3 christos /*
508 1.1.1.3 christos * Windows treats / equivalent to \, reject any / to ensure
509 1.1.1.3 christos * check below for DIR_SEP (\ on windows) are adequate.
510 1.1.1.3 christos */
511 1.1.1.3 christos if (strchr(fname, '/')) {
512 1.1.1.3 christos msyslog(LOG_ERR,
513 1.1.1.3 christos "filegen filenames must not contain '/': %s",
514 1.1.1.3 christos fname);
515 1.1.1.3 christos return FALSE;
516 1.1.1.3 christos }
517 1.1.1.3 christos #endif
518 1.1.1.3 christos
519 1.1.1.3 christos for (p = fname; p != NULL; p = strchr(p, DIR_SEP)) {
520 1.1 kardel if ('.' == p[0] && '.' == p[1]
521 1.1 kardel && ('\0' == p[2] || DIR_SEP == p[2]))
522 1.1.1.3 christos return FALSE;
523 1.1 kardel }
524 1.1.1.3 christos
525 1.1.1.3 christos return TRUE;
526 1.1 kardel }
527 1.1 kardel
528 1.1 kardel
529 1.1 kardel /*
530 1.1 kardel * filegen registry
531 1.1 kardel */
532 1.1 kardel
533 1.1 kardel static struct filegen_entry {
534 1.1 kardel char * name;
535 1.1 kardel FILEGEN * filegen;
536 1.1 kardel struct filegen_entry * next;
537 1.1 kardel } *filegen_registry = NULL;
538 1.1 kardel
539 1.1 kardel
540 1.1 kardel FILEGEN *
541 1.1 kardel filegen_get(
542 1.1 kardel const char * name
543 1.1 kardel )
544 1.1 kardel {
545 1.1 kardel struct filegen_entry *f = filegen_registry;
546 1.1 kardel
547 1.1 kardel while (f) {
548 1.1 kardel if (f->name == name || strcmp(name, f->name) == 0) {
549 1.1 kardel DPRINTF(4, ("filegen_get(%s) = %p\n",
550 1.1 kardel name, f->filegen));
551 1.1 kardel return f->filegen;
552 1.1 kardel }
553 1.1 kardel f = f->next;
554 1.1 kardel }
555 1.1 kardel DPRINTF(4, ("filegen_get(%s) = NULL\n", name));
556 1.1 kardel return NULL;
557 1.1 kardel }
558 1.1 kardel
559 1.1 kardel
560 1.1 kardel void
561 1.1 kardel filegen_register(
562 1.1.1.3 christos const char * dir,
563 1.1 kardel const char * name,
564 1.1 kardel FILEGEN * filegen
565 1.1 kardel )
566 1.1 kardel {
567 1.1 kardel struct filegen_entry **ppfe;
568 1.1 kardel
569 1.1 kardel DPRINTF(4, ("filegen_register(%s, %p)\n", name, filegen));
570 1.1 kardel
571 1.1.1.3 christos filegen_init(dir, name, filegen);
572 1.1 kardel
573 1.1 kardel ppfe = &filegen_registry;
574 1.1 kardel while (NULL != *ppfe) {
575 1.1 kardel if ((*ppfe)->name == name
576 1.1 kardel || !strcmp((*ppfe)->name, name)) {
577 1.1 kardel
578 1.1 kardel DPRINTF(5, ("replacing filegen %p\n",
579 1.1 kardel (*ppfe)->filegen));
580 1.1 kardel
581 1.1 kardel (*ppfe)->filegen = filegen;
582 1.1 kardel return;
583 1.1 kardel }
584 1.1 kardel ppfe = &((*ppfe)->next);
585 1.1 kardel }
586 1.1 kardel
587 1.1 kardel *ppfe = emalloc(sizeof **ppfe);
588 1.1 kardel
589 1.1 kardel (*ppfe)->next = NULL;
590 1.1 kardel (*ppfe)->name = estrdup(name);
591 1.1 kardel (*ppfe)->filegen = filegen;
592 1.1 kardel
593 1.1 kardel DPRINTF(6, ("adding new filegen\n"));
594 1.1 kardel
595 1.1 kardel return;
596 1.1 kardel }
597 1.1 kardel
598 1.1 kardel
599 1.1 kardel /*
600 1.1.1.3 christos * filegen_statsdir() - reset each filegen entry's dir to statsdir.
601 1.1.1.3 christos */
602 1.1.1.3 christos void
603 1.1.1.3 christos filegen_statsdir(void)
604 1.1.1.3 christos {
605 1.1.1.3 christos struct filegen_entry *f;
606 1.1.1.3 christos
607 1.1.1.3 christos for (f = filegen_registry; f != NULL; f = f->next)
608 1.1.1.3 christos filegen_config(f->filegen, statsdir, f->filegen->fname,
609 1.1.1.3 christos f->filegen->type, f->filegen->flag);
610 1.1.1.3 christos }
611 1.1.1.3 christos
612 1.1.1.3 christos
613 1.1.1.3 christos /*
614 1.1 kardel * filegen_unregister frees memory allocated by filegen_register for
615 1.1 kardel * name.
616 1.1 kardel */
617 1.1 kardel #ifdef DEBUG
618 1.1 kardel void
619 1.1 kardel filegen_unregister(
620 1.1.1.4 christos const char *name
621 1.1 kardel )
622 1.1 kardel {
623 1.1 kardel struct filegen_entry ** ppfe;
624 1.1 kardel struct filegen_entry * pfe;
625 1.1 kardel FILEGEN * fg;
626 1.1 kardel
627 1.1 kardel DPRINTF(4, ("filegen_unregister(%s)\n", name));
628 1.1 kardel
629 1.1 kardel ppfe = &filegen_registry;
630 1.1 kardel
631 1.1 kardel while (NULL != *ppfe) {
632 1.1 kardel if ((*ppfe)->name == name
633 1.1 kardel || !strcmp((*ppfe)->name, name)) {
634 1.1 kardel pfe = *ppfe;
635 1.1 kardel *ppfe = (*ppfe)->next;
636 1.1 kardel fg = pfe->filegen;
637 1.1 kardel free(pfe->name);
638 1.1 kardel free(pfe);
639 1.1 kardel filegen_uninit(fg);
640 1.1 kardel break;
641 1.1 kardel }
642 1.1 kardel ppfe = &((*ppfe)->next);
643 1.1 kardel }
644 1.1 kardel }
645 1.1 kardel #endif /* DEBUG */
646