19
#include <selinux/selinux.h>
20
static security_context_t prev_context = NULL;
21
int selinux_enabled = 0;
22
int selinux_enforce = 0;
25
#include "basenames.h"
27
#include "logrotate.h"
29
#if !defined(GLOB_ABORTED) && defined(GLOB_ABEND)
30
#define GLOB_ABORTED GLOB_ABEND
35
struct tm lastRotated; /* only tm.mon, tm_mday, tm_year are good! */
38
LIST_ENTRY(logState) list;
50
LIST_HEAD(stateSet, logState) head;
53
unsigned int hashSize;
56
char *mailCommand = DEFAULT_MAIL_COMMAND;
59
static int shred_file(char *filename, struct logInfo *log);
61
static int globerr(const char *pathname, int theerr)
63
message(MESS_ERROR, "error accessing %s: %s\n", pathname,
66
/* We want the glob operation to continue, so return 0 */
70
#define HASH_SIZE_MIN 64
71
static int allocateHash(void)
79
for (log = logs.tqh_first; log != NULL; log = log->list.tqe_next)
84
/* Enforce some reasonable minimum hash size */
85
if (hs < HASH_SIZE_MIN)
88
states = calloc(hs, sizeof(struct logStates *));
90
message(MESS_ERROR, "could not allocate memory for "
95
for (i = 0; i < hs; i++) {
96
states[i] = malloc(sizeof(struct logState));
97
if (states[i] == NULL) {
98
message(MESS_ERROR, "could not allocate memory for "
102
LIST_INIT(&(states[i]->head));
110
#define HASH_CONST 13
111
static unsigned hashIndex(const char *fn)
120
return hash % hashSize;
123
static struct logState *newState(const char *fn)
125
struct tm now = *localtime(&nowSecs);
126
struct logState *new;
129
if ((new = malloc(sizeof(*new))) == NULL)
132
if ((new->fn = strdup(fn)) == NULL)
137
memset(&new->lastRotated, 0, sizeof(new->lastRotated));
138
new->lastRotated.tm_mon = now.tm_mon;
139
new->lastRotated.tm_mday = now.tm_mday;
140
new->lastRotated.tm_year = now.tm_year;
142
/* fill in the rest of the new->lastRotated fields */
143
lr_time = mktime(&new->lastRotated);
144
new->lastRotated = *localtime(&lr_time);
149
static struct logState *findState(const char *fn)
151
unsigned int i = hashIndex(fn);
154
for (p = states[i]->head.lh_first; p != NULL; p = p->list.le_next)
155
if (!strcmp(fn, p->fn))
160
if ((p = newState(fn)) == NULL)
163
LIST_INSERT_HEAD(&(states[i]->head), p, list);
169
static int runScript(char *logfn, char *script)
174
message(MESS_DEBUG, "running script with arg %s: \"%s\"\n",
180
execl("/bin/sh", "sh", "-c", script, "logrotate_script", logfn, NULL);
188
int createOutputFile(char *fileName, int flags, struct stat *sb)
192
fd = open(fileName, flags, sb->st_mode);
194
message(MESS_ERROR, "error creating output file %s: %s\n",
195
fileName, strerror(errno));
198
if (fchmod(fd, (S_IRUSR | S_IWUSR) & sb->st_mode)) {
199
message(MESS_ERROR, "error setting mode of %s: %s\n",
200
fileName, strerror(errno));
204
if (fchown(fd, sb->st_uid, sb->st_gid)) {
205
message(MESS_ERROR, "error setting owner of %s: %s\n",
206
fileName, strerror(errno));
210
if (fchmod(fd, sb->st_mode)) {
211
message(MESS_ERROR, "error setting mode of %s: %s\n",
212
fileName, strerror(errno));
219
#define SHRED_CALL "shred -u "
220
#define SHRED_COUNT_FLAG "-n "
222
/* unlink, but try to call shred from GNU fileutils */
223
static int shred_file(char *filename, struct logInfo *log)
227
char count[DIGITS]; /* that's a lot of shredding :) */
229
if (!(log->flags & LOG_FLAG_SHRED)) {
230
return unlink(filename);
233
len = strlen(filename) + strlen(SHRED_CALL);
234
len += strlen(SHRED_COUNT_FLAG) + DIGITS;
238
message(MESS_ERROR, "malloc error while shredding");
239
return unlink(filename);
241
strcpy(cmd, SHRED_CALL);
242
if (log->shred_cycles != 0) {
243
strcat(cmd, SHRED_COUNT_FLAG);
244
snprintf(count, DIGITS - 1, "%d", log->shred_cycles);
248
strcat(cmd, filename);
252
message(MESS_ERROR, "Failed to shred %s\n, trying unlink", filename);
254
message(MESS_NORMAL, "Shred returned %d\n", ret);
256
return unlink(filename);
262
static int removeLogFile(char *name, struct logInfo *log)
264
message(MESS_DEBUG, "removing old log %s\n", name);
266
if (!debug && shred_file(name, log)) {
267
message(MESS_ERROR, "Failed to remove old log %s: %s\n",
268
name, strerror(errno));
274
static int compressLogFile(char *name, struct logInfo *log, struct stat *sb)
276
char *compressedName;
277
const char **fullCommand;
283
message(MESS_DEBUG, "compressing log with: %s\n", log->compress_prog);
287
fullCommand = alloca(sizeof(*fullCommand) *
288
(log->compress_options_count + 2));
289
fullCommand[0] = log->compress_prog;
290
for (i = 0; i < log->compress_options_count; i++)
291
fullCommand[i + 1] = log->compress_options_list[i];
292
fullCommand[log->compress_options_count + 1] = NULL;
294
compressedName = alloca(strlen(name) + strlen(log->compress_ext) + 2);
295
sprintf(compressedName, "%s%s", name, log->compress_ext);
297
if ((inFile = open(name, O_RDONLY)) < 0) {
298
message(MESS_ERROR, "unable to open %s for compression\n", name);
303
createOutputFile(compressedName, O_RDWR | O_CREAT | O_TRUNC, sb);
315
execvp(fullCommand[0], (void *) fullCommand);
324
if (!WIFEXITED(status) || WEXITSTATUS(status)) {
325
message(MESS_ERROR, "failed to compress log %s\n", name);
329
shred_file(name, log);
334
static int mailLog(char *logFile, char *mailCommand,
335
char *uncompressCommand, char *address, char *subject)
338
pid_t mailChild, uncompressChild = 0;
339
int mailStatus, uncompressStatus;
340
int uncompressPipe[2];
341
char *mailArgv[] = { mailCommand, "-s", subject, address, NULL };
344
if ((mailInput = open(logFile, O_RDONLY)) < 0) {
345
message(MESS_ERROR, "failed to open %s for mailing: %s\n", logFile,
350
if (uncompressCommand) {
351
if (pipe(uncompressPipe) < 0) {
352
message(MESS_ERROR, "error opening pipe for uncompress: %s",
356
if (!(uncompressChild = fork())) {
357
/* uncompress child */
360
dup2(uncompressPipe[1], 1);
361
close(uncompressPipe[0]);
362
close(uncompressPipe[1]);
364
execlp(uncompressCommand, uncompressCommand, NULL);
369
mailInput = uncompressPipe[0];
370
close(uncompressPipe[1]);
373
if (!(mailChild = fork())) {
378
execvp(mailArgv[0], mailArgv);
384
waitpid(mailChild, &mailStatus, 0);
386
if (!WIFEXITED(mailStatus) || WEXITSTATUS(mailStatus)) {
387
message(MESS_ERROR, "mail command failed for %s\n", logFile);
391
if (uncompressCommand) {
392
waitpid(uncompressChild, &uncompressStatus, 0);
394
if (!WIFEXITED(uncompressStatus) || WEXITSTATUS(uncompressStatus)) {
395
message(MESS_ERROR, "uncompress command failed mailing %s\n",
404
static int mailLogWrapper(char *mailFilename, char *mailCommand,
405
int logNum, struct logInfo *log)
407
/* if the log is compressed (and we're not mailing a
408
* file whose compression has been delayed), we need
409
* to uncompress it */
410
if ((log->flags & LOG_FLAG_COMPRESS) &&
411
!((log->flags & LOG_FLAG_DELAYCOMPRESS) &&
412
(log->flags & LOG_FLAG_MAILFIRST))) {
413
if (mailLog(mailFilename, mailCommand,
414
log->uncompress_prog, log->logAddress,
418
if (mailLog(mailFilename, mailCommand, NULL,
419
log->logAddress, mailFilename))
425
static int copyTruncate(char *currLog, char *saveLog, struct stat *sb,
429
int fdcurr = -1, fdsave = -1;
432
message(MESS_DEBUG, "copying %s to %s\n", currLog, saveLog);
435
if ((fdcurr = open(currLog, O_RDWR)) < 0) {
436
message(MESS_ERROR, "error opening %s: %s\n", currLog,
441
if (selinux_enabled) {
442
security_context_t oldContext;
443
if (fgetfilecon_raw(fdcurr, &oldContext) >= 0) {
444
if (getfscreatecon_raw(&prev_context) < 0) {
446
"getting default context: %s\n",
448
if (selinux_enforce) {
453
if (setfscreatecon_raw(oldContext) < 0) {
455
"setting file context %s to %s: %s\n",
456
saveLog, oldContext, strerror(errno));
457
if (selinux_enforce) {
462
message(MESS_DEBUG, "set default create context\n");
465
if (errno != ENOTSUP) {
466
message(MESS_ERROR, "getting file context %s: %s\n",
467
currLog, strerror(errno));
468
if (selinux_enforce) {
476
createOutputFile(saveLog, O_WRONLY | O_CREAT | O_TRUNC, sb);
478
if (selinux_enabled) {
479
setfscreatecon_raw(prev_context);
480
freecon(prev_context);
488
while ((cnt = read(fdcurr, buf, sizeof(buf))) > 0) {
489
if (write(fdsave, buf, cnt) != cnt) {
490
message(MESS_ERROR, "error writing to %s: %s\n",
491
saveLog, strerror(errno));
498
message(MESS_ERROR, "error reading %s: %s\n",
499
currLog, strerror(errno));
506
if (flags & LOG_FLAG_COPYTRUNCATE) {
507
message(MESS_DEBUG, "truncating %s\n", currLog);
510
if (ftruncate(fdcurr, 0)) {
511
message(MESS_ERROR, "error truncating %s: %s\n", currLog,
518
message(MESS_DEBUG, "Not truncating %s\n", currLog);
525
int findNeedRotating(struct logInfo *log, int logNum)
528
struct logState *state = NULL;
529
struct tm now = *localtime(&nowSecs);
531
message(MESS_DEBUG, "considering log %s\n", log->files[logNum]);
533
if (stat(log->files[logNum], &sb)) {
534
if ((log->flags & LOG_FLAG_MISSINGOK) && (errno == ENOENT)) {
535
message(MESS_DEBUG, " log %s does not exist -- skipping\n",
539
message(MESS_ERROR, "stat of %s failed: %s\n", log->files[logNum],
544
state = findState(log->files[logNum]);
548
if (log->criterium == ROT_SIZE) {
549
state->doRotate = (sb.st_size >= log->threshhold);
550
} else if (log->criterium == ROT_FORCE) {
551
/* user forced rotation of logs from command line */
553
} else if (state->lastRotated.tm_year > now.tm_year ||
554
(state->lastRotated.tm_year == now.tm_year &&
555
(state->lastRotated.tm_mon > now.tm_mon ||
556
(state->lastRotated.tm_mon == now.tm_mon &&
557
state->lastRotated.tm_mday > now.tm_mday)))) {
559
"log %s last rotated in the future -- rotation forced\n",
562
} else if (state->lastRotated.tm_year != now.tm_year ||
563
state->lastRotated.tm_mon != now.tm_mon ||
564
state->lastRotated.tm_mday != now.tm_mday) {
565
switch (log->criterium) {
568
1) the current weekday is before the weekday of the
570
2) more then a week has passed since the last
572
state->doRotate = ((now.tm_wday < state->lastRotated.tm_wday)
575
mktime(&state->lastRotated)) >
579
/* rotate if the logs haven't been rotated this month or
581
state->doRotate = ((now.tm_mon != state->lastRotated.tm_mon) ||
583
state->lastRotated.tm_year));
586
/* FIXME: only days=1 is implemented!! */
590
/* rotate if the logs haven't been rotated this year */
591
state->doRotate = (now.tm_year != state->lastRotated.tm_year);
598
if (log->minsize && sb.st_size < log->minsize)
602
/* The notifempty flag overrides the normal criteria */
603
if (!(log->flags & LOG_FLAG_IFEMPTY) && !sb.st_size)
606
if (state->doRotate) {
607
message(MESS_DEBUG, " log needs rotating\n");
609
message(MESS_DEBUG, " log does not need rotating\n");
615
int prerotateSingleLog(struct logInfo *log, int logNum, struct logState *state,
616
struct logNames *rotNames)
618
struct tm now = *localtime(&nowSecs);
619
char *oldName, *newName = NULL;
628
int rotateCount = log->rotateCount ? log->rotateCount : 1;
629
int logStart = (log->logStart == -1) ? 1 : log->logStart;
630
#define DATEEXT_LEN 64
631
#define PATTERN_LEN (DATEEXT_LEN * 2)
632
char dext_str[DATEEXT_LEN];
633
char dformat[DATEEXT_LEN];
634
char dext_pattern[PATTERN_LEN];
637
if (!state->doRotate)
640
/* Logs with rotateCounts of 0 are rotated once, then removed. This
641
lets scripts run properly, and everything gets mailed properly. */
643
message(MESS_DEBUG, "rotating log %s, log->rotateCount is %d\n",
644
log->files[logNum], log->rotateCount);
646
if (log->flags & LOG_FLAG_COMPRESS)
647
compext = log->compress_ext;
649
state->lastRotated = now;
652
if (log->oldDir[0] != '/') {
653
char *ld = ourDirName(log->files[logNum]);
655
malloc(strlen(ld) + strlen(log->oldDir) + 2);
656
sprintf(rotNames->dirName, "%s/%s", ld, log->oldDir);
659
rotNames->dirName = strdup(log->oldDir);
661
rotNames->dirName = ourDirName(log->files[logNum]);
663
rotNames->baseName = strdup(ourBaseName(log->files[logNum]));
665
oldName = alloca(PATH_MAX);
666
newName = alloca(PATH_MAX);
667
rotNames->disposeName = malloc(PATH_MAX);
669
if (log->extension &&
672
baseName[strlen(rotNames->baseName) -
673
strlen(log->extension)]), log->extension,
674
strlen(log->extension)) == 0) {
677
fileext = log->extension;
679
calloc(strlen(rotNames->baseName) - strlen(log->extension) + 1,
681
strncat(tempstr, rotNames->baseName,
682
strlen(rotNames->baseName) - strlen(log->extension));
683
free(rotNames->baseName);
684
rotNames->baseName = tempstr;
687
/* Allow only %Y %d %m and create valid strftime format string
688
* Construct the glob pattern corresponding to the date format */
690
if (log->dateformat) {
692
memset(dext_pattern, 0, sizeof(dext_pattern));
693
dext = log->dateformat;
696
while ((*dext != '\0') && (!hasErrors)) {
697
/* Will there be a space for a char and '\0'? */
698
if (j >= (sizeof(dext_pattern) - 1)) {
699
message(MESS_ERROR, "Date format %s is too long\n",
705
switch (*(dext + 1)) {
707
strncat(dext_pattern, "[0-9][0-9]",
708
sizeof(dext_pattern) - strlen(dext_pattern));
709
j += 10; /* strlen("[0-9][0-9]") */
712
strncat(dext_pattern, "[0-9][0-9]",
713
sizeof(dext_pattern) - strlen(dext_pattern));
715
if (j >= (sizeof(dext_pattern) - 1)) {
716
message(MESS_ERROR, "Date format %s is too long\n",
721
dformat[i++] = *(dext++);
725
/* End of year 2293 this pattern does not work. */
726
strncat(dext_pattern,
727
"[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]",
728
sizeof(dext_pattern) - strlen(dext_pattern));
730
if (j >= (sizeof(dext_pattern) - 1)) {
731
message(MESS_ERROR, "Date format %s is too long\n",
736
dformat[i++] = *(dext++);
740
dformat[i++] = *dext;
742
dext_pattern[j++] = *dext;
747
dext_pattern[j++] = *dext;
753
message(MESS_DEBUG, "Converted '%s' -> '%s'\n", log->dateformat, dformat);
754
strftime(dext_str, sizeof(dext_str), dformat, &now);
756
/* The default dateformat and glob pattern */
757
strftime(dext_str, sizeof(dext_str), "-%Y%m%d", &now);
758
strncpy(dext_pattern, "-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]",
759
sizeof(dext_pattern));
760
dext_pattern[PATTERN_LEN - 1] = '\0';
762
message(MESS_DEBUG, "dateext suffix '%s'\n", dext_str);
763
message(MESS_DEBUG, "glob pattern '%s'\n", dext_pattern);
765
/* First compress the previous log when necessary */
766
if (log->flags & LOG_FLAG_COMPRESS &&
767
log->flags & LOG_FLAG_DELAYCOMPRESS) {
768
if (log->flags & LOG_FLAG_DATEEXT) {
769
/* glob for uncompressed files with our pattern */
770
if (asprintf(&glob_pattern, "%s/%s%s%s", rotNames->dirName,
771
rotNames->baseName, dext_pattern, fileext) < 0) {
772
message(MESS_ERROR, "could not allocate glob pattern memory\n");
774
rc = glob(glob_pattern, 0, globerr, &globResult);
775
if (!rc && globResult.gl_pathc > 0) {
776
for (i = 0; i < globResult.gl_pathc && !hasErrors; i++) {
779
snprintf(oldName, PATH_MAX, "%s", (globResult.gl_pathv)[i]);
780
if (stat(oldName, &sbprev)) {
782
"previous log %s does not exist\n",
785
hasErrors = compressLogFile(oldName, log, &sbprev);
790
"glob finding logs to compress failed\n");
791
/* fallback to old behaviour */
792
snprintf(oldName, PATH_MAX, "%s/%s.%d%s", rotNames->dirName,
793
rotNames->baseName, logStart, fileext);
795
globfree(&globResult);
800
snprintf(oldName, PATH_MAX, "%s/%s.%d%s", rotNames->dirName,
801
rotNames->baseName, logStart, fileext);
802
if (stat(oldName, &sbprev)) {
803
message(MESS_DEBUG, "previous log %s does not exist\n",
806
hasErrors = compressLogFile(oldName, log, &sbprev);
811
rotNames->firstRotated =
812
malloc(strlen(rotNames->dirName) + strlen(rotNames->baseName) +
813
strlen(fileext) + strlen(compext) + 30);
815
if (log->flags & LOG_FLAG_DATEEXT) {
816
/* glob for compressed files with our pattern
817
* and compress ext */
818
if (asprintf(&glob_pattern, "%s/%s%s%s%s", rotNames->dirName,
819
rotNames->baseName, dext_pattern, fileext, compext) < 0) {
820
message(MESS_ERROR, "could not allocate glob pattern memory\n");
822
rc = glob(glob_pattern, 0, globerr, &globResult);
824
/* search for files to drop, if we find one remember it,
825
* if we find another one mail and remove the first and
826
* remember the second and so on */
829
/* remove the first (n - rotateCount) matches
830
* no real rotation needed, since the files have
831
* the date in their name */
832
for (i = 0; i < globResult.gl_pathc; i++) {
833
if (!stat((globResult.gl_pathv)[i], &fst_buf)) {
834
if ((i <= ((int) globResult.gl_pathc - rotateCount))
835
|| ((log->rotateAge > 0)
837
(((nowSecs - fst_buf.st_mtime) / 60 / 60 / 24)
838
> log->rotateAge))) {
839
if (mail_out != -1) {
841
(globResult.gl_pathv)[mail_out];
842
if (!hasErrors && log->logAddress)
844
mailLogWrapper(mailFilename,
848
message(MESS_DEBUG, "removing %s\n", mailFilename);
849
hasErrors = removeLogFile(mailFilename, log);
855
if (mail_out != -1) {
856
/* oldName is oldest Backup found (for unlink later) */
857
snprintf(oldName, PATH_MAX, "%s", (globResult.gl_pathv)[mail_out]);
858
strcpy(rotNames->disposeName, oldName);
860
free(rotNames->disposeName);
861
rotNames->disposeName = NULL;
864
message(MESS_DEBUG, "glob finding old rotated logs failed\n");
865
free(rotNames->disposeName);
866
rotNames->disposeName = NULL;
868
/* firstRotated is most recently created/compressed rotated log */
869
sprintf(rotNames->firstRotated, "%s/%s%s%s%s",
870
rotNames->dirName, rotNames->baseName, dext_str, fileext, compext);
871
globfree(&globResult);
874
if (log->rotateAge) {
876
for (i = 1; i <= rotateCount + 1; i++) {
877
snprintf(oldName, PATH_MAX, "%s/%s.%d%s%s", rotNames->dirName,
878
rotNames->baseName, i, fileext, compext);
879
if (!stat(oldName, &fst_buf)
880
&& (((nowSecs - fst_buf.st_mtime) / 60 / 60 / 24)
882
char *mailFilename = oldName;
883
if (!hasErrors && log->logAddress)
885
mailLogWrapper(mailFilename, mailCommand,
888
hasErrors = removeLogFile(mailFilename, log);
893
snprintf(oldName, PATH_MAX, "%s/%s.%d%s%s", rotNames->dirName,
894
rotNames->baseName, logStart + rotateCount, fileext,
896
strcpy(newName, oldName);
898
strcpy(rotNames->disposeName, oldName);
900
sprintf(rotNames->firstRotated, "%s/%s.%d%s%s", rotNames->dirName,
901
rotNames->baseName, logStart, fileext,
902
(log->flags & LOG_FLAG_DELAYCOMPRESS) ? "" : compext);
905
if (selinux_enabled) {
906
security_context_t oldContext = NULL;
907
if (getfilecon_raw(log->files[logNum], &oldContext) > 0) {
908
if (getfscreatecon_raw(&prev_context) < 0) {
910
"getting default context: %s\n",
912
if (selinux_enforce) {
917
if (setfscreatecon_raw(oldContext) < 0) {
919
"setting file context %s to %s: %s\n",
920
log->files[logNum], oldContext,
922
if (selinux_enforce) {
929
if (errno != ENOENT && errno != ENOTSUP) {
930
message(MESS_ERROR, "getting file context %s: %s\n",
931
log->files[logNum], strerror(errno));
932
if (selinux_enforce) {
939
for (i = rotateCount + logStart - 1; (i >= 0) && !hasErrors; i--) {
943
snprintf(oldName, PATH_MAX, "%s/%s.%d%s%s", rotNames->dirName,
944
rotNames->baseName, i, fileext, compext);
947
"renaming %s to %s (rotatecount %d, logstart %d, i %d), \n",
948
oldName, newName, rotateCount, logStart, i);
950
if (!debug && rename(oldName, newName)) {
951
if (errno == ENOENT) {
952
message(MESS_DEBUG, "old log %s does not exist\n",
955
message(MESS_ERROR, "error renaming %s to %s: %s\n",
956
oldName, newName, strerror(errno));
961
} /* !LOG_FLAG_DATEEXT */
963
if (log->flags & LOG_FLAG_DATEEXT) {
964
char *destFile = alloca(PATH_MAX);
967
if (asprintf(&(rotNames->finalName), "%s/%s%s%s", rotNames->dirName,
968
rotNames->baseName, dext_str, fileext) < 0) {
969
message(MESS_ERROR, "could not allocate finalName memory\n");
971
snprintf(destFile, PATH_MAX, "%s%s", rotNames->finalName, compext);
972
if (!stat(destFile, &fst_buf)) {
974
"destination %s already exists, skipping rotation\n",
975
rotNames->firstRotated);
979
/* note: the gzip extension is *not* used here! */
980
if (asprintf(&(rotNames->finalName), "%s/%s.%d%s", rotNames->dirName,
981
rotNames->baseName, logStart, fileext) < 0) {
982
message(MESS_ERROR, "could not allocate finalName memory\n");
986
/* if the last rotation doesn't exist, that's okay */
987
if (!debug && rotNames->disposeName
988
&& access(rotNames->disposeName, F_OK)) {
990
"log %s doesn't exist -- won't try to " "dispose of it\n",
991
rotNames->disposeName);
992
free(rotNames->disposeName);
993
rotNames->disposeName = NULL;
999
int rotateSingleLog(struct logInfo *log, int logNum, struct logState *state,
1000
struct logNames *rotNames)
1006
security_context_t savedContext = NULL;
1009
if (!state->doRotate)
1014
if (!(log->flags & (LOG_FLAG_COPYTRUNCATE | LOG_FLAG_COPY))) {
1016
if (selinux_enabled) {
1017
security_context_t oldContext = NULL;
1020
if ((fdcurr = open(log->files[logNum], O_RDWR)) < 0) {
1021
message(MESS_ERROR, "error opening %s: %s\n",
1026
if (fgetfilecon_raw(fdcurr, &oldContext) >= 0) {
1027
if (getfscreatecon_raw(&savedContext) < 0) {
1029
"getting default context: %s\n",
1031
if (selinux_enforce) {
1032
freecon(oldContext);
1033
if (close(fdcurr) < 0)
1034
message(MESS_ERROR, "error closing file %s",
1035
log->files[logNum]);
1039
if (setfscreatecon_raw(oldContext) < 0) {
1041
"setting file context %s to %s: %s\n",
1042
log->files[logNum], oldContext, strerror(errno));
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
message(MESS_DEBUG, "fscreate context set to %s\n",
1053
freecon(oldContext);
1055
if (errno != ENOTSUP) {
1056
message(MESS_ERROR, "getting file context %s: %s\n",
1057
log->files[logNum], strerror(errno));
1058
if (selinux_enforce) {
1059
if (close(fdcurr) < 0)
1060
message(MESS_ERROR, "error closing file %s",
1061
log->files[logNum]);
1066
if (close(fdcurr) < 0)
1067
message(MESS_ERROR, "error closing file %s",
1068
log->files[logNum]);
1071
message(MESS_DEBUG, "renaming %s to %s\n", log->files[logNum],
1072
rotNames->finalName);
1073
if (!debug && !hasErrors &&
1074
rename(log->files[logNum], rotNames->finalName)) {
1075
message(MESS_ERROR, "failed to rename %s to %s: %s\n",
1076
log->files[logNum], rotNames->finalName,
1080
if (!log->rotateCount) {
1081
rotNames->disposeName =
1082
realloc(rotNames->disposeName,
1083
strlen(rotNames->dirName) +
1084
strlen(rotNames->baseName) +
1085
strlen(log->files[logNum]) + 10);
1086
sprintf(rotNames->disposeName, "%s%s", rotNames->finalName,
1088
&& (log->flags & LOG_FLAG_COMPRESS)) ? log->
1090
message(MESS_DEBUG, "disposeName will be %s\n",
1091
rotNames->disposeName);
1095
if (!hasErrors && log->flags & LOG_FLAG_CREATE &&
1096
!(log->flags & (LOG_FLAG_COPYTRUNCATE | LOG_FLAG_COPY))) {
1097
if (log->createUid == NO_UID)
1098
sb.st_uid = state->sb.st_uid;
1100
sb.st_uid = log->createUid;
1102
if (log->createGid == NO_GID)
1103
sb.st_gid = state->sb.st_gid;
1105
sb.st_gid = log->createGid;
1107
if (log->createMode == NO_MODE)
1108
sb.st_mode = state->sb.st_mode & 0777;
1110
sb.st_mode = log->createMode;
1112
message(MESS_DEBUG, "creating new %s mode = 0%o uid = %d "
1113
"gid = %d\n", log->files[logNum], (unsigned int) sb.st_mode,
1114
(int) sb.st_uid, (int) sb.st_gid);
1117
fd = createOutputFile(log->files[logNum], O_CREAT | O_RDWR,
1126
if (selinux_enabled) {
1127
setfscreatecon_raw(savedContext);
1128
freecon(savedContext);
1129
savedContext = NULL;
1134
&& log->flags & (LOG_FLAG_COPYTRUNCATE | LOG_FLAG_COPY))
1136
copyTruncate(log->files[logNum], rotNames->finalName,
1137
&state->sb, log->flags);
1143
int postrotateSingleLog(struct logInfo *log, int logNum, struct logState *state,
1144
struct logNames *rotNames)
1148
if (!state->doRotate)
1151
if ((log->flags & LOG_FLAG_COMPRESS) &&
1152
!(log->flags & LOG_FLAG_DELAYCOMPRESS)) {
1153
hasErrors = compressLogFile(rotNames->finalName, log, &state->sb);
1156
if (!hasErrors && log->logAddress) {
1159
if (log->flags & LOG_FLAG_MAILFIRST)
1160
mailFilename = rotNames->firstRotated;
1162
mailFilename = rotNames->disposeName;
1166
mailLogWrapper(mailFilename, mailCommand, logNum, log);
1169
if (!hasErrors && rotNames->disposeName)
1170
hasErrors = removeLogFile(rotNames->disposeName, log);
1173
if (selinux_enabled) {
1174
setfscreatecon_raw(prev_context);
1175
freecon(prev_context);
1176
prev_context = NULL;
1182
int rotateLogSet(struct logInfo *log, int force)
1186
int logHasErrors[log->numFiles];
1188
struct logState **state;
1189
struct logNames **rotNames;
1192
log->criterium = ROT_FORCE;
1194
message(MESS_DEBUG, "\nrotating pattern: %s ", log->pattern);
1195
switch (log->criterium) {
1197
message(MESS_DEBUG, "after %d days ", log->threshhold);
1200
message(MESS_DEBUG, "weekly ");
1203
message(MESS_DEBUG, "monthly ");
1206
message(MESS_DEBUG, "yearly ");
1209
message(MESS_DEBUG, "%d bytes ", log->threshhold);
1212
message(MESS_DEBUG, "forced from command line ");
1216
if (log->rotateCount)
1217
message(MESS_DEBUG, "(%d rotations)\n", log->rotateCount);
1219
message(MESS_DEBUG, "(no old logs will be kept)\n");
1222
message(MESS_DEBUG, "olddir is %s, ", log->oldDir);
1224
if (log->flags & LOG_FLAG_IFEMPTY)
1225
message(MESS_DEBUG, "empty log files are rotated, ");
1227
message(MESS_DEBUG, "empty log files are not rotated, ");
1230
message(MESS_DEBUG, "only log files >= %d bytes are rotated, ", log->minsize);
1232
if (log->logAddress) {
1233
message(MESS_DEBUG, "old logs mailed to %s\n", log->logAddress);
1235
message(MESS_DEBUG, "old logs are removed\n");
1238
for (i = 0; i < log->numFiles; i++) {
1239
logHasErrors[i] = findNeedRotating(log, i);
1240
hasErrors |= logHasErrors[i];
1242
/* sure is a lot of findStating going on .. */
1243
if ((findState(log->files[i]))->doRotate)
1249
message(MESS_DEBUG, "not running first action script, "
1250
"since no logs will be rotated\n");
1252
message(MESS_DEBUG, "running first action script\n");
1253
if (runScript(log->pattern, log->first)) {
1254
message(MESS_ERROR, "error running first action script "
1255
"for %s\n", log->pattern);
1257
/* finish early, firstaction failed, affects all logs in set */
1263
state = malloc(log->numFiles * sizeof(struct logState *));
1264
rotNames = malloc(log->numFiles * sizeof(struct logNames *));
1267
(!(log->flags & LOG_FLAG_SHAREDSCRIPTS) && j < log->numFiles)
1268
|| ((log->flags & LOG_FLAG_SHAREDSCRIPTS) && j < 1); j++) {
1271
((log->flags & LOG_FLAG_SHAREDSCRIPTS) && i < log->numFiles)
1272
|| (!(log->flags & LOG_FLAG_SHAREDSCRIPTS) && i == j); i++) {
1273
state[i] = findState(log->files[i]);
1275
rotNames[i] = malloc(sizeof(struct logNames));
1276
memset(rotNames[i], 0, sizeof(struct logNames));
1279
prerotateSingleLog(log, i, state[i], rotNames[i]);
1280
hasErrors |= logHasErrors[i];
1284
&& (! ( (logHasErrors[j] && !(log->flags & LOG_FLAG_SHAREDSCRIPTS))
1285
|| (hasErrors && (log->flags & LOG_FLAG_SHAREDSCRIPTS)) ) )) {
1287
message(MESS_DEBUG, "not running prerotate script, "
1288
"since no logs will be rotated\n");
1290
message(MESS_DEBUG, "running prerotate script\n");
1291
if (runScript(log->pattern, log->pre)) {
1292
if (log->flags & LOG_FLAG_SHAREDSCRIPTS)
1294
"error running shared prerotate script "
1295
"for '%s'\n", log->pattern);
1298
"error running non-shared prerotate script "
1299
"for %s of '%s'\n", log->files[j], log->pattern);
1301
logHasErrors[j] = 1;
1308
((log->flags & LOG_FLAG_SHAREDSCRIPTS) && i < log->numFiles)
1309
|| (!(log->flags & LOG_FLAG_SHAREDSCRIPTS) && i == j); i++) {
1310
if (! ( (logHasErrors[i] && !(log->flags & LOG_FLAG_SHAREDSCRIPTS))
1311
|| (hasErrors && (log->flags & LOG_FLAG_SHAREDSCRIPTS)) ) ) {
1313
rotateSingleLog(log, i, state[i], rotNames[i]);
1314
hasErrors |= logHasErrors[i];
1319
&& (! ( (logHasErrors[j] && !(log->flags & LOG_FLAG_SHAREDSCRIPTS))
1320
|| (hasErrors && (log->flags & LOG_FLAG_SHAREDSCRIPTS)) ) )) {
1322
message(MESS_DEBUG, "not running postrotate script, "
1323
"since no logs were rotated\n");
1325
message(MESS_DEBUG, "running postrotate script\n");
1326
if (runScript(log->pattern, log->post)) {
1327
if (log->flags & LOG_FLAG_SHAREDSCRIPTS)
1329
"error running shared postrotate script "
1330
"for '%s'\n", log->pattern);
1333
"error running non-shared postrotate script "
1334
"for %s of '%s'\n", log->files[j], log->pattern);
1336
logHasErrors[j] = 1;
1343
((log->flags & LOG_FLAG_SHAREDSCRIPTS) && i < log->numFiles)
1344
|| (!(log->flags & LOG_FLAG_SHAREDSCRIPTS) && i == j); i++) {
1345
if (! ( (logHasErrors[i] && !(log->flags & LOG_FLAG_SHAREDSCRIPTS))
1346
|| (hasErrors && (log->flags & LOG_FLAG_SHAREDSCRIPTS)) ) ) {
1348
postrotateSingleLog(log, i, state[i], rotNames[i]);
1349
hasErrors |= logHasErrors[i];
1355
for (i = 0; i < log->numFiles; i++) {
1356
free(rotNames[i]->firstRotated);
1357
free(rotNames[i]->disposeName);
1358
free(rotNames[i]->finalName);
1359
free(rotNames[i]->dirName);
1360
free(rotNames[i]->baseName);
1368
message(MESS_DEBUG, "not running last action script, "
1369
"since no logs will be rotated\n");
1371
message(MESS_DEBUG, "running last action script\n");
1372
if (runScript(log->pattern, log->last)) {
1373
message(MESS_ERROR, "error running last action script "
1374
"for %s\n", log->pattern);
1383
static int writeState(char *stateFilename)
1390
f = fopen(stateFilename, "w");
1392
message(MESS_ERROR, "error creating state file %s: %s\n",
1393
stateFilename, strerror(errno));
1397
fprintf(f, "logrotate state -- version 2\n");
1399
for (i = 0; i < hashSize; i++) {
1400
for (p = states[i]->head.lh_first; p != NULL;
1401
p = p->list.le_next) {
1403
for (chptr = p->fn; *chptr; chptr++) {
1413
fprintf(f, " %d-%d-%d\n",
1414
p->lastRotated.tm_year + 1900,
1415
p->lastRotated.tm_mon + 1,
1416
p->lastRotated.tm_mday);
1424
static int readState(char *stateFilename)
1430
int year, month, day;
1434
struct logState *st;
1438
error = stat(stateFilename, &f_stat);
1440
if ((error && errno == ENOENT) || (!error && f_stat.st_size == 0)) {
1441
/* create the file before continuing to ensure we have write
1442
access to the file */
1443
f = fopen(stateFilename, "w");
1445
message(MESS_ERROR, "error creating state file %s: %s\n",
1446
stateFilename, strerror(errno));
1449
fprintf(f, "logrotate state -- version 2\n");
1453
message(MESS_ERROR, "error stat()ing state file %s: %s\n",
1454
stateFilename, strerror(errno));
1458
f = fopen(stateFilename, "r");
1460
message(MESS_ERROR, "error opening state file %s: %s\n",
1461
stateFilename, strerror(errno));
1465
if (!fgets(buf, sizeof(buf) - 1, f)) {
1466
message(MESS_ERROR, "error reading top line of %s\n",
1472
if (strcmp(buf, "logrotate state -- version 1\n") &&
1473
strcmp(buf, "logrotate state -- version 2\n")) {
1475
message(MESS_ERROR, "bad top line in state file %s\n",
1482
while (fgets(buf, sizeof(buf) - 1, f)) {
1486
if (buf[i - 1] != '\n') {
1487
message(MESS_ERROR, "line %d too long in state file %s\n",
1488
line, stateFilename);
1498
if (poptParseArgvString(buf, &argc, &argv) || (argc != 2) ||
1499
(sscanf(argv[1], "%d-%d-%d", &year, &month, &day) != 3)) {
1500
message(MESS_ERROR, "bad line %d in state file %s\n",
1501
line, stateFilename);
1507
/* Hack to hide earlier bug */
1508
if ((year != 1900) && (year < 1996 || year > 2100)) {
1510
"bad year %d for file %s in state file %s\n", year,
1511
argv[0], stateFilename);
1517
if (month < 1 || month > 12) {
1519
"bad month %d for file %s in state file %s\n", month,
1520
argv[0], stateFilename);
1526
/* 0 to hide earlier bug */
1527
if (day < 0 || day > 31) {
1529
"bad day %d for file %s in state file %s\n", day,
1530
argv[0], stateFilename);
1536
year -= 1900, month -= 1;
1538
if ((st = findState(argv[0])) == NULL)
1541
st->lastRotated.tm_mon = month;
1542
st->lastRotated.tm_mday = day;
1543
st->lastRotated.tm_year = year;
1545
/* fill in the rest of the st->lastRotated fields */
1546
lr_time = mktime(&st->lastRotated);
1547
st->lastRotated = *localtime(&lr_time);
1556
int main(int argc, const char **argv)
1559
char *stateFile = STATEFILE;
1564
struct logInfo *log;
1565
int state_file_ok = 1;
1567
struct poptOption options[] = {
1568
{"debug", 'd', 0, 0, 'd',
1569
"Don't do anything, just test (implies -v)"},
1570
{"force", 'f', 0, &force, 0, "Force file rotation"},
1571
{"mail", 'm', POPT_ARG_STRING, &mailCommand, 0,
1572
"Command to send mail (instead of `" DEFAULT_MAIL_COMMAND "')",
1574
{"state", 's', POPT_ARG_STRING, &stateFile, 0,
1575
"Path of state file",
1577
{"verbose", 'v', 0, 0, 'v', "Display messages during rotation"},
1578
POPT_AUTOHELP {0, 0, 0, 0, 0}
1581
logSetLevel(MESS_NORMAL);
1582
setlocale (LC_ALL, "");
1584
optCon = poptGetContext("logrotate", argc, argv, options, 0);
1585
poptReadDefaultConfig(optCon, 1);
1586
poptSetOtherOptionHelp(optCon, "[OPTION...] <configfile>");
1588
while ((arg = poptGetNextOpt(optCon)) >= 0) {
1594
logSetLevel(MESS_DEBUG);
1600
fprintf(stderr, "logrotate: bad argument %s: %s\n",
1601
poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
1603
poptFreeContext(optCon);
1607
files = poptGetArgs((poptContext) optCon);
1609
fprintf(stderr, "logrotate " VERSION
1610
" - Copyright (C) 1995-2001 Red Hat, Inc.\n");
1612
"This may be freely redistributed under the terms of "
1613
"the GNU Public License\n\n");
1614
poptPrintUsage(optCon, stderr, 0);
1615
poptFreeContext(optCon);
1619
selinux_enabled = (is_selinux_enabled() > 0);
1620
selinux_enforce = security_getenforce();
1625
if (readAllConfigPaths(files)) {
1626
poptFreeContext(optCon);
1630
poptFreeContext(optCon);
1631
nowSecs = time(NULL);
1633
if (allocateHash() != 0)
1636
if (readState(stateFile))
1642
message(MESS_DEBUG, "\nHandling %d logs\n", numLogs);
1644
for (log = logs.tqh_first; log != NULL; log = log->list.tqe_next)
1645
rc |= rotateLogSet(log, force);
1647
if (!debug && state_file_ok)
1648
rc |= writeState(stateFile);
1652
message(MESS_ERROR, "could not read state file, "
1653
"will not attempt to write into it\n");