process.c revision c5a9a4fc75359f623d03e4eab6a03c9cabe175a3
/*
* 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 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/id_space.h>
#include <sys/sysmacros.h>
#include <sys/contract.h>
#include <sys/contract_impl.h>
/*
* Process Contracts
* -----------------
*
* Generally speaking, a process contract is a contract between a
* process and a set of its descendent processes. In some cases, when
* the child processes outlive the author of the contract, the contract
* may be held by (and therefore be between the child processes and) a
* successor process which adopts the contract after the death of the
* original author.
*
* The process contract adds two new concepts to the Solaris process
* model. The first is that a process contract forms a rigid fault
* boundary around a set of processes. Hardware, software, and even
* administrator errors impacting a process in a process contract
* generate specific events and can be requested to atomically shutdown
* all processes in the contract. The second is that a process
* contract is a process collective whose leader is not a member of the
* collective. This means that the leader can reliably react to events
* in the collective, and may also act upon the collective without
* special casing itself.
*
* A composite outcome of these two concepts is that we can now create
* a tree of process contracts, rooted at init(1M), which represent
* services and subservices that are reliably observed and can be
* restarted when fatal errors occur. The service management framework
* (SMF) realizes this structure.
*
* For more details, see the "restart agreements" case, PSARC 2003/193.
*
* There are four sets of routines in this file: the process contract
* standard template operations, the process contract standard contract
* operations, a couple routines used only by the contract subsystem to
* handle process contracts' unique role as a temporary holder of
* abandoned contracts, and the interfaces which allow the system to
* create and act upon process contracts. The first two are defined by
* the contracts framework and won't be discussed further. As for the
* remaining two:
*
* Special framework interfaces
* ----------------------------
*
* contract_process_accept - determines if a process contract is a
* regent, i.e. if it can inherit other contracts.
*
* contract_process_take - tells a regent process contract to inherit
* an abandoned contract
*
* contract_process_adopt - tells a regent process contract that a
* contract it has inherited is being adopted by a process.
*
* Process contract interfaces
* ---------------------------
*
* contract_process_fork - called when a process is created; adds the
* new process to an existing contract or to a newly created one.
*
* contract_process_exit - called when a process exits
*
* contract_process_core - called when a process would have dumped core
* (even if a core file wasn't generated)
*
* contract_process_hwerr - called when a process was killed because of
* an uncorrectable hardware error
*
* contract_process_sig - called when a process was killed by a fatal
* signal sent by a process in another process contract
*
*/
/*
* Macro predicates for determining when events should be sent and how.
*/
/*
* Process contract template implementation
*/
/*
* ctmpl_process_dup
*
* The process contract template dup entry point. Other than the
* to-be-subsumed contract, which must be held, this simply copies all
* the fields of the original.
*/
static struct ct_template *
{
if (new->ctp_subsume)
}
}
}
/*
* ctmpl_process_free
*
* The process contract template free entry point. Just releases a
* to-be-subsumed contract and frees the template.
*/
static void
{
if (ctp->ctp_subsume)
}
}
}
/*
* SAFE_EV is the set of events which a non-privileged process is
* allowed to make critical but not fatal or if the PGRPONLY parameter
* is set. EXCESS tells us if "value", a critical event set, requires
* additional privilege given the template "ctp".
*/
#define SAFE_EV (CT_PR_EV_EMPTY)
/*
* ctmpl_process_set
*
* The process contract template set entry point. None of the terms
* may be unconditionally set, and setting the parameters or fatal
* event set may result in events being implicitly removed from to the
* critical event set and added to the informative event set. The
* (admittedly subtle) reason we implicitly change the critical event
* set when the parameter or fatal event set is modified but not the
* other way around is because a change to the critical event set only
* affects the contract's owner, whereas a change to the parameter set
* and fatal set can affect the execution of the application running in
* the contract (and should therefore be only made explicitly). We
* allow implicit changes at all so that setting contract terms doesn't
* become a complex dance dependent on the template's initial state and
* the desired terms.
*/
static int
{
contract_t *ct;
int error;
char *str_value;
} else {
return (EINVAL);
/*
* No process contract parameters are > 32 bits.
* Unless it is a string.
*/
if (param_value & ~UINT32_MAX)
return (EINVAL);
}
case CTPP_SUBSUME:
if (param_value != 0) {
/*
* Ensure that the contract exists, that we
* hold the contract, and that the contract is
* empty.
*/
return (ESRCH);
return (EACCES);
}
return (ENOTEMPTY);
}
} else {
}
if (ctp->ctp_subsume)
break;
case CTPP_PARAMS:
if (param_value & ~CT_PR_ALLPARAM)
return (EINVAL);
/*
* If an unprivileged process requests that
* CT_PR_PGRPONLY be set, remove any unsafe events from
* the critical event set and add them to the
* informative event set.
*/
}
break;
case CTPP_SVC_FMRI:
return (error);
else
ctp->ctp_svc_fmri =
break;
case CTPP_CREATOR_AUX:
else
ctp->ctp_svc_aux =
break;
case CTP_EV_CRITICAL:
/*
* We simply don't allow adding events to the critical
* event set which aren't permitted by our policy or by
* privilege.
*/
return (error);
break;
case CTPP_EV_FATAL:
if (param_value & ~CT_PR_ALLFATAL)
return (EINVAL);
/*
* Check to see if an unprivileged process is
* requesting that events be removed from the fatal
* event set which are still in the critical event set.
*/
int allowed =
0 : ctp->ctp_ev_fatal;
}
break;
default:
return (EINVAL);
}
return (0);
}
/*
* ctmpl_process_get
*
* The process contract template get entry point. Simply fetches and
* returns the requested term.
*/
static int
{
return (EINVAL);
}
case CTPP_SUBSUME:
break;
case CTPP_PARAMS:
break;
case CTPP_SVC_FMRI:
} else {
}
break;
case CTPP_CREATOR_AUX:
} else {
}
break;
case CTPP_EV_FATAL:
break;
default:
return (EINVAL);
}
return (0);
}
static ctmplops_t ctmpl_process_ops = {
ctmpl_process_dup, /* ctop_dup */
ctmpl_process_free, /* ctop_free */
ctmpl_process_set, /* ctop_set */
ctmpl_process_get, /* ctop_get */
ctmpl_create_inval, /* ctop_create */
};
/*
* Process contract implementation
*/
/*
* ctmpl_process_default
*
* The process contract default template entry point. Creates a
* process contract template with no parameters set, with informative
* core and signal events, critical empty and hwerr events, and fatal
* hwerr events.
*/
static ct_template_t *
contract_process_default(void)
{
new->ctp_params = 0;
}
/*
* contract_process_free
*
* The process contract free entry point.
*/
static void
{
}
}
}
}
/*
* contract_process_cankill
*
* Determine if the contract author had or if the process generating
* the event, sp, has adequate privileges to kill process tp.
*/
static int
{
int cankill;
return (1);
return (0);
}
/*
* contract_process_kill
*
* Kills all processes in a contract, or all processes in the
* intersection of a contract and ex's process group (if ex is non-NULL
* and the contract's PGRPONLY parameter is set). If checkpriv is
* true, only those processes which may be signaled by the contract
* author or ex are killed.
*/
static void
{
proc_t *p;
}
continue;
}
if (pgrp != -1)
}
/*
* contract_process_accept
*
* Tests if the process contract is willing to act as a regent for
* inherited contracts. Though brief and only called from one place,
* this functionality is kept here to avoid including knowledge of
* process contract implementation in the generic contract code.
*/
int
{
}
/*
* contract_process_take
*
* Executes the process contract side of inheriting a contract.
*/
void
{
ctp->conp_ninherited++;
}
/*
* contract_process_adopt
*
* Executes the process contract side of adopting a contract.
*/
void
{
/*
* We drop the parent lock first because a) we are passing the
* contract reference to the child, and b) contract_adopt
* expects us to return with the contract lock held.
*/
}
/*
* contract_process_abandon
*
* The process contract abandon entry point.
*/
static void
{
/*
* Shall we stay or shall we go?
*/
} else {
/*
* Strictly speaking, we actually do orphan the contract.
* Assuming our credentials allow us to kill all
* processes in the contract, this is only temporary.
*/
}
}
/*
* contract_process_destroy
*
* The process contract destroy entry point.
*/
static void
{
/*
* contract_destroy all empty children, kill or orphan the rest
*/
ctp->conp_ninherited--;
}
}
/*
* contract_process_status
*
* The process contract status entry point.
*/
static void
{
} else {
for (;;) {
KM_SLEEP);
break;
}
}
/*
* Contract terms are static; there's no need to hold the
* contract lock while accessing them.
*/
npids) == 0);
nctids) == 0);
}
/*
* if we are in a local zone and svc_fmri was inherited from
* the global zone, we provide fake svc_fmri and svc_ctid
*/
if (local_svc_zone_enter == 0||
if (detail > CTD_COMMON) {
ctp->conp_svc_ctid) == 0);
}
}
} else {
if (detail > CTD_COMMON) {
local_svc_zone_enter) == 0);
}
CT_PR_SVC_FMRI_ZONE_ENTER) == 0);
}
}
}
/*ARGSUSED*/
static int
{
return (0);
}
/* process contracts don't negotiate */
static contops_t contract_process_ops = {
contract_process_free, /* contop_free */
contract_process_abandon, /* contop_abandon */
contract_process_destroy, /* contop_destroy */
contract_process_status, /* contop_status */
contract_ack_inval, /* contop_ack */
contract_ack_inval, /* contop_nack */
contract_qack_inval, /* contop_qack */
contract_process_newct /* contop_newct */
};
/*
* contract_process_init
*
* Initializes the process contract type. Also creates a template for
* use by newproc() when it creates user processes.
*/
void
contract_process_init(void)
{
/*
* Create a template for use with init(1M) and other
* kernel-started processes.
*/
refstr_alloc("svc:/system/init:default");
}
/*
* contract_process_create
*
* create a process contract given template "tmpl" and parent process
* "parent". May fail and return NULL if project.max-contracts would
* have been exceeded.
*/
static cont_process_t *
{
return (NULL);
}
/*
* inherit svc_fmri if not defined by consumer. In this case, inherit
* also svc_ctid to keep track of the contract id where
* svc_fmri was set
*/
} else {
/* make svc_zone_enter flag false when svc_fmri is set */
ctp->conp_svc_zone_enter = 0;
}
/* set svc_aux to default value if not defined in template */
} else {
}
/*
* set svc_creator to execname
* We special case pid0 because when newproc() creates
* the init process, the p_user.u_comm field of sched's proc_t
* has not been populated yet.
*/
else
/*
* Transfer subcontracts only after new contract is visible.
* Also, only transfer contracts if the parent matches -- we
* don't want to create a cycle in the tree of contracts.
*/
contract_t *ct;
}
sct->conp_ninherited = 0;
/*
* Automatically abandon the contract.
*/
}
return (ctp);
}
/*
* contract_process_exit
*
* Called on process exit. Removes process p from process contract
* ctp. Generates an exit event, if requested. Generates an empty
* event, if p is the last member of the the process contract and empty
* events were requested.
*/
void
{
int empty;
/*
* Remove self from process contract.
*/
ctp->conp_nmembers--;
p->p_ct_process = NULL;
mutex_exit(&p->p_lock);
/*
* We check for emptiness before dropping the contract lock to
* send the exit event, otherwise we could end up with two
* empty events.
*/
}
if (empty) {
/*
* Send EMPTY message.
*/
KM_SLEEP) == 0);
CTE_INFO : 0;
}
/*
* The last one to leave an orphaned contract turns out
* the lights.
*/
return;
}
}
}
/*
* contract_process_fork
*
* Called on process fork. If the current lwp has a active process
* contract template, we attempt to create a new process contract.
* Failure to create a process contract when required is a failure in
* fork so, in such an event, we return NULL.
*
* Assuming we succeeded or skipped the previous step, we add the child
* process to the new contract (success) or to the parent's process
* contract (skip). If requested, we also send a fork event to that
* contract.
*
* Because contract_process_fork() may fail, and because we would
* prefer that process contracts not be created for processes which
* don't complete forking, this should be the last function called
* before the "all clear" point in cfork.
*/
int canfail)
{
contract_t *ct;
return (NULL);
/*
* Prevent contract_process_kill() from missing forked children
* by failing forks by parents that have just been killed.
* It's not worth hoisting the ctp test since contract creation
* is by no means the common case.
*/
canfail) {
return (NULL);
}
ctp->conp_nmembers++;
}
return (ctp);
}
/*
* contract_process_core
*
* Called on core file generation attempts. Generates a core event, if
* requested, containing the names of the process, global, and
* system-global ("zone") core files. If dumping core is in the fatal
* event set, calls contract_process_kill().
*/
void
{
if (process)
(char *)process) == 0);
if (global)
(char *)global) == 0);
if (zone) {
/*
* Only the global zone is informed of the
* local-zone generated global-zone core.
*/
KM_SLEEP) == 0);
(char *)zone) == 0);
}
}
}
}
/*
* contract_process_hwerr
*
* Called when a process is killed by an unrecoverable hardware error.
* Generates an hwerr event, if requested. If hardware errors are in
* the fatal event set, calls contract_process_kill().
*/
void
{
}
}
}
/*
* contract_process_sig
*
* Called when a process is killed by a signal originating from a
* process outside of its process contract or its process contract's
* holder. Generates an signal event, if requested, containing the
* signal number, and the sender's pid and contract id (if available).
* If signals are in the fatal event set, calls
* contract_process_kill().
*/
void
{
KM_SLEEP) == 0);
} else {
}
if (pid != -1)
if (ctid != 0)
}
}
}