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