summaryrefslogtreecommitdiffstats
path: root/pki/base/common/src/com/netscape/cms/logging
diff options
context:
space:
mode:
Diffstat (limited to 'pki/base/common/src/com/netscape/cms/logging')
-rw-r--r--pki/base/common/src/com/netscape/cms/logging/LogEntry.java136
-rw-r--r--pki/base/common/src/com/netscape/cms/logging/LogFile.java1492
-rw-r--r--pki/base/common/src/com/netscape/cms/logging/RollingLogFile.java651
3 files changed, 2279 insertions, 0 deletions
diff --git a/pki/base/common/src/com/netscape/cms/logging/LogEntry.java b/pki/base/common/src/com/netscape/cms/logging/LogEntry.java
new file mode 100644
index 000000000..cc64ea6f6
--- /dev/null
+++ b/pki/base/common/src/com/netscape/cms/logging/LogEntry.java
@@ -0,0 +1,136 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2007 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package com.netscape.cms.logging;
+
+
+import java.io.*;
+import java.util.*;
+import java.text.*;
+import com.netscape.certsrv.base.*;
+import com.netscape.certsrv.logging.*;
+
+
+/**
+ * A log entry of LogFile
+ *
+ * @version $Revision$, $Date$
+ */
+public class LogEntry {
+ private String mEntry;
+ private String mLevel;
+ private String mSource;
+ private String mDetail;
+ private String mDate;
+ private String mTime;
+ private Vector mRow;
+
+ private final String DATE_PATTERN = "dd/MMM/yyyy:HH:mm:ss z";
+
+ /**
+ * Constructor for a LogEntry.
+ *
+ */
+ public LogEntry(String entry) throws ParseException {
+ mEntry = entry;
+ mRow = parse();
+ }
+
+ /**
+ * parse a log entry
+ *
+ * return a vector of the segments of the entry
+ */
+
+ public Vector parse() throws ParseException {
+ int x = mEntry.indexOf("[");
+
+ if (x == -1)
+ throw new ParseException(mEntry, 0);
+ String temp = mEntry.substring(x + 1);
+
+ x = temp.indexOf("]");
+ if (x == -1)
+ throw new ParseException(mEntry, 0);
+
+ String dateStr = temp.substring(0, x);
+ SimpleDateFormat format = new SimpleDateFormat(DATE_PATTERN);
+ Date date = format.parse(dateStr);
+
+ mDate = DateFormat.getDateInstance().format(date);
+ mTime = DateFormat.getTimeInstance().format(date);
+
+ temp = temp.substring(x + 2);
+ x = temp.indexOf("]");
+ if (x == -1)
+ throw new ParseException(mEntry, 0);
+ mSource = temp.substring(1, x);
+
+ temp = temp.substring(x + 2);
+ x = temp.indexOf("]");
+ if (x == -1)
+ throw new ParseException(mEntry, 0);
+ mLevel = temp.substring(1, x);
+
+ mDetail = temp.substring(x + 2);
+
+ Vector row = new Vector();
+
+ row.addElement(mSource);
+ row.addElement(mLevel);
+ row.addElement(mDate);
+ row.addElement(mTime);
+ row.addElement(mDetail);
+
+ //System.out.println(mSource +"," + mLevel +","+ mDate+","+mTime+","+mDetail);
+ return row;
+
+ }
+
+ public String getSource() {
+ return mSource;
+ }
+
+ public String getLevel() {
+ return mLevel;
+ }
+
+ public String getDetail() {
+ return mDetail;
+ }
+
+ public String getDate() {
+ return mDate;
+ }
+
+ public String getTime() {
+ return mTime;
+ }
+
+ public Vector getRow() {
+ return mRow;
+ }
+
+ public String getEntry() {
+ return mEntry;
+ }
+
+ public void appendDetail(String msg) {
+ mDetail = mDetail + "\n" + msg;
+ mEntry = mEntry + "\n" + msg;
+ }
+}
diff --git a/pki/base/common/src/com/netscape/cms/logging/LogFile.java b/pki/base/common/src/com/netscape/cms/logging/LogFile.java
new file mode 100644
index 000000000..8c9a38410
--- /dev/null
+++ b/pki/base/common/src/com/netscape/cms/logging/LogFile.java
@@ -0,0 +1,1492 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2007 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package com.netscape.cms.logging;
+
+
+import java.lang.*;
+import java.io.*;
+import java.io.File.*;
+import java.util.*;
+import java.text.*;
+import java.security.*;
+import java.security.Security;
+import java.security.spec.*;
+import java.security.interfaces.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+import com.netscape.certsrv.base.*;
+import com.netscape.certsrv.apps.*;
+import com.netscape.certsrv.common.*;
+import com.netscape.certsrv.logging.*;
+import com.netscape.cmsutil.util.*;
+import org.mozilla.jss.CryptoManager;
+import org.mozilla.jss.crypto.X509Certificate;
+import org.mozilla.jss.crypto.CryptoToken;
+import org.mozilla.jss.crypto.ObjectNotFoundException;
+import org.mozilla.jss.crypto.TokenException;
+import org.mozilla.jss.util.Base64OutputStream;
+import java.io.CharConversionException;
+
+/**
+ * A log event listener which write logs to log files
+ *
+ * @version $Revision$, $Date$
+ **/
+public class LogFile implements ILogEventListener, IExtendedPluginInfo {
+ public static final String PROP_TYPE = "type";
+ public static final String PROP_REGISTER = "register";
+ public static final String PROP_ON = "enable";
+ public static final String PROP_TRACE = "trace";
+ public static final String PROP_SIGNED_AUDIT_LOG_SIGNING = "logSigning";
+ public static final String PROP_SIGNED_AUDIT_CERT_NICKNAME =
+ "signedAuditCertNickname";
+ public static final String PROP_SIGNED_AUDIT_EVENTS = "events";
+ public static final String PROP_LEVEL = "level";
+ static final String PROP_FILE_NAME = "fileName";
+ static final String PROP_LAST_HASH_FILE_NAME = "lastHashFileName";
+ static final String PROP_BUFFER_SIZE = "bufferSize";
+ static final String PROP_FLUSH_INTERVAL = "flushInterval";
+
+ private final static String LOGGING_SIGNED_AUDIT_AUDIT_LOG_STARTUP =
+ "LOGGING_SIGNED_AUDIT_AUDIT_LOG_STARTUP_2";
+ private final static String LOGGING_SIGNED_AUDIT_SIGNING =
+ "LOGGING_SIGNED_AUDIT_SIGNING_3";
+ private final static String LOGGING_SIGNED_AUDIT_AUDIT_LOG_SHUTDOWN =
+ "LOGGING_SIGNED_AUDIT_AUDIT_LOG_SHUTDOWN_2";
+ private final static String LOG_SIGNED_AUDIT_EXCEPTION =
+ "LOG_SIGNED_AUDIT_EXCEPTION_1";
+
+ protected ILogger mSignedAuditLogger = CMS.getSignedAuditLogger();
+ protected IConfigStore mConfig = null;
+
+ /**
+ * The date string used in the log file name
+ */
+ static final String DATE_PATTERN = "yyyyMMddHHmmss";
+
+ //It may be interesting to make this flexable someday....
+ protected SimpleDateFormat mLogFileDateFormat = new SimpleDateFormat(DATE_PATTERN);
+
+ /**
+ * The default output stream buffer size in bytes
+ */
+ static final int BUFFER_SIZE = 512;
+
+ /**
+ * The default output flush interval in seconds
+ */
+ static final int FLUSH_INTERVAL = 5;
+
+ /**
+ * The log file
+ */
+ protected File mFile = null;
+
+ /**
+ * The log file name
+ */
+ protected String mFileName = null;
+
+ /**
+ * The log file output stream
+ */
+ protected BufferedWriter mLogWriter = null;
+
+ /**
+ * The log date entry format pattern
+ */
+ protected String mDatePattern = "dd/MMM/yyyy:HH:mm:ss z";
+
+ /**
+ * The log date entry format
+ */
+ protected SimpleDateFormat mLogDateFormat = new SimpleDateFormat(mDatePattern);
+
+ /**
+ * The date object used for log entries
+ */
+ protected Date mDate = new Date();
+
+ /**
+ * The number of bytes written to the current log file
+ */
+ protected int mBytesWritten = 0;
+
+ /**
+ * The output buffer size in bytes
+ */
+ protected int mBufferSize = BUFFER_SIZE;
+
+ /**
+ * The output buffer flush interval
+ */
+ protected int mFlushInterval = FLUSH_INTERVAL;
+
+ /**
+ * The number of unflushed bytes
+ */
+ protected int mBytesUnflushed = 0;
+
+ /**
+ * The output buffer flush interval thread
+ */
+ private Thread mFlushThread = null;
+
+ /**
+ * The current pid for the log entries
+ */
+ protected int mPid = CMS.getpid();
+
+ /**
+ * The selected log event types
+ */
+ protected String mSelectedEventsList = null;
+ protected Vector mSelectedEvents = null;
+
+ /**
+ * The eventType that this log is triggered
+ */
+ protected String mType = null;
+
+ /**
+ * The log is turned on/off
+ */
+ protected boolean mOn = false;
+
+ /**
+ * Should this log listener self-register or not
+ */
+ protected boolean mRegister = false;
+
+ protected boolean mTrace = false;
+
+ /**
+ * Log signing is on/off
+ */
+ protected boolean mLogSigning = false;
+
+ /**
+ * Nickname of certificate to use to sign log.
+ */
+ private String mSAuditCertNickName = "";
+
+ /**
+ * The provider used by the KeyGenerator and Mac
+ */
+ static final String CRYPTO_PROVIDER = "Mozilla-JSS";
+
+ /**
+ * The log level threshold
+ * Only logs with level greater or equal than this value will be written
+ */
+ protected long mLevel = 1;
+
+ /**
+ * Constructor for a LogFile.
+ *
+ */
+ public LogFile() {
+ }
+
+ public void init(ISubsystem owner, IConfigStore config)
+ throws EBaseException {
+ mConfig = config;
+
+ try {
+ mOn = config.getBoolean(PROP_ON, true);
+ } catch (EBaseException e) {
+ throw new ELogException(CMS.getUserMessage("CMS_BASE_GET_PROPERTY_FAILED",
+ config.getName() + "." + PROP_ON));
+ }
+
+ try {
+ mLogSigning = config.getBoolean(PROP_SIGNED_AUDIT_LOG_SIGNING,
+ false);
+ } catch (EBaseException e) {
+ throw new ELogException(CMS.getUserMessage("CMS_BASE_GET_PROPERTY_FAILED",
+ config.getName() + "." + PROP_SIGNED_AUDIT_LOG_SIGNING));
+ }
+
+ if (mOn && mLogSigning) {
+ try {
+ mSAuditCertNickName = config.getString(
+ PROP_SIGNED_AUDIT_CERT_NICKNAME);
+ CMS.debug("LogFile: init(): audit log signing enabled. signedAuditCertNickname="+ mSAuditCertNickName);
+ } catch (EBaseException e) {
+ throw new ELogException(CMS.getUserMessage("CMS_BASE_GET_PROPERTY_FAILED",
+ config.getName() + "."
+ + PROP_SIGNED_AUDIT_CERT_NICKNAME));
+ }
+ if (mSAuditCertNickName == null ||
+ mSAuditCertNickName.trim().equals("")) {
+ throw new ELogException(CMS.getUserMessage(
+ "CMS_BASE_GET_PROPERTY_FAILED",
+ config.getName() + "."
+ + PROP_SIGNED_AUDIT_CERT_NICKNAME));
+ }
+ }
+
+ // selective logging
+ mSelectedEventsList = null;
+ try {
+ mSelectedEventsList = config.getString(PROP_SIGNED_AUDIT_EVENTS);
+ } catch (EBaseException e) {
+ // when not specified, ALL are selected by default
+ }
+ mSelectedEvents = string2Vector(mSelectedEventsList);
+
+ try {
+ init(config);
+ } catch (IOException e) {
+ throw new ELogException(CMS.getUserMessage("CMS_LOG_UNEXPECTED_EXCEPTION", e.toString()));
+ }
+ }
+
+ /**
+ * turns a comma-separated String into a Vector
+ */
+ protected Vector string2Vector(String theString) {
+ Vector theVector = new Vector();
+ if (theString == null) {
+ return theVector;
+ }
+
+ StringTokenizer tokens = new StringTokenizer(theString,
+ ",");
+ while (tokens.hasMoreTokens()) {
+ String eventId = tokens.nextToken().trim();
+
+ theVector.addElement(eventId);
+ CMS.debug("LogFile: log event type selected: "+eventId);
+ }
+ return theVector;
+ }
+
+ /**
+ * add the event to the selected events list
+ * @param event to be selected
+ */
+ public void selectEvent(String event) {
+ if (!mSelectedEvents.contains(event))
+ mSelectedEvents.addElement(event);
+ }
+
+ /**
+ * remove the event from the selected events list
+ * @param event to be de-selected
+ */
+ public void deselectEvent(String event) {
+ if (mSelectedEvents.contains(event))
+ mSelectedEvents.removeElement(event);
+ }
+
+ /**
+ * replace the selected events list
+ * @param events comma-separated event list
+ */
+ public void replaceEvents(String events) {
+ Vector v = string2Vector(events);
+ mSelectedEvents.removeAllElements();
+ mSelectedEvents = v;
+ }
+
+ public static String base64Encode(byte[] bytes) throws IOException {
+ // All this streaming is lame, but Base64OutputStream needs a
+ // PrintStream
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ Base64OutputStream b64 = new Base64OutputStream(new
+ PrintStream(new
+ FilterOutputStream(output)
+ )
+ );
+
+ b64.write(bytes);
+ b64.flush();
+
+ // This is internationally safe because Base64 chars are
+ // contained within 8859_1
+ return output.toString("8859_1");
+ }
+
+ private static boolean mInSignedAuditLogFailureMode = false;
+
+ private static synchronized void shutdownCMS() {
+ if( mInSignedAuditLogFailureMode == false ) {
+
+ // Set signed audit log failure mode true
+ // No, this isn't a race condition, because the method is
+ // synchronized. We just want to avoid an infinite loop.
+ mInSignedAuditLogFailureMode = true;
+
+ // Block all new incoming requests
+ if( CMS.areRequestsDisabled() == false ) {
+ // XXX is this a race condition?
+ CMS.disableRequests();
+ }
+
+ // Terminate all requests in process
+ CMS.terminateRequests();
+
+ // Call graceful shutdown of the CMS server
+ // Call force shutdown to get added functionality of
+ // making sure to kill the web server.
+
+ CMS.forceShutdown();
+ }
+ }
+
+ /**
+ * Initialize and open the log using the parameters from a config store
+ *
+ * @param config The property config store to find values in
+ */
+ public void init(IConfigStore config) throws IOException,
+ EBaseException {
+ String fileName = null;
+ String defaultFileName = null;
+ String signedAuditDefaultFileName = "";
+
+ mConfig = config;
+
+ try {
+ mTrace = config.getBoolean(PROP_TRACE, false);
+ } catch (EBaseException e) {
+ throw new ELogException(CMS.getUserMessage("CMS_BASE_GET_PROPERTY_FAILED",
+ config.getName() + "." + PROP_TRACE));
+ }
+
+ try {
+ mType = config.getString(PROP_TYPE, "system");
+ } catch (EBaseException e) {
+ throw new ELogException(CMS.getUserMessage("CMS_BASE_GET_PROPERTY_FAILED",
+ config.getName() + "." + PROP_TYPE));
+ }
+
+ try {
+ mRegister = config.getBoolean(PROP_REGISTER, true);
+ } catch (EBaseException e) {
+ throw new ELogException(CMS.getUserMessage("CMS_BASE_GET_PROPERTY_FAILED",
+ config.getName() + "." + PROP_REGISTER));
+ }
+
+ if (mOn) {
+ if (mRegister) {
+ CMS.getLogger().getLogQueue().addLogEventListener(this);
+ }
+ } else {
+ // shutdown the listener, remove the listener
+ if (mRegister) {
+ CMS.getLogger().getLogQueue().removeLogEventListener(this);
+ shutdown();
+ }
+ }
+
+ try {
+ mLevel = config.getInteger(PROP_LEVEL, 3);
+ } catch (EBaseException e) {
+ e.printStackTrace();
+ throw new ELogException(CMS.getUserMessage("CMS_BASE_GET_PROPERTY_FAILED",
+ config.getName() + "." + PROP_LEVEL));
+ }
+
+ try {
+ // retrieve the subsystem
+ String subsystem = "";
+
+ ISubsystem caSubsystem = CMS.getSubsystem( "ca" );
+ if( caSubsystem != null ) {
+ subsystem = "ca";
+ }
+
+ ISubsystem raSubsystem = CMS.getSubsystem( "ra" );
+ if( raSubsystem != null ) {
+ subsystem = "ra";
+ }
+
+ ISubsystem kraSubsystem = CMS.getSubsystem( "kra" );
+ if( kraSubsystem != null ) {
+ subsystem = "kra";
+ }
+
+ ISubsystem ocspSubsystem = CMS.getSubsystem( "ocsp" );
+ if( ocspSubsystem != null ) {
+ subsystem = "ocsp";
+ }
+
+ // retrieve the instance name
+ String instIDPath = CMS.getInstanceDir();
+ int index = instIDPath.lastIndexOf( "/" );
+ String instID = instIDPath.substring( index + 1 );
+
+ // build the default signedAudit file name
+ signedAuditDefaultFileName = subsystem + "_"
+ + instID + "_" + "audit";
+
+ } catch( Exception e2 ) {
+ throw new ELogException(
+ CMS.getUserMessage( "CMS_BASE_GET_PROPERTY_FAILED",
+ config.getName() + "." +
+ PROP_FILE_NAME ) );
+ }
+
+ // the default value is determined by the eventType.
+ if (mType.equals(ILogger.PROP_SIGNED_AUDIT)) {
+ defaultFileName = "logs/signedAudit/" + signedAuditDefaultFileName;
+ }else if (mType.equals(ILogger.PROP_SYSTEM)) {
+ defaultFileName = "logs/system";
+ }else if (mType.equals(ILogger.PROP_AUDIT)) {
+ defaultFileName = "logs/transactions";
+ }else {
+ //wont get here
+ throw new ELogException(CMS.getUserMessage("CMS_LOG_INVALID_LOG_TYPE",
+ config.getName()));
+ }
+
+ try {
+ fileName = config.getString(PROP_FILE_NAME, defaultFileName);
+ } catch (EBaseException e) {
+ throw new ELogException(CMS.getUserMessage("CMS_BASE_GET_PROPERTY_FAILED",
+ config.getName() + "." + PROP_FILE_NAME));
+ }
+
+ if (mOn) {
+ init(fileName, config.getInteger(PROP_BUFFER_SIZE, BUFFER_SIZE),
+ config.getInteger(PROP_FLUSH_INTERVAL, FLUSH_INTERVAL));
+ }
+ }
+
+ /**
+ * Initialize and open the log
+ *
+ * @param bufferSize The buffer size for the output stream in bytes
+ * @param flushInterval The interval in seconds to flush the log
+ */
+ public void init(String fileName, int bufferSize, int flushInterval) throws IOException,ELogException {
+
+ if (fileName == null)
+ throw new ELogException(CMS.getUserMessage("CMS_LOG_INVALID_FILE_NAME", "null"));
+
+ //If we want to reuse the old log files
+ //mFileName = fileName + "." + mLogFileDateFormat.format(mDate);
+ mFileName = fileName;
+ if( !Utils.isNT() ) {
+ // Always insure that a physical file exists!
+ Utils.exec( "touch " + mFileName );
+ Utils.exec( "chmod 00640 " + mFileName );
+ }
+ mFile = new File(mFileName);
+ mBufferSize = bufferSize;
+ setFlushInterval(flushInterval);
+ open();
+ }
+
+ private PrivateKey mSigningKey = null;
+ private Signature mSignature = null;
+
+ private void setupSigning() throws EBaseException {
+ try {
+
+ Provider[] providers = java.security.Security.getProviders();
+ int ps = providers.length;
+ for (int i = 0; i<ps; i++) {
+ CMS.debug("LogFile: provider "+i+"= "+providers[i].getName());
+ }
+
+ CryptoManager cm = CryptoManager.getInstance();
+
+ // find CertServer's private key
+ X509Certificate cert = cm.findCertByNickname( mSAuditCertNickName );
+ if (cert != null) {
+ CMS.debug("LogFile: setupSignig(): found cert:"+mSAuditCertNickName);
+ } else {
+ CMS.debug("LogFile: setupSignig(): cert not found:"+mSAuditCertNickName);
+ }
+ mSigningKey = cm.findPrivKeyByCert(cert);
+
+ String sigAlgorithm;
+ if( mSigningKey instanceof RSAPrivateKey ) {
+ sigAlgorithm = "SHA-256/RSA";
+ } else if( mSigningKey instanceof DSAPrivateKey ) {
+ sigAlgorithm = "SHA-256/DSA";
+ } else {
+ throw new NoSuchAlgorithmException("Unknown private key type");
+ }
+
+ CryptoToken savedToken = cm.getThreadToken();
+ try {
+ CryptoToken keyToken =
+ ((org.mozilla.jss.pkcs11.PK11PrivKey)mSigningKey)
+ .getOwningToken();
+ cm.setThreadToken(keyToken);
+ mSignature = java.security.Signature.getInstance(sigAlgorithm,
+ CRYPTO_PROVIDER);
+ } finally {
+ cm.setThreadToken(savedToken);
+ }
+
+ mSignature.initSign(mSigningKey);
+
+ // get the last signature from the currently-opened file
+ String entry = getLastSignature(mFile);
+ if( entry != null ) {
+ mSignature.update(entry.getBytes("UTF-8"));
+ mSignature.update(LINE_SEP_BYTE);
+ }
+
+ // Always start off with a signature. That way, even if there
+ // were problems with the log file we inherited, we will
+ // get a fresh start with this instance.
+ pushSignature();
+
+ } catch (CryptoManager.NotInitializedException nie) {
+ setupSigningFailure("BASE_CRYPTOMANAGER_UNINITIALIZED", nie);
+ } catch (ObjectNotFoundException onfe) {
+ setupSigningFailure("LOG_SIGNING_CERT_NOT_FOUND", onfe);
+ } catch (TokenException te) {
+ setupSigningFailure("BASE_TOKEN_ERROR_0", te);
+ } catch (NoSuchAlgorithmException nsae) {
+ setupSigningFailure("LOG_NO_SUCH_ALGORITHM_0", nsae);
+ } catch (NoSuchProviderException nspe) {
+ setupSigningFailure("BASE_PROVIDER_NOT_SUPPORTED", nspe);
+ } catch (InvalidKeyException ike) {
+ setupSigningFailure("BASE_INVALID_KEY", ike);
+ } catch (SignatureException se) {
+ setupSigningFailure("LOG_SIGNING_OP_FAILED", se);
+ } catch (UnsupportedEncodingException uee) {
+ setupSigningFailure("LOG_UNEXPECTED_EXCEPTION", uee);
+ } catch (IOException ioe) {
+ setupSigningFailure("LOG_UNEXPECTED_EXCEPTION", ioe);
+ } catch (Exception e) {
+ setupSigningFailure("LOG_UNEXPECTED_EXCEPTION", e);
+ }
+ }
+
+ private static void setupSigningFailure(String logMessageCode, Exception e)
+ throws EBaseException
+ {
+ try {
+ ConsoleError.send( new SystemEvent(
+ CMS.getLogMessage(logMessageCode)));
+ } catch(Exception e2) {
+ // don't allow an exception while printing to the console
+ // prevent us from running the rest of this function.
+ e2.printStackTrace();
+ }
+ e.printStackTrace();
+ shutdownCMS();
+ throw new EBaseException(e.toString());
+ }
+
+ /**
+ * Startup the instance
+ * <P>
+ *
+ * <ul>
+ * <li>signed.audit LOGGING_SIGNED_AUDIT_AUDIT_LOG_STARTUP used at audit
+ * function startup
+ * </ul>
+ * @exception EBaseException if an internal error occurred
+ */
+ public void startup() throws EBaseException {
+ // ensure that any low-level exceptions are reported
+ // to the signed audit log and stored as failures
+ CMS.debug("LogFile: entering LogFile.startup()");
+ if( mOn && mLogSigning ) {
+ try {
+ setupSigning();
+ audit( CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_AUDIT_LOG_STARTUP,
+ ILogger.SYSTEM_UID,
+ ILogger.SUCCESS) );
+ } catch(EBaseException e) {
+ audit( CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_AUDIT_LOG_STARTUP,
+ ILogger.SYSTEM_UID,
+ ILogger.FAILURE) );
+ throw e;
+ }
+ }
+
+ }
+
+
+ /**
+ * Retrieves the eventType this log is triggered.
+ */
+ public String getType() {
+ return mType;
+ }
+
+ /**
+ * Retrieves the log on/off.
+ */
+ public String getOn() {
+ String logStatus = null;
+ return logStatus.valueOf( mOn );
+ }
+
+ /**
+ * Retrieves the log level threshold.
+ */
+ public long getLevel() {
+ return mLevel;
+ }
+
+ /**
+ * Retrieves the base log file name.
+ */
+ public String getName() {
+ return mFileName;
+ }
+
+ private boolean firstOpen = true;
+
+ /**
+ * Record that the signed audit log has been signed
+ * <P>
+ *
+ * <ul>
+ * <li>signed.audit LOGGING_SIGNED_AUDIT_SIGNING used when a signature on the
+ * audit log is generated (same as "flush" time)
+ * </ul>
+ * @exception IOException for input/output problems
+ * @exception ELogException when plugin implementation fails
+ * @exception SignatureException when signing fails
+ * @exception InvalidKeyException when an invalid key is utilized
+ */
+ private void pushSignature() throws IOException, ELogException,
+ SignatureException, InvalidKeyException
+ {
+ byte[] sigBytes = null;
+
+ if( mSignature == null ) {
+ return;
+ }
+
+ sigBytes = mSignature.sign();
+ mSignature.initSign(mSigningKey);
+
+ Object o[] = new Object[1];
+ o[0] = null;
+
+ // cook up a signed audit log message to record mac
+ // so as to avoid infinite recursiveness of calling
+ // the log() method
+ String auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_SIGNING,
+ ILogger.SYSTEM_UID,
+ ILogger.SUCCESS,
+ base64Encode( sigBytes ) );
+
+ if( mSignedAuditLogger == null ) {
+ return;
+ }
+
+ ILogEvent ev = mSignedAuditLogger.create(
+ ILogger.EV_SIGNED_AUDIT,
+ ( Properties ) null,
+ ILogger.S_SIGNED_AUDIT,
+ ILogger.LL_SECURITY,
+ auditMessage,
+ o,
+ ILogger.L_SINGLELINE );
+
+ String logMesg = logEvt2String(ev);
+ doLog(logMesg, true);
+ }
+
+ private static String getLastSignature(File f) throws IOException {
+ BufferedReader r = new BufferedReader( new FileReader(f) );
+ String lastSig = null;
+ String curLine = null;
+ while( (curLine = r.readLine()) != null ) {
+ if( curLine.indexOf("AUDIT_LOG_SIGNING") != -1 ) {
+ lastSig = curLine;
+ }
+ }
+ r.close();
+ return lastSig;
+ }
+
+ /**
+ * Open the log file. This creates the buffered FileWriter
+ *
+ */
+ protected synchronized void open() throws IOException {
+ RandomAccessFile out;
+
+ try {
+ out = new RandomAccessFile(mFile, "rw");
+ out.seek(out.length());
+ //XXX int or long?
+ mBytesWritten = (int) out.length();
+ if( !Utils.isNT() ) {
+ try {
+ Utils.exec( "chmod 00640 " + mFile.getCanonicalPath() );
+ } catch( IOException e ) {
+ CMS.debug( "Unable to change file permissions on "
+ + mFile.toString() );
+ }
+ }
+ mLogWriter = new BufferedWriter(
+ new FileWriter(out.getFD()), mBufferSize);
+
+ // The first time we open, mSignature will not have been
+ // initialized yet. That's ok, we will push our first signature
+ // in setupSigning().
+ if( mLogSigning && (mSignature != null)) {
+ try {
+ pushSignature();
+ } catch (ELogException le) {
+ ConsoleError.send(
+ new SystemEvent(CMS.getUserMessage("CMS_LOG_ILLEGALARGUMENT",
+ mFileName)));
+ }
+ }
+ } catch (IllegalArgumentException iae) {
+ ConsoleError.send(
+ new SystemEvent(CMS.getUserMessage("CMS_LOG_ILLEGALARGUMENT",
+ mFileName)));
+ } catch(GeneralSecurityException gse) {
+ // error with signed audit log, shutdown CMS
+ gse.printStackTrace();
+ shutdownCMS();
+ }
+
+ mBytesUnflushed = 0;
+ }
+
+ /**
+ * Flush the log file. Also update the MAC for hash protected logs
+ *
+ */
+ public synchronized void flush() {
+ try {
+ if( mLogSigning ) {
+ try {
+ pushSignature();
+ } catch (ELogException le) {
+ ConsoleError.send(new SystemEvent(CMS.getUserMessage("CMS_LOG_FLUSH_LOG_FAILED", mFileName, le.toString())));
+ }
+ }
+
+ if (mLogWriter != null) {
+ mLogWriter.flush();
+ }
+ } catch (IOException e) {
+ ConsoleError.send(new SystemEvent(CMS.getUserMessage("CMS_LOG_FLUSH_LOG_FAILED", mFileName, e.toString())));
+ } catch(GeneralSecurityException gse) {
+ // error with signed audit log, shutdown CMS
+ gse.printStackTrace();
+ shutdownCMS();
+ }
+
+ mBytesUnflushed = 0;
+ }
+
+ /**
+ * Close the log file
+ *
+ */
+ protected synchronized void close() {
+ try {
+ flush();
+ if (mLogWriter != null) {
+ mLogWriter.close();
+ }
+ } catch (IOException e) {
+ ConsoleError.send(new SystemEvent(CMS.getUserMessage("CMS_LOG_CLOSE_FAILED", mFileName, e.toString())));
+ }
+ mLogWriter = null;
+ }
+
+ /**
+ * Shutdown this log file.
+ * <P>
+ *
+ * <ul>
+ * <li>signed.audit LOGGING_SIGNED_AUDIT_AUDIT_LOG_SHUTDOWN used at audit
+ * function shutdown
+ * </ul>
+ */
+ public synchronized void shutdown() {
+ String auditMessage = null;
+
+ CMS.debug("LogFile:In log shutdown");
+
+ setFlushInterval(0);
+
+ // log signed audit shutdown success
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_AUDIT_LOG_SHUTDOWN,
+ ILogger.SYSTEM_UID,
+ ILogger.SUCCESS );
+
+ audit( auditMessage );
+
+ close();
+ }
+
+ /**
+ * Set the flush interval
+ * <P>
+ * @param flushInterval The amount of time in seconds until the log
+ * is flush. A value of 0 will disable autoflush. This will also set
+ * the update period for hash protected logs.
+ **/
+ public synchronized void setFlushInterval(int flushInterval) {
+ mFlushInterval = flushInterval * 1000;
+
+ if ((mFlushThread == null) && (mFlushInterval > 0)) {
+ mFlushThread = new FlushThread();
+ mFlushThread.setDaemon(true);
+ mFlushThread.start();
+ }
+
+ this.notify();
+ }
+
+ /**
+ * Log flush thread. Sleep for the flush interval and flush the
+ * log. Changing flush interval to 0 will cause this thread to exit.
+ */
+ final class FlushThread extends Thread {
+
+ /**
+ * Flush thread constructor including thread name
+ */
+ public FlushThread() {
+ super();
+ super.setName(mFileName + ".flush-" + (Thread.activeCount() + 1));
+ }
+
+ public void run() {
+ while (mFlushInterval > 0) {
+ // Sleep for the interval and then flush the log
+ synchronized (LogFile.this) {
+ try {
+ LogFile.this.wait(mFlushInterval);
+ } catch (InterruptedException e) {
+ // This shouldn't happen very often
+ ConsoleError.send(new
+ SystemEvent(CMS.getUserMessage("CMS_LOG_THREAD_INTERRUPT", "flush")));
+ }
+ }
+
+ if (mFlushInterval == 0) {
+ break;
+ }
+
+ if (mBytesUnflushed > 0) {
+ flush();
+ }
+ }
+ mFlushThread = null;
+ }
+ }
+
+ /**
+ * Synchronized method to write a string to the log file. All I18N
+ * should take place before this call.
+ *
+ * @param entry The log entry string
+ */
+ protected synchronized void log(String entry) throws ELogException {
+ doLog(entry, false);
+ }
+
+ // Standard line separator byte. We always sign this line separator,
+ // regardless of what we actually write to the file, so that signature
+ // verification is platform-independent.
+ private static final byte LINE_SEP_BYTE = 0x0a;
+
+ /**
+ * This method actually does the logging, and is not overridden
+ * by subclasses, so you can call it and know that it will do exactly
+ * what you see below.
+ */
+ private synchronized void doLog(String entry, boolean noFlush)
+ throws ELogException {
+ if (mLogWriter == null) {
+ String[] params = { mFileName, entry };
+
+ throw new ELogException(CMS.getUserMessage("CMS_LOG_LOGFILE_CLOSED", params));
+ } else {
+ try {
+ mLogWriter.write(entry, 0/*offset*/, entry.length());
+
+ if (mLogSigning==true) {
+ if(mSignature != null) {
+ // include newline for calculating MAC
+ mSignature.update(entry.getBytes("UTF-8"));
+ } else {
+ CMS.debug("LogFile: mSignature is not yet ready... null in log()");
+ }
+ }
+ if (mTrace) {
+ CharArrayWriter cw = new CharArrayWriter(200);
+ PrintWriter pw = new PrintWriter(cw);
+ Exception e = new Exception();
+ e.printStackTrace(pw);
+ char[] c = cw.toCharArray();
+ cw.close();
+ pw.close();
+
+ CharArrayReader cr = new CharArrayReader(c);
+ LineNumberReader lr = new LineNumberReader(cr);
+
+ String text = null;
+ String method = null;
+ String fileAndLine = null;
+ if (lr.ready()) {
+ text = lr.readLine();
+ do {
+ text = lr.readLine();
+ } while (text.indexOf("logging") != -1);
+ int p = text.indexOf("(");
+ fileAndLine = text.substring(p);
+
+ String classandmethod = text.substring(0, p);
+ int q = classandmethod.lastIndexOf(".");
+ method = classandmethod.substring(q + 1);
+ mLogWriter.write(fileAndLine, 0/*offset*/, fileAndLine.length());
+ mLogWriter.write(" ", 0/*offset*/, " ".length());
+ mLogWriter.write(method, 0/*offset*/, method.length());
+ }
+ }
+ mLogWriter.newLine();
+
+ if (mLogSigning==true){
+ if(mSignature != null) {
+ mSignature.update(LINE_SEP_BYTE);
+ } else {
+ CMS.debug("LogFile: mSignature is null in log() 2");
+ }
+ }
+ } catch (IOException e) {
+ ConsoleError.send(new SystemEvent(CMS.getUserMessage("CMS_LOG_WRITE_FAILED", mFileName, entry, e.toString())));
+ } catch (IllegalStateException e) {
+ CMS.debug("LogFile: exception thrown in log(): "+e.toString());
+ ConsoleError.send(new SignedAuditEvent(CMS.getLogMessage(LOG_SIGNED_AUDIT_EXCEPTION,e.toString())));
+ } catch( GeneralSecurityException gse ) {
+ // DJN: handle error
+ CMS.debug("LogFile: exception thrown in log(): "
+ + gse.toString());
+ gse.printStackTrace();
+ ConsoleError.send(new SignedAuditEvent(CMS.getLogMessage(
+ LOG_SIGNED_AUDIT_EXCEPTION,gse.toString())));
+ }
+
+
+ // XXX
+ // Although length will be in Unicode dual-bytes, the PrintWriter
+ // will only print out 1 byte per character. I suppose this could
+ // be dependent on the encoding of your log file, but it ain't that
+ // smart yet. Also, add one for the newline. (hmm, on NT, CR+LF)
+ int nBytes = entry.length() + 1;
+
+ mBytesWritten += nBytes;
+ mBytesUnflushed += nBytes;
+
+ if (mBufferSize > 0 && mBytesUnflushed > mBufferSize && !noFlush) {
+ flush();
+ }
+ }
+ }
+
+ /**
+ * Write an event to the log file
+ *
+ * @param ev The event to be logged.
+ */
+ public void log(ILogEvent ev) throws ELogException {
+ if (ev instanceof AuditEvent) {
+ if (!mType.equals("transaction") || (!mOn) || mLevel > ev.getLevel()) {
+ return;
+ }
+ } else if (ev instanceof SystemEvent) {
+ if (!mType.equals("system") || (!mOn) || mLevel > ev.getLevel()) {
+ return;
+ }
+ } else if (ev instanceof SignedAuditEvent) {
+ if (!mType.equals("signedAudit") || (!mOn) || mLevel > ev.getLevel()) {
+ return;
+ }
+ }
+
+ // Is the event type selected?
+ // If no selection specified in configuration, then all are selected
+ // If no type specified in propertity file, then treated as selected
+ if (mSelectedEvents.size() > 0) {
+ String type = ev.getEventType();
+ if (type != null) {
+ if (!mSelectedEvents.contains(type)) {
+ CMS.debug("LogFile: event type not selected: "+type);
+ return;
+ }
+ }
+ }
+
+ String entry = logEvt2String(ev);
+
+ log(entry);
+ }
+
+ public String logEvt2String(ILogEvent ev) {
+ String entry = null;
+
+ // Hmm.. multiple threads could hit this and reset the time.
+ // Do we care?
+ mDate.setTime(ev.getTimeStamp());
+
+ // XXX
+ // This should follow the Common Log Format which still needs
+ // some work.
+ if (ev.getMultiline() == ILogger.L_MULTILINE) {
+ entry = mPid + "." + Thread.currentThread().getName() + " - ["
+ + mLogDateFormat.format(mDate) + "] [" +
+ Integer.toString(ev.getSource()) + "] [" + Integer.toString(ev.getLevel())
+ + "] " + prepareMultiline(ev.toString());
+ } else {
+ entry = mPid + "." + Thread.currentThread().getName() + " - ["
+ + mLogDateFormat.format(mDate) + "] [" +
+ Integer.toString(ev.getSource()) + "] [" + Integer.toString(ev.getLevel())
+ + "] " + ev.toString();
+ }
+
+ return entry;
+ }
+
+ /**
+ * change multi-line log entry by replace "\n" with "\n "
+ *
+ * @param original The original multi-line log entry.
+ */
+ private String prepareMultiline(String original) {
+ int i, last = 0;
+
+ //NT: \r\n, unix: \n
+ while ((i = original.indexOf("\n", last)) != -1) {
+ last = i + 1;
+ original = original.substring(0, i + 1) + " " + original.substring(i + 1);
+ }
+ return original;
+ }
+
+ /**
+ * Read all entries whose logLevel>=lowLevel && log source = source
+ * to at most maxLine entries(from end)
+ * If the parameter is -1, it's ignored and return all entries
+ *
+ * @param maxLine The maximum lines to be returned
+ * @param lowLevel The lowest log level to be returned
+ * @param source The particular log source to be returned
+ * @param fName The log file name to be read. If it's null, read the current
+ * log file
+ */
+ public Vector readEntry(int maxLine, int lowLevel, int source, String fName) {
+ Vector mEntries = new Vector();
+ String fileName = mFileName;
+ BufferedReader fBuffer;
+ int lineNo = 0; // lineNo of the current entry in the log file
+ int line = 0; // line of readed valid entries
+ String firstLine = null; // line buffer
+ String nextLine = null;
+ String entry = null;
+ LogEntry logEntry = null;
+
+ /*
+ this variable is added to accormodate misplaced multiline entries
+ write out buffered log entry when next entry is parsed successfully
+ this implementation is assuming parsing is more time consuming than
+ condition check
+ */
+ LogEntry preLogEntry = null;
+
+ if (fName != null) {
+ fileName = fName;
+ }
+ try {
+ //XXX think about this
+ fBuffer = new BufferedReader(new FileReader(fileName));
+ do {
+ try {
+ nextLine = fBuffer.readLine();
+ if (nextLine != null) {
+ if ((nextLine.length() == 0) || (nextLine.charAt(0) == ' ')) {
+ // It's a continuous line
+ entry = null;
+ if (nextLine.length() > 1)
+ firstLine = firstLine + "\n" + nextLine.substring(1);
+ else
+ firstLine = firstLine + "\n";
+
+ } else {
+ // It's a new entry
+ entry = firstLine;
+ firstLine = nextLine;
+ }
+ // parse the previous entry, the current one is buffered
+ if (entry != null) {
+ try {
+ logEntry = new LogEntry(entry);
+ // if parse succeed, write out previous entry
+ if (preLogEntry != null) {
+ if ((Integer.parseInt(preLogEntry.getLevel()) >= lowLevel) &&
+ ((Integer.parseInt(preLogEntry.getSource()) == source) ||
+ (source == ILogger.S_ALL)
+ )) {
+ mEntries.addElement(preLogEntry);
+ if (maxLine == -1) {
+ line++;
+ } else if (line < maxLine) {
+ line++;
+ } else {
+ mEntries.removeElementAt(0);
+ }
+ }
+ }
+ preLogEntry = logEntry;
+ } catch (ParseException e) {
+ if (preLogEntry != null) {
+ preLogEntry.appendDetail(entry);
+ } else {
+ firstLine = firstLine + "\n" + nextLine;
+ }
+ entry = null;
+ logEntry = null;
+ }
+ }
+ }
+ lineNo++;
+
+ } catch (IOException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, ILogger.S_OTHER,
+ ILogger.LL_FAILURE,
+ CMS.getLogMessage("LOGGING_READ_ERROR", fileName,
+ Integer.toString(lineNo)));
+ }
+
+ }
+ while (nextLine != null);
+
+ // need to process the last 2 entries of the file
+ if (firstLine != null) {
+ if (logEntry != null) {
+ preLogEntry = logEntry;
+ }
+ entry = firstLine;
+ try {
+ logEntry = new LogEntry(entry);
+
+ /* System.out.println(
+ Integer.toString(Integer.parseInt(logEntry.getLevel()))
+ +","+Integer.toString(lowLevel)+","+
+ Integer.toString(Integer.parseInt(logEntry.getSource()))
+ +","+Integer.toString(source) );
+ */
+ if (preLogEntry != null) {
+ if ((Integer.parseInt(preLogEntry.getLevel()) >= lowLevel) &&
+ ((Integer.parseInt(preLogEntry.getSource()) == source) ||
+ (source == ILogger.S_ALL)
+ )) {
+ mEntries.addElement(preLogEntry);
+ if (maxLine == -1) {
+ line++;
+ } else if (line < maxLine) {
+ line++;
+ } else {
+ mEntries.removeElementAt(0);
+ }
+ }
+ }
+ preLogEntry = logEntry;
+ } catch (ParseException e) {
+ preLogEntry.appendDetail(entry);
+ }
+
+ if (preLogEntry != null) {
+ if ((Integer.parseInt(preLogEntry.getLevel()) >= lowLevel)
+ &&
+ ((Integer.parseInt(preLogEntry.getSource()) == source)
+ ||
+ (source == ILogger.S_ALL)
+ )) {
+ // parse the entry, pass to UI
+ mEntries.addElement(preLogEntry);
+ if (maxLine == -1) {
+ line++;
+ } else if (line < maxLine) {
+ line++;
+ } else {
+ mEntries.removeElementAt(0);
+ }
+ }
+ }
+
+ }// end: last entry
+
+ try {
+ fBuffer.close();
+ } catch (IOException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, ILogger.S_OTHER,
+ ILogger.LL_FAILURE, "logging:" + fileName +
+ " failed to close for reading");
+ }
+
+ } catch (FileNotFoundException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, ILogger.S_OTHER,
+ ILogger.LL_FAILURE,
+ CMS.getLogMessage("LOGGING_FILE_NOT_FOUND",
+ fileName));
+ }
+ return mEntries;
+ }
+
+ /**
+ * Retrieves the configuration store of this subsystem.
+ * <P>
+ *
+ * @return configuration store
+ */
+ public IConfigStore getConfigStore() {
+ return mConfig;
+ }
+
+ /**
+ * Retrieve last "maxLine" number of system log with log lever >"level"
+ * and from source "source". If the parameter is omitted. All entries
+ * are sent back.
+ */
+ public synchronized NameValuePairs retrieveLogContent(Hashtable req) throws ServletException,
+ IOException, EBaseException {
+ NameValuePairs params = new NameValuePairs();
+ String tmp, fName = null;
+ int maxLine = -1, level = -1, source = -1;
+ Vector entries = null;
+
+ if ((tmp = (String)req.get(Constants.PR_LOG_ENTRY)) != null) {
+ maxLine = Integer.parseInt(tmp);
+ }
+ if ((tmp = (String)req.get(Constants.PR_LOG_LEVEL)) != null) {
+ level = Integer.parseInt(tmp);
+ }
+ if ((tmp = (String)req.get(Constants.PR_LOG_SOURCE)) != null) {
+ source = Integer.parseInt(tmp);
+ }
+ tmp = (String)req.get(Constants.PR_LOG_NAME);
+ if (!(tmp.equals(Constants.PR_CURRENT_LOG))) {
+ fName = tmp;
+ } else {
+ flush();
+ }
+
+ try {
+ entries = readEntry(maxLine, level, source, fName);
+ for (int i = 0; i < entries.size(); i++) {
+ params.add(Integer.toString(i) +
+ ((LogEntry) entries.elementAt(i)).getEntry(), "");
+ }
+ } catch (Exception e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, ILogger.S_OTHER,
+ ILogger.LL_WARN,
+ "System log parse error");
+ }
+ return params;
+ }
+
+ /**
+ * Retrieve log file list.
+ */
+ public synchronized NameValuePairs retrieveLogList(Hashtable req) throws ServletException,
+ IOException, EBaseException {
+ return null;
+ }
+
+ public String getImplName() {
+ return "LogFile";
+ }
+
+ public String getDescription() {
+ return "LogFile";
+ }
+
+ public Vector getDefaultParams() {
+ Vector v = new Vector();
+
+ v.addElement(PROP_TYPE + "=");
+ v.addElement(PROP_ON + "=");
+ v.addElement(PROP_LEVEL + "=");
+ v.addElement(PROP_FILE_NAME + "=");
+ v.addElement(PROP_BUFFER_SIZE + "=");
+ v.addElement(PROP_FLUSH_INTERVAL + "=");
+
+ // needs to find a way to determine what type you want. if this
+ // is not for the signed audit type, then we should not show the
+ // following parameters.
+ //if( mType.equals( ILogger.PROP_SIGNED_AUDIT ) ) {
+ v.addElement( PROP_SIGNED_AUDIT_LOG_SIGNING + "=" );
+ v.addElement( PROP_SIGNED_AUDIT_CERT_NICKNAME + "=" );
+ v.addElement( PROP_SIGNED_AUDIT_EVENTS + "=" );
+ //}
+
+ return v;
+ }
+
+ public Vector getInstanceParams() {
+ Vector v = new Vector();
+
+ try {
+ String logStatus = null;
+
+ if (mType == null) {
+ v.addElement(PROP_TYPE + "=");
+ }else {
+ v.addElement(PROP_TYPE + "=" +
+ mConfig.getString(PROP_TYPE));
+ }
+ v.addElement(PROP_ON + "=" + logStatus.valueOf( mOn ) );
+ if (mLevel == 0)
+ v.addElement(PROP_LEVEL + "=" + ILogger.LL_DEBUG_STRING);
+ else if (mLevel == 1)
+ v.addElement(PROP_LEVEL + "=" + ILogger.LL_INFO_STRING);
+ else if (mLevel == 2)
+ v.addElement(PROP_LEVEL + "=" + ILogger.LL_WARN_STRING);
+ else if (mLevel == 3)
+ v.addElement(PROP_LEVEL + "=" + ILogger.LL_FAILURE_STRING);
+ else if (mLevel == 4)
+ v.addElement(PROP_LEVEL + "=" + ILogger.LL_MISCONF_STRING);
+ else if (mLevel == 5)
+ v.addElement(PROP_LEVEL + "=" + ILogger.LL_CATASTRPHE_STRING);
+ else if (mLevel == 6)
+ v.addElement(PROP_LEVEL + "=" + ILogger.LL_SECURITY_STRING);
+
+ if (mFileName == null) {
+ v.addElement(PROP_FILE_NAME + "=");
+ }else {
+ v.addElement(PROP_FILE_NAME + "=" +
+ mFileName);
+ }
+ v.addElement(PROP_BUFFER_SIZE + "=" + mBufferSize);
+ v.addElement(PROP_FLUSH_INTERVAL + "=" + mFlushInterval / 1000);
+
+ if( (mType != null) && mType.equals( ILogger.PROP_SIGNED_AUDIT ) ) {
+ String logSigning = null;
+
+ v.addElement( PROP_SIGNED_AUDIT_LOG_SIGNING + "="
+ + logSigning.valueOf( mLogSigning ) );
+
+ if( mSAuditCertNickName == null ) {
+ v.addElement( PROP_SIGNED_AUDIT_CERT_NICKNAME + "=" );
+ } else {
+ v.addElement( PROP_SIGNED_AUDIT_CERT_NICKNAME + "="
+ + mSAuditCertNickName );
+ }
+
+ if( mSelectedEventsList == null ) {
+ v.addElement( PROP_SIGNED_AUDIT_EVENTS + "=" );
+ } else {
+ v.addElement( PROP_SIGNED_AUDIT_EVENTS + "="
+ + mSelectedEventsList );
+ }
+ }
+ } catch (Exception e) {
+ }
+ return v;
+ }
+
+ public String[] getExtendedPluginInfo(Locale locale) {
+ if( mType.equals( ILogger.PROP_SIGNED_AUDIT ) ) {
+ String[] params = {
+ PROP_TYPE + ";choice(transaction,signedAudit,system);The log event type this instance is listening to",
+ PROP_ON + ";boolean;Turn on the listener",
+ PROP_LEVEL + ";choice(" + ILogger.LL_DEBUG_STRING + "," +
+ ILogger.LL_INFO_STRING + "," +
+ ILogger.LL_WARN_STRING + "," +
+ ILogger.LL_FAILURE_STRING + "," +
+ ILogger.LL_MISCONF_STRING + "," +
+ ILogger.LL_CATASTRPHE_STRING + "," +
+ ILogger.LL_SECURITY_STRING + ");Only log message with level higher than this filter will be written by this listener",
+ PROP_FILE_NAME + ";string;The name of the file the log is written to",
+ PROP_BUFFER_SIZE + ";integer;The size of the buffer to receive log messages in kilobytes(KB)",
+ PROP_FLUSH_INTERVAL + ";integer;The maximum time in seconds before the buffer is flushed to the file",
+ IExtendedPluginInfo.HELP_TOKEN +
+ ";configuration-logrules-logfile",
+ IExtendedPluginInfo.HELP_TEXT +
+ ";Write the log messages to a file",
+ PROP_SIGNED_AUDIT_LOG_SIGNING +
+ ";boolean;Enable audit logs to be signed",
+ PROP_SIGNED_AUDIT_CERT_NICKNAME +
+ ";string;The nickname of the certificate to be used to sign audit logs",
+ PROP_SIGNED_AUDIT_EVENTS +
+ ";string;A comma-separated list of strings used to specify particular signed audit log events",
+ };
+
+ return params;
+ } else {
+ // mType.equals( ILogger.PROP_AUDIT ) ||
+ // mType.equals( ILogger.PROP_SYSTEM )
+ String[] params = {
+ PROP_TYPE + ";choice(transaction,signedAudit,system);The log event type this instance is listening to",
+ PROP_ON + ";boolean;Turn on the listener",
+ PROP_LEVEL + ";choice(" + ILogger.LL_DEBUG_STRING + "," +
+ ILogger.LL_INFO_STRING + "," +
+ ILogger.LL_WARN_STRING + "," +
+ ILogger.LL_FAILURE_STRING + "," +
+ ILogger.LL_MISCONF_STRING + "," +
+ ILogger.LL_CATASTRPHE_STRING + "," +
+ ILogger.LL_SECURITY_STRING + ");Only log message with level higher than this filter will be written by this listener",
+ PROP_FILE_NAME + ";string;The name of the file the log is written to",
+ PROP_BUFFER_SIZE + ";integer;The size of the buffer to receive log messages in kilobytes(KB)",
+ PROP_FLUSH_INTERVAL + ";integer;The maximum time in seconds before the buffer is flushed to the file",
+ IExtendedPluginInfo.HELP_TOKEN +
+ ";configuration-logrules-logfile",
+ IExtendedPluginInfo.HELP_TEXT +
+ ";Write the log messages to a file"
+ };
+
+ return params;
+ }
+ }
+
+ /**
+ * Signed Audit Log
+ *
+ * This method is inherited by all classes that extend this "LogFile"
+ * class, and is called to store messages to the signed audit log.
+ * <P>
+ *
+ * @param msg signed audit log message
+ */
+ protected void audit( String msg )
+ {
+ // in this case, do NOT strip preceding/trailing whitespace
+ // from passed-in String parameters
+
+ if( mSignedAuditLogger == null ) {
+ return;
+ }
+
+ mSignedAuditLogger.log( ILogger.EV_SIGNED_AUDIT,
+ null,
+ ILogger.S_SIGNED_AUDIT,
+ ILogger.LL_SECURITY,
+ msg );
+ }
+}
+
diff --git a/pki/base/common/src/com/netscape/cms/logging/RollingLogFile.java b/pki/base/common/src/com/netscape/cms/logging/RollingLogFile.java
new file mode 100644
index 000000000..32702d00d
--- /dev/null
+++ b/pki/base/common/src/com/netscape/cms/logging/RollingLogFile.java
@@ -0,0 +1,651 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2007 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package com.netscape.cms.logging;
+
+
+import java.io.*;
+import java.util.*;
+import java.text.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+import com.netscape.certsrv.apps.*;
+import com.netscape.certsrv.base.*;
+import com.netscape.certsrv.common.*;
+import com.netscape.certsrv.logging.*;
+import com.netscape.cmsutil.util.*;
+
+
+/**
+ * A rotating log file for Certificate log events. This class loosely follows
+ * the Netscape Common Log API implementing rollover interval, size and file
+ * naming conventions. It does not yet implement Disk Usage.
+ *
+ * @version $Revision$, $Date$
+ */
+public class RollingLogFile extends LogFile {
+ public static final String PROP_MAX_FILE_SIZE = "maxFileSize";
+ public static final String PROP_ROLLOVER_INTERVAL = "rolloverInterval";
+ public static final String PROP_EXPIRATION_TIME = "expirationTime";
+
+ /**
+ * The default max file size in bytes
+ */
+ static final int MAX_FILE_SIZE = 100;
+
+ /**
+ * The default rollover interval in seconds
+ */
+ static final String ROLLOVER_INTERVAL = "2592000";
+
+ /**
+ * The default expiration time in seconds
+ */
+ static final String EXPIRATION_TIME = "2592000";
+
+ /**
+ * The maximum file size in bytes
+ */
+ protected int mMaxFileSize = 0;
+
+ /**
+ * The amount of time in miniseconds between log rotations
+ */
+ protected long mRolloverInterval = 0;
+
+ /**
+ * The thread responsible for rotating the log
+ */
+ private Thread mRolloverThread = null;
+
+ /**
+ * The incrementing backup number for the log file names
+ */
+ private int mFileNumber = 1;
+
+ /**
+ * The amount of time before a backed up log is removed in milliseconds
+ */
+ protected long mExpirationTime = 0;
+
+ /**
+ * The thread responsible for removing expired log files
+ */
+ private Thread mExpirationThread = null;
+
+ /**
+ * The object used as a lock for expiration thread synchronization
+ */
+ private Object mExpLock = new Object();
+
+ private final static String LOGGING_SIGNED_AUDIT_LOG_DELETE =
+ "LOGGING_SIGNED_AUDIT_LOG_DELETE_3";
+
+ /**
+ * Construct a RollingLogFile
+ */
+ public RollingLogFile() {
+ }
+
+ /**
+ * Initialize and open a RollingLogFile using the prop config store
+ *
+ * @param config The property config store to find values in
+ */
+ public void init(IConfigStore config) throws IOException,
+ EBaseException {
+ super.init(config);
+
+ rl_init(config.getInteger(PROP_MAX_FILE_SIZE, MAX_FILE_SIZE),
+ config.getString(PROP_ROLLOVER_INTERVAL, ROLLOVER_INTERVAL),
+ config.getString(PROP_EXPIRATION_TIME, EXPIRATION_TIME));
+ }
+
+ /**
+ * Convenience routine to initialized the RollingLogFile specific
+ * attributes.
+ */
+ protected void rl_init(int maxFileSize, String rolloverInterval,
+ String expirationTime) {
+ mMaxFileSize = maxFileSize * 1024;
+ setRolloverTime(rolloverInterval);
+ setExpirationTime(expirationTime);
+ }
+
+ public void startup() throws EBaseException {
+ super.startup();
+ }
+
+ /**
+ * Shutdown this log file.
+ */
+ public synchronized void shutdown() {
+ setRolloverTime("0");
+ setExpirationTime("0");
+ super.shutdown();
+ }
+
+ /**
+ * Set the rollover interval
+ *
+ * @param rolloverSeconds The amount of time in seconds until the log
+ * is rotated. A value of 0 will disable log rollover.
+ **/
+ public synchronized void setRolloverTime(String rolloverSeconds) {
+ mRolloverInterval = Long.valueOf(rolloverSeconds).longValue() * 1000;
+
+ if ((mRolloverThread == null) && (mRolloverInterval > 0)) {
+ mRolloverThread = new RolloverThread();
+ mRolloverThread.setDaemon(true);
+ mRolloverThread.start();
+ }
+
+ this.notify();
+ }
+
+ /**
+ * Get the rollover interval
+ *
+ * @return The interval in seconds in which the log is rotated
+ **/
+ public synchronized int getRolloverTime() {
+ return (int) (mRolloverInterval / 1000);
+ }
+
+ /**
+ * Set the file expiration time
+ *
+ * @param expirationSeconds The amount of time in seconds until log files
+ * are deleted
+ **/
+ public void setExpirationTime(String expirationSeconds) {
+
+ // Need to completely protect changes to mExpiration time
+ // and make sure they only happen while the thread is sleeping
+ synchronized (mExpLock) {
+ mExpirationTime = Long.valueOf(expirationSeconds).longValue() * 1000;
+
+ if (mExpirationThread == null) {
+ if (mExpirationTime > 0) {
+ mExpirationThread = new ExpirationThread();
+ mExpirationThread.setDaemon(true);
+ mExpirationThread.start();
+ }
+ } else {
+ mExpLock.notify();
+ }
+ }
+ }
+
+ /**
+ * Get the expiration time
+ *
+ * @return The age in seconds in which log files are delete
+ **/
+ public int getExpirationTime() {
+ return (int) (mExpirationTime / 1000);
+ }
+
+ /**
+ * Rotate the log file to a backup file with a incrementing integer
+ * extension
+ **/
+ public synchronized void rotate()
+ throws IOException {
+
+ //File backupFile = new File(mFileName + "." + mFileNumber);
+ File backupFile = new File(mFileName + "." + mLogFileDateFormat.format(mDate));
+
+ // close, backup, and reopen the log file zeroizing its contents
+ super.close();
+ try {
+ if( Utils.isNT() ) {
+ // NT is very picky on the path
+ Utils.exec( "copy " +
+ mFile.getCanonicalPath().replace( '/', '\\' ) +
+ " " +
+ backupFile.getCanonicalPath().replace( '/',
+ '\\' ) );
+ } else {
+ // Create a copy of the original file which
+ // preserves the original file permissions.
+ Utils.exec( "cp -p " + mFile.getCanonicalPath() + " " +
+ backupFile.getCanonicalPath() );
+ }
+
+ // Zeroize the original file if and only if
+ // the backup copy was successful.
+ if( backupFile.exists() ) {
+
+ // Make certain that the backup file has
+ // the correct permissions.
+ if( !Utils.isNT() ) {
+ Utils.exec( "chmod 00640 " + backupFile.getCanonicalPath() );
+ }
+
+ try {
+ // Open and close the original file
+ // to zeroize its contents.
+ PrintWriter pw = new PrintWriter( mFile );
+ pw.close();
+
+ // Make certain that the original file retains
+ // the correct permissions.
+ if( !Utils.isNT() ) {
+ Utils.exec( "chmod 00640 " + mFile.getCanonicalPath() );
+ }
+ } catch ( FileNotFoundException e ) {
+ CMS.debug( "Unable to zeroize "
+ + mFile.toString() );
+ }
+ } else {
+ CMS.debug( "Unable to backup "
+ + mFile.toString() + " to "
+ + backupFile.toString() );
+ }
+ } catch( Exception e ) {
+ CMS.debug( "Unable to backup "
+ + mFile.toString() + " to "
+ + backupFile.toString() );
+ }
+ super.open(); // will reset mBytesWritten
+ mFileNumber++;
+ }
+
+ /**
+ * Remove any log files which have not been modified in the specified
+ * time
+ * <P>
+ *
+ * NOTE: automatic removal of log files is currently NOT supported!
+ * <P>
+ *
+ * <ul>
+ * <li>signed.audit LOGGING_SIGNED_AUDIT_LOG_DELETE used AFTER audit log
+ * expires (authorization should not allow, but in case authorization gets
+ * compromised make sure it is written AFTER the log expiration happens)
+ * </ul>
+ * @param expirationSeconds The number of seconds since the expired files
+ * have been modified.
+ * @return the time in milliseconds when the next file expires
+ **/
+ public long expire(long expirationSeconds) throws ELogException {
+ String auditMessage = null;
+
+ if (expirationSeconds <= 0)
+ throw new ELogException(CMS.getUserMessage("CMS_LOG_EXPIRATION_TIME_ZERO"));
+
+ long expirationTime = expirationSeconds * 1000;
+ long currentTime = System.currentTimeMillis();
+ long oldestFile = currentTime;
+
+ String dirName = mFile.getParent();
+
+ if (dirName == null)
+ dirName = ".";
+ File dir = new File(dirName);
+
+ // Get just the base name, minus the .date extension
+ //int len = mFile.getName().length() - LogFile.DATE_PATTERN.length() - 1;
+ //String baseName = mFile.getName().substring(0, len);
+ String fileName = mFile.getName();
+ String baseName = null, pathName = null;
+ int index = fileName.lastIndexOf("/");
+
+ if (index != -1) { // "/" exist in fileName
+ pathName = fileName.substring(0, index);
+ baseName = fileName.substring(index + 1);
+ if (dirName == null) {
+ dirName = pathName;
+ } else {
+ dirName = dirName.concat("/" + pathName);
+ }
+ }else { // "/" NOT exist in fileName
+ baseName = fileName;
+ }
+
+ fileFilter ff = new fileFilter(baseName + ".");
+ String[] filelist = dir.list(ff);
+
+ if (filelist == null) { // Crap! Something is wrong.
+ throw new
+ ELogException(CMS.getUserMessage("CMS_LOG_DIRECTORY_LIST_FAILED",
+ dirName, ff.toString()));
+ }
+
+ // Walk through the list of files which match this log file name
+ // and delete the old ones.
+ for (int i = 0; i < filelist.length; i++) {
+ if (pathName != null) {
+ filelist[i] = pathName + "/" + filelist[i];
+ }else {
+ filelist[i] = dirName + "/" + filelist[i];
+ }
+
+ String fullname = dirName + File.separatorChar + filelist[i];
+ File file = new File(fullname);
+ long fileTime = file.lastModified();
+
+ // Java documentation on File says lastModified() should not
+ // be interpeted. The doc is wrong. See JavaSoft bug #4094538
+ if ((currentTime - fileTime) > expirationTime) {
+ file.delete();
+
+ if (file.exists()) {
+ // log failure in deleting an expired signed audit log file
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_LOG_DELETE,
+ ILogger.SYSTEM_UID,
+ ILogger.FAILURE,
+ fullname);
+ } else {
+ // log success in deleting an expired signed audit log file
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_LOG_DELETE,
+ ILogger.SYSTEM_UID,
+ ILogger.SUCCESS,
+ fullname);
+ }
+
+ audit(auditMessage);
+ } else if (fileTime < oldestFile) {
+ oldestFile = fileTime;
+ }
+ }
+ return oldestFile + expirationTime;
+ }
+
+ //
+ // Rollover and Expiration threads
+ //
+ // At first glance you may think it's a waste of thread resources to have
+ // two threads for every log file, but the truth is that these threads are
+ // sleeping 99% of the time. NxN thread implementations (Solaris, NT,
+ // IRIX 6.4, Unixware, etc...) will handle these in user space.
+ //
+ // You may be able to join these into one thread, and deal with
+ // multiple wakeup times, but the code would sure look ugly, and the race
+ // conditions are numerous as is. Furthermore, this is what user space
+ // threads will do for you anyways.
+ //
+
+ /**
+ * Log rotation thread. Sleep for the rollover interval and rotate the
+ * log. Changing rollover interval to 0 will cause this thread to exit.
+ */
+ final class RolloverThread extends Thread {
+
+ /**
+ * Rollover thread constructor including thread name
+ */
+ public RolloverThread() {
+ super();
+ super.setName(mFileName + ".rollover-" + (Thread.activeCount() + 1));
+ }
+
+ public void run() {
+ while (mRolloverInterval > 0) {
+ // Sleep for the interval and then rotate the log
+ synchronized (RollingLogFile.this) {
+ try {
+ RollingLogFile.this.wait(mRolloverInterval);
+ } catch (InterruptedException e) {
+ // This shouldn't happen very often
+ CMS.getLogger().getLogQueue().log(new
+ SystemEvent(CMS.getUserMessage("CMS_LOG_THREAD_INTERRUPT", "rollover")));
+ }
+ }
+
+ if (mRolloverInterval == 0) {
+ break;
+ }
+
+ if (mBytesWritten > 0) {
+ try {
+ rotate();
+ } catch (IOException e) {
+ ConsoleError.send(new
+ SystemEvent(CMS.getUserMessage("CMS_LOG_ROTATE_LOG_FAILED", mFile.getName(), e.toString())));
+ break;
+ }
+ }
+ // else
+ // Don't rotate empty logs
+ // flag in log summary file?
+ }
+ mRolloverThread = null;
+ }
+ }
+
+
+ /**
+ * Log expiration thread. Sleep for the expiration interval and
+ * delete any files which are too old.
+ * Changing expiration interval to 0 will cause this thread to exit.
+ */
+ final class ExpirationThread extends Thread {
+
+ /**
+ * ExpirationThread thread constructor including thread name
+ */
+ public ExpirationThread() {
+ super();
+ super.setName(mFileName + ".expiration-" + (Thread.activeCount() + 1));
+ }
+
+ public void run() {
+ synchronized (mExpLock) {
+ while (mExpirationTime > 0) {
+ long wakeupTime = 0;
+ long sleepTime = 0;
+
+ // First, remove any old log files and figure out when the
+ // next one expires
+ try {
+ wakeupTime = expire((long) (mExpirationTime / 1000));
+ } catch (SecurityException e) {
+ ConsoleError.send(new
+ SystemEvent(CMS.getUserMessage("CMS_LOG_EXPIRE_LOG_FAILED", e.toString())));
+ break;
+ } catch (ELogException e) {
+ ConsoleError.send(new
+ SystemEvent(CMS.getUserMessage("CMS_LOG_EXPIRE_LOG_FAILED", e.toString())));
+ break;
+ }
+
+ sleepTime = wakeupTime - System.currentTimeMillis();
+ //System.out.println("wakeup " + wakeupTime);
+ //System.out.println("current "+System.currentTimeMillis());
+ //System.out.println("sleep " + sleepTime);
+ // Sleep for the interval and then check the directory
+ // Note: mExpirationTime can only change while we're
+ // sleeping
+ if (sleepTime > 0) {
+ try {
+ mExpLock.wait(sleepTime);
+ } catch (InterruptedException e) {
+ // This shouldn't happen very often
+ ConsoleError.send(new
+ SystemEvent(CMS.getUserMessage("CMS_LOG_THREAD_INTERRUPT", "expiration")));
+ }
+ }
+ }
+ }
+ mExpirationThread = null;
+ }
+ }
+
+ /**
+ * Write an event to the log file
+ *
+ * @param ev The event to be logged.
+ **/
+ public synchronized void log(ILogEvent ev) throws ELogException {
+ //xxx, Shall we log first without checking if it exceed the maximum?
+ super.log(ev); // Will increment mBytesWritten
+
+ if ((0 != mMaxFileSize) && (mBytesWritten > mMaxFileSize)) {
+ flush();
+ try {
+ rotate();
+ } catch (IOException e) {
+ throw new ELogException(CMS.getUserMessage("CMS_LOG_ROTATE_LOG_FAILED", mFile.getName(), e.toString()));
+ }
+ }
+ }
+
+ /**
+ * Retrieve log file list.
+ */
+ public synchronized NameValuePairs retrieveLogList(Hashtable req
+ ) throws ServletException,
+ IOException, EBaseException {
+ NameValuePairs params = new NameValuePairs();
+ String[] files = null;
+
+ files = fileList();
+ for (int i = 0; i < files.length; i++) {
+ params.add(files[i], "");
+ }
+ return params;
+ }
+
+ /**
+ * Get the log file list in the log directory
+ *
+ * @return an array of filenames with related path to cert server root
+ */
+ protected String[] fileList() {
+ String pathName = null, baseName = null;
+
+ String dirName = mFile.getParent();
+ String fileName = mFile.getName();
+ int index = fileName.lastIndexOf("/");
+
+ if (index != -1) { // "/" exist in fileName
+ pathName = fileName.substring(0, index);
+ baseName = fileName.substring(index + 1);
+ if (dirName == null) {
+ dirName = pathName;
+ } else {
+ dirName = dirName.concat("/" + pathName);
+ }
+ }else { // "/" NOT exist in fileName
+ baseName = fileName;
+ }
+
+ File dir = new File(dirName);
+
+ fileFilter ff = new fileFilter(baseName + ".");
+ //There are some difference here. both should work
+ //error,logs,logs/error jdk115
+ //logs/system,., logs/system jdk116
+ //System.out.println(mFile.getName()+","+dirName+","+mFile.getPath()); //log/system,.
+
+ String[] filelist = dir.list(ff);
+
+ for (int i = 0; i < filelist.length; i++) {
+ if (pathName != null) {
+ filelist[i] = pathName + "/" + filelist[i];
+ }else {
+ filelist[i] = dirName + "/" + filelist[i];
+ }
+ }
+ return filelist;
+ }
+
+ public String getImplName() {
+ return "RollingLogFile";
+ }
+
+ public String getDescription() {
+ return "RollingLogFile";
+ }
+
+ public Vector getDefaultParams() {
+ Vector v = super.getDefaultParams();
+
+ v.addElement(PROP_MAX_FILE_SIZE + "=");
+ v.addElement(PROP_ROLLOVER_INTERVAL + "=");
+ //v.addElement(PROP_EXPIRATION_TIME + "=");
+ return v;
+ }
+
+ public Vector getInstanceParams() {
+ Vector v = super.getInstanceParams();
+
+ try {
+ v.addElement(PROP_MAX_FILE_SIZE + "=" + mMaxFileSize / 1024);
+ if (mRolloverInterval / 1000 <= 60 * 60)
+ v.addElement(PROP_ROLLOVER_INTERVAL + "=" + "Hourly");
+ else if (mRolloverInterval / 1000 <= 60 * 60 * 24)
+ v.addElement(PROP_ROLLOVER_INTERVAL + "=" + "Daily");
+ else if (mRolloverInterval / 1000 <= 60 * 60 * 24 * 7)
+ v.addElement(PROP_ROLLOVER_INTERVAL + "=" + "Weekly");
+ else if (mRolloverInterval / 1000 <= 60 * 60 * 24 * 30)
+ v.addElement(PROP_ROLLOVER_INTERVAL + "=" + "Monthly");
+ else if (mRolloverInterval / 1000 <= 60 * 60 * 24 * 366)
+ v.addElement(PROP_ROLLOVER_INTERVAL + "=" + "Yearly");
+
+ //v.addElement(PROP_EXPIRATION_TIME + "=" + mExpirationTime / 1000);
+ } catch (Exception e) {
+ }
+ return v;
+ }
+
+ public String[] getExtendedPluginInfo(Locale locale) {
+ String[] p = super.getExtendedPluginInfo(locale);
+ Vector info = new Vector();
+
+ for (int i = 0; i < p.length; i++) {
+ if (!p[i].startsWith(IExtendedPluginInfo.HELP_TOKEN) && !p[i].startsWith(IExtendedPluginInfo.HELP_TEXT))
+ info.addElement(p[i]);
+ }
+ info.addElement(PROP_MAX_FILE_SIZE + ";integer;If the current log file size if bigger than this parameter in kilobytes(KB), the file will be rotated.");
+ info.addElement(PROP_ROLLOVER_INTERVAL + ";choice(Hourly,Daily,Weekly,Monthly,Yearly);The frequency of the log being rotated.");
+ info.addElement(PROP_EXPIRATION_TIME + ";integer;The amount of time before a backed up log is removed in seconds");
+ info.addElement(IExtendedPluginInfo.HELP_TOKEN +
+ //";configuration-logrules-rollinglogfile");
+ ";configuration-adminbasics");
+ info.addElement(IExtendedPluginInfo.HELP_TEXT +
+ ";Write the log messages to a file which will be rotated automatically.");
+ String[] params = new String[info.size()];
+
+ info.copyInto(params);
+ return params;
+
+ }
+}
+
+
+/**
+ * A file filter to select the file with a given prefix
+ */
+class fileFilter implements FilenameFilter {
+ String patternToMatch = null;
+
+ public fileFilter (String pattern) {
+ patternToMatch = pattern;
+ }
+
+ public boolean accept(File dir, String name) {
+ if (name.startsWith(patternToMatch))
+ return true;
+ else
+ return false;
+ }
+}