2 * ------+---------+---------+-------- + --------+---------+---------+---------*
3 * This file includes significant modifications done by:
4 * Copyright (c) 2003, 2004 - Garance Alistair Drosehn <gad@FreeBSD.org>.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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
28 * ------+---------+---------+-------- + --------+---------+---------+---------*
32 * This file contains changes from the Open Software Foundation.
36 * Copyright 1988, 1989 by the Massachusetts Institute of Technology
38 * Permission to use, copy, modify, and distribute this software and its
39 * documentation for any purpose and without fee is hereby granted, provided
40 * that the above copyright notice appear in all copies and that both that
41 * copyright notice and this permission notice appear in supporting
42 * documentation, and that the names of M.I.T. and the M.I.T. S.I.P.B. not be
43 * used in advertising or publicity pertaining to distribution of the
44 * software without specific, written prior permission. M.I.T. and the M.I.T.
45 * S.I.P.B. make no representations about the suitability of this software
46 * for any purpose. It is provided "as is" without express or implied
49 * $FreeBSD: src/usr.sbin/newsyslog/newsyslog.c,v 1.106 2006/07/21 22:13:06 sobomax Exp $
50 * $DragonFly: src/usr.sbin/newsyslog/newsyslog.c,v 1.6 2007/05/12 08:52:00 swildner Exp $
54 * newsyslog - roll over selected logs at the appropriate time, keeping the a
55 * specified number of backup files around.
59 #ifndef COMPRESS_POSTFIX
60 #define COMPRESS_POSTFIX ".gz"
62 #ifndef BZCOMPRESS_POSTFIX
63 #define BZCOMPRESS_POSTFIX ".bz2"
66 #include <sys/param.h>
67 #include <sys/queue.h>
87 #include "pathnames.h"
91 * Bit-values for the 'flags' parsed from a config-file entry.
93 #define CE_COMPACT 0x0001 /* Compact the achived log files with gzip. */
94 #define CE_BZCOMPACT 0x0002 /* Compact the achived log files with bzip2. */
95 #define CE_BINARY 0x0008 /* Logfile is in binary, do not add status */
96 /* messages to logfile(s) when rotating. */
97 #define CE_NOSIGNAL 0x0010 /* There is no process to signal when */
98 /* trimming this file. */
99 #define CE_TRIMAT 0x0020 /* trim file at a specific time. */
100 #define CE_GLOB 0x0040 /* name of the log is file name pattern. */
101 #define CE_SIGNALGROUP 0x0080 /* Signal a process-group instead of a single */
102 /* process when trimming this file. */
103 #define CE_CREATE 0x0100 /* Create the log file if it does not exist. */
104 #define CE_NODUMP 0x0200 /* Set 'nodump' on newly created log file. */
106 #define MIN_PID 5 /* Don't touch pids lower than this */
107 #define MAX_PID 99999 /* was lower, see /usr/include/sys/proc.h */
109 #define kbytes(size) (((size) + 1023) >> 10)
111 #define DEFAULT_MARKER "<default>"
112 #define DEBUG_MARKER "<debug>"
115 char *log
; /* Name of the log */
116 char *pid_file
; /* PID file */
117 char *r_reason
; /* The reason this file is being rotated */
118 int firstcreate
; /* Creating log for the first time (-C). */
119 int rotate
; /* Non-zero if this file should be rotated */
120 int fsize
; /* size found for the log file */
121 uid_t uid
; /* Owner of log */
122 gid_t gid
; /* Group of log */
123 int numlogs
; /* Number of logs to keep */
124 int trsize
; /* Size cutoff to trigger trimming the log */
125 int hours
; /* Hours between log trimming */
126 struct ptime_data
*trim_at
; /* Specific time to do trimming */
127 unsigned int permissions
; /* File permissions on the log */
128 int flags
; /* CE_COMPACT, CE_BZCOMPACT, CE_BINARY */
129 int sig
; /* Signal to send */
130 int def_cfg
; /* Using the <default> rule for this file */
131 struct conf_entry
*next
;/* Linked list pointer */
134 struct sigwork_entry
{
135 SLIST_ENTRY(sigwork_entry
) sw_nextp
;
136 int sw_signum
; /* the signal to send */
137 int sw_pidok
; /* true if pid value is valid */
138 pid_t sw_pid
; /* the process id from the PID file */
139 const char *sw_pidtype
; /* "daemon" or "process group" */
140 char sw_fname
[1]; /* file the PID was read from */
143 struct zipwork_entry
{
144 SLIST_ENTRY(zipwork_entry
) zw_nextp
;
145 const struct conf_entry
*zw_conf
; /* for chown/perm/flag info */
146 const struct sigwork_entry
*zw_swork
; /* to know success of signal */
147 int zw_fsize
; /* size of the file to compress */
148 char zw_fname
[1]; /* the file to compress */
155 SLIST_HEAD(swlisthead
, sigwork_entry
) swhead
= SLIST_HEAD_INITIALIZER(swhead
);
156 SLIST_HEAD(zwlisthead
, zipwork_entry
) zwhead
= SLIST_HEAD_INITIALIZER(zwhead
);
158 int dbg_at_times
; /* -D Show details of 'trim_at' code */
160 int archtodir
= 0; /* Archive old logfiles to other directory */
161 int createlogs
; /* Create (non-GLOB) logfiles which do not */
162 /* already exist. 1=='for entries with */
163 /* C flag', 2=='for all entries'. */
164 int verbose
= 0; /* Print out what's going on */
165 int needroot
= 1; /* Root privs are necessary */
166 int noaction
= 0; /* Don't do anything, just show it */
167 int norotate
= 0; /* Don't rotate */
168 int nosignal
; /* Do not send any signals */
169 int force
= 0; /* Force the trim no matter what */
170 int rotatereq
= 0; /* -R = Always rotate the file(s) as given */
171 /* on the command (this also requires */
172 /* that a list of files *are* given on */
173 /* the run command). */
174 char *requestor
; /* The name given on a -R request */
175 char *archdirname
; /* Directory path to old logfiles archive */
176 char *destdir
= NULL
; /* Directory to treat at root for logs */
177 const char *conf
; /* Configuration file to use */
179 struct ptime_data
*dbg_timenow
; /* A "timenow" value set via -D option */
180 struct ptime_data
*timenow
; /* The time to use for checking at-fields */
182 #define DAYTIME_LEN 16
183 char daytime
[DAYTIME_LEN
]; /* The current time in human readable form,
184 * used for rotation-tracking messages. */
185 char hostname
[MAXHOSTNAMELEN
]; /* hostname */
187 static struct conf_entry
*get_worklist(char **files
);
188 static void parse_file(FILE *cf
, const char *cfname
, struct conf_entry
**work_p
,
189 struct conf_entry
**glob_p
, struct conf_entry
**defconf_p
);
190 static char *sob(char *p
);
191 static char *son(char *p
);
192 static int isnumberstr(const char *);
193 static char *missing_field(char *p
, char *errline
);
194 static void change_attrs(const char *, const struct conf_entry
*);
195 static fk_entry
do_entry(struct conf_entry
*);
196 static fk_entry
do_rotate(const struct conf_entry
*);
197 static void do_sigwork(struct sigwork_entry
*);
198 static void do_zipwork(struct zipwork_entry
*);
199 static struct sigwork_entry
*
200 save_sigwork(const struct conf_entry
*);
201 static struct zipwork_entry
*
202 save_zipwork(const struct conf_entry
*, const struct
203 sigwork_entry
*, int, const char *);
204 static void set_swpid(struct sigwork_entry
*, const struct conf_entry
*);
205 static int sizefile(const char *);
206 static void expand_globs(struct conf_entry
**work_p
,
207 struct conf_entry
**glob_p
);
208 static void free_clist(struct conf_entry
**firstent
);
209 static void free_entry(struct conf_entry
*ent
);
210 static struct conf_entry
*init_entry(const char *fname
,
211 struct conf_entry
*src_entry
);
212 static void parse_args(int argc
, char **argv
);
213 static int parse_doption(const char *doption
);
214 static void usage(void);
215 static int log_trim(const char *logname
, const struct conf_entry
*log_ent
);
216 static int age_old_log(char *file
);
217 static void savelog(char *from
, char *to
);
218 static void createdir(const struct conf_entry
*ent
, char *dirpart
);
219 static void createlog(const struct conf_entry
*ent
);
222 * All the following take a parameter of 'int', but expect values in the
223 * range of unsigned char. Define wrappers which take values of type 'char',
224 * whether signed or unsigned, and ensure they end up in the right range.
226 #define isdigitch(Anychar) isdigit((u_char)(Anychar))
227 #define isprintch(Anychar) isprint((u_char)(Anychar))
228 #define isspacech(Anychar) isspace((u_char)(Anychar))
229 #define tolowerch(Anychar) tolower((u_char)(Anychar))
232 main(int argc
, char **argv
)
234 fk_entry free_or_keep
;
235 struct conf_entry
*p
, *q
;
236 struct sigwork_entry
*stmp
;
237 struct zipwork_entry
*ztmp
;
242 parse_args(argc
, argv
);
246 if (needroot
&& getuid() && geteuid())
247 errx(1, "must have root privs");
248 p
= q
= get_worklist(argv
);
251 * Rotate all the files which need to be rotated. Note that
252 * some users have *hundreds* of entries in newsyslog.conf!
255 free_or_keep
= do_entry(p
);
257 if (free_or_keep
== FREE_ENT
)
263 * Send signals to any processes which need a signal to tell
264 * them to close and re-open the log file(s) we have rotated.
265 * Note that zipwork_entries include pointers to these
266 * sigwork_entry's, so we can not free the entries here.
268 if (!SLIST_EMPTY(&swhead
)) {
269 if (noaction
|| verbose
)
270 printf("Signal all daemon process(es)...\n");
271 SLIST_FOREACH(stmp
, &swhead
, sw_nextp
)
274 printf("\tsleep 10\n");
277 printf("Pause 10 seconds to allow daemon(s)"
278 " to close log file(s)\n");
283 * Compress all files that we're expected to compress, now
284 * that all processes should have closed the files which
287 if (!SLIST_EMPTY(&zwhead
)) {
288 if (noaction
|| verbose
)
289 printf("Compress all rotated log file(s)...\n");
290 while (!SLIST_EMPTY(&zwhead
)) {
291 ztmp
= SLIST_FIRST(&zwhead
);
293 SLIST_REMOVE_HEAD(&zwhead
, zw_nextp
);
297 /* Now free all the sigwork entries. */
298 while (!SLIST_EMPTY(&swhead
)) {
299 stmp
= SLIST_FIRST(&swhead
);
300 SLIST_REMOVE_HEAD(&swhead
, sw_nextp
);
304 while (wait(NULL
) > 0 || errno
== EINTR
)
309 static struct conf_entry
*
310 init_entry(const char *fname
, struct conf_entry
*src_entry
)
312 struct conf_entry
*tempwork
;
315 printf("\t--> [creating entry for %s]\n", fname
);
317 tempwork
= malloc(sizeof(struct conf_entry
));
318 if (tempwork
== NULL
)
319 err(1, "malloc of conf_entry for %s", fname
);
321 if (destdir
== NULL
|| fname
[0] != '/')
322 tempwork
->log
= strdup(fname
);
324 asprintf(&tempwork
->log
, "%s%s", destdir
, fname
);
325 if (tempwork
->log
== NULL
)
326 err(1, "strdup for %s", fname
);
328 if (src_entry
!= NULL
) {
329 tempwork
->pid_file
= NULL
;
330 if (src_entry
->pid_file
)
331 tempwork
->pid_file
= strdup(src_entry
->pid_file
);
332 tempwork
->r_reason
= NULL
;
333 tempwork
->firstcreate
= 0;
334 tempwork
->rotate
= 0;
335 tempwork
->fsize
= -1;
336 tempwork
->uid
= src_entry
->uid
;
337 tempwork
->gid
= src_entry
->gid
;
338 tempwork
->numlogs
= src_entry
->numlogs
;
339 tempwork
->trsize
= src_entry
->trsize
;
340 tempwork
->hours
= src_entry
->hours
;
341 tempwork
->trim_at
= NULL
;
342 if (src_entry
->trim_at
!= NULL
)
343 tempwork
->trim_at
= ptime_init(src_entry
->trim_at
);
344 tempwork
->permissions
= src_entry
->permissions
;
345 tempwork
->flags
= src_entry
->flags
;
346 tempwork
->sig
= src_entry
->sig
;
347 tempwork
->def_cfg
= src_entry
->def_cfg
;
349 /* Initialize as a "do-nothing" entry */
350 tempwork
->pid_file
= NULL
;
351 tempwork
->r_reason
= NULL
;
352 tempwork
->firstcreate
= 0;
353 tempwork
->rotate
= 0;
354 tempwork
->fsize
= -1;
355 tempwork
->uid
= (uid_t
)-1;
356 tempwork
->gid
= (gid_t
)-1;
357 tempwork
->numlogs
= 1;
358 tempwork
->trsize
= -1;
359 tempwork
->hours
= -1;
360 tempwork
->trim_at
= NULL
;
361 tempwork
->permissions
= 0;
363 tempwork
->sig
= SIGHUP
;
364 tempwork
->def_cfg
= 0;
366 tempwork
->next
= NULL
;
372 free_entry(struct conf_entry
*ent
)
378 if (ent
->log
!= NULL
) {
380 printf("\t--> [freeing entry for %s]\n", ent
->log
);
385 if (ent
->pid_file
!= NULL
) {
387 ent
->pid_file
= NULL
;
390 if (ent
->r_reason
!= NULL
) {
392 ent
->r_reason
= NULL
;
395 if (ent
->trim_at
!= NULL
) {
396 ptime_free(ent
->trim_at
);
404 free_clist(struct conf_entry
**firstent
)
406 struct conf_entry
*ent
, *nextent
;
408 if (firstent
== NULL
)
409 return; /* There is nothing to do. */
422 do_entry(struct conf_entry
* ent
)
424 #define REASON_MAX 80
426 fk_entry free_or_keep
;
428 char temp_reason
[REASON_MAX
];
430 free_or_keep
= FREE_ENT
;
432 if (ent
->flags
& CE_COMPACT
)
433 printf("%s <%dZ>: ", ent
->log
, ent
->numlogs
);
434 else if (ent
->flags
& CE_BZCOMPACT
)
435 printf("%s <%dJ>: ", ent
->log
, ent
->numlogs
);
437 printf("%s <%d>: ", ent
->log
, ent
->numlogs
);
439 ent
->fsize
= sizefile(ent
->log
);
440 modtime
= age_old_log(ent
->log
);
442 ent
->firstcreate
= 0;
443 if (ent
->fsize
< 0) {
445 * If either the C flag or the -C option was specified,
446 * and if we won't be creating the file, then have the
447 * verbose message include a hint as to why the file
448 * will not be created.
450 temp_reason
[0] = '\0';
452 ent
->firstcreate
= 1;
453 else if ((ent
->flags
& CE_CREATE
) && createlogs
)
454 ent
->firstcreate
= 1;
455 else if (ent
->flags
& CE_CREATE
)
456 strlcpy(temp_reason
, " (no -C option)", REASON_MAX
);
458 strlcpy(temp_reason
, " (no C flag)", REASON_MAX
);
460 if (ent
->firstcreate
) {
462 printf("does not exist -> will create.\n");
464 } else if (verbose
) {
465 printf("does not exist, skipped%s.\n", temp_reason
);
468 if (ent
->flags
& CE_TRIMAT
&& !force
&& !rotatereq
) {
469 diffsecs
= ptimeget_diff(timenow
, ent
->trim_at
);
470 if (diffsecs
< 0.0) {
471 /* trim_at is some time in the future. */
473 ptime_adjust4dst(ent
->trim_at
,
475 printf("--> will trim at %s",
476 ptimeget_ctime(ent
->trim_at
));
478 return (free_or_keep
);
479 } else if (diffsecs
>= 3600.0) {
481 * trim_at is more than an hour in the past,
482 * so find the next valid trim_at time, and
483 * tell the user what that will be.
485 if (verbose
&& dbg_at_times
)
486 printf("\n\t--> prev trim at %s\t",
487 ptimeget_ctime(ent
->trim_at
));
489 ptimeset_nxtime(ent
->trim_at
);
490 printf("--> will trim at %s",
491 ptimeget_ctime(ent
->trim_at
));
493 return (free_or_keep
);
494 } else if (verbose
&& noaction
&& dbg_at_times
) {
496 * If we are just debugging at-times, then
497 * a detailed message is helpful. Also
498 * skip "doing" any commands, since they
499 * would all be turned off by no-action.
501 printf("\n\t--> timematch at %s",
502 ptimeget_ctime(ent
->trim_at
));
503 return (free_or_keep
);
504 } else if (verbose
&& ent
->hours
<= 0) {
505 printf("--> time is up\n");
508 if (verbose
&& (ent
->trsize
> 0))
509 printf("size (Kb): %d [%d] ", ent
->fsize
, ent
->trsize
);
510 if (verbose
&& (ent
->hours
> 0))
511 printf(" age (hr): %d [%d] ", modtime
, ent
->hours
);
514 * Figure out if this logfile needs to be rotated.
516 temp_reason
[0] = '\0';
519 snprintf(temp_reason
, REASON_MAX
, " due to -R from %s",
523 snprintf(temp_reason
, REASON_MAX
, " due to -F request");
524 } else if ((ent
->trsize
> 0) && (ent
->fsize
>= ent
->trsize
)) {
526 snprintf(temp_reason
, REASON_MAX
, " due to size>%dK",
528 } else if (ent
->hours
<= 0 && (ent
->flags
& CE_TRIMAT
)) {
530 } else if ((ent
->hours
> 0) && ((modtime
>= ent
->hours
) ||
536 * If the file needs to be rotated, then rotate it.
538 if (ent
->rotate
&& !norotate
) {
539 if (temp_reason
[0] != '\0')
540 ent
->r_reason
= strdup(temp_reason
);
542 printf("--> trimming log....\n");
543 if (noaction
&& !verbose
) {
544 if (ent
->flags
& CE_COMPACT
)
545 printf("%s <%dZ>: trimming\n",
546 ent
->log
, ent
->numlogs
);
547 else if (ent
->flags
& CE_BZCOMPACT
)
548 printf("%s <%dJ>: trimming\n",
549 ent
->log
, ent
->numlogs
);
551 printf("%s <%d>: trimming\n",
552 ent
->log
, ent
->numlogs
);
554 free_or_keep
= do_rotate(ent
);
557 printf("--> skipping\n");
560 return (free_or_keep
);
565 parse_args(int argc
, char **argv
)
570 timenow
= ptime_init(NULL
);
571 ptimeset_time(timenow
, time(NULL
));
572 strlcpy(daytime
, ptimeget_ctime(timenow
) + 4, DAYTIME_LEN
);
574 /* Let's get our hostname */
575 gethostname(hostname
, sizeof(hostname
));
577 /* Truncate domain */
578 if ((p
= strchr(hostname
, '.')) != NULL
)
581 /* Parse command line options. */
582 while ((ch
= getopt(argc
, argv
, "a:d:f:nrsvCD:FNR:")) != -1)
586 archdirname
= optarg
;
607 /* Useful for things like rc.diskless... */
612 * Set some debugging option. The specific option
613 * depends on the value of optarg. These options
614 * may come and go without notice or documentation.
616 if (parse_doption(optarg
))
628 requestor
= strdup(optarg
);
630 case 'm': /* Used by OpenBSD for "monitor mode" */
636 if (force
&& norotate
) {
637 warnx("Only one of -F and -N may be specified.");
643 if (optind
== argc
) {
644 warnx("At least one filename must be given when -R is specified.");
648 /* Make sure "requestor" value is safe for a syslog message. */
649 for (p
= requestor
; *p
!= '\0'; p
++) {
650 if (!isprintch(*p
) && (*p
!= '\t'))
657 * Note that the 'daytime' variable is not changed.
658 * That is only used in messages that track when a
659 * logfile is rotated, and if a file *is* rotated,
660 * then it will still rotated at the "real now" time.
663 timenow
= dbg_timenow
;
664 fprintf(stderr
, "Debug: Running as if TimeNow is %s",
665 ptimeget_ctime(dbg_timenow
));
671 * These debugging options are mainly meant for developer use, such
672 * as writing regression-tests. They would not be needed by users
673 * during normal operation of newsyslog...
676 parse_doption(const char *doption
)
678 const char TN
[] = "TN=";
681 if (strncmp(doption
, TN
, sizeof(TN
) - 1) == 0) {
683 * The "TimeNow" debugging option. This might be off
684 * by an hour when crossing a timezone change.
686 dbg_timenow
= ptime_init(NULL
);
687 res
= ptime_relparse(dbg_timenow
, PTM_PARSE_ISO8601
,
688 time(NULL
), doption
+ sizeof(TN
) - 1);
690 warnx("Non-existent time specified on -D %s", doption
);
691 return (0); /* failure */
692 } else if (res
< 0) {
693 warnx("Malformed time given on -D %s", doption
);
694 return (0); /* failure */
696 return (1); /* successfully parsed */
700 if (strcmp(doption
, "ats") == 0) {
702 return (1); /* successfully parsed */
705 /* XXX - This check could probably be dropped. */
706 if ((strcmp(doption
, "neworder") == 0) || (strcmp(doption
, "oldorder")
708 warnx("NOTE: newsyslog always uses 'neworder'.");
709 return (1); /* successfully parsed */
712 warnx("Unknown -D (debug) option: '%s'", doption
);
713 return (0); /* failure */
721 "usage: newsyslog [-CFNnrsv] [-a directory] [-d directory] [-f config-file]\n"
722 " [ [-R requestor] filename ... ]\n");
727 * Parse a configuration file and return a linked list of all the logs
728 * which should be processed.
730 static struct conf_entry
*
731 get_worklist(char **files
)
736 struct conf_entry
*defconf
, *dupent
, *ent
, *firstnew
;
737 struct conf_entry
*globlist
, *lastnew
, *worklist
;
740 defconf
= globlist
= worklist
= NULL
;
746 if (strcmp(fname
, "-") != 0)
747 f
= fopen(fname
, "r");
755 parse_file(f
, fname
, &worklist
, &globlist
, &defconf
);
759 * All config-file information has been read in and turned into
760 * a worklist and a globlist. If there were no specific files
761 * given on the run command, then the only thing left to do is to
762 * call a routine which finds all files matched by the globlist
763 * and adds them to the worklist. Then return the worklist.
765 if (*files
== NULL
) {
766 expand_globs(&worklist
, &globlist
);
767 free_clist(&globlist
);
775 * If newsyslog was given a specific list of files to process,
776 * it may be that some of those files were not listed in any
777 * config file. Those unlisted files should get the default
778 * rotation action. First, create the default-rotation action
779 * if none was found in a system config file.
781 if (defconf
== NULL
) {
782 defconf
= init_entry(DEFAULT_MARKER
, NULL
);
783 defconf
->numlogs
= 3;
784 defconf
->trsize
= 50;
785 defconf
->permissions
= S_IRUSR
|S_IWUSR
;
789 * If newsyslog was run with a list of specific filenames,
790 * then create a new worklist which has only those files in
791 * it, picking up the rotation-rules for those files from
792 * the original worklist.
794 * XXX - Note that this will copy multiple rules for a single
795 * logfile, if multiple entries are an exact match for
796 * that file. That matches the historic behavior, but do
797 * we want to continue to allow it? If so, it should
798 * probably be handled more intelligently.
800 firstnew
= lastnew
= NULL
;
801 for (given
= files
; *given
; ++given
) {
803 * First try to find exact-matches for this given file.
806 for (ent
= worklist
; ent
; ent
= ent
->next
) {
807 if (strcmp(ent
->log
, *given
) == 0) {
809 dupent
= init_entry(*given
, ent
);
813 lastnew
->next
= dupent
;
819 printf("\t+ Matched entry %s\n", *given
);
824 * There was no exact-match for this given file, so look
825 * for a "glob" entry which does match.
828 if (verbose
> 2 && globlist
!= NULL
)
829 printf("\t+ Checking globs for %s\n", *given
);
830 for (ent
= globlist
; ent
; ent
= ent
->next
) {
831 fnres
= fnmatch(ent
->log
, *given
, FNM_PATHNAME
);
833 printf("\t+ = %d for pattern %s\n", fnres
,
837 dupent
= init_entry(*given
, ent
);
841 lastnew
->next
= dupent
;
843 /* This new entry is not a glob! */
844 dupent
->flags
&= ~CE_GLOB
;
845 /* Only allow a match to one glob-entry */
851 printf("\t+ Matched %s via %s\n", *given
,
857 * This given file was not found in any config file, so
858 * add a worklist item based on the default entry.
861 printf("\t+ No entry matched %s (will use %s)\n",
862 *given
, DEFAULT_MARKER
);
863 dupent
= init_entry(*given
, defconf
);
867 lastnew
->next
= dupent
;
868 /* Mark that it was *not* found in a config file */
874 * Free all the entries in the original work list, the list of
875 * glob entries, and the default entry.
877 free_clist(&worklist
);
878 free_clist(&globlist
);
881 /* And finally, return a worklist which matches the given files. */
886 * Expand the list of entries with filename patterns, and add all files
887 * which match those glob-entries onto the worklist.
890 expand_globs(struct conf_entry
**work_p
, struct conf_entry
**glob_p
)
894 struct conf_entry
*dupent
, *ent
, *firstmatch
, *globent
;
895 struct conf_entry
*lastmatch
;
899 if ((glob_p
== NULL
) || (*glob_p
== NULL
))
900 return; /* There is nothing to do. */
903 * The worklist contains all fully-specified (non-GLOB) names.
905 * Now expand the list of filename-pattern (GLOB) entries into
906 * a second list, which (by definition) will only match files
907 * that already exist. Do not add a glob-related entry for any
908 * file which already exists in the fully-specified list.
910 firstmatch
= lastmatch
= NULL
;
911 for (globent
= *glob_p
; globent
; globent
= globent
->next
) {
913 gres
= glob(globent
->log
, GLOB_NOCHECK
, NULL
, &pglob
);
915 warn("cannot expand pattern (%d): %s", gres
,
921 printf("\t+ Expanding pattern %s\n", globent
->log
);
922 for (i
= 0; i
< pglob
.gl_matchc
; i
++) {
923 mfname
= pglob
.gl_pathv
[i
];
925 /* See if this file already has a specific entry. */
927 for (ent
= *work_p
; ent
; ent
= ent
->next
) {
928 if (strcmp(mfname
, ent
->log
) == 0) {
936 /* Make sure the named matched is a file. */
937 gres
= lstat(mfname
, &st_fm
);
939 /* Error on a file that glob() matched?!? */
940 warn("Skipping %s - lstat() error", mfname
);
943 if (!S_ISREG(st_fm
.st_mode
)) {
944 /* We only rotate files! */
946 printf("\t+ . skipping %s (!file)\n",
952 printf("\t+ . add file %s\n", mfname
);
953 dupent
= init_entry(mfname
, globent
);
957 lastmatch
->next
= dupent
;
959 /* This new entry is not a glob! */
960 dupent
->flags
&= ~CE_GLOB
;
964 printf("\t+ Done with pattern %s\n", globent
->log
);
967 /* Add the list of matched files to the end of the worklist. */
969 *work_p
= firstmatch
;
974 ent
->next
= firstmatch
;
980 * Parse a configuration file and update a linked list of all the logs to
984 parse_file(FILE *cf
, const char *cfname
, struct conf_entry
**work_p
,
985 struct conf_entry
**glob_p
, struct conf_entry
**defconf_p
)
987 char line
[BUFSIZ
], *parse
, *q
;
988 char *cp
, *errline
, *group
;
989 struct conf_entry
*lastglob
, *lastwork
, *working
;
992 int eol
, ptm_opts
, res
, special
;
995 * XXX - for now, assume that only one config file will be read,
996 * ie, this routine is only called one time.
998 lastglob
= lastwork
= NULL
;
1001 while (fgets(line
, BUFSIZ
, cf
)) {
1002 if ((line
[0] == '\n') || (line
[0] == '#') ||
1003 (strlen(line
) == 0))
1005 if (errline
!= NULL
)
1007 errline
= strdup(line
);
1008 for (cp
= line
+ 1; *cp
!= '\0'; cp
++) {
1011 if (*(cp
- 1) == '\\') {
1020 q
= parse
= missing_field(sob(line
), errline
);
1023 errx(1, "malformed line (missing fields):\n%s",
1028 * Allow people to set debug options via the config file.
1029 * (NOTE: debug optons are undocumented, and may disappear
1030 * at any time, etc).
1032 if (strcasecmp(DEBUG_MARKER
, q
) == 0) {
1033 q
= parse
= missing_field(sob(++parse
), errline
);
1036 warnx("debug line specifies no option:\n%s",
1046 working
= init_entry(q
, NULL
);
1047 if (strcasecmp(DEFAULT_MARKER
, q
) == 0) {
1049 if (defconf_p
== NULL
) {
1050 warnx("Ignoring entry for %s in %s!", q
,
1052 free_entry(working
);
1054 } else if (*defconf_p
!= NULL
) {
1055 warnx("Ignoring duplicate entry for %s!", q
);
1056 free_entry(working
);
1059 *defconf_p
= working
;
1062 q
= parse
= missing_field(sob(++parse
), errline
);
1065 errx(1, "malformed line (missing fields):\n%s",
1068 if ((group
= strchr(q
, ':')) != NULL
||
1069 (group
= strrchr(q
, '.')) != NULL
) {
1072 if (!(isnumberstr(q
))) {
1073 if ((pwd
= getpwnam(q
)) == NULL
)
1075 "error in config file; unknown user:\n%s",
1077 working
->uid
= pwd
->pw_uid
;
1079 working
->uid
= atoi(q
);
1081 working
->uid
= (uid_t
)-1;
1085 if (!(isnumberstr(q
))) {
1086 if ((grp
= getgrnam(q
)) == NULL
)
1088 "error in config file; unknown group:\n%s",
1090 working
->gid
= grp
->gr_gid
;
1092 working
->gid
= atoi(q
);
1094 working
->gid
= (gid_t
)-1;
1096 q
= parse
= missing_field(sob(++parse
), errline
);
1099 errx(1, "malformed line (missing fields):\n%s",
1103 working
->uid
= (uid_t
)-1;
1104 working
->gid
= (gid_t
)-1;
1107 if (!sscanf(q
, "%o", &working
->permissions
))
1108 errx(1, "error in config file; bad permissions:\n%s",
1111 q
= parse
= missing_field(sob(++parse
), errline
);
1114 errx(1, "malformed line (missing fields):\n%s",
1117 if (!sscanf(q
, "%d", &working
->numlogs
) || working
->numlogs
< 0)
1118 errx(1, "error in config file; bad value for count of logs to save:\n%s",
1121 q
= parse
= missing_field(sob(++parse
), errline
);
1124 errx(1, "malformed line (missing fields):\n%s",
1128 working
->trsize
= atoi(q
);
1129 else if (strcmp(q
, "*") == 0)
1130 working
->trsize
= -1;
1132 warnx("Invalid value of '%s' for 'size' in line:\n%s",
1134 working
->trsize
= -1;
1138 q
= parse
= missing_field(sob(++parse
), errline
);
1146 ul
= strtoul(q
, &ep
, 10);
1149 else if (*ep
== '*')
1150 working
->hours
= -1;
1151 else if (ul
> INT_MAX
)
1152 errx(1, "interval is too large:\n%s", errline
);
1154 working
->hours
= ul
;
1156 if (*ep
== '\0' || strcmp(ep
, "*") == 0)
1158 if (*ep
!= '@' && *ep
!= '$')
1159 errx(1, "malformed interval/at:\n%s", errline
);
1161 working
->flags
|= CE_TRIMAT
;
1162 working
->trim_at
= ptime_init(NULL
);
1163 ptm_opts
= PTM_PARSE_ISO8601
;
1165 ptm_opts
= PTM_PARSE_DWM
;
1166 ptm_opts
|= PTM_PARSE_MATCHDOM
;
1167 res
= ptime_relparse(working
->trim_at
, ptm_opts
,
1168 ptimeget_secs(timenow
), ep
+ 1);
1170 errx(1, "nonexistent time for 'at' value:\n%s",
1173 errx(1, "malformed 'at' value:\n%s", errline
);
1180 q
= parse
= sob(++parse
); /* Optional field */
1187 for (; q
&& *q
&& !isspacech(*q
); q
++) {
1188 switch (tolowerch(*q
)) {
1190 working
->flags
|= CE_BINARY
;
1194 * XXX - Ick! Ugly! Remove ASAP!
1195 * We want `c' and `C' for "create". But we
1196 * will temporarily treat `c' as `g', because
1197 * FreeBSD releases <= 4.8 have a typo of
1198 * checking ('G' || 'c') for CE_GLOB.
1201 warnx("Assuming 'g' for 'c' in flags for line:\n%s",
1203 warnx("The 'c' flag will eventually mean 'CREATE'");
1204 working
->flags
|= CE_GLOB
;
1207 working
->flags
|= CE_CREATE
;
1210 working
->flags
|= CE_NODUMP
;
1213 working
->flags
|= CE_GLOB
;
1216 working
->flags
|= CE_BZCOMPACT
;
1219 working
->flags
|= CE_NOSIGNAL
;
1222 working
->flags
|= CE_SIGNALGROUP
;
1225 /* Deprecated flag - keep for compatibility purposes */
1228 working
->flags
|= CE_COMPACT
;
1232 case 'f': /* Used by OpenBSD for "CE_FOLLOW" */
1233 case 'm': /* Used by OpenBSD for "CE_MONITOR" */
1234 case 'p': /* Used by NetBSD for "CE_PLAIN0" */
1236 errx(1, "illegal flag in config file -- %c",
1244 q
= parse
= sob(++parse
); /* Optional field */
1251 working
->pid_file
= NULL
;
1254 working
->pid_file
= strdup(q
);
1255 else if (isdigit(*q
))
1259 "illegal pid file or signal number in config file:\n%s",
1265 q
= parse
= sob(++parse
); /* Optional field */
1266 *(parse
= son(parse
)) = '\0';
1269 working
->sig
= SIGHUP
;
1273 working
->sig
= atoi(q
);
1277 "illegal signal number in config file:\n%s",
1280 if (working
->sig
< 1 || working
->sig
>= NSIG
)
1285 * Finish figuring out what pid-file to use (if any) in
1286 * later processing if this logfile needs to be rotated.
1288 if ((working
->flags
& CE_NOSIGNAL
) == CE_NOSIGNAL
) {
1290 * This config-entry specified 'n' for nosignal,
1291 * see if it also specified an explicit pid_file.
1292 * This would be a pretty pointless combination.
1294 if (working
->pid_file
!= NULL
) {
1295 warnx("Ignoring '%s' because flag 'n' was specified in line:\n%s",
1296 working
->pid_file
, errline
);
1297 free(working
->pid_file
);
1298 working
->pid_file
= NULL
;
1300 } else if (working
->pid_file
== NULL
) {
1302 * This entry did not specify the 'n' flag, which
1303 * means it should signal syslogd unless it had
1304 * specified some other pid-file (and obviously the
1305 * syslog pid-file will not be for a process-group).
1306 * Also, we should only try to notify syslog if we
1309 if (working
->flags
& CE_SIGNALGROUP
) {
1310 warnx("Ignoring flag 'U' in line:\n%s",
1312 working
->flags
&= ~CE_SIGNALGROUP
;
1315 working
->pid_file
= strdup(_PATH_SYSLOGPID
);
1319 * Add this entry to the appropriate list of entries, unless
1320 * it was some kind of special entry (eg: <default>).
1323 ; /* Do not add to any list */
1324 } else if (working
->flags
& CE_GLOB
) {
1328 lastglob
->next
= working
;
1334 lastwork
->next
= working
;
1338 if (errline
!= NULL
)
1343 missing_field(char *p
, char *errline
)
1347 errx(1, "missing field in config file:\n%s", errline
);
1352 do_rotate(const struct conf_entry
*ent
)
1354 char dirpart
[MAXPATHLEN
], namepart
[MAXPATHLEN
];
1355 char file1
[MAXPATHLEN
], file2
[MAXPATHLEN
];
1356 char zfile1
[MAXPATHLEN
], zfile2
[MAXPATHLEN
];
1357 char jfile1
[MAXPATHLEN
];
1358 int flags
, numlogs_c
;
1359 fk_entry free_or_keep
;
1360 struct sigwork_entry
*swork
;
1364 free_or_keep
= FREE_ENT
;
1369 /* build complete name of archive directory into dirpart */
1370 if (*archdirname
== '/') { /* absolute */
1371 strlcpy(dirpart
, archdirname
, sizeof(dirpart
));
1372 } else { /* relative */
1373 /* get directory part of logfile */
1374 strlcpy(dirpart
, ent
->log
, sizeof(dirpart
));
1375 if ((p
= strrchr(dirpart
, '/')) == NULL
)
1379 strlcat(dirpart
, archdirname
, sizeof(dirpart
));
1382 /* check if archive directory exists, if not, create it */
1383 if (lstat(dirpart
, &st
))
1384 createdir(ent
, dirpart
);
1386 /* get filename part of logfile */
1387 if ((p
= strrchr(ent
->log
, '/')) == NULL
)
1388 strlcpy(namepart
, ent
->log
, sizeof(namepart
));
1390 strlcpy(namepart
, p
+ 1, sizeof(namepart
));
1392 /* name of oldest log */
1393 snprintf(file1
, sizeof(file1
), "%s/%s.%d", dirpart
,
1394 namepart
, ent
->numlogs
);
1395 snprintf(zfile1
, sizeof(zfile1
), "%s%s", file1
,
1397 snprintf(jfile1
, sizeof(jfile1
), "%s%s", file1
,
1398 BZCOMPRESS_POSTFIX
);
1400 /* name of oldest log */
1401 snprintf(file1
, sizeof(file1
), "%s.%d", ent
->log
,
1403 snprintf(zfile1
, sizeof(zfile1
), "%s%s", file1
,
1405 snprintf(jfile1
, sizeof(jfile1
), "%s%s", file1
,
1406 BZCOMPRESS_POSTFIX
);
1410 printf("\trm -f %s\n", file1
);
1411 printf("\trm -f %s\n", zfile1
);
1412 printf("\trm -f %s\n", jfile1
);
1419 /* Move down log files */
1420 numlogs_c
= ent
->numlogs
; /* copy for countdown */
1421 while (numlogs_c
--) {
1423 strlcpy(file2
, file1
, sizeof(file2
));
1426 snprintf(file1
, sizeof(file1
), "%s/%s.%d",
1427 dirpart
, namepart
, numlogs_c
);
1429 snprintf(file1
, sizeof(file1
), "%s.%d", ent
->log
,
1432 strlcpy(zfile1
, file1
, sizeof(zfile1
));
1433 strlcpy(zfile2
, file2
, sizeof(zfile2
));
1434 if (lstat(file1
, &st
)) {
1435 strlcat(zfile1
, COMPRESS_POSTFIX
,
1437 strlcat(zfile2
, COMPRESS_POSTFIX
,
1439 if (lstat(zfile1
, &st
)) {
1440 strlcpy(zfile1
, file1
, sizeof(zfile1
));
1441 strlcpy(zfile2
, file2
, sizeof(zfile2
));
1442 strlcat(zfile1
, BZCOMPRESS_POSTFIX
,
1444 strlcat(zfile2
, BZCOMPRESS_POSTFIX
,
1446 if (lstat(zfile1
, &st
))
1451 printf("\tmv %s %s\n", zfile1
, zfile2
);
1453 /* XXX - Ought to be checking for failure! */
1454 rename(zfile1
, zfile2
);
1456 change_attrs(zfile2
, ent
);
1459 if (ent
->numlogs
> 0) {
1462 * Note that savelog() may succeed with using link()
1463 * for the archtodir case, but there is no good way
1464 * of knowing if it will when doing "noaction", so
1465 * here we claim that it will have to do a copy...
1468 printf("\tcp %s %s\n", ent
->log
, file1
);
1470 printf("\tln %s %s\n", ent
->log
, file1
);
1472 if (!(flags
& CE_BINARY
)) {
1473 /* Report the trimming to the old log */
1474 log_trim(ent
->log
, ent
);
1476 savelog(ent
->log
, file1
);
1478 change_attrs(file1
, ent
);
1481 /* Create the new log file and move it into place */
1483 printf("Start new log...\n");
1487 * Save all signalling and file-compression to be done after log
1488 * files from all entries have been rotated. This way any one
1489 * process will not be sent the same signal multiple times when
1490 * multiple log files had to be rotated.
1493 if (ent
->pid_file
!= NULL
)
1494 swork
= save_sigwork(ent
);
1495 if (ent
->numlogs
> 0 && (flags
& (CE_COMPACT
| CE_BZCOMPACT
))) {
1497 * The zipwork_entry will include a pointer to this
1498 * conf_entry, so the conf_entry should not be freed.
1500 free_or_keep
= KEEP_ENT
;
1501 save_zipwork(ent
, swork
, ent
->fsize
, file1
);
1504 return (free_or_keep
);
1508 do_sigwork(struct sigwork_entry
*swork
)
1510 struct sigwork_entry
*nextsig
;
1513 if (!(swork
->sw_pidok
) || swork
->sw_pid
== 0)
1514 return; /* no work to do... */
1517 * If nosignal (-s) was specified, then do not signal any process.
1518 * Note that a nosignal request triggers a warning message if the
1519 * rotated logfile needs to be compressed, *unless* -R was also
1520 * specified. We assume that an `-sR' request came from a process
1521 * which writes to the logfile, and as such, we assume that process
1522 * has already made sure the logfile is not presently in use. This
1523 * just sets swork->sw_pidok to a special value, and do_zipwork
1524 * will print any necessary warning(s).
1528 swork
->sw_pidok
= -1;
1533 * Compute the pause between consecutive signals. Use a longer
1534 * sleep time if we will be sending two signals to the same
1535 * deamon or process-group.
1538 nextsig
= SLIST_NEXT(swork
, sw_nextp
);
1539 if (nextsig
!= NULL
) {
1540 if (swork
->sw_pid
== nextsig
->sw_pid
)
1547 printf("\tkill -%d %d \t\t# %s\n", swork
->sw_signum
,
1548 (int)swork
->sw_pid
, swork
->sw_fname
);
1550 printf("\tsleep %d\n", secs
);
1554 kres
= kill(swork
->sw_pid
, swork
->sw_signum
);
1557 * Assume that "no such process" (ESRCH) is something
1558 * to warn about, but is not an error. Presumably the
1559 * process which writes to the rotated log file(s) is
1560 * gone, in which case we should have no problem with
1561 * compressing the rotated log file(s).
1564 swork
->sw_pidok
= 0;
1565 warn("can't notify %s, pid %d", swork
->sw_pidtype
,
1566 (int)swork
->sw_pid
);
1569 printf("Notified %s pid %d = %s\n", swork
->sw_pidtype
,
1570 (int)swork
->sw_pid
, swork
->sw_fname
);
1573 printf("Pause %d second(s) between signals\n",
1581 do_zipwork(struct zipwork_entry
*zwork
)
1583 const char *pgm_name
, *pgm_path
;
1584 int errsav
, fcount
, zstatus
;
1586 char zresult
[MAXPATHLEN
];
1589 strlcpy(zresult
, zwork
->zw_fname
, sizeof(zresult
));
1590 if (zwork
!= NULL
&& zwork
->zw_conf
!= NULL
) {
1591 if (zwork
->zw_conf
->flags
& CE_COMPACT
) {
1592 pgm_path
= _PATH_GZIP
;
1593 strlcat(zresult
, COMPRESS_POSTFIX
, sizeof(zresult
));
1594 } else if (zwork
->zw_conf
->flags
& CE_BZCOMPACT
) {
1595 pgm_path
= _PATH_BZIP2
;
1596 strlcat(zresult
, BZCOMPRESS_POSTFIX
, sizeof(zresult
));
1599 if (pgm_path
== NULL
) {
1600 warnx("invalid entry for %s in do_zipwork", zwork
->zw_fname
);
1603 pgm_name
= strrchr(pgm_path
, '/');
1604 if (pgm_name
== NULL
)
1605 pgm_name
= pgm_path
;
1609 if (zwork
->zw_swork
!= NULL
&& zwork
->zw_swork
->sw_pidok
<= 0) {
1611 "log %s not compressed because daemon(s) not notified",
1613 change_attrs(zwork
->zw_fname
, zwork
->zw_conf
);
1618 printf("\t%s %s\n", pgm_name
, zwork
->zw_fname
);
1619 change_attrs(zresult
, zwork
->zw_conf
);
1625 while (pidzip
< 0) {
1627 * The fork failed. If the failure was due to a temporary
1628 * problem, then wait a short time and try it again.
1631 warn("fork() for `%s %s'", pgm_name
, zwork
->zw_fname
);
1632 if (errsav
!= EAGAIN
|| fcount
> 5)
1633 errx(1, "Exiting...");
1639 /* The child process executes the compression command */
1640 execl(pgm_path
, pgm_path
, "-f", zwork
->zw_fname
, (char *)0);
1641 err(1, "execl(`%s -f %s')", pgm_path
, zwork
->zw_fname
);
1644 wpid
= waitpid(pidzip
, &zstatus
, 0);
1646 /* XXX - should this be a fatal error? */
1647 warn("%s: waitpid(%d)", pgm_path
, pidzip
);
1650 if (!WIFEXITED(zstatus
)) {
1651 warnx("`%s -f %s' did not terminate normally", pgm_name
,
1655 if (WEXITSTATUS(zstatus
)) {
1656 warnx("`%s -f %s' terminated with a non-zero status (%d)",
1657 pgm_name
, zwork
->zw_fname
, WEXITSTATUS(zstatus
));
1661 /* Compression was successful, set file attributes on the result. */
1662 change_attrs(zresult
, zwork
->zw_conf
);
1666 * Save information on any process we need to signal. Any single
1667 * process may need to be sent different signal-values for different
1668 * log files, but usually a single signal-value will cause the process
1669 * to close and re-open all of it's log files.
1671 static struct sigwork_entry
*
1672 save_sigwork(const struct conf_entry
*ent
)
1674 struct sigwork_entry
*sprev
, *stmp
;
1680 SLIST_FOREACH(stmp
, &swhead
, sw_nextp
) {
1681 ndiff
= strcmp(ent
->pid_file
, stmp
->sw_fname
);
1685 if (ent
->sig
== stmp
->sw_signum
)
1687 if (ent
->sig
> stmp
->sw_signum
) {
1694 if (stmp
!= NULL
&& ndiff
== 0)
1697 tmpsiz
= sizeof(struct sigwork_entry
) + strlen(ent
->pid_file
) + 1;
1698 stmp
= malloc(tmpsiz
);
1699 set_swpid(stmp
, ent
);
1700 stmp
->sw_signum
= ent
->sig
;
1701 strcpy(stmp
->sw_fname
, ent
->pid_file
);
1703 SLIST_INSERT_HEAD(&swhead
, stmp
, sw_nextp
);
1705 SLIST_INSERT_AFTER(sprev
, stmp
, sw_nextp
);
1710 * Save information on any file we need to compress. We may see the same
1711 * file multiple times, so check the full list to avoid duplicates. The
1712 * list itself is sorted smallest-to-largest, because that's the order we
1713 * want to compress the files. If the partition is very low on disk space,
1714 * then the smallest files are the most likely to compress, and compressing
1715 * them first will free up more space for the larger files.
1717 static struct zipwork_entry
*
1718 save_zipwork(const struct conf_entry
*ent
, const struct sigwork_entry
*swork
,
1719 int zsize
, const char *zipfname
)
1721 struct zipwork_entry
*zprev
, *ztmp
;
1725 /* Compute the size if the caller did not know it. */
1727 zsize
= sizefile(zipfname
);
1731 SLIST_FOREACH(ztmp
, &zwhead
, zw_nextp
) {
1732 ndiff
= strcmp(zipfname
, ztmp
->zw_fname
);
1735 if (zsize
> ztmp
->zw_fsize
)
1738 if (ztmp
!= NULL
&& ndiff
== 0)
1741 tmpsiz
= sizeof(struct zipwork_entry
) + strlen(zipfname
) + 1;
1742 ztmp
= malloc(tmpsiz
);
1743 ztmp
->zw_conf
= ent
;
1744 ztmp
->zw_swork
= swork
;
1745 ztmp
->zw_fsize
= zsize
;
1746 strcpy(ztmp
->zw_fname
, zipfname
);
1748 SLIST_INSERT_HEAD(&zwhead
, ztmp
, zw_nextp
);
1750 SLIST_INSERT_AFTER(zprev
, ztmp
, zw_nextp
);
1754 /* Send a signal to the pid specified by pidfile */
1756 set_swpid(struct sigwork_entry
*swork
, const struct conf_entry
*ent
)
1759 long minok
, maxok
, rval
;
1760 char *endp
, *linep
, line
[BUFSIZ
];
1764 swork
->sw_pidok
= 0;
1766 swork
->sw_pidtype
= "daemon";
1767 if (ent
->flags
& CE_SIGNALGROUP
) {
1769 * If we are expected to signal a process-group when
1770 * rotating this logfile, then the value read in should
1771 * be the negative of a valid process ID.
1775 swork
->sw_pidtype
= "process-group";
1778 f
= fopen(ent
->pid_file
, "r");
1780 warn("can't open pid file: %s", ent
->pid_file
);
1784 if (fgets(line
, BUFSIZ
, f
) == NULL
) {
1786 * Warn if the PID file is empty, but do not consider
1787 * it an error. Most likely it means the process has
1788 * has terminated, so it should be safe to rotate any
1789 * log files that the process would have been using.
1792 swork
->sw_pidok
= 1;
1793 warnx("pid file is empty: %s", ent
->pid_file
);
1795 warn("can't read from pid file: %s", ent
->pid_file
);
1803 while (*linep
== ' ')
1805 rval
= strtol(linep
, &endp
, 10);
1806 if (*endp
!= '\0' && !isspacech(*endp
)) {
1807 warnx("pid file does not start with a valid number: %s",
1809 } else if (rval
< minok
|| rval
> maxok
) {
1810 warnx("bad value '%ld' for process number in %s",
1811 rval
, ent
->pid_file
);
1813 warnx("\t(expecting value between %ld and %ld)",
1816 swork
->sw_pidok
= 1;
1817 swork
->sw_pid
= rval
;
1823 /* Log the fact that the logs were turned over */
1825 log_trim(const char *logname
, const struct conf_entry
*log_ent
)
1830 if ((f
= fopen(logname
, "a")) == NULL
)
1833 if (log_ent
->def_cfg
)
1834 xtra
= " using <default> rule";
1835 if (log_ent
->firstcreate
)
1836 fprintf(f
, "%s %s newsyslog[%d]: logfile first created%s\n",
1837 daytime
, hostname
, (int) getpid(), xtra
);
1838 else if (log_ent
->r_reason
!= NULL
)
1839 fprintf(f
, "%s %s newsyslog[%d]: logfile turned over%s%s\n",
1840 daytime
, hostname
, (int) getpid(), log_ent
->r_reason
, xtra
);
1842 fprintf(f
, "%s %s newsyslog[%d]: logfile turned over%s\n",
1843 daytime
, hostname
, (int) getpid(), xtra
);
1844 if (fclose(f
) == EOF
)
1845 err(1, "log_trim: fclose");
1849 /* Return size in kilobytes of a file */
1851 sizefile(const char *file
)
1855 if (stat(file
, &sb
) < 0)
1857 return (kbytes(dbtob(sb
.st_blocks
)));
1860 /* Return the age of old log file (file.0) */
1862 age_old_log(char *file
)
1866 char tmp
[MAXPATHLEN
+ sizeof(".0") + sizeof(COMPRESS_POSTFIX
) +
1867 sizeof(BZCOMPRESS_POSTFIX
) + 1];
1872 /* build name of archive directory into tmp */
1873 if (*archdirname
== '/') { /* absolute */
1874 strlcpy(tmp
, archdirname
, sizeof(tmp
));
1875 } else { /* relative */
1876 /* get directory part of logfile */
1877 strlcpy(tmp
, file
, sizeof(tmp
));
1878 if ((p
= strrchr(tmp
, '/')) == NULL
)
1882 strlcat(tmp
, archdirname
, sizeof(tmp
));
1885 strlcat(tmp
, "/", sizeof(tmp
));
1887 /* get filename part of logfile */
1888 if ((p
= strrchr(file
, '/')) == NULL
)
1889 strlcat(tmp
, file
, sizeof(tmp
));
1891 strlcat(tmp
, p
+ 1, sizeof(tmp
));
1893 strlcpy(tmp
, file
, sizeof(tmp
));
1896 strlcat(tmp
, ".0", sizeof(tmp
));
1897 if (stat(tmp
, &sb
) < 0) {
1899 * A plain '.0' file does not exist. Try again, first
1900 * with the added suffix of '.gz', then with an added
1901 * suffix of '.bz2' instead of '.gz'.
1903 endp
= strchr(tmp
, '\0');
1904 strlcat(tmp
, COMPRESS_POSTFIX
, sizeof(tmp
));
1905 if (stat(tmp
, &sb
) < 0) {
1906 *endp
= '\0'; /* Remove .gz */
1907 strlcat(tmp
, BZCOMPRESS_POSTFIX
, sizeof(tmp
));
1908 if (stat(tmp
, &sb
) < 0)
1912 return ((int)(ptimeget_secs(timenow
) - sb
.st_mtime
+ 1800) / 3600);
1915 /* Skip Over Blanks */
1919 while (p
&& *p
&& isspace(*p
))
1924 /* Skip Over Non-Blanks */
1928 while (p
&& *p
&& !isspace(*p
))
1933 /* Check if string is actually a number */
1935 isnumberstr(const char *string
)
1938 if (!isdigitch(*string
++))
1945 * Save the active log file under a new name. A link to the new name
1946 * is the quick-and-easy way to do this. If that fails (which it will
1947 * if the destination is on another partition), then make a copy of
1948 * the file to the new location.
1951 savelog(char *from
, char *to
)
1956 res
= link(from
, to
);
1960 if ((src
= fopen(from
, "r")) == NULL
)
1961 err(1, "can't fopen %s for reading", from
);
1962 if ((dst
= fopen(to
, "w")) == NULL
)
1963 err(1, "can't fopen %s for writing", to
);
1965 while ((c
= getc(src
)) != EOF
) {
1966 if ((putc(c
, dst
)) == EOF
)
1967 err(1, "error writing to %s", to
);
1971 err(1, "error reading from %s", from
);
1972 if ((fclose(src
)) != 0)
1973 err(1, "can't fclose %s", to
);
1974 if ((fclose(dst
)) != 0)
1975 err(1, "can't fclose %s", from
);
1978 /* create one or more directory components of a path */
1980 createdir(const struct conf_entry
*ent
, char *dirpart
)
1984 char mkdirpath
[MAXPATHLEN
];
1992 if (*s
!= '/' && *s
!= '\0')
1995 res
= lstat(mkdirpath
, &st
);
1998 printf("\tmkdir %s\n", mkdirpath
);
2000 res
= mkdir(mkdirpath
, 0755);
2002 err(1, "Error on mkdir(\"%s\") for -a",
2010 if (ent
->firstcreate
)
2011 printf("Created directory '%s' for new %s\n",
2014 printf("Created directory '%s' for -a\n", dirpart
);
2019 * Create a new log file, destroying any currently-existing version
2020 * of the log file in the process. If the caller wants a backup copy
2021 * of the file to exist, they should call 'link(logfile,logbackup)'
2022 * before calling this routine.
2025 createlog(const struct conf_entry
*ent
)
2029 char *realfile
, *slash
, tempfile
[MAXPATHLEN
];
2032 realfile
= ent
->log
;
2035 * If this log file is being created for the first time (-C option),
2036 * then it may also be true that the parent directory does not exist
2037 * yet. Check, and create that directory if it is missing.
2039 if (ent
->firstcreate
) {
2040 strlcpy(tempfile
, realfile
, sizeof(tempfile
));
2041 slash
= strrchr(tempfile
, '/');
2042 if (slash
!= NULL
) {
2044 failed
= stat(tempfile
, &st
);
2045 if (failed
&& errno
!= ENOENT
)
2046 err(1, "Error on stat(%s)", tempfile
);
2048 createdir(ent
, tempfile
);
2049 else if (!S_ISDIR(st
.st_mode
))
2050 errx(1, "%s exists but is not a directory",
2056 * First create an unused filename, so it can be chown'ed and
2057 * chmod'ed before it is moved into the real location. mkstemp
2058 * will create the file mode=600 & owned by us. Note that all
2059 * temp files will have a suffix of '.z<something>'.
2061 strlcpy(tempfile
, realfile
, sizeof(tempfile
));
2062 strlcat(tempfile
, ".zXXXXXX", sizeof(tempfile
));
2064 printf("\tmktemp %s\n", tempfile
);
2066 fd
= mkstemp(tempfile
);
2068 err(1, "can't mkstemp logfile %s", tempfile
);
2071 * Add status message to what will become the new log file.
2073 if (!(ent
->flags
& CE_BINARY
)) {
2074 if (log_trim(tempfile
, ent
))
2075 err(1, "can't add status message to log");
2079 /* Change the owner/group, if we are supposed to */
2080 if (ent
->uid
!= (uid_t
)-1 || ent
->gid
!= (gid_t
)-1) {
2082 printf("\tchown %u:%u %s\n", ent
->uid
, ent
->gid
,
2085 failed
= fchown(fd
, ent
->uid
, ent
->gid
);
2087 err(1, "can't fchown temp file %s", tempfile
);
2091 /* Turn on NODUMP if it was requested in the config-file. */
2092 if (ent
->flags
& CE_NODUMP
) {
2094 printf("\tchflags nodump %s\n", tempfile
);
2096 failed
= fchflags(fd
, UF_NODUMP
);
2098 warn("log_trim: fchflags(NODUMP)");
2104 * Note that if the real logfile still exists, and if the call
2105 * to rename() fails, then "neither the old file nor the new
2106 * file shall be changed or created" (to quote the standard).
2107 * If the call succeeds, then the file will be replaced without
2108 * any window where some other process might find that the file
2110 * XXX - ? It may be that for some error conditions, we could
2111 * retry by first removing the realfile and then renaming.
2114 printf("\tchmod %o %s\n", ent
->permissions
, tempfile
);
2115 printf("\tmv %s %s\n", tempfile
, realfile
);
2117 failed
= fchmod(fd
, ent
->permissions
);
2119 err(1, "can't fchmod temp file '%s'", tempfile
);
2120 failed
= rename(tempfile
, realfile
);
2122 err(1, "can't mv %s to %s", tempfile
, realfile
);
2130 * Change the attributes of a given filename to what was specified in
2131 * the newsyslog.conf entry. This routine is only called for files
2132 * that newsyslog expects that it has created, and thus it is a fatal
2133 * error if this routine finds that the file does not exist.
2136 change_attrs(const char *fname
, const struct conf_entry
*ent
)
2141 printf("\tchmod %o %s\n", ent
->permissions
, fname
);
2143 if (ent
->uid
!= (uid_t
)-1 || ent
->gid
!= (gid_t
)-1)
2144 printf("\tchown %u:%u %s\n",
2145 ent
->uid
, ent
->gid
, fname
);
2147 if (ent
->flags
& CE_NODUMP
)
2148 printf("\tchflags nodump %s\n", fname
);
2152 failed
= chmod(fname
, ent
->permissions
);
2155 err(1, "chmod(%s) in change_attrs", fname
);
2156 warn("change_attrs couldn't chmod(%s)", fname
);
2159 if (ent
->uid
!= (uid_t
)-1 || ent
->gid
!= (gid_t
)-1) {
2160 failed
= chown(fname
, ent
->uid
, ent
->gid
);
2162 warn("can't chown %s", fname
);
2165 if (ent
->flags
& CE_NODUMP
) {
2166 failed
= chflags(fname
, UF_NODUMP
);
2168 warn("can't chflags %s NODUMP", fname
);