xref: /titanic_50/usr/src/cmd/svc/startd/fork.c (revision e07d9cb85217949d497b02d7211de8a197d2f2eb)
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  * fork.c - safe forking for svc.startd
31  *
32  * fork_configd() and fork_sulogin() are related, special cases that handle the
33  * spawning of specific client processes for svc.startd.
34  */
35 
36 #include <sys/contract/process.h>
37 #include <sys/corectl.h>
38 #include <sys/ctfs.h>
39 #include <sys/stat.h>
40 #include <sys/types.h>
41 #include <sys/uio.h>
42 #include <sys/wait.h>
43 #include <assert.h>
44 #include <errno.h>
45 #include <fcntl.h>
46 #include <libcontract.h>
47 #include <libcontract_priv.h>
48 #include <limits.h>
49 #include <port.h>
50 #include <signal.h>
51 #include <stdarg.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <unistd.h>
56 
57 #include "configd_exit.h"
58 #include "protocol.h"
59 #include "startd.h"
60 
61 pid_t
62 startd_fork1(int *forkerr)
63 {
64 	pid_t p;
65 
66 	/*
67 	 * prefork stack
68 	 */
69 	wait_prefork();
70 
71 	p = fork1();
72 
73 	if (p == -1 && forkerr != NULL)
74 		*forkerr = errno;
75 
76 	/*
77 	 * postfork stack
78 	 */
79 	wait_postfork(p);
80 
81 	return (p);
82 }
83 
84 /*
85  * void fork_mount(char *, char *)
86  *   Run mount(1M) with the given options and mount point.  (mount(1M) has much
87  *   hidden knowledge; it's much less correct to reimplement that logic here to
88  *   save a fork(2)/exec(2) invocation.)
89  */
90 int
91 fork_mount(char *path, char *opts)
92 {
93 	pid_t pid;
94 	uint_t tries = 0;
95 	int status;
96 
97 	for (pid = fork1(); pid == -1; pid = fork1()) {
98 		if (++tries > MAX_MOUNT_RETRIES)
99 			return (-1);
100 
101 		(void) sleep(tries);
102 	}
103 
104 	if (pid != 0) {
105 		(void) waitpid(pid, &status, 0);
106 
107 		/*
108 		 * If our mount(1M) invocation exited by peculiar means, or with
109 		 * a non-zero status, our mount likelihood is low.
110 		 */
111 		if (!WIFEXITED(status) ||
112 		    WEXITSTATUS(status) != 0)
113 			return (-1);
114 
115 		return (0);
116 	}
117 
118 	(void) execl("/sbin/mount", "mount", "-o", opts, path, NULL);
119 
120 	return (-1);
121 }
122 
123 /*
124  * pid_t fork_common(...)
125  *   Common routine used by fork_sulogin and fork_configd to fork a
126  *   process in a contract with the provided terms.  Invokes
127  *   fork_sulogin (with its no-fork argument set) on errors.
128  */
129 static pid_t
130 fork_common(const char *name, int retries, ctid_t *ctidp,
131     uint_t inf, uint_t crit, uint_t fatal, uint_t param, uint64_t cookie)
132 {
133 	uint_t tries = 0;
134 	int ctfd, err;
135 	pid_t pid;
136 
137 	/*
138 	 * Establish process contract terms.
139 	 */
140 	if ((ctfd = open64(CTFS_ROOT "/process/template", O_RDWR)) == -1) {
141 		fork_sulogin(B_TRUE, "Could not open process contract template "
142 		    "for %s: %s\n", name, strerror(errno));
143 		/* NOTREACHED */
144 	}
145 
146 	err = ct_tmpl_set_critical(ctfd, crit);
147 	err |= ct_pr_tmpl_set_fatal(ctfd, fatal);
148 	err |= ct_tmpl_set_informative(ctfd, inf);
149 	err |= ct_pr_tmpl_set_param(ctfd, param);
150 	err |= ct_tmpl_set_cookie(ctfd, cookie);
151 	if (err) {
152 		(void) close(ctfd);
153 		fork_sulogin(B_TRUE, "Could not set %s process contract "
154 		    "terms\n", name);
155 		/* NOTREACHED */
156 	}
157 
158 	if (err = ct_tmpl_activate(ctfd)) {
159 		(void) close(ctfd);
160 		fork_sulogin(B_TRUE, "Could not activate %s process contract "
161 		    "template: %s\n", name, strerror(err));
162 		/* NOTREACHED */
163 	}
164 
165 	/*
166 	 * Attempt to fork "retries" times.
167 	 */
168 	for (pid = fork1(); pid == -1; pid = fork1()) {
169 		if (++tries > retries) {
170 			/*
171 			 * When we exit the sulogin session, init(1M)
172 			 * will restart svc.startd(1M).
173 			 */
174 			err = errno;
175 			(void) ct_tmpl_clear(ctfd);
176 			(void) close(ctfd);
177 			fork_sulogin(B_TRUE, "Could not fork to start %s: %s\n",
178 			    name, strerror(err));
179 			/* NOTREACHED */
180 		}
181 		(void) sleep(tries);
182 	}
183 
184 	/*
185 	 * Clean up, return pid and ctid.
186 	 */
187 	if (pid != 0 && (errno = contract_latest(ctidp)) != 0)
188 		uu_die("Could not get new contract id for %s\n", name);
189 	(void) ct_tmpl_clear(ctfd);
190 	(void) close(ctfd);
191 
192 	return (pid);
193 }
194 
195 /*
196  * void fork_sulogin(boolean_t, const char *, ...)
197  *   When we are invoked with the -s flag from boot (or run into an unfixable
198  *   situation), we run a private copy of sulogin.  When the sulogin session
199  *   is ended, we continue.  This is the last fallback action for system
200  *   maintenance.
201  *
202  *   If immediate is true, fork_sulogin() executes sulogin(1M) directly, without
203  *   forking.
204  *
205  *   Because fork_sulogin() is needed potentially before we daemonize, we leave
206  *   it outside the wait_register() framework.
207  */
208 /*PRINTFLIKE2*/
209 void
210 fork_sulogin(boolean_t immediate, const char *format, ...)
211 {
212 	va_list args;
213 	int i, fd_console;
214 
215 	(void) printf("Requesting System Maintenance Mode\n");
216 
217 	if (!booting_to_single_user)
218 		(void) printf("(See /lib/svc/share/README for more "
219 		    "information.)\n");
220 
221 	va_start(args, format);
222 	(void) vprintf(format, args);
223 	va_end(args);
224 
225 	if (!immediate) {
226 		ctid_t	ctid;
227 		pid_t	pid;
228 
229 		pid = fork_common("sulogin", MAX_SULOGIN_RETRIES, &ctid,
230 		    CT_PR_EV_HWERR, 0, CT_PR_EV_HWERR, CT_PR_PGRPONLY,
231 		    SULOGIN_COOKIE);
232 
233 		if (pid != 0) {
234 			(void) waitpid(pid, NULL, 0);
235 			contract_abandon(ctid);
236 			return;
237 		}
238 		/* close all inherited fds */
239 		closefrom(0);
240 	} else {
241 		(void) printf("Directly executing sulogin.\n");
242 		/*
243 		 * Can't call closefrom() in this MT section
244 		 * so safely close a minimum set of fds.
245 		 */
246 		for (i = 0; i < 3; i++)
247 			(void) close(i);
248 	}
249 
250 	(void) setpgrp();
251 
252 	/* open the console for sulogin */
253 	if ((fd_console = open("/dev/console", O_RDWR)) >= 0) {
254 		if (fd_console != STDIN_FILENO)
255 			while (dup2(fd_console, STDIN_FILENO) < 0 &&
256 			    errno == EINTR)
257 				;
258 		if (fd_console != STDOUT_FILENO)
259 			while (dup2(fd_console, STDOUT_FILENO) < 0 &&
260 			    errno == EINTR)
261 				;
262 		if (fd_console != STDERR_FILENO)
263 			while (dup2(fd_console, STDERR_FILENO) < 0 &&
264 			    errno == EINTR)
265 				;
266 		if (fd_console > 2)
267 			(void) close(fd_console);
268 	}
269 
270 	(void) execl("/sbin/sulogin", "sulogin", NULL);
271 
272 	uu_warn("Could not exec() sulogin");
273 
274 	exit(1);
275 }
276 
277 #define	CONFIGD_PATH	"/lib/svc/bin/svc.configd"
278 
279 /*
280  * void fork_configd(int status)
281  *   We are interested in exit events (since the parent's exiting means configd
282  *   is ready to run and since the child's exiting indicates an error case) and
283  *   in empty events.  This means we have a unique template for initiating
284  *   configd.
285  */
286 /*ARGSUSED*/
287 void
288 fork_configd(int exitstatus)
289 {
290 	pid_t pid;
291 	ctid_t ctid = -1;
292 	int err;
293 	char path[PATH_MAX];
294 
295 retry:
296 	log_framework(LOG_DEBUG, "fork_configd trying to start svc.configd\n");
297 
298 	/*
299 	 * If we're retrying, we will have an old contract lying around
300 	 * from the failure.  Since we're going to be creating a new
301 	 * contract shortly, we abandon the old one now.
302 	 */
303 	if (ctid != -1)
304 		contract_abandon(ctid);
305 	ctid = -1;
306 
307 	pid = fork_common("svc.configd", MAX_CONFIGD_RETRIES, &ctid,
308 	    0, CT_PR_EV_EXIT, 0, CT_PR_INHERIT | CT_PR_REGENT, CONFIGD_COOKIE);
309 
310 	if (pid != 0) {
311 		int exitstatus;
312 
313 		st->st_configd_pid = pid;
314 
315 		if (waitpid(pid, &exitstatus, 0) == -1) {
316 			fork_sulogin(B_FALSE, "waitpid on svc.configd "
317 			    "failed: %s\n", strerror(errno));
318 		} else if (WIFEXITED(exitstatus)) {
319 			char *errstr;
320 
321 			/*
322 			 * Examine exitstatus.  This will eventually get more
323 			 * complicated, as we will want to teach startd how to
324 			 * invoke configd with alternate repositories, etc.
325 			 *
326 			 * Note that exec(2) failure results in an exit status
327 			 * of 1, resulting in the default clause below.
328 			 */
329 
330 			/*
331 			 * Assign readable strings to cases we don't handle, or
332 			 * have error outcomes that cannot be eliminated.
333 			 */
334 			switch (WEXITSTATUS(exitstatus)) {
335 			case CONFIGD_EXIT_BAD_ARGS:
336 				errstr = "bad arguments";
337 				break;
338 
339 			case CONFIGD_EXIT_DATABASE_BAD:
340 				errstr = "database corrupt";
341 				break;
342 
343 			case CONFIGD_EXIT_DATABASE_LOCKED:
344 				errstr = "database locked";
345 				break;
346 			case CONFIGD_EXIT_INIT_FAILED:
347 				errstr = "initialization failure";
348 				break;
349 			case CONFIGD_EXIT_DOOR_INIT_FAILED:
350 				errstr = "door initialization failure";
351 				break;
352 			case CONFIGD_EXIT_DATABASE_INIT_FAILED:
353 				errstr = "database initialization failure";
354 				break;
355 			case CONFIGD_EXIT_NO_THREADS:
356 				errstr = "no threads available";
357 				break;
358 			case CONFIGD_EXIT_LOST_MAIN_DOOR:
359 				errstr = "lost door server attachment";
360 				break;
361 			case 1:
362 				errstr = "execution failure";
363 				break;
364 			default:
365 				errstr = "unknown error";
366 				break;
367 			}
368 
369 			/*
370 			 * Remedial actions for various configd failures.
371 			 */
372 			switch (WEXITSTATUS(exitstatus)) {
373 			case CONFIGD_EXIT_OKAY:
374 				break;
375 
376 			case CONFIGD_EXIT_DATABASE_LOCKED:
377 				/* attempt remount of / read-write */
378 				if (fs_is_read_only("/", NULL) == 1) {
379 					if (fs_remount("/") == -1)
380 						fork_sulogin(B_FALSE,
381 						    "remount of root "
382 						    "filesystem failed\n");
383 
384 					goto retry;
385 				}
386 				break;
387 
388 			default:
389 				fork_sulogin(B_FALSE, "svc.configd exited "
390 				    "with status %d (%s)\n",
391 				    WEXITSTATUS(exitstatus), errstr);
392 				goto retry;
393 			}
394 		} else if (WIFSIGNALED(exitstatus)) {
395 			char signame[SIG2STR_MAX];
396 
397 			if (sig2str(WTERMSIG(exitstatus), signame))
398 				(void) snprintf(signame, SIG2STR_MAX,
399 				    "signum %d", WTERMSIG(exitstatus));
400 
401 			fork_sulogin(B_FALSE, "svc.configd signalled:"
402 			    " %s\n", signame);
403 
404 			goto retry;
405 		} else {
406 			fork_sulogin(B_FALSE, "svc.configd non-exit "
407 			    "condition: 0x%x\n", exitstatus);
408 
409 			goto retry;
410 		}
411 
412 		/*
413 		 * Announce that we have a valid svc.configd status.
414 		 */
415 		MUTEX_LOCK(&st->st_configd_live_lock);
416 		st->st_configd_lives = 1;
417 		err = pthread_cond_broadcast(&st->st_configd_live_cv);
418 		assert(err == 0);
419 		MUTEX_UNLOCK(&st->st_configd_live_lock);
420 
421 		log_framework(LOG_DEBUG, "fork_configd broadcasts configd is "
422 		    "live\n");
423 		return;
424 	}
425 
426 	/*
427 	 * Set our per-process core file path to leave core files in
428 	 * /etc/svc/volatile directory, named after the PID to aid in debugging.
429 	 */
430 	(void) snprintf(path, sizeof (path),
431 	    "/etc/svc/volatile/core.configd.%%p");
432 
433 	(void) core_set_process_path(path, strlen(path) + 1, getpid());
434 
435 	log_framework(LOG_DEBUG, "executing svc.configd\n");
436 
437 	(void) execl(CONFIGD_PATH, CONFIGD_PATH, NULL);
438 
439 	/*
440 	 * Status code is used above to identify configd exec failure.
441 	 */
442 	exit(1);
443 }
444 
445 void *
446 fork_configd_thread(void *vctid)
447 {
448 	int fd, err;
449 	ctid_t configd_ctid = (ctid_t)vctid;
450 
451 	if (configd_ctid == -1) {
452 		log_framework(LOG_DEBUG,
453 		    "fork_configd_thread starting svc.configd\n");
454 		fork_configd(0);
455 	} else {
456 		/*
457 		 * configd_ctid is known:  we broadcast and continue.
458 		 * test contract for appropriate state by verifying that
459 		 * there is one or more processes within it?
460 		 */
461 		log_framework(LOG_DEBUG,
462 		    "fork_configd_thread accepting svc.configd with CTID %ld\n",
463 		    configd_ctid);
464 		MUTEX_LOCK(&st->st_configd_live_lock);
465 		st->st_configd_lives = 1;
466 		(void) pthread_cond_broadcast(&st->st_configd_live_cv);
467 		MUTEX_UNLOCK(&st->st_configd_live_lock);
468 	}
469 
470 	fd = open64(CTFS_ROOT "/process/pbundle", O_RDONLY);
471 	if (fd == -1)
472 		uu_die("process bundle open failed");
473 
474 	/*
475 	 * Make sure we get all events (including those generated by configd
476 	 * before this thread was started).
477 	 */
478 	err = ct_event_reset(fd);
479 	assert(err == 0);
480 
481 	for (;;) {
482 		int efd, sfd;
483 		ct_evthdl_t ev;
484 		uint32_t type;
485 		ctevid_t evid;
486 		ct_stathdl_t status;
487 		ctid_t ctid;
488 		uint64_t cookie;
489 		pid_t pid;
490 
491 		if (err = ct_event_read_critical(fd, &ev)) {
492 			assert(err != EINVAL && err != EAGAIN);
493 			log_error(LOG_WARNING,
494 			    "Error reading next contract event: %s",
495 			    strerror(err));
496 			continue;
497 		}
498 
499 		evid = ct_event_get_evid(ev);
500 		ctid = ct_event_get_ctid(ev);
501 		type = ct_event_get_type(ev);
502 
503 		/* Fetch cookie. */
504 		sfd = contract_open(ctid, "process", "status", O_RDONLY);
505 		if (sfd < 0) {
506 			ct_event_free(ev);
507 			continue;
508 		}
509 
510 		if (err = ct_status_read(sfd, CTD_COMMON, &status)) {
511 			log_framework(LOG_WARNING, "Could not get status for "
512 			    "contract %ld: %s\n", ctid, strerror(err));
513 
514 			ct_event_free(ev);
515 			startd_close(sfd);
516 			continue;
517 		}
518 
519 		cookie = ct_status_get_cookie(status);
520 
521 		ct_status_free(status);
522 
523 		startd_close(sfd);
524 
525 		/*
526 		 * Don't process events from contracts we aren't interested in.
527 		 */
528 		if (cookie != CONFIGD_COOKIE) {
529 			ct_event_free(ev);
530 			continue;
531 		}
532 
533 		if (type == CT_PR_EV_EXIT) {
534 			int exitstatus;
535 
536 			(void) ct_pr_event_get_pid(ev, &pid);
537 			(void) ct_pr_event_get_exitstatus(ev,
538 			    &exitstatus);
539 
540 			if (st->st_configd_pid != pid) {
541 				/*
542 				 * This is the child exiting, so we
543 				 * abandon the contract and restart
544 				 * configd.
545 				 */
546 				contract_abandon(ctid);
547 				fork_configd(exitstatus);
548 			}
549 		}
550 
551 		efd = contract_open(ctid, "process", "ctl", O_WRONLY);
552 		if (efd != -1) {
553 			(void) ct_ctl_ack(efd, evid);
554 			startd_close(efd);
555 		}
556 
557 		ct_event_free(ev);
558 
559 	}
560 
561 	/*NOTREACHED*/
562 	return (NULL);
563 }
564 
565 void
566 fork_rc_script(char rl, const char *arg, boolean_t wait)
567 {
568 	pid_t pid;
569 	int tmpl, err, stat;
570 	char path[20] = "/sbin/rc.", log[20] = "rc..log", timebuf[20];
571 	time_t now;
572 	struct tm ltime;
573 	size_t sz;
574 	char *pathenv;
575 	char **nenv;
576 
577 	path[8] = rl;
578 
579 	tmpl = open64(CTFS_ROOT "/process/template", O_RDWR);
580 	if (tmpl >= 0) {
581 		err = ct_tmpl_set_critical(tmpl, 0);
582 		assert(err == 0);
583 
584 		err = ct_tmpl_set_informative(tmpl, 0);
585 		assert(err == 0);
586 
587 		err = ct_pr_tmpl_set_fatal(tmpl, 0);
588 		assert(err == 0);
589 
590 		err = ct_tmpl_activate(tmpl);
591 		assert(err == 0);
592 
593 		err = close(tmpl);
594 		assert(err == 0);
595 	} else {
596 		uu_warn("Could not create contract template for %s.\n", path);
597 	}
598 
599 	pid = startd_fork1(NULL);
600 	if (pid < 0) {
601 		return;
602 	} else if (pid != 0) {
603 		/* parent */
604 		if (wait) {
605 			do
606 				err = waitpid(pid, &stat, 0);
607 			while (err != 0 && errno == EINTR);
608 
609 			if (!WIFEXITED(stat)) {
610 				log_framework(LOG_INFO,
611 				    "%s terminated with waitpid() status %d.\n",
612 				    path, stat);
613 			} else if (WEXITSTATUS(stat) != 0) {
614 				log_framework(LOG_INFO,
615 				    "%s failed with status %d.\n", path,
616 				    WEXITSTATUS(stat));
617 			}
618 		}
619 
620 		return;
621 	}
622 
623 	/* child */
624 
625 	log[2] = rl;
626 
627 	setlog(log);
628 
629 	now = time(NULL);
630 	sz = strftime(timebuf, sizeof (timebuf), "%b %e %T",
631 	    localtime_r(&now, &ltime));
632 	assert(sz != 0);
633 
634 	(void) fprintf(stderr, "%s Executing %s %s\n", timebuf, path, arg);
635 
636 	if (rl == 'S')
637 		pathenv = "PATH=/sbin:/usr/sbin:/usr/bin";
638 	else
639 		pathenv = "PATH=/usr/sbin:/usr/bin";
640 
641 	nenv = set_smf_env(NULL, 0, pathenv, NULL, NULL);
642 
643 	(void) execle(path, path, arg, 0, nenv);
644 
645 	perror("exec");
646 	exit(0);
647 }
648