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
);
99 fatal_error("vlock-plugins: invalid hook name '%s'", hook_name
);
102 /********************/
103 /* helper functions */
104 /********************/
106 static struct plugin
*get_plugin(const char *name
)
108 list_for_each(plugins
, plugin_item
) {
109 struct plugin
*p
= plugin_item
->data
;
110 if (strcmp(name
, p
->name
) == 0)
117 /* Load and return the named plugin. */
118 static struct plugin
*__load_plugin(const char *name
)
120 struct plugin
*p
= get_plugin(name
);
125 /* Try to open a module first. */
126 p
= new_plugin(name
, module
);
134 /* Now try to open a script. */
135 p
= new_plugin(name
, script
);
141 if (!list_append(plugins
, p
)) {
142 GUARD_ERRNO(destroy_plugin(p
));
149 /* Resolve the dependencies of the plugins. */
150 static bool __resolve_depedencies(void)
152 struct list
*required_plugins
= list_new();
154 /* Load plugins that are required. This automagically takes care of plugins
155 * that are required by the plugins loaded here because they are appended to
156 * the end of the list. */
157 list_for_each(plugins
, plugin_item
) {
158 struct plugin
*p
= plugin_item
->data
;
160 list_for_each(p
->dependencies
[REQUIRES
], dependency_item
) {
161 const char *d
= dependency_item
->data
;
162 struct plugin
*q
= __load_plugin(d
);
166 fprintf(stderr
, "vlock-plugin: '%s' requires '%s' which could not be loaded\n", p
->name
, d
);
167 list_free(required_plugins
);
172 if (!list_append(required_plugins
, q
)) {
173 GUARD_ERRNO(list_free(required_plugins
));
179 /* Fail if a plugins that is needed is not loaded. */
180 list_for_each(plugins
, plugin_item
) {
181 struct plugin
*p
= plugin_item
->data
;
183 list_for_each(p
->dependencies
[NEEDS
], dependency_item
) {
184 const char *d
= dependency_item
->data
;
185 struct plugin
*q
= get_plugin(d
);
188 fprintf(stderr
, "vlock-plugins: '%s' needs '%s' which is not loaded\n", p
->name
, d
);
189 list_free(required_plugins
);
194 if (!list_append(required_plugins
, q
)) {
195 GUARD_ERRNO(list_free(required_plugins
));
201 /* Unload plugins whose prerequisites are not present, fail if those plugins
203 list_for_each_manual(plugins
, plugin_item
) {
204 struct plugin
*p
= plugin_item
->data
;
205 bool dependencies_loaded
= true;
207 list_for_each(p
->dependencies
[DEPENDS
], dependency_item
) {
208 const char *d
= dependency_item
->data
;
209 struct plugin
*q
= get_plugin(d
);
212 dependencies_loaded
= false;
214 /* Abort if dependencies not met and plugin is required. */
215 if (list_find(required_plugins
, p
) != NULL
) {
217 "vlock-plugins: '%s' is required by some other plugin\n"
218 " but depends on '%s' which is not loaded",
220 list_free(required_plugins
);
229 if (dependencies_loaded
) {
230 plugin_item
= plugin_item
->next
;
232 plugin_item
= list_delete_item(plugins
, plugin_item
);
237 list_free(required_plugins
);
239 /* Fail if conflicting plugins are loaded. */
240 list_for_each(plugins
, plugin_item
) {
241 struct plugin
*p
= plugin_item
->data
;
243 list_for_each(p
->dependencies
[CONFLICTS
], dependency_item
) {
244 const char *d
= dependency_item
->data
;
245 if (get_plugin(d
) == NULL
) {
246 fprintf(stderr
, "vlock-plugins: '%s' and '%s' cannot be loaded at the same time", p
->name
, d
);
256 static struct list
*get_edges(void);
258 /* Sort the list of plugins according to their "preceeds" and "succeeds"
259 * dependencies. Fails if sorting is not possible because of circles. */
260 static bool sort_plugins(void)
262 struct list
*edges
= get_edges();
263 struct list
*sorted_plugins
;
268 /* Topological sort. */
269 sorted_plugins
= tsort(plugins
, edges
);
271 if (sorted_plugins
== NULL
&& errno
!= 0)
274 list_delete_for_each(edges
, edge_item
) {
275 struct edge
*e
= edge_item
->data
;
276 struct plugin
*p
= e
->predecessor
;
277 struct plugin
*s
= e
->successor
;
279 fprintf(stderr
, "\t%s\tmust come before\t%s\n", p
->name
, s
->name
);
283 if (sorted_plugins
!= NULL
) {
284 struct list
*tmp
= plugins
;
285 plugins
= sorted_plugins
;
289 fprintf(stderr
, "vlock-plugins: circular dependencies detected\n");
294 static bool append_edge(struct list
*edges
, struct plugin
*p
, struct plugin
*s
)
296 struct edge
*e
= malloc(sizeof *e
);
304 if (!list_append(edges
, e
)) {
305 GUARD_ERRNO(free(e
));
312 /* Get the edges of the plugin graph specified by each plugin's "preceeds" and
313 * "succeeds" dependencies. */
314 static struct list
*get_edges(void)
316 struct list
*edges
= list_new();
321 list_for_each(plugins
, plugin_item
) {
322 struct plugin
*p
= plugin_item
->data
;
323 /* p must come after these */
324 list_for_each(p
->dependencies
[SUCCEEDS
], predecessor_item
) {
325 struct plugin
*q
= get_plugin(predecessor_item
->data
);
328 if (!append_edge(edges
, q
, p
))
332 /* p must come before these */
333 list_for_each(p
->dependencies
[PRECEEDS
], successor_item
) {
334 struct plugin
*q
= get_plugin(successor_item
->data
);
337 if (!append_edge(edges
, p
, q
))
345 GUARD_ERRNO(list_free(edges
));
353 /* Call the "vlock_start" hook of each plugin. Fails if the hook of one of the
354 * plugins fails. In this case the "vlock_end" hooks of all plugins that were
355 * called before are called in reverse order. */
356 void handle_vlock_start(const char *hook_name
)
358 list_for_each(plugins
, plugin_item
) {
359 struct plugin
*p
= plugin_item
->data
;
361 if (!call_hook(p
, hook_name
)) {
362 list_for_each_reverse_from(plugins
, reverse_item
, plugin_item
) {
363 struct plugin
*r
= reverse_item
->data
;
364 (void) call_hook(r
, "vlock_end");
367 fatal_error("vlock-plugins: hook '%s' of plugin '%s' failed: %s", hook_name
, p
->name
, STRERROR
);
372 /* Call the "vlock_end" hook of each plugin in reverse order. Never fails. */
373 void handle_vlock_end(const char *hook_name
)
375 list_for_each_reverse(plugins
, plugin_item
) {
376 struct plugin
*p
= plugin_item
->data
;
377 (void) call_hook(p
, hook_name
);
381 /* Call the "vlock_save" hook of each plugin. Never fails. If the hook of a
382 * plugin fails its "vlock_save_abort" hook is called and both hooks are never
383 * called again afterwards. */
384 void handle_vlock_save(const char *hook_name
)
386 list_for_each(plugins
, plugin_item
) {
387 struct plugin
*p
= plugin_item
->data
;
389 if (p
->save_disabled
)
392 if (!call_hook(p
, hook_name
)) {
393 p
->save_disabled
= true;
394 call_hook(p
, "vlock_save_abort");
399 /* Call the "vlock_save" hook of each plugin. Never fails. If the hook of a
400 * plugin fails both hooks "vlock_save" and "vlock_save_abort" are never called
401 * again afterwards. */
402 void handle_vlock_save_abort(const char *hook_name
)
404 list_for_each_reverse(plugins
, plugin_item
) {
405 struct plugin
*p
= plugin_item
->data
;
407 if (p
->save_disabled
)
410 if (!call_hook(p
, hook_name
))
411 p
->save_disabled
= true;