diff --git a/data/updates.js b/data/updates.js index 5444d095e..a9897e82f 100644 --- a/data/updates.js +++ b/data/updates.js @@ -75,6 +75,7 @@ export const updates = { otp_password_scheme_removed: '2.4.5', pbkdf2_hashing: '2.4.0', passwd_file_iteration: '2.4.0', + plugin_dependencies_config: '2.4.6', process_title_imap_process: '2.4.0', process_title_initializing: '2.4.0', process_title_mail_processes: '2.4.0', diff --git a/docs/developers/design/plugins.md b/docs/developers/design/plugins.md index 48ec38fec..d7c464023 100644 --- a/docs/developers/design/plugins.md +++ b/docs/developers/design/plugins.md @@ -42,37 +42,53 @@ the API is too old to support your plugin. For example: ## Dependencies -Some plugins depend on another one. In some systems (but not all) it's -possible to handle this by giving a nicer error message than "symbol xyz -not found". There are two steps for this: +[[changed,plugin_dependencies_config]] Some plugins depend on another one. +The dependencies are declared with `struct setting_plugin_info`, which the +config process writes to the binary config. When loading the +[[setting,mail_plugins]], Dovecot verifies that all the required plugins are +also loaded before loading any of the plugins. -First create `_dependencies` array listing plugin names that -the plugin depends on, like: +Plugins in Dovecot core declare these in their source files, which are +scanned by the config build: ```c -const char *imap_quota_plugin_dependencies[] = { "quota", NULL }; +const struct setting_plugin_info imap_quota_plugin_info = { + .plugin = "lib11_imap_quota_plugin", + .required_plugins = (const char *const []) { "quota", NULL }, +}; ``` -Then you'll also have to make the plugin .so binary link to the other -plugins: +The `plugin` field is the plugin's filename without the `.so` suffix. The +plugin name used in [[setting,mail_plugins]] is derived from it. The info is +ignored if the plugin file doesn't exist in the module directory. -``` -if PLUGIN_DEPS -lib11_imap_quota_plugin_la_LIBADD = \ - ../quota/lib10_quota_plugin.la -endif -``` +External plugins export them from their settings plugin (the plugin in the +`settings/` module directory) in a NULL-terminated +`_plugin_infos` array: -`PLUGIN_DEPS` is set only if plugin dependencies are actually supported. -Otherwise the build might fail or plugin loading might fail. +```c +const struct setting_plugin_info *foo_settings_plugin_infos[] = { + &foo_plugin_info, + NULL +}; +``` -Once all this is done, trying to load imap_quota plugin without quota -plugin gives a nice error message: +Trying to load imap_quota plugin without quota plugin then gives an error: ``` -Error: Can't load plugin imap_quota_plugin: Plugin quota must be loaded also +Fatal: mail_plugins: Plugin imap_quota requires also plugin quota to be loaded ``` +Don't link the plugin against the plugins it depends on. Linking against +loadable modules isn't portable. Instead, make sure the plugin's filename +prefix (e.g. `lib11_`) sorts after the plugins it depends on. Plugins are +loaded in that order, so the dependencies' symbols are already available +when the plugin is loaded. The config process logs a warning if a required +plugin would be loaded after the plugin requiring it. + +The old `_dependencies` array is still checked, but it can't +be checked before the plugin is loaded. + ## Hooks Different kinds of plugins can also hook into various things: