[¤1] Pegasus Enhancement Proposal (PEP)
[¤2] PEP #: 312
[¤3] PEP Type: Functional
[¤4] Title: Separate OOP logs
[¤5] Version: 0.2
[¤6] Created: 18th December 2007
[¤7] Authors: Ouyang Jian
[¤8] Status: draft
[¤9]
[¤10] Version History:
[¤27]
[¤28] Abstract: Make OOP providers write their own log files.
[¤29] Definition of the Problem
[¤30] Currently log files are set to 600 mode to prevent from been removed or fabricated by non-superusers. But when OOP providers switch to non-superuser user context (registered with DesignatedUserContext), they no longer be able to write the log files again (lost).
[¤31] Users of a provider very likely to be interested in only it's agent's log. It is hard for them to strip them out from a hash file.
[¤32] Proposed Solution
[¤33] Make OOP providers write their own log files. (just like what trace file does now)
[¤34] We just need to mutex between threads in a single provider, no need to mutex between processes.
[¤35] OOP provider agent create their own log files, so the file authority problem is no longer there.
[¤36] Finally a provider user can simply get all log of his provider from it's log file, no need to filter other uninterested log, and user can remove his log freely.
[¤37] I suggest the log file name of OOP provider to be in the from of "<log name>.<module name>.<user name>.log". For example: ProviderAgentStandard.testmodule.ouyang.log.
[¤38] Rationale
[¤39] Separate single log file into log files owned by OOP processes to eliminate file authority problem.
[¤40] Provider users need not look through all the unwanted logs.
[¤42] 1) When logdir is firstly created, set it's permission to 755.
[¤43] 2) Before OOP provider is forked cimserver create log files for agent, change file authority to 600, and change file owner to the user who runs the agent.
[¤44] 3) When OOP provider agent begins, it resets log file names in LoggerRep. So all it's log will be written in its own log files
[¤45] 4) No need to use file lock to mutex between processes any more.
[¤46] Build option
[¤47] Add a macro 'PEGASUS_SEPARATE_OOP_LOG' to enable this function. Defaultly it is not defined.
[¤48] Implementation prototype
[¤49] Source files need to be changed
[¤50] Common/Logger.cpp
[¤51] Common/Logger.h
[¤52] ProviderManagerService/OOPProviderManagerRouter.cpp
[¤53] ProviderManagerService/ProviderAgent/ProviderAgent.cpp
[¤54] ProviderManagerService/ProviderAgent/cimprovagt.cpp
[¤55] Impacted modules
[¤56] libpegcommon.so
[¤57] libpegpmservice.so
[¤58] cimprovagt
[¤59] A prototype patch looks like:
Index: Common/Logger.cpp
[¤60]
===================================================================
[¤61]
RCS file: /cvs/MSB/pegasus/src/Pegasus/Common/Logger.cpp,v
[¤62]
retrieving revision 1.59
[¤63]
diff -u -r1.59 Logger.cpp
[¤64]
--- Common/Logger.cpp 27 May 2008 17:35:41 -0000 1.59
[¤65]
+++ Common/Logger.cpp 3 Jun 2008 12:27:04 -0000
[¤66]
@@ -71,6 +71,10 @@
[¤67]
LoggerRep* Logger::_rep = 0;
[¤68]
String Logger::_homeDirectory = ".";
[¤69]
[¤70]
+#if defined(PEGASUS_SEPARATE_OOP_LOG)
[¤71]
+String Logger::_agentName;
[¤72]
+#endif
[¤73]
+
[¤74]
const Uint32 Logger::_NUM_LOGLEVEL = 5;
[¤75]
[¤76]
Uint32 Logger::_severityMask;
[¤77]
@@ -162,7 +166,13 @@
[¤78]
CString lgDir = homeDirectory.getCString();
[¤79]
[¤80]
if (!System::isDirectory(lgDir))
[¤81]
+ {
[¤82]
System::makeDirectory(lgDir);
[¤83]
+# if defined(PEGASUS_SEPARATE_OOP_LOG)
[¤84]
+ System::changeFilePermissions(lgDir,
[¤85]
+ S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
[¤86]
+# endif
[¤87]
+ }
[¤88]
[¤89]
// KS: I put the second test in just in case some trys to create
[¤90]
// a completly erronous directory. At least we will get a message
[¤91]
@@ -174,18 +184,20 @@
[¤92]
cerr << MessageLoader::getMessage(parms);
[¤93]
}
[¤94]
[¤95]
- //Filelocks are not used for VMS
[¤96]
-# if !defined(PEGASUS_OS_VMS)
[¤97]
- _loggerLockFileName = _constructFileName(homeDirectory, lockFileName);
[¤98]
-
[¤99]
- // Open and close a file to make sure that the file exists, on which
[¤100]
- // file lock is requested
[¤101]
- FILE *fileLockFilePointer;
[¤102]
- fileLockFilePointer = fopen(_loggerLockFileName, "a+");
[¤103]
- if(fileLockFilePointer)
[¤104]
- {
[¤105]
- fclose(fileLockFilePointer);
[¤106]
- }
[¤107]
+# if !defined(PEGASUS_SEPARATE_OOP_LOG)
[¤108]
+ //Filelocks are not used for VMS
[¤109]
+# if !defined(PEGASUS_OS_VMS)
[¤110]
+ _loggerLockFileName = _constructFileName(homeDirectory, lockFileName);
[¤111]
+
[¤112]
+ // Open and close a file to make sure that the file exists, on which
[¤113]
+ // file lock is requested
[¤114]
+ FILE *fileLockFilePointer;
[¤115]
+ fileLockFilePointer = fopen(_loggerLockFileName, "a+");
[¤116]
+ if(fileLockFilePointer)
[¤117]
+ {
[¤118]
+ fclose(fileLockFilePointer);
[¤119]
+ }
[¤120]
+# endif
[¤121]
# endif
[¤122]
[¤123]
_logFileNames[Logger::TRACE_LOG] = _constructFileName(homeDirectory,
[¤124]
@@ -203,6 +215,36 @@
[¤125]
fileNames[Logger::ERROR_LOG]);
[¤126]
}
[¤127]
[¤128]
+# if defined(PEGASUS_SEPARATE_OOP_LOG)
[¤129]
+ void agentInit(const String &homeDirectory, const String &agentName)
[¤130]
+ {
[¤131]
+ String name;
[¤132]
+
[¤133]
+ name = "ProviderAgentTrace";
[¤134]
+ name = name + "." + agentName + ".log";
[¤135]
+
[¤136]
+ _logFileNames[Logger::TRACE_LOG] = _constructFileName(homeDirectory,
[¤137]
+ name.getCString());
[¤138]
+
[¤139]
+ name = "ProviderAgentStrandard";
[¤140]
+ name = name + "." + agentName + ".log";
[¤141]
+ _logFileNames[Logger::STANDARD_LOG] = _constructFileName(homeDirectory,
[¤142]
+ name.getCString());
[¤143]
+
[¤144]
+# ifdef PEGASUS_ENABLE_AUDIT_LOGGER
[¤145]
+ name = "ProviderAgentAudit";
[¤146]
+ name = name + "." + agentName + ".log";
[¤147]
+ _logFileNames[Logger::AUDIT_LOG] = _constructFileName(homeDirectory,
[¤148]
+ name.getCString());
[¤149]
+# endif
[¤150]
+
[¤151]
+ name = "ProviderAgentError";
[¤152]
+ name = name + "." + agentName + ".log";
[¤153]
+ _logFileNames[Logger::ERROR_LOG] = _constructFileName(homeDirectory,
[¤154]
+ name.getCString());
[¤155]
+ }
[¤156]
+# endif
[¤157]
+
[¤158]
~LoggerRep()
[¤159]
{
[¤160]
}
[¤161]
@@ -233,7 +275,9 @@
[¤162]
// Acquire AutoMutex (for thread sync)
[¤163]
// and AutoFileLock (for Process Sync).
[¤164]
AutoMutex am(_mutex);
[¤165]
+# if !defined(PEGASUS_SEPARATE_OOP_LOG)
[¤166]
AutoFileLock fileLock(_loggerLockFileName);
[¤167]
+# endif
[¤168]
[¤169]
Uint32 logFileSize = 0;
[¤170]
[¤171]
@@ -290,7 +334,9 @@
[¤172]
CString _logFileNames[int(Logger::NUM_LOGS)];
[¤173]
[¤174]
# ifndef PEGASUS_OS_VMS
[¤175]
+# if !defined(PEGASUS_SEPARATE_OOP_LOG)
[¤176]
CString _loggerLockFileName;
[¤177]
+# endif
[¤178]
Mutex _mutex;
[¤179]
# endif
[¤180]
};
[¤181]
@@ -304,6 +350,16 @@
[¤182]
//
[¤183]
///////////////////////////////////////////////////////////////////////////////
[¤184]
[¤185]
+#if defined(PEGASUS_SEPARATE_OOP_LOG)
[¤186]
+void Logger::agentInit()
[¤187]
+{
[¤188]
+ if (!_rep)
[¤189]
+ _rep = new LoggerRep(_homeDirectory);
[¤190]
+
[¤191]
+ _rep->agentInit(_homeDirectory, _agentName);
[¤192]
+}
[¤193]
+#endif
[¤194]
+
[¤195]
void Logger::_putInternal(
[¤196]
LogFileType logFileType,
[¤197]
const String& systemId,
[¤198]
Index: Common/Logger.h
[¤199]
===================================================================
[¤200]
RCS file: /cvs/MSB/pegasus/src/Pegasus/Common/Logger.h,v
[¤201]
retrieving revision 1.33
[¤202]
diff -u -r1.33 Logger.h
[¤203]
--- Common/Logger.h 15 May 2008 17:24:34 -0000 1.33
[¤204]
+++ Common/Logger.h 3 Jun 2008 12:27:20 -0000
[¤205]
@@ -72,6 +72,10 @@
[¤206]
static const Uint32 SEVERE;
[¤207]
static const Uint32 FATAL;
[¤208]
[¤209]
+#if defined(PEGASUS_SEPARATE_OOP_LOG)
[¤210]
+ static void agentInit();
[¤211]
+#endif
[¤212]
+
[¤213]
/** Puts a message to the defined log file
[¤214]
@param logFileType - Type of log file (Trace, etc.)
[¤215]
@param systemId - ID of the system generating the log entry within
[¤216]
@@ -374,8 +378,18 @@
[¤217]
return (_severityMask & logLevel) != 0;
[¤218]
}
[¤219]
[¤220]
+#if defined(PEGASUS_SEPARATE_OOP_LOG)
[¤221]
+ static void setAgentName(const String &name)
[¤222]
+ {
[¤223]
+ _agentName = name;
[¤224]
+ }
[¤225]
+#endif
[¤226]
+
[¤227]
private:
[¤228]
[¤229]
+#if defined(PEGASUS_SEPARATE_OOP_LOG)
[¤230]
+ static String _agentName;
[¤231]
+#endif
[¤232]
static LoggerRep* _rep;
[¤233]
static String _homeDirectory;
[¤234]
static Uint32 _severityMask;
[¤235]
Index: ProviderManagerService/OOPProviderManagerRouter.cpp
[¤236]
===================================================================
[¤237]
RCS file: /cvs/MSB/pegasus/src/Pegasus/ProviderManagerService/OOPProviderManagerRouter.cpp,v
[¤238]
retrieving revision 1.19
[¤239]
diff -u -r1.19 OOPProviderManagerRouter.cpp
[¤240]
--- ProviderManagerService/OOPProviderManagerRouter.cpp 12 May 2008 09:22:33 -0000 1.19
[¤241]
+++ ProviderManagerService/OOPProviderManagerRouter.cpp 3 Jun 2008 12:28:01 -0000
[¤242]
@@ -41,6 +41,7 @@
[¤243]
#include <Pegasus/Common/CIMMessageDeserializer.h>
[¤244]
#include <Pegasus/Common/OperationContextInternal.h>
[¤245]
#include <Pegasus/Common/System.h>
[¤246]
+#include <Pegasus/Common/FileSystem.h>
[¤247]
#include <Pegasus/Common/AnonymousPipe.h>
[¤248]
#include <Pegasus/Common/Tracer.h>
[¤249]
#include <Pegasus/Common/Logger.h>
[¤250]
@@ -386,6 +387,46 @@
[¤251]
PEG_METHOD_EXIT();
[¤252]
}
[¤253]
[¤254]
+#if defined(PEGASUS_SEPARATE_OOP_LOG)
[¤255]
+/**
[¤256]
+ Create log files for OOP provider agent.
[¤257]
+ */
[¤258]
+static void _createAgentLogFiles(const String &moduleName, const String &userName)
[¤259]
+{
[¤260]
+ const char *logNames[] =
[¤261]
+ {
[¤262]
+ "ProviderAgentStrandard",
[¤263]
+ "ProviderAgentError",
[¤264]
+ "ProviderAgentTrace"
[¤265]
+#ifdef PEGASUS_ENABLE_AUDIT_LOGGER
[¤266]
+ ,
[¤267]
+ "ProviderAgentAudit"
[¤268]
+#endif
[¤269]
+ };
[¤270]
+
[¤271]
+ ConfigManager* configManager = ConfigManager::getInstance();
[¤272]
+ String logDir = ConfigManager::getHomedPath(
[¤273]
+ configManager->getCurrentValue("logdir"));
[¤274]
+
[¤275]
+ int size = sizeof(logNames)/sizeof(const char*);
[¤276]
+ for (int i=0; i<size; ++i)
[¤277]
+ {
[¤278]
+ String fileName;
[¤279]
+
[¤280]
+ fileName = logDir + "/" + logNames[i] +
[¤281]
+ "." + moduleName +
[¤282]
+ "." + userName + ".log";
[¤283]
+
[¤284]
+ if(!System::exists(fileName.getCString()))
[¤285]
+ {
[¤286]
+ ofstream cf(fileName.getCString(), ios::out);
[¤287]
+ FileSystem::changeFilePermissions(fileName, S_IRUSR | S_IWUSR);
[¤288]
+ FileSystem::changeFileOwner(fileName, userName);
[¤289]
+ }
[¤290]
+ }
[¤291]
+}
[¤292]
+#endif
[¤293]
+
[¤294]
void ProviderAgentContainer::_startAgentProcess()
[¤295]
{
[¤296]
PEG_METHOD_ENTER(
[¤297]
@@ -550,6 +591,9 @@
[¤298]
[¤299]
try
[¤300]
{
[¤301]
+#if defined(PEGASUS_SEPARATE_OOP_LOG) && !defined(PEGASUS_USE_SYSLOGS)
[¤302]
+ _createAgentLogFiles(_moduleName, _userName);
[¤303]
+#endif
[¤304]
_startAgentProcess();
[¤305]
_isInitialized = true;
[¤306]
_sendInitializationData();
[¤307]
Index: ProviderManagerService/ProviderAgent/ProviderAgent.cpp
[¤308]
===================================================================
[¤309]
RCS file: /cvs/MSB/pegasus/src/Pegasus/ProviderManagerService/ProviderAgent/ProviderAgent.cpp,v
[¤310]
retrieving revision 1.14
[¤311]
diff -u -r1.14 ProviderAgent.cpp
[¤312]
--- ProviderManagerService/ProviderAgent/ProviderAgent.cpp 28 May 2008 07:35:16 -0000 1.14
[¤313]
+++ ProviderManagerService/ProviderAgent/ProviderAgent.cpp 3 Jun 2008 12:28:05 -0000
[¤314]
@@ -377,6 +377,9 @@
[¤315]
#if !defined(PEGASUS_USE_SYSLOGS)
[¤316]
Logger::setHomeDirectory(ConfigManager::getHomedPath(
[¤317]
configManager->getCurrentValue("logdir")));
[¤318]
+# if defined(PEGASUS_SEPARATE_OOP_LOG)
[¤319]
+ Logger::agentInit();
[¤320]
+# endif
[¤321]
#endif
[¤322]
System::bindVerbose = ipaRequest->bindVerbose;
[¤323]
[¤324]
Index: ProviderManagerService/ProviderAgent/cimprovagt.cpp
[¤325]
===================================================================
[¤326]
RCS file: /cvs/MSB/pegasus/src/Pegasus/ProviderManagerService/ProviderAgent/cimprovagt.cpp,v
[¤327]
retrieving revision 1.6
[¤328]
diff -u -r1.6 cimprovagt.cpp
[¤329]
--- ProviderManagerService/ProviderAgent/cimprovagt.cpp 8 May 2008 01:18:23 -0000 1.6
[¤330]
+++ ProviderManagerService/ProviderAgent/cimprovagt.cpp 3 Jun 2008 12:28:06 -0000
[¤331]
@@ -164,6 +164,12 @@
[¤332]
[¤333]
const char* moduleName = argv[5];
[¤334]
[¤335]
+#if defined(PEGASUS_SEPARATE_OOP_LOG) && !defined(PEGASUS_USE_SYSLOGS)
[¤336]
+ String agentName(moduleName);
[¤337]
+ agentName = agentName + "." + System::getEffectiveUserName();
[¤338]
+ Logger::setAgentName(agentName);
[¤339]
+#endif
[¤340]
+
[¤341]
try
[¤342]
{
[¤343]
AnonymousPipe pipeFromServer(argv[2], 0);
[¤344]
[¤345] Result of prototype
[¤346] We try the same test on prototype implementation. Here is the result:
[¤347] a) Set logLevel to INFORMATION, CIM Server did not have other work. A provider agent continuously wrote 200000 log entries, cost about 1453 clocks. While two provider agents wrote 100000 at same time cost about 1070 and 11012 clocks separately. This shows their response time got improved.
[¤348] b) Set logLevel to TRACE, a client repeatedly send "cimcli nc -n root/cimv2; cimconfig -g forceProcesses;". A provider agent continuously wrote 100000 log entries, cost about 1261 clocks. While two provider agents wrote 50000 at same time cost about 733 and 753 clocks separately.
[¤390] Discussion
[¤391]
[¤392] (r_kumpf) World writable directories are a source of security vulnerabilties. Careful attention is needed to protect such a directory from attack. Using the sticky bit is a good start. In cases where the names of the files that will be created are predictable (such as in this proposal), an attacker could create the file an advance with a different owner. That 'unauthorized' owner would be able to read the data that is subsequently written to the file. Or, an attacker could create a symbolic link to cause the data to be written in an unexpected (and harmful) place. It is best to avoid world-writable directories at all, if possible. Enabling writes to this directory by default heightens the concern.
[¤393] (ouyang_jian) A solution is to set log directory's permission to 755. Let CIM Server to create the log file, change its owner to designated user and change file permission to 600. Then start OOP provider. The drawback is designated user will not be able to remove the file but can change it. And the log file will always be created even if no log would be written from the OOP provider.
[¤394] (r_kumpf) PEP 312 was added under 'Bugs for 2.8.x Approval'. I guess it should have been here. It is closed for comment, though, so I'll add a comment here. Is performance important for the log file? Log entries of level INFORMATION and above are rarely written. TRACE entries are being reworked with PEP 315. Can you articulate the value of this enhancement if TRACE entries are not considered?
[¤395] (ouyang_jian) I agree performance it not so important. But for i5/PASE OOP providers defaultly run under non-supperuser context we do not want their log get lost. (They do log some OS version, HW info, compatibility etc. sometime)
[¤396]
[¤397]
Copyright (c) 2006 Hewlett-Packard Development Company, L.P.; IBM Corp.;
[¤398]
EMC Corporation; Symantec Corporation; The Open Group.
[¤399]
[¤400]
Permission is hereby granted, free of charge, to any person obtaining a copy
[¤401]
of this software and associated documentation files (the "Software"), to
[¤402]
deal in the Software without restriction, including without limitation the
[¤403]
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
[¤404]
sell copies of the Software, and to permit persons to whom the Software is
[¤405]
furnished to do so, subject to the following conditions:
[¤406]
[¤407]
THE ABOVE COPYRIGHT NOTICE AND THIS PERMISSION NOTICE SHALL BE INCLUDED IN
[¤408]
ALL COPIES OR SUBSTANTIAL PORTIONS OF THE SOFTWARE. THE SOFTWARE IS PROVIDED
[¤409]
"AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
[¤410]
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
[¤411]
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
[¤412]
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
[¤413]
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
[¤414]
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
[¤415]
[¤416] Template last modified: March 26th 2006 by Martin Kirk
[¤417]
Template version: 1.11