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
25 /* the list of plugins */
26 static struct list
*plugins
= &(struct list
){ NULL
, NULL
};
39 const char *dependency_names
[nr_dependencies
] = {
52 static void handle_vlock_start(const char * hook_name
);
53 static void handle_vlock_end(const char * hook_name
);
54 static void handle_vlock_save(const char * hook_name
);
55 static void handle_vlock_save_abort(const char * hook_name
);
57 const struct hook hooks
[nr_hooks
] = {
58 { "vlock_start", handle_vlock_start
},
59 { "vlock_end", handle_vlock_end
},
60 { "vlock_save", handle_vlock_save
},
61 { "vlock_save_abort", handle_vlock_save_abort
},
64 /**********************/
65 /* exported functions */
66 /**********************/
68 /* helper declarations */
69 static struct plugin
*__load_plugin(const char *name
);
70 static void __resolve_depedencies(void);
71 static void sort_plugins(void);
73 bool load_plugin(const char *name
)
75 return __load_plugin(name
) == NULL
;
78 void resolve_dependencies(void)
80 __resolve_depedencies();
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. Aborts on error. */
118 static struct plugin
*__load_plugin(const char *name
)
122 struct plugin
*p
= get_plugin(name
);
127 p
= open_module(name
, &e1
);
130 p
= open_script(name
, &e2
);
133 if (e1
== NULL
&& e2
== NULL
)
134 fatal_error("vlock-plugins: error loading plugin '%s'", name
);
137 fprintf(stderr
, "vlock-plugins: error loading module '%s': %s\n", name
, e1
);
141 fprintf(stderr
, "vlock-plugins: error loading script '%s': %s\n", name
, e2
);
148 list_append(plugins
, p
);
152 /* Resolve the dependencies of the plugins. */
153 static void __resolve_depedencies(void)
155 struct list
*required_plugins
= list_new();
157 /* Load plugins that are required. This automagically takes care of plugins
158 * that are required by the plugins loaded here because they are appended to
159 * the end of the list. */
160 list_for_each(plugins
, plugin_item
) {
161 struct plugin
*p
= plugin_item
->data
;
163 list_for_each(p
->dependencies
[REQUIRES
], dependency_item
)
164 list_append(required_plugins
, __load_plugin(dependency_item
->data
));
167 /* Fail if a plugins that is needed is not loaded. */
168 list_for_each(plugins
, plugin_item
) {
169 struct plugin
*p
= plugin_item
->data
;
171 list_for_each(p
->dependencies
[NEEDS
], dependency_item
) {
172 const char *d
= dependency_item
->data
;
173 struct plugin
*q
= get_plugin(d
);
176 fatal_error("vlock-plugins: '%s' depends on '%s' which is not loaded", p
->name
, d
);
178 list_append(required_plugins
, q
);
182 /* Unload plugins whose prerequisites are not present, fail if those plugins
184 list_for_each_manual(plugins
, plugin_item
) {
185 struct plugin
*p
= plugin_item
->data
;
186 bool dependencies_loaded
= true;
188 list_for_each(p
->dependencies
[DEPENDS
], dependency_item
) {
189 const char *d
= dependency_item
->data
;
190 struct plugin
*q
= get_plugin(d
);
193 dependencies_loaded
= false;
195 /* Abort if dependencies not met and plugin is required. */
196 if (list_find(required_plugins
, p
) != NULL
)
198 "vlock-plugins: '%s' is required by some other plugin\n"
199 " but depends on '%s' which is not loaded",
206 if (!dependencies_loaded
) {
207 plugin_item
= list_delete_item(plugins
, plugin_item
);
210 plugin_item
= plugin_item
->next
;
214 list_free(required_plugins
);
216 /* Fail if conflicting plugins are loaded. */
217 list_for_each(plugins
, plugin_item
) {
218 struct plugin
*p
= plugin_item
->data
;
220 list_for_each(p
->dependencies
[CONFLICTS
], dependency_item
) {
221 const char *d
= dependency_item
->data
;
222 if (get_plugin(d
) == NULL
)
223 fatal_error("vlock-plugins: '%s' and '%s' cannot be loaded at the same time", p
->name
, d
);
228 static struct list
*get_edges(void);
230 /* Sort the list of plugins according to their "preceeds" and "succeeds"
231 * dependencies. Fails if sorting is not possible because of circles. */
232 static void sort_plugins(void)
234 struct list
*edges
= get_edges();
235 bool circles_present
;
237 /* Topological sort. */
238 tsort(plugins
, edges
);
240 circles_present
= !list_is_empty(edges
);
242 list_delete_for_each(edges
, edge_item
) {
243 struct edge
*e
= edge_item
->data
;
244 struct plugin
*p
= e
->predecessor
;
245 struct plugin
*s
= e
->successor
;
247 fprintf(stderr
, "\t%s\tmust come before\t%s\n", p
->name
, s
->name
);
252 fatal_error("vlock-plugins: circular dependencies detected");
255 static struct edge
*make_edge(struct plugin
*p
, struct plugin
*s
)
257 struct edge
*e
= ensure_malloc(sizeof *e
);
263 /* Get the edges of the plugin graph specified by each plugin's "preceeds" and
264 * "succeeds" dependencies. */
265 static struct list
*get_edges(void)
267 struct list
*edges
= list_new();
269 list_for_each(plugins
, plugin_item
) {
270 struct plugin
*p
= plugin_item
->data
;
271 /* p must come after these */
272 list_for_each(p
->dependencies
[SUCCEEDS
], predecessor_item
) {
273 struct plugin
*q
= get_plugin(predecessor_item
->data
);
276 list_append(edges
, make_edge(q
, p
));
279 /* p must come before these */
280 list_for_each(p
->dependencies
[PRECEEDS
], successor_item
) {
281 struct plugin
*q
= get_plugin(successor_item
->data
);
284 list_append(edges
, make_edge(p
, q
));
295 /* Call the "vlock_start" hook of each plugin. Fails if the hook of one of the
296 * plugins fails. In this case the "vlock_end" hooks of all plugins that were
297 * called before are called in reverse order. */
298 void handle_vlock_start(const char *hook_name
)
300 list_for_each(plugins
, plugin_item
) {
301 struct plugin
*p
= plugin_item
->data
;
303 if (!p
->call_hook(p
, hook_name
)) {
304 list_for_each_reverse_from(plugins
, reverse_item
, plugin_item
) {
305 struct plugin
*r
= reverse_item
->data
;
306 r
->call_hook(r
, "vlock_end");
309 fatal_error("vlock-plugins: error in '%s' hook of plugin '%s'", hook_name
, p
->name
);
314 /* Call the "vlock_end" hook of each plugin in reverse order. Never fails. */
315 void handle_vlock_end(const char *hook_name
)
317 list_for_each_reverse(plugins
, plugin_item
) {
318 struct plugin
*p
= plugin_item
->data
;
319 (void) p
->call_hook(p
, hook_name
);
323 /* Call the "vlock_save" hook of each plugin. Never fails. If the hook of a
324 * plugin fails its "vlock_save_abort" hook is called and both hooks are never
325 * called again afterwards. */
326 void handle_vlock_save(const char *hook_name
)
328 list_for_each(plugins
, plugin_item
) {
329 struct plugin
*p
= plugin_item
->data
;
331 if (p
->save_disabled
)
334 if (!p
->call_hook(p
, hook_name
)) {
335 p
->save_disabled
= true;
336 p
->call_hook(p
, "vlock_save_abort");
341 /* Call the "vlock_save" hook of each plugin. Never fails. If the hook of a
342 * plugin fails both hooks "vlock_save" and "vlock_save_abort" are never called
343 * again afterwards. */
344 void handle_vlock_save_abort(const char *hook_name
)
346 list_for_each_reverse(plugins
, plugin_item
) {
347 struct plugin
*p
= plugin_item
->data
;
349 if (p
->save_disabled
)
352 if (!p
->call_hook(p
, hook_name
))
353 p
->save_disabled
= true;