OpenSolaris_b135/cmd/rcm_daemon/common/rcm_event.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (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 (c) 1999-2001 by Sun Microsystems, Inc.
 * All rights reserved.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include <door.h>
#include <assert.h>
#include <sys/acl.h>
#include <sys/stat.h>
#include <librcm_event.h>

#include "rcm_impl.h"

/*
 * Event handling routine
 */

#define	RCM_NOTIFY	0
#define	RCM_GETINFO	1
#define	RCM_REQUEST	2
#define	RCM_EFAULT	3
#define	RCM_EPERM	4
#define	RCM_EINVAL	5

static void process_event(int, int, nvlist_t *, nvlist_t **);
static void generate_reply_event(int, rcm_info_t *, nvlist_t **);
static void rcm_print_nvlist(nvlist_t *);

/*
 * Top level function for event service
 */
void
event_service(void **data, size_t *datalen)
{
	int cmd;
	int lerrno;
	int seq_num;
	nvlist_t *nvl;
	nvlist_t *ret;

	rcm_log_message(RCM_TRACE1, "received door operation\n");

	/* Decode the data from the door into an unpacked nvlist */
	if (data == NULL || datalen == NULL) {
		rcm_log_message(RCM_ERROR, "received null door argument\n");
		return;
	}
	if (lerrno = nvlist_unpack(*data, *datalen, &nvl, 0)) {
		rcm_log_message(RCM_ERROR, "received bad door argument, %s\n",
		    strerror(lerrno));
		return;
	}

	/* Do nothing if the door is just being knocked on */
	if (errno = nvlist_lookup_int32(nvl, RCM_CMD, &cmd)) {
		rcm_log_message(RCM_ERROR,
		    "bad door argument (nvlist_lookup=%s)\n", strerror(errno));
		nvlist_free(nvl);
		return;
	}
	if (cmd == CMD_KNOCK) {
		rcm_log_message(RCM_TRACE1, "door event was just a knock\n");
		nvlist_free(nvl);
		*data = NULL;
		*datalen = 0;
		return;
	}

	/*
	 * Go increment thread count. Before daemon is fully initialized,
	 * the event processing blocks inside this function.
	 */
	seq_num = rcmd_thr_incr(cmd);

	process_event(cmd, seq_num, nvl, &ret);
	nvlist_free(nvl);
	assert(ret != NULL);

	/*
	 * Decrement thread count
	 */
	rcmd_thr_decr();

out:
	*data = ret;
	*datalen = 0;
}

/*
 * Actually processes events; returns a reply event
 */
static void
process_event(int cmd, int seq_num, nvlist_t *nvl, nvlist_t **ret)
{
	int i;
	int error;
	uint_t nvl_nrsrcs = 0;
	pid_t pid;
	uint32_t flag = (uint32_t)0;
	uint64_t pid64 = (uint64_t)0;
	size_t buflen = 0;
	size_t interval_size = 0;
	timespec_t *interval = NULL;
	nvlist_t *change_data = NULL;
	nvlist_t *event_data = NULL;
	rcm_info_t *info = NULL;
	char *modname = NULL;
	char *buf = NULL;
	char **rsrcnames = NULL;
	char **nvl_rsrcs = NULL;

	rcm_log_message(RCM_TRACE2, "servicing door command=%d\n", cmd);

	rcm_print_nvlist(nvl);

	/*
	 * Extract data from the door argument nvlist.  Not all arguments
	 * are needed; sanity checks are performed later.
	 */
	(void) nvlist_lookup_string_array(nvl, RCM_RSRCNAMES, &nvl_rsrcs,
	    &nvl_nrsrcs);
	(void) nvlist_lookup_string(nvl, RCM_CLIENT_MODNAME, &modname);
	(void) nvlist_lookup_uint64(nvl, RCM_CLIENT_ID, (uint64_t *)&pid64);
	pid = (pid_t)pid64;
	(void) nvlist_lookup_uint32(nvl, RCM_REQUEST_FLAG, (uint32_t *)&flag);
	(void) nvlist_lookup_byte_array(nvl, RCM_SUSPEND_INTERVAL,
	    (uchar_t **)&interval, &interval_size);
	(void) nvlist_lookup_byte_array(nvl, RCM_CHANGE_DATA, (uchar_t **)&buf,
	    &buflen);
	if (buf != NULL && buflen > 0) {
		(void) nvlist_unpack(buf, buflen, &change_data, 0);
		buf = NULL;
		buflen = 0;
	}
	(void) nvlist_lookup_byte_array(nvl, RCM_EVENT_DATA, (uchar_t **)&buf,
	    &buflen);
	if (buf != NULL && buflen > 0)
		(void) nvlist_unpack(buf, buflen, &event_data, 0);

	rsrcnames = s_calloc(nvl_nrsrcs + 1, sizeof (char *));
	for (i = 0; i < nvl_nrsrcs; i++) {
		rsrcnames[i] = nvl_rsrcs[i];
	}
	rsrcnames[nvl_nrsrcs] = NULL;

	/*
	 * Switch off the command being performed to do the appropriate
	 * sanity checks and dispatch the arguments to the appropriate
	 * implementation routine.
	 */
	switch (cmd) {
	case CMD_REGISTER:
		if ((modname == NULL) || (rsrcnames == NULL) ||
		    (rsrcnames[0] == NULL))
			goto faildata;
		error = add_resource_client(modname, rsrcnames[0], pid, flag,
		    &info);
		break;

	case CMD_UNREGISTER:
		if ((modname == NULL) || (rsrcnames == NULL) ||
		    (rsrcnames[0] == NULL))
			goto faildata;
		error = remove_resource_client(modname, rsrcnames[0], pid,
		    flag);
		break;

	case CMD_GETINFO:
		if ((rsrcnames == NULL) &&
		    ((flag & (RCM_DR_OPERATION | RCM_MOD_INFO)) == 0))
			goto faildata;
		if ((error = get_resource_info(rsrcnames, flag, seq_num, &info))
		    == EINVAL) {
			rcm_log_message(RCM_DEBUG,
			    "invalid argument in get info request\n");
			generate_reply_event(EINVAL, NULL, ret);
			return;
		}
		break;

	case CMD_SUSPEND:
		if ((rsrcnames == NULL) || (rsrcnames[0] == NULL) ||
		    (interval == NULL))
			goto faildata;
		error = process_resource_suspend(rsrcnames, pid, flag, seq_num,
		    interval, &info);
		break;

	case CMD_RESUME:
		if ((rsrcnames == NULL) || (rsrcnames[0] == NULL))
			goto faildata;
		error = notify_resource_resume(rsrcnames, pid, flag, seq_num,
		    &info);
		break;

	case CMD_OFFLINE:
		if ((rsrcnames == NULL) || (rsrcnames[0] == NULL))
			goto faildata;
		error = process_resource_offline(rsrcnames, pid, flag, seq_num,
		    &info);
		break;

	case CMD_ONLINE:
		if ((rsrcnames == NULL) || (rsrcnames[0] == NULL))
			goto faildata;
		error = notify_resource_online(rsrcnames, pid, flag, seq_num,
		    &info);
		break;

	case CMD_REMOVE:
		if ((rsrcnames == NULL) || (rsrcnames[0] == NULL))
			goto faildata;
		error = notify_resource_remove(rsrcnames, pid, flag, seq_num,
		    &info);
		break;

	case CMD_EVENT:
		if ((rsrcnames == NULL) || (rsrcnames[0] == NULL) ||
		    (event_data == NULL))
			goto faildata;
		error = notify_resource_event(rsrcnames[0], pid, flag, seq_num,
		    event_data, &info);
		nvlist_free(event_data);
		break;

	case CMD_REQUEST_CHANGE:
		if ((rsrcnames == NULL) || (rsrcnames[0] == NULL) ||
		    (change_data == NULL))
			goto faildata;
		error = request_capacity_change(rsrcnames[0], pid, flag,
		    seq_num, change_data, &info);
		nvlist_free(change_data);
		break;

	case CMD_NOTIFY_CHANGE:
		if ((rsrcnames == NULL) || (rsrcnames[0] == NULL) ||
		    (change_data == NULL))
			goto faildata;
		error = notify_capacity_change(rsrcnames[0], pid, flag, seq_num,
		    change_data, &info);
		nvlist_free(change_data);
		break;

	case CMD_GETSTATE:
		if ((rsrcnames == NULL) || (rsrcnames[0] == NULL))
			goto faildata;
		error = get_resource_state(rsrcnames[0], pid, &info);
		break;

	default:
		rcm_log_message(RCM_WARNING,
		    gettext("unknown door command: %d\n"), cmd);
		generate_reply_event(EFAULT, NULL, ret);
		(void) free(rsrcnames);
		return;
	}

	rcm_log_message(RCM_TRACE2, "finish processing event 0x%x\n", cmd);
	generate_reply_event(error, info, ret);
	(void) free(rsrcnames);
	return;

faildata:
	rcm_log_message(RCM_WARNING,
	    gettext("data error in door arguments for cmd 0x%x\n"), cmd);

	generate_reply_event(EFAULT, NULL, ret);
	(void) free(rsrcnames);
}


/*
 * Generate reply event from resource registration information
 */
static void
generate_reply_event(int error, rcm_info_t *info, nvlist_t **ret)
{
	nvlist_t *nvl = NULL;
	rcm_info_t *tmp;
	char *buf = NULL;
	size_t buflen = 0;

	rcm_log_message(RCM_TRACE4, "generating reply event\n");

	/* Allocate an empty nvlist */
	if ((errno = nvlist_alloc(&nvl, 0, 0)) > 0) {
		rcm_log_message(RCM_ERROR,
		    gettext("nvlist_alloc failed: %s\n"), strerror(errno));
		rcmd_exit(errno);
	}

	/* Encode the result of the operation in the nvlist */
	if (errno = nvlist_add_int32(nvl, RCM_RESULT, error)) {
		rcm_log_message(RCM_ERROR,
		    gettext("nvlist_add(RESULT) failed: %s\n"),
		    strerror(errno));
		rcmd_exit(errno);
	}

	/* Go through the RCM info tuples, appending them all to the nvlist */
	tmp = info;
	while (tmp) {
		if (tmp->info) {
			buf = NULL;
			buflen = 0;
			if (errno = nvlist_pack(tmp->info, &buf, &buflen,
			    NV_ENCODE_NATIVE, 0)) {
				rcm_log_message(RCM_ERROR,
				    gettext("nvlist_pack(INFO) failed: %s\n"),
				    strerror(errno));
				rcmd_exit(errno);
			}
			if (errno = nvlist_add_byte_array(nvl, RCM_RESULT_INFO,
			    (uchar_t *)buf, buflen)) {
				rcm_log_message(RCM_ERROR,
				    gettext("nvlist_add(INFO) failed: %s\n"),
				    strerror(errno));
				rcmd_exit(errno);
			}
			(void) free(buf);
			nvlist_free(tmp->info);
		}
		info = tmp->next;
		(void) free(tmp);
		tmp = info;
	}

	/* Return the nvlist (unpacked) in the return argument */
	rcm_print_nvlist(nvl);
	*ret = nvl;
}

static void
rcm_print_nvlist(nvlist_t *nvl)
{
	uchar_t data_byte;
	int16_t data_int16;
	uint16_t data_uint16;
	int32_t data_int32;
	uint32_t data_uint32;
	int64_t data_int64;
	uint64_t data_uint64;
	char *data_string;
	char **data_strings;
	uint_t data_nstrings;
	nvpair_t *nvp = NULL;
	int i;
	char *name;
	data_type_t type;

	rcm_log_message(RCM_TRACE3, "event attributes:\n");

	while (nvp = nvlist_next_nvpair(nvl, nvp)) {
		type = nvpair_type(nvp);
		name = nvpair_name(nvp);
		rcm_log_message(RCM_TRACE3, "\t%s(%d)=", name, type);

		switch (type) {
		case DATA_TYPE_BOOLEAN:
			rcm_log_message(RCM_TRACE3, "True (boolean)\n");
			break;

		case DATA_TYPE_BYTE:
			(void) nvpair_value_byte(nvp, &data_byte);
			rcm_log_message(RCM_TRACE3, "0x%x (byte)\n",
			    data_byte);
			break;

		case DATA_TYPE_INT16:
			(void) nvpair_value_int16(nvp, &data_int16);
			rcm_log_message(RCM_TRACE3, "0x%x (int16)\n",
			    data_int16);
			break;

		case DATA_TYPE_UINT16:
			(void) nvpair_value_uint16(nvp, &data_uint16);
			rcm_log_message(RCM_TRACE3, "0x%x (uint16)\n",
			    data_uint16);
			break;

		case DATA_TYPE_INT32:
			(void) nvpair_value_int32(nvp, &data_int32);
			rcm_log_message(RCM_TRACE3, "0x%x (int32)\n",
			    data_int32);
			break;

		case DATA_TYPE_UINT32:
			(void) nvpair_value_uint32(nvp, &data_uint32);
			rcm_log_message(RCM_TRACE3, "0x%x (uint32)\n",
			    data_uint32);
			break;

		case DATA_TYPE_INT64:
			(void) nvpair_value_int64(nvp, &data_int64);
			rcm_log_message(RCM_TRACE3, "0x%lx (int64)\n",
			    data_int64);
			break;

		case DATA_TYPE_UINT64:
			(void) nvpair_value_uint64(nvp, &data_uint64);
			rcm_log_message(RCM_TRACE3, "0x%lx (uint64)\n",
			    data_uint64);
			break;

		case DATA_TYPE_STRING:
			(void) nvpair_value_string(nvp, &data_string);
			rcm_log_message(RCM_TRACE3, "\"%s\" (string)\n",
			    data_string);
			break;

		case DATA_TYPE_STRING_ARRAY:
			(void) nvpair_value_string_array(nvp, &data_strings,
			    &data_nstrings);
			for (i = 0; i < data_nstrings; i++) {
				rcm_log_message(RCM_TRACE3,
				    "\t\"%s\" (string)\n", data_strings[i]);
				if (i < (data_nstrings - 1))
					rcm_log_message(RCM_TRACE3, "\t\t\t");
			}
			break;

		default:
			rcm_log_message(RCM_TRACE3, "<not dumped>\n");
			break;
		}
	}
}