11
12
#include <sys/wait.h>
13
14
#include <unistd.h>
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;
21
25
#include "basenames.h"
23
27
#include "logrotate.h"
29
#if !defined(GLOB_ABORTED) && defined(GLOB_ABEND)
30
#define GLOB_ABORTED GLOB_ABEND
27
35
struct tm lastRotated; /* only tm.mon, tm_mday, tm_year are good! */
37
#define NO_MODE ((mode_t) -1)
38
#define NO_UID ((uid_t) -1)
39
#define NO_GID ((gid_t) -1)
38
LIST_ENTRY(logState) list;
49
LIST_HEAD(stateSet, logState) states;
42
char * mailCommand = DEFAULT_MAIL_COMMAND;
53
char *mailCommand = DEFAULT_MAIL_COMMAND;
43
54
time_t nowSecs = 0;
45
static logState * findState(const char * fn, struct stateSet * sip) {
47
logState * states = sip->states;
48
int numStates = sip->numStates;
49
struct tm now = *localtime(&nowSecs);
52
for (i = 0; i < numStates; i++)
53
if (!strcmp(fn, states[i].fn)) break;
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;
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;
66
/* fill in the rest of the st->lastRotated fields */
67
lr_time = mktime(&states[i].lastRotated);
68
states[i].lastRotated = *localtime(&lr_time);
71
sip->numStates = numStates;
77
static int runScript(char * logfn, char * script) {
56
static int shred_file(char *filename, struct logInfo *log);
58
static int globerr(const char *pathname, int theerr)
60
message(MESS_ERROR, "error accessing %s: %s\n", pathname,
63
/* We want the glob operation to continue, so return 0 */
67
static struct logState *newState(const char *fn)
69
struct tm now = *localtime(&nowSecs);
73
if ((new = malloc(sizeof(*new))) == NULL)
76
if ((new->fn = strdup(fn)) == NULL)
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;
86
/* fill in the rest of the new->lastRotated fields */
87
lr_time = mktime(&new->lastRotated);
88
new->lastRotated = *localtime(&lr_time);
93
static struct logState *findState(const char *fn)
97
for (p = states.lh_first; p != NULL; p = p->list.le_next)
98
if (!strcmp(fn, p->fn))
103
if ((p = newState(fn)) == NULL)
106
LIST_INSERT_HEAD(&states, p, list);
112
static int runScript(char *logfn, char *script)
84
message(MESS_DEBUG, "running script with arg %s: \"%s\"\n",
117
message(MESS_DEBUG, "running script with arg %s: \"%s\"\n",
90
snprintf(buf, sizeof(buf), "%s/logrotate.XXXXXX", getenv("TMPDIR") ?: "/tmp");
92
if (!filespec || (fd = mkstemp(filespec)) < 0 || fchmod(fd, 0700)) {
93
message(MESS_DEBUG, "error creating %s: %s\n", filespec,
123
execl("/bin/sh", "sh", "-c", script, "logrotate_script", logfn, NULL);
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);
113
execlp(filespec, filespec, logfn, NULL);
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)
135
fd = open(fileName, flags, sb->st_mode);
137
message(MESS_ERROR, "error creating output file %s: %s\n",
138
fileName, strerror(errno));
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));
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));
153
if (fchmod(fd, sb->st_mode)) {
154
message(MESS_ERROR, "error setting mode of %s: %s\n",
155
fileName, strerror(errno));
162
#define SHRED_CALL "shred -u "
163
#define SHRED_COUNT_FLAG "-n "
165
/* unlink, but try to call shred from GNU fileutils */
166
static int shred_file(char *filename, struct logInfo *log)
170
char count[DIGITS]; /* that's a lot of shredding :) */
172
if (!(log->flags & LOG_FLAG_SHRED)) {
173
return unlink(filename);
176
len = strlen(filename) + strlen(SHRED_CALL);
177
len += strlen(SHRED_COUNT_FLAG) + DIGITS;
181
message(MESS_ERROR, "malloc error while shredding");
182
return unlink(filename);
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);
191
strcat(cmd, filename);
195
message(MESS_ERROR, "Failed to shred %s\n, trying unlink", filename);
197
message(MESS_NORMAL, "Shred returned %d\n", ret);
199
return unlink(filename);
205
static int removeLogFile(char *name, struct logInfo *log)
207
message(MESS_DEBUG, "removing old log %s\n", name);
209
if (!debug && shred_file(name, log)) {
210
message(MESS_ERROR, "Failed to remove old log %s: %s\n",
211
name, strerror(errno));
217
static int compressLogFile(char *name, struct logInfo *log, struct stat *sb)
219
char *compressedName;
220
const char **fullCommand;
132
fullCommand = alloca(sizeof(*fullCommand) *
226
message(MESS_DEBUG, "compressing log with: %s\n", log->compress_prog);
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;
139
compressedName = alloca(strlen(name) +
140
strlen(log->compress_ext) + 2);
237
compressedName = alloca(strlen(name) + strlen(log->compress_ext) + 2);
141
238
sprintf(compressedName, "%s%s", name, log->compress_ext);
143
240
if ((inFile = open(name, O_RDONLY)) < 0) {
144
241
message(MESS_ERROR, "unable to open %s for compression\n", name);
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",
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));
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));
168
if (fchmod(outFile, sb->st_mode)) {
169
message(MESS_ERROR, "error setting mode of %s: %s\n",
170
compressedName, strerror(errno));
176
message(MESS_DEBUG, "compressing log with: %s\n", fullCommand[0]);
246
createOutputFile(compressedName, O_RDWR | O_CREAT | O_TRUNC, sb);
361
453
if (flags & LOG_FLAG_COPYTRUNCATE) {
362
message(MESS_DEBUG, "truncating %s\n", currLog);
454
message(MESS_DEBUG, "truncating %s\n", currLog);
365
457
if (ftruncate(fdcurr, 0)) {
366
message(MESS_ERROR, "error truncating %s: %s\n", currLog,
458
message(MESS_ERROR, "error truncating %s: %s\n", currLog,
377
message(MESS_DEBUG, "Not truncating %s\n", currLog);
465
message(MESS_DEBUG, "Not truncating %s\n", currLog);
385
int findNeedRotating(logInfo * log, int logNum, struct stateSet * sip) {
472
int findNeedRotating(struct logInfo *log, int logNum)
387
logState * state = NULL;
475
struct logState *state = NULL;
388
476
struct tm now = *localtime(&nowSecs);
390
478
message(MESS_DEBUG, "considering log %s\n", log->files[logNum]);
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",
398
message(MESS_ERROR, "stat of %s failed: %s\n", log->files[logNum],
481
if ((log->flags & LOG_FLAG_MISSINGOK) && (errno == ENOENT)) {
482
message(MESS_DEBUG, " log %s does not exist -- skipping\n",
486
message(MESS_ERROR, "stat of %s failed: %s\n", log->files[logNum],
403
state = findState(log->files[logNum], sip);
491
state = findState(log->files[logNum]);
404
492
state->doRotate = 0;
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 */
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 */
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)))) {
418
506
"log %s last rotated in the future -- rotation forced\n",
419
507
log->files[logNum]);
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) {
427
1) the current weekday is before the weekday of the
429
2) more then a week has passed since the last
431
state->doRotate = ((now.tm_wday < state->lastRotated.tm_wday) ||
432
((mktime(&now) - mktime(&state->lastRotated)) >
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) {
515
1) the current weekday is before the weekday of the
517
2) more then a week has passed since the last
519
state->doRotate = ((now.tm_wday < state->lastRotated.tm_wday)
522
mktime(&state->lastRotated)) >
433
523
(7 * 24 * 3600)));
436
/* rotate if the logs haven't been rotated this month or
438
state->doRotate = ((now.tm_mon != state->lastRotated.tm_mon) ||
439
(now.tm_year != state->lastRotated.tm_year));
442
/* FIXME: only days=1 is implemented!! */
526
/* rotate if the logs haven't been rotated this month or
528
state->doRotate = ((now.tm_mon != state->lastRotated.tm_mon) ||
530
state->lastRotated.tm_year));
533
/* FIXME: only days=1 is implemented!! */
537
/* rotate if the logs haven't been rotated this year */
538
state->doRotate = (now.tm_year != state->lastRotated.tm_year);
545
if (log->minsize && sb.st_size < log->minsize)
452
549
/* The notifempty flag overrides the normal criteria */
453
550
if (!(log->flags & LOG_FLAG_IFEMPTY) && !sb.st_size)
456
553
if (state->doRotate) {
457
message(MESS_DEBUG, " log needs rotating\n");
554
message(MESS_DEBUG, " log needs rotating\n");
459
message(MESS_DEBUG, " log does not need rotating\n");
556
message(MESS_DEBUG, " log does not need rotating\n");
465
int rotateSingleLog(logInfo * log, int logNum, logState * state) {
562
int prerotateSingleLog(struct logInfo *log, int logNum, struct logState *state,
563
struct logNames *rotNames)
466
565
struct tm now = *localtime(&nowSecs);
467
char * oldName, * newName = NULL;
566
char *oldName, *newName = NULL;
473
570
int hasErrors = 0;
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];
486
if (!state->doRotate) return 0;
584
if (!state->doRotate)
488
587
/* Logs with rotateCounts of 0 are rotated once, then removed. This
489
588
lets scripts run properly, and everything gets mailed properly. */
491
message(MESS_DEBUG, "rotating log %s, log->rotateCount is %d\n", log->files[logNum], log->rotateCount);
493
if (log->flags & LOG_FLAG_COMPRESS) compext = log->compress_ext;
590
message(MESS_DEBUG, "rotating log %s, log->rotateCount is %d\n",
591
log->files[logNum], log->rotateCount);
593
if (log->flags & LOG_FLAG_COMPRESS)
594
compext = log->compress_ext;
495
596
state->lastRotated = now;
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);
602
malloc(strlen(ld) + strlen(log->oldDir) + 2);
603
sprintf(rotNames->dirName, "%s/%s", ld, log->oldDir);
504
dirName = strdup(log->oldDir);
606
rotNames->dirName = strdup(log->oldDir);
506
dirName = ourDirName(log->files[logNum]);
508
baseName = strdup(ourBaseName(log->files[logNum]));
510
alloc_size = strlen(dirName) + strlen(baseName) +
511
strlen(log->files[logNum]) + strlen(fileext) +
512
strlen(compext) + 10;
514
oldName = alloca(alloc_size);
515
newName = alloca(alloc_size);
516
disposeName = alloca(alloc_size);
608
rotNames->dirName = ourDirName(log->files[logNum]);
610
rotNames->baseName = strdup(ourBaseName(log->files[logNum]));
612
oldName = alloca(PATH_MAX);
613
newName = alloca(PATH_MAX);
614
rotNames->disposeName = malloc(PATH_MAX);
518
616
if (log->extension &&
519
strncmp(&baseName[strlen(baseName)-strlen(log->extension)],
520
log->extension, strlen(log->extension)) == 0) {
619
baseName[strlen(rotNames->baseName) -
620
strlen(log->extension)]), log->extension,
621
strlen(log->extension)) == 0) {
624
fileext = log->extension;
626
calloc(strlen(rotNames->baseName) - strlen(log->extension) + 1,
628
strncat(tempstr, rotNames->baseName,
629
strlen(rotNames->baseName) - strlen(log->extension));
630
free(rotNames->baseName);
631
rotNames->baseName = tempstr;
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));
634
/* Allow only %Y %d %m and create valid strftime format string
635
* Construct the glob pattern corresponding to the date format */
637
if (log->dateformat) {
639
memset(dext_pattern, 0, sizeof(dext_pattern));
640
dext = log->dateformat;
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",
652
switch (*(dext + 1)) {
654
strncat(dext_pattern, "[0-9][0-9]",
655
sizeof(dext_pattern) - strlen(dext_pattern));
656
j += 10; /* strlen("[0-9][0-9]") */
659
strncat(dext_pattern, "[0-9][0-9]",
660
sizeof(dext_pattern) - strlen(dext_pattern));
662
if (j >= (sizeof(dext_pattern) - 1)) {
663
message(MESS_ERROR, "Date format %s is too long\n",
668
dformat[i++] = *(dext++);
672
dformat[i++] = *dext;
674
dext_pattern[j++] = *dext;
679
dext_pattern[j++] = *dext;
685
message(MESS_DEBUG, "Converted '%s' -> '%s'\n", log->dateformat, dformat);
686
strftime(dext_str, sizeof(dext_str), dformat, &now);
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';
694
message(MESS_DEBUG, "dateext suffix '%s'\n", dext_str);
695
message(MESS_DEBUG, "glob pattern '%s'\n", dext_pattern);
531
697
/* First compress the previous log when necessary */
532
698
if (log->flags & LOG_FLAG_COMPRESS &&
533
log->flags & LOG_FLAG_DELAYCOMPRESS) {
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",
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++) {
709
snprintf(oldName, PATH_MAX, "%s", (globResult.gl_pathv)[i]);
710
if (stat(oldName, &sbprev)) {
712
"previous log %s does not exist\n",
715
hasErrors = compressLogFile(oldName, log, &sbprev);
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);
725
globfree(&globResult);
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",
736
hasErrors = compressLogFile(oldName, log, &sbprev);
545
sprintf(oldName, "%s/%s.%d%s%s", dirName, baseName,
546
logStart + rotateCount, fileext, compext);
547
strcpy(newName, oldName);
549
strcpy(disposeName, oldName);
551
firstRotated = alloca(strlen(dirName) + strlen(baseName) +
552
strlen(fileext) + strlen(compext) + 30);
553
sprintf(firstRotated, "%s/%s.%d%s%s", dirName, baseName,
555
(log->flags & LOG_FLAG_DELAYCOMPRESS) ? "" : compext);
741
rotNames->firstRotated =
742
malloc(strlen(rotNames->dirName) + strlen(rotNames->baseName) +
743
strlen(fileext) + strlen(compext) + 30);
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);
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 */
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)
765
(((nowSecs - fst_buf.st_mtime) / 60 / 60 / 24)
766
> log->rotateAge))) {
767
if (mail_out != -1) {
769
(globResult.gl_pathv)[mail_out];
770
if (!hasErrors && log->logAddress)
772
mailLogWrapper(mailFilename,
776
message(MESS_DEBUG, "removing %s\n", mailFilename);
777
hasErrors = removeLogFile(mailFilename, log);
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);
788
free(rotNames->disposeName);
789
rotNames->disposeName = NULL;
792
message(MESS_DEBUG, "glob finding old rotated logs failed\n");
793
free(rotNames->disposeName);
794
rotNames->disposeName = NULL;
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);
802
if (log->rotateAge) {
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)
810
char *mailFilename = oldName;
811
if (!hasErrors && log->logAddress)
813
mailLogWrapper(mailFilename, mailCommand,
816
hasErrors = removeLogFile(mailFilename, log);
821
snprintf(oldName, PATH_MAX, "%s/%s.%d%s%s", rotNames->dirName,
822
rotNames->baseName, logStart + rotateCount, fileext,
824
strcpy(newName, oldName);
826
strcpy(rotNames->disposeName, oldName);
828
sprintf(rotNames->firstRotated, "%s/%s.%d%s%s", rotNames->dirName,
829
rotNames->baseName, logStart, fileext,
830
(log->flags & LOG_FLAG_DELAYCOMPRESS) ? "" : compext);
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",
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));
575
message(MESS_ERROR, "error getting file context %s: %s\n",
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) {
838
"getting default context: %s\n",
840
if (selinux_enforce) {
847
if (setfscreatecon_raw(oldContext) < 0) {
849
"setting file context %s to %s: %s\n",
850
log->files[logNum], oldContext,
852
if (selinux_enforce) {
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) {
582
for (i = rotateCount + logStart - 1; (i >= 0) && !hasErrors; i--) {
586
sprintf(oldName, "%s/%s.%d%s%s", dirName, baseName, i,
589
message(MESS_DEBUG, "renaming %s to %s (rotatecount %d, logstart %d, i %d), \n", oldName, newName,
590
rotateCount, logStart, i);
592
if (!debug && rename(oldName, newName)) {
593
if (errno == ENOENT) {
594
message(MESS_DEBUG, "old log %s does not exist\n",
597
message(MESS_ERROR, "error renaming %s to %s: %s\n",
598
oldName, newName, strerror(errno));
873
for (i = rotateCount + logStart - 1; (i >= 0) && !hasErrors; i--) {
877
snprintf(oldName, PATH_MAX, "%s/%s.%d%s%s", rotNames->dirName,
878
rotNames->baseName, i, fileext, compext);
881
"renaming %s to %s (rotatecount %d, logstart %d, i %d), \n",
882
oldName, newName, rotateCount, logStart, i);
884
if (!debug && rename(oldName, newName)) {
885
if (errno == ENOENT) {
886
message(MESS_DEBUG, "old log %s does not exist\n",
889
message(MESS_ERROR, "error renaming %s to %s: %s\n",
890
oldName, newName, strerror(errno));
895
} /* !LOG_FLAG_DATEEXT */
897
if (log->flags & LOG_FLAG_DATEEXT) {
899
alloca(strlen(rotNames->dirName) + strlen(rotNames->baseName) +
900
strlen(fileext) + strlen(compext) + 30);
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)) {
907
"destination %s already exists, skipping rotation\n",
908
rotNames->firstRotated);
912
/* note: the gzip extension is *not* used here! */
913
asprintf(&(rotNames->finalName), "%s/%s.%d%s", rotNames->dirName,
914
rotNames->baseName, logStart, fileext);
606
/* note: the gzip extension is *not* used here! */
607
sprintf(finalName, "%s/%s.%d%s", dirName, baseName, logStart, fileext);
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);
918
if (!debug && rotNames->disposeName
919
&& access(rotNames->disposeName, F_OK)) {
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;
930
int rotateSingleLog(struct logInfo *log, int logNum, struct logState *state,
931
struct logNames *rotNames)
937
security_context_t savedContext = NULL;
940
if (!state->doRotate)
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");
629
if (!(log->flags & (LOG_FLAG_COPYTRUNCATE|LOG_FLAG_COPY))) {
630
message(MESS_DEBUG, "renaming %s to %s\n", log->files[logNum],
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));
945
if (!(log->flags & (LOG_FLAG_COPYTRUNCATE | LOG_FLAG_COPY))) {
947
if (selinux_enabled) {
948
security_context_t oldContext = NULL;
951
if ((fdcurr = open(log->files[logNum], O_RDWR)) < 0) {
952
message(MESS_ERROR, "error opening %s: %s\n",
957
if (fgetfilecon_raw(fdcurr, &oldContext) >= 0) {
958
if (getfscreatecon_raw(&savedContext) < 0) {
960
"getting default context: %s\n",
962
if (selinux_enforce) {
966
if (close(fdcurr) < 0)
967
message(MESS_ERROR, "error closing file %s",
972
if (setfscreatecon_raw(oldContext) < 0) {
974
"setting file context %s to %s: %s\n",
975
log->files[logNum], oldContext, strerror(errno));
976
if (selinux_enforce) {
980
if (close(fdcurr) < 0)
981
message(MESS_ERROR, "error closing file %s",
986
message(MESS_DEBUG, "fscreate context set to %s\n",
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",
1003
if (close(fdcurr) < 0)
1004
message(MESS_ERROR, "error closing file %s",
1005
log->files[logNum]);
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,
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);
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;
652
createUid = log->createUid;
654
if (log->createGid == NO_GID)
655
createGid = state->sb.st_gid;
657
createGid = log->createGid;
659
if (log->createMode == NO_MODE)
660
createMode = state->sb.st_mode & 0777;
662
createMode = log->createMode;
664
message(MESS_DEBUG, "creating new log mode = 0%o uid = %d "
665
"gid = %d\n", (unsigned int)createMode, (int)createUid, (int)createGid);
668
fd = open(log->files[logNum], O_CREAT | O_RDWR, createMode);
670
message(MESS_ERROR, "error creating %s: %s\n",
671
log->files[logNum], strerror(errno));
674
if (fchmod(fd, (S_IRUSR | S_IWUSR) & createMode)) {
675
message(MESS_ERROR, "error setting mode of "
676
"%s: %s\n", log->files[logNum],
680
if (fchown(fd, createUid, createGid)) {
681
message(MESS_ERROR, "error setting owner of "
682
"%s: %s\n", log->files[logNum],
686
if (fchmod(fd, createMode)) {
687
message(MESS_ERROR, "error setting mode of "
688
"%s: %s\n", log->files[logNum],
698
if (!hasErrors && log->flags & (LOG_FLAG_COPYTRUNCATE|LOG_FLAG_COPY))
699
hasErrors = copyTruncate(log->files[logNum], finalName,
700
&state->sb, log->flags);
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");
712
(log->flags & LOG_FLAG_COMPRESS) &&
713
!(log->flags & LOG_FLAG_DELAYCOMPRESS)) {
714
hasErrors = compressLogFile(finalName, log, &state->sb);
717
if (!hasErrors && log->logAddress) {
720
if (log->flags & LOG_FLAG_MAILFIRST)
721
mailFilename = firstRotated;
723
mailFilename = disposeName;
726
/* if the log is compressed (and we're not mailing a
727
file whose compression has been delayed), we need
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,
737
if (mailLog(mailFilename, mailCommand, NULL,
738
log->logAddress, mailFilename))
744
if (!hasErrors && disposeName) {
745
message(MESS_DEBUG, "removing old log %s\n", disposeName);
747
if (!debug && unlink(disposeName)) {
748
message(MESS_ERROR, "Failed to remove old log %s: %s\n",
749
disposeName, strerror(errno));
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,
1025
&& (log->flags & LOG_FLAG_COMPRESS)) ? log->
1027
message(MESS_DEBUG, "disposeName will be %s\n",
1028
rotNames->disposeName);
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;
1037
sb.st_uid = log->createUid;
1039
if (log->createGid == NO_GID)
1040
sb.st_gid = state->sb.st_gid;
1042
sb.st_gid = log->createGid;
1044
if (log->createMode == NO_MODE)
1045
sb.st_mode = state->sb.st_mode & 0777;
1047
sb.st_mode = log->createMode;
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);
1054
fd = createOutputFile(log->files[logNum], O_CREAT | O_RDWR,
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;
1073
&& log->flags & (LOG_FLAG_COPYTRUNCATE | LOG_FLAG_COPY))
1075
copyTruncate(log->files[logNum], rotNames->finalName,
1076
&state->sb, log->flags);
1082
int postrotateSingleLog(struct logInfo *log, int logNum, struct logState *state,
1083
struct logNames *rotNames)
1087
if (!state->doRotate)
1090
if ((log->flags & LOG_FLAG_COMPRESS) &&
1091
!(log->flags & LOG_FLAG_DELAYCOMPRESS)) {
1092
hasErrors = compressLogFile(rotNames->finalName, log, &state->sb);
1095
if (!hasErrors && log->logAddress) {
1098
if (log->flags & LOG_FLAG_MAILFIRST)
1099
mailFilename = rotNames->firstRotated;
1101
mailFilename = rotNames->disposeName;
1105
mailLogWrapper(mailFilename, mailCommand, logNum, log);
1108
if (!hasErrors && rotNames->disposeName)
1109
hasErrors = removeLogFile(rotNames->disposeName, log);
1112
if (selinux_enabled) {
1113
setfscreatecon_raw(prev_context);
1114
if (prev_context != NULL) {
759
1115
freecon(prev_context);
1116
prev_context = NULL;
764
1120
return hasErrors;
767
int rotateLogSet(logInfo * log, struct stateSet * sip, int force) {
1123
int rotateLogSet(struct logInfo *log, int force)
769
1126
int hasErrors = 0;
1127
int logHasErrors[log->numFiles];
770
1128
int numRotated = 0;
1129
struct logState **state;
1130
struct logNames **rotNames;
774
1133
log->criterium = ROT_FORCE;
776
1135
message(MESS_DEBUG, "\nrotating pattern: %s ", log->pattern);
777
1136
switch (log->criterium) {
791
1153
message(MESS_DEBUG, "forced from command line ");
795
if (log->rotateCount)
1157
if (log->rotateCount)
796
1158
message(MESS_DEBUG, "(%d rotations)\n", log->rotateCount);
798
1160
message(MESS_DEBUG, "(no old logs will be kept)\n");
801
message(MESS_DEBUG, "olddir is %s, ", log->oldDir);
803
if (log->flags & LOG_FLAG_IFEMPTY)
804
message(MESS_DEBUG, "empty log files are rotated, ");
1163
message(MESS_DEBUG, "olddir is %s, ", log->oldDir);
1165
if (log->flags & LOG_FLAG_IFEMPTY)
1166
message(MESS_DEBUG, "empty log files are rotated, ");
806
message(MESS_DEBUG, "empty log files are not rotated, ");
1168
message(MESS_DEBUG, "empty log files are not rotated, ");
1171
message(MESS_DEBUG, "only log files >= %d bytes are rotated, ", log->minsize);
808
1173
if (log->logAddress) {
809
1174
message(MESS_DEBUG, "old logs mailed to %s\n", log->logAddress);
811
1176
message(MESS_DEBUG, "old logs are removed\n");
814
1179
for (i = 0; i < log->numFiles; i++) {
815
hasErrors |= findNeedRotating(log, i, sip);
817
/* sure is a lot of findStating going on .. */
818
if ((findState(log->files[i], sip))->doRotate)
1180
logHasErrors[i] = findNeedRotating(log, i);
1181
hasErrors |= logHasErrors[i];
1183
/* sure is a lot of findStating going on .. */
1184
if ((findState(log->files[i]))->doRotate)
822
1188
if (log->first) {
824
message(MESS_DEBUG, "not running first action script, "
825
"since no logs will be rotated\n");
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);
836
if (log->pre && (log->flags & LOG_FLAG_SHAREDSCRIPTS)) {
838
message(MESS_DEBUG, "not running shared prerotate script, "
839
"since no logs will be rotated\n");
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);
850
/* should there be an if(!hasErrors) here? */
1190
message(MESS_DEBUG, "not running first action script, "
1191
"since no logs will be rotated\n");
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);
1198
/* finish early, firstaction failed, affects all logs in set */
1204
state = malloc(log->numFiles * sizeof(struct logState *));
1205
rotNames = malloc(log->numFiles * sizeof(struct logNames *));
1208
(!(log->flags & LOG_FLAG_SHAREDSCRIPTS) && j < log->numFiles)
1209
|| ((log->flags & LOG_FLAG_SHAREDSCRIPTS) && j < 1); 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]);
1216
rotNames[i] = malloc(sizeof(struct logNames));
1217
memset(rotNames[i], 0, sizeof(struct logNames));
1220
prerotateSingleLog(log, i, state[i], rotNames[i]);
1221
hasErrors |= logHasErrors[i];
1225
&& (! ( (logHasErrors[j] && !(log->flags & LOG_FLAG_SHAREDSCRIPTS))
1226
|| (hasErrors && (log->flags & LOG_FLAG_SHAREDSCRIPTS)) ) )) {
1228
message(MESS_DEBUG, "not running prerotate script, "
1229
"since no logs will be rotated\n");
1231
message(MESS_DEBUG, "running prerotate script\n");
1232
if (runScript(log->pattern, log->pre)) {
1233
if (log->flags & LOG_FLAG_SHAREDSCRIPTS)
1235
"error running shared prerotate script "
1236
"for '%s'\n", log->pattern);
1239
"error running non-shared prerotate script "
1240
"for %s of '%s'\n", log->files[j], log->pattern);
1242
logHasErrors[j] = 1;
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)) ) ) {
1254
rotateSingleLog(log, i, state[i], rotNames[i]);
1255
hasErrors |= logHasErrors[i];
1260
&& (! ( (logHasErrors[j] && !(log->flags & LOG_FLAG_SHAREDSCRIPTS))
1261
|| (hasErrors && (log->flags & LOG_FLAG_SHAREDSCRIPTS)) ) )) {
1263
message(MESS_DEBUG, "not running postrotate script, "
1264
"since no logs were rotated\n");
1266
message(MESS_DEBUG, "running postrotate script\n");
1267
if (runScript(log->pattern, log->post)) {
1268
if (log->flags & LOG_FLAG_SHAREDSCRIPTS)
1270
"error running shared postrotate script "
1271
"for '%s'\n", log->pattern);
1274
"error running non-shared postrotate script "
1275
"for %s of '%s'\n", log->files[j], log->pattern);
1277
logHasErrors[j] = 1;
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)) ) ) {
1289
postrotateSingleLog(log, i, state[i], rotNames[i]);
1290
hasErrors |= logHasErrors[i];
851
1296
for (i = 0; i < log->numFiles; i++) {
852
state = findState(log->files[i], sip);
854
hasErrors |= rotateSingleLog(log, i, state);
857
if (log->post && (log->flags & LOG_FLAG_SHAREDSCRIPTS)) {
859
message(MESS_DEBUG, "not running shared postrotate script, "
860
"since no logs were rotated\n");
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);
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);
871
1307
if (log->last) {
873
message(MESS_DEBUG, "not running last action script, "
874
"since no logs will be rotated\n");
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);
1309
message(MESS_DEBUG, "not running last action script, "
1310
"since no logs will be rotated\n");
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);
885
1321
return hasErrors;
888
static int writeState(char * stateFilename, struct stateSet si) {
1324
static int writeState(char *stateFilename)
893
1330
f = fopen(stateFilename, "w");
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));
901
1337
fprintf(f, "logrotate state -- version 2\n");
903
for (i = 0; i < si.numStates; i++) {
1339
for (p = states.lh_first; p != NULL; p = p->list.le_next) {
905
for (chptr = si.states[i].fn; *chptr; chptr++) {
1341
for (chptr = p->fn; *chptr; chptr++) {
906
1342
switch (*chptr) {
911
1347
fputc(*chptr, 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);
926
static int readState(char * stateFilename, struct stateSet * sip) {
1362
static int readState(char *stateFilename)
931
1368
int year, month, day;
1372
struct logState *st;
937
1374
struct stat f_stat;
939
1376
error = stat(stateFilename, &f_stat);
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");
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));