summaryrefslogtreecommitdiffstats
path: root/pki/base/tps/src/main/RollingLogFile.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'pki/base/tps/src/main/RollingLogFile.cpp')
-rw-r--r--pki/base/tps/src/main/RollingLogFile.cpp493
1 files changed, 493 insertions, 0 deletions
diff --git a/pki/base/tps/src/main/RollingLogFile.cpp b/pki/base/tps/src/main/RollingLogFile.cpp
new file mode 100644
index 000000000..692a94334
--- /dev/null
+++ b/pki/base/tps/src/main/RollingLogFile.cpp
@@ -0,0 +1,493 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation;
+// version 2.1 of the License.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor,
+// Boston, MA 02110-1301 USA
+//
+// Copyright (C) 2010 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+
+#include <stdio.h>
+
+#ifdef XP_WIN32
+#define TPS_PUBLIC __declspec(dllexport)
+#else /* !XP_WIN32 */
+#define TPS_PUBLIC
+#endif /* !XP_WIN32 */
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "prmem.h"
+#include "prsystem.h"
+#include "plstr.h"
+#include "prio.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+#include "main/ConfigStore.h"
+#include "engine/RA.h"
+#include "main/RA_Context.h"
+#include "main/LogFile.h"
+#include "main/RollingLogFile.h"
+
+const char *RollingLogFile::CFG_MAX_FILE_SIZE= "maxFileSize";
+const char *RollingLogFile::CFG_ROLLOVER_INTERVAL= "rolloverInterval";
+const char *RollingLogFile::CFG_EXPIRATION_INTERVAL= "expirationTime";
+const int RollingLogFile::MAX_SLEEP = 21600; /* 6 hours */
+
+RollingLogFile::RollingLogFile() :
+ m_max_file_size(2000),
+ m_rollover_interval(0),
+ m_expiration_time(0),
+ m_expiration_sleep_time(0),
+ m_rotation_needed(false),
+ m_rollover_thread(NULL),
+ m_expiration_thread(NULL) { }
+
+int RollingLogFile::startup(RA_Context *ctx, const char* prefix, const char *fname, bool signed_audit)
+{
+ char configname[256];
+
+ if (ctx == NULL) {
+ return PR_FAILURE;
+ }
+
+ if (fname == NULL) {
+ ctx->LogError("RollingLogFile::startup",
+ __LINE__,
+ "startup error, fname is NULL");
+ return PR_FAILURE;
+ }
+
+ if (prefix == NULL) {
+ ctx->LogError("RollingLogFile::startup",
+ __LINE__,
+ "startup error for file %s: prefix is NULL",
+ fname);
+ return PR_FAILURE;
+ }
+
+ ConfigStore* store = RA::GetConfigStore();
+
+ if (store == NULL) {
+ ctx->LogError("RollingLogFile::startup",
+ __LINE__,
+ "Error in obtaining config store to set up rolling log for %s",
+ fname);
+ return PR_FAILURE;
+ }
+
+ PR_snprintf((char *)configname, 256, "%s.%s", prefix, CFG_MAX_FILE_SIZE);
+ m_max_file_size = store->GetConfigAsInt(configname, 2000); /* 2 MB */
+
+ PR_snprintf((char *)configname, 256, "%s.%s", prefix, CFG_ROLLOVER_INTERVAL);
+ m_rollover_interval = store->GetConfigAsInt(configname, 2592000); /* 30 days */
+
+ PR_snprintf((char *)configname, 256, "%s.%s", prefix, CFG_EXPIRATION_INTERVAL);
+ m_expiration_time = store->GetConfigAsInt(configname, 0); /* disabled, by default */
+
+ m_rollover_thread = (PRThread *) NULL;
+ m_expiration_thread = (PRThread*) NULL;
+ m_rotation_needed = false;
+
+ LogFile::startup(ctx, prefix, fname, signed_audit);
+
+ m_ctx->LogInfo( "RollingLogFile::startup",
+ __LINE__,
+ "thread = 0x%lx: Rolling log file %s startup complete",
+ PR_GetCurrentThread(), m_fname);
+ return PR_SUCCESS;
+}
+
+void RollingLogFile::shutdown()
+{
+ m_ctx->LogInfo( "RollingLogFile::shutdown",
+ __LINE__,
+ "thread = 0x%lx: Rolling log file %s shutting down",
+ PR_GetCurrentThread(), m_fname);
+
+ // interrupt and join threads
+
+ set_expiration_time(0);
+ if (m_expiration_thread != NULL) {
+ PR_Interrupt(m_expiration_thread);
+ PR_JoinThread(m_expiration_thread);
+ m_expiration_thread = (PRThread*) NULL;
+ }
+
+ set_rollover_interval(0);
+ if (m_rollover_thread != NULL) {
+ PR_Interrupt(m_rollover_thread);
+ PR_JoinThread(m_rollover_thread);
+ m_rollover_thread = (PRThread*) NULL;
+ }
+
+ LogFile::shutdown();
+}
+
+int RollingLogFile::write(char *msg) {
+ int status;
+ PR_EnterMonitor(m_monitor);
+
+ if (m_rotation_needed && m_signed && m_signed_log) {
+ rotate();
+ m_rotation_needed = false;
+ }
+
+ status = LogFile::write(msg);
+ if ((get_bytes_written() >= ((int) m_max_file_size*1024)) && (m_max_file_size >0)) {
+ if (! m_signed_log) {
+ rotate();
+ m_rotation_needed = false;
+ } else {
+ m_rotation_needed = true;
+ }
+ }
+ PR_ExitMonitor(m_monitor);
+ return status;
+}
+
+/* this is always called under a monitor */
+void RollingLogFile::rotate() {
+ PRTime now;
+ const char* time_fmt = "%Y%m%d-%H%M%S";
+ char datetime[1024];
+ char backup_fname[1024];
+ char *first_sig = (char *) NULL;
+ PRExplodedTime time;
+ int status;
+
+ now = PR_Now();
+ PR_ExplodeTime(now, PR_LocalTimeParameters, &time);
+ PR_FormatTimeUSEnglish(datetime, 1024, time_fmt, &time);
+ PR_snprintf((char *) backup_fname, 1024, "%s.%s", m_fname, datetime);
+
+ /* close the old file */
+ status = LogFile::close();
+ if (status != PR_SUCCESS) {
+ m_ctx->LogError( "RollingLogFile::rotate",
+ __LINE__,
+ "Failed to close log file %s",
+ m_fname);
+ goto loser;
+ } else {
+ m_fd = (PRFileDesc *) NULL;
+ }
+
+ status = PR_Rename(m_fname, backup_fname);
+ if (status != PR_SUCCESS) {
+ m_ctx->LogError( "RollingLogFile::rotate",
+ __LINE__,
+ "Failed to rename %s to %s",
+ m_fname, backup_fname);
+
+ status = LogFile::open();
+ if (status != PR_SUCCESS) {
+ m_ctx->LogError("RollingLogFile::rotate",
+ __LINE__,
+ "Failed to reopen log file %s",
+ m_fname);
+ }
+ goto loser;
+ }
+
+ /* open the new file */
+ m_fd = PR_Open(m_fname, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE, 440|200);
+ set_bytes_written(0);
+ if (m_fd == NULL) {
+ m_ctx->LogError( "RollingLogFile::rotate",
+ __LINE__,
+ "Failed to reopen log file %s",
+ m_fname);
+ } else {
+ if (m_signed_log) {
+ first_sig = RA::GetAuditSigningMessage("");
+ if (first_sig != NULL) {
+ status = LogFile::write(first_sig);
+ if (status != PR_SUCCESS) {
+ m_ctx->LogError("RollingLogFile::rotate",
+ __LINE__,
+ "Failed to write signature to new (rotated) log file %s",
+ m_fname);
+ } else {
+ status = LogFile::write("\n");
+ if (RA::m_last_audit_signature != NULL) {
+ PR_Free( RA::m_last_audit_signature );
+ }
+ RA::m_last_audit_signature = PL_strdup(first_sig);
+ m_signed = true;
+ }
+ PR_Free(first_sig);
+ } else {
+ m_ctx->LogError("RollingLogFile::rotate",
+ __LINE__,
+ "Failed to generate signature for new (rotated) log file %s",
+ m_fname);
+ }
+ }
+ }
+
+
+ loser:
+ m_rotation_needed = false;
+}
+
+void RollingLogFile::child_init()
+{
+ set_rollover_interval(m_rollover_interval);
+ set_expiration_time(m_expiration_time);
+}
+
+
+void RollingLogFile::set_rollover_interval(int interval)
+{
+ m_rollover_interval = interval;
+ if ((m_rollover_interval>0) && (m_rollover_thread == NULL)) {
+ m_rollover_thread = PR_CreateThread( PR_USER_THREAD,
+ start_rollover_thread,
+ (void *) this,
+ PR_PRIORITY_NORMAL, /* Priority */
+ PR_LOCAL_THREAD, /* Scope */
+ PR_JOINABLE_THREAD, /* State */
+ 0 /* Stack Size */);
+
+ } else {
+ if (m_rollover_thread != NULL) PR_Interrupt(m_rollover_thread);
+ }
+}
+
+void RollingLogFile::start_rollover_thread(void *args) {
+ RollingLogFile *rf;
+ if (args != NULL) {
+ rf = (RollingLogFile *) args;
+ rf->run_rollover_thread();
+ }
+}
+
+void RollingLogFile::run_rollover_thread() {
+
+ m_ctx->LogInfo( "RollingLogFile::run_rollover_thread",
+ __LINE__,
+ "thread = 0x%lx: Rollover thread for %s starting",
+ PR_GetCurrentThread(), m_fname);
+
+ while (m_rollover_interval > 0) {
+ PR_Sleep(PR_SecondsToInterval(m_rollover_interval));
+
+ PR_EnterMonitor(m_monitor);
+ if (m_rollover_interval == 0) break;
+ if (get_bytes_written()>0) {
+ if (! m_signed_log) {
+ rotate();
+ } else {
+ m_rotation_needed = true;
+ }
+ }
+ PR_ExitMonitor(m_monitor);
+ }
+
+ m_ctx->LogInfo( "RollingLogFile::run_rollover_thread",
+ __LINE__,
+ "thread = 0x%lx: Rollover thread for %s ending",
+ PR_GetCurrentThread(), m_fname);
+
+ PR_ExitMonitor(m_monitor);
+}
+
+void RollingLogFile::set_expiration_time(int interval)
+{
+ m_expiration_time = interval;
+ m_expiration_sleep_time = interval;
+
+ if ((interval>0) && (m_expiration_thread == NULL)) {
+ m_expiration_thread = PR_CreateThread( PR_USER_THREAD,
+ start_expiration_thread,
+ (void *) this,
+ PR_PRIORITY_NORMAL, /* Priority */
+ PR_GLOBAL_THREAD, /* Scope */
+ PR_JOINABLE_THREAD, /* State */
+ 0 /* Stack Size */);
+
+ } else {
+ if (m_expiration_thread != NULL) PR_Interrupt(m_expiration_thread);
+ }
+}
+
+void RollingLogFile::start_expiration_thread(void *args) {
+ RollingLogFile *rf;
+ if (args != NULL) {
+ rf = (RollingLogFile *) args;
+ rf->run_expiration_thread();
+ }
+}
+
+/* wait for a bit and then call expire().
+ Note that PR_Sleep() requires a small interval
+ (about 6 hrs to prevent overflow) */
+void RollingLogFile::run_expiration_thread() {
+ int interval;
+
+ m_ctx->LogInfo( "RollingLogFile::run_expiration_thread",
+ __LINE__,
+ "thread = 0x%lx: Expiration thread for %s starting",
+ PR_GetCurrentThread(), m_fname);
+
+ while (m_expiration_time > 0) {
+ expire();
+ while (m_expiration_sleep_time > 0) {
+ if (m_expiration_sleep_time > MAX_SLEEP) {
+ interval = MAX_SLEEP;
+ } else {
+ interval = m_expiration_sleep_time;
+ }
+
+ PR_Sleep(PR_SecondsToInterval(interval));
+ m_expiration_sleep_time = m_expiration_sleep_time - interval;
+
+ if (m_expiration_time == 0) break;
+ }
+
+ if (m_expiration_time == 0) break;
+ }
+
+ m_ctx->LogInfo( "RollingLogFile::run_expiration_thread",
+ __LINE__,
+ "thread = 0x%lx: Expiration thread for %s ending",
+ PR_GetCurrentThread(), m_fname);
+}
+
+/* remove log files that have not been modified in specified time */
+void RollingLogFile::expire() {
+ char basename[256];
+ char dirname[256];
+ char searchStr[256];
+ char full_search_name[256];
+ PRDir *dir;
+ PRDirEntry *entry;
+ PRFileInfo info;
+ PRTime expireTime;
+ PRTime now;
+ PRTime earliestModTime;
+ PRInt64 expiration_interval;
+ PRInt64 usec_per_sec;
+ PRInt64 tmp, tmp1;
+ PRStatus status;
+
+ if (m_expiration_time == 0) {
+ return;
+ }
+
+ if (strrchr(m_fname, '/') != NULL) {
+ PR_snprintf((char *) basename, 256, "%s", strrchr(m_fname, '/') +1);
+ PR_snprintf((char *) dirname, PL_strlen(m_fname) - PL_strlen(basename), "%s", m_fname);
+ PL_strcat(dirname, '\0');
+ } else {
+ PR_snprintf((char *) basename, 256, "%s", m_fname);
+ PR_snprintf((char *) dirname, 256, ".");
+ }
+
+ LL_I2L(tmp, m_expiration_time);
+ LL_I2L(usec_per_sec, PR_USEC_PER_SEC);
+ LL_MUL(expiration_interval, tmp, usec_per_sec);
+
+ now = PR_Now();
+ earliestModTime=now;
+ LL_SUB(expireTime, now, expiration_interval);
+
+ dir = PR_OpenDir(dirname);
+
+ if (dir == NULL) {
+ m_ctx->LogError( "RollingLogFile::expire",
+ __LINE__,
+ "Failed to open log file directory %s",
+ dirname);
+ return;
+ }
+
+ PR_snprintf(searchStr, 256, "%s.", basename);
+
+ while ((entry=PR_ReadDir(dir, PR_SKIP_BOTH)) != NULL) {
+ /* look only for entries of form basename. */
+
+ if (PL_strstr(entry->name, searchStr) != NULL) {
+ PR_snprintf(full_search_name, 256, "%s/%s", dirname, entry->name);
+ status = PR_GetFileInfo(full_search_name, &info);
+
+ if (status != PR_SUCCESS) {
+ m_ctx->LogError( "RollingLogFile::expire",
+ __LINE__,
+ "Failed to get file info for log file %s",
+ full_search_name);
+ // log failure to get file info
+ } else {
+ if (LL_CMP(info.modifyTime,<, expireTime)) {
+ status = PR_Delete(full_search_name);
+ if (status != PR_SUCCESS) {
+ m_ctx->LogError( "RollingLogFile::expire",
+ __LINE__,
+ "Failed to delete expired log file %s",
+ full_search_name);
+ } else {
+ RA::Debug("RollingLogFile::expire", "Deleted expired file: %s",
+ full_search_name);
+ }
+ } else {
+ if (LL_CMP(info.modifyTime,<,earliestModTime)) {
+ earliestModTime = info.modifyTime;
+ }
+ }
+ }
+ }
+ }
+
+ PR_CloseDir(dir);
+
+ /* set next wakeup interval */
+ /* A complicated 64-bit way of calculating :
+ m_expiration_sleep_time = (earliestModTime + m_expiration_time * 1000000 - PR_Now())/1000000;
+ */
+
+ LL_ADD(tmp, earliestModTime, expiration_interval);
+ LL_SUB(tmp1, tmp, now);
+ LL_DIV(tmp, tmp1, usec_per_sec);
+ LL_L2I(m_expiration_sleep_time, tmp);
+
+}
+
+int RollingLogFile::get_rollover_interval() {
+ return m_rollover_interval;
+}
+
+void RollingLogFile::set_rotation_needed(bool val) {
+ m_rotation_needed = val;
+}
+
+bool RollingLogFile::get_rotation_needed() {
+ return m_rotation_needed;
+}
+
+int RollingLogFile::get_expiration_time() {
+ return m_expiration_time;
+}
+
+