/contrib/ntp/ntpd/ntp_filegen.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 576 lines · 353 code · 72 blank · 151 comment · 94 complexity · 5ecabf2b67e075e099587d854405d161 MD5 · raw file

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