import ast import logging import dbus import dbus.mainloop.glib import dbus.service from gi.repository import GLib from .settings.backends import root_backend_factory # Настройка логирования logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s") logger = logging.getLogger(__name__) class Daemon(dbus.service.Object): def __init__(self, bus, object_path): super().__init__(bus, object_path) self.dbus_info = None self.polkit = None def _check_polkit_privilege(self, sender, conn, privilege): """ Проверяет привилегии с использованием PolicyKit. :param sender: Отправитель D-Bus сообщения. :param conn: Соединение D-Bus. :param privilege: Проверяемая привилегия. :return: True, если авторизация успешна; иначе False. """ if self.dbus_info is None: self.dbus_info = dbus.Interface( conn.get_object("org.freedesktop.DBus", "/org/freedesktop/DBus"), "org.freedesktop.DBus" ) pid = self.dbus_info.GetConnectionUnixProcessID(sender) if self.polkit is None: try: bus_object = dbus.SystemBus().get_object( "org.freedesktop.PolicyKit1", "/org/freedesktop/PolicyKit1/Authority" ) self.polkit = dbus.Interface(bus_object, "org.freedesktop.PolicyKit1.Authority") except Exception as e: logger.error(f"Failed to connect to PolicyKit: {e}") raise retry_limit = 3 retry_count = 0 while retry_count < retry_limit: try: auth_response = self.polkit.CheckAuthorization( ("unix-process", {"pid": dbus.UInt32(pid, variant_level=1), "start-time": dbus.UInt64(0, variant_level=1)}), privilege, {"AllowUserInteraction": "true"}, dbus.UInt32(1), "", timeout=600 ) is_auth, _, _ = auth_response return is_auth except dbus.DBusException as e: if e._dbus_error_name == "org.freedesktop.DBus.Error.ServiceUnknown": retry_count += 1 logger.warning(f"PolicyKit service unavailable, retrying ({retry_count}/{retry_limit})...") self.polkit = None else: logger.error(f"DBusException occurred: {e}") raise logger.error("Failed to authorize: PolicyKit service unavailable after retries.") return False @dbus.service.method( dbus_interface="ru.ximperlinux.TuneIt.DaemonInterface", in_signature="ssss", out_signature="s", sender_keyword="sender", connection_keyword="conn" ) def GetValue(self, backend_name, backend_params, key, gtype, sender=None, conn=None): if not self._check_polkit_privilege(sender, conn, "ru.ximperlinux.TuneIt.Daemon.auth"): raise dbus.DBusException( "org.freedesktop.DBus.Error.AccessDenied", "Permission denied" ) try: backend_params = ast.literal_eval(backend_params) backend = root_backend_factory.get_backend(backend_name, backend_params) if backend: return str(backend.get_value(key, gtype)) except Exception as e: return dbus.DBusException( "ru.ximperlinux.TuneIt.Daemon", e ) return f"backend_name: {backend_name}, params: {backend_params}, key: {key}" @dbus.service.method( dbus_interface="ru.ximperlinux.TuneIt.DaemonInterface", in_signature="sssss", out_signature="s", sender_keyword="sender", connection_keyword="conn" ) def SetValue(self, backend_name, backend_params, key, value, gtype, sender=None, conn=None): if not self._check_polkit_privilege(sender, conn, "ru.ximperlinux.TuneIt.Daemon.auth"): raise dbus.DBusException( "org.freedesktop.DBus.Error.AccessDenied", "Permission denied" ) try: backend_params = ast.literal_eval(backend_params) backend = root_backend_factory.get_backend(backend_name, backend_params) if backend: backend.set_value(key, value, gtype) except Exception as e: return dbus.DBusException( "ru.ximperlinux.TuneIt.Daemon", e ) return f"Failed to set value for backend_name: {backend_name}, key: {key}" @dbus.service.method( dbus_interface="ru.ximperlinux.TuneIt.DaemonInterface", in_signature="ssss", out_signature="s", sender_keyword="sender", connection_keyword="conn" ) def GetRange(self, backend_name, backend_params, key, gtype, sender=None, conn=None): if not self._check_polkit_privilege(sender, conn, "ru.ximperlinux.TuneIt.Daemon.auth"): raise dbus.DBusException( "org.freedesktop.DBus.Error.AccessDenied", "Permission denied" ) try: backend_params = ast.literal_eval(backend_params) backend = root_backend_factory.get_backend(backend_name, backend_params) if backend: return str(backend.get_range(key, gtype)) except Exception as e: return dbus.DBusException( "ru.ximperlinux.TuneIt.Daemon", e ) return f"Failed to get range for backend_name: {backend_name}, key: {key}" def main(): dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) try: bus = dbus.SystemBus() name = dbus.service.BusName("ru.ximperlinux.TuneIt.Daemon", bus) daemon = Daemon(bus, "/Daemon") logger.info("Service is running...") mainloop = GLib.MainLoop() mainloop.run() except KeyboardInterrupt: logger.info("Service interrupted by user.") except Exception as e: logger.error(f"Error: {e}") if __name__ == "__main__": main()