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

« back to all changes in this revision

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