| GCC Code Coverage Report | |||||||||||||||||||||
        
  | 
    |||||||||||||||||||||
| Line | Branch | Exec | Source | 
1  | 
    /* $OpenBSD: newsyslog.c,v 1.108 2017/07/24 12:57:01 jca Exp $ */  | 
    ||
2  | 
    |||
3  | 
    /*  | 
    ||
4  | 
    * Copyright (c) 1999, 2002, 2003 Todd C. Miller <Todd.Miller@courtesan.com>  | 
    ||
5  | 
    *  | 
    ||
6  | 
    * Permission to use, copy, modify, and distribute this software for any  | 
    ||
7  | 
    * purpose with or without fee is hereby granted, provided that the above  | 
    ||
8  | 
    * copyright notice and this permission notice appear in all copies.  | 
    ||
9  | 
    *  | 
    ||
10  | 
    * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES  | 
    ||
11  | 
    * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF  | 
    ||
12  | 
    * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR  | 
    ||
13  | 
    * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES  | 
    ||
14  | 
    * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN  | 
    ||
15  | 
    * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF  | 
    ||
16  | 
    * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.  | 
    ||
17  | 
    *  | 
    ||
18  | 
    * Sponsored in part by the Defense Advanced Research Projects  | 
    ||
19  | 
    * Agency (DARPA) and Air Force Research Laboratory, Air Force  | 
    ||
20  | 
    * Materiel Command, USAF, under agreement number F39502-99-1-0512.  | 
    ||
21  | 
    */  | 
    ||
22  | 
    |||
23  | 
    /*  | 
    ||
24  | 
    * Copyright (c) 1997, Jason Downs. All rights reserved.  | 
    ||
25  | 
    *  | 
    ||
26  | 
    * Redistribution and use in source and binary forms, with or without  | 
    ||
27  | 
    * modification, are permitted provided that the following conditions  | 
    ||
28  | 
    * are met:  | 
    ||
29  | 
    * 1. Redistributions of source code must retain the above copyright  | 
    ||
30  | 
    * notice, this list of conditions and the following disclaimer.  | 
    ||
31  | 
    * 2. Redistributions in binary form must reproduce the above copyright  | 
    ||
32  | 
    * notice, this list of conditions and the following disclaimer in the  | 
    ||
33  | 
    * documentation and/or other materials provided with the distribution.  | 
    ||
34  | 
    *  | 
    ||
35  | 
    * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS  | 
    ||
36  | 
    * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED  | 
    ||
37  | 
    * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE  | 
    ||
38  | 
    * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,  | 
    ||
39  | 
    * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES  | 
    ||
40  | 
    * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR  | 
    ||
41  | 
    * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER  | 
    ||
42  | 
    * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT  | 
    ||
43  | 
    * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY  | 
    ||
44  | 
    * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF  | 
    ||
45  | 
    * SUCH DAMAGE.  | 
    ||
46  | 
    */  | 
    ||
47  | 
    |||
48  | 
    /*  | 
    ||
49  | 
    * This file contains changes from the Open Software Foundation.  | 
    ||
50  | 
    */  | 
    ||
51  | 
    |||
52  | 
    /*  | 
    ||
53  | 
    * Copyright 1988, 1989 by the Massachusetts Institute of Technology  | 
    ||
54  | 
    *  | 
    ||
55  | 
    * Permission to use, copy, modify, and distribute this software  | 
    ||
56  | 
    * and its documentation for any purpose and without fee is  | 
    ||
57  | 
    * hereby granted, provided that the above copyright notice  | 
    ||
58  | 
    * appear in all copies and that both that copyright notice and  | 
    ||
59  | 
    * this permission notice appear in supporting documentation,  | 
    ||
60  | 
    * and that the names of M.I.T. and the M.I.T. S.I.P.B. not be  | 
    ||
61  | 
    * used in advertising or publicity pertaining to distribution  | 
    ||
62  | 
    * of the software without specific, written prior permission.  | 
    ||
63  | 
    * M.I.T. and the M.I.T. S.I.P.B. make no representations about  | 
    ||
64  | 
    * the suitability of this software for any purpose. It is  | 
    ||
65  | 
    * provided "as is" without express or implied warranty.  | 
    ||
66  | 
    */  | 
    ||
67  | 
    |||
68  | 
    /*  | 
    ||
69  | 
    * newsyslog - roll over selected logs at the appropriate time,  | 
    ||
70  | 
    * keeping the specified number of backup files around.  | 
    ||
71  | 
    *  | 
    ||
72  | 
    */  | 
    ||
73  | 
    |||
74  | 
    #define CONF "/etc/newsyslog.conf"  | 
    ||
75  | 
    #define PIDFILE "/var/run/syslog.pid"  | 
    ||
76  | 
    #define COMPRESS "/usr/bin/gzip"  | 
    ||
77  | 
    #define COMPRESS_POSTFIX ".gz"  | 
    ||
78  | 
    #define STATS_DIR "/var/run"  | 
    ||
79  | 
    #define SENDMAIL "/usr/sbin/sendmail"  | 
    ||
80  | 
    |||
81  | 
    #include <sys/param.h> /* DEV_BSIZE */  | 
    ||
82  | 
    #include <sys/queue.h>  | 
    ||
83  | 
    #include <sys/stat.h>  | 
    ||
84  | 
    #include <sys/time.h>  | 
    ||
85  | 
    #include <sys/wait.h>  | 
    ||
86  | 
    |||
87  | 
    #include <ctype.h>  | 
    ||
88  | 
    #include <err.h>  | 
    ||
89  | 
    #include <errno.h>  | 
    ||
90  | 
    #include <fcntl.h>  | 
    ||
91  | 
    #include <grp.h>  | 
    ||
92  | 
    #include <limits.h>  | 
    ||
93  | 
    #include <pwd.h>  | 
    ||
94  | 
    #include <signal.h>  | 
    ||
95  | 
    #include <stdio.h>  | 
    ||
96  | 
    #include <stdlib.h>  | 
    ||
97  | 
    #include <string.h>  | 
    ||
98  | 
    #include <time.h>  | 
    ||
99  | 
    #include <unistd.h>  | 
    ||
100  | 
    |||
101  | 
    #define CE_ROTATED 0x01 /* Log file has been rotated */  | 
    ||
102  | 
    #define CE_COMPACT 0x02 /* Compact the archived log files */  | 
    ||
103  | 
    #define CE_BINARY 0x04 /* Logfile is in binary, don't add */  | 
    ||
104  | 
    /* status messages */  | 
    ||
105  | 
    #define CE_MONITOR 0x08 /* Monitor for changes */  | 
    ||
106  | 
    #define CE_FOLLOW 0x10 /* Follow symbolic links */  | 
    ||
107  | 
    #define CE_TRIMAT 0x20 /* Trim at a specific time */  | 
    ||
108  | 
    |||
109  | 
    #define MIN_PID 2 /* Don't touch pids lower than this */  | 
    ||
110  | 
    #define MIN_SIZE 256 /* Don't rotate if smaller (in bytes) */  | 
    ||
111  | 
    |||
112  | 
    #define	DPRINTF(x)	do { if (verbose) printf x ; } while (0) | 
    ||
113  | 
    |||
114  | 
    struct conf_entry { | 
    ||
115  | 
    char *log; /* Name of the log */  | 
    ||
116  | 
    char *logbase; /* Basename of the log */  | 
    ||
117  | 
    char *backdir; /* Directory in which to store backups */  | 
    ||
118  | 
    uid_t uid; /* Owner of log */  | 
    ||
119  | 
    gid_t gid; /* Group of log */  | 
    ||
120  | 
    int numlogs; /* Number of logs to keep */  | 
    ||
121  | 
    off_t size; /* Size cutoff to trigger trimming the log */  | 
    ||
122  | 
    int hours; /* Hours between log trimming */  | 
    ||
123  | 
    time_t trim_at; /* Specific time at which to do trimming */  | 
    ||
124  | 
    mode_t permissions; /* File permissions on the log */  | 
    ||
125  | 
    int signal; /* Signal to send (defaults to SIGHUP) */  | 
    ||
126  | 
    int flags; /* Flags (CE_COMPACT & CE_BINARY) */  | 
    ||
127  | 
    char *whom; /* Whom to notify if logfile changes */  | 
    ||
128  | 
    char *pidfile; /* Path to file containing pid to signal */  | 
    ||
129  | 
    char *runcmd; /* Command to run instead of sending a signal */  | 
    ||
130  | 
    TAILQ_ENTRY(conf_entry) next;  | 
    ||
131  | 
    };  | 
    ||
132  | 
    TAILQ_HEAD(entrylist, conf_entry);  | 
    ||
133  | 
    |||
134  | 
    struct pidinfo { | 
    ||
135  | 
    char *file;  | 
    ||
136  | 
    int signal;  | 
    ||
137  | 
    };  | 
    ||
138  | 
    |||
139  | 
    int verbose = 0; /* Print out what's going on */  | 
    ||
140  | 
    int needroot = 1; /* Root privs are necessary */  | 
    ||
141  | 
    int noaction = 0; /* Don't do anything, just show it */  | 
    ||
142  | 
    int monitormode = 0; /* Don't do monitoring by default */  | 
    ||
143  | 
    int force = 0; /* Force the logs to be rotated */  | 
    ||
144  | 
    char *conf = CONF; /* Configuration file to use */  | 
    ||
145  | 
    time_t timenow;  | 
    ||
146  | 
    char hostname[HOST_NAME_MAX+1]; /* Hostname */  | 
    ||
147  | 
    char daytime[33]; /* timenow in human readable form */  | 
    ||
148  | 
    char *arcdir; /* Dir to put archives in (if it exists) */  | 
    ||
149  | 
    |||
150  | 
    char *lstat_log(char *, size_t, int);  | 
    ||
151  | 
    char *missing_field(char *, char *, int);  | 
    ||
152  | 
    char *sob(char *);  | 
    ||
153  | 
    char *son(char *);  | 
    ||
154  | 
    int age_old_log(struct conf_entry *);  | 
    ||
155  | 
    int domonitor(struct conf_entry *);  | 
    ||
156  | 
    int isnumberstr(char *);  | 
    ||
157  | 
    int log_trim(char *);  | 
    ||
158  | 
    int movefile(char *, char *, uid_t, gid_t, mode_t);  | 
    ||
159  | 
    int stat_suffix(char *, size_t, char *, struct stat *,  | 
    ||
160  | 
    int (*)(const char *, struct stat *));  | 
    ||
161  | 
    off_t sizefile(struct stat *);  | 
    ||
162  | 
    int parse_file(struct entrylist *, int *);  | 
    ||
163  | 
    time_t parse8601(char *);  | 
    ||
164  | 
    time_t parseDWM(char *);  | 
    ||
165  | 
    void child_killer(int);  | 
    ||
166  | 
    void compress_log(struct conf_entry *);  | 
    ||
167  | 
    void do_entry(struct conf_entry *);  | 
    ||
168  | 
    void dotrim(struct conf_entry *);  | 
    ||
169  | 
    void rotate(struct conf_entry *, const char *);  | 
    ||
170  | 
    void parse_args(int, char **);  | 
    ||
171  | 
    void run_command(char *);  | 
    ||
172  | 
    void send_signal(char *, int);  | 
    ||
173  | 
    void usage(void);  | 
    ||
174  | 
    |||
175  | 
    int  | 
    ||
176  | 
    main(int argc, char **argv)  | 
    ||
177  | 
    { | 
    ||
178  | 
    48  | 
    struct entrylist config, runlist;  | 
    |
179  | 
    struct conf_entry *p, *q, *tmp;  | 
    ||
180  | 
    struct pidinfo *pidlist, *pl;  | 
    ||
181  | 
    24  | 
    int status, listlen, ret;  | 
    |
182  | 
    char **av;  | 
    ||
183  | 
    |||
184  | 
    24  | 
    parse_args(argc, argv);  | 
    |
185  | 
    24  | 
    argc -= optind;  | 
    |
186  | 
    24  | 
    argv += optind;  | 
    |
187  | 
    |||
188  | 
    ✓✗✗✓ ✗✗  | 
    48  | 
    if (needroot && getuid() && geteuid())  | 
    
189  | 
    errx(1, "You must be root.");  | 
    ||
190  | 
    |||
191  | 
    24  | 
    TAILQ_INIT(&config);  | 
    |
192  | 
    24  | 
    TAILQ_INIT(&runlist);  | 
    |
193  | 
    |||
194  | 
    24  | 
    ret = parse_file(&config, &listlen);  | 
    |
195  | 
    ✓✗ | 24  | 
    if (argc == 0)  | 
    
196  | 
    ✓✗ | 48  | 
    TAILQ_CONCAT(&runlist, &config, next);  | 
    
197  | 
    	else { | 
    ||
198  | 
    /* Only rotate specified files. */  | 
    ||
199  | 
    listlen = 0;  | 
    ||
200  | 
    		for (av = argv; *av; av++) { | 
    ||
201  | 
    TAILQ_FOREACH_SAFE(q, &config, next, tmp)  | 
    ||
202  | 
    				if (strcmp(*av, q->log) == 0) { | 
    ||
203  | 
    TAILQ_REMOVE(&config, q, next);  | 
    ||
204  | 
    TAILQ_INSERT_TAIL(&runlist, q, next);  | 
    ||
205  | 
    listlen++;  | 
    ||
206  | 
    break;  | 
    ||
207  | 
    }  | 
    ||
208  | 
    if (q == NULL)  | 
    ||
209  | 
    				warnx("%s: %s not found", conf, *av); | 
    ||
210  | 
    }  | 
    ||
211  | 
    if (TAILQ_EMPTY(&runlist))  | 
    ||
212  | 
    errx(1, "%s: no specified log files", conf);  | 
    ||
213  | 
    }  | 
    ||
214  | 
    |||
215  | 
    24  | 
    pidlist = calloc(listlen + 1, sizeof(struct pidinfo));  | 
    |
216  | 
    ✗✓ | 24  | 
    if (pidlist == NULL)  | 
    
217  | 
    err(1, NULL);  | 
    ||
218  | 
    |||
219  | 
    24  | 
    signal(SIGCHLD, child_killer);  | 
    |
220  | 
    |||
221  | 
    /* Step 1, rotate all log files */  | 
    ||
222  | 
    ✓✓ | 624  | 
    TAILQ_FOREACH(q, &runlist, next)  | 
    
223  | 
    288  | 
    do_entry(q);  | 
    |
224  | 
    |||
225  | 
    /* Step 2, make a list of unique pid files */  | 
    ||
226  | 
    pl = pidlist;  | 
    ||
227  | 
    ✓✓ | 624  | 
    	TAILQ_FOREACH(q, &runlist, next) { | 
    
228  | 
    ✓✓ | 288  | 
    		if (q->flags & CE_ROTATED) { | 
    
229  | 
    struct pidinfo *pltmp;  | 
    ||
230  | 
    |||
231  | 
    ✗✓ | 2  | 
    			for (pltmp = pidlist; pltmp < pl; pltmp++) { | 
    
232  | 
    if ((q->pidfile && pltmp->file &&  | 
    ||
233  | 
    strcmp(pltmp->file, q->pidfile) == 0 &&  | 
    ||
234  | 
    pltmp->signal == q->signal) ||  | 
    ||
235  | 
    (q->runcmd && pltmp->file &&  | 
    ||
236  | 
    strcmp(q->runcmd, pltmp->file) == 0))  | 
    ||
237  | 
    break;  | 
    ||
238  | 
    }  | 
    ||
239  | 
    ✓✗ | 1  | 
    			if (pltmp == pl) {	/* unique entry */ | 
    
240  | 
    ✗✓ | 1  | 
    				if (q->runcmd) { | 
    
241  | 
    pl->file = q->runcmd;  | 
    ||
242  | 
    pl->signal = -1;  | 
    ||
243  | 
    				} else { | 
    ||
244  | 
    1  | 
    pl->file = q->pidfile;  | 
    |
245  | 
    1  | 
    pl->signal = q->signal;  | 
    |
246  | 
    }  | 
    ||
247  | 
    1  | 
    pl++;  | 
    |
248  | 
    1  | 
    }  | 
    |
249  | 
    1  | 
    }  | 
    |
250  | 
    }  | 
    ||
251  | 
    |||
252  | 
    /* Step 3, send a signal or run a command */  | 
    ||
253  | 
    ✓✓ | 50  | 
    	for (pl--; pl >= pidlist; pl--) { | 
    
254  | 
    ✓✗ | 1  | 
    		if (pl->file != NULL) { | 
    
255  | 
    ✗✓ | 1  | 
    if (pl->signal == -1)  | 
    
256  | 
    run_command(pl->file);  | 
    ||
257  | 
    else  | 
    ||
258  | 
    1  | 
    send_signal(pl->file, pl->signal);  | 
    |
259  | 
    }  | 
    ||
260  | 
    }  | 
    ||
261  | 
    ✓✗ | 24  | 
    if (!noaction)  | 
    
262  | 
    24  | 
    sleep(5);  | 
    |
263  | 
    |||
264  | 
    /* Step 4, compress the log.0 file if configured to do so and free */  | 
    ||
265  | 
    ✓✓ | 624  | 
    	TAILQ_FOREACH(p, &runlist, next) { | 
    
266  | 
    ✓✓✓✓ ✓✗  | 
    553  | 
    if ((p->flags & CE_COMPACT) && (p->flags & CE_ROTATED) &&  | 
    
267  | 
    1  | 
    p->numlogs > 0)  | 
    |
268  | 
    1  | 
    compress_log(p);  | 
    |
269  | 
    }  | 
    ||
270  | 
    |||
271  | 
    /* Wait for children to finish, then exit */  | 
    ||
272  | 
    ✗✓ | 24  | 
    while (waitpid(-1, &status, 0) != -1)  | 
    
273  | 
    ;  | 
    ||
274  | 
    24  | 
    return (ret);  | 
    |
275  | 
    24  | 
    }  | 
    |
276  | 
    |||
277  | 
    void  | 
    ||
278  | 
    do_entry(struct conf_entry *ent)  | 
    ||
279  | 
    { | 
    ||
280  | 
    576  | 
    struct stat sb;  | 
    |
281  | 
    int modhours;  | 
    ||
282  | 
    off_t size;  | 
    ||
283  | 
    |||
284  | 
    ✓✓ | 288  | 
    if (lstat(ent->log, &sb) != 0)  | 
    
285  | 
    48  | 
    return;  | 
    |
286  | 
    ✗✓✗✗ | 
    240  | 
    if (!S_ISREG(sb.st_mode) &&  | 
    
287  | 
    	    (!S_ISLNK(sb.st_mode) || !(ent->flags & CE_FOLLOW))) { | 
    ||
288  | 
    		DPRINTF(("--> not a regular file, skipping\n")); | 
    ||
289  | 
    return;  | 
    ||
290  | 
    }  | 
    ||
291  | 
    ✗✓✗✗ | 
    240  | 
    	if (S_ISLNK(sb.st_mode) && stat(ent->log, &sb) != 0) { | 
    
292  | 
    		DPRINTF(("--> link target does not exist, skipping\n")); | 
    ||
293  | 
    return;  | 
    ||
294  | 
    }  | 
    ||
295  | 
    ✓✓ | 240  | 
    if (ent->uid == (uid_t)-1)  | 
    
296  | 
    192  | 
    ent->uid = sb.st_uid;  | 
    |
297  | 
    ✓✓ | 240  | 
    if (ent->gid == (gid_t)-1)  | 
    
298  | 
    192  | 
    ent->gid = sb.st_gid;  | 
    |
299  | 
    |||
300  | 
    ✗✓ | 240  | 
    	DPRINTF(("%s <%d%s%s%s%s>: ", ent->log, ent->numlogs, | 
    
301  | 
    (ent->flags & CE_COMPACT) ? "Z" : "",  | 
    ||
302  | 
    (ent->flags & CE_BINARY) ? "B" : "",  | 
    ||
303  | 
    (ent->flags & CE_FOLLOW) ? "F" : "",  | 
    ||
304  | 
    (ent->flags & CE_MONITOR) && monitormode ? "M" : ""));  | 
    ||
305  | 
    240  | 
    size = sizefile(&sb);  | 
    |
306  | 
    240  | 
    modhours = age_old_log(ent);  | 
    |
307  | 
    ✓✓ | 240  | 
    	if (ent->flags & CE_TRIMAT && !force) { | 
    
308  | 
    ✗✓✗✗ | 
    24  | 
    if (timenow < ent->trim_at ||  | 
    
309  | 
    		    difftime(timenow, ent->trim_at) >= 60 * 60) { | 
    ||
310  | 
    ✗✓ | 24  | 
    			DPRINTF(("--> will trim at %s", | 
    
311  | 
    ctime(&ent->trim_at)));  | 
    ||
312  | 
    24  | 
    return;  | 
    |
313  | 
    		} else if (ent->hours <= 0) { | 
    ||
314  | 
    			DPRINTF(("--> time is up\n")); | 
    ||
315  | 
    }  | 
    ||
316  | 
    }  | 
    ||
317  | 
    ✗✓ | 432  | 
    if (ent->size > 0)  | 
    
318  | 
    216  | 
    		DPRINTF(("size (KB): %.2f [%d] ", size / 1024.0, | 
    |
319  | 
    (int)(ent->size / 1024)));  | 
    ||
320  | 
    ✗✓ | 432  | 
    if (ent->hours > 0)  | 
    
321  | 
    216  | 
    		DPRINTF(("age (hr): %d [%d] ", modhours, ent->hours)); | 
    |
322  | 
    ✗✓✗✗ ✗✗  | 
    216  | 
    if (monitormode && (ent->flags & CE_MONITOR) && domonitor(ent))  | 
    
323  | 
    		DPRINTF(("--> monitored\n")); | 
    ||
324  | 
    ✓✗ | 216  | 
    else if (!monitormode &&  | 
    
325  | 
    ✓✗✓✓ ✓✗  | 
    576  | 
    (force || (ent->size > 0 && size >= ent->size) ||  | 
    
326  | 
    ✓✓✓✗ | 
    360  | 
    (ent->hours <= 0 && (ent->flags & CE_TRIMAT)) ||  | 
    
327  | 
    ✓✓✓✓ | 
    288  | 
    (ent->hours > 0 && (modhours >= ent->hours || modhours < 0)  | 
    
328  | 
    ✓✓ | 25  | 
    	    && ((ent->flags & CE_BINARY) || size >= MIN_SIZE)))) { | 
    
329  | 
    ✗✓ | 1  | 
    		DPRINTF(("--> trimming log....\n")); | 
    
330  | 
    ✗✓ | 1  | 
    if (noaction && !verbose)  | 
    
331  | 
    			printf("%s <%d%s%s%s>\n", ent->log, | 
    ||
332  | 
    ent->numlogs,  | 
    ||
333  | 
    (ent->flags & CE_COMPACT) ? "Z" : "",  | 
    ||
334  | 
    (ent->flags & CE_BINARY) ? "B" : "",  | 
    ||
335  | 
    (ent->flags & CE_FOLLOW) ? "F" : "");  | 
    ||
336  | 
    1  | 
    dotrim(ent);  | 
    |
337  | 
    1  | 
    ent->flags |= CE_ROTATED;  | 
    |
338  | 
    1  | 
    } else  | 
    |
339  | 
    ✗✓ | 215  | 
    		DPRINTF(("--> skipping\n")); | 
    
340  | 
    504  | 
    }  | 
    |
341  | 
    |||
342  | 
    /* Run the specified command */  | 
    ||
343  | 
    void  | 
    ||
344  | 
    run_command(char *cmd)  | 
    ||
345  | 
    { | 
    ||
346  | 
    if (noaction)  | 
    ||
347  | 
    		(void)printf("run %s\n", cmd); | 
    ||
348  | 
    else  | 
    ||
349  | 
    system(cmd);  | 
    ||
350  | 
    }  | 
    ||
351  | 
    |||
352  | 
    /* Send a signal to the pid specified by pidfile */  | 
    ||
353  | 
    void  | 
    ||
354  | 
    send_signal(char *pidfile, int signal)  | 
    ||
355  | 
    { | 
    ||
356  | 
    2  | 
    char line[BUFSIZ], *ep, *err;  | 
    |
357  | 
    pid_t pid;  | 
    ||
358  | 
    long lval;  | 
    ||
359  | 
    FILE *f;  | 
    ||
360  | 
    |||
361  | 
    ✗✓ | 1  | 
    	if ((f = fopen(pidfile, "r")) == NULL) { | 
    
362  | 
    		warn("can't open %s", pidfile); | 
    ||
363  | 
    return;  | 
    ||
364  | 
    }  | 
    ||
365  | 
    |||
366  | 
    pid = 0;  | 
    ||
367  | 
    1  | 
    errno = 0;  | 
    |
368  | 
    err = NULL;  | 
    ||
369  | 
    ✓✗ | 1  | 
    	if (fgets(line, sizeof(line), f)) { | 
    
370  | 
    1  | 
    lval = strtol(line, &ep, 10);  | 
    |
371  | 
    ✓✗✓✗ ✗✓  | 
    3  | 
    if (line[0] == '\0' || (*ep != '\0' && *ep != '\n'))  | 
    
372  | 
    err = "invalid number in";  | 
    ||
373  | 
    ✓✗✗✓ | 
    2  | 
    else if (lval < 0 || (errno == ERANGE && lval == LONG_MAX))  | 
    
374  | 
    err = "out of range number in";  | 
    ||
375  | 
    ✗✓ | 1  | 
    else if (lval == 0)  | 
    
376  | 
    err = "no number in";  | 
    ||
377  | 
    ✗✓ | 1  | 
    else if (lval < MIN_PID)  | 
    
378  | 
    err = "preposterous process number in";  | 
    ||
379  | 
    else  | 
    ||
380  | 
    1  | 
    pid = (pid_t)lval;  | 
    |
381  | 
    	} else { | 
    ||
382  | 
    if (errno == 0)  | 
    ||
383  | 
    err = "empty";  | 
    ||
384  | 
    else  | 
    ||
385  | 
    err = "error reading";  | 
    ||
386  | 
    }  | 
    ||
387  | 
    1  | 
    (void)fclose(f);  | 
    |
388  | 
    |||
389  | 
    ✗✓ | 1  | 
    if (err)  | 
    
390  | 
    		warnx("%s pid file: %s", err, pidfile); | 
    ||
391  | 
    ✗✓ | 1  | 
    else if (noaction)  | 
    
392  | 
    		(void)printf("kill -%s %ld\n", sys_signame[signal], (long)pid); | 
    ||
393  | 
    ✗✓ | 1  | 
    else if (kill(pid, signal))  | 
    
394  | 
    		warnx("warning - could not send SIG%s to PID from pid file %s", | 
    ||
395  | 
    sys_signame[signal], pidfile);  | 
    ||
396  | 
    2  | 
    }  | 
    |
397  | 
    |||
398  | 
    void  | 
    ||
399  | 
    parse_args(int argc, char **argv)  | 
    ||
400  | 
    { | 
    ||
401  | 
    48  | 
    struct timeval now;  | 
    |
402  | 
    struct tm *tm;  | 
    ||
403  | 
    size_t l;  | 
    ||
404  | 
    char *p;  | 
    ||
405  | 
    int ch;  | 
    ||
406  | 
    |||
407  | 
    24  | 
    gettimeofday(&now, NULL);  | 
    |
408  | 
    24  | 
    timenow = now.tv_sec;  | 
    |
409  | 
    24  | 
    tm = gmtime(&now.tv_sec);  | 
    |
410  | 
    24  | 
    l = strftime(daytime, sizeof(daytime), "%FT%T", tm);  | 
    |
411  | 
    48  | 
    snprintf(daytime + l, sizeof(daytime) - l, ".%03ldZ",  | 
    |
412  | 
    24  | 
    now.tv_usec / 1000);  | 
    |
413  | 
    |||
414  | 
    /* Let's get our hostname */  | 
    ||
415  | 
    24  | 
    (void)gethostname(hostname, sizeof(hostname));  | 
    |
416  | 
    |||
417  | 
    /* Truncate domain */  | 
    ||
418  | 
    ✓✗ | 24  | 
    if ((p = strchr(hostname, '.')) != NULL)  | 
    
419  | 
    24  | 
    *p = '\0';  | 
    |
420  | 
    |||
421  | 
    ✗✓ | 24  | 
    	while ((ch = getopt(argc, argv, "Fmnrva:f:")) != -1) { | 
    
422  | 
    		switch (ch) { | 
    ||
423  | 
    case 'a':  | 
    ||
424  | 
    arcdir = optarg;  | 
    ||
425  | 
    break;  | 
    ||
426  | 
    case 'n':  | 
    ||
427  | 
    noaction = 1; /* This implies needroot as off */  | 
    ||
428  | 
    /* fall through */  | 
    ||
429  | 
    case 'r':  | 
    ||
430  | 
    needroot = 0;  | 
    ||
431  | 
    break;  | 
    ||
432  | 
    case 'v':  | 
    ||
433  | 
    verbose = 1;  | 
    ||
434  | 
    break;  | 
    ||
435  | 
    case 'f':  | 
    ||
436  | 
    conf = optarg;  | 
    ||
437  | 
    break;  | 
    ||
438  | 
    case 'm':  | 
    ||
439  | 
    monitormode = 1;  | 
    ||
440  | 
    break;  | 
    ||
441  | 
    case 'F':  | 
    ||
442  | 
    force = 1;  | 
    ||
443  | 
    break;  | 
    ||
444  | 
    default:  | 
    ||
445  | 
    usage();  | 
    ||
446  | 
    }  | 
    ||
447  | 
    }  | 
    ||
448  | 
    ✗✓ | 24  | 
    if (monitormode && force)  | 
    
449  | 
    errx(1, "cannot specify both -m and -F flags");  | 
    ||
450  | 
    24  | 
    }  | 
    |
451  | 
    |||
452  | 
    void  | 
    ||
453  | 
    usage(void)  | 
    ||
454  | 
    { | 
    ||
455  | 
    extern const char *__progname;  | 
    ||
456  | 
    |||
457  | 
    (void)fprintf(stderr, "usage: %s [-Fmnrv] [-a directory] "  | 
    ||
458  | 
    "[-f config_file] [log ...]\n", __progname);  | 
    ||
459  | 
    exit(1);  | 
    ||
460  | 
    }  | 
    ||
461  | 
    |||
462  | 
    /*  | 
    ||
463  | 
    * Parse a configuration file and build a linked list of all the logs  | 
    ||
464  | 
    * to process  | 
    ||
465  | 
    */  | 
    ||
466  | 
    int  | 
    ||
467  | 
    parse_file(struct entrylist *list, int *nentries)  | 
    ||
468  | 
    { | 
    ||
469  | 
    48  | 
    char line[BUFSIZ], *parse, *q, *errline, *group, *tmp, *ep;  | 
    |
470  | 
    struct conf_entry *working;  | 
    ||
471  | 
    struct passwd *pwd;  | 
    ||
472  | 
    struct group *grp;  | 
    ||
473  | 
    24  | 
    struct stat sb;  | 
    |
474  | 
    int lineno = 0;  | 
    ||
475  | 
    int ret = 0;  | 
    ||
476  | 
    FILE *f;  | 
    ||
477  | 
    long l;  | 
    ||
478  | 
    |||
479  | 
    ✗✓ | 24  | 
    if (strcmp(conf, "-") == 0)  | 
    
480  | 
    f = stdin;  | 
    ||
481  | 
    ✗✓ | 24  | 
    else if ((f = fopen(conf, "r")) == NULL)  | 
    
482  | 
    err(1, "can't open %s", conf);  | 
    ||
483  | 
    |||
484  | 
    24  | 
    *nentries = 0;  | 
    |
485  | 
    |||
486  | 
    nextline:  | 
    ||
487  | 
    ✓✓ | 744  | 
    	while (fgets(line, sizeof(line), f) != NULL) { | 
    
488  | 
    408  | 
    lineno++;  | 
    |
489  | 
    408  | 
    tmp = sob(line);  | 
    |
490  | 
    ✗✓✓✓ | 
    816  | 
    if (*tmp == '\0' || *tmp == '#')  | 
    
491  | 
    continue;  | 
    ||
492  | 
    288  | 
    errline = strdup(tmp);  | 
    |
493  | 
    ✗✓ | 288  | 
    if (errline == NULL)  | 
    
494  | 
    err(1, NULL);  | 
    ||
495  | 
    288  | 
    working = calloc(1, sizeof(*working));  | 
    |
496  | 
    ✗✓ | 288  | 
    if (working == NULL)  | 
    
497  | 
    err(1, NULL);  | 
    ||
498  | 
    |||
499  | 
    288  | 
    q = parse = missing_field(sob(line), errline, lineno);  | 
    |
500  | 
    288  | 
    *(parse = son(line)) = '\0';  | 
    |
501  | 
    288  | 
    working->log = strdup(q);  | 
    |
502  | 
    ✗✓ | 288  | 
    if (working->log == NULL)  | 
    
503  | 
    err(1, NULL);  | 
    ||
504  | 
    |||
505  | 
    ✓✗ | 288  | 
    if ((working->logbase = strrchr(working->log, '/')) != NULL)  | 
    
506  | 
    288  | 
    working->logbase++;  | 
    |
507  | 
    |||
508  | 
    288  | 
    q = parse = missing_field(sob(++parse), errline, lineno);  | 
    |
509  | 
    288  | 
    *(parse = son(parse)) = '\0';  | 
    |
510  | 
    ✓✓✗✓ | 
    528  | 
    if ((group = strchr(q, ':')) != NULL ||  | 
    
511  | 
    240  | 
    		    (group = strrchr(q, '.')) != NULL)  { | 
    |
512  | 
    48  | 
    *group++ = '\0';  | 
    |
513  | 
    ✓✗ | 48  | 
    			if (*q) { | 
    
514  | 
    ✓✗ | 48  | 
    				if (!(isnumberstr(q))) { | 
    
515  | 
    ✗✓ | 48  | 
    					if ((pwd = getpwnam(q)) == NULL) { | 
    
516  | 
    						warnx("%s:%d: unknown user" | 
    ||
517  | 
    " %s --> skipping",  | 
    ||
518  | 
    conf, lineno, q);  | 
    ||
519  | 
    ret = 1;  | 
    ||
520  | 
    goto nextline;  | 
    ||
521  | 
    }  | 
    ||
522  | 
    48  | 
    working->uid = pwd->pw_uid;  | 
    |
523  | 
    48  | 
    } else  | 
    |
524  | 
    working->uid = atoi(q);  | 
    ||
525  | 
    } else  | 
    ||
526  | 
    working->uid = (uid_t)-1;  | 
    ||
527  | 
    |||
528  | 
    q = group;  | 
    ||
529  | 
    ✓✗ | 48  | 
    			if (*q) { | 
    
530  | 
    ✓✗ | 48  | 
    				if (!(isnumberstr(q))) { | 
    
531  | 
    ✗✓ | 48  | 
    					if ((grp = getgrnam(q)) == NULL) { | 
    
532  | 
    						warnx("%s:%d: unknown group" | 
    ||
533  | 
    " %s --> skipping",  | 
    ||
534  | 
    conf, lineno, q);  | 
    ||
535  | 
    ret = 1;  | 
    ||
536  | 
    goto nextline;  | 
    ||
537  | 
    }  | 
    ||
538  | 
    48  | 
    working->gid = grp->gr_gid;  | 
    |
539  | 
    48  | 
    } else  | 
    |
540  | 
    working->gid = atoi(q);  | 
    ||
541  | 
    } else  | 
    ||
542  | 
    working->gid = (gid_t)-1;  | 
    ||
543  | 
    |||
544  | 
    48  | 
    q = parse = missing_field(sob(++parse), errline, lineno);  | 
    |
545  | 
    48  | 
    *(parse = son(parse)) = '\0';  | 
    |
546  | 
    48  | 
    		} else { | 
    |
547  | 
    240  | 
    working->uid = (uid_t)-1;  | 
    |
548  | 
    240  | 
    working->gid = (gid_t)-1;  | 
    |
549  | 
    }  | 
    ||
550  | 
    |||
551  | 
    288  | 
    l = strtol(q, &ep, 8);  | 
    |
552  | 
    ✗✓ | 288  | 
    		if (*ep != '\0' || l < 0 || l > ALLPERMS) { | 
    
553  | 
    			warnx("%s:%d: bad permissions: %s --> skipping", conf, | 
    ||
554  | 
    lineno, q);  | 
    ||
555  | 
    ret = 1;  | 
    ||
556  | 
    goto nextline;  | 
    ||
557  | 
    }  | 
    ||
558  | 
    288  | 
    working->permissions = (mode_t)l;  | 
    |
559  | 
    |||
560  | 
    288  | 
    q = parse = missing_field(sob(++parse), errline, lineno);  | 
    |
561  | 
    288  | 
    *(parse = son(parse)) = '\0';  | 
    |
562  | 
    288  | 
    l = strtol(q, &ep, 10);  | 
    |
563  | 
    ✗✓ | 288  | 
    		if (*ep != '\0' || l < 0 || l >= INT_MAX) { | 
    
564  | 
    			warnx("%s:%d: bad number: %s --> skipping", conf, | 
    ||
565  | 
    lineno, q);  | 
    ||
566  | 
    ret = 1;  | 
    ||
567  | 
    goto nextline;  | 
    ||
568  | 
    }  | 
    ||
569  | 
    288  | 
    working->numlogs = (int)l;  | 
    |
570  | 
    |||
571  | 
    288  | 
    q = parse = missing_field(sob(++parse), errline, lineno);  | 
    |
572  | 
    288  | 
    *(parse = son(parse)) = '\0';  | 
    |
573  | 
    ✓✓ | 288  | 
    if (isdigit((unsigned char)*q))  | 
    
574  | 
    168  | 
    working->size = atoi(q) * 1024;  | 
    |
575  | 
    else  | 
    ||
576  | 
    working->size = -1;  | 
    ||
577  | 
    |||
578  | 
    288  | 
    working->flags = 0;  | 
    |
579  | 
    288  | 
    q = parse = missing_field(sob(++parse), errline, lineno);  | 
    |
580  | 
    288  | 
    *(parse = son(parse)) = '\0';  | 
    |
581  | 
    288  | 
    l = strtol(q, &ep, 10);  | 
    |
582  | 
    ✗✓ | 288  | 
    		if (l < 0 || l >= INT_MAX) { | 
    
583  | 
    			warnx("%s:%d: interval out of range: %s --> skipping", | 
    ||
584  | 
    conf, lineno, q);  | 
    ||
585  | 
    ret = 1;  | 
    ||
586  | 
    goto nextline;  | 
    ||
587  | 
    }  | 
    ||
588  | 
    504  | 
    working->hours = (int)l;  | 
    |
589  | 
    ✗✓✓✗ ✓  | 
    504  | 
    		switch (*ep) { | 
    
590  | 
    case '\0':  | 
    ||
591  | 
    break;  | 
    ||
592  | 
    case '@':  | 
    ||
593  | 
    working->trim_at = parse8601(ep + 1);  | 
    ||
594  | 
    			if (working->trim_at == (time_t) - 1) { | 
    ||
595  | 
    				warnx("%s:%d: bad time: %s --> skipping", conf, | 
    ||
596  | 
    lineno, q);  | 
    ||
597  | 
    ret = 1;  | 
    ||
598  | 
    goto nextline;  | 
    ||
599  | 
    }  | 
    ||
600  | 
    working->flags |= CE_TRIMAT;  | 
    ||
601  | 
    break;  | 
    ||
602  | 
    case '$':  | 
    ||
603  | 
    48  | 
    working->trim_at = parseDWM(ep + 1);  | 
    |
604  | 
    ✗✓ | 48  | 
    			if (working->trim_at == (time_t) - 1) { | 
    
605  | 
    				warnx("%s:%d: bad time: %s --> skipping", conf, | 
    ||
606  | 
    lineno, q);  | 
    ||
607  | 
    ret = 1;  | 
    ||
608  | 
    goto nextline;  | 
    ||
609  | 
    }  | 
    ||
610  | 
    48  | 
    working->flags |= CE_TRIMAT;  | 
    |
611  | 
    48  | 
    break;  | 
    |
612  | 
    case '*':  | 
    ||
613  | 
    ✗✓ | 168  | 
    if (q == ep)  | 
    
614  | 
    break;  | 
    ||
615  | 
    /* FALLTHROUGH */  | 
    ||
616  | 
    default:  | 
    ||
617  | 
    			warnx("%s:%d: bad interval/at: %s --> skipping", conf, | 
    ||
618  | 
    lineno, q);  | 
    ||
619  | 
    ret = 1;  | 
    ||
620  | 
    goto nextline;  | 
    ||
621  | 
    }  | 
    ||
622  | 
    |||
623  | 
    288  | 
    q = sob(++parse); /* Optional field */  | 
    |
624  | 
    ✓✓✓✗ ✗✓✗✗ ✗✗  | 
    336  | 
    if (*q == 'Z' || *q == 'z' || *q == 'B' || *q == 'b' ||  | 
    
625  | 
    		    *q == 'M' || *q == 'm') { | 
    ||
626  | 
    288  | 
    *(parse = son(q)) = '\0';  | 
    |
627  | 
    ✓✓ | 1200  | 
    			while (*q) { | 
    
628  | 
    ✗✓✗✓ ✗✗✗✗ ✗  | 
    312  | 
    				switch (*q) { | 
    
629  | 
    case 'Z':  | 
    ||
630  | 
    case 'z':  | 
    ||
631  | 
    working->flags |= CE_COMPACT;  | 
    ||
632  | 
    264  | 
    break;  | 
    |
633  | 
    case 'B':  | 
    ||
634  | 
    case 'b':  | 
    ||
635  | 
    working->flags |= CE_BINARY;  | 
    ||
636  | 
    48  | 
    break;  | 
    |
637  | 
    case 'M':  | 
    ||
638  | 
    case 'm':  | 
    ||
639  | 
    working->flags |= CE_MONITOR;  | 
    ||
640  | 
    break;  | 
    ||
641  | 
    case 'F':  | 
    ||
642  | 
    case 'f':  | 
    ||
643  | 
    working->flags |= CE_FOLLOW;  | 
    ||
644  | 
    break;  | 
    ||
645  | 
    default:  | 
    ||
646  | 
    					warnx("%s:%d: illegal flag: `%c'" | 
    ||
647  | 
    " --> skipping",  | 
    ||
648  | 
    conf, lineno, *q);  | 
    ||
649  | 
    ret = 1;  | 
    ||
650  | 
    goto nextline;  | 
    ||
651  | 
    }  | 
    ||
652  | 
    312  | 
    q++;  | 
    |
653  | 
    }  | 
    ||
654  | 
    } else  | 
    ||
655  | 
    parse--; /* no flags so undo */  | 
    ||
656  | 
    |||
657  | 
    288  | 
    working->pidfile = PIDFILE;  | 
    |
658  | 
    288  | 
    working->signal = SIGHUP;  | 
    |
659  | 
    288  | 
    working->runcmd = NULL;  | 
    |
660  | 
    288  | 
    working->whom = NULL;  | 
    |
661  | 
    288  | 
    		for (;;) { | 
    |
662  | 
    360  | 
    q = parse = sob(++parse); /* Optional field */  | 
    |
663  | 
    ✓✗✓✓ | 
    720  | 
    if (q == NULL || *q == '\0')  | 
    
664  | 
    break;  | 
    ||
665  | 
    ✗✓ | 72  | 
    			if (*q == '/') { | 
    
666  | 
    *(parse = son(parse)) = '\0';  | 
    ||
667  | 
    				if (strlen(q) >= PATH_MAX) { | 
    ||
668  | 
    					warnx("%s:%d: pathname too long: %s" | 
    ||
669  | 
    " --> skipping",  | 
    ||
670  | 
    conf, lineno, q);  | 
    ||
671  | 
    ret = 1;  | 
    ||
672  | 
    goto nextline;  | 
    ||
673  | 
    }  | 
    ||
674  | 
    working->pidfile = strdup(q);  | 
    ||
675  | 
    if (working->pidfile == NULL)  | 
    ||
676  | 
    err(1, NULL);  | 
    ||
677  | 
    ✓✗✓✗ | 
    144  | 
    			} else if (*q == '"' && (tmp = strchr(q + 1, '"'))) { | 
    
678  | 
    72  | 
    *(parse = tmp) = '\0';  | 
    |
679  | 
    ✓✗ | 72  | 
    				if (*++q != '\0') { | 
    
680  | 
    72  | 
    working->runcmd = strdup(q);  | 
    |
681  | 
    ✗✓ | 72  | 
    if (working->runcmd == NULL)  | 
    
682  | 
    err(1, NULL);  | 
    ||
683  | 
    }  | 
    ||
684  | 
    72  | 
    working->pidfile = NULL;  | 
    |
685  | 
    72  | 
    working->signal = -1;  | 
    |
686  | 
    ✗✗ | 72  | 
    			} else if (strncmp(q, "SIG", 3) == 0) { | 
    
687  | 
    int i;  | 
    ||
688  | 
    |||
689  | 
    *(parse = son(parse)) = '\0';  | 
    ||
690  | 
    				for (i = 1; i < NSIG; i++) { | 
    ||
691  | 
    					if (!strcmp(sys_signame[i], q + 3)) { | 
    ||
692  | 
    working->signal = i;  | 
    ||
693  | 
    break;  | 
    ||
694  | 
    }  | 
    ||
695  | 
    }  | 
    ||
696  | 
    				if (i == NSIG) { | 
    ||
697  | 
    					warnx("%s:%d: unknown signal: %s" | 
    ||
698  | 
    " --> skipping",  | 
    ||
699  | 
    conf, lineno, q);  | 
    ||
700  | 
    ret = 1;  | 
    ||
701  | 
    goto nextline;  | 
    ||
702  | 
    }  | 
    ||
703  | 
    			} else if (working->flags & CE_MONITOR) { | 
    ||
704  | 
    *(parse = son(parse)) = '\0';  | 
    ||
705  | 
    working->whom = strdup(q);  | 
    ||
706  | 
    if (working->whom == NULL)  | 
    ||
707  | 
    err(1, NULL);  | 
    ||
708  | 
    			} else { | 
    ||
709  | 
    				warnx("%s:%d: unrecognized field: %s" | 
    ||
710  | 
    " --> skipping",  | 
    ||
711  | 
    conf, lineno, q);  | 
    ||
712  | 
    ret = 1;  | 
    ||
713  | 
    goto nextline;  | 
    ||
714  | 
    }  | 
    ||
715  | 
    }  | 
    ||
716  | 
    288  | 
    free(errline);  | 
    |
717  | 
    |||
718  | 
    ✗✓✗✗ | 
    288  | 
    		if ((working->flags & CE_MONITOR) && working->whom == NULL) { | 
    
719  | 
    			warnx("%s:%d: missing monitor notification field" | 
    ||
720  | 
    " --> skipping",  | 
    ||
721  | 
    conf, lineno);  | 
    ||
722  | 
    ret = 1;  | 
    ||
723  | 
    goto nextline;  | 
    ||
724  | 
    }  | 
    ||
725  | 
    |||
726  | 
    /* If there is an arcdir, set working->backdir. */  | 
    ||
727  | 
    ✗✓✗✗ | 
    288  | 
    		if (arcdir != NULL && working->logbase != NULL) { | 
    
728  | 
    			if (*arcdir == '/') { | 
    ||
729  | 
    /* Fully qualified arcdir */  | 
    ||
730  | 
    working->backdir = arcdir;  | 
    ||
731  | 
    			} else { | 
    ||
732  | 
    /* arcdir is relative to log's parent dir */  | 
    ||
733  | 
    *(working->logbase - 1) = '\0';  | 
    ||
734  | 
    if ((asprintf(&working->backdir, "%s/%s",  | 
    ||
735  | 
    working->log, arcdir)) == -1)  | 
    ||
736  | 
    err(1, NULL);  | 
    ||
737  | 
    *(working->logbase - 1) = '/';  | 
    ||
738  | 
    }  | 
    ||
739  | 
    /* Ignore arcdir if it doesn't exist. */  | 
    ||
740  | 
    if (stat(working->backdir, &sb) != 0 ||  | 
    ||
741  | 
    			    !S_ISDIR(sb.st_mode)) { | 
    ||
742  | 
    if (working->backdir != arcdir)  | 
    ||
743  | 
    free(working->backdir);  | 
    ||
744  | 
    working->backdir = NULL;  | 
    ||
745  | 
    }  | 
    ||
746  | 
    } else  | 
    ||
747  | 
    working->backdir = NULL;  | 
    ||
748  | 
    |||
749  | 
    /* Make sure we can't oflow PATH_MAX */  | 
    ||
750  | 
    ✗✓ | 576  | 
    		if (working->backdir != NULL) { | 
    
751  | 
    if (snprintf(line, sizeof(line), "%s/%s.%d%s",  | 
    ||
752  | 
    working->backdir, working->logbase,  | 
    ||
753  | 
    			    working->numlogs, COMPRESS_POSTFIX) >= PATH_MAX) { | 
    ||
754  | 
    				warnx("%s:%d: pathname too long: %s" | 
    ||
755  | 
    " --> skipping", conf, lineno, q);  | 
    ||
756  | 
    ret = 1;  | 
    ||
757  | 
    goto nextline;  | 
    ||
758  | 
    }  | 
    ||
759  | 
    		} else { | 
    ||
760  | 
    ✗✓ | 576  | 
    if (snprintf(line, sizeof(line), "%s.%d%s",  | 
    
761  | 
    288  | 
    working->log, working->numlogs, COMPRESS_POSTFIX)  | 
    |
762  | 
    288  | 
    			    >= PATH_MAX) { | 
    |
763  | 
    				warnx("%s:%d: pathname too long: %s" | 
    ||
764  | 
    " --> skipping", conf, lineno,  | 
    ||
765  | 
    working->log);  | 
    ||
766  | 
    ret = 1;  | 
    ||
767  | 
    goto nextline;  | 
    ||
768  | 
    }  | 
    ||
769  | 
    }  | 
    ||
770  | 
    288  | 
    TAILQ_INSERT_TAIL(list, working, next);  | 
    |
771  | 
    288  | 
    (*nentries)++;  | 
    |
772  | 
    }  | 
    ||
773  | 
    24  | 
    (void)fclose(f);  | 
    |
774  | 
    24  | 
    return (ret);  | 
    |
775  | 
    24  | 
    }  | 
    |
776  | 
    |||
777  | 
    char *  | 
    ||
778  | 
    missing_field(char *p, char *errline, int lineno)  | 
    ||
779  | 
    { | 
    ||
780  | 
    ✓✗✗✓ | 
    4464  | 
    	if (p == NULL || *p == '\0') { | 
    
781  | 
    		warnx("%s:%d: missing field", conf, lineno); | 
    ||
782  | 
    fputs(errline, stderr);  | 
    ||
783  | 
    exit(1);  | 
    ||
784  | 
    }  | 
    ||
785  | 
    1488  | 
    return (p);  | 
    |
786  | 
    }  | 
    ||
787  | 
    |||
788  | 
    void  | 
    ||
789  | 
    rotate(struct conf_entry *ent, const char *oldlog)  | 
    ||
790  | 
    { | 
    ||
791  | 
    2  | 
    char file1[PATH_MAX], file2[PATH_MAX], *suffix;  | 
    |
792  | 
    1  | 
    int numdays = ent->numlogs - 1;  | 
    |
793  | 
    int done = 0;  | 
    ||
794  | 
    |||
795  | 
    /* Remove old logs */  | 
    ||
796  | 
    1  | 
    	do { | 
    |
797  | 
    1  | 
    (void)snprintf(file1, sizeof(file1), "%s.%d", oldlog, numdays);  | 
    |
798  | 
    1  | 
    (void)snprintf(file2, sizeof(file2), "%s.%d%s", oldlog,  | 
    |
799  | 
    numdays, COMPRESS_POSTFIX);  | 
    ||
800  | 
    ✗✓ | 1  | 
    		if (noaction) { | 
    
801  | 
    			printf("\trm -f %s %s\n", file1, file2); | 
    ||
802  | 
    done = access(file1, 0) && access(file2, 0);  | 
    ||
803  | 
    		} else { | 
    ||
804  | 
    ✓✗ | 3  | 
    done = unlink(file1) && unlink(file2);  | 
    
805  | 
    }  | 
    ||
806  | 
    1  | 
    numdays++;  | 
    |
807  | 
    ✗✓ | 1  | 
    } while (done == 0);  | 
    
808  | 
    |||
809  | 
    /* Move down log files */  | 
    ||
810  | 
    ✓✓ | 14  | 
    	for (numdays = ent->numlogs - 2; numdays >= 0; numdays--) { | 
    
811  | 
    /*  | 
    ||
812  | 
    * If both the compressed archive and the non-compressed archive  | 
    ||
813  | 
    * exist, we decide which to rotate based on the CE_COMPACT flag  | 
    ||
814  | 
    */  | 
    ||
815  | 
    6  | 
    (void)snprintf(file1, sizeof(file1), "%s.%d", oldlog, numdays);  | 
    |
816  | 
    6  | 
    suffix = lstat_log(file1, sizeof(file1), ent->flags);  | 
    |
817  | 
    ✓✓ | 6  | 
    if (suffix == NULL)  | 
    
818  | 
    continue;  | 
    ||
819  | 
    1  | 
    (void)snprintf(file2, sizeof(file2), "%s.%d%s", oldlog,  | 
    |
820  | 
    1  | 
    numdays + 1, suffix);  | 
    |
821  | 
    |||
822  | 
    ✗✓ | 1  | 
    		if (noaction) { | 
    
823  | 
    			printf("\tmv %s %s\n", file1, file2); | 
    ||
824  | 
    			printf("\tchmod %o %s\n", ent->permissions, file2); | 
    ||
825  | 
    			printf("\tchown %u:%u %s\n", ent->uid, ent->gid, file2); | 
    ||
826  | 
    		} else { | 
    ||
827  | 
    ✗✓ | 1  | 
    if (rename(file1, file2))  | 
    
828  | 
    				warn("can't mv %s to %s", file1, file2); | 
    ||
829  | 
    ✗✓ | 1  | 
    if (chmod(file2, ent->permissions))  | 
    
830  | 
    				warn("can't chmod %s", file2); | 
    ||
831  | 
    ✗✓ | 1  | 
    if (chown(file2, ent->uid, ent->gid))  | 
    
832  | 
    				warn("can't chown %s", file2); | 
    ||
833  | 
    }  | 
    ||
834  | 
    }  | 
    ||
835  | 
    1  | 
    }  | 
    |
836  | 
    |||
837  | 
    void  | 
    ||
838  | 
    dotrim(struct conf_entry *ent)  | 
    ||
839  | 
    { | 
    ||
840  | 
    2  | 
    char file1[PATH_MAX], file2[PATH_MAX], oldlog[PATH_MAX];  | 
    |
841  | 
    int fd;  | 
    ||
842  | 
    |||
843  | 
    /* Is there a separate backup dir? */  | 
    ||
844  | 
    ✗✓ | 1  | 
    if (ent->backdir != NULL)  | 
    
845  | 
    snprintf(oldlog, sizeof(oldlog), "%s/%s", ent->backdir,  | 
    ||
846  | 
    ent->logbase);  | 
    ||
847  | 
    else  | 
    ||
848  | 
    1  | 
    strlcpy(oldlog, ent->log, sizeof(oldlog));  | 
    |
849  | 
    |||
850  | 
    ✓✗ | 1  | 
    if (ent->numlogs > 0)  | 
    
851  | 
    1  | 
    rotate(ent, oldlog);  | 
    |
852  | 
    ✓✗✓✗ | 
    2  | 
    if (!noaction && !(ent->flags & CE_BINARY))  | 
    
853  | 
    1  | 
    (void)log_trim(ent->log);  | 
    |
854  | 
    |||
855  | 
    1  | 
    (void)snprintf(file2, sizeof(file2), "%s.XXXXXXXXXX", ent->log);  | 
    |
856  | 
    ✗✓ | 1  | 
    	if (noaction)  { | 
    
857  | 
    		printf("\tmktemp %s\n", file2); | 
    ||
858  | 
    	} else { | 
    ||
859  | 
    ✗✓ | 1  | 
    if ((fd = mkstemp(file2)) < 0)  | 
    
860  | 
    err(1, "can't start '%s' log", file2);  | 
    ||
861  | 
    ✗✓ | 1  | 
    if (fchmod(fd, ent->permissions))  | 
    
862  | 
    err(1, "can't chmod '%s' log file", file2);  | 
    ||
863  | 
    ✗✓ | 1  | 
    if (fchown(fd, ent->uid, ent->gid))  | 
    
864  | 
    err(1, "can't chown '%s' log file", file2);  | 
    ||
865  | 
    1  | 
    (void)close(fd);  | 
    |
866  | 
    /* Add status message */  | 
    ||
867  | 
    ✓✗✗✓ | 
    2  | 
    if (!(ent->flags & CE_BINARY) && log_trim(file2))  | 
    
868  | 
    err(1, "can't add status message to log '%s'", file2);  | 
    ||
869  | 
    }  | 
    ||
870  | 
    |||
871  | 
    ✗✓ | 1  | 
    	if (ent->numlogs == 0) { | 
    
872  | 
    if (noaction)  | 
    ||
873  | 
    			printf("\trm %s\n", ent->log); | 
    ||
874  | 
    else if (unlink(ent->log))  | 
    ||
875  | 
    			warn("can't rm %s", ent->log); | 
    ||
876  | 
    	} else { | 
    ||
877  | 
    1  | 
    (void)snprintf(file1, sizeof(file1), "%s.0", oldlog);  | 
    |
878  | 
    ✗✓ | 1  | 
    		if (noaction) { | 
    
879  | 
    			printf("\tmv %s to %s\n", ent->log, file1); | 
    ||
880  | 
    			printf("\tchmod %o %s\n", ent->permissions, file1); | 
    ||
881  | 
    			printf("\tchown %u:%u %s\n", ent->uid, ent->gid, file1); | 
    ||
882  | 
    ✗✓ | 2  | 
    } else if (movefile(ent->log, file1, ent->uid, ent->gid,  | 
    
883  | 
    1  | 
    ent->permissions))  | 
    |
884  | 
    			warn("can't mv %s to %s", ent->log, file1); | 
    ||
885  | 
    }  | 
    ||
886  | 
    |||
887  | 
    /* Now move the new log file into place */  | 
    ||
888  | 
    ✗✓ | 1  | 
    if (noaction)  | 
    
889  | 
    		printf("\tmv %s to %s\n", file2, ent->log); | 
    ||
890  | 
    ✗✓ | 1  | 
    else if (rename(file2, ent->log))  | 
    
891  | 
    		warn("can't mv %s to %s", file2, ent->log); | 
    ||
892  | 
    1  | 
    }  | 
    |
893  | 
    |||
894  | 
    /* Log the fact that the logs were turned over */  | 
    ||
895  | 
    int  | 
    ||
896  | 
    log_trim(char *log)  | 
    ||
897  | 
    { | 
    ||
898  | 
    FILE *f;  | 
    ||
899  | 
    |||
900  | 
    ✗✓ | 4  | 
    if ((f = fopen(log, "a")) == NULL)  | 
    
901  | 
    return (-1);  | 
    ||
902  | 
    2  | 
    (void)fprintf(f, "%s %s newsyslog[%ld]: logfile turned over\n",  | 
    |
903  | 
    2  | 
    daytime, hostname, (long)getpid());  | 
    |
904  | 
    ✗✓ | 2  | 
    if (fclose(f) == EOF)  | 
    
905  | 
    err(1, "log_trim: fclose");  | 
    ||
906  | 
    2  | 
    return (0);  | 
    |
907  | 
    2  | 
    }  | 
    |
908  | 
    |||
909  | 
    /* Fork off compress or gzip to compress the old log file */  | 
    ||
910  | 
    void  | 
    ||
911  | 
    compress_log(struct conf_entry *ent)  | 
    ||
912  | 
    { | 
    ||
913  | 
    2  | 
    char *base, tmp[PATH_MAX];  | 
    |
914  | 
    pid_t pid;  | 
    ||
915  | 
    |||
916  | 
    ✗✓ | 1  | 
    if (ent->backdir != NULL)  | 
    
917  | 
    snprintf(tmp, sizeof(tmp), "%s/%s.0", ent->backdir,  | 
    ||
918  | 
    ent->logbase);  | 
    ||
919  | 
    else  | 
    ||
920  | 
    1  | 
    snprintf(tmp, sizeof(tmp), "%s.0", ent->log);  | 
    |
921  | 
    |||
922  | 
    ✗✓ | 1  | 
    if ((base = strrchr(COMPRESS, '/')) == NULL)  | 
    
923  | 
    base = COMPRESS;  | 
    ||
924  | 
    else  | 
    ||
925  | 
    1  | 
    base++;  | 
    |
926  | 
    ✗✓ | 1  | 
    	if (noaction) { | 
    
927  | 
    		printf("%s %s\n", base, tmp); | 
    ||
928  | 
    return;  | 
    ||
929  | 
    }  | 
    ||
930  | 
    1  | 
    pid = fork();  | 
    |
931  | 
    ✗✓ | 1  | 
    	if (pid < 0) { | 
    
932  | 
    err(1, "fork");  | 
    ||
933  | 
    ✗✓ | 1  | 
    	} else if (pid == 0) { | 
    
934  | 
    (void)execl(COMPRESS, base, "-f", tmp, (char *)NULL);  | 
    ||
935  | 
    warn(COMPRESS);  | 
    ||
936  | 
    _exit(1);  | 
    ||
937  | 
    }  | 
    ||
938  | 
    2  | 
    }  | 
    |
939  | 
    |||
940  | 
    /* Return size in bytes of a file */  | 
    ||
941  | 
    off_t  | 
    ||
942  | 
    sizefile(struct stat *sb)  | 
    ||
943  | 
    { | 
    ||
944  | 
    /* For sparse files, return the size based on number of blocks used. */  | 
    ||
945  | 
    ✗✓ | 480  | 
    if (sb->st_size / DEV_BSIZE > sb->st_blocks)  | 
    
946  | 
    return (sb->st_blocks * DEV_BSIZE);  | 
    ||
947  | 
    else  | 
    ||
948  | 
    240  | 
    return (sb->st_size);  | 
    |
949  | 
    240  | 
    }  | 
    |
950  | 
    |||
951  | 
    /* Return the age (in hours) of old log file (file.0), or -1 if none */  | 
    ||
952  | 
    int  | 
    ||
953  | 
    age_old_log(struct conf_entry *ent)  | 
    ||
954  | 
    { | 
    ||
955  | 
    480  | 
    char file[PATH_MAX];  | 
    |
956  | 
    240  | 
    struct stat sb;  | 
    |
957  | 
    |||
958  | 
    ✗✓ | 240  | 
    if (ent->backdir != NULL)  | 
    
959  | 
    (void)snprintf(file, sizeof(file), "%s/%s.0", ent->backdir,  | 
    ||
960  | 
    ent->logbase);  | 
    ||
961  | 
    else  | 
    ||
962  | 
    240  | 
    (void)snprintf(file, sizeof(file), "%s.0", ent->log);  | 
    |
963  | 
    ✓✓ | 240  | 
    	if (ent->flags & CE_COMPACT) { | 
    
964  | 
    ✓✗ | 384  | 
    if (stat_suffix(file, sizeof(file), COMPRESS_POSTFIX, &sb,  | 
    
965  | 
    ✓✓ | 384  | 
    stat) < 0 && stat(file, &sb) < 0)  | 
    
966  | 
    168  | 
    return (-1);  | 
    |
967  | 
    	} else { | 
    ||
968  | 
    ✓✗✓✗ | 
    72  | 
    if (stat(file, &sb) < 0 && stat_suffix(file, sizeof(file),  | 
    
969  | 
    24  | 
    COMPRESS_POSTFIX, &sb, stat) < 0)  | 
    |
970  | 
    24  | 
    return (-1);  | 
    |
971  | 
    }  | 
    ||
972  | 
    48  | 
    return ((int)(timenow - sb.st_mtime + 1800) / 3600);  | 
    |
973  | 
    240  | 
    }  | 
    |
974  | 
    |||
975  | 
    /* Skip Over Blanks */  | 
    ||
976  | 
    char *  | 
    ||
977  | 
    sob(char *p)  | 
    ||
978  | 
    { | 
    ||
979  | 
    ✗✓ | 5088  | 
    if (p == NULL)  | 
    
980  | 
    return(p);  | 
    ||
981  | 
    ✓✓ | 9648  | 
    while (isspace((unsigned char)*p))  | 
    
982  | 
    3552  | 
    p++;  | 
    |
983  | 
    2544  | 
    return (p);  | 
    |
984  | 
    2544  | 
    }  | 
    |
985  | 
    |||
986  | 
    /* Skip Over Non-Blanks */  | 
    ||
987  | 
    char *  | 
    ||
988  | 
    son(char *p)  | 
    ||
989  | 
    { | 
    ||
990  | 
    ✓✗✓✗ ✓✓  | 
    49896  | 
    while (p && *p && !isspace((unsigned char)*p))  | 
    
991  | 
    7848  | 
    p++;  | 
    |
992  | 
    1776  | 
    return (p);  | 
    |
993  | 
    }  | 
    ||
994  | 
    |||
995  | 
    /* Check if string is actually a number */  | 
    ||
996  | 
    int  | 
    ||
997  | 
    isnumberstr(char *string)  | 
    ||
998  | 
    { | 
    ||
999  | 
    ✓✗ | 288  | 
    	while (*string) { | 
    
1000  | 
    ✗✓ | 96  | 
    if (!isdigit((unsigned char)*string++))  | 
    
1001  | 
    96  | 
    return (0);  | 
    |
1002  | 
    }  | 
    ||
1003  | 
    return (1);  | 
    ||
1004  | 
    96  | 
    }  | 
    |
1005  | 
    |||
1006  | 
    int  | 
    ||
1007  | 
    domonitor(struct conf_entry *ent)  | 
    ||
1008  | 
    { | 
    ||
1009  | 
    char fname[PATH_MAX], *flog, *p, *rb = NULL;  | 
    ||
1010  | 
    struct stat sb, tsb;  | 
    ||
1011  | 
    off_t osize;  | 
    ||
1012  | 
    FILE *fp;  | 
    ||
1013  | 
    int rd;  | 
    ||
1014  | 
    |||
1015  | 
    if (stat(ent->log, &sb) < 0)  | 
    ||
1016  | 
    return (0);  | 
    ||
1017  | 
    |||
1018  | 
    	if (noaction) { | 
    ||
1019  | 
    if (!verbose)  | 
    ||
1020  | 
    			printf("%s: monitored\n", ent->log); | 
    ||
1021  | 
    return (1);  | 
    ||
1022  | 
    }  | 
    ||
1023  | 
    |||
1024  | 
    flog = strdup(ent->log);  | 
    ||
1025  | 
    if (flog == NULL)  | 
    ||
1026  | 
    err(1, NULL);  | 
    ||
1027  | 
    |||
1028  | 
    	for (p = flog; *p != '\0'; p++) { | 
    ||
1029  | 
    if (*p == '/')  | 
    ||
1030  | 
    *p = '_';  | 
    ||
1031  | 
    }  | 
    ||
1032  | 
    snprintf(fname, sizeof(fname), "%s/newsyslog.%s.size",  | 
    ||
1033  | 
    STATS_DIR, flog);  | 
    ||
1034  | 
    |||
1035  | 
    /* ..if it doesn't exist, simply record the current size. */  | 
    ||
1036  | 
    if ((sb.st_size == 0) || stat(fname, &tsb) < 0)  | 
    ||
1037  | 
    goto update;  | 
    ||
1038  | 
    |||
1039  | 
    fp = fopen(fname, "r");  | 
    ||
1040  | 
    	if (fp == NULL) { | 
    ||
1041  | 
    		warn("%s", fname); | 
    ||
1042  | 
    goto cleanup;  | 
    ||
1043  | 
    }  | 
    ||
1044  | 
    	if (fscanf(fp, "%lld\n", &osize) != 1) { | 
    ||
1045  | 
    fclose(fp);  | 
    ||
1046  | 
    goto update;  | 
    ||
1047  | 
    }  | 
    ||
1048  | 
    |||
1049  | 
    fclose(fp);  | 
    ||
1050  | 
    |||
1051  | 
    /* If the file is smaller, mark the entire thing as changed. */  | 
    ||
1052  | 
    if (sb.st_size < osize)  | 
    ||
1053  | 
    osize = 0;  | 
    ||
1054  | 
    |||
1055  | 
    /* Now see if current size is larger. */  | 
    ||
1056  | 
    	if (sb.st_size > osize) { | 
    ||
1057  | 
    rb = malloc(sb.st_size - osize);  | 
    ||
1058  | 
    if (rb == NULL)  | 
    ||
1059  | 
    err(1, NULL);  | 
    ||
1060  | 
    |||
1061  | 
    /* Open logfile, seek. */  | 
    ||
1062  | 
    fp = fopen(ent->log, "r");  | 
    ||
1063  | 
    		if (fp == NULL) { | 
    ||
1064  | 
    			warn("%s", ent->log); | 
    ||
1065  | 
    goto cleanup;  | 
    ||
1066  | 
    }  | 
    ||
1067  | 
    fseek(fp, osize, SEEK_SET);  | 
    ||
1068  | 
    rd = fread(rb, 1, sb.st_size - osize, fp);  | 
    ||
1069  | 
    		if (rd < 1) { | 
    ||
1070  | 
    			warn("fread"); | 
    ||
1071  | 
    fclose(fp);  | 
    ||
1072  | 
    goto cleanup;  | 
    ||
1073  | 
    }  | 
    ||
1074  | 
    fclose(fp);  | 
    ||
1075  | 
    |||
1076  | 
    /* Send message. */  | 
    ||
1077  | 
    fp = popen(SENDMAIL " -t", "w");  | 
    ||
1078  | 
    		if (fp == NULL) { | 
    ||
1079  | 
    			warn("popen"); | 
    ||
1080  | 
    goto cleanup;  | 
    ||
1081  | 
    }  | 
    ||
1082  | 
    fprintf(fp, "Auto-Submitted: auto-generated\n");  | 
    ||
1083  | 
    fprintf(fp, "To: %s\nSubject: LOGFILE NOTIFICATION: %s\n\n\n",  | 
    ||
1084  | 
    ent->whom, ent->log);  | 
    ||
1085  | 
    fwrite(rb, 1, rd, fp);  | 
    ||
1086  | 
    		fputs("\n\n", fp); | 
    ||
1087  | 
    |||
1088  | 
    pclose(fp);  | 
    ||
1089  | 
    }  | 
    ||
1090  | 
    update:  | 
    ||
1091  | 
    /* Reopen for writing and update file. */  | 
    ||
1092  | 
    fp = fopen(fname, "w");  | 
    ||
1093  | 
    	if (fp == NULL) { | 
    ||
1094  | 
    		warn("%s", fname); | 
    ||
1095  | 
    goto cleanup;  | 
    ||
1096  | 
    }  | 
    ||
1097  | 
    fprintf(fp, "%lld\n", (long long)sb.st_size);  | 
    ||
1098  | 
    fclose(fp);  | 
    ||
1099  | 
    |||
1100  | 
    cleanup:  | 
    ||
1101  | 
    free(flog);  | 
    ||
1102  | 
    free(rb);  | 
    ||
1103  | 
    return (1);  | 
    ||
1104  | 
    }  | 
    ||
1105  | 
    |||
1106  | 
    /* ARGSUSED */  | 
    ||
1107  | 
    void  | 
    ||
1108  | 
    child_killer(int signo)  | 
    ||
1109  | 
    { | 
    ||
1110  | 
    2  | 
    int save_errno = errno;  | 
    |
1111  | 
    1  | 
    int status;  | 
    |
1112  | 
    |||
1113  | 
    ✓✓ | 3  | 
    while (waitpid(-1, &status, WNOHANG) > 0)  | 
    
1114  | 
    ;  | 
    ||
1115  | 
    1  | 
    errno = save_errno;  | 
    |
1116  | 
    1  | 
    }  | 
    |
1117  | 
    |||
1118  | 
    int  | 
    ||
1119  | 
    stat_suffix(char *file, size_t size, char *suffix, struct stat *sp,  | 
    ||
1120  | 
    int (*func)(const char *, struct stat *))  | 
    ||
1121  | 
    { | 
    ||
1122  | 
    size_t n;  | 
    ||
1123  | 
    |||
1124  | 
    492  | 
    n = strlcat(file, suffix, size);  | 
    |
1125  | 
    ✓✗✓✓ | 
    492  | 
    if (n < size && func(file, sp) == 0)  | 
    
1126  | 
    49  | 
    return (0);  | 
    |
1127  | 
    197  | 
    file[n - strlen(suffix)] = '\0';  | 
    |
1128  | 
    197  | 
    return (-1);  | 
    |
1129  | 
    246  | 
    }  | 
    |
1130  | 
    |||
1131  | 
    /*  | 
    ||
1132  | 
    * lstat() a log, possibly appending a suffix; order is based on flags.  | 
    ||
1133  | 
    * Returns the suffix appended (may be empty string) or NULL if no file.  | 
    ||
1134  | 
    */  | 
    ||
1135  | 
    char *  | 
    ||
1136  | 
    lstat_log(char *file, size_t size, int flags)  | 
    ||
1137  | 
    { | 
    ||
1138  | 
    12  | 
    struct stat sb;  | 
    |
1139  | 
    |||
1140  | 
    ✓✗ | 6  | 
    	if (flags & CE_COMPACT) { | 
    
1141  | 
    ✓✓ | 6  | 
    if (stat_suffix(file, size, COMPRESS_POSTFIX, &sb, lstat) == 0)  | 
    
1142  | 
    1  | 
    return (COMPRESS_POSTFIX);  | 
    |
1143  | 
    ✗✓ | 5  | 
    if (lstat(file, &sb) == 0)  | 
    
1144  | 
    			return (""); | 
    ||
1145  | 
    	} else { | 
    ||
1146  | 
    if (lstat(file, &sb) == 0)  | 
    ||
1147  | 
    			return (""); | 
    ||
1148  | 
    if (stat_suffix(file, size, COMPRESS_POSTFIX, &sb, lstat) == 0)  | 
    ||
1149  | 
    return (COMPRESS_POSTFIX);  | 
    ||
1150  | 
    |||
1151  | 
    }  | 
    ||
1152  | 
    5  | 
    return (NULL);  | 
    |
1153  | 
    6  | 
    }  | 
    |
1154  | 
    |||
1155  | 
    /*  | 
    ||
1156  | 
    * Parse a limited subset of ISO 8601. The specific format is as follows:  | 
    ||
1157  | 
    *  | 
    ||
1158  | 
    * [CC[YY[MM[DD]]]][THH[MM[SS]]] (where `T' is the literal letter)  | 
    ||
1159  | 
    *  | 
    ||
1160  | 
    * We don't accept a timezone specification; missing fields (including timezone)  | 
    ||
1161  | 
    * are defaulted to the current date but time zero.  | 
    ||
1162  | 
    */  | 
    ||
1163  | 
    time_t  | 
    ||
1164  | 
    parse8601(char *s)  | 
    ||
1165  | 
    { | 
    ||
1166  | 
    struct tm tm, *tmp;  | 
    ||
1167  | 
    char *t;  | 
    ||
1168  | 
    long l;  | 
    ||
1169  | 
    |||
1170  | 
    tmp = localtime(&timenow);  | 
    ||
1171  | 
    tm = *tmp;  | 
    ||
1172  | 
    |||
1173  | 
    tm.tm_hour = tm.tm_min = tm.tm_sec = 0;  | 
    ||
1174  | 
    |||
1175  | 
    l = strtol(s, &t, 10);  | 
    ||
1176  | 
    if (l < 0 || l >= INT_MAX || (*t != '\0' && *t != 'T'))  | 
    ||
1177  | 
    return (-1);  | 
    ||
1178  | 
    |||
1179  | 
    /*  | 
    ||
1180  | 
    * Now t points either to the end of the string (if no time was  | 
    ||
1181  | 
    * provided) or to the letter `T' which separates date and time in  | 
    ||
1182  | 
    * ISO 8601. The pointer arithmetic is the same for either case.  | 
    ||
1183  | 
    */  | 
    ||
1184  | 
    	switch (t - s) { | 
    ||
1185  | 
    case 8:  | 
    ||
1186  | 
    tm.tm_year = ((l / 1000000) - 19) * 100;  | 
    ||
1187  | 
    l = l % 1000000;  | 
    ||
1188  | 
    case 6:  | 
    ||
1189  | 
    tm.tm_year -= tm.tm_year % 100;  | 
    ||
1190  | 
    tm.tm_year += l / 10000;  | 
    ||
1191  | 
    l = l % 10000;  | 
    ||
1192  | 
    case 4:  | 
    ||
1193  | 
    tm.tm_mon = (l / 100) - 1;  | 
    ||
1194  | 
    l = l % 100;  | 
    ||
1195  | 
    case 2:  | 
    ||
1196  | 
    tm.tm_mday = l;  | 
    ||
1197  | 
    case 0:  | 
    ||
1198  | 
    break;  | 
    ||
1199  | 
    default:  | 
    ||
1200  | 
    return (-1);  | 
    ||
1201  | 
    }  | 
    ||
1202  | 
    |||
1203  | 
    /* sanity check */  | 
    ||
1204  | 
    if (tm.tm_year < 70 || tm.tm_mon < 0 || tm.tm_mon > 12 ||  | 
    ||
1205  | 
    tm.tm_mday < 1 || tm.tm_mday > 31)  | 
    ||
1206  | 
    return (-1);  | 
    ||
1207  | 
    |||
1208  | 
    	if (*t != '\0') { | 
    ||
1209  | 
    s = ++t;  | 
    ||
1210  | 
    l = strtol(s, &t, 10);  | 
    ||
1211  | 
    if (l < 0 || l >= INT_MAX ||  | 
    ||
1212  | 
    (*t != '\0' && !isspace((unsigned char)*t)))  | 
    ||
1213  | 
    return (-1);  | 
    ||
1214  | 
    |||
1215  | 
    		switch (t - s) { | 
    ||
1216  | 
    case 6:  | 
    ||
1217  | 
    tm.tm_sec = l % 100;  | 
    ||
1218  | 
    l /= 100;  | 
    ||
1219  | 
    case 4:  | 
    ||
1220  | 
    tm.tm_min = l % 100;  | 
    ||
1221  | 
    l /= 100;  | 
    ||
1222  | 
    case 2:  | 
    ||
1223  | 
    tm.tm_hour = l;  | 
    ||
1224  | 
    case 0:  | 
    ||
1225  | 
    break;  | 
    ||
1226  | 
    default:  | 
    ||
1227  | 
    return (-1);  | 
    ||
1228  | 
    }  | 
    ||
1229  | 
    |||
1230  | 
    /* sanity check */  | 
    ||
1231  | 
    if (tm.tm_sec < 0 || tm.tm_sec > 60 || tm.tm_min < 0 ||  | 
    ||
1232  | 
    tm.tm_min > 59 || tm.tm_hour < 0 || tm.tm_hour > 23)  | 
    ||
1233  | 
    return (-1);  | 
    ||
1234  | 
    }  | 
    ||
1235  | 
    return (mktime(&tm));  | 
    ||
1236  | 
    }  | 
    ||
1237  | 
    |||
1238  | 
    /*-  | 
    ||
1239  | 
    * Parse a cyclic time specification, the format is as follows:  | 
    ||
1240  | 
    *  | 
    ||
1241  | 
    * [Dhh] or [Wd[Dhh]] or [Mdd[Dhh]]  | 
    ||
1242  | 
    *  | 
    ||
1243  | 
    * to rotate a logfile cyclic at  | 
    ||
1244  | 
    *  | 
    ||
1245  | 
    * - every day (D) within a specific hour (hh) (hh = 0...23)  | 
    ||
1246  | 
    * - once a week (W) at a specific day (d) OR (d = 0..6, 0 = Sunday)  | 
    ||
1247  | 
    * - once a month (M) at a specific day (d) (d = 1..31,l|L)  | 
    ||
1248  | 
    *  | 
    ||
1249  | 
    * We don't accept a timezone specification; missing fields  | 
    ||
1250  | 
    * are defaulted to the current date but time zero.  | 
    ||
1251  | 
    */  | 
    ||
1252  | 
    time_t  | 
    ||
1253  | 
    parseDWM(char *s)  | 
    ||
1254  | 
    { | 
    ||
1255  | 
    	static int mtab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; | 
    ||
1256  | 
    int WMseen = 0, Dseen = 0, nd;  | 
    ||
1257  | 
    96  | 
    struct tm tm, *tmp;  | 
    |
1258  | 
    48  | 
    char *t;  | 
    |
1259  | 
    long l;  | 
    ||
1260  | 
    |||
1261  | 
    48  | 
    tmp = localtime(&timenow);  | 
    |
1262  | 
    48  | 
    tm = *tmp;  | 
    |
1263  | 
    |||
1264  | 
    /* set no. of days per month */  | 
    ||
1265  | 
    |||
1266  | 
    48  | 
    nd = mtab[tm.tm_mon];  | 
    |
1267  | 
    |||
1268  | 
    ✗✓ | 48  | 
    	if (tm.tm_mon == 1) { | 
    
1269  | 
    if (((tm.tm_year + 1900) % 4 == 0) &&  | 
    ||
1270  | 
    ((tm.tm_year + 1900) % 100 != 0) &&  | 
    ||
1271  | 
    		    ((tm.tm_year + 1900) % 400 == 0)) { | 
    ||
1272  | 
    nd++; /* leap year, 29 days in february */  | 
    ||
1273  | 
    }  | 
    ||
1274  | 
    }  | 
    ||
1275  | 
    48  | 
    tm.tm_hour = tm.tm_min = tm.tm_sec = 0;  | 
    |
1276  | 
    |||
1277  | 
    72  | 
    	for (;;) { | 
    |
1278  | 
    ✓✓✗✗ | 
    72  | 
    		switch (*s) { | 
    
1279  | 
    case 'D':  | 
    ||
1280  | 
    ✗✓ | 24  | 
    if (Dseen)  | 
    
1281  | 
    return (-1);  | 
    ||
1282  | 
    24  | 
    Dseen++;  | 
    |
1283  | 
    24  | 
    s++;  | 
    |
1284  | 
    24  | 
    l = strtol(s, &t, 10);  | 
    |
1285  | 
    ✗✓ | 24  | 
    if (l < 0 || l > 23)  | 
    
1286  | 
    return (-1);  | 
    ||
1287  | 
    24  | 
    tm.tm_hour = l;  | 
    |
1288  | 
    24  | 
    break;  | 
    |
1289  | 
    |||
1290  | 
    case 'W':  | 
    ||
1291  | 
    ✗✓ | 48  | 
    if (WMseen)  | 
    
1292  | 
    return (-1);  | 
    ||
1293  | 
    48  | 
    WMseen++;  | 
    |
1294  | 
    48  | 
    s++;  | 
    |
1295  | 
    48  | 
    l = strtol(s, &t, 10);  | 
    |
1296  | 
    ✗✓ | 48  | 
    if (l < 0 || l > 6)  | 
    
1297  | 
    return (-1);  | 
    ||
1298  | 
    ✓✗ | 48  | 
    			if (l != tm.tm_wday) { | 
    
1299  | 
    int save;  | 
    ||
1300  | 
    |||
1301  | 
    ✓✓ | 48  | 
    				if (l < tm.tm_wday) { | 
    
1302  | 
    24  | 
    save = 6 - tm.tm_wday;  | 
    |
1303  | 
    24  | 
    save += (l + 1);  | 
    |
1304  | 
    24  | 
    				} else { | 
    |
1305  | 
    24  | 
    save = l - tm.tm_wday;  | 
    |
1306  | 
    }  | 
    ||
1307  | 
    |||
1308  | 
    48  | 
    tm.tm_mday += save;  | 
    |
1309  | 
    |||
1310  | 
    ✗✓ | 48  | 
    				if (tm.tm_mday > nd) { | 
    
1311  | 
    tm.tm_mon++;  | 
    ||
1312  | 
    tm.tm_mday = tm.tm_mday - nd;  | 
    ||
1313  | 
    }  | 
    ||
1314  | 
    48  | 
    }  | 
    |
1315  | 
    break;  | 
    ||
1316  | 
    |||
1317  | 
    case 'M':  | 
    ||
1318  | 
    if (WMseen)  | 
    ||
1319  | 
    return (-1);  | 
    ||
1320  | 
    WMseen++;  | 
    ||
1321  | 
    s++;  | 
    ||
1322  | 
    			if (tolower((unsigned char)*s) == 'l') { | 
    ||
1323  | 
    tm.tm_mday = nd;  | 
    ||
1324  | 
    s++;  | 
    ||
1325  | 
    t = s;  | 
    ||
1326  | 
    			} else { | 
    ||
1327  | 
    l = strtol(s, &t, 10);  | 
    ||
1328  | 
    if (l < 1 || l > 31)  | 
    ||
1329  | 
    return (-1);  | 
    ||
1330  | 
    |||
1331  | 
    if (l > nd)  | 
    ||
1332  | 
    return (-1);  | 
    ||
1333  | 
    if (l < tm.tm_mday)  | 
    ||
1334  | 
    tm.tm_mon++;  | 
    ||
1335  | 
    tm.tm_mday = l;  | 
    ||
1336  | 
    }  | 
    ||
1337  | 
    break;  | 
    ||
1338  | 
    |||
1339  | 
    default:  | 
    ||
1340  | 
    return (-1);  | 
    ||
1341  | 
    break;  | 
    ||
1342  | 
    }  | 
    ||
1343  | 
    |||
1344  | 
    ✓✓✓✗ | 
    96  | 
    if (*t == '\0' || isspace((unsigned char)*t))  | 
    
1345  | 
    break;  | 
    ||
1346  | 
    else  | 
    ||
1347  | 
    s = t;  | 
    ||
1348  | 
    }  | 
    ||
1349  | 
    48  | 
    return (mktime(&tm));  | 
    |
1350  | 
    48  | 
    }  | 
    |
1351  | 
    |||
1352  | 
    /*  | 
    ||
1353  | 
    * Move a file using rename(2) if possible and copying if not.  | 
    ||
1354  | 
    */  | 
    ||
1355  | 
    int  | 
    ||
1356  | 
    movefile(char *from, char *to, uid_t owner_uid, gid_t group_gid, mode_t perm)  | 
    ||
1357  | 
    { | 
    ||
1358  | 
    FILE *src, *dst;  | 
    ||
1359  | 
    int i;  | 
    ||
1360  | 
    |||
1361  | 
    /* try rename(2) first */  | 
    ||
1362  | 
    ✓✗ | 2  | 
    	if (rename(from, to) == 0) { | 
    
1363  | 
    ✗✓ | 1  | 
    if (chmod(to, perm))  | 
    
1364  | 
    			warn("can't chmod %s", to); | 
    ||
1365  | 
    ✗✓ | 1  | 
    if (chown(to, owner_uid, group_gid))  | 
    
1366  | 
    			warn("can't chown %s", to); | 
    ||
1367  | 
    1  | 
    return (0);  | 
    |
1368  | 
    } else if (errno != EXDEV)  | 
    ||
1369  | 
    return (-1);  | 
    ||
1370  | 
    |||
1371  | 
    /* different filesystem, have to copy the file */  | 
    ||
1372  | 
    if ((src = fopen(from, "r")) == NULL)  | 
    ||
1373  | 
    err(1, "can't fopen %s for reading", from);  | 
    ||
1374  | 
    if ((dst = fopen(to, "w")) == NULL)  | 
    ||
1375  | 
    err(1, "can't fopen %s for writing", to);  | 
    ||
1376  | 
    if (fchmod(fileno(dst), perm))  | 
    ||
1377  | 
    err(1, "can't fchmod %s", to);  | 
    ||
1378  | 
    if (fchown(fileno(dst), owner_uid, group_gid))  | 
    ||
1379  | 
    err(1, "can't fchown %s", to);  | 
    ||
1380  | 
    |||
1381  | 
    	while ((i = getc(src)) != EOF) { | 
    ||
1382  | 
    if ((putc(i, dst)) == EOF)  | 
    ||
1383  | 
    err(1, "error writing to %s", to);  | 
    ||
1384  | 
    }  | 
    ||
1385  | 
    |||
1386  | 
    if (ferror(src))  | 
    ||
1387  | 
    err(1, "error reading from %s", from);  | 
    ||
1388  | 
    if ((fclose(src)) != 0)  | 
    ||
1389  | 
    err(1, "can't fclose %s", from);  | 
    ||
1390  | 
    if ((fclose(dst)) != 0)  | 
    ||
1391  | 
    err(1, "can't fclose %s", to);  | 
    ||
1392  | 
    if ((unlink(from)) != 0)  | 
    ||
1393  | 
    err(1, "can't unlink %s", from);  | 
    ||
1394  | 
    |||
1395  | 
    return (0);  | 
    ||
1396  | 
    1  | 
    }  | 
    
| Generated by: GCOVR (Version 3.3) |