module.c (5632B)
1 /*
2 * Copyright (c) 2013-2022 Joris Vink <joris@coders.se>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include <sys/types.h>
18 #include <sys/stat.h>
19
20 #include <dlfcn.h>
21
22 #include "kore.h"
23
24 #if !defined(KORE_NO_HTTP)
25 #include "http.h"
26 #endif
27
28 #if defined(KORE_USE_PYTHON)
29 #include "python_api.h"
30 #endif
31
32 #if defined(KORE_USE_LUA)
33 #include "lua_api.h"
34 #endif
35
36 static TAILQ_HEAD(, kore_module) modules;
37
38 static void native_free(struct kore_module *);
39 static void native_load(struct kore_module *);
40 static void native_reload(struct kore_module *);
41 static void *native_getsym(struct kore_module *, const char *);
42
43 struct kore_module_functions kore_native_module = {
44 .free = native_free,
45 .load = native_load,
46 .getsym = native_getsym,
47 .reload = native_reload,
48 };
49
50 void
51 kore_module_init(void)
52 {
53 TAILQ_INIT(&modules);
54 }
55
56 void
57 kore_module_cleanup(void)
58 {
59 struct kore_module *module, *next;
60
61 for (module = TAILQ_FIRST(&modules); module != NULL; module = next) {
62 next = TAILQ_NEXT(module, list);
63 TAILQ_REMOVE(&modules, module, list);
64 module->fun->free(module);
65 }
66 }
67
68 struct kore_module *
69 kore_module_load(const char *path, const char *onload, int type)
70 {
71 struct stat st;
72 struct kore_module *module;
73
74 module = kore_malloc(sizeof(struct kore_module));
75 module->ocb = NULL;
76 module->type = type;
77 module->onload = NULL;
78 module->handle = NULL;
79
80 if (path != NULL) {
81 if (stat(path, &st) == -1)
82 fatal("stat(%s): %s", path, errno_s);
83
84 module->path = kore_strdup(path);
85 } else {
86 module->path = NULL;
87 }
88
89 switch (module->type) {
90 case KORE_MODULE_NATIVE:
91 module->fun = &kore_native_module;
92 module->runtime = &kore_native_runtime;
93 break;
94 #if defined(KORE_USE_PYTHON)
95 case KORE_MODULE_PYTHON:
96 module->fun = &kore_python_module;
97 module->runtime = &kore_python_runtime;
98 break;
99 #endif
100 #if defined(KORE_USE_LUA)
101 case KORE_MODULE_LUA:
102 module->fun = &kore_lua_module;
103 module->runtime = &kore_lua_runtime;
104 break;
105 #endif
106 default:
107 fatal("kore_module_load: unknown type %d", type);
108 }
109
110 module->fun->load(module);
111 TAILQ_INSERT_TAIL(&modules, module, list);
112
113 if (onload != NULL) {
114 module->onload = kore_strdup(onload);
115 module->ocb = kore_malloc(sizeof(*module->ocb));
116 module->ocb->runtime = module->runtime;
117 module->ocb->addr = module->fun->getsym(module, onload);
118
119 if (module->ocb->addr == NULL) {
120 fatal("%s: onload '%s' not present",
121 module->path, onload);
122 }
123 }
124
125 return (module);
126 }
127
128 void
129 kore_module_onload(void)
130 {
131 struct kore_module *module;
132
133 TAILQ_FOREACH(module, &modules, list) {
134 if (module->path == NULL || module->ocb == NULL)
135 continue;
136
137 kore_runtime_onload(module->ocb, KORE_MODULE_LOAD);
138 }
139 }
140
141 void
142 kore_module_reload(int cbs)
143 {
144 struct stat st;
145 int ret;
146 struct kore_module *module;
147
148 TAILQ_FOREACH(module, &modules, list) {
149 if (module->path == NULL)
150 continue;
151
152 if (stat(module->path, &st) == -1) {
153 kore_log(LOG_NOTICE, "stat(%s): %s, skipping reload",
154 module->path, errno_s);
155 continue;
156 }
157
158 if (module->ocb != NULL && cbs == 1) {
159 ret = kore_runtime_onload(module->ocb,
160 KORE_MODULE_UNLOAD);
161 if (ret == KORE_RESULT_ERROR) {
162 kore_log(LOG_NOTICE,
163 "%s forced no reloaded", module->path);
164 continue;
165 }
166 }
167
168 module->fun->reload(module);
169
170 if (module->onload != NULL) {
171 kore_free(module->ocb);
172 module->ocb = kore_malloc(sizeof(*module->ocb));
173 module->ocb->runtime = module->runtime;
174 module->ocb->addr =
175 module->fun->getsym(module, module->onload);
176 if (module->ocb->addr == NULL) {
177 fatal("%s: onload '%s' not present",
178 module->path, module->onload);
179 }
180 }
181
182 if (module->ocb != NULL && cbs == 1)
183 kore_runtime_onload(module->ocb, KORE_MODULE_LOAD);
184
185 kore_log(LOG_NOTICE, "reloaded '%s' module", module->path);
186 }
187
188 #if !defined(KORE_NO_HTTP)
189 kore_route_reload();
190 kore_validator_reload();
191 #endif
192 }
193
194 int
195 kore_module_loaded(void)
196 {
197 if (TAILQ_EMPTY(&modules))
198 return (0);
199
200 return (1);
201 }
202
203 void *
204 kore_module_getsym(const char *symbol, struct kore_runtime **runtime)
205 {
206 void *ptr;
207 struct kore_module *module;
208
209 if (runtime != NULL)
210 *runtime = NULL;
211
212 TAILQ_FOREACH(module, &modules, list) {
213 ptr = module->fun->getsym(module, symbol);
214 if (ptr != NULL) {
215 if (runtime != NULL)
216 *runtime = module->runtime;
217 return (ptr);
218 }
219 }
220
221 return (NULL);
222 }
223
224 static void *
225 native_getsym(struct kore_module *module, const char *symbol)
226 {
227 return (dlsym(module->handle, symbol));
228 }
229
230 static void
231 native_free(struct kore_module *module)
232 {
233 kore_free(module->path);
234 (void)dlclose(module->handle);
235 kore_free(module);
236 }
237
238 static void
239 native_reload(struct kore_module *module)
240 {
241 if (dlclose(module->handle))
242 fatal("cannot close existing module: %s", dlerror());
243 module->fun->load(module);
244 }
245
246 static void
247 native_load(struct kore_module *module)
248 {
249 module->handle = dlopen(module->path, RTLD_NOW | RTLD_GLOBAL);
250 if (module->handle == NULL)
251 fatal("%s: %s", module->path, dlerror());
252 }