Creating an SMF service for mercurial web server

Posted by Chris W Beal on Oracle Blogs See other posts from Oracle Blogs or by Chris W Beal
Published on Thu, 31 May 2012 15:23:28 +0000 Indexed on 2012/05/31 16:46 UTC
Read the original article Hit count: 427

Filed under:

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.

  1. The manifest
    1. Usually lives in /var/svc/manifest somewhere
    2. Can be imported from any location
  2. The method
    1. Usually live in /lib/svc/method
      1. I simply put the script straight in that directory. Not very repeatable, but it worked
    2. 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.




© Oracle Blogs or respective owner

Related posts about /Solaris