| File: | src/usr.bin/newsyslog/newsyslog.c |
| Warning: | line 767, column 8 Potential leak of memory pointed to by 'working' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* $OpenBSD: newsyslog.c,v 1.112 2019/06/28 13:35:02 deraadt Exp $ */ | |||
| 2 | ||||
| 3 | /* | |||
| 4 | * Copyright (c) 1999, 2002, 2003 Todd C. Miller <millert@openbsd.org> | |||
| 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" "/etc/newsyslog.conf" | |||
| 75 | #define PIDFILE"/var/run/syslog.pid" "/var/run/syslog.pid" | |||
| 76 | #define COMPRESS"/usr/bin/gzip" "/usr/bin/gzip" | |||
| 77 | #define COMPRESS_POSTFIX".gz" ".gz" | |||
| 78 | #define STATS_DIR"/var/run" "/var/run" | |||
| 79 | #define SENDMAIL"/usr/sbin/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(*__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_ROTATED0x01 0x01 /* Log file has been rotated */ | |||
| 102 | #define CE_COMPACT0x02 0x02 /* Compact the archived log files */ | |||
| 103 | #define CE_BINARY0x04 0x04 /* Logfile is in binary, don't add */ | |||
| 104 | /* status messages */ | |||
| 105 | #define CE_MONITOR0x08 0x08 /* Monitor for changes */ | |||
| 106 | #define CE_FOLLOW0x10 0x10 /* Follow symbolic links */ | |||
| 107 | #define CE_TRIMAT0x20 0x20 /* Trim at a specific time */ | |||
| 108 | ||||
| 109 | #define MIN_PID2 2 /* Don't touch pids lower than this */ | |||
| 110 | #define MIN_SIZE256 256 /* Don't rotate if smaller (in bytes) */ | |||
| 111 | ||||
| 112 | #define DPRINTF(x)do { if (verbose) printf x ; } while (0) 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)struct { struct conf_entry *tqe_next; struct conf_entry **tqe_prev ; } next; | |||
| 131 | }; | |||
| 132 | TAILQ_HEAD(entrylist, conf_entry)struct entrylist { struct conf_entry *tqh_first; struct conf_entry **tqh_last; }; | |||
| 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"/etc/newsyslog.conf"; /* Configuration file to use */ | |||
| 145 | time_t timenow; | |||
| 146 | char hostname[HOST_NAME_MAX255+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 | struct entrylist config, runlist; | |||
| 179 | struct conf_entry *p, *q, *tmp; | |||
| 180 | struct pidinfo *pidlist, *pl; | |||
| 181 | int status, listlen, ret; | |||
| 182 | char **av; | |||
| 183 | ||||
| 184 | parse_args(argc, argv); | |||
| 185 | argc -= optind; | |||
| 186 | argv += optind; | |||
| 187 | ||||
| 188 | if (needroot && getuid() && geteuid()) | |||
| 189 | errx(1, "You must be root."); | |||
| 190 | ||||
| 191 | TAILQ_INIT(&config)do { (&config)->tqh_first = ((void *)0); (&config) ->tqh_last = &(&config)->tqh_first; } while (0); | |||
| 192 | TAILQ_INIT(&runlist)do { (&runlist)->tqh_first = ((void *)0); (&runlist )->tqh_last = &(&runlist)->tqh_first; } while ( 0); | |||
| 193 | ||||
| 194 | /* Keep passwd and group files open for faster lookups. */ | |||
| 195 | setpassent(1); | |||
| 196 | setgroupent(1); | |||
| 197 | ||||
| 198 | ret = parse_file(&config, &listlen); | |||
| 199 | if (argc == 0) | |||
| 200 | TAILQ_CONCAT(&runlist, &config, next)do { if (!(((&config)->tqh_first) == ((void *)0))) { * (&runlist)->tqh_last = (&config)->tqh_first; (& config)->tqh_first->next.tqe_prev = (&runlist)-> tqh_last; (&runlist)->tqh_last = (&config)->tqh_last ; do { ((&config))->tqh_first = ((void *)0); ((&config ))->tqh_last = &((&config))->tqh_first; } while (0); } } while (0); | |||
| 201 | else { | |||
| 202 | /* Only rotate specified files. */ | |||
| 203 | listlen = 0; | |||
| 204 | for (av = argv; *av; av++) { | |||
| 205 | TAILQ_FOREACH_SAFE(q, &config, next, tmp)for ((q) = ((&config)->tqh_first); (q) != ((void *)0) && ((tmp) = ((q)->next.tqe_next), 1); (q) = (tmp)) | |||
| 206 | if (strcmp(*av, q->log) == 0) { | |||
| 207 | TAILQ_REMOVE(&config, q, next)do { if (((q)->next.tqe_next) != ((void *)0)) (q)->next .tqe_next->next.tqe_prev = (q)->next.tqe_prev; else (& config)->tqh_last = (q)->next.tqe_prev; *(q)->next.tqe_prev = (q)->next.tqe_next; ; ; } while (0); | |||
| 208 | TAILQ_INSERT_TAIL(&runlist, q, next)do { (q)->next.tqe_next = ((void *)0); (q)->next.tqe_prev = (&runlist)->tqh_last; *(&runlist)->tqh_last = (q); (&runlist)->tqh_last = &(q)->next.tqe_next ; } while (0); | |||
| 209 | listlen++; | |||
| 210 | break; | |||
| 211 | } | |||
| 212 | if (q == NULL((void *)0)) | |||
| 213 | warnx("%s: %s not found", conf, *av); | |||
| 214 | } | |||
| 215 | if (TAILQ_EMPTY(&runlist)(((&runlist)->tqh_first) == ((void *)0))) | |||
| 216 | errx(1, "%s: no specified log files", conf); | |||
| 217 | } | |||
| 218 | ||||
| 219 | pidlist = calloc(listlen + 1, sizeof(struct pidinfo)); | |||
| 220 | if (pidlist == NULL((void *)0)) | |||
| 221 | err(1, NULL((void *)0)); | |||
| 222 | ||||
| 223 | signal(SIGCHLD20, child_killer); | |||
| 224 | ||||
| 225 | /* Step 1, rotate all log files */ | |||
| 226 | TAILQ_FOREACH(q, &runlist, next)for((q) = ((&runlist)->tqh_first); (q) != ((void *)0); (q) = ((q)->next.tqe_next)) | |||
| 227 | do_entry(q); | |||
| 228 | ||||
| 229 | /* Step 2, make a list of unique pid files */ | |||
| 230 | pl = pidlist; | |||
| 231 | TAILQ_FOREACH(q, &runlist, next)for((q) = ((&runlist)->tqh_first); (q) != ((void *)0); (q) = ((q)->next.tqe_next)) { | |||
| 232 | if (q->flags & CE_ROTATED0x01) { | |||
| 233 | struct pidinfo *pltmp; | |||
| 234 | ||||
| 235 | for (pltmp = pidlist; pltmp < pl; pltmp++) { | |||
| 236 | if ((q->pidfile && pltmp->file && | |||
| 237 | strcmp(pltmp->file, q->pidfile) == 0 && | |||
| 238 | pltmp->signal == q->signal) || | |||
| 239 | (q->runcmd && pltmp->file && | |||
| 240 | strcmp(q->runcmd, pltmp->file) == 0)) | |||
| 241 | break; | |||
| 242 | } | |||
| 243 | if (pltmp == pl) { /* unique entry */ | |||
| 244 | if (q->runcmd) { | |||
| 245 | pl->file = q->runcmd; | |||
| 246 | pl->signal = -1; | |||
| 247 | } else { | |||
| 248 | pl->file = q->pidfile; | |||
| 249 | pl->signal = q->signal; | |||
| 250 | } | |||
| 251 | pl++; | |||
| 252 | } | |||
| 253 | } | |||
| 254 | } | |||
| 255 | ||||
| 256 | /* Step 3, send a signal or run a command */ | |||
| 257 | for (pl--; pl >= pidlist; pl--) { | |||
| 258 | if (pl->file != NULL((void *)0)) { | |||
| 259 | if (pl->signal == -1) | |||
| 260 | run_command(pl->file); | |||
| 261 | else | |||
| 262 | send_signal(pl->file, pl->signal); | |||
| 263 | } | |||
| 264 | } | |||
| 265 | if (!noaction) | |||
| 266 | sleep(5); | |||
| 267 | ||||
| 268 | /* Step 4, compress the log.0 file if configured to do so and free */ | |||
| 269 | TAILQ_FOREACH(p, &runlist, next)for((p) = ((&runlist)->tqh_first); (p) != ((void *)0); (p) = ((p)->next.tqe_next)) { | |||
| 270 | if ((p->flags & CE_COMPACT0x02) && (p->flags & CE_ROTATED0x01) && | |||
| 271 | p->numlogs > 0) | |||
| 272 | compress_log(p); | |||
| 273 | } | |||
| 274 | ||||
| 275 | /* Wait for children to finish, then exit */ | |||
| 276 | while (waitpid(-1, &status, 0) != -1) | |||
| 277 | ; | |||
| 278 | return (ret); | |||
| 279 | } | |||
| 280 | ||||
| 281 | void | |||
| 282 | do_entry(struct conf_entry *ent) | |||
| 283 | { | |||
| 284 | struct stat sb; | |||
| 285 | int modhours; | |||
| 286 | off_t size; | |||
| 287 | ||||
| 288 | if (lstat(ent->log, &sb) != 0) | |||
| 289 | return; | |||
| 290 | if (!S_ISREG(sb.st_mode)((sb.st_mode & 0170000) == 0100000) && | |||
| 291 | (!S_ISLNK(sb.st_mode)((sb.st_mode & 0170000) == 0120000) || !(ent->flags & CE_FOLLOW0x10))) { | |||
| 292 | DPRINTF(("--> not a regular file, skipping\n"))do { if (verbose) printf ("--> not a regular file, skipping\n" ) ; } while (0); | |||
| 293 | return; | |||
| 294 | } | |||
| 295 | if (S_ISLNK(sb.st_mode)((sb.st_mode & 0170000) == 0120000) && stat(ent->log, &sb) != 0) { | |||
| 296 | DPRINTF(("--> link target does not exist, skipping\n"))do { if (verbose) printf ("--> link target does not exist, skipping\n" ) ; } while (0); | |||
| 297 | return; | |||
| 298 | } | |||
| 299 | if (ent->uid == (uid_t)-1) | |||
| 300 | ent->uid = sb.st_uid; | |||
| 301 | if (ent->gid == (gid_t)-1) | |||
| 302 | ent->gid = sb.st_gid; | |||
| 303 | ||||
| 304 | DPRINTF(("%s <%d%s%s%s%s>: ", ent->log, ent->numlogs,do { if (verbose) printf ("%s <%d%s%s%s%s>: ", ent-> log, ent->numlogs, (ent->flags & 0x02) ? "Z" : "", ( ent->flags & 0x04) ? "B" : "", (ent->flags & 0x10 ) ? "F" : "", (ent->flags & 0x08) && monitormode ? "M" : "") ; } while (0) | |||
| 305 | (ent->flags & CE_COMPACT) ? "Z" : "",do { if (verbose) printf ("%s <%d%s%s%s%s>: ", ent-> log, ent->numlogs, (ent->flags & 0x02) ? "Z" : "", ( ent->flags & 0x04) ? "B" : "", (ent->flags & 0x10 ) ? "F" : "", (ent->flags & 0x08) && monitormode ? "M" : "") ; } while (0) | |||
| 306 | (ent->flags & CE_BINARY) ? "B" : "",do { if (verbose) printf ("%s <%d%s%s%s%s>: ", ent-> log, ent->numlogs, (ent->flags & 0x02) ? "Z" : "", ( ent->flags & 0x04) ? "B" : "", (ent->flags & 0x10 ) ? "F" : "", (ent->flags & 0x08) && monitormode ? "M" : "") ; } while (0) | |||
| 307 | (ent->flags & CE_FOLLOW) ? "F" : "",do { if (verbose) printf ("%s <%d%s%s%s%s>: ", ent-> log, ent->numlogs, (ent->flags & 0x02) ? "Z" : "", ( ent->flags & 0x04) ? "B" : "", (ent->flags & 0x10 ) ? "F" : "", (ent->flags & 0x08) && monitormode ? "M" : "") ; } while (0) | |||
| 308 | (ent->flags & CE_MONITOR) && monitormode ? "M" : ""))do { if (verbose) printf ("%s <%d%s%s%s%s>: ", ent-> log, ent->numlogs, (ent->flags & 0x02) ? "Z" : "", ( ent->flags & 0x04) ? "B" : "", (ent->flags & 0x10 ) ? "F" : "", (ent->flags & 0x08) && monitormode ? "M" : "") ; } while (0); | |||
| 309 | size = sizefile(&sb); | |||
| 310 | modhours = age_old_log(ent); | |||
| 311 | if (ent->flags & CE_TRIMAT0x20 && !force) { | |||
| 312 | if (timenow < ent->trim_at || | |||
| 313 | difftime(timenow, ent->trim_at) >= 60 * 60) { | |||
| 314 | DPRINTF(("--> will trim at %s",do { if (verbose) printf ("--> will trim at %s", ctime(& ent->trim_at)) ; } while (0) | |||
| 315 | ctime(&ent->trim_at)))do { if (verbose) printf ("--> will trim at %s", ctime(& ent->trim_at)) ; } while (0); | |||
| 316 | return; | |||
| 317 | } else if (ent->hours <= 0) { | |||
| 318 | DPRINTF(("--> time is up\n"))do { if (verbose) printf ("--> time is up\n") ; } while (0 ); | |||
| 319 | } | |||
| 320 | } | |||
| 321 | if (ent->size > 0) | |||
| 322 | DPRINTF(("size (KB): %.2f [%d] ", size / 1024.0,do { if (verbose) printf ("size (KB): %.2f [%d] ", size / 1024.0 , (int)(ent->size / 1024)) ; } while (0) | |||
| 323 | (int)(ent->size / 1024)))do { if (verbose) printf ("size (KB): %.2f [%d] ", size / 1024.0 , (int)(ent->size / 1024)) ; } while (0); | |||
| 324 | if (ent->hours > 0) | |||
| 325 | DPRINTF(("age (hr): %d [%d] ", modhours, ent->hours))do { if (verbose) printf ("age (hr): %d [%d] ", modhours, ent ->hours) ; } while (0); | |||
| 326 | if (monitormode && (ent->flags & CE_MONITOR0x08) && domonitor(ent)) | |||
| 327 | DPRINTF(("--> monitored\n"))do { if (verbose) printf ("--> monitored\n") ; } while (0); | |||
| 328 | else if (!monitormode && | |||
| 329 | (force || (ent->size > 0 && size >= ent->size) || | |||
| 330 | (ent->hours <= 0 && (ent->flags & CE_TRIMAT0x20)) || | |||
| 331 | (ent->hours > 0 && (modhours >= ent->hours || modhours < 0) | |||
| 332 | && ((ent->flags & CE_BINARY0x04) || size >= MIN_SIZE256)))) { | |||
| 333 | DPRINTF(("--> trimming log....\n"))do { if (verbose) printf ("--> trimming log....\n") ; } while (0); | |||
| 334 | if (noaction && !verbose) | |||
| 335 | printf("%s <%d%s%s%s>\n", ent->log, | |||
| 336 | ent->numlogs, | |||
| 337 | (ent->flags & CE_COMPACT0x02) ? "Z" : "", | |||
| 338 | (ent->flags & CE_BINARY0x04) ? "B" : "", | |||
| 339 | (ent->flags & CE_FOLLOW0x10) ? "F" : ""); | |||
| 340 | dotrim(ent); | |||
| 341 | ent->flags |= CE_ROTATED0x01; | |||
| 342 | } else | |||
| 343 | DPRINTF(("--> skipping\n"))do { if (verbose) printf ("--> skipping\n") ; } while (0); | |||
| 344 | } | |||
| 345 | ||||
| 346 | /* Run the specified command */ | |||
| 347 | void | |||
| 348 | run_command(char *cmd) | |||
| 349 | { | |||
| 350 | if (noaction) | |||
| 351 | (void)printf("run %s\n", cmd); | |||
| 352 | else | |||
| 353 | system(cmd); | |||
| 354 | } | |||
| 355 | ||||
| 356 | /* Send a signal to the pid specified by pidfile */ | |||
| 357 | void | |||
| 358 | send_signal(char *pidfile, int signal) | |||
| 359 | { | |||
| 360 | char line[BUFSIZ1024], *ep, *err; | |||
| 361 | pid_t pid; | |||
| 362 | long lval; | |||
| 363 | FILE *f; | |||
| 364 | ||||
| 365 | if ((f = fopen(pidfile, "r")) == NULL((void *)0)) { | |||
| 366 | warn("can't open %s", pidfile); | |||
| 367 | return; | |||
| 368 | } | |||
| 369 | ||||
| 370 | pid = 0; | |||
| 371 | errno(*__errno()) = 0; | |||
| 372 | err = NULL((void *)0); | |||
| 373 | if (fgets(line, sizeof(line), f)) { | |||
| 374 | lval = strtol(line, &ep, 10); | |||
| 375 | if (line[0] == '\0' || (*ep != '\0' && *ep != '\n')) | |||
| 376 | err = "invalid number in"; | |||
| 377 | else if (lval < 0 || (errno(*__errno()) == ERANGE34 && lval == LONG_MAX9223372036854775807L)) | |||
| 378 | err = "out of range number in"; | |||
| 379 | else if (lval == 0) | |||
| 380 | err = "no number in"; | |||
| 381 | else if (lval < MIN_PID2) | |||
| 382 | err = "preposterous process number in"; | |||
| 383 | else | |||
| 384 | pid = (pid_t)lval; | |||
| 385 | } else { | |||
| 386 | if (errno(*__errno()) == 0) | |||
| 387 | err = "empty"; | |||
| 388 | else | |||
| 389 | err = "error reading"; | |||
| 390 | } | |||
| 391 | (void)fclose(f); | |||
| 392 | ||||
| 393 | if (err) | |||
| 394 | warnx("%s pid file: %s", err, pidfile); | |||
| 395 | else if (noaction) | |||
| 396 | (void)printf("kill -%s %ld\n", sys_signame[signal], (long)pid); | |||
| 397 | else if (kill(pid, signal)) | |||
| 398 | warnx("warning - could not send SIG%s to PID from pid file %s", | |||
| 399 | sys_signame[signal], pidfile); | |||
| 400 | } | |||
| 401 | ||||
| 402 | void | |||
| 403 | parse_args(int argc, char **argv) | |||
| 404 | { | |||
| 405 | struct timeval now; | |||
| 406 | struct tm *tm; | |||
| 407 | size_t l; | |||
| 408 | char *p; | |||
| 409 | int ch; | |||
| 410 | ||||
| 411 | gettimeofday(&now, NULL((void *)0)); | |||
| 412 | timenow = now.tv_sec; | |||
| 413 | tm = gmtime(&now.tv_sec); | |||
| 414 | l = strftime(daytime, sizeof(daytime), "%FT%T", tm); | |||
| 415 | snprintf(daytime + l, sizeof(daytime) - l, ".%03ldZ", | |||
| 416 | now.tv_usec / 1000); | |||
| 417 | ||||
| 418 | /* Let's get our hostname */ | |||
| 419 | (void)gethostname(hostname, sizeof(hostname)); | |||
| 420 | ||||
| 421 | /* Truncate domain */ | |||
| 422 | if ((p = strchr(hostname, '.')) != NULL((void *)0)) | |||
| 423 | *p = '\0'; | |||
| 424 | ||||
| 425 | while ((ch = getopt(argc, argv, "Fmnrva:f:")) != -1) { | |||
| 426 | switch (ch) { | |||
| 427 | case 'a': | |||
| 428 | arcdir = optarg; | |||
| 429 | break; | |||
| 430 | case 'n': | |||
| 431 | noaction = 1; /* This implies needroot as off */ | |||
| 432 | /* fall through */ | |||
| 433 | case 'r': | |||
| 434 | needroot = 0; | |||
| 435 | break; | |||
| 436 | case 'v': | |||
| 437 | verbose = 1; | |||
| 438 | break; | |||
| 439 | case 'f': | |||
| 440 | conf = optarg; | |||
| 441 | break; | |||
| 442 | case 'm': | |||
| 443 | monitormode = 1; | |||
| 444 | break; | |||
| 445 | case 'F': | |||
| 446 | force = 1; | |||
| 447 | break; | |||
| 448 | default: | |||
| 449 | usage(); | |||
| 450 | } | |||
| 451 | } | |||
| 452 | if (monitormode && force) | |||
| 453 | errx(1, "cannot specify both -m and -F flags"); | |||
| 454 | } | |||
| 455 | ||||
| 456 | void | |||
| 457 | usage(void) | |||
| 458 | { | |||
| 459 | extern const char *__progname; | |||
| 460 | ||||
| 461 | (void)fprintf(stderr(&__sF[2]), "usage: %s [-Fmnrv] [-a directory] " | |||
| 462 | "[-f config_file] [log ...]\n", __progname); | |||
| 463 | exit(1); | |||
| 464 | } | |||
| 465 | ||||
| 466 | /* | |||
| 467 | * Parse a configuration file and build a linked list of all the logs | |||
| 468 | * to process | |||
| 469 | */ | |||
| 470 | int | |||
| 471 | parse_file(struct entrylist *list, int *nentries) | |||
| 472 | { | |||
| 473 | char line[BUFSIZ1024], *parse, *q, *errline, *group, *tmp, *ep; | |||
| 474 | struct conf_entry *working; | |||
| 475 | struct stat sb; | |||
| 476 | int lineno = 0; | |||
| 477 | int ret = 0; | |||
| 478 | FILE *f; | |||
| 479 | long l; | |||
| 480 | ||||
| 481 | if (strcmp(conf, "-") == 0) | |||
| ||||
| 482 | f = stdin(&__sF[0]); | |||
| 483 | else if ((f = fopen(conf, "r")) == NULL((void *)0)) | |||
| 484 | err(1, "can't open %s", conf); | |||
| 485 | ||||
| 486 | *nentries = 0; | |||
| 487 | ||||
| 488 | nextline: | |||
| 489 | while (fgets(line, sizeof(line), f) != NULL((void *)0)) { | |||
| 490 | lineno++; | |||
| 491 | tmp = sob(line); | |||
| 492 | if (*tmp == '\0' || *tmp == '#') | |||
| 493 | continue; | |||
| 494 | errline = strdup(tmp); | |||
| 495 | if (errline == NULL((void *)0)) | |||
| 496 | err(1, NULL((void *)0)); | |||
| 497 | working = calloc(1, sizeof(*working)); | |||
| 498 | if (working == NULL((void *)0)) | |||
| 499 | err(1, NULL((void *)0)); | |||
| 500 | ||||
| 501 | q = parse = missing_field(sob(line), errline, lineno); | |||
| 502 | *(parse = son(line)) = '\0'; | |||
| 503 | working->log = strdup(q); | |||
| 504 | if (working->log == NULL((void *)0)) | |||
| 505 | err(1, NULL((void *)0)); | |||
| 506 | ||||
| 507 | if ((working->logbase = strrchr(working->log, '/')) != NULL((void *)0)) | |||
| 508 | working->logbase++; | |||
| 509 | ||||
| 510 | q = parse = missing_field(sob(++parse), errline, lineno); | |||
| 511 | *(parse = son(parse)) = '\0'; | |||
| 512 | if ((group = strchr(q, ':')) != NULL((void *)0) || | |||
| 513 | (group = strrchr(q, '.')) != NULL((void *)0)) { | |||
| 514 | *group++ = '\0'; | |||
| 515 | if (*q == '\0') { | |||
| 516 | working->uid = (uid_t)-1; | |||
| 517 | } else if (isnumberstr(q)) { | |||
| 518 | working->uid = atoi(q); | |||
| 519 | } else if (uid_from_user(q, &working->uid) == -1) { | |||
| 520 | warnx("%s:%d: unknown user %s --> skipping", | |||
| 521 | conf, lineno, q); | |||
| 522 | ret = 1; | |||
| 523 | goto nextline; | |||
| 524 | } | |||
| 525 | ||||
| 526 | q = group; | |||
| 527 | if (*q == '\0') { | |||
| 528 | working->gid = (gid_t)-1; | |||
| 529 | } else if (isnumberstr(q)) { | |||
| 530 | working->gid = atoi(q); | |||
| 531 | } else if (gid_from_group(q, &working->gid) == -1) { | |||
| 532 | warnx("%s:%d: unknown group %s --> skipping", | |||
| 533 | conf, lineno, q); | |||
| 534 | ret = 1; | |||
| 535 | goto nextline; | |||
| 536 | } | |||
| 537 | ||||
| 538 | q = parse = missing_field(sob(++parse), errline, lineno); | |||
| 539 | *(parse = son(parse)) = '\0'; | |||
| 540 | } else { | |||
| 541 | working->uid = (uid_t)-1; | |||
| 542 | working->gid = (gid_t)-1; | |||
| 543 | } | |||
| 544 | ||||
| 545 | l = strtol(q, &ep, 8); | |||
| 546 | if (*ep != '\0' || l < 0 || l > ALLPERMS(0004000|0002000|0001000|0000700|0000070|0000007)) { | |||
| 547 | warnx("%s:%d: bad permissions: %s --> skipping", conf, | |||
| 548 | lineno, q); | |||
| 549 | ret = 1; | |||
| 550 | goto nextline; | |||
| 551 | } | |||
| 552 | working->permissions = (mode_t)l; | |||
| 553 | ||||
| 554 | q = parse = missing_field(sob(++parse), errline, lineno); | |||
| 555 | *(parse = son(parse)) = '\0'; | |||
| 556 | l = strtol(q, &ep, 10); | |||
| 557 | if (*ep != '\0' || l < 0 || l >= INT_MAX2147483647) { | |||
| 558 | warnx("%s:%d: bad number: %s --> skipping", conf, | |||
| 559 | lineno, q); | |||
| 560 | ret = 1; | |||
| 561 | goto nextline; | |||
| 562 | } | |||
| 563 | working->numlogs = (int)l; | |||
| 564 | ||||
| 565 | q = parse = missing_field(sob(++parse), errline, lineno); | |||
| 566 | *(parse = son(parse)) = '\0'; | |||
| 567 | if (isdigit((unsigned char)*q)) | |||
| 568 | working->size = atoi(q) * 1024; | |||
| 569 | else | |||
| 570 | working->size = -1; | |||
| 571 | ||||
| 572 | working->flags = 0; | |||
| 573 | q = parse = missing_field(sob(++parse), errline, lineno); | |||
| 574 | *(parse = son(parse)) = '\0'; | |||
| 575 | l = strtol(q, &ep, 10); | |||
| 576 | if (l < 0 || l >= INT_MAX2147483647) { | |||
| 577 | warnx("%s:%d: interval out of range: %s --> skipping", | |||
| 578 | conf, lineno, q); | |||
| 579 | ret = 1; | |||
| 580 | goto nextline; | |||
| 581 | } | |||
| 582 | working->hours = (int)l; | |||
| 583 | switch (*ep) { | |||
| 584 | case '\0': | |||
| 585 | break; | |||
| 586 | case '@': | |||
| 587 | working->trim_at = parse8601(ep + 1); | |||
| 588 | if (working->trim_at == (time_t) - 1) { | |||
| 589 | warnx("%s:%d: bad time: %s --> skipping", conf, | |||
| 590 | lineno, q); | |||
| 591 | ret = 1; | |||
| 592 | goto nextline; | |||
| 593 | } | |||
| 594 | working->flags |= CE_TRIMAT0x20; | |||
| 595 | break; | |||
| 596 | case '$': | |||
| 597 | working->trim_at = parseDWM(ep + 1); | |||
| 598 | if (working->trim_at == (time_t) - 1) { | |||
| 599 | warnx("%s:%d: bad time: %s --> skipping", conf, | |||
| 600 | lineno, q); | |||
| 601 | ret = 1; | |||
| 602 | goto nextline; | |||
| 603 | } | |||
| 604 | working->flags |= CE_TRIMAT0x20; | |||
| 605 | break; | |||
| 606 | case '*': | |||
| 607 | if (q == ep) | |||
| 608 | break; | |||
| 609 | /* FALLTHROUGH */ | |||
| 610 | default: | |||
| 611 | warnx("%s:%d: bad interval/at: %s --> skipping", conf, | |||
| 612 | lineno, q); | |||
| 613 | ret = 1; | |||
| 614 | goto nextline; | |||
| 615 | } | |||
| 616 | ||||
| 617 | q = sob(++parse); /* Optional field */ | |||
| 618 | if (*q == 'Z' || *q == 'z' || *q == 'B' || *q == 'b' || | |||
| 619 | *q == 'M' || *q == 'm') { | |||
| 620 | *(parse = son(q)) = '\0'; | |||
| 621 | while (*q) { | |||
| 622 | switch (*q) { | |||
| 623 | case 'Z': | |||
| 624 | case 'z': | |||
| 625 | working->flags |= CE_COMPACT0x02; | |||
| 626 | break; | |||
| 627 | case 'B': | |||
| 628 | case 'b': | |||
| 629 | working->flags |= CE_BINARY0x04; | |||
| 630 | break; | |||
| 631 | case 'M': | |||
| 632 | case 'm': | |||
| 633 | working->flags |= CE_MONITOR0x08; | |||
| 634 | break; | |||
| 635 | case 'F': | |||
| 636 | case 'f': | |||
| 637 | working->flags |= CE_FOLLOW0x10; | |||
| 638 | break; | |||
| 639 | default: | |||
| 640 | warnx("%s:%d: illegal flag: `%c'" | |||
| 641 | " --> skipping", | |||
| 642 | conf, lineno, *q); | |||
| 643 | ret = 1; | |||
| 644 | goto nextline; | |||
| 645 | } | |||
| 646 | q++; | |||
| 647 | } | |||
| 648 | } else | |||
| 649 | parse--; /* no flags so undo */ | |||
| 650 | ||||
| 651 | working->pidfile = PIDFILE"/var/run/syslog.pid"; | |||
| 652 | working->signal = SIGHUP1; | |||
| 653 | working->runcmd = NULL((void *)0); | |||
| 654 | working->whom = NULL((void *)0); | |||
| 655 | for (;;) { | |||
| 656 | q = parse = sob(++parse); /* Optional field */ | |||
| 657 | if (q == NULL((void *)0) || *q == '\0') | |||
| 658 | break; | |||
| 659 | if (*q == '/') { | |||
| 660 | *(parse = son(parse)) = '\0'; | |||
| 661 | if (strlen(q) >= PATH_MAX1024) { | |||
| 662 | warnx("%s:%d: pathname too long: %s" | |||
| 663 | " --> skipping", | |||
| 664 | conf, lineno, q); | |||
| 665 | ret = 1; | |||
| 666 | goto nextline; | |||
| 667 | } | |||
| 668 | working->pidfile = strdup(q); | |||
| 669 | if (working->pidfile == NULL((void *)0)) | |||
| 670 | err(1, NULL((void *)0)); | |||
| 671 | } else if (*q == '"' && (tmp = strchr(q + 1, '"'))) { | |||
| 672 | *(parse = tmp) = '\0'; | |||
| 673 | if (*++q != '\0') { | |||
| 674 | working->runcmd = strdup(q); | |||
| 675 | if (working->runcmd == NULL((void *)0)) | |||
| 676 | err(1, NULL((void *)0)); | |||
| 677 | } | |||
| 678 | working->pidfile = NULL((void *)0); | |||
| 679 | working->signal = -1; | |||
| 680 | } else if (strncmp(q, "SIG", 3) == 0) { | |||
| 681 | int i; | |||
| 682 | ||||
| 683 | *(parse = son(parse)) = '\0'; | |||
| 684 | for (i = 1; i < NSIG33; i++) { | |||
| 685 | if (!strcmp(sys_signame[i], q + 3)) { | |||
| 686 | working->signal = i; | |||
| 687 | break; | |||
| 688 | } | |||
| 689 | } | |||
| 690 | if (i == NSIG33) { | |||
| 691 | warnx("%s:%d: unknown signal: %s" | |||
| 692 | " --> skipping", | |||
| 693 | conf, lineno, q); | |||
| 694 | ret = 1; | |||
| 695 | goto nextline; | |||
| 696 | } | |||
| 697 | } else if (working->flags & CE_MONITOR0x08) { | |||
| 698 | *(parse = son(parse)) = '\0'; | |||
| 699 | working->whom = strdup(q); | |||
| 700 | if (working->whom == NULL((void *)0)) | |||
| 701 | err(1, NULL((void *)0)); | |||
| 702 | } else { | |||
| 703 | warnx("%s:%d: unrecognized field: %s" | |||
| 704 | " --> skipping", | |||
| 705 | conf, lineno, q); | |||
| 706 | ret = 1; | |||
| 707 | goto nextline; | |||
| 708 | } | |||
| 709 | } | |||
| 710 | free(errline); | |||
| 711 | ||||
| 712 | if ((working->flags & CE_MONITOR0x08) && working->whom == NULL((void *)0)) { | |||
| 713 | warnx("%s:%d: missing monitor notification field" | |||
| 714 | " --> skipping", | |||
| 715 | conf, lineno); | |||
| 716 | ret = 1; | |||
| 717 | goto nextline; | |||
| 718 | } | |||
| 719 | ||||
| 720 | /* If there is an arcdir, set working->backdir. */ | |||
| 721 | if (arcdir != NULL((void *)0) && working->logbase != NULL((void *)0)) { | |||
| 722 | if (*arcdir == '/') { | |||
| 723 | /* Fully qualified arcdir */ | |||
| 724 | working->backdir = arcdir; | |||
| 725 | } else { | |||
| 726 | /* arcdir is relative to log's parent dir */ | |||
| 727 | *(working->logbase - 1) = '\0'; | |||
| 728 | if ((asprintf(&working->backdir, "%s/%s", | |||
| 729 | working->log, arcdir)) == -1) | |||
| 730 | err(1, NULL((void *)0)); | |||
| 731 | *(working->logbase - 1) = '/'; | |||
| 732 | } | |||
| 733 | /* Ignore arcdir if it doesn't exist. */ | |||
| 734 | if (stat(working->backdir, &sb) != 0 || | |||
| 735 | !S_ISDIR(sb.st_mode)((sb.st_mode & 0170000) == 0040000)) { | |||
| 736 | if (working->backdir != arcdir) | |||
| 737 | free(working->backdir); | |||
| 738 | working->backdir = NULL((void *)0); | |||
| 739 | } | |||
| 740 | } else | |||
| 741 | working->backdir = NULL((void *)0); | |||
| 742 | ||||
| 743 | /* Make sure we can't oflow PATH_MAX */ | |||
| 744 | if (working->backdir != NULL((void *)0)) { | |||
| 745 | if (snprintf(line, sizeof(line), "%s/%s.%d%s", | |||
| 746 | working->backdir, working->logbase, | |||
| 747 | working->numlogs, COMPRESS_POSTFIX".gz") >= PATH_MAX1024) { | |||
| 748 | warnx("%s:%d: pathname too long: %s" | |||
| 749 | " --> skipping", conf, lineno, q); | |||
| 750 | ret = 1; | |||
| 751 | goto nextline; | |||
| 752 | } | |||
| 753 | } else { | |||
| 754 | if (snprintf(line, sizeof(line), "%s.%d%s", | |||
| 755 | working->log, working->numlogs, COMPRESS_POSTFIX".gz") | |||
| 756 | >= PATH_MAX1024) { | |||
| 757 | warnx("%s:%d: pathname too long: %s" | |||
| 758 | " --> skipping", conf, lineno, | |||
| 759 | working->log); | |||
| 760 | ret = 1; | |||
| 761 | goto nextline; | |||
| 762 | } | |||
| 763 | } | |||
| 764 | TAILQ_INSERT_TAIL(list, working, next)do { (working)->next.tqe_next = ((void *)0); (working)-> next.tqe_prev = (list)->tqh_last; *(list)->tqh_last = ( working); (list)->tqh_last = &(working)->next.tqe_next ; } while (0); | |||
| 765 | (*nentries)++; | |||
| 766 | } | |||
| 767 | (void)fclose(f); | |||
| ||||
| 768 | return (ret); | |||
| 769 | } | |||
| 770 | ||||
| 771 | char * | |||
| 772 | missing_field(char *p, char *errline, int lineno) | |||
| 773 | { | |||
| 774 | if (p == NULL((void *)0) || *p == '\0') { | |||
| 775 | warnx("%s:%d: missing field", conf, lineno); | |||
| 776 | fputs(errline, stderr(&__sF[2])); | |||
| 777 | exit(1); | |||
| 778 | } | |||
| 779 | return (p); | |||
| 780 | } | |||
| 781 | ||||
| 782 | void | |||
| 783 | rotate(struct conf_entry *ent, const char *oldlog) | |||
| 784 | { | |||
| 785 | char file1[PATH_MAX1024], file2[PATH_MAX1024], *suffix; | |||
| 786 | int numdays = ent->numlogs - 1; | |||
| 787 | int done = 0; | |||
| 788 | ||||
| 789 | /* Remove old logs */ | |||
| 790 | do { | |||
| 791 | (void)snprintf(file1, sizeof(file1), "%s.%d", oldlog, numdays); | |||
| 792 | (void)snprintf(file2, sizeof(file2), "%s.%d%s", oldlog, | |||
| 793 | numdays, COMPRESS_POSTFIX".gz"); | |||
| 794 | if (noaction) { | |||
| 795 | printf("\trm -f %s %s\n", file1, file2); | |||
| 796 | done = access(file1, 0) && access(file2, 0); | |||
| 797 | } else { | |||
| 798 | done = unlink(file1) && unlink(file2); | |||
| 799 | } | |||
| 800 | numdays++; | |||
| 801 | } while (done == 0); | |||
| 802 | ||||
| 803 | /* Move down log files */ | |||
| 804 | for (numdays = ent->numlogs - 2; numdays >= 0; numdays--) { | |||
| 805 | /* | |||
| 806 | * If both the compressed archive and the non-compressed archive | |||
| 807 | * exist, we decide which to rotate based on the CE_COMPACT flag | |||
| 808 | */ | |||
| 809 | (void)snprintf(file1, sizeof(file1), "%s.%d", oldlog, numdays); | |||
| 810 | suffix = lstat_log(file1, sizeof(file1), ent->flags); | |||
| 811 | if (suffix == NULL((void *)0)) | |||
| 812 | continue; | |||
| 813 | (void)snprintf(file2, sizeof(file2), "%s.%d%s", oldlog, | |||
| 814 | numdays + 1, suffix); | |||
| 815 | ||||
| 816 | if (noaction) { | |||
| 817 | printf("\tmv %s %s\n", file1, file2); | |||
| 818 | printf("\tchmod %o %s\n", ent->permissions, file2); | |||
| 819 | printf("\tchown %u:%u %s\n", ent->uid, ent->gid, file2); | |||
| 820 | } else { | |||
| 821 | if (rename(file1, file2)) | |||
| 822 | warn("can't mv %s to %s", file1, file2); | |||
| 823 | if (chmod(file2, ent->permissions)) | |||
| 824 | warn("can't chmod %s", file2); | |||
| 825 | if (chown(file2, ent->uid, ent->gid)) | |||
| 826 | warn("can't chown %s", file2); | |||
| 827 | } | |||
| 828 | } | |||
| 829 | } | |||
| 830 | ||||
| 831 | void | |||
| 832 | dotrim(struct conf_entry *ent) | |||
| 833 | { | |||
| 834 | char file1[PATH_MAX1024], file2[PATH_MAX1024], oldlog[PATH_MAX1024]; | |||
| 835 | int fd; | |||
| 836 | ||||
| 837 | /* Is there a separate backup dir? */ | |||
| 838 | if (ent->backdir != NULL((void *)0)) | |||
| 839 | snprintf(oldlog, sizeof(oldlog), "%s/%s", ent->backdir, | |||
| 840 | ent->logbase); | |||
| 841 | else | |||
| 842 | strlcpy(oldlog, ent->log, sizeof(oldlog)); | |||
| 843 | ||||
| 844 | if (ent->numlogs > 0) | |||
| 845 | rotate(ent, oldlog); | |||
| 846 | if (!noaction && !(ent->flags & CE_BINARY0x04)) | |||
| 847 | (void)log_trim(ent->log); | |||
| 848 | ||||
| 849 | (void)snprintf(file2, sizeof(file2), "%s.XXXXXXXXXX", ent->log); | |||
| 850 | if (noaction) { | |||
| 851 | printf("\tmktemp %s\n", file2); | |||
| 852 | } else { | |||
| 853 | if ((fd = mkstemp(file2)) == -1) | |||
| 854 | err(1, "can't start '%s' log", file2); | |||
| 855 | if (fchmod(fd, ent->permissions)) | |||
| 856 | err(1, "can't chmod '%s' log file", file2); | |||
| 857 | if (fchown(fd, ent->uid, ent->gid)) | |||
| 858 | err(1, "can't chown '%s' log file", file2); | |||
| 859 | (void)close(fd); | |||
| 860 | /* Add status message */ | |||
| 861 | if (!(ent->flags & CE_BINARY0x04) && log_trim(file2)) | |||
| 862 | err(1, "can't add status message to log '%s'", file2); | |||
| 863 | } | |||
| 864 | ||||
| 865 | if (ent->numlogs == 0) { | |||
| 866 | if (noaction) | |||
| 867 | printf("\trm %s\n", ent->log); | |||
| 868 | else if (unlink(ent->log)) | |||
| 869 | warn("can't rm %s", ent->log); | |||
| 870 | } else { | |||
| 871 | (void)snprintf(file1, sizeof(file1), "%s.0", oldlog); | |||
| 872 | if (noaction) { | |||
| 873 | printf("\tmv %s to %s\n", ent->log, file1); | |||
| 874 | printf("\tchmod %o %s\n", ent->permissions, file1); | |||
| 875 | printf("\tchown %u:%u %s\n", ent->uid, ent->gid, file1); | |||
| 876 | } else if (movefile(ent->log, file1, ent->uid, ent->gid, | |||
| 877 | ent->permissions)) | |||
| 878 | warn("can't mv %s to %s", ent->log, file1); | |||
| 879 | } | |||
| 880 | ||||
| 881 | /* Now move the new log file into place */ | |||
| 882 | if (noaction) | |||
| 883 | printf("\tmv %s to %s\n", file2, ent->log); | |||
| 884 | else if (rename(file2, ent->log)) | |||
| 885 | warn("can't mv %s to %s", file2, ent->log); | |||
| 886 | } | |||
| 887 | ||||
| 888 | /* Log the fact that the logs were turned over */ | |||
| 889 | int | |||
| 890 | log_trim(char *log) | |||
| 891 | { | |||
| 892 | FILE *f; | |||
| 893 | ||||
| 894 | if ((f = fopen(log, "a")) == NULL((void *)0)) | |||
| 895 | return (-1); | |||
| 896 | (void)fprintf(f, "%s %s newsyslog[%ld]: logfile turned over\n", | |||
| 897 | daytime, hostname, (long)getpid()); | |||
| 898 | if (fclose(f) == EOF(-1)) | |||
| 899 | err(1, "log_trim: fclose"); | |||
| 900 | return (0); | |||
| 901 | } | |||
| 902 | ||||
| 903 | /* Fork off compress or gzip to compress the old log file */ | |||
| 904 | void | |||
| 905 | compress_log(struct conf_entry *ent) | |||
| 906 | { | |||
| 907 | char *base, tmp[PATH_MAX1024]; | |||
| 908 | pid_t pid; | |||
| 909 | ||||
| 910 | if (ent->backdir != NULL((void *)0)) | |||
| 911 | snprintf(tmp, sizeof(tmp), "%s/%s.0", ent->backdir, | |||
| 912 | ent->logbase); | |||
| 913 | else | |||
| 914 | snprintf(tmp, sizeof(tmp), "%s.0", ent->log); | |||
| 915 | ||||
| 916 | if ((base = strrchr(COMPRESS"/usr/bin/gzip", '/')) == NULL((void *)0)) | |||
| 917 | base = COMPRESS"/usr/bin/gzip"; | |||
| 918 | else | |||
| 919 | base++; | |||
| 920 | if (noaction) { | |||
| 921 | printf("%s %s\n", base, tmp); | |||
| 922 | return; | |||
| 923 | } | |||
| 924 | pid = fork(); | |||
| 925 | if (pid == -1) { | |||
| 926 | err(1, "fork"); | |||
| 927 | } else if (pid == 0) { | |||
| 928 | (void)execl(COMPRESS"/usr/bin/gzip", base, "-f", tmp, (char *)NULL((void *)0)); | |||
| 929 | warn(COMPRESS"/usr/bin/gzip"); | |||
| 930 | _exit(1); | |||
| 931 | } | |||
| 932 | } | |||
| 933 | ||||
| 934 | /* Return size in bytes of a file */ | |||
| 935 | off_t | |||
| 936 | sizefile(struct stat *sb) | |||
| 937 | { | |||
| 938 | /* For sparse files, return the size based on number of blocks used. */ | |||
| 939 | if (sb->st_size / DEV_BSIZE(1 << 9) > sb->st_blocks) | |||
| 940 | return (sb->st_blocks * DEV_BSIZE(1 << 9)); | |||
| 941 | else | |||
| 942 | return (sb->st_size); | |||
| 943 | } | |||
| 944 | ||||
| 945 | /* Return the age (in hours) of old log file (file.0), or -1 if none */ | |||
| 946 | int | |||
| 947 | age_old_log(struct conf_entry *ent) | |||
| 948 | { | |||
| 949 | char file[PATH_MAX1024]; | |||
| 950 | struct stat sb; | |||
| 951 | ||||
| 952 | if (ent->backdir != NULL((void *)0)) | |||
| 953 | (void)snprintf(file, sizeof(file), "%s/%s.0", ent->backdir, | |||
| 954 | ent->logbase); | |||
| 955 | else | |||
| 956 | (void)snprintf(file, sizeof(file), "%s.0", ent->log); | |||
| 957 | if (ent->flags & CE_COMPACT0x02) { | |||
| 958 | if (stat_suffix(file, sizeof(file), COMPRESS_POSTFIX".gz", &sb, | |||
| 959 | stat) < 0 && stat(file, &sb) == -1) | |||
| 960 | return (-1); | |||
| 961 | } else { | |||
| 962 | if (stat(file, &sb) == -1 && stat_suffix(file, sizeof(file), | |||
| 963 | COMPRESS_POSTFIX".gz", &sb, stat) < 0) | |||
| 964 | return (-1); | |||
| 965 | } | |||
| 966 | return ((int)(timenow - sb.st_mtimest_mtim.tv_sec + 1800) / 3600); | |||
| 967 | } | |||
| 968 | ||||
| 969 | /* Skip Over Blanks */ | |||
| 970 | char * | |||
| 971 | sob(char *p) | |||
| 972 | { | |||
| 973 | if (p == NULL((void *)0)) | |||
| 974 | return(p); | |||
| 975 | while (isspace((unsigned char)*p)) | |||
| 976 | p++; | |||
| 977 | return (p); | |||
| 978 | } | |||
| 979 | ||||
| 980 | /* Skip Over Non-Blanks */ | |||
| 981 | char * | |||
| 982 | son(char *p) | |||
| 983 | { | |||
| 984 | while (p && *p && !isspace((unsigned char)*p)) | |||
| 985 | p++; | |||
| 986 | return (p); | |||
| 987 | } | |||
| 988 | ||||
| 989 | /* Check if string is actually a number */ | |||
| 990 | int | |||
| 991 | isnumberstr(char *string) | |||
| 992 | { | |||
| 993 | while (*string) { | |||
| 994 | if (!isdigit((unsigned char)*string++)) | |||
| 995 | return (0); | |||
| 996 | } | |||
| 997 | return (1); | |||
| 998 | } | |||
| 999 | ||||
| 1000 | int | |||
| 1001 | domonitor(struct conf_entry *ent) | |||
| 1002 | { | |||
| 1003 | char fname[PATH_MAX1024], *flog, *p, *rb = NULL((void *)0); | |||
| 1004 | struct stat sb, tsb; | |||
| 1005 | off_t osize; | |||
| 1006 | FILE *fp; | |||
| 1007 | int rd; | |||
| 1008 | ||||
| 1009 | if (stat(ent->log, &sb) == -1) | |||
| 1010 | return (0); | |||
| 1011 | ||||
| 1012 | if (noaction) { | |||
| 1013 | if (!verbose) | |||
| 1014 | printf("%s: monitored\n", ent->log); | |||
| 1015 | return (1); | |||
| 1016 | } | |||
| 1017 | ||||
| 1018 | flog = strdup(ent->log); | |||
| 1019 | if (flog == NULL((void *)0)) | |||
| 1020 | err(1, NULL((void *)0)); | |||
| 1021 | ||||
| 1022 | for (p = flog; *p != '\0'; p++) { | |||
| 1023 | if (*p == '/') | |||
| 1024 | *p = '_'; | |||
| 1025 | } | |||
| 1026 | snprintf(fname, sizeof(fname), "%s/newsyslog.%s.size", | |||
| 1027 | STATS_DIR"/var/run", flog); | |||
| 1028 | ||||
| 1029 | /* ..if it doesn't exist, simply record the current size. */ | |||
| 1030 | if ((sb.st_size == 0) || stat(fname, &tsb) == -1) | |||
| 1031 | goto update; | |||
| 1032 | ||||
| 1033 | fp = fopen(fname, "r"); | |||
| 1034 | if (fp == NULL((void *)0)) { | |||
| 1035 | warn("%s", fname); | |||
| 1036 | goto cleanup; | |||
| 1037 | } | |||
| 1038 | if (fscanf(fp, "%lld\n", &osize) != 1) { | |||
| 1039 | fclose(fp); | |||
| 1040 | goto update; | |||
| 1041 | } | |||
| 1042 | ||||
| 1043 | fclose(fp); | |||
| 1044 | ||||
| 1045 | /* If the file is smaller, mark the entire thing as changed. */ | |||
| 1046 | if (sb.st_size < osize) | |||
| 1047 | osize = 0; | |||
| 1048 | ||||
| 1049 | /* Now see if current size is larger. */ | |||
| 1050 | if (sb.st_size > osize) { | |||
| 1051 | rb = malloc(sb.st_size - osize); | |||
| 1052 | if (rb == NULL((void *)0)) | |||
| 1053 | err(1, NULL((void *)0)); | |||
| 1054 | ||||
| 1055 | /* Open logfile, seek. */ | |||
| 1056 | fp = fopen(ent->log, "r"); | |||
| 1057 | if (fp == NULL((void *)0)) { | |||
| 1058 | warn("%s", ent->log); | |||
| 1059 | goto cleanup; | |||
| 1060 | } | |||
| 1061 | fseek(fp, osize, SEEK_SET0); | |||
| 1062 | rd = fread(rb, 1, sb.st_size - osize, fp); | |||
| 1063 | if (rd < 1) { | |||
| 1064 | warn("fread"); | |||
| 1065 | fclose(fp); | |||
| 1066 | goto cleanup; | |||
| 1067 | } | |||
| 1068 | fclose(fp); | |||
| 1069 | ||||
| 1070 | /* Send message. */ | |||
| 1071 | fp = popen(SENDMAIL"/usr/sbin/sendmail" " -t", "w"); | |||
| 1072 | if (fp == NULL((void *)0)) { | |||
| 1073 | warn("popen"); | |||
| 1074 | goto cleanup; | |||
| 1075 | } | |||
| 1076 | fprintf(fp, "Auto-Submitted: auto-generated\n"); | |||
| 1077 | fprintf(fp, "To: %s\nSubject: LOGFILE NOTIFICATION: %s\n\n\n", | |||
| 1078 | ent->whom, ent->log); | |||
| 1079 | fwrite(rb, 1, rd, fp); | |||
| 1080 | fputs("\n\n", fp); | |||
| 1081 | ||||
| 1082 | pclose(fp); | |||
| 1083 | } | |||
| 1084 | update: | |||
| 1085 | /* Reopen for writing and update file. */ | |||
| 1086 | fp = fopen(fname, "w"); | |||
| 1087 | if (fp == NULL((void *)0)) { | |||
| 1088 | warn("%s", fname); | |||
| 1089 | goto cleanup; | |||
| 1090 | } | |||
| 1091 | fprintf(fp, "%lld\n", (long long)sb.st_size); | |||
| 1092 | fclose(fp); | |||
| 1093 | ||||
| 1094 | cleanup: | |||
| 1095 | free(flog); | |||
| 1096 | free(rb); | |||
| 1097 | return (1); | |||
| 1098 | } | |||
| 1099 | ||||
| 1100 | /* ARGSUSED */ | |||
| 1101 | void | |||
| 1102 | child_killer(int signo) | |||
| 1103 | { | |||
| 1104 | int save_errno = errno(*__errno()); | |||
| 1105 | int status; | |||
| 1106 | ||||
| 1107 | while (waitpid(-1, &status, WNOHANG1) > 0) | |||
| 1108 | ; | |||
| 1109 | errno(*__errno()) = save_errno; | |||
| 1110 | } | |||
| 1111 | ||||
| 1112 | int | |||
| 1113 | stat_suffix(char *file, size_t size, char *suffix, struct stat *sp, | |||
| 1114 | int (*func)(const char *, struct stat *)) | |||
| 1115 | { | |||
| 1116 | size_t n; | |||
| 1117 | ||||
| 1118 | n = strlcat(file, suffix, size); | |||
| 1119 | if (n < size && func(file, sp) == 0) | |||
| 1120 | return (0); | |||
| 1121 | file[n - strlen(suffix)] = '\0'; | |||
| 1122 | return (-1); | |||
| 1123 | } | |||
| 1124 | ||||
| 1125 | /* | |||
| 1126 | * lstat() a log, possibly appending a suffix; order is based on flags. | |||
| 1127 | * Returns the suffix appended (may be empty string) or NULL if no file. | |||
| 1128 | */ | |||
| 1129 | char * | |||
| 1130 | lstat_log(char *file, size_t size, int flags) | |||
| 1131 | { | |||
| 1132 | struct stat sb; | |||
| 1133 | ||||
| 1134 | if (flags & CE_COMPACT0x02) { | |||
| 1135 | if (stat_suffix(file, size, COMPRESS_POSTFIX".gz", &sb, lstat) == 0) | |||
| 1136 | return (COMPRESS_POSTFIX".gz"); | |||
| 1137 | if (lstat(file, &sb) == 0) | |||
| 1138 | return (""); | |||
| 1139 | } else { | |||
| 1140 | if (lstat(file, &sb) == 0) | |||
| 1141 | return (""); | |||
| 1142 | if (stat_suffix(file, size, COMPRESS_POSTFIX".gz", &sb, lstat) == 0) | |||
| 1143 | return (COMPRESS_POSTFIX".gz"); | |||
| 1144 | ||||
| 1145 | } | |||
| 1146 | return (NULL((void *)0)); | |||
| 1147 | } | |||
| 1148 | ||||
| 1149 | /* | |||
| 1150 | * Parse a limited subset of ISO 8601. The specific format is as follows: | |||
| 1151 | * | |||
| 1152 | * [CC[YY[MM[DD]]]][THH[MM[SS]]] (where `T' is the literal letter) | |||
| 1153 | * | |||
| 1154 | * We don't accept a timezone specification; missing fields (including timezone) | |||
| 1155 | * are defaulted to the current date but time zero. | |||
| 1156 | */ | |||
| 1157 | time_t | |||
| 1158 | parse8601(char *s) | |||
| 1159 | { | |||
| 1160 | struct tm tm, *tmp; | |||
| 1161 | char *t; | |||
| 1162 | long l; | |||
| 1163 | ||||
| 1164 | tmp = localtime(&timenow); | |||
| 1165 | tm = *tmp; | |||
| 1166 | ||||
| 1167 | tm.tm_hour = tm.tm_min = tm.tm_sec = 0; | |||
| 1168 | ||||
| 1169 | l = strtol(s, &t, 10); | |||
| 1170 | if (l < 0 || l >= INT_MAX2147483647 || (*t != '\0' && *t != 'T')) | |||
| 1171 | return (-1); | |||
| 1172 | ||||
| 1173 | /* | |||
| 1174 | * Now t points either to the end of the string (if no time was | |||
| 1175 | * provided) or to the letter `T' which separates date and time in | |||
| 1176 | * ISO 8601. The pointer arithmetic is the same for either case. | |||
| 1177 | */ | |||
| 1178 | switch (t - s) { | |||
| 1179 | case 8: | |||
| 1180 | tm.tm_year = ((l / 1000000) - 19) * 100; | |||
| 1181 | l = l % 1000000; | |||
| 1182 | case 6: | |||
| 1183 | tm.tm_year -= tm.tm_year % 100; | |||
| 1184 | tm.tm_year += l / 10000; | |||
| 1185 | l = l % 10000; | |||
| 1186 | case 4: | |||
| 1187 | tm.tm_mon = (l / 100) - 1; | |||
| 1188 | l = l % 100; | |||
| 1189 | case 2: | |||
| 1190 | tm.tm_mday = l; | |||
| 1191 | case 0: | |||
| 1192 | break; | |||
| 1193 | default: | |||
| 1194 | return (-1); | |||
| 1195 | } | |||
| 1196 | ||||
| 1197 | /* sanity check */ | |||
| 1198 | if (tm.tm_year < 70 || tm.tm_mon < 0 || tm.tm_mon > 12 || | |||
| 1199 | tm.tm_mday < 1 || tm.tm_mday > 31) | |||
| 1200 | return (-1); | |||
| 1201 | ||||
| 1202 | if (*t != '\0') { | |||
| 1203 | s = ++t; | |||
| 1204 | l = strtol(s, &t, 10); | |||
| 1205 | if (l < 0 || l >= INT_MAX2147483647 || | |||
| 1206 | (*t != '\0' && !isspace((unsigned char)*t))) | |||
| 1207 | return (-1); | |||
| 1208 | ||||
| 1209 | switch (t - s) { | |||
| 1210 | case 6: | |||
| 1211 | tm.tm_sec = l % 100; | |||
| 1212 | l /= 100; | |||
| 1213 | case 4: | |||
| 1214 | tm.tm_min = l % 100; | |||
| 1215 | l /= 100; | |||
| 1216 | case 2: | |||
| 1217 | tm.tm_hour = l; | |||
| 1218 | case 0: | |||
| 1219 | break; | |||
| 1220 | default: | |||
| 1221 | return (-1); | |||
| 1222 | } | |||
| 1223 | ||||
| 1224 | /* sanity check */ | |||
| 1225 | if (tm.tm_sec < 0 || tm.tm_sec > 60 || tm.tm_min < 0 || | |||
| 1226 | tm.tm_min > 59 || tm.tm_hour < 0 || tm.tm_hour > 23) | |||
| 1227 | return (-1); | |||
| 1228 | } | |||
| 1229 | return (mktime(&tm)); | |||
| 1230 | } | |||
| 1231 | ||||
| 1232 | /*- | |||
| 1233 | * Parse a cyclic time specification, the format is as follows: | |||
| 1234 | * | |||
| 1235 | * [Dhh] or [Wd[Dhh]] or [Mdd[Dhh]] | |||
| 1236 | * | |||
| 1237 | * to rotate a logfile cyclic at | |||
| 1238 | * | |||
| 1239 | * - every day (D) within a specific hour (hh) (hh = 0...23) | |||
| 1240 | * - once a week (W) at a specific day (d) OR (d = 0..6, 0 = Sunday) | |||
| 1241 | * - once a month (M) at a specific day (d) (d = 1..31,l|L) | |||
| 1242 | * | |||
| 1243 | * We don't accept a timezone specification; missing fields | |||
| 1244 | * are defaulted to the current date but time zero. | |||
| 1245 | */ | |||
| 1246 | time_t | |||
| 1247 | parseDWM(char *s) | |||
| 1248 | { | |||
| 1249 | static int mtab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; | |||
| 1250 | int WMseen = 0, Dseen = 0, nd; | |||
| 1251 | struct tm tm, *tmp; | |||
| 1252 | char *t; | |||
| 1253 | long l; | |||
| 1254 | ||||
| 1255 | tmp = localtime(&timenow); | |||
| 1256 | tm = *tmp; | |||
| 1257 | ||||
| 1258 | /* set no. of days per month */ | |||
| 1259 | ||||
| 1260 | nd = mtab[tm.tm_mon]; | |||
| 1261 | ||||
| 1262 | if (tm.tm_mon == 1) { | |||
| 1263 | if (((tm.tm_year + 1900) % 4 == 0) && | |||
| 1264 | ((tm.tm_year + 1900) % 100 != 0) && | |||
| 1265 | ((tm.tm_year + 1900) % 400 == 0)) { | |||
| 1266 | nd++; /* leap year, 29 days in february */ | |||
| 1267 | } | |||
| 1268 | } | |||
| 1269 | tm.tm_hour = tm.tm_min = tm.tm_sec = 0; | |||
| 1270 | ||||
| 1271 | for (;;) { | |||
| 1272 | switch (*s) { | |||
| 1273 | case 'D': | |||
| 1274 | if (Dseen) | |||
| 1275 | return (-1); | |||
| 1276 | Dseen++; | |||
| 1277 | s++; | |||
| 1278 | l = strtol(s, &t, 10); | |||
| 1279 | if (l < 0 || l > 23) | |||
| 1280 | return (-1); | |||
| 1281 | tm.tm_hour = l; | |||
| 1282 | break; | |||
| 1283 | ||||
| 1284 | case 'W': | |||
| 1285 | if (WMseen) | |||
| 1286 | return (-1); | |||
| 1287 | WMseen++; | |||
| 1288 | s++; | |||
| 1289 | l = strtol(s, &t, 10); | |||
| 1290 | if (l < 0 || l > 6) | |||
| 1291 | return (-1); | |||
| 1292 | if (l != tm.tm_wday) { | |||
| 1293 | int save; | |||
| 1294 | ||||
| 1295 | if (l < tm.tm_wday) { | |||
| 1296 | save = 6 - tm.tm_wday; | |||
| 1297 | save += (l + 1); | |||
| 1298 | } else { | |||
| 1299 | save = l - tm.tm_wday; | |||
| 1300 | } | |||
| 1301 | ||||
| 1302 | tm.tm_mday += save; | |||
| 1303 | ||||
| 1304 | if (tm.tm_mday > nd) { | |||
| 1305 | tm.tm_mon++; | |||
| 1306 | tm.tm_mday = tm.tm_mday - nd; | |||
| 1307 | } | |||
| 1308 | } | |||
| 1309 | break; | |||
| 1310 | ||||
| 1311 | case 'M': | |||
| 1312 | if (WMseen) | |||
| 1313 | return (-1); | |||
| 1314 | WMseen++; | |||
| 1315 | s++; | |||
| 1316 | if (tolower((unsigned char)*s) == 'l') { | |||
| 1317 | tm.tm_mday = nd; | |||
| 1318 | s++; | |||
| 1319 | t = s; | |||
| 1320 | } else { | |||
| 1321 | l = strtol(s, &t, 10); | |||
| 1322 | if (l < 1 || l > 31) | |||
| 1323 | return (-1); | |||
| 1324 | ||||
| 1325 | if (l > nd) | |||
| 1326 | return (-1); | |||
| 1327 | if (l < tm.tm_mday) | |||
| 1328 | tm.tm_mon++; | |||
| 1329 | tm.tm_mday = l; | |||
| 1330 | } | |||
| 1331 | break; | |||
| 1332 | ||||
| 1333 | default: | |||
| 1334 | return (-1); | |||
| 1335 | break; | |||
| 1336 | } | |||
| 1337 | ||||
| 1338 | if (*t == '\0' || isspace((unsigned char)*t)) | |||
| 1339 | break; | |||
| 1340 | else | |||
| 1341 | s = t; | |||
| 1342 | } | |||
| 1343 | return (mktime(&tm)); | |||
| 1344 | } | |||
| 1345 | ||||
| 1346 | /* | |||
| 1347 | * Move a file using rename(2) if possible and copying if not. | |||
| 1348 | */ | |||
| 1349 | int | |||
| 1350 | movefile(char *from, char *to, uid_t owner_uid, gid_t group_gid, mode_t perm) | |||
| 1351 | { | |||
| 1352 | FILE *src, *dst; | |||
| 1353 | int i; | |||
| 1354 | ||||
| 1355 | /* try rename(2) first */ | |||
| 1356 | if (rename(from, to) == 0) { | |||
| 1357 | if (chmod(to, perm)) | |||
| 1358 | warn("can't chmod %s", to); | |||
| 1359 | if (chown(to, owner_uid, group_gid)) | |||
| 1360 | warn("can't chown %s", to); | |||
| 1361 | return (0); | |||
| 1362 | } else if (errno(*__errno()) != EXDEV18) | |||
| 1363 | return (-1); | |||
| 1364 | ||||
| 1365 | /* different filesystem, have to copy the file */ | |||
| 1366 | if ((src = fopen(from, "r")) == NULL((void *)0)) | |||
| 1367 | err(1, "can't fopen %s for reading", from); | |||
| 1368 | if ((dst = fopen(to, "w")) == NULL((void *)0)) | |||
| 1369 | err(1, "can't fopen %s for writing", to); | |||
| 1370 | if (fchmod(fileno(dst)(!__isthreaded ? ((dst)->_file) : (fileno)(dst)), perm)) | |||
| 1371 | err(1, "can't fchmod %s", to); | |||
| 1372 | if (fchown(fileno(dst)(!__isthreaded ? ((dst)->_file) : (fileno)(dst)), owner_uid, group_gid)) | |||
| 1373 | err(1, "can't fchown %s", to); | |||
| 1374 | ||||
| 1375 | while ((i = getc(src)(!__isthreaded ? (--(src)->_r < 0 ? __srget(src) : (int )(*(src)->_p++)) : (getc)(src))) != EOF(-1)) { | |||
| 1376 | if ((putc(i, dst)(!__isthreaded ? __sputc(i, dst) : (putc)(i, dst))) == EOF(-1)) | |||
| 1377 | err(1, "error writing to %s", to); | |||
| 1378 | } | |||
| 1379 | ||||
| 1380 | if (ferror(src)(!__isthreaded ? (((src)->_flags & 0x0040) != 0) : (ferror )(src))) | |||
| 1381 | err(1, "error reading from %s", from); | |||
| 1382 | if ((fclose(src)) != 0) | |||
| 1383 | err(1, "can't fclose %s", from); | |||
| 1384 | if ((fclose(dst)) != 0) | |||
| 1385 | err(1, "can't fclose %s", to); | |||
| 1386 | if ((unlink(from)) != 0) | |||
| 1387 | err(1, "can't unlink %s", from); | |||
| 1388 | ||||
| 1389 | return (0); | |||
| 1390 | } |