newsyslog.c revision 161412
1130167Sgad/*- 2130167Sgad * ------+---------+---------+-------- + --------+---------+---------+---------* 3130167Sgad * This file includes significant modifications done by: 4130167Sgad * Copyright (c) 2003, 2004 - Garance Alistair Drosehn <gad@FreeBSD.org>. 5130167Sgad * All rights reserved. 6130167Sgad * 7130167Sgad * Redistribution and use in source and binary forms, with or without 8130167Sgad * modification, are permitted provided that the following conditions 9130167Sgad * are met: 10130167Sgad * 1. Redistributions of source code must retain the above copyright 11130167Sgad * notice, this list of conditions and the following disclaimer. 12130167Sgad * 2. Redistributions in binary form must reproduce the above copyright 13130167Sgad * notice, this list of conditions and the following disclaimer in the 14130167Sgad * documentation and/or other materials provided with the distribution. 15130167Sgad * 16130167Sgad * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17130167Sgad * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18130167Sgad * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19130167Sgad * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20130167Sgad * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21130167Sgad * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22130167Sgad * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23130167Sgad * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24130167Sgad * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25130167Sgad * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26130167Sgad * SUCH DAMAGE. 27130167Sgad * 28130167Sgad * ------+---------+---------+-------- + --------+---------+---------+---------* 29130167Sgad */ 30130167Sgad 3113244Sgraichen/* 3213244Sgraichen * This file contains changes from the Open Software Foundation. 3313244Sgraichen */ 3413244Sgraichen 3513244Sgraichen/* 3659004Shm * Copyright 1988, 1989 by the Massachusetts Institute of Technology 3759004Shm * 3859004Shm * Permission to use, copy, modify, and distribute this software and its 3959004Shm * documentation for any purpose and without fee is hereby granted, provided 4059004Shm * that the above copyright notice appear in all copies and that both that 4159004Shm * copyright notice and this permission notice appear in supporting 4259004Shm * documentation, and that the names of M.I.T. and the M.I.T. S.I.P.B. not be 4359004Shm * used in advertising or publicity pertaining to distribution of the 4459004Shm * software without specific, written prior permission. M.I.T. and the M.I.T. 4559004Shm * S.I.P.B. make no representations about the suitability of this software 4659004Shm * for any purpose. It is provided "as is" without express or implied 4759004Shm * warranty. 4859004Shm * 4959004Shm */ 5013244Sgraichen 5113244Sgraichen/* 5259004Shm * newsyslog - roll over selected logs at the appropriate time, keeping the a 5359004Shm * specified number of backup files around. 5413244Sgraichen */ 5513244Sgraichen 56114601Sobrien#include <sys/cdefs.h> 57114601Sobrien__FBSDID("$FreeBSD: head/usr.sbin/newsyslog/newsyslog.c 161412 2006-08-17 18:15:43Z delphij $"); 5813244Sgraichen 59130045Sgad#define OSF 6013244Sgraichen#ifndef COMPRESS_POSTFIX 61130045Sgad#define COMPRESS_POSTFIX ".gz" 6213244Sgraichen#endif 6380638Sobrien#ifndef BZCOMPRESS_POSTFIX 6480638Sobrien#define BZCOMPRESS_POSTFIX ".bz2" 6580638Sobrien#endif 6613244Sgraichen 6796001Smaxim#include <sys/param.h> 68130167Sgad#include <sys/queue.h> 6996001Smaxim#include <sys/stat.h> 7096001Smaxim#include <sys/wait.h> 7196001Smaxim 7230160Scharnier#include <ctype.h> 7330160Scharnier#include <err.h> 7495999Smaxim#include <errno.h> 7530160Scharnier#include <fcntl.h> 76111773Sgad#include <fnmatch.h> 77106905Ssobomax#include <glob.h> 7830160Scharnier#include <grp.h> 7943071Swollman#include <paths.h> 8030160Scharnier#include <pwd.h> 8130160Scharnier#include <signal.h> 8213244Sgraichen#include <stdio.h> 8313244Sgraichen#include <stdlib.h> 8413244Sgraichen#include <string.h> 8543071Swollman#include <time.h> 8616240Salex#include <unistd.h> 8713244Sgraichen 8843071Swollman#include "pathnames.h" 89119998Sgad#include "extern.h" 9043071Swollman 91111768Sgad/* 92111768Sgad * Bit-values for the 'flags' parsed from a config-file entry. 93111768Sgad */ 94130045Sgad#define CE_COMPACT 0x0001 /* Compact the achived log files with gzip. */ 95130045Sgad#define CE_BZCOMPACT 0x0002 /* Compact the achived log files with bzip2. */ 96130045Sgad#define CE_BINARY 0x0008 /* Logfile is in binary, do not add status */ 97111768Sgad /* messages to logfile(s) when rotating. */ 98130045Sgad#define CE_NOSIGNAL 0x0010 /* There is no process to signal when */ 99111768Sgad /* trimming this file. */ 100130045Sgad#define CE_TRIMAT 0x0020 /* trim file at a specific time. */ 101130045Sgad#define CE_GLOB 0x0040 /* name of the log is file name pattern. */ 102130045Sgad#define CE_SIGNALGROUP 0x0080 /* Signal a process-group instead of a single */ 103112003Sgad /* process when trimming this file. */ 104130045Sgad#define CE_CREATE 0x0100 /* Create the log file if it does not exist. */ 105130043Sgad#define CE_NODUMP 0x0200 /* Set 'nodump' on newly created log file. */ 10643071Swollman 107130045Sgad#define MIN_PID 5 /* Don't touch pids lower than this */ 108130045Sgad#define MAX_PID 99999 /* was lower, see /usr/include/sys/proc.h */ 109111781Sgad 110130045Sgad#define kbytes(size) (((size) + 1023) >> 10) 111111781Sgad 112130165Sgad#define DEFAULT_MARKER "<default>" 113130165Sgad#define DEBUG_MARKER "<debug>" 114130165Sgad 11513244Sgraichenstruct conf_entry { 11659003Shm char *log; /* Name of the log */ 11759003Shm char *pid_file; /* PID file */ 118111772Sgad char *r_reason; /* The reason this file is being rotated */ 119114137Sgad int firstcreate; /* Creating log for the first time (-C). */ 120111772Sgad int rotate; /* Non-zero if this file should be rotated */ 121130165Sgad int fsize; /* size found for the log file */ 122111779Sgad uid_t uid; /* Owner of log */ 123111779Sgad gid_t gid; /* Group of log */ 12459003Shm int numlogs; /* Number of logs to keep */ 125130165Sgad int trsize; /* Size cutoff to trigger trimming the log */ 12659003Shm int hours; /* Hours between log trimming */ 127120361Sgad struct ptime_data *trim_at; /* Specific time to do trimming */ 128139655Sdelphij unsigned int permissions; /* File permissions on the log */ 12980646Sobrien int flags; /* CE_COMPACT, CE_BZCOMPACT, CE_BINARY */ 13059003Shm int sig; /* Signal to send */ 131111388Sgad int def_cfg; /* Using the <default> rule for this file */ 13259003Shm struct conf_entry *next;/* Linked list pointer */ 13313244Sgraichen}; 13413244Sgraichen 135130167Sgadstruct sigwork_entry { 136130167Sgad SLIST_ENTRY(sigwork_entry) sw_nextp; 137130167Sgad int sw_signum; /* the signal to send */ 138130167Sgad int sw_pidok; /* true if pid value is valid */ 139130167Sgad pid_t sw_pid; /* the process id from the PID file */ 140130167Sgad const char *sw_pidtype; /* "daemon" or "process group" */ 141130167Sgad char sw_fname[1]; /* file the PID was read from */ 142130167Sgad}; 143130167Sgad 144130167Sgadstruct zipwork_entry { 145130167Sgad SLIST_ENTRY(zipwork_entry) zw_nextp; 146130167Sgad const struct conf_entry *zw_conf; /* for chown/perm/flag info */ 147130167Sgad const struct sigwork_entry *zw_swork; /* to know success of signal */ 148130167Sgad int zw_fsize; /* size of the file to compress */ 149130167Sgad char zw_fname[1]; /* the file to compress */ 150130167Sgad}; 151130167Sgad 152130165Sgadtypedef enum { 153130165Sgad FREE_ENT, KEEP_ENT 154130165Sgad} fk_entry; 155111388Sgad 156130167SgadSLIST_HEAD(swlisthead, sigwork_entry) swhead = SLIST_HEAD_INITIALIZER(swhead); 157130167SgadSLIST_HEAD(zwlisthead, zipwork_entry) zwhead = SLIST_HEAD_INITIALIZER(zwhead); 158130167Sgad 159120361Sgadint dbg_at_times; /* -D Show details of 'trim_at' code */ 160120361Sgad 16159004Shmint archtodir = 0; /* Archive old logfiles to other directory */ 162114137Sgadint createlogs; /* Create (non-GLOB) logfiles which do not */ 163114137Sgad /* already exist. 1=='for entries with */ 164114137Sgad /* C flag', 2=='for all entries'. */ 16559003Shmint verbose = 0; /* Print out what's going on */ 16659003Shmint needroot = 1; /* Root privs are necessary */ 16759003Shmint noaction = 0; /* Don't do anything, just show it */ 168143106Sbrooksint norotate = 0; /* Don't rotate */ 169111768Sgadint nosignal; /* Do not send any signals */ 17059003Shmint force = 0; /* Force the trim no matter what */ 171111772Sgadint rotatereq = 0; /* -R = Always rotate the file(s) as given */ 172111772Sgad /* on the command (this also requires */ 173111772Sgad /* that a list of files *are* given on */ 174111772Sgad /* the run command). */ 175111772Sgadchar *requestor; /* The name given on a -R request */ 17659004Shmchar *archdirname; /* Directory path to old logfiles archive */ 177136127Sbrookschar *destdir = NULL; /* Directory to treat at root for logs */ 178111773Sgadconst char *conf; /* Configuration file to use */ 17959003Shm 180120361Sgadstruct ptime_data *dbg_timenow; /* A "timenow" value set via -D option */ 181120361Sgadstruct ptime_data *timenow; /* The time to use for checking at-fields */ 182120361Sgad 183159998Sgad#define DAYTIME_LEN 16 184159998Sgadchar daytime[DAYTIME_LEN]; /* The current time in human readable form, 185159998Sgad * used for rotation-tracking messages. */ 18671299Sjedgarchar hostname[MAXHOSTNAMELEN]; /* hostname */ 18713244Sgraichen 188111773Sgadstatic struct conf_entry *get_worklist(char **files); 189111773Sgadstatic void parse_file(FILE *cf, const char *cfname, struct conf_entry **work_p, 190112020Sgad struct conf_entry **glob_p, struct conf_entry **defconf_p); 19116240Salexstatic char *sob(char *p); 19216240Salexstatic char *son(char *p); 193119102Sgadstatic int isnumberstr(const char *); 19459003Shmstatic char *missing_field(char *p, char *errline); 195130165Sgadstatic void change_attrs(const char *, const struct conf_entry *); 196130165Sgadstatic fk_entry do_entry(struct conf_entry *); 197130165Sgadstatic fk_entry do_rotate(const struct conf_entry *); 198130167Sgadstatic void do_sigwork(struct sigwork_entry *); 199130167Sgadstatic void do_zipwork(struct zipwork_entry *); 200130167Sgadstatic struct sigwork_entry * 201130167Sgad save_sigwork(const struct conf_entry *); 202130167Sgadstatic struct zipwork_entry * 203130167Sgad save_zipwork(const struct conf_entry *, const struct 204130167Sgad sigwork_entry *, int, const char *); 205130167Sgadstatic void set_swpid(struct sigwork_entry *, const struct conf_entry *); 206130165Sgadstatic int sizefile(const char *); 207112020Sgadstatic void expand_globs(struct conf_entry **work_p, 208112020Sgad struct conf_entry **glob_p); 209112020Sgadstatic void free_clist(struct conf_entry **firstent); 210111388Sgadstatic void free_entry(struct conf_entry *ent); 211111388Sgadstatic struct conf_entry *init_entry(const char *fname, 212111388Sgad struct conf_entry *src_entry); 213111781Sgadstatic void parse_args(int argc, char **argv); 214119904Sgadstatic int parse_doption(const char *doption); 21580640Sobrienstatic void usage(void); 216119926Sgadstatic int log_trim(const char *logname, const struct conf_entry *log_ent); 21716240Salexstatic int age_old_log(char *file); 218130038Sgadstatic void savelog(char *from, char *to); 219114137Sgadstatic void createdir(const struct conf_entry *ent, char *dirpart); 220114137Sgadstatic void createlog(const struct conf_entry *ent); 22113244Sgraichen 222111768Sgad/* 223129975Sgad * All the following take a parameter of 'int', but expect values in the 224129975Sgad * range of unsigned char. Define wrappers which take values of type 'char', 225129975Sgad * whether signed or unsigned, and ensure they end up in the right range. 226111768Sgad */ 227129975Sgad#define isdigitch(Anychar) isdigit((u_char)(Anychar)) 228129975Sgad#define isprintch(Anychar) isprint((u_char)(Anychar)) 229129975Sgad#define isspacech(Anychar) isspace((u_char)(Anychar)) 230129975Sgad#define tolowerch(Anychar) tolower((u_char)(Anychar)) 231111768Sgad 23259004Shmint 23359004Shmmain(int argc, char **argv) 23413244Sgraichen{ 235130165Sgad fk_entry free_or_keep; 23659003Shm struct conf_entry *p, *q; 237130167Sgad struct sigwork_entry *stmp; 238130167Sgad struct zipwork_entry *ztmp; 23925443Sache 240130167Sgad SLIST_INIT(&swhead); 241130167Sgad SLIST_INIT(&zwhead); 242130167Sgad 243111781Sgad parse_args(argc, argv); 244111773Sgad argc -= optind; 245111773Sgad argv += optind; 246111773Sgad 24759003Shm if (needroot && getuid() && geteuid()) 24859003Shm errx(1, "must have root privs"); 249111773Sgad p = q = get_worklist(argv); 25059003Shm 251130165Sgad /* 252130165Sgad * Rotate all the files which need to be rotated. Note that 253130165Sgad * some users have *hundreds* of entries in newsyslog.conf! 254130165Sgad */ 25559003Shm while (p) { 256130165Sgad free_or_keep = do_entry(p); 25759003Shm p = p->next; 258130165Sgad if (free_or_keep == FREE_ENT) 259130165Sgad free_entry(q); 26059003Shm q = p; 26159003Shm } 262130165Sgad 263130167Sgad /* 264130167Sgad * Send signals to any processes which need a signal to tell 265130167Sgad * them to close and re-open the log file(s) we have rotated. 266130167Sgad * Note that zipwork_entries include pointers to these 267130167Sgad * sigwork_entry's, so we can not free the entries here. 268130167Sgad */ 269130167Sgad if (!SLIST_EMPTY(&swhead)) { 270130204Sgad if (noaction || verbose) 271130167Sgad printf("Signal all daemon process(es)...\n"); 272130167Sgad SLIST_FOREACH(stmp, &swhead, sw_nextp) 273130167Sgad do_sigwork(stmp); 274130204Sgad if (noaction) 275130204Sgad printf("\tsleep 10\n"); 276130204Sgad else { 277130204Sgad if (verbose) 278130204Sgad printf("Pause 10 seconds to allow daemon(s)" 279130204Sgad " to close log file(s)\n"); 280130204Sgad sleep(10); 281130204Sgad } 282130167Sgad } 283130167Sgad /* 284130167Sgad * Compress all files that we're expected to compress, now 285130167Sgad * that all processes should have closed the files which 286130167Sgad * have been rotated. 287130167Sgad */ 288130167Sgad if (!SLIST_EMPTY(&zwhead)) { 289130204Sgad if (noaction || verbose) 290130167Sgad printf("Compress all rotated log file(s)...\n"); 291130167Sgad while (!SLIST_EMPTY(&zwhead)) { 292130167Sgad ztmp = SLIST_FIRST(&zwhead); 293130167Sgad do_zipwork(ztmp); 294130167Sgad SLIST_REMOVE_HEAD(&zwhead, zw_nextp); 295130167Sgad free(ztmp); 296130167Sgad } 297130167Sgad } 298130167Sgad /* Now free all the sigwork entries. */ 299130167Sgad while (!SLIST_EMPTY(&swhead)) { 300130167Sgad stmp = SLIST_FIRST(&swhead); 301130167Sgad SLIST_REMOVE_HEAD(&swhead, sw_nextp); 302130167Sgad free(stmp); 303130167Sgad } 304130167Sgad 30595999Smaxim while (wait(NULL) > 0 || errno == EINTR) 30695999Smaxim ; 30759003Shm return (0); 30813244Sgraichen} 30913244Sgraichen 310111388Sgadstatic struct conf_entry * 311111388Sgadinit_entry(const char *fname, struct conf_entry *src_entry) 312111388Sgad{ 313111388Sgad struct conf_entry *tempwork; 314111388Sgad 315111388Sgad if (verbose > 4) 316111388Sgad printf("\t--> [creating entry for %s]\n", fname); 317111388Sgad 318111388Sgad tempwork = malloc(sizeof(struct conf_entry)); 319111388Sgad if (tempwork == NULL) 320111388Sgad err(1, "malloc of conf_entry for %s", fname); 321111388Sgad 322136174Sbrooks if (destdir == NULL || fname[0] != '/') 323136127Sbrooks tempwork->log = strdup(fname); 324136127Sbrooks else 325136127Sbrooks asprintf(&tempwork->log, "%s%s", destdir, fname); 326111388Sgad if (tempwork->log == NULL) 327111388Sgad err(1, "strdup for %s", fname); 328111388Sgad 329111388Sgad if (src_entry != NULL) { 330111388Sgad tempwork->pid_file = NULL; 331111388Sgad if (src_entry->pid_file) 332111388Sgad tempwork->pid_file = strdup(src_entry->pid_file); 333111772Sgad tempwork->r_reason = NULL; 334114137Sgad tempwork->firstcreate = 0; 335111772Sgad tempwork->rotate = 0; 336130165Sgad tempwork->fsize = -1; 337111388Sgad tempwork->uid = src_entry->uid; 338111388Sgad tempwork->gid = src_entry->gid; 339111388Sgad tempwork->numlogs = src_entry->numlogs; 340130165Sgad tempwork->trsize = src_entry->trsize; 341111388Sgad tempwork->hours = src_entry->hours; 342120361Sgad tempwork->trim_at = NULL; 343120361Sgad if (src_entry->trim_at != NULL) 344120361Sgad tempwork->trim_at = ptime_init(src_entry->trim_at); 345111388Sgad tempwork->permissions = src_entry->permissions; 346111388Sgad tempwork->flags = src_entry->flags; 347111388Sgad tempwork->sig = src_entry->sig; 348111388Sgad tempwork->def_cfg = src_entry->def_cfg; 349111388Sgad } else { 350111388Sgad /* Initialize as a "do-nothing" entry */ 351111388Sgad tempwork->pid_file = NULL; 352111772Sgad tempwork->r_reason = NULL; 353114137Sgad tempwork->firstcreate = 0; 354111772Sgad tempwork->rotate = 0; 355130165Sgad tempwork->fsize = -1; 356111779Sgad tempwork->uid = (uid_t)-1; 357111779Sgad tempwork->gid = (gid_t)-1; 358111388Sgad tempwork->numlogs = 1; 359130165Sgad tempwork->trsize = -1; 360111388Sgad tempwork->hours = -1; 361120361Sgad tempwork->trim_at = NULL; 362111388Sgad tempwork->permissions = 0; 363111388Sgad tempwork->flags = 0; 364111388Sgad tempwork->sig = SIGHUP; 365111388Sgad tempwork->def_cfg = 0; 366111388Sgad } 367111388Sgad tempwork->next = NULL; 368111388Sgad 369111388Sgad return (tempwork); 370111388Sgad} 371111388Sgad 37259004Shmstatic void 373111388Sgadfree_entry(struct conf_entry *ent) 374111388Sgad{ 375111388Sgad 376111388Sgad if (ent == NULL) 377111388Sgad return; 378111388Sgad 379111388Sgad if (ent->log != NULL) { 380111388Sgad if (verbose > 4) 381111388Sgad printf("\t--> [freeing entry for %s]\n", ent->log); 382111388Sgad free(ent->log); 383111388Sgad ent->log = NULL; 384111388Sgad } 385111388Sgad 386111388Sgad if (ent->pid_file != NULL) { 387111388Sgad free(ent->pid_file); 388111388Sgad ent->pid_file = NULL; 389111388Sgad } 390111388Sgad 391111772Sgad if (ent->r_reason != NULL) { 392111772Sgad free(ent->r_reason); 393111772Sgad ent->r_reason = NULL; 394111772Sgad } 395111772Sgad 396120361Sgad if (ent->trim_at != NULL) { 397120361Sgad ptime_free(ent->trim_at); 398120361Sgad ent->trim_at = NULL; 399120361Sgad } 400120361Sgad 401111388Sgad free(ent); 402111388Sgad} 403111388Sgad 404111388Sgadstatic void 405112020Sgadfree_clist(struct conf_entry **firstent) 406112020Sgad{ 407112020Sgad struct conf_entry *ent, *nextent; 408112020Sgad 409112020Sgad if (firstent == NULL) 410112020Sgad return; /* There is nothing to do. */ 411112020Sgad 412112020Sgad ent = *firstent; 413112020Sgad firstent = NULL; 414112020Sgad 415112020Sgad while (ent) { 416112020Sgad nextent = ent->next; 417112020Sgad free_entry(ent); 418112020Sgad ent = nextent; 419112020Sgad } 420112020Sgad} 421112020Sgad 422130165Sgadstatic fk_entry 42359004Shmdo_entry(struct conf_entry * ent) 42413244Sgraichen{ 425130045Sgad#define REASON_MAX 80 426130165Sgad int modtime; 427130165Sgad fk_entry free_or_keep; 428120361Sgad double diffsecs; 429111772Sgad char temp_reason[REASON_MAX]; 43059003Shm 431130165Sgad free_or_keep = FREE_ENT; 43259003Shm if (verbose) { 43359003Shm if (ent->flags & CE_COMPACT) 43459003Shm printf("%s <%dZ>: ", ent->log, ent->numlogs); 43580638Sobrien else if (ent->flags & CE_BZCOMPACT) 43680638Sobrien printf("%s <%dJ>: ", ent->log, ent->numlogs); 43759003Shm else 43859003Shm printf("%s <%d>: ", ent->log, ent->numlogs); 43959003Shm } 440130165Sgad ent->fsize = sizefile(ent->log); 44159003Shm modtime = age_old_log(ent->log); 442111772Sgad ent->rotate = 0; 443114137Sgad ent->firstcreate = 0; 444130165Sgad if (ent->fsize < 0) { 445114137Sgad /* 446114137Sgad * If either the C flag or the -C option was specified, 447114137Sgad * and if we won't be creating the file, then have the 448114137Sgad * verbose message include a hint as to why the file 449114137Sgad * will not be created. 450114137Sgad */ 451114137Sgad temp_reason[0] = '\0'; 452114137Sgad if (createlogs > 1) 453114137Sgad ent->firstcreate = 1; 454114137Sgad else if ((ent->flags & CE_CREATE) && createlogs) 455114137Sgad ent->firstcreate = 1; 456114137Sgad else if (ent->flags & CE_CREATE) 457159998Sgad strlcpy(temp_reason, " (no -C option)", REASON_MAX); 458114137Sgad else if (createlogs) 459159998Sgad strlcpy(temp_reason, " (no C flag)", REASON_MAX); 460114137Sgad 461114137Sgad if (ent->firstcreate) { 462114137Sgad if (verbose) 463114137Sgad printf("does not exist -> will create.\n"); 464114137Sgad createlog(ent); 465114137Sgad } else if (verbose) { 466114137Sgad printf("does not exist, skipped%s.\n", temp_reason); 467114137Sgad } 46859003Shm } else { 469111772Sgad if (ent->flags & CE_TRIMAT && !force && !rotatereq) { 470120361Sgad diffsecs = ptimeget_diff(timenow, ent->trim_at); 471120361Sgad if (diffsecs < 0.0) { 472120361Sgad /* trim_at is some time in the future. */ 473120361Sgad if (verbose) { 474120361Sgad ptime_adjust4dst(ent->trim_at, 475120361Sgad timenow); 47643071Swollman printf("--> will trim at %s", 477120361Sgad ptimeget_ctime(ent->trim_at)); 478120361Sgad } 479130165Sgad return (free_or_keep); 480120361Sgad } else if (diffsecs >= 3600.0) { 481120361Sgad /* 482120361Sgad * trim_at is more than an hour in the past, 483120361Sgad * so find the next valid trim_at time, and 484120361Sgad * tell the user what that will be. 485120361Sgad */ 486120361Sgad if (verbose && dbg_at_times) 487120361Sgad printf("\n\t--> prev trim at %s\t", 488120361Sgad ptimeget_ctime(ent->trim_at)); 489120361Sgad if (verbose) { 490120361Sgad ptimeset_nxtime(ent->trim_at); 491120361Sgad printf("--> will trim at %s", 492120361Sgad ptimeget_ctime(ent->trim_at)); 493120361Sgad } 494130165Sgad return (free_or_keep); 495120361Sgad } else if (verbose && noaction && dbg_at_times) { 496120361Sgad /* 497120361Sgad * If we are just debugging at-times, then 498120361Sgad * a detailed message is helpful. Also 499120361Sgad * skip "doing" any commands, since they 500120361Sgad * would all be turned off by no-action. 501120361Sgad */ 502120361Sgad printf("\n\t--> timematch at %s", 503120361Sgad ptimeget_ctime(ent->trim_at)); 504130165Sgad return (free_or_keep); 50543071Swollman } else if (verbose && ent->hours <= 0) { 50643071Swollman printf("--> time is up\n"); 50743071Swollman } 50843071Swollman } 509130165Sgad if (verbose && (ent->trsize > 0)) 510130165Sgad printf("size (Kb): %d [%d] ", ent->fsize, ent->trsize); 51159003Shm if (verbose && (ent->hours > 0)) 51259003Shm printf(" age (hr): %d [%d] ", modtime, ent->hours); 513111772Sgad 514111772Sgad /* 515111772Sgad * Figure out if this logfile needs to be rotated. 516111772Sgad */ 517111772Sgad temp_reason[0] = '\0'; 518111772Sgad if (rotatereq) { 519111772Sgad ent->rotate = 1; 520111772Sgad snprintf(temp_reason, REASON_MAX, " due to -R from %s", 521111772Sgad requestor); 522111772Sgad } else if (force) { 523111772Sgad ent->rotate = 1; 524111772Sgad snprintf(temp_reason, REASON_MAX, " due to -F request"); 525130165Sgad } else if ((ent->trsize > 0) && (ent->fsize >= ent->trsize)) { 526111772Sgad ent->rotate = 1; 527111772Sgad snprintf(temp_reason, REASON_MAX, " due to size>%dK", 528130165Sgad ent->trsize); 529111772Sgad } else if (ent->hours <= 0 && (ent->flags & CE_TRIMAT)) { 530111772Sgad ent->rotate = 1; 531111772Sgad } else if ((ent->hours > 0) && ((modtime >= ent->hours) || 532111772Sgad (modtime < 0))) { 533111772Sgad ent->rotate = 1; 534111772Sgad } 535111772Sgad 536111772Sgad /* 537111772Sgad * If the file needs to be rotated, then rotate it. 538111772Sgad */ 539143106Sbrooks if (ent->rotate && !norotate) { 540111772Sgad if (temp_reason[0] != '\0') 541111772Sgad ent->r_reason = strdup(temp_reason); 54259003Shm if (verbose) 54359003Shm printf("--> trimming log....\n"); 54459003Shm if (noaction && !verbose) { 54559003Shm if (ent->flags & CE_COMPACT) 54659003Shm printf("%s <%dZ>: trimming\n", 54759003Shm ent->log, ent->numlogs); 54880638Sobrien else if (ent->flags & CE_BZCOMPACT) 54980638Sobrien printf("%s <%dJ>: trimming\n", 55080638Sobrien ent->log, ent->numlogs); 55159003Shm else 55259003Shm printf("%s <%d>: trimming\n", 55359003Shm ent->log, ent->numlogs); 55459003Shm } 555130165Sgad free_or_keep = do_rotate(ent); 55659003Shm } else { 55759003Shm if (verbose) 55859003Shm printf("--> skipping\n"); 55959003Shm } 56059003Shm } 561130165Sgad return (free_or_keep); 562111772Sgad#undef REASON_MAX 56313244Sgraichen} 56413244Sgraichen 56559004Shmstatic void 566111781Sgadparse_args(int argc, char **argv) 56713244Sgraichen{ 568111781Sgad int ch; 56959003Shm char *p; 57013244Sgraichen 571120361Sgad timenow = ptime_init(NULL); 572120361Sgad ptimeset_time(timenow, time(NULL)); 573159998Sgad strlcpy(daytime, ptimeget_ctime(timenow) + 4, DAYTIME_LEN); 57413244Sgraichen 57559003Shm /* Let's get our hostname */ 576111781Sgad (void)gethostname(hostname, sizeof(hostname)); 57713244Sgraichen 57813244Sgraichen /* Truncate domain */ 579111781Sgad if ((p = strchr(hostname, '.')) != NULL) 58013244Sgraichen *p = '\0'; 581111768Sgad 582111768Sgad /* Parse command line options. */ 583143106Sbrooks while ((ch = getopt(argc, argv, "a:d:f:nrsvCD:FNR:")) != -1) 584111781Sgad switch (ch) { 58559004Shm case 'a': 58659004Shm archtodir++; 58759004Shm archdirname = optarg; 58859004Shm break; 589136127Sbrooks case 'd': 590136127Sbrooks destdir = optarg; 591136127Sbrooks break; 592111768Sgad case 'f': 593111768Sgad conf = optarg; 594111768Sgad break; 595111768Sgad case 'n': 596111768Sgad noaction++; 597111768Sgad break; 59859003Shm case 'r': 59959003Shm needroot = 0; 60059003Shm break; 601111768Sgad case 's': 602111768Sgad nosignal = 1; 603111768Sgad break; 60459003Shm case 'v': 60559003Shm verbose++; 60659003Shm break; 607114137Sgad case 'C': 608114137Sgad /* Useful for things like rc.diskless... */ 609114137Sgad createlogs++; 610114137Sgad break; 611119904Sgad case 'D': 612119904Sgad /* 613119904Sgad * Set some debugging option. The specific option 614119904Sgad * depends on the value of optarg. These options 615119904Sgad * may come and go without notice or documentation. 616119904Sgad */ 617119904Sgad if (parse_doption(optarg)) 618119904Sgad break; 619119904Sgad usage(); 620119904Sgad /* NOTREACHED */ 62134584Spst case 'F': 62234584Spst force++; 62334584Spst break; 624143106Sbrooks case 'N': 625143106Sbrooks norotate++; 626143106Sbrooks break; 627111772Sgad case 'R': 628111772Sgad rotatereq++; 629111772Sgad requestor = strdup(optarg); 630111772Sgad break; 631111781Sgad case 'm': /* Used by OpenBSD for "monitor mode" */ 63259003Shm default: 63359003Shm usage(); 634111768Sgad /* NOTREACHED */ 63559003Shm } 636111772Sgad 637143106Sbrooks if (force && norotate) { 638143106Sbrooks warnx("Only one of -F and -N may be specified."); 639143106Sbrooks usage(); 640143106Sbrooks /* NOTREACHED */ 641143106Sbrooks } 642143106Sbrooks 643111772Sgad if (rotatereq) { 644111772Sgad if (optind == argc) { 645111772Sgad warnx("At least one filename must be given when -R is specified."); 646111772Sgad usage(); 647111772Sgad /* NOTREACHED */ 648111772Sgad } 649111772Sgad /* Make sure "requestor" value is safe for a syslog message. */ 650111772Sgad for (p = requestor; *p != '\0'; p++) { 651111772Sgad if (!isprintch(*p) && (*p != '\t')) 652111772Sgad *p = '.'; 653111772Sgad } 654111772Sgad } 655119904Sgad 656119904Sgad if (dbg_timenow) { 657119904Sgad /* 658119904Sgad * Note that the 'daytime' variable is not changed. 659119904Sgad * That is only used in messages that track when a 660119904Sgad * logfile is rotated, and if a file *is* rotated, 661119904Sgad * then it will still rotated at the "real now" time. 662119904Sgad */ 663120361Sgad ptime_free(timenow); 664119904Sgad timenow = dbg_timenow; 665119904Sgad fprintf(stderr, "Debug: Running as if TimeNow is %s", 666120361Sgad ptimeget_ctime(dbg_timenow)); 667119904Sgad } 668119904Sgad 66943071Swollman} 67013244Sgraichen 671120361Sgad/* 672120361Sgad * These debugging options are mainly meant for developer use, such 673120361Sgad * as writing regression-tests. They would not be needed by users 674120361Sgad * during normal operation of newsyslog... 675120361Sgad */ 676119904Sgadstatic int 677119904Sgadparse_doption(const char *doption) 678119904Sgad{ 679119904Sgad const char TN[] = "TN="; 680120361Sgad int res; 681119904Sgad 682119904Sgad if (strncmp(doption, TN, sizeof(TN) - 1) == 0) { 683119904Sgad /* 684120361Sgad * The "TimeNow" debugging option. This might be off 685120361Sgad * by an hour when crossing a timezone change. 686119904Sgad */ 687120361Sgad dbg_timenow = ptime_init(NULL); 688120361Sgad res = ptime_relparse(dbg_timenow, PTM_PARSE_ISO8601, 689120361Sgad time(NULL), doption + sizeof(TN) - 1); 690120361Sgad if (res == -2) { 691120361Sgad warnx("Non-existent time specified on -D %s", doption); 692120361Sgad return (0); /* failure */ 693120361Sgad } else if (res < 0) { 694119904Sgad warnx("Malformed time given on -D %s", doption); 695119904Sgad return (0); /* failure */ 696119904Sgad } 697119904Sgad return (1); /* successfully parsed */ 698119904Sgad 699119904Sgad } 700119904Sgad 701120361Sgad if (strcmp(doption, "ats") == 0) { 702120361Sgad dbg_at_times++; 703120361Sgad return (1); /* successfully parsed */ 704120361Sgad } 705120361Sgad 706159968Sgad /* XXX - This check could probably be dropped. */ 707159968Sgad if ((strcmp(doption, "neworder") == 0) || (strcmp(doption, "oldorder") 708159968Sgad == 0)) { 709159968Sgad warnx("NOTE: newsyslog always uses 'neworder'."); 710130167Sgad return (1); /* successfully parsed */ 711130167Sgad } 712130167Sgad 713130165Sgad warnx("Unknown -D (debug) option: '%s'", doption); 714119904Sgad return (0); /* failure */ 715119904Sgad} 716119904Sgad 71759004Shmstatic void 71859004Shmusage(void) 71913244Sgraichen{ 72080646Sobrien 72180646Sobrien fprintf(stderr, 722143106Sbrooks "usage: newsyslog [-CFNnrsv] [-a directory] [-d directory] [-f config-file]\n" 723111772Sgad " [ [-R requestor] filename ... ]\n"); 72459003Shm exit(1); 72513244Sgraichen} 72613244Sgraichen 72759004Shm/* 728111773Sgad * Parse a configuration file and return a linked list of all the logs 729111773Sgad * which should be processed. 73013244Sgraichen */ 73159003Shmstatic struct conf_entry * 732111773Sgadget_worklist(char **files) 73313244Sgraichen{ 73459003Shm FILE *f; 735111773Sgad const char *fname; 736111773Sgad char **given; 737111773Sgad struct conf_entry *defconf, *dupent, *ent, *firstnew; 738112020Sgad struct conf_entry *globlist, *lastnew, *worklist; 739112020Sgad int gmatch, fnres; 740111773Sgad 741112020Sgad defconf = globlist = worklist = NULL; 742111773Sgad 743111773Sgad fname = conf; 744111773Sgad if (fname == NULL) 745111773Sgad fname = _PATH_CONF; 746111773Sgad 747111773Sgad if (strcmp(fname, "-") != 0) 748111773Sgad f = fopen(fname, "r"); 749111773Sgad else { 750111773Sgad f = stdin; 751111773Sgad fname = "<stdin>"; 752111773Sgad } 753111773Sgad if (!f) 754152980Ssobomax err(1, "%s", fname); 755111773Sgad 756112020Sgad parse_file(f, fname, &worklist, &globlist, &defconf); 757111773Sgad (void) fclose(f); 758111773Sgad 759111773Sgad /* 760111773Sgad * All config-file information has been read in and turned into 761112020Sgad * a worklist and a globlist. If there were no specific files 762112020Sgad * given on the run command, then the only thing left to do is to 763112020Sgad * call a routine which finds all files matched by the globlist 764112020Sgad * and adds them to the worklist. Then return the worklist. 765111773Sgad */ 766111773Sgad if (*files == NULL) { 767112020Sgad expand_globs(&worklist, &globlist); 768112020Sgad free_clist(&globlist); 769111773Sgad if (defconf != NULL) 770111773Sgad free_entry(defconf); 771111773Sgad return (worklist); 772111773Sgad /* NOTREACHED */ 773111773Sgad } 774111773Sgad 775111773Sgad /* 776111773Sgad * If newsyslog was given a specific list of files to process, 777111773Sgad * it may be that some of those files were not listed in any 778111773Sgad * config file. Those unlisted files should get the default 779111773Sgad * rotation action. First, create the default-rotation action 780111773Sgad * if none was found in a system config file. 781111773Sgad */ 782111773Sgad if (defconf == NULL) { 783111773Sgad defconf = init_entry(DEFAULT_MARKER, NULL); 784111773Sgad defconf->numlogs = 3; 785130165Sgad defconf->trsize = 50; 786111773Sgad defconf->permissions = S_IRUSR|S_IWUSR; 787111773Sgad } 788111773Sgad 789111773Sgad /* 790111773Sgad * If newsyslog was run with a list of specific filenames, 791111773Sgad * then create a new worklist which has only those files in 792111773Sgad * it, picking up the rotation-rules for those files from 793111773Sgad * the original worklist. 794111773Sgad * 795111773Sgad * XXX - Note that this will copy multiple rules for a single 796111773Sgad * logfile, if multiple entries are an exact match for 797111773Sgad * that file. That matches the historic behavior, but do 798111773Sgad * we want to continue to allow it? If so, it should 799111773Sgad * probably be handled more intelligently. 800111773Sgad */ 801112020Sgad firstnew = lastnew = NULL; 802111773Sgad for (given = files; *given; ++given) { 803111773Sgad /* 804111773Sgad * First try to find exact-matches for this given file. 805111773Sgad */ 806112020Sgad gmatch = 0; 807111773Sgad for (ent = worklist; ent; ent = ent->next) { 808111773Sgad if (strcmp(ent->log, *given) == 0) { 809111773Sgad gmatch++; 810111773Sgad dupent = init_entry(*given, ent); 811111773Sgad if (!firstnew) 812111773Sgad firstnew = dupent; 813111773Sgad else 814112020Sgad lastnew->next = dupent; 815112020Sgad lastnew = dupent; 816111773Sgad } 817111773Sgad } 818111773Sgad if (gmatch) { 819111773Sgad if (verbose > 2) 820111773Sgad printf("\t+ Matched entry %s\n", *given); 821111773Sgad continue; 822111773Sgad } 823111773Sgad 824111773Sgad /* 825111773Sgad * There was no exact-match for this given file, so look 826111773Sgad * for a "glob" entry which does match. 827111773Sgad */ 828112020Sgad gmatch = 0; 829112020Sgad if (verbose > 2 && globlist != NULL) 830112020Sgad printf("\t+ Checking globs for %s\n", *given); 831112020Sgad for (ent = globlist; ent; ent = ent->next) { 832112020Sgad fnres = fnmatch(ent->log, *given, FNM_PATHNAME); 833112020Sgad if (verbose > 2) 834112020Sgad printf("\t+ = %d for pattern %s\n", fnres, 835112020Sgad ent->log); 836112020Sgad if (fnres == 0) { 837111773Sgad gmatch++; 838111773Sgad dupent = init_entry(*given, ent); 839111773Sgad if (!firstnew) 840111773Sgad firstnew = dupent; 841111773Sgad else 842112020Sgad lastnew->next = dupent; 843112020Sgad lastnew = dupent; 844112020Sgad /* This new entry is not a glob! */ 845111773Sgad dupent->flags &= ~CE_GLOB; 846111773Sgad /* Only allow a match to one glob-entry */ 847111773Sgad break; 848111773Sgad } 849111773Sgad } 850111773Sgad if (gmatch) { 851111773Sgad if (verbose > 2) 852111773Sgad printf("\t+ Matched %s via %s\n", *given, 853111773Sgad ent->log); 854111773Sgad continue; 855111773Sgad } 856111773Sgad 857111773Sgad /* 858111773Sgad * This given file was not found in any config file, so 859111773Sgad * add a worklist item based on the default entry. 860111773Sgad */ 861111773Sgad if (verbose > 2) 862111773Sgad printf("\t+ No entry matched %s (will use %s)\n", 863111773Sgad *given, DEFAULT_MARKER); 864111773Sgad dupent = init_entry(*given, defconf); 865111773Sgad if (!firstnew) 866111773Sgad firstnew = dupent; 867111773Sgad else 868112020Sgad lastnew->next = dupent; 869111773Sgad /* Mark that it was *not* found in a config file */ 870111773Sgad dupent->def_cfg = 1; 871112020Sgad lastnew = dupent; 872111773Sgad } 873111773Sgad 874111773Sgad /* 875112020Sgad * Free all the entries in the original work list, the list of 876112020Sgad * glob entries, and the default entry. 877111773Sgad */ 878112020Sgad free_clist(&worklist); 879112020Sgad free_clist(&globlist); 880112020Sgad free_entry(defconf); 881111773Sgad 882112020Sgad /* And finally, return a worklist which matches the given files. */ 883112013Sgad return (firstnew); 884111773Sgad} 885111773Sgad 886111773Sgad/* 887112020Sgad * Expand the list of entries with filename patterns, and add all files 888112020Sgad * which match those glob-entries onto the worklist. 889112020Sgad */ 890112020Sgadstatic void 891112020Sgadexpand_globs(struct conf_entry **work_p, struct conf_entry **glob_p) 892112020Sgad{ 893161412Sdelphij int gmatch, gres; 894161412Sdelphij size_t i; 895112020Sgad char *mfname; 896112020Sgad struct conf_entry *dupent, *ent, *firstmatch, *globent; 897112020Sgad struct conf_entry *lastmatch; 898112020Sgad glob_t pglob; 899112020Sgad struct stat st_fm; 900112020Sgad 901112020Sgad if ((glob_p == NULL) || (*glob_p == NULL)) 902112020Sgad return; /* There is nothing to do. */ 903112020Sgad 904112020Sgad /* 905112020Sgad * The worklist contains all fully-specified (non-GLOB) names. 906112020Sgad * 907112020Sgad * Now expand the list of filename-pattern (GLOB) entries into 908112020Sgad * a second list, which (by definition) will only match files 909112020Sgad * that already exist. Do not add a glob-related entry for any 910112020Sgad * file which already exists in the fully-specified list. 911112020Sgad */ 912112020Sgad firstmatch = lastmatch = NULL; 913112020Sgad for (globent = *glob_p; globent; globent = globent->next) { 914112020Sgad 915112020Sgad gres = glob(globent->log, GLOB_NOCHECK, NULL, &pglob); 916112020Sgad if (gres != 0) { 917112020Sgad warn("cannot expand pattern (%d): %s", gres, 918112020Sgad globent->log); 919112020Sgad continue; 920112020Sgad } 921112020Sgad 922112020Sgad if (verbose > 2) 923112020Sgad printf("\t+ Expanding pattern %s\n", globent->log); 924112020Sgad for (i = 0; i < pglob.gl_matchc; i++) { 925112020Sgad mfname = pglob.gl_pathv[i]; 926112020Sgad 927112020Sgad /* See if this file already has a specific entry. */ 928112020Sgad gmatch = 0; 929112020Sgad for (ent = *work_p; ent; ent = ent->next) { 930112020Sgad if (strcmp(mfname, ent->log) == 0) { 931112020Sgad gmatch++; 932112020Sgad break; 933112020Sgad } 934112020Sgad } 935112020Sgad if (gmatch) 936112020Sgad continue; 937112020Sgad 938112020Sgad /* Make sure the named matched is a file. */ 939112020Sgad gres = lstat(mfname, &st_fm); 940112020Sgad if (gres != 0) { 941112020Sgad /* Error on a file that glob() matched?!? */ 942112020Sgad warn("Skipping %s - lstat() error", mfname); 943112020Sgad continue; 944112020Sgad } 945112020Sgad if (!S_ISREG(st_fm.st_mode)) { 946112020Sgad /* We only rotate files! */ 947112020Sgad if (verbose > 2) 948112020Sgad printf("\t+ . skipping %s (!file)\n", 949112020Sgad mfname); 950112020Sgad continue; 951112020Sgad } 952112020Sgad 953112020Sgad if (verbose > 2) 954112020Sgad printf("\t+ . add file %s\n", mfname); 955112020Sgad dupent = init_entry(mfname, globent); 956112020Sgad if (!firstmatch) 957112020Sgad firstmatch = dupent; 958112020Sgad else 959112020Sgad lastmatch->next = dupent; 960112020Sgad lastmatch = dupent; 961112020Sgad /* This new entry is not a glob! */ 962112020Sgad dupent->flags &= ~CE_GLOB; 963112020Sgad } 964112020Sgad globfree(&pglob); 965112020Sgad if (verbose > 2) 966112020Sgad printf("\t+ Done with pattern %s\n", globent->log); 967112020Sgad } 968112020Sgad 969112020Sgad /* Add the list of matched files to the end of the worklist. */ 970112020Sgad if (!*work_p) 971112020Sgad *work_p = firstmatch; 972112020Sgad else { 973112020Sgad ent = *work_p; 974112020Sgad while (ent->next) 975112020Sgad ent = ent->next; 976112020Sgad ent->next = firstmatch; 977112020Sgad } 978112020Sgad 979112020Sgad} 980112020Sgad 981112020Sgad/* 982111773Sgad * Parse a configuration file and update a linked list of all the logs to 983111773Sgad * process. 984111773Sgad */ 985111773Sgadstatic void 986111773Sgadparse_file(FILE *cf, const char *cfname, struct conf_entry **work_p, 987112020Sgad struct conf_entry **glob_p, struct conf_entry **defconf_p) 988111773Sgad{ 98959003Shm char line[BUFSIZ], *parse, *q; 990107737Ssobomax char *cp, *errline, *group; 991112020Sgad struct conf_entry *lastglob, *lastwork, *working; 992111781Sgad struct passwd *pwd; 99359003Shm struct group *grp; 994120361Sgad int eol, ptm_opts, res, special; 99513244Sgraichen 996111773Sgad /* 997111773Sgad * XXX - for now, assume that only one config file will be read, 998111773Sgad * ie, this routine is only called one time. 999111773Sgad */ 1000112020Sgad lastglob = lastwork = NULL; 100180646Sobrien 1002130165Sgad errline = NULL; 1003111773Sgad while (fgets(line, BUFSIZ, cf)) { 1004107737Ssobomax if ((line[0] == '\n') || (line[0] == '#') || 1005107737Ssobomax (strlen(line) == 0)) 100659003Shm continue; 1007130165Sgad if (errline != NULL) 1008130165Sgad free(errline); 100959003Shm errline = strdup(line); 1010107737Ssobomax for (cp = line + 1; *cp != '\0'; cp++) { 1011107737Ssobomax if (*cp != '#') 1012107737Ssobomax continue; 1013107737Ssobomax if (*(cp - 1) == '\\') { 1014107737Ssobomax strcpy(cp - 1, cp); 1015107737Ssobomax cp--; 1016107737Ssobomax continue; 1017107737Ssobomax } 1018107737Ssobomax *cp = '\0'; 1019107737Ssobomax break; 1020107737Ssobomax } 102160373Sdes 102260373Sdes q = parse = missing_field(sob(line), errline); 102360373Sdes parse = son(line); 102460373Sdes if (!*parse) 102580646Sobrien errx(1, "malformed line (missing fields):\n%s", 102680646Sobrien errline); 102760373Sdes *parse = '\0'; 102860373Sdes 1029130165Sgad /* 1030130165Sgad * Allow people to set debug options via the config file. 1031130165Sgad * (NOTE: debug optons are undocumented, and may disappear 1032130165Sgad * at any time, etc). 1033130165Sgad */ 1034130165Sgad if (strcasecmp(DEBUG_MARKER, q) == 0) { 1035130165Sgad q = parse = missing_field(sob(++parse), errline); 1036130165Sgad parse = son(parse); 1037130165Sgad if (!*parse) 1038130165Sgad warnx("debug line specifies no option:\n%s", 1039130165Sgad errline); 1040130165Sgad else { 1041130165Sgad *parse = '\0'; 1042130165Sgad parse_doption(q); 1043130165Sgad } 1044130165Sgad continue; 1045130165Sgad } 1046130165Sgad 1047112020Sgad special = 0; 1048111388Sgad working = init_entry(q, NULL); 1049111388Sgad if (strcasecmp(DEFAULT_MARKER, q) == 0) { 1050112020Sgad special = 1; 1051111773Sgad if (defconf_p == NULL) { 1052111773Sgad warnx("Ignoring entry for %s in %s!", q, 1053111773Sgad cfname); 1054111773Sgad free_entry(working); 1055111773Sgad continue; 1056111773Sgad } else if (*defconf_p != NULL) { 1057111388Sgad warnx("Ignoring duplicate entry for %s!", q); 1058111388Sgad free_entry(working); 1059111388Sgad continue; 1060111388Sgad } 1061111773Sgad *defconf_p = working; 106259003Shm } 106313244Sgraichen 106459003Shm q = parse = missing_field(sob(++parse), errline); 106559003Shm parse = son(parse); 106625518Sbrian if (!*parse) 106780646Sobrien errx(1, "malformed line (missing fields):\n%s", 106880646Sobrien errline); 106959003Shm *parse = '\0'; 107059003Shm if ((group = strchr(q, ':')) != NULL || 107159003Shm (group = strrchr(q, '.')) != NULL) { 107259003Shm *group++ = '\0'; 107359003Shm if (*q) { 1074119102Sgad if (!(isnumberstr(q))) { 1075111781Sgad if ((pwd = getpwnam(q)) == NULL) 107659003Shm errx(1, 107780646Sobrien "error in config file; unknown user:\n%s", 107859003Shm errline); 1079111781Sgad working->uid = pwd->pw_uid; 108059003Shm } else 108159003Shm working->uid = atoi(q); 108259003Shm } else 1083111779Sgad working->uid = (uid_t)-1; 108413244Sgraichen 108559003Shm q = group; 108659003Shm if (*q) { 1087119102Sgad if (!(isnumberstr(q))) { 108859003Shm if ((grp = getgrnam(q)) == NULL) 108959003Shm errx(1, 109080646Sobrien "error in config file; unknown group:\n%s", 109159003Shm errline); 109259003Shm working->gid = grp->gr_gid; 109359003Shm } else 109459003Shm working->gid = atoi(q); 109559003Shm } else 1096111779Sgad working->gid = (gid_t)-1; 109713244Sgraichen 109859003Shm q = parse = missing_field(sob(++parse), errline); 109959003Shm parse = son(parse); 110059003Shm if (!*parse) 110180646Sobrien errx(1, "malformed line (missing fields):\n%s", 110280646Sobrien errline); 110359003Shm *parse = '\0'; 1104111779Sgad } else { 1105111779Sgad working->uid = (uid_t)-1; 1106111779Sgad working->gid = (gid_t)-1; 1107111779Sgad } 110859003Shm 110959003Shm if (!sscanf(q, "%o", &working->permissions)) 111059003Shm errx(1, "error in config file; bad permissions:\n%s", 111159003Shm errline); 111259003Shm 111359003Shm q = parse = missing_field(sob(++parse), errline); 111459003Shm parse = son(parse); 111525518Sbrian if (!*parse) 111680646Sobrien errx(1, "malformed line (missing fields):\n%s", 111780646Sobrien errline); 111859003Shm *parse = '\0'; 1119111400Sgad if (!sscanf(q, "%d", &working->numlogs) || working->numlogs < 0) 1120111400Sgad errx(1, "error in config file; bad value for count of logs to save:\n%s", 112159003Shm errline); 112213244Sgraichen 112359003Shm q = parse = missing_field(sob(++parse), errline); 112459003Shm parse = son(parse); 112525518Sbrian if (!*parse) 112680646Sobrien errx(1, "malformed line (missing fields):\n%s", 112780646Sobrien errline); 112859003Shm *parse = '\0'; 1129114762Sgad if (isdigitch(*q)) 1130130165Sgad working->trsize = atoi(q); 1131130165Sgad else if (strcmp(q, "*") == 0) 1132130165Sgad working->trsize = -1; 1133114762Sgad else { 1134114762Sgad warnx("Invalid value of '%s' for 'size' in line:\n%s", 1135114762Sgad q, errline); 1136130165Sgad working->trsize = -1; 1137114762Sgad } 113859003Shm 113959003Shm working->flags = 0; 114059003Shm q = parse = missing_field(sob(++parse), errline); 114159003Shm parse = son(parse); 114225518Sbrian eol = !*parse; 114359003Shm *parse = '\0'; 114443071Swollman { 114559003Shm char *ep; 114659003Shm u_long ul; 114713244Sgraichen 114843071Swollman ul = strtoul(q, &ep, 10); 114943071Swollman if (ep == q) 115043071Swollman working->hours = 0; 115143071Swollman else if (*ep == '*') 115243071Swollman working->hours = -1; 115343071Swollman else if (ul > INT_MAX) 115443071Swollman errx(1, "interval is too large:\n%s", errline); 115543071Swollman else 115643071Swollman working->hours = ul; 115743071Swollman 1158120361Sgad if (*ep == '\0' || strcmp(ep, "*") == 0) 1159120361Sgad goto no_trimat; 1160120361Sgad if (*ep != '@' && *ep != '$') 116143071Swollman errx(1, "malformed interval/at:\n%s", errline); 1162120361Sgad 1163120361Sgad working->flags |= CE_TRIMAT; 1164120361Sgad working->trim_at = ptime_init(NULL); 1165120361Sgad ptm_opts = PTM_PARSE_ISO8601; 1166120361Sgad if (*ep == '$') 1167120361Sgad ptm_opts = PTM_PARSE_DWM; 1168120361Sgad ptm_opts |= PTM_PARSE_MATCHDOM; 1169120361Sgad res = ptime_relparse(working->trim_at, ptm_opts, 1170120361Sgad ptimeget_secs(timenow), ep + 1); 1171120361Sgad if (res == -2) 1172120361Sgad errx(1, "nonexistent time for 'at' value:\n%s", 1173120361Sgad errline); 1174120361Sgad else if (res < 0) 1175120361Sgad errx(1, "malformed 'at' value:\n%s", errline); 117643071Swollman } 1177120361Sgadno_trimat: 117843071Swollman 117925518Sbrian if (eol) 118059003Shm q = NULL; 118125518Sbrian else { 118259003Shm q = parse = sob(++parse); /* Optional field */ 118359003Shm parse = son(parse); 118459003Shm if (!*parse) 118559003Shm eol = 1; 118659003Shm *parse = '\0'; 118725518Sbrian } 118825443Sache 1189111768Sgad for (; q && *q && !isspacech(*q); q++) { 1190111768Sgad switch (tolowerch(*q)) { 1191111768Sgad case 'b': 119259003Shm working->flags |= CE_BINARY; 1193111768Sgad break; 1194114137Sgad case 'c': 1195111768Sgad /* 1196114137Sgad * XXX - Ick! Ugly! Remove ASAP! 1197114137Sgad * We want `c' and `C' for "create". But we 1198114137Sgad * will temporarily treat `c' as `g', because 1199114137Sgad * FreeBSD releases <= 4.8 have a typo of 1200114137Sgad * checking ('G' || 'c') for CE_GLOB. 1201111768Sgad */ 1202114137Sgad if (*q == 'c') { 1203114137Sgad warnx("Assuming 'g' for 'c' in flags for line:\n%s", 1204114137Sgad errline); 1205114137Sgad warnx("The 'c' flag will eventually mean 'CREATE'"); 1206114137Sgad working->flags |= CE_GLOB; 1207114137Sgad break; 1208114137Sgad } 1209114137Sgad working->flags |= CE_CREATE; 1210114137Sgad break; 1211130043Sgad case 'd': 1212130043Sgad working->flags |= CE_NODUMP; 1213130043Sgad break; 1214111768Sgad case 'g': 1215106905Ssobomax working->flags |= CE_GLOB; 1216111768Sgad break; 1217111768Sgad case 'j': 1218111768Sgad working->flags |= CE_BZCOMPACT; 1219111768Sgad break; 1220111768Sgad case 'n': 1221111768Sgad working->flags |= CE_NOSIGNAL; 1222111768Sgad break; 1223112003Sgad case 'u': 1224112003Sgad working->flags |= CE_SIGNALGROUP; 1225112003Sgad break; 1226111768Sgad case 'w': 1227160560Ssobomax /* Depreciated flag - keep for compatibility purposes */ 1228111768Sgad break; 1229111768Sgad case 'z': 1230111768Sgad working->flags |= CE_COMPACT; 1231111768Sgad break; 1232111768Sgad case '-': 1233111768Sgad break; 1234111781Sgad case 'f': /* Used by OpenBSD for "CE_FOLLOW" */ 1235111781Sgad case 'm': /* Used by OpenBSD for "CE_MONITOR" */ 1236111781Sgad case 'p': /* Used by NetBSD for "CE_PLAIN0" */ 1237111768Sgad default: 123880646Sobrien errx(1, "illegal flag in config file -- %c", 123980646Sobrien *q); 1240111768Sgad } 124159003Shm } 124259003Shm 124325518Sbrian if (eol) 124459003Shm q = NULL; 124525518Sbrian else { 124659003Shm q = parse = sob(++parse); /* Optional field */ 124759003Shm parse = son(parse); 124859003Shm if (!*parse) 124959003Shm eol = 1; 125059003Shm *parse = '\0'; 125125518Sbrian } 125225443Sache 125325443Sache working->pid_file = NULL; 125425443Sache if (q && *q) { 125525443Sache if (*q == '/') 125625443Sache working->pid_file = strdup(q); 125736817Sache else if (isdigit(*q)) 125836817Sache goto got_sig; 125959003Shm else 126080646Sobrien errx(1, 126180646Sobrien "illegal pid file or signal number in config file:\n%s", 126280646Sobrien errline); 126325443Sache } 126436817Sache if (eol) 126559003Shm q = NULL; 126636817Sache else { 126759003Shm q = parse = sob(++parse); /* Optional field */ 126859003Shm *(parse = son(parse)) = '\0'; 126936817Sache } 127036817Sache 127136817Sache working->sig = SIGHUP; 127236817Sache if (q && *q) { 127336817Sache if (isdigit(*q)) { 127459003Shm got_sig: 127536817Sache working->sig = atoi(q); 127636817Sache } else { 127759003Shm err_sig: 127880646Sobrien errx(1, 127980646Sobrien "illegal signal number in config file:\n%s", 128080646Sobrien errline); 128136817Sache } 128236817Sache if (working->sig < 1 || working->sig >= NSIG) 128336817Sache goto err_sig; 128436817Sache } 1285111768Sgad 1286111768Sgad /* 1287111768Sgad * Finish figuring out what pid-file to use (if any) in 1288111768Sgad * later processing if this logfile needs to be rotated. 1289111768Sgad */ 1290111768Sgad if ((working->flags & CE_NOSIGNAL) == CE_NOSIGNAL) { 1291111768Sgad /* 1292111768Sgad * This config-entry specified 'n' for nosignal, 1293111768Sgad * see if it also specified an explicit pid_file. 1294111768Sgad * This would be a pretty pointless combination. 1295111768Sgad */ 1296111768Sgad if (working->pid_file != NULL) { 1297111768Sgad warnx("Ignoring '%s' because flag 'n' was specified in line:\n%s", 1298111768Sgad working->pid_file, errline); 1299111768Sgad free(working->pid_file); 1300111768Sgad working->pid_file = NULL; 1301111768Sgad } 1302111768Sgad } else if (working->pid_file == NULL) { 1303111768Sgad /* 1304111768Sgad * This entry did not specify the 'n' flag, which 1305111768Sgad * means it should signal syslogd unless it had 1306112003Sgad * specified some other pid-file (and obviously the 1307112003Sgad * syslog pid-file will not be for a process-group). 1308112003Sgad * Also, we should only try to notify syslog if we 1309112003Sgad * are root. 1310111768Sgad */ 1311112003Sgad if (working->flags & CE_SIGNALGROUP) { 1312112003Sgad warnx("Ignoring flag 'U' in line:\n%s", 1313112003Sgad errline); 1314112003Sgad working->flags &= ~CE_SIGNALGROUP; 1315112003Sgad } 1316111768Sgad if (needroot) 1317111768Sgad working->pid_file = strdup(_PATH_SYSLOGPID); 1318111768Sgad } 1319111768Sgad 1320112020Sgad /* 1321112020Sgad * Add this entry to the appropriate list of entries, unless 1322112020Sgad * it was some kind of special entry (eg: <default>). 1323112020Sgad */ 1324112020Sgad if (special) { 1325112020Sgad ; /* Do not add to any list */ 1326112020Sgad } else if (working->flags & CE_GLOB) { 1327112020Sgad if (!*glob_p) 1328112020Sgad *glob_p = working; 1329112020Sgad else 1330112020Sgad lastglob->next = working; 1331112020Sgad lastglob = working; 1332112020Sgad } else { 1333112020Sgad if (!*work_p) 1334112020Sgad *work_p = working; 1335112020Sgad else 1336112020Sgad lastwork->next = working; 1337112020Sgad lastwork = working; 1338112020Sgad } 1339130165Sgad } 1340130165Sgad if (errline != NULL) 134159003Shm free(errline); 134213244Sgraichen} 134313244Sgraichen 134459003Shmstatic char * 134559004Shmmissing_field(char *p, char *errline) 134613244Sgraichen{ 134780646Sobrien 134859003Shm if (!p || !*p) 134959003Shm errx(1, "missing field in config file:\n%s", errline); 135059003Shm return (p); 135113244Sgraichen} 135213244Sgraichen 1353130165Sgadstatic fk_entry 1354130165Sgaddo_rotate(const struct conf_entry *ent) 135513244Sgraichen{ 135671299Sjedgar char dirpart[MAXPATHLEN], namepart[MAXPATHLEN]; 135771299Sjedgar char file1[MAXPATHLEN], file2[MAXPATHLEN]; 135871299Sjedgar char zfile1[MAXPATHLEN], zfile2[MAXPATHLEN]; 135980638Sobrien char jfile1[MAXPATHLEN]; 1360159968Sgad int flags, numlogs_c; 1361130165Sgad fk_entry free_or_keep; 1362159968Sgad struct sigwork_entry *swork; 136359003Shm struct stat st; 136413244Sgraichen 1365119927Sgad flags = ent->flags; 1366130165Sgad free_or_keep = FREE_ENT; 1367119927Sgad 136859004Shm if (archtodir) { 136959004Shm char *p; 137013244Sgraichen 137159004Shm /* build complete name of archive directory into dirpart */ 137259004Shm if (*archdirname == '/') { /* absolute */ 137371299Sjedgar strlcpy(dirpart, archdirname, sizeof(dirpart)); 137459004Shm } else { /* relative */ 137559004Shm /* get directory part of logfile */ 1376119927Sgad strlcpy(dirpart, ent->log, sizeof(dirpart)); 137759004Shm if ((p = rindex(dirpart, '/')) == NULL) 137859004Shm dirpart[0] = '\0'; 137959004Shm else 138059004Shm *(p + 1) = '\0'; 138171299Sjedgar strlcat(dirpart, archdirname, sizeof(dirpart)); 138259004Shm } 138359004Shm 138459004Shm /* check if archive directory exists, if not, create it */ 138559004Shm if (lstat(dirpart, &st)) 1386114137Sgad createdir(ent, dirpart); 138759004Shm 138859004Shm /* get filename part of logfile */ 1389119927Sgad if ((p = rindex(ent->log, '/')) == NULL) 1390119927Sgad strlcpy(namepart, ent->log, sizeof(namepart)); 139159004Shm else 139271299Sjedgar strlcpy(namepart, p + 1, sizeof(namepart)); 139359004Shm 139459004Shm /* name of oldest log */ 139580646Sobrien (void) snprintf(file1, sizeof(file1), "%s/%s.%d", dirpart, 1396119927Sgad namepart, ent->numlogs); 139771299Sjedgar (void) snprintf(zfile1, sizeof(zfile1), "%s%s", file1, 139871299Sjedgar COMPRESS_POSTFIX); 139980638Sobrien snprintf(jfile1, sizeof(jfile1), "%s%s", file1, 140080638Sobrien BZCOMPRESS_POSTFIX); 140159004Shm } else { 140259004Shm /* name of oldest log */ 1403119927Sgad (void) snprintf(file1, sizeof(file1), "%s.%d", ent->log, 1404119927Sgad ent->numlogs); 140571299Sjedgar (void) snprintf(zfile1, sizeof(zfile1), "%s%s", file1, 140671299Sjedgar COMPRESS_POSTFIX); 140780638Sobrien snprintf(jfile1, sizeof(jfile1), "%s%s", file1, 140880638Sobrien BZCOMPRESS_POSTFIX); 140959004Shm } 141059004Shm 141159003Shm if (noaction) { 1412111967Sgad printf("\trm -f %s\n", file1); 1413111967Sgad printf("\trm -f %s\n", zfile1); 1414111967Sgad printf("\trm -f %s\n", jfile1); 141559003Shm } else { 141659003Shm (void) unlink(file1); 141759003Shm (void) unlink(zfile1); 141880638Sobrien (void) unlink(jfile1); 141959003Shm } 142013244Sgraichen 142159003Shm /* Move down log files */ 1422119927Sgad numlogs_c = ent->numlogs; /* copy for countdown */ 1423119927Sgad while (numlogs_c--) { 142459004Shm 142571299Sjedgar (void) strlcpy(file2, file1, sizeof(file2)); 142659004Shm 142759004Shm if (archtodir) 142880646Sobrien (void) snprintf(file1, sizeof(file1), "%s/%s.%d", 1429119927Sgad dirpart, namepart, numlogs_c); 143059004Shm else 1431119927Sgad (void) snprintf(file1, sizeof(file1), "%s.%d", 1432119927Sgad ent->log, numlogs_c); 143359004Shm 143471299Sjedgar (void) strlcpy(zfile1, file1, sizeof(zfile1)); 143571299Sjedgar (void) strlcpy(zfile2, file2, sizeof(zfile2)); 143659003Shm if (lstat(file1, &st)) { 143780646Sobrien (void) strlcat(zfile1, COMPRESS_POSTFIX, 143880646Sobrien sizeof(zfile1)); 143980646Sobrien (void) strlcat(zfile2, COMPRESS_POSTFIX, 144080646Sobrien sizeof(zfile2)); 144180638Sobrien if (lstat(zfile1, &st)) { 144280638Sobrien strlcpy(zfile1, file1, sizeof(zfile1)); 144380638Sobrien strlcpy(zfile2, file2, sizeof(zfile2)); 144480638Sobrien strlcat(zfile1, BZCOMPRESS_POSTFIX, 144580638Sobrien sizeof(zfile1)); 144680638Sobrien strlcat(zfile2, BZCOMPRESS_POSTFIX, 144780638Sobrien sizeof(zfile2)); 144880638Sobrien if (lstat(zfile1, &st)) 144980638Sobrien continue; 145080638Sobrien } 145159003Shm } 1452130165Sgad if (noaction) 1453111967Sgad printf("\tmv %s %s\n", zfile1, zfile2); 1454130165Sgad else { 1455130165Sgad /* XXX - Ought to be checking for failure! */ 1456130165Sgad (void)rename(zfile1, zfile2); 145759003Shm } 1458130165Sgad change_attrs(zfile2, ent); 145959003Shm } 146013244Sgraichen 1461130038Sgad if (ent->numlogs > 0) { 1462129974Sgad if (noaction) { 1463130038Sgad /* 1464130038Sgad * Note that savelog() may succeed with using link() 1465130038Sgad * for the archtodir case, but there is no good way 1466130038Sgad * of knowing if it will when doing "noaction", so 1467130038Sgad * here we claim that it will have to do a copy... 1468130038Sgad */ 1469130038Sgad if (archtodir) 1470130038Sgad printf("\tcp %s %s\n", ent->log, file1); 1471130038Sgad else 1472130038Sgad printf("\tln %s %s\n", ent->log, file1); 1473130165Sgad } else { 1474130038Sgad if (!(flags & CE_BINARY)) { 1475130038Sgad /* Report the trimming to the old log */ 1476130038Sgad log_trim(ent->log, ent); 1477130038Sgad } 1478130038Sgad savelog(ent->log, file1); 147959004Shm } 1480130165Sgad change_attrs(file1, ent); 148118075Sjkh } 148218075Sjkh 1483130038Sgad /* Create the new log file and move it into place */ 1484130038Sgad if (noaction) 1485111781Sgad printf("Start new log...\n"); 1486130038Sgad createlog(ent); 148725443Sache 1488111966Sgad /* 1489130167Sgad * Save all signalling and file-compression to be done after log 1490130167Sgad * files from all entries have been rotated. This way any one 1491130167Sgad * process will not be sent the same signal multiple times when 1492130167Sgad * multiple log files had to be rotated. 1493130167Sgad */ 1494159968Sgad swork = NULL; 1495159968Sgad if (ent->pid_file != NULL) 1496159968Sgad swork = save_sigwork(ent); 1497159968Sgad if (ent->numlogs > 0 && (flags & (CE_COMPACT | CE_BZCOMPACT))) { 1498159968Sgad /* 1499159968Sgad * The zipwork_entry will include a pointer to this 1500159968Sgad * conf_entry, so the conf_entry should not be freed. 1501159968Sgad */ 1502159968Sgad free_or_keep = KEEP_ENT; 1503159968Sgad save_zipwork(ent, swork, ent->fsize, file1); 1504130167Sgad } 1505130167Sgad 1506130165Sgad return (free_or_keep); 150713244Sgraichen} 150813244Sgraichen 1509130167Sgadstatic void 1510130167Sgaddo_sigwork(struct sigwork_entry *swork) 1511130167Sgad{ 1512130204Sgad struct sigwork_entry *nextsig; 1513130204Sgad int kres, secs; 1514130167Sgad 1515130167Sgad if (!(swork->sw_pidok) || swork->sw_pid == 0) 1516130167Sgad return; /* no work to do... */ 1517130167Sgad 1518130167Sgad /* 1519130167Sgad * If nosignal (-s) was specified, then do not signal any process. 1520130167Sgad * Note that a nosignal request triggers a warning message if the 1521130167Sgad * rotated logfile needs to be compressed, *unless* -R was also 1522130167Sgad * specified. We assume that an `-sR' request came from a process 1523130167Sgad * which writes to the logfile, and as such, we assume that process 1524130167Sgad * has already made sure the logfile is not presently in use. This 1525130167Sgad * just sets swork->sw_pidok to a special value, and do_zipwork 1526130167Sgad * will print any necessary warning(s). 1527130167Sgad */ 1528130167Sgad if (nosignal) { 1529130167Sgad if (!rotatereq) 1530130167Sgad swork->sw_pidok = -1; 1531130167Sgad return; 1532130167Sgad } 1533130167Sgad 1534130204Sgad /* 1535130204Sgad * Compute the pause between consecutive signals. Use a longer 1536130204Sgad * sleep time if we will be sending two signals to the same 1537130204Sgad * deamon or process-group. 1538130204Sgad */ 1539130204Sgad secs = 0; 1540130204Sgad nextsig = SLIST_NEXT(swork, sw_nextp); 1541130204Sgad if (nextsig != NULL) { 1542130204Sgad if (swork->sw_pid == nextsig->sw_pid) 1543130204Sgad secs = 10; 1544130204Sgad else 1545130204Sgad secs = 1; 1546130204Sgad } 1547130204Sgad 1548130167Sgad if (noaction) { 1549130204Sgad printf("\tkill -%d %d \t\t# %s\n", swork->sw_signum, 1550130204Sgad (int)swork->sw_pid, swork->sw_fname); 1551130204Sgad if (secs > 0) 1552130204Sgad printf("\tsleep %d\n", secs); 1553130167Sgad return; 1554130167Sgad } 1555130167Sgad 1556130167Sgad kres = kill(swork->sw_pid, swork->sw_signum); 1557130167Sgad if (kres != 0) { 1558130167Sgad /* 1559130167Sgad * Assume that "no such process" (ESRCH) is something 1560130167Sgad * to warn about, but is not an error. Presumably the 1561130167Sgad * process which writes to the rotated log file(s) is 1562130167Sgad * gone, in which case we should have no problem with 1563130167Sgad * compressing the rotated log file(s). 1564130167Sgad */ 1565130167Sgad if (errno != ESRCH) 1566130167Sgad swork->sw_pidok = 0; 1567130167Sgad warn("can't notify %s, pid %d", swork->sw_pidtype, 1568130167Sgad (int)swork->sw_pid); 1569130167Sgad } else { 1570130204Sgad if (verbose) 1571130204Sgad printf("Notified %s pid %d = %s\n", swork->sw_pidtype, 1572130204Sgad (int)swork->sw_pid, swork->sw_fname); 1573130204Sgad if (secs > 0) { 1574130204Sgad if (verbose) 1575130204Sgad printf("Pause %d second(s) between signals\n", 1576130204Sgad secs); 1577130204Sgad sleep(secs); 1578130167Sgad } 1579130167Sgad } 1580130167Sgad} 1581130167Sgad 1582130167Sgadstatic void 1583130167Sgaddo_zipwork(struct zipwork_entry *zwork) 1584130167Sgad{ 1585130167Sgad const char *pgm_name, *pgm_path; 1586154566Sgad int errsav, fcount, zstatus; 1587130167Sgad pid_t pidzip, wpid; 1588130167Sgad char zresult[MAXPATHLEN]; 1589130167Sgad 1590130167Sgad pgm_path = NULL; 1591130167Sgad strlcpy(zresult, zwork->zw_fname, sizeof(zresult)); 1592130167Sgad if (zwork != NULL && zwork->zw_conf != NULL) { 1593130167Sgad if (zwork->zw_conf->flags & CE_COMPACT) { 1594130167Sgad pgm_path = _PATH_GZIP; 1595130167Sgad strlcat(zresult, COMPRESS_POSTFIX, sizeof(zresult)); 1596130167Sgad } else if (zwork->zw_conf->flags & CE_BZCOMPACT) { 1597130167Sgad pgm_path = _PATH_BZIP2; 1598130167Sgad strlcat(zresult, BZCOMPRESS_POSTFIX, sizeof(zresult)); 1599130167Sgad } 1600130167Sgad } 1601130167Sgad if (pgm_path == NULL) { 1602130167Sgad warnx("invalid entry for %s in do_zipwork", zwork->zw_fname); 1603130167Sgad return; 1604130167Sgad } 1605154566Sgad pgm_name = strrchr(pgm_path, '/'); 1606154566Sgad if (pgm_name == NULL) 1607154566Sgad pgm_name = pgm_path; 1608154566Sgad else 1609154566Sgad pgm_name++; 1610130167Sgad 1611130167Sgad if (zwork->zw_swork != NULL && zwork->zw_swork->sw_pidok <= 0) { 1612130167Sgad warnx( 1613130167Sgad "log %s not compressed because daemon(s) not notified", 1614130167Sgad zwork->zw_fname); 1615130167Sgad change_attrs(zwork->zw_fname, zwork->zw_conf); 1616130167Sgad return; 1617130167Sgad } 1618130167Sgad 1619130167Sgad if (noaction) { 1620130167Sgad printf("\t%s %s\n", pgm_name, zwork->zw_fname); 1621130167Sgad change_attrs(zresult, zwork->zw_conf); 1622130167Sgad return; 1623130167Sgad } 1624130167Sgad 1625154566Sgad fcount = 1; 1626130167Sgad pidzip = fork(); 1627154566Sgad while (pidzip < 0) { 1628154566Sgad /* 1629154566Sgad * The fork failed. If the failure was due to a temporary 1630154566Sgad * problem, then wait a short time and try it again. 1631154566Sgad */ 1632154566Sgad errsav = errno; 1633154566Sgad warn("fork() for `%s %s'", pgm_name, zwork->zw_fname); 1634154566Sgad if (errsav != EAGAIN || fcount > 5) 1635154566Sgad errx(1, "Exiting..."); 1636154566Sgad sleep(fcount * 12); 1637154566Sgad fcount++; 1638154566Sgad pidzip = fork(); 1639154566Sgad } 1640154566Sgad if (!pidzip) { 1641130167Sgad /* The child process executes the compression command */ 1642130167Sgad execl(pgm_path, pgm_path, "-f", zwork->zw_fname, (char *)0); 1643154566Sgad err(1, "execl(`%s -f %s')", pgm_path, zwork->zw_fname); 1644130167Sgad } 1645130167Sgad 1646130167Sgad wpid = waitpid(pidzip, &zstatus, 0); 1647130167Sgad if (wpid == -1) { 1648154566Sgad /* XXX - should this be a fatal error? */ 1649130167Sgad warn("%s: waitpid(%d)", pgm_path, pidzip); 1650130167Sgad return; 1651130167Sgad } 1652130167Sgad if (!WIFEXITED(zstatus)) { 1653154566Sgad warnx("`%s -f %s' did not terminate normally", pgm_name, 1654154566Sgad zwork->zw_fname); 1655130167Sgad return; 1656130167Sgad } 1657130167Sgad if (WEXITSTATUS(zstatus)) { 1658154566Sgad warnx("`%s -f %s' terminated with a non-zero status (%d)", 1659154566Sgad pgm_name, zwork->zw_fname, WEXITSTATUS(zstatus)); 1660130167Sgad return; 1661130167Sgad } 1662130167Sgad 1663130167Sgad /* Compression was successful, set file attributes on the result. */ 1664130167Sgad change_attrs(zresult, zwork->zw_conf); 1665130167Sgad} 1666130167Sgad 1667130167Sgad/* 1668130167Sgad * Save information on any process we need to signal. Any single 1669130167Sgad * process may need to be sent different signal-values for different 1670130167Sgad * log files, but usually a single signal-value will cause the process 1671130167Sgad * to close and re-open all of it's log files. 1672130167Sgad */ 1673130167Sgadstatic struct sigwork_entry * 1674130167Sgadsave_sigwork(const struct conf_entry *ent) 1675130167Sgad{ 1676130167Sgad struct sigwork_entry *sprev, *stmp; 1677130167Sgad int ndiff; 1678130167Sgad size_t tmpsiz; 1679130167Sgad 1680130167Sgad sprev = NULL; 1681130167Sgad ndiff = 1; 1682130167Sgad SLIST_FOREACH(stmp, &swhead, sw_nextp) { 1683130167Sgad ndiff = strcmp(ent->pid_file, stmp->sw_fname); 1684130167Sgad if (ndiff > 0) 1685130167Sgad break; 1686130167Sgad if (ndiff == 0) { 1687130167Sgad if (ent->sig == stmp->sw_signum) 1688130167Sgad break; 1689130167Sgad if (ent->sig > stmp->sw_signum) { 1690130167Sgad ndiff = 1; 1691130167Sgad break; 1692130167Sgad } 1693130167Sgad } 1694130167Sgad sprev = stmp; 1695130167Sgad } 1696130167Sgad if (stmp != NULL && ndiff == 0) 1697130167Sgad return (stmp); 1698130167Sgad 1699130167Sgad tmpsiz = sizeof(struct sigwork_entry) + strlen(ent->pid_file) + 1; 1700130167Sgad stmp = malloc(tmpsiz); 1701130167Sgad set_swpid(stmp, ent); 1702130167Sgad stmp->sw_signum = ent->sig; 1703130167Sgad strcpy(stmp->sw_fname, ent->pid_file); 1704130167Sgad if (sprev == NULL) 1705130167Sgad SLIST_INSERT_HEAD(&swhead, stmp, sw_nextp); 1706130167Sgad else 1707130167Sgad SLIST_INSERT_AFTER(sprev, stmp, sw_nextp); 1708130167Sgad return (stmp); 1709130167Sgad} 1710130167Sgad 1711130167Sgad/* 1712130167Sgad * Save information on any file we need to compress. We may see the same 1713130167Sgad * file multiple times, so check the full list to avoid duplicates. The 1714130167Sgad * list itself is sorted smallest-to-largest, because that's the order we 1715130167Sgad * want to compress the files. If the partition is very low on disk space, 1716130167Sgad * then the smallest files are the most likely to compress, and compressing 1717130167Sgad * them first will free up more space for the larger files. 1718130167Sgad */ 1719130167Sgadstatic struct zipwork_entry * 1720130167Sgadsave_zipwork(const struct conf_entry *ent, const struct sigwork_entry *swork, 1721130167Sgad int zsize, const char *zipfname) 1722130167Sgad{ 1723130167Sgad struct zipwork_entry *zprev, *ztmp; 1724130167Sgad int ndiff; 1725130167Sgad size_t tmpsiz; 1726130167Sgad 1727130167Sgad /* Compute the size if the caller did not know it. */ 1728130167Sgad if (zsize < 0) 1729130167Sgad zsize = sizefile(zipfname); 1730130167Sgad 1731130167Sgad zprev = NULL; 1732130167Sgad ndiff = 1; 1733130167Sgad SLIST_FOREACH(ztmp, &zwhead, zw_nextp) { 1734130707Sgad ndiff = strcmp(zipfname, ztmp->zw_fname); 1735130167Sgad if (ndiff == 0) 1736130167Sgad break; 1737130167Sgad if (zsize > ztmp->zw_fsize) 1738130167Sgad zprev = ztmp; 1739130167Sgad } 1740130167Sgad if (ztmp != NULL && ndiff == 0) 1741130167Sgad return (ztmp); 1742130167Sgad 1743130167Sgad tmpsiz = sizeof(struct zipwork_entry) + strlen(zipfname) + 1; 1744130167Sgad ztmp = malloc(tmpsiz); 1745130167Sgad ztmp->zw_conf = ent; 1746130167Sgad ztmp->zw_swork = swork; 1747130167Sgad ztmp->zw_fsize = zsize; 1748130167Sgad strcpy(ztmp->zw_fname, zipfname); 1749130167Sgad if (zprev == NULL) 1750130167Sgad SLIST_INSERT_HEAD(&zwhead, ztmp, zw_nextp); 1751130167Sgad else 1752130167Sgad SLIST_INSERT_AFTER(zprev, ztmp, zw_nextp); 1753130167Sgad return (ztmp); 1754130167Sgad} 1755130167Sgad 1756130167Sgad/* Send a signal to the pid specified by pidfile */ 1757130167Sgadstatic void 1758130167Sgadset_swpid(struct sigwork_entry *swork, const struct conf_entry *ent) 1759130167Sgad{ 1760130167Sgad FILE *f; 1761130167Sgad long minok, maxok, rval; 1762130167Sgad char *endp, *linep, line[BUFSIZ]; 1763130167Sgad 1764130167Sgad minok = MIN_PID; 1765130167Sgad maxok = MAX_PID; 1766130167Sgad swork->sw_pidok = 0; 1767130167Sgad swork->sw_pid = 0; 1768130167Sgad swork->sw_pidtype = "daemon"; 1769130167Sgad if (ent->flags & CE_SIGNALGROUP) { 1770130167Sgad /* 1771130167Sgad * If we are expected to signal a process-group when 1772130167Sgad * rotating this logfile, then the value read in should 1773130167Sgad * be the negative of a valid process ID. 1774130167Sgad */ 1775130167Sgad minok = -MAX_PID; 1776130167Sgad maxok = -MIN_PID; 1777130167Sgad swork->sw_pidtype = "process-group"; 1778130167Sgad } 1779130167Sgad 1780130167Sgad f = fopen(ent->pid_file, "r"); 1781130167Sgad if (f == NULL) { 1782130167Sgad warn("can't open pid file: %s", ent->pid_file); 1783130167Sgad return; 1784130167Sgad } 1785130167Sgad 1786130167Sgad if (fgets(line, BUFSIZ, f) == NULL) { 1787130167Sgad /* 1788130167Sgad * Warn if the PID file is empty, but do not consider 1789130167Sgad * it an error. Most likely it means the process has 1790130167Sgad * has terminated, so it should be safe to rotate any 1791130167Sgad * log files that the process would have been using. 1792130167Sgad */ 1793130167Sgad if (feof(f)) { 1794130167Sgad swork->sw_pidok = 1; 1795130167Sgad warnx("pid file is empty: %s", ent->pid_file); 1796130167Sgad } else 1797130167Sgad warn("can't read from pid file: %s", ent->pid_file); 1798130167Sgad (void)fclose(f); 1799130167Sgad return; 1800130167Sgad } 1801130167Sgad (void)fclose(f); 1802130167Sgad 1803130167Sgad errno = 0; 1804130167Sgad linep = line; 1805130167Sgad while (*linep == ' ') 1806130167Sgad linep++; 1807130167Sgad rval = strtol(linep, &endp, 10); 1808130167Sgad if (*endp != '\0' && !isspacech(*endp)) { 1809130167Sgad warnx("pid file does not start with a valid number: %s", 1810130167Sgad ent->pid_file); 1811130167Sgad } else if (rval < minok || rval > maxok) { 1812130167Sgad warnx("bad value '%ld' for process number in %s", 1813130167Sgad rval, ent->pid_file); 1814130167Sgad if (verbose) 1815130167Sgad warnx("\t(expecting value between %ld and %ld)", 1816130167Sgad minok, maxok); 1817130167Sgad } else { 1818130167Sgad swork->sw_pidok = 1; 1819130167Sgad swork->sw_pid = rval; 1820130167Sgad } 1821130167Sgad 1822130167Sgad return; 1823130167Sgad} 1824130167Sgad 182513244Sgraichen/* Log the fact that the logs were turned over */ 182659004Shmstatic int 1827119926Sgadlog_trim(const char *logname, const struct conf_entry *log_ent) 182813244Sgraichen{ 182959003Shm FILE *f; 1830111388Sgad const char *xtra; 183159003Shm 1832119926Sgad if ((f = fopen(logname, "a")) == NULL) 183359003Shm return (-1); 1834111388Sgad xtra = ""; 1835111768Sgad if (log_ent->def_cfg) 1836111388Sgad xtra = " using <default> rule"; 1837114137Sgad if (log_ent->firstcreate) 1838114137Sgad fprintf(f, "%s %s newsyslog[%d]: logfile first created%s\n", 1839114137Sgad daytime, hostname, (int) getpid(), xtra); 1840114137Sgad else if (log_ent->r_reason != NULL) 1841111772Sgad fprintf(f, "%s %s newsyslog[%d]: logfile turned over%s%s\n", 1842111772Sgad daytime, hostname, (int) getpid(), log_ent->r_reason, xtra); 1843111772Sgad else 1844111772Sgad fprintf(f, "%s %s newsyslog[%d]: logfile turned over%s\n", 1845111772Sgad daytime, hostname, (int) getpid(), xtra); 184659003Shm if (fclose(f) == EOF) 1847127858Scharnier err(1, "log_trim: fclose"); 184859003Shm return (0); 184913244Sgraichen} 185013244Sgraichen 185113244Sgraichen/* Return size in kilobytes of a file */ 185259004Shmstatic int 1853130165Sgadsizefile(const char *file) 185413244Sgraichen{ 185559003Shm struct stat sb; 185613244Sgraichen 185759003Shm if (stat(file, &sb) < 0) 185859003Shm return (-1); 185959003Shm return (kbytes(dbtob(sb.st_blocks))); 186013244Sgraichen} 186113244Sgraichen 186213244Sgraichen/* Return the age of old log file (file.0) */ 186359004Shmstatic int 186459004Shmage_old_log(char *file) 186513244Sgraichen{ 186659003Shm struct stat sb; 1867114764Sgad char *endp; 1868114764Sgad char tmp[MAXPATHLEN + sizeof(".0") + sizeof(COMPRESS_POSTFIX) + 1869114764Sgad sizeof(BZCOMPRESS_POSTFIX) + 1]; 187013244Sgraichen 187159004Shm if (archtodir) { 187259004Shm char *p; 187359004Shm 187459004Shm /* build name of archive directory into tmp */ 187559004Shm if (*archdirname == '/') { /* absolute */ 187671299Sjedgar strlcpy(tmp, archdirname, sizeof(tmp)); 187759004Shm } else { /* relative */ 187859004Shm /* get directory part of logfile */ 187971299Sjedgar strlcpy(tmp, file, sizeof(tmp)); 188059004Shm if ((p = rindex(tmp, '/')) == NULL) 188159004Shm tmp[0] = '\0'; 188259004Shm else 188359004Shm *(p + 1) = '\0'; 188471299Sjedgar strlcat(tmp, archdirname, sizeof(tmp)); 188559004Shm } 188659004Shm 188771299Sjedgar strlcat(tmp, "/", sizeof(tmp)); 188859004Shm 188959004Shm /* get filename part of logfile */ 189059004Shm if ((p = rindex(file, '/')) == NULL) 189171299Sjedgar strlcat(tmp, file, sizeof(tmp)); 189259004Shm else 189371299Sjedgar strlcat(tmp, p + 1, sizeof(tmp)); 189459004Shm } else { 189571299Sjedgar (void) strlcpy(tmp, file, sizeof(tmp)); 189659004Shm } 189759004Shm 1898114764Sgad strlcat(tmp, ".0", sizeof(tmp)); 1899114764Sgad if (stat(tmp, &sb) < 0) { 1900114764Sgad /* 1901114764Sgad * A plain '.0' file does not exist. Try again, first 1902114764Sgad * with the added suffix of '.gz', then with an added 1903114764Sgad * suffix of '.bz2' instead of '.gz'. 1904114764Sgad */ 1905114764Sgad endp = strchr(tmp, '\0'); 1906114764Sgad strlcat(tmp, COMPRESS_POSTFIX, sizeof(tmp)); 1907114764Sgad if (stat(tmp, &sb) < 0) { 1908114764Sgad *endp = '\0'; /* Remove .gz */ 1909114764Sgad strlcat(tmp, BZCOMPRESS_POSTFIX, sizeof(tmp)); 1910114764Sgad if (stat(tmp, &sb) < 0) 1911114764Sgad return (-1); 1912114764Sgad } 1913114764Sgad } 1914120361Sgad return ((int)(ptimeget_secs(timenow) - sb.st_mtime + 1800) / 3600); 191513244Sgraichen} 191613244Sgraichen 191713244Sgraichen/* Skip Over Blanks */ 1918111820Sgadstatic char * 191959004Shmsob(char *p) 192013244Sgraichen{ 192159003Shm while (p && *p && isspace(*p)) 192259003Shm p++; 192359003Shm return (p); 192413244Sgraichen} 192513244Sgraichen 192613244Sgraichen/* Skip Over Non-Blanks */ 1927111820Sgadstatic char * 192859004Shmson(char *p) 192913244Sgraichen{ 193059003Shm while (p && *p && !isspace(*p)) 193159003Shm p++; 193259003Shm return (p); 193313244Sgraichen} 193443071Swollman 1935119102Sgad/* Check if string is actually a number */ 1936119102Sgadstatic int 1937119102Sgadisnumberstr(const char *string) 1938119102Sgad{ 1939119102Sgad while (*string) { 1940119102Sgad if (!isdigitch(*string++)) 1941119102Sgad return (0); 1942119102Sgad } 1943119102Sgad return (1); 1944119102Sgad} 1945119102Sgad 1946130038Sgad/* 1947130038Sgad * Save the active log file under a new name. A link to the new name 1948130038Sgad * is the quick-and-easy way to do this. If that fails (which it will 1949130038Sgad * if the destination is on another partition), then make a copy of 1950130038Sgad * the file to the new location. 1951130038Sgad */ 195259004Shmstatic void 1953130038Sgadsavelog(char *from, char *to) 195459004Shm{ 195559004Shm FILE *src, *dst; 1956130038Sgad int c, res; 195759004Shm 1958130038Sgad res = link(from, to); 1959130038Sgad if (res == 0) 1960130038Sgad return; 1961130038Sgad 196259004Shm if ((src = fopen(from, "r")) == NULL) 196359004Shm err(1, "can't fopen %s for reading", from); 196459004Shm if ((dst = fopen(to, "w")) == NULL) 196559004Shm err(1, "can't fopen %s for writing", to); 196659004Shm 196759004Shm while ((c = getc(src)) != EOF) { 196859004Shm if ((putc(c, dst)) == EOF) 196959004Shm err(1, "error writing to %s", to); 197059004Shm } 197159004Shm 197259004Shm if (ferror(src)) 197359004Shm err(1, "error reading from %s", from); 197459004Shm if ((fclose(src)) != 0) 197559004Shm err(1, "can't fclose %s", to); 197659004Shm if ((fclose(dst)) != 0) 197759004Shm err(1, "can't fclose %s", from); 197859004Shm} 197959004Shm 198059004Shm/* create one or more directory components of a path */ 198159004Shmstatic void 1982114137Sgadcreatedir(const struct conf_entry *ent, char *dirpart) 198359004Shm{ 1984111398Sgad int res; 198559004Shm char *s, *d; 198671299Sjedgar char mkdirpath[MAXPATHLEN]; 198759004Shm struct stat st; 198859004Shm 198959004Shm s = dirpart; 199059004Shm d = mkdirpath; 199159004Shm 199259004Shm for (;;) { 199359004Shm *d++ = *s++; 1994111398Sgad if (*s != '/' && *s != '\0') 1995111398Sgad continue; 1996111398Sgad *d = '\0'; 1997111398Sgad res = lstat(mkdirpath, &st); 1998111398Sgad if (res != 0) { 1999111398Sgad if (noaction) { 2000111967Sgad printf("\tmkdir %s\n", mkdirpath); 2001111398Sgad } else { 2002111398Sgad res = mkdir(mkdirpath, 0755); 2003111398Sgad if (res != 0) 2004111398Sgad err(1, "Error on mkdir(\"%s\") for -a", 2005111398Sgad mkdirpath); 2006111398Sgad } 200759004Shm } 200859004Shm if (*s == '\0') 200959004Shm break; 201059004Shm } 2011114137Sgad if (verbose) { 2012114137Sgad if (ent->firstcreate) 2013114137Sgad printf("Created directory '%s' for new %s\n", 2014114137Sgad dirpart, ent->log); 2015114137Sgad else 2016114137Sgad printf("Created directory '%s' for -a\n", dirpart); 2017114137Sgad } 201859004Shm} 201959004Shm 2020114137Sgad/* 2021114137Sgad * Create a new log file, destroying any currently-existing version 2022114137Sgad * of the log file in the process. If the caller wants a backup copy 2023114137Sgad * of the file to exist, they should call 'link(logfile,logbackup)' 2024114137Sgad * before calling this routine. 2025114137Sgad */ 2026114137Sgadvoid 2027114137Sgadcreatelog(const struct conf_entry *ent) 2028114137Sgad{ 2029114137Sgad int fd, failed; 2030114137Sgad struct stat st; 2031114137Sgad char *realfile, *slash, tempfile[MAXPATHLEN]; 2032114137Sgad 2033114137Sgad fd = -1; 2034114137Sgad realfile = ent->log; 2035114137Sgad 2036114137Sgad /* 2037114137Sgad * If this log file is being created for the first time (-C option), 2038114137Sgad * then it may also be true that the parent directory does not exist 2039114137Sgad * yet. Check, and create that directory if it is missing. 2040114137Sgad */ 2041114137Sgad if (ent->firstcreate) { 2042114137Sgad strlcpy(tempfile, realfile, sizeof(tempfile)); 2043114137Sgad slash = strrchr(tempfile, '/'); 2044114137Sgad if (slash != NULL) { 2045114137Sgad *slash = '\0'; 2046131581Ssobomax failed = stat(tempfile, &st); 2047114137Sgad if (failed && errno != ENOENT) 2048131581Ssobomax err(1, "Error on stat(%s)", tempfile); 2049114137Sgad if (failed) 2050114137Sgad createdir(ent, tempfile); 2051114137Sgad else if (!S_ISDIR(st.st_mode)) 2052114137Sgad errx(1, "%s exists but is not a directory", 2053114137Sgad tempfile); 2054114137Sgad } 2055114137Sgad } 2056114137Sgad 2057114137Sgad /* 2058114137Sgad * First create an unused filename, so it can be chown'ed and 2059114137Sgad * chmod'ed before it is moved into the real location. mkstemp 2060114137Sgad * will create the file mode=600 & owned by us. Note that all 2061114137Sgad * temp files will have a suffix of '.z<something>'. 2062114137Sgad */ 2063114137Sgad strlcpy(tempfile, realfile, sizeof(tempfile)); 2064114137Sgad strlcat(tempfile, ".zXXXXXX", sizeof(tempfile)); 2065114137Sgad if (noaction) 2066114137Sgad printf("\tmktemp %s\n", tempfile); 2067114137Sgad else { 2068114137Sgad fd = mkstemp(tempfile); 2069114137Sgad if (fd < 0) 2070114137Sgad err(1, "can't mkstemp logfile %s", tempfile); 2071114137Sgad 2072114137Sgad /* 2073114137Sgad * Add status message to what will become the new log file. 2074114137Sgad */ 2075114137Sgad if (!(ent->flags & CE_BINARY)) { 2076114137Sgad if (log_trim(tempfile, ent)) 2077114137Sgad err(1, "can't add status message to log"); 2078114137Sgad } 2079114137Sgad } 2080114137Sgad 2081114137Sgad /* Change the owner/group, if we are supposed to */ 2082114137Sgad if (ent->uid != (uid_t)-1 || ent->gid != (gid_t)-1) { 2083114137Sgad if (noaction) 2084114137Sgad printf("\tchown %u:%u %s\n", ent->uid, ent->gid, 2085114137Sgad tempfile); 2086114137Sgad else { 2087114137Sgad failed = fchown(fd, ent->uid, ent->gid); 2088114137Sgad if (failed) 2089114137Sgad err(1, "can't fchown temp file %s", tempfile); 2090114137Sgad } 2091114137Sgad } 2092114137Sgad 2093130043Sgad /* Turn on NODUMP if it was requested in the config-file. */ 2094130043Sgad if (ent->flags & CE_NODUMP) { 2095130043Sgad if (noaction) 2096130043Sgad printf("\tchflags nodump %s\n", tempfile); 2097130043Sgad else { 2098130043Sgad failed = fchflags(fd, UF_NODUMP); 2099130043Sgad if (failed) { 2100130043Sgad warn("log_trim: fchflags(NODUMP)"); 2101130043Sgad } 2102130043Sgad } 2103130043Sgad } 2104130043Sgad 2105114137Sgad /* 2106114137Sgad * Note that if the real logfile still exists, and if the call 2107114137Sgad * to rename() fails, then "neither the old file nor the new 2108114137Sgad * file shall be changed or created" (to quote the standard). 2109114137Sgad * If the call succeeds, then the file will be replaced without 2110114137Sgad * any window where some other process might find that the file 2111114137Sgad * did not exist. 2112114137Sgad * XXX - ? It may be that for some error conditions, we could 2113114137Sgad * retry by first removing the realfile and then renaming. 2114114137Sgad */ 2115114137Sgad if (noaction) { 2116114137Sgad printf("\tchmod %o %s\n", ent->permissions, tempfile); 2117114137Sgad printf("\tmv %s %s\n", tempfile, realfile); 2118114137Sgad } else { 2119114137Sgad failed = fchmod(fd, ent->permissions); 2120114137Sgad if (failed) 2121114137Sgad err(1, "can't fchmod temp file '%s'", tempfile); 2122114137Sgad failed = rename(tempfile, realfile); 2123114137Sgad if (failed) 2124114137Sgad err(1, "can't mv %s to %s", tempfile, realfile); 2125114137Sgad } 2126114137Sgad 2127114137Sgad if (fd >= 0) 2128114137Sgad close(fd); 2129114137Sgad} 2130130165Sgad 2131154566Sgad/* 2132154566Sgad * Change the attributes of a given filename to what was specified in 2133154566Sgad * the newsyslog.conf entry. This routine is only called for files 2134154566Sgad * that newsyslog expects that it has created, and thus it is a fatal 2135154566Sgad * error if this routine finds that the file does not exist. 2136154566Sgad */ 2137130165Sgadstatic void 2138130165Sgadchange_attrs(const char *fname, const struct conf_entry *ent) 2139130165Sgad{ 2140130165Sgad int failed; 2141130165Sgad 2142130165Sgad if (noaction) { 2143130165Sgad printf("\tchmod %o %s\n", ent->permissions, fname); 2144130165Sgad 2145130165Sgad if (ent->uid != (uid_t)-1 || ent->gid != (gid_t)-1) 2146130165Sgad printf("\tchown %u:%u %s\n", 2147130165Sgad ent->uid, ent->gid, fname); 2148130165Sgad 2149130165Sgad if (ent->flags & CE_NODUMP) 2150130165Sgad printf("\tchflags nodump %s\n", fname); 2151130165Sgad return; 2152130165Sgad } 2153130165Sgad 2154130165Sgad failed = chmod(fname, ent->permissions); 2155154566Sgad if (failed) { 2156154566Sgad if (errno != EPERM) 2157154566Sgad err(1, "chmod(%s) in change_attrs", fname); 2158154566Sgad warn("change_attrs couldn't chmod(%s)", fname); 2159154566Sgad } 2160130165Sgad 2161130165Sgad if (ent->uid != (uid_t)-1 || ent->gid != (gid_t)-1) { 2162130165Sgad failed = chown(fname, ent->uid, ent->gid); 2163130165Sgad if (failed) 2164130165Sgad warn("can't chown %s", fname); 2165130165Sgad } 2166130165Sgad 2167130165Sgad if (ent->flags & CE_NODUMP) { 2168130165Sgad failed = chflags(fname, UF_NODUMP); 2169130165Sgad if (failed) 2170130165Sgad warn("can't chflags %s NODUMP", fname); 2171130165Sgad } 2172130165Sgad} 2173