Bruning Questions: Documentation for SMF

June 24, 2013 - by Mr. Max Bruning

While watching a sales group meeting this week, I saw a slide that had a quote from a Joyent customer. Paraphrasing, it said:

"More documentation, specifically for SMF, is needed. It's great once you figure it out, but it has a learning curve relative to linux. We would love to just see more examples or instruction sets for common activities."

The Service Management Facility (SMF) allows one to add, delete, restart, enable, and disable "services" in the system. As example of a service might be, for instance, mysql. When the system comes up, you would like mysql to be up and running.

Traditionally, this was done by adding a file or files in /etc/rc*.d, and/or using one of various init mechanisms in linux, such as the runit command.

In this blog post, I will set up a "dummy" service so that people can see how simple this can be. The dummy service will do nothing, but it will do it correctly. It can be used as a template for setting up other services. I'll be doing everything in a virtualized SmartOS instance, running, of course, on SmartOS.

Documentation for SMF can be found at smf(5). A comparison of different init mechanisms for starting services, though dated, can be found at Comparison of init systems. This post will simply concentrate on using SMF to set up a service. A high level description can be found at Service Management Facility. A technical white paper is at Solaris Service Management Facility: Modern System Startup and Administration. And, of course, there is Ben Rockwood's excellent post An SMF Manifest Cheatsheet, which makes me wonder if I'm not wasting my time here. For explanation of fields shown in the manifest file, please consult that blog post.

So, let's get started. The first thing you'll need for your new service is to create an xml file called a "service management file" (see smf_template(5) for details). In keeping with the time-honored method of using existing code and copy/paste it to what you want, I'll start with the manifest in the smf_template(5) man page and make a few very minor modifications. Here is an example for the dummy service. This file is /var/svc/manifest/application/dummy.xml. (We can't put this into /lib/svc/manifest because the root file system is read-only on SmartOS).

A brief description of the file follows:

  • The first few lines are basically boilerplate and a comment
  • A list of dependencies is specified. These are the services that are required to be running for the dummy service to run. Basically, the dummy service will not run until at the multi-user milestone. See smf(5) for a description of milestones. Multi-user is basically equivalent to run level 2 (/etc/rc2.d).
  • A set of exec_method sections. The dummy example has one for starting and stopping the service. The one for starting the service runs /opt/dummy/dummyd. The method for stopping the service runs the kill command on the service. Note that services, by default, are expected to keep running. If you want to start a service that runs and exits, you need to specify that the service is transient. Failure to do so will cause the service to go into maintenance as SMF will continuously try to restart it for a bit and then decide something is wrong. To make a service transient, add the following lines to the service:
  • Following the exec methods are a set of properties and/or property groups for the service. This might include information about configuration, tunable variables, etc.

And here is the /opt/dummy/dummyd file.

$!/usr/bin/bash

# smf_include.sh contains things like exit values (SMF_EXIT_OK)

. /lib/svc/share/smf_include.sh

# do the service.  we'll just sleep, but in the background.
# the service will die after 60 seconds, and SMF
# should restart it

sleep 60 &

exit ${SMF_EXIT_OK}

What if it only exited, without the sleep 60? Since the dummy service has not been declared to be transient, SMF will try to restart it. This will happen for a short time before SMF decides it is restarting too often and take the service into maintenance mode. Generally, for non-transient services, you want to run something in the background and then run exit ${SMF_EXIT_OK} as shown. If you run your daemon, but not in the background (for instance, instead of sleep 60 &, you run sleep 60, SMF will time out the service, and/or tell you the start method is running (as opposed to started). And, of course, the method does not have to be a shell script, e.g., it could be compiled code.

So, let's try the new service. First, we'll "import" the service.

# cd /var/svc/manifest/application
# svccfg import dummy.xml
#

Next, enable the service.

# svcadm enable dummy
# svcs dummy
STATE          STIME    FMRI
online         18:09:29 svc:/system/dummy:default
#

Let's look at the log file.

# cat `svcs -L dummy`
[ Jun 18 18:08:51 Rereading configuration. ]
[ Jun 18 18:09:29 Enabled. ]
[ Jun 18 18:09:29 Executing start method ("/opt/dummy/dummyd"). ]
[ Jun 18 18:09:29 Method "start" exited with status 0. ]

And let's look at properties for the service.

# svccfg -s dummy
svc:/system/dummy> listprop
listprop
multi-user                                            dependency
multi-user/entities                                   fmri     svc:/milestone/multi-user
multi-user/grouping                                   astring  require_all
multi-user/restart_on                                 astring  none
multi-user/type                                       astring  service
start                                                 method
start/exec                                            astring  /opt/dummy/dummyd
start/timeout_seconds                                 count    60
start/type                                            astring  method
stop                                                  method
stop/exec                                             astring  :kill
stop/timeout_seconds                                  count    60
stop/type                                             astring  method
config                                                application
config/config_file                                    astring  /opt/dummy/dummy.conf
config/dummyflag                                      integer  0
config/dummyprops                                     astring  property1
config/local_only                                     boolean  false
general                                               framework
general/entity_stability                              astring  Unstable
manifestfiles                                         framework
manifestfiles/var_svc_manifest_application_dummy_xml  astring  /var/svc/manifest/application/dummy.xml
manifestfiles/var_svc_manifest_dummy_xml              astring  /var/svc/manifest/dummy.xml
svc:/system/dummy>

And we'll change some properties and restart the service.

svc:/system/dummy> setprop config/dummyflag = 1
setprop config/dummyflag = 1
svc:/system/dummy> setprop config/dummyprops = "new prop"
setprop config/dummyprops = "new prop"
svc:/system/dummy> listprop
listprop
multi-user                                            dependency
multi-user/entities                                   fmri     svc:/milestone/multi-user
multi-user/grouping                                   astring  require_all
multi-user/restart_on                                 astring  none
multi-user/type                                       astring  service
start                                                 method
start/exec                                            astring  /opt/dummy/dummyd
start/timeout_seconds                                 count    60
start/type                                            astring  method
stop                                                  method
stop/exec                                             astring  :kill
stop/timeout_seconds                                  count    60
stop/type                                             astring  method
config                                                application
config/config_file                                    astring  /opt/dummy/dummy.conf
config/local_only                                     boolean  false
config/dummyflag                                      integer  1
config/dummyprops                                     astring  "new prop"
general                                               framework
general/entity_stability                              astring  Unstable
manifestfiles                                         framework
manifestfiles/var_svc_manifest_application_dummy_xml  astring  /var/svc/manifest/application/dummy.xml
manifestfiles/var_svc_manifest_dummy_xml              astring  /var/svc/manifest/dummy.xml
svc:/system/dummy> end
#
# svcadm restart dummy
#

I should mention that the "old" way of doing services, i.e., scripts in /etc/rc*.d is still supported. It doesn't give you the flexibility of SMF, but can be a quick way to get something up that was running on a version of Unix/Linux that does not have SMF, but does have the rc scripts.

The most difficult part of this (for me), is figuring out the correct xml. Reading existing xml service manifests can help.

:

Sign up Now for Instant Cloud Access

Get Started

View PricingSee Benchmarks