xref: /illumos-gate/usr/src/cmd/svc/startd/specials.c (revision f808c858)
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 2006 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()
104 {
105 	ulong_t rfsid, vfsid;
106 	pid_t init_pid;
107 
108 	log_framework(LOG_DEBUG, "special_fsminimal_post_online hook "
109 	    "executed\n");
110 
111 	/*
112 	 * Are / and /var really writeable?
113 	 */
114 	switch (fs_is_read_only("/", &rfsid)) {
115 	case 1:
116 		return;		/* still read-only: install / ro root */
117 	case 0:
118 		break;
119 	case -1:
120 	default:
121 		log_error(LOG_WARNING, gettext("couldn't check status of "
122 			"root filesystem: %s\n"), strerror(errno));
123 		break;
124 	}
125 
126 	switch (fs_is_read_only("/var", &vfsid)) {
127 	case 1:
128 		if (vfsid != rfsid) {
129 			log_framework(LOG_WARNING, "/var filesystem "
130 			    "read-only after system/filesystem/minimal\n");
131 			if (fs_remount("/var"))
132 				log_framework(LOG_WARNING, "/var "
133 					"filesystem remount failed\n");
134 		}
135 		break;
136 	case 0:
137 		break;
138 	case -1:
139 	default:
140 		log_error(LOG_WARNING, gettext("couldn't check status of "
141 		    "/var filesystem: %s\n"), strerror(errno));
142 		break;
143 	}
144 
145 	/*
146 	 * Clear (dead) entries and record boot time.
147 	 */
148 	utmpx_clear_old();
149 	utmpx_write_boottime();
150 
151 	/*
152 	 * Reinitialize the logs to point to LOG_PREFIX_NORMAL.
153 	 */
154 	log_init();
155 
156 	/*
157 	 * Poke init so it will create /etc/initpipe.
158 	 */
159 	if (zone_getattr(getzoneid(), ZONE_ATTR_INITPID, &init_pid,
160 	    sizeof (init_pid)) != sizeof (init_pid)) {
161 		log_error(LOG_WARNING, "Could not get pid of init: %s.\n",
162 		    strerror(errno));
163 	} else {
164 		if (kill(init_pid, SIGHUP) != 0) {
165 			switch (errno) {
166 			case EPERM:
167 			case ESRCH:
168 				log_error(LOG_WARNING,
169 				    "Could not signal init: %s.\n",
170 				    strerror(errno));
171 				break;
172 
173 			case EINVAL:
174 			default:
175 				bad_error("kill", errno);
176 			}
177 		}
178 	}
179 
180 	/*
181 	 * Take pending snapshots and create a svc.startd instance.
182 	 */
183 	(void) startd_thread_create(restarter_post_fsminimal_thread, NULL);
184 }
185 
186 static void
187 special_single_post_online()
188 {
189 	int r;
190 
191 	log_framework(LOG_DEBUG, "special_single_post_online hook executed\n");
192 
193 	/*
194 	 * Un-set the special reconfig reboot property.
195 	 */
196 	r = libscf_set_reconfig(0);
197 	switch (r) {
198 	case 0:
199 	case ENOENT:
200 		break;
201 
202 	case EPERM:
203 	case EACCES:
204 	case EROFS:
205 		log_error(LOG_WARNING, "Could not clear reconfiguration "
206 		    "property: %s.\n", strerror(r));
207 		break;
208 
209 	default:
210 		bad_error("libscf_set_reconfig", r);
211 	}
212 
213 	if (booting_to_single_user)
214 		(void) startd_thread_create(single_user_thread, NULL);
215 }
216 
217 static service_hook_assn_t special_svcs[] = {
218 	{ "svc:/system/filesystem/root:default",
219 		special_null_transition,
220 		special_fsroot_post_online,
221 		special_null_transition },
222 	{ "svc:/system/filesystem/minimal:default",
223 		special_null_transition,
224 		special_fsminimal_post_online,
225 		special_null_transition },
226 	{ "svc:/milestone/single-user:default",
227 		special_null_transition,
228 		special_single_post_online,
229 		special_null_transition },
230 };
231 
232 void
233 special_online_hooks_get(const char *fmri, instance_hook_t *pre_onp,
234     instance_hook_t *post_onp, instance_hook_t *post_offp)
235 {
236 	int i;
237 
238 	for (i = 0; i < sizeof (special_svcs) / sizeof (service_hook_assn_t);
239 	    i++)
240 		if (strcmp(fmri, special_svcs[i].sh_fmri) == 0) {
241 			*pre_onp = special_svcs[i].sh_pre_online_hook;
242 			*post_onp = special_svcs[i].sh_post_online_hook;
243 			*post_offp = special_svcs[i].sh_post_offline_hook;
244 			return;
245 		}
246 
247 	*pre_onp = *post_onp = *post_offp = special_null_transition;
248 }
249