newsyslog.c revision 111398
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 111398 2003-02-24 02:09:02Z gad $"; 2959004Shm#endif /* not lint */ 3013244Sgraichen 3143071Swollman#define OSF 3213244Sgraichen#ifndef COMPRESS_POSTFIX 3343071Swollman#define COMPRESS_POSTFIX ".gz" 3413244Sgraichen#endif 3580638Sobrien#ifndef BZCOMPRESS_POSTFIX 3680638Sobrien#define BZCOMPRESS_POSTFIX ".bz2" 3780638Sobrien#endif 3813244Sgraichen 3996001Smaxim#include <sys/param.h> 4096001Smaxim#include <sys/stat.h> 4196001Smaxim#include <sys/wait.h> 4296001Smaxim 4330160Scharnier#include <ctype.h> 4430160Scharnier#include <err.h> 4595999Smaxim#include <errno.h> 4630160Scharnier#include <fcntl.h> 47106905Ssobomax#include <glob.h> 4830160Scharnier#include <grp.h> 4943071Swollman#include <paths.h> 5030160Scharnier#include <pwd.h> 5130160Scharnier#include <signal.h> 5213244Sgraichen#include <stdio.h> 5313244Sgraichen#include <stdlib.h> 5413244Sgraichen#include <string.h> 5543071Swollman#include <time.h> 5616240Salex#include <unistd.h> 5713244Sgraichen 5843071Swollman#include "pathnames.h" 5943071Swollman 6013244Sgraichen#define kbytes(size) (((size) + 1023) >> 10) 6159004Shm 6213244Sgraichen#ifdef _IBMR2 6313244Sgraichen/* Calculates (db * DEV_BSIZE) */ 6459003Shm#define dbtob(db) ((unsigned)(db) << UBSHIFT) 6513244Sgraichen#endif 6613244Sgraichen 6759003Shm#define CE_COMPACT 1 /* Compact the achived log files */ 6859003Shm#define CE_BINARY 2 /* Logfile is in binary, don't add */ 6980638Sobrien#define CE_BZCOMPACT 8 /* Compact the achived log files with bzip2 */ 7059004Shm /* status messages */ 7143071Swollman#define CE_TRIMAT 4 /* trim at a specific time */ 72106905Ssobomax#define CE_GLOB 16 /* name of the log is file name pattern */ 73107916Ssobomax#define CE_COMPACTWAIT 32 /* wait till compressing finishes before */ 74107916Ssobomax /* starting the next one */ 7543071Swollman 7613244Sgraichen#define NONE -1 7759003Shm 7813244Sgraichenstruct conf_entry { 7959003Shm char *log; /* Name of the log */ 8059003Shm char *pid_file; /* PID file */ 8159003Shm int uid; /* Owner of log */ 8259003Shm int gid; /* Group of log */ 8359003Shm int numlogs; /* Number of logs to keep */ 8459003Shm int size; /* Size cutoff to trigger trimming the log */ 8559003Shm int hours; /* Hours between log trimming */ 8659003Shm time_t trim_at; /* Specific time to do trimming */ 8759003Shm int permissions; /* File permissions on the log */ 8880646Sobrien int flags; /* CE_COMPACT, CE_BZCOMPACT, CE_BINARY */ 8959003Shm int sig; /* Signal to send */ 90111388Sgad int def_cfg; /* Using the <default> rule for this file */ 9159003Shm struct conf_entry *next;/* Linked list pointer */ 9213244Sgraichen}; 9313244Sgraichen 94111388Sgad#define DEFAULT_MARKER "<default>" 95111388Sgad 9659004Shmint archtodir = 0; /* Archive old logfiles to other directory */ 9759003Shmint verbose = 0; /* Print out what's going on */ 9859003Shmint needroot = 1; /* Root privs are necessary */ 9959003Shmint noaction = 0; /* Don't do anything, just show it */ 10059003Shmint force = 0; /* Force the trim no matter what */ 10159004Shmchar *archdirname; /* Directory path to old logfiles archive */ 10280640Sobrienconst char *conf = _PATH_CONF; /* Configuration file to use */ 10359003Shmtime_t timenow; 10459003Shm 10525443Sache#define MIN_PID 5 10659003Shm#define MAX_PID 99999 /* was lower, see /usr/include/sys/proc.h */ 10771299Sjedgarchar hostname[MAXHOSTNAMELEN]; /* hostname */ 108108164Strhodeschar daytime[16]; /* timenow in human readable form */ 10913244Sgraichen 11060373Sdesstatic struct conf_entry *parse_file(char **files); 11116240Salexstatic char *sob(char *p); 11216240Salexstatic char *son(char *p); 11359003Shmstatic char *missing_field(char *p, char *errline); 11459003Shmstatic void do_entry(struct conf_entry * ent); 115111388Sgadstatic void free_entry(struct conf_entry *ent); 116111388Sgadstatic struct conf_entry *init_entry(const char *fname, 117111388Sgad struct conf_entry *src_entry); 11859003Shmstatic void PRS(int argc, char **argv); 11980640Sobrienstatic void usage(void); 12080646Sobrienstatic void dotrim(char *log, const char *pid_file, int numdays, int falgs, 121111388Sgad int perm, int owner_uid, int group_gid, int sig, int def_cfg); 122111388Sgadstatic int log_trim(char *log, int def_cfg); 123107916Ssobomaxstatic void compress_log(char *log, int dowait); 124107916Ssobomaxstatic void bzcompress_log(char *log, int dowait); 12516240Salexstatic int sizefile(char *file); 12616240Salexstatic int age_old_log(char *file); 12780640Sobrienstatic pid_t get_pid(const char *pid_file); 12893659Scjcstatic time_t parse8601(char *s, char *errline); 12980646Sobrienstatic void movefile(char *from, char *to, int perm, int owner_uid, 13080646Sobrien int group_gid); 13159004Shmstatic void createdir(char *dirpart); 13293659Scjcstatic time_t parseDWM(char *s, char *errline); 13313244Sgraichen 13459004Shmint 13559004Shmmain(int argc, char **argv) 13613244Sgraichen{ 13759003Shm struct conf_entry *p, *q; 138106905Ssobomax glob_t pglob; 139106905Ssobomax int i; 14025443Sache 14159003Shm PRS(argc, argv); 14259003Shm if (needroot && getuid() && geteuid()) 14359003Shm errx(1, "must have root privs"); 14460373Sdes p = q = parse_file(argv + optind); 14559003Shm 14659003Shm while (p) { 147106905Ssobomax if ((p->flags & CE_GLOB) == 0) { 148106905Ssobomax do_entry(p); 149106905Ssobomax } else { 150106905Ssobomax if (glob(p->log, GLOB_NOCHECK, NULL, &pglob) != 0) { 151106905Ssobomax warn("can't expand pattern: %s", p->log); 152106905Ssobomax } else { 153106905Ssobomax for (i = 0; i < pglob.gl_matchc; i++) { 154106905Ssobomax p->log = pglob.gl_pathv[i]; 155106905Ssobomax do_entry(p); 156106905Ssobomax } 157106905Ssobomax globfree(&pglob); 158106905Ssobomax } 159106905Ssobomax } 16059003Shm p = p->next; 161111388Sgad free_entry(q); 16259003Shm q = p; 16359003Shm } 16495999Smaxim while (wait(NULL) > 0 || errno == EINTR) 16595999Smaxim ; 16659003Shm return (0); 16713244Sgraichen} 16813244Sgraichen 169111388Sgadstatic struct conf_entry * 170111388Sgadinit_entry(const char *fname, struct conf_entry *src_entry) 171111388Sgad{ 172111388Sgad struct conf_entry *tempwork; 173111388Sgad 174111388Sgad if (verbose > 4) 175111388Sgad printf("\t--> [creating entry for %s]\n", fname); 176111388Sgad 177111388Sgad tempwork = malloc(sizeof(struct conf_entry)); 178111388Sgad if (tempwork == NULL) 179111388Sgad err(1, "malloc of conf_entry for %s", fname); 180111388Sgad 181111388Sgad tempwork->log = strdup(fname); 182111388Sgad if (tempwork->log == NULL) 183111388Sgad err(1, "strdup for %s", fname); 184111388Sgad 185111388Sgad if (src_entry != NULL) { 186111388Sgad tempwork->pid_file = NULL; 187111388Sgad if (src_entry->pid_file) 188111388Sgad tempwork->pid_file = strdup(src_entry->pid_file); 189111388Sgad tempwork->uid = src_entry->uid; 190111388Sgad tempwork->gid = src_entry->gid; 191111388Sgad tempwork->numlogs = src_entry->numlogs; 192111388Sgad tempwork->size = src_entry->size; 193111388Sgad tempwork->hours = src_entry->hours; 194111388Sgad tempwork->trim_at = src_entry->trim_at; 195111388Sgad tempwork->permissions = src_entry->permissions; 196111388Sgad tempwork->flags = src_entry->flags; 197111388Sgad tempwork->sig = src_entry->sig; 198111388Sgad tempwork->def_cfg = src_entry->def_cfg; 199111388Sgad } else { 200111388Sgad /* Initialize as a "do-nothing" entry */ 201111388Sgad tempwork->pid_file = NULL; 202111388Sgad tempwork->uid = NONE; 203111388Sgad tempwork->gid = NONE; 204111388Sgad tempwork->numlogs = 1; 205111388Sgad tempwork->size = -1; 206111388Sgad tempwork->hours = -1; 207111388Sgad tempwork->trim_at = (time_t)0; 208111388Sgad tempwork->permissions = 0; 209111388Sgad tempwork->flags = 0; 210111388Sgad tempwork->sig = SIGHUP; 211111388Sgad tempwork->def_cfg = 0; 212111388Sgad } 213111388Sgad tempwork->next = NULL; 214111388Sgad 215111388Sgad return (tempwork); 216111388Sgad} 217111388Sgad 21859004Shmstatic void 219111388Sgadfree_entry(struct conf_entry *ent) 220111388Sgad{ 221111388Sgad 222111388Sgad if (ent == NULL) 223111388Sgad return; 224111388Sgad 225111388Sgad if (ent->log != NULL) { 226111388Sgad if (verbose > 4) 227111388Sgad printf("\t--> [freeing entry for %s]\n", ent->log); 228111388Sgad free(ent->log); 229111388Sgad ent->log = NULL; 230111388Sgad } 231111388Sgad 232111388Sgad if (ent->pid_file != NULL) { 233111388Sgad free(ent->pid_file); 234111388Sgad ent->pid_file = NULL; 235111388Sgad } 236111388Sgad 237111388Sgad free(ent); 238111388Sgad} 239111388Sgad 240111388Sgadstatic void 24159004Shmdo_entry(struct conf_entry * ent) 24213244Sgraichen{ 24359003Shm int size, modtime; 24480640Sobrien const char *pid_file; 24559003Shm 24659003Shm if (verbose) { 24759003Shm if (ent->flags & CE_COMPACT) 24859003Shm printf("%s <%dZ>: ", ent->log, ent->numlogs); 24980638Sobrien else if (ent->flags & CE_BZCOMPACT) 25080638Sobrien printf("%s <%dJ>: ", ent->log, ent->numlogs); 25159003Shm else 25259003Shm printf("%s <%d>: ", ent->log, ent->numlogs); 25359003Shm } 25459003Shm size = sizefile(ent->log); 25559003Shm modtime = age_old_log(ent->log); 25659003Shm if (size < 0) { 25759003Shm if (verbose) 25859003Shm printf("does not exist.\n"); 25959003Shm } else { 26090240Sroam if (ent->flags & CE_TRIMAT && !force) { 26143071Swollman if (timenow < ent->trim_at 26259003Shm || difftime(timenow, ent->trim_at) >= 60 * 60) { 26343071Swollman if (verbose) 26443071Swollman printf("--> will trim at %s", 26559003Shm ctime(&ent->trim_at)); 26643071Swollman return; 26743071Swollman } else if (verbose && ent->hours <= 0) { 26843071Swollman printf("--> time is up\n"); 26943071Swollman } 27043071Swollman } 27159003Shm if (verbose && (ent->size > 0)) 27259003Shm printf("size (Kb): %d [%d] ", size, ent->size); 27359003Shm if (verbose && (ent->hours > 0)) 27459003Shm printf(" age (hr): %d [%d] ", modtime, ent->hours); 27559003Shm if (force || ((ent->size > 0) && (size >= ent->size)) || 27643071Swollman (ent->hours <= 0 && (ent->flags & CE_TRIMAT)) || 27759003Shm ((ent->hours > 0) && ((modtime >= ent->hours) 27859003Shm || (modtime < 0)))) { 27959003Shm if (verbose) 28059003Shm printf("--> trimming log....\n"); 28159003Shm if (noaction && !verbose) { 28259003Shm if (ent->flags & CE_COMPACT) 28359003Shm printf("%s <%dZ>: trimming\n", 28459003Shm ent->log, ent->numlogs); 28580638Sobrien else if (ent->flags & CE_BZCOMPACT) 28680638Sobrien printf("%s <%dJ>: trimming\n", 28780638Sobrien ent->log, ent->numlogs); 28859003Shm else 28959003Shm printf("%s <%d>: trimming\n", 29059003Shm ent->log, ent->numlogs); 29159003Shm } 29235920Shoek if (ent->pid_file) { 29335920Shoek pid_file = ent->pid_file; 29435920Shoek } else { 29535920Shoek /* Only try to notify syslog if we are root */ 29635920Shoek if (needroot) 29743071Swollman pid_file = _PATH_SYSLOGPID; 29835920Shoek else 29935920Shoek pid_file = NULL; 30035920Shoek } 30159003Shm dotrim(ent->log, pid_file, ent->numlogs, 30280646Sobrien ent->flags, ent->permissions, ent->uid, ent->gid, 303111388Sgad ent->sig, ent->def_cfg); 30459003Shm } else { 30559003Shm if (verbose) 30659003Shm printf("--> skipping\n"); 30759003Shm } 30859003Shm } 30913244Sgraichen} 31013244Sgraichen 31159004Shmstatic void 31259004ShmPRS(int argc, char **argv) 31313244Sgraichen{ 31459003Shm int c; 31559003Shm char *p; 31613244Sgraichen 31759003Shm timenow = time((time_t *) 0); 318108164Strhodes (void)strncpy(daytime, ctime(&timenow) + 4, 15); 31959003Shm daytime[15] = '\0'; 32013244Sgraichen 32159003Shm /* Let's get our hostname */ 32259003Shm (void) gethostname(hostname, sizeof(hostname)); 32313244Sgraichen 32413244Sgraichen /* Truncate domain */ 32516240Salex if ((p = strchr(hostname, '.'))) { 32613244Sgraichen *p = '\0'; 32713244Sgraichen } 328105248Smaxim while ((c = getopt(argc, argv, "nrvFf:a:")) != -1) 32959003Shm switch (c) { 33059003Shm case 'n': 33159003Shm noaction++; 33235920Shoek break; 33359004Shm case 'a': 33459004Shm archtodir++; 33559004Shm archdirname = optarg; 33659004Shm break; 33759003Shm case 'r': 33859003Shm needroot = 0; 33959003Shm break; 34059003Shm case 'v': 34159003Shm verbose++; 34259003Shm break; 34359003Shm case 'f': 34459003Shm conf = optarg; 34559003Shm break; 34634584Spst case 'F': 34734584Spst force++; 34834584Spst break; 34959003Shm default: 35059003Shm usage(); 35159003Shm } 35243071Swollman} 35313244Sgraichen 35459004Shmstatic void 35559004Shmusage(void) 35613244Sgraichen{ 35780646Sobrien 35880646Sobrien fprintf(stderr, 359111388Sgad "usage: newsyslog [-Fnrv] [-f config-file] [-a directory] [ filename ... ]\n"); 36059003Shm exit(1); 36113244Sgraichen} 36213244Sgraichen 36359004Shm/* 36459004Shm * Parse a configuration file and return a linked list of all the logs to 36559004Shm * process 36613244Sgraichen */ 36759003Shmstatic struct conf_entry * 36860373Sdesparse_file(char **files) 36913244Sgraichen{ 37059003Shm FILE *f; 37159003Shm char line[BUFSIZ], *parse, *q; 372107737Ssobomax char *cp, *errline, *group; 373111388Sgad char **given; 374111388Sgad struct conf_entry *defconf, *first, *working, *worklist; 37559003Shm struct passwd *pass; 37659003Shm struct group *grp; 37725518Sbrian int eol; 37813244Sgraichen 379111388Sgad defconf = first = working = worklist = NULL; 38080646Sobrien 38159003Shm if (strcmp(conf, "-")) 38259003Shm f = fopen(conf, "r"); 38359003Shm else 38459003Shm f = stdin; 38559003Shm if (!f) 38659003Shm err(1, "%s", conf); 38759003Shm while (fgets(line, BUFSIZ, f)) { 388107737Ssobomax if ((line[0] == '\n') || (line[0] == '#') || 389107737Ssobomax (strlen(line) == 0)) 39059003Shm continue; 39159003Shm errline = strdup(line); 392107737Ssobomax for (cp = line + 1; *cp != '\0'; cp++) { 393107737Ssobomax if (*cp != '#') 394107737Ssobomax continue; 395107737Ssobomax if (*(cp - 1) == '\\') { 396107737Ssobomax strcpy(cp - 1, cp); 397107737Ssobomax cp--; 398107737Ssobomax continue; 399107737Ssobomax } 400107737Ssobomax *cp = '\0'; 401107737Ssobomax break; 402107737Ssobomax } 40360373Sdes 40460373Sdes q = parse = missing_field(sob(line), errline); 40560373Sdes parse = son(line); 40660373Sdes if (!*parse) 40780646Sobrien errx(1, "malformed line (missing fields):\n%s", 40880646Sobrien errline); 40960373Sdes *parse = '\0'; 41060373Sdes 411111388Sgad /* 412111388Sgad * If newsyslog was run with a list of specific filenames, 413111388Sgad * then this line of the config file should be skipped if 414111388Sgad * it is NOT one of those given files (except that we do 415111388Sgad * want any line that defines the <default> action). 416111388Sgad * 417111388Sgad * XXX - note that CE_GLOB processing is *NOT* done when 418111388Sgad * trying to match a filename given on the command! 419111388Sgad */ 42060373Sdes if (*files) { 421111388Sgad if (strcasecmp(DEFAULT_MARKER, q) != 0) { 422111388Sgad for (given = files; *given; ++given) { 423111388Sgad if (strcmp(*given, q) == 0) 424111388Sgad break; 425111388Sgad } 426111388Sgad if (!*given) 427111388Sgad continue; 428111388Sgad } 429111388Sgad if (verbose > 2) 430111388Sgad printf("\t+ Matched entry %s\n", q); 431111388Sgad } else { 432111388Sgad /* 433111388Sgad * If no files were specified on the command line, 434111388Sgad * then we can skip any line which defines the 435111388Sgad * default action. 436111388Sgad */ 437111388Sgad if (strcasecmp(DEFAULT_MARKER, q) == 0) { 438111388Sgad if (verbose > 2) 439111388Sgad printf("\t+ Ignoring entry for %s\n", 440111388Sgad q); 44160373Sdes continue; 442111388Sgad } 44360373Sdes } 44460373Sdes 445111388Sgad working = init_entry(q, NULL); 446111388Sgad if (strcasecmp(DEFAULT_MARKER, q) == 0) { 447111388Sgad if (defconf != NULL) { 448111388Sgad warnx("Ignoring duplicate entry for %s!", q); 449111388Sgad free_entry(working); 450111388Sgad continue; 451111388Sgad } 452111388Sgad defconf = working; 45359003Shm } else { 454111388Sgad if (!first) 455111388Sgad first = working; 456111388Sgad else 457111388Sgad worklist->next = working; 458111388Sgad worklist = working; 45959003Shm } 46013244Sgraichen 46159003Shm q = parse = missing_field(sob(++parse), errline); 46259003Shm parse = son(parse); 46325518Sbrian if (!*parse) 46480646Sobrien errx(1, "malformed line (missing fields):\n%s", 46580646Sobrien errline); 46659003Shm *parse = '\0'; 46759003Shm if ((group = strchr(q, ':')) != NULL || 46859003Shm (group = strrchr(q, '.')) != NULL) { 46959003Shm *group++ = '\0'; 47059003Shm if (*q) { 47159003Shm if (!(isnumber(*q))) { 47259003Shm if ((pass = getpwnam(q)) == NULL) 47359003Shm errx(1, 47480646Sobrien "error in config file; unknown user:\n%s", 47559003Shm errline); 47659003Shm working->uid = pass->pw_uid; 47759003Shm } else 47859003Shm working->uid = atoi(q); 47959003Shm } else 48059003Shm working->uid = NONE; 48113244Sgraichen 48259003Shm q = group; 48359003Shm if (*q) { 48459003Shm if (!(isnumber(*q))) { 48559003Shm if ((grp = getgrnam(q)) == NULL) 48659003Shm errx(1, 48780646Sobrien "error in config file; unknown group:\n%s", 48859003Shm errline); 48959003Shm working->gid = grp->gr_gid; 49059003Shm } else 49159003Shm working->gid = atoi(q); 49259003Shm } else 49359003Shm working->gid = NONE; 49413244Sgraichen 49559003Shm q = parse = missing_field(sob(++parse), errline); 49659003Shm parse = son(parse); 49759003Shm if (!*parse) 49880646Sobrien errx(1, "malformed line (missing fields):\n%s", 49980646Sobrien errline); 50059003Shm *parse = '\0'; 50159003Shm } else 50259003Shm working->uid = working->gid = NONE; 50359003Shm 50459003Shm if (!sscanf(q, "%o", &working->permissions)) 50559003Shm errx(1, "error in config file; bad permissions:\n%s", 50659003Shm errline); 50759003Shm 50859003Shm q = parse = missing_field(sob(++parse), errline); 50959003Shm parse = son(parse); 51025518Sbrian if (!*parse) 51180646Sobrien errx(1, "malformed line (missing fields):\n%s", 51280646Sobrien errline); 51359003Shm *parse = '\0'; 51459003Shm if (!sscanf(q, "%d", &working->numlogs)) 51559003Shm errx(1, "error in config file; bad number:\n%s", 51659003Shm errline); 51713244Sgraichen 51859003Shm q = parse = missing_field(sob(++parse), errline); 51959003Shm parse = son(parse); 52025518Sbrian if (!*parse) 52180646Sobrien errx(1, "malformed line (missing fields):\n%s", 52280646Sobrien errline); 52359003Shm *parse = '\0'; 52459003Shm if (isdigit(*q)) 52559003Shm working->size = atoi(q); 52659003Shm else 52759003Shm working->size = -1; 52859003Shm 52959003Shm working->flags = 0; 53059003Shm q = parse = missing_field(sob(++parse), errline); 53159003Shm parse = son(parse); 53225518Sbrian eol = !*parse; 53359003Shm *parse = '\0'; 53443071Swollman { 53559003Shm char *ep; 53659003Shm u_long ul; 53713244Sgraichen 53843071Swollman ul = strtoul(q, &ep, 10); 53943071Swollman if (ep == q) 54043071Swollman working->hours = 0; 54143071Swollman else if (*ep == '*') 54243071Swollman working->hours = -1; 54343071Swollman else if (ul > INT_MAX) 54443071Swollman errx(1, "interval is too large:\n%s", errline); 54543071Swollman else 54643071Swollman working->hours = ul; 54743071Swollman 54880646Sobrien if (*ep != '\0' && *ep != '@' && *ep != '*' && 54980646Sobrien *ep != '$') 55043071Swollman errx(1, "malformed interval/at:\n%s", errline); 55143071Swollman if (*ep == '@') { 55293659Scjc if ((working->trim_at = parse8601(ep + 1, errline)) 55359003Shm == (time_t) - 1) 55443071Swollman errx(1, "malformed at:\n%s", errline); 55543071Swollman working->flags |= CE_TRIMAT; 55659004Shm } else if (*ep == '$') { 55793659Scjc if ((working->trim_at = parseDWM(ep + 1, errline)) 55859004Shm == (time_t) - 1) 55959004Shm errx(1, "malformed at:\n%s", errline); 56059004Shm working->flags |= CE_TRIMAT; 56143071Swollman } 56243071Swollman } 56343071Swollman 56425518Sbrian if (eol) 56559003Shm q = NULL; 56625518Sbrian else { 56759003Shm q = parse = sob(++parse); /* Optional field */ 56859003Shm parse = son(parse); 56959003Shm if (!*parse) 57059003Shm eol = 1; 57159003Shm *parse = '\0'; 57225518Sbrian } 57325443Sache 57459003Shm while (q && *q && !isspace(*q)) { 57559003Shm if ((*q == 'Z') || (*q == 'z')) 57659003Shm working->flags |= CE_COMPACT; 57780638Sobrien else if ((*q == 'J') || (*q == 'j')) 57880638Sobrien working->flags |= CE_BZCOMPACT; 57959003Shm else if ((*q == 'B') || (*q == 'b')) 58059003Shm working->flags |= CE_BINARY; 581106905Ssobomax else if ((*q == 'G') || (*q == 'c')) 582106905Ssobomax working->flags |= CE_GLOB; 583107916Ssobomax else if ((*q == 'W') || (*q == 'w')) 584107916Ssobomax working->flags |= CE_COMPACTWAIT; 58559003Shm else if (*q != '-') 58680646Sobrien errx(1, "illegal flag in config file -- %c", 58780646Sobrien *q); 58859003Shm q++; 58959003Shm } 59059003Shm 59125518Sbrian if (eol) 59259003Shm q = NULL; 59325518Sbrian else { 59459003Shm q = parse = sob(++parse); /* Optional field */ 59559003Shm parse = son(parse); 59659003Shm if (!*parse) 59759003Shm eol = 1; 59859003Shm *parse = '\0'; 59925518Sbrian } 60025443Sache 60125443Sache working->pid_file = NULL; 60225443Sache if (q && *q) { 60325443Sache if (*q == '/') 60425443Sache working->pid_file = strdup(q); 60536817Sache else if (isdigit(*q)) 60636817Sache goto got_sig; 60759003Shm else 60880646Sobrien errx(1, 60980646Sobrien "illegal pid file or signal number in config file:\n%s", 61080646Sobrien errline); 61125443Sache } 61236817Sache if (eol) 61359003Shm q = NULL; 61436817Sache else { 61559003Shm q = parse = sob(++parse); /* Optional field */ 61659003Shm *(parse = son(parse)) = '\0'; 61736817Sache } 61836817Sache 61936817Sache working->sig = SIGHUP; 62036817Sache if (q && *q) { 62136817Sache if (isdigit(*q)) { 62259003Shm got_sig: 62336817Sache working->sig = atoi(q); 62436817Sache } else { 62559003Shm err_sig: 62680646Sobrien errx(1, 62780646Sobrien "illegal signal number in config file:\n%s", 62880646Sobrien errline); 62936817Sache } 63036817Sache if (working->sig < 1 || working->sig >= NSIG) 63136817Sache goto err_sig; 63236817Sache } 63359003Shm free(errline); 63459003Shm } 63559003Shm (void) fclose(f); 636111388Sgad 637111388Sgad /* 638111388Sgad * The entire config file has been processed. If there were 639111388Sgad * no specific files given on the run command, then the work 640111388Sgad * of this routine is done. 641111388Sgad */ 642111388Sgad if (*files == NULL) 643111388Sgad return (first); 644111388Sgad 645111388Sgad /* 646111388Sgad * If the program was given a specific list of files to process, 647111388Sgad * it may be that some of those files were not listed in the 648111388Sgad * config file. Those unlisted files should get the default 649111388Sgad * rotation action. First, create the default-rotation action 650111388Sgad * if none was found in the config file. 651111388Sgad */ 652111388Sgad if (defconf == NULL) { 653111388Sgad working = init_entry(DEFAULT_MARKER, NULL); 654111388Sgad working->numlogs = 3; 655111388Sgad working->size = 50; 656111388Sgad working->permissions = S_IRUSR|S_IWUSR; 657111388Sgad defconf = working; 658111388Sgad } 659111388Sgad 660111388Sgad for (given = files; *given; ++given) { 661111388Sgad for (working = first; working; working = working->next) { 662111388Sgad if (strcmp(*given, working->log) == 0) 663111388Sgad break; 664111388Sgad } 665111388Sgad if (working != NULL) 666111388Sgad continue; 667111388Sgad if (verbose > 2) 668111388Sgad printf("\t+ No entry for %s (will use %s)\n", 669111388Sgad *given, DEFAULT_MARKER); 670111388Sgad /* 671111388Sgad * This given file was not found in the config file. 672111388Sgad * Add another item on to our work list, based on the 673111388Sgad * default entry. 674111388Sgad */ 675111388Sgad working = init_entry(*given, defconf); 676111388Sgad if (!first) 677111388Sgad first = working; 678111388Sgad else 679111388Sgad worklist->next = working; 680111388Sgad /* This is a file that was *not* found in config file */ 681111388Sgad working->def_cfg = 1; 682111388Sgad worklist = working; 683111388Sgad } 684111388Sgad 685111388Sgad free_entry(defconf); 68659003Shm return (first); 68713244Sgraichen} 68813244Sgraichen 68959003Shmstatic char * 69059004Shmmissing_field(char *p, char *errline) 69113244Sgraichen{ 69280646Sobrien 69359003Shm if (!p || !*p) 69459003Shm errx(1, "missing field in config file:\n%s", errline); 69559003Shm return (p); 69613244Sgraichen} 69713244Sgraichen 69859004Shmstatic void 69980640Sobriendotrim(char *log, const char *pid_file, int numdays, int flags, int perm, 700111388Sgad int owner_uid, int group_gid, int sig, int def_cfg) 70113244Sgraichen{ 70271299Sjedgar char dirpart[MAXPATHLEN], namepart[MAXPATHLEN]; 70371299Sjedgar char file1[MAXPATHLEN], file2[MAXPATHLEN]; 70471299Sjedgar char zfile1[MAXPATHLEN], zfile2[MAXPATHLEN]; 70580638Sobrien char jfile1[MAXPATHLEN]; 70694352Ssheldonh char tfile[MAXPATHLEN]; 70759003Shm int notified, need_notification, fd, _numdays; 70859003Shm struct stat st; 70959003Shm pid_t pid; 71013244Sgraichen 71113244Sgraichen#ifdef _IBMR2 71259004Shm /* 71359004Shm * AIX 3.1 has a broken fchown- if the owner_uid is -1, it will 71459004Shm * actually change it to be owned by uid -1, instead of leaving it 71559004Shm * as is, as it is supposed to. 71659004Shm */ 71759003Shm if (owner_uid == -1) 71859003Shm owner_uid = geteuid(); 71913244Sgraichen#endif 72013244Sgraichen 72159004Shm if (archtodir) { 72259004Shm char *p; 72313244Sgraichen 72459004Shm /* build complete name of archive directory into dirpart */ 72559004Shm if (*archdirname == '/') { /* absolute */ 72671299Sjedgar strlcpy(dirpart, archdirname, sizeof(dirpart)); 72759004Shm } else { /* relative */ 72859004Shm /* get directory part of logfile */ 72971299Sjedgar strlcpy(dirpart, log, sizeof(dirpart)); 73059004Shm if ((p = rindex(dirpart, '/')) == NULL) 73159004Shm dirpart[0] = '\0'; 73259004Shm else 73359004Shm *(p + 1) = '\0'; 73471299Sjedgar strlcat(dirpart, archdirname, sizeof(dirpart)); 73559004Shm } 73659004Shm 73759004Shm /* check if archive directory exists, if not, create it */ 73859004Shm if (lstat(dirpart, &st)) 73959004Shm createdir(dirpart); 74059004Shm 74159004Shm /* get filename part of logfile */ 74259004Shm if ((p = rindex(log, '/')) == NULL) 74371299Sjedgar strlcpy(namepart, log, sizeof(namepart)); 74459004Shm else 74571299Sjedgar strlcpy(namepart, p + 1, sizeof(namepart)); 74659004Shm 74759004Shm /* name of oldest log */ 74880646Sobrien (void) snprintf(file1, sizeof(file1), "%s/%s.%d", dirpart, 74980646Sobrien namepart, numdays); 75071299Sjedgar (void) snprintf(zfile1, sizeof(zfile1), "%s%s", file1, 75171299Sjedgar COMPRESS_POSTFIX); 75280638Sobrien snprintf(jfile1, sizeof(jfile1), "%s%s", file1, 75380638Sobrien BZCOMPRESS_POSTFIX); 75459004Shm } else { 75559004Shm /* name of oldest log */ 75671299Sjedgar (void) snprintf(file1, sizeof(file1), "%s.%d", log, numdays); 75771299Sjedgar (void) snprintf(zfile1, sizeof(zfile1), "%s%s", file1, 75871299Sjedgar COMPRESS_POSTFIX); 75980638Sobrien snprintf(jfile1, sizeof(jfile1), "%s%s", file1, 76080638Sobrien BZCOMPRESS_POSTFIX); 76159004Shm } 76259004Shm 76359003Shm if (noaction) { 76459003Shm printf("rm -f %s\n", file1); 76559003Shm printf("rm -f %s\n", zfile1); 76680638Sobrien printf("rm -f %s\n", jfile1); 76759003Shm } else { 76859003Shm (void) unlink(file1); 76959003Shm (void) unlink(zfile1); 77080638Sobrien (void) unlink(jfile1); 77159003Shm } 77213244Sgraichen 77359003Shm /* Move down log files */ 77418188Sjkh _numdays = numdays; /* preserve */ 77559003Shm while (numdays--) { 77659004Shm 77771299Sjedgar (void) strlcpy(file2, file1, sizeof(file2)); 77859004Shm 77959004Shm if (archtodir) 78080646Sobrien (void) snprintf(file1, sizeof(file1), "%s/%s.%d", 78180646Sobrien dirpart, namepart, numdays); 78259004Shm else 78380646Sobrien (void) snprintf(file1, sizeof(file1), "%s.%d", log, 78480646Sobrien numdays); 78559004Shm 78671299Sjedgar (void) strlcpy(zfile1, file1, sizeof(zfile1)); 78771299Sjedgar (void) strlcpy(zfile2, file2, sizeof(zfile2)); 78859003Shm if (lstat(file1, &st)) { 78980646Sobrien (void) strlcat(zfile1, COMPRESS_POSTFIX, 79080646Sobrien sizeof(zfile1)); 79180646Sobrien (void) strlcat(zfile2, COMPRESS_POSTFIX, 79280646Sobrien sizeof(zfile2)); 79380638Sobrien if (lstat(zfile1, &st)) { 79480638Sobrien strlcpy(zfile1, file1, sizeof(zfile1)); 79580638Sobrien strlcpy(zfile2, file2, sizeof(zfile2)); 79680638Sobrien strlcat(zfile1, BZCOMPRESS_POSTFIX, 79780638Sobrien sizeof(zfile1)); 79880638Sobrien strlcat(zfile2, BZCOMPRESS_POSTFIX, 79980638Sobrien sizeof(zfile2)); 80080638Sobrien if (lstat(zfile1, &st)) 80180638Sobrien continue; 80280638Sobrien } 80359003Shm } 80459003Shm if (noaction) { 80559003Shm printf("mv %s %s\n", zfile1, zfile2); 80659003Shm printf("chmod %o %s\n", perm, zfile2); 80780684Sobrien printf("chown %d:%d %s\n", 80859003Shm owner_uid, group_gid, zfile2); 80959003Shm } else { 81059003Shm (void) rename(zfile1, zfile2); 81159003Shm (void) chmod(zfile2, perm); 81259003Shm (void) chown(zfile2, owner_uid, group_gid); 81359003Shm } 81459003Shm } 815111388Sgad if (!noaction && !(flags & CE_BINARY)) { 816111388Sgad /* Report the trimming to the old log */ 817111388Sgad (void) log_trim(log, def_cfg); 818111388Sgad } 81913244Sgraichen 82018188Sjkh if (!_numdays) { 82118075Sjkh if (noaction) 82259003Shm printf("rm %s\n", log); 82318075Sjkh else 82459003Shm (void) unlink(log); 82559003Shm } else { 82618075Sjkh if (noaction) 82759003Shm printf("mv %s to %s\n", log, file1); 82859004Shm else { 82959004Shm if (archtodir) 83080646Sobrien movefile(log, file1, perm, owner_uid, 83180646Sobrien group_gid); 83259004Shm else 83359004Shm (void) rename(log, file1); 83459004Shm } 83518075Sjkh } 83618075Sjkh 83759003Shm if (noaction) 83859003Shm printf("Start new log..."); 83959003Shm else { 84094352Ssheldonh strlcpy(tfile, log, sizeof(tfile)); 84194352Ssheldonh strlcat(tfile, ".XXXXXX", sizeof(tfile)); 84294352Ssheldonh mkstemp(tfile); 84394352Ssheldonh fd = creat(tfile, perm); 84459003Shm if (fd < 0) 84559003Shm err(1, "can't start new log"); 84659003Shm if (fchown(fd, owner_uid, group_gid)) 84759003Shm err(1, "can't chmod new log file"); 84859003Shm (void) close(fd); 849111388Sgad if (!(flags & CE_BINARY)) { 850111388Sgad /* Add status message to new log file */ 851111388Sgad if (log_trim(tfile, def_cfg)) 85259003Shm err(1, "can't add status message to log"); 853111388Sgad } 85459003Shm } 85559003Shm if (noaction) 85659003Shm printf("chmod %o %s...\n", perm, log); 85794352Ssheldonh else { 85894352Ssheldonh (void) chmod(tfile, perm); 85994352Ssheldonh if (rename(tfile, log) < 0) { 86094352Ssheldonh err(1, "can't start new log"); 86194352Ssheldonh (void) unlink(tfile); 86294352Ssheldonh } 86394352Ssheldonh } 86425443Sache 86525443Sache pid = 0; 86625443Sache need_notification = notified = 0; 86725443Sache if (pid_file != NULL) { 86825443Sache need_notification = 1; 86925443Sache pid = get_pid(pid_file); 87025443Sache } 87125443Sache if (pid) { 87225443Sache if (noaction) { 87325443Sache notified = 1; 87459003Shm printf("kill -%d %d\n", sig, (int) pid); 87559003Shm } else if (kill(pid, sig)) 87659003Shm warn("can't notify daemon, pid %d", (int) pid); 87725443Sache else { 87825443Sache notified = 1; 87925443Sache if (verbose) 88059003Shm printf("daemon pid %d notified\n", (int) pid); 88125443Sache } 88225443Sache } 88380638Sobrien if ((flags & CE_COMPACT) || (flags & CE_BZCOMPACT)) { 88425443Sache if (need_notification && !notified) 88580646Sobrien warnx( 88680646Sobrien "log %s not compressed because daemon not notified", 88780646Sobrien log); 88825443Sache else if (noaction) 88959003Shm printf("Compress %s.0\n", log); 89025443Sache else { 89125443Sache if (notified) { 89225443Sache if (verbose) 89325443Sache printf("small pause to allow daemon to close log\n"); 89431460Sache sleep(10); 89525443Sache } 89659004Shm if (archtodir) { 89780646Sobrien (void) snprintf(file1, sizeof(file1), "%s/%s", 89880646Sobrien dirpart, namepart); 89980638Sobrien if (flags & CE_COMPACT) 900107916Ssobomax compress_log(file1, 901107916Ssobomax flags & CE_COMPACTWAIT); 90280638Sobrien else if (flags & CE_BZCOMPACT) 903107916Ssobomax bzcompress_log(file1, 904107916Ssobomax flags & CE_COMPACTWAIT); 90559004Shm } else { 90680638Sobrien if (flags & CE_COMPACT) 907107916Ssobomax compress_log(log, 908107916Ssobomax flags & CE_COMPACTWAIT); 90980638Sobrien else if (flags & CE_BZCOMPACT) 910107916Ssobomax bzcompress_log(log, 911107916Ssobomax flags & CE_COMPACTWAIT); 91259004Shm } 91325443Sache } 91459003Shm } 91513244Sgraichen} 91613244Sgraichen 91713244Sgraichen/* Log the fact that the logs were turned over */ 91859004Shmstatic int 919111388Sgadlog_trim(char *log, int def_cfg) 92013244Sgraichen{ 92159003Shm FILE *f; 922111388Sgad const char *xtra; 92359003Shm 92459003Shm if ((f = fopen(log, "a")) == NULL) 92559003Shm return (-1); 926111388Sgad xtra = ""; 927111388Sgad if (def_cfg) 928111388Sgad xtra = " using <default> rule"; 929111388Sgad fprintf(f, "%s %s newsyslog[%d]: logfile turned over%s\n", 930111388Sgad daytime, hostname, (int) getpid(), xtra); 93159003Shm if (fclose(f) == EOF) 93259003Shm err(1, "log_trim: fclose:"); 93359003Shm return (0); 93413244Sgraichen} 93513244Sgraichen 93659004Shm/* Fork of gzip to compress the old log file */ 93759004Shmstatic void 938107916Ssobomaxcompress_log(char *log, int dowait) 93913244Sgraichen{ 94059003Shm pid_t pid; 94171299Sjedgar char tmp[MAXPATHLEN]; 94259003Shm 943107916Ssobomax while (dowait && (wait(NULL) > 0 || errno == EINTR)) 944107916Ssobomax ; 94571299Sjedgar (void) snprintf(tmp, sizeof(tmp), "%s.0", log); 94625443Sache pid = fork(); 94759003Shm if (pid < 0) 94880638Sobrien err(1, "gzip fork"); 94959003Shm else if (!pid) { 95079452Sbrian (void) execl(_PATH_GZIP, _PATH_GZIP, "-f", tmp, (char *)0); 95143071Swollman err(1, _PATH_GZIP); 95259003Shm } 95313244Sgraichen} 95413244Sgraichen 95580638Sobrien/* Fork of bzip2 to compress the old log file */ 95680638Sobrienstatic void 957107916Ssobomaxbzcompress_log(char *log, int dowait) 95880638Sobrien{ 95980638Sobrien pid_t pid; 96080638Sobrien char tmp[MAXPATHLEN]; 96180638Sobrien 962107916Ssobomax while (dowait && (wait(NULL) > 0 || errno == EINTR)) 963107916Ssobomax ; 96480638Sobrien snprintf(tmp, sizeof(tmp), "%s.0", log); 96580638Sobrien pid = fork(); 96680638Sobrien if (pid < 0) 96780638Sobrien err(1, "bzip2 fork"); 96880638Sobrien else if (!pid) { 96986360Sobrien execl(_PATH_BZIP2, _PATH_BZIP2, "-f", tmp, (char *)0); 97080638Sobrien err(1, _PATH_BZIP2); 97180638Sobrien } 97280638Sobrien} 97380638Sobrien 97413244Sgraichen/* Return size in kilobytes of a file */ 97559004Shmstatic int 97659004Shmsizefile(char *file) 97713244Sgraichen{ 97859003Shm struct stat sb; 97913244Sgraichen 98059003Shm if (stat(file, &sb) < 0) 98159003Shm return (-1); 98259003Shm return (kbytes(dbtob(sb.st_blocks))); 98313244Sgraichen} 98413244Sgraichen 98513244Sgraichen/* Return the age of old log file (file.0) */ 98659004Shmstatic int 98759004Shmage_old_log(char *file) 98813244Sgraichen{ 98959003Shm struct stat sb; 99059003Shm char tmp[MAXPATHLEN + sizeof(".0") + sizeof(COMPRESS_POSTFIX) + 1]; 99113244Sgraichen 99259004Shm if (archtodir) { 99359004Shm char *p; 99459004Shm 99559004Shm /* build name of archive directory into tmp */ 99659004Shm if (*archdirname == '/') { /* absolute */ 99771299Sjedgar strlcpy(tmp, archdirname, sizeof(tmp)); 99859004Shm } else { /* relative */ 99959004Shm /* get directory part of logfile */ 100071299Sjedgar strlcpy(tmp, file, sizeof(tmp)); 100159004Shm if ((p = rindex(tmp, '/')) == NULL) 100259004Shm tmp[0] = '\0'; 100359004Shm else 100459004Shm *(p + 1) = '\0'; 100571299Sjedgar strlcat(tmp, archdirname, sizeof(tmp)); 100659004Shm } 100759004Shm 100871299Sjedgar strlcat(tmp, "/", sizeof(tmp)); 100959004Shm 101059004Shm /* get filename part of logfile */ 101159004Shm if ((p = rindex(file, '/')) == NULL) 101271299Sjedgar strlcat(tmp, file, sizeof(tmp)); 101359004Shm else 101471299Sjedgar strlcat(tmp, p + 1, sizeof(tmp)); 101559004Shm } else { 101671299Sjedgar (void) strlcpy(tmp, file, sizeof(tmp)); 101759004Shm } 101859004Shm 101959003Shm if (stat(strcat(tmp, ".0"), &sb) < 0) 102059003Shm if (stat(strcat(tmp, COMPRESS_POSTFIX), &sb) < 0) 102159003Shm return (-1); 102259003Shm return ((int) (timenow - sb.st_mtime + 1800) / 3600); 102313244Sgraichen} 102413244Sgraichen 102559004Shmstatic pid_t 102680640Sobrienget_pid(const char *pid_file) 102725443Sache{ 102825443Sache FILE *f; 102959003Shm char line[BUFSIZ]; 103025443Sache pid_t pid = 0; 103113244Sgraichen 103259003Shm if ((f = fopen(pid_file, "r")) == NULL) 103325443Sache warn("can't open %s pid file to restart a daemon", 103459003Shm pid_file); 103525443Sache else { 103659003Shm if (fgets(line, BUFSIZ, f)) { 103725443Sache pid = atol(line); 103825443Sache if (pid < MIN_PID || pid > MAX_PID) { 103980646Sobrien warnx("preposterous process number: %d", 104080646Sobrien (int)pid); 104125443Sache pid = 0; 104225443Sache } 104325443Sache } else 104425443Sache warn("can't read %s pid file to restart a daemon", 104559003Shm pid_file); 104659003Shm (void) fclose(f); 104725443Sache } 1048111392Sgad return (pid); 104925443Sache} 105025443Sache 105113244Sgraichen/* Skip Over Blanks */ 105259003Shmchar * 105359004Shmsob(char *p) 105413244Sgraichen{ 105559003Shm while (p && *p && isspace(*p)) 105659003Shm p++; 105759003Shm return (p); 105813244Sgraichen} 105913244Sgraichen 106013244Sgraichen/* Skip Over Non-Blanks */ 106159003Shmchar * 106259004Shmson(char *p) 106313244Sgraichen{ 106459003Shm while (p && *p && !isspace(*p)) 106559003Shm p++; 106659003Shm return (p); 106713244Sgraichen} 106843071Swollman 106943071Swollman/* 107059004Shm * Parse a limited subset of ISO 8601. The specific format is as follows: 107143071Swollman * 107259004Shm * [CC[YY[MM[DD]]]][THH[MM[SS]]] (where `T' is the literal letter) 107343071Swollman * 107459004Shm * We don't accept a timezone specification; missing fields (including timezone) 107559004Shm * are defaulted to the current date but time zero. 107643071Swollman */ 107743071Swollmanstatic time_t 107893659Scjcparse8601(char *s, char *errline) 107943071Swollman{ 108059003Shm char *t; 108193659Scjc time_t tsecs; 108259003Shm struct tm tm, *tmp; 108359003Shm u_long ul; 108443071Swollman 108543071Swollman tmp = localtime(&timenow); 108643071Swollman tm = *tmp; 108743071Swollman 108843071Swollman tm.tm_hour = tm.tm_min = tm.tm_sec = 0; 108943071Swollman 109043071Swollman ul = strtoul(s, &t, 10); 109143071Swollman if (*t != '\0' && *t != 'T') 1092111392Sgad return (-1); 109343071Swollman 109443071Swollman /* 109559003Shm * Now t points either to the end of the string (if no time was 109659003Shm * provided) or to the letter `T' which separates date and time in 109759003Shm * ISO 8601. The pointer arithmetic is the same for either case. 109843071Swollman */ 109943071Swollman switch (t - s) { 110043071Swollman case 8: 110143071Swollman tm.tm_year = ((ul / 1000000) - 19) * 100; 110243071Swollman ul = ul % 1000000; 110343071Swollman case 6: 110480666Swollman tm.tm_year -= tm.tm_year % 100; 110543071Swollman tm.tm_year += ul / 10000; 110643071Swollman ul = ul % 10000; 110743071Swollman case 4: 110843071Swollman tm.tm_mon = (ul / 100) - 1; 110943071Swollman ul = ul % 100; 111043071Swollman case 2: 111143071Swollman tm.tm_mday = ul; 111243071Swollman case 0: 111343071Swollman break; 111443071Swollman default: 1115111392Sgad return (-1); 111643071Swollman } 111743071Swollman 111843071Swollman /* sanity check */ 111943071Swollman if (tm.tm_year < 70 || tm.tm_mon < 0 || tm.tm_mon > 12 112043071Swollman || tm.tm_mday < 1 || tm.tm_mday > 31) 1121111392Sgad return (-1); 112243071Swollman 112343071Swollman if (*t != '\0') { 112443071Swollman s = ++t; 112543071Swollman ul = strtoul(s, &t, 10); 112643071Swollman if (*t != '\0' && !isspace(*t)) 1127111392Sgad return (-1); 112843071Swollman 112943071Swollman switch (t - s) { 113043071Swollman case 6: 113143071Swollman tm.tm_sec = ul % 100; 113243071Swollman ul /= 100; 113343071Swollman case 4: 113443071Swollman tm.tm_min = ul % 100; 113543071Swollman ul /= 100; 113643071Swollman case 2: 113743071Swollman tm.tm_hour = ul; 113843071Swollman case 0: 113943071Swollman break; 114043071Swollman default: 1141111392Sgad return (-1); 114243071Swollman } 114343071Swollman 114443071Swollman /* sanity check */ 114543071Swollman if (tm.tm_sec < 0 || tm.tm_sec > 60 || tm.tm_min < 0 114643071Swollman || tm.tm_min > 59 || tm.tm_hour < 0 || tm.tm_hour > 23) 1147111392Sgad return (-1); 114843071Swollman } 114993659Scjc if ((tsecs = mktime(&tm)) == -1) 115099209Smaxim errx(1, "nonexistent time:\n%s", errline); 1151111392Sgad return (tsecs); 115243071Swollman} 115359004Shm 115459004Shm/* physically move file */ 115559004Shmstatic void 115659004Shmmovefile(char *from, char *to, int perm, int owner_uid, int group_gid) 115759004Shm{ 115859004Shm FILE *src, *dst; 115959004Shm int c; 116059004Shm 116159004Shm if ((src = fopen(from, "r")) == NULL) 116259004Shm err(1, "can't fopen %s for reading", from); 116359004Shm if ((dst = fopen(to, "w")) == NULL) 116459004Shm err(1, "can't fopen %s for writing", to); 116559004Shm if (fchown(fileno(dst), owner_uid, group_gid)) 116659004Shm err(1, "can't fchown %s", to); 116759004Shm if (fchmod(fileno(dst), perm)) 116859004Shm err(1, "can't fchmod %s", to); 116959004Shm 117059004Shm while ((c = getc(src)) != EOF) { 117159004Shm if ((putc(c, dst)) == EOF) 117259004Shm err(1, "error writing to %s", to); 117359004Shm } 117459004Shm 117559004Shm if (ferror(src)) 117659004Shm err(1, "error reading from %s", from); 117759004Shm if ((fclose(src)) != 0) 117859004Shm err(1, "can't fclose %s", to); 117959004Shm if ((fclose(dst)) != 0) 118059004Shm err(1, "can't fclose %s", from); 118159004Shm if ((unlink(from)) != 0) 118259004Shm err(1, "can't unlink %s", from); 118359004Shm} 118459004Shm 118559004Shm/* create one or more directory components of a path */ 118659004Shmstatic void 118759004Shmcreatedir(char *dirpart) 118859004Shm{ 1189111398Sgad int res; 119059004Shm char *s, *d; 119171299Sjedgar char mkdirpath[MAXPATHLEN]; 119259004Shm struct stat st; 119359004Shm 119459004Shm s = dirpart; 119559004Shm d = mkdirpath; 119659004Shm 119759004Shm for (;;) { 119859004Shm *d++ = *s++; 1199111398Sgad if (*s != '/' && *s != '\0') 1200111398Sgad continue; 1201111398Sgad *d = '\0'; 1202111398Sgad res = lstat(mkdirpath, &st); 1203111398Sgad if (res != 0) { 1204111398Sgad if (noaction) { 1205111398Sgad printf("mkdir %s\n", mkdirpath); 1206111398Sgad } else { 1207111398Sgad res = mkdir(mkdirpath, 0755); 1208111398Sgad if (res != 0) 1209111398Sgad err(1, "Error on mkdir(\"%s\") for -a", 1210111398Sgad mkdirpath); 1211111398Sgad } 121259004Shm } 121359004Shm if (*s == '\0') 121459004Shm break; 121559004Shm } 1216111398Sgad if (verbose) 1217111398Sgad printf("created directory '%s' for -a\n", dirpart); 121859004Shm} 121959004Shm 122059004Shm/*- 122159004Shm * Parse a cyclic time specification, the format is as follows: 122259004Shm * 122359004Shm * [Dhh] or [Wd[Dhh]] or [Mdd[Dhh]] 122459004Shm * 122559004Shm * to rotate a logfile cyclic at 122659004Shm * 122759004Shm * - every day (D) within a specific hour (hh) (hh = 0...23) 122859004Shm * - once a week (W) at a specific day (d) OR (d = 0..6, 0 = Sunday) 122959004Shm * - once a month (M) at a specific day (d) (d = 1..31,l|L) 123059004Shm * 123159004Shm * We don't accept a timezone specification; missing fields 123259004Shm * are defaulted to the current date but time zero. 123359004Shm */ 123459004Shmstatic time_t 123593659ScjcparseDWM(char *s, char *errline) 123659004Shm{ 123759004Shm char *t; 123893659Scjc time_t tsecs; 123959004Shm struct tm tm, *tmp; 124080742Sobrien long l; 124159004Shm int nd; 124259004Shm static int mtab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 124359004Shm int WMseen = 0; 124459004Shm int Dseen = 0; 124559004Shm 124659004Shm tmp = localtime(&timenow); 124759004Shm tm = *tmp; 124859004Shm 124959004Shm /* set no. of days per month */ 125059004Shm 125159004Shm nd = mtab[tm.tm_mon]; 125259004Shm 125359004Shm if (tm.tm_mon == 1) { 125459004Shm if (((tm.tm_year + 1900) % 4 == 0) && 125559004Shm ((tm.tm_year + 1900) % 100 != 0) && 125659004Shm ((tm.tm_year + 1900) % 400 == 0)) { 125759004Shm nd++; /* leap year, 29 days in february */ 125859004Shm } 125959004Shm } 126059004Shm tm.tm_hour = tm.tm_min = tm.tm_sec = 0; 126159004Shm 126259004Shm for (;;) { 126359004Shm switch (*s) { 126459004Shm case 'D': 126559004Shm if (Dseen) 1266111392Sgad return (-1); 126759004Shm Dseen++; 126859004Shm s++; 126980742Sobrien l = strtol(s, &t, 10); 127080742Sobrien if (l < 0 || l > 23) 1271111392Sgad return (-1); 127280742Sobrien tm.tm_hour = l; 127359004Shm break; 127459004Shm 127559004Shm case 'W': 127659004Shm if (WMseen) 1277111392Sgad return (-1); 127859004Shm WMseen++; 127959004Shm s++; 128080742Sobrien l = strtol(s, &t, 10); 128180742Sobrien if (l < 0 || l > 6) 1282111392Sgad return (-1); 128380742Sobrien if (l != tm.tm_wday) { 128459004Shm int save; 128559004Shm 128680742Sobrien if (l < tm.tm_wday) { 128759004Shm save = 6 - tm.tm_wday; 128880742Sobrien save += (l + 1); 128959004Shm } else { 129080742Sobrien save = l - tm.tm_wday; 129159004Shm } 129259004Shm 129359004Shm tm.tm_mday += save; 129459004Shm 129559004Shm if (tm.tm_mday > nd) { 129659004Shm tm.tm_mon++; 129759004Shm tm.tm_mday = tm.tm_mday - nd; 129859004Shm } 129959004Shm } 130059004Shm break; 130159004Shm 130259004Shm case 'M': 130359004Shm if (WMseen) 1304111392Sgad return (-1); 130559004Shm WMseen++; 130659004Shm s++; 130759004Shm if (tolower(*s) == 'l') { 130859004Shm tm.tm_mday = nd; 130959004Shm s++; 131059004Shm t = s; 131159004Shm } else { 131280742Sobrien l = strtol(s, &t, 10); 131380742Sobrien if (l < 1 || l > 31) 1314111392Sgad return (-1); 131559004Shm 131680742Sobrien if (l > nd) 1317111392Sgad return (-1); 131880742Sobrien tm.tm_mday = l; 131959004Shm } 132059004Shm break; 132159004Shm 132259004Shm default: 132359004Shm return (-1); 132459004Shm break; 132559004Shm } 132659004Shm 132759004Shm if (*t == '\0' || isspace(*t)) 132859004Shm break; 132959004Shm else 133059004Shm s = t; 133159004Shm } 133293659Scjc if ((tsecs = mktime(&tm)) == -1) 133399209Smaxim errx(1, "nonexistent time:\n%s", errline); 1334111392Sgad return (tsecs); 133559004Shm} 1336