# HG changeset patch # User Goffi # Date 1702501222 -3600 # Node ID 730f542e4ad0121f2c74e7028e9284992fdd2838 # Parent 1d24ff58379480987b7cdf3eb8cf165504fce55f core: add new `init_script_path` option: `init_script_path` option can be used in `[DEFAULTS]` to run a script at the end of backend initialisation. A new `init_pre_script` method is used to wait for backend to reach this stage (designed to be used mostly by CLI frontend), then the usual `ready_get` method is finished once the script is finished. diff -r 1d24ff583794 -r 730f542e4ad0 libervia/backend/bridge/bridge_constructor/bridge_template.ini --- a/libervia/backend/bridge/bridge_constructor/bridge_template.ini Tue Dec 12 12:17:15 2023 +0100 +++ b/libervia/backend/bridge/bridge_constructor/bridge_template.ini Wed Dec 13 22:00:22 2023 +0100 @@ -237,6 +237,14 @@ sig_out= doc=Return when backend is initialised +[init_pre_script] +async= +type=method +category=core +sig_in= +sig_out= +doc=Return when backend is nearly initialised, just before init script is run, if any. + [version_get] type=method category=core diff -r 1d24ff583794 -r 730f542e4ad0 libervia/backend/bridge/dbus_bridge.py --- a/libervia/backend/bridge/dbus_bridge.py Tue Dec 12 12:17:15 2023 +0100 +++ b/libervia/backend/bridge/dbus_bridge.py Wed Dec 13 22:00:22 2023 +0100 @@ -114,6 +114,7 @@ Method('image_convert', arguments='ssss', returns='s'), Method('image_generate_preview', arguments='ss', returns='s'), Method('image_resize', arguments='sii', returns='s'), + Method('init_pre_script', arguments='', returns=''), Method('is_connected', arguments='s', returns='b'), Method('main_resource_get', arguments='ss', returns='s'), Method('menu_help_get', arguments='ss', returns='s'), @@ -281,6 +282,9 @@ def dbus_image_resize(self, image_path, width, height): return self._callback("image_resize", image_path, width, height) + def dbus_init_pre_script(self, ): + return self._callback("init_pre_script", ) + def dbus_is_connected(self, profile_key="@DEFAULT@"): return self._callback("is_connected", profile_key) diff -r 1d24ff583794 -r 730f542e4ad0 libervia/backend/core/main.py --- a/libervia/backend/core/main.py Tue Dec 12 12:17:15 2023 +0100 +++ b/libervia/backend/core/main.py Wed Dec 13 22:00:22 2023 +0100 @@ -22,7 +22,7 @@ import hashlib import copy from pathlib import Path -from typing import Optional, List, Tuple, Dict +from typing import Optional, List, Tuple, Dict, cast from wokkel.data_form import Option from libervia import backend @@ -49,6 +49,7 @@ from libervia.backend.tools.common import dynamic_import from libervia.backend.tools.common import regex from libervia.backend.tools.common import data_format +from libervia.backend.tools.common import async_process from libervia.backend.stdui import ui_contact_list, ui_profile_manager import libervia.backend.plugins @@ -66,6 +67,10 @@ self._menus = {} self._menus_paths = {} # path to id. key: (menu_type, lower case tuple of path), # value: menu id + + # like initialised, but launched before init script is done, mainly useful for CLI + # frontend, so it can be used in init script, while other frontends are waiting. + self.init_pre_script = defer.Deferred() self.initialised = defer.Deferred() self.profiles = {} self.plugins = {} @@ -143,6 +148,7 @@ reactor.callLater(0, self.stop) return + self.bridge.register_method("init_pre_script", lambda: self.init_pre_script) self.bridge.register_method("ready_get", lambda: self.initialised) self.bridge.register_method("version_get", lambda: self.full_version) self.bridge.register_method("features_get", self.features_get) @@ -239,6 +245,30 @@ sys.exit(1) self._add_base_menus() + self.init_pre_script.callback(None) + + init_script = self.memory.config_get(None, "init_script_path") + if init_script is not None: + init_script = cast(str, init_script) + script_path = Path(init_script) + if not script_path.exists(): + log.error(f"Init script doesn't exist: {init_script}") + sys.exit(C.EXIT_BAD_ARG) + elif not script_path.is_file(): + log.error(f"Init script is not a file: {init_script}") + sys.exit(C.EXIT_BAD_ARG) + else: + log.info(f"Running init script {init_script!r}.") + try: + await async_process.run( + str(init_script), + verbose=True + ) + except RuntimeError as e: + log.error(f"Init script failed: {e}") + self.stopService() + sys.exit(C.EXIT_ERROR) + self.initialised.callback(None) log.info(_("Backend is ready")) diff -r 1d24ff583794 -r 730f542e4ad0 libervia/backend/memory/memory.py --- a/libervia/backend/memory/memory.py Tue Dec 12 12:17:15 2023 +0100 +++ b/libervia/backend/memory/memory.py Wed Dec 13 22:00:22 2023 +0100 @@ -380,10 +380,10 @@ auth_d.addCallback(do_start_session) return auth_d - if self.host.initialised.called: + if self.host.init_pre_script.called: return defer.succeed(None).addCallback(backend_initialised) else: - return self.host.initialised.addCallback(backend_initialised) + return self.host.init_pre_script.addCallback(backend_initialised) def stop_session(self, profile): """Delete a profile session diff -r 1d24ff583794 -r 730f542e4ad0 libervia/backend/tools/common/async_process.py --- a/libervia/backend/tools/common/async_process.py Tue Dec 12 12:17:15 2023 +0100 +++ b/libervia/backend/tools/common/async_process.py Wed Dec 13 22:00:22 2023 +0100 @@ -133,7 +133,9 @@ cmd_args = [command] + args if "env" not in kwargs: # we pass parent environment by default - kwargs["env"] = None + # FIXME: `None` doesn't seem to work, despite what documentation says, to be + # checked and reported upstream if confirmed. + kwargs["env"] = os.environ reactor.spawnProcess(prot, command, cmd_args, diff -r 1d24ff583794 -r 730f542e4ad0 libervia/cli/base.py --- a/libervia/cli/base.py Tue Dec 12 12:17:15 2023 +0100 +++ b/libervia/cli/base.py Wed Dec 13 22:00:22 2023 +0100 @@ -713,7 +713,9 @@ ) self.quit(C.EXIT_BRIDGE_ERROR, raise_exc=False) return - await self.bridge.ready_get() + # we wait on init_pre_script instead of ready_get, so the CLI frontend can be used + # in init script. + await self.bridge.init_pre_script() self.version = await self.bridge.version_get() self._bridge_connected() self.import_plugins() diff -r 1d24ff583794 -r 730f542e4ad0 libervia/frontends/bridge/dbus_bridge.py --- a/libervia/frontends/bridge/dbus_bridge.py Tue Dec 12 12:17:15 2023 +0100 +++ b/libervia/frontends/bridge/dbus_bridge.py Wed Dec 13 22:00:22 2023 +0100 @@ -82,11 +82,7 @@ 'org.freedesktop.DBus.Error.Spawn.ExecFailed'): errback(BridgeExceptionNoService()) elif e._dbus_error_name == 'org.freedesktop.DBus.Error.NotSupported': - log.error(_( - 'D-Bus is not launched, please see documentation ' - '(doc/installation.rst, section "Launching D-Bus (on servers)") to ' - 'see instructions on how to launch it.' - )) + log.error(_("D-Bus is not launched, please see README to see instructions on how to launch it")) errback(BridgeInitError) else: errback(e) @@ -446,6 +442,15 @@ error_handler = lambda err:errback(dbus_to_bridge_exception(err)) return str(self.db_core_iface.image_resize(image_path, width, height, timeout=const_TIMEOUT, reply_handler=callback, error_handler=error_handler)) + def init_pre_script(self, callback=None, errback=None): + if callback is None: + error_handler = None + else: + if errback is None: + errback = log.error + error_handler = lambda err:errback(dbus_to_bridge_exception(err)) + return self.db_core_iface.init_pre_script(timeout=const_TIMEOUT, reply_handler=callback, error_handler=error_handler) + def is_connected(self, profile_key="@DEFAULT@", callback=None, errback=None): if callback is None: error_handler = None @@ -1251,6 +1256,14 @@ self.db_core_iface.image_resize(image_path, width, height, timeout=const_TIMEOUT, reply_handler=reply_handler, error_handler=error_handler) return fut + def init_pre_script(self): + loop = asyncio.get_running_loop() + fut = loop.create_future() + reply_handler = lambda ret=None: loop.call_soon_threadsafe(fut.set_result, ret) + error_handler = lambda err: loop.call_soon_threadsafe(fut.set_exception, dbus_to_bridge_exception(err)) + self.db_core_iface.init_pre_script(timeout=const_TIMEOUT, reply_handler=reply_handler, error_handler=error_handler) + return fut + def is_connected(self, profile_key="@DEFAULT@"): loop = asyncio.get_running_loop() fut = loop.create_future() diff -r 1d24ff583794 -r 730f542e4ad0 libervia/frontends/bridge/pb.py --- a/libervia/frontends/bridge/pb.py Tue Dec 12 12:17:15 2023 +0100 +++ b/libervia/frontends/bridge/pb.py Wed Dec 13 22:00:22 2023 +0100 @@ -399,6 +399,15 @@ else: d.addErrback(self._errback, ori_errback=errback) + def init_pre_script(self, callback=None, errback=None): + d = self.root.callRemote("init_pre_script") + if callback is not None: + d.addCallback(lambda __: callback()) + if errback is None: + d.addErrback(self._generic_errback) + else: + d.addErrback(self._errback, ori_errback=errback) + def is_connected(self, profile_key="@DEFAULT@", callback=None, errback=None): d = self.root.callRemote("is_connected", profile_key) if callback is not None: @@ -955,6 +964,11 @@ d.addErrback(self._errback) return d.asFuture(asyncio.get_event_loop()) + def init_pre_script(self): + d = self.root.callRemote("init_pre_script") + d.addErrback(self._errback) + return d.asFuture(asyncio.get_event_loop()) + def is_connected(self, profile_key="@DEFAULT@"): d = self.root.callRemote("is_connected", profile_key) d.addErrback(self._errback)