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
36int need_cleanup;	/* flag indicating if clean up is needed */
37
38static mutex_t mod_lock;	/* protects module list */
39static module_t *module_head;	/* linked list of modules */
40static rsrc_node_t *rsrc_root;	/* root of all resources */
41
42/*
43 * Misc help routines
44 */
45static void rcmd_db_print();
46static void rcm_handle_free(rcm_handle_t *);
47static rcm_handle_t *rcm_handle_alloc(module_t *);
48static void rsrc_clients_free(client_t *);
49static struct rcm_mod_ops *modops_from_v1(void *);
50static int call_getinfo(struct rcm_mod_ops *, rcm_handle_t *, char *, id_t,
51    uint_t, char **, char **, nvlist_t *, rcm_info_t **);
52static int node_action(rsrc_node_t *, void *);
53
54extern 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 */
62char *
63resolve_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 */
94int
95rsrc_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 */
117static void
118module_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 */
131static void
132module_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
144struct rcm_mod_ops *
145module_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 */
158static const char *
159module_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
169int
170module_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 */
183static void
184module_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 */
217static module_t *
218module_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
313fail:
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 */
328static module_t *
329cli_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 */
373static void
374cli_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 */
413void
414add_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*/
513static client_t *
514rsrc_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 */
543client_t *
544rsrc_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 */
562static void
563rsrc_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 */
573static void
574rsrc_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 */
600static void
601rsrc_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 */
630static int
631rsrc_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 */
933int
934rsrc_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 */
975static rsrc_node_t *
976rn_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 */
992static void
993rn_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 */
1015static rsrc_node_t *
1016rn_get_sibling(rsrc_node_t *node)
1017{
1018	return (node->sibling);
1019}
1020
1021/*
1022 * Find first child
1023 */
1024static rsrc_node_t *
1025rn_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 */
1033static rsrc_node_t *
1034rn_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 */
1079static void
1080pn_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
1097static char *
1098pn_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 */
1127int
1128rsrc_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*/
1188int
1189rsrc_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 */
1244int
1245rsrc_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
1295struct 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*/
1304static void
1305walk_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 */
1369static void
1370rsrc_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 */
1392static int
1393node_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 */
1431int
1432rsrc_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 */
1458int
1459rsrc_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
1510out:
1511	(void) rcm_append_info(info, result);
1512	return (rv);
1513}
1514
1515/*
1516 * Get the list of currently loaded module
1517 */
1518rcm_info_t *
1519rsrc_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 */
1540void
1541rcmd_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 */
1607void
1608rcmd_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 */
1643int
1644proc_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 */
1665static void
1666clean_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*/
1691static int
1692clean_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
1701static void
1702clean_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
1710static void
1711db_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
1747void
1748rcmd_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*/
1762static int
1763print_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
1783static void
1784rcmd_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 */
1807static rcm_handle_t *
1808rcm_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 */
1825static void
1826rcm_handle_free(rcm_handle_t *handle)
1827{
1828	free(handle);
1829}
1830
1831/*
1832 * help function that exit on memory outage
1833 */
1834void *
1835s_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
1845void *
1846s_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
1856void *
1857s_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
1867char *
1868s_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 */
1882static struct rcm_mod_ops *
1883modops_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 */
1893static int
1894call_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
1914void
1915rcm_init_queue(rcm_queue_t *head)
1916{
1917	head->next = head->prev = head;
1918}
1919
1920void
1921rcm_enqueue_head(rcm_queue_t *head, rcm_queue_t *element)
1922{
1923	rcm_enqueue(head, element);
1924}
1925
1926void
1927rcm_enqueue_tail(rcm_queue_t *head, rcm_queue_t *element)
1928{
1929	rcm_enqueue(head->prev, element);
1930}
1931
1932void
1933rcm_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
1941rcm_queue_t *
1942rcm_dequeue_head(rcm_queue_t *head)
1943{
1944	rcm_queue_t	*element = head->next;
1945	rcm_dequeue(element);
1946	return (element);
1947}
1948
1949rcm_queue_t *
1950rcm_dequeue_tail(rcm_queue_t *head)
1951{
1952	rcm_queue_t	*element = head->prev;
1953	rcm_dequeue(element);
1954	return (element);
1955}
1956
1957void
1958rcm_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