Home | History | Annotate | Line # | Download | only in newsyslog
newsyslog.c revision 1.22
      1  1.22        ad /*	$NetBSD: newsyslog.c,v 1.22 2000/07/07 10:52:41 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.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.22        ad __RCSID("$NetBSD: newsyslog.c,v 1.22 2000/07/07 10:52:41 ad 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.22        ad int     verbose = 0;			/* Be verbose */
    106  1.22        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.22        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.22        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.22        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.22        ad 	if (sscanf(*ap++, "%d", &log->numhist) != 0) {
    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.22        ad 			PRINFO(("size (Kb): %d [%d] ", size, ent->maxsize));
    350  1.22        ad 		if (ent->maxage > 0)
    351  1.22        ad 			PRINFO((" age (hr): %d [%d] ", modtime, ent->maxage));
    352  1.21        ad 	}
    353  1.21        ad 
    354  1.22        ad 	/*
    355  1.22        ad 	 * Note: if maxage is used as a trim condition, we need at least one
    356  1.22        ad 	 * historical log file to determine the `age' of the active log file.
    357  1.22        ad 	 */
    358  1.22        ad 	if ((ent->maxage > 0 && (modtime >= ent->maxage || modtime < 0)) ||
    359  1.22        ad 	    size >= ent->maxsize || force) {
    360  1.22        ad 		PRINFO(("--> trimming log....\n"));
    361  1.22        ad 		log_trim(ent);
    362  1.22        ad 	} else
    363  1.22        ad 		PRINFO(("--> skipping\n"));
    364   1.1       cgd }
    365   1.1       cgd 
    366  1.22        ad /*
    367  1.22        ad  * Trim the specified log file.
    368  1.22        ad  */
    369  1.22        ad void
    370  1.22        ad log_trim(struct conf_entry *log)
    371   1.1       cgd {
    372  1.22        ad 	char file1[MAXPATHLEN], file2[MAXPATHLEN];
    373  1.22        ad 	char zfile1[MAXPATHLEN], zfile2[MAXPATHLEN];
    374  1.22        ad 	int i;
    375  1.22        ad 	struct stat st;
    376  1.22        ad 	pid_t pid;
    377  1.22        ad 
    378  1.22        ad 	/* Remove oldest log */
    379  1.22        ad 	snprintf(file1, sizeof(file1), "%s.%d", log->logfile,
    380  1.22        ad 	    log->numhist - 1);
    381  1.22        ad 	strcpy(zfile1, file1);
    382  1.22        ad 	strlcat(zfile1, ".gz", sizeof(zfile1));
    383  1.22        ad 
    384  1.22        ad 	PRINFO(("rm -f %s\n", file1));
    385  1.22        ad 	unlink(file1);
    386  1.22        ad 	PRINFO(("rm -f %s\n", zfile1));
    387  1.22        ad 	unlink(zfile1);
    388  1.22        ad 
    389  1.22        ad 	/* Move down log files. */
    390  1.22        ad 	for (i = log->numhist - 1; i != 0; i--) {
    391  1.22        ad 		strcpy(file2, file1);
    392  1.22        ad 		snprintf(file1, sizeof(file1), "%s.%d", log->logfile, i - 1);
    393  1.22        ad 		strcpy(zfile1, file1);
    394  1.22        ad 		strcpy(zfile2, file2);
    395  1.22        ad 
    396  1.22        ad 		if (lstat(file1, &st) != 0) {
    397  1.22        ad 			strlcat(zfile1, ".gz", sizeof(zfile1));
    398  1.22        ad 			strlcat(zfile2, ".gz", sizeof(zfile2));
    399  1.22        ad 			if (lstat(zfile1, &st) != 0)
    400  1.22        ad 				continue;
    401  1.22        ad 		}
    402  1.22        ad 
    403  1.22        ad 		PRINFO(("mv %s %s\n", zfile1, zfile2));
    404  1.22        ad 		rename(zfile1, zfile2);
    405  1.22        ad 		PRINFO(("chmod %o %s\n", log->mode, zfile2));
    406  1.22        ad 		chmod(zfile2, log->mode);
    407  1.22        ad 		PRINFO(("chown %d.%d %s\n", log->uid, log->gid, zfile2));
    408  1.22        ad 		chown(zfile2, log->uid, log->gid);
    409  1.22        ad 	}
    410  1.22        ad 
    411  1.22        ad 	if ((log->flags & CE_BINARY) == 0)
    412  1.22        ad 		log_trimmed(log);
    413  1.22        ad 
    414  1.22        ad 	if (log->numhist == 0) {
    415  1.22        ad 		PRINFO(("rm %s\n", log->logfile));
    416  1.22        ad 		unlink(log->logfile);
    417  1.22        ad 	} else {
    418  1.22        ad 		PRINFO(("mv %s to %s\n", log->logfile, file1));
    419  1.22        ad 		rename(log->logfile, file1);
    420  1.22        ad 	}
    421  1.22        ad 
    422  1.22        ad 	PRINFO(("Start new log...\n"));
    423  1.22        ad 	log_create(log);
    424  1.22        ad 
    425  1.22        ad 	if ((log->flags & CE_BINARY) == 0)
    426  1.22        ad 		log_trimmed(log);
    427  1.22        ad 
    428  1.22        ad 	PRINFO(("chmod %o %s\n", log->mode, log->logfile));
    429  1.22        ad 	chmod(log->logfile, log->mode);
    430  1.22        ad 
    431  1.22        ad 	if ((log->flags & CE_NOSIGNAL) == 0) {
    432  1.22        ad 		if (log->pidfile[0] != '\0')
    433  1.22        ad 			pid = readpidfile(log->pidfile);
    434  1.22        ad 		else
    435  1.22        ad 			pid = readpidfile(_PATH_SYSLOGDPID);
    436  1.22        ad 
    437  1.22        ad 		if (pid != (pid_t)-1) {
    438  1.22        ad 			PRINFO(("kill -%s %d\n", sys_signame[log->signum],
    439  1.22        ad 			    pid));
    440  1.22        ad 			if (kill(pid, log->signum))
    441  1.22        ad 				warn("warning - could not signal daemon");
    442  1.22        ad 		}
    443  1.22        ad 	}
    444  1.22        ad 
    445  1.22        ad 	if ((log->flags & CE_COMPACT) != 0) {
    446  1.22        ad 		PRINFO(("gzip %s.0\n", log->logfile));
    447  1.22        ad 
    448  1.22        ad 		if ((pid = fork()) < 0)
    449  1.22        ad 			err(EXIT_FAILURE, "fork");
    450  1.22        ad 		else if (pid == 0) {
    451  1.22        ad 			snprintf(file1, sizeof(file1), "%s.0", log->logfile);
    452  1.22        ad 			execl(_PATH_GZIP, "gzip", "-f", file1, NULL);
    453  1.22        ad 			err(EXIT_FAILURE, _PATH_GZIP);
    454  1.22        ad 		}
    455  1.22        ad 	}
    456   1.1       cgd }
    457   1.1       cgd 
    458  1.22        ad /*
    459  1.22        ad  * Write an entry to the log file recording the fact that it was trimmed.
    460  1.22        ad  */
    461  1.13     lukem void
    462  1.22        ad log_trimmed(struct conf_entry *log)
    463   1.1       cgd {
    464  1.22        ad 	FILE *fd;
    465  1.22        ad 	time_t now;
    466  1.22        ad 	char *daytime;
    467  1.22        ad 
    468  1.22        ad 	if ((fd = fopen(log->logfile, "at")) == NULL)
    469  1.22        ad 		err(EXIT_FAILURE, "%s", log->logfile);
    470  1.22        ad 
    471  1.22        ad 	now = time(NULL);
    472  1.22        ad 	daytime = ctime(&now) + 4;
    473  1.22        ad 	daytime[15] = '\0';
    474  1.22        ad 
    475  1.22        ad 	fprintf(fd, "%s %s newsyslog[%ld]: log file turned over\n", daytime,
    476  1.22        ad 	    hostname, (u_long)getpid());
    477  1.22        ad 	fclose(fd);
    478   1.1       cgd }
    479   1.1       cgd 
    480  1.22        ad /*
    481  1.22        ad  * Create a new log file.
    482  1.22        ad  */
    483  1.22        ad void
    484  1.22        ad log_create(struct conf_entry *ent)
    485   1.1       cgd {
    486  1.22        ad 	int fd;
    487   1.1       cgd 
    488  1.22        ad 	if ((fd = creat(ent->logfile, ent->mode)) < 0)
    489  1.22        ad 		err(EXIT_FAILURE, "%s", ent->logfile);
    490  1.22        ad 	if (fchown(fd, ent->uid, ent->gid) < 0)
    491  1.22        ad 		err(EXIT_FAILURE, "%s", ent->logfile);
    492  1.22        ad 	if (close(fd) < 0)
    493  1.22        ad 		err(EXIT_FAILURE, "%s", ent->logfile);
    494   1.1       cgd }
    495   1.1       cgd 
    496  1.22        ad /*
    497  1.22        ad  * Display program usage information.
    498  1.22        ad  */
    499  1.22        ad void
    500  1.22        ad usage(void)
    501   1.1       cgd {
    502   1.1       cgd 
    503  1.22        ad 	fprintf(stderr, "usage: newsyslog [-Frv] [-f config-file]\n");
    504  1.22        ad 	exit(EXIT_FAILURE);
    505   1.1       cgd }
    506   1.1       cgd 
    507  1.22        ad /*
    508  1.22        ad  * Return non-zero if a string represents a decimal value.
    509  1.22        ad  */
    510  1.22        ad int
    511  1.22        ad isnumber(const char *string)
    512  1.22        ad {
    513   1.1       cgd 
    514  1.22        ad 	while (isdigit(*string))
    515  1.22        ad 		string++;
    516   1.1       cgd 
    517  1.22        ad 	return (*string == '\0');
    518  1.21        ad }
    519  1.21        ad 
    520  1.22        ad /*
    521  1.22        ad  * Given a signal name, attempt to find the corresponding signal number.
    522  1.22        ad  */
    523  1.21        ad int
    524  1.22        ad getsig(const char *sig)
    525  1.21        ad {
    526  1.22        ad 	char *p;
    527  1.21        ad 	int n;
    528  1.21        ad 
    529  1.21        ad 	if (isnumber(sig)) {
    530  1.22        ad 		n = (int)strtol(sig, &p, 0);
    531  1.22        ad 		if (p != '\0' || (unsigned)n >= NSIG)
    532  1.21        ad 			return (-1);
    533  1.21        ad 		return (n);
    534  1.21        ad 	}
    535  1.21        ad 
    536  1.22        ad 	if (strncasecmp(sig, "sig", 3) == 0)
    537  1.21        ad 		sig += 3;
    538  1.22        ad 	for (n = 1; n < NSIG; n++)
    539  1.22        ad 		if (strcasecmp(sys_signame[n], sig) == 0)
    540  1.21        ad 			return (n);
    541  1.22        ad 	return (-1);
    542  1.22        ad }
    543  1.22        ad 
    544  1.22        ad /*
    545  1.22        ad  * Given a path to a PID file, return the PID contained within.
    546  1.22        ad  */
    547  1.22        ad pid_t
    548  1.22        ad readpidfile(const char *file)
    549  1.22        ad {
    550  1.22        ad 	FILE *fd;
    551  1.22        ad 	char line[BUFSIZ];
    552  1.22        ad 	pid_t pid;
    553  1.22        ad 
    554  1.22        ad 	if ((fd = fopen(file, "rt")) == NULL) {
    555  1.22        ad 		warn("%s", file);
    556  1.22        ad 		return (-1);
    557  1.21        ad 	}
    558  1.22        ad 
    559  1.22        ad 	if (fgets(line, sizeof(line) - 1, fd) != NULL) {
    560  1.22        ad 		line[sizeof(line) - 1] = '\0';
    561  1.22        ad 		pid = (pid_t)strtol(line, NULL, 0);
    562  1.22        ad 	}
    563  1.22        ad 
    564  1.22        ad 	fclose(fd);
    565  1.22        ad 	return (pid);
    566  1.22        ad }
    567  1.22        ad 
    568  1.22        ad /*
    569  1.22        ad  * Parse a user:group specification.
    570  1.22        ad  *
    571  1.22        ad  * XXX This is over the top for newsyslog(1).  It should be moved to libutil.
    572  1.22        ad  */
    573  1.22        ad int
    574  1.22        ad parseuserspec(const char *name, struct passwd **pw, struct group **gr)
    575  1.22        ad {
    576  1.22        ad 	char buf[MAXLOGNAME * 2 + 2], *group;
    577  1.22        ad 
    578  1.22        ad 	strlcpy(buf, name, sizeof(buf));
    579  1.22        ad 	*gr = NULL;
    580  1.22        ad 
    581  1.22        ad 	/*
    582  1.22        ad 	 * Before attempting to use '.' as a separator, see if the whole
    583  1.22        ad 	 * string resolves as a user name or UID.
    584  1.22        ad 	 */
    585  1.22        ad 	if ((*pw = getpwnam(buf)) != NULL) {
    586  1.22        ad 		*gr = getgrgid((*pw)->pw_gid);
    587  1.22        ad 		return (0);
    588  1.22        ad 	}
    589  1.22        ad 
    590  1.22        ad 	/* Split the user and group name. */
    591  1.22        ad 	if ((group = strchr(buf, ':')) != NULL ||
    592  1.22        ad 	    (group = strchr(buf, '.')) != NULL)
    593  1.22        ad 		*group++ = '\0';
    594  1.22        ad 
    595  1.22        ad 	if (isnumber(buf))
    596  1.22        ad 		*pw = getpwuid((uid_t)atoi(buf));
    597  1.22        ad 	else
    598  1.22        ad 		*pw = getpwnam(buf);
    599  1.22        ad 
    600  1.22        ad 	/*
    601  1.22        ad 	 * Find the group.  If a group wasn't specified, use the user's
    602  1.22        ad 	 * `natural' group.  We get to this point even if no user was found.
    603  1.22        ad 	 * This is to allow the caller to get a better idea of what went
    604  1.22        ad 	 * wrong, if anything.
    605  1.22        ad 	 */
    606  1.22        ad 	if (group == NULL || *group == '\0') {
    607  1.22        ad 		if (*pw == NULL)
    608  1.22        ad 			return (-1);
    609  1.22        ad 		*gr = getgrgid((*pw)->pw_gid);
    610  1.22        ad 	} else if (isnumber(group))
    611  1.22        ad 		*gr = getgrgid((gid_t)atoi(group));
    612  1.22        ad 	else
    613  1.22        ad 		*gr = getgrnam(group);
    614  1.22        ad 
    615  1.22        ad 	return (*gr != NULL ? 0 : -1);
    616   1.1       cgd }
    617