In order to deal with some pretty stringent backwards compatibility requirements for install, smf(5) attempts to self-assemble services during boot. I won't go into the vagaries of why it is so helpful to avoid having to write the smf(5) repository during install/upgrade now, and instead stick to the tactical details of what happens in Solaris/OpenSolaris. Most of this focuses on the behaviour of smf(5) during the first boot.
Some background details
First, a refresher (or an introduction, depending on your perspective). The delivery mechanism for services in Solaris smf are Service Manifests. They're a bunch of XML goo which describe metadata and optionally data about a service: how do I start it, what's the service model, is it an inetd service, etc. If you're interested in how to write a manifest, see the Service Developer Introduction. However, the system doesn't really know anything about a service described by a manfest until it is imported with svccfg import. At import time, the data is copied from the manifest and put into the smf(5) repository. (I wrote a bit about the design choices we made when creating the repository in another post.)
Once a service is in the repository, all of the smf(5) framework kicks in for that service -- displaying it with svcs(1), starting it during boot, restarting on failure, etc. But, until the service has been imported into the repository, smf(5) knows nothing about it. The persistent repository information is stored as a data file in /etc/svc/repository.db.
The seed repository
As I already mentioned, smf(5) only cares about the repository, so we need to generate enough of a repository to get the system to the point where it can import more manifests. What's required for that? Essentially, the / and /usr filesystems must be mounted and writable. We need the utilities from /usr and obviously we can't write to /etc/svc/repository.db without a writable root filesystem. We call this repository the seed.
When you build OpenSolaris (and when we build Solaris internally) in addition to compiling all of the smf(5) source code, we also assemble the seed repository. Essentially, what goes in the seed is what's required to start svc:/system/manifest-import:default. You can see how we assemble this during the build in usr/src/cmd/svc/seed/Makefile. We build a seed for both the global zone and the non-global zones, since the non-global zones require fewer services to get to manifest-import.
You can look at what's in the seed repository for your system. For something resembling current OpenSolaris bits, this looks like:
$ cp /lib/svc/seed/global.db /tmp/global.db
$ svccfg
svc:> repository /tmp/global.db
svc:> list
system/boot-archive
network/datalink
network/physical
system/device/local
milestone/devices
system/identity
system/filesystem/local
system/manifest-import
system/filesystem/minimal
[...]
If you look carefully at that Makefile, though, we don't place these in the normal repository location, we stash them at /lib/svc/seed/[non]global.db. This ensures that when folks use bfu, we don't over-write the local service configuration with the very limited seed repository! It is important to note that no /etc/svc/repository.db gets generated during the OpenSolaris build process.
Placing the seed
There's logic in both bfu and the i.manifest package class-action script which place the appropriate seed repository in /etc/svc/repository.db if the file doesn't already exist. Thus, if you're using bfu or the Solaris pkgadd(1M), we copy the seed to the correct location for you. The Solaris install CD/DVD/netinstall image also creates its own repository.db, based on the seed we generate during the ON build.
Finally, the boot discussion
OK, so now we have a system with a very very limited seed set of services as its repository. Those services are really limited, and don't even include essential things like ssh(1). But, the system is able to boot to the point of running the manifest-import service. We'll assume it does so successfully. Then the manifest-import start method kicks in. First we find all the manifests in /var/svc/manifest:
423 nonsite_dirs=`/usr/bin/find /var/svc/manifest/* -name site -prune -o -type d \
424 -print -prune`
425
426 nonsite_manifests=`/lib/svc/bin/mfstscan $nonsite_dirs`
427 site_manifests=`/lib/svc/bin/mfstscan /var/svc/manifest/site`
428
429 manifests="$nonsite_manifests $site_manifests"
mfstscan is a private command which finds any manifests in the subdirectories provided and checks to see if they've changed since last import. Then we import any manifests which have changed, keeping a running count so the user knows something is happening:
443 set -- $manifests
444 backup=`echo "$#/$#" | sed 's/.//g'`
445 fwidth=`echo "$#\c" | wc -c`
446
447 echo "Loading smf(5) service descriptions: \c" > /dev/msglog
448
449 i=1; n=$#
450 while [ $# -gt 0 ]; do
451 printf "%${fwidth}s/%${fwidth}s" $i $n > /dev/msglog
452 svccfg_import $1
453 i=`expr $i + 1`
454 shift
455 echo "$backup\c" > /dev/msglog
456 done
But how do services get enabled?
Some of you may have noticed, though, that OpenSolaris has most of the services specified as disabled in their service manifests. You can see this, for example, with the syslog manifest. The enabled='false' means that the service will be disabled after it is imported. But, on your running Solaris system, it is enabled.
What's happening after the manifest imports in the manifest-import start method is that profiles are being applied. As I described here, profiles are just a way to enable or disable a bunch of services. Solaris/OpenSolaris tries to deliver as many services as possible as disabled in their manifest, so that an administrator can choose carefully which services they want to enable, and when we add new ones, they won't be automatically enabled unless absolutely necessary to get to manifest-import. We look for the following profiles and apply them in order:
/var/svc/profile/generic.xml
/var/svc/profile/platform.xml
/var/svc/profile/site.xml
generic.xml is created as a link to generic_open.xml both during the build process and by the Solaris packaging tools. The platform.xml profile is created as a link to the appropriate file during boot by the manifest-import start method:
480 if [ ! -f /var/svc/profile/platform.xml ]; then
481 this_karch=`uname -m`
482 this_plat=`uname -i`
483
484 if [ -f /var/svc/profile/platform_$this_plat.xml ]; then
485 platform_profile=platform_$this_plat.xml
486 elif [ -f /var/svc/profile/platform_$this_karch.xml ]; then
487 platform_profile=platform_$this_karch.xml
488 else
489 platform_profile=platform_none.xml
490 fi
491
492 ln -s $platform_profile /var/svc/profile/platform.xml
493 fi
Finally, site.xml is left entirely to the control of the administrator. If you're interested in re-visiting this boot order, take a look at smf_bootstrap(5). These profiles enable all of the services that were just added by manifest-import. If you want to use one of our limited profiles like generic_limited_net.xml, you can do the following before reboot (i.e. during jumpstart):
$ ln -sf /var/svc/profile/generic_limited_net.xml /var/svc/profile/generic.xml
Or, you can just place your customizations into /var/svc/profile/site.xml, and they'll be applied after the Solaris/OpenSolaris-delivered profiles are applied.
Technorati Tags: OpenSolaris, Solaris, and smf.