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