fork.c revision 76cf44ab96bdcdc3cbfa2f528bc2094df8743fde
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
*
* fork_configd() and fork_sulogin() are related, special cases that handle the
* spawning of specific client processes for svc.startd.
*/
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <libcontract.h>
#include <libcontract_priv.h>
#include <limits.h>
#include <port.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <utmpx.h>
#include "configd_exit.h"
#include "protocol.h"
#include "startd.h"
startd_fork1(int *forkerr)
{
pid_t p;
/*
* prefork stack
*/
wait_prefork();
p = fork1();
/*
* postfork stack
*/
wait_postfork(p);
return (p);
}
/*
* void fork_mount(char *, char *)
* Run mount(1M) with the given options and mount point. (mount(1M) has much
* hidden knowledge; it's much less correct to reimplement that logic here to
* save a fork(2)/exec(2) invocation.)
*/
int
{
int status;
if (++tries > MAX_MOUNT_RETRIES)
return (-1);
}
if (pid != 0) {
/*
* If our mount(1M) invocation exited by peculiar means, or with
* a non-zero status, our mount likelihood is low.
*/
WEXITSTATUS(status) != 0)
return (-1);
return (0);
}
return (-1);
}
/*
* pid_t fork_common(...)
* Common routine used by fork_sulogin and fork_configd to fork a
* process in a contract with the provided terms. Invokes
* fork_sulogin (with its no-fork argument set) on errors.
*/
static pid_t
{
/*
* Establish process contract terms.
*/
/* NOTREACHED */
}
if (err) {
"terms\n", name);
/* NOTREACHED */
}
/* NOTREACHED */
}
/*
* Attempt to fork "retries" times.
*/
/*
* When we exit the sulogin session, init(1M)
* will restart svc.startd(1M).
*/
(void) ct_tmpl_clear(ctfd);
/* NOTREACHED */
}
}
/*
* Clean up, return pid and ctid.
*/
(void) ct_tmpl_clear(ctfd);
return (pid);
}
/*
* void fork_sulogin(boolean_t, const char *, ...)
* When we are invoked with the -s flag from boot (or run into an unfixable
* situation), we run a private copy of sulogin. When the sulogin session
* is ended, we continue. This is the last fallback action for system
* maintenance.
*
* If immediate is true, fork_sulogin() executes sulogin(1M) directly, without
* forking.
*
* Because fork_sulogin() is needed potentially before we daemonize, we leave
* it outside the wait_register() framework.
*/
/*PRINTFLIKE2*/
void
{
int i, fd_console;
(void) printf("Requesting System Maintenance Mode\n");
if (!booting_to_single_user)
"information.)\n");
if (!immediate) {
if (pid != 0) {
return;
}
/* close all inherited fds */
closefrom(0);
} else {
(void) printf("Directly executing sulogin.\n");
/*
* Can't call closefrom() in this MT section
* so safely close a minimum set of fds.
*/
for (i = 0; i < 3; i++)
(void) close(i);
}
(void) setpgrp();
/* open the console for sulogin */
if (fd_console != STDIN_FILENO)
;
if (fd_console != STDOUT_FILENO)
;
if (fd_console != STDERR_FILENO)
;
if (fd_console > 2)
(void) close(fd_console);
}
setutxent();
break;
}
}
}
uu_warn("Could not exec() sulogin");
exit(1);
}
#define CONFIGD_PATH "/lib/svc/bin/svc.configd"
/*
* void fork_configd(int status)
* We are interested in exit events (since the parent's exiting means configd
* is ready to run and since the child's exiting indicates an error case) and
* in empty events. This means we have a unique template for initiating
* configd.
*/
/*ARGSUSED*/
void
fork_configd(int exitstatus)
{
int err;
/*
* If we're retrying, we will have an old contract lying around
* from the failure. Since we're going to be creating a new
* contract shortly, we abandon the old one now.
*/
if (ctid != -1)
ctid = -1;
if (pid != 0) {
int exitstatus;
} else if (WIFEXITED(exitstatus)) {
char *errstr;
/*
* Examine exitstatus. This will eventually get more
* complicated, as we will want to teach startd how to
* invoke configd with alternate repositories, etc.
*
* Note that exec(2) failure results in an exit status
* of 1, resulting in the default clause below.
*/
/*
* Assign readable strings to cases we don't handle, or
* have error outcomes that cannot be eliminated.
*/
switch (WEXITSTATUS(exitstatus)) {
case CONFIGD_EXIT_BAD_ARGS:
errstr = "bad arguments";
break;
errstr = "database corrupt";
break;
errstr = "database locked";
break;
case CONFIGD_EXIT_INIT_FAILED:
errstr = "initialization failure";
break;
errstr = "door initialization failure";
break;
errstr = "database initialization failure";
break;
case CONFIGD_EXIT_NO_THREADS:
errstr = "no threads available";
break;
errstr = "lost door server attachment";
break;
case 1:
errstr = "execution failure";
break;
default:
errstr = "unknown error";
break;
}
/*
* Remedial actions for various configd failures.
*/
switch (WEXITSTATUS(exitstatus)) {
case CONFIGD_EXIT_OKAY:
break;
/* attempt remount of / read-write */
"remount of root "
"filesystem failed\n");
goto retry;
}
break;
default:
"with status %d (%s)\n",
goto retry;
}
} else if (WIFSIGNALED(exitstatus)) {
char signame[SIG2STR_MAX];
" %s\n", signame);
goto retry;
} else {
"condition: 0x%x\n", exitstatus);
goto retry;
}
/*
* Announce that we have a valid svc.configd status.
*/
"live\n");
return;
}
/*
* Set our per-process core file path to leave core files in
*/
"/etc/svc/volatile/core.configd.%%p");
/*
* Status code is used above to identify configd exec failure.
*/
exit(1);
}
void *
fork_configd_thread(void *vctid)
{
if (configd_ctid == -1) {
"fork_configd_thread starting svc.configd\n");
fork_configd(0);
} else {
/*
* configd_ctid is known: we broadcast and continue.
* test contract for appropriate state by verifying that
* there is one or more processes within it?
*/
"fork_configd_thread accepting svc.configd with CTID %ld\n",
}
if (fd == -1)
uu_die("process bundle open failed");
/*
* Make sure we get all events (including those generated by configd
* before this thread was started).
*/
for (;;) {
"Error reading next contract event: %s",
continue;
}
/* Fetch cookie. */
if (sfd < 0) {
continue;
}
continue;
}
/*
* Don't process events from contracts we aren't interested in.
*/
if (cookie != CONFIGD_COOKIE) {
continue;
}
if (type == CT_PR_EV_EXIT) {
int exitstatus;
(void) ct_pr_event_get_exitstatus(ev,
&exitstatus);
/*
* This is the child exiting, so we
* abandon the contract and restart
* configd.
*/
}
}
if (efd != -1) {
}
}
/*NOTREACHED*/
return (NULL);
}
void
{
char *pathenv;
char **nenv;
if (tmpl >= 0) {
} else {
}
if (pid < 0) {
return;
} else if (pid != 0) {
/* parent */
if (wait) {
do
;
"%s terminated with waitpid() status %d.\n",
} else if (WEXITSTATUS(stat) != 0) {
"%s failed with status %d.\n", path,
WEXITSTATUS(stat));
}
}
return;
}
/* child */
if (rl == 'S')
else
perror("exec");
exit(0);
}