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
special_null_transition()56 special_null_transition()
57 {
58 }
59
60 static void
special_fsroot_post_online()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
special_fsminimal_post_online(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
special_single_post_online(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
special_online_hooks_get(const char * fmri,instance_hook_t * pre_onp,instance_hook_t * post_onp,instance_hook_t * post_offp)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