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;
144
/* fill in the rest of the new->lastRotated fields */
145
lr_time = mktime(&new->lastRotated);
146
new->lastRotated = *localtime(&lr_time);
151
static struct logState *findState(const char *fn)
153
unsigned int i = hashIndex(fn);
156
for (p = states[i]->head.lh_first; p != NULL; p = p->list.le_next)
157
if (!strcmp(fn, p->fn))
162
if ((p = newState(fn)) == NULL)
165
LIST_INSERT_HEAD(&(states[i]->head), p, list);
171
static int runScript(char *logfn, char *script)
176
message(MESS_DEBUG, "running script with arg %s: \"%s\"\n",
182
execl("/bin/sh", "sh", "-c", script, "logrotate_script", logfn, NULL);
190
int createOutputFile(char *fileName, int flags, struct stat *sb)
194
fd = open(fileName, flags, sb->st_mode);
196
message(MESS_ERROR, "error creating output file %s: %s\n",
197
fileName, strerror(errno));
200
if (fchmod(fd, (S_IRUSR | S_IWUSR) & sb->st_mode)) {
201
message(MESS_ERROR, "error setting mode of %s: %s\n",
202
fileName, strerror(errno));
206
if (fchown(fd, sb->st_uid, sb->st_gid)) {
207
message(MESS_ERROR, "error setting owner of %s: %s\n",
208
fileName, strerror(errno));
212
if (fchmod(fd, sb->st_mode)) {
213
message(MESS_ERROR, "error setting mode of %s: %s\n",
214
fileName, strerror(errno));
221
#define SHRED_CALL "shred -u "
222
#define SHRED_COUNT_FLAG "-n "
224
/* unlink, but try to call shred from GNU fileutils */
225
static int shred_file(char *filename, struct logInfo *log)
229
char count[DIGITS]; /* that's a lot of shredding :) */
231
if (!(log->flags & LOG_FLAG_SHRED)) {
232
return unlink(filename);
235
len = strlen(filename) + strlen(SHRED_CALL);
236
len += strlen(SHRED_COUNT_FLAG) + DIGITS;
240
message(MESS_ERROR, "malloc error while shredding");
241
return unlink(filename);
243
strcpy(cmd, SHRED_CALL);
244
if (log->shred_cycles != 0) {
245
strcat(cmd, SHRED_COUNT_FLAG);
246
snprintf(count, DIGITS - 1, "%d", log->shred_cycles);
250
strcat(cmd, filename);
254
message(MESS_ERROR, "Failed to shred %s\n, trying unlink", filename);
256
message(MESS_NORMAL, "Shred returned %d\n", ret);
258
return unlink(filename);
264
static int removeLogFile(char *name, struct logInfo *log)
266
message(MESS_DEBUG, "removing old log %s\n", name);
268
if (!debug && shred_file(name, log)) {
269
message(MESS_ERROR, "Failed to remove old log %s: %s\n",
270
name, strerror(errno));
276
static int compressLogFile(char *name, struct logInfo *log, struct stat *sb)
278
char *compressedName;
279
const char **fullCommand;
286
message(MESS_DEBUG, "compressing log with: %s\n", log->compress_prog);
290
fullCommand = alloca(sizeof(*fullCommand) *
291
(log->compress_options_count + 2));
292
fullCommand[0] = log->compress_prog;
293
for (i = 0; i < log->compress_options_count; i++)
294
fullCommand[i + 1] = log->compress_options_list[i];
295
fullCommand[log->compress_options_count + 1] = NULL;
297
compressedName = alloca(strlen(name) + strlen(log->compress_ext) + 2);
298
sprintf(compressedName, "%s%s", name, log->compress_ext);
300
if ((inFile = open(name, O_RDONLY)) < 0) {
301
message(MESS_ERROR, "unable to open %s for compression\n", name);
306
createOutputFile(compressedName, O_RDWR | O_CREAT | O_TRUNC, sb);
318
execvp(fullCommand[0], (void *) fullCommand);
327
if (!WIFEXITED(status) || WEXITSTATUS(status)) {
328
message(MESS_ERROR, "failed to compress log %s\n", name);
332
utim.actime = sb->st_atime;
333
utim.modtime = sb->st_mtime;
334
utime(compressedName,&utim);
335
/* If we can't change atime/mtime, it's not a disaster.
336
It might possibly fail under SELinux. */
338
shred_file(name, log);
343
static int mailLog(char *logFile, char *mailCommand,
344
char *uncompressCommand, char *address, char *subject)
347
pid_t mailChild, uncompressChild = 0;
348
int mailStatus, uncompressStatus;
349
int uncompressPipe[2];
350
char *mailArgv[] = { mailCommand, "-s", subject, address, NULL };
353
if ((mailInput = open(logFile, O_RDONLY)) < 0) {
354
message(MESS_ERROR, "failed to open %s for mailing: %s\n", logFile,
359
if (uncompressCommand) {
360
if (pipe(uncompressPipe) < 0) {
361
message(MESS_ERROR, "error opening pipe for uncompress: %s",
365
if (!(uncompressChild = fork())) {
366
/* uncompress child */
369
dup2(uncompressPipe[1], 1);
370
close(uncompressPipe[0]);
371
close(uncompressPipe[1]);
373
execlp(uncompressCommand, uncompressCommand, NULL);
378
mailInput = uncompressPipe[0];
379
close(uncompressPipe[1]);
382
if (!(mailChild = fork())) {
387
execvp(mailArgv[0], mailArgv);
393
waitpid(mailChild, &mailStatus, 0);
395
if (!WIFEXITED(mailStatus) || WEXITSTATUS(mailStatus)) {
396
message(MESS_ERROR, "mail command failed for %s\n", logFile);
400
if (uncompressCommand) {
401
waitpid(uncompressChild, &uncompressStatus, 0);
403
if (!WIFEXITED(uncompressStatus) || WEXITSTATUS(uncompressStatus)) {
404
message(MESS_ERROR, "uncompress command failed mailing %s\n",
413
static int mailLogWrapper(char *mailFilename, char *mailCommand,
414
int logNum, struct logInfo *log)
416
/* if the log is compressed (and we're not mailing a
417
* file whose compression has been delayed), we need
418
* to uncompress it */
419
if ((log->flags & LOG_FLAG_COMPRESS) &&
420
!((log->flags & LOG_FLAG_DELAYCOMPRESS) &&
421
(log->flags & LOG_FLAG_MAILFIRST))) {
422
if (mailLog(mailFilename, mailCommand,
423
log->uncompress_prog, log->logAddress,
427
if (mailLog(mailFilename, mailCommand, NULL,
428
log->logAddress, mailFilename))
434
static int copyTruncate(char *currLog, char *saveLog, struct stat *sb,
438
int fdcurr = -1, fdsave = -1;
441
message(MESS_DEBUG, "copying %s to %s\n", currLog, saveLog);
444
if ((fdcurr = open(currLog, O_RDWR)) < 0) {
445
message(MESS_ERROR, "error opening %s: %s\n", currLog,
450
if (selinux_enabled) {
451
security_context_t oldContext;
452
if (fgetfilecon_raw(fdcurr, &oldContext) >= 0) {
453
if (getfscreatecon_raw(&prev_context) < 0) {
455
"getting default context: %s\n",
457
if (selinux_enforce) {
462
if (setfscreatecon_raw(oldContext) < 0) {
464
"setting file context %s to %s: %s\n",
465
saveLog, oldContext, strerror(errno));
466
if (selinux_enforce) {
471
message(MESS_DEBUG, "set default create context\n");
474
if (errno != ENOTSUP) {
475
message(MESS_ERROR, "getting file context %s: %s\n",
476
currLog, strerror(errno));
477
if (selinux_enforce) {
485
createOutputFile(saveLog, O_WRONLY | O_CREAT | O_TRUNC, sb);
487
if (selinux_enabled) {
488
setfscreatecon_raw(prev_context);
489
freecon(prev_context);
497
while ((cnt = read(fdcurr, buf, sizeof(buf))) > 0) {
498
if (write(fdsave, buf, cnt) != cnt) {
499
message(MESS_ERROR, "error writing to %s: %s\n",
500
saveLog, strerror(errno));
507
message(MESS_ERROR, "error reading %s: %s\n",
508
currLog, strerror(errno));
515
if (flags & LOG_FLAG_COPYTRUNCATE) {
516
message(MESS_DEBUG, "truncating %s\n", currLog);
519
if (ftruncate(fdcurr, 0)) {
520
message(MESS_ERROR, "error truncating %s: %s\n", currLog,
527
message(MESS_DEBUG, "Not truncating %s\n", currLog);
534
int findNeedRotating(struct logInfo *log, int logNum)
537
struct logState *state = NULL;
538
struct tm now = *localtime(&nowSecs);
540
message(MESS_DEBUG, "considering log %s\n", log->files[logNum]);
542
if (stat(log->files[logNum], &sb)) {
543
if ((log->flags & LOG_FLAG_MISSINGOK) && (errno == ENOENT)) {
544
message(MESS_DEBUG, " log %s does not exist -- skipping\n",
548
message(MESS_ERROR, "stat of %s failed: %s\n", log->files[logNum],
553
state = findState(log->files[logNum]);
557
if (log->criterium == ROT_SIZE) {
558
state->doRotate = (sb.st_size >= log->threshhold);
559
} else if (log->criterium == ROT_FORCE) {
560
/* user forced rotation of logs from command line */
562
} else if (state->lastRotated.tm_year > now.tm_year ||
563
(state->lastRotated.tm_year == now.tm_year &&
564
(state->lastRotated.tm_mon > now.tm_mon ||
565
(state->lastRotated.tm_mon == now.tm_mon &&
566
state->lastRotated.tm_mday > now.tm_mday)))) {
568
"log %s last rotated in the future -- rotation forced\n",
571
} else if (state->lastRotated.tm_year != now.tm_year ||
572
state->lastRotated.tm_mon != now.tm_mon ||
573
state->lastRotated.tm_mday != now.tm_mday) {
574
switch (log->criterium) {
577
1) the current weekday is before the weekday of the
579
2) more then a week has passed since the last
581
state->doRotate = ((now.tm_wday < state->lastRotated.tm_wday)
584
mktime(&state->lastRotated)) >
588
/* rotate if the logs haven't been rotated this month or
590
state->doRotate = ((now.tm_mon != state->lastRotated.tm_mon) ||
592
state->lastRotated.tm_year));
595
/* FIXME: only days=1 is implemented!! */
599
/* rotate if the logs haven't been rotated this year */
600
state->doRotate = (now.tm_year != state->lastRotated.tm_year);
607
if (log->minsize && sb.st_size < log->minsize)
611
/* The notifempty flag overrides the normal criteria */
612
if (!(log->flags & LOG_FLAG_IFEMPTY) && !sb.st_size)
615
if (state->doRotate) {
616
message(MESS_DEBUG, " log needs rotating\n");
618
message(MESS_DEBUG, " log does not need rotating\n");
624
int prerotateSingleLog(struct logInfo *log, int logNum, struct logState *state,
625
struct logNames *rotNames)
627
struct tm now = *localtime(&nowSecs);
628
char *oldName, *newName = NULL;
637
int rotateCount = log->rotateCount ? log->rotateCount : 1;
638
int logStart = (log->logStart == -1) ? 1 : log->logStart;
639
#define DATEEXT_LEN 64
640
#define PATTERN_LEN (DATEEXT_LEN * 2)
641
char dext_str[DATEEXT_LEN];
642
char dformat[DATEEXT_LEN];
643
char dext_pattern[PATTERN_LEN];
646
if (!state->doRotate)
649
/* Logs with rotateCounts of 0 are rotated once, then removed. This
650
lets scripts run properly, and everything gets mailed properly. */
652
message(MESS_DEBUG, "rotating log %s, log->rotateCount is %d\n",
653
log->files[logNum], log->rotateCount);
655
if (log->flags & LOG_FLAG_COMPRESS)
656
compext = log->compress_ext;
658
state->lastRotated = now;
661
if (log->oldDir[0] != '/') {
662
char *ld = ourDirName(log->files[logNum]);
664
malloc(strlen(ld) + strlen(log->oldDir) + 2);
665
sprintf(rotNames->dirName, "%s/%s", ld, log->oldDir);
668
rotNames->dirName = strdup(log->oldDir);
670
rotNames->dirName = ourDirName(log->files[logNum]);
672
rotNames->baseName = strdup(ourBaseName(log->files[logNum]));
674
oldName = alloca(PATH_MAX);
675
newName = alloca(PATH_MAX);
676
rotNames->disposeName = malloc(PATH_MAX);
678
if (log->extension &&
681
baseName[strlen(rotNames->baseName) -
682
strlen(log->extension)]), log->extension,
683
strlen(log->extension)) == 0) {
686
fileext = log->extension;
688
calloc(strlen(rotNames->baseName) - strlen(log->extension) + 1,
690
strncat(tempstr, rotNames->baseName,
691
strlen(rotNames->baseName) - strlen(log->extension));
692
free(rotNames->baseName);
693
rotNames->baseName = tempstr;
696
/* Allow only %Y %d %m and create valid strftime format string
697
* Construct the glob pattern corresponding to the date format */
699
if (log->dateformat) {
701
memset(dext_pattern, 0, sizeof(dext_pattern));
702
dext = log->dateformat;
705
while ((*dext != '\0') && (!hasErrors)) {
706
/* Will there be a space for a char and '\0'? */
707
if (j >= (sizeof(dext_pattern) - 1)) {
708
message(MESS_ERROR, "Date format %s is too long\n",
714
switch (*(dext + 1)) {
716
strncat(dext_pattern, "[0-9][0-9]",
717
sizeof(dext_pattern) - strlen(dext_pattern));
718
j += 10; /* strlen("[0-9][0-9]") */
721
strncat(dext_pattern, "[0-9][0-9]",
722
sizeof(dext_pattern) - strlen(dext_pattern));
724
if (j >= (sizeof(dext_pattern) - 1)) {
725
message(MESS_ERROR, "Date format %s is too long\n",
730
dformat[i++] = *(dext++);
734
/* End of year 2293 this pattern does not work. */
735
strncat(dext_pattern,
736
"[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]",
737
sizeof(dext_pattern) - strlen(dext_pattern));
739
if (j >= (sizeof(dext_pattern) - 1)) {
740
message(MESS_ERROR, "Date format %s is too long\n",
745
dformat[i++] = *(dext++);
749
dformat[i++] = *dext;
751
dext_pattern[j++] = *dext;
756
dext_pattern[j++] = *dext;
762
message(MESS_DEBUG, "Converted '%s' -> '%s'\n", log->dateformat, dformat);
763
strftime(dext_str, sizeof(dext_str), dformat, &now);
765
/* The default dateformat and glob pattern */
766
strftime(dext_str, sizeof(dext_str), "-%Y%m%d", &now);
767
strncpy(dext_pattern, "-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]",
768
sizeof(dext_pattern));
769
dext_pattern[PATTERN_LEN - 1] = '\0';
771
message(MESS_DEBUG, "dateext suffix '%s'\n", dext_str);
772
message(MESS_DEBUG, "glob pattern '%s'\n", dext_pattern);
774
/* First compress the previous log when necessary */
775
if (log->flags & LOG_FLAG_COMPRESS &&
776
log->flags & LOG_FLAG_DELAYCOMPRESS) {
777
if (log->flags & LOG_FLAG_DATEEXT) {
778
/* glob for uncompressed files with our pattern */
779
if (asprintf(&glob_pattern, "%s/%s%s%s", rotNames->dirName,
780
rotNames->baseName, dext_pattern, fileext) < 0) {
781
message(MESS_ERROR, "could not allocate glob pattern memory\n");
783
rc = glob(glob_pattern, 0, globerr, &globResult);
784
if (!rc && globResult.gl_pathc > 0) {
785
for (i = 0; i < globResult.gl_pathc && !hasErrors; i++) {
788
snprintf(oldName, PATH_MAX, "%s", (globResult.gl_pathv)[i]);
789
if (stat(oldName, &sbprev)) {
791
"previous log %s does not exist\n",
794
hasErrors = compressLogFile(oldName, log, &sbprev);
799
"glob finding logs to compress failed\n");
800
/* fallback to old behaviour */
801
snprintf(oldName, PATH_MAX, "%s/%s.%d%s", rotNames->dirName,
802
rotNames->baseName, logStart, fileext);
804
globfree(&globResult);
809
snprintf(oldName, PATH_MAX, "%s/%s.%d%s", rotNames->dirName,
810
rotNames->baseName, logStart, fileext);
811
if (stat(oldName, &sbprev)) {
812
message(MESS_DEBUG, "previous log %s does not exist\n",
815
hasErrors = compressLogFile(oldName, log, &sbprev);
820
rotNames->firstRotated =
821
malloc(strlen(rotNames->dirName) + strlen(rotNames->baseName) +
822
strlen(fileext) + strlen(compext) + 30);
824
if (log->flags & LOG_FLAG_DATEEXT) {
825
/* glob for compressed files with our pattern
826
* and compress ext */
827
if (asprintf(&glob_pattern, "%s/%s%s%s%s", rotNames->dirName,
828
rotNames->baseName, dext_pattern, fileext, compext) < 0) {
829
message(MESS_ERROR, "could not allocate glob pattern memory\n");
831
rc = glob(glob_pattern, 0, globerr, &globResult);
833
/* search for files to drop, if we find one remember it,
834
* if we find another one mail and remove the first and
835
* remember the second and so on */
838
/* remove the first (n - rotateCount) matches
839
* no real rotation needed, since the files have
840
* the date in their name */
841
for (i = 0; i < globResult.gl_pathc; i++) {
842
if (!stat((globResult.gl_pathv)[i], &fst_buf)) {
843
if ((i <= ((int) globResult.gl_pathc - rotateCount))
844
|| ((log->rotateAge > 0)
846
(((nowSecs - fst_buf.st_mtime) / 60 / 60 / 24)
847
> log->rotateAge))) {
848
if (mail_out != -1) {
850
(globResult.gl_pathv)[mail_out];
851
if (!hasErrors && log->logAddress)
853
mailLogWrapper(mailFilename,
857
message(MESS_DEBUG, "removing %s\n", mailFilename);
858
hasErrors = removeLogFile(mailFilename, log);
864
if (mail_out != -1) {
865
/* oldName is oldest Backup found (for unlink later) */
866
snprintf(oldName, PATH_MAX, "%s", (globResult.gl_pathv)[mail_out]);
867
strcpy(rotNames->disposeName, oldName);
869
free(rotNames->disposeName);
870
rotNames->disposeName = NULL;
873
message(MESS_DEBUG, "glob finding old rotated logs failed\n");
874
free(rotNames->disposeName);
875
rotNames->disposeName = NULL;
877
/* firstRotated is most recently created/compressed rotated log */
878
sprintf(rotNames->firstRotated, "%s/%s%s%s%s",
879
rotNames->dirName, rotNames->baseName, dext_str, fileext, compext);
880
globfree(&globResult);
883
if (log->rotateAge) {
885
for (i = 1; i <= rotateCount + 1; i++) {
886
snprintf(oldName, PATH_MAX, "%s/%s.%d%s%s", rotNames->dirName,
887
rotNames->baseName, i, fileext, compext);
888
if (!stat(oldName, &fst_buf)
889
&& (((nowSecs - fst_buf.st_mtime) / 60 / 60 / 24)
891
char *mailFilename = oldName;
892
if (!hasErrors && log->logAddress)
894
mailLogWrapper(mailFilename, mailCommand,
897
hasErrors = removeLogFile(mailFilename, log);
902
snprintf(oldName, PATH_MAX, "%s/%s.%d%s%s", rotNames->dirName,
903
rotNames->baseName, logStart + rotateCount, fileext,
905
strcpy(newName, oldName);
907
strcpy(rotNames->disposeName, oldName);
909
sprintf(rotNames->firstRotated, "%s/%s.%d%s%s", rotNames->dirName,
910
rotNames->baseName, logStart, fileext,
911
(log->flags & LOG_FLAG_DELAYCOMPRESS) ? "" : compext);
914
if (selinux_enabled) {
915
security_context_t oldContext = NULL;
916
if (getfilecon_raw(log->files[logNum], &oldContext) > 0) {
917
if (getfscreatecon_raw(&prev_context) < 0) {
919
"getting default context: %s\n",
921
if (selinux_enforce) {
926
if (setfscreatecon_raw(oldContext) < 0) {
928
"setting file context %s to %s: %s\n",
929
log->files[logNum], oldContext,
931
if (selinux_enforce) {
938
if (errno != ENOENT && errno != ENOTSUP) {
939
message(MESS_ERROR, "getting file context %s: %s\n",
940
log->files[logNum], strerror(errno));
941
if (selinux_enforce) {
948
for (i = rotateCount + logStart - 1; (i >= 0) && !hasErrors; i--) {
952
snprintf(oldName, PATH_MAX, "%s/%s.%d%s%s", rotNames->dirName,
953
rotNames->baseName, i, fileext, compext);
956
"renaming %s to %s (rotatecount %d, logstart %d, i %d), \n",
957
oldName, newName, rotateCount, logStart, i);
959
if (!debug && rename(oldName, newName)) {
960
if (errno == ENOENT) {
961
message(MESS_DEBUG, "old log %s does not exist\n",
964
message(MESS_ERROR, "error renaming %s to %s: %s\n",
965
oldName, newName, strerror(errno));
970
} /* !LOG_FLAG_DATEEXT */
972
if (log->flags & LOG_FLAG_DATEEXT) {
973
char *destFile = alloca(PATH_MAX);
976
if (asprintf(&(rotNames->finalName), "%s/%s%s%s", rotNames->dirName,
977
rotNames->baseName, dext_str, fileext) < 0) {
978
message(MESS_ERROR, "could not allocate finalName memory\n");
980
snprintf(destFile, PATH_MAX, "%s%s", rotNames->finalName, compext);
981
if (!stat(destFile, &fst_buf)) {
983
"destination %s already exists, skipping rotation\n",
984
rotNames->firstRotated);
988
/* note: the gzip extension is *not* used here! */
989
if (asprintf(&(rotNames->finalName), "%s/%s.%d%s", rotNames->dirName,
990
rotNames->baseName, logStart, fileext) < 0) {
991
message(MESS_ERROR, "could not allocate finalName memory\n");
995
/* if the last rotation doesn't exist, that's okay */
996
if (!debug && rotNames->disposeName
997
&& access(rotNames->disposeName, F_OK)) {
999
"log %s doesn't exist -- won't try to " "dispose of it\n",
1000
rotNames->disposeName);
1001
free(rotNames->disposeName);
1002
rotNames->disposeName = NULL;
1008
int rotateSingleLog(struct logInfo *log, int logNum, struct logState *state,
1009
struct logNames *rotNames)
1015
security_context_t savedContext = NULL;
1018
if (!state->doRotate)
1023
if (!(log->flags & (LOG_FLAG_COPYTRUNCATE | LOG_FLAG_COPY))) {
1025
if (selinux_enabled) {
1026
security_context_t oldContext = NULL;
1029
if ((fdcurr = open(log->files[logNum], O_RDWR)) < 0) {
1030
message(MESS_ERROR, "error opening %s: %s\n",
1035
if (fgetfilecon_raw(fdcurr, &oldContext) >= 0) {
1036
if (getfscreatecon_raw(&savedContext) < 0) {
1038
"getting default context: %s\n",
1040
if (selinux_enforce) {
1041
freecon(oldContext);
1042
if (close(fdcurr) < 0)
1043
message(MESS_ERROR, "error closing file %s",
1044
log->files[logNum]);
1048
if (setfscreatecon_raw(oldContext) < 0) {
1050
"setting file context %s to %s: %s\n",
1051
log->files[logNum], oldContext, strerror(errno));
1052
if (selinux_enforce) {
1053
freecon(oldContext);
1054
if (close(fdcurr) < 0)
1055
message(MESS_ERROR, "error closing file %s",
1056
log->files[logNum]);
1060
message(MESS_DEBUG, "fscreate context set to %s\n",
1062
freecon(oldContext);
1064
if (errno != ENOTSUP) {
1065
message(MESS_ERROR, "getting file context %s: %s\n",
1066
log->files[logNum], strerror(errno));
1067
if (selinux_enforce) {
1068
if (close(fdcurr) < 0)
1069
message(MESS_ERROR, "error closing file %s",
1070
log->files[logNum]);
1075
if (close(fdcurr) < 0)
1076
message(MESS_ERROR, "error closing file %s",
1077
log->files[logNum]);
1080
message(MESS_DEBUG, "renaming %s to %s\n", log->files[logNum],
1081
rotNames->finalName);
1082
if (!debug && !hasErrors &&
1083
rename(log->files[logNum], rotNames->finalName)) {
1084
message(MESS_ERROR, "failed to rename %s to %s: %s\n",
1085
log->files[logNum], rotNames->finalName,
1089
if (!log->rotateCount) {
1090
rotNames->disposeName =
1091
realloc(rotNames->disposeName,
1092
strlen(rotNames->dirName) +
1093
strlen(rotNames->baseName) +
1094
strlen(log->files[logNum]) + 10);
1095
sprintf(rotNames->disposeName, "%s%s", rotNames->finalName,
1097
&& (log->flags & LOG_FLAG_COMPRESS)) ? log->
1099
message(MESS_DEBUG, "disposeName will be %s\n",
1100
rotNames->disposeName);
1104
if (!hasErrors && log->flags & LOG_FLAG_CREATE &&
1105
!(log->flags & (LOG_FLAG_COPYTRUNCATE | LOG_FLAG_COPY))) {
1106
if (log->createUid == NO_UID)
1107
sb.st_uid = state->sb.st_uid;
1109
sb.st_uid = log->createUid;
1111
if (log->createGid == NO_GID)
1112
sb.st_gid = state->sb.st_gid;
1114
sb.st_gid = log->createGid;
1116
if (log->createMode == NO_MODE)
1117
sb.st_mode = state->sb.st_mode & 0777;
1119
sb.st_mode = log->createMode;
1121
message(MESS_DEBUG, "creating new %s mode = 0%o uid = %d "
1122
"gid = %d\n", log->files[logNum], (unsigned int) sb.st_mode,
1123
(int) sb.st_uid, (int) sb.st_gid);
1126
fd = createOutputFile(log->files[logNum], O_CREAT | O_RDWR,
1135
if (selinux_enabled) {
1136
setfscreatecon_raw(savedContext);
1137
freecon(savedContext);
1138
savedContext = NULL;
1143
&& log->flags & (LOG_FLAG_COPYTRUNCATE | LOG_FLAG_COPY))
1145
copyTruncate(log->files[logNum], rotNames->finalName,
1146
&state->sb, log->flags);
1152
int postrotateSingleLog(struct logInfo *log, int logNum, struct logState *state,
1153
struct logNames *rotNames)
1157
if (!state->doRotate)
1160
if ((log->flags & LOG_FLAG_COMPRESS) &&
1161
!(log->flags & LOG_FLAG_DELAYCOMPRESS)) {
1162
hasErrors = compressLogFile(rotNames->finalName, log, &state->sb);
1165
if (!hasErrors && log->logAddress) {
1168
if (log->flags & LOG_FLAG_MAILFIRST)
1169
mailFilename = rotNames->firstRotated;
1171
mailFilename = rotNames->disposeName;
1175
mailLogWrapper(mailFilename, mailCommand, logNum, log);
1178
if (!hasErrors && rotNames->disposeName)
1179
hasErrors = removeLogFile(rotNames->disposeName, log);
1182
if (selinux_enabled) {
1183
setfscreatecon_raw(prev_context);
1184
freecon(prev_context);
1185
prev_context = NULL;
1191
int rotateLogSet(struct logInfo *log, int force)
1195
int logHasErrors[log->numFiles];
1197
struct logState **state;
1198
struct logNames **rotNames;
1201
log->criterium = ROT_FORCE;
1203
message(MESS_DEBUG, "\nrotating pattern: %s ", log->pattern);
1204
switch (log->criterium) {
1206
message(MESS_DEBUG, "after %d days ", log->threshhold);
1209
message(MESS_DEBUG, "weekly ");
1212
message(MESS_DEBUG, "monthly ");
1215
message(MESS_DEBUG, "yearly ");
1218
message(MESS_DEBUG, "%d bytes ", log->threshhold);
1221
message(MESS_DEBUG, "forced from command line ");
1225
if (log->rotateCount)
1226
message(MESS_DEBUG, "(%d rotations)\n", log->rotateCount);
1228
message(MESS_DEBUG, "(no old logs will be kept)\n");
1231
message(MESS_DEBUG, "olddir is %s, ", log->oldDir);
1233
if (log->flags & LOG_FLAG_IFEMPTY)
1234
message(MESS_DEBUG, "empty log files are rotated, ");
1236
message(MESS_DEBUG, "empty log files are not rotated, ");
1239
message(MESS_DEBUG, "only log files >= %d bytes are rotated, ", log->minsize);
1241
if (log->logAddress) {
1242
message(MESS_DEBUG, "old logs mailed to %s\n", log->logAddress);
1244
message(MESS_DEBUG, "old logs are removed\n");
1247
for (i = 0; i < log->numFiles; i++) {
1248
logHasErrors[i] = findNeedRotating(log, i);
1249
hasErrors |= logHasErrors[i];
1251
/* sure is a lot of findStating going on .. */
1252
if ((findState(log->files[i]))->doRotate)
1258
message(MESS_DEBUG, "not running first action script, "
1259
"since no logs will be rotated\n");
1261
message(MESS_DEBUG, "running first action script\n");
1262
if (runScript(log->pattern, log->first)) {
1263
message(MESS_ERROR, "error running first action script "
1264
"for %s\n", log->pattern);
1266
/* finish early, firstaction failed, affects all logs in set */
1272
state = malloc(log->numFiles * sizeof(struct logState *));
1273
rotNames = malloc(log->numFiles * sizeof(struct logNames *));
1276
(!(log->flags & LOG_FLAG_SHAREDSCRIPTS) && j < log->numFiles)
1277
|| ((log->flags & LOG_FLAG_SHAREDSCRIPTS) && j < 1); j++) {
1280
((log->flags & LOG_FLAG_SHAREDSCRIPTS) && i < log->numFiles)
1281
|| (!(log->flags & LOG_FLAG_SHAREDSCRIPTS) && i == j); i++) {
1282
state[i] = findState(log->files[i]);
1284
rotNames[i] = malloc(sizeof(struct logNames));
1285
memset(rotNames[i], 0, sizeof(struct logNames));
1288
prerotateSingleLog(log, i, state[i], rotNames[i]);
1289
hasErrors |= logHasErrors[i];
1293
&& (! ( (logHasErrors[j] && !(log->flags & LOG_FLAG_SHAREDSCRIPTS))
1294
|| (hasErrors && (log->flags & LOG_FLAG_SHAREDSCRIPTS)) ) )) {
1296
message(MESS_DEBUG, "not running prerotate script, "
1297
"since no logs will be rotated\n");
1299
message(MESS_DEBUG, "running prerotate script\n");
1300
if (runScript(log->pattern, log->pre)) {
1301
if (log->flags & LOG_FLAG_SHAREDSCRIPTS)
1303
"error running shared prerotate script "
1304
"for '%s'\n", log->pattern);
1307
"error running non-shared prerotate script "
1308
"for %s of '%s'\n", log->files[j], log->pattern);
1310
logHasErrors[j] = 1;
1317
((log->flags & LOG_FLAG_SHAREDSCRIPTS) && i < log->numFiles)
1318
|| (!(log->flags & LOG_FLAG_SHAREDSCRIPTS) && i == j); i++) {
1319
if (! ( (logHasErrors[i] && !(log->flags & LOG_FLAG_SHAREDSCRIPTS))
1320
|| (hasErrors && (log->flags & LOG_FLAG_SHAREDSCRIPTS)) ) ) {
1322
rotateSingleLog(log, i, state[i], rotNames[i]);
1323
hasErrors |= logHasErrors[i];
1328
&& (! ( (logHasErrors[j] && !(log->flags & LOG_FLAG_SHAREDSCRIPTS))
1329
|| (hasErrors && (log->flags & LOG_FLAG_SHAREDSCRIPTS)) ) )) {
1331
message(MESS_DEBUG, "not running postrotate script, "
1332
"since no logs were rotated\n");
1334
message(MESS_DEBUG, "running postrotate script\n");
1335
if (runScript(log->pattern, log->post)) {
1336
if (log->flags & LOG_FLAG_SHAREDSCRIPTS)
1338
"error running shared postrotate script "
1339
"for '%s'\n", log->pattern);
1342
"error running non-shared postrotate script "
1343
"for %s of '%s'\n", log->files[j], log->pattern);
1345
logHasErrors[j] = 1;
1352
((log->flags & LOG_FLAG_SHAREDSCRIPTS) && i < log->numFiles)
1353
|| (!(log->flags & LOG_FLAG_SHAREDSCRIPTS) && i == j); i++) {
1354
if (! ( (logHasErrors[i] && !(log->flags & LOG_FLAG_SHAREDSCRIPTS))
1355
|| (hasErrors && (log->flags & LOG_FLAG_SHAREDSCRIPTS)) ) ) {
1357
postrotateSingleLog(log, i, state[i], rotNames[i]);
1358
hasErrors |= logHasErrors[i];
1364
for (i = 0; i < log->numFiles; i++) {
1365
free(rotNames[i]->firstRotated);
1366
free(rotNames[i]->disposeName);
1367
free(rotNames[i]->finalName);
1368
free(rotNames[i]->dirName);
1369
free(rotNames[i]->baseName);
1377
message(MESS_DEBUG, "not running last action script, "
1378
"since no logs will be rotated\n");
1380
message(MESS_DEBUG, "running last action script\n");
1381
if (runScript(log->pattern, log->last)) {
1382
message(MESS_ERROR, "error running last action script "
1383
"for %s\n", log->pattern);
1392
static int writeState(char *stateFilename)
1399
f = fopen(stateFilename, "w");
1401
message(MESS_ERROR, "error creating state file %s: %s\n",
1402
stateFilename, strerror(errno));
1406
fprintf(f, "logrotate state -- version 2\n");
1408
for (i = 0; i < hashSize; i++) {
1409
for (p = states[i]->head.lh_first; p != NULL;
1410
p = p->list.le_next) {
1412
for (chptr = p->fn; *chptr; chptr++) {
1422
fprintf(f, " %d-%d-%d\n",
1423
p->lastRotated.tm_year + 1900,
1424
p->lastRotated.tm_mon + 1,
1425
p->lastRotated.tm_mday);
1433
static int readState(char *stateFilename)
1439
int year, month, day;
1443
struct logState *st;
1447
error = stat(stateFilename, &f_stat);
1449
if ((error && errno == ENOENT) || (!error && f_stat.st_size == 0)) {
1450
/* create the file before continuing to ensure we have write
1451
access to the file */
1452
f = fopen(stateFilename, "w");
1454
message(MESS_ERROR, "error creating state file %s: %s\n",
1455
stateFilename, strerror(errno));
1458
fprintf(f, "logrotate state -- version 2\n");
1462
message(MESS_ERROR, "error stat()ing state file %s: %s\n",
1463
stateFilename, strerror(errno));
1467
f = fopen(stateFilename, "r");
1469
message(MESS_ERROR, "error opening state file %s: %s\n",
1470
stateFilename, strerror(errno));
1474
if (!fgets(buf, sizeof(buf) - 1, f)) {
1475
message(MESS_ERROR, "error reading top line of %s\n",
1481
if (strcmp(buf, "logrotate state -- version 1\n") &&
1482
strcmp(buf, "logrotate state -- version 2\n")) {
1484
message(MESS_ERROR, "bad top line in state file %s\n",
1491
while (fgets(buf, sizeof(buf) - 1, f)) {
1495
if (buf[i - 1] != '\n') {
1496
message(MESS_ERROR, "line %d too long in state file %s\n",
1497
line, stateFilename);
1507
if (poptParseArgvString(buf, &argc, &argv) || (argc != 2) ||
1508
(sscanf(argv[1], "%d-%d-%d", &year, &month, &day) != 3)) {
1509
message(MESS_ERROR, "bad line %d in state file %s\n",
1510
line, stateFilename);
1516
/* Hack to hide earlier bug */
1517
if ((year != 1900) && (year < 1970 || year > 2100)) {
1519
"bad year %d for file %s in state file %s\n", year,
1520
argv[0], stateFilename);
1526
if (month < 1 || month > 12) {
1528
"bad month %d for file %s in state file %s\n", month,
1529
argv[0], stateFilename);
1535
/* 0 to hide earlier bug */
1536
if (day < 0 || day > 31) {
1538
"bad day %d for file %s in state file %s\n", day,
1539
argv[0], stateFilename);
1545
year -= 1900, month -= 1;
1547
if ((st = findState(argv[0])) == NULL)
1550
st->lastRotated.tm_mon = month;
1551
st->lastRotated.tm_mday = day;
1552
st->lastRotated.tm_year = year;
1554
/* fill in the rest of the st->lastRotated fields */
1555
lr_time = mktime(&st->lastRotated);
1556
st->lastRotated = *localtime(&lr_time);
1565
int main(int argc, const char **argv)
1568
char *stateFile = STATEFILE;
1573
struct logInfo *log;
1574
int state_file_ok = 1;
1576
struct poptOption options[] = {
1577
{"debug", 'd', 0, 0, 'd',
1578
"Don't do anything, just test (implies -v)"},
1579
{"force", 'f', 0, &force, 0, "Force file rotation"},
1580
{"mail", 'm', POPT_ARG_STRING, &mailCommand, 0,
1581
"Command to send mail (instead of `" DEFAULT_MAIL_COMMAND "')",
1583
{"state", 's', POPT_ARG_STRING, &stateFile, 0,
1584
"Path of state file",
1586
{"verbose", 'v', 0, 0, 'v', "Display messages during rotation"},
1587
POPT_AUTOHELP {0, 0, 0, 0, 0}
1590
logSetLevel(MESS_NORMAL);
1591
setlocale (LC_ALL, "");
1593
optCon = poptGetContext("logrotate", argc, argv, options, 0);
1594
poptReadDefaultConfig(optCon, 1);
1595
poptSetOtherOptionHelp(optCon, "[OPTION...] <configfile>");
1597
while ((arg = poptGetNextOpt(optCon)) >= 0) {
1603
logSetLevel(MESS_DEBUG);
1609
fprintf(stderr, "logrotate: bad argument %s: %s\n",
1610
poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
1612
poptFreeContext(optCon);
1616
files = poptGetArgs((poptContext) optCon);
1618
fprintf(stderr, "logrotate " VERSION
1619
" - Copyright (C) 1995-2001 Red Hat, Inc.\n");
1621
"This may be freely redistributed under the terms of "
1622
"the GNU Public License\n\n");
1623
poptPrintUsage(optCon, stderr, 0);
1624
poptFreeContext(optCon);
1628
selinux_enabled = (is_selinux_enabled() > 0);
1629
selinux_enforce = security_getenforce();
1634
if (readAllConfigPaths(files)) {
1635
poptFreeContext(optCon);
1639
poptFreeContext(optCon);
1640
nowSecs = time(NULL);
1642
if (allocateHash() != 0)
1645
if (readState(stateFile))
1651
message(MESS_DEBUG, "\nHandling %d logs\n", numLogs);
1653
for (log = logs.tqh_first; log != NULL; log = log->list.tqe_next)
1654
rc |= rotateLogSet(log, force);
1656
if (!debug && state_file_ok)
1657
rc |= writeState(stateFile);
1661
message(MESS_ERROR, "could not read state file, "
1662
"will not attempt to write into it\n");