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