1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in comodliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <dlfcn.h>
30 #include <link.h>
31 #include <pthread.h>
32 #include <assert.h>
33 
34 #include <fm/topo_mod.h>
35 
36 #include <topo_error.h>
37 #include <topo_alloc.h>
38 #include <topo_subr.h>
39 
40 typedef struct topo_rtld {
41 	void *rtld_dlp;		/* libdl(3DL) handle for shared library */
42 	int (*rtld_init)(topo_mod_t *); /* shared library's _topo_init() */
43 	void (*rtld_fini)(topo_mod_t *); /* shared library's _topo_fini() */
44 } topo_rtld_t;
45 
46 static int
47 rtld_fini(topo_mod_t *mod)
48 {
49 	topo_rtld_t *rp = mod->tm_data;
50 
51 	assert(mod != NULL);
52 
53 	if (mod->tm_flags & TOPO_MOD_REG) {
54 		rp->rtld_fini(mod);
55 		if (mod->tm_flags & TOPO_MOD_REG) {
56 			topo_mod_unregister(mod);
57 		}
58 	}
59 
60 	if (getenv("TOPONODLCLOSE") == NULL)
61 		(void) dlclose(rp->rtld_dlp);
62 	topo_mod_free(mod, rp, sizeof (topo_rtld_t));
63 
64 	return (0);
65 }
66 
67 static int
68 rtld_init(topo_mod_t *mod)
69 {
70 	int err;
71 	topo_rtld_t *rp;
72 	void *dlp;
73 
74 	if ((dlp = dlopen(mod->tm_path, RTLD_LOCAL | RTLD_NOW)) == NULL) {
75 		topo_dprintf(TOPO_DBG_ERR,
76 		    "dlopen() failed: %s\n", dlerror());
77 		return (topo_mod_seterrno(mod, ETOPO_RTLD_OPEN));
78 	}
79 
80 	if ((rp = mod->tm_data = topo_mod_alloc(mod, sizeof (topo_rtld_t)))
81 	    == NULL)
82 		return (topo_mod_seterrno(mod, ETOPO_RTLD_OPEN));
83 
84 	rp->rtld_dlp = dlp;
85 	rp->rtld_init = (int (*)())dlsym(dlp, "_topo_init");
86 	rp->rtld_fini = (void (*)())dlsym(dlp, "_topo_fini");
87 
88 	if (rp->rtld_init == NULL) {
89 		(void) dlclose(dlp);
90 		topo_free(rp, sizeof (topo_rtld_t));
91 		return (topo_mod_seterrno(mod, ETOPO_RTLD_INIT));
92 	}
93 
94 	(void) pthread_mutex_unlock(&mod->tm_lock);
95 
96 	/*
97 	 * Call _topo_init() in the module.
98 	 */
99 	err = rp->rtld_init(mod);
100 
101 	if (err < 0 || !(mod->tm_flags & TOPO_MOD_REG)) {
102 		(void) rtld_fini(mod);
103 		return (topo_mod_seterrno(mod, ETOPO_MOD_NOREG));
104 	}
105 
106 	return (0);
107 }
108 
109 const topo_modops_t topo_rtld_ops = {
110 	rtld_init,
111 	rtld_fini,
112 };
113