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 thekill
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