On this page:

The Ice::Service C++ Class

Ice provides Ice::Service, a singleton class that is comparable to Ice::Application but also encapsulates the low-level, platform-specific initialization and shutdown procedures common to Unix daemons and Windows services. Ice::Service is currently available only in C++.

The Ice::Service class is declared as follows:

namespace Ice
{
    class Service
    {
    public:

        Service();
        virtual ~Service();

        virtual bool shutdown();
        virtual void interrupt();

        int main(int argc, const char* const argv[], const InitializationData& initData = InitializationData(), int version = ICE_INT_VERSION);
#ifdef _WIN32
        int main(int argv, const wchar_t* const argv[], const InitializationData& initData = InitializationData(), int version = ICE_INT_VERSION);
#endif
        int main(const StringSeq& args, const InitializationData& initData = InitializationData(), int version = ICE_INT_VERSION);

        std::shared_ptr<Communicator> communicator() const;
        static Service* instance();

        bool service() const;
        std::string name() const;
        bool checkSystem() const;

        int run(int argc, const char* const argv[], const InitializationData& initData = InitializationData(), int version= ICE_INT_VERSION);
#ifdef _WIN32
        int run(int argc, const wchar_t* const argv[], const InitializationData& initData = InitializationData(), int version = ICE_INT_VERSION);
#endif

#ifdef _WIN32
        void configureService(const std::string& name);
#else
        void configureDaemon(bool chdir, bool close, const std::string& pidFile);
#endif

        virtual void handleInterrupt(int);

    protected:

        virtual bool start(int argc, char* argv[], int& status) = 0;
        virtual void waitForShutdown();
        virtual bool stop();

        virtual std::shared_ptr<Communicator> initializeCommunicator(int& argc, char* argv[], const InitializationData& initData, int version);

        virtual void syserror(const std::string& msg);
        virtual void error(const std::string& msg);
        virtual void warning(const std::string& msg);
        virtual void trace(const std::string& msg);
        virtual void print(const std::string& msg);
   
        void enableInterrupt();
        void disableInterrupt();
   
        ...
    };
}
namespace Ice
{
    class Service
    {
    public:

        Service();
        virtual ~Service();

        virtual bool shutdown();
        virtual void interrupt();

        int main(int argc, const char* const argv[], const InitializationData& initData = InitializationData(), int version = ICE_INT_VERSION);
#ifdef _WIN32
        int main(int argv, const wchar_t* const argv[], const InitializationData& initData = InitializationData(), int version = ICE_INT_VERSION);
#endif
        int main(const StringSeq& args, const InitializationData& initData = InitializationData(), int version = ICE_INT_VERSION);

        CommunicatorPtr communicator() const;
        static Service* instance();

        bool service() const;
        std::string name() const;
        bool checkSystem() const;

        int run(int argc, const char* const argv[], const InitializationData& initData = InitializationData(), int version= ICE_INT_VERSION);
#ifdef _WIN32
        int run(int argc, const wchar_t* const argv[], const InitializationData& initData = InitializationData(), int version = ICE_INT_VERSION);
#endif

#ifdef _WIN32
        void configureService(const std::string& name);
#else
        void configureDaemon(bool chdir, bool close, const std::string& pidFile);
#endif

        virtual void handleInterrupt(int);

    protected:

        virtual bool start(int argc, char* argv[], int& status) = 0;
        virtual void waitForShutdown();
        virtual bool stop();

        virtual CommunicatorPtr initializeCommunicator(int& argc, char* argv[], const InitializationData& initData, int version);

        virtual void syserror(const std::string& msg);
        virtual void error(const std::string& msg);
        virtual void warning(const std::string& msg);
        virtual void trace(const std::string& msg);
        virtual void print(const std::string& msg);
   
        void enableInterrupt();
        void disableInterrupt();
        ...
    };
}

At a minimum, an Ice application that uses the Ice::Service class must define a subclass and override the start member function, which is where the service must perform its startup activities, such as processing command-line arguments, creating an object adapter, and registering servants. The application's main function must instantiate the subclass and typically invokes its main member function, passing the program's argument vector as parameters. The example below illustrates a minimal Ice::Service subclass:

#include <Ice/Ice.h>

class MyService : public Ice::Service 
{
protected:
    virtual bool start(int, char*[], int&) override;
private:
    std::shared_ptr<Ice::ObjectAdapter> _adapter;
};

bool
MyService::start(int argc, char* argv[], int& status)
{
    _adapter = communicator()->createObjectAdapter("MyAdapter");
    _adapter->addWithUUID(std::make_shared<MyServantI>());
    _adapter->activate();
    status = EXIT_SUCCESS;
    return true;
}

int
main(int argc, char* argv[])
{
    MyService svc;
    return svc.main(argc, argv);
}
#include <Ice/Ice.h>

class MyService : public Ice::Service 
{
protected:
    virtual bool start(int, char*[], int&);
private:
    Ice::ObjectAdapterPtr _adapter;
};

bool
MyService::start(int argc, char* argv[], int& status)
{
    _adapter = communicator()->createObjectAdapter("MyAdapter");
    _adapter->addWithUUID(new MyServantI);
    _adapter->activate();
    status = EXIT_SUCCESS;
    return true;
}

int
main(int argc, char* argv[])
{
    MyService svc;
    return svc.main(argc, argv);
}

The Service::main member function performs the following sequence of tasks:

  1. Scans a copy of the argument vector for reserved options that indicate whether the program should run as a system service and removes these options from this copy of the argument vector. Additional reserved options are supported for administrative tasks.
  2. Configures the program for running as a system service (if necessary) by invoking configureService or configureDaemon, as appropriate for the platform.
  3. Invokes the run member function with the filtered argument vector and returns its result.

Note that, as for Application::mainService::main is overloaded to accept a string sequence instead of an argc/argv pair. This is useful if you need to parse application-specific property settings on the command line.

For maximum portability, we strongly recommend that all initialization tasks be performed in the start member function and not in the global main function. For example, allocating resources in main can cause program instability for Unix daemons.


The Service::run member function executes the service in the steps shown below:

  1. Makes a copy of the provided argument vector.
  2. Installs a signal handler.
  3. Invokes the initializeCommunicator member function to obtain a communicator. The communicator instance can be accessed using the communicator member function.
  4. Invokes the start member function. If start returns false to indicate failure, run destroys the communicator and returns immediately using the exit status provided in status.
  5. Invokes the waitForShutdown member function, which should block until shutdown is invoked.
  6. Invokes the stop member function. If stop returns truerun considers the application to have terminated successfully.
  7. Destroys the communicator.
  8. Gracefully terminates the system service (if necessary).

If an unhandled exception is caught by Service::run, a descriptive message is logged, the communicator is destroyed and the service is terminated.

Ice::Service Member Functions

The virtual member functions in Ice::Service represent the points at which a subclass can intercept the service activities. All of the virtual member functions (except start) have default implementations.

The non-virtual member functions shown in the class definition are described below:

 

Unix Daemons

On Linux and macOS, passing --daemon causes your program to run as a daemon. When this option is present, Ice::Service performs the following additional actions:

The following additional command-line options can be specified in conjunction with the --daemon option:

All of these options are removed from the argument vector that is passed to the start member function.

We strongly recommend that you perform all initialization tasks in your service's start member function, and not in the global main function. This is especially important for process-specific resources such as file descriptors, threads, and mutexes, which can be affected by the use of the fork system call in Ice::Service. For example, any files opened in main are automatically closed by Ice::Service and therefore unusable in your service, unless the daemon is started with the --noclose option.

Windows Services

On Windows, Ice::Service recognizes the following command-line options:

Installing and configuring a Windows service is outside the scope of the Ice::Service class. Ice includes a utility for installing its services which you can use as a model for your own applications.

The Ice::Service class supports the Windows service control codes SERVICE_CONTROL_INTERROGATE and SERVICE_CONTROL_STOP. Upon receipt of SERVICE_CONTROL_STOPIce::Service invokes the shutdown member function.

Ice::Service Logging Considerations

A service that uses a custom logger has several ways of configuring it:

On Windows, Ice::Service installs its own logger that uses the Windows Application event log if no custom logger is defined. The source name for the event log is the service's name unless a different value is specified using the property Ice.EventLog.Source.

On Unix, the default Ice logger (which logs to the standard error output) is used when no other logger is configured. For daemons, this is not appropriate because the output will be lost. To change this, you can either implement a custom logger or set the Ice.UseSyslog property, which selects a logger implementation that logs to the syslog facility. Alternatively, you can set the Ice.LogFile property to write log messages to a file.

Note that Ice::Service may encounter errors before the communicator is initialized. In this situation, Ice::Service uses its default logger unless a process-wide logger is configured. Therefore, even if a failing service is configured to use a different logger implementation, you may find useful diagnostic information in the Application event log (on Windows) or sent to standard error (on Linux and macOS).

See Also