/* * libopensync - A synchronization framework * Copyright (C) 2004-2005 Armin Bauer * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "opensync.h" #include "opensync_internals.h" /** * @defgroup OSyncPluginPrivateAPI OpenSync Plugin Internals * @ingroup OSyncPrivate * @brief The private part of the plugins API * */ /*@{*/ #ifndef DOXYGEN_SHOULD_SKIP_THIS OSyncObjTypeSink *osync_objtype_sink_from_template(OSyncGroup *group, OSyncObjTypeTemplate *template) { g_assert(group); g_assert(template); OSyncObjTypeSink *sink = g_malloc0(sizeof(OSyncObjTypeSink)); OSyncObjType *type = osync_conv_find_objtype(group->conv_env, template->name); if (!type) { osync_debug("OSYNC", 0, "Unable to find objtype named %s to create objtype sink", template->name); return NULL; } sink->objtype = type; sink->enabled = TRUE; sink->write = TRUE; sink->read = TRUE; return sink; } OSyncObjFormatSink *osync_objformat_sink_from_template(OSyncGroup *group, OSyncObjFormatTemplate *template) { OSyncObjFormatSink *sink = g_malloc0(sizeof(OSyncObjFormatSink)); OSyncObjFormat *format = osync_conv_find_objformat(group->conv_env, template->name); if (!format) return NULL; sink->format = format; sink->functions.commit_change = template->commit_change; sink->functions.access = template->access; sink->functions.read = template->read; sink->functions.committed_all = template->committed_all; sink->functions.batch_commit = template->batch_commit; sink->extension_name = g_strdup(template->extension_name); return sink; } OSyncObjTypeTemplate *osync_plugin_find_objtype_template(OSyncPlugin *plugin, const char *objtypestr) { GList *o; for (o = plugin->accepted_objtypes; o; o = o->next) { OSyncObjTypeTemplate *template = o->data; if (!strcmp(template->name, objtypestr)) return template; } return NULL; } OSyncObjFormatTemplate *osync_plugin_find_objformat_template(OSyncObjTypeTemplate *type_template, const char *objformatstr) { GList *f; for (f = type_template->formats; f; f = f->next) { OSyncObjFormatTemplate *template = f->data; if (!strcmp(template->name, objformatstr)) return template; } return NULL; } OSyncObjFormatSink *osync_objtype_find_format_sink(OSyncObjTypeSink *sink, const char *formatstr) { GList *f; for (f = sink->formatsinks; f; f = f->next) { OSyncObjFormatSink *sink = f->data; if (!strcmp(sink->format->name, formatstr)) return sink; } return NULL; } void _osync_format_set_commit(OSyncObjTypeTemplate *template, const char *formatstr, OSyncFormatCommitFn commit_change) { OSyncObjFormatTemplate *format_template = NULL; if (formatstr) { OSyncObjFormatTemplate *format_template = osync_plugin_find_objformat_template(template, formatstr); osync_assert_msg(format_template, "Unable to set commit function. Did you forget to add the objformat?"); format_template->commit_change = commit_change; } else { GList *f = NULL; for (f = template->formats; f; f = f->next) { format_template = f->data; format_template->commit_change = commit_change; } } } void _osync_format_set_access(OSyncObjTypeTemplate *template, const char *formatstr, OSyncFormatAccessFn access) { OSyncObjFormatTemplate *format_template = NULL; if (formatstr) { format_template = osync_plugin_find_objformat_template(template, formatstr); osync_assert_msg(format_template, "Unable to set commit function. Did you forget to add the objformat?"); format_template->access = access; } else { GList *f = NULL; for (f = template->formats; f; f = f->next) { format_template = f->data; format_template->access = access; } } } void _osync_format_set_batch(OSyncObjTypeTemplate *template, const char *formatstr, OSyncFormatBatchCommitFn batch) { OSyncObjFormatTemplate *format_template = NULL; if (formatstr) { format_template = osync_plugin_find_objformat_template(template, formatstr); osync_assert_msg(format_template, "Unable to set batch commit function. Did you forget to add the objformat?"); format_template->batch_commit = batch; } else { GList *f = NULL; for (f = template->formats; f; f = f->next) { format_template = f->data; format_template->batch_commit = batch; } } } #endif /*@}*/ /** * @defgroup OSyncPluginAPI OpenSync Plugin * @ingroup OSyncPublic * @brief Functions to register and manage plugins * */ /*@{*/ /*! @brief This will create a new plugin struct * * The plugin struct represents a sync plugin * * @param env For which environment to register this plugin. May be NULL * @returns A pointer to a newly allocated plugin. * */ OSyncPlugin *osync_plugin_new(OSyncEnv *env) { OSyncPlugin *plugin = g_malloc0(sizeof(OSyncPlugin)); g_assert(plugin); memset(&(plugin->info), 0, sizeof(plugin->info)); memset(&(plugin->info.functions), 0, sizeof(plugin->info.functions)); memset(&(plugin->info.timeouts), 0, sizeof(plugin->info.timeouts)); //Set the default timeouts; plugin->info.timeouts.connect_timeout = 60; plugin->info.timeouts.sync_done_timeout = 60; plugin->info.timeouts.disconnect_timeout = 60; plugin->info.timeouts.get_changeinfo_timeout = 60; plugin->info.timeouts.get_data_timeout = 60; plugin->info.timeouts.commit_timeout = 60; plugin->info.timeouts.read_change_timeout = 60; plugin->info.plugin = plugin; plugin->info.config_type = NEEDS_CONFIGURATION; if (env) { env->plugins = g_list_append(env->plugins, plugin); plugin->env = env; plugin->real_plugin = env->current_module; } return plugin; } /*! @brief Registers a new plugin * * This function creates a new OSyncPluginInfo object, that * can be used to register a new plugin dynamically. This * can be used by a module to register multiple plugins, * instead of using get_info() function, that allows * registering of only one plugin. */ OSyncPluginInfo *osync_plugin_new_info(OSyncEnv *env) { OSyncPlugin *plg = osync_plugin_new(env); osync_trace(TRACE_INTERNAL, "%s(%p): %p", __func__, env, plg); if (!plg) return NULL; return &plg->info; } /*! @brief Used to free a plugin * * Frees a plugin * * @param plugin Pointer to the plugin * */ void osync_plugin_free(OSyncPlugin *plugin) { osync_trace(TRACE_INTERNAL, "osync_plugin_free(%p)", plugin); g_assert(plugin); if (plugin->env) plugin->env->plugins = g_list_remove(plugin->env->plugins, plugin); //FIXME Free more stuff? g_free(plugin); } /*! @brief Used to look up a symbol on the plugin * * Looks up and returns a function * * @param plugin Pointer to the plugin * @param name The name of the function to look up * @param error Pointer to a error struct * @return Pointer to the function * */ void *osync_plugin_get_function(OSyncPlugin *plugin, const char *name, OSyncError **error) { void *function; if (!plugin->real_plugin) { osync_debug("OSPLG", 1, "You need to load a plugin before getting a function"); osync_error_set(error, OSYNC_ERROR_MISCONFIGURATION, "You need to load a plugin before getting a function"); return NULL; } if (!g_module_symbol (plugin->real_plugin, name, &function)) { osync_debug("OSPLG", 0, "Unable to locate symbol %s on plugin", name); osync_error_set(error, OSYNC_ERROR_PARAMETER, "Unable to locate symbol %s: %s", name, g_module_error()); return NULL; } return function; } /*! @brief dlopen()s a format plugin * * The get_info() function on the format plugin gets called * * @param env The environment in which to open the plugin * @param path Where to find this plugin * @param error Pointer to a error struct * @return Pointer to the plugin on success, NULL otherwise * */ osync_bool osync_module_load(OSyncEnv *env, const char *path, OSyncError **error) { osync_trace(TRACE_ENTRY, "%s(%p, %s, %p)", __func__, env, path, error); /* Check if this platform supports dynamic * loading of modules */ if (!g_module_supported()) { osync_error_set(error, OSYNC_ERROR_GENERIC, "This platform does not support loading of modules"); osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error)); return FALSE; } /* Try to open the module or fail if an error occurs */ GModule *module = g_module_open(path, 0); if (!module) { osync_error_set(error, OSYNC_ERROR_GENERIC, "Unable to open module %s: %s", path, g_module_error()); osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error)); return FALSE; } /* Load the get_info symbol */ void (* fct_info)(OSyncEnv *env) = NULL; void (** fct_infop)(OSyncEnv *env) = &fct_info; if (!g_module_symbol(module, "get_info", (void **)fct_infop)) { /* If there is no get_info symbol, the file must be a implementation library * for one of the other modules. So dont throw an error error since it has been * confusing users */ osync_trace(TRACE_EXIT, "%s: Not loading implementation library", __func__); return TRUE; } env->modules = g_list_append(env->modules, module); /* Call the get_info function */ env->current_module = module; fct_info(env); env->current_module = NULL; osync_trace(TRACE_EXIT, "%s: %p", __func__, module); return TRUE; } /*! @brief Closes a module * * @param env The environment from which to remove the module * @param module The module to unload * */ void osync_module_unload(OSyncEnv *env, GModule *module) { osync_trace(TRACE_INTERNAL, "%s(%p, %p)", __func__, env, module); //FIXME Close the module! This crashes the evo2 plugin at the moment, i have no idea why... //g_module_close(plugin->real_plugin); env->modules = g_list_remove(env->modules, module); } /*! @brief Loads the modules from a given directory * * Loads all modules from a directory into a osync environment * * @param env Pointer to a OSyncEnv environment * @param path The path where to look for plugins, NULL for the default sync module directory * @param must_exist If set to TRUE, this function will return an error if the directory does not exist * @param error Pointer to a error struct to return a error * @returns TRUE on success, FALSE otherwise * */ osync_bool osync_module_load_dir(OSyncEnv *env, const char *path, osync_bool must_exist, OSyncError **error) { osync_trace(TRACE_ENTRY, "%s(%p, %s, %p)", __func__, env, path, error); GDir *dir; GError *gerror = NULL; char *filename = NULL; if (!path) { osync_error_set(error, OSYNC_ERROR_GENERIC, "Not path given to load the modules from"); osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error)); return FALSE; } //Load all available shared libraries (plugins) if (!g_file_test(path, G_FILE_TEST_IS_DIR)) { if (must_exist) { osync_error_set(error, OSYNC_ERROR_GENERIC, "Path is not loadable"); osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error)); return FALSE; } else { osync_trace(TRACE_EXIT, "%s: Directory does not exist (non-fatal)", __func__); return TRUE; } } dir = g_dir_open(path, 0, &gerror); if (!dir) { osync_error_set(error, OSYNC_ERROR_IO_ERROR, "Unable to open directory %s: %s", path, gerror->message); g_error_free(gerror); osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error)); return FALSE; } const gchar *de = NULL; while ((de = g_dir_read_name(dir))) { filename = g_strdup_printf ("%s/%s", path, de); if (!g_file_test(filename, G_FILE_TEST_IS_REGULAR) || g_file_test(filename, G_FILE_TEST_IS_SYMLINK) || !g_pattern_match_simple("*.so", filename)) { g_free(filename); continue; } OSyncError *error = NULL; if (!osync_module_load(env, filename, &error)) { osync_debug("OSPLG", 0, "Unable to load plugin %s: %s", filename, error->message); osync_error_free(&error); } g_free(filename); } g_dir_close(dir); osync_trace(TRACE_EXIT, "%s", __func__); return TRUE; } /*! @brief Returns the name of the loaded plugin * * @param plugin Pointer to the plugin * @returns Name of the plugin * */ const char *osync_plugin_get_name(OSyncPlugin *plugin) { g_assert(plugin); return plugin->info.name; } /*! @brief Returns the long name of the loaded plugin * * @param plugin Pointer to the plugin * @returns Long name of the plugin * */ const char *osync_plugin_get_longname(OSyncPlugin *plugin) { g_assert(plugin); return plugin->info.longname; } /*! @brief Returns the description of the plugin * * @param plugin Pointer to the plugin * @returns Description of the plugin * */ const char *osync_plugin_get_description(OSyncPlugin *plugin) { g_assert(plugin); return plugin->info.description; } /*! @brief Returns the timeouts of the plugin * * @param plugin Pointer to the plugin * @returns Timeouts of the plugin * */ OSyncPluginTimeouts osync_plugin_get_timeouts(OSyncPlugin *plugin) { g_assert(plugin); return plugin->info.timeouts; } /*! @brief Returns the plugin_info data, set by the plugin * * @param plugin Pointer to the plugin * @returns The void pointer set on plugin->info.plugin_data */ void *osync_plugin_get_plugin_data(OSyncPlugin *plugin) { g_assert(plugin); return plugin->info.plugin_data; } /*! @brief Get full path for plugin module * * @param plugin Pointer to the plugin * @returns full path of plugin module */ const char *osync_plugin_get_path(OSyncPlugin *plugin) { g_assert(plugin); return g_module_name(plugin->real_plugin); } /*! @brief Sets the commit function of a format * * @param info Pointer to a plugin info struct to fill * @param objtypestr The name of the object type * @param formatstr The name of the format * @param commit_change The pointer to your commit_change function * */ void osync_plugin_set_commit_objformat(OSyncPluginInfo *info, const char *objtypestr, const char *formatstr, OSyncFormatCommitFn commit_change) { OSyncObjTypeTemplate *template = NULL; if (objtypestr) { OSyncObjTypeTemplate *template = osync_plugin_find_objtype_template(info->plugin, objtypestr); osync_assert_msg(template, "Unable to accept objformat. Did you forget to add the objtype?"); _osync_format_set_commit(template, formatstr, commit_change); } else { GList *o = NULL; for (o = info->plugin->accepted_objtypes; o; o = o->next) { template = o->data; _osync_format_set_commit(template, formatstr, commit_change); } } } /*! @brief Sets the access function of a format * * @param info Pointer to a plugin info struct to fill * @param objtypestr The name of the object type * @param formatstr The name of the format * @param access The pointer to your access function * */ void osync_plugin_set_access_objformat(OSyncPluginInfo *info, const char *objtypestr, const char *formatstr, OSyncFormatAccessFn access) { OSyncObjTypeTemplate *template = NULL; if (objtypestr) { //template = osync_plugin_find_objtype_template(info->plugin, objtypestr); //osync_assert_msg(template, "Unable to accept objformat. Did you forget to add the objtype?"); //_osync_format_set_access(template, formatstr, access); } else { GList *o = NULL; for (o = info->plugin->accepted_objtypes; o; o = o->next) { template = o->data; _osync_format_set_access(template, formatstr, access); } } } /*! @brief Sets the read function of a format * * @param info Pointer to a plugin info struct to fill * @param objtypestr The name of the object type * @param formatstr The name of the format * @param read The pointer to your read function * */ void osync_plugin_set_read_objformat(OSyncPluginInfo *info, const char *objtypestr, const char *formatstr, OSyncFormatReadFn read) { OSyncObjTypeTemplate *template = osync_plugin_find_objtype_template(info->plugin, objtypestr); osync_assert_msg(template, "Unable to accept objformat. Did you forget to add the objtype?"); OSyncObjFormatTemplate *format_template = osync_plugin_find_objformat_template(template, formatstr); osync_assert_msg(format_template, "Unable to set commit function. Did you forget to add the objformat?"); format_template->read = read; } /*! @brief Sets the batch_commit function of a format * * @param info Pointer to a plugin info struct to fill * @param objtypestr The name of the object type * @param formatstr The name of the format * @param batch The pointer to your batch_commit function * */ void osync_plugin_set_batch_commit_objformat(OSyncPluginInfo *info, const char *objtypestr, const char *formatstr, OSyncFormatBatchCommitFn batch) { OSyncObjTypeTemplate *template = NULL; if (objtypestr) { template = osync_plugin_find_objtype_template(info->plugin, objtypestr); osync_assert_msg(template, "Unable to accept objformat. Did you forget to add the objtype?"); _osync_format_set_batch(template, formatstr, batch); } else { GList *o = NULL; for (o = info->plugin->accepted_objtypes; o; o = o->next) { template = o->data; _osync_format_set_batch(template, formatstr, batch); } } } /*! @brief Sets the committed_all function of a format * * @param info Pointer to a plugin info struct to fill * @param objtypestr The name of the object type * @param formatstr The name of the format * @param committed_all The pointer to your committed_all function * */ void osync_plugin_set_committed_all_objformat(OSyncPluginInfo *info, const char *objtypestr, const char *formatstr, OSyncFormatCommittedAllFn committed_all) { OSyncObjTypeTemplate *template = osync_plugin_find_objtype_template(info->plugin, objtypestr); osync_assert_msg(template, "Unable to accept objformat. Did you forget to add the objtype?"); OSyncObjFormatTemplate *format_template = osync_plugin_find_objformat_template(template, formatstr); osync_assert_msg(format_template, "Unable to set committed_all function. Did you forget to add the objformat?"); format_template->committed_all = committed_all; } /*! @brief Tells opensync that the plugin can accepts this object * * Tells opensync that the plugin can accepts this object. Used by the plugin * in the get_info() function * * @param info The plugin info on which to operate * @param objtypestr The name of the object which to accept * */ void osync_plugin_accept_objtype(OSyncPluginInfo *info, const char *objtypestr) { OSyncObjTypeTemplate *template = g_malloc0(sizeof(OSyncObjTypeTemplate)); template->name = g_strdup(objtypestr); info->plugin->accepted_objtypes = g_list_append(info->plugin->accepted_objtypes, template); } /*! @brief Tells opensync that the plugin can accepts this format for the given object * * Tells opensync that the plugin can accepts this format. Used by the plugin * in the get_info() function * * @param info The plugin info on which to operate * @param objtypestr The name of the objecttype * @param formatstr The name of the format to accept * @param extension The name of the extension that the plugin wants. NULL if none * */ void osync_plugin_accept_objformat(OSyncPluginInfo *info, const char *objtypestr, const char *formatstr, const char *extension) { OSyncObjTypeTemplate *template = osync_plugin_find_objtype_template(info->plugin, objtypestr); osync_assert_msg(template, "Unable to accept objformat. Did you forget to add the objtype?"); OSyncObjFormatTemplate *format_template = g_malloc0(sizeof(OSyncObjFormatTemplate)); format_template->name = g_strdup(formatstr); if (extension) format_template->extension_name = g_strdup(extension); template->formats = g_list_append(template->formats, format_template); } /*@}*/