In honour of the “Mugs
for Manifests” contest, I thought I would spin out another custom
service description I wrote some months ago.
My setup for working from home—key during the last six months of
Solaris 10—is to tunnel into Sun’s network via one implementation or another
of a virtual private network (VPN). In all cases, the VPN solution
runs on Solaris. Although the VPN lets your system participate more or
less like a regular host, I find it’s easier to use VNC to remotely
present an X11 display from my main workstation, muskoka. But,
of course, machine running pre-production bits can fail or be rebooted
or be reinstalled regularly, so I wanted the VNC server on my system to
always be up: I wanted a VNC service.
What’s distinct about running the VNC server is that it should run as
me, with my environment, and not as root with init(1M)’s.
svc.startd(1M), while it can run methods according to
smf_method(5), doesn’t populate the environment fully in
the sense of login(1). So we will need to extract some
data from the name
service, which is cumbersome to perform in a shell script. We’ll write
our method in Perl, which implies
Tip 1: Methods need not be shell scripts.
In fact, the start method and the stop method can be totally separate
commands: you could write one in Python, and one can be an executable Java .jar
archive, or some even more bizarre combination.
The other trick is that, if VNC fails for some reason, I want to be
aggressive about cleaning up its various leftover temporary files. For
this purpose, I run the stop method with a different
credential—the default of root—than the start
method, which is done in our brief manifest by locating the
<method_context> element on only the start method.
Tip 2: Methods need not be run with identical method contexts. Credentials, privileges, and the like may all differ from method to method.
Our manifest then looks like:
<?xml version='1.0'?>
<!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'>
<service_bundle type='manifest' name='export'>
<service name='application/vncserver' type='service' version='0'>
<single_instance/>
<instance name='sch' enabled='true'>
<dependency name='milestone' grouping='require_all' restart_on='none' type='service'>
<service_fmri value='svc:/milestone/multi-user:default'/>
</dependency>
<dependency name='autofs' grouping='require_all' restart_on='none' type='service'>
<service_fmri value='svc:/system/filesystem/autofs:default'/>
</dependency>
<dependency name='nis' grouping='require_all' restart_on='none' type='service'>
<service_fmri value='svc:/network/nis/client:default'/>
</dependency>
<exec_method name='stop' type='method' exec='/home/sch/bin/vncserver_method stop' timeout_seconds='60'/>
<exec_method name='start' type='method' exec='/home/sch/bin/vncserver_method start' timeout_seconds='300'>
<method_context>
<method_credential user='sch' group='staff' />
</method_context>
</exec_method>
</instance>
</service>
</service_bundle>
The dependencies above are needed if you use NFS for home
directories and NIS for name services; they could be reduced for less
networked setups.
And, for the method, we have a short Perl program. The complete list of
environment variables in login(1) would include
LOGNAME, PATH, MAIL, and
TZ (timezone), and exclude my silly setting of
LANG, but most of these will be set up by the shell that
the VNC startup script (its analgue to .xinitrc. The
various print calls are just to let the service log show a
little activity, and could be removed.
!/usr/perl5/bin/perl
require 5.8.3;
use strict;
use warnings;
use locale;
my ($name, $passwd, $uid, $gid, $quota, $comment, $gcos, $dir, $shell,
$expire) = getpwuid "$<";
$ENV{USER} = $name;
$ENV{HOME} = $dir;
$ENV{SHELL} = $shell;
$ENV{LANG} = "en_CA"; # Just to create havoc (i.e. expose bugs).
#
The stop method is run as root so that it can cleanup.
#
if (defined($ARGV[0]) && $ARGV[0] eq "stop") {
ksh and sh specific
print "stop method\n";
system("$ENV{SHELL}", "-c", "/opt/csw/bin/vncserver -kill :1");
if (-S "/tmp/.X11-unix/X1") {
unlink("/tmp/.X11-unix/X1");
unlink("/tmp/.X1-lock");
}
exit 0;
}
#
The start method is run with the user's identity.
#
print "start method\n";
if (-f "/tmp/.X1-lock") {
unlink("/tmp/.X1-lock");
}
if (-S "/tmp/.X11-unix/X1") {
system("logger -p 1 application/vncserver requires " .
"/tmp/.X11-unix/X1 be removed");
exit 0;
}
ksh and sh specific
{ exec "$ENV{SHELL}", "-c",
"/opt/csw/bin/vncserver -pn -geometry 1600x1200 -depth 24 :1" };
system("logger -p 1 application/vncserver can't exec /opt/csw/bin/vncserver");
exit 1;
And now we have always-on VNC service for the regular telecommuter:
$ svcs -p vncserver
STATE STIME FMRI
online 13:01:01 svc:/application/vncserver:sch
13:01:00 100577 Xvnc
13:01:17 100625 xwrits
13:01:17 100626 ctrun
13:01:17 100632 xautolock
13:11:18 102348 xlock
$ uptime
12:00pm up 23 hr(s), 4 users, load average: 0.04, 0.07, 0.07
Exercises
- Remove the hard coded display numbering (“:1″, “X1″, etc.).
- Make the resolution, display depth, RGB encoding, and other standard options into
properties.
[ T:
Solaris
smf
]