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