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;
49 LIST_HEAD(stateSet, logState) states;
53 char *mailCommand = DEFAULT_MAIL_COMMAND;
56 static int shred_file(char *filename, struct logInfo *log);
58 static int globerr(const char *pathname, int theerr)
60 message(MESS_ERROR, "error accessing %s: %s\n", pathname,
63 /* We want the glob operation to continue, so return 0 */
67 static struct logState *newState(const char *fn)
69 struct tm now = *localtime(&nowSecs);
73 if ((new = malloc(sizeof(*new))) == NULL)
76 if ((new->fn = strdup(fn)) == NULL)
81 memset(&new->lastRotated, 0, sizeof(new->lastRotated));
82 new->lastRotated.tm_mon = now.tm_mon;
83 new->lastRotated.tm_mday = now.tm_mday;
84 new->lastRotated.tm_year = now.tm_year;
86 /* fill in the rest of the new->lastRotated fields */
87 lr_time = mktime(&new->lastRotated);
88 new->lastRotated = *localtime(&lr_time);
93 static struct logState *findState(const char *fn)
97 for (p = states.lh_first; p != NULL; p = p->list.le_next)
98 if (!strcmp(fn, p->fn))
103 if ((p = newState(fn)) == NULL)
106 LIST_INSERT_HEAD(&states, p, list);
112 static int runScript(char *logfn, char *script)
117 message(MESS_DEBUG, "running script with arg %s: \"%s\"\n",
123 execl("/bin/sh", "sh", "-c", script, "logrotate_script", logfn, NULL);
131 int createOutputFile(char *fileName, int flags, struct stat *sb)
135 fd = open(fileName, flags, sb->st_mode);
137 message(MESS_ERROR, "error creating output file %s: %s\n",
138 fileName, strerror(errno));
141 if (fchmod(fd, (S_IRUSR | S_IWUSR) & sb->st_mode)) {
142 message(MESS_ERROR, "error setting mode of %s: %s\n",
143 fileName, strerror(errno));
147 if (fchown(fd, sb->st_uid, sb->st_gid)) {
148 message(MESS_ERROR, "error setting owner of %s: %s\n",
149 fileName, strerror(errno));
153 if (fchmod(fd, sb->st_mode)) {
154 message(MESS_ERROR, "error setting mode of %s: %s\n",
155 fileName, strerror(errno));
162 #define SHRED_CALL "shred -u "
163 #define SHRED_COUNT_FLAG "-n "
165 /* unlink, but try to call shred from GNU fileutils */
166 static int shred_file(char *filename, struct logInfo *log)
170 char count[DIGITS]; /* that's a lot of shredding :) */
172 if (!(log->flags & LOG_FLAG_SHRED)) {
173 return unlink(filename);
176 len = strlen(filename) + strlen(SHRED_CALL);
177 len += strlen(SHRED_COUNT_FLAG) + DIGITS;
181 message(MESS_ERROR, "malloc error while shredding");
182 return unlink(filename);
184 strcpy(cmd, SHRED_CALL);
185 if (log->shred_cycles != 0) {
186 strcat(cmd, SHRED_COUNT_FLAG);
187 snprintf(count, DIGITS - 1, "%d", log->shred_cycles);
191 strcat(cmd, filename);
195 message(MESS_ERROR, "Failed to shred %s\n, trying unlink", filename);
197 message(MESS_NORMAL, "Shred returned %d\n", ret);
199 return unlink(filename);
205 static int removeLogFile(char *name, struct logInfo *log)
207 message(MESS_DEBUG, "removing old log %s\n", name);
209 if (!debug && shred_file(name, log)) {
210 message(MESS_ERROR, "Failed to remove old log %s: %s\n",
211 name, strerror(errno));
217 static int compressLogFile(char *name, struct logInfo *log, struct stat *sb)
219 char *compressedName;
220 const char **fullCommand;
226 message(MESS_DEBUG, "compressing log with: %s\n", log->compress_prog);
230 fullCommand = alloca(sizeof(*fullCommand) *
231 (log->compress_options_count + 2));
232 fullCommand[0] = log->compress_prog;
233 for (i = 0; i < log->compress_options_count; i++)
234 fullCommand[i + 1] = log->compress_options_list[i];
235 fullCommand[log->compress_options_count + 1] = NULL;
237 compressedName = alloca(strlen(name) + strlen(log->compress_ext) + 2);
238 sprintf(compressedName, "%s%s", name, log->compress_ext);
240 if ((inFile = open(name, O_RDONLY)) < 0) {
241 message(MESS_ERROR, "unable to open %s for compression\n", name);
246 createOutputFile(compressedName, O_RDWR | O_CREAT | O_TRUNC, sb);
258 execvp(fullCommand[0], (void *) fullCommand);
267 if (!WIFEXITED(status) || WEXITSTATUS(status)) {
268 message(MESS_ERROR, "failed to compress log %s\n", name);
272 shred_file(name, log);
277 static int mailLog(char *logFile, char *mailCommand,
278 char *uncompressCommand, char *address, char *subject)
281 pid_t mailChild, uncompressChild;
282 int mailStatus, uncompressStatus;
283 int uncompressPipe[2];
284 char *mailArgv[] = { mailCommand, "-s", subject, address, NULL };
287 if ((mailInput = open(logFile, O_RDONLY)) < 0) {
288 message(MESS_ERROR, "failed to open %s for mailing: %s\n", logFile,
293 if (uncompressCommand) {
294 pipe(uncompressPipe);
295 if (!(uncompressChild = fork())) {
296 /* uncompress child */
299 dup2(uncompressPipe[1], 1);
300 close(uncompressPipe[0]);
301 close(uncompressPipe[1]);
303 execlp(uncompressCommand, uncompressCommand, NULL);
308 mailInput = uncompressPipe[0];
309 close(uncompressPipe[1]);
312 if (!(mailChild = fork())) {
317 execvp(mailArgv[0], mailArgv);
323 waitpid(mailChild, &mailStatus, 0);
325 if (!WIFEXITED(mailStatus) || WEXITSTATUS(mailStatus)) {
326 message(MESS_ERROR, "mail command failed for %s\n", logFile);
330 if (uncompressCommand) {
331 waitpid(uncompressChild, &uncompressStatus, 0);
333 if (!WIFEXITED(uncompressStatus) || WEXITSTATUS(uncompressStatus)) {
334 message(MESS_ERROR, "uncompress command failed mailing %s\n",
343 static int mailLogWrapper(char *mailFilename, char *mailCommand,
344 int logNum, struct logInfo *log)
346 /* if the log is compressed (and we're not mailing a
347 * file whose compression has been delayed), we need
348 * to uncompress it */
349 if ((log->flags & LOG_FLAG_COMPRESS) &&
350 !((log->flags & LOG_FLAG_DELAYCOMPRESS) &&
351 (log->flags & LOG_FLAG_MAILFIRST))) {
352 if (mailLog(mailFilename, mailCommand,
353 log->uncompress_prog, log->logAddress,
357 if (mailLog(mailFilename, mailCommand, NULL,
358 log->logAddress, mailFilename))
364 static int copyTruncate(char *currLog, char *saveLog, struct stat *sb,
368 int fdcurr = -1, fdsave = -1;
371 message(MESS_DEBUG, "copying %s to %s\n", currLog, saveLog);
374 if ((fdcurr = open(currLog, O_RDWR)) < 0) {
375 message(MESS_ERROR, "error opening %s: %s\n", currLog,
380 if (selinux_enabled) {
381 security_context_t oldContext;
382 if (fgetfilecon_raw(fdcurr, &oldContext) >= 0) {
383 if (getfscreatecon_raw(&prev_context) < 0) {
385 "getting default context: %s\n",
387 if (selinux_enforce) {
394 if (setfscreatecon_raw(oldContext) < 0) {
396 "setting file context %s to %s: %s\n",
397 saveLog, oldContext, strerror(errno));
398 if (selinux_enforce) {
405 message(MESS_DEBUG, "set default create context\n");
410 if (errno != ENOTSUP) {
411 message(MESS_ERROR, "getting file context %s: %s\n",
412 currLog, strerror(errno));
413 if (selinux_enforce) {
421 createOutputFile(saveLog, O_WRONLY | O_CREAT | O_TRUNC, sb);
423 if (selinux_enabled) {
424 setfscreatecon_raw(prev_context);
425 if (prev_context != NULL) {
426 freecon(prev_context);
435 while ((cnt = read(fdcurr, buf, sizeof(buf))) > 0) {
436 if (write(fdsave, buf, cnt) != cnt) {
437 message(MESS_ERROR, "error writing to %s: %s\n",
438 saveLog, strerror(errno));
445 message(MESS_ERROR, "error reading %s: %s\n",
446 currLog, strerror(errno));
453 if (flags & LOG_FLAG_COPYTRUNCATE) {
454 message(MESS_DEBUG, "truncating %s\n", currLog);
457 if (ftruncate(fdcurr, 0)) {
458 message(MESS_ERROR, "error truncating %s: %s\n", currLog,
465 message(MESS_DEBUG, "Not truncating %s\n", currLog);
472 int findNeedRotating(struct logInfo *log, int logNum)
475 struct logState *state = NULL;
476 struct tm now = *localtime(&nowSecs);
478 message(MESS_DEBUG, "considering log %s\n", log->files[logNum]);
480 if (stat(log->files[logNum], &sb)) {
481 if ((log->flags & LOG_FLAG_MISSINGOK) && (errno == ENOENT)) {
482 message(MESS_DEBUG, " log %s does not exist -- skipping\n",
486 message(MESS_ERROR, "stat of %s failed: %s\n", log->files[logNum],
491 state = findState(log->files[logNum]);
495 if (log->criterium == ROT_SIZE) {
496 state->doRotate = (sb.st_size >= log->threshhold);
497 } else if (log->criterium == ROT_FORCE) {
498 /* user forced rotation of logs from command line */
500 } else if (state->lastRotated.tm_year > now.tm_year ||
501 (state->lastRotated.tm_year == now.tm_year &&
502 (state->lastRotated.tm_mon > now.tm_mon ||
503 (state->lastRotated.tm_mon == now.tm_mon &&
504 state->lastRotated.tm_mday > now.tm_mday)))) {
506 "log %s last rotated in the future -- rotation forced\n",
509 } else if (state->lastRotated.tm_year != now.tm_year ||
510 state->lastRotated.tm_mon != now.tm_mon ||
511 state->lastRotated.tm_mday != now.tm_mday) {
512 switch (log->criterium) {
515 1) the current weekday is before the weekday of the
517 2) more then a week has passed since the last
519 state->doRotate = ((now.tm_wday < state->lastRotated.tm_wday)
522 mktime(&state->lastRotated)) >
526 /* rotate if the logs haven't been rotated this month or
528 state->doRotate = ((now.tm_mon != state->lastRotated.tm_mon) ||
530 state->lastRotated.tm_year));
533 /* FIXME: only days=1 is implemented!! */
537 /* rotate if the logs haven't been rotated this year */
538 state->doRotate = (now.tm_year != state->lastRotated.tm_year);
545 if (log->minsize && sb.st_size < log->minsize)
549 /* The notifempty flag overrides the normal criteria */
550 if (!(log->flags & LOG_FLAG_IFEMPTY) && !sb.st_size)
553 if (state->doRotate) {
554 message(MESS_DEBUG, " log needs rotating\n");
556 message(MESS_DEBUG, " log does not need rotating\n");
562 int prerotateSingleLog(struct logInfo *log, int logNum, struct logState *state,
563 struct logNames *rotNames)
565 struct tm now = *localtime(&nowSecs);
566 char *oldName, *newName = NULL;
575 int rotateCount = log->rotateCount ? log->rotateCount : 1;
576 int logStart = (log->logStart == -1) ? 1 : log->logStart;
577 #define DATEEXT_LEN 64
578 #define PATTERN_LEN (DATEEXT_LEN * 2)
579 char dext_str[DATEEXT_LEN];
580 char dformat[DATEEXT_LEN];
581 char dext_pattern[PATTERN_LEN];
584 if (!state->doRotate)
587 /* Logs with rotateCounts of 0 are rotated once, then removed. This
588 lets scripts run properly, and everything gets mailed properly. */
590 message(MESS_DEBUG, "rotating log %s, log->rotateCount is %d\n",
591 log->files[logNum], log->rotateCount);
593 if (log->flags & LOG_FLAG_COMPRESS)
594 compext = log->compress_ext;
596 state->lastRotated = now;
599 if (log->oldDir[0] != '/') {
600 char *ld = ourDirName(log->files[logNum]);
602 malloc(strlen(ld) + strlen(log->oldDir) + 2);
603 sprintf(rotNames->dirName, "%s/%s", ld, log->oldDir);
606 rotNames->dirName = strdup(log->oldDir);
608 rotNames->dirName = ourDirName(log->files[logNum]);
610 rotNames->baseName = strdup(ourBaseName(log->files[logNum]));
612 oldName = alloca(PATH_MAX);
613 newName = alloca(PATH_MAX);
614 rotNames->disposeName = malloc(PATH_MAX);
616 if (log->extension &&
619 baseName[strlen(rotNames->baseName) -
620 strlen(log->extension)]), log->extension,
621 strlen(log->extension)) == 0) {
624 fileext = log->extension;
626 calloc(strlen(rotNames->baseName) - strlen(log->extension) + 1,
628 strncat(tempstr, rotNames->baseName,
629 strlen(rotNames->baseName) - strlen(log->extension));
630 free(rotNames->baseName);
631 rotNames->baseName = tempstr;
634 /* Allow only %Y %d %m and create valid strftime format string
635 * Construct the glob pattern corresponding to the date format */
637 if (log->dateformat) {
639 memset(dext_pattern, 0, sizeof(dext_pattern));
640 dext = log->dateformat;
643 while ((*dext != '\0') && (!hasErrors)) {
644 /* Will there be a space for a char and '\0'? */
645 if (j >= (sizeof(dext_pattern) - 1)) {
646 message(MESS_ERROR, "Date format %s is too long\n",
652 switch (*(dext + 1)) {
654 strncat(dext_pattern, "[0-9][0-9]",
655 sizeof(dext_pattern) - strlen(dext_pattern));
656 j += 10; /* strlen("[0-9][0-9]") */
659 strncat(dext_pattern, "[0-9][0-9]",
660 sizeof(dext_pattern) - strlen(dext_pattern));
662 if (j >= (sizeof(dext_pattern) - 1)) {
663 message(MESS_ERROR, "Date format %s is too long\n",
668 dformat[i++] = *(dext++);
672 dformat[i++] = *dext;
674 dext_pattern[j++] = *dext;
679 dext_pattern[j++] = *dext;
685 message(MESS_DEBUG, "Converted '%s' -> '%s'\n", log->dateformat, dformat);
686 strftime(dext_str, sizeof(dext_str), dformat, &now);
688 /* The default dateformat and glob pattern */
689 strftime(dext_str, sizeof(dext_str), "-%Y%m%d", &now);
690 strncpy(dext_pattern, "-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]",
691 sizeof(dext_pattern));
692 dext_pattern[PATTERN_LEN - 1] = '\0';
694 message(MESS_DEBUG, "dateext suffix '%s'\n", dext_str);
695 message(MESS_DEBUG, "glob pattern '%s'\n", dext_pattern);
697 /* First compress the previous log when necessary */
698 if (log->flags & LOG_FLAG_COMPRESS &&
699 log->flags & LOG_FLAG_DELAYCOMPRESS) {
700 if (log->flags & LOG_FLAG_DATEEXT) {
701 /* glob for uncompressed files with our pattern */
702 asprintf(&glob_pattern, "%s/%s%s%s",
703 rotNames->dirName, rotNames->baseName, dext_pattern, fileext);
704 rc = glob(glob_pattern, 0, globerr, &globResult);
705 if (!rc && globResult.gl_pathc > 0) {
706 for (i = 0; i < globResult.gl_pathc && !hasErrors; i++) {
709 snprintf(oldName, PATH_MAX, "%s", (globResult.gl_pathv)[i]);
710 if (stat(oldName, &sbprev)) {
712 "previous log %s does not exist\n",
715 hasErrors = compressLogFile(oldName, log, &sbprev);
720 "glob finding logs to compress failed\n");
721 /* fallback to old behaviour */
722 snprintf(oldName, PATH_MAX, "%s/%s.%d%s", rotNames->dirName,
723 rotNames->baseName, logStart, fileext);
725 globfree(&globResult);
730 snprintf(oldName, PATH_MAX, "%s/%s.%d%s", rotNames->dirName,
731 rotNames->baseName, logStart, fileext);
732 if (stat(oldName, &sbprev)) {
733 message(MESS_DEBUG, "previous log %s does not exist\n",
736 hasErrors = compressLogFile(oldName, log, &sbprev);
741 rotNames->firstRotated =
742 malloc(strlen(rotNames->dirName) + strlen(rotNames->baseName) +
743 strlen(fileext) + strlen(compext) + 30);
745 if (log->flags & LOG_FLAG_DATEEXT) {
746 /* glob for compressed files with our pattern
747 * and compress ext */
748 asprintf(&glob_pattern, "%s/%s%s%s%s",
749 rotNames->dirName, rotNames->baseName, dext_pattern, fileext, compext);
750 rc = glob(glob_pattern, 0, globerr, &globResult);
752 /* search for files to drop, if we find one remember it,
753 * if we find another one mail and remove the first and
754 * remember the second and so on */
757 /* remove the first (n - rotateCount) matches
758 * no real rotation needed, since the files have
759 * the date in their name */
760 for (i = 0; i < globResult.gl_pathc; i++) {
761 if (!stat((globResult.gl_pathv)[i], &fst_buf)) {
762 if ((i <= ((int) globResult.gl_pathc - rotateCount))
763 || ((log->rotateAge > 0)
765 (((nowSecs - fst_buf.st_mtime) / 60 / 60 / 24)
766 > log->rotateAge))) {
767 if (mail_out != -1) {
769 (globResult.gl_pathv)[mail_out];
770 if (!hasErrors && log->logAddress)
772 mailLogWrapper(mailFilename,
776 message(MESS_DEBUG, "removing %s\n", mailFilename);
777 hasErrors = removeLogFile(mailFilename, log);
783 if (mail_out != -1) {
784 /* oldName is oldest Backup found (for unlink later) */
785 snprintf(oldName, PATH_MAX, "%s", (globResult.gl_pathv)[mail_out]);
786 strcpy(rotNames->disposeName, oldName);
788 free(rotNames->disposeName);
789 rotNames->disposeName = NULL;
792 message(MESS_DEBUG, "glob finding old rotated logs failed\n");
793 free(rotNames->disposeName);
794 rotNames->disposeName = NULL;
796 /* firstRotated is most recently created/compressed rotated log */
797 sprintf(rotNames->firstRotated, "%s/%s%s%s%s",
798 rotNames->dirName, rotNames->baseName, dext_str, fileext, compext);
799 globfree(&globResult);
802 if (log->rotateAge) {
804 for (i = 1; i <= rotateCount + 1; i++) {
805 snprintf(oldName, PATH_MAX, "%s/%s.%d%s%s", rotNames->dirName,
806 rotNames->baseName, i, fileext, compext);
807 if (!stat(oldName, &fst_buf)
808 && (((nowSecs - fst_buf.st_mtime) / 60 / 60 / 24)
810 char *mailFilename = oldName;
811 if (!hasErrors && log->logAddress)
813 mailLogWrapper(mailFilename, mailCommand,
816 hasErrors = removeLogFile(mailFilename, log);
821 snprintf(oldName, PATH_MAX, "%s/%s.%d%s%s", rotNames->dirName,
822 rotNames->baseName, logStart + rotateCount, fileext,
824 strcpy(newName, oldName);
826 strcpy(rotNames->disposeName, oldName);
828 sprintf(rotNames->firstRotated, "%s/%s.%d%s%s", rotNames->dirName,
829 rotNames->baseName, logStart, fileext,
830 (log->flags & LOG_FLAG_DELAYCOMPRESS) ? "" : compext);
833 if (selinux_enabled) {
834 security_context_t oldContext = NULL;
835 if (getfilecon_raw(log->files[logNum], &oldContext) > 0) {
836 if (getfscreatecon_raw(&prev_context) < 0) {
838 "getting default context: %s\n",
840 if (selinux_enforce) {
847 if (setfscreatecon_raw(oldContext) < 0) {
849 "setting file context %s to %s: %s\n",
850 log->files[logNum], oldContext,
852 if (selinux_enforce) {
863 if (errno != ENOENT && errno != ENOTSUP) {
864 message(MESS_ERROR, "getting file context %s: %s\n",
865 log->files[logNum], strerror(errno));
866 if (selinux_enforce) {
873 for (i = rotateCount + logStart - 1; (i >= 0) && !hasErrors; i--) {
877 snprintf(oldName, PATH_MAX, "%s/%s.%d%s%s", rotNames->dirName,
878 rotNames->baseName, i, fileext, compext);
881 "renaming %s to %s (rotatecount %d, logstart %d, i %d), \n",
882 oldName, newName, rotateCount, logStart, i);
884 if (!debug && rename(oldName, newName)) {
885 if (errno == ENOENT) {
886 message(MESS_DEBUG, "old log %s does not exist\n",
889 message(MESS_ERROR, "error renaming %s to %s: %s\n",
890 oldName, newName, strerror(errno));
895 } /* !LOG_FLAG_DATEEXT */
897 if (log->flags & LOG_FLAG_DATEEXT) {
899 alloca(strlen(rotNames->dirName) + strlen(rotNames->baseName) +
900 strlen(fileext) + strlen(compext) + 30);
902 asprintf(&(rotNames->finalName), "%s/%s%s%s",
903 rotNames->dirName, rotNames->baseName, dext_str, fileext);
904 sprintf(destFile, "%s%s", rotNames->finalName, compext);
905 if (!stat(destFile, &fst_buf)) {
907 "destination %s already exists, skipping rotation\n",
908 rotNames->firstRotated);
912 /* note: the gzip extension is *not* used here! */
913 asprintf(&(rotNames->finalName), "%s/%s.%d%s", rotNames->dirName,
914 rotNames->baseName, logStart, fileext);
917 /* if the last rotation doesn't exist, that's okay */
918 if (!debug && rotNames->disposeName
919 && access(rotNames->disposeName, F_OK)) {
921 "log %s doesn't exist -- won't try to " "dispose of it\n",
922 rotNames->disposeName);
923 free(rotNames->disposeName);
924 rotNames->disposeName = NULL;
930 int rotateSingleLog(struct logInfo *log, int logNum, struct logState *state,
931 struct logNames *rotNames)
937 security_context_t savedContext = NULL;
940 if (!state->doRotate)
945 if (!(log->flags & (LOG_FLAG_COPYTRUNCATE | LOG_FLAG_COPY))) {
947 if (selinux_enabled) {
948 security_context_t oldContext = NULL;
951 if ((fdcurr = open(log->files[logNum], O_RDWR)) < 0) {
952 message(MESS_ERROR, "error opening %s: %s\n",
957 if (fgetfilecon_raw(fdcurr, &oldContext) >= 0) {
958 if (getfscreatecon_raw(&savedContext) < 0) {
960 "getting default context: %s\n",
962 if (selinux_enforce) {
966 if (close(fdcurr) < 0)
967 message(MESS_ERROR, "error closing file %s",
972 if (setfscreatecon_raw(oldContext) < 0) {
974 "setting file context %s to %s: %s\n",
975 log->files[logNum], oldContext, strerror(errno));
976 if (selinux_enforce) {
980 if (close(fdcurr) < 0)
981 message(MESS_ERROR, "error closing file %s",
986 message(MESS_DEBUG, "fscreate context set to %s\n",
992 if (errno != ENOTSUP) {
993 message(MESS_ERROR, "getting file context %s: %s\n",
994 log->files[logNum], strerror(errno));
995 if (selinux_enforce) {
996 if (close(fdcurr) < 0)
997 message(MESS_ERROR, "error closing file %s",
1003 if (close(fdcurr) < 0)
1004 message(MESS_ERROR, "error closing file %s",
1005 log->files[logNum]);
1008 message(MESS_DEBUG, "renaming %s to %s\n", log->files[logNum],
1009 rotNames->finalName);
1010 if (!debug && !hasErrors &&
1011 rename(log->files[logNum], rotNames->finalName)) {
1012 message(MESS_ERROR, "failed to rename %s to %s: %s\n",
1013 log->files[logNum], rotNames->finalName,
1017 if (!log->rotateCount) {
1018 rotNames->disposeName =
1019 realloc(rotNames->disposeName,
1020 strlen(rotNames->dirName) +
1021 strlen(rotNames->baseName) +
1022 strlen(log->files[logNum]) + 10);
1023 sprintf(rotNames->disposeName, "%s%s", rotNames->finalName,
1025 && (log->flags & LOG_FLAG_COMPRESS)) ? log->
1027 message(MESS_DEBUG, "disposeName will be %s\n",
1028 rotNames->disposeName);
1032 if (!hasErrors && log->flags & LOG_FLAG_CREATE &&
1033 !(log->flags & (LOG_FLAG_COPYTRUNCATE | LOG_FLAG_COPY))) {
1034 if (log->createUid == NO_UID)
1035 sb.st_uid = state->sb.st_uid;
1037 sb.st_uid = log->createUid;
1039 if (log->createGid == NO_GID)
1040 sb.st_gid = state->sb.st_gid;
1042 sb.st_gid = log->createGid;
1044 if (log->createMode == NO_MODE)
1045 sb.st_mode = state->sb.st_mode & 0777;
1047 sb.st_mode = log->createMode;
1049 message(MESS_DEBUG, "creating new %s mode = 0%o uid = %d "
1050 "gid = %d\n", log->files[logNum], (unsigned int) sb.st_mode,
1051 (int) sb.st_uid, (int) sb.st_gid);
1054 fd = createOutputFile(log->files[logNum], O_CREAT | O_RDWR,
1063 if (selinux_enabled) {
1064 setfscreatecon_raw(savedContext);
1065 if (savedContext != NULL) {
1066 freecon(savedContext);
1067 savedContext = NULL;
1073 && log->flags & (LOG_FLAG_COPYTRUNCATE | LOG_FLAG_COPY))
1075 copyTruncate(log->files[logNum], rotNames->finalName,
1076 &state->sb, log->flags);
1082 int postrotateSingleLog(struct logInfo *log, int logNum, struct logState *state,
1083 struct logNames *rotNames)
1087 if (!state->doRotate)
1090 if ((log->flags & LOG_FLAG_COMPRESS) &&
1091 !(log->flags & LOG_FLAG_DELAYCOMPRESS)) {
1092 hasErrors = compressLogFile(rotNames->finalName, log, &state->sb);
1095 if (!hasErrors && log->logAddress) {
1098 if (log->flags & LOG_FLAG_MAILFIRST)
1099 mailFilename = rotNames->firstRotated;
1101 mailFilename = rotNames->disposeName;
1105 mailLogWrapper(mailFilename, mailCommand, logNum, log);
1108 if (!hasErrors && rotNames->disposeName)
1109 hasErrors = removeLogFile(rotNames->disposeName, log);
1112 if (selinux_enabled) {
1113 setfscreatecon_raw(prev_context);
1114 if (prev_context != NULL) {
1115 freecon(prev_context);
1116 prev_context = NULL;
1123 int rotateLogSet(struct logInfo *log, int force)
1127 int logHasErrors[log->numFiles];
1129 struct logState **state;
1130 struct logNames **rotNames;
1133 log->criterium = ROT_FORCE;
1135 message(MESS_DEBUG, "\nrotating pattern: %s ", log->pattern);
1136 switch (log->criterium) {
1138 message(MESS_DEBUG, "after %d days ", log->threshhold);
1141 message(MESS_DEBUG, "weekly ");
1144 message(MESS_DEBUG, "monthly ");
1147 message(MESS_DEBUG, "yearly ");
1150 message(MESS_DEBUG, "%d bytes ", log->threshhold);
1153 message(MESS_DEBUG, "forced from command line ");
1157 if (log->rotateCount)
1158 message(MESS_DEBUG, "(%d rotations)\n", log->rotateCount);
1160 message(MESS_DEBUG, "(no old logs will be kept)\n");
1163 message(MESS_DEBUG, "olddir is %s, ", log->oldDir);
1165 if (log->flags & LOG_FLAG_IFEMPTY)
1166 message(MESS_DEBUG, "empty log files are rotated, ");
1168 message(MESS_DEBUG, "empty log files are not rotated, ");
1171 message(MESS_DEBUG, "only log files >= %d bytes are rotated, ", log->minsize);
1173 if (log->logAddress) {
1174 message(MESS_DEBUG, "old logs mailed to %s\n", log->logAddress);
1176 message(MESS_DEBUG, "old logs are removed\n");
1179 for (i = 0; i < log->numFiles; i++) {
1180 logHasErrors[i] = findNeedRotating(log, i);
1181 hasErrors |= logHasErrors[i];
1183 /* sure is a lot of findStating going on .. */
1184 if ((findState(log->files[i]))->doRotate)
1190 message(MESS_DEBUG, "not running first action script, "
1191 "since no logs will be rotated\n");
1193 message(MESS_DEBUG, "running first action script\n");
1194 if (runScript(log->pattern, log->first)) {
1195 message(MESS_ERROR, "error running first action script "
1196 "for %s\n", log->pattern);
1198 /* finish early, firstaction failed, affects all logs in set */
1204 state = malloc(log->numFiles * sizeof(struct logState *));
1205 rotNames = malloc(log->numFiles * sizeof(struct logNames *));
1208 (!(log->flags & LOG_FLAG_SHAREDSCRIPTS) && j < log->numFiles)
1209 || ((log->flags & LOG_FLAG_SHAREDSCRIPTS) && j < 1); j++) {
1212 ((log->flags & LOG_FLAG_SHAREDSCRIPTS) && i < log->numFiles)
1213 || (!(log->flags & LOG_FLAG_SHAREDSCRIPTS) && i == j); i++) {
1214 state[i] = findState(log->files[i]);
1216 rotNames[i] = malloc(sizeof(struct logNames));
1217 memset(rotNames[i], 0, sizeof(struct logNames));
1220 prerotateSingleLog(log, i, state[i], rotNames[i]);
1221 hasErrors |= logHasErrors[i];
1225 && (! ( (logHasErrors[j] && !(log->flags & LOG_FLAG_SHAREDSCRIPTS))
1226 || (hasErrors && (log->flags & LOG_FLAG_SHAREDSCRIPTS)) ) )) {
1228 message(MESS_DEBUG, "not running prerotate script, "
1229 "since no logs will be rotated\n");
1231 message(MESS_DEBUG, "running prerotate script\n");
1232 if (runScript(log->pattern, log->pre)) {
1233 if (log->flags & LOG_FLAG_SHAREDSCRIPTS)
1235 "error running shared prerotate script "
1236 "for '%s'\n", log->pattern);
1239 "error running non-shared prerotate script "
1240 "for %s of '%s'\n", log->files[j], log->pattern);
1242 logHasErrors[j] = 1;
1249 ((log->flags & LOG_FLAG_SHAREDSCRIPTS) && i < log->numFiles)
1250 || (!(log->flags & LOG_FLAG_SHAREDSCRIPTS) && i == j); i++) {
1251 if (! ( (logHasErrors[i] && !(log->flags & LOG_FLAG_SHAREDSCRIPTS))
1252 || (hasErrors && (log->flags & LOG_FLAG_SHAREDSCRIPTS)) ) ) {
1254 rotateSingleLog(log, i, state[i], rotNames[i]);
1255 hasErrors |= logHasErrors[i];
1260 && (! ( (logHasErrors[j] && !(log->flags & LOG_FLAG_SHAREDSCRIPTS))
1261 || (hasErrors && (log->flags & LOG_FLAG_SHAREDSCRIPTS)) ) )) {
1263 message(MESS_DEBUG, "not running postrotate script, "
1264 "since no logs were rotated\n");
1266 message(MESS_DEBUG, "running postrotate script\n");
1267 if (runScript(log->pattern, log->post)) {
1268 if (log->flags & LOG_FLAG_SHAREDSCRIPTS)
1270 "error running shared postrotate script "
1271 "for '%s'\n", log->pattern);
1274 "error running non-shared postrotate script "
1275 "for %s of '%s'\n", log->files[j], log->pattern);
1277 logHasErrors[j] = 1;
1284 ((log->flags & LOG_FLAG_SHAREDSCRIPTS) && i < log->numFiles)
1285 || (!(log->flags & LOG_FLAG_SHAREDSCRIPTS) && i == j); i++) {
1286 if (! ( (logHasErrors[i] && !(log->flags & LOG_FLAG_SHAREDSCRIPTS))
1287 || (hasErrors && (log->flags & LOG_FLAG_SHAREDSCRIPTS)) ) ) {
1289 postrotateSingleLog(log, i, state[i], rotNames[i]);
1290 hasErrors |= logHasErrors[i];
1296 for (i = 0; i < log->numFiles; i++) {
1297 free(rotNames[i]->firstRotated);
1298 free(rotNames[i]->disposeName);
1299 free(rotNames[i]->finalName);
1300 free(rotNames[i]->dirName);
1301 free(rotNames[i]->baseName);
1309 message(MESS_DEBUG, "not running last action script, "
1310 "since no logs will be rotated\n");
1312 message(MESS_DEBUG, "running last action script\n");
1313 if (runScript(log->pattern, log->last)) {
1314 message(MESS_ERROR, "error running last action script "
1315 "for %s\n", log->pattern);
1324 static int writeState(char *stateFilename)
1330 f = fopen(stateFilename, "w");
1332 message(MESS_ERROR, "error creating state file %s: %s\n",
1333 stateFilename, strerror(errno));
1337 fprintf(f, "logrotate state -- version 2\n");
1339 for (p = states.lh_first; p != NULL; p = p->list.le_next) {
1341 for (chptr = p->fn; *chptr; chptr++) {
1351 fprintf(f, " %d-%d-%d\n",
1352 p->lastRotated.tm_year + 1900,
1353 p->lastRotated.tm_mon + 1,
1354 p->lastRotated.tm_mday);
1362 static int readState(char *stateFilename)
1368 int year, month, day;
1372 struct logState *st;
1376 error = stat(stateFilename, &f_stat);
1378 if ((error && errno == ENOENT) || (!error && f_stat.st_size == 0)) {
1379 /* create the file before continuing to ensure we have write
1380 access to the file */
1381 f = fopen(stateFilename, "w");
1383 message(MESS_ERROR, "error creating state file %s: %s\n",
1384 stateFilename, strerror(errno));
1387 fprintf(f, "logrotate state -- version 2\n");
1391 message(MESS_ERROR, "error stat()ing state file %s: %s\n",
1392 stateFilename, strerror(errno));
1396 f = fopen(stateFilename, "r");
1398 message(MESS_ERROR, "error opening state file %s: %s\n",
1399 stateFilename, strerror(errno));
1403 if (!fgets(buf, sizeof(buf) - 1, f)) {
1404 message(MESS_ERROR, "error reading top line of %s\n",
1410 if (strcmp(buf, "logrotate state -- version 1\n") &&
1411 strcmp(buf, "logrotate state -- version 2\n")) {
1413 message(MESS_ERROR, "bad top line in state file %s\n",
1420 while (fgets(buf, sizeof(buf) - 1, f)) {
1424 if (buf[i - 1] != '\n') {
1425 message(MESS_ERROR, "line %d too long in state file %s\n",
1426 line, stateFilename);
1436 if (poptParseArgvString(buf, &argc, &argv) || (argc != 2) ||
1437 (sscanf(argv[1], "%d-%d-%d", &year, &month, &day) != 3)) {
1438 message(MESS_ERROR, "bad line %d in state file %s\n",
1439 line, stateFilename);
1445 /* Hack to hide earlier bug */
1446 if ((year != 1900) && (year < 1996 || year > 2100)) {
1448 "bad year %d for file %s in state file %s\n", year,
1449 argv[0], stateFilename);
1455 if (month < 1 || month > 12) {
1457 "bad month %d for file %s in state file %s\n", month,
1458 argv[0], stateFilename);
1464 /* 0 to hide earlier bug */
1465 if (day < 0 || day > 31) {
1467 "bad day %d for file %s in state file %s\n", day,
1468 argv[0], stateFilename);
1474 year -= 1900, month -= 1;
1476 if ((st = findState(argv[0])) == NULL)
1479 st->lastRotated.tm_mon = month;
1480 st->lastRotated.tm_mday = day;
1481 st->lastRotated.tm_year = year;
1483 /* fill in the rest of the st->lastRotated fields */
1484 lr_time = mktime(&st->lastRotated);
1485 st->lastRotated = *localtime(&lr_time);
1494 int main(int argc, const char **argv)
1497 char *stateFile = STATEFILE;
1502 struct logInfo *log;
1504 struct poptOption options[] = {
1505 {"debug", 'd', 0, 0, 'd',
1506 "Don't do anything, just test (implies -v)"},
1507 {"force", 'f', 0, &force, 0, "Force file rotation"},
1508 {"mail", 'm', POPT_ARG_STRING, &mailCommand, 0,
1509 "Command to send mail (instead of `" DEFAULT_MAIL_COMMAND "')",
1511 {"state", 's', POPT_ARG_STRING, &stateFile, 0,
1512 "Path of state file",
1514 {"verbose", 'v', 0, 0, 'v', "Display messages during rotation"},
1515 POPT_AUTOHELP {0, 0, 0, 0, 0}
1518 logSetLevel(MESS_NORMAL);
1519 setlocale (LC_ALL, "");
1521 optCon = poptGetContext("logrotate", argc, argv, options, 0);
1522 poptReadDefaultConfig(optCon, 1);
1523 poptSetOtherOptionHelp(optCon, "[OPTION...] <configfile>");
1525 while ((arg = poptGetNextOpt(optCon)) >= 0) {
1531 logSetLevel(MESS_DEBUG);
1537 fprintf(stderr, "logrotate: bad argument %s: %s\n",
1538 poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
1540 poptFreeContext(optCon);
1544 files = poptGetArgs((poptContext) optCon);
1546 fprintf(stderr, "logrotate " VERSION
1547 " - Copyright (C) 1995-2001 Red Hat, Inc.\n");
1549 "This may be freely redistributed under the terms of "
1550 "the GNU Public License\n\n");
1551 poptPrintUsage(optCon, stderr, 0);
1552 poptFreeContext(optCon);
1556 selinux_enabled = (is_selinux_enabled() > 0);
1557 selinux_enforce = security_getenforce();
1562 if (readAllConfigPaths(files)) {
1563 poptFreeContext(optCon);
1567 poptFreeContext(optCon);
1568 nowSecs = time(NULL);
1572 if (readState(stateFile))
1575 message(MESS_DEBUG, "\nHandling %d logs\n", numLogs);
1577 for (log = logs.tqh_first; log != NULL; log = log->list.tqe_next)
1578 rc |= rotateLogSet(log, force);
1581 rc |= writeState(stateFile);