xref: /titanic_51/usr/src/cmd/rcm_daemon/common/rcm_subr.c (revision c10c16dec587a0662068f6e2991c29ed3a9db943)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  *
21  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
22  * Use is subject to license terms.
23  */
24 
25 #pragma ident	"%Z%%M%	%I%	%E% SMI"
26 
27 #include "rcm_impl.h"
28 #include "rcm_module.h"
29 
30 /*
31  * Short-circuits unloading of modules with no registrations, so that
32  * they are present during the next db_sync cycle.
33  */
34 #define	MOD_REFCNT_INIT		2
35 
36 int need_cleanup;	/* flag indicating if clean up is needed */
37 
38 static mutex_t mod_lock;	/* protects module list */
39 static module_t *module_head;	/* linked list of modules */
40 static rsrc_node_t *rsrc_root;	/* root of all resources */
41 
42 /*
43  * Misc help routines
44  */
45 static void rcmd_db_print();
46 static void rcm_handle_free(rcm_handle_t *);
47 static rcm_handle_t *rcm_handle_alloc(module_t *);
48 static void rsrc_clients_free(client_t *);
49 static struct rcm_mod_ops *modops_from_v1(void *);
50 static int call_getinfo(struct rcm_mod_ops *, rcm_handle_t *, char *, id_t,
51     uint_t, char **, char **, nvlist_t *, rcm_info_t **);
52 static int node_action(rsrc_node_t *, void *);
53 
54 extern void start_polling_thread();
55 
56 /*
57  * translate /dev name to a /devices path
58  *
59  * N.B. This routine can be enhanced to understand network names
60  *	and friendly names in the future.
61  */
62 char *
63 resolve_name(char *alias)
64 {
65 	char *tmp;
66 	const char *dev = "/dev/";
67 
68 	if (strlen(alias) == 0)
69 		return (NULL);
70 
71 	if (strncmp(alias, dev, strlen(dev)) == 0) {
72 		/*
73 		 * Treat /dev/... as a symbolic link
74 		 */
75 		tmp = s_malloc(PATH_MAX);
76 		if (realpath(alias, tmp) != NULL) {
77 			return (tmp);
78 		} else {
79 			free(tmp);
80 		}
81 		/* Fail to resolve /dev/ name, use the name as is */
82 	}
83 
84 	return (s_strdup(alias));
85 }
86 
87 /*
88  * Figure out resource type based on "resolved" name
89  *
90  * N.B. This routine does not figure out file system mount points.
91  *	This is determined at runtime when filesys module register
92  *	with RCM_FILESYS flag.
93  */
94 int
95 rsrc_get_type(const char *resolved_name)
96 {
97 	if (resolved_name[0] != '/')
98 		return (RSRC_TYPE_ABSTRACT);
99 
100 	if (strncmp("/devices/", resolved_name, 9) == 0)
101 		return (RSRC_TYPE_DEVICE);
102 
103 	return (RSRC_TYPE_NORMAL);
104 }
105 
106 /*
107  * Module operations:
108  *	module_load, module_unload, module_info, module_attach, module_detach,
109  *	cli_module_hold, cli_module_rele
110  */
111 
112 #ifdef	ENABLE_MODULE_DETACH
113 /*
114  * call unregister() entry point to allow module to unregister for
115  * resources without getting confused.
116  */
117 static void
118 module_detach(module_t *module)
119 {
120 	struct rcm_mod_ops *ops = module->modops;
121 
122 	rcm_log_message(RCM_TRACE2, "module_detach(name=%s)\n", module->name);
123 
124 	ops->rcmop_unregister(module->rcmhandle);
125 }
126 #endif	/* ENABLE_MODULE_DETACH */
127 
128 /*
129  * call register() entry point to allow module to register for resources
130  */
131 static void
132 module_attach(module_t *module)
133 {
134 	struct rcm_mod_ops *ops = module->modops;
135 
136 	rcm_log_message(RCM_TRACE2, "module_attach(name=%s)\n", module->name);
137 
138 	if (ops->rcmop_register(module->rcmhandle) != RCM_SUCCESS) {
139 		rcm_log_message(RCM_WARNING,
140 		    gettext("module %s register() failed\n"), module->name);
141 	}
142 }
143 
144 struct rcm_mod_ops *
145 module_init(module_t *module)
146 {
147 	if (module->dlhandle)
148 		/* rcm module */
149 		return (module->init());
150 	else
151 		/* rcm script */
152 		return (script_init(module));
153 }
154 
155 /*
156  * call rmc_mod_info() entry of module
157  */
158 static const char *
159 module_info(module_t *module)
160 {
161 	if (module->dlhandle)
162 		/* rcm module */
163 		return (module->info());
164 	else
165 		/* rcm script */
166 		return (script_info(module));
167 }
168 
169 int
170 module_fini(module_t *module)
171 {
172 	if (module->dlhandle)
173 		/* rcm module */
174 		return (module->fini());
175 	else
176 		/* rcm script */
177 		return (script_fini(module));
178 }
179 
180 /*
181  * call rmc_mod_fini() entry of module, dlclose module, and free memory
182  */
183 static void
184 module_unload(module_t *module)
185 {
186 	int version = module->modops->version;
187 
188 	rcm_log_message(RCM_DEBUG, "module_unload(name=%s)\n", module->name);
189 
190 	(void) module_fini(module);
191 
192 	rcm_handle_free(module->rcmhandle);
193 	free(module->name);
194 
195 	switch (version) {
196 	case RCM_MOD_OPS_V1:
197 		/*
198 		 * Free memory associated with converted ops vector
199 		 */
200 		free(module->modops);
201 		break;
202 
203 	case RCM_MOD_OPS_VERSION:
204 	default:
205 		break;
206 	}
207 
208 	if (module->dlhandle)
209 		rcm_module_close(module->dlhandle);
210 
211 	free(module);
212 }
213 
214 /*
215  * Locate the module, execute rcm_mod_init() and check ops vector version
216  */
217 static module_t *
218 module_load(char *modname)
219 {
220 	module_t *module;
221 
222 	rcm_log_message(RCM_DEBUG, "module_load(name=%s)\n", modname);
223 
224 	/*
225 	 * dlopen the module
226 	 */
227 	module = s_calloc(1, sizeof (*module));
228 	module->name = s_strdup(modname);
229 	module->modops = NULL;
230 	rcm_init_queue(&module->client_q);
231 
232 	if (rcm_is_script(modname) == 0) {
233 		/* rcm module */
234 		module->dlhandle = rcm_module_open(modname);
235 
236 		if (module->dlhandle == NULL) {
237 			rcm_log_message(RCM_NOTICE,
238 				gettext("cannot open module %s\n"), modname);
239 			goto fail;
240 		}
241 
242 		/*
243 		 * dlsym rcm_mod_init/fini/info() entry points
244 		 */
245 		module->init = (struct rcm_mod_ops *(*)())dlsym(
246 					module->dlhandle, "rcm_mod_init");
247 		module->fini = (int (*)())dlsym(
248 					module->dlhandle, "rcm_mod_fini");
249 		module->info = (const char *(*)())dlsym(module->dlhandle,
250 		    "rcm_mod_info");
251 		if (module->init == NULL || module->fini == NULL ||
252 		    module->info == NULL) {
253 			rcm_log_message(RCM_ERROR,
254 			    gettext("missing entries in module %s\n"), modname);
255 			goto fail;
256 		}
257 
258 	} else {
259 		/* rcm script */
260 		module->dlhandle = NULL;
261 		module->init = (struct rcm_mod_ops *(*)()) NULL;
262 		module->fini = (int (*)()) NULL;
263 		module->info = (const char *(*)()) NULL;
264 	}
265 
266 	if ((module->modops = module_init(module)) == NULL) {
267 		if (module->dlhandle)
268 			rcm_log_message(RCM_ERROR,
269 				gettext("cannot init module %s\n"), modname);
270 		goto fail;
271 	}
272 
273 	/*
274 	 * Check ops vector version
275 	 */
276 	switch (module->modops->version) {
277 	case RCM_MOD_OPS_V1:
278 		module->modops = modops_from_v1((void *)module->modops);
279 		break;
280 
281 	case RCM_MOD_OPS_VERSION:
282 		break;
283 
284 	default:
285 		rcm_log_message(RCM_ERROR,
286 		    gettext("module %s rejected: version %d not supported\n"),
287 		    modname, module->modops->version);
288 		(void) module_fini(module);
289 		goto fail;
290 	}
291 
292 	/*
293 	 * Make sure all fields are set
294 	 */
295 	if ((module->modops->rcmop_register == NULL) ||
296 	    (module->modops->rcmop_unregister == NULL) ||
297 	    (module->modops->rcmop_get_info == NULL) ||
298 	    (module->modops->rcmop_request_suspend == NULL) ||
299 	    (module->modops->rcmop_notify_resume == NULL) ||
300 	    (module->modops->rcmop_request_offline == NULL) ||
301 	    (module->modops->rcmop_notify_online == NULL) ||
302 	    (module->modops->rcmop_notify_remove == NULL)) {
303 		rcm_log_message(RCM_ERROR,
304 		    gettext("module %s rejected: has NULL ops fields\n"),
305 		    modname);
306 		(void) module_fini(module);
307 		goto fail;
308 	}
309 
310 	module->rcmhandle = rcm_handle_alloc(module);
311 	return (module);
312 
313 fail:
314 	if (module->modops && module->modops->version == RCM_MOD_OPS_V1)
315 		free(module->modops);
316 
317 	if (module->dlhandle)
318 		rcm_module_close(module->dlhandle);
319 
320 	free(module->name);
321 	free(module);
322 	return (NULL);
323 }
324 
325 /*
326  * add one to module hold count. load the module if not loaded
327  */
328 static module_t *
329 cli_module_hold(char *modname)
330 {
331 	module_t *module;
332 
333 	rcm_log_message(RCM_TRACE3, "cli_module_hold(%s)\n", modname);
334 
335 	(void) mutex_lock(&mod_lock);
336 	module = module_head;
337 	while (module) {
338 		if (strcmp(module->name, modname) == 0) {
339 			break;
340 		}
341 		module = module->next;
342 	}
343 
344 	if (module) {
345 		module->ref_count++;
346 		(void) mutex_unlock(&mod_lock);
347 		return (module);
348 	}
349 
350 	/*
351 	 * Module not found, attempt to load it
352 	 */
353 	if ((module = module_load(modname)) == NULL) {
354 		(void) mutex_unlock(&mod_lock);
355 		return (NULL);
356 	}
357 
358 	/*
359 	 * Hold module and link module into module list
360 	 */
361 	module->ref_count = MOD_REFCNT_INIT;
362 	module->next = module_head;
363 	module_head = module;
364 
365 	(void) mutex_unlock(&mod_lock);
366 
367 	return (module);
368 }
369 
370 /*
371  * decrement module hold count. Unload it if no reference
372  */
373 static void
374 cli_module_rele(module_t *module)
375 {
376 	module_t *curr = module_head, *prev = NULL;
377 
378 	rcm_log_message(RCM_TRACE3, "cli_module_rele(name=%s)\n", module->name);
379 
380 	(void) mutex_lock(&mod_lock);
381 	if (--(module->ref_count) != 0) {
382 		(void) mutex_unlock(&mod_lock);
383 		return;
384 	}
385 
386 	rcm_log_message(RCM_TRACE2, "unloading module %s\n", module->name);
387 
388 	/*
389 	 * Unlink the module from list
390 	 */
391 	while (curr && (curr != module)) {
392 		prev = curr;
393 		curr = curr->next;
394 	}
395 	if (curr == NULL) {
396 		rcm_log_message(RCM_ERROR,
397 		    gettext("Unexpected error: module %s not found.\n"),
398 		    module->name);
399 	} else if (prev == NULL) {
400 		module_head = curr->next;
401 	} else {
402 		prev->next = curr->next;
403 	}
404 	(void) mutex_unlock(&mod_lock);
405 
406 	module_unload(module);
407 }
408 
409 /*
410  * Gather usage info be passed back to requester. Discard info if user does
411  * not care (list == NULL).
412  */
413 void
414 add_busy_rsrc_to_list(char *alias, pid_t pid, int state, int seq_num,
415     char *modname, const char *infostr, const char *errstr,
416     nvlist_t *client_props, rcm_info_t **list)
417 {
418 	rcm_info_t *info;
419 	rcm_info_t *tmp;
420 	char *buf = NULL;
421 	size_t buflen = 0;
422 
423 	if (list == NULL) {
424 		return;
425 	}
426 
427 	info = s_calloc(1, sizeof (*info));
428 	if (errno = nvlist_alloc(&(info->info), NV_UNIQUE_NAME, 0)) {
429 		rcm_log_message(RCM_ERROR, "failed (nvlist_alloc=%s).\n",
430 		    strerror(errno));
431 		rcmd_exit(errno);
432 	}
433 
434 	/*LINTED*/
435 	if ((errno = nvlist_add_string(info->info, RCM_RSRCNAME, alias)) ||
436 	    (errno = nvlist_add_int32(info->info, RCM_SEQ_NUM, seq_num)) ||
437 	    (errno = nvlist_add_int64(info->info, RCM_CLIENT_ID, pid)) ||
438 	    (errno = nvlist_add_int32(info->info, RCM_RSRCSTATE, state))) {
439 		rcm_log_message(RCM_ERROR, "failed (nvlist_add=%s).\n",
440 		    strerror(errno));
441 		rcmd_exit(errno);
442 	}
443 
444 	/*
445 	 * Daemon calls to add_busy_rsrc_to_list may pass in
446 	 * error/info. Add these through librcm interfaces.
447 	 */
448 	if (errstr) {
449 		rcm_log_message(RCM_TRACE3, "adding error string: %s\n",
450 		    errstr);
451 		if (errno = nvlist_add_string(info->info, RCM_CLIENT_ERROR,
452 		    (char *)errstr)) {
453 			rcm_log_message(RCM_ERROR, "failed (nvlist_add=%s).\n",
454 			    strerror(errno));
455 			rcmd_exit(errno);
456 		}
457 	}
458 
459 	if (infostr) {
460 		if (errno = nvlist_add_string(info->info, RCM_CLIENT_INFO,
461 		    (char *)infostr)) {
462 			rcm_log_message(RCM_ERROR, "failed (nvlist_add=%s).\n",
463 			    strerror(errno));
464 			rcmd_exit(errno);
465 		}
466 	}
467 
468 	if (modname) {
469 		if (errno = nvlist_add_string(info->info, RCM_CLIENT_MODNAME,
470 		    modname)) {
471 			rcm_log_message(RCM_ERROR, "failed (nvlist_add=%s).\n",
472 			    strerror(errno));
473 			rcmd_exit(errno);
474 		}
475 	}
476 
477 	if (client_props) {
478 		if (errno = nvlist_pack(client_props, &buf, &buflen,
479 		    NV_ENCODE_NATIVE, 0)) {
480 			rcm_log_message(RCM_ERROR, "failed (nvlist_pack=%s).\n",
481 			    strerror(errno));
482 			rcmd_exit(errno);
483 		}
484 		if (errno = nvlist_add_byte_array(info->info,
485 		    RCM_CLIENT_PROPERTIES, (uchar_t *)buf, buflen)) {
486 			rcm_log_message(RCM_ERROR, "failed (nvlist_add=%s).\n",
487 			    strerror(errno));
488 			rcmd_exit(errno);
489 		}
490 		(void) free(buf);
491 	}
492 
493 
494 	/* link info at end of list */
495 	if (*list) {
496 		tmp = *list;
497 		while (tmp->next)
498 			tmp = tmp->next;
499 		tmp->next = info;
500 	} else {
501 		*list = info;
502 	}
503 }
504 
505 /*
506  * Resource client realted operations:
507  *	rsrc_client_alloc, rsrc_client_find, rsrc_client_add,
508  *	rsrc_client_remove, rsrc_client_action,	rsrc_client_action_list
509  */
510 
511 /* Allocate rsrc_client_t structure. Load module if necessary. */
512 /*ARGSUSED*/
513 static client_t *
514 rsrc_client_alloc(char *alias, char *modname, pid_t pid, uint_t flag)
515 {
516 	client_t *client;
517 	module_t *mod;
518 
519 	assert((alias != NULL) && (modname != NULL));
520 
521 	rcm_log_message(RCM_TRACE4, "rsrc_client_alloc(%s, %s, %ld)\n",
522 	    alias, modname, pid);
523 
524 	if ((mod = cli_module_hold(modname)) == NULL) {
525 		return (NULL);
526 	}
527 
528 	client = s_calloc(1, sizeof (client_t));
529 	client->module = mod;
530 	client->pid = pid;
531 	client->alias = s_strdup(alias);
532 	client->prv_flags = 0;
533 	client->state = RCM_STATE_ONLINE;
534 	client->flag = flag;
535 
536 	/* This queue is protected by rcm_req_lock */
537 	rcm_enqueue_tail(&mod->client_q, &client->queue);
538 
539 	return (client);
540 }
541 
542 /* Find client in list matching modname and pid */
543 client_t *
544 rsrc_client_find(char *modname, pid_t pid, client_t **list)
545 {
546 	client_t *client = *list;
547 
548 	rcm_log_message(RCM_TRACE4, "rsrc_client_find(%s, %ld, %p)\n",
549 	    modname, pid, (void *)list);
550 
551 	while (client) {
552 		if ((client->pid == pid) &&
553 		    strcmp(modname, client->module->name) == 0) {
554 			break;
555 		}
556 		client = client->next;
557 	}
558 	return (client);
559 }
560 
561 /* Add a client to client list */
562 static void
563 rsrc_client_add(client_t *client, client_t **list)
564 {
565 	rcm_log_message(RCM_TRACE4, "rsrc_client_add: %s, %s, %ld\n",
566 	    client->alias, client->module->name, client->pid);
567 
568 	client->next = *list;
569 	*list = client;
570 }
571 
572 /* Remove client from list and destroy it */
573 static void
574 rsrc_client_remove(client_t *client, client_t **list)
575 {
576 	client_t *tmp, *prev = NULL;
577 
578 	rcm_log_message(RCM_TRACE4, "rsrc_client_remove: %s, %s, %ld\n",
579 	    client->alias, client->module->name, client->pid);
580 
581 	tmp = *list;
582 	while (tmp) {
583 		if (client != tmp) {
584 			prev = tmp;
585 			tmp = tmp->next;
586 			continue;
587 		}
588 		if (prev) {
589 			prev->next = tmp->next;
590 		} else {
591 			*list = tmp->next;
592 		}
593 		tmp->next = NULL;
594 		rsrc_clients_free(tmp);
595 		return;
596 	}
597 }
598 
599 /* Free a list of clients. Called from cleanup thread only */
600 static void
601 rsrc_clients_free(client_t *list)
602 {
603 	client_t *client = list;
604 
605 	while (client) {
606 
607 		/*
608 		 * Note that the rcm daemon is single threaded while
609 		 * executing this routine. So there is no need to acquire
610 		 * rcm_req_lock here while dequeuing.
611 		 */
612 		rcm_dequeue(&client->queue);
613 
614 		if (client->module) {
615 			cli_module_rele(client->module);
616 		}
617 		list = client->next;
618 		if (client->alias) {
619 			free(client->alias);
620 		}
621 		free(client);
622 		client = list;
623 	}
624 }
625 
626 /*
627  * Invoke a callback into a single client
628  * This is the core of rcm_mod_ops interface
629  */
630 static int
631 rsrc_client_action(client_t *client, int cmd, void *arg)
632 {
633 	int			rval = RCM_SUCCESS;
634 	char			*dummy_error = NULL;
635 	char			*error = NULL;
636 	char			*info = NULL;
637 	rcm_handle_t		*hdl;
638 	nvlist_t		*client_props = NULL;
639 	rcm_info_t		*depend_info = NULL;
640 	struct rcm_mod_ops	*ops = client->module->modops;
641 	tree_walk_arg_t		*targ = (tree_walk_arg_t *)arg;
642 
643 	rcm_log_message(RCM_TRACE4,
644 	    "rsrc_client_action: %s, %s, cmd=%d, flag=0x%x\n", client->alias,
645 	    client->module->name, cmd, targ->flag);
646 
647 	/*
648 	 * Create a per-operation handle, increment seq_num by 1 so we will
649 	 * know if a module uses this handle to callback into rcm_daemon.
650 	 */
651 	hdl = rcm_handle_alloc(client->module);
652 	hdl->seq_num = targ->seq_num + 1;
653 
654 	/*
655 	 * Filter out operations for which the client didn't register.
656 	 */
657 	switch (cmd) {
658 	case CMD_SUSPEND:
659 	case CMD_RESUME:
660 	case CMD_OFFLINE:
661 	case CMD_ONLINE:
662 	case CMD_REMOVE:
663 		if ((client->flag & RCM_REGISTER_DR) == 0) {
664 			rcm_handle_free(hdl);
665 			return (RCM_SUCCESS);
666 		}
667 		break;
668 	case CMD_REQUEST_CHANGE:
669 	case CMD_NOTIFY_CHANGE:
670 		if ((client->flag & RCM_REGISTER_CAPACITY) == 0) {
671 			rcm_handle_free(hdl);
672 			return (RCM_SUCCESS);
673 		}
674 		break;
675 	case CMD_EVENT:
676 		if ((client->flag & RCM_REGISTER_EVENT) == 0) {
677 			rcm_handle_free(hdl);
678 			return (RCM_SUCCESS);
679 		}
680 		break;
681 	}
682 
683 	/*
684 	 * Create nvlist_t for any client-specific properties.
685 	 */
686 	if (errno = nvlist_alloc(&client_props, NV_UNIQUE_NAME, 0)) {
687 		rcm_log_message(RCM_ERROR,
688 		    "client action failed (nvlist_alloc=%s)\n",
689 		    strerror(errno));
690 		rcmd_exit(errno);
691 	}
692 
693 	/*
694 	 * Process the operation via a callback to the client module.
695 	 */
696 	switch (cmd) {
697 	case CMD_GETINFO:
698 		rval = call_getinfo(ops, hdl, client->alias, client->pid,
699 		    targ->flag, &info, &error, client_props, &depend_info);
700 		break;
701 
702 	case CMD_SUSPEND:
703 		if (((targ->flag & RCM_QUERY_CANCEL) == 0) &&
704 		    (client->state == RCM_STATE_SUSPEND)) {
705 			break;
706 		}
707 
708 		if ((targ->flag & RCM_QUERY) == 0) {
709 			rcm_log_message(RCM_DEBUG, "suspending %s\n",
710 			    client->alias);
711 		} else if ((targ->flag & RCM_QUERY_CANCEL) == 0) {
712 			rcm_log_message(RCM_DEBUG, "suspend query %s\n",
713 			    client->alias);
714 		} else {
715 			rcm_log_message(RCM_DEBUG,
716 			    "suspend query %s cancelled\n", client->alias);
717 		}
718 
719 		/*
720 		 * Update the client's state before the operation.
721 		 * If this is a cancelled query, then updating the state is
722 		 * the only thing that needs to be done, so break afterwards.
723 		 */
724 		if ((targ->flag & RCM_QUERY) == 0) {
725 			client->state = RCM_STATE_SUSPENDING;
726 		} else if ((targ->flag & RCM_QUERY_CANCEL) == 0) {
727 			client->state = RCM_STATE_SUSPEND_QUERYING;
728 		} else {
729 			client->state = RCM_STATE_ONLINE;
730 			break;
731 		}
732 
733 		rval = ops->rcmop_request_suspend(hdl, client->alias,
734 		    client->pid, targ->interval, targ->flag, &error,
735 		    &depend_info);
736 
737 		/* Update the client's state after the operation. */
738 		if ((targ->flag & RCM_QUERY) == 0) {
739 			if (rval == RCM_SUCCESS) {
740 				client->state = RCM_STATE_SUSPEND;
741 			} else {
742 				client->state = RCM_STATE_SUSPEND_FAIL;
743 			}
744 		} else {
745 			if (rval == RCM_SUCCESS) {
746 				client->state = RCM_STATE_SUSPEND_QUERY;
747 			} else {
748 				client->state = RCM_STATE_SUSPEND_QUERY_FAIL;
749 			}
750 		}
751 		break;
752 
753 	case CMD_RESUME:
754 		if (client->state == RCM_STATE_ONLINE) {
755 			break;
756 		}
757 		client->state = RCM_STATE_RESUMING;
758 		rval = ops->rcmop_notify_resume(hdl, client->alias, client->pid,
759 		    targ->flag, &error, &depend_info);
760 
761 		/* online state is unconditional */
762 		client->state = RCM_STATE_ONLINE;
763 		break;
764 
765 	case CMD_OFFLINE:
766 		if (((targ->flag & RCM_QUERY_CANCEL) == 0) &&
767 		    (client->state == RCM_STATE_OFFLINE)) {
768 			break;
769 		}
770 
771 		if ((targ->flag & RCM_QUERY) == 0) {
772 			rcm_log_message(RCM_DEBUG, "offlining %s\n",
773 			    client->alias);
774 		} else if ((targ->flag & RCM_QUERY_CANCEL) == 0) {
775 			rcm_log_message(RCM_DEBUG, "offline query %s\n",
776 			    client->alias);
777 		} else {
778 			rcm_log_message(RCM_DEBUG,
779 			    "offline query %s cancelled\n", client->alias);
780 		}
781 
782 		/*
783 		 * Update the client's state before the operation.
784 		 * If this is a cancelled query, then updating the state is
785 		 * the only thing that needs to be done, so break afterwards.
786 		 */
787 		if ((targ->flag & RCM_QUERY) == 0) {
788 			client->state = RCM_STATE_OFFLINING;
789 		} else if ((targ->flag & RCM_QUERY_CANCEL) == 0) {
790 			client->state = RCM_STATE_OFFLINE_QUERYING;
791 		} else {
792 			client->state = RCM_STATE_ONLINE;
793 			break;
794 		}
795 
796 		rval = ops->rcmop_request_offline(hdl, client->alias,
797 		    client->pid, targ->flag, &error, &depend_info);
798 
799 		/*
800 		 * If this is a retire operation and we managed to call
801 		 * into at least one client, set retcode to RCM_SUCCESS to
802 		 * indicate that retire has been subject to constraints
803 		 * This retcode will be further modified by actual return
804 		 * code.
805 		 */
806 		if ((targ->flag & RCM_RETIRE_REQUEST) &&
807 		    (targ->retcode == RCM_NO_CONSTRAINT)) {
808 			rcm_log_message(RCM_DEBUG,
809 			    "at least 1 client, constraint applied: %s\n",
810 			    client->alias);
811 			targ->retcode = RCM_SUCCESS;
812 		}
813 
814 		/* Update the client's state after the operation. */
815 		if ((targ->flag & RCM_QUERY) == 0) {
816 			if (rval == RCM_SUCCESS) {
817 				client->state = RCM_STATE_OFFLINE;
818 			} else {
819 				client->state = RCM_STATE_OFFLINE_FAIL;
820 			}
821 		} else {
822 			if (rval == RCM_SUCCESS) {
823 				client->state = RCM_STATE_OFFLINE_QUERY;
824 			} else {
825 				client->state = RCM_STATE_OFFLINE_QUERY_FAIL;
826 			}
827 		}
828 		break;
829 
830 	case CMD_ONLINE:
831 		if (client->state == RCM_STATE_ONLINE) {
832 			break;
833 		}
834 
835 		rcm_log_message(RCM_DEBUG, "onlining %s\n", client->alias);
836 
837 		client->state = RCM_STATE_ONLINING;
838 		rval = ops->rcmop_notify_online(hdl, client->alias, client->pid,
839 		    targ->flag, &error, &depend_info);
840 		client->state = RCM_STATE_ONLINE;
841 		break;
842 
843 	case CMD_REMOVE:
844 		rcm_log_message(RCM_DEBUG, "removing %s\n", client->alias);
845 		client->state = RCM_STATE_REMOVING;
846 		rval = ops->rcmop_notify_remove(hdl, client->alias, client->pid,
847 		    targ->flag, &error, &depend_info);
848 		client->state = RCM_STATE_REMOVE;
849 		break;
850 
851 	case CMD_REQUEST_CHANGE:
852 		rcm_log_message(RCM_DEBUG, "requesting state change of %s\n",
853 		    client->alias);
854 		if (ops->rcmop_request_capacity_change)
855 			rval = ops->rcmop_request_capacity_change(hdl,
856 			    client->alias, client->pid, targ->flag, targ->nvl,
857 			    &error, &depend_info);
858 		break;
859 
860 	case CMD_NOTIFY_CHANGE:
861 		rcm_log_message(RCM_DEBUG, "requesting state change of %s\n",
862 		    client->alias);
863 		if (ops->rcmop_notify_capacity_change)
864 			rval = ops->rcmop_notify_capacity_change(hdl,
865 			    client->alias, client->pid, targ->flag, targ->nvl,
866 			    &error, &depend_info);
867 		break;
868 
869 	case CMD_EVENT:
870 		rcm_log_message(RCM_DEBUG, "delivering event to %s\n",
871 		    client->alias);
872 		if (ops->rcmop_notify_event)
873 			rval = ops->rcmop_notify_event(hdl, client->alias,
874 			    client->pid, targ->flag, &error, targ->nvl,
875 			    &depend_info);
876 		break;
877 
878 	default:
879 		rcm_log_message(RCM_ERROR, gettext("unknown command %d\n"),
880 		    cmd);
881 		rval = RCM_FAILURE;
882 		break;
883 	}
884 
885 	/* reset error code to the most significant error */
886 	if (rval != RCM_SUCCESS)
887 		targ->retcode = rval;
888 
889 	/*
890 	 * XXX - The code below may produce duplicate rcm_info_t's on error?
891 	 */
892 	if ((cmd != CMD_GETINFO) &&
893 	    ((rval != RCM_SUCCESS) ||
894 	    (error != NULL) ||
895 	    (targ->flag & RCM_SCOPE))) {
896 		(void) call_getinfo(ops, hdl, client->alias, client->pid,
897 		    targ->flag & (~(RCM_INCLUDE_DEPENDENT|RCM_INCLUDE_SUBTREE)),
898 		    &info, &dummy_error, client_props, &depend_info);
899 		if (dummy_error)
900 			(void) free(dummy_error);
901 	} else if (cmd != CMD_GETINFO) {
902 		nvlist_free(client_props);
903 		client_props = NULL;
904 	}
905 
906 	if (client_props) {
907 		add_busy_rsrc_to_list(client->alias, client->pid, client->state,
908 		    targ->seq_num, client->module->name, info, error,
909 		    client_props, targ->info);
910 		nvlist_free(client_props);
911 	}
912 
913 	if (info)
914 		(void) free(info);
915 	if (error)
916 		(void) free(error);
917 
918 	if (depend_info) {
919 		if (targ->info) {
920 			(void) rcm_append_info(targ->info, depend_info);
921 		} else {
922 			rcm_free_info(depend_info);
923 		}
924 	}
925 
926 	rcm_handle_free(hdl);
927 	return (rval);
928 }
929 
930 /*
931  * invoke a callback into a list of clients, return 0 if all success
932  */
933 int
934 rsrc_client_action_list(client_t *list, int cmd, void *arg)
935 {
936 	int error, rval = RCM_SUCCESS;
937 	tree_walk_arg_t		*targ = (tree_walk_arg_t *)arg;
938 
939 	while (list) {
940 		client_t *client = list;
941 		list = client->next;
942 
943 		/*
944 		 * Make offline idempotent in the retire
945 		 * case
946 		 */
947 		if ((targ->flag & RCM_RETIRE_REQUEST) &&
948 		    client->state == RCM_STATE_REMOVE) {
949 			client->state = RCM_STATE_ONLINE;
950 			rcm_log_message(RCM_DEBUG, "RETIRE: idempotent client "
951 			    "state: REMOVE -> ONLINE: %s\n", client->alias);
952 		}
953 
954 		if (client->state == RCM_STATE_REMOVE)
955 			continue;
956 
957 		error = rsrc_client_action(client, cmd, arg);
958 		if (error != RCM_SUCCESS) {
959 			rval = error;
960 		}
961 	}
962 
963 	return (rval);
964 }
965 
966 /*
967  * Node realted operations:
968  *
969  *	rn_alloc, rn_free, rn_find_child,
970  *	rn_get_child, rn_get_sibling,
971  *	rsrc_node_find, rsrc_node_add_user, rsrc_node_remove_user,
972  */
973 
974 /* Allocate node based on a logical or physical name */
975 static rsrc_node_t *
976 rn_alloc(char *name, int type)
977 {
978 	rsrc_node_t *node;
979 
980 	rcm_log_message(RCM_TRACE4, "rn_alloc(%s, %d)\n", name, type);
981 
982 	node = s_calloc(1, sizeof (*node));
983 	node->name = s_strdup(name);
984 	node->type = type;
985 
986 	return (node);
987 }
988 
989 /*
990  * Free node along with its siblings and children
991  */
992 static void
993 rn_free(rsrc_node_t *node)
994 {
995 	if (node == NULL) {
996 		return;
997 	}
998 
999 	if (node->child) {
1000 		rn_free(node->child);
1001 	}
1002 
1003 	if (node->sibling) {
1004 		rn_free(node->sibling);
1005 	}
1006 
1007 	rsrc_clients_free(node->users);
1008 	free(node->name);
1009 	free(node);
1010 }
1011 
1012 /*
1013  * Find next sibling
1014  */
1015 static rsrc_node_t *
1016 rn_get_sibling(rsrc_node_t *node)
1017 {
1018 	return (node->sibling);
1019 }
1020 
1021 /*
1022  * Find first child
1023  */
1024 static rsrc_node_t *
1025 rn_get_child(rsrc_node_t *node)
1026 {
1027 	return (node->child);
1028 }
1029 
1030 /*
1031  * Find child named childname. Create it if flag is RSRC_NODE_CRTEATE
1032  */
1033 static rsrc_node_t *
1034 rn_find_child(rsrc_node_t *parent, char *childname, int flag, int type)
1035 {
1036 	rsrc_node_t *child = parent->child;
1037 	rsrc_node_t *new, *prev = NULL;
1038 
1039 	rcm_log_message(RCM_TRACE4,
1040 	    "rn_find_child(parent=%s, child=%s, 0x%x, %d)\n",
1041 	    parent->name, childname, flag, type);
1042 
1043 	/*
1044 	 * Children are ordered based on strcmp.
1045 	 */
1046 	while (child && (strcmp(child->name, childname) < 0)) {
1047 		prev = child;
1048 		child = child->sibling;
1049 	}
1050 
1051 	if (child && (strcmp(child->name, childname) == 0)) {
1052 		return (child);
1053 	}
1054 
1055 	if (flag != RSRC_NODE_CREATE)
1056 		return (NULL);
1057 
1058 	new = rn_alloc(childname, type);
1059 	new->parent = parent;
1060 	new->sibling = child;
1061 
1062 	/*
1063 	 * Set this linkage last so we don't break ongoing operations.
1064 	 *
1065 	 * N.B. Assume setting a pointer is an atomic operation.
1066 	 */
1067 	if (prev == NULL) {
1068 		parent->child = new;
1069 	} else {
1070 		prev->sibling = new;
1071 	}
1072 
1073 	return (new);
1074 }
1075 
1076 /*
1077  * Pathname related help functions
1078  */
1079 static void
1080 pn_preprocess(char *pathname, int type)
1081 {
1082 	char *tmp;
1083 
1084 	if (type != RSRC_TYPE_DEVICE)
1085 		return;
1086 
1087 	/*
1088 	 * For devices, convert ':' to '/' (treat minor nodes and children)
1089 	 */
1090 	tmp = strchr(pathname, ':');
1091 	if (tmp == NULL)
1092 		return;
1093 
1094 	*tmp = '/';
1095 }
1096 
1097 static char *
1098 pn_getnextcomp(char *pathname, char **lasts)
1099 {
1100 	char *slash;
1101 
1102 	if (pathname == NULL)
1103 		return (NULL);
1104 
1105 	/* skip slashes' */
1106 	while (*pathname == '/')
1107 		++pathname;
1108 
1109 	if (*pathname == '\0')
1110 		return (NULL);
1111 
1112 	slash = strchr(pathname, '/');
1113 	if (slash != NULL) {
1114 		*slash = '\0';
1115 		*lasts = slash + 1;
1116 	} else {
1117 		*lasts = NULL;
1118 	}
1119 
1120 	return (pathname);
1121 }
1122 
1123 /*
1124  * Find a node in tree based on device, which is the physical pathname
1125  * of the form /sbus@.../esp@.../sd@...
1126  */
1127 int
1128 rsrc_node_find(char *rsrcname, int flag, rsrc_node_t **nodep)
1129 {
1130 	char *pathname, *nodename, *lasts;
1131 	rsrc_node_t *node;
1132 	int type;
1133 
1134 	rcm_log_message(RCM_TRACE4, "rn_node_find(%s, 0x%x)\n", rsrcname, flag);
1135 
1136 	/*
1137 	 * For RSRC_TYPE_ABSTRACT, look under /ABSTRACT. For other types,
1138 	 * look under /SYSTEM.
1139 	 */
1140 	pathname = resolve_name(rsrcname);
1141 	if (pathname == NULL)
1142 		return (EINVAL);
1143 
1144 	type = rsrc_get_type(pathname);
1145 	switch (type) {
1146 	case RSRC_TYPE_DEVICE:
1147 	case RSRC_TYPE_NORMAL:
1148 		node = rn_find_child(rsrc_root, "SYSTEM", RSRC_NODE_CREATE,
1149 		    RSRC_TYPE_NORMAL);
1150 		break;
1151 
1152 	case RSRC_TYPE_ABSTRACT:
1153 		node = rn_find_child(rsrc_root, "ABSTRACT", RSRC_NODE_CREATE,
1154 		    RSRC_TYPE_NORMAL);
1155 		break;
1156 
1157 	default:
1158 		/* just to make sure */
1159 		free(pathname);
1160 		return (EINVAL);
1161 	}
1162 
1163 	/*
1164 	 * Find position of device within tree. Upon exiting the loop, device
1165 	 * should be placed between prev and curr.
1166 	 */
1167 	pn_preprocess(pathname, type);
1168 	lasts = pathname;
1169 	while ((nodename = pn_getnextcomp(lasts, &lasts)) != NULL) {
1170 		rsrc_node_t *parent = node;
1171 		node = rn_find_child(parent, nodename, flag, type);
1172 		if (node == NULL) {
1173 			assert((flag & RSRC_NODE_CREATE) == 0);
1174 			free(pathname);
1175 			*nodep = NULL;
1176 			return (RCM_SUCCESS);
1177 		}
1178 	}
1179 	free(pathname);
1180 	*nodep = node;
1181 	return (RCM_SUCCESS);
1182 }
1183 
1184 /*
1185  * add a usage client to a node
1186  */
1187 /*ARGSUSED*/
1188 int
1189 rsrc_node_add_user(rsrc_node_t *node, char *alias, char *modname, pid_t pid,
1190     uint_t flag)
1191 {
1192 	client_t *user;
1193 
1194 	rcm_log_message(RCM_TRACE3,
1195 	    "rsrc_node_add_user(%s, %s, %s, %ld, 0x%x)\n",
1196 	    node->name, alias, modname, pid, flag);
1197 
1198 	user = rsrc_client_find(modname, pid, &node->users);
1199 
1200 	/*
1201 	 * If a client_t already exists, add the registration and return
1202 	 * success if it's a valid registration request.
1203 	 *
1204 	 * Return EALREADY if the resource is already registered.
1205 	 * This means either the client_t already has the requested
1206 	 * registration flagged, or that a DR registration was attempted
1207 	 * on a resource already in use in the DR operations state model.
1208 	 */
1209 	if (user != NULL) {
1210 
1211 		if (user->flag & (flag & RCM_REGISTER_MASK)) {
1212 			return (EALREADY);
1213 		}
1214 
1215 		if ((flag & RCM_REGISTER_DR) &&
1216 		    (user->state != RCM_STATE_REMOVE)) {
1217 			return (EALREADY);
1218 		}
1219 
1220 		user->flag |= (flag & RCM_REGISTER_MASK);
1221 		if ((flag & RCM_REGISTER_DR) ||
1222 		    (user->state == RCM_STATE_REMOVE)) {
1223 			user->state = RCM_STATE_ONLINE;
1224 		}
1225 
1226 		return (RCM_SUCCESS);
1227 	}
1228 
1229 	/*
1230 	 * Otherwise create a new client_t and create a new registration.
1231 	 */
1232 	if ((user = rsrc_client_alloc(alias, modname, pid, flag)) != NULL) {
1233 		rsrc_client_add(user, &node->users);
1234 	}
1235 	if (flag & RCM_FILESYS)
1236 		node->type = RSRC_TYPE_FILESYS;
1237 
1238 	return (RCM_SUCCESS);
1239 }
1240 
1241 /*
1242  * remove a usage client of a node
1243  */
1244 int
1245 rsrc_node_remove_user(rsrc_node_t *node, char *modname, pid_t pid, uint_t flag)
1246 {
1247 	client_t *user;
1248 
1249 	rcm_log_message(RCM_TRACE3,
1250 	    "rsrc_node_remove_user(%s, %s, %ld, 0x%x)\n", node->name, modname,
1251 	    pid, flag);
1252 
1253 	user = rsrc_client_find(modname, pid, &node->users);
1254 	if ((user == NULL) || (user->state == RCM_STATE_REMOVE)) {
1255 		rcm_log_message(RCM_NOTICE, gettext(
1256 		    "client not registered: module=%s, pid=%d, dev=%s\n"),
1257 		    modname, pid, node->name);
1258 		return (ENOENT);
1259 	}
1260 
1261 	/* Strip off the registration being removed (DR, event, capacity) */
1262 	user->flag = user->flag & (~(flag & RCM_REGISTER_MASK));
1263 
1264 	/*
1265 	 * Mark the client as removed if all registrations have been removed
1266 	 */
1267 	if ((user->flag & RCM_REGISTER_MASK) == 0)
1268 		user->state = RCM_STATE_REMOVE;
1269 
1270 	return (RCM_SUCCESS);
1271 }
1272 
1273 /*
1274  * Tree walking function - rsrc_walk
1275  */
1276 
1277 #define	MAX_TREE_DEPTH		32
1278 
1279 #define	RN_WALK_CONTINUE	0
1280 #define	RN_WALK_PRUNESIB	1
1281 #define	RN_WALK_PRUNECHILD	2
1282 #define	RN_WALK_TERMINATE	3
1283 
1284 #define	EMPTY_STACK(sp)		((sp)->depth == 0)
1285 #define	TOP_NODE(sp)		((sp)->node[(sp)->depth - 1])
1286 #define	PRUNE_SIB(sp)		((sp)->prunesib[(sp)->depth - 1])
1287 #define	PRUNE_CHILD(sp)		((sp)->prunechild[(sp)->depth - 1])
1288 #define	POP_STACK(sp)		((sp)->depth)--
1289 #define	PUSH_STACK(sp, rn)	\
1290 	(sp)->node[(sp)->depth] = (rn);	\
1291 	(sp)->prunesib[(sp)->depth] = 0;	\
1292 	(sp)->prunechild[(sp)->depth] = 0;	\
1293 	((sp)->depth)++
1294 
1295 struct rn_stack {
1296 	rsrc_node_t *node[MAX_TREE_DEPTH];
1297 	char	prunesib[MAX_TREE_DEPTH];
1298 	char	prunechild[MAX_TREE_DEPTH];
1299 	int	depth;
1300 };
1301 
1302 /* walking one node and update node stack */
1303 /*ARGSUSED*/
1304 static void
1305 walk_one_node(struct rn_stack *sp, void *arg,
1306     int (*node_callback)(rsrc_node_t *, void *))
1307 {
1308 	int prunesib;
1309 	rsrc_node_t *child, *sibling;
1310 	rsrc_node_t *node = TOP_NODE(sp);
1311 
1312 	rcm_log_message(RCM_TRACE4, "walk_one_node(%s)\n", node->name);
1313 
1314 	switch (node_callback(node, arg)) {
1315 	case RN_WALK_TERMINATE:
1316 		POP_STACK(sp);
1317 		while (!EMPTY_STACK(sp)) {
1318 			node = TOP_NODE(sp);
1319 			POP_STACK(sp);
1320 		}
1321 		return;
1322 
1323 	case RN_WALK_PRUNESIB:
1324 		PRUNE_SIB(sp) = 1;
1325 		break;
1326 
1327 	case RN_WALK_PRUNECHILD:
1328 		PRUNE_CHILD(sp) = 1;
1329 		break;
1330 
1331 	case RN_WALK_CONTINUE:
1332 	default:
1333 		break;
1334 	}
1335 
1336 	/*
1337 	 * Push child on the stack
1338 	 */
1339 	if (!PRUNE_CHILD(sp) && (child = rn_get_child(node)) != NULL) {
1340 		PUSH_STACK(sp, child);
1341 		return;
1342 	}
1343 
1344 	/*
1345 	 * Pop the stack till a node's sibling can be pushed
1346 	 */
1347 	prunesib = PRUNE_SIB(sp);
1348 	POP_STACK(sp);
1349 	while (!EMPTY_STACK(sp) &&
1350 	    (prunesib || (sibling = rn_get_sibling(node)) == NULL)) {
1351 		node = TOP_NODE(sp);
1352 		prunesib = PRUNE_SIB(sp);
1353 		POP_STACK(sp);
1354 	}
1355 
1356 	if (EMPTY_STACK(sp)) {
1357 		return;
1358 	}
1359 
1360 	/*
1361 	 * push sibling onto the stack
1362 	 */
1363 	PUSH_STACK(sp, sibling);
1364 }
1365 
1366 /*
1367  * walk tree rooted at root in child-first order
1368  */
1369 static void
1370 rsrc_walk(rsrc_node_t *root, void *arg,
1371     int (*node_callback)(rsrc_node_t *, void *))
1372 {
1373 	struct rn_stack stack;
1374 
1375 	rcm_log_message(RCM_TRACE3, "rsrc_walk(%s)\n", root->name);
1376 
1377 	/*
1378 	 * Push root on stack and walk in child-first order
1379 	 */
1380 	stack.depth = 0;
1381 	PUSH_STACK(&stack, root);
1382 	PRUNE_SIB(&stack) = 1;
1383 
1384 	while (!EMPTY_STACK(&stack)) {
1385 		walk_one_node(&stack, arg, node_callback);
1386 	}
1387 }
1388 
1389 /*
1390  * Callback for a command action on a node
1391  */
1392 static int
1393 node_action(rsrc_node_t *node, void *arg)
1394 {
1395 	tree_walk_arg_t *targ = (tree_walk_arg_t *)arg;
1396 	uint_t flag = targ->flag;
1397 
1398 	rcm_log_message(RCM_TRACE4, "node_action(%s)\n", node->name);
1399 
1400 	/*
1401 	 * If flag indicates operation on a filesystem, we don't callback on
1402 	 * the filesystem root to avoid infinite recursion on filesystem module.
1403 	 *
1404 	 * N.B. Such request should only come from filesystem RCM module.
1405 	 */
1406 	if (flag & RCM_FILESYS) {
1407 		assert(node->type == RSRC_TYPE_FILESYS);
1408 		targ->flag &= ~RCM_FILESYS;
1409 		return (RN_WALK_CONTINUE);
1410 	}
1411 
1412 	/*
1413 	 * Execute state change callback
1414 	 */
1415 	(void) rsrc_client_action_list(node->users, targ->cmd, arg);
1416 
1417 	/*
1418 	 * Upon hitting a filesys root, prune children.
1419 	 * The filesys module should have taken care of
1420 	 * children by now.
1421 	 */
1422 	if (node->type == RSRC_TYPE_FILESYS)
1423 		return (RN_WALK_PRUNECHILD);
1424 
1425 	return (RN_WALK_CONTINUE);
1426 }
1427 
1428 /*
1429  * Execute a command on a subtree under root.
1430  */
1431 int
1432 rsrc_tree_action(rsrc_node_t *root, int cmd, tree_walk_arg_t *arg)
1433 {
1434 	rcm_log_message(RCM_TRACE2, "tree_action(%s, %d)\n", root->name, cmd);
1435 
1436 	arg->cmd = cmd;
1437 
1438 	/*
1439 	 * If RCM_RETIRE_REQUEST is set, just walk one node and preset
1440 	 * retcode to NO_CONSTRAINT
1441 	 */
1442 	if (arg->flag & RCM_RETIRE_REQUEST) {
1443 		rcm_log_message(RCM_TRACE1, "tree_action: RETIRE_REQ: walking "
1444 		    "only root node: %s\n", root->name);
1445 		arg->retcode = RCM_NO_CONSTRAINT;
1446 		(void) node_action(root, arg);
1447 	} else {
1448 		arg->retcode = RCM_SUCCESS;
1449 		rsrc_walk(root, (void *)arg, node_action);
1450 	}
1451 
1452 	return (arg->retcode);
1453 }
1454 
1455 /*
1456  * Get info on current regsitrations
1457  */
1458 int
1459 rsrc_usage_info(char **rsrcnames, uint_t flag, int seq_num, rcm_info_t **info)
1460 {
1461 	rsrc_node_t *node;
1462 	rcm_info_t *result = NULL;
1463 	tree_walk_arg_t arg;
1464 	int initial_req;
1465 	int rv;
1466 	int i;
1467 
1468 	arg.flag = flag;
1469 	arg.info = &result;
1470 	arg.seq_num = seq_num;
1471 
1472 	for (i = 0; rsrcnames[i] != NULL; i++) {
1473 
1474 		rcm_log_message(RCM_TRACE2, "rsrc_usage_info(%s, 0x%x, %d)\n",
1475 		    rsrcnames[i], flag, seq_num);
1476 
1477 		if (flag & RCM_INCLUDE_DEPENDENT) {
1478 			initial_req = ((seq_num & SEQ_NUM_MASK) == 0);
1479 
1480 			/*
1481 			 * if redundant request, skip the operation
1482 			 */
1483 			if (info_req_add(rsrcnames[i], flag, seq_num) != 0) {
1484 				continue;
1485 			}
1486 		}
1487 
1488 		rv = rsrc_node_find(rsrcnames[i], 0, &node);
1489 		if ((rv != RCM_SUCCESS) || (node == NULL)) {
1490 			if ((flag & RCM_INCLUDE_DEPENDENT) && initial_req)
1491 				info_req_remove(seq_num);
1492 			continue;
1493 		}
1494 
1495 		/*
1496 		 * Based on RCM_INCLUDE_SUBTREE flag, query either the subtree
1497 		 * or just the node.
1498 		 */
1499 		if (flag & RCM_INCLUDE_SUBTREE) {
1500 			(void) rsrc_tree_action(node, CMD_GETINFO, &arg);
1501 		} else {
1502 			arg.cmd = CMD_GETINFO;
1503 			(void) node_action(node, (void *)&arg);
1504 		}
1505 
1506 		if ((flag & RCM_INCLUDE_DEPENDENT) && initial_req)
1507 			info_req_remove(seq_num);
1508 	}
1509 
1510 out:
1511 	(void) rcm_append_info(info, result);
1512 	return (rv);
1513 }
1514 
1515 /*
1516  * Get the list of currently loaded module
1517  */
1518 rcm_info_t *
1519 rsrc_mod_info()
1520 {
1521 	module_t *mod;
1522 	rcm_info_t *info = NULL;
1523 
1524 	(void) mutex_lock(&mod_lock);
1525 	mod = module_head;
1526 	while (mod) {
1527 		char *modinfo = s_strdup(module_info(mod));
1528 		add_busy_rsrc_to_list("dummy", 0, 0, 0, mod->name,
1529 		    modinfo, NULL, NULL, &info);
1530 		mod = mod->next;
1531 	}
1532 	(void) mutex_unlock(&mod_lock);
1533 
1534 	return (info);
1535 }
1536 
1537 /*
1538  * Initialize resource map - load all modules
1539  */
1540 void
1541 rcmd_db_init()
1542 {
1543 	char *tmp;
1544 	DIR *mod_dir;
1545 	struct dirent *entp;
1546 	int i;
1547 	char *dir_name;
1548 	int rcm_script;
1549 
1550 	rcm_log_message(RCM_DEBUG, "rcmd_db_init(): initialize database\n");
1551 
1552 	if (script_main_init() == -1)
1553 		rcmd_exit(errno);
1554 
1555 	rsrc_root = rn_alloc("/", RSRC_TYPE_NORMAL);
1556 
1557 	for (i = 0; (dir_name = rcm_dir(i, &rcm_script)) != NULL; i++) {
1558 
1559 		if ((mod_dir = opendir(dir_name)) == NULL) {
1560 			continue;	/* try next directory */
1561 		}
1562 
1563 		rcm_log_message(RCM_TRACE2, "search directory %s\n", dir_name);
1564 
1565 		while ((entp = readdir(mod_dir)) != NULL) {
1566 			module_t *module;
1567 
1568 			if (strcmp(entp->d_name, ".") == 0 ||
1569 				strcmp(entp->d_name, "..") == 0)
1570 				continue;
1571 
1572 			if (rcm_script == 0) {
1573 				/* rcm module */
1574 				if (((tmp = strstr(entp->d_name,
1575 				    RCM_MODULE_SUFFIX)) == NULL) ||
1576 				    (tmp[strlen(RCM_MODULE_SUFFIX)] != '\0')) {
1577 					continue;
1578 				}
1579 			}
1580 
1581 			module = cli_module_hold(entp->d_name);
1582 			if (module == NULL) {
1583 				if (rcm_script == 0)
1584 					rcm_log_message(RCM_ERROR,
1585 					    gettext("%s: failed to load\n"),
1586 					    entp->d_name);
1587 				continue;
1588 			}
1589 
1590 			if (module->ref_count == MOD_REFCNT_INIT) {
1591 				/*
1592 				 * ask module to register for resource 1st time
1593 				 */
1594 				module_attach(module);
1595 			}
1596 			cli_module_rele(module);
1597 		}
1598 		(void) closedir(mod_dir);
1599 	}
1600 
1601 	rcmd_db_print();
1602 }
1603 
1604 /*
1605  * sync resource map - ask all modules to register again
1606  */
1607 void
1608 rcmd_db_sync()
1609 {
1610 	static time_t sync_time = (time_t)-1;
1611 	const time_t interval = 5;	/* resync at most every 5 sec */
1612 
1613 	module_t *mod;
1614 	time_t curr = time(NULL);
1615 
1616 	if ((sync_time != (time_t)-1) && (curr - sync_time < interval))
1617 		return;
1618 
1619 	sync_time = curr;
1620 	(void) mutex_lock(&mod_lock);
1621 	mod = module_head;
1622 	while (mod) {
1623 		/*
1624 		 * Hold module by incrementing ref count and release
1625 		 * mod_lock to avoid deadlock, since rcmop_register()
1626 		 * may callback into the daemon and request mod_lock.
1627 		 */
1628 		mod->ref_count++;
1629 		(void) mutex_unlock(&mod_lock);
1630 
1631 		mod->modops->rcmop_register(mod->rcmhandle);
1632 
1633 		(void) mutex_lock(&mod_lock);
1634 		mod->ref_count--;
1635 		mod = mod->next;
1636 	}
1637 	(void) mutex_unlock(&mod_lock);
1638 }
1639 
1640 /*
1641  * Determine if a process is alive
1642  */
1643 int
1644 proc_exist(pid_t pid)
1645 {
1646 	char path[64];
1647 	const char *procfs = "/proc";
1648 	struct stat sb;
1649 
1650 	if (pid == (pid_t)0) {
1651 		return (1);
1652 	}
1653 
1654 	(void) snprintf(path, sizeof (path), "%s/%ld", procfs, pid);
1655 	return (stat(path, &sb) == 0);
1656 }
1657 
1658 /*
1659  * Cleaup client list
1660  *
1661  * N.B. This routine runs in a single-threaded environment only. It is only
1662  *	called by the cleanup thread, which never runs in parallel with other
1663  *	threads.
1664  */
1665 static void
1666 clean_client_list(client_t **listp)
1667 {
1668 	client_t *client = *listp;
1669 
1670 	/*
1671 	 * Cleanup notification clients for which pid no longer exists
1672 	 */
1673 	while (client) {
1674 		if ((client->state != RCM_STATE_REMOVE) &&
1675 		    proc_exist(client->pid)) {
1676 			listp = &client->next;
1677 			client = *listp;
1678 			continue;
1679 		}
1680 
1681 		/*
1682 		 * Destroy this client_t. rsrc_client_remove updates
1683 		 * listp to point to the next client.
1684 		 */
1685 		rsrc_client_remove(client, listp);
1686 		client = *listp;
1687 	}
1688 }
1689 
1690 /*ARGSUSED*/
1691 static int
1692 clean_node(rsrc_node_t *node, void *arg)
1693 {
1694 	rcm_log_message(RCM_TRACE4, "clean_node(%s)\n", node->name);
1695 
1696 	clean_client_list(&node->users);
1697 
1698 	return (RN_WALK_CONTINUE);
1699 }
1700 
1701 static void
1702 clean_rsrc_tree()
1703 {
1704 	rcm_log_message(RCM_TRACE4,
1705 	    "clean_rsrc_tree(): delete stale dr clients\n");
1706 
1707 	rsrc_walk(rsrc_root, NULL, clean_node);
1708 }
1709 
1710 static void
1711 db_clean()
1712 {
1713 	extern barrier_t barrier;
1714 	extern void clean_dr_list();
1715 
1716 	for (;;) {
1717 		(void) mutex_lock(&rcm_req_lock);
1718 		start_polling_thread();
1719 		(void) mutex_unlock(&rcm_req_lock);
1720 
1721 		(void) mutex_lock(&barrier.lock);
1722 		while (need_cleanup == 0)
1723 			(void) cond_wait(&barrier.cv, &barrier.lock);
1724 		(void) mutex_unlock(&barrier.lock);
1725 
1726 		/*
1727 		 * Make sure all other threads are either blocked or exited.
1728 		 */
1729 		rcmd_set_state(RCMD_CLEANUP);
1730 
1731 		need_cleanup = 0;
1732 
1733 		/*
1734 		 * clean dr_req_list
1735 		 */
1736 		clean_dr_list();
1737 
1738 		/*
1739 		 * clean resource tree
1740 		 */
1741 		clean_rsrc_tree();
1742 
1743 		rcmd_set_state(RCMD_NORMAL);
1744 	}
1745 }
1746 
1747 void
1748 rcmd_db_clean()
1749 {
1750 	rcm_log_message(RCM_DEBUG,
1751 	    "rcm_db_clean(): launch thread to clean database\n");
1752 
1753 	if (thr_create(NULL, NULL, (void *(*)(void *))db_clean,
1754 	    NULL, THR_DETACHED, NULL) != 0) {
1755 		rcm_log_message(RCM_WARNING,
1756 		    gettext("failed to create cleanup thread %s\n"),
1757 		    strerror(errno));
1758 	}
1759 }
1760 
1761 /*ARGSUSED*/
1762 static int
1763 print_node(rsrc_node_t *node, void *arg)
1764 {
1765 	client_t *user;
1766 
1767 	rcm_log_message(RCM_DEBUG, "rscname: %s, state = 0x%x\n", node->name);
1768 	rcm_log_message(RCM_DEBUG, "	users:\n");
1769 
1770 	if ((user = node->users) == NULL) {
1771 		rcm_log_message(RCM_DEBUG, "    none\n");
1772 		return (RN_WALK_CONTINUE);
1773 	}
1774 
1775 	while (user) {
1776 		rcm_log_message(RCM_DEBUG, "	%s, %d, %s\n",
1777 		    user->module->name, user->pid, user->alias);
1778 		user = user->next;
1779 	}
1780 	return (RN_WALK_CONTINUE);
1781 }
1782 
1783 static void
1784 rcmd_db_print()
1785 {
1786 	module_t *mod;
1787 
1788 	rcm_log_message(RCM_DEBUG, "modules:\n");
1789 	(void) mutex_lock(&mod_lock);
1790 	mod = module_head;
1791 	while (mod) {
1792 		rcm_log_message(RCM_DEBUG, "	%s\n", mod->name);
1793 		mod = mod->next;
1794 	}
1795 	(void) mutex_unlock(&mod_lock);
1796 
1797 	rcm_log_message(RCM_DEBUG, "\nresource tree:\n");
1798 
1799 	rsrc_walk(rsrc_root, NULL, print_node);
1800 
1801 	rcm_log_message(RCM_DEBUG, "\n");
1802 }
1803 
1804 /*
1805  * Allocate handle from calling into each RCM module
1806  */
1807 static rcm_handle_t *
1808 rcm_handle_alloc(module_t *module)
1809 {
1810 	rcm_handle_t *hdl;
1811 
1812 	hdl = s_malloc(sizeof (rcm_handle_t));
1813 
1814 	hdl->modname = module->name;
1815 	hdl->pid = 0;
1816 	hdl->lrcm_ops = &rcm_ops;	/* for callback into daemon directly */
1817 	hdl->module = module;
1818 
1819 	return (hdl);
1820 }
1821 
1822 /*
1823  * Free rcm_handle_t
1824  */
1825 static void
1826 rcm_handle_free(rcm_handle_t *handle)
1827 {
1828 	free(handle);
1829 }
1830 
1831 /*
1832  * help function that exit on memory outage
1833  */
1834 void *
1835 s_malloc(size_t size)
1836 {
1837 	void *buf = malloc(size);
1838 
1839 	if (buf == NULL) {
1840 		rcmd_exit(ENOMEM);
1841 	}
1842 	return (buf);
1843 }
1844 
1845 void *
1846 s_calloc(int n, size_t size)
1847 {
1848 	void *buf = calloc(n, size);
1849 
1850 	if (buf == NULL) {
1851 		rcmd_exit(ENOMEM);
1852 	}
1853 	return (buf);
1854 }
1855 
1856 void *
1857 s_realloc(void *ptr, size_t size)
1858 {
1859 	void *new = realloc(ptr, size);
1860 
1861 	if (new == NULL) {
1862 		rcmd_exit(ENOMEM);
1863 	}
1864 	return (new);
1865 }
1866 
1867 char *
1868 s_strdup(const char *str)
1869 {
1870 	char *buf = strdup(str);
1871 
1872 	if (buf == NULL) {
1873 		rcmd_exit(ENOMEM);
1874 	}
1875 	return (buf);
1876 }
1877 
1878 /*
1879  * Convert a version 1 ops vector to current ops vector
1880  * Fields missing in version 1 are set to NULL.
1881  */
1882 static struct rcm_mod_ops *
1883 modops_from_v1(void *ops_v1)
1884 {
1885 	struct rcm_mod_ops *ops;
1886 
1887 	ops = s_calloc(1, sizeof (struct rcm_mod_ops));
1888 	bcopy(ops_v1, ops, sizeof (struct rcm_mod_ops_v1));
1889 	return (ops);
1890 }
1891 
1892 /* call a module's getinfo routine; detects v1 ops and adjusts the call */
1893 static int
1894 call_getinfo(struct rcm_mod_ops *ops, rcm_handle_t *hdl, char *alias, id_t pid,
1895     uint_t flag, char **info, char **error, nvlist_t *client_props,
1896     rcm_info_t **infop)
1897 {
1898 	int rval;
1899 	struct rcm_mod_ops_v1 *v1_ops;
1900 
1901 	if (ops->version == RCM_MOD_OPS_V1) {
1902 		v1_ops = (struct rcm_mod_ops_v1 *)ops;
1903 		rval = v1_ops->rcmop_get_info(hdl, alias, pid, flag, info,
1904 		    infop);
1905 		if (rval != RCM_SUCCESS && *info != NULL)
1906 			*error = strdup(*info);
1907 		return (rval);
1908 	} else {
1909 		return (ops->rcmop_get_info(hdl, alias, pid, flag, info, error,
1910 		    client_props, infop));
1911 	}
1912 }
1913 
1914 void
1915 rcm_init_queue(rcm_queue_t *head)
1916 {
1917 	head->next = head->prev = head;
1918 }
1919 
1920 void
1921 rcm_enqueue_head(rcm_queue_t *head, rcm_queue_t *element)
1922 {
1923 	rcm_enqueue(head, element);
1924 }
1925 
1926 void
1927 rcm_enqueue_tail(rcm_queue_t *head, rcm_queue_t *element)
1928 {
1929 	rcm_enqueue(head->prev, element);
1930 }
1931 
1932 void
1933 rcm_enqueue(rcm_queue_t *list_element, rcm_queue_t *element)
1934 {
1935 	element->next = list_element->next;
1936 	element->prev = list_element;
1937 	element->next->prev = element;
1938 	list_element->next = element;
1939 }
1940 
1941 rcm_queue_t *
1942 rcm_dequeue_head(rcm_queue_t *head)
1943 {
1944 	rcm_queue_t	*element = head->next;
1945 	rcm_dequeue(element);
1946 	return (element);
1947 }
1948 
1949 rcm_queue_t *
1950 rcm_dequeue_tail(rcm_queue_t *head)
1951 {
1952 	rcm_queue_t	*element = head->prev;
1953 	rcm_dequeue(element);
1954 	return (element);
1955 }
1956 
1957 void
1958 rcm_dequeue(rcm_queue_t *element)
1959 {
1960 	element->prev->next = element->next;
1961 	element->next->prev = element->prev;
1962 	element->next = element->prev = NULL;
1963 }
1964