src/plugins.{c,h}: indicate success as return value of load_plugin
[vlock.git] / src / plugins.c
blob61f1da483e5edbaf667c46d109eb807636a1a2f1
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>
17 #include "plugins.h"
19 #include "list.h"
20 #include "tsort.h"
22 #include "plugin.h"
23 #include "util.h"
25 /* the list of plugins */
26 static struct list *plugins = &(struct list){ NULL, NULL };
28 /****************/
29 /* dependencies */
30 /****************/
32 #define SUCCEEDS 0
33 #define PRECEEDS 1
34 #define REQUIRES 2
35 #define NEEDS 3
36 #define DEPENDS 4
37 #define CONFLICTS 5
39 const char *dependency_names[nr_dependencies] = {
40 "succeeds",
41 "preceeds",
42 "requires",
43 "needs",
44 "depends",
45 "conflicts",
48 /*********/
49 /* hooks */
50 /*********/
52 static void handle_vlock_start(const char * hook_name);
53 static void handle_vlock_end(const char * hook_name);
54 static void handle_vlock_save(const char * hook_name);
55 static void handle_vlock_save_abort(const char * hook_name);
57 const struct hook hooks[nr_hooks] = {
58 { "vlock_start", handle_vlock_start },
59 { "vlock_end", handle_vlock_end },
60 { "vlock_save", handle_vlock_save },
61 { "vlock_save_abort", handle_vlock_save_abort },
64 /**********************/
65 /* exported functions */
66 /**********************/
68 /* helper declarations */
69 static struct plugin *__load_plugin(const char *name);
70 static void __resolve_depedencies(void);
71 static void sort_plugins(void);
73 bool load_plugin(const char *name)
75 return __load_plugin(name) == NULL;
78 void resolve_dependencies(void)
80 __resolve_depedencies();
81 sort_plugins();
84 void unload_plugins(void)
86 list_delete_for_each(plugins, plugin_item)
87 destroy_plugin(plugin_item->data);
90 void plugin_hook(const char *hook_name)
92 for (size_t i = 0; i < nr_hooks; i++)
93 /* Get the handler and call it. */
94 if (strcmp(hook_name, hooks[i].name) == 0) {
95 hooks[i].handler(hook_name);
96 return;
99 fatal_error("vlock-plugins: invalid hook name '%s'", hook_name);
102 /********************/
103 /* helper functions */
104 /********************/
106 static struct plugin *get_plugin(const char *name)
108 list_for_each(plugins, plugin_item) {
109 struct plugin *p = plugin_item->data;
110 if (strcmp(name, p->name) == 0)
111 return p;
114 return NULL;
117 /* Load and return the named plugin. Aborts on error. */
118 static struct plugin *__load_plugin(const char *name)
120 char *e1 = NULL;
121 char *e2 = NULL;
122 struct plugin *p = get_plugin(name);
124 if (p != NULL)
125 return p;
127 p = open_module(name, &e1);
129 if (p == NULL)
130 p = open_script(name, &e2);
132 if (p == NULL) {
133 if (e1 == NULL && e2 == NULL)
134 fatal_error("vlock-plugins: error loading plugin '%s'", name);
136 if (e1 != NULL) {
137 fprintf(stderr, "vlock-plugins: error loading module '%s': %s\n", name, e1);
138 free(e1);
140 if (e2 != NULL) {
141 fprintf(stderr, "vlock-plugins: error loading script '%s': %s\n", name, e2);
142 free(e2);
145 abort();
148 list_append(plugins, p);
149 return p;
152 /* Resolve the dependencies of the plugins. */
153 static void __resolve_depedencies(void)
155 struct list *required_plugins = list_new();
157 /* Load plugins that are required. This automagically takes care of plugins
158 * that are required by the plugins loaded here because they are appended to
159 * the end of the list. */
160 list_for_each(plugins, plugin_item) {
161 struct plugin *p = plugin_item->data;
163 list_for_each(p->dependencies[REQUIRES], dependency_item)
164 list_append(required_plugins, __load_plugin(dependency_item->data));
167 /* Fail if a plugins that is needed is not loaded. */
168 list_for_each(plugins, plugin_item) {
169 struct plugin *p = plugin_item->data;
171 list_for_each(p->dependencies[NEEDS], dependency_item) {
172 const char *d = dependency_item->data;
173 struct plugin *q = get_plugin(d);
175 if (q == NULL)
176 fatal_error("vlock-plugins: '%s' depends on '%s' which is not loaded", p->name, d);
178 list_append(required_plugins, q);
182 /* Unload plugins whose prerequisites are not present, fail if those plugins
183 * are required. */
184 list_for_each_manual(plugins, plugin_item) {
185 struct plugin *p = plugin_item->data;
186 bool dependencies_loaded = true;
188 list_for_each(p->dependencies[DEPENDS], dependency_item) {
189 const char *d = dependency_item->data;
190 struct plugin *q = get_plugin(d);
192 if (q == NULL) {
193 dependencies_loaded = false;
195 /* Abort if dependencies not met and plugin is required. */
196 if (list_find(required_plugins, p) != NULL)
197 fatal_error(
198 "vlock-plugins: '%s' is required by some other plugin\n"
199 " but depends on '%s' which is not loaded",
200 p->name, d);
202 break;
206 if (!dependencies_loaded) {
207 plugin_item = list_delete_item(plugins, plugin_item);
208 destroy_plugin(p);
209 } else {
210 plugin_item = plugin_item->next;
214 list_free(required_plugins);
216 /* Fail if conflicting plugins are loaded. */
217 list_for_each(plugins, plugin_item) {
218 struct plugin *p = plugin_item->data;
220 list_for_each(p->dependencies[CONFLICTS], dependency_item) {
221 const char *d = dependency_item->data;
222 if (get_plugin(d) == NULL)
223 fatal_error("vlock-plugins: '%s' and '%s' cannot be loaded at the same time", p->name, d);
228 static struct list *get_edges(void);
230 /* Sort the list of plugins according to their "preceeds" and "succeeds"
231 * dependencies. Fails if sorting is not possible because of circles. */
232 static void sort_plugins(void)
234 struct list *edges = get_edges();
235 bool circles_present;
237 /* Topological sort. */
238 tsort(plugins, edges);
240 circles_present = !list_is_empty(edges);
242 list_delete_for_each(edges, edge_item) {
243 struct edge *e = edge_item->data;
244 struct plugin *p = e->predecessor;
245 struct plugin *s = e->successor;
247 fprintf(stderr, "\t%s\tmust come before\t%s\n", p->name, s->name);
248 free(e);
251 if (circles_present)
252 fatal_error("vlock-plugins: circular dependencies detected");
255 static struct edge *make_edge(struct plugin *p, struct plugin *s)
257 struct edge *e = ensure_malloc(sizeof *e);
258 e->predecessor = p;
259 e->successor = s;
260 return e;
263 /* Get the edges of the plugin graph specified by each plugin's "preceeds" and
264 * "succeeds" dependencies. */
265 static struct list *get_edges(void)
267 struct list *edges = list_new();
269 list_for_each(plugins, plugin_item) {
270 struct plugin *p = plugin_item->data;
271 /* p must come after these */
272 list_for_each(p->dependencies[SUCCEEDS], predecessor_item) {
273 struct plugin *q = get_plugin(predecessor_item->data);
275 if (q != NULL)
276 list_append(edges, make_edge(q, p));
279 /* p must come before these */
280 list_for_each(p->dependencies[PRECEEDS], successor_item) {
281 struct plugin *q = get_plugin(successor_item->data);
283 if (q != NULL)
284 list_append(edges, make_edge(p, q));
288 return edges;
291 /************/
292 /* handlers */
293 /************/
295 /* Call the "vlock_start" hook of each plugin. Fails if the hook of one of the
296 * plugins fails. In this case the "vlock_end" hooks of all plugins that were
297 * called before are called in reverse order. */
298 void handle_vlock_start(const char *hook_name)
300 list_for_each(plugins, plugin_item) {
301 struct plugin *p = plugin_item->data;
303 if (!p->call_hook(p, hook_name)) {
304 list_for_each_reverse_from(plugins, reverse_item, plugin_item) {
305 struct plugin *r = reverse_item->data;
306 r->call_hook(r, "vlock_end");
309 fatal_error("vlock-plugins: error in '%s' hook of plugin '%s'", hook_name, p->name);
314 /* Call the "vlock_end" hook of each plugin in reverse order. Never fails. */
315 void handle_vlock_end(const char *hook_name)
317 list_for_each_reverse(plugins, plugin_item) {
318 struct plugin *p = plugin_item->data;
319 (void) p->call_hook(p, hook_name);
323 /* Call the "vlock_save" hook of each plugin. Never fails. If the hook of a
324 * plugin fails its "vlock_save_abort" hook is called and both hooks are never
325 * called again afterwards. */
326 void handle_vlock_save(const char *hook_name)
328 list_for_each(plugins, plugin_item) {
329 struct plugin *p = plugin_item->data;
331 if (p->save_disabled)
332 continue;
334 if (!p->call_hook(p, hook_name)) {
335 p->save_disabled = true;
336 p->call_hook(p, "vlock_save_abort");
341 /* Call the "vlock_save" hook of each plugin. Never fails. If the hook of a
342 * plugin fails both hooks "vlock_save" and "vlock_save_abort" are never called
343 * again afterwards. */
344 void handle_vlock_save_abort(const char *hook_name)
346 list_for_each_reverse(plugins, plugin_item) {
347 struct plugin *p = plugin_item->data;
349 if (p->save_disabled)
350 continue;
352 if (!p->call_hook(p, hook_name))
353 p->save_disabled = true;