newsyslog.c revision 59004
113244Sgraichen/* 213244Sgraichen * This file contains changes from the Open Software Foundation. 313244Sgraichen */ 413244Sgraichen 513244Sgraichen/* 659004Shm * Copyright 1988, 1989 by the Massachusetts Institute of Technology 759004Shm * 859004Shm * Permission to use, copy, modify, and distribute this software and its 959004Shm * documentation for any purpose and without fee is hereby granted, provided 1059004Shm * that the above copyright notice appear in all copies and that both that 1159004Shm * copyright notice and this permission notice appear in supporting 1259004Shm * documentation, and that the names of M.I.T. and the M.I.T. S.I.P.B. not be 1359004Shm * used in advertising or publicity pertaining to distribution of the 1459004Shm * software without specific, written prior permission. M.I.T. and the M.I.T. 1559004Shm * S.I.P.B. make no representations about the suitability of this software 1659004Shm * for any purpose. It is provided "as is" without express or implied 1759004Shm * warranty. 1859004Shm * 1959004Shm */ 2013244Sgraichen 2113244Sgraichen/* 2259004Shm * newsyslog - roll over selected logs at the appropriate time, keeping the a 2359004Shm * specified number of backup files around. 2413244Sgraichen */ 2513244Sgraichen 2613244Sgraichen#ifndef lint 2730160Scharnierstatic const char rcsid[] = 2859003Shm"$FreeBSD: head/usr.sbin/newsyslog/newsyslog.c 59004 2000-04-04 08:50:01Z hm $"; 2959004Shm#endif /* not lint */ 3013244Sgraichen 3143071Swollman#define OSF 3213244Sgraichen#ifndef COMPRESS_POSTFIX 3343071Swollman#define COMPRESS_POSTFIX ".gz" 3413244Sgraichen#endif 3513244Sgraichen 3630160Scharnier#include <ctype.h> 3730160Scharnier#include <err.h> 3830160Scharnier#include <fcntl.h> 3930160Scharnier#include <grp.h> 4043071Swollman#include <paths.h> 4130160Scharnier#include <pwd.h> 4230160Scharnier#include <signal.h> 4313244Sgraichen#include <stdio.h> 4413244Sgraichen#include <stdlib.h> 4513244Sgraichen#include <string.h> 4643071Swollman#include <time.h> 4716240Salex#include <unistd.h> 4813244Sgraichen#include <sys/types.h> 4913244Sgraichen#include <sys/stat.h> 5013244Sgraichen#include <sys/param.h> 5113244Sgraichen#include <sys/wait.h> 5213244Sgraichen 5343071Swollman#include "pathnames.h" 5443071Swollman 5513244Sgraichen#define kbytes(size) (((size) + 1023) >> 10) 5659004Shm 5713244Sgraichen#ifdef _IBMR2 5813244Sgraichen/* Calculates (db * DEV_BSIZE) */ 5959003Shm#define dbtob(db) ((unsigned)(db) << UBSHIFT) 6013244Sgraichen#endif 6113244Sgraichen 6259003Shm#define CE_COMPACT 1 /* Compact the achived log files */ 6359003Shm#define CE_BINARY 2 /* Logfile is in binary, don't add */ 6459004Shm /* status messages */ 6543071Swollman#define CE_TRIMAT 4 /* trim at a specific time */ 6643071Swollman 6713244Sgraichen#define NONE -1 6859003Shm 6913244Sgraichenstruct conf_entry { 7059003Shm char *log; /* Name of the log */ 7159003Shm char *pid_file; /* PID file */ 7259003Shm int uid; /* Owner of log */ 7359003Shm int gid; /* Group of log */ 7459003Shm int numlogs; /* Number of logs to keep */ 7559003Shm int size; /* Size cutoff to trigger trimming the log */ 7659003Shm int hours; /* Hours between log trimming */ 7759003Shm time_t trim_at; /* Specific time to do trimming */ 7859003Shm int permissions; /* File permissions on the log */ 7959003Shm int flags; /* Flags (CE_COMPACT & CE_BINARY) */ 8059003Shm int sig; /* Signal to send */ 8159003Shm struct conf_entry *next;/* Linked list pointer */ 8213244Sgraichen}; 8313244Sgraichen 8459004Shmint archtodir = 0; /* Archive old logfiles to other directory */ 8559003Shmint verbose = 0; /* Print out what's going on */ 8659003Shmint needroot = 1; /* Root privs are necessary */ 8759003Shmint noaction = 0; /* Don't do anything, just show it */ 8859003Shmint force = 0; /* Force the trim no matter what */ 8959004Shmchar *archdirname; /* Directory path to old logfiles archive */ 9059003Shmchar *conf = _PATH_CONF; /* Configuration file to use */ 9159003Shmtime_t timenow; 9259003Shm 9325443Sache#define MIN_PID 5 9459003Shm#define MAX_PID 99999 /* was lower, see /usr/include/sys/proc.h */ 9559003Shmchar hostname[MAXHOSTNAMELEN + 1]; /* hostname */ 9659003Shmchar *daytime; /* timenow in human readable form */ 9713244Sgraichen 9816240Salexstatic struct conf_entry *parse_file(); 9916240Salexstatic char *sob(char *p); 10016240Salexstatic char *son(char *p); 10159003Shmstatic char *missing_field(char *p, char *errline); 10259003Shmstatic void do_entry(struct conf_entry * ent); 10359003Shmstatic void PRS(int argc, char **argv); 10416240Salexstatic void usage(); 10559003Shmstatic void dotrim(char *log, char *pid_file, int numdays, int falgs, int perm, int owner_uid, int group_gid, int sig); 10616240Salexstatic int log_trim(char *log); 10716240Salexstatic void compress_log(char *log); 10816240Salexstatic int sizefile(char *file); 10916240Salexstatic int age_old_log(char *file); 11025443Sachestatic pid_t get_pid(char *pid_file); 11159004Shmstatic time_t parse8601(char *s); 11259004Shmstatic void movefile(char *from, char *to, int perm, int owner_uid, int group_gid); 11359004Shmstatic void createdir(char *dirpart); 11459004Shmstatic time_t parseDWM(char *s); 11513244Sgraichen 11659004Shmint 11759004Shmmain(int argc, char **argv) 11813244Sgraichen{ 11959003Shm struct conf_entry *p, *q; 12025443Sache 12159003Shm PRS(argc, argv); 12259003Shm if (needroot && getuid() && geteuid()) 12359003Shm errx(1, "must have root privs"); 12459003Shm p = q = parse_file(); 12559003Shm 12659003Shm while (p) { 12759003Shm do_entry(p); 12859003Shm p = p->next; 12959003Shm free((char *) q); 13059003Shm q = p; 13159003Shm } 13259003Shm return (0); 13313244Sgraichen} 13413244Sgraichen 13559004Shmstatic void 13659004Shmdo_entry(struct conf_entry * ent) 13713244Sgraichen{ 13859003Shm int size, modtime; 13959003Shm char *pid_file; 14059003Shm 14159003Shm if (verbose) { 14259003Shm if (ent->flags & CE_COMPACT) 14359003Shm printf("%s <%dZ>: ", ent->log, ent->numlogs); 14459003Shm else 14559003Shm printf("%s <%d>: ", ent->log, ent->numlogs); 14659003Shm } 14759003Shm size = sizefile(ent->log); 14859003Shm modtime = age_old_log(ent->log); 14959003Shm if (size < 0) { 15059003Shm if (verbose) 15159003Shm printf("does not exist.\n"); 15259003Shm } else { 15343071Swollman if (ent->flags & CE_TRIMAT) { 15443071Swollman if (timenow < ent->trim_at 15559003Shm || difftime(timenow, ent->trim_at) >= 60 * 60) { 15643071Swollman if (verbose) 15743071Swollman printf("--> will trim at %s", 15859003Shm ctime(&ent->trim_at)); 15943071Swollman return; 16043071Swollman } else if (verbose && ent->hours <= 0) { 16143071Swollman printf("--> time is up\n"); 16243071Swollman } 16343071Swollman } 16459003Shm if (verbose && (ent->size > 0)) 16559003Shm printf("size (Kb): %d [%d] ", size, ent->size); 16659003Shm if (verbose && (ent->hours > 0)) 16759003Shm printf(" age (hr): %d [%d] ", modtime, ent->hours); 16859003Shm if (force || ((ent->size > 0) && (size >= ent->size)) || 16943071Swollman (ent->hours <= 0 && (ent->flags & CE_TRIMAT)) || 17059003Shm ((ent->hours > 0) && ((modtime >= ent->hours) 17159003Shm || (modtime < 0)))) { 17259003Shm if (verbose) 17359003Shm printf("--> trimming log....\n"); 17459003Shm if (noaction && !verbose) { 17559003Shm if (ent->flags & CE_COMPACT) 17659003Shm printf("%s <%dZ>: trimming\n", 17759003Shm ent->log, ent->numlogs); 17859003Shm else 17959003Shm printf("%s <%d>: trimming\n", 18059003Shm ent->log, ent->numlogs); 18159003Shm } 18235920Shoek if (ent->pid_file) { 18335920Shoek pid_file = ent->pid_file; 18435920Shoek } else { 18535920Shoek /* Only try to notify syslog if we are root */ 18635920Shoek if (needroot) 18743071Swollman pid_file = _PATH_SYSLOGPID; 18835920Shoek else 18935920Shoek pid_file = NULL; 19035920Shoek } 19159003Shm dotrim(ent->log, pid_file, ent->numlogs, 19236817Sache ent->flags, ent->permissions, ent->uid, ent->gid, ent->sig); 19359003Shm } else { 19459003Shm if (verbose) 19559003Shm printf("--> skipping\n"); 19659003Shm } 19759003Shm } 19813244Sgraichen} 19913244Sgraichen 20059004Shmstatic void 20159004ShmPRS(int argc, char **argv) 20213244Sgraichen{ 20359003Shm int c; 20459003Shm char *p; 20513244Sgraichen 20659003Shm timenow = time((time_t *) 0); 20759003Shm daytime = ctime(&timenow) + 4; 20859003Shm daytime[15] = '\0'; 20913244Sgraichen 21059003Shm /* Let's get our hostname */ 21159003Shm (void) gethostname(hostname, sizeof(hostname)); 21213244Sgraichen 21313244Sgraichen /* Truncate domain */ 21416240Salex if ((p = strchr(hostname, '.'))) { 21513244Sgraichen *p = '\0'; 21613244Sgraichen } 21759003Shm optind = 1; /* Start options parsing */ 21859004Shm while ((c = getopt(argc, argv, "nrvFf:a:t:")) != -1) 21959003Shm switch (c) { 22059003Shm case 'n': 22159003Shm noaction++; 22235920Shoek break; 22359004Shm case 'a': 22459004Shm archtodir++; 22559004Shm archdirname = optarg; 22659004Shm break; 22759003Shm case 'r': 22859003Shm needroot = 0; 22959003Shm break; 23059003Shm case 'v': 23159003Shm verbose++; 23259003Shm break; 23359003Shm case 'f': 23459003Shm conf = optarg; 23559003Shm break; 23634584Spst case 'F': 23734584Spst force++; 23834584Spst break; 23959003Shm default: 24059003Shm usage(); 24159003Shm } 24243071Swollman} 24313244Sgraichen 24459004Shmstatic void 24559004Shmusage(void) 24613244Sgraichen{ 24759004Shm fprintf(stderr, "usage: newsyslog [-Fnrv] [-f config-file] [-a directory]\n"); 24859003Shm exit(1); 24913244Sgraichen} 25013244Sgraichen 25159004Shm/* 25259004Shm * Parse a configuration file and return a linked list of all the logs to 25359004Shm * process 25413244Sgraichen */ 25559003Shmstatic struct conf_entry * 25659004Shmparse_file(void) 25713244Sgraichen{ 25859003Shm FILE *f; 25959003Shm char line[BUFSIZ], *parse, *q; 26059003Shm char *errline, *group; 26159003Shm struct conf_entry *first = NULL; 26259003Shm struct conf_entry *working = NULL; 26359003Shm struct passwd *pass; 26459003Shm struct group *grp; 26525518Sbrian int eol; 26613244Sgraichen 26759003Shm if (strcmp(conf, "-")) 26859003Shm f = fopen(conf, "r"); 26959003Shm else 27059003Shm f = stdin; 27159003Shm if (!f) 27259003Shm err(1, "%s", conf); 27359003Shm while (fgets(line, BUFSIZ, f)) { 27459003Shm if ((line[0] == '\n') || (line[0] == '#')) 27559003Shm continue; 27659003Shm errline = strdup(line); 27759003Shm if (!first) { 27859003Shm working = (struct conf_entry *) malloc(sizeof(struct conf_entry)); 27959003Shm first = working; 28059003Shm } else { 28159003Shm working->next = (struct conf_entry *) malloc(sizeof(struct conf_entry)); 28259003Shm working = working->next; 28359003Shm } 28413244Sgraichen 28559003Shm q = parse = missing_field(sob(line), errline); 28659003Shm parse = son(line); 28725518Sbrian if (!*parse) 28859003Shm errx(1, "malformed line (missing fields):\n%s", errline); 28959003Shm *parse = '\0'; 29059003Shm working->log = strdup(q); 29113244Sgraichen 29259003Shm q = parse = missing_field(sob(++parse), errline); 29359003Shm parse = son(parse); 29425518Sbrian if (!*parse) 29559003Shm errx(1, "malformed line (missing fields):\n%s", errline); 29659003Shm *parse = '\0'; 29759003Shm if ((group = strchr(q, ':')) != NULL || 29859003Shm (group = strrchr(q, '.')) != NULL) { 29959003Shm *group++ = '\0'; 30059003Shm if (*q) { 30159003Shm if (!(isnumber(*q))) { 30259003Shm if ((pass = getpwnam(q)) == NULL) 30359003Shm errx(1, 30459003Shm "error in config file; unknown user:\n%s", 30559003Shm errline); 30659003Shm working->uid = pass->pw_uid; 30759003Shm } else 30859003Shm working->uid = atoi(q); 30959003Shm } else 31059003Shm working->uid = NONE; 31113244Sgraichen 31259003Shm q = group; 31359003Shm if (*q) { 31459003Shm if (!(isnumber(*q))) { 31559003Shm if ((grp = getgrnam(q)) == NULL) 31659003Shm errx(1, 31759003Shm "error in config file; unknown group:\n%s", 31859003Shm errline); 31959003Shm working->gid = grp->gr_gid; 32059003Shm } else 32159003Shm working->gid = atoi(q); 32259003Shm } else 32359003Shm working->gid = NONE; 32413244Sgraichen 32559003Shm q = parse = missing_field(sob(++parse), errline); 32659003Shm parse = son(parse); 32759003Shm if (!*parse) 32859003Shm errx(1, "malformed line (missing fields):\n%s", errline); 32959003Shm *parse = '\0'; 33059003Shm } else 33159003Shm working->uid = working->gid = NONE; 33259003Shm 33359003Shm if (!sscanf(q, "%o", &working->permissions)) 33459003Shm errx(1, "error in config file; bad permissions:\n%s", 33559003Shm errline); 33659003Shm 33759003Shm q = parse = missing_field(sob(++parse), errline); 33859003Shm parse = son(parse); 33925518Sbrian if (!*parse) 34059003Shm errx(1, "malformed line (missing fields):\n%s", errline); 34159003Shm *parse = '\0'; 34259003Shm if (!sscanf(q, "%d", &working->numlogs)) 34359003Shm errx(1, "error in config file; bad number:\n%s", 34459003Shm errline); 34513244Sgraichen 34659003Shm q = parse = missing_field(sob(++parse), errline); 34759003Shm parse = son(parse); 34825518Sbrian if (!*parse) 34959003Shm errx(1, "malformed line (missing fields):\n%s", errline); 35059003Shm *parse = '\0'; 35159003Shm if (isdigit(*q)) 35259003Shm working->size = atoi(q); 35359003Shm else 35459003Shm working->size = -1; 35559003Shm 35659003Shm working->flags = 0; 35759003Shm q = parse = missing_field(sob(++parse), errline); 35859003Shm parse = son(parse); 35925518Sbrian eol = !*parse; 36059003Shm *parse = '\0'; 36143071Swollman { 36259003Shm char *ep; 36359003Shm u_long ul; 36413244Sgraichen 36543071Swollman ul = strtoul(q, &ep, 10); 36643071Swollman if (ep == q) 36743071Swollman working->hours = 0; 36843071Swollman else if (*ep == '*') 36943071Swollman working->hours = -1; 37043071Swollman else if (ul > INT_MAX) 37143071Swollman errx(1, "interval is too large:\n%s", errline); 37243071Swollman else 37343071Swollman working->hours = ul; 37443071Swollman 37559004Shm if (*ep != '\0' && *ep != '@' && *ep != '*' && *ep != '$') 37643071Swollman errx(1, "malformed interval/at:\n%s", errline); 37743071Swollman if (*ep == '@') { 37843071Swollman if ((working->trim_at = parse8601(ep + 1)) 37959003Shm == (time_t) - 1) 38043071Swollman errx(1, "malformed at:\n%s", errline); 38143071Swollman working->flags |= CE_TRIMAT; 38259004Shm } else if (*ep == '$') { 38359004Shm if ((working->trim_at = parseDWM(ep + 1)) 38459004Shm == (time_t) - 1) 38559004Shm errx(1, "malformed at:\n%s", errline); 38659004Shm working->flags |= CE_TRIMAT; 38743071Swollman } 38843071Swollman } 38943071Swollman 39025518Sbrian if (eol) 39159003Shm q = NULL; 39225518Sbrian else { 39359003Shm q = parse = sob(++parse); /* Optional field */ 39459003Shm parse = son(parse); 39559003Shm if (!*parse) 39659003Shm eol = 1; 39759003Shm *parse = '\0'; 39825518Sbrian } 39925443Sache 40059003Shm while (q && *q && !isspace(*q)) { 40159003Shm if ((*q == 'Z') || (*q == 'z')) 40259003Shm working->flags |= CE_COMPACT; 40359003Shm else if ((*q == 'B') || (*q == 'b')) 40459003Shm working->flags |= CE_BINARY; 40559003Shm else if (*q != '-') 40659003Shm errx(1, "illegal flag in config file -- %c", *q); 40759003Shm q++; 40859003Shm } 40959003Shm 41025518Sbrian if (eol) 41159003Shm q = NULL; 41225518Sbrian else { 41359003Shm q = parse = sob(++parse); /* Optional field */ 41459003Shm parse = son(parse); 41559003Shm if (!*parse) 41659003Shm eol = 1; 41759003Shm *parse = '\0'; 41825518Sbrian } 41925443Sache 42025443Sache working->pid_file = NULL; 42125443Sache if (q && *q) { 42225443Sache if (*q == '/') 42325443Sache working->pid_file = strdup(q); 42436817Sache else if (isdigit(*q)) 42536817Sache goto got_sig; 42659003Shm else 42759003Shm errx(1, "illegal pid file or signal number in config file:\n%s", errline); 42825443Sache } 42936817Sache if (eol) 43059003Shm q = NULL; 43136817Sache else { 43259003Shm q = parse = sob(++parse); /* Optional field */ 43359003Shm *(parse = son(parse)) = '\0'; 43436817Sache } 43536817Sache 43636817Sache working->sig = SIGHUP; 43736817Sache if (q && *q) { 43836817Sache if (isdigit(*q)) { 43959003Shm got_sig: 44036817Sache working->sig = atoi(q); 44136817Sache } else { 44259003Shm err_sig: 44359003Shm errx(1, "illegal signal number in config file:\n%s", errline); 44436817Sache } 44536817Sache if (working->sig < 1 || working->sig >= NSIG) 44636817Sache goto err_sig; 44736817Sache } 44859003Shm free(errline); 44959003Shm } 45059003Shm if (working) 45159003Shm working->next = (struct conf_entry *) NULL; 45259003Shm (void) fclose(f); 45359003Shm return (first); 45413244Sgraichen} 45513244Sgraichen 45659003Shmstatic char * 45759004Shmmissing_field(char *p, char *errline) 45813244Sgraichen{ 45959003Shm if (!p || !*p) 46059003Shm errx(1, "missing field in config file:\n%s", errline); 46159003Shm return (p); 46213244Sgraichen} 46313244Sgraichen 46459004Shmstatic void 46559004Shmdotrim(char *log, char *pid_file, int numdays, int flags, int perm, 46659004Shm int owner_uid, int group_gid, int sig) 46713244Sgraichen{ 46859004Shm char dirpart[MAXPATHLEN + 1], namepart[MAXPATHLEN + 1]; 46959003Shm char file1[MAXPATHLEN + 1], file2[MAXPATHLEN + 1]; 47059003Shm char zfile1[MAXPATHLEN + 1], zfile2[MAXPATHLEN + 1]; 47159003Shm int notified, need_notification, fd, _numdays; 47259003Shm struct stat st; 47359003Shm pid_t pid; 47413244Sgraichen 47513244Sgraichen#ifdef _IBMR2 47659004Shm /* 47759004Shm * AIX 3.1 has a broken fchown- if the owner_uid is -1, it will 47859004Shm * actually change it to be owned by uid -1, instead of leaving it 47959004Shm * as is, as it is supposed to. 48059004Shm */ 48159003Shm if (owner_uid == -1) 48259003Shm owner_uid = geteuid(); 48313244Sgraichen#endif 48413244Sgraichen 48559004Shm if (archtodir) { 48659004Shm char *p; 48713244Sgraichen 48859004Shm /* build complete name of archive directory into dirpart */ 48959004Shm if (*archdirname == '/') { /* absolute */ 49059004Shm strcpy(dirpart, archdirname); 49159004Shm } else { /* relative */ 49259004Shm /* get directory part of logfile */ 49359004Shm strcpy(dirpart, log); 49459004Shm if ((p = rindex(dirpart, '/')) == NULL) 49559004Shm dirpart[0] = '\0'; 49659004Shm else 49759004Shm *(p + 1) = '\0'; 49859004Shm strcat(dirpart, archdirname); 49959004Shm } 50059004Shm 50159004Shm /* check if archive directory exists, if not, create it */ 50259004Shm if (lstat(dirpart, &st)) 50359004Shm createdir(dirpart); 50459004Shm 50559004Shm /* get filename part of logfile */ 50659004Shm if ((p = rindex(log, '/')) == NULL) 50759004Shm strcpy(namepart, log); 50859004Shm else 50959004Shm strcpy(namepart, p + 1); 51059004Shm 51159004Shm /* name of oldest log */ 51259004Shm (void) sprintf(file1, "%s/%s.%d", dirpart, namepart, numdays); 51359004Shm (void) strcpy(zfile1, file1); 51459004Shm (void) strcat(zfile1, COMPRESS_POSTFIX); 51559004Shm } else { 51659004Shm /* name of oldest log */ 51759004Shm (void) sprintf(file1, "%s.%d", log, numdays); 51859004Shm (void) strcpy(zfile1, file1); 51959004Shm (void) strcat(zfile1, COMPRESS_POSTFIX); 52059004Shm } 52159004Shm 52259003Shm if (noaction) { 52359003Shm printf("rm -f %s\n", file1); 52459003Shm printf("rm -f %s\n", zfile1); 52559003Shm } else { 52659003Shm (void) unlink(file1); 52759003Shm (void) unlink(zfile1); 52859003Shm } 52913244Sgraichen 53059003Shm /* Move down log files */ 53118188Sjkh _numdays = numdays; /* preserve */ 53259003Shm while (numdays--) { 53359004Shm 53459003Shm (void) strcpy(file2, file1); 53559004Shm 53659004Shm if (archtodir) 53759004Shm (void) sprintf(file1, "%s/%s.%d", dirpart, namepart, numdays); 53859004Shm else 53959004Shm (void) sprintf(file1, "%s.%d", log, numdays); 54059004Shm 54159003Shm (void) strcpy(zfile1, file1); 54259003Shm (void) strcpy(zfile2, file2); 54359003Shm if (lstat(file1, &st)) { 54459003Shm (void) strcat(zfile1, COMPRESS_POSTFIX); 54559003Shm (void) strcat(zfile2, COMPRESS_POSTFIX); 54659003Shm if (lstat(zfile1, &st)) 54759003Shm continue; 54859003Shm } 54959003Shm if (noaction) { 55059003Shm printf("mv %s %s\n", zfile1, zfile2); 55159003Shm printf("chmod %o %s\n", perm, zfile2); 55259003Shm printf("chown %d.%d %s\n", 55359003Shm owner_uid, group_gid, zfile2); 55459003Shm } else { 55559003Shm (void) rename(zfile1, zfile2); 55659003Shm (void) chmod(zfile2, perm); 55759003Shm (void) chown(zfile2, owner_uid, group_gid); 55859003Shm } 55959003Shm } 56059003Shm if (!noaction && !(flags & CE_BINARY)) 56159003Shm (void) log_trim(log); /* Report the trimming to the old log */ 56213244Sgraichen 56318188Sjkh if (!_numdays) { 56418075Sjkh if (noaction) 56559003Shm printf("rm %s\n", log); 56618075Sjkh else 56759003Shm (void) unlink(log); 56859003Shm } else { 56918075Sjkh if (noaction) 57059003Shm printf("mv %s to %s\n", log, file1); 57159004Shm else { 57259004Shm if (archtodir) 57359004Shm movefile(log, file1, perm, owner_uid, group_gid); 57459004Shm else 57559004Shm (void) rename(log, file1); 57659004Shm } 57718075Sjkh } 57818075Sjkh 57959003Shm if (noaction) 58059003Shm printf("Start new log..."); 58159003Shm else { 58259003Shm fd = creat(log, perm); 58359003Shm if (fd < 0) 58459003Shm err(1, "can't start new log"); 58559003Shm if (fchown(fd, owner_uid, group_gid)) 58659003Shm err(1, "can't chmod new log file"); 58759003Shm (void) close(fd); 58859003Shm if (!(flags & CE_BINARY)) 58959003Shm if (log_trim(log)) /* Add status message */ 59059003Shm err(1, "can't add status message to log"); 59159003Shm } 59259003Shm if (noaction) 59359003Shm printf("chmod %o %s...\n", perm, log); 59459003Shm else 59559003Shm (void) chmod(log, perm); 59625443Sache 59725443Sache pid = 0; 59825443Sache need_notification = notified = 0; 59925443Sache if (pid_file != NULL) { 60025443Sache need_notification = 1; 60125443Sache pid = get_pid(pid_file); 60225443Sache } 60325443Sache if (pid) { 60425443Sache if (noaction) { 60525443Sache notified = 1; 60659003Shm printf("kill -%d %d\n", sig, (int) pid); 60759003Shm } else if (kill(pid, sig)) 60859003Shm warn("can't notify daemon, pid %d", (int) pid); 60925443Sache else { 61025443Sache notified = 1; 61125443Sache if (verbose) 61259003Shm printf("daemon pid %d notified\n", (int) pid); 61325443Sache } 61425443Sache } 61525443Sache if ((flags & CE_COMPACT)) { 61625443Sache if (need_notification && !notified) 61725443Sache warnx("log not compressed because daemon not notified"); 61825443Sache else if (noaction) 61959003Shm printf("Compress %s.0\n", log); 62025443Sache else { 62125443Sache if (notified) { 62225443Sache if (verbose) 62325443Sache printf("small pause to allow daemon to close log\n"); 62431460Sache sleep(10); 62525443Sache } 62659004Shm if (archtodir) { 62759004Shm (void) sprintf(file1, "%s/%s", dirpart, namepart); 62859004Shm compress_log(file1); 62959004Shm } else { 63059004Shm compress_log(log); 63159004Shm } 63225443Sache } 63359003Shm } 63413244Sgraichen} 63513244Sgraichen 63613244Sgraichen/* Log the fact that the logs were turned over */ 63759004Shmstatic int 63859004Shmlog_trim(char *log) 63913244Sgraichen{ 64059003Shm FILE *f; 64159003Shm 64259003Shm if ((f = fopen(log, "a")) == NULL) 64359003Shm return (-1); 64459003Shm fprintf(f, "%s %s newsyslog[%d]: logfile turned over\n", 64559003Shm daytime, hostname, (int) getpid()); 64659003Shm if (fclose(f) == EOF) 64759003Shm err(1, "log_trim: fclose:"); 64859003Shm return (0); 64913244Sgraichen} 65013244Sgraichen 65159004Shm/* Fork of gzip to compress the old log file */ 65259004Shmstatic void 65359004Shmcompress_log(char *log) 65413244Sgraichen{ 65559003Shm pid_t pid; 65659003Shm char tmp[MAXPATHLEN + 1]; 65759003Shm 65859003Shm (void) sprintf(tmp, "%s.0", log); 65925443Sache pid = fork(); 66059003Shm if (pid < 0) 66159003Shm err(1, "fork"); 66259003Shm else if (!pid) { 66343071Swollman (void) execl(_PATH_GZIP, _PATH_GZIP, "-f", tmp, 0); 66443071Swollman err(1, _PATH_GZIP); 66559003Shm } 66613244Sgraichen} 66713244Sgraichen 66813244Sgraichen/* Return size in kilobytes of a file */ 66959004Shmstatic int 67059004Shmsizefile(char *file) 67113244Sgraichen{ 67259003Shm struct stat sb; 67313244Sgraichen 67459003Shm if (stat(file, &sb) < 0) 67559003Shm return (-1); 67659003Shm return (kbytes(dbtob(sb.st_blocks))); 67713244Sgraichen} 67813244Sgraichen 67913244Sgraichen/* Return the age of old log file (file.0) */ 68059004Shmstatic int 68159004Shmage_old_log(char *file) 68213244Sgraichen{ 68359003Shm struct stat sb; 68459003Shm char tmp[MAXPATHLEN + sizeof(".0") + sizeof(COMPRESS_POSTFIX) + 1]; 68513244Sgraichen 68659004Shm if (archtodir) { 68759004Shm char *p; 68859004Shm 68959004Shm /* build name of archive directory into tmp */ 69059004Shm if (*archdirname == '/') { /* absolute */ 69159004Shm strcpy(tmp, archdirname); 69259004Shm } else { /* relative */ 69359004Shm /* get directory part of logfile */ 69459004Shm strcpy(tmp, file); 69559004Shm if ((p = rindex(tmp, '/')) == NULL) 69659004Shm tmp[0] = '\0'; 69759004Shm else 69859004Shm *(p + 1) = '\0'; 69959004Shm strcat(tmp, archdirname); 70059004Shm } 70159004Shm 70259004Shm strcat(tmp, "/"); 70359004Shm 70459004Shm /* get filename part of logfile */ 70559004Shm if ((p = rindex(file, '/')) == NULL) 70659004Shm strcat(tmp, file); 70759004Shm else 70859004Shm strcat(tmp, p + 1); 70959004Shm } else { 71059004Shm (void) strcpy(tmp, file); 71159004Shm } 71259004Shm 71359003Shm if (stat(strcat(tmp, ".0"), &sb) < 0) 71459003Shm if (stat(strcat(tmp, COMPRESS_POSTFIX), &sb) < 0) 71559003Shm return (-1); 71659003Shm return ((int) (timenow - sb.st_mtime + 1800) / 3600); 71713244Sgraichen} 71813244Sgraichen 71959004Shmstatic pid_t 72059004Shmget_pid(char *pid_file) 72125443Sache{ 72225443Sache FILE *f; 72359003Shm char line[BUFSIZ]; 72425443Sache pid_t pid = 0; 72513244Sgraichen 72659003Shm if ((f = fopen(pid_file, "r")) == NULL) 72725443Sache warn("can't open %s pid file to restart a daemon", 72859003Shm pid_file); 72925443Sache else { 73059003Shm if (fgets(line, BUFSIZ, f)) { 73125443Sache pid = atol(line); 73225443Sache if (pid < MIN_PID || pid > MAX_PID) { 73359003Shm warnx("preposterous process number: %d", (int) pid); 73425443Sache pid = 0; 73525443Sache } 73625443Sache } else 73725443Sache warn("can't read %s pid file to restart a daemon", 73859003Shm pid_file); 73959003Shm (void) fclose(f); 74025443Sache } 74125443Sache return pid; 74225443Sache} 74325443Sache 74413244Sgraichen/* Skip Over Blanks */ 74559003Shmchar * 74659004Shmsob(char *p) 74713244Sgraichen{ 74859003Shm while (p && *p && isspace(*p)) 74959003Shm p++; 75059003Shm return (p); 75113244Sgraichen} 75213244Sgraichen 75313244Sgraichen/* Skip Over Non-Blanks */ 75459003Shmchar * 75559004Shmson(char *p) 75613244Sgraichen{ 75759003Shm while (p && *p && !isspace(*p)) 75859003Shm p++; 75959003Shm return (p); 76013244Sgraichen} 76143071Swollman 76243071Swollman/* 76359004Shm * Parse a limited subset of ISO 8601. The specific format is as follows: 76443071Swollman * 76559004Shm * [CC[YY[MM[DD]]]][THH[MM[SS]]] (where `T' is the literal letter) 76643071Swollman * 76759004Shm * We don't accept a timezone specification; missing fields (including timezone) 76859004Shm * are defaulted to the current date but time zero. 76943071Swollman */ 77043071Swollmanstatic time_t 77159004Shmparse8601(char *s) 77243071Swollman{ 77359003Shm char *t; 77459003Shm struct tm tm, *tmp; 77559003Shm u_long ul; 77643071Swollman 77743071Swollman tmp = localtime(&timenow); 77843071Swollman tm = *tmp; 77943071Swollman 78043071Swollman tm.tm_hour = tm.tm_min = tm.tm_sec = 0; 78143071Swollman 78243071Swollman ul = strtoul(s, &t, 10); 78343071Swollman if (*t != '\0' && *t != 'T') 78443071Swollman return -1; 78543071Swollman 78643071Swollman /* 78759003Shm * Now t points either to the end of the string (if no time was 78859003Shm * provided) or to the letter `T' which separates date and time in 78959003Shm * ISO 8601. The pointer arithmetic is the same for either case. 79043071Swollman */ 79143071Swollman switch (t - s) { 79243071Swollman case 8: 79343071Swollman tm.tm_year = ((ul / 1000000) - 19) * 100; 79443071Swollman ul = ul % 1000000; 79543071Swollman case 6: 79643071Swollman tm.tm_year = tm.tm_year - (tm.tm_year % 100); 79743071Swollman tm.tm_year += ul / 10000; 79843071Swollman ul = ul % 10000; 79943071Swollman case 4: 80043071Swollman tm.tm_mon = (ul / 100) - 1; 80143071Swollman ul = ul % 100; 80243071Swollman case 2: 80343071Swollman tm.tm_mday = ul; 80443071Swollman case 0: 80543071Swollman break; 80643071Swollman default: 80743071Swollman return -1; 80843071Swollman } 80943071Swollman 81043071Swollman /* sanity check */ 81143071Swollman if (tm.tm_year < 70 || tm.tm_mon < 0 || tm.tm_mon > 12 81243071Swollman || tm.tm_mday < 1 || tm.tm_mday > 31) 81343071Swollman return -1; 81443071Swollman 81543071Swollman if (*t != '\0') { 81643071Swollman s = ++t; 81743071Swollman ul = strtoul(s, &t, 10); 81843071Swollman if (*t != '\0' && !isspace(*t)) 81943071Swollman return -1; 82043071Swollman 82143071Swollman switch (t - s) { 82243071Swollman case 6: 82343071Swollman tm.tm_sec = ul % 100; 82443071Swollman ul /= 100; 82543071Swollman case 4: 82643071Swollman tm.tm_min = ul % 100; 82743071Swollman ul /= 100; 82843071Swollman case 2: 82943071Swollman tm.tm_hour = ul; 83043071Swollman case 0: 83143071Swollman break; 83243071Swollman default: 83343071Swollman return -1; 83443071Swollman } 83543071Swollman 83643071Swollman /* sanity check */ 83743071Swollman if (tm.tm_sec < 0 || tm.tm_sec > 60 || tm.tm_min < 0 83843071Swollman || tm.tm_min > 59 || tm.tm_hour < 0 || tm.tm_hour > 23) 83943071Swollman return -1; 84043071Swollman } 84143071Swollman return mktime(&tm); 84243071Swollman} 84359004Shm 84459004Shm/* physically move file */ 84559004Shmstatic void 84659004Shmmovefile(char *from, char *to, int perm, int owner_uid, int group_gid) 84759004Shm{ 84859004Shm FILE *src, *dst; 84959004Shm int c; 85059004Shm 85159004Shm if ((src = fopen(from, "r")) == NULL) 85259004Shm err(1, "can't fopen %s for reading", from); 85359004Shm if ((dst = fopen(to, "w")) == NULL) 85459004Shm err(1, "can't fopen %s for writing", to); 85559004Shm if (fchown(fileno(dst), owner_uid, group_gid)) 85659004Shm err(1, "can't fchown %s", to); 85759004Shm if (fchmod(fileno(dst), perm)) 85859004Shm err(1, "can't fchmod %s", to); 85959004Shm 86059004Shm while ((c = getc(src)) != EOF) { 86159004Shm if ((putc(c, dst)) == EOF) 86259004Shm err(1, "error writing to %s", to); 86359004Shm } 86459004Shm 86559004Shm if (ferror(src)) 86659004Shm err(1, "error reading from %s", from); 86759004Shm if ((fclose(src)) != 0) 86859004Shm err(1, "can't fclose %s", to); 86959004Shm if ((fclose(dst)) != 0) 87059004Shm err(1, "can't fclose %s", from); 87159004Shm if ((unlink(from)) != 0) 87259004Shm err(1, "can't unlink %s", from); 87359004Shm} 87459004Shm 87559004Shm/* create one or more directory components of a path */ 87659004Shmstatic void 87759004Shmcreatedir(char *dirpart) 87859004Shm{ 87959004Shm char *s, *d; 88059004Shm char mkdirpath[MAXPATHLEN + 1]; 88159004Shm struct stat st; 88259004Shm 88359004Shm s = dirpart; 88459004Shm d = mkdirpath; 88559004Shm 88659004Shm for (;;) { 88759004Shm *d++ = *s++; 88859004Shm if (*s == '/' || *s == '\0') { 88959004Shm *d = '\0'; 89059004Shm if (lstat(mkdirpath, &st)) 89159004Shm mkdir(mkdirpath, 0755); 89259004Shm } 89359004Shm if (*s == '\0') 89459004Shm break; 89559004Shm } 89659004Shm} 89759004Shm 89859004Shm/*- 89959004Shm * Parse a cyclic time specification, the format is as follows: 90059004Shm * 90159004Shm * [Dhh] or [Wd[Dhh]] or [Mdd[Dhh]] 90259004Shm * 90359004Shm * to rotate a logfile cyclic at 90459004Shm * 90559004Shm * - every day (D) within a specific hour (hh) (hh = 0...23) 90659004Shm * - once a week (W) at a specific day (d) OR (d = 0..6, 0 = Sunday) 90759004Shm * - once a month (M) at a specific day (d) (d = 1..31,l|L) 90859004Shm * 90959004Shm * We don't accept a timezone specification; missing fields 91059004Shm * are defaulted to the current date but time zero. 91159004Shm */ 91259004Shmstatic time_t 91359004ShmparseDWM(char *s) 91459004Shm{ 91559004Shm char *t; 91659004Shm struct tm tm, *tmp; 91759004Shm u_long ul; 91859004Shm int nd; 91959004Shm static int mtab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 92059004Shm int WMseen = 0; 92159004Shm int Dseen = 0; 92259004Shm 92359004Shm tmp = localtime(&timenow); 92459004Shm tm = *tmp; 92559004Shm 92659004Shm /* set no. of days per month */ 92759004Shm 92859004Shm nd = mtab[tm.tm_mon]; 92959004Shm 93059004Shm if (tm.tm_mon == 1) { 93159004Shm if (((tm.tm_year + 1900) % 4 == 0) && 93259004Shm ((tm.tm_year + 1900) % 100 != 0) && 93359004Shm ((tm.tm_year + 1900) % 400 == 0)) { 93459004Shm nd++; /* leap year, 29 days in february */ 93559004Shm } 93659004Shm } 93759004Shm tm.tm_hour = tm.tm_min = tm.tm_sec = 0; 93859004Shm 93959004Shm for (;;) { 94059004Shm switch (*s) { 94159004Shm case 'D': 94259004Shm if (Dseen) 94359004Shm return -1; 94459004Shm Dseen++; 94559004Shm s++; 94659004Shm ul = strtoul(s, &t, 10); 94759004Shm if (ul < 0 || ul > 23) 94859004Shm return -1; 94959004Shm tm.tm_hour = ul; 95059004Shm break; 95159004Shm 95259004Shm case 'W': 95359004Shm if (WMseen) 95459004Shm return -1; 95559004Shm WMseen++; 95659004Shm s++; 95759004Shm ul = strtoul(s, &t, 10); 95859004Shm if (ul < 0 || ul > 6) 95959004Shm return -1; 96059004Shm if (ul != tm.tm_wday) { 96159004Shm int save; 96259004Shm 96359004Shm if (ul < tm.tm_wday) { 96459004Shm save = 6 - tm.tm_wday; 96559004Shm save += (ul + 1); 96659004Shm } else { 96759004Shm save = ul - tm.tm_wday; 96859004Shm } 96959004Shm 97059004Shm tm.tm_mday += save; 97159004Shm 97259004Shm if (tm.tm_mday > nd) { 97359004Shm tm.tm_mon++; 97459004Shm tm.tm_mday = tm.tm_mday - nd; 97559004Shm } 97659004Shm } 97759004Shm break; 97859004Shm 97959004Shm case 'M': 98059004Shm if (WMseen) 98159004Shm return -1; 98259004Shm WMseen++; 98359004Shm s++; 98459004Shm if (tolower(*s) == 'l') { 98559004Shm tm.tm_mday = nd; 98659004Shm s++; 98759004Shm t = s; 98859004Shm } else { 98959004Shm ul = strtoul(s, &t, 10); 99059004Shm if (ul < 1 || ul > 31) 99159004Shm return -1; 99259004Shm 99359004Shm if (ul > nd) 99459004Shm return -1; 99559004Shm tm.tm_mday = ul; 99659004Shm } 99759004Shm break; 99859004Shm 99959004Shm default: 100059004Shm return (-1); 100159004Shm break; 100259004Shm } 100359004Shm 100459004Shm if (*t == '\0' || isspace(*t)) 100559004Shm break; 100659004Shm else 100759004Shm s = t; 100859004Shm } 100959004Shm return mktime(&tm); 101059004Shm} 1011