D-Installer needs to offer a D-Bus API for bi-directional communication with the client. Sometimes, the backend has to wait until the client provides some information. A typical case is when requesting for a LUKS password meanwhile the storage system is been probed.
The minimal requirements for the communication API are:
- Emit signal when a new question is added
- Offer a way to get the list of questions
- Offer a way to get the list of unanswered questions
- Offer a way to get information from a question (e.g., the UUID the question refers to)
- Offer a way to answer a question
Note that the info and the answer can vary depending on the question. For example, asking for continuing or not requires a boolean answer, but asking for a password requires a string be provided.
D-Installer service exports a tree of objects to manage the questions:
/org/opensuse/DInstaller/Questions1
/org/opensuse/DInstaller/Questions1/1
/org/opensuse/DInstaller/Questions1/2
/org/opensuse/DInstaller/Questions1/3
...
The root object /org/opensuse/DInstaller/Questions1
implements an interface to get the paths of the children objects. It would also emit a signal when a new question is asked. This would be easily done by implementing the org.freedesktop.DBus.ObjectManager
interface, but ruby-dbus does not support ObjectManager yet.
An interface similar to ObjectManager could be defined as temporary solution. Such an interface would provide only the minimal stuff required for D-Installer.
/org/opensuse/DInstaller/Questions1
org.freedesktop.DBus.Properties
org.freedesktop.DBus.ObjectManager (not supported by ruby-dbus yet)
org.opensuse.DInstaller.Questions1 (simplified version of ObjectManager)
# Attribute containing path of all questions. A signal would be emitted when the list changes.
#
# Output example:
# [
# "/org/opensuse/DInstaller/Questions1/1",
# "/org/opensuse/DInstaller/Questions1/2,
# "/org/opensuse/DInstaller/Questions1/3,
# "/org/opensuse/DInstaller/Questions1/4"
# ]
All => as
# Method for getting list of questions pending to answer
GetPending () => as
D-Installer service exports a new question object everytime a new question is asked. As commented above, different questions can provide and require different information. To overcome this, each question object would implement the set of interfaces it needs.
A question implements at least the org.opensuse.DInstaller.Question1 interface:
/org/opensuse/DInstaller/Questions1/ID
org.freedesktop.DBus.Properties
org.opensuse.DInstaller.Question1
# Method for getting the question text
#
# Output example: "Do you want to continue?"
Text () => s
# Method to know if the question was answered
Answered () => b
If a question is expected to be answered, then the D-Bus object implements the org.opensuse.DInstaller.Question.Answer interface:
/org/opensuse/DInstaller/Questions1/ID
org.freedesktop.DBus.Properties
org.opensuse.DInstaller.Question1
org.opensuse.DInstaller.Question.Answer1
# Answer provided for this question
#
# The returned type depends on the the type of answer
Value => v
To provide an answer, the D-Bus object has to implement the proper answer interface:
/org/opensuse/DInstaller/Questions1/ID
org.freedesktop.DBus.Properties
org.opensuse.DInstaller.Question1
org.opensuse.DInstaller.Question.Answer1
org.opensuse.DInstaller.Question.Answer.Boolean1
Set (b)
org.opensuse.DInstaller.Question.Answer.String1
Set (s)
For example, a question that expects to be answered with an string would implement the following interfaces:
/org/opensuse/DInstaller/Questions1/ID
org.freedesktop.DBus.Properties
org.opensuse.DInstaller.Question1
org.opensuse.DInstaller.Question.Answer1
org.opensuse.DInstaller.Question.Answer.String1
Sometimes, the client needs to obtain information from a question. For example, when a LUKS password is requested, the client needs to know the device and maybe other properties like size, label, etc. For these cases, the D-Bus object could implement extra interfaces to provide such information. For example, a question asking for a LUKS password would implement these interfaces:
/org/opensuse/DInstaller/Questions1/ID
org.freedesktop.DBus.Properties
org.opensuse.DInstaller.Question1
org.opensuse.DInstaller.Question.Answer1
org.opensuse.DInstaller.Question.Answer.String1
org.opensuse.DInstaller.Question.LuksPassword1
Device => s
UUID => s
Label => s
Size => u
Similarly, custom interfaces can be implemented when a question requires to be answered in a specific way. For example, let's say that the client has to answer to the LUKS password question by providing a struct with two values: whether the device should be activated and the password:
/org/opensuse/DInstaller/Questions1/ID
org.freedesktop.DBus.Properties
org.opensuse.DInstaller.Question1
org.opensuse.DInstaller.Question.Answer1
org.opensuse.DInstaller.Question.LuksPassword1
org.opensuse.DInstaller.Question.Answer.LuksPassword1
Set (b, s)