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(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
);
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
);
126 /* Load and return the named plugin. */
127 static VlockPlugin
*__load_plugin(const char *name
, GError
**error
)
129 VlockPlugin
*p
= get_plugin(name
);
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
,
142 VLOCK_PLUGIN_ERROR_NOT_FOUND
))
143 /* Continue if the was no previous error or the error was "not found". */
146 /* Bail out on real errors. */
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
);
163 g_propagate_error(error
, err
);
169 plugins
= g_list_append(plugins
, 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
;
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
);
195 fprintf(stderr
, "vlock-plugins: '%s' requires '%s' which could not be loaded\n", p
->name
, d
);
196 g_list_free(required_plugins
);
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
;
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
);
217 fprintf(stderr
, "vlock-plugins: '%s' needs '%s' which is not loaded\n", p
->name
, d
);
218 g_list_free(required_plugins
);
223 required_plugins
= g_list_append(required_plugins
, q
);
227 /* Unload plugins whose prerequisites are not present, fail if those plugins
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
);
241 dependencies_loaded
= false;
243 /* Abort if dependencies not met and plugin is required. */
244 if (g_list_find(required_plugins
, p
) != NULL
) {
246 "vlock-plugins: '%s' is required by some other plugin\n"
247 " but depends on '%s' which is not loaded",
249 g_list_free(required_plugins
);
258 GList
*next_plugin_item
= g_list_next(plugin_item
);
260 if (!dependencies_loaded
) {
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
;
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
);
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
);
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
;
328 fprintf(stderr
, "vlock-plugins: circular dependencies detected\n");
333 /* Get the edges of the plugin graph specified by each plugin's "preceeds" and
334 * "succeeds" dependencies. */
335 static GList
*get_edges(void)
339 for (GList
*plugin_item
= plugins
;
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
);
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
);
360 edges
= g_list_append(edges
, make_edge(p
, q
));
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
;
378 plugin_item
= g_list_next(plugin_item
)) {
379 VlockPlugin
*p
= plugin_item
->data
;
381 if (!vlock_plugin_call_hook(p
, hook_name
)) {
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");
392 fprintf(stderr
, "vlock: plugin '%s' failed: %s\n", p
->name
, strerror(errsv
));
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
);
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
;
417 plugin_item
= g_list_next(plugin_item
)) {
418 VlockPlugin
*p
= plugin_item
->data
;
420 // if (p->save_disabled)
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
);
437 plugin_item
= g_list_previous(plugin_item
)) {
438 VlockPlugin
*p
= plugin_item
->data
;
440 // if (p->save_disabled)
443 if (!vlock_plugin_call_hook(p
, hook_name
))
444 ; // p->save_disabled = true;