// -*- C++ -*- //==================================================================== /** * @file Service_Gestalt.h * * @author Iliyan Jeliazkov */ //==================================================================== #ifndef ACE_SERVICE_GESTALT_H #define ACE_SERVICE_GESTALT_H #include /**/ "ace/pre.h" #include /**/ "ace/config-all.h" #include "ace/Default_Constants.h" #if !defined (ACE_LACKS_PRAGMA_ONCE) # pragma once #endif /* ACE_LACKS_PRAGMA_ONCE */ #include "ace/SString.h" #include "ace/Unbounded_Queue.h" #include "ace/Unbounded_Set.h" #include "ace/Service_Repository.h" #include "ace/Singleton.h" #include "ace/OS_NS_signal.h" #include "ace/Synch_Traits.h" #include "ace/Atomic_Op.h" #include "ace/Guard_T.h" ACE_BEGIN_VERSIONED_NAMESPACE_DECL #if (ACE_USES_CLASSIC_SVC_CONF == 1) class ACE_Service_Type_Factory; class ACE_Location_Node; #else class ACE_XML_Svc_Conf; class ACE_DLL; #endif /* ACE_USES_CLASSIC_SVC_CONF == 1 */ class ACE_Static_Svc_Descriptor; class ACE_Svc_Conf_Param; /** * @class ACE_Service_Gestalt * * @brief Supplies common server operations for dynamic and static * configuration of services. * * The Gestalt embodies the concept of configuration context. On one * hand, it is a flat namespace, where names correspond to a Service * Object instance. A Gestalt owns the Service Repository instance, * which in turn owns the Service Object instances. * * Another aspect of a Gestalt is its responsibility for * record-keeping and accounting for the meta-data, necessary for * locating, removing or instantiating a service. * * A repository underlies an instance of a gestalt and its lifetime * may or may not be bounded by the lifetime of the gestalt, that owns * it. This feature is important for the derived classes and the * Service Config in particular. */ class ACE_Export ACE_Service_Gestalt : private ACE_Copy_Disabled { public: enum { MAX_SERVICES = ACE_DEFAULT_SERVICE_REPOSITORY_SIZE }; enum { DEFAULT_SIZE = ACE_DEFAULT_SERVICE_GESTALT_SIZE }; /// Constructor either associates the instance with the process-wide /// singleton instance of ACE_Service_Repository, or creates and /// manages its own instance of the specified size. ACE_Service_Gestalt (size_t size = DEFAULT_SIZE, bool svc_repo_is_owned = true, bool no_static_svcs = true); /// Perform user-specified close activities and remove dynamic /// memory. ~ACE_Service_Gestalt (); /// Dump the state of an object. void dump () const; /** * Performs an open without parsing command-line arguments. The * @a logger_key indicates where to write the logging output, which * is typically either a STREAM pipe or a socket address. If * @a ignore_static_svcs is true then static services are not loaded, * otherwise, they are loaded. If @a ignore_default_svc_conf_file is * true then the @c svc.conf configuration file will be ignored. * Returns zero upon success, -1 if the file is not found or cannot * be opened (errno is set accordingly), otherwise returns the * number of errors encountered loading the services in the * specified svc.conf configuration file. If @a ignore_debug_flag is * true then the application is responsible for setting the * ACE_Log_Msg::priority_mask appropriately. */ int open (const ACE_TCHAR program_name[], const ACE_TCHAR *logger_key = 0, bool ignore_static_svcs = true, bool ignore_default_svc_conf_file = false, bool ignore_debug_flag = false); /** * This is the primary entry point into the ACE_Service_Config (the * constructor just handles simple initializations). It parses * arguments passed in from @a argc and @a argv parameters. The * arguments that are valid in a call to this method include: * * - '-b' Option to indicate that we should be a daemon. Note that when * this option is used, the process will be daemonized before the * service configuration file(s) are read. During daemonization, * (on POSIX systems) the current directory will be changed to "/" * so the caller should either fully specify the file names, or * execute a @c chroot() to the appropriate directory. * @sa ACE::daemonize(). * - '-d' Turn on debugging mode * - '-f' Specifies a configuration file name other than the default * svc.conf. Can be specified multiple times to use multiple files. * If any configuration file is provided with this option then * the default svc.conf will be ignored. * - '-k' Specifies the rendezvous point to use for the ACE distributed * logger. * - '-y' Explicitly enables the use of static services. This flag * overrides the @a ignore_static_svcs parameter value. * - '-n' Explicitly disables the use of static services. This flag * overrides the @a ignore_static_svcs parameter value. * - '-p' Specifies a pathname which is used to store the process id. * - '-s' Specifies a signal number other than SIGHUP to trigger reprocessing * of the configuration file(s). Ignored for platforms that do not * have POSIX signals, such as Windows. * - '-S' Specifies a service directive string. Enclose the string in quotes * and escape any embedded quotes with a backslash. This option * specifies service directives without the need for a configuration * file. Can be specified multiple times. * * Note: Options '-f' and '-S' complement each other. Directives * from files and from '-S' option are processed together in the * following order. First, the default file "./svc.conf" is * evaluated if not ignored, then all files are processed in the * order they are specified in '-f' @a argv parameter. Finally, all * '-S' directive strings are executed in the order the directives * appear in @a argv parameter. * * If no files or directives are added via the '-f' and '-S' * arguments, and the default file is not ignored, it will be * evaluated whether it exists or not, possibly causing a failure * return. If any other directives are added then the default file * will be evaluated only if it exists. * * @param argc The number of commandline arguments. * @param argv The array with commandline arguments * @param logger_key Indicates where to write the logging output, * which is typically either a STREAM pipe or a * socket address. * @param ignore_static_svcs If true then static services are not loaded, * otherwise, they are loaded. * @param ignore_default_svc_conf_file If false then the @c ./svc.conf * configuration file will be ignored. * @param ignore_debug_flag If false then the application is responsible * for setting the @c ACE_Log_Msg::priority_mask * appropriately. * * @retval -1 A configuration file is not found or cannot * be opened (errno is set accordingly). * @retval 0 Success. * @retval >0 The number of directive errors encountered while processing * the service configuration file(s). */ int open (int argc, ACE_TCHAR *argv[], const ACE_TCHAR *logger_key = 0, bool ignore_static_svcs = true, bool ignore_default_svc_conf_file = false, bool ignore_debug_flag = false); /// Has it been opened? Returns the difference between the times /// open and close have been called on this instance int is_opened (); /// Declare the dynamic allocation hooks. ACE_ALLOC_HOOK_DECLARE; /// Process one service configuration @a directive, which is passed as /// a string. Returns the number of errors that occurred. int process_directive (const ACE_TCHAR directive[]); /// Process one static service definition. /** * Load a new static service. * * @param ssd Service descriptor, see the document of * ACE_Static_Svc_Descriptor for more details. * * @param force_replace If set the new service descriptor replaces * any previous instance in the repository. * * @return Returns -1 if the service cannot be 'loaded'. */ int process_directive (const ACE_Static_Svc_Descriptor &ssd, bool force_replace = false); /// Process a file containing a list of service configuration /// directives. int process_file (const ACE_TCHAR file[]); /** * Locate an entry with @a name in the table. If @a ignore_suspended * is set then only consider services marked as resumed. If the * caller wants the located entry, pass back a pointer to the * located entry via @a srp. If @a name is not found, -1 is returned. * If @a name is found, but it is suspended and the caller wants to * ignore suspended services a -2 is returned. */ int find (const ACE_TCHAR name[], const ACE_Service_Type **srp = 0, bool ignore_suspended = true) const; /** * Handle the command-line options intended for the * ACE_Service_Gestalt. Note that @c argv[0] is assumed to be the * program name. * * The arguments that are valid in a call to this method are * - '-d' Turn on debugging mode * - '-f' Option to read in the list of svc.conf file names * - '-k' Option to read a wide string where in the logger output can * be written * - '-y' Turn on the flag for a repository of statically * linked services * - '-n' Need not have a repository of statically linked services * - '-S' Option to read in the list of services on the command-line * Please observe the difference between options '-f' that looks * for a list of files and here a list of services. */ int parse_args (int argc, ACE_TCHAR *argv[]); /** * Process (or re-process) service configuration requests that are * provided in the svc.conf file(s). Returns the number of errors * that occurred. */ int process_directives (bool defunct_option = false); /// Tidy up and perform last rites when ACE_Service_Config is shut /// down. This method calls @c close_svcs. Returns 0. int close (); /// Registers a service descriptor for a static service object int insert (ACE_Static_Svc_Descriptor *stsd); // = Utility methods. #if (ACE_USES_CLASSIC_SVC_CONF == 1) /// Dynamically link the shared object file and retrieve a pointer to /// the designated shared object in this file. Also account for the /// possibility to have static services registered when loading the DLL, by /// ensuring that the dynamic service is registered before any of its /// subordinate static services. Thus avoiding any finalization order /// problems. int initialize (const ACE_Service_Type_Factory *, const ACE_TCHAR *parameters); #endif /* (ACE_USES_CLASSIC_SVC_CONF == 1) */ /// Dynamically link the shared object file and retrieve a pointer to /// the designated shared object in this file. /// @deprecated /// @note This is error-prone in the presense of dynamic services, /// which in turn initialize their own static services. This method /// will allow those static services to register *before* the dynamic /// service that owns them. Upon finalization of the static services /// the process will typically crash, because the dynamic service's /// DLL may have been already released, together with the memory in /// which the static services reside. It may not crash, for /// instance, when the first static service to register is the same /// as the dynamic service being loaded. You should be so lucky! int initialize (const ACE_Service_Type *, const ACE_TCHAR *parameters); /// Initialize and activate a statically @a svc_name service. int initialize (const ACE_TCHAR *svc_name, const ACE_TCHAR *parameters); /// Resume a @a svc_name that was previously suspended or has not yet /// been resumed (e.g., a static service). int resume (const ACE_TCHAR svc_name[]); /** * Suspend @a svc_name. Note that this will not unlink the service * from the daemon if it was dynamically linked, it will mark it as * being suspended in the Service Repository and call the @c suspend() * member function on the appropriate ACE_Service_Object. A * service can be resumed later on by calling the @c resume() member * function... */ int suspend (const ACE_TCHAR svc_name[]); /// Totally remove @a svc_name from the daemon by removing it /// from the ACE_Reactor, and unlinking it if necessary. int remove (const ACE_TCHAR svc_name[]); /** * Using the supplied name, finds and (if needed) returns a pointer to a * static service descriptor. Returns 0 for success and -1 for failure */ int find_static_svc_descriptor (const ACE_TCHAR* name, ACE_Static_Svc_Descriptor **ssd = 0) const; struct Processed_Static_Svc { Processed_Static_Svc (const ACE_Static_Svc_Descriptor *); ~Processed_Static_Svc (); ACE_TCHAR * name_; const ACE_Static_Svc_Descriptor *assd_; ACE_ALLOC_HOOK_DECLARE; }; /// Get the current ACE_Service_Repository held by this object. ACE_Service_Repository* current_service_repository (); protected: int parse_args_i (int, ACE_TCHAR *argv[], bool& ignore_default_svc_conf_file); /** * Performs an open without parsing command-line arguments. The @a * logger_key indicates where to write the logging output, which is * typically either a STREAM pipe or a socket address. If @a * ignore_default_svc_conf_file is non-0 then the "svc.conf" file * will not be added by default. If @a ignore_debug_flag is non-0 * then the application is responsible for setting the @c * ACE_Log_Msg::priority_mask() appropriately. Returns number of * errors that occurred on failure and 0 otherwise. */ int open_i (const ACE_TCHAR program_name[], const ACE_TCHAR *logger_key = 0, bool ignore_static_svcs = true, bool ignore_default_svc_conf_file = false, bool ignore_debug_flag = false); /// Initialize the @c svc_conf_file_queue_ if necessary. int init_svc_conf_file_queue (); /// Add the default statically-linked services to the /// ACE_Service_Repository. int load_static_svcs (); /// Process service configuration requests that were provided on the /// command-line. Returns the number of errors that occurred. int process_commandline_directives (); /// Process a static directive without also inserting its descriptor /// the global table. This avoids multiple additions when processing /// directives in non-global gestalts. int process_directive_i (const ACE_Static_Svc_Descriptor &ssd, bool force_replace = false); #if (ACE_USES_CLASSIC_SVC_CONF == 1) /// This is the implementation function that process_directives() /// and process_directive() both call. Returns the number of errors /// that occurred. int process_directives_i (ACE_Svc_Conf_Param *param); #else /// Helper function to dynamically link in the XML Service Configurator /// parser. ACE_XML_Svc_Conf* get_xml_svc_conf (ACE_DLL &d); #endif /* ACE_USES_CLASSIC_SVC_CONF == 1 */ /// Dynamically link the shared object file and retrieve a pointer to /// the designated shared object in this file. int initialize_i (const ACE_Service_Type *sr, const ACE_TCHAR *parameters); const ACE_Static_Svc_Descriptor* find_processed_static_svc (const ACE_TCHAR*); void add_processed_static_svc (const ACE_Static_Svc_Descriptor *); /// Performs the common initialization tasks for a new or previously /// closed instance. Must not be virtual, as it is called from the /// constructor. int init_i (); protected: /// Maintain a queue of services to be configured from the /// command-line. typedef ACE_Unbounded_Queue ACE_SVC_QUEUE; typedef ACE_Unbounded_Queue_Iterator ACE_SVC_QUEUE_ITERATOR; /// Maintain a set of the statically linked service descriptors. typedef ACE_Unbounded_Set ACE_STATIC_SVCS; typedef ACE_Unbounded_Set_Iterator ACE_STATIC_SVCS_ITERATOR; typedef ACE_Unbounded_Set ACE_PROCESSED_STATIC_SVCS; typedef ACE_Unbounded_Set_Iterator ACE_PROCESSED_STATIC_SVCS_ITERATOR; friend class ACE_Dynamic_Service_Base; friend class ACE_Service_Object; friend class ACE_Service_Config; friend class ACE_Service_Config_Guard; protected: /// Do we own the service repository instance, or have only been /// given a ptr to the singleton? bool svc_repo_is_owned_; /// Repository size is necessary, so that we can close (which may /// destroy the repository instance), and then re-open again. size_t svc_repo_size_; /// Keep track of the number of times the instance has been /// initialized (opened). "If so, we can't allow to be called since /// it's not reentrant" is the original motivation, but that does not seem /// to be the case anymore. This variable is incremented by the /// method and decremented by the /// method. int is_opened_; /// Indicates where to write the logging output. This is typically /// either a STREAM pipe or a socket const ACE_TCHAR *logger_key_; /// Should we avoid loading the static services? bool no_static_svcs_; /// Queue of services specified on the command-line. ACE_SVC_QUEUE* svc_queue_; /** * Queue of svc.conf files specified on the command-line. * @@ This should probably be made to handle unicode filenames... */ ACE_SVC_QUEUE* svc_conf_file_queue_; /// The service repository to hold the services. ACE_Service_Repository* repo_; /// Repository of statically linked services. ACE_STATIC_SVCS* static_svcs_; /// Repository of statically linked services for which process /// directive was called, but the service is not already a member of /// the static_svcs_ list. ACE_PROCESSED_STATIC_SVCS* processed_static_svcs_; /// Support for intrusive reference counting ACE_Atomic_Op refcnt_; public: static void intrusive_add_ref (ACE_Service_Gestalt*); static void intrusive_remove_ref (ACE_Service_Gestalt*); }; /* class ACE_Service_Gestalt */ /** * @class ACE_Service_Type_Dynamic_Guard * * @brief A forward service declaration guard. * * Helps to resolve an issue with hybrid services, i.e. dynamic * services, accompanied by static services in the same DLL. Only * automatic instances of this class are supposed to exist. Those are * created during (dynamic) service initialization and serve to: * * (a) Ensure the service we are loading is ordered last in the * repository, following any other services it may cause to register, * as part of its own registration. This is a common case when * loading dynamic services from DLLs - there are often static * initializers, which register static services. * * (b) The SDG instance destructor detects if the dynamic service * initialized successfully and "fixes-up" all the newly registered * static services to hold a reference to the DLL, from which they * have originated. */ class ACE_Export ACE_Service_Type_Dynamic_Guard { public: ACE_Service_Type_Dynamic_Guard (ACE_Service_Repository &r, ACE_TCHAR const *name); ~ACE_Service_Type_Dynamic_Guard (); private: ACE_Service_Repository & repo_; size_t repo_begin_; ACE_TCHAR const * const name_; #if defined (ACE_MT_SAFE) && (ACE_MT_SAFE != 0) // FUZZ: disable check_for_ACE_Guard ACE_Guard< ACE_Recursive_Thread_Mutex > repo_monitor_; // FUZZ: enable check_for_ACE_Guard #endif }; ACE_END_VERSIONED_NAMESPACE_DECL #if defined (__ACE_INLINE__) #include "ace/Service_Gestalt.inl" #endif /* __ACE_INLINE__ */ #include /**/ "ace/post.h" #endif /* ACE_SERVICE_GESTALT_H */