PageRenderTime 51ms CodeModel.GetById 10ms RepoModel.GetById 1ms app.codeStats 0ms

/usr.sbin/newsyslog/newsyslog.c

https://bitbucket.org/freebsd/freebsd-head/
C | 2558 lines | 1851 code | 243 blank | 464 comment | 622 complexity | cd6704cb1be1d99eb3160835f591c371 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, BSD-3-Clause, LGPL-2.0, LGPL-2.1, BSD-2-Clause, 0BSD, JSON, AGPL-1.0, GPL-2.0

Large files files are truncated, but you can click here to view the full file

  1. /*-
  2. * ------+---------+---------+-------- + --------+---------+---------+---------*
  3. * This file includes significant modifications done by:
  4. * Copyright (c) 2003, 2004 - Garance Alistair Drosehn <gad@FreeBSD.org>.
  5. * All rights reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. * 1. Redistributions of source code must retain the above copyright
  11. * notice, this list of conditions and the following disclaimer.
  12. * 2. Redistributions in binary form must reproduce the above copyright
  13. * notice, this list of conditions and the following disclaimer in the
  14. * documentation and/or other materials provided with the distribution.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  17. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  18. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  19. * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  20. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  21. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  22. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  23. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  24. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  25. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  26. * SUCH DAMAGE.
  27. *
  28. * ------+---------+---------+-------- + --------+---------+---------+---------*
  29. */
  30. /*
  31. * This file contains changes from the Open Software Foundation.
  32. */
  33. /*
  34. * Copyright 1988, 1989 by the Massachusetts Institute of Technology
  35. *
  36. * Permission to use, copy, modify, and distribute this software and its
  37. * documentation for any purpose and without fee is hereby granted, provided
  38. * that the above copyright notice appear in all copies and that both that
  39. * copyright notice and this permission notice appear in supporting
  40. * documentation, and that the names of M.I.T. and the M.I.T. S.I.P.B. not be
  41. * used in advertising or publicity pertaining to distribution of the
  42. * software without specific, written prior permission. M.I.T. and the M.I.T.
  43. * S.I.P.B. make no representations about the suitability of this software
  44. * for any purpose. It is provided "as is" without express or implied
  45. * warranty.
  46. *
  47. */
  48. /*
  49. * newsyslog - roll over selected logs at the appropriate time, keeping the a
  50. * specified number of backup files around.
  51. */
  52. #include <sys/cdefs.h>
  53. __FBSDID("$FreeBSD$");
  54. #define OSF
  55. #include <sys/param.h>
  56. #include <sys/queue.h>
  57. #include <sys/stat.h>
  58. #include <sys/wait.h>
  59. #include <assert.h>
  60. #include <ctype.h>
  61. #include <err.h>
  62. #include <errno.h>
  63. #include <dirent.h>
  64. #include <fcntl.h>
  65. #include <fnmatch.h>
  66. #include <glob.h>
  67. #include <grp.h>
  68. #include <paths.h>
  69. #include <pwd.h>
  70. #include <signal.h>
  71. #include <stdio.h>
  72. #include <libgen.h>
  73. #include <stdlib.h>
  74. #include <string.h>
  75. #include <time.h>
  76. #include <unistd.h>
  77. #include "pathnames.h"
  78. #include "extern.h"
  79. /*
  80. * Compression suffixes
  81. */
  82. #ifndef COMPRESS_SUFFIX_GZ
  83. #define COMPRESS_SUFFIX_GZ ".gz"
  84. #endif
  85. #ifndef COMPRESS_SUFFIX_BZ2
  86. #define COMPRESS_SUFFIX_BZ2 ".bz2"
  87. #endif
  88. #ifndef COMPRESS_SUFFIX_XZ
  89. #define COMPRESS_SUFFIX_XZ ".xz"
  90. #endif
  91. #define COMPRESS_SUFFIX_MAXLEN MAX(MAX(sizeof(COMPRESS_SUFFIX_GZ),sizeof(COMPRESS_SUFFIX_BZ2)),sizeof(COMPRESS_SUFFIX_XZ))
  92. /*
  93. * Compression types
  94. */
  95. #define COMPRESS_TYPES 4 /* Number of supported compression types */
  96. #define COMPRESS_NONE 0
  97. #define COMPRESS_GZIP 1
  98. #define COMPRESS_BZIP2 2
  99. #define COMPRESS_XZ 3
  100. /*
  101. * Bit-values for the 'flags' parsed from a config-file entry.
  102. */
  103. #define CE_BINARY 0x0008 /* Logfile is in binary, do not add status */
  104. /* messages to logfile(s) when rotating. */
  105. #define CE_NOSIGNAL 0x0010 /* There is no process to signal when */
  106. /* trimming this file. */
  107. #define CE_TRIMAT 0x0020 /* trim file at a specific time. */
  108. #define CE_GLOB 0x0040 /* name of the log is file name pattern. */
  109. #define CE_SIGNALGROUP 0x0080 /* Signal a process-group instead of a single */
  110. /* process when trimming this file. */
  111. #define CE_CREATE 0x0100 /* Create the log file if it does not exist. */
  112. #define CE_NODUMP 0x0200 /* Set 'nodump' on newly created log file. */
  113. #define CE_PID2CMD 0x0400 /* Replace PID file with a shell command.*/
  114. #define MIN_PID 5 /* Don't touch pids lower than this */
  115. #define MAX_PID 99999 /* was lower, see /usr/include/sys/proc.h */
  116. #define kbytes(size) (((size) + 1023) >> 10)
  117. #define DEFAULT_MARKER "<default>"
  118. #define DEBUG_MARKER "<debug>"
  119. #define INCLUDE_MARKER "<include>"
  120. #define DEFAULT_TIMEFNAME_FMT "%Y%m%dT%H%M%S"
  121. #define MAX_OLDLOGS 65536 /* Default maximum number of old logfiles */
  122. struct compress_types {
  123. const char *flag; /* Flag in configuration file */
  124. const char *suffix; /* Compression suffix */
  125. const char *path; /* Path to compression program */
  126. };
  127. const struct compress_types compress_type[COMPRESS_TYPES] = {
  128. { "", "", "" }, /* no compression */
  129. { "Z", COMPRESS_SUFFIX_GZ, _PATH_GZIP }, /* gzip compression */
  130. { "J", COMPRESS_SUFFIX_BZ2, _PATH_BZIP2 }, /* bzip2 compression */
  131. { "X", COMPRESS_SUFFIX_XZ, _PATH_XZ } /* xz compression */
  132. };
  133. struct conf_entry {
  134. STAILQ_ENTRY(conf_entry) cf_nextp;
  135. char *log; /* Name of the log */
  136. char *pid_cmd_file; /* PID or command file */
  137. char *r_reason; /* The reason this file is being rotated */
  138. int firstcreate; /* Creating log for the first time (-C). */
  139. int rotate; /* Non-zero if this file should be rotated */
  140. int fsize; /* size found for the log file */
  141. uid_t uid; /* Owner of log */
  142. gid_t gid; /* Group of log */
  143. int numlogs; /* Number of logs to keep */
  144. int trsize; /* Size cutoff to trigger trimming the log */
  145. int hours; /* Hours between log trimming */
  146. struct ptime_data *trim_at; /* Specific time to do trimming */
  147. unsigned int permissions; /* File permissions on the log */
  148. int flags; /* CE_BINARY */
  149. int compress; /* Compression */
  150. int sig; /* Signal to send */
  151. int def_cfg; /* Using the <default> rule for this file */
  152. };
  153. struct sigwork_entry {
  154. SLIST_ENTRY(sigwork_entry) sw_nextp;
  155. int sw_signum; /* the signal to send */
  156. int sw_pidok; /* true if pid value is valid */
  157. pid_t sw_pid; /* the process id from the PID file */
  158. const char *sw_pidtype; /* "daemon" or "process group" */
  159. int run_cmd; /* run command or send PID to signal */
  160. char sw_fname[1]; /* file the PID was read from or shell cmd */
  161. };
  162. struct zipwork_entry {
  163. SLIST_ENTRY(zipwork_entry) zw_nextp;
  164. const struct conf_entry *zw_conf; /* for chown/perm/flag info */
  165. const struct sigwork_entry *zw_swork; /* to know success of signal */
  166. int zw_fsize; /* size of the file to compress */
  167. char zw_fname[1]; /* the file to compress */
  168. };
  169. struct include_entry {
  170. STAILQ_ENTRY(include_entry) inc_nextp;
  171. const char *file; /* Name of file to process */
  172. };
  173. struct oldlog_entry {
  174. char *fname; /* Filename of the log file */
  175. time_t t; /* Parsed timestamp of the logfile */
  176. };
  177. typedef enum {
  178. FREE_ENT, KEEP_ENT
  179. } fk_entry;
  180. STAILQ_HEAD(cflist, conf_entry);
  181. SLIST_HEAD(swlisthead, sigwork_entry) swhead = SLIST_HEAD_INITIALIZER(swhead);
  182. SLIST_HEAD(zwlisthead, zipwork_entry) zwhead = SLIST_HEAD_INITIALIZER(zwhead);
  183. STAILQ_HEAD(ilist, include_entry);
  184. int dbg_at_times; /* -D Show details of 'trim_at' code */
  185. int archtodir = 0; /* Archive old logfiles to other directory */
  186. int createlogs; /* Create (non-GLOB) logfiles which do not */
  187. /* already exist. 1=='for entries with */
  188. /* C flag', 2=='for all entries'. */
  189. int verbose = 0; /* Print out what's going on */
  190. int needroot = 1; /* Root privs are necessary */
  191. int noaction = 0; /* Don't do anything, just show it */
  192. int norotate = 0; /* Don't rotate */
  193. int nosignal; /* Do not send any signals */
  194. int enforcepid = 0; /* If PID file does not exist or empty, do nothing */
  195. int force = 0; /* Force the trim no matter what */
  196. int rotatereq = 0; /* -R = Always rotate the file(s) as given */
  197. /* on the command (this also requires */
  198. /* that a list of files *are* given on */
  199. /* the run command). */
  200. char *requestor; /* The name given on a -R request */
  201. char *timefnamefmt = NULL; /* Use time based filenames instead of .0 etc */
  202. char *archdirname; /* Directory path to old logfiles archive */
  203. char *destdir = NULL; /* Directory to treat at root for logs */
  204. const char *conf; /* Configuration file to use */
  205. struct ptime_data *dbg_timenow; /* A "timenow" value set via -D option */
  206. struct ptime_data *timenow; /* The time to use for checking at-fields */
  207. #define DAYTIME_LEN 16
  208. char daytime[DAYTIME_LEN]; /* The current time in human readable form,
  209. * used for rotation-tracking messages. */
  210. char hostname[MAXHOSTNAMELEN]; /* hostname */
  211. const char *path_syslogpid = _PATH_SYSLOGPID;
  212. static struct cflist *get_worklist(char **files);
  213. static void parse_file(FILE *cf, struct cflist *work_p, struct cflist *glob_p,
  214. struct conf_entry *defconf_p, struct ilist *inclist);
  215. static void add_to_queue(const char *fname, struct ilist *inclist);
  216. static char *sob(char *p);
  217. static char *son(char *p);
  218. static int isnumberstr(const char *);
  219. static int isglobstr(const char *);
  220. static char *missing_field(char *p, char *errline);
  221. static void change_attrs(const char *, const struct conf_entry *);
  222. static const char *get_logfile_suffix(const char *logfile);
  223. static fk_entry do_entry(struct conf_entry *);
  224. static fk_entry do_rotate(const struct conf_entry *);
  225. static void do_sigwork(struct sigwork_entry *);
  226. static void do_zipwork(struct zipwork_entry *);
  227. static struct sigwork_entry *
  228. save_sigwork(const struct conf_entry *);
  229. static struct zipwork_entry *
  230. save_zipwork(const struct conf_entry *, const struct
  231. sigwork_entry *, int, const char *);
  232. static void set_swpid(struct sigwork_entry *, const struct conf_entry *);
  233. static int sizefile(const char *);
  234. static void expand_globs(struct cflist *work_p, struct cflist *glob_p);
  235. static void free_clist(struct cflist *list);
  236. static void free_entry(struct conf_entry *ent);
  237. static struct conf_entry *init_entry(const char *fname,
  238. struct conf_entry *src_entry);
  239. static void parse_args(int argc, char **argv);
  240. static int parse_doption(const char *doption);
  241. static void usage(void);
  242. static int log_trim(const char *logname, const struct conf_entry *log_ent);
  243. static int age_old_log(char *file);
  244. static void savelog(char *from, char *to);
  245. static void createdir(const struct conf_entry *ent, char *dirpart);
  246. static void createlog(const struct conf_entry *ent);
  247. /*
  248. * All the following take a parameter of 'int', but expect values in the
  249. * range of unsigned char. Define wrappers which take values of type 'char',
  250. * whether signed or unsigned, and ensure they end up in the right range.
  251. */
  252. #define isdigitch(Anychar) isdigit((u_char)(Anychar))
  253. #define isprintch(Anychar) isprint((u_char)(Anychar))
  254. #define isspacech(Anychar) isspace((u_char)(Anychar))
  255. #define tolowerch(Anychar) tolower((u_char)(Anychar))
  256. int
  257. main(int argc, char **argv)
  258. {
  259. struct cflist *worklist;
  260. struct conf_entry *p;
  261. struct sigwork_entry *stmp;
  262. struct zipwork_entry *ztmp;
  263. SLIST_INIT(&swhead);
  264. SLIST_INIT(&zwhead);
  265. parse_args(argc, argv);
  266. argc -= optind;
  267. argv += optind;
  268. if (needroot && getuid() && geteuid())
  269. errx(1, "must have root privs");
  270. worklist = get_worklist(argv);
  271. /*
  272. * Rotate all the files which need to be rotated. Note that
  273. * some users have *hundreds* of entries in newsyslog.conf!
  274. */
  275. while (!STAILQ_EMPTY(worklist)) {
  276. p = STAILQ_FIRST(worklist);
  277. STAILQ_REMOVE_HEAD(worklist, cf_nextp);
  278. if (do_entry(p) == FREE_ENT)
  279. free_entry(p);
  280. }
  281. /*
  282. * Send signals to any processes which need a signal to tell
  283. * them to close and re-open the log file(s) we have rotated.
  284. * Note that zipwork_entries include pointers to these
  285. * sigwork_entry's, so we can not free the entries here.
  286. */
  287. if (!SLIST_EMPTY(&swhead)) {
  288. if (noaction || verbose)
  289. printf("Signal all daemon process(es)...\n");
  290. SLIST_FOREACH(stmp, &swhead, sw_nextp)
  291. do_sigwork(stmp);
  292. if (noaction)
  293. printf("\tsleep 10\n");
  294. else {
  295. if (verbose)
  296. printf("Pause 10 seconds to allow daemon(s)"
  297. " to close log file(s)\n");
  298. sleep(10);
  299. }
  300. }
  301. /*
  302. * Compress all files that we're expected to compress, now
  303. * that all processes should have closed the files which
  304. * have been rotated.
  305. */
  306. if (!SLIST_EMPTY(&zwhead)) {
  307. if (noaction || verbose)
  308. printf("Compress all rotated log file(s)...\n");
  309. while (!SLIST_EMPTY(&zwhead)) {
  310. ztmp = SLIST_FIRST(&zwhead);
  311. do_zipwork(ztmp);
  312. SLIST_REMOVE_HEAD(&zwhead, zw_nextp);
  313. free(ztmp);
  314. }
  315. }
  316. /* Now free all the sigwork entries. */
  317. while (!SLIST_EMPTY(&swhead)) {
  318. stmp = SLIST_FIRST(&swhead);
  319. SLIST_REMOVE_HEAD(&swhead, sw_nextp);
  320. free(stmp);
  321. }
  322. while (wait(NULL) > 0 || errno == EINTR)
  323. ;
  324. return (0);
  325. }
  326. static struct conf_entry *
  327. init_entry(const char *fname, struct conf_entry *src_entry)
  328. {
  329. struct conf_entry *tempwork;
  330. if (verbose > 4)
  331. printf("\t--> [creating entry for %s]\n", fname);
  332. tempwork = malloc(sizeof(struct conf_entry));
  333. if (tempwork == NULL)
  334. err(1, "malloc of conf_entry for %s", fname);
  335. if (destdir == NULL || fname[0] != '/')
  336. tempwork->log = strdup(fname);
  337. else
  338. asprintf(&tempwork->log, "%s%s", destdir, fname);
  339. if (tempwork->log == NULL)
  340. err(1, "strdup for %s", fname);
  341. if (src_entry != NULL) {
  342. tempwork->pid_cmd_file = NULL;
  343. if (src_entry->pid_cmd_file)
  344. tempwork->pid_cmd_file = strdup(src_entry->pid_cmd_file);
  345. tempwork->r_reason = NULL;
  346. tempwork->firstcreate = 0;
  347. tempwork->rotate = 0;
  348. tempwork->fsize = -1;
  349. tempwork->uid = src_entry->uid;
  350. tempwork->gid = src_entry->gid;
  351. tempwork->numlogs = src_entry->numlogs;
  352. tempwork->trsize = src_entry->trsize;
  353. tempwork->hours = src_entry->hours;
  354. tempwork->trim_at = NULL;
  355. if (src_entry->trim_at != NULL)
  356. tempwork->trim_at = ptime_init(src_entry->trim_at);
  357. tempwork->permissions = src_entry->permissions;
  358. tempwork->flags = src_entry->flags;
  359. tempwork->compress = src_entry->compress;
  360. tempwork->sig = src_entry->sig;
  361. tempwork->def_cfg = src_entry->def_cfg;
  362. } else {
  363. /* Initialize as a "do-nothing" entry */
  364. tempwork->pid_cmd_file = NULL;
  365. tempwork->r_reason = NULL;
  366. tempwork->firstcreate = 0;
  367. tempwork->rotate = 0;
  368. tempwork->fsize = -1;
  369. tempwork->uid = (uid_t)-1;
  370. tempwork->gid = (gid_t)-1;
  371. tempwork->numlogs = 1;
  372. tempwork->trsize = -1;
  373. tempwork->hours = -1;
  374. tempwork->trim_at = NULL;
  375. tempwork->permissions = 0;
  376. tempwork->flags = 0;
  377. tempwork->compress = COMPRESS_NONE;
  378. tempwork->sig = SIGHUP;
  379. tempwork->def_cfg = 0;
  380. }
  381. return (tempwork);
  382. }
  383. static void
  384. free_entry(struct conf_entry *ent)
  385. {
  386. if (ent == NULL)
  387. return;
  388. if (ent->log != NULL) {
  389. if (verbose > 4)
  390. printf("\t--> [freeing entry for %s]\n", ent->log);
  391. free(ent->log);
  392. ent->log = NULL;
  393. }
  394. if (ent->pid_cmd_file != NULL) {
  395. free(ent->pid_cmd_file);
  396. ent->pid_cmd_file = NULL;
  397. }
  398. if (ent->r_reason != NULL) {
  399. free(ent->r_reason);
  400. ent->r_reason = NULL;
  401. }
  402. if (ent->trim_at != NULL) {
  403. ptime_free(ent->trim_at);
  404. ent->trim_at = NULL;
  405. }
  406. free(ent);
  407. }
  408. static void
  409. free_clist(struct cflist *list)
  410. {
  411. struct conf_entry *ent;
  412. while (!STAILQ_EMPTY(list)) {
  413. ent = STAILQ_FIRST(list);
  414. STAILQ_REMOVE_HEAD(list, cf_nextp);
  415. free_entry(ent);
  416. }
  417. free(list);
  418. list = NULL;
  419. }
  420. static fk_entry
  421. do_entry(struct conf_entry * ent)
  422. {
  423. #define REASON_MAX 80
  424. int modtime;
  425. fk_entry free_or_keep;
  426. double diffsecs;
  427. char temp_reason[REASON_MAX];
  428. int oversized;
  429. free_or_keep = FREE_ENT;
  430. if (verbose)
  431. printf("%s <%d%s>: ", ent->log, ent->numlogs,
  432. compress_type[ent->compress].flag);
  433. ent->fsize = sizefile(ent->log);
  434. oversized = ((ent->trsize > 0) && (ent->fsize >= ent->trsize));
  435. modtime = age_old_log(ent->log);
  436. ent->rotate = 0;
  437. ent->firstcreate = 0;
  438. if (ent->fsize < 0) {
  439. /*
  440. * If either the C flag or the -C option was specified,
  441. * and if we won't be creating the file, then have the
  442. * verbose message include a hint as to why the file
  443. * will not be created.
  444. */
  445. temp_reason[0] = '\0';
  446. if (createlogs > 1)
  447. ent->firstcreate = 1;
  448. else if ((ent->flags & CE_CREATE) && createlogs)
  449. ent->firstcreate = 1;
  450. else if (ent->flags & CE_CREATE)
  451. strlcpy(temp_reason, " (no -C option)", REASON_MAX);
  452. else if (createlogs)
  453. strlcpy(temp_reason, " (no C flag)", REASON_MAX);
  454. if (ent->firstcreate) {
  455. if (verbose)
  456. printf("does not exist -> will create.\n");
  457. createlog(ent);
  458. } else if (verbose) {
  459. printf("does not exist, skipped%s.\n", temp_reason);
  460. }
  461. } else {
  462. if (ent->flags & CE_TRIMAT && !force && !rotatereq &&
  463. !oversized) {
  464. diffsecs = ptimeget_diff(timenow, ent->trim_at);
  465. if (diffsecs < 0.0) {
  466. /* trim_at is some time in the future. */
  467. if (verbose) {
  468. ptime_adjust4dst(ent->trim_at,
  469. timenow);
  470. printf("--> will trim at %s",
  471. ptimeget_ctime(ent->trim_at));
  472. }
  473. return (free_or_keep);
  474. } else if (diffsecs >= 3600.0) {
  475. /*
  476. * trim_at is more than an hour in the past,
  477. * so find the next valid trim_at time, and
  478. * tell the user what that will be.
  479. */
  480. if (verbose && dbg_at_times)
  481. printf("\n\t--> prev trim at %s\t",
  482. ptimeget_ctime(ent->trim_at));
  483. if (verbose) {
  484. ptimeset_nxtime(ent->trim_at);
  485. printf("--> will trim at %s",
  486. ptimeget_ctime(ent->trim_at));
  487. }
  488. return (free_or_keep);
  489. } else if (verbose && noaction && dbg_at_times) {
  490. /*
  491. * If we are just debugging at-times, then
  492. * a detailed message is helpful. Also
  493. * skip "doing" any commands, since they
  494. * would all be turned off by no-action.
  495. */
  496. printf("\n\t--> timematch at %s",
  497. ptimeget_ctime(ent->trim_at));
  498. return (free_or_keep);
  499. } else if (verbose && ent->hours <= 0) {
  500. printf("--> time is up\n");
  501. }
  502. }
  503. if (verbose && (ent->trsize > 0))
  504. printf("size (Kb): %d [%d] ", ent->fsize, ent->trsize);
  505. if (verbose && (ent->hours > 0))
  506. printf(" age (hr): %d [%d] ", modtime, ent->hours);
  507. /*
  508. * Figure out if this logfile needs to be rotated.
  509. */
  510. temp_reason[0] = '\0';
  511. if (rotatereq) {
  512. ent->rotate = 1;
  513. snprintf(temp_reason, REASON_MAX, " due to -R from %s",
  514. requestor);
  515. } else if (force) {
  516. ent->rotate = 1;
  517. snprintf(temp_reason, REASON_MAX, " due to -F request");
  518. } else if (oversized) {
  519. ent->rotate = 1;
  520. snprintf(temp_reason, REASON_MAX, " due to size>%dK",
  521. ent->trsize);
  522. } else if (ent->hours <= 0 && (ent->flags & CE_TRIMAT)) {
  523. ent->rotate = 1;
  524. } else if ((ent->hours > 0) && ((modtime >= ent->hours) ||
  525. (modtime < 0))) {
  526. ent->rotate = 1;
  527. }
  528. /*
  529. * If the file needs to be rotated, then rotate it.
  530. */
  531. if (ent->rotate && !norotate) {
  532. if (temp_reason[0] != '\0')
  533. ent->r_reason = strdup(temp_reason);
  534. if (verbose)
  535. printf("--> trimming log....\n");
  536. if (noaction && !verbose)
  537. printf("%s <%d%s>: trimming\n", ent->log,
  538. ent->numlogs,
  539. compress_type[ent->compress].flag);
  540. free_or_keep = do_rotate(ent);
  541. } else {
  542. if (verbose)
  543. printf("--> skipping\n");
  544. }
  545. }
  546. return (free_or_keep);
  547. #undef REASON_MAX
  548. }
  549. static void
  550. parse_args(int argc, char **argv)
  551. {
  552. int ch;
  553. char *p;
  554. timenow = ptime_init(NULL);
  555. ptimeset_time(timenow, time(NULL));
  556. strlcpy(daytime, ptimeget_ctime(timenow) + 4, DAYTIME_LEN);
  557. /* Let's get our hostname */
  558. (void)gethostname(hostname, sizeof(hostname));
  559. /* Truncate domain */
  560. if ((p = strchr(hostname, '.')) != NULL)
  561. *p = '\0';
  562. /* Parse command line options. */
  563. while ((ch = getopt(argc, argv, "a:d:f:nrst:vCD:FNPR:S:")) != -1)
  564. switch (ch) {
  565. case 'a':
  566. archtodir++;
  567. archdirname = optarg;
  568. break;
  569. case 'd':
  570. destdir = optarg;
  571. break;
  572. case 'f':
  573. conf = optarg;
  574. break;
  575. case 'n':
  576. noaction++;
  577. break;
  578. case 'r':
  579. needroot = 0;
  580. break;
  581. case 's':
  582. nosignal = 1;
  583. break;
  584. case 't':
  585. if (optarg[0] == '\0' ||
  586. strcmp(optarg, "DEFAULT") == 0)
  587. timefnamefmt = strdup(DEFAULT_TIMEFNAME_FMT);
  588. else
  589. timefnamefmt = strdup(optarg);
  590. break;
  591. case 'v':
  592. verbose++;
  593. break;
  594. case 'C':
  595. /* Useful for things like rc.diskless... */
  596. createlogs++;
  597. break;
  598. case 'D':
  599. /*
  600. * Set some debugging option. The specific option
  601. * depends on the value of optarg. These options
  602. * may come and go without notice or documentation.
  603. */
  604. if (parse_doption(optarg))
  605. break;
  606. usage();
  607. /* NOTREACHED */
  608. case 'F':
  609. force++;
  610. break;
  611. case 'N':
  612. norotate++;
  613. break;
  614. case 'P':
  615. enforcepid++;
  616. break;
  617. case 'R':
  618. rotatereq++;
  619. requestor = strdup(optarg);
  620. break;
  621. case 'S':
  622. path_syslogpid = optarg;
  623. break;
  624. case 'm': /* Used by OpenBSD for "monitor mode" */
  625. default:
  626. usage();
  627. /* NOTREACHED */
  628. }
  629. if (force && norotate) {
  630. warnx("Only one of -F and -N may be specified.");
  631. usage();
  632. /* NOTREACHED */
  633. }
  634. if (rotatereq) {
  635. if (optind == argc) {
  636. warnx("At least one filename must be given when -R is specified.");
  637. usage();
  638. /* NOTREACHED */
  639. }
  640. /* Make sure "requestor" value is safe for a syslog message. */
  641. for (p = requestor; *p != '\0'; p++) {
  642. if (!isprintch(*p) && (*p != '\t'))
  643. *p = '.';
  644. }
  645. }
  646. if (dbg_timenow) {
  647. /*
  648. * Note that the 'daytime' variable is not changed.
  649. * That is only used in messages that track when a
  650. * logfile is rotated, and if a file *is* rotated,
  651. * then it will still rotated at the "real now" time.
  652. */
  653. ptime_free(timenow);
  654. timenow = dbg_timenow;
  655. fprintf(stderr, "Debug: Running as if TimeNow is %s",
  656. ptimeget_ctime(dbg_timenow));
  657. }
  658. }
  659. /*
  660. * These debugging options are mainly meant for developer use, such
  661. * as writing regression-tests. They would not be needed by users
  662. * during normal operation of newsyslog...
  663. */
  664. static int
  665. parse_doption(const char *doption)
  666. {
  667. const char TN[] = "TN=";
  668. int res;
  669. if (strncmp(doption, TN, sizeof(TN) - 1) == 0) {
  670. /*
  671. * The "TimeNow" debugging option. This might be off
  672. * by an hour when crossing a timezone change.
  673. */
  674. dbg_timenow = ptime_init(NULL);
  675. res = ptime_relparse(dbg_timenow, PTM_PARSE_ISO8601,
  676. time(NULL), doption + sizeof(TN) - 1);
  677. if (res == -2) {
  678. warnx("Non-existent time specified on -D %s", doption);
  679. return (0); /* failure */
  680. } else if (res < 0) {
  681. warnx("Malformed time given on -D %s", doption);
  682. return (0); /* failure */
  683. }
  684. return (1); /* successfully parsed */
  685. }
  686. if (strcmp(doption, "ats") == 0) {
  687. dbg_at_times++;
  688. return (1); /* successfully parsed */
  689. }
  690. /* XXX - This check could probably be dropped. */
  691. if ((strcmp(doption, "neworder") == 0) || (strcmp(doption, "oldorder")
  692. == 0)) {
  693. warnx("NOTE: newsyslog always uses 'neworder'.");
  694. return (1); /* successfully parsed */
  695. }
  696. warnx("Unknown -D (debug) option: '%s'", doption);
  697. return (0); /* failure */
  698. }
  699. static void
  700. usage(void)
  701. {
  702. fprintf(stderr,
  703. "usage: newsyslog [-CFNPnrsv] [-a directory] [-d directory] [-f config_file]\n"
  704. " [-S pidfile] [-t timefmt] [[-R tagname] file ...]\n");
  705. exit(1);
  706. }
  707. /*
  708. * Parse a configuration file and return a linked list of all the logs
  709. * which should be processed.
  710. */
  711. static struct cflist *
  712. get_worklist(char **files)
  713. {
  714. FILE *f;
  715. char **given;
  716. struct cflist *cmdlist, *filelist, *globlist;
  717. struct conf_entry *defconf, *dupent, *ent;
  718. struct ilist inclist;
  719. struct include_entry *inc;
  720. int gmatch, fnres;
  721. defconf = NULL;
  722. STAILQ_INIT(&inclist);
  723. filelist = malloc(sizeof(struct cflist));
  724. if (filelist == NULL)
  725. err(1, "malloc of filelist");
  726. STAILQ_INIT(filelist);
  727. globlist = malloc(sizeof(struct cflist));
  728. if (globlist == NULL)
  729. err(1, "malloc of globlist");
  730. STAILQ_INIT(globlist);
  731. inc = malloc(sizeof(struct include_entry));
  732. if (inc == NULL)
  733. err(1, "malloc of inc");
  734. inc->file = conf;
  735. if (inc->file == NULL)
  736. inc->file = _PATH_CONF;
  737. STAILQ_INSERT_TAIL(&inclist, inc, inc_nextp);
  738. STAILQ_FOREACH(inc, &inclist, inc_nextp) {
  739. if (strcmp(inc->file, "-") != 0)
  740. f = fopen(inc->file, "r");
  741. else {
  742. f = stdin;
  743. inc->file = "<stdin>";
  744. }
  745. if (!f)
  746. err(1, "%s", inc->file);
  747. if (verbose)
  748. printf("Processing %s\n", inc->file);
  749. parse_file(f, filelist, globlist, defconf, &inclist);
  750. (void) fclose(f);
  751. }
  752. /*
  753. * All config-file information has been read in and turned into
  754. * a filelist and a globlist. If there were no specific files
  755. * given on the run command, then the only thing left to do is to
  756. * call a routine which finds all files matched by the globlist
  757. * and adds them to the filelist. Then return the worklist.
  758. */
  759. if (*files == NULL) {
  760. expand_globs(filelist, globlist);
  761. free_clist(globlist);
  762. if (defconf != NULL)
  763. free_entry(defconf);
  764. return (filelist);
  765. /* NOTREACHED */
  766. }
  767. /*
  768. * If newsyslog was given a specific list of files to process,
  769. * it may be that some of those files were not listed in any
  770. * config file. Those unlisted files should get the default
  771. * rotation action. First, create the default-rotation action
  772. * if none was found in a system config file.
  773. */
  774. if (defconf == NULL) {
  775. defconf = init_entry(DEFAULT_MARKER, NULL);
  776. defconf->numlogs = 3;
  777. defconf->trsize = 50;
  778. defconf->permissions = S_IRUSR|S_IWUSR;
  779. }
  780. /*
  781. * If newsyslog was run with a list of specific filenames,
  782. * then create a new worklist which has only those files in
  783. * it, picking up the rotation-rules for those files from
  784. * the original filelist.
  785. *
  786. * XXX - Note that this will copy multiple rules for a single
  787. * logfile, if multiple entries are an exact match for
  788. * that file. That matches the historic behavior, but do
  789. * we want to continue to allow it? If so, it should
  790. * probably be handled more intelligently.
  791. */
  792. cmdlist = malloc(sizeof(struct cflist));
  793. if (cmdlist == NULL)
  794. err(1, "malloc of cmdlist");
  795. STAILQ_INIT(cmdlist);
  796. for (given = files; *given; ++given) {
  797. /*
  798. * First try to find exact-matches for this given file.
  799. */
  800. gmatch = 0;
  801. STAILQ_FOREACH(ent, filelist, cf_nextp) {
  802. if (strcmp(ent->log, *given) == 0) {
  803. gmatch++;
  804. dupent = init_entry(*given, ent);
  805. STAILQ_INSERT_TAIL(cmdlist, dupent, cf_nextp);
  806. }
  807. }
  808. if (gmatch) {
  809. if (verbose > 2)
  810. printf("\t+ Matched entry %s\n", *given);
  811. continue;
  812. }
  813. /*
  814. * There was no exact-match for this given file, so look
  815. * for a "glob" entry which does match.
  816. */
  817. gmatch = 0;
  818. if (verbose > 2 && globlist != NULL)
  819. printf("\t+ Checking globs for %s\n", *given);
  820. STAILQ_FOREACH(ent, globlist, cf_nextp) {
  821. fnres = fnmatch(ent->log, *given, FNM_PATHNAME);
  822. if (verbose > 2)
  823. printf("\t+ = %d for pattern %s\n", fnres,
  824. ent->log);
  825. if (fnres == 0) {
  826. gmatch++;
  827. dupent = init_entry(*given, ent);
  828. /* This new entry is not a glob! */
  829. dupent->flags &= ~CE_GLOB;
  830. STAILQ_INSERT_TAIL(cmdlist, dupent, cf_nextp);
  831. /* Only allow a match to one glob-entry */
  832. break;
  833. }
  834. }
  835. if (gmatch) {
  836. if (verbose > 2)
  837. printf("\t+ Matched %s via %s\n", *given,
  838. ent->log);
  839. continue;
  840. }
  841. /*
  842. * This given file was not found in any config file, so
  843. * add a worklist item based on the default entry.
  844. */
  845. if (verbose > 2)
  846. printf("\t+ No entry matched %s (will use %s)\n",
  847. *given, DEFAULT_MARKER);
  848. dupent = init_entry(*given, defconf);
  849. /* Mark that it was *not* found in a config file */
  850. dupent->def_cfg = 1;
  851. STAILQ_INSERT_TAIL(cmdlist, dupent, cf_nextp);
  852. }
  853. /*
  854. * Free all the entries in the original work list, the list of
  855. * glob entries, and the default entry.
  856. */
  857. free_clist(filelist);
  858. free_clist(globlist);
  859. free_entry(defconf);
  860. /* And finally, return a worklist which matches the given files. */
  861. return (cmdlist);
  862. }
  863. /*
  864. * Expand the list of entries with filename patterns, and add all files
  865. * which match those glob-entries onto the worklist.
  866. */
  867. static void
  868. expand_globs(struct cflist *work_p, struct cflist *glob_p)
  869. {
  870. int gmatch, gres;
  871. size_t i;
  872. char *mfname;
  873. struct conf_entry *dupent, *ent, *globent;
  874. glob_t pglob;
  875. struct stat st_fm;
  876. /*
  877. * The worklist contains all fully-specified (non-GLOB) names.
  878. *
  879. * Now expand the list of filename-pattern (GLOB) entries into
  880. * a second list, which (by definition) will only match files
  881. * that already exist. Do not add a glob-related entry for any
  882. * file which already exists in the fully-specified list.
  883. */
  884. STAILQ_FOREACH(globent, glob_p, cf_nextp) {
  885. gres = glob(globent->log, GLOB_NOCHECK, NULL, &pglob);
  886. if (gres != 0) {
  887. warn("cannot expand pattern (%d): %s", gres,
  888. globent->log);
  889. continue;
  890. }
  891. if (verbose > 2)
  892. printf("\t+ Expanding pattern %s\n", globent->log);
  893. for (i = 0; i < pglob.gl_matchc; i++) {
  894. mfname = pglob.gl_pathv[i];
  895. /* See if this file already has a specific entry. */
  896. gmatch = 0;
  897. STAILQ_FOREACH(ent, work_p, cf_nextp) {
  898. if (strcmp(mfname, ent->log) == 0) {
  899. gmatch++;
  900. break;
  901. }
  902. }
  903. if (gmatch)
  904. continue;
  905. /* Make sure the named matched is a file. */
  906. gres = lstat(mfname, &st_fm);
  907. if (gres != 0) {
  908. /* Error on a file that glob() matched?!? */
  909. warn("Skipping %s - lstat() error", mfname);
  910. continue;
  911. }
  912. if (!S_ISREG(st_fm.st_mode)) {
  913. /* We only rotate files! */
  914. if (verbose > 2)
  915. printf("\t+ . skipping %s (!file)\n",
  916. mfname);
  917. continue;
  918. }
  919. if (verbose > 2)
  920. printf("\t+ . add file %s\n", mfname);
  921. dupent = init_entry(mfname, globent);
  922. /* This new entry is not a glob! */
  923. dupent->flags &= ~CE_GLOB;
  924. /* Add to the worklist. */
  925. STAILQ_INSERT_TAIL(work_p, dupent, cf_nextp);
  926. }
  927. globfree(&pglob);
  928. if (verbose > 2)
  929. printf("\t+ Done with pattern %s\n", globent->log);
  930. }
  931. }
  932. /*
  933. * Parse a configuration file and update a linked list of all the logs to
  934. * process.
  935. */
  936. static void
  937. parse_file(FILE *cf, struct cflist *work_p, struct cflist *glob_p,
  938. struct conf_entry *defconf_p, struct ilist *inclist)
  939. {
  940. char line[BUFSIZ], *parse, *q;
  941. char *cp, *errline, *group;
  942. struct conf_entry *working;
  943. struct passwd *pwd;
  944. struct group *grp;
  945. glob_t pglob;
  946. int eol, ptm_opts, res, special;
  947. size_t i;
  948. errline = NULL;
  949. while (fgets(line, BUFSIZ, cf)) {
  950. if ((line[0] == '\n') || (line[0] == '#') ||
  951. (strlen(line) == 0))
  952. continue;
  953. if (errline != NULL)
  954. free(errline);
  955. errline = strdup(line);
  956. for (cp = line + 1; *cp != '\0'; cp++) {
  957. if (*cp != '#')
  958. continue;
  959. if (*(cp - 1) == '\\') {
  960. strcpy(cp - 1, cp);
  961. cp--;
  962. continue;
  963. }
  964. *cp = '\0';
  965. break;
  966. }
  967. q = parse = missing_field(sob(line), errline);
  968. parse = son(line);
  969. if (!*parse)
  970. errx(1, "malformed line (missing fields):\n%s",
  971. errline);
  972. *parse = '\0';
  973. /*
  974. * Allow people to set debug options via the config file.
  975. * (NOTE: debug options are undocumented, and may disappear
  976. * at any time, etc).
  977. */
  978. if (strcasecmp(DEBUG_MARKER, q) == 0) {
  979. q = parse = missing_field(sob(++parse), errline);
  980. parse = son(parse);
  981. if (!*parse)
  982. warnx("debug line specifies no option:\n%s",
  983. errline);
  984. else {
  985. *parse = '\0';
  986. parse_doption(q);
  987. }
  988. continue;
  989. } else if (strcasecmp(INCLUDE_MARKER, q) == 0) {
  990. if (verbose)
  991. printf("Found: %s", errline);
  992. q = parse = missing_field(sob(++parse), errline);
  993. parse = son(parse);
  994. if (!*parse) {
  995. warnx("include line missing argument:\n%s",
  996. errline);
  997. continue;
  998. }
  999. *parse = '\0';
  1000. if (isglobstr(q)) {
  1001. res = glob(q, GLOB_NOCHECK, NULL, &pglob);
  1002. if (res != 0) {
  1003. warn("cannot expand pattern (%d): %s",
  1004. res, q);
  1005. continue;
  1006. }
  1007. if (verbose > 2)
  1008. printf("\t+ Expanding pattern %s\n", q);
  1009. for (i = 0; i < pglob.gl_matchc; i++)
  1010. add_to_queue(pglob.gl_pathv[i],
  1011. inclist);
  1012. globfree(&pglob);
  1013. } else
  1014. add_to_queue(q, inclist);
  1015. continue;
  1016. }
  1017. special = 0;
  1018. working = init_entry(q, NULL);
  1019. if (strcasecmp(DEFAULT_MARKER, q) == 0) {
  1020. special = 1;
  1021. if (defconf_p != NULL) {
  1022. warnx("Ignoring duplicate entry for %s!", q);
  1023. free_entry(working);
  1024. continue;
  1025. }
  1026. defconf_p = working;
  1027. }
  1028. q = parse = missing_field(sob(++parse), errline);
  1029. parse = son(parse);
  1030. if (!*parse)
  1031. errx(1, "malformed line (missing fields):\n%s",
  1032. errline);
  1033. *parse = '\0';
  1034. if ((group = strchr(q, ':')) != NULL ||
  1035. (group = strrchr(q, '.')) != NULL) {
  1036. *group++ = '\0';
  1037. if (*q) {
  1038. if (!(isnumberstr(q))) {
  1039. if ((pwd = getpwnam(q)) == NULL)
  1040. errx(1,
  1041. "error in config file; unknown user:\n%s",
  1042. errline);
  1043. working->uid = pwd->pw_uid;
  1044. } else
  1045. working->uid = atoi(q);
  1046. } else
  1047. working->uid = (uid_t)-1;
  1048. q = group;
  1049. if (*q) {
  1050. if (!(isnumberstr(q))) {
  1051. if ((grp = getgrnam(q)) == NULL)
  1052. errx(1,
  1053. "error in config file; unknown group:\n%s",
  1054. errline);
  1055. working->gid = grp->gr_gid;
  1056. } else
  1057. working->gid = atoi(q);
  1058. } else
  1059. working->gid = (gid_t)-1;
  1060. q = parse = missing_field(sob(++parse), errline);
  1061. parse = son(parse);
  1062. if (!*parse)
  1063. errx(1, "malformed line (missing fields):\n%s",
  1064. errline);
  1065. *parse = '\0';
  1066. } else {
  1067. working->uid = (uid_t)-1;
  1068. working->gid = (gid_t)-1;
  1069. }
  1070. if (!sscanf(q, "%o", &working->permissions))
  1071. errx(1, "error in config file; bad permissions:\n%s",
  1072. errline);
  1073. q = parse = missing_field(sob(++parse), errline);
  1074. parse = son(parse);
  1075. if (!*parse)
  1076. errx(1, "malformed line (missing fields):\n%s",
  1077. errline);
  1078. *parse = '\0';
  1079. if (!sscanf(q, "%d", &working->numlogs) || working->numlogs < 0)
  1080. errx(1, "error in config file; bad value for count of logs to save:\n%s",
  1081. errline);
  1082. q = parse = missing_field(sob(++parse), errline);
  1083. parse = son(parse);
  1084. if (!*parse)
  1085. errx(1, "malformed line (missing fields):\n%s",
  1086. errline);
  1087. *parse = '\0';
  1088. if (isdigitch(*q))
  1089. working->trsize = atoi(q);
  1090. else if (strcmp(q, "*") == 0)
  1091. working->trsize = -1;
  1092. else {
  1093. warnx("Invalid value of '%s' for 'size' in line:\n%s",
  1094. q, errline);
  1095. working->trsize = -1;
  1096. }
  1097. working->flags = 0;
  1098. working->compress = COMPRESS_NONE;
  1099. q = parse = missing_field(sob(++parse), errline);
  1100. parse = son(parse);
  1101. eol = !*parse;
  1102. *parse = '\0';
  1103. {
  1104. char *ep;
  1105. u_long ul;
  1106. ul = strtoul(q, &ep, 10);
  1107. if (ep == q)
  1108. working->hours = 0;
  1109. else if (*ep == '*')
  1110. working->hours = -1;
  1111. else if (ul > INT_MAX)
  1112. errx(1, "interval is too large:\n%s", errline);
  1113. else
  1114. working->hours = ul;
  1115. if (*ep == '\0' || strcmp(ep, "*") == 0)
  1116. goto no_trimat;
  1117. if (*ep != '@' && *ep != '$')
  1118. errx(1, "malformed interval/at:\n%s", errline);
  1119. working->flags |= CE_TRIMAT;
  1120. working->trim_at = ptime_init(NULL);
  1121. ptm_opts = PTM_PARSE_ISO8601;
  1122. if (*ep == '$')
  1123. ptm_opts = PTM_PARSE_DWM;
  1124. ptm_opts |= PTM_PARSE_MATCHDOM;
  1125. res = ptime_relparse(working->trim_at, ptm_opts,
  1126. ptimeget_secs(timenow), ep + 1);
  1127. if (res == -2)
  1128. errx(1, "nonexistent time for 'at' value:\n%s",
  1129. errline);
  1130. else if (res < 0)
  1131. errx(1, "malformed 'at' value:\n%s", errline);
  1132. }
  1133. no_trimat:
  1134. if (eol)
  1135. q = NULL;
  1136. else {
  1137. q = parse = sob(++parse); /* Optional field */
  1138. parse = son(parse);
  1139. if (!*parse)
  1140. eol = 1;
  1141. *parse = '\0';
  1142. }
  1143. for (; q && *q && !isspacech(*q); q++) {
  1144. switch (tolowerch(*q)) {
  1145. case 'b':
  1146. working->flags |= CE_BINARY;
  1147. break;
  1148. case 'c':
  1149. /*
  1150. * XXX - Ick! Ugly! Remove ASAP!
  1151. * We want `c' and `C' for "create". But we
  1152. * will temporarily treat `c' as `g', because
  1153. * FreeBSD releases <= 4.8 have a typo of
  1154. * checking ('G' || 'c') for CE_GLOB.
  1155. */
  1156. if (*q == 'c') {
  1157. warnx("Assuming 'g' for 'c' in flags for line:\n%s",
  1158. errline);
  1159. warnx("The 'c' flag will eventually mean 'CREATE'");
  1160. working->flags |= CE_GLOB;
  1161. break;
  1162. }
  1163. working->flags |= CE_CREATE;
  1164. break;
  1165. case 'd':
  1166. working->flags |= CE_NODUMP;
  1167. break;
  1168. case 'g':
  1169. working->flags |= CE_GLOB;
  1170. break;
  1171. case 'j':
  1172. working->compress = COMPRESS_BZIP2;
  1173. break;
  1174. case 'n':
  1175. working->flags |= CE_NOSIGNAL;
  1176. break;
  1177. case 'r':
  1178. working->flags |= CE_PID2CMD;
  1179. break;
  1180. case 'u':
  1181. working->flags |= CE_SIGNALGROUP;
  1182. break;
  1183. case 'w':
  1184. /* Depreciated flag - keep for compatibility purposes */
  1185. break;
  1186. case 'x':
  1187. working->compress = COMPRESS_XZ;
  1188. break;
  1189. case 'z':
  1190. working->compress = COMPRESS_GZIP;
  1191. break;
  1192. case '-':
  1193. break;
  1194. case 'f': /* Used by OpenBSD for "CE_FOLLOW" */
  1195. case 'm': /* Used by OpenBSD for "CE_MONITOR" */
  1196. case 'p': /* Used by NetBSD for "CE_PLAIN0" */
  1197. default:
  1198. errx(1, "illegal flag in config file -- %c",
  1199. *q);
  1200. }
  1201. }
  1202. if (eol)
  1203. q = NULL;
  1204. else {
  1205. q = parse = sob(++parse); /* Optional field */
  1206. parse = son(parse);
  1207. if (!*parse)
  1208. eol = 1;
  1209. *parse = '\0';
  1210. }
  1211. working->pid_cmd_file = NULL;
  1212. if (q && *q) {
  1213. if (*q == '/')
  1214. working->pid_cmd_file = strdup(q);
  1215. else if (isdigit(*q))
  1216. goto got_sig;
  1217. else
  1218. errx(1,
  1219. "illegal pid file or signal number in config file:\n%s",
  1220. errline);
  1221. }
  1222. if (eol)
  1223. q = NULL;
  1224. else {
  1225. q = parse = sob(++parse); /* Optional field */
  1226. *(parse = son(parse)) = '\0';
  1227. }
  1228. working->sig = SIGHUP;
  1229. if (q && *q) {
  1230. if (isdigit(*q)) {
  1231. got_sig:
  1232. working->sig = atoi(q);
  1233. } else {
  1234. err_sig:
  1235. errx(1,
  1236. "illegal signal number in config file:\n%s",
  1237. errline);
  1238. }
  1239. if (working->sig < 1 || working->sig >= NSIG)
  1240. goto err_sig;
  1241. }
  1242. /*
  1243. * Finish figuring out what pid-file to use (if any) in
  1244. * later processing if this logfile needs to be rotated.
  1245. */
  1246. if ((working->flags & CE_NOSIGNAL) == CE_NOSIGNAL) {
  1247. /*
  1248. * This config-entry specified 'n' for nosignal,
  1249. * see if it also specified an explicit pid_cmd_file.
  1250. * This would be a pretty pointless combination.
  1251. */
  1252. if (working->pid_cmd_file != NULL) {
  1253. warnx("Ignoring '%s' because flag 'n' was specified in line:\n%s",
  1254. working->pid_cmd_file, errline);
  1255. free(working->pid_cmd_file);
  1256. working->pid_cmd_file = NULL;
  1257. }
  1258. } else if (working->pid_cmd_file == NULL) {
  1259. /*
  1260. * This entry did not specify the 'n' flag, which
  1261. * means it should signal syslogd unless it had
  1262. * specified some other pid-file (and obviously the
  1263. * syslog pid-file will not be for a process-group).
  1264. * Also, we should only try to notify syslog if we
  1265. * are root.
  1266. */
  1267. if (working->flags & CE_SIGNALGROUP) {
  1268. warnx("Ignoring flag 'U' in line:\n%s",
  1269. errline);
  1270. working->flags &= ~CE_SIGNALGROUP;
  1271. }
  1272. if (needroot)
  1273. working->pid_cmd_file = strdup(path_syslogpid);
  1274. }
  1275. /*
  1276. * Add this entry to the appropriate list of entries, unless
  1277. * it was some kind of special entry (eg: <default>).
  1278. */
  1279. if (special) {
  1280. ; /* Do not add to any list */
  1281. } else if (working->flags & CE_GLOB) {
  1282. STAILQ_INSERT_TAIL(glob_p, working, cf_nextp);
  1283. } else {
  1284. STAILQ_INSERT_TAIL(work_p, working, cf_nextp);
  1285. }
  1286. }
  1287. if (errline != NULL)
  1288. free(errline);
  1289. }
  1290. static char *
  1291. missing_field(char *p, char *errline)
  1292. {
  1293. if (!p || !*p)
  1294. errx(1, "missing field in config file:\n%s", errline);
  1295. return (p);
  1296. }
  1297. /*
  1298. * In our sort we return it in the reverse of what qsort normally
  1299. * would do, as we want the newest files first. If we have two
  1300. * entries with the same time we don't really care about order.
  1301. *
  1302. * Support function for qsort() in delete_oldest_timelog().
  1303. */
  1304. static int
  1305. oldlog_entry_compare(const void *a, const void *b)
  1306. {
  1307. const struct oldlog_entry *ola = a, *olb = b;
  1308. if (ola->t > olb->t)
  1309. return (-1);
  1310. else if (ola->t < olb->t)
  1311. return (1);
  1312. else
  1313. return (0);
  1314. }
  1315. /*
  1316. * Delete the oldest logfiles, when using time based filenames.
  1317. */
  1318. static void
  1319. delete_oldest_timelog(const struct conf_entry *ent, const char *archive_dir)
  1320. {
  1321. char *logfname, *s, *dir, errbuf[80];
  1322. int dir_fd, i, logcnt, max_logcnt, valid;
  1323. struct oldlog_entry *oldlogs;
  1324. size_t logfname_len;
  1325. struct dirent *dp;
  1326. const char *cdir;
  1327. struct tm tm;
  1328. DIR *dirp;
  1329. int c;
  1330. oldlogs = malloc(MAX_OLDLOGS * sizeof(struct oldlog_entry));
  1331. max_logcnt = MAX_OLDLOGS;
  1332. logcnt = 0;
  1333. if (archive_dir != NULL && archive_dir[0] != '\0')
  1334. cdir = archive_dir;
  1335. else
  1336. if ((cdir = dirname(ent->log)) == NULL)
  1337. err(1, "dirname()");
  1338. if ((dir = strdup(cdir)) == NULL)
  1339. err(1, "strdup()");
  1340. if ((s = basename(ent->log)) == NULL)
  1341. err(1, "basename()");
  1342. if ((logfname = strdup(s)) == NULL)
  1343. err(1, "strdup()");
  1344. logfname_len = strlen(logfname);
  1345. if (strcmp(logfname, "/") == 0)
  1346. errx(1, "Invalid log filename - became '/'");
  1347. if (verbose > 2)
  1348. printf("Searching for old logs in %s\n", dir);
  1349. /* First we create a 'list' of all archived logfiles */
  1350. if ((dirp = opendir(dir)) == NULL)
  1351. err(1, "Cannot open log directory '%s'", dir);
  1352. dir_fd = dirfd(dirp);
  1353. while ((dp = readdir(dirp)) != NULL) {
  1354. if (dp->d_type != DT_REG)
  1355. continue;
  1356. /* Ignore everything but files with our logfile prefix */
  1357. if (strncmp(dp->d_name, logfname, logfname_len) != 0)
  1358. continue;
  1359. /* Ignore the actual non-rotated logfile */
  1360. if (dp->d_namlen == logfname_len)
  1361. continue;
  1362. /*
  1363. * Make sure we created have found a logfile, so the
  1364. * postfix is valid, IE format is: '.<time>(.[bg]z)?'.
  1365. */
  1366. if (dp->d_name[logfname_len] != '.') {
  1367. if (verbose)
  1368. printf("Ignoring %s which has unexpected "
  1369. "extension '%s'\n", dp->d_name,
  1370. &dp->d_name[logfname_len]);
  1371. continue;
  1372. }
  1373. if ((s = strptime(&dp->d_name[logfname_len + 1],
  1374. timefnamefmt, &tm)) == NULL) {
  1375. /*
  1376. * We could special case "old" sequentially
  1377. * named logfiles here, but we do not as that
  1378. * would require special handling to decide
  1379. * which one was the oldest compared to "new"
  1380. * time based logfiles.
  1381. */
  1382. if (verbose)
  1383. printf("Ignoring %s which does not "
  1384. "match time format\n", dp->d_name);
  1385. continue;
  1386. }
  1387. for (c = 0; c < COMPRESS_TYPES; c++)
  1388. if (strcmp(s, compress_type[c].suffix) == 0)
  1389. valid = 1;
  1390. if (valid != 1) {
  1391. if (verbose)
  1392. printf("Ignoring %s which has unexpected "
  1393. "extension '%s'\n", dp->d_name, s);
  1394. continue;
  1395. }
  1396. /*
  1397. * We should now have old an old rotated logfile, so
  1398. * add it to the 'list'.
  1399. */
  1400. if ((oldlogs[logcnt].t = timegm(&tm)) == -1)
  1401. err(1, "Could not convert time string to time value");
  1402. if ((oldlogs[logcnt].fname = strdup(dp->d_name)) == NULL)
  1403. err(1, "strdup()");
  1404. logcnt++;
  1405. /*
  1406. * It is very unlikely we ever run out of space in the
  1407. * logfile array from the default size, but lets
  1408. * handle it anyway...
  1409. */
  1410. if (logcnt >= max_logcnt) {
  1411. max_logcnt *= 4;
  1412. /* Detect integer overflow */
  1413. if (max_logcnt < logcnt)
  1414. errx(1, "Too many old logfiles found");
  1415. oldlogs = realloc(oldlogs,
  1416. max_logcnt * sizeof(struct oldlog_entry));
  1417. if (oldlogs == NULL)
  1418. err(1, "realloc()");
  1419. }
  1420. }
  1421. /* Second, if needed we delete oldest archived logfiles */
  1422. if (logcnt > 0 && logcnt >= ent->numlogs && ent->numlogs > 1) {
  1423. oldlogs = realloc(oldlogs, logcnt *
  1424. sizeof(struct oldlog_entry));
  1425. if (oldlogs == NULL)
  1426. err(1, "realloc()");
  1427. /*
  1428. * We now sort the logs in the order of newest to
  1429. * oldest. That way we can simply skip over the
  1430. * number of records we want to keep.
  1431. */
  1432. qsort(oldlogs, logcnt, sizeof(struct oldlog_entry),
  1433. oldlog_entry_compare);
  1434. for (i = ent->numlogs - 1; i < logcnt; i++) {
  1435. if (noaction)
  1436. printf("\trm -f %s/%s\n", dir,
  1437. oldlogs[i].fname);
  1438. else if (unlinkat(dir_fd, oldlogs[i].fname, 0) != 0) {
  1439. snprintf(errbuf, sizeof(errbuf),
  1440. "Could not delet old logfile '%s'",
  1441. oldlogs[i].fname);
  1442. perror(errbuf);
  1443. }
  1444. }
  1445. } else if (verbose > 1)
  1446. printf("No old logs to delete for logfile %s\n", ent->log);
  1447. /* Third, cleanup */
  1448. closedir(dirp);
  1449. for (i = 0; i < logcnt; i++) {
  1450. assert(oldlogs[i].fname != NULL);
  1451. free(oldlogs[i].fname);
  1452. }
  1453. free(oldlogs);
  1454. free(logfname);
  1455. free(dir);
  1456. }
  1457. /*
  1458. * Generate a log filename, when using classic filenames.
  1459. */
  1460. static void
  1461. gen_classiclog_fname(char *fname, size_t fname_sz, const char *archive_dir,
  1462. const char *namepart, int numlogs_c)
  1463. {
  1464. if (archive_dir[0] != '\0')
  1465. (void) snprintf(fname, fname_sz, "%s/%s.%d", archive_dir,
  1466. namepart, numlogs_c);
  1467. else
  1468. (void) snprintf(fname, fname_sz, "%s.%d", namepart, numlogs_c);
  1469. }
  1470. /*
  1471. * Delete a rotated logfile, when using classic filenames.
  1472. */
  1473. static void
  1474. delete_classiclog(const char *archive_dir, const char *namepart, int numlog_c)
  1475. {
  1476. char file1[MAXPATHLEN], zfile1[MAXPATHLEN];
  1477. int c;
  1478. gen_classiclog_fname(file1, sizeof(file1), archive_dir, namepart,
  1479. numlog_c);
  1480. for (c = 0; c < COMPRESS_TYPES; c++) {
  1481. (void) snprintf(zfile1, sizeof(zfile1), "%s%s", file1,
  1482. compress_type[c].suffix);
  1483. if (noaction)
  1484. printf("\trm -f %s\n", zfile1);
  1485. else
  1486. (void) unlink(zfile1);
  1487. }
  1488. }
  1489. /*
  1490. * Only add to the queue if the file hasn't already been added. This is
  1491. * done to prevent circular include loops.
  1492. */
  1493. static void
  1494. add_to_queue(const char *fname, struct ilist *inclist)
  1495. {
  1496. struct include_entry *inc;
  1497. STAILQ_FOREACH(inc, inclist, inc_nextp) {
  1498. if (strcmp(fname, inc->file) == 0) {
  1499. warnx("duplicate include detected: %s", fname);
  1500. return;
  1501. }
  1502. }
  1503. inc = malloc(sizeof(struct include_entry));
  1504. if (inc == NULL)
  1505. err(1, "malloc of inc");
  1506. inc->file = strdup(fname);
  1507. if (verbose > 2)
  1508. printf("\t+ Adding %s to the processing queue.\n", fname);
  1509. STAILQ_INSERT_TAIL(inclist, inc, inc_nextp);
  1510. }
  1511. /*
  1512. * Search for logfile and return its compression suffix (if supported)
  1513. * The suffix detection is first-match in the order of compress_types
  1514. *
  1515. * Note: if logfile without suffix exists (uncompressed, COMPRESS_NONE)
  1516. * a zero-length string is returned
  1517. */
  1518. static const char *
  1519. get_logfile_suffix(const char *logfile)
  1520. {
  1521. struct stat st;
  1522. char zfile[MAXPATHLEN];
  1523. int c;
  1524. for (c = 0; c < COMPRESS_TYPES; c++) {
  1525. (void) strlcpy(zfile, logfile, MAXPATHLEN);
  1526. (void) strlcat(zfile, compress_type[c].suffix, MAXPATHLEN);
  1527. if (lstat(zfile, &st) == 0)
  1528. return (compress_type[c].suffix);
  1529. }
  1530. return (NULL);
  1531. }
  1532. static fk_entry
  1533. do_rotate(const struct conf_entry *ent)
  1534. {
  1535. char dirpart[MAXPATHLEN], namepart[MAXPATHLEN];
  1536. char file1[MAXPATHLEN], file2[MAXPATHLEN];
  1537. char zfile1[MAXPATHLEN], zfile2[MAXPATHLEN];
  1538. const char *logfile_suffix;
  1539. char datetimestr[30];
  1540. int flags, numlogs_c;
  1541. fk_entry free_or_keep;
  1542. struct sigwork_entry *swork;
  1543. struct stat st;
  1544. struct tm tm;
  1545. time_t now;
  1546. flags = ent->flags;
  1547. free_or_keep = FREE_ENT;
  1548. if (archtodir) {
  1549. char *p;
  1550. /* build complete name of archive directory into dirpart */
  1551. if (*archdirname == '/') { /* absolute */
  1552. strlcpy(dirpart, archdirname, sizeof(dirpart));
  1553. } else { /* relative */
  1554. /* get directory part of logfile */
  1555. strlcpy(dirpart, ent->log, sizeof(dirpart));
  1556. if ((p = strrchr(dirpart, '/')) == NULL)
  1557. dirpart[0] = '\0';
  1558. else
  1559. *(p + 1) = '\0';
  1560. strlcat(dirpart, archdirname, sizeof(dirpart));
  1561. }
  1562. /* check if archive directory exists, if not, create it */
  1563. if (lstat(dirpart, &st))
  1564. createdir(ent, dirpart);
  1565. /* get filename part of logfile */
  1566. if ((p = strrchr(ent->log, '/')) == NULL)
  1567. strlcpy(namepart, ent->log, sizeof(namepart));
  1568. else
  1569. strlcpy(namepart, p + 1, sizeof(namepart));
  1570. } else {
  1571. /*
  1572. * Tell utility functions we are not using an archive
  1573. * dir.
  1574. */
  1575. dirpart[0] = '\0';
  1576. strlcpy(namepart, ent->log, sizeof(namepart));
  1577. }
  1578. /* Delete old logs */
  1579. if (timefnamefmt != NULL)
  1580. delete_oldest_timelog(ent, dirpart);
  1581. else {
  1582. /*
  1583. * Handle cleaning up after legacy newsyslog where we
  1584. * kept ent->numlogs + 1 files. This code can go away
  1585. * at some point in the future.
  1586. */
  1587. delete_classiclog(dirpart, namepart, ent->numlogs);
  1588. if (ent->numlogs > 0)
  1589. delete_classiclog(dirpart, namepart, ent->numlogs - 1);
  1590. }
  1591. if (timefnamefmt != NULL) {
  1592. /* If time functions fails we can't really do any sensible */
  1593. if (time(&now) == (time_t)-1 ||
  1594. localtime_r(&now, &tm) == NULL)
  1595. bzero(&tm, sizeof(tm));
  1596. strftime(datetimestr, sizeof(datetimestr), timefnamefmt, &tm);
  1597. if (archtodir)
  1598. (void) snprintf(file1, sizeof(file1), "%s/%s.%s",
  1599. dirpart, namepart, datetimestr);
  1600. else
  1601. (void) snprintf(file1, sizeof(file1), "%s.%s",
  1602. ent->log, datetimestr);
  1603. /* Don't run the code to move down logs */
  1604. numlogs_c = -1;
  1605. } else {
  1606. gen_classiclog_fname(file1, sizeof(file1), dirpart, namepart,
  1607. ent->numlogs - 1);
  1608. numlogs_c = ent->numlogs - 2; /* copy for countdown */
  1609. }
  1610. /* Move down log files */
  1611. for (; numlogs_c >= 0; numlogs_c--) {
  1612. (void) strlcpy(file2, file1, sizeof(file2));
  1613. gen_classiclog_fname(file1, sizeof(file1), dirpart, namepart,
  1614. numlogs_c);
  1615. logfile_suffix = get_logfile_suffix(file1);
  1616. if (logfile_suffix == NULL)
  1617. continue;
  1618. (void) strlcpy(zfile1, file1, MAXPATHLEN);
  1619. (void) strlcpy(zfile2, file2, MAXPATHLEN);
  1620. (void) strlcat(zfile1, logfile_suffix, MAXPATHLEN);
  1621. (void) strlcat(zfile2, logfile_suffix, MAXPATHLEN);
  1622. if (noaction)
  1623. printf("\tmv %s %s\n", zfile1, zfile2);
  1624. else {
  1625. /* XXX - Ought to be checking for failure! */
  1626. (void)rename(zfile1, zfile2);
  1627. }
  1628. change_attrs(zfile2, ent);
  1629. }
  1630. if (ent->numlogs > 0) {
  1631. if (noaction) {
  1632. /*
  1633. * Note that savelog() may succeed with using link()
  1634. * for the archtodir case, but there is no good way
  1635. * of knowing if it will when doing "noaction", so
  1636. * here we claim that it will have to do a copy...
  1637. */
  1638. if (archtodir)
  1639. printf("\tcp %s %s\n", ent->log, file1);
  1640. else
  1641. printf("\tln %s %s\n", ent->log, file1);
  1642. } else {
  1643. if (!(flags & CE_BINARY)) {
  1644. /* Report the trimming to the old log */
  1645. log_trim(ent->log, ent);
  1646. }
  1647. savelog(ent->log, file1);
  1648. }
  1649. change_attrs(file1, ent);
  1650. }
  1651. /* Create the new log file and move it into place *…

Large files files are truncated, but you can click here to view the full file