~ubuntu-branches/ubuntu/trusty/logrotate/trusty

« back to all changes in this revision

Viewing changes to .pc/sharedscripts-519432.patch/logrotate.c

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