YaST/DBus Service/Description/Factory
From openSUSE
| Revision as of 08:48, 22 April 2009 Mvidner (Talk | contribs) proper "YaST" capitalization � Previous diff |
Revision as of 08:56, 22 April 2009 Mvidner (Talk | contribs) proofreading Next diff → |
||
| Line 18: | Line 18: | ||
| * Access to YaST functions from every programming language or application which supports communication via DBus. That means the functionality is even available in shell (via dbus-send or qdbus tool) or in a DBus browser (e.g. kdbus, qdbusviewer...) | * Access to YaST functions from every programming language or application which supports communication via DBus. That means the functionality is even available in shell (via dbus-send or qdbus tool) or in a DBus browser (e.g. kdbus, qdbusviewer...) | ||
| - | * PolicyKit integration - The service uses PolicyKit for checking user authorizations, this enables access to priviledged operations to non-root users. This is very important when using YaST as a backend in desktop oriented applications. | + | * PolicyKit integration - The service uses PolicyKit for checking user authorizations, this enables access to privileged operations to non-root users. This is very important when using YaST as a backend in desktop oriented applications. |
| This service moves the DBus support to higher level - the original SCR DBus service exports only low-level YaST name space (SCR::). This service can export every YaST name space written in any supported programming language (YCP, Perl, Python, C++, Ruby). | This service moves the DBus support to higher level - the original SCR DBus service exports only low-level YaST name space (SCR::). This service can export every YaST name space written in any supported programming language (YCP, Perl, Python, C++, Ruby). | ||
| Line 36: | Line 36: | ||
| YaST name spaces are available as objects with path '''/org/opensuse/YaST/modules/<namespace_name>'''. Every exported object (YaST name space) has these interfaces: | YaST name spaces are available as objects with path '''/org/opensuse/YaST/modules/<namespace_name>'''. Every exported object (YaST name space) has these interfaces: | ||
| - | * '''org.opensuse.YaST.Values''' - This is the standard YaST DBus interface, it uses DBus data types directly. This interface tries to autoconvert the passed DBus values to YCP values if possible. E.g. string is converted to YCP symbol if the function expects a symbol parameter. | + | * '''org.opensuse.YaST.Values''' - This is the standard YaST DBus interface, it uses DBus data types directly. This interface tries to autoconvert the passed DBus values to YCP values if possible. E.g. a string is converted to a YCP symbol if the function expects a symbol parameter. |
| * '''org.opensuse.YaST.YCPValues''' - This interface accepts marshaled YCP values (see [[#Marshalling YCP Values]] section). Any YCP value can be passed via this interface. This interface is rather a workaround for using YaST functions which would not be accessible otherwise or to pass special values. This interface should not be needed normally, all officially supported functions must be accessible using '''org.opensuse.YaST.Values''' interface. | * '''org.opensuse.YaST.YCPValues''' - This interface accepts marshaled YCP values (see [[#Marshalling YCP Values]] section). Any YCP value can be passed via this interface. This interface is rather a workaround for using YaST functions which would not be accessible otherwise or to pass special values. This interface should not be needed normally, all officially supported functions must be accessible using '''org.opensuse.YaST.Values''' interface. | ||
| Line 49: | Line 49: | ||
| * '''Import(string name_space)''' - explicitly import an YaST name space. Because name spaces are imported automatically this method should not be normally needed. It is intended for graphical DBus browsers which can send only method requested to known objects. | * '''Import(string name_space)''' - explicitly import an YaST name space. Because name spaces are imported automatically this method should not be normally needed. It is intended for graphical DBus browsers which can send only method requested to known objects. | ||
| - | * '''Lock()''' and '''Unlock()''' - should be used for accessing state full functions from a long running client. These functions prevents from shutting down the service. The service is kept running when there is at least one running client which has called Lock() method. | + | * '''Lock()''' and '''Unlock()''' - should be used for accessing stateful functions from a long running client. These functions prevent the service from shutting down after a timeout. The service is kept running when there is at least one running client which has called Lock() method. |
| Line 56: | Line 56: | ||
| There is a special interface '''org.opensuse.yast.YCPValues''' for passing types or values which are not suppoerted directly by DBus. This interface uses a special structure for encoding any YCP value. The structure (let's call it '''bsv structure''') has three components: | There is a special interface '''org.opensuse.yast.YCPValues''' for passing types or values which are not suppoerted directly by DBus. This interface uses a special structure for encoding any YCP value. The structure (let's call it '''bsv structure''') has three components: | ||
| - | * '''boolean''' - nil flag, if it is <tt>true</tt> the structure represents ''nil'' value, all other flags are unused and have undefined state (there are just some dummy values to keep the same signature for all YCP values) | + | * '''boolean''' - nil flag, if it is <tt>true</tt> the structure represents the ''nil'' value, all other fields are unused and have an undefined state (there are just some dummy values to keep the same signature for all YCP values) |
| * '''string''' - name of the YCP type, e.g. ''string'', ''integer'', ''list''... | * '''string''' - name of the YCP type, e.g. ''string'', ''integer'', ''list''... | ||
| * '''variant''' - the value transferred using same or similar DBus type, e.g. YCP symbol is transferred as DBus string, YCP list is transferred as DBus array... YCP containers are recursively encoded, the variant can be another '''bsv structure'''. | * '''variant''' - the value transferred using same or similar DBus type, e.g. YCP symbol is transferred as DBus string, YCP list is transferred as DBus array... YCP containers are recursively encoded, the variant can be another '''bsv structure'''. | ||
Revision as of 08:56, 22 April 2009
The YaST DBus Namespace Service
| Version: Factory
| This article is about the latest development version of the YaST DBus service which is part of the Factory distribution. The current Factory version is 2.18.x, for other versions see YaST/DBus_Service/Versions |
| This document describes the current state of the development version which is work in progress. The service may be changed anytime, this is unstable version. |
Contents |
The Purpose of the Service
YaST DBus name space service enables to use the YaST libraries and functionality from other applications via DBus interface.
The advantages are
- Access to YaST functions from every programming language or application which supports communication via DBus. That means the functionality is even available in shell (via dbus-send or qdbus tool) or in a DBus browser (e.g. kdbus, qdbusviewer...)
- PolicyKit integration - The service uses PolicyKit for checking user authorizations, this enables access to privileged operations to non-root users. This is very important when using YaST as a backend in desktop oriented applications.
This service moves the DBus support to higher level - the original SCR DBus service exports only low-level YaST name space (SCR::). This service can export every YaST name space written in any supported programming language (YCP, Perl, Python, C++, Ruby).
How to Install the Service
The DBus service is available in yast2-core-2.18.6 and newer. It is currently available in FACTORY, it will be included in openSUSE 11.2. The service is part of the basic YaST component, the service does not need any extra configuration, it is enabled by default. It uses PolicyKit for access control, unauthorized use is not possible.
How to Use the Service
The service has DBus name org.opensuse.YaST.modules and it is available on the system bus.
YaST name spaces are available as objects with path /org/opensuse/YaST/modules/<namespace_name>. Every exported object (YaST name space) has these interfaces:
- org.opensuse.YaST.Values - This is the standard YaST DBus interface, it uses DBus data types directly. This interface tries to autoconvert the passed DBus values to YCP values if possible. E.g. a string is converted to a YCP symbol if the function expects a symbol parameter.
- org.opensuse.YaST.YCPValues - This interface accepts marshaled YCP values (see #Marshalling YCP Values section). Any YCP value can be passed via this interface. This interface is rather a workaround for using YaST functions which would not be accessible otherwise or to pass special values. This interface should not be needed normally, all officially supported functions must be accessible using org.opensuse.YaST.Values interface.
Each object exports only the methods of the underlying name space, the global variables are not exported. If they need to be accessible they should be wrapped in get()/set() functions. See #Exporting Global Variables from Name Spaces section.
The Module Manager Interface
The service provides a special interface org.opensuse.YaST.modules.ModuleManager at object /org/opensuse/YaST/modules. This is the interface to access the YaST DBus service itself.
Currently there are available these methods:
- Import(string name_space) - explicitly import an YaST name space. Because name spaces are imported automatically this method should not be normally needed. It is intended for graphical DBus browsers which can send only method requested to known objects.
- Lock() and Unlock() - should be used for accessing stateful functions from a long running client. These functions prevent the service from shutting down after a timeout. The service is kept running when there is at least one running client which has called Lock() method.
Marshalling YCP Values
There is a special interface org.opensuse.yast.YCPValues for passing types or values which are not suppoerted directly by DBus. This interface uses a special structure for encoding any YCP value. The structure (let's call it bsv structure) has three components:
- boolean - nil flag, if it is true the structure represents the nil value, all other fields are unused and have an undefined state (there are just some dummy values to keep the same signature for all YCP values)
- string - name of the YCP type, e.g. string, integer, list...
- variant - the value transferred using same or similar DBus type, e.g. YCP symbol is transferred as DBus string, YCP list is transferred as DBus array... YCP containers are recursively encoded, the variant can be another bsv structure.
This encoding enables transferring values like [1, "2", `symbol, nil] which cannot be transferred via direct DBus values.
Access control via PolicyKit
PolicyKit Check
Each incoming method request is check by PolicyKit and the requested method is executed only when PolicyKit returns YES. If PolicyKit returns different result the service returns DBus exception org.freedesktop.PolicyKit.Error.NotAuthorized followed by the PolicyKit result (see http://hal.freedesktop.org/docs/PolicyKit/polkit-polkit-simple.html#polkit-dbus-error-generate).
The client should authorize itself if needed and call the method again (see http://hal.freedesktop.org/docs/PolicyKit/model-theory-of-operation.html for details).
PolicyKit Action ID
The service checks whether the requester is allowed to call the YaST function. YaST uses prefix org.opensuse.yast.modules, name space name and function name to create the PolicyKit action ID.
PolicyKit allows only lower case letters and numbers in action ID - name space and function name are therefore lower cased.
Example:
PolicyKit action ID for Mode::normal() call is org.opensuse.yast.modules.mode.normal.
Obtaining a PolicyKit Authorization
A PolicyKit authorization can be obtained
- implicitly from a *.policy configuration file
- the user authenticates as the administrator (root) user
polkit-auth --obtain org.opensuse.yast.modules.yapi-samba.getservicestatus
- the adminitrator grants the autorization to the user
polkit-auth --user lslezak --grant org.opensuse.yast.modules.yapi-samba.getservicestatus
Service start and shutdown
The service is started automatically by the DBus daemon when it is not running.
The service is automatically shut down when at least 2 minutes has elapsed since the last request. This prevents from consuming memory by an unused service.
The service does not import any name space at start up by default. The name spaces are dynamically imported when needed.
Examples
Here are examples how to invoke YaPI::Samba::GetServiceStatus() function. This example requires org.opensuse.yast.modules.yapi-samba.getservicestatus PolicyKit authorization, see #Obtaining a PolicyKit Authorization.
Python Example
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 Examples
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
How to export YaST functionality
This section is important mainly for YaST developers and users who are interested in implementation details.
The Dbus service can export already existing functions. But to avoid some problems the exported functions should follow these rules because of completely different usage:
- The exported functions should be state less - no separate Read(), Edit() and Write() functions, but a single function for each exported functionality - e.g. DeleteUser() which calls all needed functions.
The reason is that the name space exported via DBus service is just one instance (a singleton) shared by all clients. The current state may contain internal data from previous calls from another clients. Stateless functions ensure that the same pre-conditions are used in all cases.
Running multiple clients in parallel can lead to race conditions, data loss, miss-configuration or other severe problems.
- Input and output parameters should have just basic data types like string, integer... YaST has more data types than DBus (e.g. symbol) or it has a special value which cannot be transferred by DBus directly (nil) or the transferred value can be is useless out side the service (like a function pointer).
- Use single type containers, when using list or map types DBus requires the same type for all values, e.g. [1, 2, 3] can be converted to a DBus array while [1, "2", `symbol] cannot.
- DBus does not support some special characters in object and method names, especially double-colon (:) is problematic for YaST. The DBus service automatically replaces invalid characters to underscore (_). Example: YaPI::Samba module is available at YaPI__Samba DBus object.
- The exported function names should not differ only in upper or lower case letters. The reason is that e.g. Function() and funtion() will have the same PolicyKit action ID (see PolicyKit Action ID section) and the access right would be shared between them which is usually not intended and it is error prone.
- Each exported function (respective its action ID) must be defined in a PolicyKit configuration file. See PolicyKit documentation http://hal.freedesktop.org/docs/PolicyKit/polkit-conf.html#conf-declaring-actions . Defining a PolicyKit action ID means that the function is officially supported over the DBus service.
It is a good idea to validate the PolicyKit config during make check.
- The exported name space neither its imported name spaces should not define constructors. The reason is security - because of the auto-import functionality the constructor could potentially execute an action which is not allowed to the calling client. Another problem is that the constructor is called just once (at first import) and using constructor leads to a state full API which should be avoided.
- The exported function must not use any UI:: functions, the service cannot open any dialog because it has no terminal or X session and all UI:: calls will very likely fail. The service should be used as a backend providing some functionality to an application, UI should not be used in the service anyway.
Debugging
Starting the Service Manually
The service can be started explicitly using /usr/lib/YaST2/bin/yast_modules_dbus_server executable. The service may be owned only by root user, so it can be started only from a root shell. Use --disable-timer option to disable automatic shutdown feature.
The optional arguments can be used to pre-load requested name spaces.
Example:
/usr/lib/YaST2/bin/yast_modules_dbus_server --disable-timer Label Pkg
Logging
The service uses standard YaST log file /var/log/YaST2/y2log. The service log is marked with [y2dbus] component string on each line.
It is possible to enable debug logging with variable Y2LOG=1.
Reloading the DBus Configuration
During the development the service rarely refused to start claiming that it is already running. Reloading the DBus configuration solved the problem. Use ReloadConfig() method of the DBus daemon if it happens:
dbus-send --system --dest=org.freedesktop.DBus --print-reply --type=method_call / org.freedesktop.DBus.ReloadConfig
Future Enhancements
SCR Service Integration
This new DBus service should replace the previous YaST SCR service. Currently the SCR name space is not exported because it must be handled specially.
The reason is that SCR:: provides overloaded methods and every call must be resolved at runtime. Another reason is that the interpreter allows to start multiple SCR instances so it is handled in a different way than all other name spaces.
Exporting Global Variables from Name Spaces
Global variables could be exported via DBus object properties if really needed. Currently they have to be wrapped in get()/set() methods if they need to be exported.

