This chapter discusses architectural details you should keep in mind when creating a public module for a data service.
The following topics are included:
Data store functions, which facilitate activities related to the public module and underlying data service themselves. These functions include configure(), mklocation(), status(), and version().
dhcptab container functions, which facilitate the creation of the dhcptab container, the writing of records to the dhcptab container, and the query of records in the dhcptab container. The open_dt() function creates a handle for the container, and the other functions take a pointer to that handle. The close_dt() function destroys the handle when it closes the container.
Network container functions, which facilitate the creation of DHCP network containers, the writing of records to the network containers, and the query of records in the network containers. The open_dn() function creates a handle for the container, and the other functions take a pointer to that handle. The close_dn() function destroys the handle when it closes the container.
The functions are described in more detail in Chapter 3, Service Provider Layer API.
The DHCP server implements multithreading, which enables it to service many clients simultaneously. Public modules are required to be MT-SAFE to support multithreading by the DHCP server.
To make your module MT-SAFE, you must synchronize calls to add_d?(), delete_d?(), and modify_d?() so that they are called serially. For example, if one thread is inside add_dn() for a given DHCP network container, no other thread may be inside add_dn(), delete_dn(), modify_dn(), or lookup_dn() for that same container. If your public module supports a local file-system-based data service, you can use the synchronization service to take care of this for you. See Synchronizing Access to File-System-Based Containers for more information.
When you write a public module that provides access to containers in a local file-system-based data service (the data service runs on the same machine as the DHCP server), it can be difficult to synchronize access to the underlying data service between multiple processes and threads.
The Solaris DHCP synchronization service simplifies the design of public modules using local file-system-based data services by pushing synchronization up into the Framework Configuration Layer. When you design your module to use this framework, your code becomes simpler and your design cleaner.
The synchronization service provides public modules with per-container exclusive synchronization of all callers of the add_d?(), delete_d?(), and modify_d?() Service Provider Layer API calls. This means that if one thread is inside add_dn() for a given DHCP network container, no other thread may be inside add_dn(), delete_dn(), modify_dn() or lookup_dn() for that same container. However, other threads may be within routines that provide no synchronization guarantees, such as close_dn().
Per-container shared synchronization of all callers of lookup_d?() is also provided. Thus, there may be many threads performing a lookup on the same container, but only one thread may perform an add, delete, or modify operation.
The synchronization service is implemented as a daemon (/usr/lib/inet/dsvclockd). Lock manager requests are made on the public module's behalf through Framework Configuration Layer API calls. The interface between the Framework Configuration layer and the lock manager daemon uses the Solaris doors interprocess communication mechanism. See, for example, door_create(3DOOR) and door_call(3DOOR).
The Framework Configuration layer starts the dsvclockd daemon if a public module requests synchronization and the daemon is not already running. The daemon automatically exits if it manages no locks for 15 minutes. To change this interval, you can create a /etc/default/dsvclockd file and set the IDLE default to the number of idle minutes before the daemon terminates.
dsvc_synchtype_t dsvc_synchtype = DSVC_SYNCH_DSVCD;
A public module notifies the Framework Configuration Layer that it does not require synchronization services by including the following global variable in one of the module's source files:
dsvc_synchtype_t dsvc_synchtype = DSVC_SYNCH_NONE;
DSVC_SYNCH_DSVCD and DSVC_SYNCH_NONE are the only two synchronization types that exist currently.
The architecture provides a facility that helps a files-based module avoid record update collisions. The Service Provider API facilitates the maintenance of data consistency through the use of a per-record update signature, an unsigned 64–bit integer. The update signature is the d?_sig element of the d?_rec_t container record data structure, defined in /usr/include/dhcp_svc_public.h. All layers of the architecture use d?_rec_t records, from the Application/Service Layer through the Framework Configuration Layer API and on through to the Service Provider Layer API. Above the Service Provider Layer, the update signature is an opaque object which is not manipulated by users of the Framework Configuration Layer API.
When a module receives a d?_rec_t record through a Service Provider Layer API function call, it should perform a lookup in the data service to find a record that matches the key fields of the d?_rec_t, and compare the signature of the internal record against the d?_rec_t passed by the call. If the signature of the internal record does not match that of the passed record, then the record has been changed since the consumer acquired it from the public module. In this case, the module should return DSVC_COLLISION, which informs the caller that the record has been changed since it was acquired. If the signatures match, the module should increment the update signature of the argument record before it stores the record.
When a module receives a new d?_rec_t record through the Service Provider Layer API, the module must assign a value to the update signature before it adds the record to the container. The simplest way is to set the value to 1.
However, in certain rare situations a collision might not be detected if the signature always has the same initial value. Consider the following scenario. Thread A adds a record with a signature of 1, and Thread B looks up that record. Thread A deletes the record and creates a new record with the same key fields and a signature of 1 since it has just been created. Thread B then modifies the record it looked up, but that has already been deleted. The module compares the key fields and signatures of the record looked up by Thread B and the record in the data store, finds them to be the same, and makes the modification. Such a modification attempt should have been a collision because the records are, in fact, not the same.
The ds_SUNWfiles.so and ds_SUNWbinfiles.so modules provided with Solaris DHCP address such a possibility. They divide the update signature into two fields to ensure the uniqueness of each record's signature. The first 16 bit field of the update signature is set to a randomly generated number. This field never changes in the record after it is set. The lower 48 bit field of the signature is set to 1 and then incremented each time the record is updated.
The modules provided with Solaris DHCP illustrate one approach you can use to avoid record update collisions. You can devise your own method or use a similar one.
The public module and containers must both contain version numbers to enable the architecture's upgrading mechanism to work.
where name is the name of the module and ver is the container format version number. The name must use a prefix that is an internationally known identifier associated with your organization. For example, the public modules that Sun Microsystems provides have names prefixed with SUNW, the stock ticker symbol for Sun. For example, the NIS+ public module is named ds_SUNWnisplus.so.1. By including such an identifier in the module name, you avoid public module name collisions in the /usr/lib/inet/dhcp/svc public module directory.
If your company name is Inet DataBase, for example, you might call your module ds_IDBtrees.so.1
Internally, the data store container names must contain the version number to enable you to produce revisions of your container formats whenever necessary. This naming scheme allows the coexistence of multiple versions of a container, which is a requirement for the architecture's container version upgrade mechanism to work.
The names used for the containers should include a globally recognizable token to ensure that the names are unique.
For example, the NIS+ public module provided with Solaris DHCP would create the dhcptab container internally as SUNWnisplus1_dhcptab. The container for the 172.21.174.0 network table would be SUNWnisplus1_172.21.174.0.
If your company name is Inet DataBase, and your public module is ds_IDBtrees.so.1, you would name your containers IDBtrees1_dhcptab and IDBtrees1_172.21.174.0.
The dhcptab container holds DHCP configuration data, described in the dhcptab man page. Only one instance of a dhcptab container is maintained in the DHCP service.
dhcptab records are passed between the Framework Configuration Layer and the Service Provider Layer by way of an internal structure, dt_rec_t. The include file /usr/include/dhcp_svc_public.h shows the structure.
DHCP network containers contain IP address records, described in the dhcp_network man page. These containers are named to indicate the data store and the dotted IP address of the network to which the IP addresses belong, such as 10.0.0.0. Any number of DHCP network containers may exist, one for each network supported by the DHCP service.
DHCP network records are passed between the Framework Configuration Layer and the Service Provider Layer by way of an internal structure, dn_rec_t. The include file /usr/include/dhcp_svc_public.h shows the structure.
The Solaris DHCP data access architecture provides an optional feature for passing data-store-specific configuration data to the public module (and thus the data store). This feature is implemented as an ASCII string which is passed through the DHCP service management interface (dhcpconfig or dhcpmgr) and stored by the Framework Configuration Layer on the DHCP server machine. See the dhcpsvc.conf(4) man page for more information. You determine what kind of information is passed in the string, and the DHCP administrator provides the value of the string through the administration tool. The string might, for example, contain a user name and password needed to log in to a database.
To obtain the information from the DHCP administrator, you must write a JavaBeans™ component to present an appropriate dialog. The information is then passed to the management interface as a single ASCII string. You should document the format of the ASCII string token to facilitate debugging. To support this feature, the public module must implement and export the configure() function, described in Chapter 3, Service Provider Layer API.
The architecture does not encrypt the ASCII string. It is saved in clear text in the /etc/inet/dhcpsvc.conf file. If you require encrypted information, the bean must encrypt the information before passing it to the Framework Configuration Layer.
You do not need to be concerned with container version upgrades, because the architecture facilitates the coexistence of different container versions when you follow the naming guidelines described in Naming the Public Module and Data Store Containers. The administrative tools use this feature of the architecture to enable DHCP administrators to automatically upgrade from one container version to another.
The container format version is set in the Framework Configuration Layer configuration file automatically, either by the installation (when upgrading Solaris DHCP) or through the administrative interface during initial DHCP service configuration. If you install a new version of a public module that includes a new container version, the administrative interface automatically detects the new version, and asks the administrator to decide whether to upgrade the public module version. The upgrade can be deferred. The DHCP service will continue to run with the original version of the public module until the administrator upgrades the module.
The dhcpmgr and dhcpconfig management tools provide DHCP service configuration capabilities to system administrators. If you want your module to be available to users of the tools so they can configure the underlying data service, you must provide a JavaBeans™ component, known as a bean, for the public module.
The bean provides the public module with the context necessary to set the PATH variable, and optionally the RESOURCE_CONFIG variable, in dhcpsvc.conf.
The DSModule interface is contained in the dhcpmgr.jar file. In order to compile the bean against this interface, you must add /usr/sadm/admin/dhcpmgr/dhcpmgr.jar to the javac class path. For example, for your bean named myModule.java, type
javac -classpath /usr/sadm/admin/dhcpmgr/dhcpmgr.jar myModule.java
abstract java.awt.Component getComponent()
Returns a component that is displayed as one of the wizard steps for the DHCP Configuration Wizard. The returned component should be a panel containing GUI components to be used to obtain data-store-specific data from the user during configuration. The configuration data itself will be returned to the wizard as a result of calls to the getPath() and getAdditional() methods. See getPath() and getAdditional() for more information.
abstract java.lang.String getDescription()
Returns a description that is used by the DHCP Configuration Wizard when it adds the data store to the list of data store selections. For example, the management bean for the ds_SUNWfiles.so public module returns “Text files” as the description.
abstract java.lang.String getPath()
Returns the path/location that is used by the data store (the PATH value in the Framework Configuration Layer configuration file /etc/inet/dhcpsvc.conf), or null if not set. The path/location value should be supplied by the user by interaction with the management bean's component. See Passing Data Store Configuration Data.
abstract java.lang.String getAdditional()
Returns additional data-store-specific information, such as the RESOURCE_CONFIG value in the Framework Configuration Layer configuration file /etc/inet/dhcpsvc.conf. The value returned by this method is most likely supplied by the user by interaction with the management bean's component. See Passing Data Store Configuration Data.
The public module management bean must be archived as a JAR file. The name of the JAR file must consist of the name of the public module and a .jar suffix. For example, the name of the public module management bean for the ds_SUNWfiles.so public module is SUNWfiles.jar.
The JAR file must contain a manifest that identifies the bean class. For example, the manifest for the SUNWfiles.jar JAR file contains:
The com/sun/dhcpmgr/client/SUNWfiles/SUNWfiles.class class is the Java class that implements the com/sun/dhcpmgr/client/DSModule interface.