shl: move log.[ch] to shl_log.[ch]
[platform/upstream/kmscon.git] / src / kmscon_module.c
1 /*
2  * kmscon - Module handling
3  *
4  * Copyright (c) 2012-2013 David Herrmann <dh.herrmann@googlemail.com>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files
8  * (the "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included
15  * in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  */
25
26 #include <dirent.h>
27 #include <dlfcn.h>
28 #include <errno.h>
29 #include <stdbool.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <sys/types.h>
33 #include "githead.h"
34 #include "kmscon_module.h"
35 #include "kmscon_module_interface.h"
36 #include "shl_dlist.h"
37 #include "shl_log.h"
38 #include "shl_misc.h"
39
40 #define LOG_SUBSYSTEM "module"
41
42 static struct shl_dlist module_list = SHL_DLIST_INIT(module_list);
43
44 int kmscon_module_open(struct kmscon_module **out, const char *file)
45 {
46         int ret;
47         struct kmscon_module *module;
48         void *handle;
49
50         if (!out || !file)
51                 return -EINVAL;
52
53         log_debug("opening module %s", file);
54
55         handle = dlopen(file, RTLD_NOW);
56         if (!handle) {
57                 log_error("cannot open module %s (%d): %s",
58                           file, errno, dlerror());
59                 return -EFAULT;
60         }
61
62         module = dlsym(handle, "module");
63         if (!module) {
64                 log_error("cannot find module-info for %s", file);
65                 ret = -EFAULT;
66                 goto err_unload;
67         }
68
69         if (strcmp(module->info.githead, BUILD_GIT_HEAD)) {
70                 log_error("incompatible module %s (%s != %s)",
71                           file, module->info.githead, BUILD_GIT_HEAD);
72                 ret = -EFAULT;
73                 goto err_unload;
74         }
75
76         if (module->ref != 0) {
77                 log_error("module %s already loaded (%ld)",
78                           file, module->ref);
79                 ret = -EFAULT;
80                 goto err_unload;
81         }
82
83         log_debug("Initializing module: %s", file);
84
85         module->ref = 1;
86         module->loaded = false;
87         module->handle = handle;
88
89         module->file = strdup(file);
90         if (!module->file) {
91                 ret = -ENOMEM;
92                 goto err_unload;
93         }
94
95         log_debug("  Date: %s %s", module->info.date, module->info.time);
96         log_debug("  GIT: %s", module->info.githead);
97         log_debug("  Hooks: %p %p %p %p",
98                   module->info.init,
99                   module->info.load,
100                   module->info.unload,
101                   module->info.exit);
102
103         if (module->info.init) {
104                 ret = module->info.init();
105                 if (ret) {
106                         log_error("loading module %s failed: %d",
107                                   module->file, ret);
108                         goto err_file;
109                 }
110         }
111
112         *out = module;
113         return 0;
114
115 err_file:
116         free(module->file);
117 err_unload:
118         dlclose(handle);
119         return ret;
120 }
121
122 void kmscon_module_ref(struct kmscon_module *module)
123 {
124         if (!module || !module->ref)
125                 return;
126
127         ++module->ref;
128 }
129
130 void kmscon_module_unref(struct kmscon_module *module)
131 {
132         if (!module || !module->ref || --module->ref)
133                 return;
134
135         log_debug("closing module %s", module->file);
136
137         if (module->info.exit)
138                 module->info.exit();
139
140         free(module->file);
141         dlclose(module->handle);
142 }
143
144 int kmscon_module_load(struct kmscon_module *module)
145 {
146         int ret;
147
148         if (!module)
149                 return -EINVAL;
150
151         if (module->loaded)
152                 return -EALREADY;
153
154         log_debug("loading module %s", module->file);
155
156         if (module->info.load)
157                 ret = module->info.load();
158         else
159                 ret = 0;
160
161         if (ret)
162                 return ret;
163
164         module->loaded = true;
165         return 0;
166 }
167
168 void kmscon_module_unload(struct kmscon_module *module)
169 {
170         if (!module || !module->loaded)
171                 return;
172
173         log_debug("unloading module %s", module->file);
174
175         if (module->info.unload)
176                 module->info.unload();
177         module->loaded = false;
178 }
179
180 void kmscon_load_modules(void)
181 {
182         int ret;
183         DIR *ent;
184         struct dirent *buf, *de;
185         char *file;
186         struct kmscon_module *mod;
187
188         log_debug("loading global modules from %s", BUILD_MODULE_DIR);
189
190         if (!shl_dlist_empty(&module_list)) {
191                 log_error("trying to load global modules twice");
192                 return;
193         }
194
195         ent = opendir(BUILD_MODULE_DIR);
196         if (!ent) {
197                 if (errno == ENOTDIR || errno == ENOENT)
198                         log_debug("module directory %s not available",
199                                   BUILD_MODULE_DIR);
200                 else
201                         log_error("cannot open module directory %s (%d): %m",
202                                   BUILD_MODULE_DIR, errno);
203                 return;
204         }
205
206         ret = shl_dirent(BUILD_MODULE_DIR, &buf);
207         if (ret) {
208                 log_error("cannot allocate dirent object");
209                 closedir(ent);
210                 return;
211         }
212
213         while (true) {
214                 ret = readdir_r(ent, buf, &de);
215                 if (ret != 0) {
216                         log_error("cannot read directory %s: %d",
217                                   BUILD_MODULE_DIR, ret);
218                         break;
219                 } else if (!de) {
220                         break;
221                 }
222
223                 if (de->d_type == DT_DIR)
224                         continue;
225
226                 if (de->d_type != DT_REG &&
227                     de->d_type != DT_LNK &&
228                     de->d_type != DT_UNKNOWN) {
229                         log_warning("non-module file %s in module dir %s",
230                                     de->d_name, BUILD_MODULE_DIR);
231                         continue;
232                 }
233
234                 if (!shl_ends_with(de->d_name, ".so"))
235                         continue;
236
237                 ret = asprintf(&file, "%s/%s", BUILD_MODULE_DIR, de->d_name);
238                 if (ret < 0) {
239                         log_error("cannot allocate memory for module file name");
240                         continue;
241                 }
242
243                 ret = kmscon_module_open(&mod, file);
244                 free(file);
245
246                 if (ret)
247                         continue;
248
249                 ret = kmscon_module_load(mod);
250                 if (ret) {
251                         kmscon_module_unref(mod);
252                         continue;
253                 }
254
255                 shl_dlist_link(&module_list, &mod->list);
256         }
257
258         free(buf);
259         closedir(ent);
260 }
261
262 void kmscon_unload_modules(void)
263 {
264         struct kmscon_module *module;
265
266         log_debug("unloading modules");
267
268         while (!shl_dlist_empty(&module_list)) {
269                 module = shl_dlist_entry(module_list.prev, struct kmscon_module,
270                                          list);
271                 shl_dlist_unlink(&module->list);
272                 kmscon_module_unload(module);
273                 kmscon_module_unref(module);
274         }
275 }