newsyslog.c revision 111768
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 111768 2003-03-02 22:05:17Z 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 67111768Sgad/* 68111768Sgad * Bit-values for the 'flags' parsed from a config-file entry. 69111768Sgad */ 70111768Sgad#define CE_COMPACT 0x0001 /* Compact the achived log files with gzip. */ 71111768Sgad#define CE_BZCOMPACT 0x0002 /* Compact the achived log files with bzip2. */ 72111768Sgad#define CE_COMPACTWAIT 0x0004 /* wait until compressing one file finishes */ 73111768Sgad /* before starting the next step. */ 74111768Sgad#define CE_BINARY 0x0008 /* Logfile is in binary, do not add status */ 75111768Sgad /* messages to logfile(s) when rotating. */ 76111768Sgad#define CE_NOSIGNAL 0x0010 /* There is no process to signal when */ 77111768Sgad /* trimming this file. */ 78111768Sgad#define CE_TRIMAT 0x0020 /* trim file at a specific time. */ 79111768Sgad#define CE_GLOB 0x0040 /* name of the log is file name pattern. */ 8043071Swollman 8113244Sgraichen#define NONE -1 8259003Shm 8313244Sgraichenstruct conf_entry { 8459003Shm char *log; /* Name of the log */ 8559003Shm char *pid_file; /* PID file */ 8659003Shm int uid; /* Owner of log */ 8759003Shm int gid; /* Group of log */ 8859003Shm int numlogs; /* Number of logs to keep */ 8959003Shm int size; /* Size cutoff to trigger trimming the log */ 9059003Shm int hours; /* Hours between log trimming */ 9159003Shm time_t trim_at; /* Specific time to do trimming */ 9259003Shm int permissions; /* File permissions on the log */ 9380646Sobrien int flags; /* CE_COMPACT, CE_BZCOMPACT, CE_BINARY */ 9459003Shm int sig; /* Signal to send */ 95111388Sgad int def_cfg; /* Using the <default> rule for this file */ 9659003Shm struct conf_entry *next;/* Linked list pointer */ 9713244Sgraichen}; 9813244Sgraichen 99111388Sgad#define DEFAULT_MARKER "<default>" 100111388Sgad 10159004Shmint archtodir = 0; /* Archive old logfiles to other directory */ 10259003Shmint verbose = 0; /* Print out what's going on */ 10359003Shmint needroot = 1; /* Root privs are necessary */ 10459003Shmint noaction = 0; /* Don't do anything, just show it */ 105111768Sgadint nosignal; /* Do not send any signals */ 10659003Shmint force = 0; /* Force the trim no matter what */ 10759004Shmchar *archdirname; /* Directory path to old logfiles archive */ 10880640Sobrienconst char *conf = _PATH_CONF; /* Configuration file to use */ 10959003Shmtime_t timenow; 11059003Shm 11125443Sache#define MIN_PID 5 11259003Shm#define MAX_PID 99999 /* was lower, see /usr/include/sys/proc.h */ 11371299Sjedgarchar hostname[MAXHOSTNAMELEN]; /* hostname */ 114108164Strhodeschar daytime[16]; /* timenow in human readable form */ 11513244Sgraichen 11660373Sdesstatic struct conf_entry *parse_file(char **files); 11716240Salexstatic char *sob(char *p); 11816240Salexstatic char *son(char *p); 11959003Shmstatic char *missing_field(char *p, char *errline); 12059003Shmstatic void do_entry(struct conf_entry * ent); 121111388Sgadstatic void free_entry(struct conf_entry *ent); 122111388Sgadstatic struct conf_entry *init_entry(const char *fname, 123111388Sgad struct conf_entry *src_entry); 12459003Shmstatic void PRS(int argc, char **argv); 12580640Sobrienstatic void usage(void); 126111768Sgadstatic void dotrim(const struct conf_entry *trim_ent, char *log, 127111768Sgad int numdays, int flags, int perm, int owner_uid, 128111768Sgad int group_gid, int sig); 129111768Sgadstatic int log_trim(const char *log, const struct conf_entry *log_ent); 130107916Ssobomaxstatic void compress_log(char *log, int dowait); 131107916Ssobomaxstatic void bzcompress_log(char *log, int dowait); 13216240Salexstatic int sizefile(char *file); 13316240Salexstatic int age_old_log(char *file); 13480640Sobrienstatic pid_t get_pid(const char *pid_file); 13593659Scjcstatic time_t parse8601(char *s, char *errline); 13680646Sobrienstatic void movefile(char *from, char *to, int perm, int owner_uid, 13780646Sobrien int group_gid); 13859004Shmstatic void createdir(char *dirpart); 13993659Scjcstatic time_t parseDWM(char *s, char *errline); 14013244Sgraichen 141111768Sgad/* 142111768Sgad * All the following are defined to work on an 'int', in the 143111768Sgad * range 0 to 255, plus EOF. Define wrappers which can take 144111768Sgad * values of type 'char', either signed or unsigned. 145111768Sgad */ 146111768Sgad#define isspacech(Anychar) isspace(((int) Anychar) & 255) 147111768Sgad#define tolowerch(Anychar) tolower(((int) Anychar) & 255) 148111768Sgad 14959004Shmint 15059004Shmmain(int argc, char **argv) 15113244Sgraichen{ 15259003Shm struct conf_entry *p, *q; 153111529Sgad char *savglob; 154106905Ssobomax glob_t pglob; 155106905Ssobomax int i; 15625443Sache 15759003Shm PRS(argc, argv); 15859003Shm if (needroot && getuid() && geteuid()) 15959003Shm errx(1, "must have root privs"); 16060373Sdes p = q = parse_file(argv + optind); 16159003Shm 16259003Shm while (p) { 163106905Ssobomax if ((p->flags & CE_GLOB) == 0) { 164106905Ssobomax do_entry(p); 165106905Ssobomax } else { 166106905Ssobomax if (glob(p->log, GLOB_NOCHECK, NULL, &pglob) != 0) { 167106905Ssobomax warn("can't expand pattern: %s", p->log); 168106905Ssobomax } else { 169111529Sgad savglob = p->log; 170106905Ssobomax for (i = 0; i < pglob.gl_matchc; i++) { 171106905Ssobomax p->log = pglob.gl_pathv[i]; 172106905Ssobomax do_entry(p); 173106905Ssobomax } 174106905Ssobomax globfree(&pglob); 175111529Sgad p->log = savglob; 176106905Ssobomax } 177106905Ssobomax } 17859003Shm p = p->next; 179111388Sgad free_entry(q); 18059003Shm q = p; 18159003Shm } 18295999Smaxim while (wait(NULL) > 0 || errno == EINTR) 18395999Smaxim ; 18459003Shm return (0); 18513244Sgraichen} 18613244Sgraichen 187111388Sgadstatic struct conf_entry * 188111388Sgadinit_entry(const char *fname, struct conf_entry *src_entry) 189111388Sgad{ 190111388Sgad struct conf_entry *tempwork; 191111388Sgad 192111388Sgad if (verbose > 4) 193111388Sgad printf("\t--> [creating entry for %s]\n", fname); 194111388Sgad 195111388Sgad tempwork = malloc(sizeof(struct conf_entry)); 196111388Sgad if (tempwork == NULL) 197111388Sgad err(1, "malloc of conf_entry for %s", fname); 198111388Sgad 199111388Sgad tempwork->log = strdup(fname); 200111388Sgad if (tempwork->log == NULL) 201111388Sgad err(1, "strdup for %s", fname); 202111388Sgad 203111388Sgad if (src_entry != NULL) { 204111388Sgad tempwork->pid_file = NULL; 205111388Sgad if (src_entry->pid_file) 206111388Sgad tempwork->pid_file = strdup(src_entry->pid_file); 207111388Sgad tempwork->uid = src_entry->uid; 208111388Sgad tempwork->gid = src_entry->gid; 209111388Sgad tempwork->numlogs = src_entry->numlogs; 210111388Sgad tempwork->size = src_entry->size; 211111388Sgad tempwork->hours = src_entry->hours; 212111388Sgad tempwork->trim_at = src_entry->trim_at; 213111388Sgad tempwork->permissions = src_entry->permissions; 214111388Sgad tempwork->flags = src_entry->flags; 215111388Sgad tempwork->sig = src_entry->sig; 216111388Sgad tempwork->def_cfg = src_entry->def_cfg; 217111388Sgad } else { 218111388Sgad /* Initialize as a "do-nothing" entry */ 219111388Sgad tempwork->pid_file = NULL; 220111388Sgad tempwork->uid = NONE; 221111388Sgad tempwork->gid = NONE; 222111388Sgad tempwork->numlogs = 1; 223111388Sgad tempwork->size = -1; 224111388Sgad tempwork->hours = -1; 225111388Sgad tempwork->trim_at = (time_t)0; 226111388Sgad tempwork->permissions = 0; 227111388Sgad tempwork->flags = 0; 228111388Sgad tempwork->sig = SIGHUP; 229111388Sgad tempwork->def_cfg = 0; 230111388Sgad } 231111388Sgad tempwork->next = NULL; 232111388Sgad 233111388Sgad return (tempwork); 234111388Sgad} 235111388Sgad 23659004Shmstatic void 237111388Sgadfree_entry(struct conf_entry *ent) 238111388Sgad{ 239111388Sgad 240111388Sgad if (ent == NULL) 241111388Sgad return; 242111388Sgad 243111388Sgad if (ent->log != NULL) { 244111388Sgad if (verbose > 4) 245111388Sgad printf("\t--> [freeing entry for %s]\n", ent->log); 246111388Sgad free(ent->log); 247111388Sgad ent->log = NULL; 248111388Sgad } 249111388Sgad 250111388Sgad if (ent->pid_file != NULL) { 251111388Sgad free(ent->pid_file); 252111388Sgad ent->pid_file = NULL; 253111388Sgad } 254111388Sgad 255111388Sgad free(ent); 256111388Sgad} 257111388Sgad 258111388Sgadstatic void 25959004Shmdo_entry(struct conf_entry * ent) 26013244Sgraichen{ 26159003Shm int size, modtime; 26259003Shm 26359003Shm if (verbose) { 26459003Shm if (ent->flags & CE_COMPACT) 26559003Shm printf("%s <%dZ>: ", ent->log, ent->numlogs); 26680638Sobrien else if (ent->flags & CE_BZCOMPACT) 26780638Sobrien printf("%s <%dJ>: ", ent->log, ent->numlogs); 26859003Shm else 26959003Shm printf("%s <%d>: ", ent->log, ent->numlogs); 27059003Shm } 27159003Shm size = sizefile(ent->log); 27259003Shm modtime = age_old_log(ent->log); 27359003Shm if (size < 0) { 27459003Shm if (verbose) 27559003Shm printf("does not exist.\n"); 27659003Shm } else { 27790240Sroam if (ent->flags & CE_TRIMAT && !force) { 27843071Swollman if (timenow < ent->trim_at 27959003Shm || difftime(timenow, ent->trim_at) >= 60 * 60) { 28043071Swollman if (verbose) 28143071Swollman printf("--> will trim at %s", 28259003Shm ctime(&ent->trim_at)); 28343071Swollman return; 28443071Swollman } else if (verbose && ent->hours <= 0) { 28543071Swollman printf("--> time is up\n"); 28643071Swollman } 28743071Swollman } 28859003Shm if (verbose && (ent->size > 0)) 28959003Shm printf("size (Kb): %d [%d] ", size, ent->size); 29059003Shm if (verbose && (ent->hours > 0)) 29159003Shm printf(" age (hr): %d [%d] ", modtime, ent->hours); 29259003Shm if (force || ((ent->size > 0) && (size >= ent->size)) || 29343071Swollman (ent->hours <= 0 && (ent->flags & CE_TRIMAT)) || 29459003Shm ((ent->hours > 0) && ((modtime >= ent->hours) 29559003Shm || (modtime < 0)))) { 29659003Shm if (verbose) 29759003Shm printf("--> trimming log....\n"); 29859003Shm if (noaction && !verbose) { 29959003Shm if (ent->flags & CE_COMPACT) 30059003Shm printf("%s <%dZ>: trimming\n", 30159003Shm ent->log, ent->numlogs); 30280638Sobrien else if (ent->flags & CE_BZCOMPACT) 30380638Sobrien printf("%s <%dJ>: trimming\n", 30480638Sobrien ent->log, ent->numlogs); 30559003Shm else 30659003Shm printf("%s <%d>: trimming\n", 30759003Shm ent->log, ent->numlogs); 30859003Shm } 309111768Sgad dotrim(ent, ent->log, ent->numlogs, ent->flags, 310111768Sgad ent->permissions, ent->uid, ent->gid, ent->sig); 31159003Shm } else { 31259003Shm if (verbose) 31359003Shm printf("--> skipping\n"); 31459003Shm } 31559003Shm } 31613244Sgraichen} 31713244Sgraichen 31859004Shmstatic void 31959004ShmPRS(int argc, char **argv) 32013244Sgraichen{ 32159003Shm int c; 32259003Shm char *p; 32313244Sgraichen 32459003Shm timenow = time((time_t *) 0); 325108164Strhodes (void)strncpy(daytime, ctime(&timenow) + 4, 15); 32659003Shm daytime[15] = '\0'; 32713244Sgraichen 32859003Shm /* Let's get our hostname */ 32959003Shm (void) gethostname(hostname, sizeof(hostname)); 33013244Sgraichen 33113244Sgraichen /* Truncate domain */ 33216240Salex if ((p = strchr(hostname, '.'))) { 33313244Sgraichen *p = '\0'; 33413244Sgraichen } 335111768Sgad 336111768Sgad /* Parse command line options. */ 337111768Sgad while ((c = getopt(argc, argv, "a:f:nrsvF")) != -1) 33859003Shm switch (c) { 33959004Shm case 'a': 34059004Shm archtodir++; 34159004Shm archdirname = optarg; 34259004Shm break; 343111768Sgad case 'f': 344111768Sgad conf = optarg; 345111768Sgad break; 346111768Sgad case 'n': 347111768Sgad noaction++; 348111768Sgad break; 34959003Shm case 'r': 35059003Shm needroot = 0; 35159003Shm break; 352111768Sgad case 's': 353111768Sgad nosignal = 1; 354111768Sgad break; 35559003Shm case 'v': 35659003Shm verbose++; 35759003Shm break; 35834584Spst case 'F': 35934584Spst force++; 36034584Spst break; 36159003Shm default: 36259003Shm usage(); 363111768Sgad /* NOTREACHED */ 36459003Shm } 36543071Swollman} 36613244Sgraichen 36759004Shmstatic void 36859004Shmusage(void) 36913244Sgraichen{ 37080646Sobrien 37180646Sobrien fprintf(stderr, 372111768Sgad "usage: newsyslog [-Fnrsv] [-f config-file] [-a directory] [ filename ... ]\n"); 37359003Shm exit(1); 37413244Sgraichen} 37513244Sgraichen 37659004Shm/* 37759004Shm * Parse a configuration file and return a linked list of all the logs to 37859004Shm * process 37913244Sgraichen */ 38059003Shmstatic struct conf_entry * 38160373Sdesparse_file(char **files) 38213244Sgraichen{ 38359003Shm FILE *f; 38459003Shm char line[BUFSIZ], *parse, *q; 385107737Ssobomax char *cp, *errline, *group; 386111388Sgad char **given; 387111388Sgad struct conf_entry *defconf, *first, *working, *worklist; 38859003Shm struct passwd *pass; 38959003Shm struct group *grp; 39025518Sbrian int eol; 39113244Sgraichen 392111388Sgad defconf = first = working = worklist = NULL; 39380646Sobrien 39459003Shm if (strcmp(conf, "-")) 39559003Shm f = fopen(conf, "r"); 39659003Shm else 39759003Shm f = stdin; 39859003Shm if (!f) 39959003Shm err(1, "%s", conf); 40059003Shm while (fgets(line, BUFSIZ, f)) { 401107737Ssobomax if ((line[0] == '\n') || (line[0] == '#') || 402107737Ssobomax (strlen(line) == 0)) 40359003Shm continue; 40459003Shm errline = strdup(line); 405107737Ssobomax for (cp = line + 1; *cp != '\0'; cp++) { 406107737Ssobomax if (*cp != '#') 407107737Ssobomax continue; 408107737Ssobomax if (*(cp - 1) == '\\') { 409107737Ssobomax strcpy(cp - 1, cp); 410107737Ssobomax cp--; 411107737Ssobomax continue; 412107737Ssobomax } 413107737Ssobomax *cp = '\0'; 414107737Ssobomax break; 415107737Ssobomax } 41660373Sdes 41760373Sdes q = parse = missing_field(sob(line), errline); 41860373Sdes parse = son(line); 41960373Sdes if (!*parse) 42080646Sobrien errx(1, "malformed line (missing fields):\n%s", 42180646Sobrien errline); 42260373Sdes *parse = '\0'; 42360373Sdes 424111388Sgad /* 425111388Sgad * If newsyslog was run with a list of specific filenames, 426111388Sgad * then this line of the config file should be skipped if 427111388Sgad * it is NOT one of those given files (except that we do 428111388Sgad * want any line that defines the <default> action). 429111388Sgad * 430111388Sgad * XXX - note that CE_GLOB processing is *NOT* done when 431111388Sgad * trying to match a filename given on the command! 432111388Sgad */ 43360373Sdes if (*files) { 434111388Sgad if (strcasecmp(DEFAULT_MARKER, q) != 0) { 435111388Sgad for (given = files; *given; ++given) { 436111388Sgad if (strcmp(*given, q) == 0) 437111388Sgad break; 438111388Sgad } 439111388Sgad if (!*given) 440111388Sgad continue; 441111388Sgad } 442111388Sgad if (verbose > 2) 443111388Sgad printf("\t+ Matched entry %s\n", q); 444111388Sgad } else { 445111388Sgad /* 446111388Sgad * If no files were specified on the command line, 447111388Sgad * then we can skip any line which defines the 448111388Sgad * default action. 449111388Sgad */ 450111388Sgad if (strcasecmp(DEFAULT_MARKER, q) == 0) { 451111388Sgad if (verbose > 2) 452111388Sgad printf("\t+ Ignoring entry for %s\n", 453111388Sgad q); 45460373Sdes continue; 455111388Sgad } 45660373Sdes } 45760373Sdes 458111388Sgad working = init_entry(q, NULL); 459111388Sgad if (strcasecmp(DEFAULT_MARKER, q) == 0) { 460111388Sgad if (defconf != NULL) { 461111388Sgad warnx("Ignoring duplicate entry for %s!", q); 462111388Sgad free_entry(working); 463111388Sgad continue; 464111388Sgad } 465111388Sgad defconf = working; 46659003Shm } else { 467111388Sgad if (!first) 468111388Sgad first = working; 469111388Sgad else 470111388Sgad worklist->next = working; 471111388Sgad worklist = working; 47259003Shm } 47313244Sgraichen 47459003Shm q = parse = missing_field(sob(++parse), errline); 47559003Shm parse = son(parse); 47625518Sbrian if (!*parse) 47780646Sobrien errx(1, "malformed line (missing fields):\n%s", 47880646Sobrien errline); 47959003Shm *parse = '\0'; 48059003Shm if ((group = strchr(q, ':')) != NULL || 48159003Shm (group = strrchr(q, '.')) != NULL) { 48259003Shm *group++ = '\0'; 48359003Shm if (*q) { 48459003Shm if (!(isnumber(*q))) { 48559003Shm if ((pass = getpwnam(q)) == NULL) 48659003Shm errx(1, 48780646Sobrien "error in config file; unknown user:\n%s", 48859003Shm errline); 48959003Shm working->uid = pass->pw_uid; 49059003Shm } else 49159003Shm working->uid = atoi(q); 49259003Shm } else 49359003Shm working->uid = NONE; 49413244Sgraichen 49559003Shm q = group; 49659003Shm if (*q) { 49759003Shm if (!(isnumber(*q))) { 49859003Shm if ((grp = getgrnam(q)) == NULL) 49959003Shm errx(1, 50080646Sobrien "error in config file; unknown group:\n%s", 50159003Shm errline); 50259003Shm working->gid = grp->gr_gid; 50359003Shm } else 50459003Shm working->gid = atoi(q); 50559003Shm } else 50659003Shm working->gid = NONE; 50713244Sgraichen 50859003Shm q = parse = missing_field(sob(++parse), errline); 50959003Shm parse = son(parse); 51059003Shm if (!*parse) 51180646Sobrien errx(1, "malformed line (missing fields):\n%s", 51280646Sobrien errline); 51359003Shm *parse = '\0'; 51459003Shm } else 51559003Shm working->uid = working->gid = NONE; 51659003Shm 51759003Shm if (!sscanf(q, "%o", &working->permissions)) 51859003Shm errx(1, "error in config file; bad permissions:\n%s", 51959003Shm errline); 52059003Shm 52159003Shm q = parse = missing_field(sob(++parse), errline); 52259003Shm parse = son(parse); 52325518Sbrian if (!*parse) 52480646Sobrien errx(1, "malformed line (missing fields):\n%s", 52580646Sobrien errline); 52659003Shm *parse = '\0'; 527111400Sgad if (!sscanf(q, "%d", &working->numlogs) || working->numlogs < 0) 528111400Sgad errx(1, "error in config file; bad value for count of logs to save:\n%s", 52959003Shm errline); 53013244Sgraichen 53159003Shm q = parse = missing_field(sob(++parse), errline); 53259003Shm parse = son(parse); 53325518Sbrian if (!*parse) 53480646Sobrien errx(1, "malformed line (missing fields):\n%s", 53580646Sobrien errline); 53659003Shm *parse = '\0'; 53759003Shm if (isdigit(*q)) 53859003Shm working->size = atoi(q); 53959003Shm else 54059003Shm working->size = -1; 54159003Shm 54259003Shm working->flags = 0; 54359003Shm q = parse = missing_field(sob(++parse), errline); 54459003Shm parse = son(parse); 54525518Sbrian eol = !*parse; 54659003Shm *parse = '\0'; 54743071Swollman { 54859003Shm char *ep; 54959003Shm u_long ul; 55013244Sgraichen 55143071Swollman ul = strtoul(q, &ep, 10); 55243071Swollman if (ep == q) 55343071Swollman working->hours = 0; 55443071Swollman else if (*ep == '*') 55543071Swollman working->hours = -1; 55643071Swollman else if (ul > INT_MAX) 55743071Swollman errx(1, "interval is too large:\n%s", errline); 55843071Swollman else 55943071Swollman working->hours = ul; 56043071Swollman 56180646Sobrien if (*ep != '\0' && *ep != '@' && *ep != '*' && 56280646Sobrien *ep != '$') 56343071Swollman errx(1, "malformed interval/at:\n%s", errline); 56443071Swollman if (*ep == '@') { 56593659Scjc if ((working->trim_at = parse8601(ep + 1, errline)) 56659003Shm == (time_t) - 1) 56743071Swollman errx(1, "malformed at:\n%s", errline); 56843071Swollman working->flags |= CE_TRIMAT; 56959004Shm } else if (*ep == '$') { 57093659Scjc if ((working->trim_at = parseDWM(ep + 1, errline)) 57159004Shm == (time_t) - 1) 57259004Shm errx(1, "malformed at:\n%s", errline); 57359004Shm working->flags |= CE_TRIMAT; 57443071Swollman } 57543071Swollman } 57643071Swollman 57725518Sbrian if (eol) 57859003Shm q = NULL; 57925518Sbrian else { 58059003Shm q = parse = sob(++parse); /* Optional field */ 58159003Shm parse = son(parse); 58259003Shm if (!*parse) 58359003Shm eol = 1; 58459003Shm *parse = '\0'; 58525518Sbrian } 58625443Sache 587111768Sgad for (; q && *q && !isspacech(*q); q++) { 588111768Sgad switch (tolowerch(*q)) { 589111768Sgad case 'b': 59059003Shm working->flags |= CE_BINARY; 591111768Sgad break; 592111768Sgad case 'c': 593111768Sgad /* 594111768Sgad * netbsd uses 'c' for "create". We will 595111768Sgad * temporarily accept it for 'g', because 596111768Sgad * earlier freebsd versions had a typo 597111768Sgad * of ('G' || 'c')... 598111768Sgad */ 599111768Sgad warnx("Assuming 'g' for 'c' in flags for line:\n%s", 600111768Sgad errline); 601111768Sgad /* FALLTHROUGH */ 602111768Sgad case 'g': 603106905Ssobomax working->flags |= CE_GLOB; 604111768Sgad break; 605111768Sgad case 'j': 606111768Sgad working->flags |= CE_BZCOMPACT; 607111768Sgad break; 608111768Sgad case 'n': 609111768Sgad working->flags |= CE_NOSIGNAL; 610111768Sgad break; 611111768Sgad case 'w': 612107916Ssobomax working->flags |= CE_COMPACTWAIT; 613111768Sgad break; 614111768Sgad case 'z': 615111768Sgad working->flags |= CE_COMPACT; 616111768Sgad break; 617111768Sgad case '-': 618111768Sgad break; 619111768Sgad default: 62080646Sobrien errx(1, "illegal flag in config file -- %c", 62180646Sobrien *q); 622111768Sgad } 62359003Shm } 62459003Shm 62525518Sbrian if (eol) 62659003Shm q = NULL; 62725518Sbrian else { 62859003Shm q = parse = sob(++parse); /* Optional field */ 62959003Shm parse = son(parse); 63059003Shm if (!*parse) 63159003Shm eol = 1; 63259003Shm *parse = '\0'; 63325518Sbrian } 63425443Sache 63525443Sache working->pid_file = NULL; 63625443Sache if (q && *q) { 63725443Sache if (*q == '/') 63825443Sache working->pid_file = strdup(q); 63936817Sache else if (isdigit(*q)) 64036817Sache goto got_sig; 64159003Shm else 64280646Sobrien errx(1, 64380646Sobrien "illegal pid file or signal number in config file:\n%s", 64480646Sobrien errline); 64525443Sache } 64636817Sache if (eol) 64759003Shm q = NULL; 64836817Sache else { 64959003Shm q = parse = sob(++parse); /* Optional field */ 65059003Shm *(parse = son(parse)) = '\0'; 65136817Sache } 65236817Sache 65336817Sache working->sig = SIGHUP; 65436817Sache if (q && *q) { 65536817Sache if (isdigit(*q)) { 65659003Shm got_sig: 65736817Sache working->sig = atoi(q); 65836817Sache } else { 65959003Shm err_sig: 66080646Sobrien errx(1, 66180646Sobrien "illegal signal number in config file:\n%s", 66280646Sobrien errline); 66336817Sache } 66436817Sache if (working->sig < 1 || working->sig >= NSIG) 66536817Sache goto err_sig; 66636817Sache } 667111768Sgad 668111768Sgad /* 669111768Sgad * Finish figuring out what pid-file to use (if any) in 670111768Sgad * later processing if this logfile needs to be rotated. 671111768Sgad */ 672111768Sgad if ((working->flags & CE_NOSIGNAL) == CE_NOSIGNAL) { 673111768Sgad /* 674111768Sgad * This config-entry specified 'n' for nosignal, 675111768Sgad * see if it also specified an explicit pid_file. 676111768Sgad * This would be a pretty pointless combination. 677111768Sgad */ 678111768Sgad if (working->pid_file != NULL) { 679111768Sgad warnx("Ignoring '%s' because flag 'n' was specified in line:\n%s", 680111768Sgad working->pid_file, errline); 681111768Sgad free(working->pid_file); 682111768Sgad working->pid_file = NULL; 683111768Sgad } 684111768Sgad } else if (nosignal) { 685111768Sgad /* 686111768Sgad * While this entry might usually signal some 687111768Sgad * process via the pid-file, newsyslog was run 688111768Sgad * with '-s', so quietly ignore the pid-file. 689111768Sgad */ 690111768Sgad if (working->pid_file != NULL) { 691111768Sgad free(working->pid_file); 692111768Sgad working->pid_file = NULL; 693111768Sgad } 694111768Sgad } else if (working->pid_file == NULL) { 695111768Sgad /* 696111768Sgad * This entry did not specify the 'n' flag, which 697111768Sgad * means it should signal syslogd unless it had 698111768Sgad * specified some other pid-file. But we only 699111768Sgad * try to notify syslog if we are root 700111768Sgad */ 701111768Sgad if (needroot) 702111768Sgad working->pid_file = strdup(_PATH_SYSLOGPID); 703111768Sgad } 704111768Sgad 70559003Shm free(errline); 706111768Sgad errline = NULL; 70759003Shm } 70859003Shm (void) fclose(f); 709111388Sgad 710111388Sgad /* 711111388Sgad * The entire config file has been processed. If there were 712111388Sgad * no specific files given on the run command, then the work 713111388Sgad * of this routine is done. 714111388Sgad */ 715111388Sgad if (*files == NULL) 716111388Sgad return (first); 717111388Sgad 718111388Sgad /* 719111388Sgad * If the program was given a specific list of files to process, 720111388Sgad * it may be that some of those files were not listed in the 721111388Sgad * config file. Those unlisted files should get the default 722111388Sgad * rotation action. First, create the default-rotation action 723111388Sgad * if none was found in the config file. 724111388Sgad */ 725111388Sgad if (defconf == NULL) { 726111388Sgad working = init_entry(DEFAULT_MARKER, NULL); 727111388Sgad working->numlogs = 3; 728111388Sgad working->size = 50; 729111388Sgad working->permissions = S_IRUSR|S_IWUSR; 730111388Sgad defconf = working; 731111388Sgad } 732111388Sgad 733111388Sgad for (given = files; *given; ++given) { 734111388Sgad for (working = first; working; working = working->next) { 735111388Sgad if (strcmp(*given, working->log) == 0) 736111388Sgad break; 737111388Sgad } 738111388Sgad if (working != NULL) 739111388Sgad continue; 740111388Sgad if (verbose > 2) 741111388Sgad printf("\t+ No entry for %s (will use %s)\n", 742111388Sgad *given, DEFAULT_MARKER); 743111388Sgad /* 744111388Sgad * This given file was not found in the config file. 745111388Sgad * Add another item on to our work list, based on the 746111388Sgad * default entry. 747111388Sgad */ 748111388Sgad working = init_entry(*given, defconf); 749111388Sgad if (!first) 750111388Sgad first = working; 751111388Sgad else 752111388Sgad worklist->next = working; 753111388Sgad /* This is a file that was *not* found in config file */ 754111388Sgad working->def_cfg = 1; 755111388Sgad worklist = working; 756111388Sgad } 757111388Sgad 758111388Sgad free_entry(defconf); 75959003Shm return (first); 76013244Sgraichen} 76113244Sgraichen 76259003Shmstatic char * 76359004Shmmissing_field(char *p, char *errline) 76413244Sgraichen{ 76580646Sobrien 76659003Shm if (!p || !*p) 76759003Shm errx(1, "missing field in config file:\n%s", errline); 76859003Shm return (p); 76913244Sgraichen} 77013244Sgraichen 77159004Shmstatic void 772111768Sgaddotrim(const struct conf_entry *trim_ent, char *log, int numdays, int flags, 773111768Sgad int perm, int owner_uid, int group_gid, int sig) 77413244Sgraichen{ 77571299Sjedgar char dirpart[MAXPATHLEN], namepart[MAXPATHLEN]; 77671299Sjedgar char file1[MAXPATHLEN], file2[MAXPATHLEN]; 77771299Sjedgar char zfile1[MAXPATHLEN], zfile2[MAXPATHLEN]; 77880638Sobrien char jfile1[MAXPATHLEN]; 77994352Ssheldonh char tfile[MAXPATHLEN]; 78059003Shm int notified, need_notification, fd, _numdays; 78159003Shm struct stat st; 78259003Shm pid_t pid; 78313244Sgraichen 78413244Sgraichen#ifdef _IBMR2 78559004Shm /* 78659004Shm * AIX 3.1 has a broken fchown- if the owner_uid is -1, it will 78759004Shm * actually change it to be owned by uid -1, instead of leaving it 78859004Shm * as is, as it is supposed to. 78959004Shm */ 79059003Shm if (owner_uid == -1) 79159003Shm owner_uid = geteuid(); 79213244Sgraichen#endif 79313244Sgraichen 79459004Shm if (archtodir) { 79559004Shm char *p; 79613244Sgraichen 79759004Shm /* build complete name of archive directory into dirpart */ 79859004Shm if (*archdirname == '/') { /* absolute */ 79971299Sjedgar strlcpy(dirpart, archdirname, sizeof(dirpart)); 80059004Shm } else { /* relative */ 80159004Shm /* get directory part of logfile */ 80271299Sjedgar strlcpy(dirpart, log, sizeof(dirpart)); 80359004Shm if ((p = rindex(dirpart, '/')) == NULL) 80459004Shm dirpart[0] = '\0'; 80559004Shm else 80659004Shm *(p + 1) = '\0'; 80771299Sjedgar strlcat(dirpart, archdirname, sizeof(dirpart)); 80859004Shm } 80959004Shm 81059004Shm /* check if archive directory exists, if not, create it */ 81159004Shm if (lstat(dirpart, &st)) 81259004Shm createdir(dirpart); 81359004Shm 81459004Shm /* get filename part of logfile */ 81559004Shm if ((p = rindex(log, '/')) == NULL) 81671299Sjedgar strlcpy(namepart, log, sizeof(namepart)); 81759004Shm else 81871299Sjedgar strlcpy(namepart, p + 1, sizeof(namepart)); 81959004Shm 82059004Shm /* name of oldest log */ 82180646Sobrien (void) snprintf(file1, sizeof(file1), "%s/%s.%d", dirpart, 82280646Sobrien namepart, numdays); 82371299Sjedgar (void) snprintf(zfile1, sizeof(zfile1), "%s%s", file1, 82471299Sjedgar COMPRESS_POSTFIX); 82580638Sobrien snprintf(jfile1, sizeof(jfile1), "%s%s", file1, 82680638Sobrien BZCOMPRESS_POSTFIX); 82759004Shm } else { 82859004Shm /* name of oldest log */ 82971299Sjedgar (void) snprintf(file1, sizeof(file1), "%s.%d", log, numdays); 83071299Sjedgar (void) snprintf(zfile1, sizeof(zfile1), "%s%s", file1, 83171299Sjedgar COMPRESS_POSTFIX); 83280638Sobrien snprintf(jfile1, sizeof(jfile1), "%s%s", file1, 83380638Sobrien BZCOMPRESS_POSTFIX); 83459004Shm } 83559004Shm 83659003Shm if (noaction) { 83759003Shm printf("rm -f %s\n", file1); 83859003Shm printf("rm -f %s\n", zfile1); 83980638Sobrien printf("rm -f %s\n", jfile1); 84059003Shm } else { 84159003Shm (void) unlink(file1); 84259003Shm (void) unlink(zfile1); 84380638Sobrien (void) unlink(jfile1); 84459003Shm } 84513244Sgraichen 84659003Shm /* Move down log files */ 84718188Sjkh _numdays = numdays; /* preserve */ 84859003Shm while (numdays--) { 84959004Shm 85071299Sjedgar (void) strlcpy(file2, file1, sizeof(file2)); 85159004Shm 85259004Shm if (archtodir) 85380646Sobrien (void) snprintf(file1, sizeof(file1), "%s/%s.%d", 85480646Sobrien dirpart, namepart, numdays); 85559004Shm else 85680646Sobrien (void) snprintf(file1, sizeof(file1), "%s.%d", log, 85780646Sobrien numdays); 85859004Shm 85971299Sjedgar (void) strlcpy(zfile1, file1, sizeof(zfile1)); 86071299Sjedgar (void) strlcpy(zfile2, file2, sizeof(zfile2)); 86159003Shm if (lstat(file1, &st)) { 86280646Sobrien (void) strlcat(zfile1, COMPRESS_POSTFIX, 86380646Sobrien sizeof(zfile1)); 86480646Sobrien (void) strlcat(zfile2, COMPRESS_POSTFIX, 86580646Sobrien sizeof(zfile2)); 86680638Sobrien if (lstat(zfile1, &st)) { 86780638Sobrien strlcpy(zfile1, file1, sizeof(zfile1)); 86880638Sobrien strlcpy(zfile2, file2, sizeof(zfile2)); 86980638Sobrien strlcat(zfile1, BZCOMPRESS_POSTFIX, 87080638Sobrien sizeof(zfile1)); 87180638Sobrien strlcat(zfile2, BZCOMPRESS_POSTFIX, 87280638Sobrien sizeof(zfile2)); 87380638Sobrien if (lstat(zfile1, &st)) 87480638Sobrien continue; 87580638Sobrien } 87659003Shm } 87759003Shm if (noaction) { 87859003Shm printf("mv %s %s\n", zfile1, zfile2); 87959003Shm printf("chmod %o %s\n", perm, zfile2); 88080684Sobrien printf("chown %d:%d %s\n", 88159003Shm owner_uid, group_gid, zfile2); 88259003Shm } else { 88359003Shm (void) rename(zfile1, zfile2); 88459003Shm (void) chmod(zfile2, perm); 88559003Shm (void) chown(zfile2, owner_uid, group_gid); 88659003Shm } 88759003Shm } 888111388Sgad if (!noaction && !(flags & CE_BINARY)) { 889111388Sgad /* Report the trimming to the old log */ 890111768Sgad (void) log_trim(log, trim_ent); 891111388Sgad } 89213244Sgraichen 89318188Sjkh if (!_numdays) { 89418075Sjkh if (noaction) 89559003Shm printf("rm %s\n", log); 89618075Sjkh else 89759003Shm (void) unlink(log); 89859003Shm } else { 89918075Sjkh if (noaction) 90059003Shm printf("mv %s to %s\n", log, file1); 90159004Shm else { 90259004Shm if (archtodir) 90380646Sobrien movefile(log, file1, perm, owner_uid, 90480646Sobrien group_gid); 90559004Shm else 90659004Shm (void) rename(log, file1); 90759004Shm } 90818075Sjkh } 90918075Sjkh 91059003Shm if (noaction) 91159003Shm printf("Start new log..."); 91259003Shm else { 91394352Ssheldonh strlcpy(tfile, log, sizeof(tfile)); 91494352Ssheldonh strlcat(tfile, ".XXXXXX", sizeof(tfile)); 91594352Ssheldonh mkstemp(tfile); 91694352Ssheldonh fd = creat(tfile, perm); 91759003Shm if (fd < 0) 91859003Shm err(1, "can't start new log"); 91959003Shm if (fchown(fd, owner_uid, group_gid)) 92059003Shm err(1, "can't chmod new log file"); 92159003Shm (void) close(fd); 922111388Sgad if (!(flags & CE_BINARY)) { 923111388Sgad /* Add status message to new log file */ 924111768Sgad if (log_trim(tfile, trim_ent)) 92559003Shm err(1, "can't add status message to log"); 926111388Sgad } 92759003Shm } 92859003Shm if (noaction) 92959003Shm printf("chmod %o %s...\n", perm, log); 93094352Ssheldonh else { 93194352Ssheldonh (void) chmod(tfile, perm); 93294352Ssheldonh if (rename(tfile, log) < 0) { 93394352Ssheldonh err(1, "can't start new log"); 93494352Ssheldonh (void) unlink(tfile); 93594352Ssheldonh } 93694352Ssheldonh } 93725443Sache 93825443Sache pid = 0; 93925443Sache need_notification = notified = 0; 940111768Sgad if (trim_ent->pid_file != NULL) { 94125443Sache need_notification = 1; 942111768Sgad pid = get_pid(trim_ent->pid_file); 94325443Sache } 94425443Sache if (pid) { 94525443Sache if (noaction) { 94625443Sache notified = 1; 94759003Shm printf("kill -%d %d\n", sig, (int) pid); 94859003Shm } else if (kill(pid, sig)) 94959003Shm warn("can't notify daemon, pid %d", (int) pid); 95025443Sache else { 95125443Sache notified = 1; 95225443Sache if (verbose) 95359003Shm printf("daemon pid %d notified\n", (int) pid); 95425443Sache } 95525443Sache } 95680638Sobrien if ((flags & CE_COMPACT) || (flags & CE_BZCOMPACT)) { 95725443Sache if (need_notification && !notified) 95880646Sobrien warnx( 95980646Sobrien "log %s not compressed because daemon not notified", 96080646Sobrien log); 96125443Sache else if (noaction) 96259003Shm printf("Compress %s.0\n", log); 96325443Sache else { 96425443Sache if (notified) { 96525443Sache if (verbose) 96625443Sache printf("small pause to allow daemon to close log\n"); 96731460Sache sleep(10); 96825443Sache } 96959004Shm if (archtodir) { 97080646Sobrien (void) snprintf(file1, sizeof(file1), "%s/%s", 97180646Sobrien dirpart, namepart); 97280638Sobrien if (flags & CE_COMPACT) 973107916Ssobomax compress_log(file1, 974107916Ssobomax flags & CE_COMPACTWAIT); 97580638Sobrien else if (flags & CE_BZCOMPACT) 976107916Ssobomax bzcompress_log(file1, 977107916Ssobomax flags & CE_COMPACTWAIT); 97859004Shm } else { 97980638Sobrien if (flags & CE_COMPACT) 980107916Ssobomax compress_log(log, 981107916Ssobomax flags & CE_COMPACTWAIT); 98280638Sobrien else if (flags & CE_BZCOMPACT) 983107916Ssobomax bzcompress_log(log, 984107916Ssobomax flags & CE_COMPACTWAIT); 98559004Shm } 98625443Sache } 98759003Shm } 98813244Sgraichen} 98913244Sgraichen 99013244Sgraichen/* Log the fact that the logs were turned over */ 99159004Shmstatic int 992111768Sgadlog_trim(const char *log, const struct conf_entry *log_ent) 99313244Sgraichen{ 99459003Shm FILE *f; 995111388Sgad const char *xtra; 99659003Shm 99759003Shm if ((f = fopen(log, "a")) == NULL) 99859003Shm return (-1); 999111388Sgad xtra = ""; 1000111768Sgad if (log_ent->def_cfg) 1001111388Sgad xtra = " using <default> rule"; 1002111388Sgad fprintf(f, "%s %s newsyslog[%d]: logfile turned over%s\n", 1003111388Sgad daytime, hostname, (int) getpid(), xtra); 100459003Shm if (fclose(f) == EOF) 100559003Shm err(1, "log_trim: fclose:"); 100659003Shm return (0); 100713244Sgraichen} 100813244Sgraichen 100959004Shm/* Fork of gzip to compress the old log file */ 101059004Shmstatic void 1011107916Ssobomaxcompress_log(char *log, int dowait) 101213244Sgraichen{ 101359003Shm pid_t pid; 101471299Sjedgar char tmp[MAXPATHLEN]; 101559003Shm 1016107916Ssobomax while (dowait && (wait(NULL) > 0 || errno == EINTR)) 1017107916Ssobomax ; 101871299Sjedgar (void) snprintf(tmp, sizeof(tmp), "%s.0", log); 101925443Sache pid = fork(); 102059003Shm if (pid < 0) 102180638Sobrien err(1, "gzip fork"); 102259003Shm else if (!pid) { 102379452Sbrian (void) execl(_PATH_GZIP, _PATH_GZIP, "-f", tmp, (char *)0); 102443071Swollman err(1, _PATH_GZIP); 102559003Shm } 102613244Sgraichen} 102713244Sgraichen 102880638Sobrien/* Fork of bzip2 to compress the old log file */ 102980638Sobrienstatic void 1030107916Ssobomaxbzcompress_log(char *log, int dowait) 103180638Sobrien{ 103280638Sobrien pid_t pid; 103380638Sobrien char tmp[MAXPATHLEN]; 103480638Sobrien 1035107916Ssobomax while (dowait && (wait(NULL) > 0 || errno == EINTR)) 1036107916Ssobomax ; 103780638Sobrien snprintf(tmp, sizeof(tmp), "%s.0", log); 103880638Sobrien pid = fork(); 103980638Sobrien if (pid < 0) 104080638Sobrien err(1, "bzip2 fork"); 104180638Sobrien else if (!pid) { 104286360Sobrien execl(_PATH_BZIP2, _PATH_BZIP2, "-f", tmp, (char *)0); 104380638Sobrien err(1, _PATH_BZIP2); 104480638Sobrien } 104580638Sobrien} 104680638Sobrien 104713244Sgraichen/* Return size in kilobytes of a file */ 104859004Shmstatic int 104959004Shmsizefile(char *file) 105013244Sgraichen{ 105159003Shm struct stat sb; 105213244Sgraichen 105359003Shm if (stat(file, &sb) < 0) 105459003Shm return (-1); 105559003Shm return (kbytes(dbtob(sb.st_blocks))); 105613244Sgraichen} 105713244Sgraichen 105813244Sgraichen/* Return the age of old log file (file.0) */ 105959004Shmstatic int 106059004Shmage_old_log(char *file) 106113244Sgraichen{ 106259003Shm struct stat sb; 106359003Shm char tmp[MAXPATHLEN + sizeof(".0") + sizeof(COMPRESS_POSTFIX) + 1]; 106413244Sgraichen 106559004Shm if (archtodir) { 106659004Shm char *p; 106759004Shm 106859004Shm /* build name of archive directory into tmp */ 106959004Shm if (*archdirname == '/') { /* absolute */ 107071299Sjedgar strlcpy(tmp, archdirname, sizeof(tmp)); 107159004Shm } else { /* relative */ 107259004Shm /* get directory part of logfile */ 107371299Sjedgar strlcpy(tmp, file, sizeof(tmp)); 107459004Shm if ((p = rindex(tmp, '/')) == NULL) 107559004Shm tmp[0] = '\0'; 107659004Shm else 107759004Shm *(p + 1) = '\0'; 107871299Sjedgar strlcat(tmp, archdirname, sizeof(tmp)); 107959004Shm } 108059004Shm 108171299Sjedgar strlcat(tmp, "/", sizeof(tmp)); 108259004Shm 108359004Shm /* get filename part of logfile */ 108459004Shm if ((p = rindex(file, '/')) == NULL) 108571299Sjedgar strlcat(tmp, file, sizeof(tmp)); 108659004Shm else 108771299Sjedgar strlcat(tmp, p + 1, sizeof(tmp)); 108859004Shm } else { 108971299Sjedgar (void) strlcpy(tmp, file, sizeof(tmp)); 109059004Shm } 109159004Shm 109259003Shm if (stat(strcat(tmp, ".0"), &sb) < 0) 109359003Shm if (stat(strcat(tmp, COMPRESS_POSTFIX), &sb) < 0) 109459003Shm return (-1); 109559003Shm return ((int) (timenow - sb.st_mtime + 1800) / 3600); 109613244Sgraichen} 109713244Sgraichen 109859004Shmstatic pid_t 109980640Sobrienget_pid(const char *pid_file) 110025443Sache{ 110125443Sache FILE *f; 110259003Shm char line[BUFSIZ]; 110325443Sache pid_t pid = 0; 110413244Sgraichen 110559003Shm if ((f = fopen(pid_file, "r")) == NULL) 110625443Sache warn("can't open %s pid file to restart a daemon", 110759003Shm pid_file); 110825443Sache else { 110959003Shm if (fgets(line, BUFSIZ, f)) { 111025443Sache pid = atol(line); 111125443Sache if (pid < MIN_PID || pid > MAX_PID) { 111280646Sobrien warnx("preposterous process number: %d", 111380646Sobrien (int)pid); 111425443Sache pid = 0; 111525443Sache } 111625443Sache } else 111725443Sache warn("can't read %s pid file to restart a daemon", 111859003Shm pid_file); 111959003Shm (void) fclose(f); 112025443Sache } 1121111392Sgad return (pid); 112225443Sache} 112325443Sache 112413244Sgraichen/* Skip Over Blanks */ 112559003Shmchar * 112659004Shmsob(char *p) 112713244Sgraichen{ 112859003Shm while (p && *p && isspace(*p)) 112959003Shm p++; 113059003Shm return (p); 113113244Sgraichen} 113213244Sgraichen 113313244Sgraichen/* Skip Over Non-Blanks */ 113459003Shmchar * 113559004Shmson(char *p) 113613244Sgraichen{ 113759003Shm while (p && *p && !isspace(*p)) 113859003Shm p++; 113959003Shm return (p); 114013244Sgraichen} 114143071Swollman 114243071Swollman/* 114359004Shm * Parse a limited subset of ISO 8601. The specific format is as follows: 114443071Swollman * 114559004Shm * [CC[YY[MM[DD]]]][THH[MM[SS]]] (where `T' is the literal letter) 114643071Swollman * 114759004Shm * We don't accept a timezone specification; missing fields (including timezone) 114859004Shm * are defaulted to the current date but time zero. 114943071Swollman */ 115043071Swollmanstatic time_t 115193659Scjcparse8601(char *s, char *errline) 115243071Swollman{ 115359003Shm char *t; 115493659Scjc time_t tsecs; 115559003Shm struct tm tm, *tmp; 115659003Shm u_long ul; 115743071Swollman 115843071Swollman tmp = localtime(&timenow); 115943071Swollman tm = *tmp; 116043071Swollman 116143071Swollman tm.tm_hour = tm.tm_min = tm.tm_sec = 0; 116243071Swollman 116343071Swollman ul = strtoul(s, &t, 10); 116443071Swollman if (*t != '\0' && *t != 'T') 1165111392Sgad return (-1); 116643071Swollman 116743071Swollman /* 116859003Shm * Now t points either to the end of the string (if no time was 116959003Shm * provided) or to the letter `T' which separates date and time in 117059003Shm * ISO 8601. The pointer arithmetic is the same for either case. 117143071Swollman */ 117243071Swollman switch (t - s) { 117343071Swollman case 8: 117443071Swollman tm.tm_year = ((ul / 1000000) - 19) * 100; 117543071Swollman ul = ul % 1000000; 117643071Swollman case 6: 117780666Swollman tm.tm_year -= tm.tm_year % 100; 117843071Swollman tm.tm_year += ul / 10000; 117943071Swollman ul = ul % 10000; 118043071Swollman case 4: 118143071Swollman tm.tm_mon = (ul / 100) - 1; 118243071Swollman ul = ul % 100; 118343071Swollman case 2: 118443071Swollman tm.tm_mday = ul; 118543071Swollman case 0: 118643071Swollman break; 118743071Swollman default: 1188111392Sgad return (-1); 118943071Swollman } 119043071Swollman 119143071Swollman /* sanity check */ 119243071Swollman if (tm.tm_year < 70 || tm.tm_mon < 0 || tm.tm_mon > 12 119343071Swollman || tm.tm_mday < 1 || tm.tm_mday > 31) 1194111392Sgad return (-1); 119543071Swollman 119643071Swollman if (*t != '\0') { 119743071Swollman s = ++t; 119843071Swollman ul = strtoul(s, &t, 10); 119943071Swollman if (*t != '\0' && !isspace(*t)) 1200111392Sgad return (-1); 120143071Swollman 120243071Swollman switch (t - s) { 120343071Swollman case 6: 120443071Swollman tm.tm_sec = ul % 100; 120543071Swollman ul /= 100; 120643071Swollman case 4: 120743071Swollman tm.tm_min = ul % 100; 120843071Swollman ul /= 100; 120943071Swollman case 2: 121043071Swollman tm.tm_hour = ul; 121143071Swollman case 0: 121243071Swollman break; 121343071Swollman default: 1214111392Sgad return (-1); 121543071Swollman } 121643071Swollman 121743071Swollman /* sanity check */ 121843071Swollman if (tm.tm_sec < 0 || tm.tm_sec > 60 || tm.tm_min < 0 121943071Swollman || tm.tm_min > 59 || tm.tm_hour < 0 || tm.tm_hour > 23) 1220111392Sgad return (-1); 122143071Swollman } 122293659Scjc if ((tsecs = mktime(&tm)) == -1) 122399209Smaxim errx(1, "nonexistent time:\n%s", errline); 1224111392Sgad return (tsecs); 122543071Swollman} 122659004Shm 122759004Shm/* physically move file */ 122859004Shmstatic void 122959004Shmmovefile(char *from, char *to, int perm, int owner_uid, int group_gid) 123059004Shm{ 123159004Shm FILE *src, *dst; 123259004Shm int c; 123359004Shm 123459004Shm if ((src = fopen(from, "r")) == NULL) 123559004Shm err(1, "can't fopen %s for reading", from); 123659004Shm if ((dst = fopen(to, "w")) == NULL) 123759004Shm err(1, "can't fopen %s for writing", to); 123859004Shm if (fchown(fileno(dst), owner_uid, group_gid)) 123959004Shm err(1, "can't fchown %s", to); 124059004Shm if (fchmod(fileno(dst), perm)) 124159004Shm err(1, "can't fchmod %s", to); 124259004Shm 124359004Shm while ((c = getc(src)) != EOF) { 124459004Shm if ((putc(c, dst)) == EOF) 124559004Shm err(1, "error writing to %s", to); 124659004Shm } 124759004Shm 124859004Shm if (ferror(src)) 124959004Shm err(1, "error reading from %s", from); 125059004Shm if ((fclose(src)) != 0) 125159004Shm err(1, "can't fclose %s", to); 125259004Shm if ((fclose(dst)) != 0) 125359004Shm err(1, "can't fclose %s", from); 125459004Shm if ((unlink(from)) != 0) 125559004Shm err(1, "can't unlink %s", from); 125659004Shm} 125759004Shm 125859004Shm/* create one or more directory components of a path */ 125959004Shmstatic void 126059004Shmcreatedir(char *dirpart) 126159004Shm{ 1262111398Sgad int res; 126359004Shm char *s, *d; 126471299Sjedgar char mkdirpath[MAXPATHLEN]; 126559004Shm struct stat st; 126659004Shm 126759004Shm s = dirpart; 126859004Shm d = mkdirpath; 126959004Shm 127059004Shm for (;;) { 127159004Shm *d++ = *s++; 1272111398Sgad if (*s != '/' && *s != '\0') 1273111398Sgad continue; 1274111398Sgad *d = '\0'; 1275111398Sgad res = lstat(mkdirpath, &st); 1276111398Sgad if (res != 0) { 1277111398Sgad if (noaction) { 1278111398Sgad printf("mkdir %s\n", mkdirpath); 1279111398Sgad } else { 1280111398Sgad res = mkdir(mkdirpath, 0755); 1281111398Sgad if (res != 0) 1282111398Sgad err(1, "Error on mkdir(\"%s\") for -a", 1283111398Sgad mkdirpath); 1284111398Sgad } 128559004Shm } 128659004Shm if (*s == '\0') 128759004Shm break; 128859004Shm } 1289111398Sgad if (verbose) 1290111398Sgad printf("created directory '%s' for -a\n", dirpart); 129159004Shm} 129259004Shm 129359004Shm/*- 129459004Shm * Parse a cyclic time specification, the format is as follows: 129559004Shm * 129659004Shm * [Dhh] or [Wd[Dhh]] or [Mdd[Dhh]] 129759004Shm * 129859004Shm * to rotate a logfile cyclic at 129959004Shm * 130059004Shm * - every day (D) within a specific hour (hh) (hh = 0...23) 130159004Shm * - once a week (W) at a specific day (d) OR (d = 0..6, 0 = Sunday) 130259004Shm * - once a month (M) at a specific day (d) (d = 1..31,l|L) 130359004Shm * 130459004Shm * We don't accept a timezone specification; missing fields 130559004Shm * are defaulted to the current date but time zero. 130659004Shm */ 130759004Shmstatic time_t 130893659ScjcparseDWM(char *s, char *errline) 130959004Shm{ 131059004Shm char *t; 131193659Scjc time_t tsecs; 131259004Shm struct tm tm, *tmp; 131380742Sobrien long l; 131459004Shm int nd; 131559004Shm static int mtab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 131659004Shm int WMseen = 0; 131759004Shm int Dseen = 0; 131859004Shm 131959004Shm tmp = localtime(&timenow); 132059004Shm tm = *tmp; 132159004Shm 132259004Shm /* set no. of days per month */ 132359004Shm 132459004Shm nd = mtab[tm.tm_mon]; 132559004Shm 132659004Shm if (tm.tm_mon == 1) { 132759004Shm if (((tm.tm_year + 1900) % 4 == 0) && 132859004Shm ((tm.tm_year + 1900) % 100 != 0) && 132959004Shm ((tm.tm_year + 1900) % 400 == 0)) { 133059004Shm nd++; /* leap year, 29 days in february */ 133159004Shm } 133259004Shm } 133359004Shm tm.tm_hour = tm.tm_min = tm.tm_sec = 0; 133459004Shm 133559004Shm for (;;) { 133659004Shm switch (*s) { 133759004Shm case 'D': 133859004Shm if (Dseen) 1339111392Sgad return (-1); 134059004Shm Dseen++; 134159004Shm s++; 134280742Sobrien l = strtol(s, &t, 10); 134380742Sobrien if (l < 0 || l > 23) 1344111392Sgad return (-1); 134580742Sobrien tm.tm_hour = l; 134659004Shm break; 134759004Shm 134859004Shm case 'W': 134959004Shm if (WMseen) 1350111392Sgad return (-1); 135159004Shm WMseen++; 135259004Shm s++; 135380742Sobrien l = strtol(s, &t, 10); 135480742Sobrien if (l < 0 || l > 6) 1355111392Sgad return (-1); 135680742Sobrien if (l != tm.tm_wday) { 135759004Shm int save; 135859004Shm 135980742Sobrien if (l < tm.tm_wday) { 136059004Shm save = 6 - tm.tm_wday; 136180742Sobrien save += (l + 1); 136259004Shm } else { 136380742Sobrien save = l - tm.tm_wday; 136459004Shm } 136559004Shm 136659004Shm tm.tm_mday += save; 136759004Shm 136859004Shm if (tm.tm_mday > nd) { 136959004Shm tm.tm_mon++; 137059004Shm tm.tm_mday = tm.tm_mday - nd; 137159004Shm } 137259004Shm } 137359004Shm break; 137459004Shm 137559004Shm case 'M': 137659004Shm if (WMseen) 1377111392Sgad return (-1); 137859004Shm WMseen++; 137959004Shm s++; 138059004Shm if (tolower(*s) == 'l') { 138159004Shm tm.tm_mday = nd; 138259004Shm s++; 138359004Shm t = s; 138459004Shm } else { 138580742Sobrien l = strtol(s, &t, 10); 138680742Sobrien if (l < 1 || l > 31) 1387111392Sgad return (-1); 138859004Shm 138980742Sobrien if (l > nd) 1390111392Sgad return (-1); 139180742Sobrien tm.tm_mday = l; 139259004Shm } 139359004Shm break; 139459004Shm 139559004Shm default: 139659004Shm return (-1); 139759004Shm break; 139859004Shm } 139959004Shm 140059004Shm if (*t == '\0' || isspace(*t)) 140159004Shm break; 140259004Shm else 140359004Shm s = t; 140459004Shm } 140593659Scjc if ((tsecs = mktime(&tm)) == -1) 140699209Smaxim errx(1, "nonexistent time:\n%s", errline); 1407111392Sgad return (tsecs); 140859004Shm} 1409