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
31 /* the list of plugins */
32 static GList
*plugins
= NULL
;
45 const char *dependency_names
[nr_dependencies
] = {
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(GError
**error
);
77 static bool sort_plugins(GError
**error
);
79 bool load_plugin(const char *name
, GError
**error
)
81 return __load_plugin(name
, error
) != NULL
;
84 bool resolve_dependencies(GError
**error
)
86 return __resolve_depedencies(error
) && sort_plugins(error
);
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
);
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
,
120 (GCompareFunc
) plugin_name_compare
);
128 /* Load and return the named plugin. */
129 static VlockPlugin
*__load_plugin(const char *name
, GError
**error
)
131 VlockPlugin
*p
= get_plugin(name
);
138 /* Possible plugin types. */
139 GType plugin_types
[] = { TYPE_VLOCK_MODULE
, TYPE_VLOCK_SCRIPT
, 0 };
141 for (size_t i
= 0; plugin_types
[i
] != 0; i
++) {
142 if (err
== NULL
|| g_error_matches(err
,
144 VLOCK_PLUGIN_ERROR_NOT_FOUND
))
145 /* Continue if the was no previous error or the error was "not found". */
148 /* Bail out on real errors. */
151 /* Create the plugin. */
152 p
= g_object_new(plugin_types
[i
], "name", name
, NULL
);
154 /* Try to open the plugin. */
155 if (vlock_plugin_open(p
, &err
)) {
156 g_assert(err
== NULL
);
159 g_assert(err
!= NULL
);
168 g_propagate_error(error
, err
);
174 plugins
= g_list_append(plugins
, p
);
180 /* Resolve the dependencies of the plugins. */
181 static bool __resolve_depedencies(GError
**error
)
183 GList
*required_plugins
= NULL
;
185 /* Load plugins that are required. This automagically takes care of plugins
186 * that are required by the plugins loaded here because they are appended to
187 * the end of the list. */
188 for (GList
*plugin_item
= plugins
;
190 plugin_item
= g_list_next(plugin_item
)) {
191 VlockPlugin
*p
= plugin_item
->data
;
193 for (GList
*dependency_item
= p
->dependencies
[REQUIRES
];
194 dependency_item
!= NULL
;
195 dependency_item
= g_list_next(dependency_item
)) {
196 const char *d
= dependency_item
->data
;
197 VlockPlugin
*q
= __load_plugin(d
, NULL
);
203 VLOCK_PLUGIN_ERROR_DEPENDENCY
,
204 "'%s' requires '%s' which could not be loaded", p
->name
, d
);
205 g_list_free(required_plugins
);
209 required_plugins
= g_list_append(required_plugins
, p
);
213 /* Fail if a plugins that is needed is not loaded. */
214 for (GList
*plugin_item
= plugins
;
216 plugin_item
= g_list_next(plugin_item
)) {
217 VlockPlugin
*p
= plugin_item
->data
;
219 for (GList
*dependency_item
= p
->dependencies
[NEEDS
];
220 dependency_item
!= NULL
;
221 dependency_item
= g_list_next(dependency_item
)) {
222 const char *d
= dependency_item
->data
;
223 VlockPlugin
*q
= get_plugin(d
);
229 VLOCK_PLUGIN_ERROR_DEPENDENCY
,
230 "'%s' needs '%s' which is not loaded", p
->name
, d
);
231 g_list_free(required_plugins
);
236 required_plugins
= g_list_append(required_plugins
, q
);
240 /* Unload plugins whose prerequisites are not present, fail if those plugins
242 for (GList
*plugin_item
= plugins
;
243 plugin_item
!= NULL
; ) {
244 VlockPlugin
*p
= plugin_item
->data
;
245 bool dependencies_loaded
= true;
247 for (GList
*dependency_item
= p
->dependencies
[DEPENDS
];
248 dependency_item
!= NULL
;
249 dependency_item
= g_list_next(dependency_item
)) {
250 const char *d
= dependency_item
->data
;
251 VlockPlugin
*q
= get_plugin(d
);
254 dependencies_loaded
= false;
256 /* Abort if dependencies not met and plugin is required. */
257 if (g_list_find(required_plugins
, p
) != NULL
) {
261 VLOCK_PLUGIN_ERROR_DEPENDENCY
,
262 "'%s' is required by some other plugin but depends on '%s' which is not loaded",
265 g_list_free(required_plugins
);
274 GList
*next_plugin_item
= g_list_next(plugin_item
);
276 if (!dependencies_loaded
) {
278 plugins
= g_list_delete_link(plugins
, plugin_item
);
281 plugin_item
= next_plugin_item
;
284 g_list_free(required_plugins
);
286 /* Fail if conflicting plugins are loaded. */
287 for (GList
*plugin_item
= plugins
;
289 plugin_item
= g_list_next(plugin_item
)) {
290 VlockPlugin
*p
= plugin_item
->data
;
292 for (GList
*dependency_item
= p
->dependencies
[CONFLICTS
];
293 dependency_item
!= NULL
;
294 dependency_item
= g_list_next(dependency_item
)) {
295 const char *d
= dependency_item
->data
;
296 if (get_plugin(d
) != NULL
) {
300 VLOCK_PLUGIN_ERROR_DEPENDENCY
,
301 "'%s' and '%s' cannot be loaded at the same time",
313 static GList
*get_edges(void);
315 /* Sort the list of plugins according to their "preceeds" and "succeeds"
316 * dependencies. Fails if sorting is not possible because of circles. */
317 static bool sort_plugins(GError
**error
)
319 GList
*edges
= get_edges();
320 GList
*sorted_plugins
;
322 /* Topological sort. */
323 sorted_plugins
= tsort(plugins
, &edges
);
325 bool tsort_successful
= (edges
== NULL
);
327 if (tsort_successful
) {
328 /* Switch the global list of plugins for the sorted list. The global list
329 * is static and cannot be freed. */
331 g_assert(edges
== NULL
);
332 g_assert(g_list_length(sorted_plugins
) == g_list_length(plugins
));
334 GList
*tmp
= plugins
;
335 plugins
= sorted_plugins
;
341 GString
*error_message
= g_string_new("circular dependencies detected:");
343 while (edges
!= NULL
) {
344 struct edge
*e
= edges
->data
;
345 VlockPlugin
*p
= e
->predecessor
;
346 VlockPlugin
*s
= e
->successor
;
348 g_string_append_printf(error_message
,
349 "\n\t'%s'\tmust come before\t'%s'",
353 edges
= g_list_delete_link(edges
, edges
);
358 VLOCK_PLUGIN_ERROR_DEPENDENCY
,
362 g_string_free(error_message
, true);
367 /* Get the edges of the plugin graph specified by each plugin's "preceeds" and
368 * "succeeds" dependencies. */
369 static GList
*get_edges(void)
373 for (GList
*plugin_item
= plugins
;
375 plugin_item
= g_list_next(plugin_item
)) {
376 VlockPlugin
*p
= plugin_item
->data
;
377 /* p must come after these */
378 for (GList
*predecessor_item
= p
->dependencies
[SUCCEEDS
];
379 predecessor_item
!= NULL
;
380 predecessor_item
= g_list_next(predecessor_item
)) {
381 VlockPlugin
*q
= get_plugin(predecessor_item
->data
);
384 edges
= g_list_append(edges
, make_edge(q
, p
));
387 /* p must come before these */
388 for (GList
*successor_item
= p
->dependencies
[PRECEEDS
];
389 successor_item
!= NULL
;
390 successor_item
= g_list_next(successor_item
)) {
391 VlockPlugin
*q
= get_plugin(successor_item
->data
);
394 edges
= g_list_append(edges
, make_edge(p
, q
));
405 /* Call the "vlock_start" hook of each plugin. Fails if the hook of one of the
406 * plugins fails. In this case the "vlock_end" hooks of all plugins that were
407 * called before are called in reverse order. */
408 void handle_vlock_start(const char *hook_name
)
410 for (GList
*plugin_item
= plugins
;
412 plugin_item
= g_list_next(plugin_item
)) {
413 VlockPlugin
*p
= plugin_item
->data
;
415 if (!vlock_plugin_call_hook(p
, hook_name
)) {
418 for (GList
*reverse_item
= g_list_previous(plugin_item
);
419 reverse_item
!= NULL
;
420 reverse_item
= g_list_previous(reverse_item
)) {
421 VlockPlugin
*r
= reverse_item
->data
;
422 (void) vlock_plugin_call_hook(r
, "vlock_end");
426 fprintf(stderr
, "vlock: plugin '%s' failed: %s\n", p
->name
,
434 /* Call the "vlock_end" hook of each plugin in reverse order. Never fails. */
435 void handle_vlock_end(const char *hook_name
)
437 for (GList
*plugin_item
= g_list_last(plugins
);
439 plugin_item
= g_list_previous(plugin_item
)) {
440 VlockPlugin
*p
= plugin_item
->data
;
441 (void) vlock_plugin_call_hook(p
, hook_name
);
445 /* Call the "vlock_save" hook of each plugin. Never fails. If the hook of a
446 * plugin fails its "vlock_save_abort" hook is called and both hooks are never
447 * called again afterwards. */
448 void handle_vlock_save(const char *hook_name
)
450 for (GList
*plugin_item
= plugins
;
452 plugin_item
= g_list_next(plugin_item
)) {
453 VlockPlugin
*p
= plugin_item
->data
;
455 if (p
->save_disabled
)
458 if (!vlock_plugin_call_hook(p
, hook_name
)) {
459 p
->save_disabled
= true;
460 (void) vlock_plugin_call_hook(p
, "vlock_save_abort");
465 /* Call the "vlock_save" hook of each plugin. Never fails. If the hook of a
466 * plugin fails both hooks "vlock_save" and "vlock_save_abort" are never called
467 * again afterwards. */
468 void handle_vlock_save_abort(const char *hook_name
)
470 for (GList
*plugin_item
= g_list_last(plugins
);
472 plugin_item
= g_list_previous(plugin_item
)) {
473 VlockPlugin
*p
= plugin_item
->data
;
475 if (p
->save_disabled
)
478 if (!vlock_plugin_call_hook(p
, hook_name
))
479 p
->save_disabled
= true;