/* * 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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "util.h" #include "events.h" #include "ncp.h" #include "ncu.h" #include "objects.h" /* * door_if.c * This file contains functions which implement the command interface to * nwam via the door NWAM_DOOR. Doors provide a LPC mechanism that allows * for threads in one process to cause code to execute in another process. * Doors also provide the ability to pass data and file descriptors. See * libdoor(3LIB) for more information. * * This file exports one function, door_initialize() which sets up that door. * It sets up the static routine door_switch() to be called when a client * calls the door (via door_call(3C)). The structure nwam_request_t is * passed as data and contains data to specify the type of action requested * and any data need to meet that request. Comments in that routine specify * how each of those requests work. */ /* * This routine is the door routine for NWAM_DOOR. It's executed anytime a * client does a door_call(3C) on NWAM_DOOR. Note that this is executed in * a thread managed by the door library. door_return(3C) should not return * normally as it's returning to the call from another process. */ static void door_switch(void *cookie, char *argp, size_t arg_size, door_desc_t *dp, uint_t n_desc) { nwamd_door_arg_t *req, *res = NULL; size_t res_size = sizeof (nwamd_door_arg_t); uint_t num_wlans; nwamd_object_t obj = NULL; nwamd_ncu_t *ncu; ucred_t *ucr = NULL; uid_t uid; struct passwd *pwd; cookie = cookie; arg_size = arg_size; dp = dp; n_desc = n_desc; /* LINTED E_BAD_PTR_CAST_ALIGN */ req = (nwamd_door_arg_t *)argp; req->error = NWAM_SUCCESS; if (door_ucred(&ucr) != 0) { nlog(LOG_ERR, "door_switch: door_ucred failed: %s", strerror(errno)); req->error = NWAM_ERROR_INTERNAL; goto done; } uid = ucred_getruid(ucr); if ((pwd = getpwuid(uid)) == NULL) { nlog(LOG_ERR, "door_switch: getpwuid failed: %s", strerror(errno)); endpwent(); req->error = NWAM_ERROR_INTERNAL; goto done; } switch (req->type) { /* *** Requests to register/unregister for events *** */ case NWAM_REQUEST_TYPE_EVENT_REGISTER: if (chkauthattr(AUTOCONF_READ_AUTH, pwd->pw_name) == 0) { nlog(LOG_ERR, "door_switch: need AUTOCONF_READ_AUTH for " "register action"); req->error = NWAM_PERMISSION_DENIED; goto done; } req->error = nwam_event_queue_init (req->data.register_info.name); if (req->error != NWAM_SUCCESS) { nlog(LOG_ERR, "door_switch: could not register " "events for %s", req->data.register_info.name); } goto done; case NWAM_REQUEST_TYPE_EVENT_UNREGISTER: nwam_event_queue_fini(req->data.register_info.name); goto done; case NWAM_REQUEST_TYPE_WLAN_SCAN: nlog(LOG_DEBUG, "door_switch: processing WLAN scan request: " "link %s", req->data.wlan_info.name); req->error = nwamd_wlan_scan(req->data.wlan_info.name); goto done; case NWAM_REQUEST_TYPE_WLAN_SCAN_RESULTS: nlog(LOG_DEBUG, "door_switch: processing WLAN scan results " "request: link %s", req->data.wlan_info.name); obj = nwamd_ncu_object_find(NWAM_NCU_TYPE_LINK, req->data.wlan_info.name); if (obj == NULL) { nlog(LOG_ERR, "door_switch: link %s not found", req->data.wlan_info.name); req->error = NWAM_ENTITY_NOT_FOUND; goto done; } ncu = obj->object_data; num_wlans = ncu->ncu_node.u_link.wifi_num_wlans; res_size = (num_wlans * sizeof (nwam_wlan_t)) + sizeof (nwamd_door_arg_t); if ((res = malloc(res_size)) == NULL) { nwamd_object_unlock(obj); req->error = NWAM_NO_MEMORY; goto done; } (void) memcpy(res, req, sizeof (nwamd_door_arg_t)); (void) memcpy(res->data.wlan_info.wlans, ncu->ncu_node.u_link.wifi_scandata, num_wlans * sizeof (nwam_wlan_t)); res->data.wlan_info.num_wlans = num_wlans; nlog(LOG_DEBUG, "door_if: returning %d scan results", num_wlans); nwamd_object_unlock(obj); res->error = NWAM_SUCCESS; goto done; case NWAM_REQUEST_TYPE_WLAN_SELECT: nlog(LOG_DEBUG, "door_switch: processing WLAN selection : " "link %s ESSID %s , BSSID %s", req->data.wlan_info.name, req->data.wlan_info.essid, req->data.wlan_info.bssid); req->error = nwamd_wlan_select(req->data.wlan_info.name, req->data.wlan_info.essid, req->data.wlan_info.bssid, req->data.wlan_info.add_to_known_wlans); goto done; case NWAM_REQUEST_TYPE_WLAN_SET_KEY: nlog(LOG_DEBUG, "door_switch: processing WLAN key input : " "link %s ESSID %s BSSID %s", req->data.wlan_info.name, req->data.wlan_info.essid, req->data.wlan_info.bssid); req->error = nwamd_wlan_set_key(req->data.wlan_info.name, req->data.wlan_info.essid, NULL, req->data.wlan_info.security_mode, req->data.wlan_info.keyslot, req->data.wlan_info.key); goto done; /* Request nwamd to perform enable, disable, refresh, destroy, rename */ case NWAM_REQUEST_TYPE_ACTION: { char *name = req->data.object_action.name; nwam_action_t action = req->data.object_action.action; nwam_object_type_t object_type = req->data.object_action.object_type; char *obj_type_str = (char *)nwam_object_type_to_string(object_type); switch (action) { case NWAM_ACTION_ENABLE: if (chkauthattr(AUTOCONF_WRITE_AUTH, pwd->pw_name) == 0 || chkauthattr(AUTOCONF_REFRESH_AUTH, pwd->pw_name) == 0) { nwam_record_audit_event(ucr, ADT_nwam_enable, name, obj_type_str, ADT_FAILURE, ADT_FAIL_VALUE_AUTH); nlog(LOG_ERR, "door_switch: need AUTOCONF_WRITE_AUTH and " "AUTOCONF_REFRESH_AUTH for enable action"); req->error = NWAM_PERMISSION_DENIED; goto done; } nwam_record_audit_event(ucr, ADT_nwam_enable, name, obj_type_str, ADT_SUCCESS, ADT_SUCCESS); nlog(LOG_DEBUG, "door_switch: activating %s", name); switch (object_type) { case NWAM_OBJECT_TYPE_ENM: obj = nwamd_object_find(NWAM_OBJECT_TYPE_ENM, name); if (obj == NULL) { nlog(LOG_ERR, "door_switch: " "ENM %s not found", name); req->error = NWAM_ENTITY_NOT_FOUND; goto done; } if (obj->object_state == NWAM_STATE_ONLINE) { nlog(LOG_DEBUG, "door_switch: " "ENM %s is already enabled", name); req->error = NWAM_ENTITY_IN_USE; nwamd_object_unlock(obj); goto done; } nwamd_object_unlock(obj); req->error = nwamd_enm_action(name, action); break; case NWAM_OBJECT_TYPE_LOC: if (strcmp(name, active_loc) == 0) { nlog(LOG_DEBUG, "door_switch: " "loc %s is already enabled", name); req->error = NWAM_ENTITY_IN_USE; goto done; } else { req->error = nwamd_loc_action(name, action); } break; case NWAM_OBJECT_TYPE_NCU: obj = nwamd_object_find(NWAM_OBJECT_TYPE_NCU, name); if (obj == NULL) { nlog(LOG_ERR, "door_switch: " "NCU %s not found", name); req->error = NWAM_ENTITY_NOT_FOUND; goto done; } if (obj->object_state == NWAM_STATE_ONLINE) { nlog(LOG_DEBUG, "door_switch: " "NCU %s is already enabled", name); req->error = NWAM_ENTITY_IN_USE; nwamd_object_unlock(obj); goto done; } nwamd_object_unlock(obj); req->error = nwamd_ncu_action(name, action); break; case NWAM_OBJECT_TYPE_NCP: if (strcmp(name, active_ncp) == 0) { nlog(LOG_DEBUG, "door_switch: " "NCP %s is already enabled", name); req->error = NWAM_ENTITY_IN_USE; goto done; } else { req->error = nwamd_ncp_action(name, action); } break; default: nlog(LOG_ERR, "door_switch: received invalid " "object type %d (%s)", object_type, nwam_object_type_to_string(object_type)); req->status = NWAM_REQUEST_STATUS_UNKNOWN; goto door_return; } if (req->error != 0) { nlog(LOG_ERR, "door_switch: %s could not be " "enabled", name); } goto done; case NWAM_ACTION_DISABLE: if (chkauthattr(AUTOCONF_WRITE_AUTH, pwd->pw_name) == 0 || chkauthattr(AUTOCONF_REFRESH_AUTH, pwd->pw_name) == 0) { nwam_record_audit_event(ucr, ADT_nwam_disable, name, obj_type_str, ADT_FAILURE, ADT_FAIL_VALUE_AUTH); nlog(LOG_ERR, "door_switch: need AUTOCONF_WRITE_AUTH and " "AUTOCONF_REFRESH_AUTH for disable action"); req->error = NWAM_PERMISSION_DENIED; goto done; } nwam_record_audit_event(ucr, ADT_nwam_disable, name, obj_type_str, ADT_SUCCESS, ADT_SUCCESS); nlog(LOG_DEBUG, "door_switch: deactivating %s", name); switch (object_type) { case NWAM_OBJECT_TYPE_ENM: obj = nwamd_object_find(NWAM_OBJECT_TYPE_ENM, name); if (obj == NULL) { nlog(LOG_ERR, "door_switch: " "ENM %s not found", name); req->error = NWAM_ENTITY_NOT_FOUND; goto done; } if (obj->object_state == NWAM_STATE_DISABLED) { nlog(LOG_ERR, "door_switch: " "ENM %s is not enabled", name); req->error = NWAM_ENTITY_INVALID_STATE; nwamd_object_unlock(obj); goto done; } nwamd_object_unlock(obj); req->error = nwamd_enm_action(name, action); break; case NWAM_OBJECT_TYPE_LOC: if (strcmp(name, active_loc) != 0) { nlog(LOG_DEBUG, "door_switch: " "loc %s is not enabled", name); req->error = NWAM_ENTITY_INVALID_STATE; goto done; } else { req->error = nwamd_loc_action(name, action); } break; case NWAM_OBJECT_TYPE_NCU: obj = nwamd_object_find(NWAM_OBJECT_TYPE_NCU, name); if (obj == NULL) { nlog(LOG_ERR, "door_switch: " "NCU %s not found", name); req->error = NWAM_ENTITY_NOT_FOUND; goto done; } if (obj->object_state == NWAM_STATE_DISABLED) { nlog(LOG_ERR, "door_switch: " "NCU %s is not enabled", name); req->error = NWAM_ENTITY_INVALID_STATE; nwamd_object_unlock(obj); goto done; } nwamd_object_unlock(obj); req->error = nwamd_ncu_action(name, action); break; case NWAM_OBJECT_TYPE_NCP: nlog(LOG_ERR, "door_switch: NCPs can not be " "disabled"); req->error = NWAM_INVALID_ARG; goto done; default: nlog(LOG_ERR, "door_switch: received invalid " "object type %d (%s)", object_type, nwam_object_type_to_string(object_type)); req->status = NWAM_REQUEST_STATUS_UNKNOWN; goto door_return; } if (req->error != 0) { nlog(LOG_ERR, "door_switch: %s could not be " "disabled", name); } goto done; case NWAM_ACTION_ADD: case NWAM_ACTION_REFRESH: /* * Called whenever an object is committed in the * library. Reread that committed object into nwamd. */ if (chkauthattr(AUTOCONF_REFRESH_AUTH, pwd->pw_name) == 0) { nlog(LOG_ERR, "door_switch: need AUTOCONF_REFRESH_AUTH " "for refresh action"); req->error = NWAM_PERMISSION_DENIED; goto done; } nlog(LOG_DEBUG, "door_switch: refreshing %s", name); switch (object_type) { case NWAM_OBJECT_TYPE_ENM: req->error = nwamd_enm_action(name, action); break; case NWAM_OBJECT_TYPE_LOC: req->error = nwamd_loc_action(name, action); break; case NWAM_OBJECT_TYPE_KNOWN_WLAN: req->error = nwamd_known_wlan_action(name, action); break; case NWAM_OBJECT_TYPE_NCU: /* * An NCU was committed. Only refresh the * NCU if it is part of the active NCP. */ if (strcmp(req->data.object_action.parent, active_ncp) == 0) { req->error = nwamd_ncu_action(name, action); } else { nlog(LOG_DEBUG, "door_switch: %s's ncp " "%s is not active, nothing to do", name, req->data.object_action.parent); } break; default: nlog(LOG_ERR, "door_switch: received invalid " "object type %d (%s)", object_type, nwam_object_type_to_string(object_type)); req->status = NWAM_REQUEST_STATUS_UNKNOWN; goto door_return; } if (req->error != 0) { nlog(LOG_ERR, "door_switch: %s could not be " "refreshed", name); } goto done; case NWAM_ACTION_RENAME: { const char *o_name = req->data.object_action.orig_name; const char *n_name = req->data.object_action.name; /* * Object has been renamed, orig_name has the old * name. Simply, _DESTROY the old object and * _REFRESH the new one. */ if (chkauthattr(AUTOCONF_WRITE_AUTH, pwd->pw_name) == 0 || chkauthattr(AUTOCONF_REFRESH_AUTH, pwd->pw_name) == 0) { nlog(LOG_ERR, "door_switch: need AUTOCONF_WRITE_AUTH and " "AUTOCONF_REFRESH_AUTH for rename action"); req->error = NWAM_PERMISSION_DENIED; goto done; } nlog(LOG_DEBUG, "door_switch: renaming %s to %s", o_name, n_name); switch (object_type) { case NWAM_OBJECT_TYPE_ENM: if ((req->error = nwamd_enm_action(o_name, NWAM_ACTION_DESTROY)) == 0) { req->error = nwamd_enm_action(n_name, NWAM_ACTION_REFRESH); } break; case NWAM_OBJECT_TYPE_LOC: if ((req->error = nwamd_loc_action(o_name, NWAM_ACTION_DESTROY)) == 0) { req->error = nwamd_loc_action(n_name, NWAM_ACTION_REFRESH); } break; case NWAM_OBJECT_TYPE_KNOWN_WLAN: if ((req->error = nwamd_known_wlan_action (o_name, NWAM_ACTION_DESTROY)) == 0) { req->error = nwamd_known_wlan_action (n_name, NWAM_ACTION_REFRESH); } break; case NWAM_OBJECT_TYPE_NCU: /* * NCU was renamed. Only _DESTORY and * _REFRESH if it if part of the active NCP. */ if (strcmp(req->data.object_action.parent, active_ncp) == 0) { if ((req->error = nwamd_ncu_action (o_name, NWAM_ACTION_DESTROY)) == 0) { req->error = nwamd_ncu_action (n_name, NWAM_ACTION_REFRESH); } } else { nlog(LOG_DEBUG, "door_switch: %s's ncp " "%s is not active, nothing to do", n_name, req->data.object_action.parent); } break; default: nlog(LOG_ERR, "door_switch: received invalid " "object type %d (%s)", object_type, nwam_object_type_to_string(object_type)); req->status = NWAM_REQUEST_STATUS_UNKNOWN; goto door_return; } if (req->error != 0) { nlog(LOG_ERR, "door_switch: %s could not be " "renamed to %s", o_name, n_name); } goto done; } case NWAM_ACTION_DESTROY: /* * Object was destroyed, remove from nwamd. * Library has already made sure that the object is * not active. */ if (chkauthattr(AUTOCONF_WRITE_AUTH, pwd->pw_name) == 0 || chkauthattr(AUTOCONF_REFRESH_AUTH, pwd->pw_name) == 0) { nlog(LOG_ERR, "door_switch: need AUTOCONF_WRITE_AUTH and " "AUTOCONF_REFRESH_AUTH for destroy action"); req->error = NWAM_PERMISSION_DENIED; goto done; } nlog(LOG_DEBUG, "door_switch: removing %s", name); switch (object_type) { case NWAM_OBJECT_TYPE_ENM: req->error = nwamd_enm_action(name, NWAM_ACTION_DESTROY); break; case NWAM_OBJECT_TYPE_LOC: req->error = nwamd_loc_action(name, NWAM_ACTION_DESTROY); break; case NWAM_OBJECT_TYPE_KNOWN_WLAN: req->error = nwamd_known_wlan_action(name, NWAM_ACTION_DESTROY); break; case NWAM_OBJECT_TYPE_NCU: /* * Make sure parent NCP is not active. * Nothing to do for NCU of inactive NCP. */ if (strcmp(req->data.object_action.parent, active_ncp) == 0) { nlog(LOG_ERR, "door_switch: %s's ncp " "%s is active, cannot destroy", name, req->data.object_action.parent); req->error = NWAM_ENTITY_IN_USE; } else { nlog(LOG_DEBUG, "door_switch: %s's ncp " "is not active, nothing to do", name, req->data.object_action.parent); } break; case NWAM_OBJECT_TYPE_NCP: if (strcmp(req->data.object_action.parent, active_ncp) == 0) { nlog(LOG_ERR, "door_switch: %s is " "active, cannot destroy", name); req->error = NWAM_ENTITY_IN_USE; } else { nlog(LOG_DEBUG, "door_switch: %s is " "not active, nothing to do", name); } break; default: nlog(LOG_ERR, "door_switch: received invalid " "object type %d (%s)", object_type, nwam_object_type_to_string(object_type)); req->status = NWAM_REQUEST_STATUS_UNKNOWN; goto door_return; } if (req->error != NWAM_SUCCESS) { nlog(LOG_ERR, "door_switch: %s could not be " "destroyed", name); } goto done; default: nlog(LOG_ERR, "door_switch: received unknown " "action %d (%s)", action, nwam_action_to_string(action)); req->status = NWAM_REQUEST_STATUS_UNKNOWN; goto door_return; } /* NOTREACHED */ goto done; } /* Request the state from nwamd */ case NWAM_REQUEST_TYPE_STATE: { const char *name = req->data.object_state.name; nwam_object_type_t object_type = req->data.object_state.object_type; if (chkauthattr(AUTOCONF_READ_AUTH, pwd->pw_name) == 0) { nlog(LOG_ERR, "door_switch: need AUTOCONF_READ_AUTH for " "request state action"); req->error = NWAM_PERMISSION_DENIED; goto done; } req->error = NWAM_SUCCESS; nlog(LOG_DEBUG, "door_switch: retrieving state for %s", name); switch (object_type) { case NWAM_OBJECT_TYPE_LOC: if (strcmp(active_loc, name) == 0) { req->data.object_state.state = NWAM_STATE_ONLINE; req->data.object_state.aux_state = NWAM_AUX_STATE_ACTIVE; nlog(LOG_DEBUG, "door_switch: loc %s is active", name); } else { /* get actual state from object */ obj = nwamd_object_find(NWAM_OBJECT_TYPE_LOC, name); if (obj == NULL) { nlog(LOG_ERR, "door_switch: loc %s not found", name); req->error = NWAM_ENTITY_NOT_FOUND; goto done; } nlog(LOG_DEBUG, "door_switch: loc %s is %s", name, nwam_state_to_string(obj->object_state)); req->data.object_state.state = obj->object_state; req->data.object_state.aux_state = obj->object_aux_state; nwamd_object_unlock(obj); } goto done; case NWAM_OBJECT_TYPE_NCP: if (strcmp(active_ncp, name) == 0) { req->data.object_state.state = NWAM_STATE_ONLINE; req->data.object_state.aux_state = NWAM_AUX_STATE_ACTIVE; nlog(LOG_DEBUG, "door_switch: NCP %s is active", name); } else { req->data.object_state.state = NWAM_STATE_OFFLINE; req->data.object_state.aux_state = NWAM_AUX_STATE_CONDITIONS_NOT_MET; nlog(LOG_DEBUG, "door_switch: " "NCP %s is inactive", name); } goto done; case NWAM_OBJECT_TYPE_ENM: obj = nwamd_object_find(NWAM_OBJECT_TYPE_ENM, name); if (obj == NULL) { nlog(LOG_ERR, "door_switch: ENM %s not found", name); req->error = NWAM_ENTITY_NOT_FOUND; goto done; } nlog(LOG_DEBUG, "door_switch: ENM %s is %s", name, nwam_state_to_string(obj->object_state)); req->data.object_state.state = obj->object_state; req->data.object_state.aux_state = obj->object_aux_state; nwamd_object_unlock(obj); goto done; case NWAM_OBJECT_TYPE_NCU: obj = nwamd_object_find(NWAM_OBJECT_TYPE_NCU, name); if (obj == NULL) { nlog(LOG_ERR, "door_switch: NCU %s not found", name); req->error = NWAM_ENTITY_NOT_FOUND; goto done; } nlog(LOG_DEBUG, "door_switch: NCU %s is %s", name, nwam_state_to_string(obj->object_state)); req->data.object_state.state = obj->object_state; req->data.object_state.aux_state = obj->object_aux_state; nwamd_object_unlock(obj); goto done; default: nlog(LOG_ERR, "door_switch: received invalid " "object type %d (%s)", object_type, nwam_object_type_to_string(object_type)); req->status = NWAM_REQUEST_STATUS_UNKNOWN; goto door_return; } /* NOTREACHED */ goto done; } /* Request the current priority-group from nwamd */ case NWAM_REQUEST_TYPE_PRIORITY_GROUP: { if (chkauthattr(AUTOCONF_READ_AUTH, pwd->pw_name) == 0) { nlog(LOG_ERR, "door_switch: need AUTOCONF_READ_AUTH for " "request priority-group action"); req->error = NWAM_PERMISSION_DENIED; goto done; } nlog(LOG_DEBUG, "door_switch: " "retrieving active priority-group: %d", current_ncu_priority_group); req->data.priority_group_info.priority = current_ncu_priority_group; req->error = NWAM_SUCCESS; goto done; } /* Somebody talking gibberish to us? */ default: nlog(LOG_ERR, "door_switch: received unknown request type %d", req->type); req->status = NWAM_REQUEST_STATUS_UNKNOWN; goto door_return; } done: if (res == NULL) res = req; if (res->error == NWAM_SUCCESS) res->status = NWAM_REQUEST_STATUS_OK; else res->status = NWAM_REQUEST_STATUS_FAILED; door_return: ucred_free(ucr); endpwent(); if (door_return((char *)res, res_size, NULL, 0) == -1) { nlog(LOG_ERR, "door_switch: type %d door_return failed: %s", req->type, strerror(errno)); } } /* * We initialize the nwamd door here. Failure to have this happen is critical * to the daemon so we log a message and pass up notice to the caller who * will most likely abort trying to start. This routine is meant to only * be called once. */ boolean_t door_initialize(void) { const int door_mode = 0444; int doorfd; struct stat buf; if ((doorfd = door_create(door_switch, NULL, DOOR_NO_CANCEL)) == -1) { nlog(LOG_ERR, "Unable to create door: %s", strerror(errno)); return (B_FALSE); } if (stat(NWAM_DOOR, &buf) < 0) { int nwam_door_fd; if ((nwam_door_fd = creat(NWAM_DOOR, door_mode)) < 0) { nlog(LOG_ERR, "Couldn't create door: %s", strerror(errno)); (void) door_revoke(doorfd); doorfd = -1; return (B_FALSE); } (void) close(nwam_door_fd); } else { if (buf.st_mode != door_mode) { if (chmod(NWAM_DOOR, door_mode) == -1) { nlog(LOG_ERR, "couldn't change mode of %s: %s", NWAM_DOOR, strerror(errno)); } } } /* cleanup anything hanging around from a previous invocation */ (void) fdetach(NWAM_DOOR); /* Place our door in the file system so that others can find us. */ if (fattach(doorfd, NWAM_DOOR) < 0) { nlog(LOG_ERR, "Couldn't attach door: %s", strerror(errno)); (void) door_revoke(doorfd); doorfd = -1; return (B_FALSE); } /* * Note we lost the reference to doorfd and it will be cleaned up * when we exit. Our "reference" to it is via door_switch(). */ return (B_TRUE); }