/** Enterprise Control Configuration and Logging Copyright (C) 2012 Free Software Foundation, Inc. Written by: Richard Frith-Macdonald 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. */ #ifndef INCLUDED_ECPROCESS_H #define INCLUDED_ECPROCESS_H #import #import "EcAlarm.h" #import "EcAlarmDestination.h" /** Convenience macros to raise unique alarms (which do not clear automatically) * for exceptions or unexpected code/data errors. The unique specificProblem * for each alarm is derived from the file and line at which it is raised. * These macros should only be used when it's impossible/impractical to have * the code automatically detect that a problem has gone away, and clear the * alarm. */ #define EcExceptionCritical(cause, format, args...) \ ({ if (nil == EcProc) NSLog(@"%@",\ [NSString stringWithFormat: @"%s at %@ line %d ... %@", \ (nil == (cause) ? "Code/Data Error (critical)" : "Exception"), \ [[NSString stringWithUTF8String: __FILE__] lastPathComponent], __LINE__, \ [NSString stringWithFormat: (format), ##args]]); else \ [EcProc ecException: (cause) \ specificProblem: [NSString stringWithFormat: @"%s at %@ line %d", \ (nil == (cause) ? "Code/Data Error (critical)" : "Exception"), \ [[NSString stringWithUTF8String: __FILE__] lastPathComponent], __LINE__] \ perceivedSeverity: EcAlarmSeverityCritical \ message: (format), ##args ]; \ }) #define EcExceptionMajor(cause, format, args...) \ ({ if (nil == EcProc) NSLog(@"%@",\ [NSString stringWithFormat: @"%s at %@ line %d ... %@", \ (nil == (cause) ? "Code/Data Error" : "Exception"), \ [[NSString stringWithUTF8String: __FILE__] lastPathComponent], __LINE__, \ [NSString stringWithFormat: (format), ##args]]); else \ [EcProc ecException: (cause) \ specificProblem: [NSString stringWithFormat: @"%s at %@ line %d", \ (nil == (cause) ? "Code/Data Error" : "Exception"), \ [[NSString stringWithUTF8String: __FILE__] lastPathComponent], __LINE__] \ perceivedSeverity: EcAlarmSeverityMajor \ message: (format), ##args ]; \ }) #define EcExceptionMinor(cause, format, args...) \ ({ if (nil == EcProc) NSLog(@"%@",\ [NSString stringWithFormat: @"%s at %@ line %d ... %@", \ (nil == (cause) ? "Code/Data Error (minor)" : "Exception"), \ [[NSString stringWithUTF8String: __FILE__] lastPathComponent], __LINE__, \ [NSString stringWithFormat: (format), ##args]]); else \ [EcProc ecException: (cause) \ specificProblem: [NSString stringWithFormat: @"%s at %@ line %d", \ (nil == (cause) ? "Code/Data Error (minor)" : "Exception"), \ [[NSString stringWithUTF8String: __FILE__] lastPathComponent], __LINE__] \ perceivedSeverity: EcAlarmSeverityMinor \ message: (format), ##args ]; \ }) /* Diagnostic logging including a stack trace (either from an exception or, * if the exception is nil, from the point at which the log is generated. * This is like the other EcException macros, except that it does not * generate an alarm. */ #define EcExceptionDebug(cause, format, args...) \ ({ if (nil == EcProc) NSLog(@"%@",\ [NSString stringWithFormat: @"%s at %@ line %d ... %@", \ (nil == (cause) ? "Code/Data Diagnostics" : "Harmless Exception"), \ [[NSString stringWithUTF8String: __FILE__] lastPathComponent], __LINE__, \ [NSString stringWithFormat: (format), ##args]]); else \ [EcProc ecException: (cause) \ specificProblem: [NSString stringWithFormat: @"%s at %@ line %d", \ (nil == (cause) ? "Code/Data Diagnostics" : "Harmless Exception"), \ [[NSString stringWithUTF8String: __FILE__] lastPathComponent], __LINE__] \ perceivedSeverity: EcAlarmSeverityCleared \ message: (format), ##args ]; \ }) @class NSFileHandle; typedef enum { LT_DEBUG, /* Debug message - internal info. */ LT_WARNING, /* Warning - possibly a real problem. */ LT_ERROR, /* Error - needs dealing with. */ LT_AUDIT, /* Not a problem but needs logging. */ LT_ALERT, /* Severe - needs immediate attention. */ LT_CONSOLE, /* For Console (implicit audit). */ } EcLogType; /** * A 'ping' can be sent from or to any process to check that it is alive. * A 'gnip' should be sent in response to each 'ping' * Each 'ping' carries a sequence number which should be echoed in its 'gnip'. * Optionally, the 'ping' can also transport a serialized property-list * to provide additional data or instructions - the value of which is * dependent on the program being pinged - normally this value is nil. */ @protocol CmdPing - (oneway void) cmdGnip: (id )from sequence: (unsigned)num extra: (in bycopy NSData*)data; - (oneway void) cmdPing: (id )from sequence: (unsigned)num extra: (in bycopy NSData*)data; @end /** The CmdConfig protocol is needed by objects that send and receive * configuration information. */ @protocol CmdConfig - (oneway void) requestConfigFor: (id)c; - (oneway void) updateConfig: (in bycopy NSData*)info; @end @protocol EcConfigForwarded; /** The EcConfigForwarding protocol is provided by a process to allow * cooperating client processes to know how it is configured and to get * automatic updates when its configuration changes. */ @protocol EcConfigForwarding /** This method may be called by a client when it wishes to stop receiving * configuraton updates from the server. */ - (oneway void) ecCancelConfigFwdTo: (id)client; /** This method is called by the client when it wants to start receiving * configuration updates from the server. The method returns the current * configuration of the server (at the point when the method was called). * NB. With multithreaded applications it is possible that setting up * configuration forwarding could cause a client to be informed of a * configuration change before the setup method returns the configuration. */ - (bycopy NSDictionary*) ecSetupConfigFwdTo: (id)client; @end /** This is the protocol to which a client of configuration forwarding must * confirm. It contains a single method that the server calls to pass config * to the client. */ @protocol EcConfigForwarded - (oneway void) ecForwardedConfig: (in bycopy NSDictionary*)info from: (id)server; @end /** Messages that the Command server may send to clients. */ @protocol CmdClient /** Passes a property list message to the client (eg from the Console). */ - (oneway void) cmdMesgData: (in bycopy NSData*)dat from: (NSString*)name; /** Tells the client to shut down. */ - (oneway void) cmdQuit: (NSInteger)status; /** Asks the client for its process identifier. */ - (int) processIdentifier; /** Asks the client whether it is awake (-ecAwaken has been called) */ - (BOOL) ecDidAwaken; /** Asks the client whether it is completely awake (-ecAwaken has ended) */ - (BOOL) ecDidAwakenCompletely; /** Instructs the client process to connect and re-register with the Command * server. */ - (oneway void) ecReconnect; @end /** Messages a Command logging process can be expected to handle. */ @protocol CmdLogger - (void) flush; - (oneway void) logMessage: (NSString*)msg type: (EcLogType)t name: (NSString*)c; - (oneway void) logMessage: (NSString*)msg type: (EcLogType)t for: (id)o; - (bycopy NSData*) registerClient: (id)c identifier: (int)p name: (NSString*)n transient: (BOOL)t; - (void) unregisterByObject: (byref id)obj status: (int)s; - (void) woken: (id)obj; @end @protocol Console - (oneway void) information: (NSString*)txt; @end /** Messages that clients may send to the server. * NB. The -registerClient:identifier:name:transient: method must be sent * before the -command:to:from: or -reply:to:from: methods. */ @protocol Command /** Request a count of the active clients of the Command server */ - (unsigned) activeCount; /** Pass an alarm to the Command server for forwarding to the Control * server for central handling to send alerts or SNMP integration. */ - (oneway void) alarm: (in bycopy EcAlarm*)alarm; /** Pass an alarm clear to the Command server for forwarding to its * clients for clearing. */ - (oneway void) clear: (in bycopy EcAlarm*)alarm; /** Send a text command to a process owned by the Command server. */ - (oneway void) command: (in bycopy NSData*)dat to: (NSString*)t from: (NSString*)f; /** Request immediate launch of the named process.
* Returns NO if the process cannot be launched.
* Returns YES if the process is already running or launching has started. */ - (BOOL) launch: (NSString*)name; /** Registers as a client process of the Command server. */ - (bycopy NSData*) registerClient: (id)c identifier: (int)p name: (NSString*)n transient: (BOOL)t; /** Replies to a text command sent by another process. */ - (oneway void) reply: (NSString*)msg to: (NSString*)n from: (NSString*)c; /** Request immediate restart of the named process.
* The reason string is displayed in reporting etc.
* Returns YES if the process was found, NO otherwise. */ - (BOOL) restart: (NSString*)name reason: (NSString*)reason; /** Shut down the Command server and all its clients.
* Clients which fail to shut down gracefully before the specified timestamp * will be forcibly killed. The timestamp is constrained to be at least half * a second in the future and not more than 15 minutes in the future. */ - (oneway void) terminate: (NSDate*)byDate; /** This is meant to be used remotely by all sorts of software running * on the machine and which is *not* a full Command client (ie, not a * subclass of EcProcess) but which still wants to retrieve * configuration from a central location (the Control/Command servers).
* The returned value is a a serialized property list ... you need to * deserialize using the standard GNUstep property list APIs.
* NB: The configuration might change later on, so you must not cache * the configuration after asking for it, but rather ask for it each * time your software needs it. */ - (bycopy NSData *) configurationFor: (NSString *)name; /** Informs the Command server that a previously registered client considers * itself to have started up and to now be stable. */ - (void) woken: (id)obj; @end /* * Messages that clients may send to the server. */ @protocol Control - (oneway void) alarm: (in bycopy EcAlarm*)alarm; - (oneway void) command: (in bycopy NSData*)cmd from: (NSString*)f; - (oneway void) domanage: (NSString*)name; - (oneway void) information: (NSString*)msg type: (EcLogType)t to: (NSString*)to from: (NSString*)from; - (bycopy NSData*) registerCommand: (id)c name: (NSString*)n; - (bycopy NSString*) registerConsole: (id)c name: (NSString*)n pass: (NSString*)p; - (oneway void) reply: (NSString*)msg to: (NSString*)n from: (NSString*)c; - (oneway void) servers: (in bycopy NSData*)a on: (id)s; - (oneway void) unmanage: (NSString*)name; - (void) unregister: (id)o; @end /* * Useful functions - */ extern void cmdSetHome(NSString *home); extern NSString *cmdDataDir(); extern NSString *cmdLogsDir(NSString *date); extern NSString *cmdLogKey(EcLogType t); extern NSString *cmdLogName(); extern NSString *cmdLogFormat(EcLogType t, NSString *fmt); extern NSString *ecFullName(); extern void ecSetLogsSubdirectory(NSString *pathComponent); /** Return the native thread ID of the current thread, or NSNotFound if * that is not available. */ extern NSUInteger ecNativeThreadID(); /* Set/get version/compilation date. */ extern NSString* cmdVersion(NSString *ver); /** * Command line arguments - * * *

On startup, these are taken from the command line, or from the * local user defaults database of the person running the program.
* If the EcEffectiveUser specifies an alternative user, or the * program is able to read the database for the 'ecuser' user, then * the other values are read from the defaults database of that user. *

*

After startup, the command line arguments still take precedence, * but values retrieved from the network configuration system will * then override any read from the local user defaults database. *

*

Settings in the network configuration system will have no effect on * the following defaults which are used BEFORE the network configuration * can be read. *

* * EcCoreSize * * Specifies the maximum size (in MB) for any core-dump of the * process.
* If this is not set, the default size of 2GB is used.
* If this is negative, the size is unlimited.
* If this is zero then no core dumping is performed. *
* EcDaemon * To specify whether the program should run in the background * (boolean, YES if the program is to run as a daemon, NO otherwise).
* The value in the network configuration has no effect. *
* EcEffectiveUser * To tell the server to change to being this user on startup. * defaults to 'ecuser', but the default can be overridden by specifying * '-DEC_EFFECTIVE_USER+@"username"' in the local.make make file.
* Set a value of '*' to remain whoever runs the program * rather than changing. *
* EcInstance * To set the program instance ID (a non-negative integer value).
* If this is specified, the program name has a hyphen and the * id appended to it by the '-initWithDefaults:' method. *
* EcKeepStandardError * * This boolean value determines whether the standard error output * should be kept as it is on process startup, or should be merged * with the local debug log to file.
* The default (EcKeepStandardError set to NO) is to merge the * standard error logging with the debug logging. *
* EcKillDebugOutput * * This boolean value determines whether debug output (including anything * written to the standard error output if that is merged with debug) * should be discarded (sent to the null device).
* This setting cannot e controlled from the Console command line.
* The default (EcKillDebugOutput set to NO) is to write debug output to * file. *
* EcTransient * * This boolean option is used to specify that the program * should not be restarted automatically by the Command * server if/when it disconnects from that server. * *
*

The following settings will be revised after startup to include the * values from the network configuration system. *

* * EcAuditFlush * A flush interval in seconds (optionally followed by a colon * and a buffer size in KiloBytes) to control flushing of audit logs.
* Setting an interval of zero or less disables flushing by timer.
* Setting a size of zero or less, disables buffering (so logs are * flushed immediately). *
* EcDebug- * * Any key of the form EcDebug-xxx turns on the xxx debug level * on program startup.
* The value of 'XXX' must match the name of a debug mode used * by the program! *
* EcDescriptorsMaximum * * To protect against file descriptor leaks, a process will * check for the ability to create a pipe once a minute.
* If it can't do so, it will shut down with an error message.
* To increase the chances of a successful shutdown, two * descriptors are reserved on the first check, and closed * when a shutdown is attempted.
* If EcDescriptorsMaximum is defined to a positive integer value, * it is used to trigger earlier shutdown once the specified * number of open file descriptors has been reached, rather * than waiting for the operating system imposed limit. *
* EcMemory * * This boolean value determines whether statistics on creation * and destruction of objects are maintained.
* This may be set in the NSUserDefaults system or in Control.plist, * but may be overridden by using the 'memory' command in the * Console program. *
* EcMemoryAlarm * * This may be used to control the alarm severity level at which the * system starts raising alarms about memory usage. Setting it to Warning * means that alarms are raised as soon as the base/allowed memory limit * is passed, while setting it to Critical only causes an alarm to be * raised when the MemoryMaximum limit is close.
* The default value is Major (the possible values are Warning, Minor, * Major, and Critical).
* This may be set in the NSUserDefaults system or in Control.plist, * but may be overridden by using the 'memory' command in the * Console program. *
* EcMemoryAllowed * * This may be used to specify the process memory usage * (in megabytes by default) before memory usage alarms may begin.
* If this setting is not specified (or a negative or excessive value * is specified) then memory is monitored for ten minutes and the * base/allowed threshold is set at either the peak during that period * (plus a twenty percent margin to allow further memory growth) or at * half the MemoryMaximum value, whichever is the greater.
* This may be set in the NSUserDefaults system or in Control.plist, * but may be overridden by using the 'memory' command in the * Console program. *
* EcMemoryIdle * * This optional integer value (0 to 23) may be used to specify the hour * of the day in which restarts are preferred to take place (some time * when the process is likely to be idle). If a process is near a * maximum permitted memory usage at this time of day, * the -ecRestart: method will be called.
* Near means over three quarters of the way between the base/allowed * memory usage (as determined by MemoryAllowed) and MemoryMaximum.
* This may be set in the NSUserDefaults system or in Control.plist, * but may be overridden by using the 'memory' command in the * Console program. *
* EcMemoryMaximum * * This may be used to specify the maximum process memory allowed * (in megabytes by default) before the process is forced to restart due * to excessive memory usage.
* If the total memory usage of the process reaches this threshold, * the -ecRestart: method will be called
* The process will also generate alarms depending on the memory usage * once the bas/allowed memory usge has been passed. The severity of * the alarms depends on how far from the base/allowed memory to the * maximum memory the current ten minute average of the usage is.
* This may be set in the NSUserDefaults system or in Control.plist, * but may be overridden by using the 'memory' command in the * Console program. *
* EcMemoryType * * This controls the type of memory considered by the EcMemoryAllowed, * EcMemoryIdle and EcMemoryMaximum options.
* Total (the default), considers the total process memory usage.
* Resident, considers current resident memory used.
* Data, considers only dynamically allocated and stack memory.
* While using Total and Resident are easy to relate to the output of * system tools such as ps and top, the Data * memory type is probably of most use for tracking memory leaks on a * system with many processes running, since it excludes memory shared * between multiple processes (used for the program code and for shared * libraries). *
* EcMemoryUnit * * This controls the units in which memory information is displayed, * the default being K/KB/KiB (1024 bytes). Other possible settings are * M/MB/MiB (1048576 bytes) and P/Pg/Page (system memory pages, typically * 4096 bytes). * * EcRelease * * This boolean value determines whether checks for memory problems * caused by release an object too many times are done. Turning * this on has a big impact on program performance and is not * recommended except for debugging crashes and other memory * issues.
* This may be set in the NSUserDefaults system or in Control.plist, * but may be overridden by using the 'release' command in the * Console program. *
* EcTesting * * This boolean value determines whether the server is running in * test mode (which may enable extra logging or prevent the server * from communicating with live systems etc ... the actual * behavior is server dependent).
* This may be set on the command line or in Control.plist, but * may be overridden by using the 'testing' command in the * Console program. *
* EcWellKnownHostNames * A dictionary mapping host names/address values to well known * names (the canonical values used by Command and Control). * *
* Alarm mechanism *

* The EcProcess class conforms to the EcAlarmDestination protocol * to allow sending alarm information to a centralised alarm system * via the Command server (the Control server acts as a sink for * those alarms and provides SNMP integration). *

*

* In addition to the standard alarm destination behavior, the * process automates some things:
* On successful startup and registration with the Command server, * a -domanage: message is automatically sent for the default * managed object, clearing any outstanding alarms.
* On successful shutdown (ie when -cmdQuit: is called with zero * as its argument), an -unmanage: message is automatically sent * to clear any outstanding alarms for the default managed object.
* If you want to raise alarms which will persist after a successful * shutdown you should therefore do so by creating a different managed * object for which to raise those alarms. *

*

As a convenience, the class provides various methods to raise different * kinds of alarms for specific common purposes: *

* * Configuration problems * -alarmConfigurationFor:specificProblem:additionalText:critical: * * Exceptions and unexpected errors * -ecException:specificProblem:perceivedSeverity:message:,... * * *

* To further aid with logging/alarming about unexpected code and data * problems, there are macros to provide detailed logs as well as * specific alarms of different severity. *

*/ @interface EcProcess : NSObject { /** Any method which is executing in the main thread (and needs to * return before a quit can be handled in the main thread) must * increment this counter on entry and decrement it again before exit. * This allows the process to ensure that it calls -ecHandleQuit when * no such method is in progress. */ NSUInteger ecDeferQuit; } /** This method is provided to prompt for an encryption key using the * specified key name and read in a value from the terminal.
* The entered value must be an even numbered sequence of hexadecimal * digits, each pair representing one byte of the key.
* The key length (number of bytes) must be the specified size, a value * between 16 and 128, which is exactly half the number of hexadecimal * digits that must be entered.
* If the digest is not supplied, the user will be required to * enter the value twice (and the two values must match) for * confirmation.
* If the digest is supplied, the md5 digest of the entered key must * match it (but the user does not need to enter the value twice). */ + (NSString*) ecGetKey: (const char*)name size: (unsigned)size md5: (NSData*)digest; /** Provides initial configuration. * This method is used by -init and its return value is passed to * -initWithDefaults: method.
* The default implementation simply sets the ProgramName and * HomeDirectory defaults to the current program name and * the user home directory ('.').
* Subclasses may override this method to provide additional * default configuration for processes using them. The returned * dictionary is mutable so that a subclass may simply modify * the configuration provided by the superclass implementation. */ + (NSMutableDictionary*) ecInitialDefaults; /** Returns the lock used by the -ecDoLock and -ecUnLock methods. */ + (NSRecursiveLock*) ecLock; /** Registers an NSUserDefaults key that the receiver understands.
* This is primarily intended for user defaults which can reasonably * be supplied at the command line when a process is started (and for * which the process should therefore supply help information).
* The type text must be a a short string saying what kind of value * must be provided (eg 'YES/NO') for the default, or nil if no help * is to be provided for the default.
* The help text should be a description of what the default does, * or nil if no help is to be provided for the default.
* The action may either be NULL or a selector for a message to be sent * to the EcProc instance with a single argument (the new default value) * when the value of the user default changes.
* The value may either be NULL, or be an object to be set in the registration * domain of the defaults system (as long as this method is called before * the EcProcess instance is initialized).
* If the same default name is registered more than once, the values * from the last registration are used, except for the case where the * cmd argument is NULL, in that case the previous selector is kept * in the new registration.
* This method should be called in your +initialize method, so that all * supported defaults are already registered by the time your process * tries to respond to being started with a --help command line argument.
* NB. defaults keys do not have to be registered (and can still be updated * using the 'defaults' command), but registration provides a more user * friendly interface.
* If this method is called later (once the EcProcess instance has been * initialized) it will not update the current user defaults until you also * call the -ecUpdateRegisteredDefaults method. */ + (void) ecRegisterDefault: (NSString*)name withTypeText: (NSString*)type andHelpText: (NSString*)help action: (SEL)cmd value: (id)value; /** Convenience method to create the singleton EcProcess instance * using the initial configuration provided by the +ecInitialDefaults * method.
* Raises NSGenericException if the singleton instance has already * been created. */ + (void) ecSetup; /** Convenience method to produce a generic configuration alarm and send * it via the -alarm: method.
* The managed object may be nil (in which case it's the default managed object * for the current process).
* The implied event type is EcAlarmEventTypeProcessingError.
* The implied probable cause is EcAlarmConfigurationOrCustomizationError.
* The implied severity is EcAlarmSeverityMajor unless isCritical is YES.
* The implied trend is EcAlarmTrendNone.
* The implied proposed repair action is to check/correct the config.
* The specific problem and additional text should be used to suggest * what is wrong with the config and where the config error should be * found/corrected. */ - (EcAlarm*) alarmConfigurationFor: (NSString*)managedObject specificProblem: (NSString*)specificProblem additionalText: (NSString*)additionalText critical: (BOOL)isCritical; /** Returns the array of current alarms. */ - (NSArray*) alarms; /** Convenience method to clear an alarm as produced by the * -alarmConfigurationFor:specificProblem:additionalText:critical: * method. */ - (void) clearConfigurationFor: (NSString*)managedObject specificProblem: (NSString*)specificProblem additionalText: (NSString*)additionalText; /** Returns the alarm destination for this process. */ - (EcAlarmDestination*) ecAlarmDestination; /** Return a short copyright notice ... subclasses should override. */ - (NSString*) ecCopyright; /** Obtain a lock on the shared EcProcess for thread-safe updates to * process-wide variables. */ - (void) ecDoLock; /** Called once other stages of a graceful shutdown has completed in * order to perform final cleanup and have the process exit with the * expected status.
* Called automatically in the main thread by the -ecHandleQuit method. * Subclasses overriding -ecDidQuit must call the superclass * implementation at the end of their handling code. */ - (oneway void) ecDidQuit; /** Called by -ecQuitFor:with: or -cmdQuit: (after the -ecWillQuit and * before the -ecDidQuit methods) as a method for subclasses to use to * implement their own behaviors.
* Subclass implementations should call the superclass implementation * as the last thing they do.
* This method is always called in the main thread of the process and * when the ecDeferQuit instance variable is zero. */ - (void) ecHandleQuit; /** Returns YES if the process is attempting a graceful shutdown, * NO otherwise. This also checks to see if the process has been * attempting to shut down for too long, and if it has been going * on for over three minutes (or the value set by -ecQuitLimit:), * aborts the process.
* Subclasses must not override this method. */ - (BOOL) ecIsQuitting; /** Returns the interval since the process started quitting, or zero * if it is not quitting (as determined by calling -ecIsQuitting). */ - (NSTimeInterval) ecQuitDuration; /** This method is designed for handling an orderly shutdown by noting * the supplied reason and status, and then calling -ecWillQuit, * -ecHandleQuit, and finally calling -ecDidQuit.
* Subclasses should not normally override this method. Instead override * the -ecHandleQuit method.
* For backward compatibility, this will call the -cmdQuit: method if a * subclass has overridden it.
* NB. On unix the possible range of status values is actually from -128 * to 127 and we have certain conventions: * * 0 * A clean/normal shutdown, no restart required * -1 * An intentional shutdown where restart is required * -2 * Process unable to register with name server * -3 * Fatal error in process configuration information * -4 * Process registration was rejected by the Command server * -5 to -9 * Reserved for possible expansion * -10 to -128 * Unused/free * positive integer * Process caught a unix signal and shut down; the exist status * is the number of that signal * */ - (oneway void) ecQuitFor: (NSString*)reason with: (NSInteger)status; /** Sets the number of seconds the graceful shutdown process is permitted * to take before the process will attempt to abort on the next check to * see if it quitting (-ecIsQuitting). The default is 180.0 seconds.
* This method returns the previous value of the setting. */ - (NSTimeInterval) ecQuitLimit: (NSTimeInterval)seconds; /** Returns the quit reason supplied to the -ecQuitFor:with: method. */ - (NSString*) ecQuitReason; /** Returns the quit status supplied to the -ecQuitFor:with: or -cmdQuit: * method. */ - (NSInteger) ecQuitStatus; /** This method may be called to prompt the process to connect to the * Command server if it is not already connected. */ - (oneway void) ecReconnect; /** This method is designed for handling an orderly restart.
* The default implementation calls -ecQuitFor:with: with minus one as * the status code so that the Command server will start the process * again.
* The method is called automatically when the MemoryMaximum limit is * exceeded (to gracefully handle memory leaks by restarting).
* Subclasses may override this method to allow the shutdown process to be * handled differently. */ - (oneway void) ecRestart: (NSString*)reason; /** Return the timestamp at which this process started up (when the * receiver was initialised). */ - (NSDate*) ecStarted; /** Release a lock on the shared EcProcess after thread-safe updates to * process-wide variables. */ - (void) ecUnLock; /** This is ignored if the process is already quitting, * otherwise it returns after setting the start time used by the * -ecIsQuitting method and (if -ecQuitReason is not nil/empty) * generating a log of why quitting was started.
* Called automatically when the process starts shutting down. */ - (void) ecWillQuit; /* Call these methods during initialisation of your instance * to set up automatic management of connections to servers. * You then access the servers by calling -(id)server: (NSString*)serverName. */ /** This is a convenience method equivalent to calling * -addServerToList:for: passing nil as the second argument. */ - (void) addServerToList: (NSString*)serverName; /** Adds the specified serverName to the list of named servers to which * we make automatic distributed object connections.
* By default the supplied serverName is taken as the name of the server to * which the distributed objects connection is made, and the connection * is to any host on the local network. However configuration in the * user defaults system (using keys derived from the serverName) may * be used to modify this behavior: * * serverNameName * Specifies the actual distributed objects port name to which the * connection is made, instead of using serverName. * serverNameHost * Specifies the actual distributed objects host name to which the * connection is made, instead of using an asterisk (any host). * serverNameBroadcast * Specifies that the server should actually be configured to be * a broadcast proxy (see [EcBroadcastProxy]).
* The value of this field must be an array containing the configuration * information for the [EcBroadcastProxy] instance.
* If this is defined then the serverNameName and serverNameHost values * (if present) are ignored, and the connections are made to the * individual servers listed in the elements of this array.
*
* The argument anObject is an object which will be messaged when the * connection to the server is established (or lost). The messages * sent are those in the RemoteServerDelegate informal * protocol.
* If no object is specified, the receiver is used.
* Once a server has been added to the list, it can be accessed using * the -server: or -server:forNumber: method. */ - (void) addServerToList: (NSString*)serverName for: (id)anObject; /** Removes the serverName from the list of server processes for which * we automatically maintain distributed object connections.
* See the addServerToList:for: metho for more details. */ - (void) removeServerFromList: (NSString*)serverName; /** Deprecated; do not use. */ - (void) cmdAlert: (NSString*)fmt arguments: (va_list)args; /** Deprecated; do not use. */ - (void) cmdAlert: (NSString*)fmt, ... NS_FORMAT_FUNCTION(1,2); /** Archives debug log files into the appropriate subdirectory for the * supplied date (or the files last modification date if when is nil).
* Returns a text description of any archiving actually done.
* The subdirectory is created if necessary. */ - (NSString*) ecArchive: (NSDate*)when; /** Send a log message to the server. */ - (void) cmdAudit: (NSString*)fmt arguments: (va_list)args; /** Send a log message to the server by calling the -cmdAudit:arguments: method. */ - (void) cmdAudit: (NSString*)fmt, ... NS_FORMAT_FUNCTION(1,2); /** Handles loss of connection to the server. */ - (id) cmdConnectionBecameInvalid: (NSNotification*)notification; /** Returns the path to the data storage directory used by this process * to store files containing persistent information. */ - (NSString*) cmdDataDirectory; /** Send a debug message - as long as the debug mode specified as 'type' * is currently set. */ - (void) cmdDbg: (NSString*)type msg: (NSString*)fmt arguments: (va_list)args; /** Send a debug message - as long as the debug mode specified as 'type' * is currently set. Operates by calling the -cmdDbg:msg:arguments: method. */ - (void) cmdDbg: (NSString*)type msg: (NSString*)fmt, ... NS_FORMAT_FUNCTION(2,3); /** Send a debug message with debug mode 'basicMode'.
* Calls the -cmdDbg:msg:arguments: method. */ - (void) cmdDebug: (NSString*)fmt arguments: (va_list)args; /** Send a debug message with debug mode 'basicMode'.
* Operates by calling the -cmdDebug:arguments: method. */ - (void) cmdDebug: (NSString*)fmt, ... NS_FORMAT_FUNCTION(1,2); /** Called automatically in response to a local NSUserDefaults database change * or in response to a configuration update from the Control server.
* This is automatically called after -cmdUpdate: (even if the user defaults * database has not actually changed), in which case the notification * argument is nil.
* An automatic call to this method is (if it does not raise an exception) * immediately followed by a call to the -cmdUpdated method.
* This method deals with the updates for any defaults registered using * the +ecRegisterDefault:withTypeText:andHelpText:action:value: method, so * if you override this to handle configuration changes, don't forget * to call the superclass implementation.
* If you wish to manage updates from the central database in a specific * order, you may wish to override the -cmdUpdate: and/or -cmdUpdated method. */ - (void) cmdDefaultsChanged: (NSNotification*)n; /** Deprecated; do not use. */ - (void) cmdError: (NSString*)fmt arguments: (va_list)args; /** Deprecated; do not use. */ - (void) cmdError: (NSString*)fmt, ... NS_FORMAT_FUNCTION(1,2); /** Flush logging information. */ - (void) cmdFlushLogs; /** This message returns YES if the receiver is intended to be a client * of a Command server, and NO if it is a standalone process which does * not need to contact the Command server.
* The default implementation returns YES, but subclasses may override * this method to return NO if they do not wish to contact the Command * server. */ - (BOOL) cmdIsClient; /** Returns a flag indicating whether this process is currently connected * it its Command server. */ - (BOOL) cmdIsConnected; /** Returns YES is the process is running in test mode, NO otherwise.
* Test mode is defined by the EcTesting user default. */ - (BOOL) cmdIsTesting; /** Closes a file previously obtained using the -cmdLogFile: method.
* Returns a description of any file archiving done, or nil if the file * dis not exist.
* You should not close a logging handle directly, use this method. */ - (NSString*) cmdLogEnd: (NSString*)name; /** Obtain a file handle for logging purposes. The file will have the * specified name and will be created (if necessary) in the processes * logging directory.
* If there is already a handle for the specified file, this method * returns the existing handle rather than creating a new one.
* Do not close this file handle other than by calling the -cmdLogEnd: method. */ - (NSFileHandle*) cmdLogFile: (NSString*)name; /** Used by the Command server to send messages to your application. */ - (void) cmdMesgData: (NSData*)dat from: (NSString*)name; /** This method is called whenever the Command server sends an instruction * for your Command client process to act upon. Often the command has been * entered by an operator and you need to respond with a text string.
* To implement support for an operator command, you must write a method * whose name is of the form -cmdMesgXXX: where XXX is the command (a * lowercase string) that the operator will enter. This method must accept * a single NSArray object as an argument and must return a readable string * as a result.
* The array argument will contain the words in the command line entered * by the operator (with the command itsself as the first item in the * array).
* There are two special cases ... when the operator types 'help XXX' your * method will be called with 'help'as the first element of the array and * you should respond with some useful help text, and when the operator * simply wants a short description of what the command does, the array * argument will be nil (and your method should respond with a short * description of the command). */ - (NSString*) ecMesg: (NSArray*)msg from: (NSString*)operator; /** Attempt to establish connection to Command server etc. * Return a proxy to that server if it is available. */ - (id) cmdNewServer; /** This method notes the supplied status and sets a nil quit reason, it * then calls -ecWillQuit, -ecHandleQuit, and finally calls * -ecDidQuit at process termination.
* Subclasses should override -ecHandleQuit rather than this method. */ - (oneway void) cmdQuit: (NSInteger)status; /** Returns non-zero (a signal) if the process has received a unix signal. */ - (int) cmdSignalled; /** Used to tell your application about central configuration changes.
* This is called before the NSUserDefaults system is updated with the * changes, so you may use it to update internal state in the knowledge * that code watching for user defaults change notifications will not * have updated yet.
* The base class implementation is responsible for updating the user * defaults system ... so be sure that your implementation calls the * superclass implementation (unless you wish to suppress the configuration * update) after performing any pre-update operations.
* You may alter the info dictionary prior to passing it to the superclass * implementation if you wish to adjust the new configuration before it * takes effect.
* The order of execution of a configuration update is therefore as follows: * * Any subclass implementation of -cmdUpdate: is entered. * * The base implementation of -cmdUpdate: is entered, the stored * configuration is changed as necessary, the user defaults database is * updated. * * Any subclass implementation of the -cmdDefaultsChanged: method is * entered (either as a result of an NSUserDefaults notification, * or directly after the -cmdUpdate: method). * * The base implementation of the -cmdDefaultsChanged: method is * entered, and any messages registered using the * +ecRegisterDefault:withTypeText:andHelpText:action:value: method are * sent if the corresponding default value has changed. * * The base implementation of the -cmdDefaultsChanged: method ends. * * Any subclass implementation of the -cmdDefaultsChanged: method ends. * * Once all NSUserDefaults notifications have completed (ie in a * succeeding run loop itermation), the -cmdUpdated method is called. * * * You should usually override the -cmdUpdated method to handle changes * to particular defaults values (whether from the central database used * by the Control sarver or from local NSUserDefaults changes).
* Use this method only when you want to check/override changes * before they take effect. */ - (void) cmdUpdate: (NSMutableDictionary*)info; /** Used to tell your application about configuration changes (including * changes to the NSUserDefaults system).
* NB. This method will be called even if your implementation of * -cmdUpdate: suppresses the actual update. In this situation this * method will find the configuration unchanged since the previous * time that it was called.
* This method is called in a run loop iteration of the main thread after * any NSUserDefaults notifications so that it can check the state of things * after any code dealing with the notifications has run.
* The return value of this method is used to control automatic generation * of alarms for fatal configuration errors by passing it to the * -ecConfigurationError:,... method.
* When you implement this method, you must ensure that your implementation * calls the superclass implementation, and if that returns a non-nil * result, you should pass that on as the return value from your own * implementation. */ - (NSString*) cmdUpdated; - (void) log: (NSString*)message type: (EcLogType)t; /** Send a warning message to the server. */ - (void) cmdWarn: (NSString*)fmt arguments: (va_list)args; /** Send a warning message to the server. */ - (void) cmdWarn: (NSString*)fmt, ... NS_FORMAT_FUNCTION(1,2); /* Return interval between timeouts. */ - (NSTimeInterval) cmdInterval; /** Register a debug mode 'mode' */ - (void) setCmdDebug: (NSString*)mode withDescription: (NSString*)desc; /** Sets the interval between timeouts while the runloop is running.
* Any value below 0.001 is ignored and 10 is used.
* Any value above 300 is ignored and 60 is used.
* The default value is 60 seconds. */ - (void) setCmdInterval: (NSTimeInterval)interval; /** Specify a handler method to be invoked after each timeout to let you * perform additional tasks. */ - (void) setCmdTimeout: (SEL)sel; /** Schedule a timeout to go off as soon as possible ... subsequent timeouts * go off at the normal interval after that one.
* This method is called automatically near the start of -ecRun. */ - (void) triggerCmdTimeout; /** Returns the base name for this process (before any instance ID was * added). If the process has no instance ID, this returns the same as * the -cmdName method. */ - (NSString*) cmdBase; /** Deprecated ... use -cmdDefaults instead. */ - (id) cmdConfig: (NSString*)key; /** Check to see if a debug mode is active. */ - (BOOL) cmdDebugMode: (NSString*)mode; /** Set a particular (named) debug mode to be active or inactive. */ - (void) cmdDebugMode: (NSString*)mode active: (BOOL)flag; /** Returns the NSUserDefaults instance containing the configuration * information for this process. */ - (NSUserDefaults*) cmdDefaults; /** Returns the instance ID used for this process, or nil if there is none. */ - (NSString*) cmdInstance; /** Utility method to perform partial (case insensitive) matching of * an abbreviated command word (val) to a keyword (key) */ - (BOOL) cmdMatch: (NSString*)val toKey: (NSString*)key; /** Returns an array of commands that the named operator is permitted * to use. */ - (NSArray*) ecCommands: (NSString*)operator; /** Sets the operator config. */ - (void) ecOperators: (NSDictionary*)operators; /** Handle with care - this method invokes the cmdMesg... methods. */ - (NSString*) ecMesg: (NSArray*)msg from: (NSString*)operator; /** Returns the name by which this process is known to the Command server. */ - (NSString*) cmdName; /** May be used withing cmdMesg... methods to return formatted text to * the Console. */ - (void) cmdPrintf: (NSString*)fmt arguments: (va_list)args; /** May be used withing cmdMesg... methods to return formatted text to * the Console. */ - (void) cmdPrintf: (NSString*)fmt, ... NS_FORMAT_FUNCTION(1,2); /** Should be over-ridden to perform extra tidy up on shutdown of the * process - should call [super cmdQuit:...] at the end of the method. */ - (oneway void) cmdQuit: (NSInteger)status; /** Used to tell your application about configuration changes (the * default implementation merges the configuration change into the * NSUserDefaults system and sends the defaults change notification).
* If you want to deal with configuration changes actively - override * this and call [super cmdUpdate:...] to install the changed * configuration before anything else. * NB. This method WILL be called before your application is * initialised. Make sure it is safe. */ - (void) cmdUpdate: (NSMutableDictionary*)info; /** This calls the designated initialiser (-initWithDefaults:) passing * the results of a call to +ecInitialDefaults as its argument. */ - (id) init; /** [-initWithDefaults:] is the Designated initialiser
* It adds the defaults specified to the defaults system.
* It sets the process name to be that specified in the * 'ProgramName' default with an '-id' affix if Instance is used * to provide an instance id.
* Moves to the directory (relative to the current user's home directory) * given in 'HomeDirectory'.
* If 'HomeDirectory' is not present in the defaults system (or is * an empty string) then no directory change is done.
* Please note, that the base implementation of this method may * cause other methods (eg -cmdUpdated and -cmdDefaultsChanged:) to be called, * so you must take care that when you override those methods, your own * implementations do not depend on initialisation having completed. * It's therefore recommended that you use 'lazy' initialisation of subclass * instance variables as/when they are needed, rather than initialising * them in the -initWithDefaults: method.
* For a normal process, the recommended place to perform initialisation is * immediately after initialisation (when configuration information has been * retrieved from the Command server), typically by overriding the * -ecAwaken method. */ - (id) initWithDefaults: (NSDictionary*)defs; /* * How commands sent to the client via cmdMesg: are invoked - * * If a method exists whose name is 'cmdMesgfoo:' where 'foo' is the * command given, then that method is registered when the receiver * is initialised (or -cmdMesgCache is called) and invoked. * * The action methods should use the [-cmdPrintf:] method repeatedly to * add text to be returned to the caller. * * By default the method 'cmdMesghelp:' is defined to handle the 'help' * command. To extend the help facility - invoke [super cmdMesghelp:] * at the start of your own implementation. * NB. This method may call any other 'cmdMesg...:' method passing it * an array with the first parameter set to be the string 'help'. * In this case the method should use 'cmdLine:' to return it's help * information. * * We also have 'cmdMesgdebug:' to activate debug logging and * 'cmdMesgnodebug:' to deactivate it. The known debug modes may * be extended by using the 'setCmdDebug:withDescription:' method. * * 'cmdMesgmemory:' is for reporting process memory allocation stats - * it should be overridden to give detailed info. * * 'cmdMesgstatus:' is for reporting process status information and * should be overridden to give detailed info. * * 'cmdMesgarchive:' forces an archive of the debug log. */ - (void) cmdMesgCache; - (void) cmdMesgarchive: (NSArray*)msg; - (void) cmdMesgdebug: (NSArray*)msg; - (void) cmdMesghelp: (NSArray*)msg; - (void) cmdMesgmemory: (NSArray*)msg; - (void) cmdMesgnodebug: (NSArray*)msg; - (void) cmdMesgstatus: (NSArray*)msg; /** Returns the system process identifier for the client process. */ - (int) processIdentifier; /** * Returns a proxy object to a[n automatically managed] server process.
* The serverName must previously have been registered using the * -addServerToList:for: -addServerToList: method. */ - (id) server: (NSString *)serverName; /** * Like -server:, but if the configuration contains a multiple servers, * this tries to locate the specific server that is set up to deal with * cases where the last two digits of an identifer as as specified.
* This mechanism permits work to be balanced/shared over up to 100 separate * server processes. */ - (id) server: (NSString *)serverName forNumber: (NSString*)num; /** * Standard servers return NO to the following. But if we are using * a multiple/broadcast server, this returns YES. */ - (BOOL) isServerMultiple: (NSString *)serverName; /** This method is called at the start of -ecRun in order to allow a subclass * to perform initialisation after configuration information has been received * from the Command server, but before the process has become a registered DO * server and has entered the run loop with a regular timer set up.
* This is the recommended location to perform any initialisation of your * subclass which needs configuration information from the Command server; * override this method to perform your initialisation.
* If you are not using -ecRun you should call this method explicitly in your * own code.
* The default implementation does nothing but record the fact that it has * been called (for -ecDidAwaken).
*/ - (void) ecAwaken; /** Called to handle fatal configuration problems (or with a nil argument, * to clear any outstanding alarm about a configuration problem).
* If err is not nil, a configuration error alarm will be raised (using the * err string as the 'additional text' of the alarm), and the process * will be terminated by a call to -cmdQuit: with an argument of 1.
* If you override this method, you should ensure that your implementation * calls the superclass implementation.
* This method is called automatically with the result of -cmdUpdated when * process configuration changes. */ - (void) ecConfigurationError: (NSString*)fmt, ... NS_FORMAT_FUNCTION(1,2); /** Returns YES if the base implementation of -ecAwaken has been called, * NO otherwise. You may use this in conjunction with -ecDoLock and * -ecUnLock to ensure that you have thread-safe initialisation of your * program (though the locking is normally unnecessary if -ecAwaken is * only called from -ecRun).
* As long as subclasses call the superclass implementation of -ecAwaken * at the start of their own implementation, this will be true during the * entire awakening process. */ - (BOOL) ecDidAwaken; /** Returns YES if the internbal call to the -ecAwaken method has ended, * NO otherwise. This should tell you whether the process has completely * woken up. */ - (BOOL) ecDidAwakenCompletely; /** Records the timestamp of the latest significant input for this process. * If when is nil the current timestmp is used. */ - (void) ecHadIP: (NSDate*)when; /** Records the timestamp of the latest significant output for this process. * If when is nil the current timestmp is used. */ - (void) ecHadOP: (NSDate*)when; /** Called on the first timeout of a new day.
* The argument 'when' is the timestamp of the timeout.
* If you override this, don't forget to call the superclass * implementation in order to perform regular housekeeping. */ - (void) ecNewDay: (NSCalendarDate*)when; /** Called on the first timeout of a new hour.
* The argument 'when' is the timestamp of the timeout.
* If you override this, don't forget to call the superclass * implementation in order to perform regular housekeeping. */ - (void) ecNewHour: (NSCalendarDate*)when; /** Called on the first timeout of a new minute.
* The argument 'when' is the timestamp of the timeout.
* If you override this, don't forget to call the superclass * implementation in order to perform regular housekeeping. */ - (void) ecNewMinute: (NSCalendarDate*)when; /** Return heap memory known not to be leaked ... for use in internal * monitoring of memory usage. You should override this to add in any * heap store you have used and know is not leaked.
* When generating warning messages about possible memory leaks, * this value is taken into consideration. */ - (NSUInteger) ecNotLeaked; /** This method calls -ecAwaken, establishes the receiver as a DO server, * calls -triggerCmdTimeout, and then repeatedly runs the runloop.
* Returns zero when the run loop completes.
* Returns one (immediately) if the receiver is transient.
* Returns two if unable to register as a DO server.
*/ - (int) ecRun; /** Logs a message iff the process is running in test mode * (that is, when EcTesting is set). */ - (void) ecTestLog: (NSString*)fmt arguments: (va_list)args; /** Logs a message iff the process is running in test mode.
* Operates by calling the -ecTestLog:arguments: method. */ - (void) ecTestLog: (NSString*)fmt, ... NS_FORMAT_FUNCTION(1,2); /** This method must be called to make the user defaults system aware of * any changeds to registered defaults made after the EcProcess instance * has been created. */ - (void) ecUpdateRegisteredDefaults; /** Returns the directory set as the root for files owned by the ECCL user */ - (NSString*) ecUserDirectory; /** Method to log an exception (or other unexpected error) and raise an * alarm about it, providing a unique specificProblem value to identify * the location in the code, and a perceivedSeverity to let people know * how serious the problem is likely to be. Use EcAlarmSeverityMajor * if you really do not know.
* This method serves a dual purpose, as it generates an alarm to alert * people about an unexpected problem, but it also logs detailed information * about that problem (including a stack trace) as an aid to debugging and * analysis.
* You may use EcAlarmSeverityClear to suppress the use of an alarm and just * generate debug logging including a stack trace in the case where you want * to know what's going on at some point. */ - (EcAlarm*) ecException: (NSException*)cause specificProblem: (NSString*)specificProblem perceivedSeverity: (EcAlarmSeverity)perceivedSeverity message: (NSString*)format, ... NS_FORMAT_FUNCTION(4,5); /** Supporting code called by the * -ecException:specificProblem:perceivedSeverity:message:,... method. */ - (EcAlarm*) ecException: (NSException*)cause specificProblem: (NSString*)specificProblem perceivedSeverity: (EcAlarmSeverity)perceivedSeverity message: (NSString*)format arguments: (va_list)args; @end @interface NSObject (RemoteServerDelegate) - (void) cmdMadeConnectionToServer: (NSString *)serverName; - (void) cmdLostConnectionToServer: (NSString *)serverName; @end extern EcProcess *EcProc; /* Single instance or nil */ extern NSString *cmdBasicDbg; /* Debug normal stuff. */ extern NSString *cmdConnectDbg; /* Debug connection attempts. */ extern NSString *cmdDetailDbg; /* Debug stuff in more detail. */ /* Deprecated synonym for cmdBasicDbg. */ extern NSString *cmdDefaultDbg; extern NSString * const EcDidQuitNotification; /* Posted in -ecDidQuit */ extern NSString * const EcWillQuitNotification; /* Posted in -ecWillQuit */ #endif /* INCLUDED_ECPROCESS_H */