Ice applications often require remote administration, such as when an IceGrid node needs to gracefully deactivate a running server. The Ice run time provides an extensible, centralized facility for exporting administrative functionality. This facility consists of an object adapter named
Ice.Admin, an Ice object activated on this adapter, and configuration properties that enable the facility and specify its features.
32.19.1 The admin Object
The Ice.Admin adapter hosts a single object whose identity name is
admin. Although this identity name cannot be changed, you can define the identity category using the configuration property
Ice.Admin.InstanceName (see
Appendix C). If you enable the
Ice.Admin adapter without defining this property, the category uses a UUID by default and therefore the object’s identity changes with each instance of the process.
As explained in Chapter 34, an Ice object is actually a collection of sub-objects known as facets whose types are not necessarily related. Although facets are typically used for extending and versioning types, they also allow a group of interfaces with a common purpose to be consolidated into a single Ice object with an established interface for navigation. These qualities make facets an excellent match for the requirements of the administrative facility.
Each facet of the admin object represents a distinct administrative capability. The object does not have a default facet (that is, a facet with an empty name). However, the Ice run time implements two built-in facets that it adds to the
admin object:
An application can control which facets are installed with a configuration property (see
Section 32.19.6). An application can also install its own facets if necessary (see
Section 32.19.7). Administrative facets are not required to inherit from a common Slice interface.
The administrative facility is disabled by default. To enable it, you must specify endpoints for the administrative object adapter using the property
Ice.Admin.Endpoints. In addition, you must do one of the following:
•
Define the Ice.Admin.ServerId and
Ice.Default.Locator properties. If you do not supply a value for
Ice.Admin.InstanceName, Ice uses a UUID by default.
The Ice.Admin.ServerId and
Ice.Default.Locator properties are typically used in conjunction with an activation service such as IceGrid, as discussed in
Section 39.21.
The endpoints for the Ice.Admin adapter must be chosen with caution.
Section 32.19.8 addresses the security considerations of using the administrative facility.
It may be necessary to postpone the creation of the administrative object adapter until all facets are installed or other initialization activities have taken place. In this situation, you can define the following configuration property:
32.19.3 Using the admin Object
A program can obtain a proxy for its admin object by calling the
getAdmin operation on a communicator:
module Ice {
local interface Communicator {
// ...
Object* getAdmin();
};
};
This operation returns a null proxy if the administrative facility is disabled. The proxy returned by
getAdmin cannot be used for invoking operations because it refers to the default facet and, as we mentioned previously, the
admin object does not support a default facet. A program must first obtain a new version of the proxy that is configured with the name of a particular administrative facet before invoking operations on it. Although it cannot be used for invocations, the original proxy is still useful because it contains the endpoints of the
Ice.Admin object adapter and therefore the program may elect to export that proxy to a remote client.
To administer a program remotely, somehow you must obtain a proxy for the program’s
admin object. There are several ways for the administrative client to accomplish this:
The identity category, represented here by instance-name, is the value of the
Ice.Admin.InstanceName property or a UUID if that property is not defined. (Clearly, the use of a UUID makes the proxy much more difficult for a client to construct on its own.) The name of the administrative facet is supplied as the value of the
-f option, and the endpoints of the
Ice.Admin adapter appear last in the proxy. See
Appendix D for more information on stringified proxies.
•
Use the getServerAdmin operation on the
IceGrid::Admin interface, if the remote program was activated by IceGrid (see
Section 39.21.3).
Having obtained the proxy, the administrative client must select a facet before invoking any operations. For example, the code below shows how to obtain the configuration properties of the remote program:
// C++
Ice::ObjectPrx adminObj = ...;
Ice::PropertiesAdminPrx propAdmin =
Ice::PropertiesAdminPrx::checkedCast(adminObj,
"Properties");
Ice::PropertyDict props = propAdmin‑>getPropertiesForPrefix("");
Here we used an overloaded version of checkedCast to supply the facet name of interest (
Properties). We could have selected the facet using the proxy method
ice_facet instead, as shown below:
// C++
Ice::ObjectPrx adminObj = ...;
Ice::PropertiesAdminPrx propAdmin =
Ice::PropertiesAdminPrx::checkedCast(
adminObj‑>ice_facet("Properties"));
Ice::PropertyDict props = propAdmin‑>getPropertiesForPrefix("");
A remote client must also know (or be able to determine) which facets are available in the target server. Typically this information is statically configured in the client, since the client must also know the interface types of any facets that it uses. If an invocation on a facet raises
FacetNotExistException, the client may have used an incorrect facet name, or the server may have disabled the facet in question.
32.19.4 The Process Facet
An activation service, such as an IceGrid node (see Chapter 39), needs a reliable way to gracefully deactivate a server. One approach is to use a platform-specific mechanism, such as POSIX signals. This works well on POSIX platforms when the server is prepared to intercept signals and react appropriately (see
Section 31.12). On Windows platforms, it works less reliably for C++ servers, and not at all for Java servers. For these reasons, the
Process facet provides an alternative that is both portable and reliable.
Section 32.19.8 discusses the security risks associated with enabling the
Process facet.
The Slice interface Ice::Process allows an activation service to request a graceful shutdown of the program:
module Ice {
interface Process {
["ami"] void shutdown();
void writeMessage(string message, int fd);
};
};
When shutdown is invoked, the object implementing this interface is expected to initiate the termination of its process. The activation service may expect the program to terminate within a certain period of time, after which it may terminate the program abruptly.
The writeMessage operation allows remote clients to print a message to the program’s standard output (
fd == 1) or standard error (
fd == 2) channels.
The default implementation of the Process facet requires cooperation from an application in order to successfully terminate a process. Specifically, the facet invokes
shutdown on its communicator and assumes that the application uses this event as a signal to commence its termination procedure. For example, an application typically uses a thread (often the main thread) to call the communicator operation
waitForShutdown, which blocks the calling thread until the communicator is shut down or destroyed. After
waitForShutdown returns, the calling thread can initiate a graceful shutdown of its process.
Refer to Section 32.2 for more information on the communicator operations
shutdown and
waitForShutdown.
You can replace the default Process facet if your application requires a different scheme for gracefully shutting itself down. To define your own facet, create a servant that implements the
Ice::Process interface. As an example, the servant definition shown below duplicates the functionality of the default
Process facet:
class ProcessI : public Ice::Process {
public:
ProcessI(const Ice::CommunicatorPtr& communicator) :
_communicator(communicator)
{}
void shutdown(const Ice::Current&)
{
_communicator‑>shutdown();
}
void writeMessage(const string& msg, Ice::Int fd,
const Ice::Current&)
{
if(fd == 1) cout << msg << endl;
else if(fd == 2) cerr << msg << endl;
}
private:
const Ice::CommunicatorPtr _communicator;
};
As you can see, the default implementation of shutdown simply shuts down the communicator, which initiates an orderly termination of the Ice run time’s server-side components and prevents object adapters from dispatching any new requests. You can add your own application-specific behavior to the
shutdown method to ensure that your program terminates in a timely manner.
To avoid the risk of a race condition, the recommended strategy for replacing the Process facet is to delay creation of the administrative facets so that your application has a chance to replace the facet:
// C++Ice::CommunicatorPtr communicator = ...
communicator->removeAdminFacet("Process");
Ice::ProcessPtr myProcessFacet = new MyProcessFacet(...);
communicator->addAdminFacet(myProcessFacet, "Process");
If the
Ice.Admin.ServerId and
Ice.Default.Locator properties are defined, the Ice run time performs the following steps after creating the
Ice.Admin adapter:
•
Invokes getRegistry on the proxy to obtain a proxy for the locator registry
•
Invokes setServerProcessProxy on the locator registry and supplies the value of
Ice.Admin.ServerId along with a proxy for the
Process facet
The identifier specified by Ice.Admin.ServerId must uniquely identify the process within the locator registry.
In the case of IceGrid, the node defines the Ice.Admin.ServerId and
Ice.Default.Locator properties for each deployed server. The node also supplies a value for
Ice.Admin.Endpoints if the property is not defined by the server. See
Chapter 39 for more information.
32.19.5 The Properties Facet
An administrator may find it useful to be able to view the configuration properties of a remote Ice application. For example, the IceGrid administrative tools allow you to query the properties of active servers. The
Properties facet supplies this functionality.
The Ice::PropertiesAdmin interface provides access to the communicator’s configuration properties:
module Ice {
interface PropertiesAdmin {
["ami"] string getProperty(string key);
["ami"] PropertyDict getPropertiesForPrefix(string prefix);
};
};
The getProperty operation retrieves the value of a single property, and the
getPropertiesForPrefix operation returns a dictionary of properties whose keys match the given prefix. These operations have the same semantics as those in the
Ice::Properties interface described in
Section 30.8.1.
The Ice run time enables all of its built-in administrative facets by default, and an application may install its own facets. You can control which facets the Ice run time enables using the
Ice.Admin.Facets property. For example, the following property definition enables the
Properties facet and leaves the
Process facet (and any application-defined facets) disabled:
To specify more than one facet, separate them with a comma or white space. A facet whose name contains white space must be enclosed in single or double quotes.
module Ice {
local interface Communicator {
// ...
void addAdminFacet(Object servant, string facet);
Object removeAdminFacet(string facet);
};
};
The addAdminFacet operation installs a new facet with the given name, or raises
AlreadyRegisteredException if a facet already exists with the same name. The
removeAdminFacet operation removes (and returns) the facet with the given name, or raises
NotRegisteredException if no matching facet is found.
The mechanism for filtering administrative facets described in Section 32.19.6 also applies to application-defined facets. If you call
addAdminFacet while a filter is in effect, and the name of your custom facet does not match the filter, the Ice run time will not expose your facet but instead keeps a reference to it so that a subsequent call to
removeAdminFacet is possible.
With respect to the default functionality, the Properties facet could expose sensitive configuration information, and the
Process facet supports a
shutdown operation that opens the door for a denial-of-service attack.
• The Process facet allows the IceGrid node to gracefully terminate the process.
• The Properties facet enables IceGrid administrative clients to obtain configuration information about activated servers.
A reasonably secure value for the Ice.Admin.Endpoints property is one that uses the local host interface (
-h 127.0.0.1), which restricts access to clients that run on the same host. Incidentally, this is the default value that IceGrid defines for its servers, although you can override that if you like. Note that using a local host endpoint does not preclude remote administration for IceGrid servers because IceGrid transparently routes requests on
admin objects to the appropriate server via its node (see
Section 39.21.3).
The default identity of the admin object has a UUID for its category, which makes it difficult for a hostile client to guess. Depending on your requirements, the use of a UUID may be an advantage or a disadvantage. For example, in a trusted environment, the use of a UUID may create additional work, such as the need to add an interface that an administrative client can use to obtain the identity or proxy of a remote
admin object. An obscure identity might be more of a hindrance in this situation, and therefore specifying a static category via the
Ice.Admin.InstanceName property is a reasonable alternative. In general, however, we recommend using the default behavior.