Home | History | Annotate | Download | only in startd
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     27 
     28 /*
     29  * specials.c - knowledge of special services
     30  *
     31  * svc.startd(1M) has duties that cannot be carried out without knowledge of the
     32  * transition of various services, such as the milestones, to their online
     33  * states.  Hooks are called with the restarter instance's ri_lock held, so
     34  * operations on all instances (or on the graph) should be performed
     35  * asynchronously.
     36  */
     37 
     38 #include <sys/statvfs.h>
     39 #include <sys/types.h>
     40 #include <assert.h>
     41 #include <errno.h>
     42 #include <libintl.h>
     43 #include <limits.h>
     44 #include <locale.h>
     45 #include <pthread.h>
     46 #include <signal.h>
     47 #include <stdio.h>
     48 #include <string.h>
     49 #include <strings.h>
     50 #include <time.h>
     51 #include <zone.h>
     52 
     53 #include "startd.h"
     54 
     55 void
     56 special_null_transition()
     57 {
     58 }
     59 
     60 static void
     61 special_fsroot_post_online()
     62 {
     63 	static int once;
     64 	char *locale;
     65 
     66 	/*
     67 	 * /usr, with timezone and locale data, is now available.
     68 	 */
     69 	if (!st->st_log_timezone_known) {
     70 		tzset();
     71 		st->st_log_timezone_known = 1;
     72 	}
     73 
     74 	if (!st->st_log_locale_known) {
     75 		locale = st->st_locale;
     76 
     77 		(void) setlocale(LC_ALL, "");
     78 		st->st_locale = setlocale(LC_MESSAGES, "");
     79 		if (st->st_locale) {
     80 			st->st_locale = safe_strdup(st->st_locale);
     81 			xstr_sanitize(st->st_locale);
     82 			free(locale);
     83 		} else {
     84 			st->st_locale = locale;
     85 		}
     86 
     87 		(void) textdomain(TEXT_DOMAIN);
     88 		st->st_log_locale_known = 1;
     89 	}
     90 
     91 	if (once)
     92 		return;
     93 
     94 	/*
     95 	 * ctime(3C) ends with '\n\0'.
     96 	 */
     97 	once++;
     98 	log_framework(LOG_INFO, "system start time was %s",
     99 	    ctime(&st->st_start_time.tv_sec));
    100 }
    101 
    102 static void
    103 special_fsminimal_post_online(void)
    104 {
    105 	ulong_t rfsid, fsid;
    106 	pid_t init_pid;
    107 	int ret;
    108 
    109 	log_framework(LOG_DEBUG, "special_fsminimal_post_online hook "
    110 	    "executed\n");
    111 
    112 	/*
    113 	 * If /var is still read-only, and it is on a separate filesystem, then
    114 	 * attempt to mount it read-write now.
    115 	 */
    116 	if ((ret = fs_is_read_only("/var", &fsid)) == 1) {
    117 		(void) fs_is_read_only("/", &rfsid);
    118 
    119 		if (rfsid != fsid) {
    120 			log_framework(LOG_WARNING, "/var filesystem "
    121 			    "read-only after system/filesystem/minimal\n");
    122 			if (fs_remount("/var"))
    123 				log_framework(LOG_WARNING, "/var "
    124 				    "filesystem remount failed\n");
    125 		}
    126 	}
    127 
    128 	if ((ret = fs_is_read_only("/var", &fsid)) != 1) {
    129 		if (ret != 0)
    130 			log_error(LOG_WARNING, gettext("couldn't check status "
    131 			    "of /var filesystem: %s\n"), strerror(errno));
    132 
    133 		/*
    134 		 * Clear (dead) entries and record boot time.
    135 		 */
    136 		utmpx_clear_old();
    137 		utmpx_write_boottime();
    138 
    139 		/*
    140 		 * Reinitialize the logs to point to LOG_PREFIX_NORMAL.
    141 		 */
    142 		log_init();
    143 
    144 		/*
    145 		 * Poke init so it will create /var/run/initpipe.
    146 		 */
    147 		if (zone_getattr(getzoneid(), ZONE_ATTR_INITPID, &init_pid,
    148 		    sizeof (init_pid)) != sizeof (init_pid)) {
    149 			log_error(LOG_WARNING, "Could not get pid of init: "
    150 			    "%s.\n", strerror(errno));
    151 		} else {
    152 			if (kill(init_pid, SIGHUP) != 0) {
    153 				switch (errno) {
    154 				case EPERM:
    155 				case ESRCH:
    156 					log_error(LOG_WARNING,
    157 					    "Could not signal init: %s.\n",
    158 					    strerror(errno));
    159 					break;
    160 
    161 				case EINVAL:
    162 				default:
    163 					bad_error("kill", errno);
    164 				}
    165 			}
    166 		}
    167 	}
    168 
    169 	if ((ret = fs_is_read_only("/etc/svc", &fsid)) != 1) {
    170 		if (ret != 0)
    171 			log_error(LOG_WARNING, gettext("couldn't check status "
    172 			    "of /etc/svc filesystem: %s\n"), strerror(errno));
    173 
    174 		/*
    175 		 * Take pending snapshots and create a svc.startd instance.
    176 		 */
    177 		(void) startd_thread_create(restarter_post_fsminimal_thread,
    178 		    NULL);
    179 	}
    180 }
    181 
    182 static void
    183 special_single_post_online(void)
    184 {
    185 	int r;
    186 
    187 	log_framework(LOG_DEBUG, "special_single_post_online hook executed\n");
    188 
    189 	/*
    190 	 * Un-set the special reconfig reboot property.
    191 	 */
    192 	r = libscf_set_reconfig(0);
    193 	switch (r) {
    194 	case 0:
    195 	case ENOENT:
    196 		break;
    197 
    198 	case EPERM:
    199 	case EACCES:
    200 	case EROFS:
    201 		log_error(LOG_WARNING, "Could not clear reconfiguration "
    202 		    "property: %s.\n", strerror(r));
    203 		break;
    204 
    205 	default:
    206 		bad_error("libscf_set_reconfig", r);
    207 	}
    208 
    209 	if (booting_to_single_user)
    210 		(void) startd_thread_create(single_user_thread, NULL);
    211 }
    212 
    213 static service_hook_assn_t special_svcs[] = {
    214 	{ "svc:/system/filesystem/root:default",
    215 		special_null_transition,
    216 		special_fsroot_post_online,
    217 		special_null_transition },
    218 	{ "svc:/system/filesystem/minimal:default",
    219 		special_null_transition,
    220 		special_fsminimal_post_online,
    221 		special_null_transition },
    222 	{ "svc:/milestone/single-user:default",
    223 		special_null_transition,
    224 		special_single_post_online,
    225 		special_null_transition },
    226 };
    227 
    228 void
    229 special_online_hooks_get(const char *fmri, instance_hook_t *pre_onp,
    230     instance_hook_t *post_onp, instance_hook_t *post_offp)
    231 {
    232 	int i;
    233 
    234 	for (i = 0; i < sizeof (special_svcs) / sizeof (service_hook_assn_t);
    235 	    i++)
    236 		if (strcmp(fmri, special_svcs[i].sh_fmri) == 0) {
    237 			*pre_onp = special_svcs[i].sh_pre_online_hook;
    238 			*post_onp = special_svcs[i].sh_post_online_hook;
    239 			*post_offp = special_svcs[i].sh_post_offline_hook;
    240 			return;
    241 		}
    242 
    243 	*pre_onp = *post_onp = *post_offp = special_null_transition;
    244 }
    245