1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
24 * All rights reserved.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29#include <door.h>
30#include <assert.h>
31#include <sys/acl.h>
32#include <sys/stat.h>
33#include <librcm_event.h>
34
35#include "rcm_impl.h"
36
37/*
38 * Event handling routine
39 */
40
41#define	RCM_NOTIFY	0
42#define	RCM_GETINFO	1
43#define	RCM_REQUEST	2
44#define	RCM_EFAULT	3
45#define	RCM_EPERM	4
46#define	RCM_EINVAL	5
47
48static void process_event(int, int, nvlist_t *, nvlist_t **);
49static void generate_reply_event(int, rcm_info_t *, nvlist_t **);
50static void rcm_print_nvlist(nvlist_t *);
51
52/*
53 * Top level function for event service
54 */
55void
56event_service(void **data, size_t *datalen)
57{
58	int cmd;
59	int lerrno;
60	int seq_num;
61	nvlist_t *nvl;
62	nvlist_t *ret;
63
64	rcm_log_message(RCM_TRACE1, "received door operation\n");
65
66	/* Decode the data from the door into an unpacked nvlist */
67	if (data == NULL || datalen == NULL) {
68		rcm_log_message(RCM_ERROR, "received null door argument\n");
69		return;
70	}
71	if (lerrno = nvlist_unpack(*data, *datalen, &nvl, 0)) {
72		rcm_log_message(RCM_ERROR, "received bad door argument, %s\n",
73		    strerror(lerrno));
74		return;
75	}
76
77	/* Do nothing if the door is just being knocked on */
78	if (errno = nvlist_lookup_int32(nvl, RCM_CMD, &cmd)) {
79		rcm_log_message(RCM_ERROR,
80		    "bad door argument (nvlist_lookup=%s)\n", strerror(errno));
81		nvlist_free(nvl);
82		return;
83	}
84	if (cmd == CMD_KNOCK) {
85		rcm_log_message(RCM_TRACE1, "door event was just a knock\n");
86		nvlist_free(nvl);
87		*data = NULL;
88		*datalen = 0;
89		return;
90	}
91
92	/*
93	 * Go increment thread count. Before daemon is fully initialized,
94	 * the event processing blocks inside this function.
95	 */
96	seq_num = rcmd_thr_incr(cmd);
97
98	process_event(cmd, seq_num, nvl, &ret);
99	nvlist_free(nvl);
100	assert(ret != NULL);
101
102	/*
103	 * Decrement thread count
104	 */
105	rcmd_thr_decr();
106
107out:
108	*data = ret;
109	*datalen = 0;
110}
111
112/*
113 * Actually processes events; returns a reply event
114 */
115static void
116process_event(int cmd, int seq_num, nvlist_t *nvl, nvlist_t **ret)
117{
118	int i;
119	int error;
120	uint_t nvl_nrsrcs = 0;
121	pid_t pid;
122	uint32_t flag = (uint32_t)0;
123	uint64_t pid64 = (uint64_t)0;
124	size_t buflen = 0;
125	size_t interval_size = 0;
126	timespec_t *interval = NULL;
127	nvlist_t *change_data = NULL;
128	nvlist_t *event_data = NULL;
129	rcm_info_t *info = NULL;
130	char *modname = NULL;
131	char *buf = NULL;
132	char **rsrcnames = NULL;
133	char **nvl_rsrcs = NULL;
134
135	rcm_log_message(RCM_TRACE2, "servicing door command=%d\n", cmd);
136
137	rcm_print_nvlist(nvl);
138
139	/*
140	 * Extract data from the door argument nvlist.  Not all arguments
141	 * are needed; sanity checks are performed later.
142	 */
143	(void) nvlist_lookup_string_array(nvl, RCM_RSRCNAMES, &nvl_rsrcs,
144	    &nvl_nrsrcs);
145	(void) nvlist_lookup_string(nvl, RCM_CLIENT_MODNAME, &modname);
146	(void) nvlist_lookup_uint64(nvl, RCM_CLIENT_ID, (uint64_t *)&pid64);
147	pid = (pid_t)pid64;
148	(void) nvlist_lookup_uint32(nvl, RCM_REQUEST_FLAG, (uint32_t *)&flag);
149	(void) nvlist_lookup_byte_array(nvl, RCM_SUSPEND_INTERVAL,
150	    (uchar_t **)&interval, &interval_size);
151	(void) nvlist_lookup_byte_array(nvl, RCM_CHANGE_DATA, (uchar_t **)&buf,
152	    &buflen);
153	if (buf != NULL && buflen > 0) {
154		(void) nvlist_unpack(buf, buflen, &change_data, 0);
155		buf = NULL;
156		buflen = 0;
157	}
158	(void) nvlist_lookup_byte_array(nvl, RCM_EVENT_DATA, (uchar_t **)&buf,
159	    &buflen);
160	if (buf != NULL && buflen > 0)
161		(void) nvlist_unpack(buf, buflen, &event_data, 0);
162
163	rsrcnames = s_calloc(nvl_nrsrcs + 1, sizeof (char *));
164	for (i = 0; i < nvl_nrsrcs; i++) {
165		rsrcnames[i] = nvl_rsrcs[i];
166	}
167	rsrcnames[nvl_nrsrcs] = NULL;
168
169	/*
170	 * Switch off the command being performed to do the appropriate
171	 * sanity checks and dispatch the arguments to the appropriate
172	 * implementation routine.
173	 */
174	switch (cmd) {
175	case CMD_REGISTER:
176		if ((modname == NULL) || (rsrcnames == NULL) ||
177		    (rsrcnames[0] == NULL))
178			goto faildata;
179		error = add_resource_client(modname, rsrcnames[0], pid, flag,
180		    &info);
181		break;
182
183	case CMD_UNREGISTER:
184		if ((modname == NULL) || (rsrcnames == NULL) ||
185		    (rsrcnames[0] == NULL))
186			goto faildata;
187		error = remove_resource_client(modname, rsrcnames[0], pid,
188		    flag);
189		break;
190
191	case CMD_GETINFO:
192		if ((rsrcnames == NULL) &&
193		    ((flag & (RCM_DR_OPERATION | RCM_MOD_INFO)) == 0))
194			goto faildata;
195		if ((error = get_resource_info(rsrcnames, flag, seq_num, &info))
196		    == EINVAL) {
197			rcm_log_message(RCM_DEBUG,
198			    "invalid argument in get info request\n");
199			generate_reply_event(EINVAL, NULL, ret);
200			return;
201		}
202		break;
203
204	case CMD_SUSPEND:
205		if ((rsrcnames == NULL) || (rsrcnames[0] == NULL) ||
206		    (interval == NULL))
207			goto faildata;
208		error = process_resource_suspend(rsrcnames, pid, flag, seq_num,
209		    interval, &info);
210		break;
211
212	case CMD_RESUME:
213		if ((rsrcnames == NULL) || (rsrcnames[0] == NULL))
214			goto faildata;
215		error = notify_resource_resume(rsrcnames, pid, flag, seq_num,
216		    &info);
217		break;
218
219	case CMD_OFFLINE:
220		if ((rsrcnames == NULL) || (rsrcnames[0] == NULL))
221			goto faildata;
222		error = process_resource_offline(rsrcnames, pid, flag, seq_num,
223		    &info);
224		break;
225
226	case CMD_ONLINE:
227		if ((rsrcnames == NULL) || (rsrcnames[0] == NULL))
228			goto faildata;
229		error = notify_resource_online(rsrcnames, pid, flag, seq_num,
230		    &info);
231		break;
232
233	case CMD_REMOVE:
234		if ((rsrcnames == NULL) || (rsrcnames[0] == NULL))
235			goto faildata;
236		error = notify_resource_remove(rsrcnames, pid, flag, seq_num,
237		    &info);
238		break;
239
240	case CMD_EVENT:
241		if ((rsrcnames == NULL) || (rsrcnames[0] == NULL) ||
242		    (event_data == NULL))
243			goto faildata;
244		error = notify_resource_event(rsrcnames[0], pid, flag, seq_num,
245		    event_data, &info);
246		nvlist_free(event_data);
247		break;
248
249	case CMD_REQUEST_CHANGE:
250		if ((rsrcnames == NULL) || (rsrcnames[0] == NULL) ||
251		    (change_data == NULL))
252			goto faildata;
253		error = request_capacity_change(rsrcnames[0], pid, flag,
254		    seq_num, change_data, &info);
255		nvlist_free(change_data);
256		break;
257
258	case CMD_NOTIFY_CHANGE:
259		if ((rsrcnames == NULL) || (rsrcnames[0] == NULL) ||
260		    (change_data == NULL))
261			goto faildata;
262		error = notify_capacity_change(rsrcnames[0], pid, flag, seq_num,
263		    change_data, &info);
264		nvlist_free(change_data);
265		break;
266
267	case CMD_GETSTATE:
268		if ((rsrcnames == NULL) || (rsrcnames[0] == NULL))
269			goto faildata;
270		error = get_resource_state(rsrcnames[0], pid, &info);
271		break;
272
273	default:
274		rcm_log_message(RCM_WARNING,
275		    gettext("unknown door command: %d\n"), cmd);
276		generate_reply_event(EFAULT, NULL, ret);
277		(void) free(rsrcnames);
278		return;
279	}
280
281	rcm_log_message(RCM_TRACE2, "finish processing event 0x%x\n", cmd);
282	generate_reply_event(error, info, ret);
283	(void) free(rsrcnames);
284	return;
285
286faildata:
287	rcm_log_message(RCM_WARNING,
288	    gettext("data error in door arguments for cmd 0x%x\n"), cmd);
289
290	generate_reply_event(EFAULT, NULL, ret);
291	(void) free(rsrcnames);
292}
293
294
295/*
296 * Generate reply event from resource registration information
297 */
298static void
299generate_reply_event(int error, rcm_info_t *info, nvlist_t **ret)
300{
301	nvlist_t *nvl = NULL;
302	rcm_info_t *tmp;
303	char *buf = NULL;
304	size_t buflen = 0;
305
306	rcm_log_message(RCM_TRACE4, "generating reply event\n");
307
308	/* Allocate an empty nvlist */
309	if ((errno = nvlist_alloc(&nvl, 0, 0)) > 0) {
310		rcm_log_message(RCM_ERROR,
311		    gettext("nvlist_alloc failed: %s\n"), strerror(errno));
312		rcmd_exit(errno);
313	}
314
315	/* Encode the result of the operation in the nvlist */
316	if (errno = nvlist_add_int32(nvl, RCM_RESULT, error)) {
317		rcm_log_message(RCM_ERROR,
318		    gettext("nvlist_add(RESULT) failed: %s\n"),
319		    strerror(errno));
320		rcmd_exit(errno);
321	}
322
323	/* Go through the RCM info tuples, appending them all to the nvlist */
324	tmp = info;
325	while (tmp) {
326		if (tmp->info) {
327			buf = NULL;
328			buflen = 0;
329			if (errno = nvlist_pack(tmp->info, &buf, &buflen,
330			    NV_ENCODE_NATIVE, 0)) {
331				rcm_log_message(RCM_ERROR,
332				    gettext("nvlist_pack(INFO) failed: %s\n"),
333				    strerror(errno));
334				rcmd_exit(errno);
335			}
336			if (errno = nvlist_add_byte_array(nvl, RCM_RESULT_INFO,
337			    (uchar_t *)buf, buflen)) {
338				rcm_log_message(RCM_ERROR,
339				    gettext("nvlist_add(INFO) failed: %s\n"),
340				    strerror(errno));
341				rcmd_exit(errno);
342			}
343			(void) free(buf);
344			nvlist_free(tmp->info);
345		}
346		info = tmp->next;
347		(void) free(tmp);
348		tmp = info;
349	}
350
351	/* Return the nvlist (unpacked) in the return argument */
352	rcm_print_nvlist(nvl);
353	*ret = nvl;
354}
355
356static void
357rcm_print_nvlist(nvlist_t *nvl)
358{
359	uchar_t data_byte;
360	int16_t data_int16;
361	uint16_t data_uint16;
362	int32_t data_int32;
363	uint32_t data_uint32;
364	int64_t data_int64;
365	uint64_t data_uint64;
366	char *data_string;
367	char **data_strings;
368	uint_t data_nstrings;
369	nvpair_t *nvp = NULL;
370	int i;
371	char *name;
372	data_type_t type;
373
374	rcm_log_message(RCM_TRACE3, "event attributes:\n");
375
376	while (nvp = nvlist_next_nvpair(nvl, nvp)) {
377		type = nvpair_type(nvp);
378		name = nvpair_name(nvp);
379		rcm_log_message(RCM_TRACE3, "\t%s(%d)=", name, type);
380
381		switch (type) {
382		case DATA_TYPE_BOOLEAN:
383			rcm_log_message(RCM_TRACE3, "True (boolean)\n");
384			break;
385
386		case DATA_TYPE_BYTE:
387			(void) nvpair_value_byte(nvp, &data_byte);
388			rcm_log_message(RCM_TRACE3, "0x%x (byte)\n",
389			    data_byte);
390			break;
391
392		case DATA_TYPE_INT16:
393			(void) nvpair_value_int16(nvp, &data_int16);
394			rcm_log_message(RCM_TRACE3, "0x%x (int16)\n",
395			    data_int16);
396			break;
397
398		case DATA_TYPE_UINT16:
399			(void) nvpair_value_uint16(nvp, &data_uint16);
400			rcm_log_message(RCM_TRACE3, "0x%x (uint16)\n",
401			    data_uint16);
402			break;
403
404		case DATA_TYPE_INT32:
405			(void) nvpair_value_int32(nvp, &data_int32);
406			rcm_log_message(RCM_TRACE3, "0x%x (int32)\n",
407			    data_int32);
408			break;
409
410		case DATA_TYPE_UINT32:
411			(void) nvpair_value_uint32(nvp, &data_uint32);
412			rcm_log_message(RCM_TRACE3, "0x%x (uint32)\n",
413			    data_uint32);
414			break;
415
416		case DATA_TYPE_INT64:
417			(void) nvpair_value_int64(nvp, &data_int64);
418			rcm_log_message(RCM_TRACE3, "0x%lx (int64)\n",
419			    data_int64);
420			break;
421
422		case DATA_TYPE_UINT64:
423			(void) nvpair_value_uint64(nvp, &data_uint64);
424			rcm_log_message(RCM_TRACE3, "0x%lx (uint64)\n",
425			    data_uint64);
426			break;
427
428		case DATA_TYPE_STRING:
429			(void) nvpair_value_string(nvp, &data_string);
430			rcm_log_message(RCM_TRACE3, "\"%s\" (string)\n",
431			    data_string);
432			break;
433
434		case DATA_TYPE_STRING_ARRAY:
435			(void) nvpair_value_string_array(nvp, &data_strings,
436			    &data_nstrings);
437			for (i = 0; i < data_nstrings; i++) {
438				rcm_log_message(RCM_TRACE3,
439				    "\t\"%s\" (string)\n", data_strings[i]);
440				if (i < (data_nstrings - 1))
441					rcm_log_message(RCM_TRACE3, "\t\t\t");
442			}
443			break;
444
445		default:
446			rcm_log_message(RCM_TRACE3, "<not dumped>\n");
447			break;
448		}
449	}
450}
451