17
#include <sys/types.h>
21
#include <selinux/selinux.h>
22
static security_context_t prev_context = NULL;
23
int selinux_enabled = 0;
24
int selinux_enforce = 0;
27
#include "basenames.h"
29
#include "logrotate.h"
31
#if !defined(GLOB_ABORTED) && defined(GLOB_ABEND)
32
#define GLOB_ABORTED GLOB_ABEND
37
struct tm lastRotated; /* only tm.mon, tm_mday, tm_year are good! */
40
LIST_ENTRY(logState) list;
52
LIST_HEAD(stateSet, logState) head;
55
unsigned int hashSize;
58
char *mailCommand = DEFAULT_MAIL_COMMAND;
61
static int shred_file(char *filename, struct logInfo *log);
63
static int globerr(const char *pathname, int theerr)
65
message(MESS_ERROR, "error accessing %s: %s\n", pathname,
68
/* We want the glob operation to continue, so return 0 */
72
#define HASH_SIZE_MIN 64
73
static int allocateHash(void)
81
for (log = logs.tqh_first; log != NULL; log = log->list.tqe_next)
86
/* Enforce some reasonable minimum hash size */
87
if (hs < HASH_SIZE_MIN)
90
states = calloc(hs, sizeof(struct logStates *));
92
message(MESS_ERROR, "could not allocate memory for "
97
for (i = 0; i < hs; i++) {
98
states[i] = malloc(sizeof(struct logState));
99
if (states[i] == NULL) {
100
message(MESS_ERROR, "could not allocate memory for "
104
LIST_INIT(&(states[i]->head));
112
#define HASH_CONST 13
113
static unsigned hashIndex(const char *fn)
122
return hash % hashSize;
125
static struct logState *newState(const char *fn)
127
struct tm now = *localtime(&nowSecs);
128
struct logState *new;
131
if ((new = malloc(sizeof(*new))) == NULL)
134
if ((new->fn = strdup(fn)) == NULL)
139
memset(&new->lastRotated, 0, sizeof(new->lastRotated));
140
new->lastRotated.tm_mon = now.tm_mon;
141
new->lastRotated.tm_mday = now.tm_mday;
142
new->lastRotated.tm_year = now.tm_year;
143
new->lastRotated.tm_hour = now.tm_hour;
144
new->lastRotated.tm_isdst = now.tm_isdst;
146
/* fill in the rest of the new->lastRotated fields */
147
lr_time = mktime(&new->lastRotated);
148
new->lastRotated = *localtime(&lr_time);
153
static struct logState *findState(const char *fn)
155
unsigned int i = hashIndex(fn);
158
for (p = states[i]->head.lh_first; p != NULL; p = p->list.le_next)
159
if (!strcmp(fn, p->fn))
164
if ((p = newState(fn)) == NULL)
167
LIST_INSERT_HEAD(&(states[i]->head), p, list);
173
static int runScript(char *logfn, char *script)
178
message(MESS_DEBUG, "running script with arg %s: \"%s\"\n",
184
execl("/bin/sh", "sh", "-c", script, "logrotate_script", logfn, NULL);
192
int createOutputFile(char *fileName, int flags, struct stat *sb)
196
fd = open(fileName, flags, sb->st_mode);
198
message(MESS_ERROR, "error creating output file %s: %s\n",
199
fileName, strerror(errno));
202
if (fchmod(fd, (S_IRUSR | S_IWUSR) & sb->st_mode)) {
203
message(MESS_ERROR, "error setting mode of %s: %s\n",
204
fileName, strerror(errno));
208
if (fchown(fd, sb->st_uid, sb->st_gid)) {
209
message(MESS_ERROR, "error setting owner of %s: %s\n",
210
fileName, strerror(errno));
214
if (fchmod(fd, sb->st_mode)) {
215
message(MESS_ERROR, "error setting mode of %s: %s\n",
216
fileName, strerror(errno));
223
#define SHRED_CALL "shred -u "
224
#define SHRED_COUNT_FLAG "-n "
226
/* unlink, but try to call shred from GNU fileutils */
227
static int shred_file(char *filename, struct logInfo *log)
231
char count[DIGITS]; /* that's a lot of shredding :) */
233
if (!(log->flags & LOG_FLAG_SHRED)) {
234
return unlink(filename);
237
len = strlen(filename) + strlen(SHRED_CALL);
238
len += strlen(SHRED_COUNT_FLAG) + DIGITS;
242
message(MESS_ERROR, "malloc error while shredding");
243
return unlink(filename);
245
strcpy(cmd, SHRED_CALL);
246
if (log->shred_cycles != 0) {
247
strcat(cmd, SHRED_COUNT_FLAG);
248
snprintf(count, DIGITS - 1, "%d", log->shred_cycles);
252
strcat(cmd, filename);
256
message(MESS_ERROR, "Failed to shred %s\n, trying unlink", filename);
258
message(MESS_NORMAL, "Shred returned %d\n", ret);
260
return unlink(filename);
266
static int removeLogFile(char *name, struct logInfo *log)
268
message(MESS_DEBUG, "removing old log %s\n", name);
270
if (!debug && shred_file(name, log)) {
271
message(MESS_ERROR, "Failed to remove old log %s: %s\n",
272
name, strerror(errno));
278
static int compressLogFile(char *name, struct logInfo *log, struct stat *sb)
280
char *compressedName;
281
const char **fullCommand;
288
message(MESS_DEBUG, "compressing log with: %s\n", log->compress_prog);
292
fullCommand = alloca(sizeof(*fullCommand) *
293
(log->compress_options_count + 2));
294
fullCommand[0] = log->compress_prog;
295
for (i = 0; i < log->compress_options_count; i++)
296
fullCommand[i + 1] = log->compress_options_list[i];
297
fullCommand[log->compress_options_count + 1] = NULL;
299
compressedName = alloca(strlen(name) + strlen(log->compress_ext) + 2);
300
sprintf(compressedName, "%s%s", name, log->compress_ext);
302
if ((inFile = open(name, O_RDONLY)) < 0) {
303
message(MESS_ERROR, "unable to open %s for compression\n", name);
308
createOutputFile(compressedName, O_RDWR | O_CREAT | O_TRUNC, sb);
320
execvp(fullCommand[0], (void *) fullCommand);
329
if (!WIFEXITED(status) || WEXITSTATUS(status)) {
330
message(MESS_ERROR, "failed to compress log %s\n", name);
334
utim.actime = sb->st_atime;
335
utim.modtime = sb->st_mtime;
336
utime(compressedName,&utim);
337
/* If we can't change atime/mtime, it's not a disaster.
338
It might possibly fail under SELinux. */
340
shred_file(name, log);
345
static int mailLog(char *logFile, char *mailCommand,
346
char *uncompressCommand, char *address, char *subject)
349
pid_t mailChild, uncompressChild = 0;
350
int mailStatus, uncompressStatus;
351
int uncompressPipe[2];
352
char *mailArgv[] = { mailCommand, "-s", subject, address, NULL };
355
if ((mailInput = open(logFile, O_RDONLY)) < 0) {
356
message(MESS_ERROR, "failed to open %s for mailing: %s\n", logFile,
361
if (uncompressCommand) {
362
if (pipe(uncompressPipe) < 0) {
363
message(MESS_ERROR, "error opening pipe for uncompress: %s",
367
if (!(uncompressChild = fork())) {
368
/* uncompress child */
371
dup2(uncompressPipe[1], 1);
372
close(uncompressPipe[0]);
373
close(uncompressPipe[1]);
375
execlp(uncompressCommand, uncompressCommand, NULL);
380
mailInput = uncompressPipe[0];
381
close(uncompressPipe[1]);
384
if (!(mailChild = fork())) {
389
execvp(mailArgv[0], mailArgv);
395
waitpid(mailChild, &mailStatus, 0);
397
if (!WIFEXITED(mailStatus) || WEXITSTATUS(mailStatus)) {
398
message(MESS_ERROR, "mail command failed for %s\n", logFile);
402
if (uncompressCommand) {
403
waitpid(uncompressChild, &uncompressStatus, 0);
405
if (!WIFEXITED(uncompressStatus) || WEXITSTATUS(uncompressStatus)) {
406
message(MESS_ERROR, "uncompress command failed mailing %s\n",
415
static int mailLogWrapper(char *mailFilename, char *mailCommand,
416
int logNum, struct logInfo *log)
418
/* if the log is compressed (and we're not mailing a
419
* file whose compression has been delayed), we need
420
* to uncompress it */
421
if ((log->flags & LOG_FLAG_COMPRESS) &&
422
!((log->flags & LOG_FLAG_DELAYCOMPRESS) &&
423
(log->flags & LOG_FLAG_MAILFIRST))) {
424
if (mailLog(mailFilename, mailCommand,
425
log->uncompress_prog, log->logAddress,
429
if (mailLog(mailFilename, mailCommand, NULL,
430
log->logAddress, mailFilename))
436
static int copyTruncate(char *currLog, char *saveLog, struct stat *sb,
440
int fdcurr = -1, fdsave = -1;
443
message(MESS_DEBUG, "copying %s to %s\n", currLog, saveLog);
446
if ((fdcurr = open(currLog, O_RDWR)) < 0) {
447
message(MESS_ERROR, "error opening %s: %s\n", currLog,
452
if (selinux_enabled) {
453
security_context_t oldContext;
454
if (fgetfilecon_raw(fdcurr, &oldContext) >= 0) {
455
if (getfscreatecon_raw(&prev_context) < 0) {
457
"getting default context: %s\n",
459
if (selinux_enforce) {
464
if (setfscreatecon_raw(oldContext) < 0) {
466
"setting file context %s to %s: %s\n",
467
saveLog, oldContext, strerror(errno));
468
if (selinux_enforce) {
473
message(MESS_DEBUG, "set default create context\n");
476
if (errno != ENOTSUP) {
477
message(MESS_ERROR, "getting file context %s: %s\n",
478
currLog, strerror(errno));
479
if (selinux_enforce) {
487
createOutputFile(saveLog, O_WRONLY | O_CREAT | O_TRUNC, sb);
489
if (selinux_enabled) {
490
setfscreatecon_raw(prev_context);
491
freecon(prev_context);
499
while ((cnt = read(fdcurr, buf, sizeof(buf))) > 0) {
500
if (write(fdsave, buf, cnt) != cnt) {
501
message(MESS_ERROR, "error writing to %s: %s\n",
502
saveLog, strerror(errno));
509
message(MESS_ERROR, "error reading %s: %s\n",
510
currLog, strerror(errno));
517
if (flags & LOG_FLAG_COPYTRUNCATE) {
518
message(MESS_DEBUG, "truncating %s\n", currLog);
521
if (ftruncate(fdcurr, 0)) {
522
message(MESS_ERROR, "error truncating %s: %s\n", currLog,
529
message(MESS_DEBUG, "Not truncating %s\n", currLog);
536
int findNeedRotating(struct logInfo *log, int logNum)
539
struct logState *state = NULL;
540
struct tm now = *localtime(&nowSecs);
542
message(MESS_DEBUG, "considering log %s\n", log->files[logNum]);
544
if (stat(log->files[logNum], &sb)) {
545
if ((log->flags & LOG_FLAG_MISSINGOK) && (errno == ENOENT)) {
546
message(MESS_DEBUG, " log %s does not exist -- skipping\n",
550
message(MESS_ERROR, "stat of %s failed: %s\n", log->files[logNum],
555
state = findState(log->files[logNum]);
559
if (log->criterium == ROT_SIZE) {
560
state->doRotate = (sb.st_size >= log->threshhold);
561
} else if (log->criterium == ROT_FORCE) {
562
/* user forced rotation of logs from command line */
564
} else if (state->lastRotated.tm_year > now.tm_year ||
565
(state->lastRotated.tm_year == now.tm_year &&
566
(state->lastRotated.tm_mon > now.tm_mon ||
567
(state->lastRotated.tm_mon == now.tm_mon &&
568
state->lastRotated.tm_mday > now.tm_mday)))) {
570
"log %s last rotated in the future -- rotation forced\n",
573
} else if (state->lastRotated.tm_year != now.tm_year ||
574
state->lastRotated.tm_mon != now.tm_mon ||
575
state->lastRotated.tm_mday != now.tm_mday) {
576
switch (log->criterium) {
579
1) the current weekday is before the weekday of the
581
2) more then a week has passed since the last
583
state->doRotate = ((now.tm_wday < state->lastRotated.tm_wday)
586
mktime(&state->lastRotated)) >
590
/* rotate if the logs haven't been rotated this month or
592
state->doRotate = ((now.tm_mon != state->lastRotated.tm_mon) ||
594
state->lastRotated.tm_year));
597
/* FIXME: only days=1 is implemented!! */
601
/* rotate if the logs haven't been rotated this year */
602
state->doRotate = (now.tm_year != state->lastRotated.tm_year);
609
if (log->minsize && sb.st_size < log->minsize)
613
/* The notifempty flag overrides the normal criteria */
614
if (!(log->flags & LOG_FLAG_IFEMPTY) && !sb.st_size)
617
if (state->doRotate) {
618
message(MESS_DEBUG, " log needs rotating\n");
620
message(MESS_DEBUG, " log does not need rotating\n");
626
int prerotateSingleLog(struct logInfo *log, int logNum, struct logState *state,
627
struct logNames *rotNames)
629
struct tm now = *localtime(&nowSecs);
630
char *oldName, *newName = NULL;
639
int rotateCount = log->rotateCount ? log->rotateCount : 1;
640
int logStart = (log->logStart == -1) ? 1 : log->logStart;
641
#define DATEEXT_LEN 64
642
#define PATTERN_LEN (DATEEXT_LEN * 2)
643
char dext_str[DATEEXT_LEN];
644
char dformat[DATEEXT_LEN];
645
char dext_pattern[PATTERN_LEN];
648
if (!state->doRotate)
651
/* Logs with rotateCounts of 0 are rotated once, then removed. This
652
lets scripts run properly, and everything gets mailed properly. */
654
message(MESS_DEBUG, "rotating log %s, log->rotateCount is %d\n",
655
log->files[logNum], log->rotateCount);
657
if (log->flags & LOG_FLAG_COMPRESS)
658
compext = log->compress_ext;
660
state->lastRotated = now;
663
if (log->oldDir[0] != '/') {
664
char *ld = ourDirName(log->files[logNum]);
666
malloc(strlen(ld) + strlen(log->oldDir) + 2);
667
sprintf(rotNames->dirName, "%s/%s", ld, log->oldDir);
670
rotNames->dirName = strdup(log->oldDir);
672
rotNames->dirName = ourDirName(log->files[logNum]);
674
rotNames->baseName = strdup(ourBaseName(log->files[logNum]));
676
oldName = alloca(PATH_MAX);
677
newName = alloca(PATH_MAX);
678
rotNames->disposeName = malloc(PATH_MAX);
680
if (log->extension &&
683
baseName[strlen(rotNames->baseName) -
684
strlen(log->extension)]), log->extension,
685
strlen(log->extension)) == 0) {
688
fileext = log->extension;
690
calloc(strlen(rotNames->baseName) - strlen(log->extension) + 1,
692
strncat(tempstr, rotNames->baseName,
693
strlen(rotNames->baseName) - strlen(log->extension));
694
free(rotNames->baseName);
695
rotNames->baseName = tempstr;
698
/* Allow only %Y %d %m and create valid strftime format string
699
* Construct the glob pattern corresponding to the date format */
701
if (log->dateformat) {
703
memset(dext_pattern, 0, sizeof(dext_pattern));
704
dext = log->dateformat;
707
while ((*dext != '\0') && (!hasErrors)) {
708
/* Will there be a space for a char and '\0'? */
709
if (j >= (sizeof(dext_pattern) - 1)) {
710
message(MESS_ERROR, "Date format %s is too long\n",
716
switch (*(dext + 1)) {
718
strncat(dext_pattern, "[0-9][0-9]",
719
sizeof(dext_pattern) - strlen(dext_pattern));
720
j += 10; /* strlen("[0-9][0-9]") */
723
strncat(dext_pattern, "[0-9][0-9]",
724
sizeof(dext_pattern) - strlen(dext_pattern));
726
if (j >= (sizeof(dext_pattern) - 1)) {
727
message(MESS_ERROR, "Date format %s is too long\n",
732
dformat[i++] = *(dext++);
736
/* End of year 2293 this pattern does not work. */
737
strncat(dext_pattern,
738
"[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]",
739
sizeof(dext_pattern) - strlen(dext_pattern));
741
if (j >= (sizeof(dext_pattern) - 1)) {
742
message(MESS_ERROR, "Date format %s is too long\n",
747
dformat[i++] = *(dext++);
751
dformat[i++] = *dext;
753
dext_pattern[j++] = *dext;
758
dext_pattern[j++] = *dext;
764
message(MESS_DEBUG, "Converted '%s' -> '%s'\n", log->dateformat, dformat);
765
strftime(dext_str, sizeof(dext_str), dformat, &now);
767
/* The default dateformat and glob pattern */
768
strftime(dext_str, sizeof(dext_str), "-%Y%m%d", &now);
769
strncpy(dext_pattern, "-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]",
770
sizeof(dext_pattern));
771
dext_pattern[PATTERN_LEN - 1] = '\0';
773
message(MESS_DEBUG, "dateext suffix '%s'\n", dext_str);
774
message(MESS_DEBUG, "glob pattern '%s'\n", dext_pattern);
776
/* First compress the previous log when necessary */
777
if (log->flags & LOG_FLAG_COMPRESS &&
778
log->flags & LOG_FLAG_DELAYCOMPRESS) {
779
if (log->flags & LOG_FLAG_DATEEXT) {
780
/* glob for uncompressed files with our pattern */
781
if (asprintf(&glob_pattern, "%s/%s%s%s", rotNames->dirName,
782
rotNames->baseName, dext_pattern, fileext) < 0) {
783
message(MESS_ERROR, "could not allocate glob pattern memory\n");
785
rc = glob(glob_pattern, 0, globerr, &globResult);
786
if (!rc && globResult.gl_pathc > 0) {
787
for (i = 0; i < globResult.gl_pathc && !hasErrors; i++) {
790
snprintf(oldName, PATH_MAX, "%s", (globResult.gl_pathv)[i]);
791
if (stat(oldName, &sbprev)) {
793
"previous log %s does not exist\n",
796
hasErrors = compressLogFile(oldName, log, &sbprev);
801
"glob finding logs to compress failed\n");
802
/* fallback to old behaviour */
803
snprintf(oldName, PATH_MAX, "%s/%s.%d%s", rotNames->dirName,
804
rotNames->baseName, logStart, fileext);
806
globfree(&globResult);
811
snprintf(oldName, PATH_MAX, "%s/%s.%d%s", rotNames->dirName,
812
rotNames->baseName, logStart, fileext);
813
if (stat(oldName, &sbprev)) {
814
message(MESS_DEBUG, "previous log %s does not exist\n",
817
hasErrors = compressLogFile(oldName, log, &sbprev);
822
rotNames->firstRotated =
823
malloc(strlen(rotNames->dirName) + strlen(rotNames->baseName) +
824
strlen(fileext) + strlen(compext) + 30);
826
if (log->flags & LOG_FLAG_DATEEXT) {
827
/* glob for compressed files with our pattern
828
* and compress ext */
829
if (asprintf(&glob_pattern, "%s/%s%s%s%s", rotNames->dirName,
830
rotNames->baseName, dext_pattern, fileext, compext) < 0) {
831
message(MESS_ERROR, "could not allocate glob pattern memory\n");
833
rc = glob(glob_pattern, 0, globerr, &globResult);
835
/* search for files to drop, if we find one remember it,
836
* if we find another one mail and remove the first and
837
* remember the second and so on */
840
/* remove the first (n - rotateCount) matches
841
* no real rotation needed, since the files have
842
* the date in their name */
843
for (i = 0; i < globResult.gl_pathc; i++) {
844
if (!stat((globResult.gl_pathv)[i], &fst_buf)) {
845
if ((i <= ((int) globResult.gl_pathc - rotateCount))
846
|| ((log->rotateAge > 0)
848
(((nowSecs - fst_buf.st_mtime) / 60 / 60 / 24)
849
> log->rotateAge))) {
850
if (mail_out != -1) {
852
(globResult.gl_pathv)[mail_out];
853
if (!hasErrors && log->logAddress)
855
mailLogWrapper(mailFilename,
859
message(MESS_DEBUG, "removing %s\n", mailFilename);
860
hasErrors = removeLogFile(mailFilename, log);
866
if (mail_out != -1) {
867
/* oldName is oldest Backup found (for unlink later) */
868
snprintf(oldName, PATH_MAX, "%s", (globResult.gl_pathv)[mail_out]);
869
strcpy(rotNames->disposeName, oldName);
871
free(rotNames->disposeName);
872
rotNames->disposeName = NULL;
875
message(MESS_DEBUG, "glob finding old rotated logs failed\n");
876
free(rotNames->disposeName);
877
rotNames->disposeName = NULL;
879
/* firstRotated is most recently created/compressed rotated log */
880
sprintf(rotNames->firstRotated, "%s/%s%s%s%s",
881
rotNames->dirName, rotNames->baseName, dext_str, fileext,
882
(log->flags & LOG_FLAG_DELAYCOMPRESS) ? "" : compext);
883
globfree(&globResult);
886
if (log->rotateAge) {
888
for (i = 1; i <= rotateCount + 1; i++) {
889
snprintf(oldName, PATH_MAX, "%s/%s.%d%s%s", rotNames->dirName,
890
rotNames->baseName, i, fileext, compext);
891
if (!stat(oldName, &fst_buf)
892
&& (((nowSecs - fst_buf.st_mtime) / 60 / 60 / 24)
894
char *mailFilename = oldName;
895
if (!hasErrors && log->logAddress)
897
mailLogWrapper(mailFilename, mailCommand,
900
hasErrors = removeLogFile(mailFilename, log);
905
snprintf(oldName, PATH_MAX, "%s/%s.%d%s%s", rotNames->dirName,
906
rotNames->baseName, logStart + rotateCount, fileext,
908
strcpy(newName, oldName);
910
strcpy(rotNames->disposeName, oldName);
912
sprintf(rotNames->firstRotated, "%s/%s.%d%s%s", rotNames->dirName,
913
rotNames->baseName, logStart, fileext,
914
(log->flags & LOG_FLAG_DELAYCOMPRESS) ? "" : compext);
917
if (selinux_enabled) {
918
security_context_t oldContext = NULL;
919
if (getfilecon_raw(log->files[logNum], &oldContext) > 0) {
920
if (getfscreatecon_raw(&prev_context) < 0) {
922
"getting default context: %s\n",
924
if (selinux_enforce) {
929
if (setfscreatecon_raw(oldContext) < 0) {
931
"setting file context %s to %s: %s\n",
932
log->files[logNum], oldContext,
934
if (selinux_enforce) {
941
if (errno != ENOENT && errno != ENOTSUP) {
942
message(MESS_ERROR, "getting file context %s: %s\n",
943
log->files[logNum], strerror(errno));
944
if (selinux_enforce) {
951
for (i = rotateCount + logStart - 1; (i >= 0) && !hasErrors; i--) {
955
snprintf(oldName, PATH_MAX, "%s/%s.%d%s%s", rotNames->dirName,
956
rotNames->baseName, i, fileext, compext);
959
"renaming %s to %s (rotatecount %d, logstart %d, i %d), \n",
960
oldName, newName, rotateCount, logStart, i);
962
if (!debug && rename(oldName, newName)) {
963
if (errno == ENOENT) {
964
message(MESS_DEBUG, "old log %s does not exist\n",
967
message(MESS_ERROR, "error renaming %s to %s: %s\n",
968
oldName, newName, strerror(errno));
973
} /* !LOG_FLAG_DATEEXT */
975
if (log->flags & LOG_FLAG_DATEEXT) {
976
char *destFile = alloca(PATH_MAX);
979
if (asprintf(&(rotNames->finalName), "%s/%s%s%s", rotNames->dirName,
980
rotNames->baseName, dext_str, fileext) < 0) {
981
message(MESS_ERROR, "could not allocate finalName memory\n");
983
snprintf(destFile, PATH_MAX, "%s%s", rotNames->finalName, compext);
984
if (!stat(destFile, &fst_buf)) {
986
"destination %s already exists, skipping rotation\n",
987
rotNames->firstRotated);
991
/* note: the gzip extension is *not* used here! */
992
if (asprintf(&(rotNames->finalName), "%s/%s.%d%s", rotNames->dirName,
993
rotNames->baseName, logStart, fileext) < 0) {
994
message(MESS_ERROR, "could not allocate finalName memory\n");
998
/* if the last rotation doesn't exist, that's okay */
999
if (!debug && rotNames->disposeName
1000
&& access(rotNames->disposeName, F_OK)) {
1002
"log %s doesn't exist -- won't try to " "dispose of it\n",
1003
rotNames->disposeName);
1004
free(rotNames->disposeName);
1005
rotNames->disposeName = NULL;
1011
int rotateSingleLog(struct logInfo *log, int logNum, struct logState *state,
1012
struct logNames *rotNames)
1018
security_context_t savedContext = NULL;
1021
if (!state->doRotate)
1026
if (!(log->flags & (LOG_FLAG_COPYTRUNCATE | LOG_FLAG_COPY))) {
1028
if (selinux_enabled) {
1029
security_context_t oldContext = NULL;
1032
if ((fdcurr = open(log->files[logNum], O_RDWR)) < 0) {
1033
message(MESS_ERROR, "error opening %s: %s\n",
1038
if (fgetfilecon_raw(fdcurr, &oldContext) >= 0) {
1039
if (getfscreatecon_raw(&savedContext) < 0) {
1041
"getting default context: %s\n",
1043
if (selinux_enforce) {
1044
freecon(oldContext);
1045
if (close(fdcurr) < 0)
1046
message(MESS_ERROR, "error closing file %s",
1047
log->files[logNum]);
1051
if (setfscreatecon_raw(oldContext) < 0) {
1053
"setting file context %s to %s: %s\n",
1054
log->files[logNum], oldContext, strerror(errno));
1055
if (selinux_enforce) {
1056
freecon(oldContext);
1057
if (close(fdcurr) < 0)
1058
message(MESS_ERROR, "error closing file %s",
1059
log->files[logNum]);
1063
message(MESS_DEBUG, "fscreate context set to %s\n",
1065
freecon(oldContext);
1067
if (errno != ENOTSUP) {
1068
message(MESS_ERROR, "getting file context %s: %s\n",
1069
log->files[logNum], strerror(errno));
1070
if (selinux_enforce) {
1071
if (close(fdcurr) < 0)
1072
message(MESS_ERROR, "error closing file %s",
1073
log->files[logNum]);
1078
if (close(fdcurr) < 0)
1079
message(MESS_ERROR, "error closing file %s",
1080
log->files[logNum]);
1083
message(MESS_DEBUG, "renaming %s to %s\n", log->files[logNum],
1084
rotNames->finalName);
1085
if (!debug && !hasErrors &&
1086
rename(log->files[logNum], rotNames->finalName)) {
1087
message(MESS_ERROR, "failed to rename %s to %s: %s\n",
1088
log->files[logNum], rotNames->finalName,
1092
if (!log->rotateCount) {
1093
rotNames->disposeName =
1094
realloc(rotNames->disposeName,
1095
strlen(rotNames->dirName) +
1096
strlen(rotNames->baseName) +
1097
strlen(log->files[logNum]) + 10);
1098
sprintf(rotNames->disposeName, "%s%s", rotNames->finalName,
1100
&& (log->flags & LOG_FLAG_COMPRESS)) ? log->
1102
message(MESS_DEBUG, "disposeName will be %s\n",
1103
rotNames->disposeName);
1107
if (!hasErrors && log->flags & LOG_FLAG_CREATE &&
1108
!(log->flags & (LOG_FLAG_COPYTRUNCATE | LOG_FLAG_COPY))) {
1109
if (log->createUid == NO_UID)
1110
sb.st_uid = state->sb.st_uid;
1112
sb.st_uid = log->createUid;
1114
if (log->createGid == NO_GID)
1115
sb.st_gid = state->sb.st_gid;
1117
sb.st_gid = log->createGid;
1119
if (log->createMode == NO_MODE)
1120
sb.st_mode = state->sb.st_mode & 0777;
1122
sb.st_mode = log->createMode;
1124
message(MESS_DEBUG, "creating new %s mode = 0%o uid = %d "
1125
"gid = %d\n", log->files[logNum], (unsigned int) sb.st_mode,
1126
(int) sb.st_uid, (int) sb.st_gid);
1129
fd = createOutputFile(log->files[logNum], O_CREAT | O_RDWR,
1138
if (selinux_enabled) {
1139
setfscreatecon_raw(savedContext);
1140
freecon(savedContext);
1141
savedContext = NULL;
1146
&& log->flags & (LOG_FLAG_COPYTRUNCATE | LOG_FLAG_COPY))
1148
copyTruncate(log->files[logNum], rotNames->finalName,
1149
&state->sb, log->flags);
1155
int postrotateSingleLog(struct logInfo *log, int logNum, struct logState *state,
1156
struct logNames *rotNames)
1160
if (!state->doRotate)
1163
if ((log->flags & LOG_FLAG_COMPRESS) &&
1164
!(log->flags & LOG_FLAG_DELAYCOMPRESS)) {
1165
hasErrors = compressLogFile(rotNames->finalName, log, &state->sb);
1168
if (!hasErrors && log->logAddress) {
1171
if (log->flags & LOG_FLAG_MAILFIRST)
1172
mailFilename = rotNames->firstRotated;
1174
mailFilename = rotNames->disposeName;
1178
mailLogWrapper(mailFilename, mailCommand, logNum, log);
1181
if (!hasErrors && rotNames->disposeName)
1182
hasErrors = removeLogFile(rotNames->disposeName, log);
1185
if (selinux_enabled) {
1186
setfscreatecon_raw(prev_context);
1187
freecon(prev_context);
1188
prev_context = NULL;
1194
int rotateLogSet(struct logInfo *log, int force)
1198
int logHasErrors[log->numFiles];
1200
struct logState **state;
1201
struct logNames **rotNames;
1204
log->criterium = ROT_FORCE;
1206
message(MESS_DEBUG, "\nrotating pattern: %s ", log->pattern);
1207
switch (log->criterium) {
1209
message(MESS_DEBUG, "after %d days ", log->threshhold);
1212
message(MESS_DEBUG, "weekly ");
1215
message(MESS_DEBUG, "monthly ");
1218
message(MESS_DEBUG, "yearly ");
1221
message(MESS_DEBUG, "%d bytes ", log->threshhold);
1224
message(MESS_DEBUG, "forced from command line ");
1228
if (log->rotateCount)
1229
message(MESS_DEBUG, "(%d rotations)\n", log->rotateCount);
1231
message(MESS_DEBUG, "(no old logs will be kept)\n");
1234
message(MESS_DEBUG, "olddir is %s, ", log->oldDir);
1236
if (log->flags & LOG_FLAG_IFEMPTY)
1237
message(MESS_DEBUG, "empty log files are rotated, ");
1239
message(MESS_DEBUG, "empty log files are not rotated, ");
1242
message(MESS_DEBUG, "only log files >= %d bytes are rotated, ", log->minsize);
1244
if (log->logAddress) {
1245
message(MESS_DEBUG, "old logs mailed to %s\n", log->logAddress);
1247
message(MESS_DEBUG, "old logs are removed\n");
1250
for (i = 0; i < log->numFiles; i++) {
1251
logHasErrors[i] = findNeedRotating(log, i);
1252
hasErrors |= logHasErrors[i];
1254
/* sure is a lot of findStating going on .. */
1255
if ((findState(log->files[i]))->doRotate)
1261
message(MESS_DEBUG, "not running first action script, "
1262
"since no logs will be rotated\n");
1264
message(MESS_DEBUG, "running first action script\n");
1265
if (runScript(log->pattern, log->first)) {
1266
message(MESS_ERROR, "error running first action script "
1267
"for %s\n", log->pattern);
1269
/* finish early, firstaction failed, affects all logs in set */
1275
state = malloc(log->numFiles * sizeof(struct logState *));
1276
rotNames = malloc(log->numFiles * sizeof(struct logNames *));
1279
(!(log->flags & LOG_FLAG_SHAREDSCRIPTS) && j < log->numFiles)
1280
|| ((log->flags & LOG_FLAG_SHAREDSCRIPTS) && j < 1); j++) {
1283
((log->flags & LOG_FLAG_SHAREDSCRIPTS) && i < log->numFiles)
1284
|| (!(log->flags & LOG_FLAG_SHAREDSCRIPTS) && i == j); i++) {
1285
state[i] = findState(log->files[i]);
1287
rotNames[i] = malloc(sizeof(struct logNames));
1288
memset(rotNames[i], 0, sizeof(struct logNames));
1291
prerotateSingleLog(log, i, state[i], rotNames[i]);
1292
hasErrors |= logHasErrors[i];
1296
&& (! ( (logHasErrors[j] && !(log->flags & LOG_FLAG_SHAREDSCRIPTS))
1297
|| (hasErrors && (log->flags & LOG_FLAG_SHAREDSCRIPTS)) ) )) {
1299
message(MESS_DEBUG, "not running prerotate script, "
1300
"since no logs will be rotated\n");
1302
message(MESS_DEBUG, "running prerotate script\n");
1303
if (runScript(log->pattern, log->pre)) {
1304
if (log->flags & LOG_FLAG_SHAREDSCRIPTS)
1306
"error running shared prerotate script "
1307
"for '%s'\n", log->pattern);
1310
"error running non-shared prerotate script "
1311
"for %s of '%s'\n", log->files[j], log->pattern);
1313
logHasErrors[j] = 1;
1320
((log->flags & LOG_FLAG_SHAREDSCRIPTS) && i < log->numFiles)
1321
|| (!(log->flags & LOG_FLAG_SHAREDSCRIPTS) && i == j); i++) {
1322
if (! ( (logHasErrors[i] && !(log->flags & LOG_FLAG_SHAREDSCRIPTS))
1323
|| (hasErrors && (log->flags & LOG_FLAG_SHAREDSCRIPTS)) ) ) {
1325
rotateSingleLog(log, i, state[i], rotNames[i]);
1326
hasErrors |= logHasErrors[i];
1331
&& (! ( (logHasErrors[j] && !(log->flags & LOG_FLAG_SHAREDSCRIPTS))
1332
|| (hasErrors && (log->flags & LOG_FLAG_SHAREDSCRIPTS)) ) )) {
1334
message(MESS_DEBUG, "not running postrotate script, "
1335
"since no logs were rotated\n");
1337
message(MESS_DEBUG, "running postrotate script\n");
1338
if (runScript(log->pattern, log->post)) {
1339
if (log->flags & LOG_FLAG_SHAREDSCRIPTS)
1341
"error running shared postrotate script "
1342
"for '%s'\n", log->pattern);
1345
"error running non-shared postrotate script "
1346
"for %s of '%s'\n", log->files[j], log->pattern);
1348
logHasErrors[j] = 1;
1355
((log->flags & LOG_FLAG_SHAREDSCRIPTS) && i < log->numFiles)
1356
|| (!(log->flags & LOG_FLAG_SHAREDSCRIPTS) && i == j); i++) {
1357
if (! ( (logHasErrors[i] && !(log->flags & LOG_FLAG_SHAREDSCRIPTS))
1358
|| (hasErrors && (log->flags & LOG_FLAG_SHAREDSCRIPTS)) ) ) {
1360
postrotateSingleLog(log, i, state[i], rotNames[i]);
1361
hasErrors |= logHasErrors[i];
1367
for (i = 0; i < log->numFiles; i++) {
1368
free(rotNames[i]->firstRotated);
1369
free(rotNames[i]->disposeName);
1370
free(rotNames[i]->finalName);
1371
free(rotNames[i]->dirName);
1372
free(rotNames[i]->baseName);
1380
message(MESS_DEBUG, "not running last action script, "
1381
"since no logs will be rotated\n");
1383
message(MESS_DEBUG, "running last action script\n");
1384
if (runScript(log->pattern, log->last)) {
1385
message(MESS_ERROR, "error running last action script "
1386
"for %s\n", log->pattern);
1395
static int writeState(char *stateFilename)
1402
f = fopen(stateFilename, "w");
1404
message(MESS_ERROR, "error creating state file %s: %s\n",
1405
stateFilename, strerror(errno));
1409
fprintf(f, "logrotate state -- version 2\n");
1411
for (i = 0; i < hashSize; i++) {
1412
for (p = states[i]->head.lh_first; p != NULL;
1413
p = p->list.le_next) {
1415
for (chptr = p->fn; *chptr; chptr++) {
1425
fprintf(f, " %d-%d-%d\n",
1426
p->lastRotated.tm_year + 1900,
1427
p->lastRotated.tm_mon + 1,
1428
p->lastRotated.tm_mday);
1436
static int readState(char *stateFilename)
1442
int year, month, day;
1446
struct logState *st;
1450
error = stat(stateFilename, &f_stat);
1452
if ((error && errno == ENOENT) || (!error && f_stat.st_size == 0)) {
1453
/* create the file before continuing to ensure we have write
1454
access to the file */
1455
f = fopen(stateFilename, "w");
1457
message(MESS_ERROR, "error creating state file %s: %s\n",
1458
stateFilename, strerror(errno));
1461
fprintf(f, "logrotate state -- version 2\n");
1465
message(MESS_ERROR, "error stat()ing state file %s: %s\n",
1466
stateFilename, strerror(errno));
1470
f = fopen(stateFilename, "r");
1472
message(MESS_ERROR, "error opening state file %s: %s\n",
1473
stateFilename, strerror(errno));
1477
if (!fgets(buf, sizeof(buf) - 1, f)) {
1478
message(MESS_ERROR, "error reading top line of %s\n",
1484
if (strcmp(buf, "logrotate state -- version 1\n") &&
1485
strcmp(buf, "logrotate state -- version 2\n")) {
1487
message(MESS_ERROR, "bad top line in state file %s\n",
1494
while (fgets(buf, sizeof(buf) - 1, f)) {
1498
if (buf[i - 1] != '\n') {
1499
message(MESS_ERROR, "line %d too long in state file %s\n",
1500
line, stateFilename);
1510
if (poptParseArgvString(buf, &argc, &argv) || (argc != 2) ||
1511
(sscanf(argv[1], "%d-%d-%d", &year, &month, &day) != 3)) {
1512
message(MESS_ERROR, "bad line %d in state file %s\n",
1513
line, stateFilename);
1519
/* Hack to hide earlier bug */
1520
if ((year != 1900) && (year < 1970 || year > 2100)) {
1522
"bad year %d for file %s in state file %s\n", year,
1523
argv[0], stateFilename);
1529
if (month < 1 || month > 12) {
1531
"bad month %d for file %s in state file %s\n", month,
1532
argv[0], stateFilename);
1538
/* 0 to hide earlier bug */
1539
if (day < 0 || day > 31) {
1541
"bad day %d for file %s in state file %s\n", day,
1542
argv[0], stateFilename);
1548
year -= 1900, month -= 1;
1550
if ((st = findState(argv[0])) == NULL)
1553
st->lastRotated.tm_mon = month;
1554
st->lastRotated.tm_mday = day;
1555
st->lastRotated.tm_year = year;
1557
/* fill in the rest of the st->lastRotated fields */
1558
lr_time = mktime(&st->lastRotated);
1559
st->lastRotated = *localtime(&lr_time);
1568
int main(int argc, const char **argv)
1571
char *stateFile = STATEFILE;
1576
struct logInfo *log;
1577
int state_file_ok = 1;
1579
struct poptOption options[] = {
1580
{"debug", 'd', 0, 0, 'd',
1581
"Don't do anything, just test (implies -v)"},
1582
{"force", 'f', 0, &force, 0, "Force file rotation"},
1583
{"mail", 'm', POPT_ARG_STRING, &mailCommand, 0,
1584
"Command to send mail (instead of `" DEFAULT_MAIL_COMMAND "')",
1586
{"state", 's', POPT_ARG_STRING, &stateFile, 0,
1587
"Path of state file",
1589
{"verbose", 'v', 0, 0, 'v', "Display messages during rotation"},
1590
POPT_AUTOHELP {0, 0, 0, 0, 0}
1593
logSetLevel(MESS_NORMAL);
1594
setlocale (LC_ALL, "");
1596
optCon = poptGetContext("logrotate", argc, argv, options, 0);
1597
poptReadDefaultConfig(optCon, 1);
1598
poptSetOtherOptionHelp(optCon, "[OPTION...] <configfile>");
1600
while ((arg = poptGetNextOpt(optCon)) >= 0) {
1606
logSetLevel(MESS_DEBUG);
1612
fprintf(stderr, "logrotate: bad argument %s: %s\n",
1613
poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
1615
poptFreeContext(optCon);
1619
files = poptGetArgs((poptContext) optCon);
1621
fprintf(stderr, "logrotate " VERSION
1622
" - Copyright (C) 1995-2001 Red Hat, Inc.\n");
1624
"This may be freely redistributed under the terms of "
1625
"the GNU Public License\n\n");
1626
poptPrintUsage(optCon, stderr, 0);
1627
poptFreeContext(optCon);
1631
selinux_enabled = (is_selinux_enabled() > 0);
1632
selinux_enforce = security_getenforce();
1637
if (readAllConfigPaths(files)) {
1638
poptFreeContext(optCon);
1642
poptFreeContext(optCon);
1643
nowSecs = time(NULL);
1645
if (allocateHash() != 0)
1648
if (readState(stateFile))
1654
message(MESS_DEBUG, "\nHandling %d logs\n", numLogs);
1656
for (log = logs.tqh_first; log != NULL; log = log->list.tqe_next)
1657
rc |= rotateLogSet(log, force);
1659
if (!debug && state_file_ok)
1660
rc |= writeState(stateFile);
1664
message(MESS_ERROR, "could not read state file, "
1665
"will not attempt to write into it\n");