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