~davewalker/ubuntu/lucid/logrotate/sync_3.7.8-5

« back to all changes in this revision

Viewing changes to .pc/datehack.patch/logrotate.c

  • Committer: Dave Walker (Daviey)
  • Date: 2010-04-03 21:21:05 UTC
  • mfrom: (4.2.3 sid)
  • Revision ID: davewalker@ubuntu.com-20100403212105-c47hkcp4sgyq3jek
* Merge from debian unstable (LP: #554823)
  - remaining changes:
     + debian/control: Drop mailx to Suggests for Ubuntu; it's only 
       used on request, and we don't configure an MTA by default.
  - Fixes config parser to not get confused when a wildcard produces 
    no results. (LP: #392532)
* New patch:
  + parser571033.patch: fix the config parser to not get confused when
    a wildcard produces no results. (Closes: 571033)
* Switch to dpkg-source 3.0 (quilt) format
* Bump debhelper version to 7 (dh_clean -k -> dh_prep).
* Update standards version to 3.8.4 (no changes).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#include <sys/queue.h>
 
2
#include <alloca.h>
 
3
#include <ctype.h>
 
4
#include <dirent.h>
 
5
#include <errno.h>
 
6
#include <fcntl.h>
 
7
#include <popt.h>
 
8
#include <stdio.h>
 
9
#include <stdlib.h>
 
10
#include <string.h>
 
11
#include <sys/stat.h>
 
12
#include <sys/wait.h>
 
13
#include <time.h>
 
14
#include <unistd.h>
 
15
#include <glob.h>
 
16
#include <locale.h>
 
17
 
 
18
#ifdef WITH_SELINUX
 
19
#include <selinux/selinux.h>
 
20
static security_context_t prev_context = NULL;
 
21
int selinux_enabled = 0;
 
22
int selinux_enforce = 0;
 
23
#endif
 
24
 
 
25
#include "basenames.h"
 
26
#include "log.h"
 
27
#include "logrotate.h"
 
28
 
 
29
#if !defined(GLOB_ABORTED) && defined(GLOB_ABEND)
 
30
#define GLOB_ABORTED GLOB_ABEND
 
31
#endif
 
32
 
 
33
struct logState {
 
34
    char *fn;
 
35
    struct tm lastRotated;      /* only tm.mon, tm_mday, tm_year are good! */
 
36
    struct stat sb;
 
37
    int doRotate;
 
38
    LIST_ENTRY(logState) list;
 
39
};
 
40
 
 
41
struct logNames {
 
42
    char *firstRotated;
 
43
    char *disposeName;
 
44
    char *finalName;
 
45
    char *dirName;
 
46
    char *baseName;
 
47
};
 
48
 
 
49
struct logStates {
 
50
        LIST_HEAD(stateSet, logState) head;
 
51
} **states;
 
52
 
 
53
unsigned int hashSize;
 
54
int numLogs = 0;
 
55
int debug = 0;
 
56
char *mailCommand = DEFAULT_MAIL_COMMAND;
 
57
time_t nowSecs = 0;
 
58
 
 
59
static int shred_file(char *filename, struct logInfo *log);
 
60
 
 
61
static int globerr(const char *pathname, int theerr)
 
62
{
 
63
    message(MESS_ERROR, "error accessing %s: %s\n", pathname,
 
64
            strerror(theerr));
 
65
 
 
66
    /* We want the glob operation to continue, so return 0 */
 
67
    return 1;
 
68
}
 
69
 
 
70
#define HASH_SIZE_MIN 64
 
71
static int allocateHash(void)
 
72
{
 
73
        struct logInfo *log;
 
74
        unsigned int hs;
 
75
        int i;
 
76
 
 
77
        hs = 0;
 
78
 
 
79
        for (log = logs.tqh_first; log != NULL; log = log->list.tqe_next)
 
80
                hs += log->numFiles;
 
81
 
 
82
        hs *= 2;
 
83
 
 
84
        /* Enforce some reasonable minimum hash size */
 
85
        if (hs < HASH_SIZE_MIN)
 
86
                hs = HASH_SIZE_MIN;
 
87
 
 
88
        states = calloc(hs, sizeof(struct logStates *));
 
89
        if (states == NULL) {
 
90
                message(MESS_ERROR, "could not allocate memory for "
 
91
                                "hash table\n");
 
92
                return 1;
 
93
        }
 
94
 
 
95
        for (i = 0; i < hs; i++) {
 
96
                states[i] = malloc(sizeof(struct logState));
 
97
                if (states[i] == NULL) {
 
98
                        message(MESS_ERROR, "could not allocate memory for "
 
99
                                "hash element\n");
 
100
                        return 1;
 
101
                }
 
102
                LIST_INIT(&(states[i]->head));
 
103
        }
 
104
 
 
105
        hashSize = hs;
 
106
 
 
107
        return 0;
 
108
}
 
109
 
 
110
#define HASH_CONST 13
 
111
static unsigned hashIndex(const char *fn)
 
112
{
 
113
        unsigned hash = 0;
 
114
 
 
115
        while (*fn) {
 
116
                hash *= HASH_CONST;
 
117
                hash += *fn++;
 
118
        }
 
119
 
 
120
        return hash % hashSize;
 
121
}
 
122
 
 
123
static struct logState *newState(const char *fn)
 
124
{
 
125
        struct tm now = *localtime(&nowSecs);
 
126
        struct logState *new;
 
127
        time_t lr_time;
 
128
 
 
129
        if ((new = malloc(sizeof(*new))) == NULL)
 
130
                return NULL;
 
131
 
 
132
        if ((new->fn = strdup(fn)) == NULL)
 
133
                return NULL;
 
134
 
 
135
        new->doRotate = 0;
 
136
 
 
137
        memset(&new->lastRotated, 0, sizeof(new->lastRotated));
 
138
        new->lastRotated.tm_mon = now.tm_mon;
 
139
        new->lastRotated.tm_mday = now.tm_mday;
 
140
        new->lastRotated.tm_year = now.tm_year;
 
141
 
 
142
        /* fill in the rest of the new->lastRotated fields */
 
143
        lr_time = mktime(&new->lastRotated);
 
144
        new->lastRotated = *localtime(&lr_time);
 
145
 
 
146
        return new;
 
147
}
 
148
 
 
149
static struct logState *findState(const char *fn)
 
150
{
 
151
        unsigned int i = hashIndex(fn);
 
152
        struct logState *p;
 
153
 
 
154
        for (p = states[i]->head.lh_first; p != NULL; p = p->list.le_next)
 
155
                if (!strcmp(fn, p->fn))
 
156
                        break;
 
157
 
 
158
        /* new state */
 
159
        if (p == NULL) {
 
160
                if ((p = newState(fn)) == NULL)
 
161
                        return NULL;
 
162
 
 
163
                LIST_INSERT_HEAD(&(states[i]->head), p, list);
 
164
        }
 
165
 
 
166
        return p;
 
167
}
 
168
 
 
169
static int runScript(char *logfn, char *script)
 
170
{
 
171
    int rc;
 
172
 
 
173
    if (debug) {
 
174
        message(MESS_DEBUG, "running script with arg %s: \"%s\"\n",
 
175
                logfn, script);
 
176
        return 0;
 
177
    }
 
178
 
 
179
        if (!fork()) {
 
180
                execl("/bin/sh", "sh", "-c", script, "logrotate_script", logfn, NULL);
 
181
                exit(1);
 
182
        }
 
183
 
 
184
    wait(&rc);
 
185
    return rc;
 
186
}
 
187
 
 
188
int createOutputFile(char *fileName, int flags, struct stat *sb)
 
189
{
 
190
    int fd;
 
191
 
 
192
    fd = open(fileName, flags, sb->st_mode);
 
193
    if (fd < 0) {
 
194
        message(MESS_ERROR, "error creating output file %s: %s\n",
 
195
                fileName, strerror(errno));
 
196
        return -1;
 
197
    }
 
198
    if (fchmod(fd, (S_IRUSR | S_IWUSR) & sb->st_mode)) {
 
199
        message(MESS_ERROR, "error setting mode of %s: %s\n",
 
200
                fileName, strerror(errno));
 
201
        close(fd);
 
202
        return -1;
 
203
    }
 
204
    if (fchown(fd, sb->st_uid, sb->st_gid)) {
 
205
        message(MESS_ERROR, "error setting owner of %s: %s\n",
 
206
                fileName, strerror(errno));
 
207
        close(fd);
 
208
        return -1;
 
209
    }
 
210
    if (fchmod(fd, sb->st_mode)) {
 
211
        message(MESS_ERROR, "error setting mode of %s: %s\n",
 
212
                fileName, strerror(errno));
 
213
        close(fd);
 
214
        return -1;
 
215
    }
 
216
    return fd;
 
217
}
 
218
 
 
219
#define SHRED_CALL "shred -u "
 
220
#define SHRED_COUNT_FLAG "-n "
 
221
#define DIGITS 10
 
222
/* unlink, but try to call shred from GNU fileutils */
 
223
static int shred_file(char *filename, struct logInfo *log)
 
224
{
 
225
        int len, ret;
 
226
        char *cmd;
 
227
        char count[DIGITS];    /*  that's a lot of shredding :)  */
 
228
 
 
229
        if (!(log->flags & LOG_FLAG_SHRED)) {
 
230
                return unlink(filename);
 
231
        }
 
232
 
 
233
        len = strlen(filename) + strlen(SHRED_CALL);
 
234
        len += strlen(SHRED_COUNT_FLAG) + DIGITS;
 
235
        cmd = malloc(len);
 
236
 
 
237
        if (!cmd) {
 
238
                message(MESS_ERROR, "malloc error while shredding");
 
239
                return unlink(filename);
 
240
        }
 
241
        strcpy(cmd, SHRED_CALL);
 
242
        if (log->shred_cycles != 0) {
 
243
                strcat(cmd, SHRED_COUNT_FLAG);
 
244
                snprintf(count, DIGITS - 1, "%d", log->shred_cycles);
 
245
                strcat(count, " ");
 
246
                strcat(cmd, count);
 
247
        }
 
248
        strcat(cmd, filename);
 
249
        ret = system(cmd);
 
250
        free(cmd);
 
251
        if (ret != 0) {
 
252
                message(MESS_ERROR, "Failed to shred %s\n, trying unlink", filename);
 
253
                if (ret != -1) {
 
254
                        message(MESS_NORMAL, "Shred returned %d\n", ret);
 
255
                }
 
256
                return unlink(filename);
 
257
        } else {
 
258
                return ret;
 
259
        }
 
260
}
 
261
 
 
262
static int removeLogFile(char *name, struct logInfo *log)
 
263
{
 
264
    message(MESS_DEBUG, "removing old log %s\n", name);
 
265
 
 
266
    if (!debug && shred_file(name, log)) {
 
267
        message(MESS_ERROR, "Failed to remove old log %s: %s\n",
 
268
                name, strerror(errno));
 
269
        return 1;
 
270
    }
 
271
    return 0;
 
272
}
 
273
 
 
274
static int compressLogFile(char *name, struct logInfo *log, struct stat *sb)
 
275
{
 
276
    char *compressedName;
 
277
    const char **fullCommand;
 
278
    int inFile;
 
279
    int outFile;
 
280
    int i;
 
281
    int status;
 
282
 
 
283
    message(MESS_DEBUG, "compressing log with: %s\n", log->compress_prog);
 
284
    if (debug)
 
285
        return 0;
 
286
 
 
287
    fullCommand = alloca(sizeof(*fullCommand) *
 
288
                         (log->compress_options_count + 2));
 
289
    fullCommand[0] = log->compress_prog;
 
290
    for (i = 0; i < log->compress_options_count; i++)
 
291
        fullCommand[i + 1] = log->compress_options_list[i];
 
292
    fullCommand[log->compress_options_count + 1] = NULL;
 
293
 
 
294
    compressedName = alloca(strlen(name) + strlen(log->compress_ext) + 2);
 
295
    sprintf(compressedName, "%s%s", name, log->compress_ext);
 
296
 
 
297
    if ((inFile = open(name, O_RDONLY)) < 0) {
 
298
        message(MESS_ERROR, "unable to open %s for compression\n", name);
 
299
        return 1;
 
300
    }
 
301
 
 
302
    outFile =
 
303
        createOutputFile(compressedName, O_RDWR | O_CREAT | O_TRUNC, sb);
 
304
    if (outFile < 0) {
 
305
        close(inFile);
 
306
        return 1;
 
307
    }
 
308
 
 
309
    if (!fork()) {
 
310
        dup2(inFile, 0);
 
311
        close(inFile);
 
312
        dup2(outFile, 1);
 
313
        close(outFile);
 
314
 
 
315
        execvp(fullCommand[0], (void *) fullCommand);
 
316
        exit(1);
 
317
    }
 
318
 
 
319
    close(inFile);
 
320
    close(outFile);
 
321
 
 
322
    wait(&status);
 
323
 
 
324
    if (!WIFEXITED(status) || WEXITSTATUS(status)) {
 
325
        message(MESS_ERROR, "failed to compress log %s\n", name);
 
326
        return 1;
 
327
    }
 
328
 
 
329
    shred_file(name, log);
 
330
 
 
331
    return 0;
 
332
}
 
333
 
 
334
static int mailLog(char *logFile, char *mailCommand,
 
335
                   char *uncompressCommand, char *address, char *subject)
 
336
{
 
337
    int mailInput;
 
338
    pid_t mailChild, uncompressChild = 0;
 
339
    int mailStatus, uncompressStatus;
 
340
    int uncompressPipe[2];
 
341
    char *mailArgv[] = { mailCommand, "-s", subject, address, NULL };
 
342
    int rc = 0;
 
343
 
 
344
    if ((mailInput = open(logFile, O_RDONLY)) < 0) {
 
345
        message(MESS_ERROR, "failed to open %s for mailing: %s\n", logFile,
 
346
                strerror(errno));
 
347
        return 1;
 
348
    }
 
349
 
 
350
    if (uncompressCommand) {
 
351
                if (pipe(uncompressPipe) < 0) {
 
352
                        message(MESS_ERROR, "error opening pipe for uncompress: %s",
 
353
                                        strerror(errno));
 
354
                        return 1;
 
355
                }
 
356
                if (!(uncompressChild = fork())) {
 
357
                        /* uncompress child */
 
358
                        dup2(mailInput, 0);
 
359
                        close(mailInput);
 
360
                        dup2(uncompressPipe[1], 1);
 
361
                        close(uncompressPipe[0]);
 
362
                        close(uncompressPipe[1]);
 
363
 
 
364
                        execlp(uncompressCommand, uncompressCommand, NULL);
 
365
                        exit(1);
 
366
                }
 
367
 
 
368
                close(mailInput);
 
369
                mailInput = uncompressPipe[0];
 
370
                close(uncompressPipe[1]);
 
371
    }
 
372
 
 
373
    if (!(mailChild = fork())) {
 
374
        dup2(mailInput, 0);
 
375
        close(mailInput);
 
376
        close(1);
 
377
 
 
378
        execvp(mailArgv[0], mailArgv);
 
379
        exit(1);
 
380
    }
 
381
 
 
382
    close(mailInput);
 
383
 
 
384
    waitpid(mailChild, &mailStatus, 0);
 
385
 
 
386
    if (!WIFEXITED(mailStatus) || WEXITSTATUS(mailStatus)) {
 
387
        message(MESS_ERROR, "mail command failed for %s\n", logFile);
 
388
        rc = 1;
 
389
    }
 
390
 
 
391
    if (uncompressCommand) {
 
392
        waitpid(uncompressChild, &uncompressStatus, 0);
 
393
 
 
394
        if (!WIFEXITED(uncompressStatus) || WEXITSTATUS(uncompressStatus)) {
 
395
            message(MESS_ERROR, "uncompress command failed mailing %s\n",
 
396
                    logFile);
 
397
            rc = 1;
 
398
        }
 
399
    }
 
400
 
 
401
    return rc;
 
402
}
 
403
 
 
404
static int mailLogWrapper(char *mailFilename, char *mailCommand,
 
405
                          int logNum, struct logInfo *log)
 
406
{
 
407
    /* if the log is compressed (and we're not mailing a
 
408
     * file whose compression has been delayed), we need
 
409
     * to uncompress it */
 
410
    if ((log->flags & LOG_FLAG_COMPRESS) &&
 
411
        !((log->flags & LOG_FLAG_DELAYCOMPRESS) &&
 
412
          (log->flags & LOG_FLAG_MAILFIRST))) {
 
413
        if (mailLog(mailFilename, mailCommand,
 
414
                    log->uncompress_prog, log->logAddress,
 
415
                    log->files[logNum]))
 
416
            return 1;
 
417
    } else {
 
418
        if (mailLog(mailFilename, mailCommand, NULL,
 
419
                    log->logAddress, mailFilename))
 
420
            return 1;
 
421
    }
 
422
    return 0;
 
423
}
 
424
 
 
425
static int copyTruncate(char *currLog, char *saveLog, struct stat *sb,
 
426
                        int flags)
 
427
{
 
428
    char buf[BUFSIZ];
 
429
    int fdcurr = -1, fdsave = -1;
 
430
    ssize_t cnt;
 
431
 
 
432
    message(MESS_DEBUG, "copying %s to %s\n", currLog, saveLog);
 
433
 
 
434
    if (!debug) {
 
435
        if ((fdcurr = open(currLog, O_RDWR)) < 0) {
 
436
            message(MESS_ERROR, "error opening %s: %s\n", currLog,
 
437
                    strerror(errno));
 
438
            return 1;
 
439
        }
 
440
#ifdef WITH_SELINUX
 
441
        if (selinux_enabled) {
 
442
            security_context_t oldContext;
 
443
            if (fgetfilecon_raw(fdcurr, &oldContext) >= 0) {
 
444
                if (getfscreatecon_raw(&prev_context) < 0) {
 
445
                    message(MESS_ERROR,
 
446
                            "getting default context: %s\n",
 
447
                            strerror(errno));
 
448
                    if (selinux_enforce) {
 
449
                                freecon(oldContext);
 
450
                                return 1;
 
451
                    }
 
452
                }
 
453
                if (setfscreatecon_raw(oldContext) < 0) {
 
454
                    message(MESS_ERROR,
 
455
                            "setting file context %s to %s: %s\n",
 
456
                            saveLog, oldContext, strerror(errno));
 
457
                        if (selinux_enforce) {
 
458
                                freecon(oldContext);
 
459
                                return 1;
 
460
                    }
 
461
                }
 
462
                message(MESS_DEBUG, "set default create context\n");
 
463
                freecon(oldContext);
 
464
            } else {
 
465
                    if (errno != ENOTSUP) {
 
466
                            message(MESS_ERROR, "getting file context %s: %s\n",
 
467
                                    currLog, strerror(errno));
 
468
                            if (selinux_enforce) {
 
469
                                    return 1;
 
470
                            }
 
471
                    }
 
472
            }
 
473
        }
 
474
#endif
 
475
        fdsave =
 
476
            createOutputFile(saveLog, O_WRONLY | O_CREAT | O_TRUNC, sb);
 
477
#ifdef WITH_SELINUX
 
478
        if (selinux_enabled) {
 
479
            setfscreatecon_raw(prev_context);
 
480
                freecon(prev_context);
 
481
                prev_context = NULL;
 
482
        }
 
483
#endif
 
484
        if (fdsave < 0) {
 
485
            close(fdcurr);
 
486
            return 1;
 
487
        }
 
488
        while ((cnt = read(fdcurr, buf, sizeof(buf))) > 0) {
 
489
            if (write(fdsave, buf, cnt) != cnt) {
 
490
                message(MESS_ERROR, "error writing to %s: %s\n",
 
491
                        saveLog, strerror(errno));
 
492
                close(fdcurr);
 
493
                close(fdsave);
 
494
                return 1;
 
495
            }
 
496
        }
 
497
        if (cnt != 0) {
 
498
            message(MESS_ERROR, "error reading %s: %s\n",
 
499
                    currLog, strerror(errno));
 
500
            close(fdcurr);
 
501
            close(fdsave);
 
502
            return 1;
 
503
        }
 
504
    }
 
505
 
 
506
    if (flags & LOG_FLAG_COPYTRUNCATE) {
 
507
        message(MESS_DEBUG, "truncating %s\n", currLog);
 
508
 
 
509
        if (!debug)
 
510
            if (ftruncate(fdcurr, 0)) {
 
511
                message(MESS_ERROR, "error truncating %s: %s\n", currLog,
 
512
                        strerror(errno));
 
513
                close(fdcurr);
 
514
                close(fdsave);
 
515
                return 1;
 
516
            }
 
517
    } else
 
518
        message(MESS_DEBUG, "Not truncating %s\n", currLog);
 
519
 
 
520
    close(fdcurr);
 
521
    close(fdsave);
 
522
    return 0;
 
523
}
 
524
 
 
525
int findNeedRotating(struct logInfo *log, int logNum)
 
526
{
 
527
    struct stat sb;
 
528
    struct logState *state = NULL;
 
529
    struct tm now = *localtime(&nowSecs);
 
530
 
 
531
    message(MESS_DEBUG, "considering log %s\n", log->files[logNum]);
 
532
 
 
533
    if (stat(log->files[logNum], &sb)) {
 
534
        if ((log->flags & LOG_FLAG_MISSINGOK) && (errno == ENOENT)) {
 
535
            message(MESS_DEBUG, "  log %s does not exist -- skipping\n",
 
536
                    log->files[logNum]);
 
537
            return 0;
 
538
        }
 
539
        message(MESS_ERROR, "stat of %s failed: %s\n", log->files[logNum],
 
540
                strerror(errno));
 
541
        return 1;
 
542
    }
 
543
 
 
544
    state = findState(log->files[logNum]);
 
545
    state->doRotate = 0;
 
546
    state->sb = sb;
 
547
 
 
548
    if (log->criterium == ROT_SIZE) {
 
549
        state->doRotate = (sb.st_size >= log->threshhold);
 
550
    } else if (log->criterium == ROT_FORCE) {
 
551
        /* user forced rotation of logs from command line */
 
552
        state->doRotate = 1;
 
553
    } else if (state->lastRotated.tm_year > now.tm_year ||
 
554
               (state->lastRotated.tm_year == now.tm_year &&
 
555
                (state->lastRotated.tm_mon > now.tm_mon ||
 
556
                 (state->lastRotated.tm_mon == now.tm_mon &&
 
557
                  state->lastRotated.tm_mday > now.tm_mday)))) {
 
558
        message(MESS_ERROR,
 
559
                "log %s last rotated in the future -- rotation forced\n",
 
560
                log->files[logNum]);
 
561
        state->doRotate = 1;
 
562
    } else if (state->lastRotated.tm_year != now.tm_year ||
 
563
               state->lastRotated.tm_mon != now.tm_mon ||
 
564
               state->lastRotated.tm_mday != now.tm_mday) {
 
565
        switch (log->criterium) {
 
566
        case ROT_WEEKLY:
 
567
            /* rotate if:
 
568
               1) the current weekday is before the weekday of the
 
569
               last rotation
 
570
               2) more then a week has passed since the last
 
571
               rotation */
 
572
            state->doRotate = ((now.tm_wday < state->lastRotated.tm_wday)
 
573
                               ||
 
574
                               ((mktime(&now) -
 
575
                                 mktime(&state->lastRotated)) >
 
576
                                (7 * 24 * 3600)));
 
577
            break;
 
578
        case ROT_MONTHLY:
 
579
            /* rotate if the logs haven't been rotated this month or
 
580
               this year */
 
581
            state->doRotate = ((now.tm_mon != state->lastRotated.tm_mon) ||
 
582
                               (now.tm_year !=
 
583
                                state->lastRotated.tm_year));
 
584
            break;
 
585
        case ROT_DAYS:
 
586
            /* FIXME: only days=1 is implemented!! */
 
587
            state->doRotate = 1;
 
588
            break;
 
589
        case ROT_YEARLY:
 
590
            /* rotate if the logs haven't been rotated this year */
 
591
            state->doRotate = (now.tm_year != state->lastRotated.tm_year);
 
592
            break;
 
593
        default:
 
594
            /* ack! */
 
595
            state->doRotate = 0;
 
596
            break;
 
597
        }
 
598
        if (log->minsize && sb.st_size < log->minsize)
 
599
            state->doRotate = 0;
 
600
    }
 
601
 
 
602
    /* The notifempty flag overrides the normal criteria */
 
603
    if (!(log->flags & LOG_FLAG_IFEMPTY) && !sb.st_size)
 
604
        state->doRotate = 0;
 
605
 
 
606
    if (state->doRotate) {
 
607
        message(MESS_DEBUG, "  log needs rotating\n");
 
608
    } else {
 
609
        message(MESS_DEBUG, "  log does not need rotating\n");
 
610
    }
 
611
 
 
612
    return 0;
 
613
}
 
614
 
 
615
int prerotateSingleLog(struct logInfo *log, int logNum, struct logState *state,
 
616
                       struct logNames *rotNames)
 
617
{
 
618
    struct tm now = *localtime(&nowSecs);
 
619
    char *oldName, *newName = NULL;
 
620
    char *tmp;
 
621
    char *compext = "";
 
622
    char *fileext = "";
 
623
    int hasErrors = 0;
 
624
    int i, j;
 
625
    char *glob_pattern;
 
626
    glob_t globResult;
 
627
    int rc;
 
628
    int rotateCount = log->rotateCount ? log->rotateCount : 1;
 
629
    int logStart = (log->logStart == -1) ? 1 : log->logStart;
 
630
#define DATEEXT_LEN 64
 
631
#define PATTERN_LEN (DATEEXT_LEN * 2)
 
632
        char dext_str[DATEEXT_LEN];
 
633
        char dformat[DATEEXT_LEN];
 
634
        char dext_pattern[PATTERN_LEN];
 
635
        char *dext;
 
636
 
 
637
    if (!state->doRotate)
 
638
        return 0;
 
639
 
 
640
    /* Logs with rotateCounts of 0 are rotated once, then removed. This
 
641
       lets scripts run properly, and everything gets mailed properly. */
 
642
 
 
643
    message(MESS_DEBUG, "rotating log %s, log->rotateCount is %d\n",
 
644
            log->files[logNum], log->rotateCount);
 
645
 
 
646
    if (log->flags & LOG_FLAG_COMPRESS)
 
647
        compext = log->compress_ext;
 
648
 
 
649
    state->lastRotated = now;
 
650
 
 
651
    if (log->oldDir) {
 
652
        if (log->oldDir[0] != '/') {
 
653
            char *ld = ourDirName(log->files[logNum]);
 
654
            rotNames->dirName =
 
655
                malloc(strlen(ld) + strlen(log->oldDir) + 2);
 
656
            sprintf(rotNames->dirName, "%s/%s", ld, log->oldDir);
 
657
            free(ld);
 
658
        } else
 
659
            rotNames->dirName = strdup(log->oldDir);
 
660
    } else
 
661
        rotNames->dirName = ourDirName(log->files[logNum]);
 
662
 
 
663
    rotNames->baseName = strdup(ourBaseName(log->files[logNum]));
 
664
 
 
665
    oldName = alloca(PATH_MAX);
 
666
    newName = alloca(PATH_MAX);
 
667
    rotNames->disposeName = malloc(PATH_MAX);
 
668
 
 
669
    if (log->extension &&
 
670
        strncmp(&
 
671
                (rotNames->
 
672
                 baseName[strlen(rotNames->baseName) -
 
673
                          strlen(log->extension)]), log->extension,
 
674
                strlen(log->extension)) == 0) {
 
675
        char *tempstr;
 
676
 
 
677
        fileext = log->extension;
 
678
        tempstr =
 
679
            calloc(strlen(rotNames->baseName) - strlen(log->extension) + 1,
 
680
                   sizeof(char));
 
681
        strncat(tempstr, rotNames->baseName,
 
682
                strlen(rotNames->baseName) - strlen(log->extension));
 
683
        free(rotNames->baseName);
 
684
        rotNames->baseName = tempstr;
 
685
    }
 
686
        
 
687
        /* Allow only %Y %d %m and create valid strftime format string
 
688
         * Construct the glob pattern corresponding to the date format */
 
689
        dext_str[0] = '\0';
 
690
        if (log->dateformat) {
 
691
                i = j = 0;
 
692
                memset(dext_pattern, 0, sizeof(dext_pattern));
 
693
                dext = log->dateformat;
 
694
                while (*dext == ' ')
 
695
                        dext++;
 
696
                while ((*dext != '\0') && (!hasErrors)) {
 
697
                        /* Will there be a space for a char and '\0'? */
 
698
                        if (j >= (sizeof(dext_pattern) - 1)) {
 
699
                                message(MESS_ERROR, "Date format %s is too long\n",
 
700
                                                log->dateformat);
 
701
                                hasErrors = 1;
 
702
                                break;
 
703
                        }
 
704
                        if (*dext == '%') {
 
705
                                switch (*(dext + 1)) {
 
706
                                        case 'Y':
 
707
                                                strncat(dext_pattern, "[0-9][0-9]",
 
708
                                                                sizeof(dext_pattern) - strlen(dext_pattern));
 
709
                                                j += 10; /* strlen("[0-9][0-9]") */
 
710
                                        case 'm':
 
711
                                        case 'd':
 
712
                                                strncat(dext_pattern, "[0-9][0-9]",
 
713
                                                                sizeof(dext_pattern) - strlen(dext_pattern));
 
714
                                                j += 10;
 
715
                                                if (j >= (sizeof(dext_pattern) - 1)) {
 
716
                                                        message(MESS_ERROR, "Date format %s is too long\n",
 
717
                                                                        log->dateformat);
 
718
                                                        hasErrors = 1;
 
719
                                                        break;
 
720
                                                }
 
721
                                                dformat[i++] = *(dext++);
 
722
                                                dformat[i] = *dext;
 
723
                                                break;
 
724
                                        case 's':
 
725
                                                /* End of year 2293 this pattern does not work. */
 
726
                                                strncat(dext_pattern,
 
727
                                                                "[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]",
 
728
                                                                sizeof(dext_pattern) - strlen(dext_pattern));
 
729
                                                j += 50;
 
730
                                                if (j >= (sizeof(dext_pattern) - 1)) {
 
731
                                                        message(MESS_ERROR, "Date format %s is too long\n",
 
732
                                                                        log->dateformat);
 
733
                                                        hasErrors = 1;
 
734
                                                        break;
 
735
                                                }
 
736
                                                dformat[i++] = *(dext++);
 
737
                                                dformat[i] = *dext;
 
738
                                                break;
 
739
                                        default:
 
740
                                                dformat[i++] = *dext;
 
741
                                                dformat[i] = '%';
 
742
                                                dext_pattern[j++] = *dext;
 
743
                                                break;
 
744
                                }
 
745
                        } else {
 
746
                                dformat[i] = *dext;
 
747
                                dext_pattern[j++] = *dext;
 
748
                        }
 
749
                        ++i;
 
750
                        ++dext;
 
751
                }
 
752
                dformat[i] = '\0';
 
753
                message(MESS_DEBUG, "Converted '%s' -> '%s'\n", log->dateformat, dformat);
 
754
                strftime(dext_str, sizeof(dext_str), dformat, &now);
 
755
        } else {
 
756
                /* The default dateformat and glob pattern */
 
757
                strftime(dext_str, sizeof(dext_str), "-%Y%m%d", &now);
 
758
                strncpy(dext_pattern, "-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]",
 
759
                                sizeof(dext_pattern));
 
760
                dext_pattern[PATTERN_LEN - 1] = '\0';
 
761
        }
 
762
        message(MESS_DEBUG, "dateext suffix '%s'\n", dext_str);
 
763
        message(MESS_DEBUG, "glob pattern '%s'\n", dext_pattern);
 
764
 
 
765
    /* First compress the previous log when necessary */
 
766
    if (log->flags & LOG_FLAG_COMPRESS &&
 
767
        log->flags & LOG_FLAG_DELAYCOMPRESS) {
 
768
        if (log->flags & LOG_FLAG_DATEEXT) {
 
769
                /* glob for uncompressed files with our pattern */
 
770
                if (asprintf(&glob_pattern, "%s/%s%s%s", rotNames->dirName,
 
771
                                        rotNames->baseName, dext_pattern, fileext) < 0) {
 
772
                        message(MESS_ERROR, "could not allocate glob pattern memory\n");
 
773
                }
 
774
            rc = glob(glob_pattern, 0, globerr, &globResult);
 
775
            if (!rc && globResult.gl_pathc > 0) {
 
776
                for (i = 0; i < globResult.gl_pathc && !hasErrors; i++) {
 
777
                    struct stat sbprev;
 
778
 
 
779
                        snprintf(oldName, PATH_MAX, "%s", (globResult.gl_pathv)[i]);
 
780
                        if (stat(oldName, &sbprev)) {
 
781
                        message(MESS_DEBUG,
 
782
                                "previous log %s does not exist\n",
 
783
                                oldName);
 
784
                    } else {
 
785
                        hasErrors = compressLogFile(oldName, log, &sbprev);
 
786
                    }
 
787
                }
 
788
            } else {
 
789
                message(MESS_DEBUG,
 
790
                        "glob finding logs to compress failed\n");
 
791
                /* fallback to old behaviour */
 
792
                snprintf(oldName, PATH_MAX, "%s/%s.%d%s", rotNames->dirName,
 
793
                        rotNames->baseName, logStart, fileext);
 
794
            }
 
795
            globfree(&globResult);
 
796
            free(glob_pattern);
 
797
        } else {
 
798
            struct stat sbprev;
 
799
 
 
800
            snprintf(oldName, PATH_MAX, "%s/%s.%d%s", rotNames->dirName,
 
801
                    rotNames->baseName, logStart, fileext);
 
802
            if (stat(oldName, &sbprev)) {
 
803
                message(MESS_DEBUG, "previous log %s does not exist\n",
 
804
                        oldName);
 
805
            } else {
 
806
                hasErrors = compressLogFile(oldName, log, &sbprev);
 
807
            }
 
808
        }
 
809
    }
 
810
 
 
811
    rotNames->firstRotated =
 
812
        malloc(strlen(rotNames->dirName) + strlen(rotNames->baseName) +
 
813
               strlen(fileext) + strlen(compext) + 30);
 
814
 
 
815
    if (log->flags & LOG_FLAG_DATEEXT) {
 
816
        /* glob for compressed files with our pattern
 
817
         * and compress ext */
 
818
        if (asprintf(&glob_pattern, "%s/%s%s%s%s", rotNames->dirName,
 
819
                                rotNames->baseName, dext_pattern, fileext, compext) < 0) {
 
820
                message(MESS_ERROR, "could not allocate glob pattern memory\n");
 
821
        }
 
822
        rc = glob(glob_pattern, 0, globerr, &globResult);
 
823
        if (!rc) {
 
824
            /* search for files to drop, if we find one remember it,
 
825
             * if we find another one mail and remove the first and
 
826
             * remember the second and so on */
 
827
            struct stat fst_buf;
 
828
            int mail_out = -1;
 
829
            /* remove the first (n - rotateCount) matches
 
830
             * no real rotation needed, since the files have
 
831
             * the date in their name */
 
832
            for (i = 0; i < globResult.gl_pathc; i++) {
 
833
                if (!stat((globResult.gl_pathv)[i], &fst_buf)) {
 
834
                    if ((i <= ((int) globResult.gl_pathc - rotateCount))
 
835
                        || ((log->rotateAge > 0)
 
836
                            &&
 
837
                            (((nowSecs - fst_buf.st_mtime) / 60 / 60 / 24)
 
838
                             > log->rotateAge))) {
 
839
                        if (mail_out != -1) {
 
840
                            char *mailFilename =
 
841
                                (globResult.gl_pathv)[mail_out];
 
842
                            if (!hasErrors && log->logAddress)
 
843
                                hasErrors =
 
844
                                    mailLogWrapper(mailFilename,
 
845
                                                   mailCommand, logNum,
 
846
                                                   log);
 
847
                            if (!hasErrors)
 
848
                                message(MESS_DEBUG, "removing %s\n", mailFilename);
 
849
                                hasErrors = removeLogFile(mailFilename, log);
 
850
                        }
 
851
                        mail_out = i;
 
852
                    }
 
853
                }
 
854
            }
 
855
            if (mail_out != -1) {
 
856
                /* oldName is oldest Backup found (for unlink later) */
 
857
                snprintf(oldName, PATH_MAX, "%s", (globResult.gl_pathv)[mail_out]);
 
858
                strcpy(rotNames->disposeName, oldName);
 
859
            } else {
 
860
                free(rotNames->disposeName);
 
861
                rotNames->disposeName = NULL;
 
862
            }
 
863
        } else {
 
864
            message(MESS_DEBUG, "glob finding old rotated logs failed\n");
 
865
            free(rotNames->disposeName);
 
866
            rotNames->disposeName = NULL;
 
867
        }
 
868
        /* firstRotated is most recently created/compressed rotated log */
 
869
        sprintf(rotNames->firstRotated, "%s/%s%s%s%s",
 
870
                rotNames->dirName, rotNames->baseName, dext_str, fileext, compext);
 
871
        globfree(&globResult);
 
872
        free(glob_pattern);
 
873
    } else {
 
874
        if (log->rotateAge) {
 
875
            struct stat fst_buf;
 
876
            for (i = 1; i <= rotateCount + 1; i++) {
 
877
                snprintf(oldName, PATH_MAX, "%s/%s.%d%s%s", rotNames->dirName,
 
878
                        rotNames->baseName, i, fileext, compext);
 
879
                if (!stat(oldName, &fst_buf)
 
880
                    && (((nowSecs - fst_buf.st_mtime) / 60 / 60 / 24)
 
881
                        > log->rotateAge)) {
 
882
                    char *mailFilename = oldName;
 
883
                    if (!hasErrors && log->logAddress)
 
884
                        hasErrors =
 
885
                            mailLogWrapper(mailFilename, mailCommand,
 
886
                                           logNum, log);
 
887
                    if (!hasErrors)
 
888
                        hasErrors = removeLogFile(mailFilename, log);
 
889
                }
 
890
            }
 
891
        }
 
892
 
 
893
        snprintf(oldName, PATH_MAX, "%s/%s.%d%s%s", rotNames->dirName,
 
894
                rotNames->baseName, logStart + rotateCount, fileext,
 
895
                compext);
 
896
        strcpy(newName, oldName);
 
897
 
 
898
        strcpy(rotNames->disposeName, oldName);
 
899
 
 
900
        sprintf(rotNames->firstRotated, "%s/%s.%d%s%s", rotNames->dirName,
 
901
                rotNames->baseName, logStart, fileext,
 
902
                (log->flags & LOG_FLAG_DELAYCOMPRESS) ? "" : compext);
 
903
 
 
904
#ifdef WITH_SELINUX
 
905
        if (selinux_enabled) {
 
906
            security_context_t oldContext = NULL;
 
907
            if (getfilecon_raw(log->files[logNum], &oldContext) > 0) {
 
908
                        if (getfscreatecon_raw(&prev_context) < 0) {
 
909
                                message(MESS_ERROR,
 
910
                                        "getting default context: %s\n",
 
911
                                        strerror(errno));
 
912
                                if (selinux_enforce) {
 
913
                                        freecon(oldContext);
 
914
                                        return 1;
 
915
                                }
 
916
                        }
 
917
                        if (setfscreatecon_raw(oldContext) < 0) {
 
918
                                message(MESS_ERROR,
 
919
                                        "setting file context %s to %s: %s\n",
 
920
                                        log->files[logNum], oldContext,
 
921
                                        strerror(errno));
 
922
                                if (selinux_enforce) {
 
923
                                        freecon(oldContext);
 
924
                                        return 1;
 
925
                                }
 
926
                        }
 
927
                        freecon(oldContext);
 
928
            } else {
 
929
                if (errno != ENOENT && errno != ENOTSUP) {
 
930
                        message(MESS_ERROR, "getting file context %s: %s\n",
 
931
                                log->files[logNum], strerror(errno));
 
932
                        if (selinux_enforce) {
 
933
                                return 1;
 
934
                        }
 
935
                }
 
936
            }
 
937
        }
 
938
#endif
 
939
        for (i = rotateCount + logStart - 1; (i >= 0) && !hasErrors; i--) {
 
940
            tmp = newName;
 
941
            newName = oldName;
 
942
            oldName = tmp;
 
943
                snprintf(oldName, PATH_MAX, "%s/%s.%d%s%s", rotNames->dirName,
 
944
                    rotNames->baseName, i, fileext, compext);
 
945
 
 
946
            message(MESS_DEBUG,
 
947
                    "renaming %s to %s (rotatecount %d, logstart %d, i %d), \n",
 
948
                    oldName, newName, rotateCount, logStart, i);
 
949
 
 
950
            if (!debug && rename(oldName, newName)) {
 
951
                if (errno == ENOENT) {
 
952
                    message(MESS_DEBUG, "old log %s does not exist\n",
 
953
                            oldName);
 
954
                } else {
 
955
                    message(MESS_ERROR, "error renaming %s to %s: %s\n",
 
956
                            oldName, newName, strerror(errno));
 
957
                    hasErrors = 1;
 
958
                }
 
959
            }
 
960
        }
 
961
    }                           /* !LOG_FLAG_DATEEXT */
 
962
 
 
963
        if (log->flags & LOG_FLAG_DATEEXT) {
 
964
                char *destFile = alloca(PATH_MAX);
 
965
                struct stat fst_buf;
 
966
 
 
967
                if (asprintf(&(rotNames->finalName), "%s/%s%s%s", rotNames->dirName,
 
968
                                        rotNames->baseName, dext_str, fileext) < 0) {
 
969
                        message(MESS_ERROR, "could not allocate finalName memory\n");
 
970
                }
 
971
                snprintf(destFile, PATH_MAX, "%s%s", rotNames->finalName, compext);
 
972
                if (!stat(destFile, &fst_buf)) {
 
973
                        message(MESS_DEBUG,
 
974
                                        "destination %s already exists, skipping rotation\n",
 
975
                                        rotNames->firstRotated);
 
976
                        hasErrors = 1;
 
977
                }
 
978
        } else {
 
979
                /* note: the gzip extension is *not* used here! */
 
980
                if (asprintf(&(rotNames->finalName), "%s/%s.%d%s", rotNames->dirName,
 
981
                                        rotNames->baseName, logStart, fileext) < 0) {
 
982
                        message(MESS_ERROR, "could not allocate finalName memory\n");
 
983
                }
 
984
        }
 
985
 
 
986
    /* if the last rotation doesn't exist, that's okay */
 
987
    if (!debug && rotNames->disposeName
 
988
        && access(rotNames->disposeName, F_OK)) {
 
989
        message(MESS_DEBUG,
 
990
                "log %s doesn't exist -- won't try to " "dispose of it\n",
 
991
                rotNames->disposeName);
 
992
        free(rotNames->disposeName);
 
993
        rotNames->disposeName = NULL;
 
994
    }
 
995
 
 
996
    return hasErrors;
 
997
}
 
998
 
 
999
int rotateSingleLog(struct logInfo *log, int logNum, struct logState *state,
 
1000
                    struct logNames *rotNames)
 
1001
{
 
1002
    int hasErrors = 0;
 
1003
    struct stat sb;
 
1004
    int fd;
 
1005
#ifdef WITH_SELINUX
 
1006
        security_context_t savedContext = NULL;
 
1007
#endif
 
1008
 
 
1009
    if (!state->doRotate)
 
1010
        return 0;
 
1011
 
 
1012
    if (!hasErrors) {
 
1013
 
 
1014
        if (!(log->flags & (LOG_FLAG_COPYTRUNCATE | LOG_FLAG_COPY))) {
 
1015
#ifdef WITH_SELINUX
 
1016
                if (selinux_enabled) {
 
1017
                        security_context_t oldContext = NULL;
 
1018
                        int fdcurr = -1;
 
1019
 
 
1020
                        if ((fdcurr = open(log->files[logNum], O_RDWR)) < 0) {
 
1021
                                message(MESS_ERROR, "error opening %s: %s\n",
 
1022
                                                log->files[logNum],
 
1023
                                        strerror(errno));
 
1024
                                return 1;
 
1025
                        }
 
1026
                        if (fgetfilecon_raw(fdcurr, &oldContext) >= 0) {
 
1027
                                if (getfscreatecon_raw(&savedContext) < 0) {
 
1028
                                        message(MESS_ERROR,
 
1029
                                                "getting default context: %s\n",
 
1030
                                                strerror(errno));
 
1031
                                        if (selinux_enforce) {
 
1032
                                                freecon(oldContext);
 
1033
                                                if (close(fdcurr) < 0)
 
1034
                                                        message(MESS_ERROR, "error closing file %s",
 
1035
                                                                        log->files[logNum]);
 
1036
                                                return 1;
 
1037
                                        }
 
1038
                                }
 
1039
                                if (setfscreatecon_raw(oldContext) < 0) {
 
1040
                                        message(MESS_ERROR,
 
1041
                                                "setting file context %s to %s: %s\n",
 
1042
                                                log->files[logNum], oldContext, strerror(errno));
 
1043
                                        if (selinux_enforce) {
 
1044
                                                freecon(oldContext);
 
1045
                                                if (close(fdcurr) < 0)
 
1046
                                                        message(MESS_ERROR, "error closing file %s",
 
1047
                                                                        log->files[logNum]);
 
1048
                                                return 1;
 
1049
                                        }
 
1050
                                }
 
1051
                                message(MESS_DEBUG, "fscreate context set to %s\n",
 
1052
                                                oldContext);
 
1053
                                freecon(oldContext);
 
1054
                        } else {
 
1055
                                if (errno != ENOTSUP) {
 
1056
                                        message(MESS_ERROR, "getting file context %s: %s\n",
 
1057
                                                log->files[logNum], strerror(errno));
 
1058
                                        if (selinux_enforce) {
 
1059
                                                if (close(fdcurr) < 0)
 
1060
                                                        message(MESS_ERROR, "error closing file %s",
 
1061
                                                                        log->files[logNum]);
 
1062
                                                return 1;
 
1063
                                        }
 
1064
                                }
 
1065
                        }
 
1066
                        if (close(fdcurr) < 0)
 
1067
                                message(MESS_ERROR, "error closing file %s",
 
1068
                                                log->files[logNum]);
 
1069
                }
 
1070
#endif
 
1071
                message(MESS_DEBUG, "renaming %s to %s\n", log->files[logNum],
 
1072
                    rotNames->finalName);
 
1073
            if (!debug && !hasErrors &&
 
1074
                rename(log->files[logNum], rotNames->finalName)) {
 
1075
                message(MESS_ERROR, "failed to rename %s to %s: %s\n",
 
1076
                        log->files[logNum], rotNames->finalName,
 
1077
                        strerror(errno));
 
1078
            }
 
1079
 
 
1080
            if (!log->rotateCount) {
 
1081
                rotNames->disposeName =
 
1082
                    realloc(rotNames->disposeName,
 
1083
                            strlen(rotNames->dirName) +
 
1084
                            strlen(rotNames->baseName) +
 
1085
                            strlen(log->files[logNum]) + 10);
 
1086
                sprintf(rotNames->disposeName, "%s%s", rotNames->finalName,
 
1087
                        (log->compress_ext
 
1088
                         && (log->flags & LOG_FLAG_COMPRESS)) ? log->
 
1089
                        compress_ext : "");
 
1090
                message(MESS_DEBUG, "disposeName will be %s\n",
 
1091
                        rotNames->disposeName);
 
1092
            }
 
1093
        }
 
1094
 
 
1095
        if (!hasErrors && log->flags & LOG_FLAG_CREATE &&
 
1096
            !(log->flags & (LOG_FLAG_COPYTRUNCATE | LOG_FLAG_COPY))) {
 
1097
            if (log->createUid == NO_UID)
 
1098
                sb.st_uid = state->sb.st_uid;
 
1099
            else
 
1100
                sb.st_uid = log->createUid;
 
1101
 
 
1102
            if (log->createGid == NO_GID)
 
1103
                sb.st_gid = state->sb.st_gid;
 
1104
            else
 
1105
                sb.st_gid = log->createGid;
 
1106
 
 
1107
            if (log->createMode == NO_MODE)
 
1108
                sb.st_mode = state->sb.st_mode & 0777;
 
1109
            else
 
1110
                sb.st_mode = log->createMode;
 
1111
 
 
1112
            message(MESS_DEBUG, "creating new %s mode = 0%o uid = %d "
 
1113
                    "gid = %d\n", log->files[logNum], (unsigned int) sb.st_mode,
 
1114
                    (int) sb.st_uid, (int) sb.st_gid);
 
1115
 
 
1116
            if (!debug) {
 
1117
                        fd = createOutputFile(log->files[logNum], O_CREAT | O_RDWR,
 
1118
                                                  &sb);
 
1119
                        if (fd < 0)
 
1120
                                hasErrors = 1;
 
1121
                        else
 
1122
                                close(fd);
 
1123
            }
 
1124
        }
 
1125
#ifdef WITH_SELINUX
 
1126
        if (selinux_enabled) {
 
1127
            setfscreatecon_raw(savedContext);
 
1128
                freecon(savedContext);
 
1129
                savedContext = NULL;
 
1130
        }
 
1131
#endif
 
1132
 
 
1133
        if (!hasErrors
 
1134
            && log->flags & (LOG_FLAG_COPYTRUNCATE | LOG_FLAG_COPY))
 
1135
            hasErrors =
 
1136
                copyTruncate(log->files[logNum], rotNames->finalName,
 
1137
                             &state->sb, log->flags);
 
1138
 
 
1139
    }
 
1140
    return hasErrors;
 
1141
}
 
1142
 
 
1143
int postrotateSingleLog(struct logInfo *log, int logNum, struct logState *state,
 
1144
                        struct logNames *rotNames)
 
1145
{
 
1146
    int hasErrors = 0;
 
1147
 
 
1148
    if (!state->doRotate)
 
1149
        return 0;
 
1150
 
 
1151
    if ((log->flags & LOG_FLAG_COMPRESS) &&
 
1152
        !(log->flags & LOG_FLAG_DELAYCOMPRESS)) {
 
1153
        hasErrors = compressLogFile(rotNames->finalName, log, &state->sb);
 
1154
    }
 
1155
 
 
1156
    if (!hasErrors && log->logAddress) {
 
1157
        char *mailFilename;
 
1158
 
 
1159
        if (log->flags & LOG_FLAG_MAILFIRST)
 
1160
            mailFilename = rotNames->firstRotated;
 
1161
        else
 
1162
            mailFilename = rotNames->disposeName;
 
1163
 
 
1164
        if (mailFilename)
 
1165
            hasErrors =
 
1166
                mailLogWrapper(mailFilename, mailCommand, logNum, log);
 
1167
    }
 
1168
 
 
1169
    if (!hasErrors && rotNames->disposeName)
 
1170
        hasErrors = removeLogFile(rotNames->disposeName, log);
 
1171
 
 
1172
#ifdef WITH_SELINUX
 
1173
        if (selinux_enabled) {
 
1174
                setfscreatecon_raw(prev_context);
 
1175
                freecon(prev_context);
 
1176
                prev_context = NULL;
 
1177
        }
 
1178
#endif
 
1179
    return hasErrors;
 
1180
}
 
1181
 
 
1182
int rotateLogSet(struct logInfo *log, int force)
 
1183
{
 
1184
    int i, j;
 
1185
    int hasErrors = 0;
 
1186
    int logHasErrors[log->numFiles];
 
1187
    int numRotated = 0;
 
1188
    struct logState **state;
 
1189
    struct logNames **rotNames;
 
1190
 
 
1191
    if (force)
 
1192
        log->criterium = ROT_FORCE;
 
1193
 
 
1194
    message(MESS_DEBUG, "\nrotating pattern: %s ", log->pattern);
 
1195
    switch (log->criterium) {
 
1196
    case ROT_DAYS:
 
1197
        message(MESS_DEBUG, "after %d days ", log->threshhold);
 
1198
        break;
 
1199
    case ROT_WEEKLY:
 
1200
        message(MESS_DEBUG, "weekly ");
 
1201
        break;
 
1202
    case ROT_MONTHLY:
 
1203
        message(MESS_DEBUG, "monthly ");
 
1204
        break;
 
1205
    case ROT_YEARLY:
 
1206
        message(MESS_DEBUG, "yearly ");
 
1207
        break;
 
1208
    case ROT_SIZE:
 
1209
        message(MESS_DEBUG, "%d bytes ", log->threshhold);
 
1210
        break;
 
1211
    case ROT_FORCE:
 
1212
        message(MESS_DEBUG, "forced from command line ");
 
1213
        break;
 
1214
    }
 
1215
 
 
1216
    if (log->rotateCount)
 
1217
        message(MESS_DEBUG, "(%d rotations)\n", log->rotateCount);
 
1218
    else
 
1219
        message(MESS_DEBUG, "(no old logs will be kept)\n");
 
1220
 
 
1221
    if (log->oldDir)
 
1222
        message(MESS_DEBUG, "olddir is %s, ", log->oldDir);
 
1223
 
 
1224
    if (log->flags & LOG_FLAG_IFEMPTY)
 
1225
        message(MESS_DEBUG, "empty log files are rotated, ");
 
1226
    else
 
1227
        message(MESS_DEBUG, "empty log files are not rotated, ");
 
1228
 
 
1229
    if (log->minsize) 
 
1230
        message(MESS_DEBUG, "only log files >= %d bytes are rotated, ", log->minsize);
 
1231
 
 
1232
    if (log->logAddress) {
 
1233
        message(MESS_DEBUG, "old logs mailed to %s\n", log->logAddress);
 
1234
    } else {
 
1235
        message(MESS_DEBUG, "old logs are removed\n");
 
1236
    }
 
1237
 
 
1238
    for (i = 0; i < log->numFiles; i++) {
 
1239
        logHasErrors[i] = findNeedRotating(log, i);
 
1240
        hasErrors |= logHasErrors[i];
 
1241
 
 
1242
        /* sure is a lot of findStating going on .. */
 
1243
        if ((findState(log->files[i]))->doRotate)
 
1244
            numRotated++;
 
1245
    }
 
1246
 
 
1247
    if (log->first) {
 
1248
        if (!numRotated) {
 
1249
            message(MESS_DEBUG, "not running first action script, "
 
1250
                    "since no logs will be rotated\n");
 
1251
        } else {
 
1252
            message(MESS_DEBUG, "running first action script\n");
 
1253
            if (runScript(log->pattern, log->first)) {
 
1254
                message(MESS_ERROR, "error running first action script "
 
1255
                        "for %s\n", log->pattern);
 
1256
                hasErrors = 1;
 
1257
                /* finish early, firstaction failed, affects all logs in set */
 
1258
                return hasErrors;
 
1259
            }
 
1260
        }
 
1261
    }
 
1262
 
 
1263
    state = malloc(log->numFiles * sizeof(struct logState *));
 
1264
    rotNames = malloc(log->numFiles * sizeof(struct logNames *));
 
1265
 
 
1266
    for (j = 0;
 
1267
         (!(log->flags & LOG_FLAG_SHAREDSCRIPTS) && j < log->numFiles)
 
1268
         || ((log->flags & LOG_FLAG_SHAREDSCRIPTS) && j < 1); j++) {
 
1269
 
 
1270
        for (i = j;
 
1271
             ((log->flags & LOG_FLAG_SHAREDSCRIPTS) && i < log->numFiles)
 
1272
             || (!(log->flags & LOG_FLAG_SHAREDSCRIPTS) && i == j); i++) {
 
1273
            state[i] = findState(log->files[i]);
 
1274
 
 
1275
            rotNames[i] = malloc(sizeof(struct logNames));
 
1276
            memset(rotNames[i], 0, sizeof(struct logNames));
 
1277
 
 
1278
            logHasErrors[i] |=
 
1279
                prerotateSingleLog(log, i, state[i], rotNames[i]);
 
1280
            hasErrors |= logHasErrors[i];
 
1281
        }
 
1282
 
 
1283
        if (log->pre
 
1284
            && (! ( (logHasErrors[j] && !(log->flags & LOG_FLAG_SHAREDSCRIPTS))
 
1285
                   || (hasErrors && (log->flags & LOG_FLAG_SHAREDSCRIPTS)) ) )) {
 
1286
            if (!numRotated) {
 
1287
                message(MESS_DEBUG, "not running prerotate script, "
 
1288
                        "since no logs will be rotated\n");
 
1289
            } else {
 
1290
                message(MESS_DEBUG, "running prerotate script\n");
 
1291
                if (runScript(log->pattern, log->pre)) {
 
1292
                    if (log->flags & LOG_FLAG_SHAREDSCRIPTS)
 
1293
                        message(MESS_ERROR,
 
1294
                                "error running shared prerotate script "
 
1295
                                "for '%s'\n", log->pattern);
 
1296
                    else {
 
1297
                        message(MESS_ERROR,
 
1298
                                "error running non-shared prerotate script "
 
1299
                                "for %s of '%s'\n", log->files[j], log->pattern);
 
1300
                    }
 
1301
                    logHasErrors[j] = 1;
 
1302
                    hasErrors = 1;
 
1303
                }
 
1304
            }
 
1305
        }
 
1306
 
 
1307
        for (i = j;
 
1308
             ((log->flags & LOG_FLAG_SHAREDSCRIPTS) && i < log->numFiles)
 
1309
             || (!(log->flags & LOG_FLAG_SHAREDSCRIPTS) && i == j); i++) {
 
1310
            if (! ( (logHasErrors[i] && !(log->flags & LOG_FLAG_SHAREDSCRIPTS))
 
1311
                   || (hasErrors && (log->flags & LOG_FLAG_SHAREDSCRIPTS)) ) ) {
 
1312
                logHasErrors[i] |=
 
1313
                    rotateSingleLog(log, i, state[i], rotNames[i]);
 
1314
                hasErrors |= logHasErrors[i];
 
1315
            }
 
1316
        }
 
1317
 
 
1318
        if (log->post
 
1319
            && (! ( (logHasErrors[j] && !(log->flags & LOG_FLAG_SHAREDSCRIPTS))
 
1320
                   || (hasErrors && (log->flags & LOG_FLAG_SHAREDSCRIPTS)) ) )) {
 
1321
            if (!numRotated) {
 
1322
                message(MESS_DEBUG, "not running postrotate script, "
 
1323
                        "since no logs were rotated\n");
 
1324
            } else {
 
1325
                message(MESS_DEBUG, "running postrotate script\n");
 
1326
                if (runScript(log->pattern, log->post)) {
 
1327
                    if (log->flags & LOG_FLAG_SHAREDSCRIPTS)
 
1328
                        message(MESS_ERROR,
 
1329
                                "error running shared postrotate script "
 
1330
                                "for '%s'\n", log->pattern);
 
1331
                    else {
 
1332
                        message(MESS_ERROR,
 
1333
                                "error running non-shared postrotate script "
 
1334
                                "for %s of '%s'\n", log->files[j], log->pattern);
 
1335
                    }
 
1336
                    logHasErrors[j] = 1;
 
1337
                    hasErrors = 1;
 
1338
                }
 
1339
            }
 
1340
        }
 
1341
 
 
1342
        for (i = j;
 
1343
             ((log->flags & LOG_FLAG_SHAREDSCRIPTS) && i < log->numFiles)
 
1344
             || (!(log->flags & LOG_FLAG_SHAREDSCRIPTS) && i == j); i++) {
 
1345
            if (! ( (logHasErrors[i] && !(log->flags & LOG_FLAG_SHAREDSCRIPTS))
 
1346
                   || (hasErrors && (log->flags & LOG_FLAG_SHAREDSCRIPTS)) ) ) {
 
1347
                logHasErrors[i] |=
 
1348
                    postrotateSingleLog(log, i, state[i], rotNames[i]);
 
1349
                hasErrors |= logHasErrors[i];
 
1350
            }
 
1351
        }
 
1352
 
 
1353
    }
 
1354
 
 
1355
    for (i = 0; i < log->numFiles; i++) {
 
1356
        free(rotNames[i]->firstRotated);
 
1357
        free(rotNames[i]->disposeName);
 
1358
        free(rotNames[i]->finalName);
 
1359
        free(rotNames[i]->dirName);
 
1360
        free(rotNames[i]->baseName);
 
1361
        free(rotNames[i]);
 
1362
    }
 
1363
    free(rotNames);
 
1364
    free(state);
 
1365
 
 
1366
    if (log->last) {
 
1367
        if (!numRotated) {
 
1368
            message(MESS_DEBUG, "not running last action script, "
 
1369
                    "since no logs will be rotated\n");
 
1370
        } else {
 
1371
            message(MESS_DEBUG, "running last action script\n");
 
1372
            if (runScript(log->pattern, log->last)) {
 
1373
                message(MESS_ERROR, "error running last action script "
 
1374
                        "for %s\n", log->pattern);
 
1375
                hasErrors = 1;
 
1376
            }
 
1377
        }
 
1378
    }
 
1379
 
 
1380
    return hasErrors;
 
1381
}
 
1382
 
 
1383
static int writeState(char *stateFilename)
 
1384
{
 
1385
    struct logState *p;
 
1386
    FILE *f;
 
1387
    char *chptr;
 
1388
    int i;
 
1389
 
 
1390
    f = fopen(stateFilename, "w");
 
1391
    if (!f) {
 
1392
        message(MESS_ERROR, "error creating state file %s: %s\n",
 
1393
                stateFilename, strerror(errno));
 
1394
        return 1;
 
1395
    }
 
1396
 
 
1397
    fprintf(f, "logrotate state -- version 2\n");
 
1398
 
 
1399
        for (i = 0; i < hashSize; i++) {
 
1400
                for (p = states[i]->head.lh_first; p != NULL;
 
1401
                                p = p->list.le_next) {
 
1402
                        fputc('"', f);
 
1403
                        for (chptr = p->fn; *chptr; chptr++) {
 
1404
                                switch (*chptr) {
 
1405
                                case '"':
 
1406
                                        fputc('\\', f);
 
1407
                                }
 
1408
 
 
1409
                                fputc(*chptr, f);
 
1410
                        }
 
1411
 
 
1412
                        fputc('"', f);
 
1413
                        fprintf(f, " %d-%d-%d\n",
 
1414
                        p->lastRotated.tm_year + 1900,
 
1415
                        p->lastRotated.tm_mon + 1,
 
1416
                        p->lastRotated.tm_mday);
 
1417
                }
 
1418
        }
 
1419
 
 
1420
        fclose(f);
 
1421
        return 0;
 
1422
}
 
1423
 
 
1424
static int readState(char *stateFilename)
 
1425
{
 
1426
    FILE *f;
 
1427
    char buf[1024];
 
1428
    const char **argv;
 
1429
    int argc;
 
1430
    int year, month, day;
 
1431
    int i;
 
1432
    int line = 0;
 
1433
    int error;
 
1434
    struct logState *st;
 
1435
    time_t lr_time;
 
1436
    struct stat f_stat;
 
1437
 
 
1438
    error = stat(stateFilename, &f_stat);
 
1439
 
 
1440
    if ((error && errno == ENOENT) || (!error && f_stat.st_size == 0)) {
 
1441
        /* create the file before continuing to ensure we have write
 
1442
           access to the file */
 
1443
        f = fopen(stateFilename, "w");
 
1444
        if (!f) {
 
1445
            message(MESS_ERROR, "error creating state file %s: %s\n",
 
1446
                    stateFilename, strerror(errno));
 
1447
            return 1;
 
1448
        }
 
1449
        fprintf(f, "logrotate state -- version 2\n");
 
1450
        fclose(f);
 
1451
        return 0;
 
1452
    } else if (error) {
 
1453
        message(MESS_ERROR, "error stat()ing state file %s: %s\n",
 
1454
                stateFilename, strerror(errno));
 
1455
        return 1;
 
1456
    }
 
1457
 
 
1458
    f = fopen(stateFilename, "r");
 
1459
    if (!f) {
 
1460
        message(MESS_ERROR, "error opening state file %s: %s\n",
 
1461
                stateFilename, strerror(errno));
 
1462
        return 1;
 
1463
    }
 
1464
 
 
1465
    if (!fgets(buf, sizeof(buf) - 1, f)) {
 
1466
        message(MESS_ERROR, "error reading top line of %s\n",
 
1467
                stateFilename);
 
1468
        fclose(f);
 
1469
        return 1;
 
1470
    }
 
1471
 
 
1472
    if (strcmp(buf, "logrotate state -- version 1\n") &&
 
1473
        strcmp(buf, "logrotate state -- version 2\n")) {
 
1474
        fclose(f);
 
1475
        message(MESS_ERROR, "bad top line in state file %s\n",
 
1476
                stateFilename);
 
1477
        return 1;
 
1478
    }
 
1479
 
 
1480
    line++;
 
1481
 
 
1482
    while (fgets(buf, sizeof(buf) - 1, f)) {
 
1483
        argv = NULL;
 
1484
        line++;
 
1485
        i = strlen(buf);
 
1486
        if (buf[i - 1] != '\n') {
 
1487
            message(MESS_ERROR, "line %d too long in state file %s\n",
 
1488
                    line, stateFilename);
 
1489
            fclose(f);
 
1490
            return 1;
 
1491
        }
 
1492
 
 
1493
        buf[i - 1] = '\0';
 
1494
 
 
1495
        if (i == 1)
 
1496
            continue;
 
1497
 
 
1498
        if (poptParseArgvString(buf, &argc, &argv) || (argc != 2) ||
 
1499
            (sscanf(argv[1], "%d-%d-%d", &year, &month, &day) != 3)) {
 
1500
            message(MESS_ERROR, "bad line %d in state file %s\n",
 
1501
                    line, stateFilename);
 
1502
                free(argv);
 
1503
            fclose(f);
 
1504
            return 1;
 
1505
        }
 
1506
 
 
1507
        /* Hack to hide earlier bug */
 
1508
        if ((year != 1900) && (year < 1996 || year > 2100)) {
 
1509
            message(MESS_ERROR,
 
1510
                    "bad year %d for file %s in state file %s\n", year,
 
1511
                    argv[0], stateFilename);
 
1512
            free(argv);
 
1513
            fclose(f);
 
1514
            return 1;
 
1515
        }
 
1516
 
 
1517
        if (month < 1 || month > 12) {
 
1518
            message(MESS_ERROR,
 
1519
                    "bad month %d for file %s in state file %s\n", month,
 
1520
                    argv[0], stateFilename);
 
1521
            free(argv);
 
1522
            fclose(f);
 
1523
            return 1;
 
1524
        }
 
1525
 
 
1526
        /* 0 to hide earlier bug */
 
1527
        if (day < 0 || day > 31) {
 
1528
            message(MESS_ERROR,
 
1529
                    "bad day %d for file %s in state file %s\n", day,
 
1530
                    argv[0], stateFilename);
 
1531
            free(argv);
 
1532
            fclose(f);
 
1533
            return 1;
 
1534
        }
 
1535
 
 
1536
        year -= 1900, month -= 1;
 
1537
 
 
1538
        if ((st = findState(argv[0])) == NULL)
 
1539
                return 1;
 
1540
 
 
1541
        st->lastRotated.tm_mon = month;
 
1542
        st->lastRotated.tm_mday = day;
 
1543
        st->lastRotated.tm_year = year;
 
1544
 
 
1545
        /* fill in the rest of the st->lastRotated fields */
 
1546
        lr_time = mktime(&st->lastRotated);
 
1547
        st->lastRotated = *localtime(&lr_time);
 
1548
 
 
1549
        free(argv);
 
1550
    }
 
1551
 
 
1552
    fclose(f);
 
1553
    return 0;
 
1554
}
 
1555
 
 
1556
int main(int argc, const char **argv)
 
1557
{
 
1558
    int force = 0;
 
1559
    char *stateFile = STATEFILE;
 
1560
    int rc = 0;
 
1561
    int arg;
 
1562
    const char **files;
 
1563
    poptContext optCon;
 
1564
        struct logInfo *log;
 
1565
        int state_file_ok = 1;
 
1566
 
 
1567
    struct poptOption options[] = {
 
1568
        {"debug", 'd', 0, 0, 'd',
 
1569
         "Don't do anything, just test (implies -v)"},
 
1570
        {"force", 'f', 0, &force, 0, "Force file rotation"},
 
1571
        {"mail", 'm', POPT_ARG_STRING, &mailCommand, 0,
 
1572
         "Command to send mail (instead of `" DEFAULT_MAIL_COMMAND "')",
 
1573
         "command"},
 
1574
        {"state", 's', POPT_ARG_STRING, &stateFile, 0,
 
1575
         "Path of state file",
 
1576
         "statefile"},
 
1577
        {"verbose", 'v', 0, 0, 'v', "Display messages during rotation"},
 
1578
        POPT_AUTOHELP {0, 0, 0, 0, 0}
 
1579
    };
 
1580
 
 
1581
    logSetLevel(MESS_NORMAL);
 
1582
    setlocale (LC_ALL, "");
 
1583
 
 
1584
    optCon = poptGetContext("logrotate", argc, argv, options, 0);
 
1585
    poptReadDefaultConfig(optCon, 1);
 
1586
    poptSetOtherOptionHelp(optCon, "[OPTION...] <configfile>");
 
1587
 
 
1588
    while ((arg = poptGetNextOpt(optCon)) >= 0) {
 
1589
        switch (arg) {
 
1590
        case 'd':
 
1591
            debug = 1;
 
1592
            /* fallthrough */
 
1593
        case 'v':
 
1594
            logSetLevel(MESS_DEBUG);
 
1595
            break;
 
1596
        }
 
1597
    }
 
1598
 
 
1599
    if (arg < -1) {
 
1600
        fprintf(stderr, "logrotate: bad argument %s: %s\n",
 
1601
                poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
 
1602
                poptStrerror(rc));
 
1603
        poptFreeContext(optCon);
 
1604
        return 2;
 
1605
    }
 
1606
 
 
1607
    files = poptGetArgs((poptContext) optCon);
 
1608
    if (!files) {
 
1609
        fprintf(stderr, "logrotate " VERSION
 
1610
                " - Copyright (C) 1995-2001 Red Hat, Inc.\n");
 
1611
        fprintf(stderr,
 
1612
                "This may be freely redistributed under the terms of "
 
1613
                "the GNU Public License\n\n");
 
1614
        poptPrintUsage(optCon, stderr, 0);
 
1615
        poptFreeContext(optCon);
 
1616
        exit(1);
 
1617
    }
 
1618
#ifdef WITH_SELINUX
 
1619
    selinux_enabled = (is_selinux_enabled() > 0);
 
1620
    selinux_enforce = security_getenforce();
 
1621
#endif
 
1622
 
 
1623
        TAILQ_INIT(&logs);
 
1624
 
 
1625
        if (readAllConfigPaths(files)) {
 
1626
        poptFreeContext(optCon);
 
1627
        exit(1);
 
1628
    }
 
1629
 
 
1630
    poptFreeContext(optCon);
 
1631
    nowSecs = time(NULL);
 
1632
 
 
1633
        if (allocateHash() != 0)
 
1634
                return 1;
 
1635
 
 
1636
        if (readState(stateFile))
 
1637
        {
 
1638
                state_file_ok = 0;
 
1639
                /* exit(1); */
 
1640
        }
 
1641
 
 
1642
        message(MESS_DEBUG, "\nHandling %d logs\n", numLogs);
 
1643
 
 
1644
        for (log = logs.tqh_first; log != NULL; log = log->list.tqe_next)
 
1645
                rc |= rotateLogSet(log, force);
 
1646
 
 
1647
        if (!debug && state_file_ok)
 
1648
                rc |= writeState(stateFile);
 
1649
 
 
1650
        if (!state_file_ok)
 
1651
        {
 
1652
                message(MESS_ERROR, "could not read state file, "
 
1653
                                "will not attempt to write into it\n");
 
1654
                rc = 1;
 
1655
        }
 
1656
        
 
1657
        return (rc != 0);
 
1658
}