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 void __resolve_depedencies(void);
72 static void sort_plugins(void);
74 bool load_plugin(const char *name
)
76 return __load_plugin(name
) == NULL
;
79 void resolve_dependencies(void)
81 __resolve_depedencies();
85 void unload_plugins(void)
87 list_delete_for_each(plugins
, plugin_item
)
88 destroy_plugin(plugin_item
->data
);
91 void plugin_hook(const char *hook_name
)
93 for (size_t i
= 0; i
< nr_hooks
; i
++)
94 /* Get the handler and call it. */
95 if (strcmp(hook_name
, hooks
[i
].name
) == 0) {
96 hooks
[i
].handler(hook_name
);
100 fatal_error("vlock-plugins: invalid hook name '%s'", hook_name
);
103 /********************/
104 /* helper functions */
105 /********************/
107 static struct plugin
*get_plugin(const char *name
)
109 list_for_each(plugins
, plugin_item
) {
110 struct plugin
*p
= plugin_item
->data
;
111 if (strcmp(name
, p
->name
) == 0)
118 /* Load and return the named plugin. */
119 static struct plugin
*__load_plugin(const char *name
)
121 struct plugin
*p
= get_plugin(name
);
126 /* Try to open a module first. */
127 p
= new_plugin(name
, module
);
135 /* Now try to open a script. */
136 p
= new_plugin(name
, script
);
142 list_append(plugins
, p
);
146 /* Resolve the dependencies of the plugins. */
147 static void __resolve_depedencies(void)
149 struct list
*required_plugins
= list_new();
151 /* Load plugins that are required. This automagically takes care of plugins
152 * that are required by the plugins loaded here because they are appended to
153 * the end of the list. */
154 list_for_each(plugins
, plugin_item
) {
155 struct plugin
*p
= plugin_item
->data
;
157 list_for_each(p
->dependencies
[REQUIRES
], dependency_item
)
158 list_append(required_plugins
, __load_plugin(dependency_item
->data
));
161 /* Fail if a plugins that is needed is not loaded. */
162 list_for_each(plugins
, plugin_item
) {
163 struct plugin
*p
= plugin_item
->data
;
165 list_for_each(p
->dependencies
[NEEDS
], dependency_item
) {
166 const char *d
= dependency_item
->data
;
167 struct plugin
*q
= get_plugin(d
);
170 fatal_error("vlock-plugins: '%s' depends on '%s' which is not loaded", p
->name
, d
);
172 list_append(required_plugins
, q
);
176 /* Unload plugins whose prerequisites are not present, fail if those plugins
178 list_for_each_manual(plugins
, plugin_item
) {
179 struct plugin
*p
= plugin_item
->data
;
180 bool dependencies_loaded
= true;
182 list_for_each(p
->dependencies
[DEPENDS
], dependency_item
) {
183 const char *d
= dependency_item
->data
;
184 struct plugin
*q
= get_plugin(d
);
187 dependencies_loaded
= false;
189 /* Abort if dependencies not met and plugin is required. */
190 if (list_find(required_plugins
, p
) != NULL
)
192 "vlock-plugins: '%s' is required by some other plugin\n"
193 " but depends on '%s' which is not loaded",
200 if (!dependencies_loaded
) {
201 plugin_item
= list_delete_item(plugins
, plugin_item
);
204 plugin_item
= plugin_item
->next
;
208 list_free(required_plugins
);
210 /* Fail if conflicting plugins are loaded. */
211 list_for_each(plugins
, plugin_item
) {
212 struct plugin
*p
= plugin_item
->data
;
214 list_for_each(p
->dependencies
[CONFLICTS
], dependency_item
) {
215 const char *d
= dependency_item
->data
;
216 if (get_plugin(d
) == NULL
)
217 fatal_error("vlock-plugins: '%s' and '%s' cannot be loaded at the same time", p
->name
, d
);
222 static struct list
*get_edges(void);
224 /* Sort the list of plugins according to their "preceeds" and "succeeds"
225 * dependencies. Fails if sorting is not possible because of circles. */
226 static void sort_plugins(void)
228 struct list
*edges
= get_edges();
229 bool circles_present
;
231 /* Topological sort. */
232 tsort(plugins
, edges
);
234 circles_present
= !list_is_empty(edges
);
236 list_delete_for_each(edges
, edge_item
) {
237 struct edge
*e
= edge_item
->data
;
238 struct plugin
*p
= e
->predecessor
;
239 struct plugin
*s
= e
->successor
;
241 fprintf(stderr
, "\t%s\tmust come before\t%s\n", p
->name
, s
->name
);
246 fatal_error("vlock-plugins: circular dependencies detected");
249 static struct edge
*make_edge(struct plugin
*p
, struct plugin
*s
)
251 struct edge
*e
= ensure_malloc(sizeof *e
);
257 /* Get the edges of the plugin graph specified by each plugin's "preceeds" and
258 * "succeeds" dependencies. */
259 static struct list
*get_edges(void)
261 struct list
*edges
= list_new();
263 list_for_each(plugins
, plugin_item
) {
264 struct plugin
*p
= plugin_item
->data
;
265 /* p must come after these */
266 list_for_each(p
->dependencies
[SUCCEEDS
], predecessor_item
) {
267 struct plugin
*q
= get_plugin(predecessor_item
->data
);
270 list_append(edges
, make_edge(q
, p
));
273 /* p must come before these */
274 list_for_each(p
->dependencies
[PRECEEDS
], successor_item
) {
275 struct plugin
*q
= get_plugin(successor_item
->data
);
278 list_append(edges
, make_edge(p
, q
));
289 /* Call the "vlock_start" hook of each plugin. Fails if the hook of one of the
290 * plugins fails. In this case the "vlock_end" hooks of all plugins that were
291 * called before are called in reverse order. */
292 void handle_vlock_start(const char *hook_name
)
294 list_for_each(plugins
, plugin_item
) {
295 struct plugin
*p
= plugin_item
->data
;
297 if (!call_hook(p
, hook_name
)) {
298 list_for_each_reverse_from(plugins
, reverse_item
, plugin_item
) {
299 struct plugin
*r
= reverse_item
->data
;
300 (void) call_hook(r
, "vlock_end");
303 fatal_error("vlock-plugins: error in '%s' hook of plugin '%s'", hook_name
, p
->name
);
308 /* Call the "vlock_end" hook of each plugin in reverse order. Never fails. */
309 void handle_vlock_end(const char *hook_name
)
311 list_for_each_reverse(plugins
, plugin_item
) {
312 struct plugin
*p
= plugin_item
->data
;
313 (void) call_hook(p
, hook_name
);
317 /* Call the "vlock_save" hook of each plugin. Never fails. If the hook of a
318 * plugin fails its "vlock_save_abort" hook is called and both hooks are never
319 * called again afterwards. */
320 void handle_vlock_save(const char *hook_name
)
322 list_for_each(plugins
, plugin_item
) {
323 struct plugin
*p
= plugin_item
->data
;
325 if (p
->save_disabled
)
328 if (!call_hook(p
, hook_name
)) {
329 p
->save_disabled
= true;
330 call_hook(p
, "vlock_save_abort");
335 /* Call the "vlock_save" hook of each plugin. Never fails. If the hook of a
336 * plugin fails both hooks "vlock_save" and "vlock_save_abort" are never called
337 * again afterwards. */
338 void handle_vlock_save_abort(const char *hook_name
)
340 list_for_each_reverse(plugins
, plugin_item
) {
341 struct plugin
*p
= plugin_item
->data
;
343 if (p
->save_disabled
)
346 if (!call_hook(p
, hook_name
))
347 p
->save_disabled
= true;