1 /*
2   $Id:$
3 
4   dynlib.c: Bare dynamic library load/unload support for uim
5 
6   Copyright (c) 2004-2013 uim Project https://github.com/uim/uim
7 
8   All rights reserved.
9 
10   Redistribution and use in source and binary forms, with or without
11   modification, are permitted provided that the following conditions
12   are met:
13 
14   1. Redistributions of source code must retain the above copyright
15      notice, this list of conditions and the following disclaimer.
16   2. Redistributions in binary form must reproduce the above copyright
17      notice, this list of conditions and the following disclaimer in the
18      documentation and/or other materials provided with the distribution.
19   3. Neither the name of authors nor the names of its contributors
20      may be used to endorse or promote products derived from this software
21      without specific prior written permission.
22 
23   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
24   ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26   ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
27   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29   OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32   OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33   SUCH DAMAGE.
34 */
35 
36 #include <config.h>
37 
38 #include <stdio.h>
39 #include <dlfcn.h>
40 
41 #include "uim.h"
42 #include "uim-internal.h"
43 #include "uim-scm.h"
44 #include "uim-scm-abbrev.h"
45 #if UIM_USE_NOTIFY_PLUGINS
46 #include "uim-notify.h"
47 #endif
48 #include "gettext.h"
49 
50 #ifndef HAVE_DLFUNC
51 #define dlfunc(handle, symbol) \
52   ((void (*)(void))(uintptr_t)dlsym((handle), (symbol)))
53 #endif
54 
55 /*
56  * SIOD's verbose-level compatible definition.
57  * See sigscheme/operations-siod.c for further information.
58  */
59 #define UIM_VLEVEL_DYNLIB 3
60 
61 #ifdef DEBUG
62 #define DPRINTFN(n,x)  if ((n) <= verbose_level()) fprintf x;
63 #else
64 #define DPRINTFN(n,x)
65 #endif
66 
67 static long verbose_level(void);
68 
69 struct dynlib_unbind_args {
70   uim_lisp lib_ptr;
71   uim_lisp init_proc;
72   uim_lisp quit_proc;
73 };
74 static uim_lisp dynlib_unbind(uim_lisp, uim_lisp, uim_lisp);
75 static void *dynlib_unbind_internal(struct dynlib_unbind_args *);
76 static uim_lisp dynlib_bind(uim_lisp);
77 static void *dynlib_bind_internal(uim_lisp);
78 static uim_lisp dynlib_unbind_all(uim_lisp);
79 
80 static long
verbose_level(void)81 verbose_level(void)
82 {
83   uim_lisp vlevel;
84 
85   vlevel = uim_scm_callf("verbose", "");
86   return C_INT(vlevel);
87 }
88 
89 /* Called from uim_init */
90 void
uim_init_dynlib(void)91 uim_init_dynlib(void)
92 {
93   uim_scm_init_proc1("%%dynlib-bind", dynlib_bind);
94   uim_scm_init_proc3("%%dynlib-unbind", dynlib_unbind);
95   uim_scm_init_proc1("%%dynlib-unbind-all", dynlib_unbind_all);
96 }
97 
98 /* Called from uim_quit */
99 void
uim_quit_dynlib(void)100 uim_quit_dynlib(void)
101 {
102 }
103 
104 static uim_lisp
dynlib_unbind(uim_lisp lib_ptr,uim_lisp init_proc,uim_lisp quit_proc)105 dynlib_unbind(uim_lisp lib_ptr,
106 	      uim_lisp init_proc,
107 	      uim_lisp quit_proc)
108 {
109   struct dynlib_unbind_args args;
110   args.lib_ptr = lib_ptr;
111   args.init_proc = init_proc;
112   args.quit_proc = quit_proc;
113 
114   return uim_scm_call_with_gc_ready_stack((uim_gc_gate_func_ptr)dynlib_unbind_internal, (void *)&args);
115 }
116 
117 static void *
dynlib_unbind_internal(struct dynlib_unbind_args * args)118 dynlib_unbind_internal(struct dynlib_unbind_args *args)
119 {
120   void *library;
121   void (*dynlib_instance_quit)(void);
122 
123   library = C_PTR(args->lib_ptr);
124   dynlib_instance_quit = C_FPTR(args->quit_proc);
125 
126   (*dynlib_instance_quit)();
127   dlclose(library);
128 
129   return uim_scm_t();
130 }
131 
132 static uim_lisp
dynlib_bind(uim_lisp name)133 dynlib_bind(uim_lisp name)
134 {
135   return uim_scm_call_with_gc_ready_stack((uim_gc_gate_func_ptr)dynlib_bind_internal, (void *)name);
136 }
137 
138 static void *
dynlib_bind_internal(uim_lisp name)139 dynlib_bind_internal(uim_lisp name)
140 {
141   void *library;
142   void (*dynlib_instance_init)(void);
143   void (*dynlib_instance_quit)(void);
144 
145   DPRINTFN(UIM_VLEVEL_DYNLIB, (stderr, "Loading %s", REFER_C_STR(name)));
146   library = dlopen(REFER_C_STR(name), RTLD_NOW);
147 
148   if (library == NULL) {
149     uim_notify_fatal(_("dynlib: %s: Load failed."), dlerror());
150     return uim_scm_f();
151   }
152 
153   dynlib_instance_init
154     = (void (*)(void))dlfunc(library, "uim_dynlib_instance_init");
155   dynlib_instance_quit
156     = (void (*)(void))dlfunc(library, "uim_dynlib_instance_quit");
157   if (!dynlib_instance_init) {
158     uim_notify_fatal(_("dynlib: %s: Initialization failed."), REFER_C_STR(name));
159     return uim_scm_f();
160   }
161 
162   DPRINTFN(UIM_VLEVEL_DYNLIB, (stderr, "Calling dynlib_instance_init() for %s.\n", REFER_C_STR(name)));
163   (*dynlib_instance_init)();
164 
165   return LIST3(MAKE_PTR(library),
166 	       MAKE_FPTR(dynlib_instance_init),
167 	       MAKE_FPTR(dynlib_instance_quit));
168 }
169 
170 
171 static void *
dynlib_unbind_all_internal(uim_lisp plugin_alist_)172 dynlib_unbind_all_internal(uim_lisp plugin_alist_)
173 {
174   /* call dlclose(3) collectively at the end in order to avoid GC problem */
175   uim_lisp alist_ = plugin_alist_;
176 
177   while (!NULLP(alist_)) {
178     uim_lisp plugin_, quit_proc_;
179     void (*dynlib_instance_quit)(void);
180 
181     plugin_ = CAR(alist_);
182     quit_proc_ = CAR(CDR(CDR(CDR(plugin_))));
183     if (!FALSEP(quit_proc_)) {
184       dynlib_instance_quit = C_FPTR(quit_proc_);
185       (*dynlib_instance_quit)();
186     }
187     alist_ = CDR(alist_);
188   }
189 
190   alist_ = plugin_alist_;
191   while (!NULLP(alist_)) {
192     uim_lisp plugin_, lib_;
193     void *library;
194 
195     plugin_ = CAR(alist_);
196     lib_ = CAR(CDR(plugin_));
197     if (!FALSEP(lib_)) {
198       library = C_PTR(lib_);
199       dlclose(library);
200     }
201     alist_ = CDR(alist_);
202   }
203 
204   return uim_scm_t();
205 }
206 
207 static uim_lisp
dynlib_unbind_all(uim_lisp plugin_alist_)208 dynlib_unbind_all(uim_lisp plugin_alist_)
209 {
210   return uim_scm_call_with_gc_ready_stack((uim_gc_gate_func_ptr)dynlib_unbind_all_internal, plugin_alist_);
211 }
212