client.c revision f2fa366a878dc4913b5f87b617e6cb4198b91052
/*
* 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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* This is the client layer for svc.configd. All direct protocol interactions
* are handled here.
*
* Essentially, the job of this layer is to turn the idempotent protocol
* into a series of non-idempotent calls into the object layer, while
* also handling the necessary locking.
*/
#include <alloca.h>
#include <assert.h>
#include <door.h>
#include <errno.h>
#include <limits.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libuutil.h>
#include "configd.h"
#include "repcache_protocol.h"
#define INVALID_CHANGEID (0)
/*
* lint doesn't like constant assertions
*/
#ifdef lint
#define assert_nolint(x) (void)0
#else
#define assert_nolint(x) assert(x)
#endif
/*
* Protects client linkage and the freelist
*/
#define CLIENT_HASH_SIZE 64
static uu_avl_pool_t *entity_pool;
static uu_avl_pool_t *iter_pool;
static uu_list_pool_t *client_pool;
static uint_t request_log_cur;
static uint32_t client_maxid;
static request_log_entry_t *
get_log(void)
{
}
void
{
(void) pthread_mutex_lock(&request_log_lock);
if (request_log_cur == request_log_size)
request_log_cur = 0;
(void) pthread_mutex_unlock(&request_log_lock);
}
}
/*
* Note that the svc.configd dmod will join all of the per-thread log entries
* with the main log, so that even if the log is disabled, there is some
* information available.
*/
static request_log_entry_t *
{
return (rlp);
}
void
end_log(void)
{
}
static void
void *ptr)
{
return;
return;
/*
* For entities, it's useful to have the node pointer at the start
* of the request.
*/
}
int
client_is_privileged(void)
{
return (1);
return (0);
return (ucred_is_privileged(uc));
}
/*ARGSUSED*/
static int
{
return (1);
return (-1);
return (0);
}
/*ARGSUSED*/
static int
{
return (1);
return (-1);
return (0);
}
/*ARGSUSED*/
static int
{
return (1);
return (-1);
return (0);
}
static int
client_hash_init(void)
{
int x;
return (0);
for (x = 0; x < CLIENT_HASH_SIZE; x++) {
return (0);
}
return (1);
}
static repcache_client_t *
client_alloc(void)
{
return (NULL);
goto fail;
goto fail;
return (cp);
fail:
return (NULL);
}
static void
{
}
static void
{
/*
* We assume it does not already exist
*/
}
static repcache_client_t *
{
/*
* Bump the reference count
*/
}
return (cp);
}
static void
{
}
/*
* We only allow one thread to be inserting at a time, to prevent
*/
static void
{
while (cp->rc_insert_thr != 0) {
}
}
static void
{
cp->rc_insert_thr = 0;
}
/*ARGSUSED*/
static repcache_entity_t *
{
}
return (ep);
}
static void
{
}
static repcache_entity_t *
{
}
return (ep);
}
/*
* Fails with
* _DUPLICATE_ID - the ids are equal
* _UNKNOWN_ID - an id does not designate an active register
*/
static int
{
return (REP_PROTOCOL_FAIL_DUPLICATE_ID);
return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
}
/*
* locks are ordered by id number
*/
} else {
}
}
return (REP_PROTOCOL_SUCCESS);
}
static void
{
}
static void
{
}
static void
{
}
static void
{
}
}
/*ARGSUSED*/
static repcache_iter_t *
{
return (iter);
}
static void
{
}
static repcache_iter_t *
{
}
return (iter);
}
/*
* Fails with
* _UNKNOWN_ID - iter_id or entity_id does not designate an active register
*/
static int
{
return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
}
}
return (REP_PROTOCOL_SUCCESS);
}
static void
{
}
static void
{
}
static void
{
}
static void
{
}
}
/*
* Ensure that the passed client id is no longer usable, wait for any
* outstanding invocations to complete, then destroy the client
* structure.
*/
static void
{
return;
}
/* kick the waiters out */
perror("door_revoke");
}
/*
* destroy outstanding objects
*/
/*
* clean up notifications
*/
}
/*
* Fails with
* _TYPE_MISMATCH - the entity is already set up with a different type
* _NO_RESOURCES - out of memory
*/
static int
{
return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
return (REP_PROTOCOL_SUCCESS);
}
break;
default:
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
}
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
}
return (REP_PROTOCOL_SUCCESS);
}
/*ARGSUSED*/
static void
{
return;
}
/*
* If we fail, we only return the response code.
* If we succeed, we don't return anything after the '\0' in rpr_name.
*/
else
}
/*ARGSUSED*/
static void
{
return;
}
}
/*
* Fails with
* _DUPLICATE_ID - the ids are equal
* _UNKNOWN_ID - an id does not designate an active register
* _INVALID_TYPE - type is invalid
* _TYPE_MISMATCH - np doesn't carry children of type type
* _DELETED - np has been deleted
* _NO_RESOURCES
* _BACKEND_ACCESS
*/
static int
struct rep_protocol_entity_get_child *rpr)
{
int result;
if (result != REP_PROTOCOL_SUCCESS)
return (result);
return (result);
}
/*
* Returns _FAIL_DUPLICATE_ID, _FAIL_UNKNOWN_ID, _FAIL_NOT_SET, _FAIL_DELETED,
* _FAIL_TYPE_MISMATCH, _FAIL_NOT_FOUND (scope has no parent), or _SUCCESS.
* Fails with
* _DUPLICATE_ID - the ids are equal
* _UNKNOWN_ID - an id does not designate an active register
* _NOT_SET - child is not set
* _DELETED - child has been deleted
* _TYPE_MISMATCH - child's parent does not match that of the parent register
* _NOT_FOUND - child has no parent (and is a scope)
*/
static int
{
int result;
if (result != REP_PROTOCOL_SUCCESS)
return (result);
return (result);
}
static int
{
int result;
return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
switch (rpr->rpr_object) {
case RP_ENTITY_GET_INVALIDATE:
break;
break;
default:
break;
}
return (result);
}
static int
{
int result;
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
} else {
if (result == REP_PROTOCOL_DONE)
}
return (result);
}
static int
{
return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
return (REP_PROTOCOL_SUCCESS);
}
/*
* Fails with
* _BAD_REQUEST - request has invalid changeid
* rpr_name is invalid
* cannot create children for parent's type of node
* _DUPLICATE_ID - request has duplicate ids
* _UNKNOWN_ID - request has unknown id
* _DELETED - parent has been deleted
* _NOT_SET - parent is reset
* _NOT_APPLICABLE - rpr_childtype is _PROPERTYGRP
* _INVALID_TYPE - parent is corrupt or rpr_childtype is invalid
* _TYPE_MISMATCH - parent cannot have children of type rpr_childtype
* _NO_RESOURCES
* _PERMISSION_DENIED
* _BACKEND_ACCESS
* _BACKEND_READONLY
* _EXISTS - child already exists
*/
static int
struct rep_protocol_entity_create_child *rpr)
{
int result;
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
if (result != REP_PROTOCOL_SUCCESS)
return (result);
} else {
if (result == REP_PROTOCOL_SUCCESS)
}
return (result);
}
static int
struct rep_protocol_entity_create_pg *rpr)
{
int result;
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
if (result != REP_PROTOCOL_SUCCESS)
return (result);
} else {
if (result == REP_PROTOCOL_SUCCESS)
}
return (result);
}
static int
struct rep_protocol_entity_delete *rpr)
{
int result;
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
} else {
if (result == REP_PROTOCOL_SUCCESS)
}
return (result);
}
static rep_protocol_responseid_t
{
return (REP_PROTOCOL_SUCCESS);
}
/*
* Fails with
* _MISORDERED - the iterator exists and is not reset
* _NO_RESOURCES - out of memory
*/
static int
{
/*
* If the iter already exists, and hasn't been read from,
* we assume the previous call succeeded.
*/
if (sequence != 0)
return (REP_PROTOCOL_FAIL_MISORDERED);
return (REP_PROTOCOL_SUCCESS);
}
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
}
iter->ri_sequence = 0;
return (REP_PROTOCOL_SUCCESS);
}
/*
* Fails with
* _UNKNOWN_ID
* _MISORDERED - iterator has already been started
* _NOT_SET
* _DELETED
* _TYPE_MISMATCH - entity cannot have type children
* _BAD_REQUEST - rpr_flags is invalid
* rpr_pattern is invalid
* _NO_RESOURCES
* _INVALID_TYPE
* _BACKEND_ACCESS
*/
static int
{
int result;
if (result != REP_PROTOCOL_SUCCESS)
return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
goto end;
}
goto end;
}
if (result == REP_PROTOCOL_SUCCESS)
iter->ri_sequence++;
end:
return (result);
}
/*
* Returns
* _UNKNOWN_ID
* _NOT_SET - iter has not been started
* _MISORDERED
* _BAD_REQUEST - iter walks values
* _TYPE_MISMATCH - iter does not walk type entities
* _DELETED - parent was deleted
* _NO_RESOURCES
* _INVALID_TYPE - type is invalid
* _DONE
* _SUCCESS
*
* For composed property group iterators, can also return
* _TYPE_MISMATCH - parent cannot have type children
* _BACKEND_ACCESS
*/
static rep_protocol_responseid_t
{
if (result != REP_PROTOCOL_SUCCESS)
return (result);
if (iter->ri_sequence == 0) {
return (REP_PROTOCOL_FAIL_NOT_SET);
}
if (sequence == 1) {
return (REP_PROTOCOL_FAIL_MISORDERED);
}
return (REP_PROTOCOL_SUCCESS);
}
if (result == REP_PROTOCOL_SUCCESS)
iter->ri_sequence++;
return (result);
}
return (REP_PROTOCOL_FAIL_MISORDERED);
}
/*ARGSUSED*/
static void
{
int repeat;
goto out;
}
if (iter->ri_sequence == 0) {
goto out;
}
goto out;
}
iter->ri_sequence++;
out:
/*
* If we fail, we only return the response code.
* If we succeed, rc_iter_next_value has shortened *outsz
* to only include the value bytes needed.
*/
}
static int
{
return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
if (iter->ri_sequence != 0) {
iter->ri_sequence = 0;
}
return (REP_PROTOCOL_SUCCESS);
}
static rep_protocol_responseid_t
{
return (REP_PROTOCOL_SUCCESS);
}
static rep_protocol_responseid_t
{
if (result != REP_PROTOCOL_SUCCESS)
return (result);
goto end;
}
goto end;
}
end:
if (result == REP_PROTOCOL_SUCCESS)
else
return (result);
}
/*ARGSUSED*/
static void
{
return;
}
return;
}
switch (tx->re_txstate) {
case REPCACHE_TX_INIT:
break;
case REPCACHE_TX_SETUP:
}
break;
case REPCACHE_TX_COMMITTED:
break;
default:
assert(0); /* CAN'T HAPPEN */
break;
}
}
static rep_protocol_responseid_t
{
int result;
if (result != REP_PROTOCOL_SUCCESS)
return (result);
return (result);
}
static rep_protocol_responseid_t
{
int result;
if (result != REP_PROTOCOL_SUCCESS)
return (result);
} else {
else
}
return (result);
}
static rep_protocol_responseid_t
struct rep_protocol_snapshot_take_named *rpr)
{
int result;
if (result != REP_PROTOCOL_SUCCESS)
return (result);
} else {
}
return (result);
}
static rep_protocol_responseid_t
{
int result;
if (result != REP_PROTOCOL_SUCCESS)
return (result);
return (result);
}
/*ARGSUSED*/
static void
{
rep_protocol_value_type_t t = 0;
return;
}
else
}
/*
* Fails with:
* _UNKNOWN_ID - an id does not designate an active register
* _NOT_SET - The property is not set
* _DELETED - The property has been deleted
* _TYPE_MISMATCH - The object is not a property
* _NOT_FOUND - The property has no values.
*
* Succeeds with:
* _SUCCESS - The property has 1 value.
* _TRUNCATED - The property has >1 value.
*/
/*ARGSUSED*/
static void
{
return;
}
outsz);
/*
* If we fail, we only return the response code.
* If we succeed, rc_node_get_property_value has shortened *outsz
* to only include the value bytes needed.
*/
}
static rep_protocol_responseid_t
{
int fds[2];
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
goto fail;
}
/*
* While the following can race with other threads setting up a
* notification, the worst that can happen is that our fd has
* already been closed before we return.
*/
ours);
if (result != REP_PROTOCOL_SUCCESS)
goto fail;
return (REP_PROTOCOL_SUCCESS);
fail:
return (result);
}
static rep_protocol_responseid_t
struct rep_protocol_notify_request *rpr)
{
rpr->rpr_pattern));
rpr->rpr_pattern));
default:
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
}
}
/*ARGSUSED*/
static void
{
int result;
if (cp->rc_notify_thr != 0) {
return;
}
if (result == REP_PROTOCOL_SUCCESS) {
&cp->rc_notify_ptr);
} else {
}
} else {
}
}
cp->rc_notify_thr = 0;
if (result != REP_PROTOCOL_SUCCESS)
}
/*
* Can return:
* _PERMISSION_DENIED not enough privileges to do request.
* _BAD_REQUEST name is not valid or reserved
* _TRUNCATED name is too long for current repository path
* _UNKNOWN failed for unknown reason (details written to
* console)
* _BACKEND_READONLY backend is not writable
*
* _SUCCESS Backup completed successfully.
*/
static rep_protocol_responseid_t
struct rep_protocol_backup_request *rpr)
{
return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
if (result == REP_PROTOCOL_SUCCESS)
} else {
}
return (result);
}
const void *rpr);
/*ARGSUSED*/
static void
{
}
/*ARGSUSED*/
static void
{
}
p, #p, simple_handler, (void *)(&f), NULL, \
sizeof (in), sizeof (rep_protocol_response_t), 0 \
}
#define PROTO_FD_OUT(p, f, in) { \
p, #p, NULL, (void *)(&f), simple_fd_handler, \
sizeof (in), \
sizeof (rep_protocol_response_t), \
}
#define PROTO_VARIN(p, f, insz) { \
insz, sizeof (rep_protocol_response_t), \
}
#define PROTO_UINT_OUT(p, f, in) { \
sizeof (in), \
sizeof (struct rep_protocol_integer_response), 0 \
}
#define PROTO_NAME_OUT(p, f, in) { \
sizeof (in), \
sizeof (struct rep_protocol_name_response), 0 \
}
#define PROTO_FMRI_OUT(p, f, in) { \
sizeof (in), \
sizeof (struct rep_protocol_fmri_response), 0 \
}
#define PROTO_VALUE_OUT(p, f, in) { \
sizeof (in), \
sizeof (struct rep_protocol_value_response), 0 \
}
static struct protocol_entry {
const char *pt_name;
void *pt_arg;
} protocol_table[] = {
struct rep_protocol_entity_setup),
struct rep_protocol_entity_name),
struct rep_protocol_entity_parent_type),
struct rep_protocol_entity_get_child),
struct rep_protocol_entity_parent),
struct rep_protocol_entity_get),
struct rep_protocol_entity_update),
struct rep_protocol_entity_create_child),
struct rep_protocol_entity_create_pg),
struct rep_protocol_entity_delete),
struct rep_protocol_entity_reset),
struct rep_protocol_entity_teardown),
struct rep_protocol_iter_request),
struct rep_protocol_iter_start),
struct rep_protocol_iter_read),
struct rep_protocol_iter_read_value),
struct rep_protocol_iter_request),
struct rep_protocol_iter_request),
struct rep_protocol_entity_pair),
struct rep_protocol_snapshot_take),
struct rep_protocol_snapshot_take_named),
struct rep_protocol_snapshot_attach),
struct rep_protocol_property_request),
struct rep_protocol_property_request),
struct rep_protocol_propertygrp_request),
struct rep_protocol_transaction_start),
struct rep_protocol_notify_request),
struct rep_protocol_wait_request),
struct rep_protocol_backup_request),
};
/*
* The number of entries, sans PROTO_END()
*/
#define PROTOCOL_ENTRIES \
#define PROTOCOL_PREFIX "REP_PROTOCOL_"
int
client_init(void)
{
int i;
struct protocol_entry *e;
if (!client_hash_init())
return (0);
if (request_log_size > 0) {
sizeof (request_log_entry_t));
}
/*
* update the names to not include REP_PROTOCOL_
*/
for (i = 0; i < PROTOCOL_ENTRIES; i++) {
e = &protocol_table[i];
strlen(PROTOCOL_PREFIX)) == 0);
}
/*
* verify the protocol table is consistent
*/
for (i = 0; i < PROTOCOL_ENTRIES; i++) {
e = &protocol_table[i];
if (e->pt_flags & PROTO_FLAG_PANIC)
e->pt_handler == NULL);
else
(e->pt_handler != NULL ||
e->pt_fd_handler != NULL));
}
return (1);
}
static void
{
struct protocol_entry *e;
int retfd = -1;
if (n_desc != 0)
uu_die("can't happen: %d descriptors @%p (cookie %p)",
if (argp == DOOR_UNREF_DATA) {
goto bad_end;
}
/*
* To simplify returning just a result code, we set up for
* that case here.
*/
if (arg_size < sizeof (request_code)) {
goto end_unheld;
}
/* LINTED alignment */
}
/*
* In order to avoid locking problems on removal, we handle the
* "close" case before doing a lookup.
*/
if (request_code == REP_PROTOCOL_CLOSE) {
goto end_unheld;
}
/*
* cp is held
*/
goto bad_end;
if (request_code < REP_PROTOCOL_BASE ||
goto end;
}
if (e->pt_flags & PROTO_FLAG_VARINPUT) {
if (arg_size < e->pt_in_size) {
goto end;
}
} else if (arg_size != e->pt_in_size) {
goto end;
}
if (retsize != e->pt_out_max) {
retsize = e->pt_out_max;
}
if (e->pt_flags & PROTO_FLAG_RETFD)
else
end:
/* LINTED alignment */
end_log();
}
} else {
/* LINTED alignment */
}
if (retfd != -1) {
} else {
}
end_log();
}
}
int
{
int fd;
#ifdef DOOR_NO_CANCEL
#endif
cp = client_alloc();
return (REPOSITORY_DOOR_FAIL_NO_RESOURCES);
(void) pthread_mutex_lock(&client_lock);
(void) pthread_mutex_unlock(&client_lock);
return (REPOSITORY_DOOR_FAIL_NO_RESOURCES);
}
#ifdef DOOR_PARAM_DATA_MIN
sizeof (enum rep_protocol_requestid));
#endif
if (fd >= 0)
return (REPOSITORY_DOOR_FAIL_NO_RESOURCES);
}
return (REPOSITORY_DOOR_SUCCESS);
}