openSUSE:YaST 开发 DBUS 服务

跳转到:导航搜索


YaST DBus 命名空间服务

本文档描述了 YaST 包版本 2.18.x(意味着 openSUSE 11.2)的开发状态,目前仍在进行中。该服务随时可能更改,这是一个不稳定版本。


服务目的


YaST DBus 命名空间服务使其他应用程序能够通过 DBus 接口使用 YaST 库和功能。

优点包括:

  • 可以通过支持 DBus 通信的任何编程语言或应用程序访问 YaST 函数。这意味着该功能甚至可以在 shell(通过 dbus-send 或 qdbus 工具)或 DBus 浏览器(例如 kdbus、qdbusviewer...)中使用。
  • PolicyKit 集成 - 该服务使用 PolicyKit 检查用户授权,这使得非 root 用户可以访问特权操作。这在使用 YaST 作为面向桌面应用程序的后端时非常重要。

此服务将 DBus 支持移至更高级别 - 最初的 SCR DBus 服务仅导出低级 YaST 命名空间(SCR::)。此服务可以导出用任何受支持的编程语言(YCP、Perl、Python、C++、Ruby)编写的每个 YaST 命名空间。


如何安装服务


DBus 服务在 yast2-core-2.18.6 及更高版本中可用。它目前在 FACTORY 中可用,将在 openSUSE 11.2 中包含。该服务是 YaST 基本组件的一部分,该服务不需要额外的配置,默认情况下已启用。它使用 PolicyKit 进行访问控制,未经授权的使用是不可能的。


如何使用服务


该服务的 DBus 名称为 org.opensuse.YaST.modules,它在系统总线上可用。

YaST 命名空间作为路径为 /org/opensuse/YaST/modules/<namespace_name> 的对象可用。每个导出的对象(YaST 命名空间)都具有以下接口:

  • org.opensuse.YaST.Values - 这是标准的 YaST DBus 接口,它直接使用 DBus 数据类型。此接口尝试在可能的情况下将传入的 DBus 值自动转换为 YCP 值。例如,字符串将转换为 YCP 符号,如果函数期望符号参数。
  • org.opensuse.YaST.YCPValues - 此接口接受编组的 YCP 值(参见 #YCP 值的编组 部分)。任何 YCP 值都可以通过此接口传递。此接口更像是一种变通方法,用于使用否则无法访问的 YaST 函数或传递特殊值。通常不需要此接口,所有官方支持的函数都应可以使用 org.opensuse.YaST.Values 接口访问。

注意: 命名空间分隔符 '::' 已替换为 '/',后者用作 DBus 路径分隔符。这使得可以在命名空间名称中创建层次结构,例如 YaPI::SambaYaPI::USERS 将在 YaPI/ 子路径中可用。

每个对象仅导出底层命名空间的方法,不导出全局变量。如果需要访问它们,则应将它们包装在 get()/set() 函数中。请参阅 #从命名空间导出全局变量 部分。


模块管理器接口

该服务在对象 /org/opensuse/YaST/modules 上提供了一个特殊的接口 org.opensuse.YaST.modules.ModuleManager。这是访问 YaST DBus 服务本身的接口。

当前有这些可用方法:

  • Import(string name_space) - 显式导入 YaST 命名空间。由于命名空间是自动导入的,因此通常不需要此方法。它适用于只能发送请求到已知对象的图形 DBus 浏览器。
  • Lock()Unlock() - 应该用于从长期运行的客户端访问有状态函数。这些函数防止服务在超时后关闭。当至少有一个正在运行的客户端调用 Lock() 方法时,服务保持运行状态。


YCP 值的编组

有一个特殊的接口 org.opensuse.yast.YCPValues 用于传递 DBus 不直接支持的类型或值。此接口使用一种特殊的结构来编码任何 YCP 值。该结构(我们称之为 bsv 结构)具有三个组件:

  • boolean - nil 标志,如果为true则该结构表示 nil 值,所有其他字段未使用并且具有未定义的状态(只是有一些虚拟值来保持所有 YCP 值的相同签名)。
  • string - YCP 类型的名称,例如 stringintegerlist...
  • variant - 使用相同或类似的 DBus 类型传输的值,例如 YCP 符号作为 DBus 字符串传输,YCP 列表作为 DBus 数组传输... YCP 容器是递归编码的,variant 可以是另一个 bsv 结构

这种编码使得传输如下值成为可能:[1, "2", `symbol, nil]这些值无法通过直接的 DBus 值传输。


通过 PolicyKit 进行访问控制

PolicyKit 检查

每个传入的方法请求都由 PolicyKit 检查,并且只有当 PolicyKit 返回 YES 时才执行请求的方法。如果 PolicyKit 返回不同的结果,则服务返回 DBus 异常 org.freedesktop.PolicyKit.Error.NotAuthorized,后跟 PolicyKit 结果(参见 http://hal.freedesktop.org/docs/PolicyKit/polkit-polkit-simple.html#polkit-dbus-error-generate)。

如果需要,客户端应授权自身并再次调用该方法(有关详细信息,请参阅 http://hal.freedesktop.org/docs/PolicyKit/model-theory-of-operation.html)。

PolicyKit 操作 ID

该服务检查请求者是否有权调用 YaST 函数。YaST 使用前缀 org.opensuse.yast.modules、命名空间名称和函数名称来创建 PolicyKit 操作 ID。

PolicyKit 仅允许操作 ID 中的小写字母和数字 - 因此,命名空间和函数名称都转换为小写。命名空间分隔符 '::' 替换为 '.',后者用作操作 ID 中的分隔符。

示例

  PolicyKit action ID for Mode::normal() call is org.opensuse.yast.modules.mode.normal.


获取 PolicyKit 授权

可以获得 PolicyKit 授权:

  • 隐式来自 *.policy 配置文件
  • 用户以管理员(root)身份验证
polkit-auth --obtain org.opensuse.yast.modules.yapi.samba.getservicestatus
  • 管理员授予用户授权
polkit-auth --user login_name --grant org.opensuse.yast.modules.yapi.samba.getservicestatus

服务启动和关闭

如果未运行,则 DBus 守护程序会自动启动该服务。

在最后一次请求后至少 2 分钟后,该服务会自动关闭。这可以防止未使用的服务占用内存。

默认情况下,该服务在启动时不会导入任何命名空间。命名空间是根据需要动态导入的。

示例

以下是如何调用 YaPI::Samba::GetServiceStatus() 函数的示例。此示例需要 org.opensuse.yast.modules.yapi.samba.getservicestatus PolicyKit 授权,请参阅 #获取 PolicyKit 授权

Python 示例

import dbus
bus = dbus.SystemBus()
obj = bus.get_object('org.opensuse.YaST.modules', '/org/opensuse/YaST/modules/YaPI/Samba')
ifce=dbus.Interface(obj,'org.opensuse.YaST.Values')
result=ifce.GetServiceStatus()
print result

Shell 示例

dbus-send --system --dest=org.opensuse.YaST.modules --print-reply --type=method_call /org/opensuse/YaST/modules/YaPI/Samba org.opensuse.YaST.Values.GetServiceStatus
qdbus --system org.opensuse.YaST.modules /org/opensuse/YaST/modules/YaPI__Samba org.opensuse.YaST.Values.GetServiceStatus


如何导出 YaST 功能


本节对于 YaST 开发人员和对实现细节感兴趣的用户非常重要。

Dbus 服务可以导出现有的函数。但为了避免一些问题,导出的函数应遵循以下规则,因为使用方式完全不同:

  • 导出的函数应该是无状态的 - 没有单独的 Read()Edit()Write() 函数,而是为每个导出的功能提供一个函数 - 例如 DeleteUser(),它调用所有必要的函数。
    原因是,通过 DBus 服务导出的命名空间只是所有客户端共享的一个实例(单例)。当前状态可能包含来自另一个客户端的先前调用的内部数据。无状态函数可确保在所有情况下都使用相同的先决条件。
    并行运行多个客户端可能会导致竞争条件、数据丢失、错误配置或其他严重问题,如果使用有状态的 API。
  • 输入和输出参数应该只有基本数据类型,例如 stringinteger... YaST 有 DBus 没有的更多数据类型(例如 symbol),或者它有一个无法直接通过 DBus 传输的特殊值(nil),或者传输的值在服务外部无用(例如 函数指针)。
  • 使用单个类型容器,当使用 listmap 类型时,DBus 需要所有值的相同类型,例如:[1, 2, 3]可以转换为 DBus 数组,而[1, "2", `symbol]不能。
  • DBus 不支持对象和方法名称中的某些特殊字符,尤其是双冒号 (:) 对 YaST 来说是个问题。DBus 服务会自动将无效字符替换为下划线 (_),双双冒号替换为斜杠 (/)。示例:YaPI::Samba 模块作为 YaPI/Samba DBus 对象可用。
  • 导出的函数名称不应仅在大小写字母上有所不同。原因是,例如,Function() 和 function() 将具有相同的 PolicyKit 操作 ID(参见 #PolicyKit 操作 ID 部分),并且访问权限将在它们之间共享,这通常不是预期的并且容易出错。
 check-local:
       polkit-policy-file-validate polkit_policy_file
  • 导出的命名空间及其导入的命名空间都不应定义构造函数。原因是安全性 - 由于自动导入功能,构造函数可能会执行未经调用客户端授权的操作。另一个问题是构造函数仅调用一次(在第一次导入时),并且使用构造函数会导致应避免的有状态 API。
  • 导出的函数不得使用任何 UI:: 函数,因为该服务无法打开任何对话框,因为它没有终端或 X 会话,并且所有 UI:: 调用都极有可能失败。该服务应作为提供功能的后端,不应在服务中使用 UI。


调试


手动启动服务

可以使用以下命令显式启动该服务:/usr/lib/YaST2/bin/yast_modules_dbus_server可执行文件。该服务可能仅由 root 用户拥有,因此只能从 root shell 启动。使用--disable-timer选项以禁用自动关闭功能。

可以使用可选参数预加载请求的命名空间。

示例

 /usr/lib/YaST2/bin/yast_modules_dbus_server --disable-timer Label Pkg

日志记录

该服务使用标准的 YaST 日志文件/var/log/YaST2/y2log。服务日志用[y2dbus]组件字符串标记在每一行。

可以使用变量启用调试日志记录Y2DEBUG=1.

重新加载 DBus 配置

在开发过程中,该服务很少拒绝启动,声称它已经在运行。重新加载 DBus 配置解决了问题。如果发生这种情况,请使用 DBus 守护程序的 ReloadConfig() 方法

 dbus-send --print-reply --system --dest=org.freedesktop.DBus / org.freedesktop.DBus.ReloadConfig


未来增强


SCR 服务集成

这个新的 DBus 服务应该取代以前的 。目前 SCR 命名空间未导出,因为它必须特别处理。

原因是 SCR:: 提供重载的方法,并且每次调用都必须在运行时解析。另一个原因是解释器允许启动多个 SCR 实例,因此它的处理方式与其他所有命名空间不同。

从命名空间导出全局变量

如果确实需要,全局变量可以通过 DBus 对象属性导出。目前,如果需要导出它们,则必须将它们包装在 get()/set() 方法中。