D-Bus API设计指南(译文)

摘要

D-Bus的最常见用途是实现一项服务,该服务将被多个客户端程序使用,因此,在总线上导出的所有接口都形成一个公共API。设计D-Bus API就像设计任何其他API:都有很大的灵活性,但是要遵循一些设计模式并避免反模式。

本指南旨在说明编写D-Bus API的最佳实践。多年来,在许多项目中使用D-Bus对其进行了完善。对于使用诸如GDBus这样的通用D-Bus库来实现api,将给出建议,但是详细的实现说明留给了库的文档。请注意,您不应使用的 dbus-glib 实现 D-Bus 的服务,因为它已被废弃,无人维护。大多数服务还应避免使用 libdbus (dbus-1),它是一个低级库,很难正确使用:它旨在通过QtDBus之类的语言绑定来使用 。

有关D-Bus本身的文档,请参阅 D-Bus规范

API

D-Bus API是一个或多个接口的规范,将由总线上的服务公开的对象来实现。通常,API被设计为一组接口文件,并且服务的实现遵循这些文件。但是,某些项目选择在服务代码中定义API,并使用D-Bus自省功能从运行的服务中导出XML接口文件 。两者都是有效的方法。

为简单起见,本文档使用D-Bus接口的XML描述作为规范表示。

接口文件

D-Bus接口文件是描述一个或多个D-Bus接口的XML文件,并且是以机器可读的方式描述D-Bus API的最佳方法。该格式在D-Bus规范中进行了描述, 并由 gdbus-codegen 之类的工具支持。

应将公共API的接口文件安装到 ( d a t a d i r ) ∗ / d b u s − 1 / i n t e r f a c e s ,以便其他服务可以加载它们。不应安装专用 A P I 。每个 D − B u s 接口应该安装一个文件,以该接口命名,其中包含单个顶级 < n o d e > 元素,并且在其下具有单个 < i n t e r f a c e > 。例如,接口 c o m . e x a m p l e . M y S e r v i c e 1. M a n a g e r 将由文件 ∗ (datadir)*/dbus-1/interfaces,以便其他服务可以加载它们。不应安装专用API。每个D-Bus接口应该安装一个文件,以该接口命名,其中包含单个顶级 <node>元素,并且在其下具有单个<interface>。例如,接口 com.example.MyService1.Manager将由文件 * (datadir)/dbus1/interfaces,以便其他服务可以加载它们。不应安装专用API。每个DBus接口应该安装一个文件,以该接口命名,其中包含单个顶级<node>元素,并且在其下具有单个<interface>。例如,接口com.example.MyService1.Manager将由文件(datadir)/dbus-1/interfaces/com.example.MyService1.Manager.xml: 描述:

示例 D-Bus 总线接口XML

一个简短的示例接口XML文档。

<!DOCTYPE node PUBLIC
    "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
    "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd" >
<node xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd">
  <interface name="com.example.MyService1.InterestingInterface">
    <method name="AddContact">
      <arg name="name" direction="in" type="s">
        <doc:doc><doc:summary>Name of new contact</doc:summary></doc:doc>
      </arg>
      <arg name="email" direction="in" type="s">
        <doc:doc><doc:summary>E-mail address of new contact</doc:summary></doc:doc>
      </arg>
      <arg name="id" direction="out" type="u">
        <doc:doc><doc:summary>ID of newly added contact</doc:summary></doc:doc>
      </arg>
      <doc:doc>
        <doc:description>
          <doc:para>
            Adds a new contact to the address book with their name and
            e-mail address.
          </doc:para>
        </doc:description>
      </doc:doc>
    </method>
  </interface>
</node>

如果客户端B需要使用服务A定义的接口,则客户端B应该声明对服务A的构建时间依赖性,并将接口文件的已安装副本用于其必须执行的任何代码生成。它不应该有接口的本地副本,因为这会与服务A的git存储库中的规范副本不同步。

API版本控制

就像C API一样,D-Bus接口应设计为可与不兼容API的版本并行使用。这是通过在每个接口名称,服务名称和对象路径中包括一个版本号来实现的,该版本号在每次向后不兼容的更改时都会递增。

从第一个发行版开始,所有API中都应包含版本号,因为这意味着升级到新版本就像增加编号一样简单,而不是在任何地方都插入一个编号,这会花费更多的精力。

可以在不增加版本号的情况下将新的API添加到D-Bus接口,因为此类添加仍向后兼容。但是,如果客户端 希望针对未实现新方法的服务的较早版本运行,则客户端应妥善处理所有D-Bus方法调用中的org.freedesktop.DBus.Error.UnknownMethod错误回复。(这也防止使用生成的绑定;应该使用通用的D-Bus方法调用而不是特定的生成的绑定来调用客户端希望正常使用的任何方法。)

当API被破坏,更改或删除时,服务的版本号必须增加;例如,从com.example.MyService1 到com.example.MyService2。如果向后兼容性维持在通过实施新旧和接口服务,该服务可以拥有两个公开的名字和客户端可以任意使用他们的支持。

Annotations中所讨论,应在XML接口XML中使用注解标记新的或已弃用的API。

但是请注意,要同时支持多个接口版本,还需要对对象路径进行版本控制,因此不得 将对象放在根路径(‘/’)上的总线上。这是出于技术原因:从D-Bus服务发送的信号的原始服务名称被其唯一名称覆盖(例如com.example.MyService2 被 :1:15 覆盖)。如果在实现服务接口的两个版本的对象之间共享对象路径,则客户端程序无法分辨信号来自哪个对象。解决方案是在所有对象路径中包含版本号,例如 /com/example/MyService1/Manager 而不是 / 或 /com/example/MyService/Manager。

总之,所有服务名称,接口名称和对象路径中都应包含版本号:

  • com.example.MyService1
  • com.example.MyService1.InterestingInterface
  • com.example.MyService1.OtherInterface
  • /com/example/MyService1/Manager
  • /com/example/MyService1/OtherObject

API设计

D-Bus API设计与C API设计大致相同,但是还有一些需要注意的地方,这些地方来自于D-Bus的特性(显式错误、信号和属性),以及它作为IPC系统的实现。

D-Bus方法调用比C函数调用昂贵得多,通常需要几毫秒的时间才能完成往返。因此,设计应尽量减少执行操作所需的方法调用数。

类型系统非常具有表现力,特别是与C相比,API应该充分利用它。

同样,它对信号和属性的支持使其与普通C API有所区别,并且编写良好的D-Bus API在适当的地方可以充分利用这些功能。它们可以与D-Bus规范中定义的标准接口耦合,以允许对层次结构中的属性和对象的一致访问。

尽量减少往返

每个D-Bus方法调用都是从客户端程序到服务的往返,然后再返回,这很昂贵-大约需要一毫秒。D-Bus API设计中的首要任务之一是最大程度地减少客户所需的往返次数。这可以通过提供特定的便捷API和设计API的组合来实现,这些API可以在单个调用中对大型数据集进行操作,而不是需要与数据集中的元素一样多的调用。

考虑一个地址簿API com.example.MyService1.AddressBook。它可能具有 AddContact(ss) → (u) 方法来添加联系人(采用姓名和电子邮件地址参数并返回唯一的联系人ID),以及 RemoveContact(u) 方法来通过ID删除联系人 。在对通讯录中的单个联系人进行操作的正常情况下,这些呼叫是最佳的。但是,如果用户要导入联系人列表或同时删除多个联系人,则必须为每个联系人进行一次呼叫-对于大型联系人列表,这可能需要很长时间。

该接口可以使用 UpdateContacts(a(ss)au) 方法代替 AddContact 和 RemoveContact 方法,该方法采用包含新联系人详细信息的结构数组和要删除的联系人ID数组,并返回新联系人的ID数组。这将所需的往返次数减少到一个。

添加便利性API以专门针对该任务的单个方法调用替换一系列常用方法调用,最好在对该API进行概要分析并确定瓶颈后再进行,否则可能导致过早的优化。通常可以在客户端初始化期间添加便捷方法的一个区域,在该区域中需要多个方法调用以建立其与服务的状态。

利用类型系统

D-Bus的类型系统类似于Python的类型系统,但语法更短,C开发人员可能不熟悉。有效使用类型系统的关键是尽可能多地暴露类型中的结构。特别是,应避免通过D-Bus发送结构化字符串,因为它们需要构建和解析。两者都是复杂的操作,容易产生错误。

对于在受限情况下使用的API,枚举值应作为无符号整数传输。对于需要由第三方扩展或在松散耦合的系统中使用的API,枚举值应为某种已定义格式的字符串。

将值作为整数传输意味着可以避免字符串解析和匹配,消息更加紧凑,并且开发人员可以更轻松地避免输入错误(例如,如果在实现中使用C枚举)。

以字符串形式传输值意味着第三方可以定义其他值,而不必担心与整数值发生冲突;例如通过使用与D-Bus接口相同的反向域名命名空间。

在这两种情况下,接口文档都应描述每个值的含义。它应说明该类型是否可以在将来扩展,如果可以,则说明服务和客户端应如何处理无法识别的值-通常通过将其视为等于“未知”或“失败”值来进行处理。传统上,零用作“未知”值。

例如,代替:

<!--
    Status:
    Status of the object.
    Valid statuses: ‘unknown’, ‘ready’, ‘complete’.
-->
<property name="Status" type="s" access="read" />

采用:

<!--
    Status:
    Status of the object.
    Valid statuses: 0 = Unknown, 1 = Ready, 2 = Complete.
    Unrecognized statuses should be considered equal to Unknown.
-->
<property name="Status" type="u" access="read" />

类似地,应该使用枚举值而不是布尔值,因为它们允许在将来添加额外的值,而且相对于布尔值没有歧义。

例如,代替:

<!--
   MoveAddressBook:
   @direction: %TRUE to move it up in the list, %FALSE to move it down
   Move this address book up or down in the user’s list of address books.
   Higher address books have their contacts displayed first in search
   results.
-->
<method name="MoveAddressBook">
  <arg name="direction" type="b" direction="in" />
</method>

比布尔更明确:

<!--
   MoveAddressBook:
   @direction: 0 = Move it up in the list, 1 = Move it down
   Move this address book up or down in the user’s list of address books.
   Higher address books have their contacts displayed first in search
   results.
   Unrecognized enum values for @direction will result in the address book
   not moving.
-->
<method name="MoveAddressBook">
  <arg name="direction" type="u" direction="in" />
</method>

还应使用枚举值代替人类可读的字符串,如果可能的话,不应通过总线发送该字符串。服务和客户端可能在不同的语言环境中运行,因此以不同的方式解释任何人类可读的字符串,或以错误的语言将其呈现给用户。传输枚举值并将其转换为客户端中人类可读的字符串。

在服务已从其他地方接收到人类可读的字符串的情况下,应将其未经修改地传递给客户端,最好是将其语言环境与客户端一起传递。将人类可读的信息传递给客户端比没有传递更好。

例如,代替:

<!--
   ProgressNotification:
   @progress_message: Human readable progress message string.
   Emitted whenever significant progress is made with some example
   operation. The @progress_message can be displayed in a UI dialogue to
   please the user.
-->
<signal name="ProgressNotification">
  <arg name="progress_message" type="s" />
</method>

进度应以枚举值的形式发布:

<!--
   ProgressNotification:
   @progress_state: 0 = Preparing, 1 = In progress, 2 = Finished
   Emitted whenever significant progress is made with some example
   operation. The @progress_state is typically converted to a human readable
   string and presented to the user. Unrecognized @progress_state values
   should be treated as state 1, in progress.
-->
<signal name="ProgressNotification">
  <arg name="progress_state" type="u" />
</method>

D-Bus不存在C所拥有的有符号整数与无符号整数的问题(特别是,它不执行隐式符号转换),因此应始终选择整数类型以使其可能包含的数据具有适当的大小和有符号性。通常,与有符号值相比,更需要无符号值。

结构几乎可以在D-Bus类型的任何地方使用,并且结构数组特别有用。数据字段相关的任何地方都应使用结构。但是请注意,结构在将来是不可扩展的,因此请始终考虑可扩展性

例如,代替几个 相同索引的数组,数组是包含在相同项目集的不同属性的 :

<!--
   AddContacts:
   @names: Array of contact names to add.
   @emails: Corresponding array of contact e-mail addresses.
   @ids: Returned array of the IDs of the new contacts. This will be the
     same length as @names.
   Add zero or more contacts to the address book, using their names and
   e-mail addresses. @names and @emails must be the same length.
-->
<method name="AddContacts">
  <arg name="names" type="as" direction="in" />
  <arg name="emails" type="as" direction="in" />
  <arg name="ids" type="au" direction="out" />
</method>

可以将这些数组组合为一个结构的单个数组:

<!--
   AddContacts:
   @details: Array of (contact name, contact e-mail address) to add.
   @ids: Returned array of the IDs of the new contacts. This will be the
     same length as @details.
   Add zero or more contacts to the address book, using their names and
   e-mail addresses.
-->
<method name="AddContacts">
  <arg name="details" type="a(ss)" direction="in" />
  <arg name="ids" type="au" direction="out" />
</method>

请注意,D-Bus阵列会自动以其长度进行传输,因此无需对它们进行空终止或单独编码其长度。

可扩展性

一些D-Bus API具有非常明确的用例,并且永远不需要扩展。其他的则用于随时间变化的松散耦合系统中,因此应设计为从一开始就可扩展,而无需将来破坏API。在拥有更复杂的API和将来能够轻松扩展它之间进行权衡。

D-Bus中可扩展性的关键工具是a {sv},即将字符串映射到变量的字典。如果将定义良好的命名空间字符串用作字典键,则任意D-Bus对等方都可以将其所需的任何信息添加到字典中。任何其他了解它的对等方都可以查询和检索信息;其他同行将忽略它。

使用a {sv}的可扩展API的规范示例是 Telepathy。它使用a {sv} 值作为结构中的最终元素,以允许将来扩展它们。

辅助工具是在方法调用中使用标志字段。接受标志集完全在接口设计器的控制下,并且与枚举类型一样,将来可以扩展而不会破坏API。添加更多标志可以调整方法调用的功能。

使用信号、属性和错误

由于IPC固有的延迟,D-Bus方法调用是显式异步。这意味着对等方不应阻止从方法调用中收到答复。他们应该安排其他工作(在主循环中),并在收到答复后进行处理。即使方法回复可能需要一段时间,但仍可以确保调用方最终收到准确的一个回复。该答复可能是方法的返回值,方法的错误或D-Bus守护程序的错误,指示服务以某种方式失败(例如,由于崩溃)。

因此,服务实现应注意始终对每个方法调用准确地答复一次。在长时间运行的操作结束时进行答复是正确的-客户端将耐心等待,直到操作完成并收到答复。

请注意,除非调用被显示禁用,否则D-Bus客户端绑定可能实现数十秒的合成超时。对于运行时间很长的操作,应禁用客户端绑定的超时,并在客户端的UI中明确指出应用程序尚未冻结,而只是在运行长时间的操作。

在这种情况下要避免的一种反模式是,在接收到方法调用时启动长时间运行的操作,然后永远不响应方法调用,而是通过信号通知客户端操作已经完成。这种方法是不正确的,因为信号发射与方法调用之间没有一对一的关系-理论上,信号可能会多次发射,或者永远不会发射,这是客户端无法处理的。

同样,使用D-Bus错误答复来表示由方法调用触发的操作失败,而不要在方法的答复中使用自定义错误代码。这意味着答复总是表示成功,而错误总是表示失败-而不是根据其参数或者在其他参数中必须返回伪值的答复。使用D-Bus错误答复还意味着可以在调试工具中突出显示此类故障,从而简化调试。

客户应为每个方法调用处理所有可能的标准和已记录的D-Bus错误。与普通的C函数调用相比,IPC本质上具有更多的潜在故障,并且客户端应准备好妥善处理所有这些故障。

使用标准接口

尽可能使用标准的D-Bus接口。

属性

D-Bus规范定义了org.freedesktop.DBus.Properties 接口,所有对象都应使用该接口通过PropertiesChanged信号通知客户端其属性值的更改。该信号消除了对单个PropertyName Changed信号的需要,并允许在单个信号发射中通知多个属性,从而减少了IPC往返。同样,Get和Set方法可用于操作对象上的属性,从而消除了多余的 Get PropertyName和Set PropertyName方法。

例如,考虑使用以下方法实现接口 com.example.MyService1.SomeInterface 的对象 :

  • GetName() → (s)
  • SetName(s) → ()
  • GetStatus() → (u)
  • RunOperation(ss) → (u)

和信号:

  • NameChanged(u)
  • StatusChanged(u)

该接口可以简化为一个方法:

  • RunOperation(ss) → (u)

然后,该对象可以实现org.freedesktop.DBus.Properties 接口并定义属性:

  • Name of type s, read–write
  • Status of type u, read-only

该NameChanged和StatusChanged信号将被替换 org.freedesktop.DBus.Properties.PropertiesChanged。

对象管理器

该规范还定义了 org.freedesktop.DBus.ObjectManager 接口,该接口应在服务需要以平面或树状结构公开可变数量的相同类的对象,并且希望客户端对以下内容感兴趣时使用:大多数或所有对象。例如,通讯簿服务可以使用它来公开多个通讯簿,每个通讯簿都是一个单独的对象。该GetManagedObjects方法允许完整的对象树进行查询,返回所有对象的属性也省去了进一步IPC往返查询性能。

如果预期客户端不会对大多数公开的对象感兴趣,则不应该使用ObjectManager,因为它无论如何都会将所有对象发送到每个客户端,从而浪费总线带宽。因此,文件管理器不应该使用ObjectManager公开整个文件系统层次结构。

例如,考虑一个使用以下方法实现接口com.example.MyService1.AddressBookManager的对象 :

  • GetAddressBooks() → (ao)

和信号:

  • AddressBookAdded(o)
  • AddressBookRemoved(o)

如果管理器对象位于路径/com/example/MyService1/AddressBookManager,则每个通讯簿都是子对象,例如 /com/example/MyService1/AddressBookManager/SomeAddressBook。

可以消除该接口,并在管理器对象上实现org.freedesktop.DBus.ObjectManager接口。

对getaddressbook的调用将变成对getmanagedobject的调用,而AddressBookAdded和addressbookremove的信号将变成对InterfacesAdded和interfacesremove的输出。

命名约定

从服务名称到方法参数的所有D-Bus名称均遵循一组约定。遵循这些约定可使API使用起来更加自然,并与系统上的所有其他服务保持一致。

服务使用反向域名表示法,基于提供服务的项目的域名(全部为小写),再加上服务的唯一名称(以驼峰形式)。

例如, 由网站为Chocolateteapot.com的软件供应商提供的名为ContactDex的通讯簿应用程序的版本2 将使用服务名称com.chocolateteapot.ContactDex2。

几乎所有名称都使用驼峰大小写,没有下划线,如下例所示。方法和信号参数是一个例外,使用 lowercase_with_underscores。类型信息永远不会编码在参数名称中(即不是 匈牙利符号)。

  • 服务名称

    com.example.MyService1

  • 接口名称

    com.example.MyService1.SomeInterface

  • 对象路径(根对象)

    /com/example/MyService1

  • 对象路径(子对象)

    /com/example/MyService1/SomeChild

  • 对象路径(孙对象)

    /com/example/MyService1/AnotherChild/Grandchild1

  • 方法名称

    com.example.MyService1.SomeInterface.MethodName

  • 信号名称

    com.example.MyService1.SomeInterface.SignalName

  • 物业名称

    com.example.MyService1.SomeInterface.PropertyName

另请参阅:API版本控制

代码生成

与其手动实现D-Bus接口的服务器端和客户端,不如编写接口XML描述并使用gdbus-codegen之 类的工具来生成类型安全的C API,然后使用这些来构建实现,通常会更容易。这避免了编写代码来为每个方法调用构建和读取D-Bus参数变量的繁琐且容易出错的过程。

使用代码生成器超出了本指南的范围。有关更多信息,请参见 gdbus-codegen 手册

注解

可以将注释添加到接口XML,以在API上公开元数据,文档或代码生成工具可以使用这些元数据来修改其输出。D-Bus规范中给出了一些标准注释 ,但是其他注释可以由特定工具定义。

例如,gdbus-codegen定义了一些有用的注释,这些注释在其手册页上列出。

以下注释是最有用的:

  • org.freedesktop.DBus.Deprecated

    将符号标记为已弃用。每当更改API、指定引入弃用的版本、弃用的原因以及要使用的替换符号时,都应使用此选项。

  • org.gtk.GDBus.Since

    在初始发行版之后,将符号标记为已添加到API。其中应包括首次添加符号的版本。

  • org.gtk.GDBus.C.Name和org.freedesktop.DBus.GLib.CSymbol

    两者可互换使用,以提示生成符号代码时要使用的C函数名称。使用此批注可使生成的绑定更易于使用。

  • org.freedesktop.DBus.Property.EmitsChangedSignal

    指示是否期望某个属性发出更改信号。这可能会影响代码生成,但也是有用的记录,因为客户端程序随后会知道何时收到属性更改通知以及何时需要重新查询。

文献资料

就像C API一样,必须记录D-Bus API。有多种方法可以记录D-Bus接口XML文件中的接口、方法、属性和信号,但是每种方法都有其缺点。您应该选择最适合您使用的工具和工作流程的方法。

XML注释

建议将包含gtk-doc格式文档的XML注释 与 gdbus-codegen一起使用。使用gdbus-codegen可以提取这些注释,将其转换为DocBook格式,并包含在项目的API手册中。例如:

D-Bus接口XML中的文档注释

例如:org.freedesktop.DBus.Properties接口的自省XML中的gtk-doc样式的文档注释。

<!--
    org.freedesktop.DBus.Properties:
    @short_description: Standard property getter/setter interface
    Interface for all objects which expose properties on the bus, allowing
    those properties to be got, set, and signals emitted to notify of changes
    to the property values.
-->
<interface name="org.freedesktop.DBus.Properties">
  <!--
      Get:
      @interface_name: Name of the interface the property is defined on.
      @property_name: Name of the property to get.
      @value: Property value, wrapped in a variant.
      Retrieves the value of the property at @property_name on
      @interface_name on this object. If @interface_name is an empty string,
      all interfaces will be searched for @property_name; if multiple
      properties match, the result is undefined.
      If @interface_name or @property_name do not exist, a
      #org.freedesktop.DBus.Error.InvalidArgs error is returned.
  -->
  <method name="Get">
    <arg type="s" name="interface_name" direction="in"/>
    <arg type="s" name="property_name" direction="in"/>
    <arg type="v" name="value" direction="out"/>
  </method>
  <!--
      PropertiesChanged:
      @interface_name: Name of the interface the properties changed on.
      @changed_properties: Map of property name to updated value for the
        changed properties.
      @invalidated_properties: List of names of other properties which have
        changed, but whose updated values are not notified.
      Emitted when one or more properties change values on @interface_name.
      A property may be listed in @changed_properties or
      @invalidated_properties depending on whether the service wants to
      broadcast the property’s new value. If a value is large or infrequently
      used, the service might not want to broadcast it, and will wait for
      clients to request it instead.
  -->
  <signal name="PropertiesChanged">
    <arg type="s" name="interface_name"/>
    <arg type="a{sv}" name="changed_properties"/>
    <arg type="as" name="invalidated_properties"/>
  </signal>
</interface>

XML注释

文档也可以作为注释元素添加到XML中。gdbus-codegen也支持这种格式,但是首选gtk-doc注释。例如:

D-Bus接口XML中的文档注释

例如:GDBus文档注释在org.freedesktop.DBus.Properties接口的自省XML中。

<interface name="org.freedesktop.DBus.Properties">
  <annotation name="org.gtk.GDBus.DocString" value="Interface for all
    objects which expose properties on the bus, allowing those properties to
    be got, set, and signals emitted to notify of changes to the property
    values."/>
  <annotation name="org.gtk.GDBus.DocString.Short"
    value="Standard property getter/setter interface"/>
  <method name="Get">
    <annotation name="org.gtk.GDBus.DocString" value="Retrieves the value of
      the property at @property_name on @interface_name on this object. If
      @interface_name is an empty string, all interfaces will be searched
      for @property_name; if multiple properties match, the result is
      undefined.
      If @interface_name or @property_name do not exist, a
      #org.freedesktop.DBus.Error.InvalidArgs error is returned."/>
    <arg type="s" name="interface_name" direction="in">
      <annotation name="org.gtk.GDBus.DocString"
        value="Name of the interface the property is defined on."/>
    </arg>
    <arg type="s" name="property_name" direction="in">
      <annotation name="org.gtk.GDBus.DocString"
        value="Name of the property to get."/>
    </arg>
    <arg type="v" name="value" direction="out">
      <annotation name="org.gtk.GDBus.DocString"
        value="Property value, wrapped in a variant."/>
    </arg>
  </method>
  <signal name="PropertiesChanged">
    <annotation name="org.gtk.GDBus.DocString" value="Emitted when one or
      more properties change values on @interface_name. A property may be
      listed in @changed_properties or @invalidated_properties depending on
      whether the service wants to broadcast the property’s new value. If a
      value is large or infrequently used, the service might not want to
      broadcast it, and will wait for clients to request it instead."/>
    <arg type="s" name="interface_name">
      <annotation name="org.gtk.GDBus.DocString"
        value="Name of the interface the properties changed on."/>
    </arg>
    <arg type="a{sv}" name="changed_properties">
      <annotation name="org.gtk.GDBus.DocString"
        value="Map of property name to updated value for the changed
          properties."/>
    </arg>
    <arg type="as" name="invalidated_properties">
      <annotation name="org.gtk.GDBus.DocString"
        value="List of names of other properties which have changed, but
          whose updated values are not notified."/>
    </arg>
  </signal>
</interface>

服务文件

每个D-Bus服务必须安装一个.service文件,该文件描述其服务名称以及运行该服务以启动该服务的命令。这样可以激活服务(请参阅 D-Bus规范)。

服务文件必须以服务的知名名称命名,例如,文件com.example.MyService1.service以知名名称 com.example.MyService1命名。文件必须安装在 ( d a t a d i r ) ∗ / d b u s − 1 / s e r v i c e s 的会话总线和 ∗ (datadir)*/dbus-1/services 的会话总线和 * (datadir)/dbus1/services的会话总线和(datadir)/dbus-1/system-services的系统总线。但是请注意,系统总线上的服务也几乎总是需要 安全策略

安全政策

在较高级别上,D-Bus安全模型是:

  • 有一个系统总线,以及零个或多个会话总线。
  • 任何进程都可以连接到系统总线。系统总线限制可以拥有名称或发送方法调用,并且只有以特权用户身份运行的进程才能接收未发送给他们的单播消息。每个进程都可以接收广播。
  • 每个会话总线都有一个所有者(用户)。只有其所有者可以连接;在通用Linux上,会话总线不被视为特权边界,因此它上的进程之间没有进一步的特权分离。

保护D-Bus服务的完整范围不在本指南的范围内,但是,在设计API以简化安全策略实施时,可以采取一些步骤。

D-Bus安全策略以XML文件的形式写入 ( d a t a d i r ) / d b u s − 1 / s y s t e m . d ∗ , ∗ (datadir)/dbus-1/system.d*, * (datadir)/dbus1/system.d,(datadir)/dbus-1/session.d, ( s y s c o n f d i r ) / d b u s − 1 / s y s t e m . d ∗ 和 ∗ (sysconfdir)/dbus-1/system.d* 和* (sysconfdir)/dbus1/system.d(sysconfdir)/dbus-1/session.d并使用允许/拒绝模型,其中可以根据与之匹配的所有策略规则的总和来允许或拒绝每个消息(方法调用,信号发射等)。策略中的每个或 规则都应具有自己的 send_destination或receive_sender属性集。

在设计API时,请记住需要编写和安装这样的安全策略,并考虑拆分方法或提供更多接受受约束参数的受限制版本,以便在受信任程度较低的客户端需要时,可以使用限制较少的安全策略公开它们。由于dbus-daemon 1.10,安全策略应该安装到 ( d a t a d i r ) 而不是 ( f i l e ( (datadir)而不是(file( (datadir)而不是(file((sysconfdir));后者用于系统管理员。

其次,系统总线的默认D-Bus安全策略具有足够的限制,允许敏感数据(如密码)通过总线以单播消息(包括单播信号)安全发送;因此,没有必要通过实现额外的安全性来将api复杂化。但是,请注意,敏感数据不能通过广播信号发送,因为总线上的所有节点都可以看到它们。会话总线的默认策略没有限制,但通常不是安全边界。

Debugging

在D-Bus上运行的调试服务可能很棘手,特别是如果它们是通过服务激活启动的,因此在您无法控制的环境中进行调试。

提供三种主要工具:D-Bus Monitor,Bustle和D-Feet。

D-Bus Monitor

dbus-monitor 是D-Bus的核心工具,它允许在会话或系统总线上进行监听,并打印其看到的所有消息。可以使用标准 D-Bus匹配规则过滤消息, 以使消息流更易于管理。

早期版本的D-Bus要求手动放松系统总线的安全策略以允许窃听所有消息。这意味着调试是困难且不安全的。最新版本的D-Bus增加了对root用户的仅监视器连接的支持,这意味着dbus-monitor可以以root用户身份运行,从而 可以在不修改其安全策略的情况下轻松监视系统总线上的所有消息。

Bustle

Bustle是 dbus-monitor的图形版本 ,其UI专注于通过在时间轴上绘制消息来分析D-Bus性能。查找服务和客户端之间IPC性能的瓶颈的理想选择。

D-Feet

D-Feet是D-Bus的自检工具,它以图形方式显示总线上的所有对等点,并列出其对象,接口,方法,信号和属性以供检查。对于调试与总线上存在的服务及其所暴露的对象有关的各种问题,此功能很有用。

关于

原文链接:https://dbus.freedesktop.org/doc/dbus-api-design.html

原文作者:Philip Withnall

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值