Documentation for SMF

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 itsleep 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 dummySTATE          STIME    FMRIonline         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 dummysvc:/system/dummy> listproplistpropmulti-user                                            dependencymulti-user/entities                                   fmri     svc:/milestone/multi-usermulti-user/grouping                                   astring  require_allmulti-user/restart_on                                 astring  nonemulti-user/type                                       astring  servicestart                                                 methodstart/exec                                            astring  /opt/dummy/dummydstart/timeout_seconds                                 count    60start/type                                            astring  methodstop                                                  methodstop/exec                                             astring  :killstop/timeout_seconds                                  count    60stop/type                                             astring  methodconfig                                                applicationconfig/config_file                                    astring  /opt/dummy/dummy.confconfig/dummyflag                                      integer  0config/dummyprops                                     astring  property1config/local_only                                     boolean  falsegeneral                                               frameworkgeneral/entity_stability                              astring  Unstablemanifestfiles                                         frameworkmanifestfiles/var_svc_manifest_application_dummy_xml  astring  /var/svc/manifest/application/dummy.xmlmanifestfiles/var_svc_manifest_dummy_xml              astring  /var/svc/manifest/dummy.xmlsvc:/system/dummy>

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

svc:/system/dummy> setprop config/dummyflag = 1setprop config/dummyflag = 1svc:/system/dummy> setprop config/dummyprops = "new prop"setprop config/dummyprops = "new prop"svc:/system/dummy> listproplistpropmulti-user                                            dependencymulti-user/entities                                   fmri     svc:/milestone/multi-usermulti-user/grouping                                   astring  require_allmulti-user/restart_on                                 astring  nonemulti-user/type                                       astring  servicestart                                                 methodstart/exec                                            astring  /opt/dummy/dummydstart/timeout_seconds                                 count    60start/type                                            astring  methodstop                                                  methodstop/exec                                             astring  :killstop/timeout_seconds                                  count    60stop/type                                             astring  methodconfig                                                applicationconfig/config_file                                    astring  /opt/dummy/dummy.confconfig/local_only                                     boolean  falseconfig/dummyflag                                      integer  1config/dummyprops                                     astring  "new prop"general                                               frameworkgeneral/entity_stability                              astring  Unstablemanifestfiles                                         frameworkmanifestfiles/var_svc_manifest_application_dummy_xml  astring  /var/svc/manifest/application/dummy.xmlmanifestfiles/var_svc_manifest_dummy_xml              astring  /var/svc/manifest/dummy.xmlsvc:/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.



Post written by Mr. Max Bruning