smf(5): asking versus doing
Let’s consider how applications are traditionally started: we execute (or the system executes) a command, such as
fooadm(1M), which in turn calls
fork(2), does some detachment work, and then calls
exec(2) to run
food(1M) (which is what we wanted). A schematic of this sequence would look like
For long-running, always-needed applications (which we call services), this model raises some questions:
- Who is responsible for
fooadm(1M) finishes, the intent behind the running
food(1M) is murky. Is it acceptable for
food(1M) to exit after some period of time? Or is exit a failure condition?
- How did
food(1M) get the privileges, resources, etc. that it needed to run? Either
fooadm(1M) was executed holding enough privileges for
food(1M) to function normally, or
food(1M) is setuid-root and must verify that its requesters are authorized to execute it for some set of its offered operations. Ditto for resource assignments.
- Why does each
food(1M) pair on the system (
bard(1M), …) handle this relationship slightly differently? I can only speculate.
(Lest anyone assume I’m pretending to novelty: most restarters (
init(1M)) or super-servers (
inetd(1M)) have answered the first two of these questions by offering a single, specific application model. But many daemons we run on systems today fit neither of these application models well.)
smf(5), the service management facility, directly forking a service is discouraged. Instead, one requests that a service be enabled, and the master restarter,
svc.startd(1M) or a delegate—like
inetd(1M)—will do the
exec(2) sequence. The equivalent diagram might be drawn as
Upon receiving an enable request from
svcadm enable <em>fmri</em>,
svc.startd(1M) determines if the service’s dependencies are satisfied and, if so, requests that the responsible restarter start an instance of the service. What the responsible restarter is doing is:
- setting the privileges, resource bindings, and application-specific environment for the service,
- creating a fault boundary to place the service instance within,
exec(2) to run the service*, and
- watching the service for error conditions, upon which the service is terminated (and restarted if appropriate).
(The combination of the master restarter and the delegates are handling these calculations and operations for every service on the system, propagating their state changes and evaluating the impact of those state changes in turn.)
Moreover, because the
smf_enable_instance(3SCF) request is evaluated based on the authorizations of the caller,
fooadm(1M) can be run with no significant privileges. Since we split authorizations into action authorizations (non-persistent operations, like “restart this service”) and modify authorizations (changing configuration aspects), it becomes straightforward to create an operator role that can tend a service, but not change its configuration or affect any other independent service on the system.
More flexible administrative assignments is one aspect of inserting
smf(5) into Solaris, but we’ll contrast these two approaches again—and reveal exactly what those purple rectangles represent.
- Not every restarter need offer a
exec(2) application model, but presently all
smf(5) restarters do.