src/plugins.{c,h}, src/tsort.{c,h}: proper error handling in dependency handling
[vlock.git] / src / plugins.c
blob5876fc853a78340b3d7fae657ad13caf70bd332b
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 bool __resolve_depedencies(void);
72 static bool sort_plugins(void);
74 bool load_plugin(const char *name)
76 return __load_plugin(name) != NULL;
79 bool resolve_dependencies(void)
81 return __resolve_depedencies() && 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. */
118 static struct plugin *__load_plugin(const char *name)
120 struct plugin *p = get_plugin(name);
122 if (p != NULL)
123 return p;
125 /* Try to open a module first. */
126 p = new_plugin(name, module);
128 if (p != NULL)
129 goto success;
131 if (errno != ENOENT)
132 return NULL;
134 /* Now try to open a script. */
135 p = new_plugin(name, script);
137 if (p == NULL)
138 return NULL;
140 success:
141 if (!list_append(plugins, p)) {
142 GUARD_ERRNO(destroy_plugin(p));
143 return NULL;
146 return p;
149 /* Resolve the dependencies of the plugins. */
150 static bool __resolve_depedencies(void)
152 struct list *required_plugins = list_new();
154 /* Load plugins that are required. This automagically takes care of plugins
155 * that are required by the plugins loaded here because they are appended to
156 * the end of the list. */
157 list_for_each(plugins, plugin_item) {
158 struct plugin *p = plugin_item->data;
160 list_for_each(p->dependencies[REQUIRES], dependency_item) {
161 const char *d = dependency_item->data;
162 struct plugin *q = __load_plugin(d);
164 if (q == NULL) {
165 int errsv = errno;
166 fprintf(stderr, "vlock-plugin: '%s' requires '%s' which could not be loaded\n", p->name, d);
167 list_free(required_plugins);
168 errno = errsv;
169 return false;
172 if (!list_append(required_plugins, q)) {
173 GUARD_ERRNO(list_free(required_plugins));
174 return false;
179 /* Fail if a plugins that is needed is not loaded. */
180 list_for_each(plugins, plugin_item) {
181 struct plugin *p = plugin_item->data;
183 list_for_each(p->dependencies[NEEDS], dependency_item) {
184 const char *d = dependency_item->data;
185 struct plugin *q = get_plugin(d);
187 if (q == NULL) {
188 fprintf(stderr, "vlock-plugins: '%s' needs '%s' which is not loaded\n", p->name, d);
189 list_free(required_plugins);
190 errno = 0;
191 return false;
194 if (!list_append(required_plugins, q)) {
195 GUARD_ERRNO(list_free(required_plugins));
196 return false;
201 /* Unload plugins whose prerequisites are not present, fail if those plugins
202 * are required. */
203 list_for_each_manual(plugins, plugin_item) {
204 struct plugin *p = plugin_item->data;
205 bool dependencies_loaded = true;
207 list_for_each(p->dependencies[DEPENDS], dependency_item) {
208 const char *d = dependency_item->data;
209 struct plugin *q = get_plugin(d);
211 if (q == NULL) {
212 dependencies_loaded = false;
214 /* Abort if dependencies not met and plugin is required. */
215 if (list_find(required_plugins, p) != NULL) {
216 fprintf(stderr,
217 "vlock-plugins: '%s' is required by some other plugin\n"
218 " but depends on '%s' which is not loaded",
219 p->name, d);
220 list_free(required_plugins);
221 errno = 0;
222 return false;
225 break;
229 if (dependencies_loaded) {
230 plugin_item = plugin_item->next;
231 } else {
232 plugin_item = list_delete_item(plugins, plugin_item);
233 destroy_plugin(p);
237 list_free(required_plugins);
239 /* Fail if conflicting plugins are loaded. */
240 list_for_each(plugins, plugin_item) {
241 struct plugin *p = plugin_item->data;
243 list_for_each(p->dependencies[CONFLICTS], dependency_item) {
244 const char *d = dependency_item->data;
245 if (get_plugin(d) == NULL) {
246 fprintf(stderr, "vlock-plugins: '%s' and '%s' cannot be loaded at the same time", p->name, d);
247 errno = 0;
248 return false;
253 return true;
256 static struct list *get_edges(void);
258 /* Sort the list of plugins according to their "preceeds" and "succeeds"
259 * dependencies. Fails if sorting is not possible because of circles. */
260 static bool sort_plugins(void)
262 struct list *edges = get_edges();
263 struct list *sorted_plugins;
265 if (edges == NULL)
266 return false;
268 /* Topological sort. */
269 sorted_plugins = tsort(plugins, edges);
271 if (sorted_plugins == NULL && errno != 0)
272 return false;
274 list_delete_for_each(edges, edge_item) {
275 struct edge *e = edge_item->data;
276 struct plugin *p = e->predecessor;
277 struct plugin *s = e->successor;
279 fprintf(stderr, "\t%s\tmust come before\t%s\n", p->name, s->name);
280 free(e);
283 if (sorted_plugins != NULL) {
284 struct list *tmp = plugins;
285 plugins = sorted_plugins;
286 list_free(tmp);
287 return true;
288 } else {
289 fprintf(stderr, "vlock-plugins: circular dependencies detected\n");
290 return false;
294 static bool append_edge(struct list *edges, struct plugin *p, struct plugin *s)
296 struct edge *e = malloc(sizeof *e);
298 if (e == NULL)
299 return false;
301 e->predecessor = p;
302 e->successor = s;
304 if (!list_append(edges, e)) {
305 GUARD_ERRNO(free(e));
306 return false;
309 return true;
312 /* Get the edges of the plugin graph specified by each plugin's "preceeds" and
313 * "succeeds" dependencies. */
314 static struct list *get_edges(void)
316 struct list *edges = list_new();
318 if (edges == NULL)
319 return NULL;
321 list_for_each(plugins, plugin_item) {
322 struct plugin *p = plugin_item->data;
323 /* p must come after these */
324 list_for_each(p->dependencies[SUCCEEDS], predecessor_item) {
325 struct plugin *q = get_plugin(predecessor_item->data);
327 if (q != NULL)
328 if (!append_edge(edges, q, p))
329 goto error;
332 /* p must come before these */
333 list_for_each(p->dependencies[PRECEEDS], successor_item) {
334 struct plugin *q = get_plugin(successor_item->data);
336 if (q != NULL)
337 if (!append_edge(edges, p, q))
338 goto error;
342 return edges;
344 error:
345 GUARD_ERRNO(list_free(edges));
346 return NULL;
349 /************/
350 /* handlers */
351 /************/
353 /* Call the "vlock_start" hook of each plugin. Fails if the hook of one of the
354 * plugins fails. In this case the "vlock_end" hooks of all plugins that were
355 * called before are called in reverse order. */
356 void handle_vlock_start(const char *hook_name)
358 list_for_each(plugins, plugin_item) {
359 struct plugin *p = plugin_item->data;
361 if (!call_hook(p, hook_name)) {
362 list_for_each_reverse_from(plugins, reverse_item, plugin_item) {
363 struct plugin *r = reverse_item->data;
364 (void) call_hook(r, "vlock_end");
367 fatal_error("vlock-plugins: hook '%s' of plugin '%s' failed: %s", hook_name, p->name, STRERROR);
372 /* Call the "vlock_end" hook of each plugin in reverse order. Never fails. */
373 void handle_vlock_end(const char *hook_name)
375 list_for_each_reverse(plugins, plugin_item) {
376 struct plugin *p = plugin_item->data;
377 (void) call_hook(p, hook_name);
381 /* Call the "vlock_save" hook of each plugin. Never fails. If the hook of a
382 * plugin fails its "vlock_save_abort" hook is called and both hooks are never
383 * called again afterwards. */
384 void handle_vlock_save(const char *hook_name)
386 list_for_each(plugins, plugin_item) {
387 struct plugin *p = plugin_item->data;
389 if (p->save_disabled)
390 continue;
392 if (!call_hook(p, hook_name)) {
393 p->save_disabled = true;
394 call_hook(p, "vlock_save_abort");
399 /* Call the "vlock_save" hook of each plugin. Never fails. If the hook of a
400 * plugin fails both hooks "vlock_save" and "vlock_save_abort" are never called
401 * again afterwards. */
402 void handle_vlock_save_abort(const char *hook_name)
404 list_for_each_reverse(plugins, plugin_item) {
405 struct plugin *p = plugin_item->data;
407 if (p->save_disabled)
408 continue;
410 if (!call_hook(p, hook_name))
411 p->save_disabled = true;