Index: usr.sbin/newsyslog/newsyslog.8 =================================================================== --- usr.sbin/newsyslog/newsyslog.8 +++ usr.sbin/newsyslog/newsyslog.8 @@ -17,7 +17,7 @@ .\" the suitability of this software for any purpose. It is .\" provided "as is" without express or implied warranty. .\" -.Dd November 10, 2018 +.Dd August 28, 2019 .Dt NEWSYSLOG 8 .Os .Sh NAME @@ -176,6 +176,9 @@ based file names, and the other way around. The time format should contain at least year, month, day, and hour to make sure rotating of old logfiles can select the correct logfiles. +.Pp +It is also possible to specify a time format in the configuration file +on a per-entry basis. .It Fl C If specified once, then .Nm @@ -268,12 +271,20 @@ .Nm configuration file .It Pa /etc/newsyslog.conf.d -By default each file in this directory ending in '.conf' and not beginning with -a '.' will be included by the default +By default, +each file in this directory ending in +.Sq Pa .conf +and not beginning with a +.Sq Li \&. +will be included by the default .Pa newsyslog.conf . .It Pa /usr/local/etc/newsyslog.conf.d -By default each file in this directory ending in '.conf' and not beginning with -a '.' will be included by the default +By default, +each file in this directory ending in +.Sq Pa .conf +and not beginning with a +.Sq Li \&. +will be included by the default .Pa newsyslog.conf . .El .Sh COMPATIBILITY Index: usr.sbin/newsyslog/newsyslog.c =================================================================== --- usr.sbin/newsyslog/newsyslog.c +++ usr.sbin/newsyslog/newsyslog.c @@ -114,6 +114,7 @@ #define CE_PID2CMD 0x0400 /* Replace PID file with a shell command.*/ #define CE_PLAIN0 0x0800 /* Do not compress zero'th history file */ #define CE_RFC5424 0x1000 /* Use RFC5424 format rotation message */ +#define CE_SUFXRULE 0x2000 /* Define the rotation suffix rule */ #define MIN_PID 5 /* Don't touch pids lower than this */ #define MAX_PID 99999 /* was lower, see /usr/include/sys/proc.h */ @@ -167,6 +168,7 @@ int compress; /* Compression */ int sig; /* Signal to send */ int def_cfg; /* Using the rule for this file */ + char *suffix; /* Rotation suffix for CE_SUFXRULE */ }; struct sigwork_entry { @@ -282,7 +284,7 @@ static int parse_doption(const char *doption); static void usage(void); static int log_trim(const char *logname, const struct conf_entry *log_ent); -static int age_old_log(const char *file); +static int age_old_log(const char *file, const char *); static void savelog(char *from, char *to); static void createdir(const struct conf_entry *ent, char *dirpart); static void createlog(const struct conf_entry *ent); @@ -305,6 +307,7 @@ struct conf_entry *p; struct sigwork_entry *stmp; struct zipwork_entry *ztmp; + STAILQ_HEAD(, conf_entry) flhead = STAILQ_HEAD_INITIALIZER(flhead); SLIST_INIT(&swhead); SLIST_INIT(&zwhead); @@ -326,6 +329,8 @@ STAILQ_REMOVE_HEAD(worklist, cf_nextp); if (do_entry(p) == FREE_ENT) free_entry(p); + else + STAILQ_INSERT_HEAD(&flhead, p, cf_nextp); } /* @@ -371,7 +376,14 @@ SLIST_REMOVE_HEAD(&swhead, sw_nextp); free(stmp); } - + /* + * Free remaining worklist entries. + */ + while (!STAILQ_EMPTY(&flhead)) { + p = STAILQ_FIRST(&flhead); + STAILQ_REMOVE_HEAD(&flhead, cf_nextp); + free_entry(p); + } while (wait(NULL) > 0 || errno == EINTR) ; return (0); @@ -385,7 +397,7 @@ if (verbose > 4) printf("\t--> [creating entry for %s]\n", fname); - tempwork = malloc(sizeof(struct conf_entry)); + tempwork = calloc(1, sizeof(struct conf_entry)); if (tempwork == NULL) err(1, "malloc of conf_entry for %s", fname); @@ -447,28 +459,23 @@ if (ent == NULL) return; - if (ent->log != NULL) { - if (verbose > 4) - printf("\t--> [freeing entry for %s]\n", ent->log); - free(ent->log); - ent->log = NULL; - } + if (ent->log != NULL && verbose > 4) + printf("\t--> [freeing entry for %s]\n", ent->log); + free(ent->log); + ent->log = NULL; - if (ent->pid_cmd_file != NULL) { - free(ent->pid_cmd_file); - ent->pid_cmd_file = NULL; - } + free(ent->pid_cmd_file); + ent->pid_cmd_file = NULL; - if (ent->r_reason != NULL) { - free(ent->r_reason); - ent->r_reason = NULL; - } + free(ent->r_reason); + ent->r_reason = NULL; - if (ent->trim_at != NULL) { - ptime_free(ent->trim_at); - ent->trim_at = NULL; - } + ptime_free(ent->trim_at); + ent->trim_at = NULL; + free(ent->suffix); + ent->suffix = NULL; + free(ent); } @@ -488,22 +495,24 @@ } static fk_entry -do_entry(struct conf_entry * ent) +do_entry(struct conf_entry *ent) { #define REASON_MAX 80 int modtime; fk_entry free_or_keep; double diffsecs; char temp_reason[REASON_MAX]; + const char *fmt; int oversized; free_or_keep = FREE_ENT; + fmt = (ent->flags & CE_SUFXRULE) ? ent->suffix : timefnamefmt; if (verbose) printf("%s <%d%s>: ", ent->log, ent->numlogs, compress_type[ent->compress].flag); ent->fsize = sizefile(ent->log); oversized = ((ent->trsize > 0) && (ent->fsize >= ent->trsize)); - modtime = age_old_log(ent->log); + modtime = age_old_log(ent->log, fmt); ent->rotate = 0; ent->firstcreate = 0; if (ent->fsize < 0) { @@ -1302,6 +1311,27 @@ case 'r': working->flags |= CE_PID2CMD; break; + case 's': + q++; + if (*q == '\0' || *q != (char)'\"') + errx(1, + "missing \" in the suffix rule"); + q++; + cp = q; + while (*q != '\0' && !isspacech(*q) && + *q != (char)'\"') + q++; + if (*q != (char)'\"') + errx(1, + "missing \" in the suffix rule"); + working->suffix = calloc(1, (q - cp) + 1); + if (working->suffix == NULL) + errx(1, "malloc error"); + memcpy(working->suffix, cp, q - cp); + if (strchr(working->suffix, '/') != NULL) + errx(1, "invalid suffix rule"); + working->flags |= CE_SUFXRULE; + break; case 't': working->flags |= CE_RFC5424; break; @@ -1457,7 +1487,7 @@ */ static int validate_old_timelog(int fd, const struct dirent *dp, const char *logfname, - struct tm *tm) + struct tm *tm, const char *fmt) { struct stat sb; size_t logfname_len; @@ -1496,8 +1526,7 @@ return (0); } memset(tm, 0, sizeof(*tm)); - if ((s = strptime(&dp->d_name[logfname_len + 1], - timefnamefmt, tm)) == NULL) { + if ((s = strptime(&dp->d_name[logfname_len + 1], fmt, tm)) == NULL) { /* * We could special case "old" sequentially named logfiles here, * but we do not as that would require special handling to @@ -1529,7 +1558,7 @@ delete_oldest_timelog(const struct conf_entry *ent, const char *archive_dir) { char *basebuf, *dirbuf, errbuf[80]; - const char *base, *dir; + const char *base, *dir, *fmt; int dir_fd, i, logcnt, max_logcnt; struct oldlog_entry *oldlogs; struct dirent *dp; @@ -1539,6 +1568,7 @@ oldlogs = malloc(MAX_OLDLOGS * sizeof(struct oldlog_entry)); max_logcnt = MAX_OLDLOGS; logcnt = 0; + fmt = (ent->flags & CE_SUFXRULE) ? ent->suffix : timefnamefmt; if (archive_dir != NULL && archive_dir[0] != '\0') { dirbuf = NULL; @@ -1563,7 +1593,7 @@ err(1, "Cannot open log directory '%s'", dir); dir_fd = dirfd(dirp); while ((dp = readdir(dirp)) != NULL) { - if (validate_old_timelog(dir_fd, dp, base, &tm) == 0) + if (validate_old_timelog(dir_fd, dp, base, &tm, fmt) == 0) continue; /* @@ -1726,7 +1756,7 @@ char file1[MAXPATHLEN], file2[MAXPATHLEN]; char zfile1[MAXPATHLEN], zfile2[MAXPATHLEN]; const char *logfile_suffix; - char datetimestr[30]; + char datetimestr[MAXPATHLEN]; int flags, numlogs_c; fk_entry free_or_keep; struct sigwork_entry *swork; @@ -1772,7 +1802,7 @@ } /* Delete old logs */ - if (timefnamefmt != NULL) + if (timefnamefmt != NULL || ent->flags & CE_SUFXRULE) delete_oldest_timelog(ent, dirpart); else { /* @@ -1787,13 +1817,16 @@ } - if (timefnamefmt != NULL) { + if (timefnamefmt != NULL || ent->flags & CE_SUFXRULE) { + const char *fmt; + /* If time functions fails we can't really do any sensible */ if (time(&now) == (time_t)-1 || localtime_r(&now, &tm) == NULL) bzero(&tm, sizeof(tm)); - strftime(datetimestr, sizeof(datetimestr), timefnamefmt, &tm); + fmt = (ent->flags & CE_SUFXRULE) ? ent->suffix : timefnamefmt; + strftime(datetimestr, sizeof(datetimestr), fmt, &tm); if (archtodir) (void) snprintf(file1, sizeof(file1), "%s/%s.%s", dirpart, namepart, datetimestr); @@ -2363,7 +2396,7 @@ * based filenames. */ static time_t -mtime_old_timelog(const char *file) +mtime_old_timelog(const char *file, const char *fmt) { struct stat sb; struct tm tm; @@ -2398,7 +2431,7 @@ dir_fd = dirfd(dirp); /* Open the archive dir and find the most recent archive of logfname. */ while ((dp = readdir(dirp)) != NULL) { - if (validate_old_timelog(dir_fd, dp, logfname, &tm) == 0) + if (validate_old_timelog(dir_fd, dp, logfname, &tm, fmt) == 0) continue; if (fstatat(dir_fd, dp->d_name, &sb, AT_SYMLINK_NOFOLLOW) == -1) { @@ -2418,7 +2451,7 @@ /* Return the age in hours of the most recent archive of the logfile. */ static int -age_old_log(const char *file) +age_old_log(const char *file, const char *fmt) { struct stat sb; const char *logfile_suffix; @@ -2464,8 +2497,8 @@ (void) strlcpy(tmp, file, tmpsiz); } - if (timefnamefmt != NULL) { - mtime = mtime_old_timelog(tmp); + if (fmt != NULL) { + mtime = mtime_old_timelog(tmp, fmt); if (mtime == -1) return (-1); } else { Index: usr.sbin/newsyslog/newsyslog.conf.5 =================================================================== --- usr.sbin/newsyslog/newsyslog.conf.5 +++ usr.sbin/newsyslog/newsyslog.conf.5 @@ -21,7 +21,7 @@ .\" the suitability of this software for any purpose. It is .\" provided "as is" without express or implied warranty. .\" -.Dd August 21, 2018 +.Dd August 28, 2019 .Dt NEWSYSLOG.CONF 5 .Os .Sh NAME @@ -314,6 +314,21 @@ .Ar path_to_pid_cmd_file after rotation instead of trying to send signal to a process id stored in the file. +.It Cm S Ns Li Do Ar timefmt Dc +specifies that +.Xr newsyslog 8 +will create the +.Dq rotated +logfiles using the specified time format instead of the default +sequential filenames. +.Ar timefmt +must have no whitespace and be enclosed in double quotes. +This is equivalent to the +.Fl t +command line flag though applied only to the entry. +The time format is described in the +.Xr strftime 3 +manual page. .It Cm T if this flag is set the informational rotation message written to the log file will be in the format specified by RFC5424.