Home | History | Annotate | Line # | Download | only in newsyslog
newsyslog.c revision 1.29
      1  1.29        ad /*	$NetBSD: newsyslog.c,v 1.29 2000/07/11 11:39:47 ad Exp $	*/
      2  1.22        ad 
      3  1.22        ad /*
      4  1.22        ad  * Copyright (c) 1999, 2000 Andrew Doran <ad (at) NetBSD.org>
      5  1.22        ad  * All rights reserved.
      6  1.22        ad  *
      7  1.22        ad  * Redistribution and use in source and binary forms, with or without
      8  1.22        ad  * modification, are permitted provided that the following conditions
      9  1.22        ad  * are met:
     10  1.22        ad  * 1. Redistributions of source code must retain the above copyright
     11  1.22        ad  *    notice, this list of conditions and the following disclaimer.
     12  1.22        ad  * 2. Redistributions in binary form must reproduce the above copyright
     13  1.22        ad  *    notice, this list of conditions and the following disclaimer in the
     14  1.22        ad  *    documentation and/or other materials provided with the distribution.
     15  1.22        ad  *
     16  1.22        ad  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     17  1.22        ad  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     18  1.22        ad  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     19  1.22        ad  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     20  1.22        ad  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     21  1.22        ad  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     22  1.22        ad  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     23  1.22        ad  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     24  1.22        ad  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     25  1.22        ad  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     26  1.22        ad  * SUCH DAMAGE.
     27  1.22        ad  *
     28  1.22        ad  */
     29  1.12   thorpej 
     30   1.1       cgd /*
     31   1.1       cgd  * This file contains changes from the Open Software Foundation.
     32   1.1       cgd  */
     33   1.1       cgd 
     34   1.1       cgd /*
     35  1.22        ad  * Copyright 1988, 1989 by the Massachusetts Institute of Technology
     36  1.22        ad  *
     37  1.22        ad  * Permission to use, copy, modify, and distribute this software
     38  1.22        ad  * and its documentation for any purpose and without fee is
     39  1.22        ad  * hereby granted, provided that the above copyright notice
     40  1.22        ad  * appear in all copies and that both that copyright notice and
     41  1.22        ad  * this permission notice appear in supporting documentation,
     42  1.22        ad  * and that the names of M.I.T. and the M.I.T. S.I.P.B. not be
     43  1.22        ad  * used in advertising or publicity pertaining to distribution
     44  1.22        ad  * of the software without specific, written prior permission.
     45  1.22        ad  * M.I.T. and the M.I.T. S.I.P.B. make no representations about
     46  1.22        ad  * the suitability of this software for any purpose.  It is
     47  1.22        ad  * provided "as is" without express or implied warranty.
     48  1.22        ad  *
     49  1.22        ad  */
     50   1.1       cgd 
     51   1.1       cgd /*
     52  1.22        ad  * newsyslog(1) - a program to roll over log files provided that specified
     53  1.22        ad  * critera are met, optionally preserving a number of historical log files.
     54   1.1       cgd  */
     55   1.1       cgd 
     56  1.13     lukem #include <sys/cdefs.h>
     57   1.5   mycroft #ifndef lint
     58  1.29        ad __RCSID("$NetBSD: newsyslog.c,v 1.29 2000/07/11 11:39:47 ad Exp $");
     59   1.5   mycroft #endif /* not lint */
     60   1.1       cgd 
     61   1.1       cgd #include <sys/types.h>
     62   1.1       cgd #include <sys/time.h>
     63   1.1       cgd #include <sys/stat.h>
     64   1.1       cgd #include <sys/param.h>
     65   1.1       cgd 
     66  1.13     lukem #include <ctype.h>
     67  1.13     lukem #include <fcntl.h>
     68  1.13     lukem #include <grp.h>
     69  1.13     lukem #include <pwd.h>
     70  1.13     lukem #include <signal.h>
     71  1.13     lukem #include <stdio.h>
     72  1.13     lukem #include <stdlib.h>
     73  1.22        ad #include <stdarg.h>
     74  1.13     lukem #include <string.h>
     75  1.14    kleink #include <time.h>
     76  1.13     lukem #include <unistd.h>
     77  1.22        ad #include <errno.h>
     78  1.22        ad #include <err.h>
     79  1.22        ad #include <util.h>
     80  1.22        ad 
     81  1.22        ad #include "pathnames.h"
     82  1.22        ad 
     83  1.28        ad #define	PRHDRINFO(x)	((void)(verbose ? printf x : 0))
     84  1.28        ad #define	PRINFO(x)	((void)(verbose ? printf("  ") + printf x : 0))
     85  1.22        ad 
     86  1.29        ad #define	CE_COMPRESS	0x01	/* Compress the achived log files */
     87  1.29        ad #define	CE_BINARY	0x02	/* Logfile is a binary file/non-syslog */
     88  1.29        ad #define	CE_NOSIGNAL	0x04	/* Don't send a signal when trimmed */
     89  1.29        ad #define CE_CREATE	0x08	/* Create log file if none exists */
     90  1.29        ad #define CE_PLAIN0	0x10	/* Do not compress zero'th history file */
     91  1.13     lukem 
     92   1.1       cgd struct conf_entry {
     93  1.22        ad 	uid_t	uid;			/* Owner of log */
     94  1.22        ad 	gid_t	gid;			/* Group of log */
     95  1.22        ad 	mode_t	mode;			/* File permissions */
     96  1.22        ad 	int	numhist;		/* Number of historical logs to keep */
     97  1.22        ad 	size_t	maxsize;		/* Maximum log size */
     98  1.22        ad 	int	maxage;			/* Hours between log trimming */
     99  1.22        ad 	int	flags;			/* Flags (CE_*) */
    100  1.22        ad 	int	signum;			/* Signal to send */
    101  1.22        ad 	char	pidfile[MAXPATHLEN];	/* File containing PID to signal */
    102  1.22        ad 	char	logfile[MAXPATHLEN];	/* Path to log file */
    103   1.1       cgd };
    104   1.1       cgd 
    105  1.25        ad int     verbose = 0;			/* Be verbose */
    106  1.28        ad int	noaction = 0;			/* Take no action */
    107  1.25        ad char    hostname[MAXHOSTNAMELEN + 1];	/* Hostname, stripped of domain */
    108   1.1       cgd 
    109  1.22        ad int	main(int, char **);
    110  1.22        ad int	parse(struct conf_entry *, FILE *, size_t *);
    111  1.22        ad 
    112  1.29        ad void	log_compress(struct conf_entry *, const char *);
    113  1.22        ad void	log_create(struct conf_entry *);
    114  1.22        ad void	log_examine(struct conf_entry *, int);
    115  1.22        ad void	log_trim(struct conf_entry *);
    116  1.22        ad void	log_trimmed(struct conf_entry *);
    117  1.22        ad 
    118  1.22        ad int	getsig(const char *);
    119  1.22        ad int	isnumber(const char *);
    120  1.22        ad int	parseuserspec(const char *, struct passwd **, struct group **);
    121  1.22        ad pid_t	readpidfile(const char *);
    122  1.22        ad void	usage(void);
    123  1.22        ad 
    124  1.22        ad /*
    125  1.22        ad  * Program entry point.
    126  1.22        ad  */
    127  1.13     lukem int
    128  1.22        ad main(int argc, char **argv)
    129   1.1       cgd {
    130  1.29        ad 	struct conf_entry log;
    131  1.22        ad 	FILE *fd;
    132  1.22        ad 	char *p, *cfile;
    133  1.25        ad 	int c, force, needroot;
    134  1.22        ad 	size_t lineno;
    135  1.22        ad 
    136  1.22        ad 	force = 0;
    137  1.22        ad 	needroot = 1;
    138  1.22        ad 	cfile = _PATH_NEWSYSLOGCONF;
    139   1.1       cgd 
    140  1.22        ad 	gethostname(hostname, sizeof(hostname));
    141  1.15       mrg 	hostname[sizeof(hostname) - 1] = '\0';
    142   1.8       jtc 
    143   1.8       jtc 	/* Truncate domain */
    144  1.22        ad 	if ((p = strchr(hostname, '.')) != NULL)
    145   1.8       jtc 		*p = '\0';
    146  1.22        ad 
    147  1.22        ad 	/* Parse command line options */
    148  1.28        ad 	while ((c = getopt(argc, argv, "f:nrvF")) != -1) {
    149  1.22        ad 		switch (c) {
    150  1.28        ad 		case 'f':
    151  1.28        ad 			cfile = optarg;
    152  1.28        ad 			break;
    153  1.28        ad 		case 'n':
    154  1.28        ad 			noaction = 1;
    155  1.29        ad 			verbose = 1;
    156  1.28        ad 			break;
    157  1.22        ad 		case 'r':
    158  1.22        ad 			needroot = 0;
    159  1.22        ad 			break;
    160  1.22        ad 		case 'v':
    161  1.22        ad 			verbose = 1;
    162  1.22        ad 			break;
    163  1.22        ad 		case 'F':
    164  1.22        ad 			force = 1;
    165  1.22        ad 			break;
    166  1.22        ad 		default:
    167  1.22        ad 			usage();
    168  1.28        ad 			/* NOTREACHED */
    169  1.22        ad 		}
    170   1.8       jtc 	}
    171   1.1       cgd 
    172  1.28        ad 	if (needroot && geteuid() != 0)
    173  1.22        ad 		errx(EXIT_FAILURE, "must be run as root");
    174  1.22        ad 
    175  1.22        ad 	if (strcmp(cfile, "-") == 0)
    176  1.22        ad 		fd = stdin;
    177  1.22        ad 	else if ((fd = fopen(cfile, "rt")) == NULL)
    178  1.22        ad 		err(EXIT_FAILURE, "%s", cfile);
    179  1.22        ad 
    180  1.29        ad 	for (lineno = 0; !parse(&log, fd, &lineno);)
    181  1.29        ad 		log_examine(&log, force);
    182  1.22        ad 
    183  1.22        ad 	if (fd != stdin)
    184  1.22        ad 		fclose(fd);
    185   1.1       cgd 
    186  1.22        ad 	exit(EXIT_SUCCESS);
    187  1.22        ad 	/* NOTREACHED */
    188   1.1       cgd }
    189   1.1       cgd 
    190  1.22        ad /*
    191  1.22        ad  * Parse a single line from the configuration file.
    192   1.1       cgd  */
    193  1.22        ad int
    194  1.22        ad parse(struct conf_entry *log, FILE *fd, size_t *_lineno)
    195   1.1       cgd {
    196  1.22        ad 	char *line, *q, **ap, *argv[10];
    197  1.22        ad 	struct passwd *pw;
    198  1.22        ad 	struct group *gr;
    199  1.22        ad 	int nf, lineno, i;
    200  1.22        ad 
    201  1.22        ad 	if ((line = fparseln(fd, NULL, _lineno, NULL, 0)) == NULL)
    202  1.22        ad 		return (-1);
    203  1.22        ad 	lineno = (int)*_lineno;
    204  1.22        ad 
    205  1.22        ad 	for (ap = argv, nf = 0; (*ap = strsep(&line, " \t")) != NULL;)
    206  1.22        ad 		if (**ap != '\0') {
    207  1.22        ad 			if (++nf == sizeof(argv) / sizeof(argv[0])) {
    208  1.22        ad 				warnx("config line %d: too many fields",
    209  1.22        ad 				    lineno);
    210  1.22        ad 				return (-1);
    211  1.22        ad 			}
    212  1.22        ad 			ap++;
    213  1.21        ad 		}
    214  1.22        ad 
    215  1.22        ad 	if (nf == 0)
    216  1.22        ad 		return (0);
    217  1.22        ad 
    218  1.22        ad 	if (nf < 6)
    219  1.22        ad 		errx(EXIT_FAILURE, "config line %d: too few fields", lineno);
    220  1.22        ad 
    221  1.22        ad 	ap = argv;
    222  1.22        ad 	strlcpy(log->logfile, *ap++, sizeof(log->logfile));
    223  1.21        ad 
    224  1.22        ad 	if (strchr(*ap, ':') != NULL) {
    225  1.22        ad 		if (parseuserspec(*ap++, &pw, &gr)) {
    226  1.22        ad 			warnx("config line %d: unknown user/group", lineno);
    227  1.22        ad 			return (-1);
    228  1.22        ad 		}
    229  1.22        ad 		log->uid = pw->pw_uid;
    230  1.22        ad 		log->gid = gr->gr_gid;
    231  1.22        ad 		if (nf < 7)
    232  1.22        ad 			errx(EXIT_FAILURE, "config line %d: too few fields",
    233  1.22        ad 			    lineno);
    234  1.22        ad 	}
    235  1.22        ad 
    236  1.22        ad 	if (sscanf(*ap++, "%o", &i) != 1) {
    237  1.22        ad 		warnx("config line %d: bad permissions", lineno);
    238  1.22        ad 		return (-1);
    239  1.22        ad 	}
    240  1.22        ad 	log->mode = (mode_t)i;
    241  1.22        ad 
    242  1.26   aymeric 	if (sscanf(*ap++, "%d", &log->numhist) != 1) {
    243  1.22        ad 		warnx("config line %d: bad log count", lineno);
    244  1.22        ad 		return (-1);
    245  1.22        ad 	}
    246  1.22        ad 
    247  1.22        ad 	if (isdigit(**ap))
    248  1.22        ad 		log->maxsize = atoi(*ap);
    249  1.22        ad 	else if (**ap == '*')
    250  1.22        ad 		log->maxsize = (size_t)-1;
    251  1.22        ad 	else {
    252  1.22        ad 		warnx("config line %d: bad log size", lineno);
    253  1.22        ad 		return (-1);
    254  1.22        ad 	}
    255  1.22        ad 	ap++;
    256  1.22        ad 
    257  1.22        ad 	if (isdigit(**ap))
    258  1.22        ad 		log->maxage = atoi(*ap);
    259  1.22        ad 	else if (**ap == '*')
    260  1.22        ad 		log->maxage = -1;
    261  1.22        ad 	else {
    262  1.22        ad 		warnx("config line %d: bad log age", lineno);
    263  1.22        ad 		return (-1);
    264  1.22        ad 	}
    265  1.22        ad 	ap++;
    266  1.22        ad 
    267  1.22        ad 	log->flags = 0;
    268  1.28        ad 	for (q = *ap++; q != NULL && *q != '\0'; q++) {
    269  1.22        ad 		switch (tolower(*q)) {
    270  1.22        ad 		case 'b':
    271  1.22        ad 			log->flags |= CE_BINARY;
    272  1.22        ad 			break;
    273  1.22        ad 		case 'c':
    274  1.22        ad 			log->flags |= CE_CREATE;
    275  1.22        ad 			break;
    276  1.22        ad 		case 'n':
    277  1.22        ad 			log->flags |= CE_NOSIGNAL;
    278  1.22        ad 			break;
    279  1.29        ad 		case 'p':
    280  1.29        ad 			log->flags |= CE_PLAIN0;
    281  1.29        ad 			break;
    282  1.22        ad 		case 'z':
    283  1.29        ad 			log->flags |= CE_COMPRESS;
    284  1.22        ad 			break;
    285  1.22        ad 		case '-':
    286  1.22        ad 			break;
    287  1.22        ad 		default:
    288  1.22        ad 			warnx("config line %d: bad flags", lineno);
    289  1.22        ad 			return (-1);
    290  1.22        ad 		}
    291  1.22        ad 	}
    292   1.1       cgd 
    293  1.22        ad 	if (*ap != NULL && **ap == '/')
    294  1.22        ad 		strlcpy(log->pidfile, *ap++, sizeof(log->pidfile));
    295  1.22        ad 	else
    296  1.22        ad 		log->pidfile[0] = '\0';
    297  1.22        ad 
    298  1.22        ad 	if (*ap != NULL && (log->signum = getsig(*ap++)) < 0) {
    299  1.22        ad 		warnx("config line %d: bad signal type", lineno);
    300  1.22        ad 		return (-1);
    301  1.22        ad 	} else
    302  1.22        ad 		log->signum = SIGHUP;
    303  1.22        ad 
    304  1.22        ad 	return (0);
    305   1.1       cgd }
    306   1.1       cgd 
    307  1.22        ad /*
    308  1.22        ad  * Examine a log file.  If the trim conditions are met, call log_trim() to
    309  1.22        ad  * trim the log file.
    310  1.22        ad  */
    311  1.13     lukem void
    312  1.29        ad log_examine(struct conf_entry *log, int force)
    313  1.22        ad {
    314  1.22        ad 	struct stat sb;
    315  1.22        ad 	size_t size;
    316  1.28        ad 	int age;
    317  1.22        ad 	char tmp[MAXPATHLEN];
    318  1.22        ad 	time_t now;
    319  1.22        ad 
    320  1.29        ad 	if (log->logfile[0] == '\0')
    321  1.22        ad 		return;
    322  1.28        ad 
    323  1.29        ad 	PRHDRINFO(("\n%s <%d%s>: ", log->logfile, log->numhist,
    324  1.29        ad 	    (log->flags & CE_COMPRESS) != 0 ? "Z" : ""));
    325  1.28        ad 
    326  1.29        ad 	if (stat(log->logfile, &sb) < 0) {
    327  1.29        ad 		if (errno == ENOENT && (log->flags & CE_CREATE) != 0) {
    328  1.28        ad 			PRHDRINFO(("creating; "));
    329  1.28        ad 			if (!noaction)
    330  1.29        ad 				log_create(log);
    331  1.28        ad 			else {
    332  1.28        ad 				PRHDRINFO(("can't proceed with `-n'\n"));
    333  1.28        ad 				return;
    334  1.28        ad 			}
    335  1.29        ad 			if (stat(log->logfile, &sb))
    336  1.29        ad 				err(EXIT_FAILURE, "%s", log->logfile);
    337  1.28        ad 		} else if (errno == ENOENT) {
    338  1.28        ad 			PRHDRINFO(("does not exist --> skip log\n"));
    339  1.22        ad 			return;
    340  1.28        ad 		} else if (errno != 0)
    341  1.29        ad 			err(EXIT_FAILURE, "%s", log->logfile);
    342  1.22        ad 	}
    343  1.22        ad 
    344  1.22        ad 
    345  1.22        ad 	size = ((size_t)sb.st_blocks * S_BLKSIZE) >> 10;
    346  1.20        ad 
    347  1.22        ad 	now = time(NULL);
    348  1.29        ad 	strlcpy(tmp, log->logfile, sizeof(tmp));
    349  1.22        ad 	strlcat(tmp, ".0", sizeof(tmp));
    350  1.22        ad 	if (stat(tmp, &sb) < 0) {
    351  1.22        ad 		strlcat(tmp, ".gz", sizeof(tmp));
    352  1.22        ad 		if (stat(tmp, &sb) < 0)
    353  1.28        ad 			age = -1;
    354  1.11   thorpej 		else
    355  1.28        ad 			age = (int)(now - sb.st_mtime + 1800) / 3600;
    356  1.17  christos 	} else
    357  1.28        ad 		age = (int)(now - sb.st_mtime + 1800) / 3600;
    358  1.11   thorpej 
    359  1.22        ad 	if (verbose) {
    360  1.29        ad 		if (log->maxsize != (size_t)-1)
    361  1.28        ad 			PRHDRINFO(("size (Kb): %lu [%lu] ",
    362  1.27     assar 				(u_long)size,
    363  1.29        ad 				(u_long)log->maxsize));
    364  1.29        ad 		if (log->maxage > 0)
    365  1.29        ad 			PRHDRINFO(("age (hr): %d [%d] ", age, log->maxage));
    366  1.21        ad 	}
    367  1.21        ad 
    368  1.22        ad 	/*
    369  1.22        ad 	 * Note: if maxage is used as a trim condition, we need at least one
    370  1.22        ad 	 * historical log file to determine the `age' of the active log file.
    371  1.22        ad 	 */
    372  1.29        ad 	if ((log->maxage > 0 && (age >= log->maxage || age < 0)) ||
    373  1.29        ad 	    size >= log->maxsize || force) {
    374  1.28        ad 		PRHDRINFO(("--> trim log\n"));
    375  1.29        ad 		log_trim(log);
    376  1.22        ad 	} else
    377  1.28        ad 		PRHDRINFO(("--> skip log\n"));
    378   1.1       cgd }
    379   1.1       cgd 
    380  1.22        ad /*
    381  1.22        ad  * Trim the specified log file.
    382  1.22        ad  */
    383  1.22        ad void
    384  1.22        ad log_trim(struct conf_entry *log)
    385   1.1       cgd {
    386  1.22        ad 	char file1[MAXPATHLEN], file2[MAXPATHLEN];
    387  1.22        ad 	int i;
    388  1.22        ad 	struct stat st;
    389  1.22        ad 	pid_t pid;
    390  1.22        ad 
    391  1.29        ad 	/* Remove oldest historical log */
    392  1.29        ad 	snprintf(file1, sizeof(file1), "%s.%d", log->logfile, log->numhist - 1);
    393  1.22        ad 
    394  1.22        ad 	PRINFO(("rm -f %s\n", file1));
    395  1.28        ad 	if (!noaction)
    396  1.28        ad 		unlink(file1);
    397  1.29        ad 	strlcat(file1, ".gz", sizeof(file1));
    398  1.29        ad 	PRINFO(("rm -f %s\n", file1));
    399  1.28        ad 	if (!noaction)
    400  1.29        ad 		unlink(file1);
    401  1.22        ad 
    402  1.28        ad 	/* Move down log files */
    403  1.22        ad 	for (i = log->numhist - 1; i != 0; i--) {
    404  1.22        ad 		snprintf(file1, sizeof(file1), "%s.%d", log->logfile, i - 1);
    405  1.29        ad 		snprintf(file2, sizeof(file2), "%s.%d", log->logfile, i);
    406  1.22        ad 
    407  1.22        ad 		if (lstat(file1, &st) != 0) {
    408  1.29        ad 			strlcat(file1, ".gz", sizeof(file1));
    409  1.29        ad 			strlcat(file2, ".gz", sizeof(file2));
    410  1.29        ad 			if (lstat(file1, &st) != 0)
    411  1.22        ad 				continue;
    412  1.22        ad 		}
    413  1.22        ad 
    414  1.29        ad 		PRINFO(("mv %s %s\n", file1, file2));
    415  1.28        ad 		if (!noaction)
    416  1.29        ad 			if (rename(file1, file2))
    417  1.29        ad 				err(EXIT_FAILURE, "%s", file1);
    418  1.29        ad 		PRINFO(("chmod %o %s\n", log->mode, file2));
    419  1.28        ad 		if (!noaction)
    420  1.29        ad 			if (chmod(file2, log->mode))
    421  1.29        ad 				err(EXIT_FAILURE, "%s", file2);
    422  1.29        ad 		PRINFO(("chown %d:%d %s\n", log->uid, log->gid, file2));
    423  1.28        ad 		if (!noaction)
    424  1.29        ad 			if (chown(file2, log->uid, log->gid))
    425  1.29        ad 				err(EXIT_FAILURE, "%s", file2);
    426  1.29        ad 
    427  1.29        ad 	}
    428  1.29        ad 
    429  1.29        ad 	/*
    430  1.29        ad 	 * If a historical log file isn't compressed, and 'z' has been
    431  1.29        ad 	 * specified, compress it.  (This is convenient, but is also needed
    432  1.29        ad 	 * if 'p' has been specified.)  It should be noted that gzip(1)
    433  1.29        ad 	 * preserves file ownership and file mode.
    434  1.29        ad 	 */
    435  1.29        ad 	for (i = (log->flags & CE_PLAIN0) != 0; i < log->numhist; i++) {
    436  1.29        ad 		snprintf(file1, sizeof(file1), "%s.%d", log->logfile, i);
    437  1.29        ad 		if (lstat(file1, &st) != 0)
    438  1.29        ad 			continue;
    439  1.29        ad 		snprintf(file2, sizeof(file2), "%s.gz", file1);
    440  1.29        ad 		if (lstat(file2, &st) == 0)
    441  1.29        ad 			continue;
    442  1.29        ad 		log_compress(log, file1);
    443  1.22        ad 	}
    444  1.22        ad 
    445  1.28        ad 	log_trimmed(log);
    446  1.22        ad 
    447  1.22        ad 	if (log->numhist == 0) {
    448  1.28        ad 		PRINFO(("rm -f %s\n", log->logfile));
    449  1.28        ad 		if (!noaction)
    450  1.28        ad 			if (unlink(log->logfile))
    451  1.28        ad 				err(EXIT_FAILURE, "%s", log->logfile);
    452  1.22        ad 	} else {
    453  1.29        ad 		snprintf(file1, sizeof(file1), "%s.0", log->logfile);
    454  1.28        ad 		PRINFO(("mv %s %s\n", log->logfile, file1));
    455  1.28        ad 		if (!noaction)
    456  1.28        ad 			if (rename(log->logfile, file1))
    457  1.28        ad 				err(EXIT_FAILURE, "%s", log->logfile);
    458  1.22        ad 	}
    459  1.22        ad 
    460  1.29        ad 	PRINFO(("(create new log)\n"));
    461  1.22        ad 	log_create(log);
    462  1.28        ad 	log_trimmed(log);
    463  1.22        ad 
    464  1.22        ad 	PRINFO(("chmod %o %s\n", log->mode, log->logfile));
    465  1.28        ad 	if (!noaction)
    466  1.28        ad 		if (chmod(log->logfile, log->mode))
    467  1.28        ad 			err(EXIT_FAILURE, "%s", log->logfile);
    468  1.22        ad 
    469  1.22        ad 	if ((log->flags & CE_NOSIGNAL) == 0) {
    470  1.22        ad 		if (log->pidfile[0] != '\0')
    471  1.22        ad 			pid = readpidfile(log->pidfile);
    472  1.22        ad 		else
    473  1.22        ad 			pid = readpidfile(_PATH_SYSLOGDPID);
    474  1.22        ad 
    475  1.22        ad 		if (pid != (pid_t)-1) {
    476  1.29        ad 			PRINFO(("kill -%s %lu\n", sys_signame[log->signum],
    477  1.29        ad 			    (u_long)pid));
    478  1.28        ad 			if (!noaction)
    479  1.28        ad 				if (kill(pid, log->signum))
    480  1.28        ad 					warn("kill");
    481  1.22        ad 		}
    482  1.22        ad 	}
    483  1.29        ad 
    484  1.29        ad 	if ((log->flags & (CE_PLAIN0 | CE_COMPRESS)) == CE_COMPRESS) {
    485  1.29        ad 		snprintf(file1, sizeof(file1), "%s.0", log->logfile);
    486  1.29        ad 		log_compress(log, file1);
    487  1.22        ad 	}
    488   1.1       cgd }
    489   1.1       cgd 
    490  1.22        ad /*
    491  1.22        ad  * Write an entry to the log file recording the fact that it was trimmed.
    492  1.22        ad  */
    493  1.13     lukem void
    494  1.22        ad log_trimmed(struct conf_entry *log)
    495   1.1       cgd {
    496  1.22        ad 	FILE *fd;
    497  1.22        ad 	time_t now;
    498  1.22        ad 	char *daytime;
    499  1.22        ad 
    500  1.28        ad 	if ((log->flags & CE_BINARY) != 0)
    501  1.28        ad 		return;
    502  1.28        ad 	PRINFO(("(append rotation notice to %s)\n", log->logfile));
    503  1.28        ad 	if (noaction)
    504  1.28        ad 		return;
    505  1.28        ad 
    506  1.22        ad 	if ((fd = fopen(log->logfile, "at")) == NULL)
    507  1.22        ad 		err(EXIT_FAILURE, "%s", log->logfile);
    508  1.22        ad 
    509  1.22        ad 	now = time(NULL);
    510  1.22        ad 	daytime = ctime(&now) + 4;
    511  1.22        ad 	daytime[15] = '\0';
    512  1.22        ad 
    513  1.29        ad 	fprintf(fd, "%s %s newsyslog[%lu]: log file turned over\n", daytime,
    514  1.29        ad 	    hostname, (u_long)getpid());
    515  1.22        ad 	fclose(fd);
    516   1.1       cgd }
    517   1.1       cgd 
    518  1.22        ad /*
    519  1.29        ad  * Create a new log file.
    520  1.22        ad  */
    521  1.22        ad void
    522  1.29        ad log_create(struct conf_entry *log)
    523   1.1       cgd {
    524  1.22        ad 	int fd;
    525   1.1       cgd 
    526  1.28        ad 	if (noaction)
    527  1.28        ad 		return;
    528  1.28        ad 
    529  1.29        ad 	if ((fd = creat(log->logfile, log->mode)) < 0)
    530  1.29        ad 		err(EXIT_FAILURE, "%s", log->logfile);
    531  1.29        ad 	if (fchown(fd, log->uid, log->gid) < 0)
    532  1.29        ad 		err(EXIT_FAILURE, "%s", log->logfile);
    533  1.28        ad 	close(fd);
    534   1.1       cgd }
    535   1.1       cgd 
    536  1.22        ad /*
    537  1.29        ad  * Compress a log file.  This routine takes an additional string argument:
    538  1.29        ad  * it is also used to compress historical log files.
    539  1.29        ad  */
    540  1.29        ad void
    541  1.29        ad log_compress(struct conf_entry *log, const char *fn)
    542  1.29        ad {
    543  1.29        ad 	pid_t pid;
    544  1.29        ad 
    545  1.29        ad 	PRINFO(("gzip %s\n", fn));
    546  1.29        ad 	if (!noaction) {
    547  1.29        ad 		if ((pid = fork()) < 0)
    548  1.29        ad 			err(EXIT_FAILURE, "fork");
    549  1.29        ad 		else if (pid == 0) {
    550  1.29        ad 			execl(_PATH_GZIP, "gzip", "-f", fn, NULL);
    551  1.29        ad 			err(EXIT_FAILURE, _PATH_GZIP);
    552  1.29        ad 		}
    553  1.29        ad 	}
    554  1.29        ad }
    555  1.29        ad 
    556  1.29        ad /*
    557  1.22        ad  * Display program usage information.
    558  1.22        ad  */
    559  1.22        ad void
    560  1.22        ad usage(void)
    561   1.1       cgd {
    562   1.1       cgd 
    563  1.22        ad 	fprintf(stderr, "usage: newsyslog [-Frv] [-f config-file]\n");
    564  1.22        ad 	exit(EXIT_FAILURE);
    565   1.1       cgd }
    566   1.1       cgd 
    567  1.22        ad /*
    568  1.22        ad  * Return non-zero if a string represents a decimal value.
    569  1.22        ad  */
    570  1.22        ad int
    571  1.22        ad isnumber(const char *string)
    572  1.22        ad {
    573   1.1       cgd 
    574  1.22        ad 	while (isdigit(*string))
    575  1.22        ad 		string++;
    576   1.1       cgd 
    577  1.22        ad 	return (*string == '\0');
    578  1.21        ad }
    579  1.21        ad 
    580  1.22        ad /*
    581  1.22        ad  * Given a signal name, attempt to find the corresponding signal number.
    582  1.22        ad  */
    583  1.21        ad int
    584  1.22        ad getsig(const char *sig)
    585  1.21        ad {
    586  1.22        ad 	char *p;
    587  1.21        ad 	int n;
    588  1.21        ad 
    589  1.21        ad 	if (isnumber(sig)) {
    590  1.22        ad 		n = (int)strtol(sig, &p, 0);
    591  1.28        ad 		if (p != '\0' || n < 0 || n >= NSIG)
    592  1.21        ad 			return (-1);
    593  1.21        ad 		return (n);
    594  1.21        ad 	}
    595  1.21        ad 
    596  1.28        ad 	if (strncasecmp(sig, "SIG", 3) == 0)
    597  1.21        ad 		sig += 3;
    598  1.22        ad 	for (n = 1; n < NSIG; n++)
    599  1.22        ad 		if (strcasecmp(sys_signame[n], sig) == 0)
    600  1.21        ad 			return (n);
    601  1.22        ad 	return (-1);
    602  1.22        ad }
    603  1.22        ad 
    604  1.22        ad /*
    605  1.22        ad  * Given a path to a PID file, return the PID contained within.
    606  1.22        ad  */
    607  1.22        ad pid_t
    608  1.22        ad readpidfile(const char *file)
    609  1.22        ad {
    610  1.22        ad 	FILE *fd;
    611  1.22        ad 	char line[BUFSIZ];
    612  1.22        ad 	pid_t pid;
    613  1.22        ad 
    614  1.28        ad #ifdef notyet
    615  1.28        ad 	if (file[0] != '/')
    616  1.28        ad 		snprintf(tmp, sizeof(tmp), "%s%s", _PATH_VARRUN, file);
    617  1.28        ad 	else
    618  1.28        ad 		strlcpy(tmp, file, sizeof(tmp));
    619  1.28        ad #endif
    620  1.28        ad 
    621  1.25        ad 	if ((fd = fopen(file, "rt")) == NULL) {
    622  1.25        ad 		warn("%s", file);
    623  1.22        ad 		return (-1);
    624  1.21        ad 	}
    625  1.25        ad 
    626  1.22        ad 	if (fgets(line, sizeof(line) - 1, fd) != NULL) {
    627  1.22        ad 		line[sizeof(line) - 1] = '\0';
    628  1.22        ad 		pid = (pid_t)strtol(line, NULL, 0);
    629  1.29        ad 	} else {
    630  1.29        ad 		warnx("unable to read %s", file);
    631  1.29        ad 		pid = (pid_t)-1;
    632  1.22        ad 	}
    633  1.29        ad 
    634  1.22        ad 	fclose(fd);
    635  1.22        ad 	return (pid);
    636  1.22        ad }
    637  1.22        ad 
    638  1.22        ad /*
    639  1.22        ad  * Parse a user:group specification.
    640  1.22        ad  *
    641  1.22        ad  * XXX This is over the top for newsyslog(1).  It should be moved to libutil.
    642  1.22        ad  */
    643  1.22        ad int
    644  1.22        ad parseuserspec(const char *name, struct passwd **pw, struct group **gr)
    645  1.22        ad {
    646  1.22        ad 	char buf[MAXLOGNAME * 2 + 2], *group;
    647  1.22        ad 
    648  1.22        ad 	strlcpy(buf, name, sizeof(buf));
    649  1.22        ad 	*gr = NULL;
    650  1.22        ad 
    651  1.22        ad 	/*
    652  1.22        ad 	 * Before attempting to use '.' as a separator, see if the whole
    653  1.28        ad 	 * string resolves as a user name.
    654  1.22        ad 	 */
    655  1.22        ad 	if ((*pw = getpwnam(buf)) != NULL) {
    656  1.22        ad 		*gr = getgrgid((*pw)->pw_gid);
    657  1.22        ad 		return (0);
    658  1.22        ad 	}
    659  1.22        ad 
    660  1.22        ad 	/* Split the user and group name. */
    661  1.22        ad 	if ((group = strchr(buf, ':')) != NULL ||
    662  1.22        ad 	    (group = strchr(buf, '.')) != NULL)
    663  1.22        ad 		*group++ = '\0';
    664  1.22        ad 
    665  1.22        ad 	if (isnumber(buf))
    666  1.22        ad 		*pw = getpwuid((uid_t)atoi(buf));
    667  1.22        ad 	else
    668  1.22        ad 		*pw = getpwnam(buf);
    669  1.22        ad 
    670  1.22        ad 	/*
    671  1.22        ad 	 * Find the group.  If a group wasn't specified, use the user's
    672  1.22        ad 	 * `natural' group.  We get to this point even if no user was found.
    673  1.22        ad 	 * This is to allow the caller to get a better idea of what went
    674  1.22        ad 	 * wrong, if anything.
    675  1.22        ad 	 */
    676  1.22        ad 	if (group == NULL || *group == '\0') {
    677  1.22        ad 		if (*pw == NULL)
    678  1.22        ad 			return (-1);
    679  1.22        ad 		*gr = getgrgid((*pw)->pw_gid);
    680  1.22        ad 	} else if (isnumber(group))
    681  1.22        ad 		*gr = getgrgid((gid_t)atoi(group));
    682  1.22        ad 	else
    683  1.22        ad 		*gr = getgrnam(group);
    684  1.22        ad 
    685  1.28        ad 	return (*pw != NULL && *gr != NULL ? 0 : -1);
    686   1.1       cgd }
    687