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
26 /* the list of plugins */
27 static struct list
*plugins
= &(struct list
){ NULL
, NULL
};
40 const char *dependency_names
[nr_dependencies
] = {
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
);
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)
115 /* Load and return the named plugin. */
116 static struct plugin
*__load_plugin(const char *name
)
118 struct plugin
*p
= get_plugin(name
);
123 /* Try to open a module first. */
124 p
= new_plugin(name
, module
);
132 /* Now try to open a script. */
133 p
= new_plugin(name
, script
);
139 if (!list_append(plugins
, p
)) {
140 GUARD_ERRNO(destroy_plugin(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
);
164 fprintf(stderr
, "vlock-plugins: '%s' requires '%s' which could not be loaded\n", p
->name
, d
);
165 list_free(required_plugins
);
170 if (!list_append(required_plugins
, q
)) {
171 GUARD_ERRNO(list_free(required_plugins
));
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
);
186 fprintf(stderr
, "vlock-plugins: '%s' needs '%s' which is not loaded\n", p
->name
, d
);
187 list_free(required_plugins
);
192 if (!list_append(required_plugins
, q
)) {
193 GUARD_ERRNO(list_free(required_plugins
));
199 /* Unload plugins whose prerequisites are not present, fail if those plugins
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
);
210 dependencies_loaded
= false;
212 /* Abort if dependencies not met and plugin is required. */
213 if (list_find(required_plugins
, p
) != NULL
) {
215 "vlock-plugins: '%s' is required by some other plugin\n"
216 " but depends on '%s' which is not loaded",
218 list_free(required_plugins
);
227 if (dependencies_loaded
) {
228 plugin_item
= plugin_item
->next
;
230 plugin_item
= list_delete_item(plugins
, plugin_item
);
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
);
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
;
266 /* Topological sort. */
267 sorted_plugins
= tsort(plugins
, edges
);
269 if (sorted_plugins
== NULL
&& errno
!= 0)
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
);
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
);
297 fprintf(stderr
, "vlock-plugins: circular dependencies detected\n");
302 static bool append_edge(struct list
*edges
, struct plugin
*p
, struct plugin
*s
)
304 struct edge
*e
= malloc(sizeof *e
);
312 if (!list_append(edges
, e
)) {
313 GUARD_ERRNO(free(e
));
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();
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
);
336 if (!append_edge(edges
, q
, p
))
340 /* p must come before these */
341 list_for_each(p
->dependencies
[PRECEEDS
], successor_item
) {
342 struct plugin
*q
= get_plugin(successor_item
->data
);
345 if (!append_edge(edges
, p
, q
))
353 GUARD_ERRNO(list_free(edges
));
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
)) {
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");
378 fprintf(stderr
, "vlock: plugin '%s' failed: %s\n", p
->name
, strerror(errsv
));
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
)
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
)
423 if (!call_hook(p
, hook_name
))
424 p
->save_disabled
= true;