~ubuntu-branches/ubuntu/natty/logrotate/natty-updates

« back to all changes in this revision

Viewing changes to logrotate.c

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2008-11-07 14:57:09 UTC
  • mfrom: (1.1.3 upstream)
  • Revision ID: james.westby@ubuntu.com-20081107145709-s8ktufsiktmkuidl
Tags: 3.7.7-1ubuntu1
* Merge with Debian experimental (LP: #64964). Remaining Ubuntu changes:
  - debian/control: Drop mailx to Suggests for Ubuntu; it's only used on
    request, and we don't configure an MTA by default.

Show diffs side-by-side

added added

removed removed

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