src/script.{c,h}: convert scripts to GObject
[vlock.git] / src / plugins.c
blobd3f91ac2c596e068b936c9068812b2c3ad2b5e4d
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>
17 #include <assert.h>
19 #include <glib.h>
21 #include "plugins.h"
23 #include "tsort.h"
25 #include "plugin.h"
26 #include "module.h"
27 #include "script.h"
29 #include "util.h"
31 /* the list of plugins */
32 static GList *plugins = NULL;
34 /****************/
35 /* dependencies */
36 /****************/
38 #define SUCCEEDS 0
39 #define PRECEEDS 1
40 #define REQUIRES 2
41 #define NEEDS 3
42 #define DEPENDS 4
43 #define CONFLICTS 5
45 const char *dependency_names[nr_dependencies] = {
46 "succeeds",
47 "preceeds",
48 "requires",
49 "needs",
50 "depends",
51 "conflicts",
54 /*********/
55 /* hooks */
56 /*********/
58 static void handle_vlock_start(const char * hook_name);
59 static void handle_vlock_end(const char * hook_name);
60 static void handle_vlock_save(const char * hook_name);
61 static void handle_vlock_save_abort(const char * hook_name);
63 const struct hook hooks[nr_hooks] = {
64 { "vlock_start", handle_vlock_start },
65 { "vlock_end", handle_vlock_end },
66 { "vlock_save", handle_vlock_save },
67 { "vlock_save_abort", handle_vlock_save_abort },
70 /**********************/
71 /* exported functions */
72 /**********************/
74 /* helper declarations */
75 static VlockPlugin *__load_plugin(const char *name, GError **error);
76 static bool __resolve_depedencies(void);
77 static bool sort_plugins(void);
79 bool load_plugin(const char *name)
81 return __load_plugin(name, NULL) != NULL;
84 bool resolve_dependencies(void)
86 return __resolve_depedencies() && sort_plugins();
89 void unload_plugins(void)
91 while (plugins != NULL) {
92 g_object_unref(plugins->data);
93 plugins = g_list_delete_link(plugins, plugins);
97 void plugin_hook(const char *hook_name)
99 for (size_t i = 0; i < nr_hooks; i++)
100 /* Get the handler and call it. */
101 if (strcmp(hook_name, hooks[i].name) == 0) {
102 hooks[i].handler(hook_name);
103 break;
107 /********************/
108 /* helper functions */
109 /********************/
111 static gint plugin_name_compare(VlockPlugin *p, const char *name)
113 return strcmp(name, p->name);
116 static VlockPlugin *get_plugin(const char *name)
118 GList *item = g_list_find_custom(plugins, name, (GCompareFunc) plugin_name_compare);
120 if (item != NULL)
121 return item->data;
122 else
123 return NULL;
126 /* Load and return the named plugin. */
127 static VlockPlugin *__load_plugin(const char *name, GError **error)
129 VlockPlugin *p = get_plugin(name);
131 if (p != NULL)
132 return p;
134 GError *err = NULL;
136 /* Possible plugin types. */
137 GType plugin_types[] = { TYPE_VLOCK_MODULE, TYPE_VLOCK_SCRIPT, 0 };
139 for (size_t i = 0; plugin_types[i] != 0; i++) {
140 if (err == NULL || g_error_matches(err,
141 VLOCK_PLUGIN_ERROR,
142 VLOCK_PLUGIN_ERROR_NOT_FOUND))
143 /* Continue if the was no previous error or the error was "not found". */
144 g_clear_error(&err);
145 else
146 /* Bail out on real errors. */
147 break;
149 /* Create the plugin. */
150 p = g_object_new(plugin_types[i], "name", name, NULL);
152 /* Try to open the plugin. */
153 if (!vlock_plugin_open(p, &err)) {
154 g_assert(err != NULL);
155 g_object_unref(p);
156 p = NULL;
160 if (err != NULL) {
161 g_assert(p == NULL);
163 g_propagate_error(error, err);
165 return NULL;
166 } else {
167 g_assert(p != NULL);
169 plugins = g_list_append(plugins, p);
171 return p;
175 /* Resolve the dependencies of the plugins. */
176 static bool __resolve_depedencies(void)
178 GList *required_plugins = NULL;
180 /* Load plugins that are required. This automagically takes care of plugins
181 * that are required by the plugins loaded here because they are appended to
182 * the end of the list. */
183 for (GList *plugin_item = plugins;
184 plugin_item != NULL;
185 plugin_item = g_list_next(plugin_item)) {
186 VlockPlugin *p = plugin_item->data;
188 for (GList *dependency_item = p->dependencies[REQUIRES];
189 dependency_item != NULL;
190 dependency_item = g_list_next(dependency_item)) {
191 const char *d = dependency_item->data;
192 VlockPlugin *q = __load_plugin(d, NULL);
194 if (q == NULL) {
195 fprintf(stderr, "vlock-plugins: '%s' requires '%s' which could not be loaded\n", p->name, d);
196 g_list_free(required_plugins);
197 return false;
200 required_plugins = g_list_append(required_plugins, p);
204 /* Fail if a plugins that is needed is not loaded. */
205 for (GList *plugin_item = plugins;
206 plugin_item != NULL;
207 plugin_item = g_list_next(plugin_item)) {
208 VlockPlugin *p = plugin_item->data;
210 for (GList *dependency_item = p->dependencies[NEEDS];
211 dependency_item != NULL;
212 dependency_item = g_list_next(dependency_item)) {
213 const char *d = dependency_item->data;
214 VlockPlugin *q = get_plugin(d);
216 if (q == NULL) {
217 fprintf(stderr, "vlock-plugins: '%s' needs '%s' which is not loaded\n", p->name, d);
218 g_list_free(required_plugins);
219 errno = 0;
220 return false;
223 required_plugins = g_list_append(required_plugins, q);
227 /* Unload plugins whose prerequisites are not present, fail if those plugins
228 * are required. */
229 for (GList *plugin_item = plugins;
230 plugin_item != NULL; ) {
231 VlockPlugin *p = plugin_item->data;
232 bool dependencies_loaded = true;
234 for (GList *dependency_item = p->dependencies[DEPENDS];
235 dependency_item != NULL;
236 dependency_item = g_list_next(dependency_item)) {
237 const char *d = dependency_item->data;
238 VlockPlugin *q = get_plugin(d);
240 if (q == NULL) {
241 dependencies_loaded = false;
243 /* Abort if dependencies not met and plugin is required. */
244 if (g_list_find(required_plugins, p) != NULL) {
245 fprintf(stderr,
246 "vlock-plugins: '%s' is required by some other plugin\n"
247 " but depends on '%s' which is not loaded",
248 p->name, d);
249 g_list_free(required_plugins);
250 errno = 0;
251 return false;
254 break;
258 GList *next_plugin_item = g_list_next(plugin_item);
260 if (!dependencies_loaded) {
261 g_object_unref(p);
262 plugins = g_list_delete_link(plugins, plugin_item);
265 plugin_item = next_plugin_item;
268 g_list_free(required_plugins);
270 /* Fail if conflicting plugins are loaded. */
271 for (GList *plugin_item = plugins;
272 plugin_item != NULL;
273 plugin_item = g_list_next(plugin_item)) {
274 VlockPlugin *p = plugin_item->data;
276 for (GList *dependency_item = p->dependencies[CONFLICTS];
277 dependency_item != NULL;
278 dependency_item = g_list_next(dependency_item)) {
279 const char *d = dependency_item->data;
280 if (get_plugin(d) != NULL) {
281 fprintf(stderr, "vlock-plugins: '%s' and '%s' cannot be loaded at the same time\n", p->name, d);
282 errno = 0;
283 return false;
288 return true;
291 static GList *get_edges(void);
293 /* Sort the list of plugins according to their "preceeds" and "succeeds"
294 * dependencies. Fails if sorting is not possible because of circles. */
295 static bool sort_plugins(void)
297 GList *edges = get_edges();
298 GList *sorted_plugins;
300 /* Topological sort. */
301 sorted_plugins = tsort(plugins, &edges);
303 bool tsort_successful = (edges == NULL);
305 while (edges != NULL) {
306 struct edge *e = edges->data;
307 VlockPlugin *p = e->predecessor;
308 VlockPlugin *s = e->successor;
310 fprintf(stderr, "\t%s\tmust come before\t%s\n", p->name, s->name);
311 free(e);
312 edges = g_list_delete_link(edges, edges);
315 if (tsort_successful) {
316 /* Switch the global list of plugins for the sorted list. The global list
317 * is static and cannot be freed. */
319 assert(g_list_length(sorted_plugins) == g_list_length(plugins));
321 GList *tmp = plugins;
322 plugins = sorted_plugins;
324 g_list_free(tmp);
326 return true;
327 } else {
328 fprintf(stderr, "vlock-plugins: circular dependencies detected\n");
329 return false;
333 /* Get the edges of the plugin graph specified by each plugin's "preceeds" and
334 * "succeeds" dependencies. */
335 static GList *get_edges(void)
337 GList *edges = NULL;
339 for (GList *plugin_item = plugins;
340 plugin_item != NULL;
341 plugin_item = g_list_next(plugin_item)) {
342 VlockPlugin *p = plugin_item->data;
343 /* p must come after these */
344 for (GList *predecessor_item = p->dependencies[SUCCEEDS];
345 predecessor_item != NULL;
346 predecessor_item = g_list_next(predecessor_item)) {
347 VlockPlugin *q = get_plugin(predecessor_item->data);
349 if (q != NULL)
350 edges = g_list_append(edges, make_edge(q, p));
353 /* p must come before these */
354 for (GList *successor_item = p->dependencies[PRECEEDS];
355 successor_item != NULL;
356 successor_item = g_list_next(successor_item)) {
357 VlockPlugin *q = get_plugin(successor_item->data);
359 if (q != NULL)
360 edges = g_list_append(edges, make_edge(p, q));
364 return edges;
367 /************/
368 /* handlers */
369 /************/
371 /* Call the "vlock_start" hook of each plugin. Fails if the hook of one of the
372 * plugins fails. In this case the "vlock_end" hooks of all plugins that were
373 * called before are called in reverse order. */
374 void handle_vlock_start(const char *hook_name)
376 for (GList *plugin_item = plugins;
377 plugin_item != NULL;
378 plugin_item = g_list_next(plugin_item)) {
379 VlockPlugin *p = plugin_item->data;
381 if (!vlock_plugin_call_hook(p, hook_name)) {
382 int errsv = errno;
384 for (GList *reverse_item = g_list_previous(plugin_item);
385 reverse_item != NULL;
386 reverse_item = g_list_previous(reverse_item)) {
387 VlockPlugin *r = reverse_item->data;
388 (void) vlock_plugin_call_hook(r, "vlock_end");
391 if (errsv)
392 fprintf(stderr, "vlock: plugin '%s' failed: %s\n", p->name, strerror(errsv));
394 exit(EXIT_FAILURE);
399 /* Call the "vlock_end" hook of each plugin in reverse order. Never fails. */
400 void handle_vlock_end(const char *hook_name)
402 for (GList *plugin_item = g_list_last(plugins);
403 plugin_item != NULL;
404 plugin_item = g_list_previous(plugin_item)) {
405 VlockPlugin *p = plugin_item->data;
406 (void) vlock_plugin_call_hook(p, hook_name);
410 /* Call the "vlock_save" hook of each plugin. Never fails. If the hook of a
411 * plugin fails its "vlock_save_abort" hook is called and both hooks are never
412 * called again afterwards. */
413 void handle_vlock_save(const char *hook_name)
415 for (GList *plugin_item = plugins;
416 plugin_item != NULL;
417 plugin_item = g_list_next(plugin_item)) {
418 VlockPlugin *p = plugin_item->data;
420 // if (p->save_disabled)
421 // continue;
423 if (!vlock_plugin_call_hook(p, hook_name)) {
424 // p->save_disabled = true;
425 (void) vlock_plugin_call_hook(p, "vlock_save_abort");
430 /* Call the "vlock_save" hook of each plugin. Never fails. If the hook of a
431 * plugin fails both hooks "vlock_save" and "vlock_save_abort" are never called
432 * again afterwards. */
433 void handle_vlock_save_abort(const char *hook_name)
435 for (GList *plugin_item = g_list_last(plugins);
436 plugin_item != NULL;
437 plugin_item = g_list_previous(plugin_item)) {
438 VlockPlugin *p = plugin_item->data;
440 // if (p->save_disabled)
441 // continue;
443 if (!vlock_plugin_call_hook(p, hook_name))
444 ; // p->save_disabled = true;