~ubuntu-branches/ubuntu/edgy/logrotate/edgy

« back to all changes in this revision

Viewing changes to logrotate.c

  • Committer: Bazaar Package Importer
  • Author(s): Paul Martin
  • Date: 2004-06-11 13:51:34 UTC
  • mfrom: (1.1.1 upstream)
  • mto: This revision was merged to the branch mainline in revision 3.
  • Revision ID: james.westby@ubuntu.com-20040611135134-xwwlztb186794ikt
Tags: 3.7-2
* Added commented out stuff in debian/rules to build a 
  logrotate-selinux package should Russell's move to get libselinux1 
  made "base" fail.
* Patch: 21-taboo-to-debug, reduces the "Ignoring..." messages of
  the taboo filter from ERROR to DEBUG. (Closes: #249073)
* Patch: 30-config-h, changed to fix upstream bug with mailing
  logs. (Closes: #253837)

Show diffs side-by-side

added added

removed removed

Lines of Context:
8
8
#include <stdlib.h>
9
9
#include <string.h>
10
10
#include <sys/stat.h>
 
11
#include <sys/wait.h>
11
12
#include <time.h>
12
13
#include <unistd.h>
13
14
 
 
15
#ifdef WITH_SELINUX
 
16
#include <selinux/selinux.h>
 
17
static security_context_t prev_context=NULL;
 
18
int selinux_enabled=0;
 
19
#endif
 
20
 
14
21
#include "basenames.h"
15
22
#include "log.h"
16
23
#include "logrotate.h"
18
25
typedef struct {
19
26
    char * fn;
20
27
    struct tm lastRotated;      /* only tm.mon, tm_mday, tm_year are good! */
 
28
    struct stat sb;
 
29
    int doRotate;
21
30
} logState;
22
31
 
 
32
struct stateSet {
 
33
    logState * states;
 
34
    int numStates;
 
35
};
 
36
 
23
37
#define NO_MODE ((mode_t) -1)
24
38
#define NO_UID  ((uid_t) -1)
25
39
#define NO_GID  ((gid_t) -1)
26
40
 
27
41
int debug = 0;
28
42
char * mailCommand = DEFAULT_MAIL_COMMAND;
 
43
time_t nowSecs = 0;
29
44
 
30
 
static logState * findState(const char * fn, logState ** statesPtr, 
31
 
                            int * numStatesPtr) {
 
45
static logState * findState(const char * fn, struct stateSet * sip) {
32
46
    int i;
33
 
    logState * states = *statesPtr;
34
 
    int numStates = *numStatesPtr;
35
 
    time_t nowSecs = time(NULL);
 
47
    logState * states = sip->states;
 
48
    int numStates = sip->numStates;
36
49
    struct tm now = *localtime(&nowSecs);
37
50
    time_t lr_time;
38
51
 
44
57
        states = realloc(states, sizeof(*states) * numStates);
45
58
        states[i].fn = strdup(fn);
46
59
        memset(&states[i].lastRotated, 0, sizeof(states[i].lastRotated));
 
60
        states[i].doRotate = 0;
47
61
 
48
62
        states[i].lastRotated.tm_mon = now.tm_mon;
49
63
        states[i].lastRotated.tm_mday = now.tm_mday;
53
67
        lr_time = mktime(&states[i].lastRotated);
54
68
        states[i].lastRotated = *localtime(&lr_time);
55
69
 
56
 
        *statesPtr = states;
57
 
        *numStatesPtr = numStates;
 
70
        sip->states = states;
 
71
        sip->numStates = numStates;
58
72
    }
59
73
 
60
74
    return (states + i);
62
76
 
63
77
static int runScript(char * logfn, char * script) {
64
78
    int fd;
65
 
    char filespec[32];
66
 
    char * cmd;
 
79
    char *filespec;
67
80
    int rc;
 
81
    char buf[256];
68
82
 
69
83
    if (debug) {
70
84
        message(MESS_DEBUG, "running script with arg %s: \"%s\"\n", 
72
86
        return 0;
73
87
    }
74
88
 
75
 
    strcpy(filespec, "/tmp/logrotate.XXXXXX");
76
 
    if ((fd = mkstemp(filespec)) < 0 || fchmod(fd, 0700)) {
 
89
    filespec = buf;
 
90
    snprintf(buf, sizeof(buf), "%s/logrotate.XXXXXX", getenv("TMPDIR") ?: "/tmp");
 
91
    fd = -1;
 
92
    if (!filespec || (fd = mkstemp(filespec)) < 0 || fchmod(fd, 0700)) {
77
93
        message(MESS_DEBUG, "error creating %s: %s\n", filespec,
78
94
                strerror(errno));
79
 
        if (fd >= 0) close(fd);
 
95
        if (fd >= 0) {
 
96
            close(fd);
 
97
            unlink(filespec);
 
98
        }
80
99
        return -1;
81
100
    }
82
101
 
83
 
    if (write(fd, "#!/bin/sh\n\n", 11) != 11) {
84
 
        close(fd);
85
 
        unlink(filespec);
86
 
        return -1;
87
 
    }
88
 
    if (write(fd, script, strlen(script)) != strlen(script)) {
 
102
    if (write(fd, "#!/bin/sh\n\n", 11) != 11 ||
 
103
        write(fd, script, strlen(script)) != strlen(script)) {
 
104
        message(MESS_DEBUG, "error writing %s\n", filespec);
89
105
        close(fd);
90
106
        unlink(filespec);
91
107
        return -1;
93
109
 
94
110
    close(fd);
95
111
 
96
 
    cmd = alloca(strlen(filespec) + strlen(logfn) + 20);
97
 
    sprintf(cmd, "/bin/sh %s '%s'", filespec, logfn);
98
 
    rc = system(cmd);
 
112
    if (!fork()) {
 
113
        execlp(filespec, logfn, NULL);
 
114
        exit(1);
 
115
    }
 
116
 
 
117
    wait(&rc);
99
118
 
100
119
    unlink(filespec);
101
120
 
102
121
    return rc;
103
122
}
104
123
 
105
 
static int copyTruncate(char * currLog, char * saveLog, struct stat * sb) {
 
124
static int compressLogFile(char * name, logInfo * log) {
 
125
    char * compressedName;
 
126
    const char ** fullCommand;
 
127
    int inFile;
 
128
    int outFile;
 
129
    int i;
 
130
    int status;
 
131
 
 
132
    fullCommand = alloca(sizeof(*fullCommand) * 
 
133
                         (log->compress_options_count + 2));
 
134
    fullCommand[0] = log->compress_prog;
 
135
    for (i = 0; i < log->compress_options_count; i++)
 
136
        fullCommand[i + 1] = log->compress_options_list[i];
 
137
    fullCommand[log->compress_options_count + 1] = NULL;
 
138
    
 
139
    compressedName = alloca(strlen(name) + 
 
140
                            strlen(log->compress_ext) + 2);
 
141
    sprintf(compressedName, "%s%s", name, log->compress_ext);
 
142
 
 
143
    if ((inFile = open(name, O_RDONLY)) < 0) {
 
144
        message(MESS_ERROR, "unable to open %s for compression\n", name);
 
145
        return 1;
 
146
    }
 
147
    
 
148
    if ((outFile = open(compressedName, O_RDWR | O_CREAT | O_TRUNC, 0666)) < 0) {
 
149
        message(MESS_ERROR, "unable to open %s for compressed output\n", 
 
150
                compressedName);
 
151
        close(inFile);
 
152
        return 1;
 
153
    }
 
154
 
 
155
    message(MESS_DEBUG, "compressing log with: %s\n", fullCommand[0]);
 
156
 
 
157
    if (!fork()) {
 
158
        dup2(inFile, 0);
 
159
        close(inFile);
 
160
        dup2(outFile, 1);
 
161
        close(outFile);
 
162
 
 
163
        execvp(fullCommand[0], (void *) fullCommand);
 
164
        exit(1);
 
165
    }
 
166
 
 
167
    close(inFile);
 
168
    close(outFile);
 
169
 
 
170
    wait(&status);
 
171
 
 
172
    if (!WIFEXITED(status) || WEXITSTATUS(status)) {
 
173
        message(MESS_ERROR, "failed to compress log %s\n", name);
 
174
        return 1;
 
175
    }
 
176
 
 
177
    unlink(name);
 
178
 
 
179
    return 0;
 
180
}
 
181
 
 
182
static int mailLog(char * logFile, char * mailCommand, char * uncompressCommand, 
 
183
                   char * address, char * subject) {
 
184
    int mailInput;
 
185
    pid_t mailChild, uncompressChild;
 
186
    int mailStatus, uncompressStatus;
 
187
    int uncompressPipe[2];
 
188
    char * mailArgv[] = { mailCommand, "-s", subject, address, NULL };
 
189
    int rc = 0;
 
190
 
 
191
    if ((mailInput = open(logFile, O_RDONLY)) < 0) {
 
192
        message(MESS_ERROR, "failed to open %s for mailing: %s\n", logFile,
 
193
                strerror(errno));
 
194
        return 1;
 
195
    }
 
196
 
 
197
    if (uncompressCommand) {
 
198
        pipe(uncompressPipe);
 
199
        if (!(uncompressChild = fork())) {
 
200
            /* uncompress child */
 
201
            dup2(mailInput, 0);
 
202
            close(mailInput);
 
203
            dup2(uncompressPipe[1], 1);
 
204
            close(uncompressPipe[0]);
 
205
            close(uncompressPipe[1]);
 
206
 
 
207
            execlp(uncompressCommand, uncompressCommand, NULL);
 
208
            exit(1);
 
209
        }
 
210
 
 
211
        close(mailInput);
 
212
        mailInput = uncompressPipe[0];
 
213
        close(uncompressPipe[1]);
 
214
    }
 
215
 
 
216
    if (!(mailChild = fork())) {
 
217
        dup2(mailInput, 0);
 
218
        close(mailInput);
 
219
        close(1);
 
220
 
 
221
        execvp(mailArgv[0], mailArgv);
 
222
        exit(1);
 
223
    }
 
224
 
 
225
    close(mailInput);
 
226
 
 
227
    waitpid(mailChild, &mailStatus, 0);
 
228
 
 
229
    if (!WIFEXITED(mailStatus) || WEXITSTATUS(mailStatus)) {
 
230
        message(MESS_ERROR, "mail command failed for %s\n", logFile);
 
231
        rc = 1;
 
232
    }
 
233
 
 
234
    if (uncompressCommand) {
 
235
        waitpid(uncompressChild, &uncompressStatus, 0);
 
236
 
 
237
        if (!WIFEXITED(uncompressStatus) || WEXITSTATUS(uncompressStatus)) {
 
238
            message(MESS_ERROR, "uncompress command failed mailing %s\n", 
 
239
                    logFile);
 
240
            rc = 1;
 
241
        }
 
242
    }
 
243
 
 
244
    return rc;
 
245
}
 
246
 
 
247
static int copyTruncate(char * currLog, char * saveLog, struct stat * sb, int flags) {
106
248
    char buf[BUFSIZ];
107
249
    int fdcurr = -1, fdsave = -1;
108
250
    ssize_t cnt;
115
257
                strerror(errno));
116
258
            return 1;
117
259
        }
118
 
        if ((fdsave = open(saveLog, O_WRONLY | O_CREAT | O_TRUNC,
119
 
                sb->st_mode)) < 0) {
 
260
#ifdef WITH_SELINUX
 
261
        if ((selinux_enabled=(is_selinux_enabled()>0)))
 
262
          {
 
263
            security_context_t oldContext;
 
264
            if (fgetfilecon(fdcurr, &oldContext) >=0) {
 
265
              if (getfscreatecon(&prev_context) < 0) {
 
266
                message(MESS_ERROR, "error getting default context: %s\n", 
 
267
                        strerror(errno));
 
268
                freecon(oldContext);
 
269
                return 1;
 
270
              }
 
271
              if (setfscreatecon(oldContext) < 0) {
 
272
                message(MESS_ERROR, "error setting file context %s to %s: %s\n", 
 
273
                        saveLog, oldContext,strerror(errno));
 
274
                freecon(oldContext);
 
275
                return 1;
 
276
              }
 
277
              freecon(oldContext);
 
278
            } else {
 
279
              message(MESS_ERROR, "error getting file context %s: %s\n", currLog,
 
280
                      strerror(errno));
 
281
              return 1;
 
282
            }
 
283
          }
 
284
#endif
 
285
        fdsave = open(saveLog, O_WRONLY | O_CREAT | O_TRUNC,sb->st_mode);
 
286
#ifdef WITH_SELINUX
 
287
        if (selinux_enabled) {
 
288
          setfscreatecon(prev_context);
 
289
          if (prev_context!= NULL) {
 
290
            freecon(prev_context);
 
291
            prev_context=NULL;
 
292
          }
 
293
        }
 
294
#endif
 
295
        if (fdsave < 0) {
120
296
            message(MESS_ERROR, "error creating %s: %s\n", saveLog,
121
297
                strerror(errno));
122
298
            close(fdcurr);
123
299
            return 1;
124
300
        }
125
 
        if (fchmod(fdsave, sb->st_mode)) {
 
301
        if (fchmod(fdsave, (S_IRUSR | S_IWUSR) & sb->st_mode)) {
126
302
            message(MESS_ERROR, "error setting mode of %s: %s\n",
127
303
                saveLog, strerror(errno));
128
304
            close(fdcurr);
136
312
            close(fdsave);
137
313
            return 1;
138
314
        }
 
315
        if (fchmod(fdsave, sb->st_mode)) {
 
316
            message(MESS_ERROR, "error setting mode of %s: %s\n",
 
317
                saveLog, strerror(errno));
 
318
            close(fdcurr);
 
319
            close(fdsave);
 
320
            return 1;
 
321
        }
139
322
        while ((cnt = read(fdcurr, buf, sizeof(buf))) > 0) {
140
323
            if (write(fdsave, buf, cnt) != cnt) {
141
324
                message(MESS_ERROR, "error writing to %s: %s\n", 
154
337
        }
155
338
    }
156
339
 
157
 
    message(MESS_DEBUG, "truncating %s\n", currLog);
 
340
    if (flags & LOG_FLAG_COPYTRUNCATE) {
 
341
        message(MESS_DEBUG, "truncating %s\n", currLog);
158
342
 
159
 
    if (!debug) {
160
 
        if (ftruncate(fdcurr, 0)) {
161
 
            message(MESS_ERROR, "error truncating %s: %s\n", currLog,
162
 
                strerror(errno));
163
 
            close(fdcurr);
164
 
            close(fdsave);
165
 
            return 1;
166
 
        } else {
167
 
            close(fdcurr);
168
 
            close(fdsave);
169
 
        }
 
343
        if (!debug) {
 
344
            if (ftruncate(fdcurr, 0)) {
 
345
                message(MESS_ERROR, "error truncating %s: %s\n", currLog,
 
346
                    strerror(errno));
 
347
                close(fdcurr);
 
348
                close(fdsave);
 
349
                return 1;
 
350
            } else {
 
351
                close(fdcurr);
 
352
                close(fdsave);
 
353
            }
 
354
        }
 
355
    } else {
 
356
        message(MESS_DEBUG, "Not truncating %s\n", currLog);
 
357
        close(fdcurr);
 
358
        close(fdsave);
170
359
    }
171
360
 
172
361
    return 0;
173
362
}
174
363
 
175
 
int rotateSingleLog(logInfo * log, int logNum, logState ** statesPtr, 
176
 
                    int * numStatesPtr, FILE * errorFile) {
 
364
int findNeedRotating(logInfo * log, int logNum, struct stateSet * sip) {
177
365
    struct stat sb;
178
 
    time_t nowSecs = time(NULL);
 
366
    logState * state = NULL;
 
367
    struct tm now = *localtime(&nowSecs);
 
368
    
 
369
    message(MESS_DEBUG, "considering log %s\n", log->files[logNum]);
 
370
    
 
371
    if (stat(log->files[logNum], &sb)) {
 
372
        if ((log->flags & LOG_FLAG_MISSINGOK) && (errno == ENOENT)) {
 
373
            message(MESS_DEBUG, "  log %s does not exist -- skipping\n", 
 
374
                    log->files[logNum]);
 
375
            return 0;
 
376
        }
 
377
        message(MESS_ERROR, "stat of %s failed: %s\n", log->files[logNum],
 
378
                strerror(errno));
 
379
        return 1;
 
380
    }
 
381
 
 
382
    state = findState(log->files[logNum], sip);
 
383
    state->doRotate = 0;
 
384
    state->sb = sb;
 
385
 
 
386
    if (log->criterium == ROT_SIZE) {
 
387
        state->doRotate = (sb.st_size >= log->threshhold);
 
388
    } else if (log->criterium == ROT_FORCE) {
 
389
        /* user forced rotation of logs from command line */
 
390
        state->doRotate = 1;
 
391
    } else if (state->lastRotated.tm_year > now.tm_year || 
 
392
               (state->lastRotated.tm_year == now.tm_year && 
 
393
                (state->lastRotated.tm_mon > now.tm_mon ||
 
394
                 (state->lastRotated.tm_mon == now.tm_mon &&
 
395
                  state->lastRotated.tm_mday > now.tm_mday)))) {
 
396
        message(MESS_ERROR,
 
397
                "log %s last rotated in the future -- rotation forced\n",
 
398
                log->files[logNum]);
 
399
        state->doRotate = 1;
 
400
    } else if (state->lastRotated.tm_year != now.tm_year || 
 
401
               state->lastRotated.tm_mon != now.tm_mon ||
 
402
               state->lastRotated.tm_mday != now.tm_mday) {
 
403
        switch (log->criterium) {
 
404
          case ROT_WEEKLY:
 
405
            /* rotate if:
 
406
                  1) the current weekday is before the weekday of the
 
407
                     last rotation
 
408
                  2) more then a week has passed since the last
 
409
                     rotation */
 
410
            state->doRotate = ((now.tm_wday < state->lastRotated.tm_wday) ||
 
411
                               ((mktime(&now) - mktime(&state->lastRotated)) >
 
412
                                (7 * 24 * 3600)));
 
413
            break;
 
414
          case ROT_MONTHLY:
 
415
            /* rotate if the logs haven't been rotated this month or
 
416
               this year */
 
417
              state->doRotate = ((now.tm_mon != state->lastRotated.tm_mon) ||
 
418
                                 (now.tm_year != state->lastRotated.tm_year));
 
419
            break;
 
420
          case ROT_DAYS:
 
421
            /* FIXME: only days=1 is implemented!! */
 
422
              state->doRotate = 1;
 
423
              break;
 
424
        default:
 
425
            /* ack! */
 
426
            state->doRotate = 0;
 
427
            break;
 
428
        }
 
429
    }
 
430
 
 
431
    /* The notifempty flag overrides the normal criteria */
 
432
    if (!(log->flags & LOG_FLAG_IFEMPTY) && !sb.st_size)
 
433
        state->doRotate = 0;
 
434
    
 
435
    if (state->doRotate) {
 
436
        message(MESS_DEBUG, "  log needs rotating\n");
 
437
    } else {
 
438
        message(MESS_DEBUG, "  log does not need rotating\n");
 
439
    }
 
440
 
 
441
    return 0;
 
442
}
 
443
 
 
444
int rotateSingleLog(logInfo * log, int logNum, logState * state) {
179
445
    struct tm now = *localtime(&nowSecs);
180
446
    char * oldName, * newName = NULL;
181
447
    char * disposeName;
184
450
    char * compext = "";
185
451
    char * fileext = "";
186
452
    int hasErrors = 0;
187
 
    int doRotate = 0;
188
453
    int i;
189
454
    int fd;
190
 
    logState * state = NULL;
191
455
    uid_t createUid;
192
456
    gid_t createGid;
193
457
    mode_t createMode;
194
458
    char * baseName;
195
459
    char * dirName;
196
460
    char * firstRotated;
 
461
    size_t alloc_size;
197
462
    int rotateCount = log->rotateCount ? log->rotateCount : 1;
198
 
 
199
 
    /* Logs with rotateCounts of 0 are rotated to .1, then removed. This
 
463
    int logStart = (log->logStart == -1) ? 1 : log->logStart;
 
464
 
 
465
    if (!state->doRotate) return 0;
 
466
 
 
467
    /* Logs with rotateCounts of 0 are rotated once, then removed. This
200
468
       lets scripts run properly, and everything gets mailed properly. */
201
469
 
202
 
    message(MESS_DEBUG, "rotating file %s\n", log->files[logNum]);
203
 
 
 
470
    message(MESS_DEBUG, "rotating log %s, log->rotateCount is %d\n", log->files[logNum], log->rotateCount);
 
471
    
204
472
    if (log->flags & LOG_FLAG_COMPRESS) compext = log->compress_ext;
205
473
    
206
 
    if (stat(log->files[logNum], &sb)) {
207
 
        if ((log->flags & LOG_FLAG_MISSINGOK) && (errno == ENOENT)) {
208
 
            message(MESS_DEBUG, "file %s does not exist -- skipping\n", 
209
 
                    log->files[logNum]);
210
 
            return 0;
211
 
        }
212
 
        fprintf(errorFile, "stat of %s failed: %s\n", log->files[logNum],
 
474
    state->lastRotated = now;
 
475
    
 
476
    if (log->oldDir) {
 
477
        if (log->oldDir[0] != '/') {
 
478
            char *ld = ourDirName(log->files[logNum]);
 
479
            dirName = malloc(strlen(ld) + strlen(log->oldDir) + 2);
 
480
            sprintf(dirName, "%s/%s", ld, log->oldDir);
 
481
            free(ld);
 
482
        } else
 
483
          dirName = strdup(log->oldDir);
 
484
    } else
 
485
        dirName = ourDirName(log->files[logNum]);
 
486
 
 
487
    baseName = strdup(ourBaseName(log->files[logNum]));
 
488
 
 
489
    alloc_size = strlen(dirName) + strlen(baseName) + 
 
490
                 strlen(log->files[logNum]) + strlen(fileext) +
 
491
                 strlen(compext) + 10;
 
492
    
 
493
    oldName = alloca(alloc_size);
 
494
    newName = alloca(alloc_size);
 
495
    disposeName = alloca(alloc_size);
 
496
    
 
497
    if (log->extension &&
 
498
        strncmp(&baseName[strlen(baseName)-strlen(log->extension)],
 
499
                log->extension, strlen(log->extension)) == 0) {
 
500
        char *tempstr;
 
501
        
 
502
        fileext = log->extension;
 
503
        tempstr = calloc(strlen(baseName)-strlen(log->extension)+1, sizeof(char));
 
504
        strncat(tempstr, baseName,
 
505
                strlen(baseName)-strlen(log->extension));
 
506
        free(baseName);
 
507
        baseName = tempstr;
 
508
    }
 
509
    
 
510
    /* First compress the previous log when necessary */
 
511
    if (log->flags & LOG_FLAG_COMPRESS &&
 
512
        log->flags & LOG_FLAG_DELAYCOMPRESS) {
 
513
        struct stat sbprev;
 
514
        
 
515
        sprintf(oldName, "%s/%s.%d%s", dirName, baseName, logStart, fileext);
 
516
        if (stat(oldName, &sbprev)) {
 
517
            message(MESS_DEBUG, "previous log %s does not exist\n",
 
518
                    oldName);
 
519
        } else {
 
520
            hasErrors = compressLogFile(oldName, log);
 
521
        }
 
522
    }
 
523
    
 
524
    sprintf(oldName, "%s/%s.%d%s%s", dirName, baseName,
 
525
            logStart + rotateCount, fileext, compext);
 
526
    strcpy(newName, oldName);
 
527
    
 
528
    strcpy(disposeName, oldName);
 
529
    
 
530
    firstRotated = alloca(strlen(dirName) + strlen(baseName) +
 
531
                          strlen(fileext) + strlen(compext) + 30);
 
532
    sprintf(firstRotated, "%s/%s.%d%s%s", dirName, baseName,
 
533
            logStart, fileext, 
 
534
            (log->flags & LOG_FLAG_DELAYCOMPRESS) ? "" : compext);
 
535
    
 
536
#ifdef WITH_SELINUX
 
537
    if ((selinux_enabled=(is_selinux_enabled()>0))) {
 
538
      security_context_t oldContext=NULL;
 
539
      if (getfilecon(log->files[logNum], &oldContext)>0) {
 
540
        if (getfscreatecon(&prev_context) < 0) {
 
541
          message(MESS_ERROR, "error getting default context: %s\n", 
 
542
                  strerror(errno));
 
543
          freecon(oldContext);
 
544
          return 1;
 
545
        }
 
546
        if (setfscreatecon(oldContext) < 0) {
 
547
          message(MESS_ERROR, "error setting file context %s to %s: %s\n", 
 
548
                  log->files[logNum], oldContext,strerror(errno));
 
549
          freecon(oldContext);
 
550
          return 1;
 
551
        }
 
552
        freecon(oldContext);
 
553
      } else {
 
554
        message(MESS_ERROR, "error getting file context %s: %s\n", 
 
555
                log->files[logNum], 
213
556
                strerror(errno));
214
 
        hasErrors = 1;
215
 
    }
216
 
 
 
557
        return 1;
 
558
      }
 
559
    }
 
560
#endif
 
561
    for (i = rotateCount + logStart - 1; (i >= 0) && !hasErrors; i--) {
 
562
        tmp = newName;
 
563
        newName = oldName;
 
564
        oldName = tmp;
 
565
        sprintf(oldName, "%s/%s.%d%s%s", dirName, baseName, i,
 
566
                fileext, compext);
 
567
        
 
568
        message(MESS_DEBUG, "renaming %s to %s (rotatecount %d, logstart %d, i %d), \n", oldName, newName,
 
569
                rotateCount, logStart, i);
 
570
        
 
571
        if (!debug && rename(oldName, newName)) {
 
572
            if (errno == ENOENT) {
 
573
                message(MESS_DEBUG, "old log %s does not exist\n",
 
574
                        oldName);
 
575
            } else {
 
576
                message(MESS_ERROR, "error renaming %s to %s: %s\n",
 
577
                        oldName, newName, strerror(errno));
 
578
                hasErrors = 1;
 
579
            }
 
580
        }
 
581
    }
 
582
    
 
583
    finalName = oldName;
 
584
    
 
585
    /* note: the gzip extension is *not* used here! */
 
586
    sprintf(finalName, "%s/%s.%d%s", dirName, baseName, logStart, fileext);
 
587
    
 
588
    /* if the last rotation doesn't exist, that's okay */
 
589
    if (!debug && access(disposeName, F_OK)) {
 
590
        message(MESS_DEBUG, "log %s doesn't exist -- won't try to "
 
591
                "dispose of it\n", disposeName);
 
592
        disposeName = NULL;
 
593
    } 
 
594
    
 
595
    free(dirName);
 
596
    free(baseName);
 
597
    
217
598
    if (!hasErrors) {
218
 
        state = findState(log->files[logNum], statesPtr, numStatesPtr);
219
 
 
220
 
        if (log->criterium == ROT_SIZE) {
221
 
            doRotate = (sb.st_size >= log->threshhold);
222
 
        }
223
 
        else if (log->criterium == ROT_FORCE) {
224
 
            /* user forced rotation of logs from command line */
225
 
            doRotate = 1;
226
 
        } else if (state->lastRotated.tm_year > now.tm_year || 
227
 
                      (state->lastRotated.tm_year == now.tm_year && 
228
 
                          (state->lastRotated.tm_mon > now.tm_mon ||
229
 
                              (state->lastRotated.tm_mon == now.tm_mon &&
230
 
                               state->lastRotated.tm_mday > now.tm_mday)
231
 
                          )
232
 
                      )
233
 
                  ) {
234
 
            message(MESS_ERROR,
235
 
            "file %s last rotated in the future -- rotation forced\n",
236
 
            log->files[logNum]);
237
 
            doRotate = 1;
238
 
        } else if (state->lastRotated.tm_year != now.tm_year || 
239
 
                   state->lastRotated.tm_mon != now.tm_mon ||
240
 
                   state->lastRotated.tm_mday != now.tm_mday) {
241
 
            switch (log->criterium) {
242
 
              case ROT_WEEKLY:
243
 
                /* rotate if:
244
 
                      1) the current weekday is before the weekday of the
245
 
                         last rotation
246
 
                      2) more then a week has passed since the last
247
 
                         rotation 
248
 
                */
249
 
                doRotate = ((now.tm_wday < state->lastRotated.tm_wday) ||
250
 
                            ((mktime(&now) - mktime(&state->lastRotated)) >
251
 
                               (7 * 24 * 3600)));
252
 
                break;
253
 
              case ROT_MONTHLY:
254
 
                /* rotate if the logs haven't been rotated this month or
255
 
                   this year */
256
 
                doRotate = ((now.tm_mon != state->lastRotated.tm_mon) ||
257
 
                            (now.tm_year != state->lastRotated.tm_year));
258
 
                break;
259
 
              case ROT_DAYS:
260
 
                /* FIXME: only days=1 is implemented!! */
261
 
                doRotate = 1;
262
 
                break;
263
 
              default:
264
 
                /* ack! */
265
 
                doRotate = 0;
266
 
                break;
267
 
            }
268
 
        } else {
269
 
            doRotate = 0;
270
 
        }
271
 
    }
272
 
 
273
 
    /* The notifempty flag overrides the normal criteria */
274
 
    if (!(log->flags & LOG_FLAG_IFEMPTY) && !sb.st_size)
275
 
        doRotate = 0;
276
 
 
277
 
    if (!hasErrors && doRotate) {
278
 
        message(MESS_DEBUG, "log needs rotating\n");
279
 
 
280
 
        state->lastRotated = now;
281
 
 
282
 
        if (log->oldDir)
283
 
            dirName = strdup(log->oldDir);
284
 
        else
285
 
            dirName = ourDirName(log->files[logNum]);
286
 
        baseName = strdup(ourBaseName(log->files[logNum]));
287
 
 
288
 
        oldName = alloca(strlen(dirName) + strlen(baseName) + 
289
 
                            strlen(log->files[logNum]) + 10);
290
 
        newName = alloca(strlen(dirName) + strlen(baseName) + 
291
 
                            strlen(log->files[logNum]) + 10);
292
 
        disposeName = alloca(strlen(dirName) + strlen(baseName) + 
293
 
                            strlen(log->files[logNum]) + 10);
294
 
 
295
 
 
296
 
        if (log->extension)
297
 
            fileext = log->extension;
298
 
 
299
 
        if (log->extension &&
300
 
                strncmp(&baseName[strlen(baseName)-strlen(log->extension)],
301
 
                log->extension, strlen(log->extension)) == 0) {
302
 
            char *tempstr;
303
 
            tempstr = calloc(strlen(baseName)-strlen(log->extension)+1, sizeof(char));
304
 
            strncat(tempstr, baseName,
305
 
                    strlen(baseName)-strlen(log->extension));
306
 
            free(baseName);
307
 
            baseName = tempstr;
308
 
        }
309
 
 
310
 
        /* First compress the previous log when necessary */
311
 
        if (log->flags & LOG_FLAG_COMPRESS &&
312
 
                    log->flags & LOG_FLAG_DELAYCOMPRESS) {
313
 
            struct stat sbprev;
314
 
 
315
 
            sprintf(oldName, "%s/%s.1%s", dirName, baseName, fileext);
316
 
            if (stat(oldName, &sbprev)) {
317
 
                message(MESS_DEBUG, "previous log %s does not exist\n",
318
 
                                    oldName);
319
 
            } else {
320
 
                char * command;
321
 
 
322
 
                command = alloca(strlen(oldName) +
323
 
                                    strlen(log->compress_prog) + 1 + strlen(log->compress_options) + 20);
324
 
                sprintf(command, "%s %s '%s'", log->compress_prog, log->compress_options, oldName);
325
 
                message(MESS_DEBUG, "compressing previous log with: %s\n",
326
 
                                    command);
327
 
                if (!debug && system(command)) {
328
 
                    fprintf(errorFile,
329
 
                        "failed to compress previous log %s\n", oldName);
330
 
                    hasErrors = 1;
331
 
                }
332
 
            }
333
 
        }
334
 
 
335
 
        sprintf(oldName, "%s/%s.%d%s%s", dirName, baseName,
336
 
                rotateCount + 1, fileext, compext);
337
 
 
338
 
        strcpy(disposeName, oldName);
339
 
 
340
 
        firstRotated = alloca(strlen(dirName) + strlen(baseName) +
341
 
                              strlen(fileext) + strlen(compext) + 30);
342
 
        sprintf(firstRotated, "%s/%s.1%s%s", dirName, baseName,
343
 
                fileext, compext);
344
 
 
345
 
        for (i = rotateCount; i && !hasErrors; i--) {
346
 
            tmp = newName;
347
 
            newName = oldName;
348
 
            oldName = tmp;
349
 
            sprintf(oldName, "%s/%s.%d%s%s", dirName, baseName, i,
350
 
                    fileext, compext);
351
 
 
352
 
            message(MESS_DEBUG, "renaming %s to %s\n", oldName, newName);
353
 
 
354
 
            if (!debug && rename(oldName, newName)) {
355
 
                if (errno == ENOENT) {
356
 
                    message(MESS_DEBUG, "old log %s does not exist\n",
357
 
                            oldName);
358
 
                } else {
359
 
                    fprintf(errorFile, "error renaming %s to %s: %s\n",
360
 
                            oldName, newName, strerror(errno));
361
 
                    hasErrors = 1;
362
 
                }
363
 
            }
364
 
        }
365
 
 
366
 
        finalName = oldName;
367
 
 
368
 
        /* note: the gzip extension is *not* used here! */
369
 
        sprintf(finalName, "%s/%s.1%s", dirName, baseName, fileext);
370
 
 
371
 
        /* if the last rotation doesn't exist, that's okay */
372
 
        if (!debug && access(disposeName, F_OK)) {
373
 
            message(MESS_DEBUG, "file %s doesn't exist -- won't try "
374
 
                    "dispose of it\n", disposeName);
375
 
            disposeName = NULL;
376
 
        } 
377
 
 
378
 
        free(dirName);
379
 
        free(baseName);
380
 
 
381
 
        if (!hasErrors) {
382
 
            if (log->pre && !(log->flags & LOG_FLAG_SHAREDSCRIPTS)) {
383
 
                message(MESS_DEBUG, "running prerotate script\n");
384
 
                if (runScript(log->files[logNum], log->pre)) {
385
 
                    fprintf(errorFile, "error running prerotate script -- 
386
 
                                leaving old log in place\n");
387
 
                    hasErrors = 1;
388
 
                }
389
 
            }
390
 
 
391
 
            if (!(log->flags & LOG_FLAG_COPYTRUNCATE)) {
392
 
                message(MESS_DEBUG, "renaming %s to %s\n", log->files[logNum], 
 
599
        if (log->pre && !(log->flags & LOG_FLAG_SHAREDSCRIPTS)) {
 
600
            message(MESS_DEBUG, "running prerotate script\n");
 
601
            if (runScript(log->files[logNum], log->pre)) {
 
602
                message(MESS_ERROR, "error running prerotate script, "
 
603
                        "leaving old log in place\n");
 
604
                hasErrors = 1;
 
605
            }
 
606
        }
 
607
        
 
608
        if (!(log->flags & (LOG_FLAG_COPYTRUNCATE|LOG_FLAG_COPY))) {
 
609
            message(MESS_DEBUG, "renaming %s to %s\n", log->files[logNum], 
393
610
                    finalName);
394
 
 
395
 
                if (!debug && !hasErrors &&
396
 
                        rename(log->files[logNum], finalName)) {
397
 
                    fprintf(errorFile, "failed to rename %s to %s: %s\n",
 
611
            
 
612
            if (!debug && !hasErrors &&
 
613
                rename(log->files[logNum], finalName)) {
 
614
                message(MESS_ERROR, "failed to rename %s to %s: %s\n",
398
615
                        log->files[logNum], finalName, strerror(errno));
399
 
                }
400
 
            }
401
 
 
402
 
            if (!hasErrors && log->flags & LOG_FLAG_CREATE &&
403
 
                        !(log->flags & LOG_FLAG_COPYTRUNCATE)) {
404
 
                if (log->createUid == NO_UID)
405
 
                    createUid = sb.st_uid;
406
 
                else
407
 
                    createUid = log->createUid;
408
 
            
409
 
                if (log->createGid == NO_GID)
410
 
                    createGid = sb.st_gid;
411
 
                else
412
 
                    createGid = log->createGid;
413
 
            
414
 
                if (log->createMode == NO_MODE)
415
 
                    createMode = sb.st_mode & 0777;
416
 
                else
417
 
                    createMode = log->createMode;
418
 
            
419
 
                message(MESS_DEBUG, "creating new log mode = 0%o uid = %d "
420
 
                        "gid = %d\n", createMode, createUid, createGid);
421
 
 
422
 
                if (!debug) {
423
 
                    fd = open(log->files[logNum], O_CREAT | O_RDWR, createMode);
424
 
                    if (fd < 0) {
425
 
                        message(MESS_ERROR, "error creating %s: %s\n", 
426
 
                                log->files[logNum], strerror(errno));
427
 
                        hasErrors = 1;
428
 
                    } else {
429
 
                        if (fchmod(fd, createMode)) {
430
 
                            message(MESS_ERROR, "error setting mode of "
431
 
                                    "%s: %s\n", log->files[logNum], 
432
 
                                     strerror(errno));
433
 
                            hasErrors = 1;
434
 
                        }
435
 
                        if (fchown(fd, createUid, createGid)) {
436
 
                            message(MESS_ERROR, "error setting owner of "
437
 
                                    "%s: %s\n", log->files[logNum], 
438
 
                                     strerror(errno));
439
 
                            hasErrors = 1;
440
 
                        }
441
 
 
442
 
                        close(fd);
 
616
            }
 
617
 
 
618
            if (!log->rotateCount) {
 
619
              disposeName = alloca(strlen(dirName) + strlen(baseName) + 
 
620
                                   strlen(log->files[logNum]) + 10);
 
621
              sprintf(disposeName, "%s%s", finalName, (log->compress_ext && (log->flags & LOG_FLAG_COMPRESS))?log->compress_ext:"");
 
622
              message(MESS_DEBUG, "disposeName will be %s\n", disposeName);
 
623
            }
 
624
        }
 
625
        
 
626
        if (!hasErrors && log->flags & LOG_FLAG_CREATE &&
 
627
            !(log->flags & (LOG_FLAG_COPYTRUNCATE|LOG_FLAG_COPY))) {
 
628
            if (log->createUid == NO_UID)
 
629
                createUid = state->sb.st_uid;
 
630
            else
 
631
                createUid = log->createUid;
 
632
            
 
633
            if (log->createGid == NO_GID)
 
634
                createGid = state->sb.st_gid;
 
635
            else
 
636
                createGid = log->createGid;
 
637
            
 
638
            if (log->createMode == NO_MODE)
 
639
                createMode = state->sb.st_mode & 0777;
 
640
            else
 
641
                createMode = log->createMode;
 
642
            
 
643
            message(MESS_DEBUG, "creating new log mode = 0%o uid = %d "
 
644
                    "gid = %d\n", (unsigned int)createMode, (int)createUid, (int)createGid);
 
645
            
 
646
            if (!debug) {
 
647
                fd = open(log->files[logNum], O_CREAT | O_RDWR, createMode);
 
648
                if (fd < 0) {
 
649
                    message(MESS_ERROR, "error creating %s: %s\n", 
 
650
                            log->files[logNum], strerror(errno));
 
651
                    hasErrors = 1;
 
652
                } else {
 
653
                    if (fchmod(fd, (S_IRUSR | S_IWUSR) & createMode)) {
 
654
                        message(MESS_ERROR, "error setting mode of "
 
655
                                "%s: %s\n", log->files[logNum], 
 
656
                                strerror(errno));
 
657
                        hasErrors = 1;
 
658
                    }
 
659
                    if (fchown(fd, createUid, createGid)) {
 
660
                        message(MESS_ERROR, "error setting owner of "
 
661
                                "%s: %s\n", log->files[logNum], 
 
662
                                strerror(errno));
 
663
                        hasErrors = 1;
443
664
                    }
444
 
                }
445
 
            }
446
 
 
447
 
            if (!hasErrors && log->flags & LOG_FLAG_COPYTRUNCATE) {
448
 
                hasErrors = copyTruncate(log->files[logNum], finalName, &sb);
449
 
 
450
 
            }
451
 
 
452
 
            if (!hasErrors && log->post && 
453
 
                    !(log->flags & LOG_FLAG_SHAREDSCRIPTS)) {
454
 
                message(MESS_DEBUG, "running postrotate script\n");
455
 
                if (runScript(log->files[logNum], log->post)) {
456
 
                    fprintf(errorFile, "error running postrotate script\n");
457
 
                    hasErrors = 1;
458
 
                }
459
 
            }
460
 
 
461
 
            if (!hasErrors && !log->rotateCount) {
462
 
                message(MESS_DEBUG, "removing rotated log (rotateCount == 0)");
463
 
                if (unlink(finalName)) {
464
 
                    fprintf(errorFile, "Failed to remove old log %s: %s\n",
465
 
                                finalName, strerror(errno));
466
 
                    hasErrors = 1;
467
 
                }
468
 
            }
469
 
 
470
 
            if (!hasErrors && log->rotateCount && 
471
 
                        (log->flags & LOG_FLAG_COMPRESS) &&
472
 
                        !(log->flags & LOG_FLAG_DELAYCOMPRESS)) {
473
 
                char * command;
474
 
 
475
 
                command = alloca(strlen(finalName) + strlen(log->compress_prog) + 1 + strlen(log->compress_options) + 20);
476
 
 
477
 
                sprintf(command, "%s %s '%s'", log->compress_prog, log->compress_options, finalName);
478
 
                message(MESS_DEBUG, "compressing new log with: %s\n", command);
479
 
                if (!debug && system(command)) {
480
 
                    fprintf(errorFile, "failed to compress log %s\n", 
481
 
                                finalName);
482
 
                    hasErrors = 1;
483
 
                }
484
 
            }
485
 
 
486
 
            if (!hasErrors && log->logAddress) {
487
 
                char * command;
488
 
                char * mailFilename;
489
 
 
490
 
                if (log->flags & LOG_FLAG_MAILFIRST)
491
 
                    mailFilename = firstRotated;
492
 
                else
493
 
                    mailFilename = disposeName;
494
 
 
495
 
                if (mailFilename) {
496
 
                    command = alloca(strlen(mailFilename) + 100 + 
497
 
                                     strlen(log->uncompress_prog));
498
 
 
499
 
                    if ((log->flags & LOG_FLAG_COMPRESS) &&
500
 
                            !((log->flags & LOG_FLAG_DELAYCOMPRESS) &&
501
 
                            (log->flags & LOG_FLAG_MAILFIRST)))
502
 
                        sprintf(command, "%s < %s | %s '%s' %s", 
503
 
                                    log->uncompress_prog, mailFilename, mailCommand,
504
 
                                    log->files[logNum],
505
 
                                    log->logAddress);
506
 
                    else
507
 
                        sprintf(command, "%s '%s' %s < %s", mailCommand, 
508
 
                                    mailFilename, log->logAddress, mailFilename);
509
 
 
510
 
                    message(MESS_DEBUG, "executing: \"%s\"\n", command);
511
 
 
512
 
                    if (!debug && system(command)) {
513
 
                        sprintf(newName, "%s.%d", log->files[logNum], getpid());
514
 
                        fprintf(errorFile, "Failed to mail %s to %s!\n",
515
 
                                mailFilename, log->logAddress);
516
 
 
517
 
                        hasErrors = 1;
518
 
                    } 
519
 
                }
520
 
            }
521
 
 
522
 
            if (!hasErrors && disposeName) {
523
 
                message(MESS_DEBUG, "removing old log %s\n", disposeName);
524
 
 
525
 
                if (!debug && unlink(disposeName)) {
526
 
                    fprintf(errorFile, "Failed to remove old log %s: %s\n",
527
 
                                disposeName, strerror(errno));
528
 
                    hasErrors = 1;
529
 
                }
530
 
            }
531
 
 
532
 
        }
533
 
    } else if (!doRotate) {
534
 
        message(MESS_DEBUG, "log does not need rotating\n");
 
665
                    if (fchmod(fd, createMode)) {
 
666
                        message(MESS_ERROR, "error setting mode of "
 
667
                                "%s: %s\n", log->files[logNum], 
 
668
                                strerror(errno));
 
669
                        hasErrors = 1;
 
670
                    }
 
671
 
 
672
                    close(fd);
 
673
                }
 
674
            }
 
675
        }
 
676
        
 
677
        if (!hasErrors && log->flags & (LOG_FLAG_COPYTRUNCATE|LOG_FLAG_COPY))
 
678
            hasErrors = copyTruncate(log->files[logNum], finalName,
 
679
                                     &state->sb, log->flags);
 
680
        
 
681
        if (!hasErrors && log->post && 
 
682
            !(log->flags & LOG_FLAG_SHAREDSCRIPTS)) {
 
683
            message(MESS_DEBUG, "running postrotate script\n");
 
684
            if (runScript(log->files[logNum], log->post)) {
 
685
                message(MESS_ERROR, "error running postrotate script\n");
 
686
                hasErrors = 1;
 
687
            }
 
688
        }
 
689
 
 
690
        if (!hasErrors && 
 
691
            (log->flags & LOG_FLAG_COMPRESS) &&
 
692
            !(log->flags & LOG_FLAG_DELAYCOMPRESS)) {
 
693
            hasErrors = compressLogFile(finalName, log);
 
694
        }
 
695
        
 
696
        if (!hasErrors && log->logAddress) {
 
697
            char * mailFilename;
 
698
            
 
699
            if (log->flags & LOG_FLAG_MAILFIRST)
 
700
                mailFilename = firstRotated;
 
701
            else
 
702
                mailFilename = disposeName;
 
703
 
 
704
            if (mailFilename) {
 
705
                /* if the log is compressed (and we're not mailing a
 
706
                   file whose compression has been delayed), we need
 
707
                   to uncompress it */
 
708
                if ((log->flags & LOG_FLAG_COMPRESS) &&
 
709
                    !((log->flags & LOG_FLAG_DELAYCOMPRESS) &&
 
710
                      (log->flags & LOG_FLAG_MAILFIRST))) {
 
711
                    if (mailLog(mailFilename, mailCommand, 
 
712
                                log->uncompress_prog, log->logAddress, 
 
713
                                log->files[logNum])) 
 
714
                        hasErrors = 1;
 
715
                } else {
 
716
                    if (mailLog(mailFilename, mailCommand, NULL, 
 
717
                                log->logAddress, mailFilename))
 
718
                        hasErrors = 1;
 
719
                }
 
720
            }
 
721
        }
 
722
        
 
723
        if (!hasErrors && disposeName) {
 
724
            message(MESS_DEBUG, "removing old log %s\n", disposeName);
 
725
            
 
726
            if (!debug && unlink(disposeName)) {
 
727
                message(MESS_ERROR, "Failed to remove old log %s: %s\n",
 
728
                        disposeName, strerror(errno));
 
729
                hasErrors = 1;
 
730
            }
 
731
        }
535
732
    }
536
 
 
 
733
    
 
734
#ifdef WITH_SELINUX
 
735
        if (selinux_enabled) {
 
736
          setfscreatecon(prev_context);
 
737
          if (prev_context!= NULL) {
 
738
            freecon(prev_context);
 
739
            prev_context=NULL;
 
740
          }
 
741
        }
 
742
#endif
537
743
    return hasErrors;
538
744
}
539
745
 
540
 
int rotateLogSet(logInfo * log, logState ** statesPtr, int * numStatesPtr, 
541
 
                 int force) {
 
746
int rotateLogSet(logInfo * log, struct stateSet * sip, int force) {
542
747
    int i;
543
748
    int hasErrors = 0;
 
749
    int numRotated = 0;
 
750
    logState * state;
544
751
 
545
752
    if (force)
546
753
        log->criterium = ROT_FORCE;
547
 
 
548
 
    message(MESS_DEBUG, "rotating pattern: %s ", log->pattern);
 
754
    
 
755
    message(MESS_DEBUG, "\nrotating pattern: %s ", log->pattern);
549
756
    switch (log->criterium) {
550
 
      case ROT_DAYS:
 
757
    case ROT_DAYS:
551
758
        message(MESS_DEBUG, "after %d days ", log->threshhold);
552
759
        break;
553
 
      case ROT_WEEKLY:
 
760
    case ROT_WEEKLY:
554
761
        message(MESS_DEBUG, "weekly ");
555
762
        break;
556
 
      case ROT_MONTHLY:
 
763
    case ROT_MONTHLY:
557
764
        message(MESS_DEBUG, "monthly ");
558
765
        break;
559
 
      case ROT_SIZE:
 
766
    case ROT_SIZE:
560
767
        message(MESS_DEBUG, "%d bytes ", log->threshhold);
561
768
        break;
562
 
      case ROT_FORCE:
 
769
    case ROT_FORCE:
563
770
        message(MESS_DEBUG, "forced from command line ");
564
771
        break;
565
772
    }
566
 
 
 
773
    
567
774
    if (log->rotateCount) 
568
775
        message(MESS_DEBUG, "(%d rotations)\n", log->rotateCount);
569
776
    else
570
777
        message(MESS_DEBUG, "(no old logs will be kept)\n");
571
 
 
 
778
    
572
779
    if (log->oldDir) 
573
 
        message(MESS_DEBUG, "olddir is %s ", log->oldDir);
574
 
 
 
780
        message(MESS_DEBUG, "olddir is %s, ", log->oldDir);
 
781
    
575
782
    if (log->flags & LOG_FLAG_IFEMPTY) 
576
 
        message(MESS_DEBUG, "empty log files are rotated ");
 
783
        message(MESS_DEBUG, "empty log files are rotated, ");
577
784
    else
578
 
        message(MESS_DEBUG, "empty log files are not rotated ");
579
 
 
 
785
        message(MESS_DEBUG, "empty log files are not rotated, ");
 
786
    
580
787
    if (log->logAddress) {
581
788
        message(MESS_DEBUG, "old logs mailed to %s\n", log->logAddress);
582
789
    } else {
583
790
        message(MESS_DEBUG, "old logs are removed\n");
584
791
    }
 
792
    
 
793
    for (i = 0; i < log->numFiles; i++) {
 
794
        hasErrors |= findNeedRotating(log, i, sip);
 
795
        
 
796
        /* sure is a lot of findStating going on .. */
 
797
        if ((findState(log->files[i], sip))->doRotate)
 
798
            numRotated++;
 
799
    }
 
800
    
 
801
    if (log->first) {
 
802
        if (!numRotated) {
 
803
            message(MESS_DEBUG, "not running first action script, "
 
804
                    "since no logs will be rotated\n");
 
805
        } else {
 
806
            message(MESS_DEBUG, "running first action script\n");
 
807
            if (runScript(log->pattern, log->first)) {
 
808
                message(MESS_ERROR, "error running first action script "
 
809
                        "for %s\n", log->pattern);
 
810
                hasErrors = 1;
 
811
            }
 
812
        }
 
813
    }
585
814
 
586
815
    if (log->pre && (log->flags & LOG_FLAG_SHAREDSCRIPTS)) {
587
 
        message(MESS_DEBUG, "running shared prerotate script\n");
588
 
        if (runScript(log->pattern, log->pre)) {
589
 
            fprintf(stderr, "error running shared prerotate script for %s-- 
590
 
                        leaving old logs in place\n", log->pattern);
591
 
            hasErrors = 1;
 
816
        if (!numRotated) {
 
817
            message(MESS_DEBUG, "not running shared prerotate script, "
 
818
                    "since no logs will be rotated\n");
 
819
        } else {
 
820
            message(MESS_DEBUG, "running shared prerotate script\n");
 
821
            if (runScript(log->pattern, log->pre)) {
 
822
                message(MESS_ERROR, "error running shared prerotate script "
 
823
                        "for %s\n", log->pattern);
 
824
                hasErrors = 1;
 
825
            }
592
826
        }
593
827
    }
594
 
 
595
 
    for (i = 0; i < log->numFiles; i++)
596
 
        hasErrors |= rotateSingleLog(log, i, statesPtr, numStatesPtr, 
597
 
                                        stderr);
598
 
 
 
828
    
 
829
    /* should there be an if(!hasErrors) here? */
 
830
    for (i = 0; i < log->numFiles; i++) {
 
831
        state = findState(log->files[i], sip);
 
832
        
 
833
        hasErrors |= rotateSingleLog(log, i, state);
 
834
    }
 
835
    
599
836
    if (log->post && (log->flags & LOG_FLAG_SHAREDSCRIPTS)) {
600
 
        message(MESS_DEBUG, "running shared postrotate script\n");
601
 
        if (runScript(log->pattern, log->post)) {
602
 
            fprintf(stderr, 
603
 
               "error running shared postrotate script for %s\n", log->pattern);
604
 
            hasErrors = 1;
605
 
        }
606
 
    }
607
 
 
 
837
        if (!numRotated) {
 
838
            message(MESS_DEBUG, "not running shared postrotate script, "
 
839
                    "since no logs were rotated\n");
 
840
        } else {
 
841
            message(MESS_DEBUG, "running shared postrotate script\n");
 
842
            if (runScript(log->pattern, log->post)) {
 
843
                message(MESS_ERROR, "error running shared postrotate script "
 
844
                        "for %s\n", log->pattern);
 
845
                hasErrors = 1;
 
846
            }
 
847
        }
 
848
    }
 
849
    
 
850
    if (log->last) {
 
851
        if (!numRotated) {
 
852
            message(MESS_DEBUG, "not running last action script, "
 
853
                    "since no logs will be rotated\n");
 
854
        } else {
 
855
            message(MESS_DEBUG, "running last action script\n");
 
856
            if (runScript(log->pattern, log->last)) {
 
857
                message(MESS_ERROR, "error running last action script "
 
858
                        "for %s\n", log->pattern);
 
859
                hasErrors = 1;
 
860
            }
 
861
        }
 
862
    }
 
863
    
608
864
    return hasErrors;
609
865
}
610
866
 
611
 
static int writeState(char * stateFilename, logState * states, 
612
 
                      int numStates) {
 
867
static int writeState(char * stateFilename, struct stateSet si) {
613
868
    FILE * f;
614
869
    char * chptr;
615
870
    int i;
616
 
 
 
871
    
617
872
    f = fopen(stateFilename, "w");
 
873
    
618
874
    if (!f) {
619
875
        message(MESS_ERROR, "error creating state file %s: %s\n", 
620
 
                    stateFilename, strerror(errno));
 
876
                stateFilename, strerror(errno));
621
877
        return 1;
622
878
    }
623
 
 
 
879
    
624
880
    fprintf(f, "logrotate state -- version 2\n");
625
 
 
626
 
    for (i = 0; i < numStates; i++) {
 
881
    
 
882
    for (i = 0; i < si.numStates; i++) {
627
883
        fputc('"', f);
628
 
        for (chptr = states[i].fn; *chptr; chptr++) {
 
884
        for (chptr = si.states[i].fn; *chptr; chptr++) {
629
885
            switch (*chptr) {
630
 
              case '\\':
631
 
              case '\'':
632
 
              case '"':
633
 
                  fputc('\\', f);
 
886
            case '"':
 
887
                fputc('\\', f);
634
888
            }
635
 
 
 
889
            
636
890
            fputc(*chptr, f);
637
891
        }
638
 
 
 
892
        
639
893
        fputc('"', f);
640
894
        fprintf(f, " %d-%d-%d\n", 
641
 
                states[i].lastRotated.tm_year + 1900,
642
 
                states[i].lastRotated.tm_mon + 1,
643
 
                states[i].lastRotated.tm_mday);
 
895
                si.states[i].lastRotated.tm_year + 1900,
 
896
                si.states[i].lastRotated.tm_mon + 1,
 
897
                si.states[i].lastRotated.tm_mday);
644
898
    }
645
 
 
 
899
    
646
900
    fclose(f);
647
 
 
 
901
    
648
902
    return 0;
649
903
}
650
904
 
651
 
static int readState(char * stateFilename, logState ** statesPtr, 
652
 
                     int * numStatesPtr) {
 
905
static int readState(char * stateFilename, struct stateSet * sip) {
653
906
    FILE * f;
654
907
    char buf[1024];
655
908
    const char ** argv;
665
918
    error = stat(stateFilename, &f_stat);
666
919
 
667
920
    if ((error && errno == ENOENT) ||
668
 
        (!error && f_stat.st_size == 0)) {
 
921
        (!error && f_stat.st_size == 0)) {
669
922
        /* create the file before continuing to ensure we have write
670
923
           access to the file */
671
924
        f = fopen(stateFilename, "w");
672
925
        if (!f) {
673
926
            message(MESS_ERROR, "error creating state file %s: %s\n", 
674
 
                        stateFilename, strerror(errno));
 
927
                    stateFilename, strerror(errno));
675
928
            return 1;
676
929
        }
677
930
        fprintf(f, "logrotate state -- version 2\n");
678
931
        fclose(f);
679
932
        return 0;
680
933
    } else if (error) {
681
 
        message(MESS_ERROR, "error using stat() on state file %s: %s\n", 
682
 
                    stateFilename, strerror(errno));
 
934
        message(MESS_ERROR, "error stat()ing state file %s: %s\n", 
 
935
                stateFilename, strerror(errno));
683
936
        return 1;
684
937
    }
685
938
 
686
939
    f = fopen(stateFilename, "r");
687
940
    if (!f) {
688
 
        message(MESS_ERROR, "error opening state file %s: %s\n", 
 
941
        message(MESS_ERROR, "error opening state file %s: %s\n",
689
942
                stateFilename, strerror(errno));
690
943
        return 1;
691
944
    }
692
 
 
 
945
    
693
946
    if (!fgets(buf, sizeof(buf) - 1, f)) {
694
947
        message(MESS_ERROR, "error reading top line of %s\n", stateFilename);
695
948
        fclose(f);
696
949
        return 1;
697
950
    }
698
 
 
 
951
    
699
952
    if (strcmp(buf, "logrotate state -- version 1\n") &&
700
 
           strcmp(buf, "logrotate state -- version 2\n")) {
 
953
        strcmp(buf, "logrotate state -- version 2\n")) {
701
954
        fclose(f);
702
955
        message(MESS_ERROR, "bad top line in state file %s\n", stateFilename);
703
956
        return 1;
704
957
    }
705
 
 
 
958
    
706
959
    line++;
707
 
 
 
960
    
708
961
    while (fgets(buf, sizeof(buf) - 1, f)) {
709
962
        line++;
710
963
        i = strlen(buf);
711
964
        if (buf[i - 1] != '\n') {
712
965
            message(MESS_ERROR, "line too long in state file %s\n", 
713
 
                        stateFilename);
 
966
                    stateFilename);
714
967
            fclose(f);
715
968
            return 1;
716
969
        }
717
 
 
 
970
        
718
971
        buf[i - 1] = '\0';
719
 
 
 
972
        
720
973
        if (i == 1) continue;
721
 
 
 
974
        
722
975
        if (poptParseArgvString(buf, &argc, &argv) || (argc != 2) ||
723
 
                (sscanf(argv[1], "%d-%d-%d", &year, &month, &day) != 3)) {
 
976
            (sscanf(argv[1], "%d-%d-%d", &year, &month, &day) != 3)) {
724
977
            message(MESS_ERROR, "bad line %d in state file %s\n", 
725
978
                    line, stateFilename);
726
979
            fclose(f);
727
980
            return 1;
728
981
        }
729
 
 
 
982
        
730
983
        /* Hack to hide earlier bug */
731
 
        if ((year != 1900) && (year < 1970 || year > 2100)) {
 
984
        if ((year != 1900) && (year < 1996 || year > 2100)) {
732
985
            message(MESS_ERROR, "bad year %d for file %s in state file %s\n",
733
 
                        year, argv[0], stateFilename);
 
986
                    year, argv[0], stateFilename);
734
987
            fclose(f);
735
988
            return 1;
736
989
        }
737
 
 
 
990
        
738
991
        if (month < 1 || month > 12) {
739
992
            message(MESS_ERROR, "bad month %d for file %s in state file %s\n",
740
 
                        month, argv[0], stateFilename);
 
993
                    month, argv[0], stateFilename);
741
994
            fclose(f);
742
995
            return 1;
743
996
        }
744
 
 
 
997
        
745
998
        /* 0 to hide earlier bug */
746
999
        if (day < 0 || day > 31) {
747
1000
            message(MESS_ERROR, "bad day %d for file %s in state file %s\n",
748
 
                        day, argv[0], stateFilename);
 
1001
                    day, argv[0], stateFilename);
749
1002
            fclose(f);
750
1003
            return 1;
751
1004
        }
752
1005
 
753
1006
        year -= 1900, month -= 1;
754
1007
 
755
 
        st = findState(argv[0], statesPtr, numStatesPtr);
 
1008
        st = findState(argv[0], sip);
756
1009
 
757
1010
        st->lastRotated.tm_mon = month;
758
1011
        st->lastRotated.tm_mday = day;
774
1027
int main(int argc, const char ** argv) {
775
1028
    logInfo defConfig = { NULL, NULL, 0, NULL, ROT_SIZE, 
776
1029
                          /* threshHold */ 1024 * 1024, 0,
 
1030
                          /* log start */ -1,
777
1031
                          /* pre, post */ NULL, NULL,
 
1032
                          /* first, last */ NULL, NULL,
778
1033
                          /* logAddress */ NULL, 
779
1034
                          /* extension */ NULL, 
780
1035
                          /* compression */ COMPRESS_COMMAND,
781
 
                          UNCOMPRESS_COMMAND, COMPRESS_OPTIONS, COMPRESS_EXT,
 
1036
                          UNCOMPRESS_COMMAND, COMPRESS_EXT,
 
1037
                          /* rotate pattern */ NULL,
782
1038
                          /* flags */ LOG_FLAG_IFEMPTY,
783
1039
                          /* createMode */ NO_MODE, NO_UID, NO_GID };
784
 
    int numLogs = 0, numStates = 0;
 
1040
    int numLogs = 0;
785
1041
    int force = 0;
786
1042
    logInfo * logs = NULL;
787
 
    logState * states = NULL;
 
1043
    struct stateSet si = { NULL, 0 };
788
1044
    char * stateFile = STATEFILE;
789
1045
    int i;
790
1046
    int rc = 0;
793
1049
    poptContext optCon;
794
1050
    struct poptOption options[] = {
795
1051
        { "debug", 'd', 0, 0, 'd', 
796
 
                "Don't do anything, just test (implies -v)" },
 
1052
          "Don't do anything, just test (implies -v)" },
797
1053
        { "force", 'f', 0 , &force, 0, "Force file rotation" },
798
1054
        { "mail", 'm', POPT_ARG_STRING, &mailCommand, 0, 
799
 
                "Command to use to mail logs", "command" },
 
1055
          "Command to send mail (instead of `" DEFAULT_MAIL_COMMAND "')",
 
1056
          "command" },
800
1057
        { "state", 's', POPT_ARG_STRING, &stateFile, 0, "Path of state file",
801
 
                "statefile" },
 
1058
          "statefile" },
802
1059
        { "verbose", 'v', 0, 0, 'v', "Display messages during rotation" },
803
1060
        POPT_AUTOHELP
804
1061
        { 0, 0, 0, 0, 0 } 
805
1062
    };
806
 
 
 
1063
    
807
1064
    logSetLevel(MESS_NORMAL);
808
1065
 
809
1066
    optCon = poptGetContext("logrotate", argc, argv, options,0);
812
1069
 
813
1070
    while ((arg = poptGetNextOpt(optCon)) >= 0) {
814
1071
        switch (arg) {
815
 
          case 'd':
 
1072
        case 'd':
816
1073
            debug = 1;
817
1074
            /* fallthrough */
818
 
          case 'v':
 
1075
        case 'v':
819
1076
            logSetLevel(MESS_DEBUG);
820
1077
            break;
821
1078
        }
828
1085
        return 2;
829
1086
    }
830
1087
 
831
 
    files = poptGetArgs(optCon);
 
1088
    files = poptGetArgs((poptContext) optCon);
832
1089
    if (!files) {
833
1090
        fprintf(stderr, "logrotate " VERSION 
834
 
                    " - Copyright (C) 1995-2001 Red Hat, Inc.\n");
 
1091
                " - Copyright (C) 1995-2001 Red Hat, Inc.\n");
835
1092
        fprintf(stderr, "This may be freely redistributed under the terms of "
836
 
                    "the GNU Public License\n\n");
 
1093
                "the GNU Public License\n\n");
837
1094
        poptPrintUsage(optCon, stderr, 0);
838
1095
        exit(1);
839
1096
    }
844
1101
        }
845
1102
    }
846
1103
 
847
 
    if (readState(stateFile, &states, &numStates)) {
 
1104
    nowSecs = time(NULL);
 
1105
 
 
1106
    if (readState(stateFile, &si)) {
848
1107
        exit(1);
849
1108
    }
850
1109
 
851
 
    message(MESS_DEBUG, "Handling %d logs\n", numLogs);
 
1110
    message(MESS_DEBUG, "\nHandling %d logs\n", numLogs);
852
1111
 
853
1112
    for (i = 0; i < numLogs; i++) {
854
 
        rc |= rotateLogSet(logs + i, &states, &numStates, force);
 
1113
        rc |= rotateLogSet(logs + i, &si, force);
855
1114
    }
856
1115
 
857
 
    if (!debug && writeState(stateFile, states, numStates)) {
 
1116
    if (!debug && writeState(stateFile, si)) {
858
1117
        exit(1);
859
1118
    }
860
1119