door.c revision ab32bdf2f746488f918233b2d8cabd5835efe9f3
/*
* 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.
*/
/*
* This file contains the routines that service the libdoor(3LIB) interface.
* This interface is intended for use by an external GUI utility to provide
* status information to users and allow control over nwam behavior in certain
* situations.
*
* The daemon has one active thread for each door call. Typically, a client
* will make a blocking call to await new events, and if an active client is
* busy, we will enqueue a small number of events here. If too many are
* enqueued, then we begin dropping events, and a single special "lost" event
* is placed in the queue. Clients are expected to start over at that point.
*
* For client events that require a response from the client, the server must
* assume a response if "lost" occurs or if there are no clients.
*
* When no clients are present, we just drop events. No history is maintained.
*
* Thread cancellation notes: In general, we disable cancellation for all
* calls, as allowing cancellation would require special handlers throughout
* the nwamd code to deal with the release of locks taken in various contexts.
* Instead, we allow it to run to completion on the assumption that all calls
* are expected to run without significant blocking.
*
* The one exception to this is the event-wait function, which intentionally
* blocks indefinitely. This request must enable cancellation so that an idle
* client can be terminated cleanly.
*/
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <pthread.h>
#include <door.h>
#include <alloca.h>
#include <fcntl.h>
#include <errno.h>
#include <syslog.h>
#include <string.h>
#include <pwd.h>
#include <auth_attr.h>
#include <auth_list.h>
#include <secdb.h>
#include <bsm/adt_event.h>
#include "defines.h"
#include "structures.h"
#include "functions.h"
#include "variables.h"
/* Idle time before declaring a client to be dead. */
static int door_fd = -1;
static adt_session_data_t *cur_ah;
/*
* event_queue is a simple circular queue of fixed size. 'evput' is the next
* location to write, and 'evget' is the next waiting event. The queue size is
* chosen so that it's extremely unlikely that a functioning GUI could get this
* far behind on events and still be at all usable. (Too large, and we'd wait
* too long backing off to automatic mode on a broken GUI. Too small, and an
* easily.)
*/
#define MAX_DESCR_EVENTS 64
static struct wireless_lan *current_wlans;
static size_t current_wlansize;
/*
* This lock protects the event queue and the current_wlans list.
*/
/*
* sleep_cv is used to block waiting for new events to appear in an empty
* queue. client_cv is used to wait for event client threads to wake up and
* return before shutting down the daemon.
*/
static uint_t sleeping_clients;
static boolean_t active_clients;
static uint32_t client_expire;
/*
* Register a "user logout" event with the auditing system.
* A "logout" occurs when the GUI stops calling the event wait system (detected
* either by idle timer or queue overflow), or when a different authorized user
* calls the daemon (the previous one is logged out), or when the daemon itself
* is shut down.
*/
static void
audit_detach(void)
{
(void) adt_end_session(cur_ah);
}
/*
* Register either a normal "user login" event (if 'attached' is set) or a
* failed login (if 'attached' is not set) with auditing.
*/
static void
{
return;
}
goto failure;
}
goto failure;
}
if (attached) {
} else {
}
if (retv != 0) {
goto failure;
}
/*
* Only successful attach records result in a detach. All else have
* (at most) a single failure record, and nothing else. Thus, we do
* not set cur_ah until we know we've written an attach record.
*/
if (attached) {
return;
}
(void) adt_end_session(ah);
}
/* Convert descriptive event to a text name for debug log */
static const char *
{
/*
* Cast to int so that compiler and lint don't complain about extra
* 'default' case, and so that we can handle stray values.
*/
switch ((int)evt) {
case deInitial:
return ("Initial");
case deInterfaceUp:
return ("InterfaceUp");
case deInterfaceDown:
return ("InterfaceDown");
case deInterfaceAdded:
return ("InterfaceAdded");
case deInterfaceRemoved:
return ("InterfaceRemoved");
case deWlanConnectFail:
return ("WlanConnectFail");
case deWlanDisconnect:
return ("WlanDisconnect");
case deWlanConnected:
return ("WlanConnected");
case deLLPSelected:
return ("LLPSelected");
case deLLPUnselected:
return ("LLPUnselected");
case deULPActivated:
return ("ULPActivated");
case deULPDeactivated:
return ("ULPDeactivated");
case deScanChange:
return ("ScanChange");
case deScanSame:
return ("ScanSame");
case deWlanKeyNeeded:
return ("WlanKeyNeeded");
case deWlanSelectionNeeded:
return ("WlanSelectionNeeded");
default:
return ("unknown");
}
}
/*
* This is called only by ndcWaitEvent, which holds event_lock until it has
* copied out the data from the entry.
*/
static const nwam_descr_event_t *
get_descr_event(void)
{
if (!active_clients) {
return (&init);
}
return (NULL);
evget = event_queue;
/* If this event has a new WLAN snapshot, then update */
}
return (nde);
}
/*
* {start,put}_descr_event are called by the reporting functions. This
* function starts a new descriptive event and returns with the lock held (if
* the return value is non-NULL).
*/
static nwam_descr_event_t *
{
dprintf("dropping event %s; no active client",
return (NULL);
}
audit_detach();
(void) pthread_mutex_unlock(&event_lock);
return (NULL);
} else {
return (nde);
}
}
/* Finish reporting the event; must not be called if nde is NULL */
static void
{
dprintf("putting descr event %s %s",
sizeof (nde->nde_interface));
nde = event_queue;
(void) pthread_cond_signal(&sleep_cv);
}
/* Cannot drop the lock unless we've acquired it. */
(void) pthread_mutex_unlock(&event_lock);
}
/*
* Finish reporting an event that sets the WLAN snapshot. If there's no
* client, then update the saved snapshot right now, as we won't be queuing the
* event.
*/
static boolean_t
{
struct wireless_lan *saved_wlans;
return (B_FALSE);
}
return (B_TRUE);
} else {
/* If the UI isn't running, then save the cached results */
if (pthread_mutex_lock(&event_lock) == 0) {
(void) pthread_mutex_unlock(&event_lock);
} else {
}
return (B_FALSE);
}
}
void
{
}
}
void
{
}
}
void
report_interface_added(const char *ifname)
{
}
void
report_interface_removed(const char *ifname)
{
}
void
report_wlan_connect_fail(const char *ifname)
{
}
void
{
}
}
void
{
}
}
void
report_llp_selected(const char *ifname)
{
}
void
{
}
}
void
report_ulp_activated(const char *ulpname)
{
}
void
report_ulp_deactivated(const char *ulpname)
{
}
void
{
}
{
return (B_TRUE);
} else {
return (B_FALSE);
}
}
int wlan_cnt)
{
}
/* ARGSUSED */
static void
thread_cancel_handler(void *arg)
{
if (--sleeping_clients == 0) {
(void) pthread_cond_signal(&client_cv);
/*
* On the wrong thread; must call start_timer from the main
* thread.
*/
if (client_expire < timer_expire)
}
(void) pthread_mutex_unlock(&event_lock);
}
/*
* A timer is set when there are waiting event collectors. If there haven't
* been any collectors for "a long time," then we assume that the user
* interface has been terminated or is jammed.
*/
void
{
if (active_clients && sleeping_clients == 0) {
if (client_expire > now) {
} else {
"no active door clients left; flushing queue");
if (pthread_mutex_lock(&event_lock) == 0) {
(void) np_queue_add_event(EV_RESELECT,
NULL);
}
audit_detach();
(void) pthread_mutex_unlock(&event_lock);
}
}
}
}
/*
* This is called for an unrecognized UID. We check to see if the user is
* authorized to issue commands to the NWAM daemon.
*/
static boolean_t
{
} else {
}
if (pthread_mutex_lock(&event_lock) == 0) {
if (attached) {
audit_detach();
}
(void) pthread_mutex_unlock(&event_lock);
} else {
}
return (attached);
}
/* ARGSUSED */
static void
{
/* LINTED: alignment */
int retv = -1;
return;
}
return;
}
/*
* Only the blocking event wait can be canceled, and then only when
* headed for a block.
*/
case ndcNull:
dprintf("door: null event from client");
retv = 0;
break;
case ndcWaitEvent: {
const nwam_descr_event_t *nde;
break;
(void) pthread_mutex_unlock(&event_lock);
dprintf("door: returning waiting event %s",
NULL, 0);
return;
}
door_fd != -1) {
break;
}
break;
}
dprintf("door: returning waited-for event %s",
return;
}
case ndcGetLLPList: {
/*
* Note that door_return never returns here, so we can't just
* use malloc'd memory. Copy over to a stack-allocated buffer
* and do the string pointer fix-ups.
*/
break;
errno = 0;
(void) pthread_mutex_unlock(&machine_lock);
sizeof (nld->nld_selected));
sizeof (nld->nld_locked));
while (count-- > 0) {
llp->llp_ipv4addrstr -=
llp->llp_ipv6addrstr -=
llp++;
}
} else {
dprintf("door: no LLP list to get");
break;
}
dprintf("door: get llp list returning %d entries",
return;
}
case ndcSetLLPPriority:
break;
dprintf("door: set priority on %s to %d",
(void) pthread_mutex_unlock(&machine_lock);
break;
case ndcLockLLP:
break;
dprintf("door: unlocking llp selection");
else
(void) pthread_mutex_unlock(&machine_lock);
break;
case ndcGetWlanList: {
char *wlans;
/*
* We protect ourselves here against a malicious or confused
* user. The list is stable only while we're holding the lock,
* and the lock can't be held during the door return.
*/
break;
if (current_wlans == NULL) {
(void) pthread_mutex_unlock(&event_lock);
dprintf("door: no WLAN list to get");
break;
}
(void) pthread_mutex_unlock(&event_lock);
dprintf("door: get wlan list returning %lu bytes",
return;
}
case ndcGetKnownAPList: {
/*
* Note that door_return never returns here, so we can't just
* use malloc'd memory. Copy over to a stack-allocated buffer
* and do the string pointer fix-ups.
*/
errno = 0;
while (count-- > 0) {
kap++;
}
} else {
dprintf("door: no known AP list to get");
break;
}
dprintf("door: get known AP list returning %u entries",
return;
}
case ndcAddKnownAP:
dprintf("door: adding known AP %s %s",
break;
case ndcDeleteKnownAP:
dprintf("door: removing known AP %s %s",
break;
case ndcSelectWlan:
break;
dprintf("door: selecting WLAN on %s as %s %s",
/*
* Check if we're already connected to the requested
* changing anything. Otherwise, tear down the interface
* (disconnecting from the WLAN) and set up again.
*/
retv = 0;
} else {
if (link_layer_profile != NULL)
}
(void) pthread_mutex_unlock(&machine_lock);
break;
case ndcWlanKey:
break;
dprintf("door: selecting WLAN key on %s for %s %s",
(void) pthread_mutex_unlock(&machine_lock);
break;
case ndcStartRescan:
dprintf("door: rescan requested on %s",
ndc->ndc_interface);
break;
}
break;
(void) pthread_mutex_unlock(&machine_lock);
break;
default:
break;
}
if (retv != 0)
dprintf("door: returning to caller with error %d (%s)",
}
static void
door_cleanup(void)
{
if (door_fd != -1) {
(void) door_revoke(door_fd);
door_fd = -1;
}
(void) unlink(DOOR_FILENAME);
}
void
terminate_door(void)
{
door_cleanup();
if (pthread_mutex_lock(&event_lock) != 0)
return;
if (sleeping_clients != 0)
while (sleeping_clients != 0) {
(void) pthread_cond_broadcast(&sleep_cv);
break;
}
audit_detach();
(void) pthread_mutex_unlock(&event_lock);
}
void
initialize_door(void)
{
int did;
/* Do a low-overhead "touch" on the file that will be the door node. */
if (did != -1) {
}
(void) atexit(door_cleanup);
/* Create the door. */
if (door_fd == -1) {
}
/* Attach the door to the file. */
(void) fdetach(DOOR_FILENAME);
}
}