usr/src/cmd/svc/startd/specials.c
author eschrock
Fri, 26 Oct 2007 13:47:19 -0700
changeset 5345 44060de1d838
parent 5017 3e669302a3e8
permissions -rw-r--r--
PSARC 2007/606 nvlist extensions 6608068 typo in ipmi_sdr_refresh() can cause segfault on allocation failure 6608069 libipmi should have support for user management 6608070 nvlist_lookup_nvpair() would be useful 6608094 nvlist_exists() would be useful 6608096 zprop_parse_value() should accept boolean values for boolean types 6608098 startd's fsminimal filesystem checks could be a little more explicit

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/*
 * specials.c - knowledge of special services
 *
 * svc.startd(1M) has duties that cannot be carried out without knowledge of the
 * transition of various services, such as the milestones, to their online
 * states.  Hooks are called with the restarter instance's ri_lock held, so
 * operations on all instances (or on the graph) should be performed
 * asynchronously.
 */

#include <sys/statvfs.h>
#include <sys/types.h>
#include <assert.h>
#include <errno.h>
#include <libintl.h>
#include <limits.h>
#include <locale.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <time.h>
#include <zone.h>

#include "startd.h"

void
special_null_transition()
{
}

static void
special_fsroot_post_online()
{
	static int once;
	char *locale;

	/*
	 * /usr, with timezone and locale data, is now available.
	 */
	if (!st->st_log_timezone_known) {
		tzset();
		st->st_log_timezone_known = 1;
	}

	if (!st->st_log_locale_known) {
		locale = st->st_locale;

		(void) setlocale(LC_ALL, "");
		st->st_locale = setlocale(LC_MESSAGES, "");
		if (st->st_locale) {
			st->st_locale = safe_strdup(st->st_locale);
			xstr_sanitize(st->st_locale);
			free(locale);
		} else {
			st->st_locale = locale;
		}

		(void) textdomain(TEXT_DOMAIN);
		st->st_log_locale_known = 1;
	}

	if (once)
		return;

	/*
	 * ctime(3C) ends with '\n\0'.
	 */
	once++;
	log_framework(LOG_INFO, "system start time was %s",
	    ctime(&st->st_start_time.tv_sec));
}

static void
special_fsminimal_post_online(void)
{
	ulong_t rfsid, fsid;
	pid_t init_pid;
	int ret;

	log_framework(LOG_DEBUG, "special_fsminimal_post_online hook "
	    "executed\n");

	/*
	 * If /var is still read-only, and it is on a separate filesystem, then
	 * attempt to mount it read-write now.
	 */
	if ((ret = fs_is_read_only("/var", &fsid)) == 1) {
		(void) fs_is_read_only("/", &rfsid);

		if (rfsid != fsid) {
			log_framework(LOG_WARNING, "/var filesystem "
			    "read-only after system/filesystem/minimal\n");
			if (fs_remount("/var"))
				log_framework(LOG_WARNING, "/var "
				    "filesystem remount failed\n");
		}
	}

	if ((ret = fs_is_read_only("/var", &fsid)) != 1) {
		if (ret != 0)
			log_error(LOG_WARNING, gettext("couldn't check status "
			    "of /var filesystem: %s\n"), strerror(errno));

		/*
		 * Clear (dead) entries and record boot time.
		 */
		utmpx_clear_old();
		utmpx_write_boottime();

		/*
		 * Reinitialize the logs to point to LOG_PREFIX_NORMAL.
		 */
		log_init();

		/*
		 * Poke init so it will create /var/run/initpipe.
		 */
		if (zone_getattr(getzoneid(), ZONE_ATTR_INITPID, &init_pid,
		    sizeof (init_pid)) != sizeof (init_pid)) {
			log_error(LOG_WARNING, "Could not get pid of init: "
			    "%s.\n", strerror(errno));
		} else {
			if (kill(init_pid, SIGHUP) != 0) {
				switch (errno) {
				case EPERM:
				case ESRCH:
					log_error(LOG_WARNING,
					    "Could not signal init: %s.\n",
					    strerror(errno));
					break;

				case EINVAL:
				default:
					bad_error("kill", errno);
				}
			}
		}
	}

	if ((ret = fs_is_read_only("/etc/svc", &fsid)) != 1) {
		if (ret != 0)
			log_error(LOG_WARNING, gettext("couldn't check status "
			    "of /etc/svc filesystem: %s\n"), strerror(errno));

		/*
		 * Take pending snapshots and create a svc.startd instance.
		 */
		(void) startd_thread_create(restarter_post_fsminimal_thread,
		    NULL);
	}
}

static void
special_single_post_online(void)
{
	int r;

	log_framework(LOG_DEBUG, "special_single_post_online hook executed\n");

	/*
	 * Un-set the special reconfig reboot property.
	 */
	r = libscf_set_reconfig(0);
	switch (r) {
	case 0:
	case ENOENT:
		break;

	case EPERM:
	case EACCES:
	case EROFS:
		log_error(LOG_WARNING, "Could not clear reconfiguration "
		    "property: %s.\n", strerror(r));
		break;

	default:
		bad_error("libscf_set_reconfig", r);
	}

	if (booting_to_single_user)
		(void) startd_thread_create(single_user_thread, NULL);
}

static service_hook_assn_t special_svcs[] = {
	{ "svc:/system/filesystem/root:default",
		special_null_transition,
		special_fsroot_post_online,
		special_null_transition },
	{ "svc:/system/filesystem/minimal:default",
		special_null_transition,
		special_fsminimal_post_online,
		special_null_transition },
	{ "svc:/milestone/single-user:default",
		special_null_transition,
		special_single_post_online,
		special_null_transition },
};

void
special_online_hooks_get(const char *fmri, instance_hook_t *pre_onp,
    instance_hook_t *post_onp, instance_hook_t *post_offp)
{
	int i;

	for (i = 0; i < sizeof (special_svcs) / sizeof (service_hook_assn_t);
	    i++)
		if (strcmp(fmri, special_svcs[i].sh_fmri) == 0) {
			*pre_onp = special_svcs[i].sh_pre_online_hook;
			*post_onp = special_svcs[i].sh_post_online_hook;
			*post_offp = special_svcs[i].sh_post_offline_hook;
			return;
		}

	*pre_onp = *post_onp = *post_offp = special_null_transition;
}