Creating an SMF service for mercurial web server
- by Chris W Beal
I'm working on a project at the moment, which has a number of contributers. We're managing the project gate (which is stand alone) with mercurial. We want to have an easy way of seeing the changelog, so we can show management what is going on.
Luckily mercurial provides a basic web server which allows you to see the changes, and drill in to change sets. This can be run as a daemon, but as it was running on our build server, every time it was rebooted, someone needed to remember to start the process again. This is of course a classic usage of SMF.
Now I'm not an experienced person at writing SMF services, so it took me 1/2 an hour or so to figure it out the first time. But going forward I should know what I'm doing a bit better. I did reference this doc extensively.
Taking a step back, the command to start the mercurial web server is
$ hg serve -p <port number> -d
So we somehow need to get SMF to run that command for us.
In the simplest form, SMF services are really made up of two components.
The manifest
Usually lives in /var/svc/manifest somewhere
Can be imported from any location
The method
Usually live in /lib/svc/method
I simply put the script straight in that directory. Not very repeatable, but it worked
Can take an argument of start, stop, or refresh
Lets start with the manifest. This looks pretty complex, but all it's doing is describing the service name, the dependencies, the start and stop methods, and some properties.
The properties can be by instance, that is to say I could have multiple hg serve processes handling different mercurial projects, on different ports simultaneously
Here is the manifest I wrote. I stole extensively from the examples in the Documentation.
So my manifest looks like this
$ cat hg-serve.xml
<?xml version="1.0"?>
<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
<service_bundle type='manifest' name='hg-serve'>
<service
name='application/network/hg-serve'
type='service'
version='1'>
<dependency
name='network'
grouping='require_all'
restart_on='none'
type='service'>
<service_fmri value='svc:/milestone/network:default' />
</dependency>
<exec_method
type='method'
name='start'
exec='/lib/svc/method/hg-serve %m'
timeout_seconds='2' />
<exec_method
type='method'
name='stop'
exec=':kill'
timeout_seconds='2'>
</exec_method>
<instance name='project-gate' enabled='true'>
<method_context>
<method_credential user='root' group='root' />
</method_context>
<property_group name='hg-serve' type='application'>
<propval name='path' type='astring' value='/src/project-gate'/>
<propval name='port' type='astring' value='9998' />
</property_group>
</instance>
<stability value='Evolving' />
<template>
<common_name>
<loctext xml:lang='C'>hg-serve</loctext>
</common_name>
<documentation>
<manpage title='hg' section='1' />
</documentation>
</template>
</service>
</service_bundle>
So the only things I had to decide on in this are the service name "application/network/hg-serve" the start and stop methods (more of which later) and the properties. This is the information I need to pass to the start method script. In my case the port I want to start the web server on "9998", and the path to the source gate "/src/project-gate". These can be read in to the start method.
So now lets look at the method scripts
$ cat /lib/svc/method/hg-serve
#!/sbin/sh
#
#
# Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
#
# Standard prolog
#
. /lib/svc/share/smf_include.sh
if [ -z $SMF_FMRI ]; then
echo "SMF framework variables are not initialized."
exit $SMF_EXIT_ERR
fi
#
# Build the command line flags
#
# Get the port and directory from the SMF properties
port=`svcprop -c -p hg-serve/port $SMF_FMRI`
dir=`svcprop -c -p hg-serve/path $SMF_FMRI`
echo "$1"
case "$1" in
'start')
cd $dir
/usr/bin/hg serve -d -p $port
;;
*)
echo "Usage: $0 {start|refresh|stop}"
exit 1
;;
esac
exit $SMF_EXIT_OK
This is all pretty self explanatory, we read the port and directory using svcprop, and use those simply to run a command in the start case. We don't need to implement a stop case, as the manifest says to use "exec=':kill'for the stop method.
Now all we need to do is import the manifest and start the service, but first verify the manifest
# svccfg verify /path/to/hg-serve.xml
If that doesn't give an error try importing it
# svccfg import /path/to/hg-serve.xml
If like me you originally put the hg-serve.xml file in /var/svc/manifest somewhere you'll get an error and told to restart the import service
svccfg: Restarting svc:/system/manifest-import
The manifest being imported is from a standard location and should be imported with the command : svcadm restart svc:/system/manifest-import
# svcadm restart svc:/system/manifest-import
and you're nearly done. You can look at the service using svcs -l
# svcs -l hg-serve
fmri svc:/application/network/hg-serve:project-gate
name hg-serve
enabled false
state disabled
next_state none
state_time Thu May 31 16:11:47 2012
logfile /var/svc/log/application-network-hg-serve:project-gate.log
restarter svc:/system/svc/restarter:default
contract_id 15749
manifest /var/svc/manifest/network/hg/hg-serve.xml
dependency require_all/none svc:/milestone/network:default (online)
And look at the interesting properties
# svcprop hg-serve
hg-serve/path astring /src/project-gate
hg-serve/port astring 9998
...stuff deleted....
Then simply enable the service and if every things gone right, you can point your browser at http://server:9998 and get a nice graphical log of project activity.
# svcadm enable hg-serve
# svcs -l hg-serve
fmri svc:/application/network/hg-serve:project-gate
name hg-serve
enabled true
state online
next_state none
state_time Thu May 31 16:18:11 2012
logfile /var/svc/log/application-network-hg-serve:project-gate.log
restarter svc:/system/svc/restarter:default
contract_id 15858
manifest /var/svc/manifest/network/hg/hg-serve.xml
dependency require_all/none svc:/milestone/network:default (online)
None of this is rocket science, but a bit fiddly. Hence I thought I'd blog it. It might just be you see this in google and it clicks with you more than one of the many other blogs or how tos about it. Plus I can always refer back to it myself in 3 weeks, when I want to add another project to the server, and I've forgotten how to do it.