librestart.c revision 2a3221a4fea0ec9c322f85a262eab79e762d32f2
/*
* 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"
#include <librestart.h>
#include <librestart_priv.h>
#include <libscf.h>
#include <libscf_priv.h>
#include <assert.h>
#include <ctype.h>
#include <dlfcn.h>
#include <errno.h>
#include <exec_attr.h>
#include <grp.h>
#include <libsysevent.h>
#include <libuutil.h>
#include <limits.h>
#include <link.h>
#include <malloc.h>
#include <pool.h>
#include <priv.h>
#include <project.h>
#include <pthread.h>
#include <pwd.h>
#include <secdb.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <time.h>
#include <unistd.h>
#define walkcontext _walkcontext
#include <ucontext.h>
#define min(a, b) ((a) > (b) ? (b) : (a))
#define MKW_TRUE ":true"
#define MKW_KILL ":kill"
#define MKW_KILL_PROC ":kill_process"
#define ALLOCFAIL ((char *)"Allocation failure.")
#define RCBROKEN ((char *)"Repository connection broken.")
#define MAX_COMMIT_RETRIES 20
/*
* bad_fail() catches bugs in this and lower layers by reporting supposedly
* impossible function failures. The NDEBUG case keeps the strings out of the
* library but still calls abort() so we can root-cause from the coredump.
*/
#ifndef NDEBUG
"At %s:%d, %s() failed with unexpected error %d. Aborting.\n", \
abort(); \
}
#else
#endif
struct restarter_event_handle {
char *reh_restarter_name;
char *reh_delegate_channel_name;
char *reh_delegate_subscriber_id;
char *reh_master_channel_name;
char *reh_master_subscriber_id;
int (*reh_handler)(restarter_event_t *);
};
struct restarter_event {
char *re_instance_name;
};
static const char * const allocfail = "Allocation failure.\n";
static const char * const rcbroken = "Repository connection broken.\n";
static int method_context_safety = 0; /* Can safely call pools/projects. */
int ndebug = 1;
static void
{
if (h == NULL)
return;
/*
* Just free the memory -- don't unbind the sysevent handle,
* as otherwise events may be lost if this is just a restarter
* restart.
*/
if (h->reh_restarter_name != NULL)
free(h->reh_restarter_name);
if (h->reh_delegate_channel_name != NULL)
if (h->reh_delegate_subscriber_id != NULL)
if (h->reh_master_channel_name != NULL)
if (h->reh_master_subscriber_id != NULL)
free(h);
}
static const char *
{
char *last_part;
last_part++;
return (last_part);
}
char *
{
const char *name;
char prefix_name[3];
return (NULL);
if (type == RESTARTER_CHANNEL_DELEGATE)
else if (type == RESTARTER_CHANNEL_MASTER)
else {
return (NULL);
}
/*
* Should check for [a-z],[A-Z],[0-9],.,_,-,:
*/
return (NULL);
}
return (chan_name);
}
int
{
int ret = 0;
e = uu_zalloc(sizeof (restarter_event_t));
if (e == NULL)
e->re_event_handle = h;
e->re_sysevent = syse;
&(e->re_type)) != 0) ||
RESTARTER_NAME_INSTANCE, &(e->re_instance_name)) != 0)) {
uu_warn("%s: Can't decode nvlist for event %p\n",
h->reh_restarter_name, (void *)syse);
ret = 0;
} else {
ret = h->reh_handler(e);
}
uu_free(e);
return (ret);
}
/*
* restarter_bind_handle(uint32_t, char *, int (*)(restarter_event_t *), int,
* restarter_event_handle_t **)
*
* Bind to a delegated restarter event channel.
* Each delegated restarter gets its own channel for resource management.
*
* Returns 0 on success or
* ENOTSUP version mismatch
* EINVAL restarter_name or event_handle is NULL
* ENOMEM out of memory, too many channels, or too many subscriptions
* EBUSY sysevent_evc_bind() could not establish binding
* EFAULT internal sysevent_evc_bind()/sysevent_evc_subscribe() error
* EMFILE out of file descriptors
* EPERM insufficient privilege for sysevent_evc_bind()
* EEXIST already subscribed
*/
int
{
int err;
if (version != RESTARTER_EVENT_VERSION)
return (ENOTSUP);
return (EINVAL);
if (flags & RESTARTER_FLAG_DEBUG)
ndebug++;
return (ENOMEM);
if (h->reh_delegate_subscriber_id == NULL ||
h->reh_master_subscriber_id == NULL ||
h->reh_restarter_name == NULL) {
return (ENOMEM);
}
if (h->reh_delegate_channel_name == NULL ||
h->reh_master_channel_name == NULL) {
return (ENOMEM);
}
return (err);
}
return (err);
}
h->reh_handler = event_handler;
return (err);
}
*rehp = h;
return (0);
}
{
return (e->re_event_handle);
}
{
return (e->re_type);
}
{
}
int
{
if (e == NULL)
return (-1);
*next_state = e->re_next_state;
return (0);
}
/*
* Commit the state, next state, and auxiliary state into the repository.
* Let the graph engine know about the state change and error. On success,
* return 0. On error, return
* EINVAL - aux has spaces
* - inst is invalid or not an instance FMRI
* EPROTO - librestart compiled against different libscf
* ENOMEM - out of memory
* - repository server out of resources
* ENOTACTIVE - repository server not running
* ECONNABORTED - repository connection established, but then broken
* - unknown libscf error
* ENOENT - inst does not exist in the repository
* EPERM - insufficient permissions
* EACCESS - backend access denied
* EROFS - backend is readonly
* EFAULT - internal sysevent_evc_publish() error
* EBADF - h is invalid (sysevent_evc_publish() returned EINVAL)
*/
int
const char *aux)
{
int retries;
int ret = 0;
char *p = (char *)aux;
/* Validate format of auxiliary state: no spaces allowed */
if (p != NULL) {
while (*p != '\0') {
if (isspace(*p))
return (EINVAL);
p++;
}
}
switch (scf_error()) {
return (EPROTO);
case SCF_ERROR_NO_MEMORY:
return (ENOMEM);
default:
}
}
switch (scf_error()) {
case SCF_ERROR_NO_SERVER:
return (ENOTACTIVE);
case SCF_ERROR_NO_RESOURCES:
return (ENOMEM);
case SCF_ERROR_IN_USE:
default:
}
}
!= 0 ||
goto errout;
}
if (ret != 0)
goto errout;
if (ret == 0)
break;
switch (ret) {
case EAGAIN:
/* Queue is full */
break;
case EFAULT:
case ENOMEM:
goto errout;
case EINVAL:
goto errout;
case EOVERFLOW:
default:
}
}
(void) scf_handle_unbind(scf_h);
return (ret);
}
restarter_string_to_state(char *string)
{
return (RESTARTER_STATE_NONE);
return (RESTARTER_STATE_UNINIT);
return (RESTARTER_STATE_MAINT);
return (RESTARTER_STATE_OFFLINE);
return (RESTARTER_STATE_DISABLED);
return (RESTARTER_STATE_ONLINE);
return (RESTARTER_STATE_DEGRADED);
else {
return (RESTARTER_STATE_NONE);
}
}
{
if (state == RESTARTER_STATE_NONE)
else if (state == RESTARTER_STATE_UNINIT)
else if (state == RESTARTER_STATE_MAINT)
else if (state == RESTARTER_STATE_OFFLINE)
len));
else if (state == RESTARTER_STATE_DISABLED)
len));
else if (state == RESTARTER_STATE_ONLINE)
else if (state == RESTARTER_STATE_DEGRADED)
len));
else
}
/*
* Sets pg to the name property group of s_inst. If it doesn't exist, it is
* added.
*
* Fails with
* ECONNABORTED - repository disconnection or unknown libscf error
* EBADF - inst is not set
* ECANCELED - inst is deleted
* EPERM - permission is denied
* EACCES - backend denied access
* EROFS - backend readonly
*/
static int
{
return (0);
switch (scf_error()) {
default:
return (ECONNABORTED);
case SCF_ERROR_NOT_SET:
return (EBADF);
case SCF_ERROR_DELETED:
return (ECANCELED);
case SCF_ERROR_NOT_FOUND:
break;
}
return (0);
switch (scf_error()) {
default:
return (ECONNABORTED);
case SCF_ERROR_DELETED:
return (ECANCELED);
case SCF_ERROR_EXISTS:
goto again;
return (EPERM);
case SCF_ERROR_BACKEND_ACCESS:
return (EACCES);
return (EROFS);
case SCF_ERROR_NOT_SET: /* should be caught above */
}
return (0);
}
/*
* Fails with
* ECONNABORTED
* ECANCELED - pg was deleted
*/
static int
{
int r;
for (;;) {
ty) == 0)
break;
switch (scf_error()) {
default:
return (ECONNABORTED);
case SCF_ERROR_DELETED:
return (ECANCELED);
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_IN_USE:
case SCF_ERROR_NOT_SET:
bad_fail("scf_transaction_property_change_type",
scf_error());
}
break;
switch (scf_error()) {
default:
return (ECONNABORTED);
case SCF_ERROR_DELETED:
return (ECANCELED);
case SCF_ERROR_EXISTS:
break;
case SCF_ERROR_IN_USE:
case SCF_ERROR_NOT_SET:
}
}
assert(r == 0);
return (0);
}
/*
* Commit new_state, new_next_state, and aux to the repository for id. If
* successful, also set id's state and next-state as given, and return 0.
* Fails with
* ENOMEM - out of memory
* ECONNABORTED - repository connection broken
* - unknown libscf error
* EINVAL - id->i_fmri is invalid or not an instance FMRI
* ENOENT - id->i_fmri does not exist
* EPERM - insufficient permissions
* EACCES - backend access denied
* EROFS - backend is readonly
*/
int
{
char str_state[MAX_SCF_STATE_STRING_SZ];
int ret = 0, r;
char *default_aux = "none";
scf_transaction_t *t = NULL;
/* If aux state is unset, set aux to a default string. */
aux = default_aux;
(t = scf_transaction_create(h)) == NULL ||
goto out;
}
sizeof (str_new_state));
sizeof (str_new_state_next));
sizeof (str_state));
sizeof (str_state_next));
switch (scf_error()) {
default:
ret = ECONNABORTED;
break;
break;
case SCF_ERROR_NOT_FOUND:
break;
}
goto out;
}
case 0:
break;
case ECONNABORTED:
case EPERM:
case EACCES:
case EROFS:
ret = r;
goto out;
case ECANCELED:
goto out;
case EBADF:
default:
bad_fail("instance_get_or_add_pg", r);
}
for (;;) {
if (scf_transaction_start(t, pg) != 0) {
switch (scf_error()) {
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_NOT_SET:
goto add_pg;
goto out;
case SCF_ERROR_BACKEND_ACCESS:
goto out;
goto out;
case SCF_ERROR_IN_USE:
}
}
SCF_TYPE_ASTRING, v_state)) != 0 ||
SCF_TYPE_ASTRING, v_state_next)) != 0 ||
SCF_TYPE_ASTRING, v_aux)) != 0 ||
SCF_TYPE_TIME, v_stime)) != 0) {
switch (r) {
case ECONNABORTED:
ret = ECONNABORTED;
goto out;
case ECANCELED:
goto add_pg;
default:
bad_fail("tx_set_value", r);
}
}
ret = scf_transaction_commit(t);
if (ret == 1)
break;
if (ret == -1) {
switch (scf_error()) {
default:
ret = ECONNABORTED;
goto out;
goto out;
case SCF_ERROR_BACKEND_ACCESS:
goto out;
goto out;
case SCF_ERROR_NOT_SET:
}
}
switch (scf_error()) {
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_NOT_SET:
goto add_pg;
}
}
}
ret = 0;
out:
return (ret);
}
/*
* Fails with
* EINVAL - type is invalid
* ENOMEM
* ECONNABORTED - repository connection broken
* EBADF - s_inst is not set
* ECANCELED - s_inst is deleted
* EPERM - permission denied
* EACCES - backend access denied
* EROFS - backend readonly
*/
int
{
scf_handle_t *h;
scf_transaction_t *t = NULL;
const char *pname;
uint64_t c;
switch (type) {
primary = 1;
break;
primary = 0;
break;
default:
return (EINVAL);
}
h = scf_instance_handle(s_inst);
pg = scf_pg_create(h);
prop = scf_property_create(h);
iter = scf_iter_create(h);
t = scf_transaction_create(h);
goto remove_contract_cleanup;
}
add:
if (ret != 0)
goto remove_contract_cleanup;
for (;;) {
if (scf_transaction_start(t, pg) != 0) {
switch (scf_error()) {
default:
ret = ECONNABORTED;
goto remove_contract_cleanup;
case SCF_ERROR_DELETED:
goto add;
goto remove_contract_cleanup;
case SCF_ERROR_BACKEND_ACCESS:
goto remove_contract_cleanup;
goto remove_contract_cleanup;
case SCF_ERROR_IN_USE:
case SCF_ERROR_NOT_SET:
}
}
t_cid = scf_entry_create(h);
pname, SCF_TYPE_COUNT) != 0) {
switch (scf_error()) {
default:
ret = ECONNABORTED;
goto remove_contract_cleanup;
case SCF_ERROR_DELETED:
goto add;
case SCF_ERROR_NOT_FOUND:
goto new;
case SCF_ERROR_IN_USE:
case SCF_ERROR_NOT_SET:
"scf_transaction_property_changetype",
scf_error());
}
}
switch (scf_error()) {
default:
ret = ECONNABORTED;
goto remove_contract_cleanup;
case SCF_ERROR_NOT_SET:
"scf_iter_property_values",
scf_error());
}
}
val = scf_value_create(h);
goto remove_contract_cleanup;
}
if (ret == -1) {
switch (scf_error()) {
ret = ECONNABORTED;
goto remove_contract_cleanup;
case SCF_ERROR_DELETED:
goto add;
default:
bad_fail("scf_iter_next_value",
scf_error());
}
}
if (ret == 1) {
if (c != contract_id) {
val);
} else {
}
goto next_val;
}
} else {
switch (scf_error()) {
default:
ret = ECONNABORTED;
goto remove_contract_cleanup;
case SCF_ERROR_TYPE_MISMATCH:
break;
case SCF_ERROR_NOT_SET:
bad_fail("scf_property_is_type",
scf_error());
}
}
} else {
switch (scf_error()) {
default:
ret = ECONNABORTED;
goto remove_contract_cleanup;
case SCF_ERROR_DELETED:
goto add;
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_NOT_SET:
}
new:
SCF_TYPE_COUNT) != 0) {
switch (scf_error()) {
default:
ret = ECONNABORTED;
goto remove_contract_cleanup;
case SCF_ERROR_DELETED:
goto add;
case SCF_ERROR_EXISTS:
goto replace;
case SCF_ERROR_NOT_SET:
bad_fail("scf_transaction_property_new",
scf_error());
}
}
}
ret = scf_transaction_commit(t);
if (ret == -1) {
switch (scf_error()) {
default:
ret = ECONNABORTED;
goto remove_contract_cleanup;
case SCF_ERROR_DELETED:
goto add;
goto remove_contract_cleanup;
case SCF_ERROR_BACKEND_ACCESS:
goto remove_contract_cleanup;
goto remove_contract_cleanup;
case SCF_ERROR_NOT_SET:
}
}
if (ret == 1) {
ret = 0;
break;
}
switch (scf_error()) {
default:
ret = ECONNABORTED;
goto remove_contract_cleanup;
case SCF_ERROR_DELETED:
goto add;
case SCF_ERROR_NOT_SET:
}
}
}
return (ret);
}
/*
* Fails with
* EINVAL - type is invalid
* ENOMEM
* ECONNABORTED - repository disconnection
* EBADF - s_inst is not set
* ECANCELED - s_inst is deleted
* EPERM
* EACCES
* EROFS
*/
int
{
scf_handle_t *h;
scf_transaction_t *t = NULL;
const char *pname;
if (type == RESTARTER_CONTRACT_PRIMARY)
primary = 1;
else if (type == RESTARTER_CONTRACT_TRANSIENT)
primary = 0;
else
return (EINVAL);
h = scf_instance_handle(s_inst);
pg = scf_pg_create(h);
prop = scf_property_create(h);
iter = scf_iter_create(h);
t = scf_transaction_create(h);
goto out;
}
add:
if (ret != 0)
goto out;
for (;;) {
if (scf_transaction_start(t, pg) != 0) {
switch (scf_error()) {
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
goto add;
goto out;
case SCF_ERROR_BACKEND_ACCESS:
goto out;
goto out;
case SCF_ERROR_IN_USE:
case SCF_ERROR_NOT_SET:
}
}
t_cid = scf_entry_create(h);
goto out;
}
pname, SCF_TYPE_COUNT) != 0) {
switch (scf_error()) {
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
goto add;
case SCF_ERROR_NOT_FOUND:
goto new;
case SCF_ERROR_IN_USE:
case SCF_ERROR_NOT_SET:
"scf_transaction_propert_change_type",
scf_error());
}
}
switch (scf_error()) {
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_NOT_SET:
"scf_iter_property_values",
scf_error());
}
}
val = scf_value_create(h);
goto out;
}
if (ret == -1) {
switch (scf_error()) {
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
goto add;
"scf_iter_next_value",
scf_error());
}
}
if (ret == 1) {
goto next_val;
}
} else {
switch (scf_error()) {
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_TYPE_MISMATCH:
break;
case SCF_ERROR_NOT_SET:
bad_fail("scf_property_is_type",
scf_error());
}
}
} else {
switch (scf_error()) {
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
goto add;
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_NOT_SET:
}
new:
SCF_TYPE_COUNT) != 0) {
switch (scf_error()) {
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
goto add;
case SCF_ERROR_EXISTS:
goto replace;
case SCF_ERROR_NOT_SET:
bad_fail("scf_transaction_property_new",
scf_error());
}
}
}
val = scf_value_create(h);
goto out;
}
ret = scf_transaction_commit(t);
if (ret == -1) {
switch (scf_error()) {
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
goto add;
goto out;
case SCF_ERROR_BACKEND_ACCESS:
goto out;
goto out;
case SCF_ERROR_NOT_SET:
}
}
if (ret == 1) {
ret = 0;
break;
}
switch (scf_error()) {
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
goto add;
case SCF_ERROR_NOT_SET:
}
}
}
out:
return (ret);
}
int
{
void *libhndl;
return (1);
return (0);
return (0);
return (1);
}
static int
{
if (scf_error() == SCF_ERROR_CONNECTION_BROKEN)
return (-1);
}
if (scf_error() == SCF_ERROR_CONNECTION_BROKEN)
return (-1);
}
return (szret >= 0 ? 0 : -1);
}
/*
* Try to load mcp->pwd, if it isn't already.
* Fails with
* ENOMEM - malloc() failed
* ENOENT - no entry found
* EIO - I/O error
* EMFILE - process out of file descriptors
* ENFILE - system out of file handles
*/
static int
{
return (0);
return (ENOMEM);
}
do {
errno = 0;
return (0);
switch (errno) {
case 0:
default:
/*
* Until bug 5065780 is fixed, getpwuid_r() can fail with
* ENOENT, particularly on the miniroot. Since the
* documentation is inaccurate, we'll return ENOENT for unknown
* errors.
*/
return (ENOENT);
case EIO:
case EMFILE:
case ENFILE:
return (errno);
case ERANGE:
/* NOTREACHED */
}
}
/*
* Get the user id for str. Returns 0 on success or
* ERANGE the uid is too big
* EINVAL the string starts with a digit, but is not a valid uid
* ENOMEM out of memory
* ENOENT no passwd entry for str
* EIO an I/O error has occurred
*/
int
{
char *cp;
errno = 0;
return (errno);
}
return (EINVAL);
return (EINVAL);
return (0);
} else {
return (ENOMEM);
}
do {
errno = 0;
pwdp =
return (0);
} else {
switch (errno) {
case 0:
return (ENOENT);
case ENOENT:
case EIO:
case EMFILE:
case ENFILE:
return (errno);
case ERANGE:
default:
/* NOTREACHED */
}
}
}
}
{
char *cp;
errno = 0;
return (-1);
return (-1);
return (gid);
} else {
char *buffer;
errno = 0;
}
}
/*
* Fails with
* ENOMEM - out of memory
* ENOENT - no passwd entry
* no project entry
* EIO - an I/O error occurred
* EMFILE - the process is out of file descriptors
* ENFILE - the system is out of file handles
* ERANGE - the project id is out of range
* EINVAL - str is invalid
* E2BIG - the project entry was too big
* -1 - the name service switch is misconfigured
*/
int
{
int ret;
void *buf;
/* Don't change project for root services */
return (0);
}
case 0:
break;
case ENOMEM:
case ENOENT:
case EIO:
case EMFILE:
case ENFILE:
return (ret);
default:
}
return (ENOMEM);
do {
errno = 0;
bufsz);
/* to be continued ... */
} else {
char *cp;
}
errno = 0;
return (errno);
}
return (EINVAL);
return (ERANGE);
return (ENOMEM);
do {
errno = 0;
}
if (pp) {
}
switch (errno) {
case 0:
return (ENOENT);
case EIO:
case EMFILE:
case ENFILE:
return (errno);
case ERANGE:
return (E2BIG);
default:
return (-1);
}
}
/*
* Parse the supp_groups property value and populate ci->groups. Returns
* EINVAL (get_gid() failed for one of the components), E2BIG (the property has
* more than NGROUPS_MAX-1 groups), or 0 on success.
*/
int
{
uint_t i;
const char * const whitespace = " \t";
const char * const illegal = ", \t";
if (str[0] == '\0') {
return (0);
}
/* skip whitespace */
/* find the end */
/* skip whitespace after end */
/* if there's a comma, it separates the fields */
if (*next == ',')
++next;
*end = '\0';
return (EINVAL);
}
++i;
if (i > NGROUPS_MAX - 1) {
return (E2BIG);
}
}
return (0);
}
/*
* Eventually, we will return a structured error in the case of
* retryable or abortable failures such as memory allocation errors and
* repository connection failures. For now, these failures are just
* encoded in the failure string.
*/
static const char *
{
const char *cmdp;
0)
return ("Could not get profile property.");
/* Extract the command from the command line. */
} else {
}
/* Require that cmdp[0] == '/'? */
return ("Could not find profile.");
/* Based on pfexec.c */
/* Get the euid first so we don't override ci->pwd for the uid. */
errstr = "Could not interpret profile euid.";
goto out;
}
}
errstr = "Could not interpret profile uid.";
goto out;
}
}
errstr = "Could not interpret profile gid.";
goto out;
}
}
errstr = "Could not interpret profile egid.";
goto out;
}
}
else
errstr = "Could not interpret profile "
"limitprivs.";
goto out;
}
}
else
errstr = "Could not interpret profile privs.";
goto out;
}
}
out:
return (errstr);
}
/*
* Eventually, we will return a structured error in the case of
* retryable or abortable failures such as memory allocation errors and
* repository connection failures. For now, these failures are just
* encoded in the failure string.
*/
static const char *
struct method_context *ci)
{
int r;
0) {
errstr = "Could not get user property.";
goto out;
}
errstr = "Could not interpret user property.";
goto out;
}
0) {
errstr = "Could not get group property.";
goto out;
}
errstr = "Could not interpret group property.";
goto out;
}
} else {
switch (r = lookup_pwd(ci)) {
case 0:
break;
case ENOENT:
errstr = "No passwd entry.";
goto out;
case ENOMEM:
errstr = "Out of memory.";
goto out;
case EIO:
case EMFILE:
case ENFILE:
errstr = "getpwuid_r() failed.";
goto out;
default:
bad_fail("lookup_pwd", r);
}
}
val) != 0) {
errstr = "Could not get supplemental groups property.";
goto out;
}
case 0:
break;
case EINVAL:
errstr =
"Could not interpret supplemental groups property.";
goto out;
case E2BIG:
errstr = "Too many supplemental groups.";
goto out;
default:
bad_fail("get_groups", r);
}
} else {
}
val) != 0) {
errstr = "Could not get privileges property.";
goto out;
}
/*
* For default privs, we need to keep priv_set == NULL, as
* we use this test elsewhere.
*/
} else {
errstr = "Could not interpret privileges "
"property.";
}
goto out;
}
}
errstr = "Could not get limit_privileges property.";
goto out;
}
/*
* L must default to all privileges so root NPA services see
* iE = all. "zone" is all privileges available in the current
* zone, equivalent to "all" in the global zone.
*/
else {
errstr = "Could not interpret limit_privileges "
"property.";
}
goto out;
}
out:
return (errstr);
}
static int
{
size_t i = 0;
int ret;
if (scf_error() == SCF_ERROR_NOT_FOUND)
return (ENOENT);
return (scf_error());
}
return (scf_error());
if (type != SCF_TYPE_ASTRING)
return (EINVAL);
return (scf_error());
return (ret);
}
goto out;
}
if (ret == -1) {
goto out;
}
goto out;
}
char **env;
goto out;
}
}
}
if (ret == -1)
out:
return (ret);
}
/*
* Fetch method context information from the repository, allocate and fill
* a method_context structure, return it in *mcpp, and return NULL. On error,
* return a human-readable string which indicates the error.
*
* Eventually, we will return a structured error in the case of
* retryable or abortable failures such as memory allocation errors and
* repository connection failures. For now, these failures are just
* encoded in the failure string.
*/
const char *
struct method_context **mcpp)
{
scf_handle_t *h;
int ret;
struct method_context *cip;
return ("Unknown method_context version.");
/* Get the handle before we allocate anything. */
h = scf_instance_handle(inst);
if (h == NULL)
return (scf_strerror(scf_error()));
return (ALLOCFAIL);
return (ALLOCFAIL);
}
goto out;
}
/*
* The method environment, and the credentials/profile data,
* may be found either in the pg for the method (methpg),
* instpg below).
*/
SCF_SUCCESS) {
goto out;
}
instpg) != SCF_SUCCESS) {
if (scf_error() != SCF_ERROR_NOT_FOUND) {
goto out;
}
}
}
switch (ret) {
case 0:
case ENOENT:
break;
case ENOMEM:
errstr = "Out of memory.";
goto out;
case EINVAL:
errstr = "Invalid method environment.";
goto out;
default:
goto out;
}
}
if (ret) {
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
/* No context: use defaults */
goto out;
goto out;
case SCF_ERROR_DELETED:
errstr = "\"use_profile\" property deleted.";
goto out;
case SCF_ERROR_NOT_SET:
default:
}
}
switch (scf_error()) {
break;
case SCF_ERROR_DELETED:
errstr = "\"use profile\" property deleted.";
break;
case SCF_ERROR_NOT_SET:
default:
}
goto out;
}
if (ty != SCF_TYPE_BOOLEAN) {
errstr = "\"use profile\" property is not boolean.";
goto out;
}
switch (scf_error()) {
break;
errstr =
"\"use profile\" property has multiple values.";
break;
case SCF_ERROR_NOT_FOUND:
errstr = "\"use profile\" property has no values.";
break;
default:
}
goto out;
}
/* get ids & privileges */
if (use_profile)
else
goto out;
/* get working directory */
errstr = "Could not get value for working directory.";
goto out;
}
case 0:
break;
case ENOMEM:
errstr = "Out of memory.";
goto out;
case ENOENT:
case EIO:
case EMFILE:
case ENFILE:
errstr = "Could not get passwd entry.";
goto out;
default:
}
goto out;
}
} else {
goto out;
}
}
/* get (optional) corefile pattern */
SCF_SUCCESS) {
errstr = "Could not get value for corefile pattern.";
goto out;
}
goto out;
}
} else {
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
/* okay if missing. */
break;
goto out;
case SCF_ERROR_DELETED:
errstr = "\"corefile_pattern\" property deleted.";
goto out;
case SCF_ERROR_NOT_SET:
default:
}
}
if (restarter_rm_libs_loadable()) {
/* get project */
errstr = "Could not get project.";
goto out;
}
case 0:
break;
case ENOMEM:
errstr = "Out of memory.";
goto out;
case ENOENT:
errstr = "Missing passwd or project entry.";
goto out;
case EIO:
errstr = "I/O error.";
goto out;
case EMFILE:
case ENFILE:
errstr = "Out of file descriptors.";
goto out;
case -1:
errstr = "Name service switch is misconfigured.";
goto out;
case ERANGE:
errstr = "Project ID too big.";
goto out;
case EINVAL:
errstr = "Project ID is invalid.";
goto out;
case E2BIG:
errstr = "Project entry is too big.";
goto out;
default:
}
/* get resource pool */
errstr = "Could not get value of resource pool.";
goto out;
}
goto out;
}
}
}
out:
(void) scf_value_destroy(val);
return (errstr);
}
/*
* Modify the current process per the given method_context. On success, returns
* 0. Note that the environment is not modified by this function to include the
* environment variables in cip->env.
*
* On failure, sets *fp to NULL or the name of the function which failed,
* and returns one of the following error codes. The words in parentheses are
* the values to which *fp may be set for the error case.
* ENOMEM - malloc() failed
* EIO - an I/O error occurred (getpwuid_r, chdir)
* EMFILE - process is out of file descriptors (getpwuid_r)
* ENFILE - system is out of file handles (getpwuid_r)
* EINVAL - gid or egid is out of range (setregid)
* ngroups is too big (setgroups)
* project's project id is bad (setproject)
* uid or euid is out of range (setreuid)
* poolname is invalid (pool_set_binding)
* EPERM - insufficient privilege (setregid, initgroups, setgroups, setppriv,
* setproject, setreuid, settaskid)
* ENOENT - uid has a passwd entry but no shadow entry
* working_dir does not exist (chdir)
* uid has no passwd entry
* the pool could not be found (pool_set_binding)
* EFAULT - lpriv_set or priv_set has a bad address (setppriv)
* working_dir has a bad address (chdir)
* EACCES - could not access working_dir (chdir)
* in a TASK_FINAL task (setproject, settaskid)
* no resource pool accepting default binding exists (setproject)
* ELOOP - too many symbolic links in working_dir (chdir)
* ENAMETOOLONG - working_dir is too long (chdir)
* ENOLINK - working_dir is on an inaccessible remote machine (chdir)
* ENOTDIR - working_dir is not a directory (chdir)
* ESRCH - uid is not a user of project (setproject)
* project is invalid (setproject)
* the resource pool specified for project is unknown (setproject)
* EBADF - the configuration for the pool is invalid (pool_set_binding)
* -1 - core_set_process_path() failed (core_set_process_path)
* a resource control assignment failed (setproject)
* a system error occurred during pool_set_binding (pool_set_binding)
*/
int
{
int r, ret;
*fp = "setregid";
goto out;
}
} else {
case 0:
break;
case ENOMEM:
case ENOENT:
goto out;
case EIO:
case EMFILE:
case ENFILE:
*fp = "getpwuid_r";
goto out;
default:
}
}
*fp = "setregid";
goto out;
}
}
case 0:
break;
case ENOMEM:
case ENOENT:
goto out;
case EIO:
case EMFILE:
case ENFILE:
*fp = "getpwuid_r";
goto out;
default:
}
}
/* Ok if cip->gid == -1 */
*fp = "initgroups";
goto out;
}
*fp = "setgroups";
goto out;
}
*fp = "setppriv";
goto out;
}
}
goto out;
}
}
*fp = "core_set_process_path";
ret = -1;
goto out;
}
}
if (restarter_rm_libs_loadable()) {
switch (errno) {
case EACCES:
case EPERM:
*fp = "settaskid";
goto out;
case EINVAL:
default:
}
}
} else {
case 0:
break;
case ENOMEM:
case ENOENT:
goto out;
case EIO:
case EMFILE:
case ENFILE:
*fp = "getpwuid_r";
goto out;
default:
}
*fp = "setproject";
TASK_NORMAL)) {
case 0:
break;
case SETPROJ_ERR_TASK:
case SETPROJ_ERR_POOL:
goto out;
default:
ret = -1;
goto out;
}
}
if (mypid == -1)
*fp = "pool_set_binding";
mypid) != PO_SUCCESS) {
switch (pool_error()) {
case POE_INVALID_SEARCH:
break;
case POE_BADPARAM:
break;
case POE_INVALID_CONF:
break;
case POE_SYSTEM:
ret = -1;
break;
default:
bad_fail("pool_set_binding",
pool_error());
}
goto out;
}
}
}
/*
* Now, we have to assume our ID. If the UID is 0, we want it to be
* privilege-aware, otherwise the limit set gets used instead of E/P.
* We can do this by setting P as well, which keeps
* PA status (see priv_can_clear_PA()).
*/
*fp = "setreuid";
goto out;
}
*fp = "setppriv";
goto out;
}
}
/*
* The last thing to do is chdir to the specified working directory.
* This should come after the uid switching as only the user might
* have access to the specified directory.
*/
do
if (r != 0) {
*fp = "chdir";
goto out;
}
}
ret = 0;
out:
return (ret);
}
void
{
size_t i;
}
}
/*
* Method keyword functions
*/
int
restarter_is_null_method(const char *meth)
{
}
static int
{
const char *cp;
int sig;
return (-1);
++cp;
if (*cp == '\0')
return (SIGTERM);
if (*cp != '-')
return (-1);
}
int
restarter_is_kill_proc_method(const char *method)
{
sizeof (MKW_KILL_PROC) - 1));
}
int
restarter_is_kill_method(const char *method)
{
}
/*
* Stubs for now.
*/
/* ARGSUSED */
int
{
return (-1);
}
/* ARGSUSED */
{
return (-1);
}
/* ARGSUSED */
void
{
}