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