vlock 2.2
[vlock.git] / src / plugins.c
blob2cad49b6aefa13b3f56489a7db29840bca87431a
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 break;
100 /********************/
101 /* helper functions */
102 /********************/
104 static struct plugin *get_plugin(const char *name)
106 list_for_each(plugins, plugin_item) {
107 struct plugin *p = plugin_item->data;
108 if (strcmp(name, p->name) == 0)
109 return p;
112 return NULL;
115 /* Load and return the named plugin. */
116 static struct plugin *__load_plugin(const char *name)
118 struct plugin *p = get_plugin(name);
120 if (p != NULL)
121 return p;
123 /* Try to open a module first. */
124 p = new_plugin(name, module);
126 if (p != NULL)
127 goto success;
129 if (errno != ENOENT)
130 return NULL;
132 /* Now try to open a script. */
133 p = new_plugin(name, script);
135 if (p == NULL)
136 return NULL;
138 success:
139 if (!list_append(plugins, p)) {
140 GUARD_ERRNO(destroy_plugin(p));
141 return NULL;
144 return p;
147 /* Resolve the dependencies of the plugins. */
148 static bool __resolve_depedencies(void)
150 struct list *required_plugins = list_new();
152 /* Load plugins that are required. This automagically takes care of plugins
153 * that are required by the plugins loaded here because they are appended to
154 * the end of the list. */
155 list_for_each(plugins, plugin_item) {
156 struct plugin *p = plugin_item->data;
158 list_for_each(p->dependencies[REQUIRES], dependency_item) {
159 const char *d = dependency_item->data;
160 struct plugin *q = __load_plugin(d);
162 if (q == NULL) {
163 int errsv = errno;
164 fprintf(stderr, "vlock-plugins: '%s' requires '%s' which could not be loaded\n", p->name, d);
165 list_free(required_plugins);
166 errno = errsv;
167 return false;
170 if (!list_append(required_plugins, q)) {
171 GUARD_ERRNO(list_free(required_plugins));
172 return false;
177 /* Fail if a plugins that is needed is not loaded. */
178 list_for_each(plugins, plugin_item) {
179 struct plugin *p = plugin_item->data;
181 list_for_each(p->dependencies[NEEDS], dependency_item) {
182 const char *d = dependency_item->data;
183 struct plugin *q = get_plugin(d);
185 if (q == NULL) {
186 fprintf(stderr, "vlock-plugins: '%s' needs '%s' which is not loaded\n", p->name, d);
187 list_free(required_plugins);
188 errno = 0;
189 return false;
192 if (!list_append(required_plugins, q)) {
193 GUARD_ERRNO(list_free(required_plugins));
194 return false;
199 /* Unload plugins whose prerequisites are not present, fail if those plugins
200 * are required. */
201 list_for_each_manual(plugins, plugin_item) {
202 struct plugin *p = plugin_item->data;
203 bool dependencies_loaded = true;
205 list_for_each(p->dependencies[DEPENDS], dependency_item) {
206 const char *d = dependency_item->data;
207 struct plugin *q = get_plugin(d);
209 if (q == NULL) {
210 dependencies_loaded = false;
212 /* Abort if dependencies not met and plugin is required. */
213 if (list_find(required_plugins, p) != NULL) {
214 fprintf(stderr,
215 "vlock-plugins: '%s' is required by some other plugin\n"
216 " but depends on '%s' which is not loaded",
217 p->name, d);
218 list_free(required_plugins);
219 errno = 0;
220 return false;
223 break;
227 if (dependencies_loaded) {
228 plugin_item = plugin_item->next;
229 } else {
230 plugin_item = list_delete_item(plugins, plugin_item);
231 destroy_plugin(p);
235 list_free(required_plugins);
237 /* Fail if conflicting plugins are loaded. */
238 list_for_each(plugins, plugin_item) {
239 struct plugin *p = plugin_item->data;
241 list_for_each(p->dependencies[CONFLICTS], dependency_item) {
242 const char *d = dependency_item->data;
243 if (get_plugin(d) != NULL) {
244 fprintf(stderr, "vlock-plugins: '%s' and '%s' cannot be loaded at the same time\n", p->name, d);
245 errno = 0;
246 return false;
251 return true;
254 static struct list *get_edges(void);
256 /* Sort the list of plugins according to their "preceeds" and "succeeds"
257 * dependencies. Fails if sorting is not possible because of circles. */
258 static bool sort_plugins(void)
260 struct list *edges = get_edges();
261 struct list *sorted_plugins;
263 if (edges == NULL)
264 return false;
266 /* Topological sort. */
267 sorted_plugins = tsort(plugins, edges);
269 if (sorted_plugins == NULL && errno != 0)
270 return false;
272 list_delete_for_each(edges, edge_item) {
273 struct edge *e = edge_item->data;
274 struct plugin *p = e->predecessor;
275 struct plugin *s = e->successor;
277 fprintf(stderr, "\t%s\tmust come before\t%s\n", p->name, s->name);
278 free(e);
281 if (sorted_plugins != NULL) {
282 /* Switch the global list of plugins for the sorted list. The global list
283 * is static and cannot be freed. */
284 struct list_item *first = sorted_plugins->first;
285 struct list_item *last = sorted_plugins->last;
287 sorted_plugins->first = plugins->first;
288 sorted_plugins->last = plugins->last;
290 plugins->first = first;
291 plugins->last = last;
293 list_free(sorted_plugins);
295 return true;
296 } else {
297 fprintf(stderr, "vlock-plugins: circular dependencies detected\n");
298 return false;
302 static bool append_edge(struct list *edges, struct plugin *p, struct plugin *s)
304 struct edge *e = malloc(sizeof *e);
306 if (e == NULL)
307 return false;
309 e->predecessor = p;
310 e->successor = s;
312 if (!list_append(edges, e)) {
313 GUARD_ERRNO(free(e));
314 return false;
317 return true;
320 /* Get the edges of the plugin graph specified by each plugin's "preceeds" and
321 * "succeeds" dependencies. */
322 static struct list *get_edges(void)
324 struct list *edges = list_new();
326 if (edges == NULL)
327 return NULL;
329 list_for_each(plugins, plugin_item) {
330 struct plugin *p = plugin_item->data;
331 /* p must come after these */
332 list_for_each(p->dependencies[SUCCEEDS], predecessor_item) {
333 struct plugin *q = get_plugin(predecessor_item->data);
335 if (q != NULL)
336 if (!append_edge(edges, q, p))
337 goto error;
340 /* p must come before these */
341 list_for_each(p->dependencies[PRECEEDS], successor_item) {
342 struct plugin *q = get_plugin(successor_item->data);
344 if (q != NULL)
345 if (!append_edge(edges, p, q))
346 goto error;
350 return edges;
352 error:
353 GUARD_ERRNO(list_free(edges));
354 return NULL;
357 /************/
358 /* handlers */
359 /************/
361 /* Call the "vlock_start" hook of each plugin. Fails if the hook of one of the
362 * plugins fails. In this case the "vlock_end" hooks of all plugins that were
363 * called before are called in reverse order. */
364 void handle_vlock_start(const char *hook_name)
366 list_for_each(plugins, plugin_item) {
367 struct plugin *p = plugin_item->data;
369 if (!call_hook(p, hook_name)) {
370 int errsv = errno;
372 list_for_each_reverse_from(plugins, reverse_item, plugin_item->previous) {
373 struct plugin *r = reverse_item->data;
374 (void) call_hook(r, "vlock_end");
377 if (errsv)
378 fprintf(stderr, "vlock: plugin '%s' failed: %s\n", p->name, strerror(errsv));
380 exit(EXIT_FAILURE);
385 /* Call the "vlock_end" hook of each plugin in reverse order. Never fails. */
386 void handle_vlock_end(const char *hook_name)
388 list_for_each_reverse(plugins, plugin_item) {
389 struct plugin *p = plugin_item->data;
390 (void) call_hook(p, hook_name);
394 /* Call the "vlock_save" hook of each plugin. Never fails. If the hook of a
395 * plugin fails its "vlock_save_abort" hook is called and both hooks are never
396 * called again afterwards. */
397 void handle_vlock_save(const char *hook_name)
399 list_for_each(plugins, plugin_item) {
400 struct plugin *p = plugin_item->data;
402 if (p->save_disabled)
403 continue;
405 if (!call_hook(p, hook_name)) {
406 p->save_disabled = true;
407 (void) call_hook(p, "vlock_save_abort");
412 /* Call the "vlock_save" hook of each plugin. Never fails. If the hook of a
413 * plugin fails both hooks "vlock_save" and "vlock_save_abort" are never called
414 * again afterwards. */
415 void handle_vlock_save_abort(const char *hook_name)
417 list_for_each_reverse(plugins, plugin_item) {
418 struct plugin *p = plugin_item->data;
420 if (p->save_disabled)
421 continue;
423 if (!call_hook(p, hook_name))
424 p->save_disabled = true;