Posts Tagged ‘smf’

Persistent Search Domains With NWAM and DHCP

Tuesday, January 11th, 2011

What I Want

I want to be able to refer to systems on both my home and work networks by their hostnames rather than their fully-qualified domain names, so, ‘prey’ instead of ‘prey.thestaticvoid.com’ and ‘acad2′ instead of ‘acad2.es.gwu.edu’.

The Problem

I would typically set my home and work domains as the search setting in /etc/resolv.conf. Unfortunately, either NWAM or the Solaris DHCP client (I haven’t decided which) overwrites resolv.conf on every new connection. DHCP on Linux does the same thing, but I can configure it by editing dhclient.conf (or whatever is being used these days, it’s been a while. I think I just set my domains in the NetworkManager GUI and forget about it).

The Solaris DHCP client configuration is not nearly as flexible, and neither is NWAM which gives you the option of replacing resolv.conf with information supplied by the DHCP server, or provided by you, but not a mix of both. I do like having the nameservers set by the DHCP server, so supplying a manual configuration is not an option.

What I Tried

The first thing I tried was setting the LOCALDOMAIN environmental variable in /etc/profile. From the resolv.conf man page:

You can override the search keyword of the system
resolv.conf file on a per-process basis by setting the
environment variable LOCALDOMAIN to a space-separated list
of search domains.

I thought, great, a way to manage domain search settings without worrying about what’s doing what to resolv.conf. It didn’t work as advertised:

% LOCALDOMAIN=thestaticvoid.com ping prey
ping: unknown host prey
% s touch /etc/resolv.conf
% LOCALDOMAIN=thestaticvoid.com ping prey
prey is alive
% LOCALDOMAIN=thestaticvoid.com ping prey
ping: unknown host prey

Next, I considered adding an NWAM Network Modifier to set my search string in resolv.conf after a new connection is established. This worked reasonably well, but didn’t handle the case when you switch from one network to another, for example, from wireless to wired. The only events in NWAM that can trigger a script when the network connection changes happens before DHCP messes up resolv.conf.

Finally, in the course of my testing, I discovered that the svc:/network/dns/client service was restarting with every network connection change. I looked into its manifest and saw that it was designed to wait for changes to resolv.conf:

<!--
 Wait for potential DHCP modification of resolv.conf.
-->
<dependency
   name='net'
   grouping='require_all'
   restart_on='none'
   type='service'>
    <service_fmri value='svc:/network/service' />
</dependency>

So I could write another service which depends on dns/client and restarts whenever dns/client does and I would have the last word about what goes into my configuration file!

My Solution

I wrote a service, svc:/network/dns/resolv-conf, with the following manifest:

<?xml version="1.0"?>
<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">

<service_bundle type="manifest" name="dns-resolv-conf">
    <service name="network/dns/resolv-conf"
       type="service"
       version="1">
        <create_default_instance enabled="false" />
        <single_instance />

        <dependency name="dns-client"
           grouping="require_all"
           restart_on="restart"
           type="service">
            <service_fmri value="svc:/network/dns/client" />
        </dependency>

        <dependent name="resolv-conf"
           grouping="optional_all"
           restart_on="restart">
            <service_fmri value="svc:/milestone/name-services" />
        </dependent>

        <exec_method type="method"
           name="start"
           exec="/lib/svc/method/dns-resolv-conf start"
           timeout_seconds="60" />

        <exec_method type="method"
           name="stop"
           exec="/lib/svc/method/dns-resolv-conf stop"
           timeout_seconds="60" />

        <property_group name="options" type="application">
            <propval name="search" type="astring" value="" />
        </property_group>

        <property_group name="startd" type="framework">
            <propval name="duration" type="astring" value="transient" />
        </property_group>

        <stability value="Unstable" />

        <template>
            <common_name>
                <loctext xml:lang="C">resolv.conf Settings</loctext>
            </common_name>
            <documentation>
                <manpage title="resolv.conf" section="4"
                   manpath="/usr/share/man" />
            </documentation>
        </template>
    </service>
</service_bundle>

which calls the script, /lib/svc/method/dns-resolv-conf containing:

#!/sbin/sh

. /lib/svc/share/smf_include.sh

search=$(svcprop -p options/search $SMF_FMRI)

case "$1" in
    "start")
        # Don't do anything if search option not provided.
        [ "$search" == '""' ] && exit $SMF_EXIT_OK

        # Reverse the lines because we either want to:
        #   add the search line after the *last* domain line or
        #   add it to the very top of the file if there is no domain line
        tac /etc/resolv.conf | grep -v "^search" | gawk '
            /^domain/ {
                if (!isset) {
                    print "search", $2, search
                    isset=1
                }
            }

            END {
                if (!isset) {
                    print "search", search
                }
            }

            1
        '
search="$search" | tac > /etc/resolv.conf.new && mv -f /etc/resolv.conf.new /etc/resolv.conf
        ;;

    "stop")
        # Just get rid of any search lines, I guess.
        grep -v "^search" /etc/resolv.conf > /etc/resolv.conf.new && mv -f /etc/resolv.conf.new /etc/resolv.conf
        ;;

    *)
        echo "Usage: $0 { start | stop }"
        exit $SMF_EXIT_ERR_CONFIG
esac

exit $SMF_EXIT_OK

So now I can set my search options like:

% svccfg -s resolv-conf setprop 'options/search="thestaticvoid.com es.gwu.edu"'
% svcadm refresh resolv-conf
% svcadm enable resolv-conf
% cat /etc/resolv.conf
domain  iss.gwu.edu
search iss.gwu.edu thestaticvoid.com es.gwu.edu
nameserver  161.253.152.50
nameserver  128.164.141.12

Problem solved! Or at least worked-around in the least hacky way I can!

Start Virtual NICs on OpenSolaris Boot

Wednesday, December 23rd, 2009

One of the more frustrating things I deal with on OpenSolaris is that every time I reboot, I have to manually bring up each virtual network interface in order to start all of my zones. There is a bug report for this problem that says a fix will be integrated into b132, which is just a few weeks away, but in the mean time, I’ve whipped up an SMF service to handle this for me. Create a file vnic.xml:

<?xml version="1.0"?>
<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">

<service_bundle type='manifest' name='vnic'>

<service
    name='network/vnic'
    type='service'
    version='1'>

    <dependency
        name='network_service'
        grouping='require_all'
        restart_on='none'
        type='service'>
        <service_fmri value='svc:/network/service' />
    </dependency>

    <dependent
        name='network_vnic'
        grouping='optional_all'
        restart_on='none'>
        <service_fmri value='svc:/system/zones' />
    </dependent>

    <exec_method
        type='method'
        name='start'
        exec='/usr/sbin/dladm up-vnic ${SMF_FMRI/*:/}'
        timeout_seconds='60' />

    <exec_method
        type='method'
        name='stop'
        exec=':true'
        timeout_seconds='60' />

    <property_group name='startd' type='framework'>
        <propval name='duration' type='astring' value='transient' />
    </property_group>

    <stability value='Unstable' />

    <template>
        <common_name>
            <loctext xml:lang='C'>
            Virtual Network Interface
            </loctext>
        </common_name>
        <documentation>
            <manpage title='dladm' section='1M'
                manpath='/usr/share/man' />
        </documentation>
    </template>
</service>

</service_bundle>

This service should run sometime after the network is started but before the zones are started. Load it in with svccfg -v import vnic.xml and create an instance of the service for each of the VNICs that you want to start. For example, if you want to start vnic0 on boot:

# svccfg -s vnic add vnic0
# svcadm refresh vnic0
# svcadm enable vnic0

UPDATE: Build 132 is out an this functionality has been integrated as the svc:/network/datalink-management:default service. The services that were added above can be removed by running svccfg delete vnic.

Mixer State in OpenSolaris

Wednesday, November 25th, 2009

I’ve recently installed OpenSolaris on my desktop and noticed that my volume settings do not persist between reboots. A quick search revealed that that functionality hasn’t been implemented yet. The thread suggested using the mixerctl command to save and restore the mixer state so I’ve thrown together an SMF service to do it automatically on boot and shutdown.

First, the script which should go into /lib/svc/method/sound-mixer:

#!/sbin/sh

. /lib/svc/share/smf_include.sh
smf_is_globalzone || exit $SMF_EXIT_OK

ctl_file=$(svcprop -p options/ctl_file $SMF_FMRI)

case "$1" in
'start')
        if [ ! -f $ctl_file ]; then
                echo "Mixer control file $ctl_file does not exist."
                exit $SMF_EXIT_OK
        fi

        if ! /usr/sbin/mixerctl -r $ctl_file; then
                echo "Error restoring mixer state."
                exit $SMF_EXIT_OK
        fi
        ;;

'stop')
        if ! /usr/sbin/mixerctl -f -s $ctl_file; then
                echo "Error saving mixer state."
                exit $SMF_EXIT_OK
        fi
        ;;

*)
        echo "Usage: $0 { start | stop }"
        exit $SMF_EXIT_ERR_CONFIG
        ;;
esac

exit $SMF_EXIT_OK

Second, the manifest which can be saved anywhere and loaded with svccfg -v import <manifest>:

<?xml version="1.0"?>
<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">

<service_bundle type='manifest' name='mixer'>

<service
       name='system/sound/mixer'
       type='service'
       version='1'>

        <create_default_instance enabled='true' />
        <single_instance />

        <dependency
           name='fs-local'
           grouping='require_all'
           restart_on='none'
           type='service'>
                <service_fmri value='svc:/system/filesystem/local' />
        </dependency>
       
        <dependency
           name='device-audio'
           grouping='require_all'
           restart_on='none'
           type='service'>
                <service_fmri value='svc:/system/device/audio' />
        </dependency>

        <exec_method
               type='method'
               name='start'
               exec='/lib/svc/method/sound-mixer start'
               timeout_seconds='60' />

        <exec_method
               type='method'
               name='stop'
               exec='/lib/svc/method/sound-mixer stop'
               timeout_seconds='60' />

        <property_group name='options' type='application'>
                <propval name='ctl_file' type='astring' value='/etc/sound/mixer.state' />
        </property_group>

        <property_group name='startd' type='framework'>
                <propval name='duration' type='astring' value='transient' />
        </property_group>

        <stability value='Unstable' />

        <template>
                <common_name>
                        <loctext xml:lang='C'>Mixer State Saver</loctext>
                </common_name>
                <documentation>
                        <manpage title='mixerctl' section='1M'
                           manpath='/usr/share/man' />
                </documentation>
        </template>

</service>

</service_bundle>

UPDATE: In b130, the audioctl command replaces mixerctl. In the sound-mixer script above, change /usr/sbin/mixerctl -r $ctl_file to /usr/bin/audioctl load-controls $ctl_file and /usr/sbin/mixerctl -f -s $ctl_file to /usr/bin/audioctl save-controls -f $ctl_file.