--- /dev/null
+/*
+ * kmscon - Module handling
+ *
+ * Copyright (c) 2012-2013 David Herrmann <dh.herrmann@googlemail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <dirent.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include "githead.h"
+#include "kmscon_module.h"
+#include "kmscon_module_interface.h"
+#include "log.h"
+#include "shl_dlist.h"
+#include "shl_misc.h"
+
+#define LOG_SUBSYSTEM "module"
+
+static struct shl_dlist module_list = SHL_DLIST_INIT(module_list);
+
+int kmscon_module_open(struct kmscon_module **out, const char *file)
+{
+ int ret;
+ struct kmscon_module *module;
+ void *handle;
+
+ if (!out || !file)
+ return -EINVAL;
+
+ log_debug("opening module %s", file);
+
+ handle = dlopen(file, RTLD_NOW);
+ if (!handle) {
+ log_error("cannot open module %s (%d): %s",
+ file, errno, dlerror());
+ return -EFAULT;
+ }
+
+ module = dlsym(handle, "module");
+ if (!module) {
+ log_error("cannot find module-info for %s", file);
+ ret = -EFAULT;
+ goto err_unload;
+ }
+
+ if (strcmp(module->info.githead, BUILD_GIT_HEAD)) {
+ log_error("incompatible module %s (%s != %s)",
+ file, module->info.githead, BUILD_GIT_HEAD);
+ ret = -EFAULT;
+ goto err_unload;
+ }
+
+ if (module->ref != 0) {
+ log_error("module %s already loaded (%ld)",
+ file, module->ref);
+ ret = -EFAULT;
+ goto err_unload;
+ }
+
+ log_debug("Initializing module: %s", file);
+
+ module->ref = 1;
+ module->loaded = false;
+ module->handle = handle;
+
+ module->file = strdup(file);
+ if (!module->file) {
+ ret = -ENOMEM;
+ goto err_unload;
+ }
+
+ log_debug(" Date: %s %s", module->info.date, module->info.time);
+ log_debug(" GIT: %s", module->info.githead);
+ log_debug(" Hooks: %p %p %p %p",
+ module->info.init,
+ module->info.load,
+ module->info.unload,
+ module->info.exit);
+
+ if (module->info.init) {
+ ret = module->info.init();
+ if (ret) {
+ log_error("loading module %s failed: %d",
+ module->file, ret);
+ goto err_file;
+ }
+ }
+
+ *out = module;
+ return 0;
+
+err_file:
+ free(module->file);
+err_unload:
+ dlclose(handle);
+ return ret;
+}
+
+void kmscon_module_ref(struct kmscon_module *module)
+{
+ if (!module || !module->ref)
+ return;
+
+ ++module->ref;
+}
+
+void kmscon_module_unref(struct kmscon_module *module)
+{
+ if (!module || !module->ref || --module->ref)
+ return;
+
+ log_debug("closing module %s", module->file);
+
+ if (module->info.exit)
+ module->info.exit();
+
+ free(module->file);
+ dlclose(module->handle);
+}
+
+int kmscon_module_load(struct kmscon_module *module)
+{
+ int ret;
+
+ if (!module)
+ return -EINVAL;
+
+ if (module->loaded)
+ return -EALREADY;
+
+ log_debug("loading module %s", module->file);
+
+ if (module->info.load)
+ ret = module->info.load();
+ else
+ ret = 0;
+
+ if (ret)
+ return ret;
+
+ module->loaded = true;
+ return 0;
+}
+
+void kmscon_module_unload(struct kmscon_module *module)
+{
+ if (!module || !module->loaded)
+ return;
+
+ log_debug("unloading module %s", module->file);
+
+ if (module->info.unload)
+ module->info.unload();
+ module->loaded = false;
+}
+
+void kmscon_load_modules(void)
+{
+ int ret;
+ DIR *ent;
+ struct dirent *buf, *de;
+ char *file;
+ struct kmscon_module *mod;
+
+ log_debug("loading global modules from %s", BUILD_MODULE_DIR);
+
+ if (!shl_dlist_empty(&module_list)) {
+ log_error("trying to load global modules twice");
+ return;
+ }
+
+ ent = opendir(BUILD_MODULE_DIR);
+ if (!ent) {
+ if (errno == ENOTDIR || errno == ENOENT)
+ log_debug("module directory %s not available",
+ BUILD_MODULE_DIR);
+ else
+ log_error("cannot open module directory %s (%d): %m",
+ BUILD_MODULE_DIR, errno);
+ return;
+ }
+
+ ret = shl_dirent(BUILD_MODULE_DIR, &buf);
+ if (ret) {
+ log_error("cannot allocate dirent object");
+ closedir(ent);
+ return;
+ }
+
+ while (true) {
+ ret = readdir_r(ent, buf, &de);
+ if (ret != 0) {
+ log_error("cannot read directory %s: %d",
+ BUILD_MODULE_DIR, ret);
+ break;
+ } else if (!de) {
+ break;
+ }
+
+ if (de->d_type == DT_DIR)
+ continue;
+
+ if (de->d_type != DT_REG &&
+ de->d_type != DT_LNK &&
+ de->d_type != DT_UNKNOWN) {
+ log_warning("non-module file %s in module dir %s",
+ de->d_name, BUILD_MODULE_DIR);
+ continue;
+ }
+
+ if (!shl_ends_with(de->d_name, ".so"))
+ continue;
+
+ ret = asprintf(&file, "%s/%s", BUILD_MODULE_DIR, de->d_name);
+ if (ret < 0) {
+ log_error("cannot allocate memory for module file name");
+ continue;
+ }
+
+ ret = kmscon_module_open(&mod, file);
+ free(file);
+
+ if (ret)
+ continue;
+
+ ret = kmscon_module_load(mod);
+ if (ret) {
+ kmscon_module_unref(mod);
+ continue;
+ }
+
+ shl_dlist_link(&module_list, &mod->list);
+ }
+
+ free(buf);
+ closedir(ent);
+}
+
+void kmscon_unload_modules(void)
+{
+ struct kmscon_module *module;
+
+ log_debug("unloading modules");
+
+ while (!shl_dlist_empty(&module_list)) {
+ module = shl_dlist_entry(module_list.prev, struct kmscon_module,
+ list);
+ shl_dlist_unlink(&module->list);
+ kmscon_module_unload(module);
+ kmscon_module_unref(module);
+ }
+}
--- /dev/null
+/*
+ * kmscon - Module handling
+ *
+ * Copyright (c) 2012-2013 David Herrmann <dh.herrmann@googlemail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * Module Handling
+ * Several subsystems of kmscon provide a generic interface that is implemented
+ * by different backends. The user can choose a backend that is then used.
+ * To make out-of-tree development easier and, more importantly, to reduce the
+ * direct dependencies to external libraries, this subsystem implements a
+ * dynamically-loadable module system.
+ *
+ * Modules can be loaded and unloaded during runtime. A module basically
+ * provides memory-storage for code. As long as any code of a module is still
+ * used (that is, registered as callback) we must not unload the module.
+ * Therefore, we use reference-counting to allow other subsystems to acquire and
+ * release code sections.
+ *
+ * A module needs to provide "module_init". Everything else is optional.
+ * "module_init" is called after the module has been loaded and should
+ * initialize the module. "module_exit" is called after the module has been
+ * unloaded and the last reference to the module has been dropped. Therefore, it
+ * is safe to release all allocated resources in "module_exit".
+ *
+ * "module_load" is called after "module_init". A module should register its
+ * resources here. "module_unload" is called when the module is scheduled for
+ * removal. A module should unregister its resources here. However, it must not
+ * release the resources as there might still be users of it. Only when
+ * "module_exit" is called, kmscon guarantees that there are no more users and
+ * the module can release its resources.
+ */
+
+#ifndef KMSCON_MODULE_H
+#define KMSCON_MODULE_H
+
+#include <stdlib.h>
+
+struct kmscon_module;
+
+int kmscon_module_open(struct kmscon_module **out, const char *file);
+void kmscon_module_ref(struct kmscon_module *module);
+void kmscon_module_unref(struct kmscon_module *module);
+
+int kmscon_module_load(struct kmscon_module *module);
+void kmscon_module_unload(struct kmscon_module *module);
+
+void kmscon_load_modules(void);
+void kmscon_unload_modules(void);
+
+#endif /* KMSCON_MODULE_H */
--- /dev/null
+/*
+ * kmscon - Module Interface
+ *
+ * Copyright (c) 2012-2013 David Herrmann <dh.herrmann@googlemail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * Public Module Interface
+ */
+
+#ifndef KMSCON_MODULE_INTERFACE_H
+#define KMSCON_MODULE_INTERFACE_H
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include "githead.h"
+#include "kmscon_module.h"
+#include "shl_dlist.h"
+
+struct kmscon_module_info {
+ const char *githead;
+ const char *date;
+ const char *time;
+ int (*init) (void);
+ int (*load) (void);
+ void (*unload) (void);
+ void (*exit) (void);
+};
+
+struct kmscon_module {
+ struct kmscon_module_info info;
+ struct shl_dlist list;
+ unsigned long ref;
+ bool loaded;
+ void *handle;
+ char *file;
+};
+
+#define KMSCON_MODULE(_init, _load, _unload, _exit) \
+ struct kmscon_module module = { \
+ .info = { \
+ .githead = BUILD_GIT_HEAD, \
+ .date = __DATE__, \
+ .time = __TIME__, \
+ .init = _init, \
+ .load = _load, \
+ .unload = _unload, \
+ .exit = _exit, \
+ }, \
+ };
+
+extern struct kmscon_module module;
+#define KMSCON_THIS_MODULE (&module)
+
+#endif /* KMSCON_MODULE_INTERFACE_H */