2012-02-19 12:53:41 +00:00
/ * * Enterprise Control Configuration and Logging
Copyright ( C ) 2012 Free Software Foundation , Inc .
Written by : Richard Frith - Macdonald < rfm @ gnu . org >
Date : Febrary 2010
Originally developed from 1996 to 2012 by Brainstorm , and donated to
the FSF .
This file is part of the GNUstep project .
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 ; either
version 3 of the License , or ( at your option ) any later version .
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
Library 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 02111 USA .
* /
2021-06-18 09:32:45 +00:00
# import < Foundation / Foundation . h >
2012-02-19 11:59:22 +00:00
# import < GNUstepBase / GSObjCRuntime . h >
2013-08-22 18:11:15 +00:00
# import < GNUstepBase / NSObject + GNUstepBase . h >
2024-12-03 17:21:08 +00:00
2021-06-18 09:32:45 +00:00
# if GS_USE _GNUTLS
# import < GNUstepBase / GSTLS . h >
2022-11-07 15:21:51 +00:00
static void
setupTLS ( NSUserDefaults * u )
{
# if ! defined ( TLS_DISTRIBUTED _OBJECTS )
if ( [ u boolForKey : @ "EncryptedDO" ] )
# endif
{
2022-11-09 10:34:04 +00:00
ENTER_POOL
2022-11-07 15:21:51 +00:00
/ * Enable encrypted DO if supported by the base library .
* /
if ( [ NSSocketPort respondsToSelector :
@ selector ( setClientOptionsForTLS : ) ] )
{
NSDictionary * d ;
2022-11-09 10:34:04 +00:00
if ( nil = = ( d = [ u dictionaryForKey : @ "ClientOptionsForTLS" ] )
&& nil = = ( d = [ u dictionaryForKey : @ "OptionsForTLS" ] ) )
2022-11-07 15:21:51 +00:00
{
2022-11-09 10:34:04 +00:00
d = [ NSDictionary dictionary ] ;
2022-11-07 15:21:51 +00:00
}
else
{
2022-11-09 10:34:04 +00:00
NSMutableDictionary * opts ;
id o ;
2022-11-07 15:21:51 +00:00
/ * If we were passed data rather than filenames
* we must set it up as cached data corresponding
* to well known names .
* /
opts = AUTORELEASE ( [ d mutableCopy ] ) ;
o = [ opts objectForKey : GSTLSCertificateKeyFile ] ;
if ( [ o isKindOfClass : [ NSData class ] ] )
{
[ GSTLSObject setData : o
2022-11-09 10:34:04 +00:00
forTLSFile : @ "distributed-objects-client-key" ] ;
[ opts setObject : @ "distributed-objects-client-key"
2022-11-07 15:21:51 +00:00
forKey : GSTLSCertificateKeyFile ] ;
}
o = [ opts objectForKey : GSTLSCertificateFile ] ;
if ( [ o isKindOfClass : [ NSData class ] ] )
{
[ GSTLSObject setData : o
2022-11-09 10:34:04 +00:00
forTLSFile : @ "distributed-objects-client-crt" ] ;
[ opts setObject : @ "distributed-objects-client-crt"
2022-11-07 15:21:51 +00:00
forKey : GSTLSCertificateFile ] ;
}
2022-11-09 10:34:04 +00:00
d = opts ;
}
[ NSSocketPort
performSelector : @ selector ( setClientOptionsForTLS : )
withObject : d ] ;
if ( nil = = ( d = [ u dictionaryForKey : @ "ServerOptionsForTLS" ] )
&& nil = = ( d = [ u dictionaryForKey : @ "OptionsForTLS" ] ) )
{
d = [ NSDictionary dictionary ] ;
}
else
{
NSMutableDictionary * opts ;
id o ;
/ * If we were passed data rather than filenames
* we must set it up as cached data corresponding
* to well known names .
* /
opts = AUTORELEASE ( [ d mutableCopy ] ) ;
o = [ opts objectForKey : GSTLSCertificateKeyFile ] ;
if ( [ o isKindOfClass : [ NSData class ] ] )
2022-11-07 15:21:51 +00:00
{
2022-11-09 10:34:04 +00:00
[ GSTLSObject setData : o
forTLSFile : @ "distributed-objects-server-key" ] ;
[ opts setObject : @ "distributed-objects-server-key"
forKey : GSTLSCertificateKeyFile ] ;
2022-11-07 15:21:51 +00:00
}
2022-11-09 10:34:04 +00:00
o = [ opts objectForKey : GSTLSCertificateFile ] ;
if ( [ o isKindOfClass : [ NSData class ] ] )
2022-11-07 15:21:51 +00:00
{
2022-11-09 10:34:04 +00:00
[ GSTLSObject setData : o
forTLSFile : @ "distributed-objects-server-crt" ] ;
[ opts setObject : @ "distributed-objects-server-crt"
forKey : GSTLSCertificateFile ] ;
2022-11-07 15:21:51 +00:00
}
2022-11-09 10:34:04 +00:00
d = opts ;
2022-11-07 15:21:51 +00:00
}
[ NSSocketPort
performSelector : @ selector ( setServerOptionsForTLS : )
2022-11-09 10:34:04 +00:00
withObject : d ] ;
2022-11-07 15:21:51 +00:00
}
2022-11-09 10:34:04 +00:00
LEAVE_POOL
2022-11-07 15:21:51 +00:00
}
}
# else
static void
setupTLS ( NSUserDefaults * u )
{
}
2021-06-18 09:32:45 +00:00
# endif
2012-02-19 11:59:22 +00:00
# import "EcProcess.h"
# import "EcLogger.h"
# import "EcAlarm.h"
# import "EcAlarmDestination.h"
# import "EcHost.h"
# import "EcUserDefaults.h"
# import "EcBroadcastProxy.h"
2015-07-15 08:41:14 +00:00
# import "EcMemoryLogger.h"
2012-02-19 11:59:22 +00:00
# include "config.h"
2022-11-04 12:18:47 +00:00
# if defined ( HAVE_LIBCRYPT )
extern char * crypt ( const char * key , const char * salt ) ;
# endif
2022-07-12 11:26:12 +00:00
# if HAVE_VALGRIND _VALGRIND _H
# include < valgrind / valgrind . h >
# else
# if HAVE_VALGRIND _H
# include < valgrind . h >
# endif
# endif
2012-02-19 11:59:22 +00:00
# ifdef HAVE_SYS _SIGNAL _H
# include < sys / signal . h >
# endif
# ifdef HAVE_SYS _FILE _H
# include < sys / file . h >
# endif
# ifdef HAVE_SYS _FCNTL _H
# include < sys / fcntl . h >
# endif
# ifdef HAVE_UNISTD _H
# include < unistd . h >
# endif
# ifdef HAVE_PWD _H
# include < pwd . h >
# endif
2012-02-19 14:42:50 +00:00
# ifdef HAVE_SYS _TIME _H
# include < sys / time . h >
# endif
# ifdef HAVE_SYS _RESOURCE _H
# include < sys / resource . h >
# endif
2015-09-16 12:24:34 +00:00
2018-03-02 11:04:25 +00:00
# if defined ( HAVE_TERMIOS _H )
# include < termios . h >
# endif
2015-09-16 12:24:34 +00:00
# if defined ( HAVE_GETTID )
# include < sys / syscall . h >
# include < sys / types . h >
# endif
2015-07-03 19:23:42 +00:00
# include < stdio . h >
2012-02-19 14:42:50 +00:00
2020-07-23 06:11:26 +00:00
NSString * const EcDidQuitNotification = @ "EcDidQuitNotification" ;
NSString * const EcWillQuitNotification = @ "EcWillQuitNotification" ;
2012-02-19 11:59:22 +00:00
2022-06-17 10:32:27 +00:00
static NSLock_error _handler * original_NSLock _error _handler = NULL ;
static void
EcLock_error _handler ( id obj , SEL _cmd , BOOL stop , NSString * msg )
{
if ( stop && EcProc )
{
EcAlarm * a ;
a = [ EcAlarm alarmForManagedObject : nil
at : nil
withEventType : EcAlarmEventTypeProcessingError
probableCause : EcAlarmSoftwareProgramError
2022-06-17 13:07:54 +00:00
specificProblem : @ "Deadlock detected in this process"
2022-06-17 10:32:27 +00:00
perceivedSeverity : EcAlarmSeverityCritical
proposedRepairAction :
2022-06-17 13:07:54 +00:00
_ ( @ "Please examine in gdb to determine the cause of the"
@ " deadlock if possible, then obtain a core dump for examination;"
@ " using 'kill -ABRT' for example" )
2022-06-17 10:32:27 +00:00
additionalText : [ obj description ] ] ;
[ EcProc alarm : a ] ;
}
( original_NSLock _error _handler ) ( obj , _cmd , stop , msg ) ;
}
2017-11-02 16:16:21 +00:00
# ifndef __MINGW __
static int reservedPipe [ 2 ] = { 0 , 0 } ;
static NSInteger descriptorsMaximum = 0 ;
# endif
2021-06-23 14:44:18 +00:00
/ * Flag to say whether a restart was caused by the mememory maximum
* /
static BOOL memRestart = NO ;
2012-02-19 11:59:22 +00:00
# if ! defined ( EC_DEFAULTS _PREFIX )
# define EC_DEFAULTS _PREFIX nil
# endif
# if ! defined ( EC_EFFECTIVE _USER )
2012-10-09 12:53:56 +00:00
# define EC_EFFECTIVE _USER nil
2012-02-19 11:59:22 +00:00
# endif
2015-09-16 12:24:34 +00:00
NSUInteger
ecNativeThreadID ( )
{
# if defined ( __MINGW __ )
return ( NSUInteger ) GetCurrentThreadId ( ) ;
# elif defined ( HAVE_GETTID )
return ( NSUInteger ) syscall ( SYS_gettid ) ;
# else
return NSNotFound ;
# endif
}
2014-11-02 14:30:24 +00:00
2018-03-03 18:36:01 +00:00
static NSString * const ecControlKey = @ "EcControlKey" ;
2018-03-02 11:04:25 +00:00
/ * Return the number of bytes represented by a hexadecimal string ( length / 2 )
* or the number of 8 bit characters if the string is not hexadecimal digits .
* If the string is hexadecimal , standardise o uppercase .
* /
static size_t
checkHex ( char * str )
{
const char * src = str ;
uint8_t * dst = ( uint8_t * ) str ;
size_t l ;
while ( * src )
{
if ( isxdigit ( * src ) )
{
if ( islower ( * src ) )
{
* dst = toupper ( * src ) ;
}
else
{
* dst = * src ;
}
dst + + ;
}
else if ( ! isspace ( * src ) )
{
* dst = ' \ 0 ' ;
return 0 ; // Bad character
}
src + + ;
}
* dst = ' \ 0 ' ;
l = ( ( char * ) dst ) - str ;
if ( l % 2 = = 1 )
{
return 0 ; // Not an even number of digits
}
return l / 2 ; // Return number of bytes represented
}
# if 0
static size_t
trim ( char * str )
{
size_t len = 0 ;
char * frontp = str - 1 ;
char * endp = NULL ;
if ( NULL = = str || ' \ 0 ' = = str [ 0 ] )
{
return 0 ;
}
len = strlen ( str ) ;
endp = str + len ;
while ( isspace ( * ( + + frontp ) ) )
;
while ( isspace ( * ( - - endp ) ) && endp ! = frontp )
;
if ( str + len - 1 ! = endp )
{
* + + endp = ' \ 0 ' ;
}
else if ( frontp ! = str && endp = = frontp )
{
* str = ' \ 0 ' ;
}
if ( frontp ! = str )
{
endp = str ;
while ( * frontp )
{
* endp + + = * frontp + + ;
}
* endp = ' \ 0 ' ;
}
return endp - str ;
}
# endif
2022-11-07 15:21:51 +00:00
2014-11-02 14:30:24 +00:00
@ interface EcDefaultRegistration : NSObject
{
NSString * name ; // The name / key of the default ( without prefix )
NSString * type ; // The type text for the default
NSString * help ; // The help text for the default
SEL cmd ; // method to update when default values change
id obj ; // The latest value of the default
2015-11-18 17:28:59 +00:00
id val ; // The fallback value of the default
2014-11-02 14:30:24 +00:00
}
+ ( void ) defaultsChanged : ( NSUserDefaults * ) defs ;
2018-01-19 09:50:43 +00:00
+ ( NSMutableString * ) listHelp : ( NSString * ) key ;
2015-11-18 17:28:59 +00:00
+ ( NSDictionary * ) merge : ( NSDictionary * ) d ;
2014-11-02 14:30:24 +00:00
+ ( void ) registerDefault : ( NSString * ) name
withTypeText : ( NSString * ) type
andHelpText : ( NSString * ) help
2015-11-18 17:28:59 +00:00
action : ( SEL ) cmd
value : ( id ) value ;
2014-11-02 14:30:24 +00:00
+ ( void ) showHelp ;
@ end
2012-02-19 11:59:22 +00:00
/ * Lock for controlling access to per - process singleton instance .
* /
2012-03-09 09:22:09 +00:00
static NSRecursiveLock * ecLock = nil ;
2012-02-19 11:59:22 +00:00
2018-01-04 11:23:29 +00:00
static NSString * configError = nil ;
static BOOL configInProgress = NO ;
2012-02-19 11:59:22 +00:00
static BOOL cmdFlagDaemon = NO ;
static BOOL cmdFlagTesting = NO ;
2014-05-16 22:13:43 +00:00
static BOOL cmdIsRunning = NO ;
2020-04-04 10:03:26 +00:00
static BOOL cmdIsRegistered = NO ;
2015-03-26 11:33:02 +00:00
static BOOL cmdKeepStderr = NO ;
2019-03-01 13:00:09 +00:00
static BOOL cmdKillDebug = NO ;
2015-07-21 09:30:24 +00:00
static NSString * cmdBase = nil ;
2012-02-19 11:59:22 +00:00
static NSString * cmdInst = nil ;
static NSString * cmdName = nil ;
static NSString * cmdUser = nil ;
static NSUserDefaults * cmdDefs = nil ;
static NSString * cmdDebugName = nil ;
static NSMutableDictionary * cmdLogMap = nil ;
2015-07-15 08:41:14 +00:00
static id < EcMemoryLogger > cmdMemoryLogger = nil ;
2020-03-23 13:35:58 +00:00
static NSMutableArray * ecConfigClients = nil ;
2012-02-19 11:59:22 +00:00
2014-11-26 08:59:50 +00:00
static NSDate * started = nil ; / * Time object was created . * /
2015-07-09 12:08:58 +00:00
static NSDate * memStats = nil ; / * Time stats were started . * /
2014-11-26 08:59:50 +00:00
static NSTimeInterval lastIP = 0.0 ; / * Time of last input to object . * /
static NSTimeInterval lastOP = 0.0 ; / * Time of last output by object . * /
2012-02-19 11:59:22 +00:00
static Class cDateClass = 0 ;
static Class dateClass = 0 ;
static Class stringClass = 0 ;
2012-02-19 14:42:50 +00:00
static int cmdSignalled = 0 ;
2012-02-19 11:59:22 +00:00
2023-03-08 14:43:48 +00:00
static NSDictionary * ecOperators = nil ; // Operator configuration
2017-10-26 14:12:33 +00:00
static NSTimeInterval initAt = 0.0 ;
2018-06-24 14:37:35 +00:00
/ * Internal value for use only by quitting mechanism .
2017-11-02 09:19:57 +00:00
* /
2018-01-04 13:32:42 +00:00
static NSTimeInterval beganQuitting = 0.0 ; // Start of orderly shutdown
2019-07-12 09:56:04 +00:00
static BOOL ecQuitHandled = NO ; // Has ecHandleQuit run ?
2019-07-15 09:10:05 +00:00
static NSTimeInterval ecQuitLimit = 180.0 ; // Time allowed for quit
2023-01-21 19:45:49 +00:00
static NSInteger ecQuitStatus = 0 ; // Status for the quit
2018-06-24 14:37:35 +00:00
static NSString * ecQuitReason = nil ; // Reason for the quit
2017-11-02 09:19:57 +00:00
2019-07-12 09:56:04 +00:00
/ * Test to see if the process is trying to quit gracefully .
2017-11-02 09:19:57 +00:00
* If quitting has taken over three minutes , abort immediately .
* /
static BOOL
ecIsQuitting ( )
{
if ( 0.0 = = beganQuitting )
{
return NO ;
}
NSTimeInterval now = [ NSDate timeIntervalSinceReferenceDate ] ;
2019-07-15 09:10:05 +00:00
if ( now - beganQuitting >= ecQuitLimit )
2017-11-02 09:19:57 +00:00
{
NSLog ( @ "abort: quitting took too long (after %g sec)\n" ,
( now - beganQuitting ) ) ;
signal ( SIGABRT , SIG_DFL ) ;
abort ( ) ;
}
return YES ;
}
2012-02-19 11:59:22 +00:00
static RETSIGTYPE
ihandler ( int sig )
{
static BOOL beenHere = NO ;
signal ( sig , ihandler ) ;
if ( NO = = beenHere )
{
beenHere = YES ;
signal ( SIGABRT , SIG_DFL ) ;
abort ( ) ;
}
exit ( sig ) ;
# if RETSIGTYPE ! = void
return 0 ;
# endif
}
static RETSIGTYPE
qhandler ( int sig )
{
2013-08-20 09:05:39 +00:00
if ( SIGHUP = = sig )
{
static int hupCount = 0 ;
/ * We allow multiple HUP signals since , while shutting down we may
* attempt to write out messages to our terminal , generating more
* signals , and we want to ignore those and shut down cleanly .
* /
if ( hupCount + + < 1000 )
{
cmdSignalled = 0 ; // Allow signal to be set .
}
}
2012-02-19 11:59:22 +00:00
/ * We store the signal value in a global variable and return to normal
2013-08-19 09:05:07 +00:00
* processing . . . that way later code can check on the state of the
2012-02-19 11:59:22 +00:00
* variable and take action outside the handler .
* We can ' t act immediately here inside the handler as the signal may
* have interrupted some vital library ( eg malloc ( ) ) and left it in a
* state such that our code can ' t continue . For instance if we try to
* cleanup after a signal and call free ( ) , the process may hang waiting
2015-07-03 19:23:42 +00:00
* for a lock that the interupted function still holds .
2012-02-19 11:59:22 +00:00
* /
2012-02-19 14:42:50 +00:00
if ( 0 = = cmdSignalled )
2012-02-19 11:59:22 +00:00
{
2013-08-20 09:05:39 +00:00
cmdSignalled = sig ; // Record signal for event loop .
2012-02-19 11:59:22 +00:00
}
else
{
static BOOL beenHere = NO ;
2013-08-20 09:05:39 +00:00
/ * We have been signalled more than once . . . so let ' s try to
* crash rather than continuing .
* /
2012-02-19 11:59:22 +00:00
if ( NO = = beenHere )
{
beenHere = YES ;
signal ( SIGABRT , SIG_DFL ) ;
abort ( ) ;
}
2013-08-20 09:05:39 +00:00
exit ( cmdSignalled ) ; // Exit with * first * signal number
2012-02-19 11:59:22 +00:00
}
# if RETSIGTYPE ! = void
return 0 ;
# endif
}
NSString *
cmdVersion ( NSString * ver )
{
2013-08-22 18:11:15 +00:00
static NSString * version = @ "1997-2013" ;
2012-02-19 11:59:22 +00:00
if ( ver ! = nil )
{
ASSIGNCOPY ( version , ver ) ;
}
return version ;
}
static NSString * homeDir = nil ;
NSString *
cmdHomeDir ( )
{
return homeDir ;
}
void
cmdSetHome ( NSString * home )
{
ASSIGNCOPY ( homeDir , home ) ;
}
2015-02-23 14:42:27 +00:00
static NSString * logsDir = nil ;
NSString *
ecLogsSubdirectory ( )
{
return logsDir ;
}
void
ecSetLogsSubdirectory ( NSString * pathComponent )
{
ASSIGNCOPY ( logsDir , pathComponent ) ;
}
2012-02-19 11:59:22 +00:00
static NSString * userDir = nil ;
static NSString *
cmdUserDir ( )
{
if ( userDir = = nil )
return NSHomeDirectoryForUser ( cmdUser ) ;
else
return userDir ;
}
static NSString *
cmdSetUserDirectory ( NSString * dir )
{
if ( dir = = nil )
{
dir = NSHomeDirectoryForUser ( cmdUser ) ;
}
else if ( [ dir isAbsolutePath ] = = NO )
{
dir = [ NSHomeDirectoryForUser ( cmdUser )
stringByAppendingPathComponent : dir ] ;
}
ASSIGNCOPY ( userDir , dir ) ;
return userDir ;
}
static NSString * dataDir = nil ;
2018-03-02 11:04:25 +00:00
/ * Return the current data directory .
* Create the directory path if necessary .
* /
NSString *
cmdDataDir ( )
{
if ( dataDir = = nil )
{
NSFileManager * mgr = [ NSFileManager defaultManager ] ;
NSString * str = cmdUserDir ( ) ;
BOOL flag ;
if ( [ mgr fileExistsAtPath : str isDirectory : & flag ] = = NO )
{
if ( [ mgr createDirectoryAtPath : str
withIntermediateDirectories : YES
attributes : nil
error : NULL ] = = NO )
{
if ( [ mgr fileExistsAtPath : str isDirectory : & flag ] = = NO )
{
NSLog ( @ "Unable to create directory - %@" , str ) ;
return nil ;
}
}
else
{
flag = YES ;
}
}
if ( flag = = NO )
{
NSLog ( @ "The path '%@' is not a directory" , str ) ;
return nil ;
}
str = [ str stringByAppendingPathComponent : @ "Data" ] ;
if ( [ mgr fileExistsAtPath : str isDirectory : & flag ] = = NO )
{
if ( [ mgr createDirectoryAtPath : str
withIntermediateDirectories : YES
attributes : nil
error : NULL ] = = NO )
{
if ( [ mgr fileExistsAtPath : str isDirectory : & flag ] = = NO )
{
NSLog ( @ "Unable to create directory - %@" , str ) ;
return nil ;
}
}
else
{
flag = YES ;
}
}
if ( flag = = NO )
{
NSLog ( @ "The path '%@' is not a directory" , str ) ;
return nil ;
}
if ( homeDir ! = nil )
{
str = [ str stringByAppendingPathComponent : homeDir ] ;
if ( [ mgr fileExistsAtPath : str isDirectory : & flag ] = = NO )
{
if ( [ mgr createDirectoryAtPath : str
withIntermediateDirectories : YES
attributes : nil
error : NULL ] = = NO )
{
if ( [ mgr fileExistsAtPath : str isDirectory : & flag ] = = NO )
{
NSLog ( @ "Unable to create directory - %@" , str ) ;
return nil ;
}
}
else
{
flag = YES ;
}
}
if ( flag = = NO )
{
NSLog ( @ "The path '%@' is not a directory" , str ) ;
return nil ;
}
}
ASSIGNCOPY ( dataDir , str ) ;
}
return dataDir ;
}
/ * Return the current logging directory - if ' today ' is not nil , treat it as
2012-02-19 11:59:22 +00:00
* the name of a subdirectory in which todays logs should be archived .
* Create the directory path if necessary .
* /
NSString *
cmdLogsDir ( NSString * date )
{
NSFileManager * mgr = [ NSFileManager defaultManager ] ;
NSString * str = cmdUserDir ( ) ;
2015-02-23 14:42:27 +00:00
NSString * component ;
2012-02-19 11:59:22 +00:00
BOOL flag ;
if ( [ mgr fileExistsAtPath : str isDirectory : & flag ] = = NO )
{
2012-08-10 08:52:08 +00:00
if ( [ mgr createDirectoryAtPath : str
withIntermediateDirectories : YES
attributes : nil
error : NULL ] = = NO )
2012-02-19 11:59:22 +00:00
{
if ( [ mgr fileExistsAtPath : str isDirectory : & flag ] = = NO )
{
NSLog ( @ "Unable to create directory - %@" , str ) ;
return nil ;
}
}
else
{
flag = YES ;
}
}
if ( flag = = NO )
{
NSLog ( @ "The path '%@' is not a directory" , str ) ;
return nil ;
}
2015-02-23 14:42:27 +00:00
component = ecLogsSubdirectory ( ) ;
if ( nil = = component )
{
component = @ "DebugLogs" ;
}
str = [ str stringByAppendingPathComponent : component ] ;
2012-02-19 11:59:22 +00:00
if ( [ mgr fileExistsAtPath : str isDirectory : & flag ] = = NO )
{
2012-08-10 08:52:08 +00:00
if ( [ mgr createDirectoryAtPath : str
withIntermediateDirectories : YES
attributes : nil
error : NULL ] = = NO )
2012-02-19 11:59:22 +00:00
{
if ( [ mgr fileExistsAtPath : str isDirectory : & flag ] = = NO )
{
NSLog ( @ "Unable to create directory - %@" , str ) ;
return nil ;
}
}
else
{
flag = YES ;
}
}
if ( flag = = NO )
{
NSLog ( @ "The path '%@' is not a directory" , str ) ;
return nil ;
}
if ( date ! = nil )
{
str = [ str stringByAppendingPathComponent : date ] ;
if ( [ mgr fileExistsAtPath : str isDirectory : & flag ] = = NO )
{
2012-08-10 08:52:08 +00:00
if ( [ mgr createDirectoryAtPath : str
withIntermediateDirectories : YES
attributes : nil
error : NULL ] = = NO )
2012-02-19 11:59:22 +00:00
{
if ( [ mgr fileExistsAtPath : str isDirectory : & flag ] = = NO )
{
NSLog ( @ "Unable to create directory - %@" , str ) ;
return nil ;
}
}
else
{
flag = YES ;
}
}
if ( flag = = NO )
{
NSLog ( @ "The path '%@' is not a directory" , str ) ;
return nil ;
}
}
if ( homeDir ! = nil )
{
str = [ str stringByAppendingPathComponent : homeDir ] ;
if ( [ mgr fileExistsAtPath : str isDirectory : & flag ] = = NO )
{
2012-08-10 08:52:08 +00:00
if ( [ mgr createDirectoryAtPath : str
withIntermediateDirectories : YES
attributes : nil
error : NULL ] = = NO )
2012-02-19 11:59:22 +00:00
{
if ( [ mgr fileExistsAtPath : str isDirectory : & flag ] = = NO )
{
NSLog ( @ "Unable to create directory - %@" , str ) ;
return nil ;
}
}
else
{
flag = YES ;
}
}
if ( flag = = NO )
{
NSLog ( @ "The path '%@' is not a directory" , str ) ;
return nil ;
}
}
return str ;
}
NSString *
cmdLogKey ( EcLogType t )
{
switch ( t )
{
case LT_DEBUG : return @ "Debug" ;
case LT_WARNING : return @ "Warn" ;
case LT_ERROR : return @ "Error" ;
case LT_AUDIT : return @ "Audit" ;
case LT_ALERT : return @ "Alert" ;
2018-07-30 20:26:41 +00:00
case LT_CONSOLE : return @ "Audit" ; // Console messages are audited
2012-02-19 11:59:22 +00:00
default : return @ "UnknownLogType" ;
}
}
NSString *
cmdLogName ( )
{
2012-05-08 16:08:30 +00:00
static NSString * cmdLogName = nil ;
2012-02-19 11:59:22 +00:00
2012-05-08 16:08:30 +00:00
if ( nil = = cmdLogName )
2012-02-19 11:59:22 +00:00
{
2012-05-08 16:08:30 +00:00
[ ecLock lock ] ;
if ( nil = = cmdLogName )
2012-02-19 11:59:22 +00:00
{
2018-03-02 11:04:25 +00:00
NSString * n = cmdName ;
2012-05-08 16:08:30 +00:00
if ( nil = = n )
{
n = [ [ NSProcessInfo processInfo ] processName ] ;
}
cmdLogName = [ n copy ] ;
2012-02-19 11:59:22 +00:00
}
2012-05-08 16:08:30 +00:00
[ ecLock unlock ] ;
2012-02-19 11:59:22 +00:00
}
2012-05-08 16:08:30 +00:00
return cmdLogName ;
2012-02-19 11:59:22 +00:00
}
NSString *
cmdLogFormat ( EcLogType t , NSString * fmt )
{
static NSString * h = nil ;
NSCalendarDate * c = [ [ cDateClass alloc ] init ] ;
NSString * f = cmdLogKey ( t ) ;
NSString * n = cmdLogName ( ) ;
NSString * d ;
NSString * result ;
if ( h = = nil )
{
2012-05-08 16:08:30 +00:00
h = [ [ [ NSHost currentHost ] wellKnownName ] copy ] ;
2012-02-19 11:59:22 +00:00
}
2015-05-07 06:44:22 +00:00
d = [ c descriptionWithCalendarFormat : @ "%Y-%m-%d %H:%M:%S.%F %z" locale : nil ] ;
2012-02-19 11:59:22 +00:00
result = [ stringClass stringWithFormat : @ "%@(%@): %@ %@ - %@\n" ,
n , h , d , f , fmt ] ;
RELEASE ( c ) ;
return result ;
}
EcProcess * EcProc = nil ;
2012-10-12 17:20:45 +00:00
static NSConnection * EcProcConnection = nil ;
2012-02-19 11:59:22 +00:00
static EcAlarmDestination * alarmDestination = nil ;
static EcLogger * alertLogger = nil ;
static EcLogger * auditLogger = nil ;
static EcLogger * debugLogger = nil ;
static EcLogger * errorLogger = nil ;
static EcLogger * warningLogger = nil ;
static NSMutableSet * cmdActions = nil ;
static id cmdServer = nil ;
static id cmdPTimer = nil ;
2012-11-23 12:45:09 +00:00
static NSDictionary * cmdConf = nil ;
2012-02-19 11:59:22 +00:00
static NSDate * cmdFirst = nil ;
static NSDate * cmdLast = nil ;
static BOOL cmdIsTransient = NO ;
static NSMutableSet * cmdDebugModes = nil ;
static NSMutableDictionary * cmdDebugKnown = nil ;
static NSMutableString * replyBuffer = nil ;
static SEL cmdTimSelector = 0 ;
static NSTimeInterval cmdTimInterval = 60.0 ;
static NSMutableArray * noNetConfig = nil ;
static NSMutableDictionary * servers = nil ;
2013-04-30 08:13:45 +00:00
static int coreSize = -2 ; // Not yet set
2013-04-30 06:57:37 +00:00
2025-01-08 14:49:32 +00:00
static BOOL __hasLSAN = NO ;
static BOOL __setLSAN = NO ;
int __lsan _do _recoverable _leak _check ( void ) __attribute __ ( ( weak ) ) ;
static int ( * ecLeakCheck ) ( void ) = __lsan _do _recoverable _leak _check ;
static BOOL
hasLSAN ( )
{
if ( NO = = __setLSAN )
{
if ( ecLeakCheck )
{
__hasLSAN = YES ;
}
__setLSAN = YES ;
}
return __hasLSAN ;
}
2012-02-19 11:59:22 +00:00
static NSString * hostName = nil ;
2012-05-08 16:08:30 +00:00
static NSString *
ecHostName ( )
{
NSString * name ;
[ ecLock lock ] ;
2012-10-05 14:40:19 +00:00
if ( nil = = hostName )
{
hostName = [ [ [ NSHost currentHost ] wellKnownName ] retain ] ;
}
2012-05-08 16:08:30 +00:00
name = [ hostName retain ] ;
[ ecLock unlock ] ;
return [ name autorelease ] ;
}
2012-02-19 11:59:22 +00:00
2023-04-07 11:09:24 +00:00
static NSString * fullName = nil ;
NSString *
ecFullName ( )
{
NSString * name ;
[ ecLock lock ] ;
if ( nil = = fullName )
{
fullName = [ [ NSString alloc ] initWithFormat : @ "%@:%@" ,
ecHostName ( ) , cmdLogName ( ) ] ;
}
name = [ fullName retain ] ;
[ ecLock unlock ] ;
return [ name autorelease ] ;
}
2019-08-11 12:35:10 +00:00
static EcAlarmSeverity memAlarm = EcAlarmSeverityMajor ;
2019-06-01 14:44:32 +00:00
static NSString * memType = nil ;
2021-06-04 15:10:01 +00:00
static NSString * memUnit = @ "KB" ;
static int memSize = 1024 ; // Report KB
static NSTimeInterval memTime = 0.0 ; // Time of last check
2015-07-05 11:39:18 +00:00
static uint64_t memMaximum = 0 ;
static uint64_t memAllowed = 0 ;
2023-01-21 19:32:56 +00:00
static uint64_t memInitial = 0 ;
2015-07-13 10:02:31 +00:00
static uint64_t excAvge = 0 ; // current period average
2015-07-07 15:11:34 +00:00
static uint64_t memAvge = 0 ; // current period average
2015-07-13 10:02:31 +00:00
static uint64_t excStrt = 0 ; // excluded usage at first check
static uint64_t memStrt = 0 ; // total usage at first check
static uint64_t excLast = 0 ; // excluded usage at last check
static uint64_t memLast = 0 ; // total usage at last check
static uint64_t excPrev = 0 ; // excluded usage at previous warning
static uint64_t memPrev = 0 ; // total usage at previous warning
static uint64_t excPeak = 0 ; // excluded peak usage
static uint64_t memPeak = 0 ; // total peak usage
2019-08-10 09:19:16 +00:00
static uint64_t memBase = 0 ; // base memory threshold
static uint64_t memWarn = 0 ; // warning alarm threshold
static uint64_t memMinr = 0 ; // minor alarm threshold
static uint64_t memMajr = 0 ; // major alarm threshold
static uint64_t memCrit = 0 ; // critical aarm threshold
2015-07-07 15:11:34 +00:00
static uint64_t memSlot = 0 ; // minute counter
2015-07-13 10:02:31 +00:00
static uint64_t excRoll [ 10 ] ; // last N values
2015-07-07 15:11:34 +00:00
static uint64_t memRoll [ 10 ] ; // last N values
2015-07-03 19:23:42 +00:00
# define MEMCOUNT ( sizeof ( memRoll ) / sizeof ( * memRoll ) )
2012-02-19 11:59:22 +00:00
2019-08-11 12:35:10 +00:00
static NSString *
setMemAlarm ( NSString * str )
{
if ( nil = = str ) str = @ "Major" ;
if ( [ str caseInsensitiveCompare : @ "critical" ] = = NSOrderedSame )
{
memAlarm = EcAlarmSeverityCritical ;
}
else if ( [ str caseInsensitiveCompare : @ "major" ] = = NSOrderedSame )
{
memAlarm = EcAlarmSeverityMajor ;
}
else if ( [ str caseInsensitiveCompare : @ "minor" ] = = NSOrderedSame )
{
memAlarm = EcAlarmSeverityMinor ;
}
else if ( [ str caseInsensitiveCompare : @ "warning" ] = = NSOrderedSame )
{
memAlarm = EcAlarmSeverityWarning ;
}
else
{
memAlarm = EcAlarmSeverityMajor ;
}
return [ EcAlarm stringFromSeverity : memAlarm ] ;
}
2019-08-10 09:19:16 +00:00
static void
setMemBase ( )
{
if ( 0 = = memAllowed )
{
if ( 0 = = memBase || memSlot < MEMCOUNT )
{
2019-08-11 12:35:10 +00:00
/ * Base must be 20 % larger than peak over first ten minutes .
* /
memBase = ( memPeak * 120 ) / 100 ;
/ * The base memory must be at least half the maximum memory .
* /
if ( memMaximum > 0 && memBase < memMaximum * 1024 * 512 )
{
memBase = memMaximum * 1024 * 512 ;
}
2019-08-10 09:19:16 +00:00
}
}
else
{
memBase = memAllowed * 1024 * 1024 ;
}
if ( memMaximum > 0 )
{
uint64_t max = memMaximum * 1024 * 1024 ;
if ( max >= memBase + 1024 )
{
uint64_t band = ( max - memBase ) / 4 ;
memWarn = memBase ;
memMinr = memWarn + band ;
memMajr = memMinr + band ;
memCrit = memMajr + band ;
}
else
{
memWarn = memMinr = memMajr = memCrit = memBase ;
}
}
else
{
memWarn = memMinr = memMajr = memCrit = 0 ;
}
}
2012-02-19 11:59:22 +00:00
2023-03-08 14:43:48 +00:00
/ * Returns the found command , or nil if none is found , or an empty string
* if there was a match but it was in the array of commands to be blockd .
* /
2012-02-19 11:59:22 +00:00
static NSString *
2023-03-10 16:01:19 +00:00
findAction ( NSString * cmd , NSArray * allow )
2012-02-19 11:59:22 +00:00
{
2012-07-05 13:15:27 +00:00
NSString * found = nil ;
2023-03-08 14:43:48 +00:00
BOOL match = NO ;
2012-07-05 13:15:27 +00:00
2012-02-19 11:59:22 +00:00
cmd = [ cmd lowercaseString ] ;
2012-07-05 13:15:27 +00:00
[ ecLock lock ] ;
2023-03-08 14:43:48 +00:00
if ( nil = = ( found = [ cmdActions member : cmd ] )
2023-03-10 16:01:19 +00:00
|| ( allow && NO = = [ allow containsObject : found ] ) )
2012-02-19 11:59:22 +00:00
{
NSEnumerator * enumerator ;
NSString * name ;
2023-03-08 14:43:48 +00:00
if ( found )
{
found = nil ;
match = YES ;
}
2012-02-19 11:59:22 +00:00
enumerator = [ cmdActions objectEnumerator ] ;
while ( nil ! = ( name = [ enumerator nextObject ] ) )
{
if ( YES = = [ name hasPrefix : cmd ] )
{
2023-03-08 14:43:48 +00:00
match = YES ;
2023-03-10 16:01:19 +00:00
if ( allow && NO = = [ allow containsObject : name ] )
2023-03-08 14:43:48 +00:00
{
2023-03-10 16:01:19 +00:00
continue ; // This match is not allowed
2023-03-08 14:43:48 +00:00
}
else if ( nil = = found )
2012-02-19 11:59:22 +00:00
{
found = name ;
}
else
{
2012-07-05 13:15:27 +00:00
found = nil ; // Ambiguous
break ;
2012-02-19 11:59:22 +00:00
}
}
}
}
2023-03-08 14:43:48 +00:00
if ( found )
{
cmd = [ found retain ] ;
}
else if ( match )
{
cmd = @ "" ;
}
else
{
cmd = nil ;
}
2012-07-05 13:15:27 +00:00
[ ecLock unlock ] ;
return [ cmd autorelease ] ;
2012-02-19 11:59:22 +00:00
}
static NSString *
2012-03-09 09:22:09 +00:00
ecCommandHost ( )
2012-02-19 11:59:22 +00:00
{
NSString * host ;
host = [ cmdDefs stringForKey : @ "CommandHost" ] ;
if ( nil = = host )
{
host = @ "" ; / * Local host * /
}
return host ;
}
static NSString *
2012-03-09 09:22:09 +00:00
ecCommandName ( )
2012-02-19 11:59:22 +00:00
{
NSString * name ;
name = [ cmdDefs stringForKey : @ "CommandName" ] ;
if ( nil = = name )
{
name = @ "Command" ;
}
return name ;
}
2015-10-14 11:25:48 +00:00
NSString * cmdBasicDbg = @ "basicMode" ;
2015-10-14 11:30:35 +00:00
NSString * cmdDefaultDbg = @ "basicMode" ; // Allow older code to link
2012-02-19 11:59:22 +00:00
NSString * cmdConnectDbg = @ "connectMode" ;
NSString * cmdDetailDbg = @ "detailMode" ;
static int comp_len = 0 ;
static int
comp ( const char * s0 , const char * s1 )
{
comp_len = 0 ;
if ( s0 = = 0 ) {
s0 = "" ;
}
if ( s1 = = 0 ) {
s1 = "" ;
}
while ( * s0 ) {
if ( * s0 ! = * s1 ) {
char c0 = islower ( * s0 ) ? toupper ( * s0 ) : * s0 ;
char c1 = islower ( * s1 ) ? toupper ( * s1 ) : * s1 ;
if ( c0 ! = c1 ) {
if ( c0 ! = ' \ 0 ' ) {
comp_len = -1 ; / * s0 is not a substring of s1 . * /
}
return ( -1 ) ;
}
}
comp_len + + ;
s0 + + ;
s1 + + ;
}
if ( * s0 ! = * s1 ) {
return ( -1 ) ;
}
return ( 0 ) ;
}
static NSString *
findMode ( NSDictionary * d , NSString * s )
{
NSArray * a = [ d allKeys ] ;
NSString * o ;
unsigned int i ;
const char * s0 = [ s UTF8String ] ;
const char * s1 ;
int best_pos = -1 ;
int best_len = 0 ;
for ( i = 0 ; i < [ a count ] ; i + + )
{
o = ( NSString * ) [ a objectAtIndex : i ] ;
s1 = [ o UTF8String ] ;
if ( comp ( s0 , s1 ) = = 0 )
{
return o ;
}
if ( comp_len > best_len )
{
best_len = comp_len ;
best_pos = i ;
}
}
if ( best_pos >= 0 )
{
return ( NSString * ) [ a objectAtIndex : best_pos ] ;
}
return nil ;
}
/ *
* Auxiliary object representing a remote server a subclass might need
* to connect to . This class is for EcProcess . m internal use .
* /
@ interface RemoteServer : NSObject
{
/ * This is the string which identifies this server * /
NSString * defaultName ;
/ * These are the actual name and host for this server , as obtained
by configuration for the ` defaultName ' server * /
NSString * name ;
NSString * host ;
/ * The same for multiple servers * /
NSArray * multiple ;
/ * The real object representing the remote server . * /
id proxy ;
/ * An object responding to cmdMadeConnectionToServer : and / or
cmdLostConnectionToServer : * /
id delegate ;
}
/ * Initialize the object - string is the default server name * /
- ( id ) initWithDefaultName : ( NSString * ) string
delegate : ( id ) object ;
- ( NSString * ) defaultName ;
- ( void ) setName : ( NSString * ) string ;
- ( NSString * ) name ;
- ( void ) setHost : ( NSString * ) string ;
- ( void ) setMultiple : ( NSArray * ) config ;
- ( NSArray * ) multiple ;
/ *
* Return a proxy to the remote server ; create one if needed by making
* a connection , using name and host .
* If the server is multiple , create a EcBroadcastProxy object , and returns
* that object .
* /
- ( id ) proxy ;
/ *
* Internal connection management methods
* /
- ( id ) connectionBecameInvalid : ( NSNotification * ) notification ;
- ( BOOL ) connection : ( NSConnection * ) ancestor
shouldMakeNewConnection : ( NSConnection * ) newConn ;
2012-10-12 14:59:15 +00:00
- ( void ) BCP : ( EcBroadcastProxy * ) proxy
lostConnectionToServer : ( NSString * ) name
host : ( NSString * ) host ;
- ( void ) BCP : ( EcBroadcastProxy * ) proxy
madeConnectionToServer : ( NSString * ) name
host : ( NSString * ) host ;
2012-02-19 11:59:22 +00:00
/ *
* Returns YES if the connection is ALIVE , NO if the connection is DEAD
* /
- ( BOOL ) isConnected ;
- ( NSString * ) description ;
- ( void ) update ;
@ end
@ implementation RemoteServer
- ( id ) initWithDefaultName : ( NSString * ) string
delegate : ( id ) object
{
self = [ super init ] ;
if ( self ! = nil )
{
ASSIGNCOPY ( defaultName , string ) ;
ASSIGN ( name , defaultName ) ;
host = @ "*" ;
multiple = nil ;
proxy = nil ;
delegate = object ;
/ *
* Grab configuration information .
* /
[ self update ] ;
}
return self ;
}
- ( void ) dealloc
{
DESTROY ( defaultName ) ;
DESTROY ( name ) ;
DESTROY ( host ) ;
DESTROY ( multiple ) ;
DESTROY ( proxy ) ;
[ [ NSNotificationCenter defaultCenter ] removeObserver : self ] ;
[ super dealloc ] ;
}
- ( NSString * ) defaultName
{
return defaultName ;
}
- ( void ) setName : ( NSString * ) string
{
if ( [ name isEqual : string ] = = NO )
{
ASSIGNCOPY ( name , string ) ;
DESTROY ( proxy ) ;
}
}
- ( NSString * ) name
{
return name ;
}
- ( void ) setHost : ( NSString * ) string
{
if ( [ host isEqual : string ] = = NO )
{
ASSIGNCOPY ( host , string ) ;
DESTROY ( proxy ) ;
}
}
- ( NSString * ) host
{
return host ;
}
- ( void ) setMultiple : ( NSArray * ) config
{
if ( [ multiple isEqual : config ] = = NO )
{
ASSIGNCOPY ( multiple , config ) ;
DESTROY ( proxy ) ;
}
}
- ( NSArray * ) multiple
{
return multiple ;
}
- ( id ) proxy
{
if ( nil = = proxy )
{
if ( nil = = multiple )
{
[ EcProc cmdDbg : cmdConnectDbg
msg : @ "Looking for service %@ on host %@" , name , host ] ;
proxy = [ NSConnection rootProxyForConnectionWithRegisteredName : name
host : host
usingNameServer : [ NSSocketPortNameServer sharedInstance ] ] ;
if ( proxy ! = nil )
{
id connection = [ proxy connectionForProxy ] ;
RETAIN ( proxy ) ;
[ connection setDelegate : self ] ;
[ [ NSNotificationCenter defaultCenter ]
addObserver : self
selector : @ selector ( connectionBecameInvalid : )
name : NSConnectionDidDieNotification
object : connection ] ;
if ( [ delegate respondsToSelector :
@ selector ( cmdMadeConnectionToServer : ) ] = = YES )
{
[ delegate cmdMadeConnectionToServer : defaultName ] ;
}
[ EcProc cmdDbg : cmdConnectDbg
msg : @ "Connected to %@ server on host %@" ,
name , host ] ;
}
else
{
[ EcProc cmdDbg : cmdConnectDbg
msg : @ "Failed to contact %@ server on host %@" ,
name , host ] ;
}
}
else / * a multiple server * /
{
proxy = [ [ EcBroadcastProxy alloc ] initWithReceivers : multiple ] ;
[ proxy BCPsetDelegate : self ] ;
}
}
return proxy ;
}
- ( id ) connectionBecameInvalid : ( NSNotification * ) notification
{
id connection = [ notification object ] ;
[ [ NSNotificationCenter defaultCenter ]
removeObserver : self
name : NSConnectionDidDieNotification
object : connection ] ;
if ( [ connection isKindOfClass : [ NSConnection class ] ] )
{
2012-10-12 18:07:34 +00:00
if ( connection = = [ proxy connectionForProxy ] )
2012-02-19 11:59:22 +00:00
{
[ EcProc cmdDbg : cmdConnectDbg
msg : @ "lost connection - clearing %@." ,
name ] ;
if ( [ delegate respondsToSelector :
@ selector ( cmdLostConnectionToServer : ) ] = = YES )
{
[ delegate cmdLostConnectionToServer : defaultName ] ;
}
RELEASE ( proxy ) ;
proxy = nil ;
}
}
else
{
[ self error : "non-Connection sent invalidation" ] ;
}
return self ;
}
/ * Debugging purposes only * /
- ( BOOL ) connection : ( NSConnection * ) ancestor
shouldMakeNewConnection : ( NSConnection * ) newConn
{
[ EcProc cmdDbg : cmdConnectDbg
2023-04-24 16:45:52 +00:00
msg : @ "New connection %p created" , newConn ] ;
2012-02-19 11:59:22 +00:00
return YES ;
}
- ( BOOL ) isConnected
{
if ( proxy ! = nil )
{
return YES ;
}
else
{
return NO ;
}
}
- ( NSString * ) description
{
if ( multiple = = nil )
{
NSString * status ;
if ( proxy ! = nil )
{
status = @ "LIVE" ;
}
else
{
status = @ "DEAD" ;
}
return [ NSString stringWithFormat :
@ "Connection to server `%@' on host `%@' is %@" ,
name , host , status ] ;
}
else / * multiple server * /
{
if ( proxy = = nil )
{
return [ NSString stringWithFormat :
@ "Multiple connection to servers %@\n"
@ " has not yet been initialized" ,
multiple ] ;
}
else
{
return [ proxy BCPstatus ] ;
}
}
}
- ( void ) BCP : ( EcBroadcastProxy * ) proxy
lostConnectionToServer : ( NSString * ) name
host : ( NSString * ) host
{
if ( [ delegate respondsToSelector :
@ selector ( cmdLostConnectionToServer : ) ] = = YES )
{
/ * FIXME : How do we inform delegate of this ? Is it of any use ? * /
// [ delegate cmdLostConnectionToServer : defaultName ] ;
}
}
- ( void ) BCP : ( EcBroadcastProxy * ) proxy
madeConnectionToServer : ( NSString * ) name
host : ( NSString * ) host
{
if ( [ delegate respondsToSelector :
@ selector ( cmdLostConnectionToServer : ) ] = = YES )
{
/ * FIXME : How do we inform delegate of this ? Is it of any use ? * /
// [ delegate cmdMadConnectionToServer : defaultName ] ;
}
}
- ( void ) update
{
NSString * configKey ;
id configValue ;
configKey = [ defaultName stringByAppendingString : @ "Name" ] ;
configValue = [ cmdDefs stringForKey : configKey ] ;
if ( nil ! = configValue )
{
[ self setName : configValue ] ;
}
configKey = [ defaultName stringByAppendingString : @ "Host" ] ;
configValue = [ cmdDefs stringForKey : configKey ] ;
if ( nil ! = configValue )
{
[ self setHost : configValue ] ;
}
configKey = [ defaultName stringByAppendingString : @ "BroadCast" ] ;
configValue = [ cmdDefs arrayForKey : configKey ] ;
if ( nil ! = configValue )
{
[ self setMultiple : configValue ] ;
}
}
@ end
2014-11-02 14:30:24 +00:00
@ interface EcProcess ( Defaults )
- ( void ) _defMemory : ( id ) val ;
- ( void ) _defRelease : ( id ) val ;
- ( void ) _defTesting : ( id ) val ;
@ end
2012-02-19 11:59:22 +00:00
@ interface EcProcess ( Private )
- ( void ) cmdMesgrelease : ( NSArray * ) msg ;
- ( void ) cmdMesgtesting : ( NSArray * ) msg ;
2015-07-08 09:06:26 +00:00
- ( void ) _memCheck ;
2017-06-23 10:26:17 +00:00
- ( NSString * ) _moveLog : ( NSString * ) name to : ( NSDate * ) when ;
2012-02-19 11:59:22 +00:00
- ( void ) _timedOut : ( NSTimer * ) timer ;
2012-11-23 12:45:09 +00:00
- ( void ) _update : ( NSMutableDictionary * ) info ;
2012-02-19 11:59:22 +00:00
@ end
@ implementation EcProcess
2013-08-22 18:11:15 +00:00
+ ( void ) atExit
{
if ( [ NSObject shouldCleanUp ] )
{
DESTROY ( EcProc ) ;
DESTROY ( EcProcConnection ) ;
DESTROY ( alarmDestination ) ;
DESTROY ( alertLogger ) ;
DESTROY ( auditLogger ) ;
DESTROY ( cmdActions ) ;
DESTROY ( cmdConf ) ;
DESTROY ( cmdDebugKnown ) ;
DESTROY ( cmdDebugModes ) ;
DESTROY ( cmdDebugName ) ;
DESTROY ( cmdDefs ) ;
DESTROY ( cmdFirst ) ;
DESTROY ( cmdInst ) ;
DESTROY ( cmdLast ) ;
DESTROY ( cmdLogMap ) ;
DESTROY ( cmdName ) ;
DESTROY ( cmdPTimer ) ;
DESTROY ( cmdServer ) ;
DESTROY ( cmdUser ) ;
DESTROY ( dataDir ) ;
DESTROY ( debugLogger ) ;
DESTROY ( ecLock ) ;
DESTROY ( errorLogger ) ;
DESTROY ( homeDir ) ;
DESTROY ( hostName ) ;
DESTROY ( noNetConfig ) ;
DESTROY ( replyBuffer ) ;
DESTROY ( servers ) ;
DESTROY ( started ) ;
DESTROY ( userDir ) ;
DESTROY ( warningLogger ) ;
2015-07-15 08:41:14 +00:00
DESTROY ( cmdMemoryLogger ) ;
2013-08-22 18:11:15 +00:00
}
}
2015-07-15 08:41:14 +00:00
2018-03-02 11:04:25 +00:00
- ( Class ) _memoryLoggerClassFromBundle : ( NSString * ) bundleName
2015-07-15 08:41:14 +00:00
{
NSString * path = nil ;
Class c = Nil ;
NSBundle * bundle = nil ;
NSArray * paths =
NSSearchPathForDirectoriesInDomains ( NSLibraryDirectory ,
NSAllDomainsMask ,
YES ) ;
NSEnumerator * e = [ paths objectEnumerator ] ;
while ( nil ! = ( path = [ e nextObject ] ) )
{
path = [ path stringByAppendingPathComponent : @ "Bundles" ] ;
path = [ path stringByAppendingPathComponent : bundleName ] ;
path = [ path stringByAppendingPathExtension : @ "bundle" ] ;
bundle = [ NSBundle bundleWithPath : path ] ;
if ( bundle ! = nil )
{
break ;
}
}
if ( nil = = bundle )
{
[ self cmdWarn : @ "Could not load bundle '%@'" , bundleName ] ;
}
else if ( Nil = = ( c = [ bundle principalClass ] ) )
{
[ self cmdWarn : @ "Could not load principal class from %@ at %@." ,
bundleName , path ] ;
}
else if ( NO = = [ c conformsToProtocol : @ protocol ( EcMemoryLogger ) ] )
{
[ self cmdWarn :
@ "%@ does not implement the EcMemoryLogger protocol" ,
NSStringFromClass ( c ) ] ;
c = Nil ;
}
return c ;
}
2018-03-02 11:04:25 +00:00
+ ( NSString * ) ecGetKey : ( const char * ) name
size : ( unsigned ) size
md5 : ( NSData * ) digest
{
struct termios old ;
struct termios new ;
char * one = NULL ;
char * two = NULL ;
FILE * stream ;
NSString * key ;
if ( size < 16 ) size = 16 ;
if ( size > 128 ) size = 128 ;
/ * Open the terminal
* /
if ( ( stream = fopen ( "/dev/tty" , "r+" ) ) = = NULL )
{
return nil ;
}
/ * Turn echoing off
* /
if ( tcgetattr ( fileno ( stream ) , & old ) ! = 0 )
{
fclose ( stream ) ;
return nil ;
}
new = old ;
new . c_lflag & = ~ ECHO ;
if ( tcsetattr ( fileno ( stream ) , TCSAFLUSH , & new ) ! = 0 )
{
fclose ( stream ) ;
return nil ;
}
while ( NULL = = one || NULL = = two )
{
int olen = 0 ;
int tlen = 0 ;
while ( olen ! = size )
{
size_t len = 0 ;
fprintf ( stream , "\nPlease enter %s: " , name ) ;
if ( one ! = NULL ) { free ( one ) ; one = NULL ; }
olen = getline ( & one , & len , stream ) ;
if ( olen < 0 )
{
if ( one ! = NULL ) { free ( one ) ; one = NULL ; }
fclose ( stream ) ;
return nil ;
}
olen = checkHex ( one ) ;
if ( olen ! = size )
{
fprintf ( stream , "\n%s must be %u hexadecimal digits.\n" , name ,
2018-10-24 10:28:27 +00:00
size * 2 ) ;
2018-03-02 11:04:25 +00:00
olen = 0 ;
}
else if ( nil ! = digest )
{
CREATE_AUTORELEASE _POOL ( pool ) ;
2021-01-14 09:14:32 +00:00
NSString * s ;
2018-03-02 11:04:25 +00:00
NSData * d ;
NSData * md5 ;
s = [ NSString stringWithUTF8String : one ] ;
d = [ [ NSData alloc ] initWithHexadecimalRepresentation : s ] ;
md5 = [ d md5Digest ] ;
RELEASE ( d ) ;
if ( [ digest isEqual : md5 ] )
{
/ * If the digest of the key matches the expected value ,
* we assume entry was correct and set two to be the
* same as one so we will not prompt for a confirmation .
* /
two = malloc ( len + 1 ) ;
strcpy ( two , one ) ;
tlen = olen ;
}
DESTROY ( pool ) ;
}
}
while ( 0 = = tlen )
{
size_t len = 0 ;
fprintf ( stream , "\nPlease re-enter %s to confirm: " , name ) ;
if ( two ! = NULL ) { free ( two ) ; two = NULL ; }
tlen = getline ( & two , & len , stream ) ;
if ( tlen < 0 )
{
if ( one ! = NULL ) { free ( one ) ; one = NULL ; }
if ( two ! = NULL ) { free ( two ) ; two = NULL ; }
fclose ( stream ) ;
return nil ;
}
tlen = checkHex ( two ) ;
if ( tlen ! = size )
{
fprintf ( stream , "\n%s must be %u hexadecimal digits.\n" , name ,
2018-10-24 10:28:27 +00:00
size * 2 ) ;
2018-03-02 11:04:25 +00:00
tlen = 0 ;
}
}
if ( strcmp ( one , two ) ! = 0 )
{
free ( one ) ; one = NULL ;
free ( two ) ; two = NULL ;
fprintf ( stream ,
"\nThe strings you entered do not match, please try again." ) ;
}
}
/ * Restore terminal . * /
( void ) tcsetattr ( fileno ( stream ) , TCSAFLUSH , & old ) ;
key = [ NSString stringWithUTF8String : one ] ;
free ( one ) ;
free ( two ) ;
fprintf ( stream , "\n%s accepted.\n" , name ) ;
fclose ( stream ) ;
return key ;
}
2015-07-15 08:41:14 +00:00
2014-04-28 06:40:03 +00:00
+ ( NSMutableDictionary * ) ecInitialDefaults
{
2015-01-26 12:50:42 +00:00
NSProcessInfo * pi ;
2014-04-28 06:40:03 +00:00
id objects [ 2 ] ;
id keys [ 2 ] ;
2015-01-26 12:50:42 +00:00
pi = [ NSProcessInfo processInfo ] ;
objects [ 0 ] = [ pi processName ] ;
2014-04-28 06:40:03 +00:00
objects [ 1 ] = @ "." ;
2020-11-17 10:30:03 +00:00
keys [ 0 ] = @ "ProgramName" ;
keys [ 1 ] = @ "HomeDirectory" ;
2014-04-28 06:40:03 +00:00
return [ NSMutableDictionary dictionaryWithObjects : objects
forKeys : keys
count : 2 ] ;
}
2018-03-02 11:04:25 +00:00
+ ( NSDictionary * ) ecPrepareWithDefaults : ( NSDictionary * ) defs
2015-11-18 17:28:59 +00:00
{
2018-03-02 11:04:25 +00:00
static BOOL prepared = NO ;
2014-11-02 14:30:24 +00:00
2018-03-02 11:04:25 +00:00
[ ecLock lock ] ;
if ( NO = = prepared )
2014-07-30 10:44:08 +00:00
{
2018-03-02 11:04:25 +00:00
NSProcessInfo * pinfo ;
NSFileManager * mgr ;
NSEnumerator * enumerator ;
NSString * str ;
NSString * dbg ;
NSString * prf ;
BOOL flag ;
2013-11-04 07:28:57 +00:00
2018-03-02 11:04:25 +00:00
started = RETAIN ( [ dateClass date ] ) ;
2013-11-04 07:28:57 +00:00
2018-03-02 11:04:25 +00:00
pinfo = [ NSProcessInfo processInfo ] ;
mgr = [ NSFileManager defaultManager ] ;
prf = EC_DEFAULTS _PREFIX ;
if ( nil = = prf )
{
prf = @ "" ;
}
2013-11-04 07:28:57 +00:00
2020-07-03 10:50:25 +00:00
ASSIGN ( cmdDefs , [ NSUserDefaults userDefaultsWithPrefix : prf ] ) ;
2018-03-02 11:04:25 +00:00
defs = [ EcDefaultRegistration merge : defs ] ;
2020-05-18 11:34:43 +00:00
[ cmdDefs registerDefaults : defs ] ;
2012-02-19 11:59:22 +00:00
2021-06-17 14:27:39 +00:00
/ * When a process is launched by the Command server it should have
* the desired name set using - LaunchedAs name in the arguments .
* In this case it is also expected to have a property list dictionary
* provided on STDIN preceded by a count specifying the number of bytes
* of property list data . The count is a four byte value in network
* byte order .
* /
if ( nil ! = ( str = [ cmdDefs stringForKey : @ "LaunchedAs" ] ) )
{
NSFileHandle * fh = [ NSFileHandle fileHandleWithStandardInput ] ;
NSData * d = [ fh readDataOfLength : 4 ] ;
uint32_t l = 0 ;
if ( d )
{
uint32_t b ;
memcpy ( & b , [ d bytes ] , 4 ) ;
l = GSSwapBigI32ToHost ( b ) ;
}
d = [ fh readDataOfLength : l ] ;
NS_DURING
{
defs = [ NSPropertyListSerialization propertyListFromData : d
mutabilityOption : 0
format : NULL
errorDescription : NULL ] ;
}
NS_HANDLER
{
defs = nil ;
NSLog ( @ "Unable to de-serialize property list from STDIN" ) ;
}
NS_ENDHANDLER
if ( [ defs count ] > 0 )
{
NSMutableDictionary * m ;
m = AUTORELEASE ( [ [ cmdDefs volatileDomainForName : NSArgumentDomain ]
mutableCopy ] ) ;
[ m addEntriesFromDictionary : defs ] ;
[ cmdDefs removeVolatileDomainForName : NSArgumentDomain ] ;
[ cmdDefs setVolatileDomain : m forName : NSArgumentDomain ] ;
}
}
2022-11-07 15:21:51 +00:00
setupTLS ( cmdDefs ) ;
2018-03-02 11:04:25 +00:00
cmdUser = EC_EFFECTIVE _USER ;
if ( nil = = cmdUser )
{
cmdUser = [ [ cmdDefs stringForKey : @ "EffectiveUser" ] retain ] ;
}
if ( YES = = [ cmdUser isEqual : @ "*" ]
|| YES = = [ cmdUser isEqualToString : NSUserName ( ) ] )
{
ASSIGN ( cmdUser , NSUserName ( ) ) ;
}
else if ( [ cmdUser length ] = = 0 )
{
NSLog ( @ "This software is not configured to run as any user.\n"
@ "You may use the EffectiveUser user default setting"
@ " to specify the user (setting this to an asterisk ('*')"
@ " allows the software to run as any user). Alternatively"
@ " an EC_EFFECTIVE_USER can be defined when the ec library"
@ " is built." ) ;
exit ( 1 ) ;
}
else
{
const char * user = [ cmdUser UTF8String ] ;
struct passwd * pwd = getpwnam ( user ) ;
int uid ;
2015-07-21 09:30:24 +00:00
2018-03-02 11:04:25 +00:00
if ( pwd ! = 0 )
{
uid = pwd -> pw_uid ;
}
else
{
NSLog ( @ "This software is configured to run as the user '%@',"
@ " but there does not appear to be any such user." , cmdUser ) ;
if ( [ cmdUser isEqual : EC_EFFECTIVE _USER ] )
{
NSLog ( @ "You may use the EffectiveUser user default setting"
@ " to override the user (setting this to an asterisk ('*')"
@ " allows the software to run as any user). Alternatively"
@ " a different EC_EFFECTIVE_USER can be defined when the"
@ " ec library is built." ) ;
}
exit ( 1 ) ;
}
2012-02-19 11:59:22 +00:00
2018-03-02 11:04:25 +00:00
if ( uid ! = ( int ) geteuid ( ) )
{
if ( geteuid ( ) = = 0 || ( int ) getuid ( ) = = uid )
{
if ( 0 ! = setuid ( uid ) )
{
[ ecLock unlock ] ;
NSLog ( @ "You must be '%@' to run this." , cmdUser ) ;
exit ( 1 ) ;
}
}
else
{
[ ecLock unlock ] ;
NSLog ( @ "You must be '%@' to run this." , cmdUser ) ;
exit ( 1 ) ;
}
}
GSSetUserName ( cmdUser ) ;
if ( NO = = [ cmdUser isEqualToString : NSUserName ( ) ] )
{
[ ecLock unlock ] ;
NSLog ( @ "You must be '%@' to run this." , cmdUser ) ;
exit ( 1 ) ;
}
2020-07-03 10:50:25 +00:00
ASSIGN ( cmdDefs , [ NSUserDefaults userDefaultsWithPrefix : prf ] ) ;
2018-03-02 11:04:25 +00:00
if ( defs ! = nil )
{
[ cmdDefs registerDefaults : defs ] ;
}
}
/ * See if we should keep stderr separate , or merge it with
* our debug output ( the default ) .
2019-03-01 13:00:09 +00:00
* In addition , we can kill debug output ( and standard error
* if it ' s not kept separate ) by sending it to / dev / null
2018-03-02 11:04:25 +00:00
* /
cmdKeepStderr = [ cmdDefs boolForKey : @ "KeepStandardError" ] ;
2019-03-01 13:00:09 +00:00
cmdKillDebug = [ cmdDefs boolForKey : @ "KillDebugOutput" ] ;
2018-03-02 11:04:25 +00:00
if ( nil = = noNetConfig )
{
noNetConfig = [ [ NSMutableArray alloc ] initWithCapacity : 4 ] ;
[ noNetConfig
addObject : [ prf stringByAppendingString : @ "Daemon" ] ] ;
[ noNetConfig
addObject : [ prf stringByAppendingString : @ "EffectiveUser" ] ] ;
[ noNetConfig
addObject : [ prf stringByAppendingString : @ "Instance" ] ] ;
[ noNetConfig
addObject : [ prf stringByAppendingString : @ "Transient" ] ] ;
}
defs = [ cmdDefs dictionaryRepresentation ] ;
enumerator = [ defs keyEnumerator ] ;
dbg = [ prf stringByAppendingString : @ "Debug-" ] ;
while ( ( str = [ enumerator nextObject ] ) ! = nil )
{
2020-07-08 15:48:26 +00:00
NSString * key = nil ;
if ( [ str hasPrefix : @ "Debug-" ] )
{
key = [ str substringFromIndex : 6 ] ;
str = [ prf stringByAppendingString : str ] ;
}
else if ( [ str hasPrefix : dbg ] )
{
key = [ str substringFromIndex : [ dbg length ] ] ;
}
if ( key ! = nil )
2018-03-02 11:04:25 +00:00
{
id obj = [ defs objectForKey : str ] ;
if ( [ cmdDebugKnown objectForKey : key ] = = nil )
{
[ cmdDebugKnown setObject : key forKey : key ] ;
}
if ( [ obj isKindOfClass : stringClass ] )
{
if ( [ obj intValue ] ! = 0
|| [ obj isEqual : @ "YES" ] || [ obj isEqual : @ "yes" ] )
{
if ( [ cmdDebugModes member : key ] = = nil )
{
[ cmdDebugModes addObject : key ] ;
}
}
else
{
if ( [ cmdDebugModes member : key ] ! = nil )
{
[ cmdDebugModes removeObject : key ] ;
}
}
}
}
}
/ * See if we have a name specified for this process .
* /
ASSIGN ( cmdName , [ cmdDefs stringForKey : @ "ProgramName" ] ) ;
2012-02-19 14:42:50 +00:00
2018-03-02 11:04:25 +00:00
/ * If there ' s no ProgramName specified , but this is a Control server ,
* try looking for the ControlName instead .
* /
if ( nil = = cmdName
&& Nil ! = NSClassFromString ( @ "EcControl" )
&& YES = = [ self isSubclassOfClass : NSClassFromString ( @ "EcControl" ) ] )
{
ASSIGN ( cmdName , [ cmdDefs stringForKey : @ "ControlName" ] ) ;
}
/ * If there ' s no ProgramName specified , but this is a Command server ,
* try looking for the CommandName instead .
* /
if ( nil = = cmdName
&& Nil ! = NSClassFromString ( @ "EcCommand" )
&& YES = = [ self isSubclassOfClass : NSClassFromString ( @ "EcCommand" ) ] )
{
ASSIGN ( cmdName , [ cmdDefs stringForKey : @ "CommandName" ] ) ;
}
/ * Finally , if no name is given at all , use the standard process name .
* /
if ( nil = = cmdName )
{
ASSIGN ( cmdName , [ pinfo processName ] ) ;
}
/ * This is the base name of the process ( without instance )
* /
if ( nil = = cmdBase )
{
ASSIGN ( cmdBase , cmdName ) ;
}
/ *
* Make sure our users home directory exists .
* /
str = [ cmdDefs objectForKey : @ "UserDirectory" ] ;
str = cmdSetUserDirectory ( str ) ;
2012-02-19 14:42:50 +00:00
if ( [ mgr fileExistsAtPath : str isDirectory : & flag ] = = NO )
{
2012-08-10 08:52:08 +00:00
if ( [ mgr createDirectoryAtPath : str
withIntermediateDirectories : YES
attributes : nil
error : NULL ] = = NO )
2012-02-19 14:42:50 +00:00
{
if ( [ mgr fileExistsAtPath : str isDirectory : & flag ] = = NO )
{
2018-03-02 11:04:25 +00:00
[ ecLock unlock ] ;
2012-02-19 14:42:50 +00:00
NSLog ( @ "Unable to create directory - %@" , str ) ;
2018-03-02 11:04:25 +00:00
exit ( 1 ) ;
2012-02-19 14:42:50 +00:00
}
}
else
{
flag = YES ;
}
}
if ( flag = = NO )
{
2018-03-02 11:04:25 +00:00
[ ecLock unlock ] ;
2012-02-19 14:42:50 +00:00
NSLog ( @ "The path '%@' is not a directory" , str ) ;
2018-03-02 11:04:25 +00:00
exit ( 1 ) ;
2012-02-19 14:42:50 +00:00
}
2018-03-02 11:04:25 +00:00
str = [ cmdDefs objectForKey : @ "HomeDirectory" ] ;
if ( str ! = nil )
{
if ( [ str length ] = = 0 )
{
str = nil ;
}
else if ( [ str isAbsolutePath ] = = YES )
{
NSLog ( @ "Absolute HomeDirectory ignored." ) ;
str = nil ;
}
cmdSetHome ( str ) ;
}
str = [ [ cmdDefs stringForKey : @ "Instance" ] stringByTrimmingSpaces ] ;
if ( nil ! = str )
{
if ( [ str length ] > 0 && isdigit ( [ str characterAtIndex : 0 ] ) )
{
str = [ NSString stringWithFormat : @ "%d" , [ str intValue ] ] ;
}
else
{
str = nil ;
}
}
ASSIGN ( cmdInst , str ) ;
if ( nil ! = cmdInst )
{
str = [ [ NSString alloc ] initWithFormat : @ "%@-%@" , cmdName , cmdInst ] ;
ASSIGN ( cmdName , str ) ;
[ str release ] ;
}
str = userDir ;
if ( cmdHomeDir ( ) ! = nil )
{
str = [ str stringByAppendingPathComponent : cmdHomeDir ( ) ] ;
}
str = [ str stringByStandardizingPath ] ;
2012-02-19 14:42:50 +00:00
if ( [ mgr fileExistsAtPath : str isDirectory : & flag ] = = NO )
{
2012-08-10 08:52:08 +00:00
if ( [ mgr createDirectoryAtPath : str
withIntermediateDirectories : YES
attributes : nil
error : NULL ] = = NO )
2012-02-19 14:42:50 +00:00
{
if ( [ mgr fileExistsAtPath : str isDirectory : & flag ] = = NO )
{
2018-03-02 11:04:25 +00:00
[ ecLock unlock ] ;
2012-02-19 14:42:50 +00:00
NSLog ( @ "Unable to create directory - %@" , str ) ;
2018-03-02 11:04:25 +00:00
exit ( 1 ) ;
2012-02-19 14:42:50 +00:00
}
}
else
{
flag = YES ;
}
}
if ( flag = = NO )
{
2018-03-02 11:04:25 +00:00
[ ecLock unlock ] ;
2012-02-19 14:42:50 +00:00
NSLog ( @ "The path '%@' is not a directory" , str ) ;
2018-03-02 11:04:25 +00:00
exit ( 1 ) ;
2012-02-19 14:42:50 +00:00
}
2018-03-02 11:04:25 +00:00
if ( [ mgr changeCurrentDirectoryPath : str ] = = NO )
2012-02-19 14:42:50 +00:00
{
2018-03-02 11:04:25 +00:00
[ ecLock unlock ] ;
NSLog ( @ "Unable to move to directory - %@" , str ) ;
exit ( 1 ) ;
}
/ *
* Make sure the data directory exists .
* /
if ( cmdDataDir ( ) = = nil )
{
[ ecLock unlock ] ;
NSLog ( @ "Unable to create/access data directory" ) ;
exit ( 1 ) ;
}
/ *
* Make sure the logs directory exists .
* /
if ( cmdLogsDir ( nil ) = = nil )
{
[ ecLock unlock ] ;
NSLog ( @ "Unable to create/access logs directory" ) ;
exit ( 1 ) ;
2012-02-19 14:42:50 +00:00
}
2018-03-02 11:04:25 +00:00
[ [ NSProcessInfo processInfo ] setProcessName : cmdName ] ;
prepared = YES ;
}
[ ecLock unlock ] ;
return defs ;
}
+ ( void ) ecRegisterDefault : ( NSString * ) name
withTypeText : ( NSString * ) type
andHelpText : ( NSString * ) help
action : ( SEL ) cmd
{
[ EcDefaultRegistration registerDefault : name
withTypeText : type
andHelpText : help
action : cmd
value : nil ] ;
}
+ ( void ) ecRegisterDefault : ( NSString * ) name
withTypeText : ( NSString * ) type
andHelpText : ( NSString * ) help
action : ( SEL ) cmd
value : ( id ) value
{
[ EcDefaultRegistration registerDefault : name
withTypeText : type
andHelpText : help
action : cmd
value : value ] ;
}
+ ( void ) ecSetup
{
if ( nil ! = EcProc )
{
[ NSException raise : NSGenericException
format : @ "+ecSetup called when EcProcess is already set up" ] ;
2012-02-19 14:42:50 +00:00
}
2021-01-14 09:14:32 +00:00
( void ) [ [ self alloc ] init ] ;
2018-03-02 11:04:25 +00:00
}
- ( void ) _commandRemove
{
id connection = [ cmdServer connectionForProxy ] ;
if ( nil ! = connection )
{
[ connection setDelegate : nil ] ;
[ [ NSNotificationCenter defaultCenter ]
removeObserver : self
name : NSConnectionDidDieNotification
object : connection ] ;
[ connection invalidate ] ;
}
DESTROY ( cmdServer ) ;
2020-04-04 10:03:26 +00:00
cmdIsRegistered = NO ;
2018-03-02 11:04:25 +00:00
}
- ( void ) _connectionRegistered
{
2020-04-04 10:03:26 +00:00
cmdIsRegistered = YES ;
2018-03-02 11:04:25 +00:00
[ alarmDestination domanage : nil ] ;
}
static NSString * noFiles = @ "No log files to archive" ;
- ( NSString * ) cmdBase
{
return cmdBase ;
}
- ( id ) cmdConfig : ( NSString * ) key
{
return [ cmdDefs objectForKey : key ] ;
}
- ( NSString * ) cmdDataDirectory
{
return cmdDataDir ( ) ;
2012-02-19 14:42:50 +00:00
}
2012-02-19 11:59:22 +00:00
- ( NSUserDefaults * ) cmdDefaults
{
return cmdDefs ;
}
2018-01-04 11:23:29 +00:00
/ * This method handles the final stage of a configuration update either
* from the Control server or via the local NSUserDefaults system .
2020-03-23 13:35:58 +00:00
* If no error has occurred so far , we call the method to check / apply
2018-01-04 11:23:29 +00:00
* the updated config .
* If an error occurs at any stage , we reset the error string and call
* the method to report it .
* /
- ( void ) _checkUpdate
{
NSString * err ;
if ( nil = = configError )
{
NS_DURING
ASSIGN ( configError , [ self cmdUpdated ] ) ;
NS_HANDLER
NSLog ( @ "Problem after updating config (in cmdUpdated) %@" ,
localException ) ;
ASSIGN ( configError , @ "the -cmdUpdated method raised an exception" ) ;
NS_ENDHANDLER
}
err = AUTORELEASE ( configError ) ;
configError = nil ;
/ * NB . if err is nil this will clear any currently raised alarm
* /
2019-07-05 15:55:23 +00:00
[ self ecConfigurationError : @ "%@" , ( nil = = err ) ? @ "" : err ] ;
2020-03-23 13:35:58 +00:00
/ * Forward new config to any listening clients .
* Remove any clients to which forwarding failed .
* /
if ( [ ecConfigClients count ] > 0 )
{
NSDictionary * d = [ cmdDefs dictionaryRepresentation ] ;
NSMutableArray * a ;
NSUInteger c ;
[ self ecDoLock ] ;
2020-03-23 13:40:51 +00:00
a = [ ecConfigClients mutableCopy ] ;
2020-03-23 13:35:58 +00:00
[ self ecUnLock ] ;
c = [ a count ] ;
while ( c - - > 0 )
{
id < EcConfigForwarded > client = [ a objectAtIndex : c ] ;
id o = ( id ) client ;
if ( YES = = [ o respondsToSelector : @ selector ( connectionForProxy ) ]
&& NO = = [ [ o connectionForProxy ] isValid ] )
{
continue ; // Skip send on invalid DO connection
}
NS_DURING
[ client ecForwardedConfig : d from : self ] ;
[ a removeObjectAtIndex : c ] ;
NS_HANDLER
EcExceptionMajor ( localException , @ "Failed to forward config" ) ;
NS_ENDHANDLER
}
if ( ( c = [ a count ] ) > 0 )
{
[ self ecDoLock ] ;
while ( c - - > 0 )
{
[ ecConfigClients removeObjectIdenticalTo : [ a objectAtIndex : c ] ] ;
}
[ self ecUnLock ] ;
}
RELEASE ( a ) ;
}
2018-01-04 11:23:29 +00:00
}
/ * This method is called when the defaults database is updated for any
* reason and also if the configuration from the Control server changes .
* In the latter case , the notification argument is nil .
* If no error has occurred , we call - cmdDefaultsChanged :
* After this is done , we check that the update is OK ( on the next runloop
* iteration in the main thread ) . The async processing ensures that all
* handling of defaults database notifications has been done before we
* check the effects of the update .
* /
- ( void ) _defaultsChanged : ( NSNotification * ) n
{
if ( YES = = configInProgress )
{
return ; // Ignore defaults updates during configuration update .
}
2018-01-04 11:30:24 +00:00
if ( YES = = ecIsQuitting ( ) )
{
NSLog ( @ "NSUserDefaults change during process shutdown ... ignored." ) ;
return ; // Ignore defaults changes during shutdown .
}
2018-01-04 11:23:29 +00:00
if ( nil = = configError )
{
NS_DURING
[ self cmdDefaultsChanged : n ] ;
NS_HANDLER
NSLog ( @ "Problem in cmdDefaultsChanged:) %@" , localException ) ;
ASSIGN ( configError ,
@ "the -cmdDefaultsChanged: method raised an exception" ) ;
NS_ENDHANDLER
}
[ self performSelectorOnMainThread : @ selector ( _checkUpdate )
withObject : nil
waitUntilDone : NO ] ;
}
2018-06-24 14:37:35 +00:00
/ * This method is only ever run asynchronously in the main thread .
* /
- ( void ) _ecQuit
{
if ( ecDeferQuit > 0 )
{
/ * Some method needs to complete in this ( the main ) thread
* before we can quit . Try again in a short time .
* /
[ self performSelector : _cmd
withObject : nil
afterDelay : 0.01 ] ;
}
else
{
[ self ecHandleQuit ] ;
}
}
2012-02-19 11:59:22 +00:00
- ( void ) cmdDefaultsChanged : ( NSNotification * ) n
{
2012-11-23 12:45:09 +00:00
NSEnumerator * enumerator ;
2012-02-19 11:59:22 +00:00
NSDictionary * dict ;
NSString * mode ;
2012-11-23 12:45:09 +00:00
NSString * str ;
2013-04-30 06:57:37 +00:00
int i ;
2012-02-19 11:59:22 +00:00
2014-11-02 14:30:24 +00:00
[ EcDefaultRegistration defaultsChanged : cmdDefs ] ;
2019-03-14 14:32:59 +00:00
/ * Update debug output kill status if necessary .
* /
if ( [ cmdDefs boolForKey : @ "KillDebugOutput" ] ! = cmdKillDebug )
{
NSFileHandle * hdl ;
[ ecLock lock ] ;
hdl = [ cmdLogMap objectForKey : cmdDebugName ] ;
if ( hdl ! = nil )
{
if ( cmdKillDebug = = NO )
{
NSString * msg ;
msg = cmdLogFormat ( LT_WARNING ,
@ "Logging suppressed by KillDebugOutput=YES" ) ;
[ hdl writeData : [ msg dataUsingEncoding : NSUTF8StringEncoding ] ] ;
}
[ self ecLogEnd : cmdDebugName to : nil ] ;
}
[ ecLock unlock ] ;
cmdKillDebug = ( NO = = cmdKillDebug ? YES : NO ) ;
[ self cmdLogFile : cmdDebugName ] ;
}
2012-11-23 12:45:09 +00:00
enumerator = [ cmdDebugKnown keyEnumerator ] ;
2012-02-19 11:59:22 +00:00
while ( nil ! = ( mode = [ enumerator nextObject ] ) )
{
NSString * key = [ @ "Debug-" stringByAppendingString : mode ] ;
if ( YES = = [ cmdDefs boolForKey : key ] )
{
[ cmdDebugModes addObject : mode ] ;
}
else
{
[ cmdDebugModes removeObject : mode ] ;
}
}
dict = [ cmdDefs dictionaryForKey : @ "WellKnownHostNames" ] ;
if ( nil ! = dict )
{
[ NSHost setWellKnownNames : dict ] ;
2012-10-05 14:40:19 +00:00
[ ecLock lock ] ;
2012-05-08 16:08:30 +00:00
ASSIGN ( hostName , [ [ NSHost currentHost ] wellKnownName ] ) ;
2012-10-05 14:40:19 +00:00
[ ecLock unlock ] ;
2012-02-19 11:59:22 +00:00
}
2012-11-23 12:45:09 +00:00
if ( ( str = [ cmdDefs stringForKey : @ "CmdInterval" ] ) ! = nil )
{
[ self setCmdInterval : [ str floatValue ] ] ;
}
2014-11-01 09:18:02 +00:00
# ifndef __MINGW __
descriptorsMaximum = [ cmdDefs integerForKey : @ "DescriptorsMaximum" ] ;
# endif
2025-01-08 14:49:32 +00:00
if ( NO = = hasLSAN ( ) )
{
setMemAlarm ( [ cmdDefs stringForKey : @ "MemoryAlarm" ] ) ;
2019-08-11 12:35:10 +00:00
2025-01-08 14:49:32 +00:00
memAllowed = ( uint64_t ) [ cmdDefs integerForKey : @ "MemoryAllowed" ] ;
2015-07-08 09:06:26 +00:00
# if SIZEOF_VOIDP = = 4
2025-01-08 14:49:32 +00:00
if ( memAllowed >= 4 * 1024 )
{
[ self cmdError :
@ "MemoryAllowed (%" PRIu64 " too large for 32bit machine..."
@ " using 0" , memAllowed ] ;
memAllowed = 0 ;
}
2015-07-08 09:06:26 +00:00
# endif
2013-08-20 14:35:26 +00:00
2025-01-08 14:49:32 +00:00
memMaximum = ( uint64_t ) [ cmdDefs integerForKey : @ "MemoryMaximum" ] ;
2015-07-08 09:06:26 +00:00
# if SIZEOF_VOIDP = = 4
2025-01-08 14:49:32 +00:00
if ( memMaximum >= 4 * 1024 )
{
[ self cmdError :
@ "MemoryMaximum (%" PRIu64 " too large for 32bit machine..."
@ " using 0" , memMaximum ] ;
memMaximum = 0 ; // Disabled
}
2015-07-08 09:06:26 +00:00
# endif
2025-01-08 14:49:32 +00:00
}
2012-11-23 12:45:09 +00:00
2013-04-30 08:13:45 +00:00
str = [ cmdDefs stringForKey : @ "CoreSize" ] ;
if ( nil = = str )
2013-04-30 06:57:37 +00:00
{
2015-07-03 19:23:42 +00:00
i = 2 * 1024 ; // 2 GB default
2013-04-30 08:13:45 +00:00
}
else
{
i = [ str intValue ] ;
if ( i < 0 )
{
i = -1 ; // unlimited
}
2013-04-30 06:57:37 +00:00
}
if ( i ! = coreSize )
{
struct rlimit rlim ;
rlim_t want ;
coreSize = i ;
2013-04-30 08:13:45 +00:00
if ( coreSize < 0 )
{
want = RLIM_INFINITY ;
}
else
{
want = i * 1024 * 1024 ;
}
if ( getrlimit ( RLIMIT_CORE , & rlim ) < 0 )
2013-04-30 06:57:37 +00:00
{
2013-04-30 08:13:45 +00:00
NSLog ( @ "Unable to get core file size limit: %d" , errno ) ;
}
else
{
if ( RLIM_INFINITY ! = rlim . rlim_max && rlim . rlim_max < want )
2013-04-30 06:57:37 +00:00
{
2013-09-20 09:59:21 +00:00
int maxMB = ( int ) ( rlim . rlim_max / ( 1024 * 1024 ) ) ;
if ( RLIM_INFINITY = = want )
{
NSLog ( @ "Hard limit for core file size (%dMB)"
@ " less than requested (unlimited); using %dMB." ,
maxMB , maxMB ) ;
}
else
{
NSLog ( @ "Hard limit for core file size (%dMB)"
@ " less than requested (%dMB); using %dMB." ,
maxMB , coreSize , maxMB ) ;
}
want = rlim . rlim_max ;
2013-04-30 06:57:37 +00:00
}
2013-09-20 09:59:21 +00:00
rlim . rlim_cur = want ;
if ( setrlimit ( RLIMIT_CORE , & rlim ) < 0 )
2013-04-30 06:57:37 +00:00
{
2013-09-20 09:59:21 +00:00
if ( coreSize > 0 )
{
NSLog ( @ "Unable to set core file size limit to %uMB"
@ ", errno: %d" , coreSize , errno ) ;
}
else if ( coreSize < 0 )
{
NSLog ( @ "Unable to set core file size unlimited"
@ ", errno: %d" , errno ) ;
}
else
2013-04-30 06:57:37 +00:00
{
2013-09-20 09:59:21 +00:00
NSLog ( @ "Unable to set core dumps disabled"
@ ", errno: %d" , errno ) ;
2013-04-30 06:57:37 +00:00
}
}
}
}
2012-11-23 12:45:09 +00:00
if ( servers ! = nil )
{
NSEnumerator * e ;
RemoteServer * server ;
e = [ servers objectEnumerator ] ;
while ( ( server = [ e nextObject ] ) )
{
[ server update ] ;
}
}
2012-02-19 11:59:22 +00:00
}
- ( NSString * ) cmdInstance
{
return cmdInst ;
}
- ( BOOL ) cmdIsDaemon
{
return cmdFlagDaemon ;
}
- ( BOOL ) cmdIsTesting
{
return cmdFlagTesting ;
}
- ( NSDate * ) cmdLastIP
{
2014-11-26 08:59:50 +00:00
if ( 0.0 = = lastIP )
{
return nil ;
}
return [ dateClass dateWithTimeIntervalSinceReferenceDate : lastIP ] ;
2012-02-19 11:59:22 +00:00
}
- ( NSDate * ) cmdLastOP
{
2014-11-26 08:59:50 +00:00
if ( 0.0 = = lastOP )
{
return nil ;
}
return [ dateClass dateWithTimeIntervalSinceReferenceDate : lastOP ] ;
2012-02-19 11:59:22 +00:00
}
2017-07-13 09:25:33 +00:00
- ( NSString * ) ecLogEnd : ( NSString * ) name to : ( NSDate * ) when
2012-02-19 11:59:22 +00:00
{
2017-06-23 10:26:17 +00:00
NSString * status = nil ;
2012-02-19 11:59:22 +00:00
if ( [ name length ] = = 0 )
{
NSLog ( @ "Attempt to end log with empty filename" ) ;
}
2017-06-23 10:26:17 +00:00
else
2012-02-19 11:59:22 +00:00
{
2017-06-23 10:26:17 +00:00
NSFileHandle * hdl ;
2014-04-28 09:00:24 +00:00
2017-06-23 10:26:17 +00:00
name = [ name lastPathComponent ] ;
2012-02-19 11:59:22 +00:00
2017-06-23 10:26:17 +00:00
[ self ecDoLock ] ;
hdl = [ cmdLogMap objectForKey : name ] ;
if ( hdl ! = nil )
{
/ * If the file is empty , remove it , otherwise archive it .
* /
2017-07-13 09:25:33 +00:00
status = [ self _moveLog : name to : when ] ;
2014-04-28 09:00:24 +00:00
2017-06-23 10:26:17 +00:00
/ * Ensure that all data is written to file , then close it unless it ' s
* stderr ( which we must keep open for logging at all times ) .
* /
fflush ( stderr ) ;
if ( [ hdl fileDescriptor ] ! = 2 )
{
NS_DURING
[ hdl closeFile ] ;
NS_HANDLER
NS_ENDHANDLER
}
2012-02-19 11:59:22 +00:00
2017-06-23 10:26:17 +00:00
/ *
* Unregister filename .
* /
[ cmdLogMap removeObjectForKey : name ] ;
}
[ self ecUnLock ] ;
2012-02-19 11:59:22 +00:00
}
2017-06-23 10:26:17 +00:00
return status ;
2012-02-19 11:59:22 +00:00
}
2017-07-13 09:25:33 +00:00
- ( NSString * ) cmdLogEnd : ( NSString * ) name
{
return [ self ecLogEnd : name to : nil ] ;
}
2012-02-19 11:59:22 +00:00
- ( NSFileHandle * ) cmdLogFile : ( NSString * ) name
{
NSFileHandle * hdl ;
NSString * status = nil ;
if ( [ name length ] = = 0 )
{
NSLog ( @ "Attempt to log with empty filename" ) ;
return nil ;
}
name = [ name lastPathComponent ] ;
2015-05-14 13:47:30 +00:00
[ self ecDoLock ] ;
2012-02-19 11:59:22 +00:00
hdl = [ cmdLogMap objectForKey : name ] ;
2017-06-23 10:26:17 +00:00
if ( nil = = hdl )
2012-02-19 11:59:22 +00:00
{
2017-06-23 10:26:17 +00:00
/ * Archive any old left - over file .
* /
[ self _moveLog : name to : nil ] ;
2012-02-19 11:59:22 +00:00
2019-03-14 14:32:59 +00:00
if ( YES = = cmdKillDebug && [ name isEqual : cmdDebugName ] = = YES )
2012-02-19 11:59:22 +00:00
{
2019-03-14 14:32:59 +00:00
/ * Output is killed so we don ' t need to create the file
* and can simply write to / dev / null
* /
hdl = [ NSFileHandle fileHandleWithNullDevice ] ;
2012-02-19 11:59:22 +00:00
}
else
{
2019-03-14 14:32:59 +00:00
NSFileManager * mgr = [ NSFileManager defaultManager ] ;
NSString * path ;
path = [ cmdLogsDir ( nil ) stringByAppendingPathComponent : name ] ;
/ * Create the file if necessary , and open it for updating .
* /
if ( [ mgr isWritableFileAtPath : path ] = = NO
&& [ mgr createFileAtPath : path contents : nil attributes : nil ] = = NO )
2012-02-19 11:59:22 +00:00
{
2019-03-14 14:32:59 +00:00
NSLog ( @ "File '%@' is not writable and can't be created" , path ) ;
2012-02-19 11:59:22 +00:00
}
else
{
2019-03-14 14:32:59 +00:00
hdl = [ NSFileHandle fileHandleForUpdatingAtPath : path ] ;
if ( hdl = = nil )
{
if ( status ! = nil )
{
NSLog ( @ "%@" , status ) ;
}
NSLog ( @ "Unable to log to %@" , path ) ;
}
else
{
[ hdl seekToEndOfFile ] ;
}
2012-02-19 11:59:22 +00:00
}
}
2019-03-14 14:32:59 +00:00
2012-02-19 11:59:22 +00:00
if ( hdl = = nil )
{
2019-03-14 14:32:59 +00:00
[ self ecUnLock ] ;
2012-02-19 11:59:22 +00:00
return nil ;
}
2019-03-01 13:00:09 +00:00
/ * As a special case , if this is the default debug file
2012-02-19 11:59:22 +00:00
* we must set it up to write to stderr .
* /
2015-03-26 11:33:02 +00:00
if ( NO = = cmdKeepStderr && [ name isEqual : cmdDebugName ] = = YES )
2012-02-19 11:59:22 +00:00
{
int desc ;
desc = [ hdl fileDescriptor ] ;
if ( desc ! = 2 )
{
dup2 ( desc , 2 ) ;
2015-05-14 13:14:32 +00:00
NS_DURING
[ hdl closeFile ] ;
NS_HANDLER
2015-05-14 13:47:30 +00:00
NS_ENDHANDLER
2012-02-19 11:59:22 +00:00
hdl = [ NSFileHandle fileHandleWithStandardError ] ;
}
}
/ *
* Store the file handle in the dictionary for later use .
* /
[ cmdLogMap setObject : hdl forKey : name ] ;
if ( status ! = nil )
{
NSLog ( @ "%@" , status ) ;
}
}
2015-05-14 13:47:30 +00:00
[ hdl retain ] ;
[ self ecUnLock ] ;
return [ hdl autorelease ] ;
2012-02-19 11:59:22 +00:00
}
2012-02-20 11:01:04 +00:00
- ( void ) cmdLostConnectionToServer : ( NSString * ) name
{
return ;
}
- ( void ) cmdMadeConnectionToServer : ( NSString * ) name
{
return ;
}
2012-02-19 11:59:22 +00:00
- ( NSString * ) cmdName
{
return cmdName ;
}
2012-02-19 14:42:50 +00:00
- ( int ) cmdSignalled
2012-02-19 11:59:22 +00:00
{
2012-02-19 14:42:50 +00:00
return cmdSignalled ;
2012-02-19 11:59:22 +00:00
}
2019-02-11 16:17:16 +00:00
- ( EcAlarmDestination * ) ecAlarmDestination
{
return alarmDestination ;
}
2017-06-23 12:37:34 +00:00
static BOOL ecDidAwaken = NO ;
2023-10-10 13:15:55 +00:00
static BOOL ecDidAwakenCompletely = NO ;
2017-06-23 12:37:34 +00:00
2017-06-23 11:51:34 +00:00
- ( void ) ecAwaken
{
2017-06-23 12:37:34 +00:00
ecDidAwaken = YES ;
}
2019-07-05 14:53:39 +00:00
- ( void ) ecConfigurationError : ( NSString * ) fmt , . . .
2017-06-23 14:18:21 +00:00
{
2019-07-05 14:53:39 +00:00
NSString * err ;
va_list ap ;
va_start ( ap , fmt ) ;
err = [ NSString stringWithFormat : fmt arguments : ap ] ;
va_end ( ap ) ;
2017-06-23 14:18:21 +00:00
if ( [ err length ] > 0 )
{
EcAlarm * a ;
2018-08-28 10:50:47 +00:00
/ * We may need to truncate additional text to fit the limit for
* an alarm , so we must log to stderr * before * doing that .
2017-06-23 14:18:21 +00:00
* /
2018-08-28 10:50:47 +00:00
NSLog ( @ "ecConfigurationError: %@" , err ) ;
2017-06-23 14:18:21 +00:00
err = [ err stringByTrimmingSpaces ] ;
if ( [ err length ] > 255 )
{
err = [ err substringToIndex : 255 ] ;
while ( 255 < strlen ( [ err UTF8String ] ) )
{
err = [ err substringToIndex : [ err length ] - 1 ] ;
}
}
a = [ EcAlarm alarmForManagedObject : nil
at : nil
withEventType : EcAlarmEventTypeProcessingError
probableCause : EcAlarmConfigurationOrCustomizationError
specificProblem : @ "Fatal configuration error"
perceivedSeverity : EcAlarmSeverityMajor
proposedRepairAction :
_ ( @ "Correct config (check additional text and/or log for details)." )
additionalText : err ] ;
[ self alarm : a ] ;
2019-02-11 16:17:16 +00:00
[ [ self ecAlarmDestination ] shutdown ] ;
2020-10-17 16:13:26 +00:00
/ * Fatal configuration error should be -3 so the Command server knows
* /
[ self ecQuitFor : @ "configuration error" with : -3 ] ;
2017-06-23 14:18:21 +00:00
}
else
{
[ self clearConfigurationFor : nil
specificProblem : @ "Fatal configuration error"
additionalText : @ "Configuration updated" ] ;
}
}
2017-06-23 12:37:34 +00:00
- ( BOOL ) ecDidAwaken
{
return ecDidAwaken ;
2017-06-23 11:51:34 +00:00
}
2023-10-10 13:15:55 +00:00
- ( BOOL ) ecDidAwakenCompletely
{
return ecDidAwakenCompletely ;
}
2018-06-24 14:37:35 +00:00
- ( oneway void ) ecDidQuit
2017-11-02 16:16:21 +00:00
{
NSArray * keys ;
NSUInteger index ;
2018-06-24 14:37:35 +00:00
NSInteger status ;
NSDate * now ;
2017-11-02 16:16:21 +00:00
if ( NO = = ecIsQuitting ( ) )
{
2018-06-24 14:37:35 +00:00
[ self ecWillQuit ] ;
2017-11-02 16:16:21 +00:00
}
if ( cmdPTimer ! = nil )
{
[ cmdPTimer invalidate ] ;
cmdPTimer = nil ;
}
2018-06-24 14:37:35 +00:00
status = ecQuitStatus ;
2017-11-02 16:16:21 +00:00
if ( 0 = = status )
{
/ * Normal shutdown . . . unmanage this process first .
* /
2019-02-11 16:17:16 +00:00
[ [ self ecAlarmDestination ] unmanage : nil ] ;
2017-11-02 16:16:21 +00:00
}
2019-02-11 16:17:16 +00:00
[ [ self ecAlarmDestination ] shutdown ] ;
2017-11-02 16:16:21 +00:00
/ * Almost done . . . flush any logs then write the final audit log and
* flush again ( so that audit log should be the last in the file ) .
* /
[ self cmdFlushLogs ] ;
if ( 0 = = status )
{
2018-01-04 14:14:14 +00:00
[ self cmdAudit : @ "Shutdown '%@' (normal)" , [ self cmdName ] ] ;
2017-11-02 16:16:21 +00:00
}
2018-06-12 14:23:12 +00:00
else if ( -1 = = status )
{
[ self cmdAudit : @ "Shutdown '%@' (restart)" , [ self cmdName ] ] ;
}
2017-11-02 16:16:21 +00:00
else
{
2018-01-04 14:14:14 +00:00
[ self cmdAudit : @ "Shutdown '%@' (status %" PRIdPTR ")" ,
2017-11-02 16:16:21 +00:00
[ self cmdName ] , status ] ;
}
[ auditLogger flush ] ;
/ * Now that the audit log has been flushed to the Command / Control
* servers , we can unregister from Command .
* /
2021-06-23 15:18:41 +00:00
if ( nil ! = cmdServer )
{
NS_DURING
{
if ( cmdIsRegistered )
{
cmdIsRegistered = NO ;
[ cmdServer unregisterByObject : self status : ecQuitStatus ] ;
}
}
NS_HANDLER
{
[ self _commandRemove ] ;
NSLog ( @ "Caught exception unregistering from Command: %@" ,
localException ) ;
}
NS_ENDHANDLER
[ self _commandRemove ] ;
}
2017-11-02 16:16:21 +00:00
/ * Re - do the alarm destination shut down , just in case an alarm
* occurred while we were flushing logs and / or unregistering .
* /
2019-02-11 16:17:16 +00:00
[ [ self ecAlarmDestination ] shutdown ] ;
2017-11-02 16:16:21 +00:00
DESTROY ( alarmDestination ) ;
/ * Ensure our DO connection is invalidated so there will be no more
2018-01-04 11:23:29 +00:00
* remote communications or connection related events .
2017-11-02 16:16:21 +00:00
* /
2018-01-04 11:23:29 +00:00
[ EcProcConnection setDelegate : nil ] ;
[ [ NSNotificationCenter defaultCenter ]
removeObserver : self
name : nil
object : EcProcConnection ] ;
2017-11-02 16:16:21 +00:00
[ EcProcConnection invalidate ] ;
/ * The very last thing we do is to close down the log filed so they
* are archived to the correct directory for the current date .
* /
keys = [ cmdLogMap allKeys ] ;
2018-06-24 14:37:35 +00:00
now = [ NSDate date ] ;
2017-11-02 16:16:21 +00:00
for ( index = 0 ; index < [ keys count ] ; index + + )
{
[ self ecLogEnd : [ keys objectAtIndex : index ] to : now ] ;
}
2020-07-23 06:11:26 +00:00
[ [ NSNotificationCenter defaultCenter ]
postNotificationName : EcDidQuitNotification
object : self ] ;
2017-11-02 16:16:21 +00:00
exit ( status ) ;
}
2019-05-28 09:16:54 +00:00
- ( EcAlarm * ) ecException : ( NSException * ) cause
specificProblem : ( NSString * ) specificProblem
perceivedSeverity : ( EcAlarmSeverity ) perceivedSeverity
message : ( NSString * ) format , . . .
2019-05-27 16:37:18 +00:00
{
2019-05-28 09:16:54 +00:00
va_list ap ;
EcAlarm * a ;
2019-05-27 16:37:18 +00:00
va_start ( ap , format ) ;
2019-05-28 09:16:54 +00:00
a = [ self ecException : cause
specificProblem : specificProblem
2021-07-07 12:23:07 +00:00
perceivedSeverity : perceivedSeverity
2019-05-28 09:16:54 +00:00
message : format
arguments : ap ] ;
2019-05-27 16:37:18 +00:00
va_end ( ap ) ;
2019-05-28 09:16:54 +00:00
return a ;
2019-05-27 16:37:18 +00:00
}
2019-05-28 09:16:54 +00:00
- ( EcAlarm * ) ecException : ( NSException * ) cause
specificProblem : ( NSString * ) specificProblem
perceivedSeverity : ( EcAlarmSeverity ) perceivedSeverity
message : ( NSString * ) format
arguments : ( va_list ) args
2019-05-27 16:37:18 +00:00
{
EcAlarm * alarm ;
NSArray * stack ;
NSCalendarDate * now ;
NSString * msg ;
NSMutableString * full ;
2020-06-29 09:51:38 +00:00
ENTER_POOL
2019-05-27 21:35:47 +00:00
if ( nil = = ( msg = specificProblem ) )
{
msg = ( nil = = cause ) ? @ "Code/Data Error" : @ "Exception" ;
}
if ( [ msg length ] > 255 )
2019-05-27 16:37:18 +00:00
{
2019-05-27 21:35:47 +00:00
msg = [ msg substringToIndex : 255 ] ;
2019-05-27 16:37:18 +00:00
}
2019-05-27 21:35:47 +00:00
while ( strlen ( [ msg UTF8String ] ) > 255 )
{
msg = [ msg substringToIndex : [ msg length ] - 1 ] ;
}
specificProblem = msg ;
2019-05-27 16:37:18 +00:00
msg = [ NSString stringWithFormat : format arguments : args ] ;
full = [ NSMutableString stringWithCapacity : 1000 ] ;
[ full appendFormat : @ "%@: %@" , specificProblem , msg ] ;
if ( nil = = ( stack = [ cause callStackSymbols ] ) )
{
stack = [ NSThread callStackSymbols ] ;
}
if ( nil ! = cause )
{
NSDictionary * info = [ cause userInfo ] ;
[ full appendFormat : @ " <NSException> NAME:%@ REASON:%@" ,
[ cause name ] , [ cause reason ] ] ;
if ( nil ! = info )
{
[ full appendFormat : @ " INFO:%@" , info ] ;
}
}
if ( nil ! = stack )
{
NSUInteger count = [ stack count ] ;
NSUInteger index ;
2024-01-15 15:24:42 +00:00
/ * Delete the frame containign this method , so we show the
* trace to the actual point where the method was called .
* /
for ( index = 0 ; index < count ; index + + )
{
NSString * line = [ stack objectAtIndex : index ] ;
NSRange r ;
r = [ line rangeOfString :
@ "_ecException_specificProblem_perceivedSeverity_message_" ] ;
if ( r . length > 0 )
{
NSMutableArray * m = AUTORELEASE ( [ stack mutableCopy ] ) ;
r = NSMakeRange ( 0 , index + 1 ) ;
[ m removeObjectsInRange : r ] ;
stack = m ;
count = [ stack count ] ;
break ;
}
}
2019-05-27 16:37:18 +00:00
[ full appendString : @ "\nCall Stack trace:\n" ] ;
for ( index = 0 ; index < count ; index + + )
{
NSString * line = [ stack objectAtIndex : index ] ;
[ full appendFormat : @ "%3u: %@\n" , ( unsigned ) index , line ] ;
}
}
NSLog ( @ "%@" , full ) ;
now = [ NSCalendarDate date ] ;
if ( [ msg length ] > 250 )
{
msg = [ [ msg substringToIndex : 250 ] stringByAppendingString : @ " ..." ] ;
}
2019-05-27 21:35:47 +00:00
while ( strlen ( [ msg UTF8String ] ) > 255 )
{
msg = [ msg substringToIndex : [ msg length ] - 1 ] ;
}
2019-05-27 16:37:18 +00:00
2020-06-29 09:51:38 +00:00
if ( EcAlarmSeverityCleared = = perceivedSeverity )
{
alarm = nil ;
}
else
{
alarm = [ EcAlarm alarmForManagedObject : nil
at : now
withEventType : EcAlarmEventTypeProcessingError
probableCause : EcAlarmSoftwareProgramError
specificProblem : specificProblem
perceivedSeverity : perceivedSeverity
proposedRepairAction : @ "Check debug log file for details,"
@ " correct the problem, use the Console to clear the alarm"
@ " in the originating process."
additionalText : msg ] ;
[ self alarm : alarm ] ;
RETAIN ( alarm ) ;
}
LEAVE_POOL ;
2019-05-28 09:16:54 +00:00
return AUTORELEASE ( alarm ) ;
2019-05-27 16:37:18 +00:00
}
2017-11-03 07:17:41 +00:00
- ( void ) ecHandleQuit
{
2019-07-12 09:56:04 +00:00
if ( NO = = ecIsQuitting ( ) )
{
[ self ecWillQuit ] ;
}
ecQuitHandled = YES ;
[ self performSelectorOnMainThread : @ selector ( ecDidQuit )
withObject : nil
waitUntilDone : NO ] ;
2017-11-03 07:17:41 +00:00
}
2017-11-02 10:06:34 +00:00
- ( BOOL ) ecIsQuitting
{
return ecIsQuitting ( ) ;
}
2019-07-15 09:10:05 +00:00
- ( NSTimeInterval ) ecQuitDuration
{
if ( ecIsQuitting ( ) )
{
return [ NSDate timeIntervalSinceReferenceDate ] - beganQuitting ;
}
return 0.0 ;
}
2018-01-05 09:47:26 +00:00
- ( oneway void ) ecQuitFor : ( NSString * ) reason with : ( NSInteger ) status
2017-11-02 16:16:21 +00:00
{
2018-06-24 14:37:35 +00:00
[ ecLock lock ] ;
if ( 0.0 = = beganQuitting )
{
2020-06-24 12:26:31 +00:00
NSLog ( @ "-[%@ ecQuit: %@ for: %ld]" , NSStringFromClass ( [ self class ] ) ,
reason , ( long ) status ) ;
2024-11-25 11:58:52 +00:00
RELEASE ( ecQuitReason ) ;
ecQuitReason = [ reason copy ] ;
2018-06-24 14:37:35 +00:00
ecQuitStatus = status ;
}
[ ecLock unlock ] ;
[ self ecWillQuit ] ;
2018-01-04 13:32:42 +00:00
if ( class_getMethodImplementation ( [ EcProcess class ] , @ selector ( cmdQuit : ) )
! = class_getMethodImplementation ( [ self class ] , @ selector ( cmdQuit : ) ) )
{
/ * The - cmdQuit : method was overridden by a subclass , so we must call
2019-07-15 08:30:40 +00:00
* it for backward compatibility .
2018-01-04 13:32:42 +00:00
* /
[ self cmdQuit : status ] ;
}
else
{
2018-06-24 14:37:35 +00:00
[ self performSelectorOnMainThread : @ selector ( _ecQuit )
withObject : nil
waitUntilDone : NO ] ;
2018-01-04 13:32:42 +00:00
}
2017-11-02 16:16:21 +00:00
}
2019-07-15 09:10:05 +00:00
- ( NSTimeInterval ) ecQuitLimit : ( NSTimeInterval ) seconds
{
NSTimeInterval old = ecQuitLimit ;
ecQuitLimit = seconds ;
return old ;
}
2018-06-24 14:37:35 +00:00
- ( NSString * ) ecQuitReason
{
return ecQuitReason ;
}
- ( NSInteger ) ecQuitStatus
{
return ecQuitStatus ;
}
2018-01-05 09:47:26 +00:00
- ( oneway void ) ecRestart : ( NSString * ) reason
{
if ( NO = = [ NSThread isMainThread ] )
{
[ self performSelectorOnMainThread : _cmd
withObject : reason
waitUntilDone : NO ] ;
return ;
}
2018-06-12 14:23:12 +00:00
[ self cmdAudit : @ "Restarting '%@' (%@)" , [ self cmdName ] , reason ] ;
[ auditLogger flush ] ;
2021-06-23 14:44:18 +00:00
if ( memRestart )
{
[ self ecQuitFor : reason with : -5 ] ; // -5 tells Command to relaunch
}
else
{
[ self ecQuitFor : reason with : -1 ] ; // -1 tells Command to relaunch
}
2018-01-05 09:47:26 +00:00
}
2014-09-16 18:57:07 +00:00
- ( void ) ecLoggersChanged : ( NSNotification * ) n
{
DESTROY ( alertLogger ) ;
DESTROY ( auditLogger ) ;
DESTROY ( debugLogger ) ;
DESTROY ( errorLogger ) ;
DESTROY ( warningLogger ) ;
}
2013-04-08 15:50:32 +00:00
- ( NSDate * ) ecStarted
2012-02-19 11:59:22 +00:00
{
2012-02-19 14:42:50 +00:00
return started ;
2012-02-19 11:59:22 +00:00
}
2018-06-24 14:37:35 +00:00
- ( void ) ecWillQuit
2017-11-02 10:06:34 +00:00
{
2018-06-24 14:37:35 +00:00
NSTimeInterval now = [ NSDate timeIntervalSinceReferenceDate ] ;
if ( 0.0 = = beganQuitting )
{
beganQuitting = now ;
# ifndef __MINGW __
if ( reservedPipe [ 1 ] > 0 )
{
close ( reservedPipe [ 0 ] ) ; reservedPipe [ 0 ] = 0 ;
close ( reservedPipe [ 1 ] ) ; reservedPipe [ 1 ] = 0 ;
}
# endif
if ( [ ecQuitReason length ] > 0 )
{
NSLog ( @ "will quit: %@" , ecQuitReason ) ;
}
2020-07-23 06:11:26 +00:00
[ [ NSNotificationCenter defaultCenter ]
postNotificationName : EcWillQuitNotification
object : self ] ;
2018-06-24 14:37:35 +00:00
}
2019-07-15 08:30:40 +00:00
else
2018-06-24 14:37:35 +00:00
{
if ( [ ecQuitReason length ] > 0 )
{
2019-07-15 08:30:40 +00:00
NSLog ( @ "ignored: quit requested (%@) while quitting after %g sec.\n" ,
2018-06-24 14:37:35 +00:00
ecQuitReason , ( now - beganQuitting ) ) ;
}
else
{
2019-07-15 08:30:40 +00:00
NSLog ( @ "ignored: quit requested while quitting after %g sec.\n" ,
2018-06-24 14:37:35 +00:00
( now - beganQuitting ) ) ;
}
}
2017-11-02 10:06:34 +00:00
}
2013-04-09 11:31:01 +00:00
- ( oneway void ) alarm : ( in bycopy EcAlarm * ) event
{
2019-02-11 16:17:16 +00:00
[ [ self ecAlarmDestination ] alarm : event ] ;
2013-04-09 11:31:01 +00:00
}
- ( EcAlarm * ) alarmConfigurationFor : ( NSString * ) managedObject
specificProblem : ( NSString * ) specificProblem
additionalText : ( NSString * ) additionalText
critical : ( BOOL ) isCritical
{
EcAlarmSeverity severity ;
NSString * action ;
EcAlarm * a ;
2019-05-28 09:16:54 +00:00
NSString * s ;
s = specificProblem ;
if ( [ s length ] > 255 )
{
s = [ s substringToIndex : 255 ] ;
}
while ( strlen ( [ s UTF8String ] ) > 255 )
{
s = [ s substringToIndex : [ s length ] - 1 ] ;
}
specificProblem = s ;
s = additionalText ;
if ( [ s length ] > 255 )
{
s = [ s substringToIndex : 255 ] ;
}
while ( strlen ( [ s UTF8String ] ) > 255 )
{
s = [ s substringToIndex : [ s length ] - 1 ] ;
}
additionalText = s ;
2013-04-09 11:31:01 +00:00
if ( YES = = isCritical )
{
severity = EcAlarmSeverityCritical ;
}
else
{
severity = EcAlarmSeverityMajor ;
}
action = @ "Check/correct configuration" ; // FIXME . . . localize
a = [ EcAlarm alarmForManagedObject : managedObject
at : nil
withEventType : EcAlarmEventTypeProcessingError
probableCause : EcAlarmConfigurationOrCustomizationError
specificProblem : specificProblem
perceivedSeverity : severity
proposedRepairAction : action
additionalText : additionalText ] ;
[ self alarm : a ] ;
return a ;
}
2012-11-30 14:26:23 +00:00
- ( NSArray * ) alarms
{
2019-02-11 16:17:16 +00:00
return [ [ self ecAlarmDestination ] alarms ] ;
2012-11-30 14:26:23 +00:00
}
2013-04-09 11:31:01 +00:00
- ( void ) clearConfigurationFor : ( NSString * ) managedObject
specificProblem : ( NSString * ) specificProblem
additionalText : ( NSString * ) additionalText
2012-02-19 11:59:22 +00:00
{
2013-04-09 11:31:01 +00:00
EcAlarm * a ;
a = [ EcAlarm alarmForManagedObject : managedObject
at : nil
withEventType : EcAlarmEventTypeProcessingError
probableCause : EcAlarmConfigurationOrCustomizationError
specificProblem : specificProblem
perceivedSeverity : EcAlarmSeverityCleared
proposedRepairAction : nil
additionalText : additionalText ] ;
[ self alarm : a ] ;
2012-02-19 11:59:22 +00:00
}
- ( oneway void ) domanage : ( in bycopy NSString * ) managedObject
{
2019-02-11 16:17:16 +00:00
[ [ self ecAlarmDestination ] domanage : managedObject ] ;
2012-02-19 11:59:22 +00:00
}
- ( oneway void ) unmanage : ( in bycopy NSString * ) managedObject
{
2019-02-11 16:17:16 +00:00
[ [ self ecAlarmDestination ] unmanage : managedObject ] ;
2012-02-19 11:59:22 +00:00
}
2015-01-26 12:50:42 +00:00
- ( int ) processIdentifier
{
static int pi = 0 ;
if ( 0 = = pi )
{
pi = [ [ NSProcessInfo processInfo ] processIdentifier ] ;
}
return pi ;
}
2012-02-19 11:59:22 +00:00
- ( void ) setCmdInterval : ( NSTimeInterval ) interval
{
2015-10-29 12:06:38 +00:00
if ( interval > 300.0 )
2012-02-19 11:59:22 +00:00
{
2012-06-12 07:46:29 +00:00
NSLog ( @ "Ignored attempt to set timer interval to %g ... using 60.0" , interval ) ;
2012-02-19 11:59:22 +00:00
interval = 60.0 ;
}
if ( interval < 0.001 )
{
NSLog ( @ "Ignored attempt to set timer interval to %g ... using 10.0" , interval ) ;
interval = 10.0 ;
}
if ( interval ! = cmdTimInterval )
{
cmdTimInterval = interval ;
[ self triggerCmdTimeout ] ;
}
}
2020-03-23 13:35:58 +00:00
- ( oneway void ) ecCancelConfigFwdTo : ( id < EcConfigForwarded > ) client
{
[ self ecDoLock ] ;
[ ecConfigClients removeObjectIdenticalTo : client ] ;
[ self ecUnLock ] ;
}
2012-03-09 09:22:09 +00:00
- ( NSString * ) ecCopyright
2012-02-19 11:59:22 +00:00
{
return @ "" ;
}
2012-05-09 13:18:32 +00:00
- ( void ) ecDoLock
{
[ ecLock lock ] ;
}
2020-03-23 13:35:58 +00:00
- ( bycopy NSDictionary * ) ecSetupConfigFwdTo : ( id < EcConfigForwarded > ) client
{
NSDictionary * config = nil ;
[ self ecDoLock ] ;
/ * Ensure the array exists ; do this before - indexOfObjectIdenticalTo : as
* calling the method on a nil arraqy would always return 0.
* /
if ( nil = = ecConfigClients )
{
ecConfigClients = [ NSMutableArray new ] ;
}
if ( [ ecConfigClients indexOfObjectIdenticalTo : client ] = = NSNotFound )
{
[ ecConfigClients addObject : client ] ;
}
config = [ cmdDefs dictionaryRepresentation ] ;
[ self ecUnLock ] ;
return config ;
}
2012-05-09 13:18:32 +00:00
- ( void ) ecUnLock
{
[ ecLock unlock ] ;
}
2017-07-26 14:15:41 +00:00
+ ( NSRecursiveLock * ) ecLock
{
return ecLock ;
}
2012-02-19 11:59:22 +00:00
+ ( void ) initialize
{
2022-06-17 10:32:27 +00:00
if ( NULL = = original_NSLock _error _handler )
{
original_NSLock _error _handler = _NSLock _error _handler ;
_NSLock _error _handler = EcLock_error _handler ;
}
2012-03-09 09:22:09 +00:00
if ( nil = = ecLock )
2012-02-19 11:59:22 +00:00
{
2022-11-07 15:21:51 +00:00
setupTLS ( [ NSUserDefaults standardUserDefaults ] ) ;
2012-03-09 09:22:09 +00:00
ecLock = [ NSRecursiveLock new ] ;
2012-02-19 11:59:22 +00:00
dateClass = [ NSDate class ] ;
cDateClass = [ NSCalendarDate class ] ;
stringClass = [ NSString class ] ;
cmdLogMap = [ [ NSMutableDictionary alloc ] initWithCapacity : 4 ] ;
cmdDebugModes = [ [ NSMutableSet alloc ] initWithCapacity : 4 ] ;
cmdDebugKnown = [ [ NSMutableDictionary alloc ] initWithCapacity : 4 ] ;
[ cmdDebugKnown setObject : @ "Mode for distributed object connections"
forKey : cmdConnectDbg ] ;
[ cmdDebugKnown setObject : @ "Standard mode for basic debug information"
2015-10-14 11:25:48 +00:00
forKey : cmdBasicDbg ] ;
2012-02-19 11:59:22 +00:00
[ cmdDebugKnown setObject : @ "Detailed but general purpose debugging"
forKey : cmdDetailDbg ] ;
2015-10-14 11:25:48 +00:00
[ cmdDebugModes addObject : cmdBasicDbg ] ;
2012-02-19 11:59:22 +00:00
2014-11-02 14:30:24 +00:00
[ self ecRegisterDefault : @ "Memory"
withTypeText : @ "YES/NO"
andHelpText : @ "Enable memory allocation checks"
2015-11-18 17:41:07 +00:00
action : @ selector ( _defMemory : )
value : @ "YES" ] ;
2014-11-02 14:30:24 +00:00
[ self ecRegisterDefault : @ "Release"
withTypeText : @ "YES/NO"
andHelpText : @ "Turn on double release checks (debug)"
2015-11-18 17:41:07 +00:00
action : @ selector ( _defRelease : )
value : @ "NO" ] ;
2014-11-02 14:30:24 +00:00
[ self ecRegisterDefault : @ "Testing"
withTypeText : @ "YES/NO"
andHelpText : @ "Run in test mode (if supported)"
2015-11-18 17:41:07 +00:00
action : @ selector ( _defTesting : )
value : @ "NO" ] ;
2012-02-19 11:59:22 +00:00
/ *
* Set the timeouts for the default connection so that
* they will be inherited by other connections .
* A two minute timeout is long enough for almost all
* circumstances .
* /
[ [ NSConnection defaultConnection ] setRequestTimeout : 120.0 ] ;
[ [ NSConnection defaultConnection ] setReplyTimeout : 120.0 ] ;
2013-08-22 18:11:15 +00:00
[ self registerAtExit ] ;
2012-02-19 11:59:22 +00:00
}
}
- ( void ) addServerToList : ( NSString * ) serverName
{
[ self addServerToList : serverName for : nil ] ;
}
- ( void ) addServerToList : ( NSString * ) serverName for : ( id ) anObject
{
RemoteServer * remote ;
if ( ( serverName = = nil )
|| ( [ serverName isKindOfClass : [ NSString class ] ] = = NO ) )
{
NSLog ( @ "Warning: invalid string passed to addServerToList:for:" ) ;
return ;
}
if ( anObject = = nil )
{
anObject = self ;
}
if ( servers = = nil )
{
servers = [ [ NSMutableDictionary alloc ] initWithCapacity : 2 ] ;
}
remote = [ [ RemoteServer alloc ] initWithDefaultName : serverName
delegate : anObject ] ;
[ servers setObject : remote forKey : serverName ] ;
[ remote release ] ;
}
- ( void ) removeServerFromList : ( NSString * ) serverName
{
if ( ( serverName = = nil )
|| ( [ serverName isKindOfClass : [ NSString class ] ] = = NO ) )
{
NSLog ( @ "Warning: invalid array passed to removeServerFromList:" ) ;
return ;
}
[ servers removeObjectForKey : serverName ] ;
}
- ( id ) cmdConnectionBecameInvalid : ( NSNotification * ) notification
{
id connection ;
connection = [ notification object ] ;
2013-11-04 07:28:57 +00:00
[ connection setDelegate : nil ] ;
2012-02-19 11:59:22 +00:00
[ [ NSNotificationCenter defaultCenter ]
removeObserver : self
name : NSConnectionDidDieNotification
object : connection ] ;
if ( cmdServer ! = nil && connection = = [ cmdServer connectionForProxy ] )
{
2019-02-11 16:17:16 +00:00
[ [ self ecAlarmDestination ] setDestination : nil ] ;
2012-02-19 11:59:22 +00:00
DESTROY ( cmdServer ) ;
2023-04-24 16:45:52 +00:00
NSLog ( @ "lost connection %p to command server\n" , connection ) ;
2012-02-19 11:59:22 +00:00
/ *
* Cause timeout to go off really soon so we will try to
* re - establish the link to the server .
* /
2015-05-14 13:47:30 +00:00
[ self triggerCmdTimeout ] ;
2012-02-19 11:59:22 +00:00
}
else
{
2013-11-04 07:28:57 +00:00
NSLog ( @ "unknown connection sent invalidation\n" ) ;
2012-02-19 11:59:22 +00:00
}
return self ;
}
2012-05-25 19:10:27 +00:00
- ( void ) cmdAlert : ( NSString * ) fmt arguments : ( va_list ) args
2012-02-19 11:59:22 +00:00
{
if ( nil = = alertLogger )
{
alertLogger = [ [ EcLogger loggerForType : LT_ALERT ] retain ] ;
}
2012-05-25 19:10:27 +00:00
[ alertLogger log : fmt arguments : args ] ;
}
- ( void ) cmdAlert : ( NSString * ) fmt , . . .
{
va_list ap ;
2012-02-19 11:59:22 +00:00
va_start ( ap , fmt ) ;
2012-05-25 19:10:27 +00:00
[ self cmdAlert : fmt arguments : ap ] ;
2012-02-19 11:59:22 +00:00
va_end ( ap ) ;
}
2017-06-23 10:26:17 +00:00
- ( NSString * ) ecArchive : ( NSDate * ) when
2012-02-19 11:59:22 +00:00
{
NSString * status = @ "" ;
if ( [ cmdLogMap count ] = = 0 )
{
status = noFiles ;
}
else
{
2015-05-14 13:47:30 +00:00
NSEnumerator * enumerator ;
2012-02-19 11:59:22 +00:00
NSString * name ;
2015-05-14 13:47:30 +00:00
[ self ecDoLock ] ;
enumerator = [ [ cmdLogMap allKeys ] objectEnumerator ] ;
[ self ecUnLock ] ;
2012-02-19 11:59:22 +00:00
while ( ( name = [ enumerator nextObject ] ) ! = nil )
{
NSString * s ;
2017-11-02 09:19:57 +00:00
s = [ self ecLogEnd : name to : when ] ;
2017-06-23 10:26:17 +00:00
if ( nil ! = s )
{
if ( [ status length ] > 0 )
status = [ status stringByAppendingString : @ "\n" ] ;
status = [ status stringByAppendingString : s ] ;
}
2017-11-02 09:19:57 +00:00
if ( NO = = ecIsQuitting ( ) )
2012-02-19 11:59:22 +00:00
{
[ self cmdLogFile : name ] ;
}
}
}
return status ;
}
2012-05-25 19:10:27 +00:00
- ( void ) cmdAudit : ( NSString * ) fmt arguments : ( va_list ) args
2012-02-19 11:59:22 +00:00
{
if ( nil = = auditLogger )
{
auditLogger = [ [ EcLogger loggerForType : LT_AUDIT ] retain ] ;
}
2012-05-25 19:10:27 +00:00
[ auditLogger log : fmt arguments : args ] ;
}
- ( void ) cmdAudit : ( NSString * ) fmt , . . .
{
va_list ap ;
2012-02-19 11:59:22 +00:00
va_start ( ap , fmt ) ;
2012-05-25 19:10:27 +00:00
[ self cmdAudit : fmt arguments : ap ] ;
2012-02-19 11:59:22 +00:00
va_end ( ap ) ;
}
2012-05-25 19:10:27 +00:00
- ( void ) cmdDbg : ( NSString * ) type msg : ( NSString * ) fmt arguments : ( va_list ) args
2012-02-19 11:59:22 +00:00
{
if ( nil ! = [ cmdDebugModes member : type ] )
{
if ( nil = = debugLogger )
{
debugLogger = [ [ EcLogger loggerForType : LT_DEBUG ] retain ] ;
}
2012-05-25 19:10:27 +00:00
[ debugLogger log : fmt arguments : args ] ;
2012-02-19 11:59:22 +00:00
}
}
2012-05-25 19:10:27 +00:00
- ( void ) cmdDbg : ( NSString * ) type msg : ( NSString * ) fmt , . . .
{
va_list ap ;
va_start ( ap , fmt ) ;
[ self cmdDbg : type msg : fmt arguments : ap ] ;
va_end ( ap ) ;
}
- ( void ) cmdDebug : ( NSString * ) fmt arguments : ( va_list ) args
2012-02-19 11:59:22 +00:00
{
2015-10-14 11:25:48 +00:00
if ( nil ! = [ cmdDebugModes member : cmdBasicDbg ] )
2012-02-19 11:59:22 +00:00
{
if ( nil = = debugLogger )
{
debugLogger = [ [ EcLogger loggerForType : LT_DEBUG ] retain ] ;
}
2012-05-25 19:10:27 +00:00
[ debugLogger log : fmt arguments : args ] ;
2012-02-19 11:59:22 +00:00
}
}
2012-05-25 19:10:27 +00:00
- ( void ) cmdDebug : ( NSString * ) fmt , . . .
2012-02-19 11:59:22 +00:00
{
va_list ap ;
2012-05-25 19:10:27 +00:00
va_start ( ap , fmt ) ;
[ self cmdDebug : fmt arguments : ap ] ;
va_end ( ap ) ;
}
- ( void ) cmdError : ( NSString * ) fmt arguments : ( va_list ) args
{
2012-02-19 11:59:22 +00:00
if ( nil = = errorLogger )
{
errorLogger = [ [ EcLogger loggerForType : LT_ERROR ] retain ] ;
}
2012-05-25 19:10:27 +00:00
[ errorLogger log : fmt arguments : args ] ;
}
- ( void ) cmdError : ( NSString * ) fmt , . . .
{
va_list ap ;
2012-02-19 11:59:22 +00:00
va_start ( ap , fmt ) ;
2012-05-25 19:10:27 +00:00
[ self cmdError : fmt arguments : ap ] ;
2012-02-19 11:59:22 +00:00
va_end ( ap ) ;
}
- ( void ) cmdFlushLogs
{
[ alertLogger flush ] ;
[ auditLogger flush ] ;
[ debugLogger flush ] ;
[ errorLogger flush ] ;
[ warningLogger flush ] ;
}
- ( NSTimeInterval ) cmdInterval
{
return cmdTimInterval ;
}
- ( BOOL ) cmdIsClient
{
return YES ;
}
- ( void ) log : ( NSString * ) message type : ( EcLogType ) t
{
switch ( t )
{
case LT_DEBUG :
[ self cmdDebug : @ "%@" , message ] ;
break ;
case LT_WARNING :
[ self cmdWarn : @ "%@" , message ] ;
break ;
case LT_ERROR :
2020-03-23 13:35:58 +00:00
[ self cmdError : @ "%@" , message ] ;
2012-02-19 11:59:22 +00:00
break ;
case LT_ALERT :
2020-03-23 13:35:58 +00:00
[ self cmdAlert : @ "%@" , message ] ;
2012-02-19 11:59:22 +00:00
break ;
case LT_AUDIT :
2018-07-30 20:26:41 +00:00
case LT_CONSOLE :
2012-02-19 11:59:22 +00:00
[ self cmdAudit : @ "%@" , message ] ;
break ;
default :
2020-03-23 13:35:58 +00:00
[ self cmdError : @ "%@" , message ] ;
2012-02-19 11:59:22 +00:00
break ;
}
}
- ( id ) cmdNewServer
{
static BOOL connecting = NO ;
if ( NO = = connecting )
{
/ *
* Use the ' cmdLast ' variable to ensure that we don ' t try to
* check memory usage or connect to the command server more
* than once every 10 sec .
* /
if ( cmdLast = = nil || [ cmdLast timeIntervalSinceNow ] < -10.0 )
{
connecting = YES ;
ASSIGN ( cmdLast , [ dateClass date ] ) ;
if ( cmdFirst = = nil )
{
ASSIGN ( cmdFirst , cmdLast ) ;
}
2019-05-31 16:33:53 +00:00
if ( nil = = cmdServer && YES = = [ self cmdIsClient ] )
2012-02-19 11:59:22 +00:00
{
NSString * name = nil ;
NSString * host = nil ;
id proxy ;
NS_DURING
{
2014-03-25 18:44:47 +00:00
NSSocketPortNameServer * ns ;
2012-03-09 09:22:09 +00:00
host = ecCommandHost ( ) ;
name = ecCommandName ( ) ;
2012-02-19 11:59:22 +00:00
2014-03-25 18:44:47 +00:00
ns = [ NSSocketPortNameServer sharedInstance ] ;
2012-02-19 11:59:22 +00:00
proxy = [ NSConnection
rootProxyForConnectionWithRegisteredName : name
host : host
2014-03-25 18:44:47 +00:00
usingNameServer : ns ] ;
2020-04-04 10:03:26 +00:00
[ proxy setProtocolForProxy : @ protocol ( Command ) ] ;
if ( nil = = proxy )
{
NSLog ( @ "Unable to connect to Command server" ) ;
}
else
{
NSConnection * connection ;
ASSIGN ( cmdServer , proxy ) ;
connection = [ cmdServer connectionForProxy ] ;
[ connection enableMultipleThreads ] ;
if ( nil = = alarmDestination )
{
alarmDestination = [ EcAlarmDestination new ] ;
}
[ [ self ecAlarmDestination ] setDestination : cmdServer ] ;
[ [ NSNotificationCenter defaultCenter ]
addObserver : self
selector : @ selector ( cmdConnectionBecameInvalid : )
name : NSConnectionDidDieNotification
object : connection ] ;
}
2012-02-19 11:59:22 +00:00
}
NS_HANDLER
{
proxy = nil ;
NSLog ( @ "Exception connecting to Command server %@ on %@): %@" ,
name , host , localException ) ;
}
NS_ENDHANDLER
2020-04-04 10:03:26 +00:00
/ * We only register and fetch new configuration information
* if we are NOT in the process of shutting down : a process
* which is shutting down should only use the Command server
* for logging / alerting purposes .
* /
if ( proxy ! = nil && 0.0 = = beganQuitting )
2012-02-19 11:59:22 +00:00
{
NSMutableDictionary * r = nil ;
NS_DURING
{
NSData * d ;
d = [ proxy registerClient : self
2020-07-03 10:50:25 +00:00
identifier : [ self processIdentifier ]
2012-02-19 11:59:22 +00:00
name : cmdLogName ( )
transient : cmdIsTransient ] ;
r = [ NSPropertyListSerialization
propertyListWithData : d
options : NSPropertyListMutableContainers
format : 0
error : 0 ] ;
}
NS_HANDLER
{
r = [ NSMutableDictionary dictionaryWithCapacity : 1 ] ;
[ r setObject : [ localException reason ]
forKey : @ "rejected" ] ;
NSLog ( @ "Caught exception registering with Command: %@" ,
localException ) ;
}
NS_ENDHANDLER
/ * We could be rejected or told to back off ,
* otherwise we continue as normal .
* /
2019-05-31 16:33:53 +00:00
if ( [ r objectForKey : @ "rejected" ] ! = nil )
2012-02-19 11:59:22 +00:00
{
2017-11-02 09:19:57 +00:00
NSString * shutdown ;
shutdown = [ NSString stringWithFormat :
@ " rejected by Command - %@" ,
[ r objectForKey : @ "rejected" ] ] ;
2019-05-31 16:33:53 +00:00
NSLog ( @ "Unable to connect to Command server ... %@" ,
shutdown ) ;
2020-10-17 16:13:26 +00:00
/ * Rejected by Command server .
* NB . The Command server should expect a -4 exit
* /
[ self ecQuitFor : shutdown with : -4 ] ;
2012-02-19 11:59:22 +00:00
}
2019-05-31 16:33:53 +00:00
else if ( [ r objectForKey : @ "back-off" ] ! = nil )
{
2020-04-04 10:03:26 +00:00
NSLog ( @ "Unable to register with Command server ..."
2019-05-31 16:33:53 +00:00
@ " back-off" ) ;
}
else
2012-02-19 11:59:22 +00:00
{
2012-11-23 12:45:09 +00:00
[ self _update : r ] ;
2013-11-04 07:28:57 +00:00
/ * If we just connected to the command server ,
* and we have a registered connection , then we
* can tell it that any alarm for failure to
* register must be cleared .
* /
if ( nil ! = cmdServer && [ EcProcConnection isValid ] )
{
[ self _connectionRegistered ] ;
}
2012-02-19 11:59:22 +00:00
}
}
}
connecting = NO ;
2019-05-31 16:33:53 +00:00
if ( nil = = cmdLast && nil = = cmdServer && YES = = [ self cmdIsClient ] )
{
/ * If cmdLast is nil , a reconnect must have been requested
* while we were attempting a connect . . . so try again .
* /
cmdServer = [ self cmdNewServer ] ;
}
2012-02-19 11:59:22 +00:00
}
2019-05-31 16:33:53 +00:00
else
2012-02-19 11:59:22 +00:00
{
2019-06-27 12:46:21 +00:00
// NSLog ( @ "Unable to connect to Command server ... not retry time yet" ) ;
2012-02-19 11:59:22 +00:00
}
}
2019-05-31 16:33:53 +00:00
else
{
NSLog ( @ "Unable to connect to Command server ... attempt in progress" ) ;
}
2012-02-19 11:59:22 +00:00
return cmdServer ;
}
2012-05-25 19:10:27 +00:00
- ( void ) cmdWarn : ( NSString * ) fmt arguments : ( va_list ) args
2012-02-19 11:59:22 +00:00
{
if ( nil = = warningLogger )
{
warningLogger = [ [ EcLogger loggerForType : LT_WARNING ] retain ] ;
}
2012-05-25 19:10:27 +00:00
[ warningLogger log : fmt arguments : args ] ;
}
- ( void ) cmdWarn : ( NSString * ) fmt , . . .
{
va_list ap ;
2012-02-19 11:59:22 +00:00
va_start ( ap , fmt ) ;
2012-05-25 19:10:27 +00:00
[ self cmdWarn : fmt arguments : ap ] ;
2012-02-19 11:59:22 +00:00
va_end ( ap ) ;
}
2012-03-09 09:22:09 +00:00
- ( void ) ecNewDay : ( NSCalendarDate * ) when
2012-02-19 11:59:22 +00:00
{
2017-09-18 08:36:48 +00:00
static BOOL beenHere = NO ;
2015-07-21 13:32:22 +00:00
static NSDictionary * defs = nil ;
2017-06-23 10:26:17 +00:00
NSDictionary * d = [ cmdDefs volatileDomainForName : @ "EcCommand" ] ;
2015-07-21 13:32:22 +00:00
2017-09-18 08:36:48 +00:00
if ( YES = = beenHere )
{
2017-11-01 12:06:23 +00:00
/ * Archive previous day ' s logs . Force logs to be archived for
* yesterday even if they have been modified today ( on the basis
2017-09-18 08:36:48 +00:00
* that only the very latest info in them should be from today ) .
* /
2017-11-01 12:06:23 +00:00
when = [ when dateByAddingTimeInterval : -3600.0 ] ;
2017-09-18 08:36:48 +00:00
NSLog ( @ "Daily: %@" , [ self ecArchive : when ] ) ;
2017-06-23 10:26:17 +00:00
2017-09-20 07:43:01 +00:00
if ( nil ! = defs )
2015-07-21 13:32:22 +00:00
{
2017-09-20 07:43:01 +00:00
NSEnumerator * e ;
NSString * k ;
2017-06-23 10:26:17 +00:00
2017-09-20 07:43:01 +00:00
/ * Check information left in the EcCommand domain .
* /
e = [ [ d allKeys ] objectEnumerator ] ;
while ( nil ! = ( k = [ e nextObject ] ) )
2017-06-23 10:26:17 +00:00
{
2017-09-20 07:43:01 +00:00
id v = [ d objectForKey : k ] ;
if ( [ v isEqual : [ defs objectForKey : k ] ] )
{
2019-05-27 15:24:23 +00:00
[ self cmdWarn : @ "The Console defaults override for '%@'"
2017-09-20 07:43:01 +00:00
@ " has been left at '%@' for more than a day."
@ " Please reset it ('tell %@ defaults delete %@') after"
@ " updating Control.plist as required." ,
k , v , [ self cmdName ] , k ] ;
}
2017-06-23 10:26:17 +00:00
}
2015-07-21 13:32:22 +00:00
}
}
2017-09-20 07:43:01 +00:00
else
{
/ * First time round we must not archive since that will have
* been done on startup anyway .
* /
beenHere = YES ;
}
2015-07-21 13:32:22 +00:00
ASSIGNCOPY ( defs , d ) ;
2012-02-19 11:59:22 +00:00
}
2012-03-09 09:22:09 +00:00
- ( void ) ecNewHour : ( NSCalendarDate * ) when
2012-02-19 11:59:22 +00:00
{
return ;
}
2012-03-09 09:22:09 +00:00
- ( void ) ecNewMinute : ( NSCalendarDate * ) when
2012-02-19 11:59:22 +00:00
{
2014-11-01 09:18:02 +00:00
# ifndef __MINGW __
2017-11-02 09:19:57 +00:00
if ( NO = = ecIsQuitting ( ) )
2014-11-01 09:18:02 +00:00
{
NSString * shutdown = nil ;
int p [ 2 ] ;
if ( pipe ( p ) = = 0 )
{
if ( 0 = = reservedPipe [ 1 ] )
{
reservedPipe [ 0 ] = p [ 0 ] ;
reservedPipe [ 1 ] = p [ 1 ] ;
}
else
{
close ( p [ 0 ] ) ;
close ( p [ 1 ] ) ;
}
if ( descriptorsMaximum > 0 )
{
if ( p [ 0 ] > descriptorsMaximum || p [ 1 ] > descriptorsMaximum )
{
shutdown = [ NSString stringWithFormat :
@ "Open file descriptor limit (%lu) exceeded" ,
( unsigned long ) descriptorsMaximum ] ;
}
}
}
else
{
shutdown = @ "Process ran out of file descriptors" ;
}
if ( nil ! = shutdown )
{
/ * We hope that closing two reserved file descriptors will allow
* us to shut down gracefully and restart .
* /
if ( reservedPipe [ 1 ] > 0 )
{
close ( reservedPipe [ 0 ] ) ; reservedPipe [ 0 ] = 0 ;
close ( reservedPipe [ 1 ] ) ; reservedPipe [ 1 ] = 0 ;
}
2020-10-17 16:13:26 +00:00
[ self ecQuitFor : shutdown with : -1 ] ; // relaunch
2014-11-01 09:18:02 +00:00
return ;
}
}
# endif
2014-06-20 16:32:12 +00:00
/ * We want to be sure we work with reasonably up to date information .
* /
[ NSHost flushHostCache ] ;
2019-05-27 15:24:23 +00:00
[ cmdDefs purgeSettings ] ;
2015-07-08 09:06:26 +00:00
[ self _memCheck ] ;
2012-02-19 11:59:22 +00:00
}
2012-03-09 09:22:09 +00:00
- ( void ) ecHadIP : ( NSDate * ) when
2012-02-19 11:59:22 +00:00
{
2014-11-26 08:59:50 +00:00
if ( nil = = when )
2012-02-19 11:59:22 +00:00
{
2014-11-26 08:59:50 +00:00
lastIP = [ dateClass timeIntervalSinceReferenceDate ] ;
}
else
{
lastIP = [ when timeIntervalSinceReferenceDate ] ;
2012-02-19 11:59:22 +00:00
}
}
2012-03-09 09:22:09 +00:00
- ( void ) ecHadOP : ( NSDate * ) when
2012-02-19 11:59:22 +00:00
{
2014-11-26 08:59:50 +00:00
if ( nil = = when )
{
lastOP = [ dateClass timeIntervalSinceReferenceDate ] ;
}
else
2012-02-19 11:59:22 +00:00
{
2014-11-26 08:59:50 +00:00
lastOP = [ when timeIntervalSinceReferenceDate ] ;
2012-02-19 11:59:22 +00:00
}
}
2012-03-09 09:22:09 +00:00
- ( NSUInteger ) ecNotLeaked
2012-02-19 11:59:22 +00:00
{
return 0 ;
}
2012-03-09 09:22:09 +00:00
- ( int ) ecRun
2012-02-19 11:59:22 +00:00
{
2017-07-27 13:42:29 +00:00
CREATE_AUTORELEASE _POOL ( arp ) ;
2020-06-24 12:26:31 +00:00
NSSocketPortNameServer * ns ;
NSString * name ;
NSRunLoop * loop ;
NSDate * future ;
2012-02-19 11:59:22 +00:00
if ( YES = = cmdIsTransient )
{
2014-05-16 22:13:43 +00:00
[ self cmdWarn : @ "Attempted to run transient process." ] ;
2012-08-02 11:30:15 +00:00
[ self cmdFlushLogs ] ;
2012-12-06 08:08:39 +00:00
[ arp release ] ;
2012-02-19 11:59:22 +00:00
return 1 ;
}
2020-06-26 18:59:55 +00:00
/ * Now that startup has completed we re - register our connection under
* our normal name ( which removes the name used during startup ) .
* /
2020-06-24 12:26:31 +00:00
NSAssert ( nil ! = EcProcConnection , NSGenericException ) ;
ns = [ NSSocketPortNameServer sharedInstance ] ;
name = [ self cmdName ] ;
if ( [ EcProcConnection registerName : name withNameServer : ns ] = = NO )
2012-02-19 11:59:22 +00:00
{
2013-11-04 07:28:57 +00:00
EcAlarm * a ;
2020-06-24 12:26:31 +00:00
DESTROY ( EcProcConnection ) ;
2013-11-04 07:28:57 +00:00
NSLog ( @ "Unable to register with name server. Perhaps a copy of this process is already running (or is hung or blocked waiting for a database query etc), or perhaps an old version was killed and is still registered. Check the state of any running process and and check the process registration with gdomap." ) ;
a = [ EcAlarm alarmForManagedObject : nil
at : nil
withEventType : EcAlarmEventTypeProcessingError
probableCause : EcAlarmSoftwareProgramAbnormallyTerminated
2013-12-09 21:55:18 +00:00
specificProblem : @ "Unable to register"
2013-11-04 07:28:57 +00:00
perceivedSeverity : EcAlarmSeverityMajor
proposedRepairAction :
_ ( @ "Check for running copy of process and/or registration in gdomap." )
additionalText : _ ( @ "Process probably already running (possibly hung/delayed) or problem in name registration with distributed objects system (gdomap)" ) ] ;
[ self alarm : a ] ;
2019-02-11 16:17:16 +00:00
[ [ self ecAlarmDestination ] shutdown ] ;
2020-10-17 16:13:26 +00:00
/ * NB . The Command server knowns that -2 means a name server failure .
* /
[ self ecQuitFor : @ "unable to register with name server" with : -2 ] ;
2012-08-02 11:30:15 +00:00
[ self cmdFlushLogs ] ;
2012-12-06 08:08:39 +00:00
[ arp release ] ;
2012-02-19 11:59:22 +00:00
return 2 ;
}
2014-09-04 21:24:19 +00:00
else
{
EcAlarm * a ;
a = [ EcAlarm alarmForManagedObject : nil
at : nil
withEventType : EcAlarmEventTypeProcessingError
probableCause : EcAlarmSoftwareProgramAbnormallyTerminated
specificProblem : @ "Unable to register"
perceivedSeverity : EcAlarmSeverityCleared
proposedRepairAction : nil
additionalText : nil ] ;
[ self alarm : a ] ;
}
2012-02-19 11:59:22 +00:00
2020-06-24 12:26:31 +00:00
[ EcProcConnection setDelegate : self ] ;
2012-02-19 11:59:22 +00:00
[ [ NSNotificationCenter defaultCenter ]
addObserver : self
2013-03-18 07:42:41 +00:00
selector : @ selector ( cmdConnectionBecameInvalid : )
2012-02-19 11:59:22 +00:00
name : NSConnectionDidDieNotification
2020-06-24 12:26:31 +00:00
object : EcProcConnection ] ;
2012-02-19 11:59:22 +00:00
2013-11-04 07:28:57 +00:00
[ self _connectionRegistered ] ;
2018-11-09 15:25:23 +00:00
/ * Called to permit subclasses to initialise before entering run loop .
* /
[ self ecAwaken ] ;
2023-10-10 13:21:36 +00:00
ecDidAwakenCompletely = YES ;
2018-11-09 15:25:23 +00:00
RELEASE ( arp ) ;
arp = [ NSAutoreleasePool new ] ;
2017-10-26 14:12:33 +00:00
[ self cmdAudit : @ "Started '%@' in %g seconds" ,
[ self cmdName ] , [ NSDate timeIntervalSinceReferenceDate ] - initAt ] ;
2014-05-16 16:39:05 +00:00
[ self cmdFlushLogs ] ;
2014-05-16 22:13:43 +00:00
cmdIsRunning = YES ;
2012-02-19 11:59:22 +00:00
2017-06-23 11:01:03 +00:00
[ self triggerCmdTimeout ] ; / * make sure that regular timers run . * /
2020-07-03 10:50:25 +00:00
NS_DURING
[ cmdServer woken : self ] ;
NS_HANDLER
DESTROY ( cmdServer ) ;
NS_ENDHANDLER
if ( nil = = cmdServer )
{
DESTROY ( cmdLast ) ; // Allow immediate retry
[ self cmdNewServer ] ;
}
2012-12-06 08:08:39 +00:00
loop = [ NSRunLoop currentRunLoop ] ;
2016-11-29 14:14:05 +00:00
future = [ NSDate distantFuture ] ;
2012-12-06 08:08:39 +00:00
while ( YES = = [ EcProcConnection isValid ] )
2012-02-19 11:59:22 +00:00
{
NS_DURING
{
2012-12-06 08:08:39 +00:00
NSDate * d = [ loop limitDateForMode : NSDefaultRunLoopMode ] ;
2012-12-06 08:10:57 +00:00
if ( 0 = = cmdSignalled )
{
2016-11-29 14:14:05 +00:00
if ( nil = = d )
{
d = future ;
}
2012-12-06 08:10:57 +00:00
[ loop acceptInputForMode : NSDefaultRunLoopMode beforeDate : d ] ;
}
2012-12-06 08:08:39 +00:00
if ( 0 ! = cmdSignalled )
2012-02-19 14:42:50 +00:00
{
2012-12-06 08:08:39 +00:00
int sig = cmdSignalled ;
2017-11-02 09:19:57 +00:00
NSString * shutdown ;
2012-12-06 08:08:39 +00:00
2017-11-02 09:19:57 +00:00
shutdown
= [ NSString stringWithFormat : @ "signal %d received" , sig ] ;
2012-12-06 08:08:39 +00:00
cmdSignalled = 0 ;
2017-11-02 16:16:21 +00:00
[ self ecQuitFor : shutdown with : sig ] ;
2012-02-19 14:42:50 +00:00
}
2012-02-19 11:59:22 +00:00
}
NS_HANDLER
{
2020-03-23 13:35:58 +00:00
[ self cmdAlert : @ "Problem running server: %@" , localException ] ;
2017-05-09 16:07:51 +00:00
[ NSThread sleepForTimeInterval : 1.0 ] ;
2012-02-19 11:59:22 +00:00
}
NS_ENDHANDLER ;
2012-12-06 15:41:36 +00:00
[ arp emptyPool ] ;
2012-02-19 11:59:22 +00:00
}
2012-12-06 08:08:39 +00:00
[ arp release ] ;
2012-02-19 11:59:22 +00:00
/ * finish server * /
2017-11-02 16:16:21 +00:00
[ self ecQuitFor : nil with : 0 ] ;
2014-05-16 22:13:43 +00:00
cmdIsRunning = NO ;
2012-10-12 17:20:45 +00:00
DESTROY ( EcProcConnection ) ;
2012-02-19 11:59:22 +00:00
return 0 ;
}
2019-05-31 16:33:53 +00:00
- ( void ) ecReconnect
{
if ( NO = = [ NSThread isMainThread ]
|| NO = = [ [ [ NSRunLoop currentRunLoop ] currentMode ]
isEqual : NSDefaultRunLoopMode ] )
{
NSArray * modes = [ NSArray arrayWithObject : NSDefaultRunLoopMode ] ;
[ self performSelectorOnMainThread : _cmd
withObject : nil
waitUntilDone : NO
modes : modes ] ;
}
else
{
NS_DURING
[ cmdServer registerClient : self
2020-07-03 10:50:25 +00:00
identifier : [ self processIdentifier ]
2019-05-31 16:33:53 +00:00
name : cmdLogName ( )
transient : cmdIsTransient ] ;
NS_HANDLER
DESTROY ( cmdServer ) ;
NS_ENDHANDLER
if ( nil = = cmdServer )
{
DESTROY ( cmdLast ) ; // Allow immediate retry
[ self cmdNewServer ] ;
}
}
}
2013-03-18 07:42:41 +00:00
- ( void ) ecTestLog : ( NSString * ) fmt arguments : ( va_list ) args
{
if ( YES = = cmdFlagTesting )
{
NSLogv ( fmt , args ) ;
}
}
- ( void ) ecTestLog : ( NSString * ) fmt , . . .
{
if ( YES = = cmdFlagTesting )
{
va_list ap ;
va_start ( ap , fmt ) ;
[ self ecTestLog : fmt arguments : ap ] ;
va_end ( ap ) ;
}
}
2020-05-18 11:34:43 +00:00
- ( void ) ecUpdateRegisteredDefaults
{
NSDictionary * d ;
[ ecLock lock ] ;
d = [ cmdDefs volatileDomainForName : NSRegistrationDomain ] ;
d = [ EcDefaultRegistration merge : d ] ;
[ cmdDefs registerDefaults : d ] ;
[ ecLock unlock ] ;
}
2013-07-12 19:05:52 +00:00
- ( NSString * ) ecUserDirectory
{
return cmdUserDir ( ) ;
}
2012-02-19 11:59:22 +00:00
- ( void ) setCmdDebug : ( NSString * ) mode withDescription : ( NSString * ) desc
{
[ cmdDebugKnown setObject : desc forKey : mode ] ;
2017-05-16 11:43:29 +00:00
if ( YES = = [ cmdDefs boolForKey : [ @ "Debug-" stringByAppendingString : mode ] ] )
{
[ cmdDebugModes addObject : mode ] ;
}
else
{
[ cmdDebugModes removeObject : mode ] ;
}
2012-02-19 11:59:22 +00:00
}
- ( void ) setCmdTimeout : ( SEL ) sel
{
cmdTimSelector = sel ;
[ self triggerCmdTimeout ] ;
}
- ( void ) triggerCmdTimeout
{
2015-05-14 13:47:30 +00:00
if ( NO = = [ NSThread isMainThread ] )
{
[ self performSelectorOnMainThread : _cmd
withObject : nil
waitUntilDone : NO ] ;
return ;
}
2012-02-19 11:59:22 +00:00
if ( cmdPTimer ! = nil )
{
/ *
* If the timer is due to go off soon - don ' t reset it -
* continually resetting could lead to it never firing .
* /
if ( [ [ cmdPTimer fireDate ] timeIntervalSinceNow ] <= 0.01 )
{
return ;
}
[ cmdPTimer invalidate ] ;
cmdPTimer = nil ;
}
cmdPTimer = [ NSTimer scheduledTimerWithTimeInterval : 0.001
target : self
selector : @ selector ( _timedOut : )
userInfo : nil
repeats : NO ] ;
}
- ( BOOL ) cmdDebugMode : ( NSString * ) mode
{
if ( [ cmdDebugModes member : mode ] = = nil )
return NO ;
return YES ;
}
- ( void ) cmdDebugMode : ( NSString * ) mode active : ( BOOL ) flag
{
if ( ( mode = findMode ( cmdDebugKnown , mode ) ) ! = nil )
{
if ( flag = = YES && [ cmdDebugModes member : mode ] = = nil )
{
[ cmdDebugModes addObject : mode ] ;
}
if ( flag = = NO && [ cmdDebugModes member : mode ] ! = nil )
{
[ cmdDebugModes removeObject : mode ] ;
}
}
}
2015-01-26 12:50:42 +00:00
- ( oneway void ) cmdGnip : ( id < CmdPing > ) from
sequence : ( unsigned ) num
extra : ( in bycopy NSData * ) data
2012-02-19 11:59:22 +00:00
{
[ self cmdDbg : cmdConnectDbg msg : @ "cmdGnip: %lx sequence: %u extra: %lx" ,
( unsigned long ) from , num , ( unsigned long ) data ] ;
}
- ( BOOL ) cmdIsConnected
{
return cmdServer ! = nil ;
}
- ( BOOL ) cmdMatch : ( NSString * ) val toKey : ( NSString * ) key
{
unsigned int len = [ val length ] ;
if ( len = = 0 )
{
return NO ;
}
if ( len > [ key length ] )
{
return NO ;
}
if ( [ key compare : val
options : NSCaseInsensitiveSearch | NSLiteralSearch
range : NSMakeRange ( 0 , len ) ] ! = NSOrderedSame )
{
return NO ;
}
return YES ;
}
2012-07-05 13:15:27 +00:00
- ( void ) cmdMesgCache
{
NSEnumerator * enumerator ;
NSString * name ;
/ * The cmdActions set contains the names of all the commands this
* instance will accept from the Command server . These are methods
* taking an array of strings as an argument and returning a string
* as their result . All have names of the form cmdMesgXXX : where
* XXX is the ( lowercase ) command .
* /
[ ecLock lock ] ;
if ( nil = = cmdActions )
{
cmdActions = [ NSMutableSet new ] ;
}
[ cmdActions removeAllObjects ] ;
enumerator = [ GSObjCMethodNames ( self , YES ) objectEnumerator ] ;
while ( nil ! = ( name = [ enumerator nextObject ] ) )
{
NSRange r = [ name rangeOfString : @ ":" ] ;
if ( [ name hasPrefix : @ "cmdMesg" ] && 1 = = r . length && r . location > 7 )
{
name = [ name substringWithRange : NSMakeRange ( 7 , r . location - 7 ) ] ;
if ( YES = = [ name isEqual : [ name lowercaseString ] ] )
{
[ cmdActions addObject : name ] ;
}
}
}
2012-07-09 10:18:24 +00:00
[ ecLock unlock ] ;
2012-07-05 13:15:27 +00:00
}
2023-03-08 14:43:48 +00:00
- ( void ) ecOperators : ( NSDictionary * ) dict
{
if ( NO = = [ dict isKindOfClass : [ NSDictionary class ] ] )
{
dict = nil ;
}
[ ecLock lock ] ;
ASSIGN ( ecOperators , dict ) ;
[ ecLock unlock ] ;
}
2023-03-10 16:01:19 +00:00
- ( NSArray * ) ecCommands : ( NSString * ) operator
2023-03-08 14:43:48 +00:00
{
2023-03-10 16:01:19 +00:00
static NSArray * empty = nil ;
NSArray * allow = nil ;
NSString * name ;
id obj ;
2023-03-08 14:43:48 +00:00
if ( nil = = operator )
{
name = @ "" ;
}
else
{
NSRange r = [ operator rangeOfString : @ ":" ] ;
if ( r . length > 0 )
{
name = [ operator substringToIndex : r . location ] ;
}
else
{
name = operator ;
}
}
[ ecLock lock ] ;
2023-03-10 16:01:19 +00:00
if ( nil = = empty )
{
empty = [ NSArray new ] ;
}
2023-03-08 14:43:48 +00:00
obj = [ ecOperators objectForKey : name ] ;
2023-03-10 16:01:19 +00:00
if ( NO = = [ obj isKindOfClass : [ NSDictionary class ] ] )
2023-03-08 14:43:48 +00:00
{
2023-03-10 16:01:19 +00:00
NSLog ( @ "Operator '%@' not found; no access to commands" , operator ) ;
obj = empty ;
2023-03-08 14:43:48 +00:00
}
2023-03-10 16:01:19 +00:00
else if ( nil = = [ obj objectForKey : @ "Commands" ] && [ name length ] > 0 )
2023-03-08 14:43:48 +00:00
{
2023-03-10 16:01:19 +00:00
obj = [ ecOperators objectForKey : @ "" ] ;
if ( NO = = [ obj isKindOfClass : [ NSDictionary class ] ] )
{
obj = nil ; // Non - dictionary default entry ignored .
}
2023-03-08 14:43:48 +00:00
}
2023-03-10 16:01:19 +00:00
if ( [ obj isKindOfClass : [ NSDictionary class ] ] )
2023-03-08 14:43:48 +00:00
{
2023-03-10 16:01:19 +00:00
obj = [ obj objectForKey : @ "Commands" ] ;
if ( [ obj isKindOfClass : [ NSString class ] ] )
2023-03-08 14:43:48 +00:00
{
2023-03-10 16:01:19 +00:00
/ * A string is the name to get the Commands of another agent .
* /
name = ( NSString * ) obj ;
obj = [ ecOperators objectForKey : name ] ;
if ( [ obj isKindOfClass : [ NSDictionary class ] ] )
{
obj = [ obj objectForKey : @ "Commands" ] ;
if ( NO = = [ obj isKindOfClass : [ NSArray class ] ] )
{
NSLog ( @ "Operator '%@' Commands link to '%@' which does"
@ " not have Commands; no access to commands" ,
operator , name ) ;
obj = empty ;
}
}
else
{
NSLog ( @ "Operator '%@' Commands link to '%@' not found;"
@ " no access to commands" , operator , name ) ;
obj = empty ;
}
2023-03-08 14:43:48 +00:00
}
2023-03-10 16:01:19 +00:00
else if ( obj ! = nil && NO = = [ obj isKindOfClass : [ NSArray class ] ] )
2023-03-08 14:43:48 +00:00
{
2023-03-10 16:01:19 +00:00
NSLog ( @ "Operator '%@' Commands entry invalid;"
@ " no access to commands" , operator ) ;
obj = empty ;
2023-03-08 14:43:48 +00:00
}
}
2023-03-10 16:01:19 +00:00
allow = ( NSArray * ) AUTORELEASE ( RETAIN ( obj ) ) ;
2023-03-08 14:43:48 +00:00
[ ecLock unlock ] ;
2023-03-10 16:01:19 +00:00
return allow ;
2023-03-08 14:43:48 +00:00
}
- ( NSString * ) ecMesg : ( NSArray * ) msg from : ( NSString * ) operator
2012-02-19 11:59:22 +00:00
{
NSMutableString * saved ;
NSString * result ;
NSString * cmd ;
SEL sel ;
if ( msg = = nil || [ msg count ] < 1 )
{
return @ "no command specified\n" ;
}
2023-03-10 16:01:19 +00:00
cmd = findAction ( [ msg objectAtIndex : 0 ] , [ self ecCommands : operator ] ) ;
2012-02-19 11:59:22 +00:00
if ( nil = = cmd )
{
return @ "unrecognised command\n" ;
}
2023-03-08 14:43:48 +00:00
else if ( 0 = = [ cmd length ] )
{
return @ "blocked command\n" ;
}
2012-02-19 11:59:22 +00:00
sel = NSSelectorFromString ( [ NSString stringWithFormat : @ "cmdMesg%@:" , cmd ] ) ;
saved = replyBuffer ;
replyBuffer = [ NSMutableString stringWithCapacity : 50000 ] ;
NS_DURING
{
[ self performSelector : sel withObject : msg ] ;
}
NS_HANDLER
{
[ self cmdPrintf : @ "\n%@ during command\n" , localException ] ;
}
NS_ENDHANDLER
result = replyBuffer ;
replyBuffer = saved ;
return result ;
}
/ *
* Name - cmdMesgData : from :
* Purpose - Invoke other methods to handle commands .
* /
- ( void ) cmdMesgData : ( NSData * ) dat from : ( NSString * ) name
{
NSArray * msg ;
NSString * val ;
msg = [ NSPropertyListSerialization
propertyListWithData : dat
options : NSPropertyListMutableContainers
format : 0
error : 0 ] ;
2023-03-08 14:43:48 +00:00
val = [ self ecMesg : msg from : name ] ;
2012-02-19 11:59:22 +00:00
if ( cmdServer )
{
NS_DURING
{
2023-04-07 11:09:24 +00:00
[ cmdServer reply : val to : name from : ecFullName ( ) ] ;
2012-02-19 11:59:22 +00:00
}
NS_HANDLER
{
2013-11-04 07:28:57 +00:00
[ self _commandRemove ] ;
2012-02-19 11:59:22 +00:00
NSLog ( @ "Caught exception sending client reply to Command: %@ %@" ,
name , localException ) ;
}
NS_ENDHANDLER
}
}
2012-11-16 16:35:41 +00:00
- ( void ) cmdMesgalarms : ( NSArray * ) msg
{
if ( [ msg count ] = = 0 )
{
[ self cmdPrintf : @ "reports current alarms" ] ;
}
else
{
if ( [ [ msg objectAtIndex : 0 ] isEqualToString : @ "help" ] )
{
[ self cmdPrintf : @ "\nThe alarms command is used to report the" ] ;
[ self cmdPrintf : @ " alarms currently active for this process.\n" ] ;
2012-11-23 13:51:06 +00:00
[ self cmdPrintf : @ "NB. Each individual process identifies current" ] ;
[ self cmdPrintf : @ " alarms by address within the process.\n" ] ;
[ self cmdPrintf : @ "This differs from the Control server which" ] ;
[ self cmdPrintf : @ " uses a unique notification ID intended\n" ] ;
[ self cmdPrintf : @ "for working with external SNMP systems.\n" ] ;
2012-11-16 16:35:41 +00:00
}
else
{
2019-02-11 16:17:16 +00:00
NSArray * a = [ [ self ecAlarmDestination ] alarms ] ;
2012-11-16 16:35:41 +00:00
if ( 0 = = [ a count ] )
{
[ self cmdPrintf : @ "No alarms currently active.\n" ] ;
}
else
{
int i ;
a = [ a sortedArrayUsingSelector : @ selector ( compare : ) ] ;
[ self cmdPrintf : @ "Current alarms -\n" ] ;
for ( i = 0 ; i < [ a count ] ; i + + )
{
EcAlarm * alarm = [ a objectAtIndex : i ] ;
[ self cmdPrintf : @ "%@\n" , [ alarm description ] ] ;
}
}
}
}
}
2012-02-19 11:59:22 +00:00
- ( void ) cmdMesgarchive : ( NSArray * ) msg
{
if ( [ msg count ] = = 0 )
{
[ self cmdPrintf : @ "archives log files" ] ;
}
else
{
2012-11-16 16:35:41 +00:00
if ( [ [ msg objectAtIndex : 0 ] caseInsensitiveCompare : @ "help" ]
= = NSOrderedSame )
2012-02-19 11:59:22 +00:00
{
[ self cmdPrintf : @ "\nThe archive command is used to archive the" ] ;
[ self cmdPrintf : @ " debug file to a subdirectory.\n" ] ;
[ self cmdPrintf : @ "You should not need it - as archiving should" ] ;
[ self cmdPrintf : @ "be done automatically at midnight.\n" ] ;
}
else
{
2017-06-23 10:26:17 +00:00
[ self cmdPrintf : @ "\n%@\n" , [ self ecArchive : nil ] ] ;
2012-02-19 11:59:22 +00:00
}
}
}
2012-11-16 16:35:41 +00:00
- ( void ) cmdMesgclear : ( NSArray * ) msg
{
if ( [ msg count ] = = 0 )
{
[ self cmdPrintf : @ "clears current alarms" ] ;
}
else
{
if ( [ [ msg objectAtIndex : 0 ] isEqualToString : @ "help" ] )
{
[ self cmdPrintf : @ "\nThe clear command is used to clear the" ] ;
[ self cmdPrintf : @ " alarms currently active for this process.\n" ] ;
2012-11-23 12:45:09 +00:00
[ self cmdPrintf : @ "You may use the word 'all' or a space separated" ] ;
2012-11-23 13:51:06 +00:00
[ self cmdPrintf : @ " list of alarm addresses.\n" ] ;
[ self cmdPrintf : @ "NB. Each individual process identifies current" ] ;
[ self cmdPrintf : @ " alarms by address within the process.\n" ] ;
[ self cmdPrintf : @ "This differs from the Control server which" ] ;
[ self cmdPrintf : @ " uses a unique notification ID intended\n" ] ;
[ self cmdPrintf : @ "for working with external SNMP systems.\n" ] ;
2019-05-28 15:17:03 +00:00
[ self cmdPrintf : @ "Clearing an alarm in this process will also" ] ;
[ self cmdPrintf : @ "clear it in the Control server.\n" ] ;
2012-11-16 16:35:41 +00:00
}
else
{
2019-02-11 16:17:16 +00:00
NSArray * a = [ [ self ecAlarmDestination ] alarms ] ;
2012-11-16 16:35:41 +00:00
NSUInteger count = [ msg count ] ;
if ( count < 2 )
{
[ self cmdPrintf : @ "The 'clear' command requires an alarm"
2023-01-03 10:49:06 +00:00
@ " address or the word all\n" ] ;
2012-11-16 16:35:41 +00:00
}
else
{
NSUInteger alarmCount = [ a count ] ;
EcAlarm * alarm ;
NSUInteger index ;
for ( index = 1 ; index < count ; index + + )
{
2015-07-03 07:32:18 +00:00
NSUInteger addr ;
2012-11-16 16:35:41 +00:00
NSString * arg = [ msg objectAtIndex : index ] ;
if ( [ arg caseInsensitiveCompare : _ ( @ "all" ) ]
= = NSOrderedSame )
{
NSUInteger i ;
for ( i = 0 ; i < alarmCount ; i + + )
{
alarm = [ a objectAtIndex : i ] ;
[ self cmdPrintf : @ "Clearing %@\n" , alarm ] ;
alarm = [ alarm clear ] ;
2019-02-11 16:17:16 +00:00
[ [ self ecAlarmDestination ] alarm : alarm ] ;
2012-11-16 16:35:41 +00:00
}
}
2015-07-03 07:32:18 +00:00
else if ( 1 = = sscanf ( [ arg UTF8String ] , "%" PRIxPTR , & addr ) )
2012-11-16 16:35:41 +00:00
{
NSUInteger i ;
alarm = nil ;
for ( i = 0 ; i < alarmCount ; i + + )
{
alarm = [ a objectAtIndex : i ] ;
2015-07-03 07:32:18 +00:00
if ( ( NSUInteger ) alarm = = addr )
2012-11-16 16:35:41 +00:00
{
break ;
}
alarm = nil ;
}
if ( nil = = alarm )
{
[ self cmdPrintf :
2012-11-23 12:45:09 +00:00
@ "No alarm found with the address '%@'\n" ,
2012-11-16 16:35:41 +00:00
arg ] ;
}
else
{
[ self cmdPrintf : @ "Clearing %@\n" , alarm ] ;
alarm = [ alarm clear ] ;
2019-02-11 16:17:16 +00:00
[ [ self ecAlarmDestination ] alarm : alarm ] ;
2012-11-16 16:35:41 +00:00
}
}
2012-11-23 12:45:09 +00:00
else
{
[ self cmdPrintf : @ "Not a hexadecimal address: '%@'\n" ,
arg ] ;
}
2012-11-16 16:35:41 +00:00
}
}
}
}
}
2012-02-19 11:59:22 +00:00
- ( void ) cmdMesgdebug : ( NSArray * ) msg
{
if ( [ msg count ] = = 0 )
{
[ self cmdPrintf : @ "turns on debug logging" ] ;
}
else
{
2012-11-16 16:35:41 +00:00
if ( [ [ msg objectAtIndex : 0 ] caseInsensitiveCompare : @ "help" ]
= = NSOrderedSame )
2012-02-19 11:59:22 +00:00
{
[ self cmdPrintf : @ "\nWithout parameters, the debug command is " ] ;
[ self cmdPrintf : @ "used to list the currently active " ] ;
[ self cmdPrintf : @ "debug modes.\n" ] ;
[ self cmdPrintf : @ "With the single parameter 'default', the debug " ] ;
[ self cmdPrintf : @ "command is used to revert to default " ] ;
[ self cmdPrintf : @ "debug settings.\n" ] ;
[ self cmdPrintf : @ "With the single parameter 'all', the debug " ] ;
[ self cmdPrintf : @ "command is used to activate all " ] ;
[ self cmdPrintf : @ "debugging.\n" ] ;
[ self cmdPrintf : @ "With any other parameter, the debug command " ] ;
[ self cmdPrintf : @ "is used to activate one of the " ] ;
[ self cmdPrintf : @ "debug modes listed below.\n\n" ] ;
[ self cmdPrintf : @ "%@\n" , cmdDebugKnown ] ;
}
2019-10-05 14:43:58 +00:00
else if ( YES = = cmdKillDebug )
{
[ self cmdPrintf :
@ "All output to STDERR suppressed by KillDebugOutput=YES\n" ] ;
}
2012-02-19 11:59:22 +00:00
else if ( [ msg count ] > 1 )
{
NSString * mode = ( NSString * ) [ msg objectAtIndex : 1 ] ;
NSString * key ;
if ( [ mode caseInsensitiveCompare : @ "default" ] = = NSOrderedSame )
{
NSEnumerator * enumerator = [ cmdDebugKnown keyEnumerator ] ;
while ( nil ! = ( mode = [ enumerator nextObject ] ) )
{
key = [ @ "Debug-" stringByAppendingString : mode ] ;
2020-07-08 15:48:26 +00:00
[ cmdDefs setCommand : nil forKey : [ cmdDefs key : key ] ] ;
2012-02-19 11:59:22 +00:00
}
[ self cmdPrintf : @ "Now using debug settings from config.\n" ] ;
}
else if ( [ mode caseInsensitiveCompare : @ "all" ] = = NSOrderedSame )
{
NSEnumerator * enumerator = [ cmdDebugKnown keyEnumerator ] ;
while ( nil ! = ( mode = [ enumerator nextObject ] ) )
{
key = [ @ "Debug-" stringByAppendingString : mode ] ;
2020-07-08 15:48:26 +00:00
[ cmdDefs setCommand : @ "YES" forKey : [ cmdDefs key : key ] ] ;
2012-02-19 11:59:22 +00:00
}
[ self cmdPrintf : @ "All debugging is now active.\n" ] ;
}
else
{
[ self cmdPrintf : @ "debug mode '" ] ;
if ( ( mode = findMode ( cmdDebugKnown , mode ) ) = = nil )
{
[ self cmdPrintf : @ "%@' is not known.\n" , mode ] ;
}
else
{
[ self cmdPrintf : @ "%@" , mode ] ;
if ( [ cmdDebugModes member : mode ] = = nil )
{
[ self cmdPrintf : @ "' is now active." ] ;
}
else
{
[ self cmdPrintf : @ "' is already active." ] ;
}
key = [ @ "Debug-" stringByAppendingString : mode ] ;
2020-07-08 15:48:26 +00:00
[ cmdDefs setCommand : @ "YES" forKey : [ cmdDefs key : key ] ] ;
2012-02-19 11:59:22 +00:00
}
}
}
else
{
[ self cmdPrintf : @ "%@\n" , [ EcLogger loggerForType : LT_DEBUG ] ] ;
[ self cmdPrintf : @ "Current active debug modes -\n" ] ;
if ( [ cmdDebugModes count ] = = 0 )
{
[ self cmdPrintf : @ "\nNone.\n" ] ;
}
else
{
[ self cmdPrintf : @ "%@\n" , cmdDebugModes ] ;
}
}
}
}
2015-07-21 13:32:22 +00:00
- ( void ) cmdMesgdefaults : ( NSArray * ) msg
{
if ( [ msg count ] = = 0 )
{
[ self cmdPrintf :
@ "temporarily overrides defaults/Control.plist settings" ] ;
}
else
{
if ( [ [ msg objectAtIndex : 0 ] caseInsensitiveCompare : @ "help" ]
= = NSOrderedSame )
{
2016-03-24 08:50:16 +00:00
[ self cmdPrintf : @ "\nWithout parameters,\n the defaults command is" ] ;
[ self cmdPrintf : @ " used to list the current defaults overrides.\n" ] ;
[ self cmdPrintf : @ "With the 'delete' parameter followed by a name," ] ;
[ self cmdPrintf : @ "\n removes an override.\n" ] ;
2019-05-27 15:24:23 +00:00
[ self cmdPrintf : @ "With the 'life' parameter followed by a number\n" ] ;
[ self cmdPrintf : @ " of hours (1 to 168), a name, and a value,\n" ] ;
[ self cmdPrintf : @ " sets an override of the default.\n" ] ;
2015-11-18 16:16:10 +00:00
[ self cmdPrintf : @ "With the 'write' parameter followed by a name " ] ;
2016-03-24 08:50:16 +00:00
[ self cmdPrintf : @ "and value,\n sets an override of the default.\n" ] ;
[ self cmdPrintf : @ "With the 'read' parameter followed by a name,\n" ] ;
[ self cmdPrintf : @ " shows the effective default after overrides.\n" ] ;
[ self cmdPrintf : @ "With the 'revert' parameter,\n the command" ] ;
[ self cmdPrintf : @ " is used to revert all overides.\n" ] ;
[ self cmdPrintf : @ "With the 'list' parameter,\n this lists" ] ;
2018-01-19 09:50:43 +00:00
[ self cmdPrintf : @ " registered (not all) defaults names.\n" ] ;
[ self cmdPrintf : @ "With the 'list' parameter followed by a name,\n" ] ;
[ self cmdPrintf : @ " shows the help for the specified default.\n" ] ;
2019-05-27 15:38:47 +00:00
[ self cmdPrintf : @ "With the 'show' parameter,\n" ] ;
[ self cmdPrintf : @ " shows all current names and lifes.\n" ] ;
2015-07-21 13:32:22 +00:00
}
2015-11-18 16:16:10 +00:00
else if ( [ msg count ] > 1 && [ [ msg objectAtIndex : 1 ] isEqual : @ "list" ] )
{
2018-01-19 09:50:43 +00:00
NSString * key = nil ;
if ( [ msg count ] > 2 )
{
key = [ msg objectAtIndex : 2 ] ;
}
[ self cmdPrintf : @ "%@" , [ EcDefaultRegistration listHelp : key ] ] ;
2015-11-18 16:16:10 +00:00
}
2015-10-14 11:25:48 +00:00
else if ( [ msg count ] > 1 && [ [ msg objectAtIndex : 1 ] isEqual : @ "revert" ] )
{
[ cmdDefs revertSettings ] ;
2016-03-24 08:50:16 +00:00
[ self cmdPrintf : @ "All override settings are removed.\n" ] ;
2015-10-14 11:25:48 +00:00
}
2019-05-27 15:38:47 +00:00
else if ( [ msg count ] > 1 && [ [ msg objectAtIndex : 1 ] isEqual : @ "show" ] )
{
NSDictionary * d = [ cmdDefs commandExpiries ] ;
if ( [ d count ] = = 0 )
{
[ self cmdPrintf : @ "There are currently no default overrides.\n" ] ;
}
else
{
[ self cmdPrintf : @ "Current default overrides: %@\n" ,
[ d descriptionWithLocale : [ NSLocale autoupdatingCurrentLocale ]
indent : 0 ] ] ;
}
}
2015-07-21 13:32:22 +00:00
else if ( [ msg count ] > 2 )
{
NSString * mode = ( NSString * ) [ msg objectAtIndex : 1 ] ;
NSString * key = ( NSString * ) [ msg objectAtIndex : 2 ] ;
2019-05-27 15:24:23 +00:00
unsigned hours = 0 ;
2015-11-18 10:43:24 +00:00
id old ;
2015-07-21 13:32:22 +00:00
id val ;
2019-05-27 15:24:23 +00:00
/ * Lifetime may be from 1 to 168 hours
* /
if ( [ msg count ] > 3 && [ key length ] > 0
&& ( hours = [ key intValue ] ) > 0 && hours <= 168 )
{
key = ( NSString * ) [ msg objectAtIndex : 3 ] ;
}
2015-11-18 10:43:24 +00:00
old = [ cmdDefs objectForKey : key ] ;
2015-07-21 13:32:22 +00:00
if ( [ mode caseInsensitiveCompare : @ "delete" ] = = NSOrderedSame )
{
2018-03-03 18:36:01 +00:00
if ( [ key isEqualToString : ecControlKey ] )
{
[ self cmdPrintf : @ "%@ can only be set on startup.\n" , key ] ;
2019-03-14 14:32:59 +00:00
return ;
}
else if ( [ key isEqualToString : @ "KillDebugOutput" ] )
{
[ self cmdPrintf : @ "%@ can not be overridden.\n" , key ] ;
return ;
2018-03-03 18:36:01 +00:00
}
else
{
2023-03-08 14:59:23 +00:00
[ cmdDefs setCommand : nil forKey : [ cmdDefs key : key ] ] ;
2018-03-03 18:36:01 +00:00
val = [ cmdDefs objectForKey : key ] ;
}
2015-07-21 13:32:22 +00:00
}
2019-05-27 15:24:23 +00:00
else if ( hours > 0
&& [ mode caseInsensitiveCompare : @ "life" ] = = NSOrderedSame )
{
if ( [ key isEqualToString : ecControlKey ] )
{
[ self cmdPrintf : @ "%@ can only be set on startup.\n" , key ] ;
return ;
}
else if ( [ key isEqualToString : @ "KillDebugOutput" ] )
{
[ self cmdPrintf : @ "%@ can not be overridden.\n" , key ] ;
return ;
}
else if ( [ msg count ] = = 5 )
{
NSTimeInterval t = hours * 60.0 * 60.0 ;
val = [ msg objectAtIndex : 4 ] ;
2023-03-08 14:59:23 +00:00
[ cmdDefs setCommand : val
forKey : [ cmdDefs key : key ]
lifetime : t ] ;
2019-05-27 15:24:23 +00:00
val = [ cmdDefs objectForKey : key ] ;
}
else if ( [ msg count ] = = 4 )
{
[ self cmdPrintf : @ "Missing value for '%@ %@' (no effect).\n" ,
mode , key ] ;
val = old ;
}
else
{
[ self cmdPrintf : @ "Too many values for '%@ %@' (ignored).\n" ,
mode , key ] ;
val = old ;
}
}
2015-12-18 15:27:24 +00:00
else if ( [ mode caseInsensitiveCompare : @ "write" ] = = NSOrderedSame
2016-03-24 08:50:16 +00:00
|| [ mode caseInsensitiveCompare : @ "set" ] = = NSOrderedSame )
2015-07-21 13:32:22 +00:00
{
2018-03-03 18:36:01 +00:00
if ( [ key isEqualToString : ecControlKey ] )
{
[ self cmdPrintf : @ "%@ can only be set on startup.\n" , key ] ;
2019-03-14 14:32:59 +00:00
return ;
}
else if ( [ key isEqualToString : @ "KillDebugOutput" ] )
{
[ self cmdPrintf : @ "%@ can not be overridden.\n" , key ] ;
return ;
2018-03-03 18:36:01 +00:00
}
else if ( [ msg count ] = = 4 )
2015-12-18 15:27:24 +00:00
{
val = [ msg objectAtIndex : 3 ] ;
2023-03-08 14:59:23 +00:00
[ cmdDefs setCommand : val forKey : [ cmdDefs key : key ] ] ;
2015-12-18 15:27:24 +00:00
val = [ cmdDefs objectForKey : key ] ;
}
else if ( [ msg count ] = = 3 )
{
[ self cmdPrintf : @ "Missing value for '%@ %@' (no effect).\n" ,
mode , key ] ;
val = old ;
}
else
{
[ self cmdPrintf : @ "Too many values for '%@ %@' (ignored).\n" ,
mode , key ] ;
val = old ;
}
2015-07-21 13:32:22 +00:00
}
2015-12-18 15:27:24 +00:00
else if ( [ mode caseInsensitiveCompare : @ "read" ] = = NSOrderedSame
|| [ mode caseInsensitiveCompare : @ "get" ] = = NSOrderedSame )
2015-11-18 11:42:21 +00:00
{
2018-03-03 18:36:01 +00:00
if ( [ key isEqualToString : ecControlKey ] )
{
[ self cmdPrintf : @ "%@ can not be displayed.\n" , key ] ;
val = nil ;
}
else
{
val = [ cmdDefs objectForKey : key ] ;
}
2015-11-18 11:42:21 +00:00
}
2015-11-18 11:07:08 +00:00
else
{
2015-11-18 11:42:21 +00:00
/ * To be tolerant of typing errors and maintain backward
* compatibility , anything else is treated as a ' read '
2015-11-18 11:25:49 +00:00
* /
2015-11-18 11:42:21 +00:00
[ self cmdPrintf : @ "Unrecognised command '%@' (assume 'read').\n" ,
key ] ;
2015-11-18 11:25:49 +00:00
val = [ cmdDefs objectForKey : key ] ;
2015-11-18 11:07:08 +00:00
}
2018-03-03 18:36:01 +00:00
if ( [ key isEqualToString : ecControlKey ] )
{
if ( [ old length ] = = 0 )
{
[ self cmdPrintf : @ "%@ is not set.\n" , key ] ;
}
else
{
[ self cmdPrintf : @ "%@ was set on startup.\n" , key ] ;
}
}
else if ( val = = old || [ val isEqual : old ] )
2015-11-18 10:43:24 +00:00
{
if ( nil = = val )
{
2016-03-24 08:50:16 +00:00
[ self cmdPrintf :
@ "The override setting for the default '%@' is"
2015-11-18 10:43:24 +00:00
@ " unchanged (and not set).\n" , key ] ;
}
else
{
2016-03-24 08:50:16 +00:00
[ self cmdPrintf :
@ "The override setting for the default '%@' is"
2015-11-18 10:43:24 +00:00
@ " unchanged (%@).\n" , key , val ] ;
}
}
else if ( nil = = val )
2015-07-21 13:47:50 +00:00
{
2016-03-24 08:50:16 +00:00
[ self cmdPrintf : @ "The override setting for the default '%@' is"
@ " deleted (was %@).\n" , key , old ] ;
2015-07-21 13:47:50 +00:00
}
2015-11-18 12:46:57 +00:00
else if ( nil = = old )
{
2016-03-24 08:50:16 +00:00
[ self cmdPrintf : @ "The override setting for the default '%@' is"
2015-11-18 12:46:57 +00:00
@ " set to: %@ (was not set).\n" , key , val ] ;
}
2015-07-21 13:47:50 +00:00
else
{
2016-03-24 08:50:16 +00:00
[ self cmdPrintf : @ "The override setting for the default '%@' is"
2015-11-18 10:43:24 +00:00
@ " set to: %@ (was %@).\n" , key , val , old ] ;
2015-07-21 13:47:50 +00:00
}
2015-07-21 13:32:22 +00:00
}
else
{
NSDictionary * d = [ cmdDefs volatileDomainForName : @ "EcCommand" ] ;
2015-07-21 13:50:21 +00:00
NSArray * a ;
NSEnumerator * e ;
2015-07-21 13:32:22 +00:00
NSString * k ;
2016-03-24 08:50:16 +00:00
[ self cmdPrintf : @ "Console temporary overrides of defaults:\n" ] ;
2015-07-21 13:50:21 +00:00
a = [ [ d allKeys ] sortedArrayUsingSelector : @ selector ( compare : ) ] ;
e = [ a objectEnumerator ] ;
2015-07-21 13:32:22 +00:00
k = [ e nextObject ] ;
if ( nil = = k )
{
[ self cmdPrintf : @ " None.\n" ] ;
}
else
{
while ( nil ! = k )
{
id v = [ d objectForKey : k ] ;
[ self cmdPrintf : @ " %@ = %@\n" , k , v ] ;
k = [ e nextObject ] ;
}
}
}
}
}
2012-02-19 11:59:22 +00:00
- ( void ) cmdMesghelp : ( NSArray * ) msg
{
2012-07-05 13:15:27 +00:00
NSEnumerator * e ;
2012-02-19 11:59:22 +00:00
NSString * cmd ;
SEL sel ;
2012-07-05 13:15:27 +00:00
[ ecLock lock ] ;
2017-07-11 08:32:59 +00:00
e = [ [ [ cmdActions allObjects ] sortedArrayUsingSelector : @ selector ( compare : ) ]
objectEnumerator ] ;
2012-07-05 13:15:27 +00:00
[ ecLock unlock ] ;
2012-02-19 11:59:22 +00:00
if ( [ msg count ] = = 0 )
{
[ self cmdPrintf : @ "provides helpful information :-)" ] ;
return ;
}
else if ( [ msg count ] > 1 )
{
NSString * found ;
cmd = [ msg objectAtIndex : 1 ] ;
2023-03-08 14:43:48 +00:00
found = findAction ( cmd , nil ) ;
2012-02-19 11:59:22 +00:00
if ( [ cmd caseInsensitiveCompare : @ "control" ] = = NSOrderedSame )
{
[ self cmdPrintf : @ "Detailed help on the 'control' command -\n" ] ;
[ self cmdPrintf : @ "This command enables you to send an" ] ;
[ self cmdPrintf : @ "instruction to the 'Control' server rather\n" ] ;
[ self cmdPrintf : @ "than to the currently connected server.\n" ] ;
[ self cmdPrintf : @ "Everything typed on the line after the word" ] ;
[ self cmdPrintf : @ " 'control' is treated as a command to\n" ] ;
[ self cmdPrintf : @ "the 'Control' server process.\n" ] ;
[ self cmdPrintf : @ "\nTo disconnect from the server type -\n" ] ;
[ self cmdPrintf : @ " control connect\n" ] ;
[ self cmdPrintf : @ "\nTo disconnect from the host type -\n" ] ;
[ self cmdPrintf : @ " control host\n" ] ;
return ;
}
else if ( nil = = found )
{
[ self cmdPrintf : @ "Unable to find the '%@' command -\n" , cmd ] ;
}
2023-03-08 14:43:48 +00:00
else if ( [ found length ] = = 0 )
{
[ self cmdPrintf : @ "The '%@' command is blocked -\n" , cmd ] ;
}
2012-02-19 11:59:22 +00:00
else if ( [ found caseInsensitiveCompare : @ "help" ] ! = NSOrderedSame )
{
NSMutableArray * m ;
[ self cmdPrintf : @ "Detailed help on the '%@' command -\n" , found ] ;
sel = NSSelectorFromString (
[ NSString stringWithFormat : @ "cmdMesg%@:" , found ] ) ;
/ * To get the help on a command , we invoke that command
* by passing the command and arguments ( ie , the msg array ) .
* The command implementation should check the argument 0 -
* if it is "help" , it should print out help on itself .
* Save expanded ( unabbreviated ) commands so the methods
* getting the help request don ' t need to recheck the values .
* /
m = [ [ msg mutableCopy ] autorelease ] ;
[ m replaceObjectAtIndex : 0 withObject : @ "help" ] ;
[ m replaceObjectAtIndex : 1 withObject : found ] ;
[ self performSelector : sel withObject : m ] ;
return ;
}
}
[ self cmdPrintf : @ "\n" ] ;
[ self cmdPrintf : @ "For help on a particular command, type 'help <cmd>'\n" ] ;
[ self cmdPrintf : @ "\n" ] ;
[ self cmdPrintf : @ "These are the commands available to you -\n" ] ;
[ self cmdPrintf : @ "\n" ] ;
while ( ( cmd = [ e nextObject ] ) ! = nil )
{
unsigned l ;
sel = NSSelectorFromString (
[ NSString stringWithFormat : @ "cmdMesg%@:" , cmd ] ) ;
[ self cmdPrintf : @ "%@ - " , cmd ] ;
l = [ cmd length ] ;
while ( l + + < 9 )
{
[ self cmdPrintf : @ " " ] ;
}
[ self performSelector : sel withObject : nil ] ;
[ self cmdPrintf : @ "\n" ] ;
}
}
- ( void ) cmdMesgnodebug : ( NSArray * ) msg
{
if ( [ msg count ] = = 0 )
{
[ self cmdPrintf : @ "turns off debug logging" ] ;
}
else
{
2012-11-16 16:35:41 +00:00
if ( [ [ msg objectAtIndex : 0 ] caseInsensitiveCompare : @ "help" ]
= = NSOrderedSame )
2012-02-19 11:59:22 +00:00
{
[ self cmdPrintf : @ "\n" ] ;
[ self cmdPrintf : @ "Without parameters, the nodebug command is " ] ;
[ self cmdPrintf : @ "used to list the currently inactive\n" ] ;
[ self cmdPrintf : @ "debug modes.\n" ] ;
[ self cmdPrintf : @ "With the single parameter 'all', the nodebug " ] ;
[ self cmdPrintf : @ "command is used to deactivate all\n" ] ;
[ self cmdPrintf : @ "debugging.\n" ] ;
[ self cmdPrintf : @ "With the single parameter 'default', the " ] ;
[ self cmdPrintf : @ "nodebug command is used to revert to default " ] ;
[ self cmdPrintf : @ "debug settings.\n" ] ;
[ self cmdPrintf : @ "With any other parameter, the nodebug command is" ] ;
[ self cmdPrintf : @ " used to deactivate one of the\n" ] ;
[ self cmdPrintf : @ "debug modes listed below.\n" ] ;
[ self cmdPrintf : @ "\n" ] ;
[ self cmdPrintf : @ "%@\n" , cmdDebugKnown ] ;
}
else if ( [ msg count ] > 1 )
{
NSString * mode = ( NSString * ) [ msg objectAtIndex : 1 ] ;
NSString * key ;
if ( [ mode caseInsensitiveCompare : @ "default" ] = = NSOrderedSame )
{
NSEnumerator * enumerator = [ cmdDebugKnown keyEnumerator ] ;
while ( nil ! = ( mode = [ enumerator nextObject ] ) )
{
key = [ @ "Debug-" stringByAppendingString : mode ] ;
2020-07-08 15:48:26 +00:00
[ cmdDefs setCommand : nil forKey : [ cmdDefs key : key ] ] ;
2012-02-19 11:59:22 +00:00
}
[ self cmdPrintf : @ "Now using debug settings from config.\n" ] ;
}
else if ( [ mode caseInsensitiveCompare : @ "all" ] = = NSOrderedSame )
{
NSEnumerator * enumerator = [ cmdDebugKnown keyEnumerator ] ;
while ( nil ! = ( mode = [ enumerator nextObject ] ) )
{
key = [ @ "Debug-" stringByAppendingString : mode ] ;
2020-07-08 15:48:26 +00:00
[ cmdDefs setCommand : @ "NO" forKey : [ cmdDefs key : key ] ] ;
2012-02-19 11:59:22 +00:00
}
[ self cmdPrintf : @ "All debugging is now inactive.\n" ] ;
}
else
{
[ self cmdPrintf : @ "debug mode '" ] ;
if ( ( mode = findMode ( cmdDebugKnown , mode ) ) = = nil )
{
[ self cmdPrintf : @ "%@' is not known.\n" , mode ] ;
}
else
{
[ self cmdPrintf : @ "%@' is " , mode ] ;
if ( [ cmdDebugModes member : mode ] = = nil )
{
[ self cmdPrintf : @ "already inactive.\n" ] ;
}
else
{
[ self cmdPrintf : @ "now deactivated.\n" ] ;
}
key = [ @ "Debug-" stringByAppendingString : mode ] ;
2020-07-08 15:48:26 +00:00
[ cmdDefs setCommand : @ "NO" forKey : [ cmdDefs key : key ] ] ;
2012-02-19 11:59:22 +00:00
}
}
}
else
{
NSArray * a = [ cmdDebugKnown allKeys ] ;
NSMutableSet * s = [ NSMutableSet setWithArray : a ] ;
/ *
* Find items known but not active .
* /
[ s minusSet : cmdDebugModes ] ;
[ self cmdPrintf : @ "Current inactive debug modes -\n" ] ;
if ( a = = 0 )
{
[ self cmdPrintf : @ "none.\n" ] ;
}
else
{
[ self cmdPrintf : @ "%@\n" , s ] ;
}
}
}
}
- ( void ) cmdMesgmemory : ( NSArray * ) msg
{
2015-07-08 09:06:26 +00:00
if ( [ msg count ] = = 0 )
2012-02-19 11:59:22 +00:00
{
[ self cmdPrintf : @ "controls recording of memory management statistics" ] ;
}
else
{
2015-07-09 12:08:58 +00:00
[ self cmdPrintf : @ "\n%@ on %@ running since %@\n\n" ,
cmdLogName ( ) , ecHostName ( ) , [ self ecStarted ] ] ;
2012-11-16 16:35:41 +00:00
if ( [ [ msg objectAtIndex : 0 ] caseInsensitiveCompare : @ "help" ]
2015-07-08 09:06:26 +00:00
= = NSOrderedSame || ( [ msg count ] > 1
&& [ [ msg objectAtIndex : 1 ] caseInsensitiveCompare : @ "help" ]
= = NSOrderedSame ) )
2012-02-19 11:59:22 +00:00
{
2015-07-08 09:06:26 +00:00
[ self cmdPrintf : @ " \ n \
Without parameters , \ n \
the memory command is used to list the changes in the numbers of objects \ n \
allocated since the command was last issued . \ n \
With the single parameter ' all ' , \ n \
the memory command is used to list the cumulative totals of objects \ n \
2015-07-09 12:08:58 +00:00
allocated since the gathering of memory usage statistics was turned on . \ n \
With the single parameter ' current ' , \ n \
the memory command is used to list the current totals of objects \ n \
allocated ( and not deallocated ) since the gathering of memory usage \ n \
statistics was turned on . \ n \
2015-07-08 09:06:26 +00:00
With the single parameter ' yes ' , \ n \
the memory command is used to turn on gathering of memory usage statistics . \ n \
With the single parameter ' no ' , \ n \
the memory command is used to turn off gathering of memory usage statistics . \ n \
With the single parameter ' default ' , \ n \
2024-12-03 17:21:08 +00:00
the gathering of memory usage statistics reverts to the default setting . \ n " ] ;
2025-01-08 14:49:32 +00:00
if ( hasLSAN ( ) )
{
[ self cmdPrintf : @ " \
2024-12-03 17:21:08 +00:00
With the single parameter ' leakcheck ' , \ n \
2025-01-02 12:48:18 +00:00
performs a leak check using LeakAnalyzer pritning the results to the log . \ n " ] ;
2025-01-08 14:49:32 +00:00
}
else
{
[ self cmdPrintf : @ " \
2019-08-11 12:35:10 +00:00
With two parameters ( ' alarm ' and a severity name ) , \ n \
the threshold for severity of alarms ( warning , minor , major , critical ) . \ n \
Set to ' default ' to revert to the default . \ n \
2015-07-08 09:06:26 +00:00
With two parameters ( ' allowed ' and a number ) , \ n \
2019-08-11 12:35:10 +00:00
the base / allowed process memory size is set ( in MB ) . \ n \
2015-07-08 09:06:26 +00:00
Set to ' default ' to revert to the default . \ n \
2019-08-10 09:19:16 +00:00
With two parameters ( ' idle ' and a number ) , \ n \
the hour of the day when the process is considered idle is set . \ n \
Set to ' default ' to revert to the default . \ n \
2015-07-08 09:06:26 +00:00
With two parameters ( ' maximum ' and a number ) , \ n \
the maximum process size ( in MB ) is set . On reaching the limit , the \ n \
process restarts unless the limit is zero ( meaning no maximum ) . \ n \
Set to ' default ' to revert to the default . " ] ;
2025-01-08 14:49:32 +00:00
}
2025-01-02 12:48:18 +00:00
[ self cmdPrintf : @ " \
With two parameters ( ' class ' and a class name ) , \ n \
new instances of the class are recorded / traced . \ n \
With two parameters ( ' list ' and a class ) , \ n \
recorded instances of the class are reported . \ n " ] ;
2012-02-19 11:59:22 +00:00
[ self cmdPrintf : @ "\n" ] ;
2022-07-12 11:26:12 +00:00
return ;
2012-02-19 11:59:22 +00:00
}
2022-07-12 11:26:12 +00:00
# if defined ( __VALGRIND _MAJOR __ )
VALGRIND_PRINTF ( "Console 'memory' command %s\n" ,
[ [ msg description ] UTF8String ] ) ;
# endif
if ( [ msg count ] = = 2 )
2012-02-19 11:59:22 +00:00
{
2021-01-28 16:22:07 +00:00
NSString * word = [ [ msg objectAtIndex : 1 ] lowercaseString ] ;
NSString * s ;
2012-02-19 11:59:22 +00:00
2021-01-28 16:22:07 +00:00
if ( [ word isEqual : @ "current" ]
|| [ word isEqual : @ "cur" ] )
2015-07-09 12:08:58 +00:00
{
if ( NO = = [ cmdDefs boolForKey : @ "Memory" ] )
{
[ self cmdPrintf :
@ "Memory statistics were not being gathered.\n" ] ;
[ self cmdPrintf : @ "Memory statistics Will start from NOW.\n" ] ;
}
else
{
const char * list ;
list = ( const char * ) GSDebugAllocationList ( NO ) ;
[ self cmdPrintf : @ "Memory current stats at %@:\n%s" ,
[ NSDate date ] , list ] ;
}
2020-07-08 15:48:26 +00:00
[ cmdDefs setCommand : @ "YES" forKey : [ cmdDefs key : @ "Memory" ] ] ;
2015-07-09 12:08:58 +00:00
}
2021-01-28 16:22:07 +00:00
else if ( [ word isEqual : @ "default" ] || [ word isEqual : @ "def" ] )
2012-02-19 11:59:22 +00:00
{
2020-07-08 15:48:26 +00:00
[ cmdDefs setCommand : nil forKey : [ cmdDefs key : @ "Memory" ] ] ;
2012-02-19 11:59:22 +00:00
[ self cmdPrintf : @ "Memory checking: %s\n" ,
[ cmdDefs boolForKey : @ "Memory" ] ? "YES" : "NO" ] ;
}
2021-01-28 16:22:07 +00:00
else if ( [ word isEqual : @ "all" ] )
2012-02-19 11:59:22 +00:00
{
if ( NO = = [ cmdDefs boolForKey : @ "Memory" ] )
{
[ self cmdPrintf :
@ "Memory statistics were not being gathered.\n" ] ;
[ self cmdPrintf : @ "Memory statistics Will start from NOW.\n" ] ;
}
else
{
const char * list ;
2021-01-15 14:51:00 +00:00
list = ( const char * ) GSDebugAllocationListAll ( ) ;
2015-07-09 12:08:58 +00:00
[ self cmdPrintf : @ "Memory total allocation stats at %@:\n%s" ,
[ NSDate date ] , list ] ;
2012-02-19 11:59:22 +00:00
}
2020-07-08 15:48:26 +00:00
[ cmdDefs setCommand : @ "YES" forKey : [ cmdDefs key : @ "Memory" ] ] ;
2012-02-19 11:59:22 +00:00
}
2021-01-28 16:22:07 +00:00
else if ( [ word isEqual : @ "on" ] || [ word isEqual : @ "yes" ]
|| [ word isEqual : @ "true" ] || [ word isEqual : @ "1" ] )
2012-02-19 11:59:22 +00:00
{
if ( NO = = [ cmdDefs boolForKey : @ "Memory" ] )
{
[ self cmdPrintf :
@ "Memory statistics were not being gathered.\n" ] ;
[ self cmdPrintf : @ "Statistics Will start from NOW.\n" ] ;
}
else
{
[ self cmdPrintf :
@ "Memory statistics are already being gathered.\n" ] ;
}
2020-07-08 15:48:26 +00:00
[ cmdDefs setCommand : @ "YES" forKey : [ cmdDefs key : @ "Memory" ] ] ;
2012-02-19 11:59:22 +00:00
}
2021-01-28 16:22:07 +00:00
else if ( [ word isEqual : @ "off" ] || [ word isEqual : @ "no" ]
|| [ word isEqual : @ "false" ] || [ word isEqual : @ "0" ] )
2012-02-19 11:59:22 +00:00
{
if ( NO = = [ cmdDefs boolForKey : @ "Memory" ] )
{
[ self cmdPrintf :
@ "Memory statistics were not being gathered.\n" ] ;
}
[ self cmdPrintf : @ "Memory statistics are turned off NOW.\n" ] ;
2020-07-08 15:48:26 +00:00
[ cmdDefs setCommand : @ "NO" forKey : [ cmdDefs key : @ "Memory" ] ] ;
2012-02-19 11:59:22 +00:00
}
2025-01-08 14:49:32 +00:00
else if ( hasLSAN ( )
&& ( [ word isEqual : @ "leakcheck" ] || [ word isEqual : @ "leak" ] ) )
2025-01-02 12:17:39 +00:00
{
2025-01-08 14:49:32 +00:00
if ( ecLeakCheck && ecLeakCheck ( ) )
2025-01-02 12:17:39 +00:00
{
s = @ "Memory leaks found and logged!" ;
}
else
{
s = @ "No memory leaks found." ;
}
[ self cmdPrintf : @ "%@\n" , s ] ;
}
2025-01-08 14:49:32 +00:00
else if ( NO = = hasLSAN ( ) && [ word isEqual : @ "alarm" ] )
2021-01-28 16:22:07 +00:00
{
if ( nil = = ( s = [ cmdDefs stringForKey : @ "MemoryAlarm" ] ) )
{
s = [ [ EcAlarm stringFromSeverity : memAlarm ]
stringByAppendingString : @ " (default)" ] ;
}
else
{
s = [ EcAlarm stringFromSeverity : memAlarm ] ;
}
[ self cmdPrintf : @ "MemoryAlarm is %@.\n" , s ] ;
}
2025-01-08 14:49:32 +00:00
else if ( NO = = hasLSAN ( ) && [ word isEqual : @ "allowed" ] )
2021-01-28 16:22:07 +00:00
{
if ( nil = = ( s = [ cmdDefs stringForKey : @ "MemoryAllowed" ] ) )
{
s = @ "default" ;
}
[ self cmdPrintf : @ "MemoryAllowed setting is %@.\n" , s ] ;
}
2025-01-08 14:49:32 +00:00
else if ( NO = = hasLSAN ( ) && [ word isEqual : @ "idle" ] )
2021-01-28 16:22:07 +00:00
{
if ( nil = = ( s = [ cmdDefs stringForKey : @ "MemoryIdle" ] ) )
{
s = @ "default" ;
}
[ self cmdPrintf : @ "MemoryIdle setting is %@.\n" , s ] ;
}
2025-01-08 14:49:32 +00:00
else if ( NO = = hasLSAN ( )
&& ( [ word isEqual : @ "maximum" ] || [ word isEqual : @ "max" ] ) )
2021-01-28 16:22:07 +00:00
{
if ( nil = = ( s = [ cmdDefs stringForKey : @ "MemoryMaximum" ] ) )
{
s = @ "default" ;
}
[ self cmdPrintf : @ "MemoryMaximum setting is %@.\n" , s ] ;
}
else
{
if ( [ cmdDefs boolForKey : @ "Memory" ] )
{
[ self cmdPrintf :
@ "Memory statistics are currently being gathered.\n" ] ;
}
else
{
[ self cmdPrintf :
@ "Memory statistics are NOT being gathered.\n" ] ;
}
}
2012-02-19 11:59:22 +00:00
}
2014-05-08 10:49:24 +00:00
else if ( [ msg count ] = = 3 )
{
2021-01-28 16:22:07 +00:00
NSString * op = [ [ msg objectAtIndex : 1 ] lowercaseString ] ;
2015-07-08 09:06:26 +00:00
NSString * arg = [ msg objectAtIndex : 2 ] ;
2025-01-08 14:49:32 +00:00
NSInteger val = [ arg integerValue ] ;
Class c ;
2015-07-08 09:06:26 +00:00
2025-01-08 14:49:32 +00:00
if ( hasLSAN ( ) )
2025-01-02 12:17:39 +00:00
{
2025-01-08 14:49:32 +00:00
if ( [ op isEqual : @ "alarm" ]
|| [ op isEqual : @ "allowed" ]
|| [ op isEqual : @ "idle" ]
|| [ op isEqual : @ "maximum" ] || [ op isEqual : @ "max" ] )
{
[ self cmdPrintf :
@ "Command meaningless: linked with asan/lsan.\n" ] ;
return ;
}
2025-01-02 12:17:39 +00:00
}
2025-01-08 14:49:32 +00:00
else
{
if ( [ op isEqual : @ "alarm" ] )
2019-08-11 12:35:10 +00:00
{
2025-01-08 14:49:32 +00:00
arg = [ arg stringByTrimmingSpaces ] ;
if ( [ arg caseInsensitiveCompare : @ "default" ] = = NSOrderedSame )
{
[ cmdDefs setCommand : nil
forKey : [ cmdDefs key : @ "MemoryAlarm" ] ] ;
[ self cmdPrintf : @ "MemoryAlarm using default value.\n" ] ;
}
else
{
arg = setMemAlarm ( arg ) ;
[ cmdDefs setCommand : arg
forKey : [ cmdDefs key : @ "MemoryAlarm" ] ] ;
[ self cmdPrintf : @ "MemoryAlarm set to %@.\n" , arg ] ;
}
return ;
2019-08-11 12:35:10 +00:00
}
2025-01-08 14:49:32 +00:00
else if ( [ op isEqual : @ "allowed" ] )
2019-08-11 12:35:10 +00:00
{
2025-01-08 14:49:32 +00:00
if ( val <= 0 )
{
[ cmdDefs setCommand : nil
forKey : [ cmdDefs key : @ "MemoryAllowed" ] ] ;
if ( 0 = = memAllowed )
{
/ * The threshold was set back to zero . . . to be
* calculated from a ten minute baseline .
* /
memSlot = 0 ;
}
[ self cmdPrintf : @ "MemoryAllowed using default value.\n" ] ;
}
else
{
arg = [ NSString stringWithFormat :
@ "%" PRIu64 , ( uint64_t ) val ] ;
[ cmdDefs setCommand : arg
forKey : [ cmdDefs key : @ "MemoryAllowed" ] ] ;
[ self cmdPrintf : @ "MemoryAllowed set to %@MB.\n" , arg ] ;
}
[ self _memCheck ] ;
return ;
2019-08-11 12:35:10 +00:00
}
2025-01-08 14:49:32 +00:00
else if ( [ op isEqual : @ "idle" ] )
2019-08-10 09:19:16 +00:00
{
2025-01-08 14:49:32 +00:00
if ( ! isdigit ( [ arg characterAtIndex : 0 ] ) )
{
val = -1 ;
}
if ( val >= 0 && val < 24 )
{
arg = [ NSString stringWithFormat : @ "%d" , ( int ) val ] ;
[ cmdDefs setCommand : arg
forKey : [ cmdDefs key : @ "MemoryIdle" ] ] ;
[ self cmdPrintf : @ "MemoryIdle set to %@.\n" , arg ] ;
}
else
{
[ cmdDefs setCommand : nil
forKey : [ cmdDefs key : @ "MemoryIdle" ] ] ;
[ self cmdPrintf : @ "MemoryIdle using default value.\n" ] ;
}
[ self _memCheck ] ;
return ;
2019-08-10 09:19:16 +00:00
}
2025-01-08 14:49:32 +00:00
else if ( [ op isEqual : @ "maximum" ] || [ op isEqual : @ "max" ] )
{
if ( val <= 0 )
{
if ( [ arg caseInsensitiveCompare : @ "default" ]
= = NSOrderedSame )
{
[ cmdDefs setCommand : nil
forKey : [ cmdDefs key : @ "MemoryMaximum" ] ] ;
[ self cmdPrintf :
@ "MemoryMaximum using default value.\n" ] ;
}
else
{
[ cmdDefs setCommand : @ "0"
forKey : [ cmdDefs key : @ "MemoryMaximum" ] ] ;
[ self cmdPrintf :
@ "MemoryMaximum restart turned off.\n" ] ;
}
}
else
{
arg = [ NSString stringWithFormat :
@ "%" PRIu64 , ( uint64_t ) val ] ;
[ cmdDefs setCommand : arg
forKey : [ cmdDefs key : @ "MemoryMaximum" ] ] ;
[ self cmdPrintf : @ "MemoryMaximum set to %@MB.\n" , arg ] ;
}
[ self _memCheck ] ;
return ;
}
}
2015-07-08 09:06:26 +00:00
2025-01-08 14:49:32 +00:00
/ * Not a recognised command . . . assume it ' s the namer of a class .
* /
c = NSClassFromString ( arg ) ;
if ( Nil = = c )
{
[ self cmdPrintf : @ "Unable to find class '%@'.\n" , arg ] ;
}
else
{
if ( [ op caseInsensitiveCompare : @ "class" ] = = NSOrderedSame )
{
GSDebugAllocationRecordAndTrace ( c ,
YES , ( NSObject * ( * ) ( id ) ) 1 ) ;
[ self cmdPrintf : @ "Tracking instances of '%@'.\n" , arg ] ;
}
else if ( [ op caseInsensitiveCompare : @ "list" ] = = NSOrderedSame )
{
NSMapTable * map ;
NSEnumerator * e ;
NSObject * k ;
map = GSDebugAllocationTaggedObjects ( c ) ;
[ self cmdPrintf : @ "Tracked instances of '%@':\n" , arg ] ;
e = [ map keyEnumerator ] ;
while ( ( k = [ e nextObject ] ) ! = nil )
{
NSObject * v = [ map objectForKey : k ] ;
2024-06-18 11:29:17 +00:00
2025-01-08 14:49:32 +00:00
[ self cmdPrintf : @ "%@ allocated at %@\n" , k , v ] ;
}
}
else
{
[ self cmdPrintf : @ "Unknown memory command '%@'.\n" , op ] ;
}
}
2014-05-08 10:49:24 +00:00
}
2012-02-19 11:59:22 +00:00
else
{
if ( NO = = [ cmdDefs boolForKey : @ "Memory" ] )
{
[ self cmdPrintf : @ "Memory stats are not being gathered.\n" ] ;
}
else
{
2015-07-09 12:24:06 +00:00
const char * list ;
NSDate * now ;
2012-02-19 11:59:22 +00:00
2015-07-09 12:24:06 +00:00
now = [ NSDate date ] ;
2012-02-19 11:59:22 +00:00
list = ( const char * ) GSDebugAllocationList ( YES ) ;
2015-07-09 12:08:58 +00:00
if ( nil = = memStats )
{
[ self cmdPrintf : @ "Memory change stats at %@:\n%s" ,
2015-07-09 12:24:06 +00:00
now , list ] ;
2015-07-09 12:08:58 +00:00
}
else
{
2015-07-09 12:27:06 +00:00
[ self cmdPrintf : @ "Memory change stats at %@\n"
@ " (since %@):\n%s" , now , memStats , list ] ;
2015-07-09 12:08:58 +00:00
}
2015-07-09 12:24:06 +00:00
ASSIGN ( memStats , now ) ;
2012-02-19 11:59:22 +00:00
}
}
}
}
- ( void ) cmdMesgstatus : ( NSArray * ) msg
{
if ( [ msg count ] = = 0 )
{
[ self cmdPrintf : @ "provides server status information" ] ;
}
else
{
2019-08-09 11:34:58 +00:00
NSTimeInterval ti = [ self ecQuitDuration ] ;
2021-06-04 15:10:01 +00:00
NSString * s ;
2019-08-09 11:34:58 +00:00
2012-02-19 11:59:22 +00:00
[ self cmdPrintf : @ "\n%@ on %@ running since %@\n" ,
2013-04-08 15:50:32 +00:00
cmdLogName ( ) , ecHostName ( ) , [ self ecStarted ] ] ;
2014-11-26 08:59:50 +00:00
if ( lastIP > 0.0 )
2012-02-19 11:59:22 +00:00
{
[ self cmdPrintf : @ "Last IP at %@\n" , [ self cmdLastIP ] ] ;
}
2014-11-26 08:59:50 +00:00
if ( lastOP > 0.0 )
2012-02-19 11:59:22 +00:00
{
[ self cmdPrintf : @ "Last OP at %@\n" , [ self cmdLastOP ] ] ;
}
2019-08-09 11:34:58 +00:00
if ( ti > 0 )
{
2021-06-04 15:10:01 +00:00
if ( nil = = ( s = [ self ecQuitReason ] ) )
2019-08-09 11:34:58 +00:00
{
[ self cmdPrintf : @ "Quitting for %g seconds\n" , ti ] ;
}
else
{
[ self cmdPrintf : @ "Quitting for %g seconds: %@\n" , ti , s ] ;
}
}
2012-02-19 11:59:22 +00:00
if ( servers ! = nil )
{
NSEnumerator * e ;
RemoteServer * server ;
e = [ servers objectEnumerator ] ;
while ( ( server = ( RemoteServer * ) [ e nextObject ] ) ! = 0 )
{
[ self cmdPrintf : @ "%@\n" , server ] ;
}
}
2025-01-08 14:49:32 +00:00
if ( hasLSAN ( ) )
2019-08-10 09:19:16 +00:00
{
2025-01-08 14:49:32 +00:00
[ self cmdPrintf : @ "Unknown memory usage: built with asan/lsan.\n" ] ;
2019-08-10 09:19:16 +00:00
}
2025-01-08 14:49:32 +00:00
else
{
if ( memTime <= 0.0 )
{
s = @ "" ;
}
else
{
s = [ NSString stringWithFormat : @ " (last checked at %@)" ,
[ NSDate dateWithTimeIntervalSinceReferenceDate : memTime ] ] ;
}
[ self cmdPrintf : @ "%@ memory usage%@:\n"
@ " %" PRIu64 "%@ (current), %" PRIu64 "%@ (peak)\n" ,
memType , s , memLast / memSize , memUnit , memPeak / memSize , memUnit ] ;
[ self cmdPrintf : @ " %" PRIu64 "%@ (average),"
@ " %" PRIu64 "%@ (start)\n" ,
memAvge / memSize , memUnit , memStrt / memSize , memUnit ] ;
setMemBase ( ) ;
if ( memSlot < MEMCOUNT )
{
[ self cmdPrintf : @ "Waiting (for %d min"
@ " of baseline stats collection).\n" ,
( int ) ( MEMCOUNT - memSlot ) ] ;
}
2019-08-10 09:19:16 +00:00
2025-01-08 14:49:32 +00:00
if ( memAllowed > 0 )
{
[ self cmdPrintf : @ "MemoryAllowed: %" PRIu64 @ "%@\n the process"
@ " is expected to use up to this much memory.\n" ,
memAllowed * 1024 * 1024 / memSize , memUnit ] ;
}
if ( memMaximum > 0 )
{
NSString * idle ;
int hour ;
uint64_t limit ;
if ( 0 = = memAllowed )
{
[ self cmdPrintf : @ "Estimated base: %" PRIu64
@ "%@\n the process is expected to use up to"
@ " this much memory.\n" ,
memAllowed * 1024 * 1024 / memSize , memUnit ] ;
}
[ self cmdPrintf : @ "MemoryMaximum: %" PRIu64
@ "%@\n the process is restarted when peak memory usage"
@ " is above this limit.\n" ,
memMaximum * 1024 * 1024 / memSize , memUnit ] ;
idle = [ cmdDefs stringForKey : @ "MemoryIdle" ] ;
if ( [ idle length ] > 0
&& ( hour = [ idle intValue ] ) >= 0 && hour < 24 )
{
[ self cmdPrintf : @ " The process is also restarted if"
@ " memory is above %" PRIu64 "%@\n during the hour"
@ " from %02d:00.\n" ,
memCrit / memSize , memUnit , hour ] ;
}
limit = memWarn ;
switch ( memAlarm )
{
case EcAlarmSeverityCritical : limit = memCrit ; break ;
case EcAlarmSeverityMajor : limit = memMajr ; break ;
case EcAlarmSeverityMinor : limit = memMinr ; break ;
default : limit = memWarn ; break ;
}
[ self cmdPrintf : @ "Alarms are raised when memory usage"
@ " is above: %" PRIu64 "%@.\n" , limit / memSize , memUnit ] ;
}
}
2012-02-19 11:59:22 +00:00
}
}
2015-01-26 12:50:42 +00:00
- ( oneway void ) cmdPing : ( id < CmdPing > ) from
sequence : ( unsigned ) num
extra : ( in bycopy NSData * ) data
2012-02-19 11:59:22 +00:00
{
2017-11-02 10:14:30 +00:00
/ * When responding to a ping from a remote process , we also check
* and abort if we have spent too long trying to quit .
* /
ecIsQuitting ( ) ;
2012-02-19 11:59:22 +00:00
[ self cmdDbg : cmdConnectDbg msg : @ "cmdPing: %lx sequence: %u extra: %lx" ,
( unsigned long ) from , num , ( unsigned long ) data ] ;
[ from cmdGnip : self sequence : num extra : nil ] ;
}
2012-05-25 19:10:27 +00:00
- ( void ) cmdPrintf : ( NSString * ) fmt arguments : ( va_list ) args
{
NSString * tmp ;
tmp = [ [ stringClass alloc ] initWithFormat : fmt arguments : args ] ;
[ replyBuffer appendString : tmp ] ;
[ tmp release ] ;
}
2012-02-19 11:59:22 +00:00
- ( void ) cmdPrintf : ( NSString * ) fmt , . . .
{
va_list ap ;
va_start ( ap , fmt ) ;
2012-05-25 19:10:27 +00:00
[ self cmdPrintf : fmt arguments : ap ] ;
2012-02-19 11:59:22 +00:00
va_end ( ap ) ;
}
2015-10-15 07:09:52 +00:00
- ( oneway void ) cmdQuit : ( NSInteger ) status
2012-02-19 11:59:22 +00:00
{
2018-06-24 14:37:35 +00:00
/ * NB . must not call - ecQuitFor : with : since that method calls ths one
* and we do not want nmutual recursion .
* /
[ ecLock lock ] ;
if ( 0.0 = = beganQuitting )
{
2020-06-24 12:26:31 +00:00
NSLog ( @ "-[%@ cmdQuit: %ld]" , NSStringFromClass ( [ self class ] ) ,
( long ) status ) ;
2024-11-25 11:58:52 +00:00
DESTROY ( ecQuitReason ) ;
2018-06-24 14:37:35 +00:00
ecQuitStatus = status ;
}
[ ecLock unlock ] ;
[ self ecWillQuit ] ;
[ self performSelectorOnMainThread : @ selector ( _ecQuit )
withObject : nil
waitUntilDone : NO ] ;
2012-02-19 11:59:22 +00:00
}
- ( void ) cmdUpdate : ( NSMutableDictionary * ) info
{
2018-01-04 11:23:29 +00:00
ASSIGNCOPY ( cmdConf , info ) ;
[ cmdDefs setConfiguration : cmdConf ] ;
2012-02-19 11:59:22 +00:00
}
2013-03-13 10:08:13 +00:00
- ( NSString * ) cmdUpdated
2012-11-23 12:45:09 +00:00
{
2013-03-13 10:08:13 +00:00
return nil ;
2012-11-23 12:45:09 +00:00
}
2012-02-19 11:59:22 +00:00
- ( void ) dealloc
{
[ [ NSNotificationCenter defaultCenter ] removeObserver : self ] ;
2012-03-09 09:22:09 +00:00
[ ecLock lock ] ;
2012-02-19 11:59:22 +00:00
if ( self = = EcProc )
{
EcProc = nil ;
}
2012-03-09 09:22:09 +00:00
[ ecLock unlock ] ;
2012-02-19 11:59:22 +00:00
[ super dealloc ] ;
}
- ( NSString * ) description
{
return [ stringClass stringWithFormat : @ "%@ (%@) on %@" ,
2012-05-08 16:08:30 +00:00
[ super description ] , cmdLogName ( ) , ecHostName ( ) ] ;
2012-02-19 11:59:22 +00:00
}
- ( id ) init
{
2014-04-28 06:40:03 +00:00
CREATE_AUTORELEASE _POOL ( pool ) ;
2012-02-19 11:59:22 +00:00
2014-04-28 06:40:03 +00:00
self = [ self initWithDefaults : [ [ self class ] ecInitialDefaults ] ] ;
RELEASE ( pool ) ;
return self ;
2012-02-19 11:59:22 +00:00
}
2020-06-24 12:26:31 +00:00
- ( BOOL ) ecPrepareUnique
{
NSSocketPortNameServer * ns = [ NSSocketPortNameServer sharedInstance ] ;
NSString * name = [ self cmdName ] ;
NSString * prep = [ name stringByAppendingString : @ " (starting)" ] ;
NSPort * p ;
/ * First try registering as a non - functional process using our unique
* name with th suffix ' ( starting ) ' to prevent other instances trying
* to start up at the same time .
* /
p = ( NSPort * ) [ NSSocketPort port ] ;
EcProcConnection = [ [ NSConnection alloc ] initWithReceivePort : p
sendPort : nil ] ;
[ EcProcConnection setRootObject : self ] ;
if ( [ EcProcConnection registerName : prep withNameServer : ns ] = = NO )
{
p = [ ns portForName : prep onHost : @ "" ] ;
DESTROY ( EcProcConnection ) ;
NSLog ( @ "There is already a process: %@, on %@" , prep , p ) ;
return NO ;
}
/ * Now check to see if there is an instance already running .
* /
p = [ ns portForName : name onHost : @ "" ] ;
if ( nil ! = p )
{
[ ns removePortForName : prep ] ;
DESTROY ( EcProcConnection ) ;
NSLog ( @ "There is already a process: %@, on %@" , name , p ) ;
return NO ;
}
/ * Yippee . . . there is no other copy of this process running and we have
* grabbed the name of a process starting up , so no other process can
* conflict with our startup .
* /
return YES ;
}
2012-02-19 11:59:22 +00:00
- ( id ) initWithDefaults : ( NSDictionary * ) defs
{
2012-03-09 09:22:09 +00:00
[ ecLock lock ] ;
2017-10-26 14:12:33 +00:00
initAt = [ NSDate timeIntervalSinceReferenceDate ] ;
2012-02-19 11:59:22 +00:00
if ( nil ! = EcProc )
{
[ self release ] ;
2012-03-09 09:22:09 +00:00
[ ecLock unlock ] ;
2012-02-19 11:59:22 +00:00
[ NSException raise : NSGenericException
format : @ "EcProcess initialiser called more than once" ] ;
}
if ( nil = = ( self = [ super init ] ) )
2018-03-02 11:04:25 +00:00
{
[ ecLock unlock ] ;
return nil ;
}
else
{
NSArray * args = [ [ NSProcessInfo processInfo ] arguments ] ;
NSString * str ;
NSString * prf ;
NSInteger i ;
2012-02-19 11:59:22 +00:00
2018-03-02 11:04:25 +00:00
EcProc = self ;
2012-02-19 11:59:22 +00:00
2018-03-02 11:04:25 +00:00
prf = EC_DEFAULTS _PREFIX ;
if ( nil = = prf )
{
prf = @ "" ;
2012-02-19 11:59:22 +00:00
}
2018-03-02 11:04:25 +00:00
started = RETAIN ( [ dateClass date ] ) ;
2021-01-14 09:14:32 +00:00
[ [ self class ] ecPrepareWithDefaults : defs ] ;
2012-02-20 13:07:54 +00:00
2018-03-02 11:04:25 +00:00
if ( [ args containsObject : @ "--help" ] || [ args containsObject : @ "-H" ] )
2012-02-20 13:07:54 +00:00
{
2018-03-02 11:04:25 +00:00
GSPrintf ( stderr , @ "Standard command-line arguments ...\n\n" ) ;
2012-02-20 13:07:54 +00:00
2018-03-02 11:04:25 +00:00
if ( [ self isKindOfClass : NSClassFromString ( @ "EcControl" ) ] )
{
GSPrintf ( stderr ,
@ "-%@Daemon NO Run process in the foreground.\n" ,
prf ) ;
}
else if ( [ self isKindOfClass : NSClassFromString ( @ "EcConsole" ) ] )
{
GSPrintf ( stderr ,
@ "-%@ControlHost [aHost] Host of the Control server to use.\n"
@ "-%@ControlName [aName] Name of the Control server to use.\n"
@ "-%@Daemon [YES/NO] Fork process to run in background?\n" ,
prf , prf , prf ) ;
}
else if ( [ self isKindOfClass : NSClassFromString ( @ "EcCommand" ) ] )
{
GSPrintf ( stderr ,
@ "-%@ControlHost [aHost] Host of the Control server to use.\n"
@ "-%@ControlName [aName] Name of the Control server to use.\n"
@ "-%@Daemon NO Run process in in the foreground.\n" ,
prf , prf , prf ) ;
}
else
{
GSPrintf ( stderr ,
@ "-%@CommandHost [aHost] Host of the Command server to use.\n"
@ "-%@CommandName [aName] Name of the Command server to use.\n"
2018-11-23 11:40:55 +00:00
@ "-%@Daemon [YES/NO] Fork process to run in background?\n"
2018-03-02 11:04:25 +00:00
@ "-%@Transient [YES/NO] Expect this process be short-lived?\n" ,
prf , prf , prf , prf ) ;
}
2012-02-20 13:07:54 +00:00
2018-03-02 11:04:25 +00:00
GSPrintf ( stderr , @ "\n" ) ;
GSPrintf ( stderr ,
@ "-%@CoreSize [MB] Maximum core dump size\n"
@ " 0 = no dumps, -1 = unlimited\n"
@ "-%@DescriptorsMaximum [N]\n"
@ " Set maximum file descriptors to use\n"
@ "-%@Debug-name [YES/NO] Turn on/off the named type of debug\n"
@ "-%@EffectiveUser [aName] User to run this process as\n"
@ "-%@HomeDirectory [relDir] Relative home within user directory\n"
@ "-%@UserDirectory [dir] Override home directory for user\n"
@ "-%@Instance [aNumber] Instance number for multiple copies\n"
2019-08-11 12:35:10 +00:00
@ "-%@MemoryAlarm [severity] When to start raising alarms\n"
2019-08-10 09:19:16 +00:00
@ "-%@MemoryAllowed [MB] Expected memory usage (base size)\n"
@ "-%@MemoryIdle [0-23] Hour of day preferred for restart\n"
2018-03-02 11:04:25 +00:00
@ "-%@MemoryMaximum [MB] Maximum memory usage (before restart)\n"
2019-06-01 14:28:21 +00:00
@ "-%@MemoryType [aName] Type of memory to measure. One of\n"
@ " Total, Resident or Data.\n"
2021-06-04 15:10:01 +00:00
@ "-%@MemoryUnit [aName] Unit to display memory in. One of\n"
@ " KB, MB or Page (KB by default).\n"
2018-03-02 11:04:25 +00:00
@ "-%@ProgramName [aName] Name to use for this program\n"
@ "\n--version to get version information and quit\n\n" ,
2021-06-04 15:10:01 +00:00
prf , prf , prf , prf , prf , prf , prf , prf , prf , prf ,
prf , prf , prf , prf ) ;
2012-02-19 11:59:22 +00:00
2018-03-02 11:04:25 +00:00
[ EcDefaultRegistration showHelp ] ;
2015-07-21 09:30:24 +00:00
2018-03-02 11:04:25 +00:00
RELEASE ( self ) ;
2012-03-09 09:22:09 +00:00
[ ecLock unlock ] ;
2018-03-02 11:04:25 +00:00
return nil ;
2012-02-19 11:59:22 +00:00
}
2018-03-02 11:04:25 +00:00
if ( [ args containsObject : @ "--version" ] )
2012-02-19 11:59:22 +00:00
{
2018-03-02 11:04:25 +00:00
NSLog ( @ "%@ %@" , [ self ecCopyright ] , cmdVersion ( nil ) ) ;
RELEASE ( self ) ;
[ ecLock unlock ] ;
return nil ;
2012-02-19 11:59:22 +00:00
}
2020-06-24 12:26:31 +00:00
cmdIsTransient = [ cmdDefs boolForKey : @ "Transient" ] ;
if ( NO = = cmdIsTransient )
{
if ( NO = = [ self ecPrepareUnique ] )
{
RELEASE ( self ) ;
[ ecLock unlock ] ;
2020-11-02 11:36:57 +00:00
exit ( -2 ) ; // Unable to register with name server
2020-06-24 12:26:31 +00:00
}
}
2012-02-19 11:59:22 +00:00
for ( i = 0 ; i < 32 ; i + + )
{
switch ( i )
{
case SIGPROF :
case SIGABRT :
2022-10-10 11:51:47 +00:00
break ; / * Not overridable * /
case SIGUSR1 :
case SIGUSR2 :
break ; / * For apps . . . not errors * /
2012-02-19 11:59:22 +00:00
case SIGPIPE :
case SIGTTOU :
case SIGTTIN :
case SIGCHLD :
/ * SIGWINCH is generated when the terminal size
changes ( for example when you resize the xterm ) .
Ignore it . * /
# ifdef SIGWINCH
case SIGWINCH :
# endif
signal ( i , SIG_IGN ) ;
break ;
2013-08-19 09:05:07 +00:00
case SIGHUP :
if ( [ cmdDefs boolForKey : @ "Daemon" ] = = YES )
{
signal ( i , SIG_IGN ) ;
}
else
{
signal ( i , qhandler ) ;
}
break ;
2012-02-19 11:59:22 +00:00
case SIGINT :
case SIGTERM :
signal ( i , qhandler ) ;
break ;
case SIGSTOP :
case SIGCONT :
case SIGTSTP :
signal ( i , SIG_DFL ) ;
break ;
default :
signal ( i , ihandler ) ;
break ;
}
}
/ * Archive any existing debug log left over by a crash .
* /
str = [ cmdName stringByAppendingPathExtension : @ "debug" ] ;
if ( cmdDebugName = = nil || [ cmdDebugName isEqual : str ] = = NO )
{
NSFileHandle * hdl ;
2017-09-19 12:52:15 +00:00
NSString * result ;
2012-02-19 11:59:22 +00:00
2017-09-19 12:52:15 +00:00
/ * Force archiving of old logfile .
2012-02-19 11:59:22 +00:00
* /
2017-09-19 12:52:15 +00:00
result = [ self ecArchive : nil ] ;
if ( result ! = noFiles )
{
NSLog ( @ "Startup: %@" , result ) ;
}
2012-02-19 11:59:22 +00:00
ASSIGNCOPY ( cmdDebugName , str ) ;
hdl = [ self cmdLogFile : cmdDebugName ] ;
if ( hdl = = nil )
{
2020-06-24 12:26:31 +00:00
DESTROY ( EcProcConnection ) ;
2012-03-09 09:22:09 +00:00
[ ecLock unlock ] ;
2012-02-19 11:59:22 +00:00
exit ( 1 ) ;
}
}
2013-11-04 07:28:57 +00:00
[ [ NSNotificationCenter defaultCenter ]
addObserver : self
2018-01-04 11:23:29 +00:00
selector : @ selector ( _defaultsChanged : )
2013-11-04 07:28:57 +00:00
name : NSUserDefaultsDidChangeNotification
object : [ NSUserDefaults standardUserDefaults ] ] ;
2014-09-16 18:57:07 +00:00
[ [ NSNotificationCenter defaultCenter ]
addObserver : self
selector : @ selector ( ecLoggersChanged : )
name : EcLoggersDidChangeNotification
object : nil ] ;
2012-07-05 13:15:27 +00:00
[ self cmdMesgCache ] ;
2012-02-19 11:59:22 +00:00
2013-11-04 07:28:57 +00:00
[ self cmdDefaultsChanged : nil ] ;
2012-02-19 11:59:22 +00:00
if ( [ cmdDefs objectForKey : @ "CmdInterval" ] ! = nil )
{
[ self setCmdInterval : [ cmdDefs floatForKey : @ "CmdInterval" ] ] ;
}
2017-06-23 13:13:54 +00:00
/ * Log that we are starting up , after the config required for logging
* is in place , but before we have updated config from the Command
* server ( since updating config may generater log files ) .
* /
2017-07-13 09:10:13 +00:00
[ self cmdAudit : @ "Starting '%@'" , [ self cmdName ] ] ;
2017-06-23 13:13:54 +00:00
2012-02-19 11:59:22 +00:00
if ( YES = = [ self cmdIsClient ] && nil = = [ self cmdNewServer ] )
{
NSLog ( @ "Giving up - unable to contact '%@' server on '%@'" ,
2012-03-09 09:22:09 +00:00
ecCommandName ( ) , ecCommandHost ( ) ) ;
2012-02-19 11:59:22 +00:00
[ self release ] ;
self = nil ;
}
}
2012-03-09 09:22:09 +00:00
[ ecLock unlock ] ;
2012-02-19 11:59:22 +00:00
2017-06-23 13:13:54 +00:00
if ( self ! = nil )
2012-02-19 11:59:22 +00:00
{
2017-06-23 13:13:54 +00:00
/ * Put self in background .
* /
if ( [ cmdDefs boolForKey : @ "Daemon" ] = = YES )
{
int pid = fork ( ) ;
2012-02-19 11:59:22 +00:00
2017-06-23 13:13:54 +00:00
if ( pid = = 0 )
{
cmdFlagDaemon = YES ;
setpgid ( 0 , getpid ( ) ) ;
}
else
{
if ( pid < 0 )
{
printf ( "Failed fork to run as daemon.\r\n" ) ;
}
else
{
printf ( "Process backgrounded (running as daemon)\r\n" ) ;
}
exit ( 0 ) ;
}
}
2012-02-19 11:59:22 +00:00
}
return self ;
}
/ *
* Implement the CmdConfig protocol .
* /
- ( void ) replaceFile : ( NSData * ) data
name : ( NSString * ) name
isConfig : ( BOOL ) f
{
[ NSException raise : NSGenericException
format : @ "Illegal method call" ] ;
}
2015-01-26 12:50:42 +00:00
- ( oneway void ) requestConfigFor : ( id < CmdConfig > ) c
2012-02-19 11:59:22 +00:00
{
[ NSException raise : NSGenericException
format : @ "Illegal method call" ] ;
}
- ( void ) requestFile : ( BOOL ) flag
name : ( NSString * ) name
for : ( id < CmdConfig > ) c
{
[ NSException raise : NSGenericException
format : @ "Illegal method call" ] ;
}
2015-01-26 12:50:42 +00:00
- ( oneway void ) updateConfig : ( in bycopy NSData * ) info
2012-02-19 11:59:22 +00:00
{
id plist = [ NSPropertyListSerialization
propertyListWithData : info
options : NSPropertyListMutableContainers
format : 0
error : 0 ] ;
if ( nil ! = plist )
{
2012-11-23 12:45:09 +00:00
[ self _update : plist ] ;
2012-02-19 11:59:22 +00:00
}
}
- ( id ) server : ( NSString * ) serverName
{
RemoteServer * server ;
server = ( RemoteServer * ) [ servers objectForKey : serverName ] ;
if ( server = = nil )
{
NSLog ( @ "Trying to ask for not-existent server %@" , serverName ) ;
return nil ;
}
return [ server proxy ] ;
}
- ( id ) server : ( NSString * ) serverName forNumber : ( NSString * ) num
{
RemoteServer * server ;
NSArray * config ;
server = ( RemoteServer * ) [ servers objectForKey : serverName ] ;
if ( server = = nil )
{
NSLog ( @ "Trying to ask for not-existent server %@" , serverName ) ;
return nil ;
}
config = [ server multiple ] ;
if ( config ! = nil && [ config count ] > 1 )
{
int val = -1 ;
unsigned count = [ config count ] ;
/ *
* Get trailing two digits of number . . . in range 00 to 99
* /
if ( [ num length ] >= 2 )
{
val = [ [ num substringFromIndex : [ num length ] - 2 ] intValue ] ;
}
if ( val < 0 )
{
val = 0 ;
}
/ *
* Try to find a broadcast server with a numeric range matching
* the number we were given .
* /
while ( count - - > 0 )
{
NSDictionary * d = [ config objectAtIndex : count ] ;
if ( val >= [ [ d objectForKey : @ "Low" ] intValue ]
&& val <= [ [ d objectForKey : @ "High" ] intValue ] )
{
return [ [ server proxy ] BCPproxy : count ] ;
}
}
2020-03-23 13:35:58 +00:00
[ self cmdError : @ "Attempt to get %@ server for number %@ with bad config" ,
serverName , num ] ;
2012-02-19 11:59:22 +00:00
return nil ;
}
return [ server proxy ] ;
}
- ( BOOL ) isServerMultiple : ( NSString * ) serverName
{
RemoteServer * server ;
server = ( RemoteServer * ) [ servers objectForKey : serverName ] ;
if ( server = = nil )
{
NSLog ( @ "Trying to ask for not-existent server %@" , serverName ) ;
return NO ;
}
return ( [ server multiple ] = = nil ) ? NO : YES ;
}
2021-01-28 17:43:15 +00:00
/ * Allow the process to be terminated using the same API as Command server
* /
- ( oneway void ) terminate
{
[ self ecQuitFor : @ "external -terminate: requested" with : 0 ] ;
}
2013-07-13 07:58:07 +00:00
2012-02-19 11:59:22 +00:00
@ end
@ implementation EcProcess ( Private )
- ( void ) cmdMesgrelease : ( NSArray * ) msg
{
if ( [ msg count ] = = 0 )
{
[ self cmdPrintf : @ "controls double release memory error detection" ] ;
return ;
}
if ( [ [ msg objectAtIndex : 0 ] caseInsensitiveCompare : @ "help" ] = = NSOrderedSame )
{
[ self cmdPrintf : @ "controls double release memory error detection\n" ] ;
2013-04-23 12:43:39 +00:00
[ self cmdPrintf : @ "to report if an object is released too many times.\n" ] ;
2012-02-19 11:59:22 +00:00
[ self cmdPrintf : @ "This has a big impact on program performance.\n" ] ;
[ self cmdPrintf : @ "'release yes' turns on checking\n" ] ;
[ self cmdPrintf : @ "'release no' turns off checking\n" ] ;
[ self cmdPrintf : @ "'release default' reverts to default setting\n" ] ;
[ self cmdPrintf : @ "'release' reports current status\n" ] ;
return ;
}
if ( [ msg count ] = = 1 )
{
[ self cmdPrintf : @ "Double release checking: %s\n" ,
[ cmdDefs boolForKey : @ "Release" ] ? "YES" : "NO" ] ;
}
if ( [ msg count ] > 1 )
{
if ( [ [ msg objectAtIndex : 1 ] caseInsensitiveCompare : @ "default" ]
= = NSOrderedSame )
{
2020-07-08 15:48:26 +00:00
[ cmdDefs setCommand : nil
forKey : [ cmdDefs key : @ "Release" ] ] ;
2012-02-19 11:59:22 +00:00
}
else
{
2020-07-08 15:48:26 +00:00
[ cmdDefs setCommand : [ msg objectAtIndex : 1 ]
forKey : [ cmdDefs key : @ "Release" ] ] ;
2012-02-19 11:59:22 +00:00
}
[ self cmdPrintf : @ "Double release checking: %s\n" ,
[ cmdDefs boolForKey : @ "Release" ] ? "YES" : "NO" ] ;
}
}
- ( void ) cmdMesgtesting : ( NSArray * ) msg
{
if ( [ msg count ] = = 0 )
{
[ self cmdPrintf : @ "controls whether server is running in testing mode" ] ;
return ;
}
if ( [ [ msg objectAtIndex : 0 ] caseInsensitiveCompare : @ "help" ] = = NSOrderedSame )
{
[ self cmdPrintf : @ "controls whether server is running in testing mode\n" ] ;
[ self cmdPrintf : @ "Behavior in testing mode is server dependent.\n" ] ;
[ self cmdPrintf : @ "'testing yes' turns on testing mode\n" ] ;
[ self cmdPrintf : @ "'testing no' turns off testing mode\n" ] ;
[ self cmdPrintf : @ "'testing default' reverts to default setting\n" ] ;
[ self cmdPrintf : @ "'testing' reports current status\n" ] ;
return ;
}
if ( [ msg count ] = = 1 )
{
[ self cmdPrintf : @ "Server running in testing mode: %s\n" ,
cmdFlagTesting ? "YES" : "NO" ] ;
}
if ( [ msg count ] > 1 )
{
if ( [ [ msg objectAtIndex : 1 ] caseInsensitiveCompare : @ "default" ]
= = NSOrderedSame )
{
2020-07-08 15:48:26 +00:00
[ cmdDefs setCommand : nil
forKey : [ cmdDefs key : @ "Testing" ] ] ;
2012-02-19 11:59:22 +00:00
}
else
{
2020-07-08 15:48:26 +00:00
[ cmdDefs setCommand : [ msg objectAtIndex : 1 ]
forKey : [ cmdDefs key : @ "Testing" ] ] ;
2012-02-19 11:59:22 +00:00
}
[ self cmdPrintf : @ "Server running in testing mode: %s\n" ,
cmdFlagTesting ? "YES" : "NO" ] ;
}
}
2017-06-23 10:26:17 +00:00
- ( void ) _ensureMemLogger
2015-07-15 08:41:14 +00:00
{
NSString * bundle = [ cmdDefs stringForKey : @ "MemoryLoggerBundle" ] ;
Class cls = Nil ;
if ( nil = = bundle )
{
DESTROY ( cmdMemoryLogger ) ;
return ;
}
// This is a reasonable fast path if we have already loaded the bundle
cls = NSClassFromString ( bundle ) ;
if ( ( Nil = = cls )
|| ( NO = = [ cls conformsToProtocol : @ protocol ( EcMemoryLogger ) ] ) )
{
cls = [ self _memoryLoggerClassFromBundle : bundle ] ;
}
if ( Nil = = cls )
{
// No usable logger class , destroy any we might have
DESTROY ( cmdMemoryLogger ) ;
return ;
}
if ( NO = = [ cmdMemoryLogger isKindOfClass : cls ] )
{
// If it ' s no longer the right class , destroy it
DESTROY ( cmdMemoryLogger ) ;
}
if ( nil = = cmdMemoryLogger )
{
NS_DURING
{
cmdMemoryLogger = [ cls new ] ;
}
NS_HANDLER
{
[ self cmdWarn : @ "Exception creating memory logger: %@" ,
localException ] ;
}
NS_ENDHANDLER
}
}
2015-07-08 09:06:26 +00:00
- ( void ) _memCheck
{
2025-01-08 14:49:32 +00:00
if ( NO = = hasLSAN ( ) )
2019-06-01 14:49:13 +00:00
{
2025-01-08 14:49:32 +00:00
static EcAlarm * alarm = nil ;
static char buf [ 64 ] = { 0 } ;
EcAlarmSeverity severity = EcAlarmSeverityCleared ;
uint64_t mTotal , mResident , mShared , mText , mLib , mData , mDirty ;
BOOL memDebug = [ cmdDefs boolForKey : @ "Memory" ] ;
int pageSize = 4096 ;
FILE * fptr ;
NSString * str ;
int i ;
2015-07-08 09:06:26 +00:00
2025-01-08 14:49:32 +00:00
memTime = [ NSDate timeIntervalSinceReferenceDate ] ;
if ( nil = = ( str = [ cmdDefs stringForKey : @ "MemoryUnit" ] ) )
{
memSize = 1024 ;
memUnit = @ "KB" ;
}
else if ( [ str caseInsensitiveCompare : @ "MB" ] = = NSOrderedSame )
{
memSize = 1024 * 1024 ;
memUnit = @ "MB" ;
}
else if ( [ str caseInsensitiveCompare : @ "Page" ] = = NSOrderedSame )
{
memSize = pageSize ;
memUnit = @ "Pg" ;
2019-06-01 14:35:53 +00:00
}
else
{
2025-01-08 14:49:32 +00:00
memSize = 1024 ;
memUnit = @ "KB" ;
}
if ( nil = = ( str = [ cmdDefs stringForKey : @ "MemoryType" ] ) )
{
str = @ "Total" ;
}
else if ( [ str caseInsensitiveCompare : @ "Resident" ] = = NSOrderedSame )
{
str = @ "Resident" ;
}
else if ( [ str caseInsensitiveCompare : @ "Data" ] = = NSOrderedSame )
{
str = @ "Data" ;
}
else
{
str = @ "Total" ;
}
if ( NO = = [ memType isEqual : str ] )
{
ASSIGNCOPY ( memType , str ) ;
memSlot = 0 ;
}
/ * / proc / pid / statm reports the process memory size in 4 KB pages
* /
if ( ' \ 0 ' = = * buf )
{
sprintf ( buf , "/proc/%d/statm" ,
[ [ NSProcessInfo processInfo ] processIdentifier ] ) ;
}
fptr = fopen ( buf , "r" ) ;
memLast = 1 ;
mTotal = mResident = mShared = mText = mLib = mData = mDirty = 0 ;
if ( NULL ! = fptr )
{
if ( fscanf ( fptr , "%" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64
" %" PRIu64 " %" PRIu64 ,
& mTotal , & mResident , & mShared , & mText , & mLib , & mData , & mDirty ) ! = 7 )
2019-06-01 14:28:21 +00:00
{
2025-01-08 14:49:32 +00:00
memLast = 1 ;
2019-06-01 14:28:21 +00:00
}
else
{
2025-01-08 14:49:32 +00:00
if ( [ memType isEqualToString : @ "Resident" ] )
{
memLast = mResident ;
}
else if ( [ memType isEqualToString : @ "Data" ] )
{
memLast = mData ;
}
else
{
memLast = mTotal ;
}
memLast * = pageSize ;
if ( memLast <= 0 ) memLast = 1 ;
2019-06-01 14:28:21 +00:00
}
2025-01-08 14:49:32 +00:00
fclose ( fptr ) ;
}
excLast = ( uint64_t ) [ self ecNotLeaked ] ;
2015-07-08 09:06:26 +00:00
2025-01-08 14:49:32 +00:00
[ self _ensureMemLogger ] ;
if ( nil ! = cmdMemoryLogger )
{
NS_DURING
{
[ cmdMemoryLogger process : self
didUseMemory : mTotal * pageSize
notLeaked : excLast
resident : mResident * pageSize
data : mData * pageSize ] ;
}
NS_HANDLER
{
[ self cmdWarn :
@ "Exception logging memory usage to bundle: %@" ,
localException ] ;
}
NS_ENDHANDLER
}
2015-07-08 09:06:26 +00:00
2025-01-08 14:49:32 +00:00
/ * Do initial population so we can work immediately .
* /
if ( 0 = = memSlot )
{
for ( i = 1 ; i < MEMCOUNT ; i + + )
{
excRoll [ i ] = excLast ;
memRoll [ i ] = memLast ;
}
memPrev = memStrt = memLast ;
excPrev = excStrt = excLast ;
}
excRoll [ memSlot % MEMCOUNT ] = excLast ;
memRoll [ memSlot % MEMCOUNT ] = memLast ;
memSlot + + ;
2015-07-08 09:06:26 +00:00
2025-01-08 14:49:32 +00:00
/ * Find the average usage over the last set of samples .
* Round up to a block size .
* /
excAvge = 0 ;
memAvge = 0 ;
for ( i = 0 ; i < MEMCOUNT ; i + + )
{
excAvge + = excRoll [ i ] ;
memAvge + = memRoll [ i ] ;
}
excAvge / = MEMCOUNT ;
memAvge / = MEMCOUNT ;
2023-01-21 19:32:56 +00:00
2025-01-08 14:49:32 +00:00
/ * Convert to 1 KB blocks .
* /
if ( memAvge % 1024 )
{
memAvge = ( ( memAvge / 1024 ) + 1 ) * 1024 ;
}
if ( excAvge % 1024 )
{
excAvge = ( ( excAvge / 1024 ) + 1 ) * 1024 ;
}
2023-01-21 19:45:49 +00:00
2025-01-08 14:49:32 +00:00
/ * Update peak memory usage if necessary .
* /
if ( memLast > memPeak )
{
memPeak = memLast ;
}
if ( excLast > excPeak )
{
excPeak = excLast ;
}
if ( 0 = = memInitial )
{
memInitial = memPeak ;
}
2023-01-21 19:32:56 +00:00
2025-01-08 14:49:32 +00:00
/ * If we have a defined maximum memory usage for the process ,
* we should perform a restart once that limit is passed .
* /
2024-10-18 11:05:22 +00:00
if ( memMaximum > 0 )
{
2025-01-08 14:49:32 +00:00
static EcAlarm * raised = nil ;
uint64_t minMax = ( memInitial * 12 ) / 10 ;
2024-10-18 11:05:22 +00:00
2025-01-08 14:49:32 +00:00
if ( minMax > ( memMaximum * 1024 * 1024 ) )
2024-10-18 11:05:22 +00:00
{
2025-01-08 14:49:32 +00:00
unsigned long oldMaximum = ( unsigned long ) memMaximum ;
memMaximum = 0 ;
if ( nil = = raised )
2024-10-18 11:05:22 +00:00
{
2025-01-08 14:49:32 +00:00
EcAlarm * a ;
NSString * repair ;
NSString * additional ;
repair = [ NSString stringWithFormat :
@ "Reconfigure MemoryMaximum to good value (at least %luMB)" ,
( unsigned long ) ( minMax / ( 1024 * 1024 ) ) ] ;
additional = [ NSString stringWithFormat :
@ "configured value (%luMB) ignored" , oldMaximum ] ;
a = [ EcAlarm alarmForManagedObject : nil
at : nil
withEventType : EcAlarmEventTypeProcessingError
probableCause : EcAlarmConfigurationOrCustomizationError
specificProblem : @ "MemoryMaximum too low"
perceivedSeverity : EcAlarmSeverityMajor
proposedRepairAction : repair
additionalText : additional ] ;
ASSIGN ( raised , a ) ;
[ self alarm : raised ] ;
2024-10-18 11:05:22 +00:00
}
}
2025-01-08 14:49:32 +00:00
else
{
if ( raised )
{
EcAlarm * a = [ raised clear ] ;
2015-07-08 09:06:26 +00:00
2025-01-08 14:49:32 +00:00
DESTROY ( raised ) ;
[ self alarm : a ] ;
}
}
if ( memMaximum > 0 )
2019-08-11 12:35:10 +00:00
{
2025-01-08 14:49:32 +00:00
int64_t excess = memPeak - ( memMaximum * 1024 * 1024 ) ;
if ( excess > 0 )
{
if ( NO = = memRestart )
{
memRestart = YES ;
NSLog ( @ "MemoryMaximum exceeded by %" PRId64 " bytes"
@ " ... initiating restart" , excess ) ;
[ self ecRestart : @ "memory usage limit reached" ] ;
}
return ;
}
2019-08-11 12:35:10 +00:00
}
2019-08-10 09:19:16 +00:00
}
2025-01-08 14:49:32 +00:00
setMemBase ( ) ;
if ( memWarn > 0 && memAvge > memWarn )
2019-08-10 09:19:16 +00:00
{
2025-01-08 14:49:32 +00:00
if ( memAvge > memCrit )
2019-08-11 12:35:10 +00:00
{
2025-01-08 14:49:32 +00:00
severity = EcAlarmSeverityCritical ;
2019-08-11 12:35:10 +00:00
}
2025-01-08 14:49:32 +00:00
else if ( memAvge > memMajr )
2019-08-11 12:35:10 +00:00
{
2025-01-08 14:49:32 +00:00
if ( memAlarm ! = EcAlarmSeverityCritical )
{
severity = EcAlarmSeverityMajor ;
}
}
else if ( memAvge > memMinr )
{
if ( memAlarm ! = EcAlarmSeverityCritical
&& memAlarm ! = EcAlarmSeverityMajor )
{
severity = EcAlarmSeverityMinor ;
}
}
else
{
if ( memAlarm ! = EcAlarmSeverityCritical
&& memAlarm ! = EcAlarmSeverityMajor
&& memAlarm ! = EcAlarmSeverityMinor )
{
severity = EcAlarmSeverityWarning ;
}
2019-08-11 12:35:10 +00:00
}
2019-08-10 09:19:16 +00:00
}
2015-07-08 09:06:26 +00:00
2025-01-08 14:49:32 +00:00
if ( EcAlarmSeverityCleared = = severity )
2019-08-10 09:19:16 +00:00
{
2025-01-08 14:49:32 +00:00
if ( nil ! = alarm )
{
EcAlarm * clear = [ alarm clear ] ;
2015-07-09 12:58:57 +00:00
2025-01-08 14:49:32 +00:00
DESTROY ( alarm ) ;
[ self alarm : clear ] ;
}
2019-08-10 09:19:16 +00:00
}
2025-01-08 14:49:32 +00:00
else
{
NSString * additional ;
2015-07-09 12:58:57 +00:00
2025-01-08 14:49:32 +00:00
additional = [ NSString stringWithFormat :
@ "Average %@ memory usage %lu%@ (base %lu%@, max %lu%@)" ,
memType ,
( unsigned long ) memAvge / memSize , memUnit ,
( unsigned long ) memBase / memSize , memUnit ,
( unsigned long ) memMaximum * 1024 * 1024 / memSize , memUnit ] ;
2015-07-08 09:06:26 +00:00
2025-01-08 14:49:32 +00:00
NSLog ( @ "%@" , additional ) ;
2019-08-10 09:19:16 +00:00
2025-01-08 14:49:32 +00:00
/ * When we are critically close to reaching our memory limit
* AND it ' s the time of day when the process is idle , we need
* to restart .
2019-08-10 09:19:16 +00:00
* /
2025-01-08 14:49:32 +00:00
if ( EcAlarmSeverityCritical = = severity )
2019-08-10 09:19:16 +00:00
{
2025-01-08 14:49:32 +00:00
NSString * idle ;
int hour ;
/ * Idle period is hour of the day and number of hours from 1 to 10
* /
idle = [ cmdDefs stringForKey : @ "MemoryIdle" ] ;
if ( [ idle length ] > 0
&& ( hour = [ idle intValue ] ) >= 0 && hour < 24 )
2019-08-10 09:19:16 +00:00
{
2025-01-08 14:49:32 +00:00
if ( [ [ NSCalendarDate date ] hourOfDay ] = = hour )
2019-08-10 09:19:16 +00:00
{
2025-01-08 14:49:32 +00:00
if ( NO = = memRestart )
{
memRestart = YES ;
NSLog ( @ "MemoryMaximum near in idle time; restart" ) ;
[ self ecRestart : @ "memory usage limit when idle" ] ;
}
return ;
2019-08-10 09:19:16 +00:00
}
}
}
2025-01-08 14:49:32 +00:00
if ( [ alarm perceivedSeverity ] ! = severity )
{
EcAlarm * a ;
a = [ EcAlarm alarmForManagedObject : nil
at : nil
withEventType : EcAlarmEventTypeProcessingError
probableCause : EcAlarmOutOfMemory
specificProblem : @ "MemoryAllowed exceeded"
perceivedSeverity : severity
proposedRepairAction : @ "Investigate possible leak,"
@ " monitor usage, restart process when necessary."
additionalText : additional ] ;
ASSIGN ( alarm , a ) ;
[ self alarm : alarm ] ;
}
2019-08-10 09:19:16 +00:00
}
2025-01-08 14:49:32 +00:00
if ( YES = = memDebug )
2019-08-10 09:19:16 +00:00
{
2025-01-08 14:49:32 +00:00
[ self cmdDbg : cmdDetailDbg
msg : @ "%@ memory usage %" PRIu64 "%@ (reserved: %" PRIu64 "%@)" ,
memType , memLast / memSize , memUnit , excLast / memSize , memUnit ] ;
2019-08-10 09:19:16 +00:00
}
2015-07-08 09:06:26 +00:00
}
}
2017-06-23 10:26:17 +00:00
- ( NSString * ) _moveLog : ( NSString * ) name to : ( NSDate * ) when
2012-02-19 11:59:22 +00:00
{
2017-06-23 10:26:17 +00:00
NSString * status = nil ;
2012-02-19 11:59:22 +00:00
NS_DURING
{
2017-06-23 10:26:17 +00:00
NSFileManager * mgr = [ NSFileManager defaultManager ] ;
NSString * from ;
NSDictionary * attr ;
2012-02-19 11:59:22 +00:00
2017-06-23 10:26:17 +00:00
from = [ cmdLogsDir ( nil ) stringByAppendingPathComponent : name ] ;
attr = [ mgr fileAttributesAtPath : from traverseLink : NO ] ;
if ( nil ! = attr )
{
if ( [ [ attr objectForKey : NSFileSize ] intValue ] = = 0 )
{
[ mgr removeFileAtPath : from handler : nil ] ;
status = [ NSString stringWithFormat :
@ "Removed empty log %@" , from ] ;
}
else
{
NSString * where ;
NSString * sub ;
2012-02-19 11:59:22 +00:00
2017-06-23 10:26:17 +00:00
if ( nil = = when )
{
when = [ attr fileModificationDate ] ;
}
sub = [ when descriptionWithCalendarFormat : @ "%Y-%m-%d"
timeZone : nil
locale : nil ] ;
where = cmdLogsDir ( sub ) ;
if ( where ! = nil )
{
NSString * path ;
NSString * base ;
NSString * gzpath ;
unsigned count = 0 ;
path = [ where stringByAppendingPathComponent : name ] ;
/ *
* Check for pre - existing file - if found , try another .
* /
base = path ;
path = [ base stringByAppendingPathExtension : @ "0" ] ;
gzpath = [ path stringByAppendingPathExtension : @ "gz" ] ;
while ( [ mgr fileExistsAtPath : path ] = = YES
|| [ mgr fileExistsAtPath : gzpath ] = = YES )
{
NSString * ext ;
2012-02-19 11:59:22 +00:00
2017-06-23 10:26:17 +00:00
ext = [ stringClass stringWithFormat : @ "%u" , + + count ] ;
path = [ base stringByAppendingPathExtension : ext ] ;
gzpath = [ path stringByAppendingPathExtension : @ "gz" ] ;
}
2012-02-19 11:59:22 +00:00
2017-06-23 10:26:17 +00:00
if ( [ mgr movePath : from
toPath : path
handler : nil ] = = NO )
{
status = [ NSString stringWithFormat :
@ "Unable to move %@ to %@" , from , path ] ;
}
else
{
status = [ NSString stringWithFormat :
@ "Moved %@ to %@" , from , path ] ;
}
}
else
{
status = [ NSString stringWithFormat :
@ "Unable to archive log %@ into %@" , name , sub ] ;
}
}
}
2012-02-19 11:59:22 +00:00
}
NS_HANDLER
{
status = [ NSString stringWithFormat : @ "Problem in %@ with %@ to %@ - %@" ,
2017-06-23 10:26:17 +00:00
NSStringFromSelector ( _cmd ) , name , when , localException ] ;
2012-02-19 11:59:22 +00:00
}
NS_ENDHANDLER
return status ;
}
- ( void ) _timedOut : ( NSTimer * ) timer
{
static BOOL inProgress = NO ;
2012-02-19 14:42:50 +00:00
int sig = [ self cmdSignalled ] ;
2012-02-19 11:59:22 +00:00
cmdPTimer = nil ;
if ( sig > 0 )
{
2017-11-02 09:19:57 +00:00
NSString * shutdown ;
shutdown = [ NSString stringWithFormat : @ "signal %d received" , sig ] ;
2017-11-02 16:16:21 +00:00
[ self ecQuitFor : shutdown with : sig ] ;
2012-02-19 11:59:22 +00:00
}
2017-11-02 09:19:57 +00:00
if ( YES = = ecIsQuitting ( ) )
2014-05-16 22:13:43 +00:00
{
NSLog ( @ "_timedOut: ignored because process is quitting" ) ;
}
else if ( YES = = inProgress )
2012-02-19 11:59:22 +00:00
{
NSLog ( @ "_timedOut: ignored because timeout already in progress" ) ;
}
else
{
BOOL delay = NO ;
inProgress = YES ;
2014-05-16 22:13:43 +00:00
/ * We only perform timeouts if the process is actually
* running ( don ' t want them during startup before the
* thing is fully initialised .
* So if not running , skip to scheduling next timeout .
* /
if ( YES = = cmdIsRunning )
{
NS_DURING
{
NSCalendarDate * now = [ NSCalendarDate date ] ;
2017-07-06 15:51:34 +00:00
static int lastDay = -1 ;
static int lastHour = -1 ;
static int lastMinute = -1 ;
static int lastTenSecond = -1 ;
2014-05-16 22:13:43 +00:00
BOOL newDay = NO ;
BOOL newHour = NO ;
BOOL newMinute = NO ;
BOOL newTenSecond = NO ;
int i ;
i = [ now dayOfWeek ] ;
if ( i ! = lastDay )
{
lastDay = i ;
newDay = YES ;
newHour = YES ;
newMinute = YES ;
newTenSecond = YES ;
}
i = [ now hourOfDay ] ;
if ( i ! = lastHour )
{
lastHour = i ;
newHour = YES ;
newMinute = YES ;
newTenSecond = YES ;
}
i = [ now minuteOfHour ] ;
if ( i ! = lastMinute )
{
lastMinute = i ;
newMinute = YES ;
newTenSecond = YES ;
}
i = [ now secondOfMinute ] / 10 ;
if ( i ! = lastTenSecond )
{
lastTenSecond = i ;
newTenSecond = YES ;
}
if ( YES = = newTenSecond )
{
[ self cmdNewServer ] ;
}
if ( YES = = newMinute )
{
[ self ecNewMinute : now ] ;
}
if ( YES = = newHour )
{
[ self ecNewHour : now ] ;
}
if ( YES = = newDay )
{
[ self ecNewDay : now ] ;
}
if ( cmdTimSelector ! = 0 )
{
[ self performSelector : cmdTimSelector ] ;
}
}
NS_HANDLER
{
2015-05-14 13:47:30 +00:00
NSLog ( @ "Exception performing regular timeout: %@" ,
localException ) ;
2014-05-16 22:13:43 +00:00
delay = YES ; // Avoid runaway logging .
}
NS_ENDHANDLER
}
2012-02-19 11:59:22 +00:00
if ( cmdPTimer = = nil )
{
NSTimeInterval when = cmdTimInterval ;
2015-10-30 11:02:26 +00:00
if ( when < 0.001 || ( when < 10.0 && YES = = delay ) )
2012-02-19 11:59:22 +00:00
{
when = 10.0 ;
}
2015-10-30 11:02:26 +00:00
if ( when > 300.0 )
{
when = 60.0 ;
}
2012-02-19 11:59:22 +00:00
cmdPTimer =
[ NSTimer scheduledTimerWithTimeInterval : when
target : self
selector : @ selector ( _timedOut : )
userInfo : nil
repeats : NO ] ;
}
inProgress = NO ;
}
}
2012-11-23 12:45:09 +00:00
- ( void ) _update : ( NSMutableDictionary * ) info
{
NSMutableDictionary * newConfig ;
NSDictionary * dict ;
NSEnumerator * enumerator ;
NSString * key ;
2018-01-04 11:30:24 +00:00
if ( YES = = ecIsQuitting ( ) )
{
NSLog ( @ "Configuration change during process shutdown ... ignored." ) ;
return ; // Ignore config updates while quitting
}
2023-03-08 14:43:48 +00:00
/ * The configuration should contain information about any operators
* who are allowed to issue commands to this process .
* /
[ self ecOperators : [ info objectForKey : @ "Operators" ] ] ;
2012-11-23 12:45:09 +00:00
newConfig = [ NSMutableDictionary dictionaryWithCapacity : 32 ] ;
/ *
* Put all values for this application in the cmdConf dictionary .
* /
dict = [ info objectForKey : cmdLogName ( ) ] ;
if ( dict ! = nil )
{
enumerator = [ dict keyEnumerator ] ;
while ( ( key = [ enumerator nextObject ] ) ! = nil )
{
id obj ;
if ( [ noNetConfig containsObject : key ] )
{
[ self cmdWarn : @ "Bad key '%@' in net config." , key ] ;
continue ;
}
obj = [ dict objectForKey : key ] ;
[ newConfig setObject : obj forKey : key ] ;
}
}
/ *
* Add any default values to the cmdConf
* dictionary where we don ' t have application
* specific values .
* /
dict = [ info objectForKey : @ "*" ] ;
if ( dict )
{
enumerator = [ dict keyEnumerator ] ;
while ( ( key = [ enumerator nextObject ] ) ! = nil )
{
if ( [ newConfig objectForKey : key ] = = nil )
{
id obj ;
if ( [ noNetConfig containsObject : key ] )
{
[ self cmdWarn : @ "Bad key '%@' in net config." , key ] ;
continue ;
}
obj = [ dict objectForKey : key ] ;
[ newConfig setObject : obj forKey : key ] ;
}
}
}
if ( nil = = cmdConf || [ cmdConf isEqual : newConfig ] = = NO )
{
2018-01-04 11:23:29 +00:00
DESTROY ( configError ) ;
configInProgress = YES ;
2012-11-23 12:45:09 +00:00
NS_DURING
[ self cmdUpdate : newConfig ] ;
NS_HANDLER
2013-11-04 07:28:57 +00:00
NSLog ( @ "Problem before updating config (in cmdUpdate:) %@" ,
localException ) ;
2018-01-04 11:23:29 +00:00
ASSIGN ( configError , @ "the -cmdUpdate: method raised an exception" ) ;
2012-11-23 12:45:09 +00:00
NS_ENDHANDLER
2018-01-04 11:23:29 +00:00
configInProgress = NO ;
[ self _defaultsChanged : nil ] ;
2012-11-23 12:45:09 +00:00
}
}
2012-02-19 11:59:22 +00:00
@ end
2014-03-28 08:07:59 +00:00
@ implementation EcProcess ( Test )
- ( bycopy NSString * ) ecTestCommand : ( in bycopy NSString * ) command
{
NSEnumerator * enumerator ;
NSMutableArray * words ;
NSString * word ;
command = [ command stringByTrimmingSpaces ] ;
words = [ NSMutableArray arrayWithCapacity : 16 ] ;
enumerator = [ [ command componentsSeparatedByString : @ " " ] objectEnumerator ] ;
while ( ( word = [ enumerator nextObject ] ) ! = nil )
{
[ words addObject : [ word stringByTrimmingSpaces ] ] ;
}
if ( [ words count ] = = 0 )
{
return nil ;
}
2023-03-08 14:43:48 +00:00
return [ self ecMesg : words from : nil ] ;
2014-03-28 08:07:59 +00:00
}
- ( bycopy NSData * ) ecTestConfigForKey : ( in bycopy NSString * ) key
{
id result = [ cmdDefs objectForKey : key ] ;
if ( nil ! = result )
{
result = [ NSPropertyListSerialization
dataFromPropertyList : result
format : NSPropertyListBinaryFormat_v1 _0
errorDescription : 0 ] ;
}
return result ;
}
- ( void ) ecTestSetConfig : ( in bycopy NSData * ) data
forKey : ( in bycopy NSString * ) key
{
id val ;
if ( nil = = data )
{
val = data ;
}
else
{
val = [ NSPropertyListSerialization
propertyListWithData : data
options : NSPropertyListMutableContainers
format : 0
error : 0 ] ;
}
2020-07-08 15:48:26 +00:00
[ cmdDefs setCommand : val forKey : [ cmdDefs key : key ] ] ;
2014-03-28 08:07:59 +00:00
}
@ end
2014-11-02 14:30:24 +00:00
@ implementation EcProcess ( Defaults )
- ( void ) _defMemory : ( id ) val
{
2015-07-09 12:08:58 +00:00
BOOL stats = [ val boolValue ] ;
GSDebugAllocationActive ( stats ) ;
if ( YES = = stats && nil = = memStats )
{
ASSIGN ( memStats , [ NSDate date ] ) ;
}
if ( NO = = stats && nil ! = memStats )
{
DESTROY ( memStats ) ;
}
2014-11-02 14:30:24 +00:00
}
- ( void ) _defRelease : ( id ) val
{
[ NSObject enableDoubleReleaseCheck : [ val boolValue ] ] ;
}
- ( void ) _defTesting : ( id ) val
{
cmdFlagTesting = [ val boolValue ] ;
}
@ end
@ implementation EcDefaultRegistration
static NSMutableDictionary * regDefs = nil ;
+ ( void ) defaultsChanged : ( NSUserDefaults * ) defs
{
NSEnumerator * e ;
NSString * n ;
[ ecLock lock ] ;
e = [ [ regDefs allKeys ] objectEnumerator ] ;
[ ecLock unlock ] ;
while ( nil ! = ( n = [ e nextObject ] ) )
{
EcDefaultRegistration * d ;
id o = nil ;
SEL c = NULL ;
[ ecLock lock ] ;
d = [ regDefs objectForKey : n ] ;
if ( nil ! = d )
{
o = [ defs objectForKey : n ] ;
if ( o ! = d -> obj && NO = = [ o isEqual : d -> obj ] )
{
ASSIGNCOPY ( d -> obj , o ) ;
o = d -> obj ;
c = d -> cmd ;
}
}
[ ecLock unlock ] ;
if ( NULL ! = c && [ EcProc respondsToSelector : c ] )
{
[ EcProc performSelector : c withObject : o ] ;
}
}
}
+ ( void ) initialize
{
regDefs = [ NSMutableDictionary new ] ;
}
2018-01-19 09:50:43 +00:00
/ * Key may be one of :
* nil list all registered defaults keys
* empty list all registered defaults help
* other list the registered defaults help for the specified key
* /
+ ( NSMutableString * ) listHelp : ( NSString * ) key
2015-11-18 16:16:10 +00:00
{
NSMutableString * out = [ NSMutableString stringWithCapacity : 1000 ] ;
NSArray * keys ;
NSString * prf ;
NSEnumerator * e ;
NSString * k ;
NSUInteger max = 0 ;
prf = EC_DEFAULTS _PREFIX ;
if ( nil = = prf )
{
prf = @ "" ;
}
2015-11-18 17:28:59 +00:00
[ ecLock lock ] ;
2015-11-18 16:16:10 +00:00
keys = [ regDefs allKeys ] ;
2015-11-18 17:28:59 +00:00
[ ecLock unlock ] ;
2015-11-18 16:16:10 +00:00
e = [ keys objectEnumerator ] ;
while ( nil ! = ( k = [ e nextObject ] ) )
{
2015-11-18 17:28:59 +00:00
EcDefaultRegistration * d ;
2015-11-18 16:16:10 +00:00
2015-11-18 17:28:59 +00:00
[ ecLock lock ] ;
d = [ regDefs objectForKey : k ] ;
2015-11-18 16:16:10 +00:00
if ( nil ! = d -> type && nil ! = d -> help )
{
NSUInteger length = [ prf length ] + 5 ;
length + = [ k length ] + [ d -> type length ] ;
if ( length > max )
{
max = length ;
}
}
2015-11-18 17:28:59 +00:00
[ ecLock unlock ] ;
2015-11-18 16:16:10 +00:00
}
keys = [ keys sortedArrayUsingSelector : @ selector ( compare : ) ] ;
e = [ keys objectEnumerator ] ;
2018-01-19 09:50:43 +00:00
if ( nil = = key )
2015-11-18 16:16:10 +00:00
{
2018-01-19 09:50:43 +00:00
unsigned col = 0 ;
2015-11-18 16:16:10 +00:00
2018-01-19 09:50:43 +00:00
/ * We just want to list all the keys . . .
* /
while ( nil ! = ( k = [ e nextObject ] ) )
2015-11-18 16:16:10 +00:00
{
2018-01-19 09:50:43 +00:00
if ( col + [ k length ] > 70 )
{
[ out appendString : @ "\n" ] ;
col = 0 ;
}
if ( col > 0 )
2015-11-18 16:16:10 +00:00
{
2018-01-19 09:50:43 +00:00
[ out appendString : @ " " ] ;
col + + ;
}
[ out appendString : k ] ;
col = [ k length ] ;
}
if ( col > 0 )
{
[ out appendString : @ "\n" ] ;
}
}
else
{
while ( nil ! = ( k = [ e nextObject ] ) )
{
EcDefaultRegistration * d ;
2015-11-18 16:16:10 +00:00
2018-01-19 09:50:43 +00:00
if ( [ key length ] > 0 )
{
/ * We want help for a specific key .
* /
if ( [ key caseInsensitiveCompare : k ] ! = NSOrderedSame )
2015-11-18 16:16:10 +00:00
{
2018-01-19 09:50:43 +00:00
NSString * pk = [ prf stringByAppendingString : k ] ;
if ( [ key caseInsensitiveCompare : pk ] ! = NSOrderedSame )
{
continue ; / * This is not the key we are looking for * /
}
2015-11-18 16:16:10 +00:00
}
}
2018-01-19 09:50:43 +00:00
[ ecLock lock ] ;
d = [ regDefs objectForKey : k ] ;
if ( nil ! = d -> type && nil ! = d -> help )
2015-11-18 16:16:10 +00:00
{
2018-01-19 09:50:43 +00:00
/ * If the help text is short enough , put it all on one line .
* /
if ( [ d -> help length ] + max < 80 )
{
NSMutableString * m ;
m = [ NSMutableString stringWithFormat : @ "-%@%@ [%@] " ,
prf , k , d -> type ] ;
while ( [ m length ] < max )
{
[ m appendString : @ " " ] ;
}
[ out appendFormat : @ "%@%@\n" , m , d -> help ] ;
}
else
{
[ out appendFormat : @ "-%@%@ [%@]\n %@\n" ,
prf , k , d -> type , d -> help ] ;
}
2015-11-18 16:16:10 +00:00
}
2018-01-19 09:50:43 +00:00
[ ecLock unlock ] ;
2015-11-18 16:16:10 +00:00
}
}
return out ;
}
2015-11-18 17:28:59 +00:00
+ ( NSDictionary * ) merge : ( NSDictionary * ) d
{
NSMutableDictionary * m = AUTORELEASE ( [ d mutableCopy ] ) ;
NSEnumerator * e ;
NSString * k ;
if ( nil = = m )
{
m = [ NSMutableDictionary dictionaryWithCapacity : [ regDefs count ] ] ;
}
[ ecLock lock ] ;
e = [ regDefs keyEnumerator ] ;
while ( nil ! = ( k = [ e nextObject ] ) )
{
EcDefaultRegistration * r = [ regDefs objectForKey : k ] ;
if ( nil ! = r -> val && nil = = [ d objectForKey : k ] )
{
[ m setObject : r -> val forKey : k ] ;
}
}
[ ecLock unlock ] ;
return m ;
}
2014-11-02 14:30:24 +00:00
+ ( void ) registerDefault : ( NSString * ) name
withTypeText : ( NSString * ) type
andHelpText : ( NSString * ) help
action : ( SEL ) cmd
2015-11-18 17:28:59 +00:00
value : ( id ) value
2014-11-02 14:30:24 +00:00
{
static NSCharacterSet * w = nil ;
EcDefaultRegistration * d ;
if ( nil = = w )
{
w = RETAIN ( [ NSCharacterSet whitespaceAndNewlineCharacterSet ] ) ;
}
if ( [ type length ] > 0 )
{
type = [ type stringByTrimmingSpaces ] ;
if ( [ type length ] = = 0 )
{
type = nil ;
}
else
{
NSUInteger length = [ type length ] ;
NSMutableString * m = nil ;
while ( length - - > 0 )
{
unichar u = [ type characterAtIndex : length ] ;
if ( u ! = ' ' && [ w characterIsMember : u ] )
{
if ( nil = = m )
{
m = AUTORELEASE ( [ type mutableCopy ] ) ;
type = m ;
}
[ m replaceCharactersInRange : NSMakeRange ( length , 1 )
withString : @ " " ] ;
}
}
}
}
if ( [ help length ] > 0 )
{
help = [ help stringByTrimmingSpaces ] ;
if ( [ help length ] = = 0 )
{
help = nil ;
}
}
[ ecLock lock ] ;
d = [ regDefs objectForKey : name ] ;
if ( nil = = d )
{
d = [ EcDefaultRegistration new ] ;
ASSIGNCOPY ( d -> name , name ) ;
[ regDefs setObject : d forKey : d -> name ] ;
RELEASE ( d ) ;
}
ASSIGNCOPY ( d -> type , type ) ;
ASSIGNCOPY ( d -> help , help ) ;
if ( 0 ! = cmd )
{
d -> cmd = cmd ;
}
2015-11-18 17:28:59 +00:00
ASSIGNCOPY ( d -> val , value ) ;
2014-11-02 14:30:24 +00:00
[ ecLock unlock ] ;
}
+ ( void ) showHelp
{
2018-01-19 09:50:43 +00:00
GSPrintf ( stderr , @ "%@" , [ self listHelp : @ "" ] ) ;
2014-11-02 14:30:24 +00:00
}
- ( void ) dealloc
{
RELEASE ( name ) ;
RELEASE ( type ) ;
RELEASE ( help ) ;
RELEASE ( obj ) ;
[ super dealloc ] ;
}
@ end