xref: /illumos-gate/usr/src/cmd/svc/startd/specials.c (revision 16b76d3cb933ff92018a2a75594449010192eacb)
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
54 special_null_transition()
55 {
56 }
57 
58 static void
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
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
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
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