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