Home | History | Annotate | Line # | Download | only in newsyslog
newsyslog.c revision 1.27
      1  1.27     assar /*	$NetBSD: newsyslog.c,v 1.27 2000/07/10 02:23:04 assar 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.22        ad  *
     55  1.22        ad  * XXX too much size_t fsckage.
     56   1.1       cgd  */
     57   1.1       cgd 
     58  1.13     lukem #include <sys/cdefs.h>
     59   1.5   mycroft #ifndef lint
     60  1.27     assar __RCSID("$NetBSD: newsyslog.c,v 1.27 2000/07/10 02:23:04 assar Exp $");
     61   1.5   mycroft #endif /* not lint */
     62   1.1       cgd 
     63   1.1       cgd #include <sys/types.h>
     64   1.1       cgd #include <sys/time.h>
     65   1.1       cgd #include <sys/stat.h>
     66   1.1       cgd #include <sys/param.h>
     67   1.1       cgd 
     68  1.13     lukem #include <ctype.h>
     69  1.13     lukem #include <fcntl.h>
     70  1.13     lukem #include <grp.h>
     71  1.13     lukem #include <pwd.h>
     72  1.13     lukem #include <signal.h>
     73  1.13     lukem #include <stdio.h>
     74  1.13     lukem #include <stdlib.h>
     75  1.22        ad #include <stdarg.h>
     76  1.13     lukem #include <string.h>
     77  1.14    kleink #include <time.h>
     78  1.13     lukem #include <unistd.h>
     79  1.22        ad #include <errno.h>
     80  1.22        ad #include <err.h>
     81  1.22        ad #include <util.h>
     82  1.22        ad 
     83  1.22        ad #include "pathnames.h"
     84  1.22        ad 
     85  1.22        ad #define	PRINFO(x)	((void)(verbose ? printf x : 0))
     86  1.22        ad 
     87  1.22        ad #define	CE_COMPACT	1	/* Compact the achived log files */
     88  1.22        ad #define	CE_BINARY	2	/* Logfile is a binary file/non-syslog */
     89  1.22        ad #define	CE_NOSIGNAL	4	/* Don't send a signal when trimmed */
     90  1.22        ad #define CE_CREATE	8	/* Create log file if none exists */
     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.25        ad char    hostname[MAXHOSTNAMELEN + 1];	/* Hostname, stripped of domain */
    107   1.1       cgd 
    108  1.22        ad int	main(int, char **);
    109  1.22        ad int	parse(struct conf_entry *, FILE *, size_t *);
    110  1.22        ad 
    111  1.22        ad void	log_create(struct conf_entry *);
    112  1.22        ad void	log_examine(struct conf_entry *, int);
    113  1.22        ad void	log_trim(struct conf_entry *);
    114  1.22        ad void	log_trimmed(struct conf_entry *);
    115  1.22        ad 
    116  1.22        ad int	getsig(const char *);
    117  1.22        ad int	isnumber(const char *);
    118  1.22        ad int	parseuserspec(const char *, struct passwd **, struct group **);
    119  1.22        ad pid_t	readpidfile(const char *);
    120  1.22        ad void	usage(void);
    121  1.22        ad 
    122  1.22        ad /*
    123  1.22        ad  * Program entry point.
    124  1.22        ad  */
    125  1.13     lukem int
    126  1.22        ad main(int argc, char **argv)
    127   1.1       cgd {
    128  1.22        ad 	struct conf_entry ent;
    129  1.22        ad 	FILE *fd;
    130  1.22        ad 	char *p, *cfile;
    131  1.25        ad 	int c, force, needroot;
    132  1.22        ad 	size_t lineno;
    133  1.22        ad 
    134  1.22        ad 	force = 0;
    135  1.22        ad 	needroot = 1;
    136  1.22        ad 	cfile = _PATH_NEWSYSLOGCONF;
    137   1.1       cgd 
    138  1.22        ad 	gethostname(hostname, sizeof(hostname));
    139  1.15       mrg 	hostname[sizeof(hostname) - 1] = '\0';
    140   1.8       jtc 
    141   1.8       jtc 	/* Truncate domain */
    142  1.22        ad 	if ((p = strchr(hostname, '.')) != NULL)
    143   1.8       jtc 		*p = '\0';
    144  1.22        ad 
    145  1.22        ad 	/* Parse command line options */
    146  1.25        ad 	while ((c = getopt(argc, argv, "Frvf:")) != -1) {
    147  1.22        ad 		switch (c) {
    148  1.22        ad 		case 'r':
    149  1.22        ad 			needroot = 0;
    150  1.22        ad 			break;
    151  1.22        ad 		case 'v':
    152  1.22        ad 			verbose = 1;
    153  1.22        ad 			break;
    154  1.22        ad 		case 'f':
    155  1.22        ad 			cfile = optarg;
    156  1.22        ad 			break;
    157  1.22        ad 		case 'F':
    158  1.22        ad 			force = 1;
    159  1.22        ad 			break;
    160  1.22        ad 		default:
    161  1.22        ad 			usage();
    162  1.22        ad 		}
    163   1.8       jtc 	}
    164   1.1       cgd 
    165  1.25        ad 	if (needroot && getuid() != 0 && geteuid() != 0)
    166  1.22        ad 		errx(EXIT_FAILURE, "must be run as root");
    167  1.22        ad 
    168  1.22        ad 	if (strcmp(cfile, "-") == 0)
    169  1.22        ad 		fd = stdin;
    170  1.22        ad 	else if ((fd = fopen(cfile, "rt")) == NULL)
    171  1.22        ad 		err(EXIT_FAILURE, "%s", cfile);
    172  1.22        ad 
    173  1.22        ad 	for (lineno = 0; !parse(&ent, fd, &lineno);)
    174  1.22        ad 		log_examine(&ent, force);
    175  1.22        ad 
    176  1.22        ad 	if (fd != stdin)
    177  1.22        ad 		fclose(fd);
    178   1.1       cgd 
    179  1.22        ad 	exit(EXIT_SUCCESS);
    180  1.22        ad 	/* NOTREACHED */
    181   1.1       cgd }
    182   1.1       cgd 
    183  1.22        ad /*
    184  1.22        ad  * Parse a single line from the configuration file.
    185   1.1       cgd  */
    186  1.22        ad int
    187  1.22        ad parse(struct conf_entry *log, FILE *fd, size_t *_lineno)
    188   1.1       cgd {
    189  1.22        ad 	char *line, *q, **ap, *argv[10];
    190  1.22        ad 	struct passwd *pw;
    191  1.22        ad 	struct group *gr;
    192  1.22        ad 	int nf, lineno, i;
    193  1.22        ad 
    194  1.22        ad 	if ((line = fparseln(fd, NULL, _lineno, NULL, 0)) == NULL)
    195  1.22        ad 		return (-1);
    196  1.22        ad 	lineno = (int)*_lineno;
    197  1.22        ad 
    198  1.22        ad 	for (ap = argv, nf = 0; (*ap = strsep(&line, " \t")) != NULL;)
    199  1.22        ad 		if (**ap != '\0') {
    200  1.22        ad 			if (++nf == sizeof(argv) / sizeof(argv[0])) {
    201  1.22        ad 				warnx("config line %d: too many fields",
    202  1.22        ad 				    lineno);
    203  1.22        ad 				return (-1);
    204  1.22        ad 			}
    205  1.22        ad 			ap++;
    206  1.21        ad 		}
    207  1.22        ad 
    208  1.22        ad 	if (nf == 0)
    209  1.22        ad 		return (0);
    210  1.22        ad 
    211  1.22        ad 	if (nf < 6)
    212  1.22        ad 		errx(EXIT_FAILURE, "config line %d: too few fields", lineno);
    213  1.22        ad 
    214  1.22        ad 	ap = argv;
    215  1.22        ad 	strlcpy(log->logfile, *ap++, sizeof(log->logfile));
    216  1.21        ad 
    217  1.22        ad 	if (strchr(*ap, ':') != NULL) {
    218  1.22        ad 		if (parseuserspec(*ap++, &pw, &gr)) {
    219  1.22        ad 			warnx("config line %d: unknown user/group", lineno);
    220  1.22        ad 			return (-1);
    221  1.22        ad 		}
    222  1.22        ad 		log->uid = pw->pw_uid;
    223  1.22        ad 		log->gid = gr->gr_gid;
    224  1.22        ad 		if (nf < 7)
    225  1.22        ad 			errx(EXIT_FAILURE, "config line %d: too few fields",
    226  1.22        ad 			    lineno);
    227  1.22        ad 	}
    228  1.22        ad 
    229  1.22        ad 	if (sscanf(*ap++, "%o", &i) != 1) {
    230  1.22        ad 		warnx("config line %d: bad permissions", lineno);
    231  1.22        ad 		return (-1);
    232  1.22        ad 	}
    233  1.22        ad 	log->mode = (mode_t)i;
    234  1.22        ad 
    235  1.26   aymeric 	if (sscanf(*ap++, "%d", &log->numhist) != 1) {
    236  1.22        ad 		warnx("config line %d: bad log count", lineno);
    237  1.22        ad 		return (-1);
    238  1.22        ad 	}
    239  1.22        ad 
    240  1.22        ad 	if (isdigit(**ap))
    241  1.22        ad 		log->maxsize = atoi(*ap);
    242  1.22        ad 	else if (**ap == '*')
    243  1.22        ad 		log->maxsize = (size_t)-1;
    244  1.22        ad 	else {
    245  1.22        ad 		warnx("config line %d: bad log size", lineno);
    246  1.22        ad 		return (-1);
    247  1.22        ad 	}
    248  1.22        ad 	ap++;
    249  1.22        ad 
    250  1.22        ad 	if (isdigit(**ap))
    251  1.22        ad 		log->maxage = atoi(*ap);
    252  1.22        ad 	else if (**ap == '*')
    253  1.22        ad 		log->maxage = -1;
    254  1.22        ad 	else {
    255  1.22        ad 		warnx("config line %d: bad log age", lineno);
    256  1.22        ad 		return (-1);
    257  1.22        ad 	}
    258  1.22        ad 	ap++;
    259  1.22        ad 
    260  1.22        ad 	log->flags = 0;
    261  1.22        ad 	for (q = *ap++; q != NULL && *q != '\0' && !isspace(*q); q++) {
    262  1.22        ad 		switch (tolower(*q)) {
    263  1.22        ad 		case 'b':
    264  1.22        ad 			log->flags |= CE_BINARY;
    265  1.22        ad 			break;
    266  1.22        ad 		case 'c':
    267  1.22        ad 			log->flags |= CE_CREATE;
    268  1.22        ad 			break;
    269  1.22        ad 		case 'n':
    270  1.22        ad 			log->flags |= CE_NOSIGNAL;
    271  1.22        ad 			break;
    272  1.22        ad 		case 'z':
    273  1.22        ad 			log->flags |= CE_COMPACT;
    274  1.22        ad 			break;
    275  1.22        ad 		case '-':
    276  1.22        ad 			break;
    277  1.22        ad 		default:
    278  1.22        ad 			warnx("config line %d: bad flags", lineno);
    279  1.22        ad 			return (-1);
    280  1.22        ad 		}
    281  1.22        ad 	}
    282   1.1       cgd 
    283  1.22        ad 	if (*ap != NULL && **ap == '/')
    284  1.22        ad 		strlcpy(log->pidfile, *ap++, sizeof(log->pidfile));
    285  1.22        ad 	else
    286  1.22        ad 		log->pidfile[0] = '\0';
    287  1.22        ad 
    288  1.22        ad 	if (*ap != NULL && (log->signum = getsig(*ap++)) < 0) {
    289  1.22        ad 		warnx("config line %d: bad signal type", lineno);
    290  1.22        ad 		return (-1);
    291  1.22        ad 	} else
    292  1.22        ad 		log->signum = SIGHUP;
    293  1.22        ad 
    294  1.22        ad 	return (0);
    295   1.1       cgd }
    296   1.1       cgd 
    297  1.22        ad /*
    298  1.22        ad  * Examine a log file.  If the trim conditions are met, call log_trim() to
    299  1.22        ad  * trim the log file.
    300  1.22        ad  */
    301  1.13     lukem void
    302  1.22        ad log_examine(struct conf_entry *ent, int force)
    303  1.22        ad {
    304  1.22        ad 	struct stat sb;
    305  1.22        ad 	size_t size;
    306  1.22        ad 	int modtime;
    307  1.22        ad 	char tmp[MAXPATHLEN];
    308  1.22        ad 	time_t now;
    309  1.22        ad 
    310  1.22        ad 	if (ent->logfile[0] == '\0')
    311  1.22        ad 		return;
    312  1.22        ad 	if (stat(ent->logfile, &sb) < 0) {
    313  1.22        ad 		if (errno == ENOENT && (ent->flags & CE_CREATE) != 0) {
    314  1.22        ad 			PRINFO(("%s: no file, creating\n", ent->logfile));
    315  1.22        ad 			log_create(ent);
    316  1.22        ad 			errno = 0;
    317  1.22        ad 			stat(ent->logfile, &sb);
    318  1.22        ad 		}
    319  1.22        ad 
    320  1.22        ad 		if (errno != 0) {
    321  1.22        ad 			PRINFO(("%s: %s\n", ent->logfile, strerror(errno)));
    322  1.22        ad 			return;
    323  1.22        ad 		}
    324  1.22        ad 	}
    325  1.22        ad 
    326  1.22        ad 	if (verbose) {
    327  1.22        ad 		if ((ent->flags & CE_COMPACT) != 0)
    328  1.22        ad 			PRINFO(("%s <%dZ>: ", ent->logfile, ent->numhist));
    329  1.22        ad 		else
    330  1.22        ad 			PRINFO(("%s <%d>: ", ent->logfile, ent->numhist));
    331  1.22        ad 	}
    332  1.22        ad 
    333  1.22        ad 	size = ((size_t)sb.st_blocks * S_BLKSIZE) >> 10;
    334  1.20        ad 
    335  1.22        ad 	now = time(NULL);
    336  1.22        ad 	strlcpy(tmp, ent->logfile, sizeof(tmp));
    337  1.22        ad 	strlcat(tmp, ".0", sizeof(tmp));
    338  1.22        ad 	if (stat(tmp, &sb) < 0) {
    339  1.22        ad 		strlcat(tmp, ".gz", sizeof(tmp));
    340  1.22        ad 		if (stat(tmp, &sb) < 0)
    341  1.22        ad 			modtime = -1;
    342  1.11   thorpej 		else
    343  1.22        ad 			modtime = (int)(now - sb.st_mtime + 1800) / 3600;
    344  1.17  christos 	} else
    345  1.22        ad 		modtime = (int)(now - sb.st_mtime + 1800) / 3600;
    346  1.11   thorpej 
    347  1.22        ad 	if (verbose) {
    348  1.22        ad 		if (ent->maxsize != (size_t)-1)
    349  1.27     assar 			PRINFO(("size (Kb): %lu [%lu] ",
    350  1.27     assar 				(u_long)size,
    351  1.27     assar 				(u_long)ent->maxsize));
    352  1.22        ad 		if (ent->maxage > 0)
    353  1.22        ad 			PRINFO((" age (hr): %d [%d] ", modtime, ent->maxage));
    354  1.21        ad 	}
    355  1.21        ad 
    356  1.22        ad 	/*
    357  1.22        ad 	 * Note: if maxage is used as a trim condition, we need at least one
    358  1.22        ad 	 * historical log file to determine the `age' of the active log file.
    359  1.22        ad 	 */
    360  1.22        ad 	if ((ent->maxage > 0 && (modtime >= ent->maxage || modtime < 0)) ||
    361  1.22        ad 	    size >= ent->maxsize || force) {
    362  1.22        ad 		PRINFO(("--> trimming log....\n"));
    363  1.22        ad 		log_trim(ent);
    364  1.22        ad 	} else
    365  1.22        ad 		PRINFO(("--> skipping\n"));
    366   1.1       cgd }
    367   1.1       cgd 
    368  1.22        ad /*
    369  1.22        ad  * Trim the specified log file.
    370  1.22        ad  */
    371  1.22        ad void
    372  1.22        ad log_trim(struct conf_entry *log)
    373   1.1       cgd {
    374  1.22        ad 	char file1[MAXPATHLEN], file2[MAXPATHLEN];
    375  1.22        ad 	char zfile1[MAXPATHLEN], zfile2[MAXPATHLEN];
    376  1.22        ad 	int i;
    377  1.22        ad 	struct stat st;
    378  1.22        ad 	pid_t pid;
    379  1.22        ad 
    380  1.22        ad 	/* Remove oldest log */
    381  1.22        ad 	snprintf(file1, sizeof(file1), "%s.%d", log->logfile,
    382  1.22        ad 	    log->numhist - 1);
    383  1.22        ad 	strcpy(zfile1, file1);
    384  1.22        ad 	strlcat(zfile1, ".gz", sizeof(zfile1));
    385  1.22        ad 
    386  1.22        ad 	PRINFO(("rm -f %s\n", file1));
    387  1.22        ad 	unlink(file1);
    388  1.22        ad 	PRINFO(("rm -f %s\n", zfile1));
    389  1.22        ad 	unlink(zfile1);
    390  1.22        ad 
    391  1.22        ad 	/* Move down log files. */
    392  1.22        ad 	for (i = log->numhist - 1; i != 0; i--) {
    393  1.22        ad 		strcpy(file2, file1);
    394  1.22        ad 		snprintf(file1, sizeof(file1), "%s.%d", log->logfile, i - 1);
    395  1.22        ad 		strcpy(zfile1, file1);
    396  1.22        ad 		strcpy(zfile2, file2);
    397  1.22        ad 
    398  1.22        ad 		if (lstat(file1, &st) != 0) {
    399  1.22        ad 			strlcat(zfile1, ".gz", sizeof(zfile1));
    400  1.22        ad 			strlcat(zfile2, ".gz", sizeof(zfile2));
    401  1.22        ad 			if (lstat(zfile1, &st) != 0)
    402  1.22        ad 				continue;
    403  1.22        ad 		}
    404  1.22        ad 
    405  1.22        ad 		PRINFO(("mv %s %s\n", zfile1, zfile2));
    406  1.22        ad 		rename(zfile1, zfile2);
    407  1.22        ad 		PRINFO(("chmod %o %s\n", log->mode, zfile2));
    408  1.22        ad 		chmod(zfile2, log->mode);
    409  1.22        ad 		PRINFO(("chown %d.%d %s\n", log->uid, log->gid, zfile2));
    410  1.22        ad 		chown(zfile2, log->uid, log->gid);
    411  1.22        ad 	}
    412  1.22        ad 
    413  1.22        ad 	if ((log->flags & CE_BINARY) == 0)
    414  1.22        ad 		log_trimmed(log);
    415  1.22        ad 
    416  1.22        ad 	if (log->numhist == 0) {
    417  1.22        ad 		PRINFO(("rm %s\n", log->logfile));
    418  1.22        ad 		unlink(log->logfile);
    419  1.22        ad 	} else {
    420  1.22        ad 		PRINFO(("mv %s to %s\n", log->logfile, file1));
    421  1.22        ad 		rename(log->logfile, file1);
    422  1.22        ad 	}
    423  1.22        ad 
    424  1.22        ad 	PRINFO(("Start new log...\n"));
    425  1.22        ad 	log_create(log);
    426  1.22        ad 
    427  1.22        ad 	if ((log->flags & CE_BINARY) == 0)
    428  1.22        ad 		log_trimmed(log);
    429  1.22        ad 
    430  1.22        ad 	PRINFO(("chmod %o %s\n", log->mode, log->logfile));
    431  1.22        ad 	chmod(log->logfile, log->mode);
    432  1.22        ad 
    433  1.22        ad 	if ((log->flags & CE_NOSIGNAL) == 0) {
    434  1.22        ad 		if (log->pidfile[0] != '\0')
    435  1.22        ad 			pid = readpidfile(log->pidfile);
    436  1.22        ad 		else
    437  1.22        ad 			pid = readpidfile(_PATH_SYSLOGDPID);
    438  1.22        ad 
    439  1.22        ad 		if (pid != (pid_t)-1) {
    440  1.22        ad 			PRINFO(("kill -%s %d\n", sys_signame[log->signum],
    441  1.22        ad 			    pid));
    442  1.22        ad 			if (kill(pid, log->signum))
    443  1.22        ad 				warn("warning - could not signal daemon");
    444  1.22        ad 		}
    445  1.22        ad 	}
    446  1.22        ad 
    447  1.22        ad 	if ((log->flags & CE_COMPACT) != 0) {
    448  1.22        ad 		PRINFO(("gzip %s.0\n", log->logfile));
    449  1.22        ad 
    450  1.22        ad 		if ((pid = fork()) < 0)
    451  1.22        ad 			err(EXIT_FAILURE, "fork");
    452  1.22        ad 		else if (pid == 0) {
    453  1.22        ad 			snprintf(file1, sizeof(file1), "%s.0", log->logfile);
    454  1.22        ad 			execl(_PATH_GZIP, "gzip", "-f", file1, NULL);
    455  1.22        ad 			err(EXIT_FAILURE, _PATH_GZIP);
    456  1.22        ad 		}
    457  1.22        ad 	}
    458   1.1       cgd }
    459   1.1       cgd 
    460  1.22        ad /*
    461  1.22        ad  * Write an entry to the log file recording the fact that it was trimmed.
    462  1.22        ad  */
    463  1.13     lukem void
    464  1.22        ad log_trimmed(struct conf_entry *log)
    465   1.1       cgd {
    466  1.22        ad 	FILE *fd;
    467  1.22        ad 	time_t now;
    468  1.22        ad 	char *daytime;
    469  1.22        ad 
    470  1.22        ad 	if ((fd = fopen(log->logfile, "at")) == NULL)
    471  1.22        ad 		err(EXIT_FAILURE, "%s", log->logfile);
    472  1.22        ad 
    473  1.22        ad 	now = time(NULL);
    474  1.22        ad 	daytime = ctime(&now) + 4;
    475  1.22        ad 	daytime[15] = '\0';
    476  1.22        ad 
    477  1.22        ad 	fprintf(fd, "%s %s newsyslog[%ld]: log file turned over\n", daytime,
    478  1.22        ad 	    hostname, (u_long)getpid());
    479  1.22        ad 	fclose(fd);
    480   1.1       cgd }
    481   1.1       cgd 
    482  1.22        ad /*
    483  1.22        ad  * Create a new log file.
    484  1.22        ad  */
    485  1.22        ad void
    486  1.22        ad log_create(struct conf_entry *ent)
    487   1.1       cgd {
    488  1.22        ad 	int fd;
    489   1.1       cgd 
    490  1.22        ad 	if ((fd = creat(ent->logfile, ent->mode)) < 0)
    491  1.22        ad 		err(EXIT_FAILURE, "%s", ent->logfile);
    492  1.22        ad 	if (fchown(fd, ent->uid, ent->gid) < 0)
    493  1.22        ad 		err(EXIT_FAILURE, "%s", ent->logfile);
    494  1.22        ad 	if (close(fd) < 0)
    495  1.22        ad 		err(EXIT_FAILURE, "%s", ent->logfile);
    496   1.1       cgd }
    497   1.1       cgd 
    498  1.22        ad /*
    499  1.22        ad  * Display program usage information.
    500  1.22        ad  */
    501  1.22        ad void
    502  1.22        ad usage(void)
    503   1.1       cgd {
    504   1.1       cgd 
    505  1.22        ad 	fprintf(stderr, "usage: newsyslog [-Frv] [-f config-file]\n");
    506  1.22        ad 	exit(EXIT_FAILURE);
    507   1.1       cgd }
    508   1.1       cgd 
    509  1.22        ad /*
    510  1.22        ad  * Return non-zero if a string represents a decimal value.
    511  1.22        ad  */
    512  1.22        ad int
    513  1.22        ad isnumber(const char *string)
    514  1.22        ad {
    515   1.1       cgd 
    516  1.22        ad 	while (isdigit(*string))
    517  1.22        ad 		string++;
    518   1.1       cgd 
    519  1.22        ad 	return (*string == '\0');
    520  1.21        ad }
    521  1.21        ad 
    522  1.22        ad /*
    523  1.22        ad  * Given a signal name, attempt to find the corresponding signal number.
    524  1.22        ad  */
    525  1.21        ad int
    526  1.22        ad getsig(const char *sig)
    527  1.21        ad {
    528  1.22        ad 	char *p;
    529  1.21        ad 	int n;
    530  1.21        ad 
    531  1.21        ad 	if (isnumber(sig)) {
    532  1.22        ad 		n = (int)strtol(sig, &p, 0);
    533  1.22        ad 		if (p != '\0' || (unsigned)n >= NSIG)
    534  1.21        ad 			return (-1);
    535  1.21        ad 		return (n);
    536  1.21        ad 	}
    537  1.21        ad 
    538  1.22        ad 	if (strncasecmp(sig, "sig", 3) == 0)
    539  1.21        ad 		sig += 3;
    540  1.22        ad 	for (n = 1; n < NSIG; n++)
    541  1.22        ad 		if (strcasecmp(sys_signame[n], sig) == 0)
    542  1.21        ad 			return (n);
    543  1.22        ad 	return (-1);
    544  1.22        ad }
    545  1.22        ad 
    546  1.22        ad /*
    547  1.22        ad  * Given a path to a PID file, return the PID contained within.
    548  1.22        ad  */
    549  1.22        ad pid_t
    550  1.22        ad readpidfile(const char *file)
    551  1.22        ad {
    552  1.22        ad 	FILE *fd;
    553  1.22        ad 	char line[BUFSIZ];
    554  1.22        ad 	pid_t pid;
    555  1.22        ad 
    556  1.25        ad 	if ((fd = fopen(file, "rt")) == NULL) {
    557  1.25        ad 		warn("%s", file);
    558  1.22        ad 		return (-1);
    559  1.21        ad 	}
    560  1.25        ad 
    561  1.22        ad 	if (fgets(line, sizeof(line) - 1, fd) != NULL) {
    562  1.22        ad 		line[sizeof(line) - 1] = '\0';
    563  1.22        ad 		pid = (pid_t)strtol(line, NULL, 0);
    564  1.22        ad 	}
    565  1.25        ad 
    566  1.22        ad 	fclose(fd);
    567  1.22        ad 	return (pid);
    568  1.22        ad }
    569  1.22        ad 
    570  1.22        ad /*
    571  1.22        ad  * Parse a user:group specification.
    572  1.22        ad  *
    573  1.22        ad  * XXX This is over the top for newsyslog(1).  It should be moved to libutil.
    574  1.22        ad  */
    575  1.22        ad int
    576  1.22        ad parseuserspec(const char *name, struct passwd **pw, struct group **gr)
    577  1.22        ad {
    578  1.22        ad 	char buf[MAXLOGNAME * 2 + 2], *group;
    579  1.22        ad 
    580  1.22        ad 	strlcpy(buf, name, sizeof(buf));
    581  1.22        ad 	*gr = NULL;
    582  1.22        ad 
    583  1.22        ad 	/*
    584  1.22        ad 	 * Before attempting to use '.' as a separator, see if the whole
    585  1.22        ad 	 * string resolves as a user name or UID.
    586  1.22        ad 	 */
    587  1.22        ad 	if ((*pw = getpwnam(buf)) != NULL) {
    588  1.22        ad 		*gr = getgrgid((*pw)->pw_gid);
    589  1.22        ad 		return (0);
    590  1.22        ad 	}
    591  1.22        ad 
    592  1.22        ad 	/* Split the user and group name. */
    593  1.22        ad 	if ((group = strchr(buf, ':')) != NULL ||
    594  1.22        ad 	    (group = strchr(buf, '.')) != NULL)
    595  1.22        ad 		*group++ = '\0';
    596  1.22        ad 
    597  1.22        ad 	if (isnumber(buf))
    598  1.22        ad 		*pw = getpwuid((uid_t)atoi(buf));
    599  1.22        ad 	else
    600  1.22        ad 		*pw = getpwnam(buf);
    601  1.22        ad 
    602  1.22        ad 	/*
    603  1.22        ad 	 * Find the group.  If a group wasn't specified, use the user's
    604  1.22        ad 	 * `natural' group.  We get to this point even if no user was found.
    605  1.22        ad 	 * This is to allow the caller to get a better idea of what went
    606  1.22        ad 	 * wrong, if anything.
    607  1.22        ad 	 */
    608  1.22        ad 	if (group == NULL || *group == '\0') {
    609  1.22        ad 		if (*pw == NULL)
    610  1.22        ad 			return (-1);
    611  1.22        ad 		*gr = getgrgid((*pw)->pw_gid);
    612  1.22        ad 	} else if (isnumber(group))
    613  1.22        ad 		*gr = getgrgid((gid_t)atoi(group));
    614  1.22        ad 	else
    615  1.22        ad 		*gr = getgrnam(group);
    616  1.22        ad 
    617  1.22        ad 	return (*gr != NULL ? 0 : -1);
    618   1.1       cgd }
    619