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:
multi-user
milestone. See smf(5) for a description of milestones. Multi-user is basically equivalent to run level 2 (/etc/rc2.d
).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: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.