summaryrefslogtreecommitdiff
path: root/usr/src/lib/libzdoor/common/zdoor.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libzdoor/common/zdoor.c')
-rw-r--r--usr/src/lib/libzdoor/common/zdoor.c440
1 files changed, 440 insertions, 0 deletions
diff --git a/usr/src/lib/libzdoor/common/zdoor.c b/usr/src/lib/libzdoor/common/zdoor.c
new file mode 100644
index 0000000000..ad7c7851be
--- /dev/null
+++ b/usr/src/lib/libzdoor/common/zdoor.c
@@ -0,0 +1,440 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2011 Joyent, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <alloca.h>
+#include <door.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libzonecfg.h>
+#include <pthread.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stropts.h>
+#include <unistd.h>
+#include <zdoor.h>
+#include <zone.h>
+
+#include "zdoor-int.h"
+#include "zerror.h"
+#include "ztree.h"
+
+extern void *
+zonecfg_notify_bind(int(*func)(const char *zonename, zoneid_t zid,
+ const char *newstate, const char *oldstate,
+ hrtime_t when, void *p), void *p);
+
+extern void
+zonecfg_notify_unbind(void *handle);
+
+/*
+ * _callback(cookie, door_args...) is our private function that we tell
+ * the Solaris door API about. This function does some sanity checking on
+ * arguments and issues a callback to the owner of this door. That API
+ * will return us memory that needs to be sent back to the client on the
+ * other end of the door, but since the door_return API never gives you
+ * back control of the function, this does a simple alloca/memcpy and
+ * frees up the memory pointed to by the parent. While this really doesn't
+ * let a client do much other than pass a simple struct of primitives (or
+ * more likely more common a char *), that's the way the door API works,
+ * and so this isn't really imposing any restriction that didn't already
+ * need to be dealt with by someone. This is why the zdoor_result structure
+ * takes a char *, rather than a void * for the data pointer.
+ */
+static void
+_callback(void *cookie, char *argp, size_t arg_size, door_desc_t *dp,
+ uint_t n_desc)
+{
+ zdoor_result_t *result = NULL;
+ void *door_response = NULL;
+ int size = 0;
+ dtree_entry_t *entry = (dtree_entry_t *)cookie;
+ zoneid_t did = -1;
+
+ if (entry == NULL) {
+ zdoor_warn("_callback: NULL cookie? door_returning");
+ (void) door_return(NULL, 0, NULL, 0);
+ }
+
+ (void) pthread_mutex_lock(&entry->dte_parent->zte_parent->zdh_lock);
+ zdoor_debug("_callback: calling back with %p", entry->dte_cookie);
+ result = entry->dte_callback(entry->dte_cookie, argp, arg_size);
+ zdoor_debug("_callback: app callback returned %p", result);
+ (void) pthread_mutex_unlock(&entry->dte_parent->zte_parent->zdh_lock);
+
+ if (result == NULL) {
+ zdoor_debug("_callback: door_returning NULL");
+ (void) door_return(NULL, 0, NULL, 0);
+ }
+
+ if (result->zdr_data != NULL && result->zdr_size > 0) {
+ door_response = alloca(result->zdr_size);
+ if (door_response != NULL) {
+ size = result->zdr_size;
+ (void) memcpy(door_response,
+ (void *) result->zdr_data, size);
+ }
+ }
+
+ if (result->zdr_data != NULL)
+ free(result->zdr_data);
+ free(result);
+
+ zdoor_debug("_callback: door_returning %p, %d", door_response, size);
+ (void) door_return(door_response, size, NULL, 0);
+}
+
+static void
+zdoor_stop(dtree_entry_t *entry)
+{
+ zoneid_t zid = -1;
+
+ if (entry == NULL) {
+ zdoor_debug("zdoor_stop: NULL arguments");
+ return;
+ }
+
+ zdoor_debug("zdoor_stop: entry=%p, zone=%s, service=%s",
+ entry, entry->dte_parent->zte_zonename, entry->dte_service);
+
+ zid = getzoneidbyname(entry->dte_parent->zte_zonename);
+ (void) zdoor_fattach(zid, entry->dte_service, entry->dte_door, 1);
+ (void) door_revoke(entry->dte_door);
+ entry->dte_door = -1;
+
+ zdoor_debug("zdoor_stop returning");
+}
+
+/*
+ * zdoor_create is called both by the main API
+ * call zdoor_open, as well as by the zone_monitor code upon a zone restart
+ * (assuming it already has a door in it). This code assumes that the
+ * permissions were correct (e.g., the target door is not a GZ, that this
+ * program is being run out of the GZ), but does not assume that the target
+ * door file has not changed out from under us, so that is explicitly rechecked.
+ *
+ * This also assumes the parent has already locked handle.
+ */
+static int
+zdoor_create(dtree_entry_t *entry)
+{
+ int status = ZDOOR_OK;
+ zoneid_t zid = -1;
+ zdoor_handle_t handle = NULL;
+
+ if (entry == NULL) {
+ zdoor_debug("zdoor_create: NULL arguments");
+ return (ZDOOR_ARGS_ERROR);
+ }
+
+ zdoor_debug("zdoor_create: entry=%p, zone=%s, service=%s",
+ entry, entry->dte_parent->zte_zonename, entry->dte_service);
+
+ handle = entry->dte_parent->zte_parent;
+
+ zid = getzoneidbyname(entry->dte_parent->zte_zonename);
+ if (zid < 0) {
+ zdoor_info("zdoor_create: %s is a non-existient zone",
+ entry->dte_parent->zte_zonename);
+ return (ZDOOR_ERROR);
+ }
+ if (!zdoor_zone_is_running(zid)) {
+ zdoor_debug("zdoor_create: %s is not running",
+ entry->dte_parent->zte_zonename);
+ return (ZDOOR_ZONE_NOT_RUNNING);
+ }
+
+ entry->dte_door = door_create(_callback, entry, 0);
+ zdoor_info("zdoor_create: door_create returned %d", entry->dte_door);
+ if (entry->dte_door < 0) {
+ zdoor_stop(entry);
+ return (ZDOOR_ERROR);
+ }
+
+ status = zdoor_fattach(zid, entry->dte_service, entry->dte_door, 0);
+
+ zdoor_debug("zdoor_create: returning %d", status);
+ return (status);
+}
+
+
+/*
+ * door_visitor(entry) is a callback from the ztree code that checks whether
+ * or not we should be taking some action on a given door. Note that the
+ * callpath to this API is:
+ * SYSTEM ->
+ * zone_monitor ->
+ * ztree_walk ->
+ * door_visitor
+ *
+ * Which is important to note that this API assumes that all things needing
+ * locking are locked by a parent caller (which is the zone_monitor).
+ */
+static void
+zdoor_visitor(dtree_entry_t *entry)
+{
+ if (entry == NULL) {
+ zdoor_info("zdoor_visitor: entered with NULL entry");
+ return;
+ }
+
+ zdoor_debug("zdoor_visitor: entered for entry=%p, service=%s",
+ entry, entry->dte_service);
+
+ if (entry->dte_parent->zte_action == ZDOOR_ACTION_STOP) {
+ zdoor_debug(" stopping zdoor");
+ zdoor_stop(entry);
+ } else if (entry->dte_parent->zte_action == ZDOOR_ACTION_START) {
+ zdoor_debug(" starting zdoor");
+ if (zdoor_create(entry) != ZDOOR_OK) {
+ zdoor_error("door_visitor: Unable to restart zdoor\n");
+ }
+ }
+}
+
+/*
+ * zone_monitor(zonename, zid, newstate, oldstate, when, cookie) is our
+ * registered callback with libzonecfg to notify us of any changes to a
+ * given zone. This activates a walk on all doors for a zone iff the state
+ * is changing from running or into running.
+ */
+static int
+zone_monitor(const char *zonename, zoneid_t zid, const char *newstate,
+ const char *oldstate, hrtime_t when, void *p)
+{
+ zdoor_handle_t handle = (zdoor_handle_t)p;
+ ztree_entry_t *entry = NULL;
+
+ if (handle == NULL) {
+ zdoor_warn("zone_monitor: entered with NULL handle?");
+ return (-1);
+ }
+
+ zdoor_info("zone_monitor: zone=%s, zid=%d, newst=%s, oldst=%s, p=%p",
+ zonename, zid, newstate, oldstate, p);
+
+ (void) pthread_mutex_lock(&(handle->zdh_lock));
+ entry = ztree_zone_find(handle, zonename);
+ if (entry != NULL) {
+ zdoor_debug(" found entry in ztree");
+ entry->zte_action = ZDOOR_ACTION_NOOP;
+ if (strcmp("running", newstate) == 0) {
+ if (strcmp("ready", oldstate) == 0)
+ entry->zte_action = ZDOOR_ACTION_START;
+ } else if (strcmp("shutting_down", newstate) == 0) {
+ if (strcmp("running", oldstate) == 0)
+ entry->zte_action = ZDOOR_ACTION_STOP;
+ }
+ zdoor_debug(" set state to: %d", entry->zte_action);
+ if (entry->zte_action != ZDOOR_ACTION_NOOP)
+ ztree_walk_doors(handle, zonename);
+ }
+ (void) pthread_mutex_unlock(&(handle->zdh_lock));
+
+ zdoor_info("zone_monitor: returning");
+ return (0);
+}
+
+zdoor_handle_t
+zdoor_handle_init()
+{
+ zdoor_handle_t handle = NULL;
+
+ zdoor_debug("zdoor_handle_init entered");
+
+ handle = (zdoor_handle_t)calloc(1, sizeof (struct zdoor_handle));
+ if (handle == NULL) {
+ OUT_OF_MEMORY();
+ return (NULL);
+ }
+
+ (void) pthread_mutex_init(&(handle->zdh_lock), NULL);
+ handle->zdh_zonecfg_handle = zonecfg_notify_bind(zone_monitor, handle);
+ if (handle->zdh_zonecfg_handle == NULL) {
+ zdoor_error("zonecfg_notify_bind failure: %s", strerror(errno));
+ return (NULL);
+ }
+
+ zdoor_debug("zdoor_handle_init returning %p", handle);
+ return (handle);
+}
+
+void
+zdoor_handle_destroy(zdoor_handle_t handle)
+{
+ if (handle == NULL) {
+ zdoor_debug("zdoor_handle_destroy: NULL arguments");
+ return;
+ }
+
+ zdoor_debug("zdoor_handle_destroy: handle=%p", handle);
+
+ (void) pthread_mutex_lock(&(handle->zdh_lock));
+ zonecfg_notify_unbind(handle->zdh_zonecfg_handle);
+ (void) pthread_mutex_unlock(&(handle->zdh_lock));
+ (void) pthread_mutex_destroy(&(handle->zdh_lock));
+ free(handle);
+}
+
+/*
+ * zdoor_open(zone, service, biscuit, callback) is the main public facing API in
+ * libzdoor. It will open a door with the name .[service] under
+ * [zonepath]/root/var/tmp, where [zonepath] is resolved on the fly. Note this
+ * API can only be invoked from the global zone, and will not allow you to open
+ * a zdoor in the global zone.
+ */
+int
+zdoor_open(zdoor_handle_t handle, const char *zonename, const char *service,
+ void *biscuit, zdoor_callback callback)
+{
+ zdoor_cookie_t *zdoor_cookie = NULL;
+ int door = -1;
+ int rc = -1;
+ int status = ZDOOR_OK;
+ zoneid_t zid = -1;
+ dtree_entry_t *entry = NULL;
+
+ if (handle == NULL || zonename == NULL ||
+ service == NULL || callback == NULL) {
+ zdoor_debug("zdoor_open: NULL arguments");
+ return (ZDOOR_ARGS_ERROR);
+ }
+ zdoor_debug("zdoor_open: entered: handle=%p, zone=%s, service=%s",
+ handle, zonename, service);
+
+ if (getzoneid() != GLOBAL_ZONEID) {
+ zdoor_warn("zdoor_open: not invoked from global zone");
+ return (ZDOOR_NOT_GLOBAL_ZONE);
+ }
+
+
+ zid = getzoneidbyname(zonename);
+ if (zid < 0) {
+ zdoor_info("zdoor_open: %s is a non-existent zone", zonename);
+ return (ZDOOR_ARGS_ERROR);
+ }
+
+ if (zid == GLOBAL_ZONEID) {
+ zdoor_warn("zdoor_open: zdoors not allowed in global zone");
+ return (ZDOOR_ZONE_FORBIDDEN);
+ }
+
+ if (!zdoor_zone_is_running(zid)) {
+ zdoor_info("zdoor_open: %s is not running", zonename);
+ return (ZDOOR_ZONE_NOT_RUNNING);
+ }
+
+ zdoor_cookie = zdoor_cookie_create(zonename, service, biscuit);
+ if (zdoor_cookie == NULL) {
+ OUT_OF_MEMORY();
+ return (ZDOOR_OUT_OF_MEMORY);
+ }
+
+ (void) pthread_mutex_lock(&(handle->zdh_lock));
+ rc = ztree_zone_add(handle, zonename, zdoor_visitor);
+ if (rc != ZTREE_SUCCESS && rc != ZTREE_ALREADY_EXISTS) {
+ zdoor_debug("zdoor_open: unable to add zone to ztree: %d", rc);
+ status = ZDOOR_ERROR;
+ goto out;
+ }
+ rc = ztree_door_add(handle, zonename, service, callback,
+ zdoor_cookie);
+ if (rc != ZTREE_SUCCESS) {
+ zdoor_debug("zdoor_open: unable to add door to ztree: %d", rc);
+ if (rc == ZTREE_ALREADY_EXISTS) {
+ zdoor_warn("service %s already has a zdoor", service);
+ }
+ status = ZDOOR_ERROR;
+ goto out;
+ }
+
+ entry = ztree_door_find(handle, zonename, service);
+ if (entry == NULL) {
+ zdoor_debug("zdoor_open: unable to find door in ztree?");
+ status = ZDOOR_ERROR;
+ goto out;
+ }
+ if (zdoor_create(entry) != ZDOOR_OK) {
+ zdoor_info("zdoor_open: zdoor_create failed.");
+ status = ZDOOR_ERROR;
+ goto out;
+ }
+out:
+ if (status != ZDOOR_OK) {
+ zdoor_debug("zdoor_open: status not ok, stopping and cleaning");
+ zdoor_stop(entry);
+ ztree_door_remove(handle, entry);
+ zdoor_cookie_free(zdoor_cookie);
+ }
+ (void) pthread_mutex_unlock(&(handle->zdh_lock));
+ zdoor_debug("zdoor_open: returning %d", status);
+ return (status);
+}
+
+/*
+ * zdoor_close(zone, service) unregisters a previously created zdoor, and
+ * returns the biscuit provided at creation time, so the caller can free it.
+ * Returns NULL on any error.
+ */
+void *
+zdoor_close(zdoor_handle_t handle, const char *zonename, const char *service)
+{
+ int rc = 0;
+ dtree_entry_t *entry = NULL;
+ zdoor_cookie_t *cookie = NULL;
+ void *biscuit = NULL;
+
+ if (handle == NULL || zonename == NULL || service == NULL) {
+ zdoor_debug("zdoor_close: NULL arguments");
+ return (NULL);
+ }
+
+ zdoor_debug("zdoor_close: entered handle=%p, zone=%s, service=%s",
+ handle, zonename, service);
+
+ (void) pthread_mutex_lock(&(handle->zdh_lock));
+
+ entry = ztree_door_find(handle, zonename, service);
+ if (entry != NULL) {
+ zdoor_debug("zdoor_close: found door in ztree, stopping");
+ zdoor_stop(entry);
+ cookie = ztree_door_remove(handle, entry);
+ if (cookie != NULL) {
+ biscuit = cookie->zdc_biscuit;
+ zdoor_cookie_free(cookie);
+ }
+ } else {
+ zdoor_debug("zdoor_close: didn't find door in ztree");
+ }
+
+ (void) pthread_mutex_unlock(&(handle->zdh_lock));
+
+ zdoor_debug("zdoor_close: returning %p", biscuit);
+ return (biscuit);
+}