redo plugin structure
[vlock.git] / src / plugins.c
blob14ae542df8cd7770798cec6ea7e8813d08d51672
1 /* plugins.c -- plugins for vlock, the VT locking program for linux
3 * This program is copyright (C) 2007 Frank Benkstein, and is free
4 * software which is freely distributable under the terms of the
5 * GNU General Public License version 2, included as the file COPYING in this
6 * distribution. It is NOT public domain software, and any
7 * redistribution not permitted by the GNU General Public License is
8 * expressly forbidden without prior written permission from
9 * the author.
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <errno.h>
18 #include "plugins.h"
20 #include "list.h"
21 #include "tsort.h"
23 #include "plugin.h"
24 #include "util.h"
26 /* the list of plugins */
27 static struct list *plugins = &(struct list){ NULL, NULL };
29 /****************/
30 /* dependencies */
31 /****************/
33 #define SUCCEEDS 0
34 #define PRECEEDS 1
35 #define REQUIRES 2
36 #define NEEDS 3
37 #define DEPENDS 4
38 #define CONFLICTS 5
40 const char *dependency_names[nr_dependencies] = {
41 "succeeds",
42 "preceeds",
43 "requires",
44 "needs",
45 "depends",
46 "conflicts",
49 /*********/
50 /* hooks */
51 /*********/
53 static void handle_vlock_start(const char * hook_name);
54 static void handle_vlock_end(const char * hook_name);
55 static void handle_vlock_save(const char * hook_name);
56 static void handle_vlock_save_abort(const char * hook_name);
58 const struct hook hooks[nr_hooks] = {
59 { "vlock_start", handle_vlock_start },
60 { "vlock_end", handle_vlock_end },
61 { "vlock_save", handle_vlock_save },
62 { "vlock_save_abort", handle_vlock_save_abort },
65 /**********************/
66 /* exported functions */
67 /**********************/
69 /* helper declarations */
70 static struct plugin *__load_plugin(const char *name);
71 static void __resolve_depedencies(void);
72 static void sort_plugins(void);
74 bool load_plugin(const char *name)
76 return __load_plugin(name) == NULL;
79 void resolve_dependencies(void)
81 __resolve_depedencies();
82 sort_plugins();
85 void unload_plugins(void)
87 list_delete_for_each(plugins, plugin_item)
88 destroy_plugin(plugin_item->data);
91 void plugin_hook(const char *hook_name)
93 for (size_t i = 0; i < nr_hooks; i++)
94 /* Get the handler and call it. */
95 if (strcmp(hook_name, hooks[i].name) == 0) {
96 hooks[i].handler(hook_name);
97 return;
100 fatal_error("vlock-plugins: invalid hook name '%s'", hook_name);
103 /********************/
104 /* helper functions */
105 /********************/
107 static struct plugin *get_plugin(const char *name)
109 list_for_each(plugins, plugin_item) {
110 struct plugin *p = plugin_item->data;
111 if (strcmp(name, p->name) == 0)
112 return p;
115 return NULL;
118 /* Load and return the named plugin. */
119 static struct plugin *__load_plugin(const char *name)
121 struct plugin *p = get_plugin(name);
123 if (p != NULL)
124 return p;
126 /* Try to open a module first. */
127 p = new_plugin(name, module);
129 if (p != NULL)
130 goto success;
132 if (errno != ENOENT)
133 return NULL;
135 /* Now try to open a script. */
136 p = new_plugin(name, script);
138 if (p == NULL)
139 return NULL;
141 success:
142 list_append(plugins, p);
143 return p;
146 /* Resolve the dependencies of the plugins. */
147 static void __resolve_depedencies(void)
149 struct list *required_plugins = list_new();
151 /* Load plugins that are required. This automagically takes care of plugins
152 * that are required by the plugins loaded here because they are appended to
153 * the end of the list. */
154 list_for_each(plugins, plugin_item) {
155 struct plugin *p = plugin_item->data;
157 list_for_each(p->dependencies[REQUIRES], dependency_item)
158 list_append(required_plugins, __load_plugin(dependency_item->data));
161 /* Fail if a plugins that is needed is not loaded. */
162 list_for_each(plugins, plugin_item) {
163 struct plugin *p = plugin_item->data;
165 list_for_each(p->dependencies[NEEDS], dependency_item) {
166 const char *d = dependency_item->data;
167 struct plugin *q = get_plugin(d);
169 if (q == NULL)
170 fatal_error("vlock-plugins: '%s' depends on '%s' which is not loaded", p->name, d);
172 list_append(required_plugins, q);
176 /* Unload plugins whose prerequisites are not present, fail if those plugins
177 * are required. */
178 list_for_each_manual(plugins, plugin_item) {
179 struct plugin *p = plugin_item->data;
180 bool dependencies_loaded = true;
182 list_for_each(p->dependencies[DEPENDS], dependency_item) {
183 const char *d = dependency_item->data;
184 struct plugin *q = get_plugin(d);
186 if (q == NULL) {
187 dependencies_loaded = false;
189 /* Abort if dependencies not met and plugin is required. */
190 if (list_find(required_plugins, p) != NULL)
191 fatal_error(
192 "vlock-plugins: '%s' is required by some other plugin\n"
193 " but depends on '%s' which is not loaded",
194 p->name, d);
196 break;
200 if (!dependencies_loaded) {
201 plugin_item = list_delete_item(plugins, plugin_item);
202 destroy_plugin(p);
203 } else {
204 plugin_item = plugin_item->next;
208 list_free(required_plugins);
210 /* Fail if conflicting plugins are loaded. */
211 list_for_each(plugins, plugin_item) {
212 struct plugin *p = plugin_item->data;
214 list_for_each(p->dependencies[CONFLICTS], dependency_item) {
215 const char *d = dependency_item->data;
216 if (get_plugin(d) == NULL)
217 fatal_error("vlock-plugins: '%s' and '%s' cannot be loaded at the same time", p->name, d);
222 static struct list *get_edges(void);
224 /* Sort the list of plugins according to their "preceeds" and "succeeds"
225 * dependencies. Fails if sorting is not possible because of circles. */
226 static void sort_plugins(void)
228 struct list *edges = get_edges();
229 bool circles_present;
231 /* Topological sort. */
232 tsort(plugins, edges);
234 circles_present = !list_is_empty(edges);
236 list_delete_for_each(edges, edge_item) {
237 struct edge *e = edge_item->data;
238 struct plugin *p = e->predecessor;
239 struct plugin *s = e->successor;
241 fprintf(stderr, "\t%s\tmust come before\t%s\n", p->name, s->name);
242 free(e);
245 if (circles_present)
246 fatal_error("vlock-plugins: circular dependencies detected");
249 static struct edge *make_edge(struct plugin *p, struct plugin *s)
251 struct edge *e = ensure_malloc(sizeof *e);
252 e->predecessor = p;
253 e->successor = s;
254 return e;
257 /* Get the edges of the plugin graph specified by each plugin's "preceeds" and
258 * "succeeds" dependencies. */
259 static struct list *get_edges(void)
261 struct list *edges = list_new();
263 list_for_each(plugins, plugin_item) {
264 struct plugin *p = plugin_item->data;
265 /* p must come after these */
266 list_for_each(p->dependencies[SUCCEEDS], predecessor_item) {
267 struct plugin *q = get_plugin(predecessor_item->data);
269 if (q != NULL)
270 list_append(edges, make_edge(q, p));
273 /* p must come before these */
274 list_for_each(p->dependencies[PRECEEDS], successor_item) {
275 struct plugin *q = get_plugin(successor_item->data);
277 if (q != NULL)
278 list_append(edges, make_edge(p, q));
282 return edges;
285 /************/
286 /* handlers */
287 /************/
289 /* Call the "vlock_start" hook of each plugin. Fails if the hook of one of the
290 * plugins fails. In this case the "vlock_end" hooks of all plugins that were
291 * called before are called in reverse order. */
292 void handle_vlock_start(const char *hook_name)
294 list_for_each(plugins, plugin_item) {
295 struct plugin *p = plugin_item->data;
297 if (!call_hook(p, hook_name)) {
298 list_for_each_reverse_from(plugins, reverse_item, plugin_item) {
299 struct plugin *r = reverse_item->data;
300 (void) call_hook(r, "vlock_end");
303 fatal_error("vlock-plugins: error in '%s' hook of plugin '%s'", hook_name, p->name);
308 /* Call the "vlock_end" hook of each plugin in reverse order. Never fails. */
309 void handle_vlock_end(const char *hook_name)
311 list_for_each_reverse(plugins, plugin_item) {
312 struct plugin *p = plugin_item->data;
313 (void) call_hook(p, hook_name);
317 /* Call the "vlock_save" hook of each plugin. Never fails. If the hook of a
318 * plugin fails its "vlock_save_abort" hook is called and both hooks are never
319 * called again afterwards. */
320 void handle_vlock_save(const char *hook_name)
322 list_for_each(plugins, plugin_item) {
323 struct plugin *p = plugin_item->data;
325 if (p->save_disabled)
326 continue;
328 if (!call_hook(p, hook_name)) {
329 p->save_disabled = true;
330 call_hook(p, "vlock_save_abort");
335 /* Call the "vlock_save" hook of each plugin. Never fails. If the hook of a
336 * plugin fails both hooks "vlock_save" and "vlock_save_abort" are never called
337 * again afterwards. */
338 void handle_vlock_save_abort(const char *hook_name)
340 list_for_each_reverse(plugins, plugin_item) {
341 struct plugin *p = plugin_item->data;
343 if (p->save_disabled)
344 continue;
346 if (!call_hook(p, hook_name))
347 p->save_disabled = true;