The Linux Foundation

 
LSB Package API

From The Linux Foundation

(Difference between revisions)
m
 
(9 intermediate revisions by one user not shown)
Line 1: Line 1:
This document describes a proposed D-BUS API for LSB application installers, the ''LSB Package API''. Note that the design is a work in progress and some desirable features are still missing, as also stated in the [[#To Be Done]] section. An initial implementation is attached to this page.
+
The API formally proposed here has been renamed to ''[[Burgdorf Packaging API|"Burgdorf Packaging API"]]'' to emphasize it's unofficial state. Follow the link for the specification.
 
+
== Introduction ==
+
 
+
The LSB Package API is an interface that allows ISVs to install LSB-compliant applications in such a way that they are integrated into the distribution's packaging system. This enables users to manage third-party software packages as easily as packages installed from the distributor, and frees ISVs from the need to provide packages for different packaging systems in order to achieve integration with the distribution.
+
 
+
The idea and basic design of the LSB Package API comes from the [[Berlin Packaging API|"Berlin Packaging API"]] proposal. In fact, the LSB Package API can be considered a further development and implementation of the Berlin API. It is very recommended to read [https://lists.linux-foundation.org/pipermail/packaging/2007-January/000376.html Ian Murdock's summary] of what the Berlin API is about before reading on.
+
 
+
== The Interace ==
+
 
+
The distribution's packaging system is interfaced through a D-Bus service - the '''LSB Package Service''' - registered as <tt>org.linuxbase.Packages</tt>. This service offers the following two methods:
+
 
+
* '''<code>RegisterPackage(in STRING manifest_path, out UINT32 pkgid)</code>'''
+
* '''<code>ClosePackage(in UINT32 pkgid)</code>'''
+
 
+
To install an application, an ISV-provided installer first calls the <code>RegisterPackage()</code> method. As argument, it passes an absolute path to a ''package manifest file'', which describes a software package and lists the files that need to be installed (see the [[#Package Manifest Files]] section). If the package manifest is successfully validated and the described package does not conflict with the base system (e.g. because a file that would need to be installed already exists on the file system), the LSB Package Service then adds the package to the package system's database. Finally, it creates the directories needed by the package and places empty "stub files" at the paths where the package's files need to be installed according to the manifest.
+
 
+
While the created directories are owned by the <tt>root</tt> user and group, the stub files' ownership is set to the UID of the calling process. Thus, the installer can overwrite these with the "real" files of the installed application and adjust the files' permissions, without a need for root privileges.
+
 
+
Once the installer is finished, it calls the ClosePackage() method, passing the package identifier returned by RegisterPackage() as argument. This causes the package service to change the former stub files' owner to <tt>root</tt>, thus protecting the installed application's files from being modified by unprivileged programs. Additionally, the service calls out to the packaging system again, which may gather information about the installed files to add it to the database; it might for instance determine the size of the installed package. With the return of this method, the installation is complete.
+
 
+
The methods of the LSB Package API are protected by a [http://people.freedesktop.org/~david/polkit-spec.html PolicyKit] policy (<tt>org.linuxbase.packages.install</tt>). This enables system adminstrators to require authentication prior to installing an application, or to restrict the set of users which are allowed to install third-party software.
+
 
+
The advantages of this installation mechanism (as opposed to classical installation methods on Linux) are:
+
 
+
* '''Portability.''' As the distribution's packaging system does not need to be interfaced directly, an LSB-compliant application distributed using the LSB Package API can be installed on any system providing the LSB package service. Ideally, this service would be part of the LSB itself, which would mean any LSB-compliant distribution would provide it.
+
 
+
* '''System Integrity'''. The package service can verify any data passed to it before the installation is started. That way, damage to the base system due to the installation of third-party software can be prevented.
+
 
+
* '''Security.''' The indirect interfacing of the packaging system and the installation on top of stub files created by the package service allows most installers to run entirely without root privileges. This greatly reduces the danger of malicious installers.
+
 
+
== Package Manifest Files ==
+
 
+
A package manifest file describes the software package to be installed. It contains:
+
 
+
* Metadata about the package, like its name, provider, version etc.
+
* Information about the files which need to be installed.
+
 
+
The package manifest file's format is an XML dialect.
+
 
+
=== Elements and attributes ===
+
 
+
; <pre><package
+
;  name='...'
+
;  provider='...'
+
;  version='...'
+
;  arch='...'
+
;  displayed-name='...'
+
;  displayed-provider='...'>
+
;
+
; <displayed-name>?
+
; <displayed-provider>?
+
; <description>?
+
; <file>*
+
;
+
;</package></pre>
+
: The top-level element of any package manifest file. The attributes have the following meaning:
+
: * '''<tt>name</tt>''': The name of the package to install. May only consist of the characters A-Z, a-z, 0-9 and dashes. (The name must not start with a dash.)
+
: * '''<tt>provider</tt>''': A LANANA-registered provider name, or a fully qualified domain name registered by the provider (e.g. "www.foobar.com").
+
: * '''<tt>version</tt>''': The package's version. ''(NOTE: There is no specified version format yet.)''
+
: * '''<tt>arch</tt>''': The system architecture for which the package was built. ''(NOTE: There are no allowed values defined yet.)''
+
 
+
; <pre><displayed-name>...</displayed-name></pre>
+
: Specifies the name of the package like it should be represented to the user. If the manifest file lacks an <tt><displayed-name></tt> element, the displayed name equals the package name specified through the <tt><package></tt> tag's <tt>name</tt> attribute.
+
 
+
; <pre><displayed-provider>...</displayed-provider></pre>
+
: Specifies the name of the provider like it should be represented to the user. If the manifest file lacks an <tt><displayed-name></tt> element, the displayed name equals the provider name through the <tt><package></tt> tag's <tt>provider</tt> attribute.
+
 
+
; <pre><description>...</description></pre>
+
: Defines a description for the package.
+
 
+
; <pre><file type='...' location='...'>...</file></pre>
+
: Defines a package file. Attributes are:
+
: * '''<tt>type</tt>''' ''(optional)'': The type of the file. Either "regular" for a regular file or "dir" for a directory. Omitting this attribute means the file is a regular one.
+
: * '''<tt>location</tt>''' ''(optional)'': The location where the file should be placed. See [[#Package File Locations]] for more information. Omitting this attribute means the file is placed into the "data" location.
+
:
+
: The content of a <tt><file></tt> tag must be the package file's destination path relative to the file's location. Although being interpreted as relative, it may start with a leading '/' character. To specify a file location itself as a package file, use "/" (without quotes) as path. The <tt>..</tt> path component is not allowed.
+
:
+
: Only files and directories that don't exist on the installing system may be specified. If the LSB Package Service encounters a package file or directory which already exists on the file system, package registration will be canceled.
+
:
+
: '''''Note''': If a directory is not explicitly mentioned as a package file in the package manifest, it is NOT created by the LSB Package Service. Thus, every directory which cannot be expected to exist on the install target system must be explicitly mentioned as a <tt><file></tt> element.''
+
 
+
=== Package File Locations ===
+
 
+
In order to reduce interference with the base system, the package files' destiation paths are restricted to a set of safe ''package file locations''. These are listed below.
+
 
+
{| border="1" cellspacing="0" cellpadding="5"
+
! <tt>location</tt> Attribute Value
+
! Purpose
+
! Path
+
|-
+
| <tt>data</tt>
+
| application data files (private libraries, images, ...)
+
| /opt/''provider''-''name''
+
|-
+
| <tt>etc</tt>
+
| configuration files
+
| /etc/opt/''provider''-''name''/
+
|-
+
| <tt>var</tt>
+
| variable data
+
| /var/opt/''provider''-''name''/
+
|-
+
| <tt>bin</tt>
+
| "public" application binaries (part of $PATH)
+
| /opt/bin/
+
|-
+
| <tt>xdg-applications</tt>
+
| .desktop files (for menu entries)
+
| /usr/local/share/applications/
+
|-
+
|}
+
 
+
''provider'' and ''name'' are substituted by the package's provider and package name, respectively.
+
 
+
'''''NOTE:''' The locations listed above are not meant to be the only ones, they are just the ones currently implemented; see [[#To Be Done]].''
+
+
=== A Simple Example ===
+
 
+
The example below is the package manifest file of ''LSB Hello World'', a very simple application I have written to demonstrate how software deployment using the LSB Package API could look like. The program - including a simple installer based on [[#liblsb_package]] - is attached to this document.
+
 
+
<pre><?xml version="1.0" encoding="UTF-8"?>
+
 
+
<package name="helloworld" provider="lsb" version="0.1" arch="i386" >
+
 
+
  <displayed-name>LSB Hello World</displayed-name>
+
  <displayed-provider>Linux Standard Base</displayed-provider>
+
  <description>This program demonstrates how applications can be deployed using the LSB Package API.</description>
+
 
+
  <file location="bin">/lsb-helloworld</file>
+
 
+
  <file location="data" type="dir">/</file>
+
  <file location="data" type="dir">/share</file>
+
  <file location="data">/share/icon.png</file>
+
  <file location="data">/share/image.png</file>
+
 
+
  <file location="etc" type="dir">/</file>
+
  <file location="etc">/message</file>
+
 
+
  <file location="xdg-applications">/lsb-helloworld.desktop</file>
+
</package></pre>
+
 
+
== Implementation Details ==
+
 
+
The prototype implementation consists of three parts:
+
 
+
* The '''<tt>[[#lsb_packaged]]</tt>''' daemon, which provides the LSB Package Service.
+
* A set of '''[[#lsb_packaged backends]]''' for different packaging systems. Currently implemented are simple backends for RPM and the Debian packaging system (dpkg).
+
* The '''<tt>[[#liblsb_package]]</tt>''' library, a convenience interface for implementing installers and <tt>lsb_packaged</tt> backends.
+
 
+
=== <tt>lsb_packaged</tt> ===
+
 
+
<tt>lsb_packaged</tt> is a daemon that implements the LSB Package Service. The main duty of the actual daemon is to receive incoming D-Bus messages on the system bus and to verify them, including parsing and validating the manifest file, checking for file conflicts, and ensuring correct PolicyKit authentication. The actual package registration and stub file creation is done by the backend, which is dynamically loaded and called by the daemon using <code>dlopen()</code>. Which backend is loaded is defined in the <tt>/etc/lsb_package/backend</tt> configuration file. It must contain nothing but the path to the object file to be loaded, e.g. <tt>/usr/share/liblsb_package-rpm.so</tt>.
+
 
+
The <tt>lsb_packaged</tt> daemon is not persistent. Instead, [http://www.freedesktop.org/wiki/IntroductionToDBus#head-900e59c577277516e6d8d8a3afbb30beff4794bd D-Bus system activation] to start the daemon if a method of the LSB Package API is called. After all requests have been processed, <tt>lsb_packaged</tt> will exit again. This limits the runtime of <tt>lsb_packaged</tt> to the moments it is actually needed.
+
 
+
<tt>lsb_packaged</tt> depends on the following libraries:
+
 
+
* '''dbus-glib (>= 0.73)''' to register the <tt>org.linuxbase.Packages</tt> service and listen for incoming messages.
+
* '''libexpat''' to parse package manifest files.
+
* '''PolicyKit (>= 0.7)''' to enforce the <tt>org.linuxbase.packages.install</tt> policy.
+
* '''liblsb_package'''.
+
 
+
=== <tt>liblsb_package</tt> ===
+
 
+
The <tt>liblsb_package</tt> library provides a convenient programming interface to access the LSB Package API from a program written in C or C++. Additionally, it defines a set of functions and structs for the implementation of <tt>lsb_packaged</tt> backends. The former purpose is covered by the <tt><lsb_package.h></tt> header file, while the declarations for the latter reside in <tt><lsb_package-backend.h></tt>.
+
 
+
'''<tt><lsb_package.h></tt>''' declares the following structures and functions:
+
 
+
; <pre>typedef struct
+
;{
+
;  int code;
+
;  char *msg;
+
;}
+
;regerr_t;</pre>
+
: Stores information about an error which occured during package registration. Every error has a code which indicates its type (<tt>code</tt>) and an error message (<tt>msg</tt>) with more details about the error. Valid error codes are:
+
:{| border="1" cellspacing="0" cellpadding="5"
+
! Name
+
! Description
+
|-
+
| <tt>RERR_MANIFEST</tt>
+
| invalid package manifest
+
|-
+
| <tt>RERR_NAME_CONFLICT</tt>
+
| package name conflict (e.g. package with the same name and provider already installed)
+
|-
+
| <tt>RERR_FILE_CONFLICT</tt>
+
| package file conflict (e.g. a specified file already exists)
+
|-
+
| <tt>RERR_ACCESS</tt>
+
| access to LSB Package API denied (e.g. missing authentication)
+
|-
+
| <tt>RERR_INTERNAL</tt>
+
| internal error (e.g. D-Bus error, IO, memory, ...)
+
|}
+
 
+
; <pre>typedef unisgned int pkgid_t;</pre>
+
: A package identifier as returned by <tt>register_package()</tt>.
+
 
+
; <pre>void free_regerr(regerr_t *err);</pre>
+
: Frees a <tt>regerr_t</tt> struct.
+
 
+
; <pre>int obtain_install_auth(uint32_t xid);</pre>
+
: Tries to obtain the rights to install third-party software. If the program does not have these rights yet, the user is asked for authentication using PolicyKit. If PolicyKit creates an authentication dialog window and the <tt>xid</tt> argument is not 0, it defines the X window ID of the  window for which the dialog should be transient. If install rights were successfully obtained, 0 is returned, otherwise -1.
+
: This function should be called before using <tt>register_package()</tt>.
+
 
+
; <pre>pkgid_t register_package(const char *manifest_path, regerr_t **err);</pre>
+
: Calls the LSB Package API's <tt>RegisterPackage()</tt> method with the passed package manifest file path as argument. If an error occurs during package registration, <code>*err</code> is changed to point to a <tt>regerr_t</tt> struct describing the error, and 0 is returned. Otherwise, <tt>register_package()</tt> returns an identifier for the registered package, which should be passed to <tt>close_package()</tt> when the installation is complete.
+
 
+
; <pre>void close_package(pkid_t pkgid);</pre>
+
: Calls the LSB Package API's <tt>ClosePackage()</tt> method with the passed package identifier as argument. Usually, the return value of a former <tt>register_package()</tt> call is passed.
+
 
+
'''<tt><lsb_package-backend.h></tt>''' declares the following:
+
 
+
; <pre>typedef enum
+
;{
+
;  FLOC_DATA,
+
;  FLOC_ETC,
+
;  FLOC_VAR,
+
;  FLOC_BIN,
+
;  FLOC_XDG_APPLICATIONS
+
;}
+
;pfloc_t;</pre>
+
: Represents a package file location (see [[#Package File Locations]]). Despite the leading <tt>FLOC_</tt>, the name of each of the enum constants equals the name of the represented file location in upper case, with dashes replaced by underscores. For instance, <tt>FLOC_XDG_APPLICATIONS</tt> stands for the "xdg-applications" location.
+
 
+
; <pre>typedef enum
+
;{
+
;  FTYPE_REGULAR,
+
;  FTYPE_DIR,
+
;  FTYPE_SYMLINK
+
;}
+
;pftype_t;</pre>
+
: Represents a package file type. The <tt>FTYPE_REGULAR</tt> constant stands for a regular file, while <tt>FTYPE_DIR</tt> is synonymous for the directory file type. Note that <tt>FTYPE_SYMLINK</tt> is not yet supported properly.
+
 
+
; <pre>typedef struct
+
;{
+
;  char *path;
+
;  pftype_t type;
+
;  pfloc_t location;
+
;}
+
;pfile_t;</pre>
+
: Represents a package file. The struct's members represent the values of the equally named <tt><file></tt> tag's attributes.
+
 
+
; <pre>typedef struct
+
;{
+
;  pfilelist_t *next;
+
;  pfile_t *file;
+
;}
+
;pfilelist_t;</pre>
+
: A singly-linked package file list structure.
+
 
+
; <pre>typedef struct
+
;{
+
;  char *pkgname;
+
;  char *pkgprovider;
+
;  char *pkgversion;
+
;  char *pkgarch;
+
;  pfilelist_t *pkgfiles;
+
;  char *pkgdescription;
+
;  char *pkgdisplayedname;
+
;  char *pkgdisplayedprovider;
+
;}
+
; pmanifest_t;</pre>
+
: Represents a complete package manifest. <tt>pkgname</tt>, <tt>pkgprovider</tt>, <tt>pkgversion</tt>, and <tt>pkgarch</tt> store the values of the <tt><package></tt> tag's <tt>name</tt>, <tt>provider</tt>, <tt>version</tt>, and <tt>arch</tt> attributes, respectively. <tt>pkgfiles</tt> is a list of the files soecified as belonging to the package. <tt>pkgdescription</tt>, <tt>pkgdisplayedname</tt>, and <tt>pkgdisplayedprovider</tt> store the contents of the manifest's <tt><description></tt>, <tt><displayed-name></tt> and <tt><displayed-provider></tt> elements.
+
 
+
; <pre>regerr_t *new_regerr(int errcode, const char *fmt, ...);</pre>
+
: Creates a new <tt>regerr_t</tt> struct with the specified error code and message. The message is created from the passed format and format arguments using <tt>printf()</tt>-style formatting. The returned struct must be freed with <tt>free_regerr()</tt>.
+
 
+
; <pre>char *full_pfile_path(pfile_t *file, pmanifest_t *mf);</pre>
+
: Returns the canonical absolute path of a package file.
+
 
+
; <pre>void mkpfile(pfile_t *file, pmanifest_t *mf, uid_t uid, regerr_t **err);</pre>
+
: Creates the passed package file on the file system. If it is not a directory, the created file's ownership is set to the passed UID.
+
 
+
<tt>liblsb_package</tt> depends on the following libraries:
+
 
+
* '''dbus-glib (>= 0.73)''' to call the LSB Package API methods.
+
* '''PolicyKit (>= 0.7)''' for <tt>obtain_install_rights()</tt>.
+
+
=== <tt>lsb_packaged</tt> Backends ===
+
 
+
An <tt>lsb_packaged</tt> backend implements the LSB Package API for a particular packaging system. A backend is simply a shared object file which exports the following interfaces:
+
 
+
* <pre>void _register_package(pmanifest_t *mf, uid_t uid, regerr_t **err);</pre>
+
*: In this function, the backend shall add the package described by the passed manifest to its database. Additionally, it shall create all package directories and stub files using <tt>mkpfile()</tt>. If the package cannot be registered or an error occurs, <tt>*err</tt> shall be set to point to a <tt>regerr_t</tt> struct describing the error.
+
 
+
* <pre>void _close_package(pmanifest_t *mf);</pre>
+
*: Called by the package service after a package was closed with the <tt>ClosePackage()</tt> method. In this function, the backend may retrieve information about the installed package files, e.g. to determine the package's size. However, the success of an installation shall NOT depend on the success of <tt>_close_package()</tt>; instead, a successful run of <tt>_register_package()</tt> shall suffice for a successful package installation.
+
 
+
Currently, there are simple backends for both RPM and dpkg. As the latter does not export a programming interface, the backend for dpkg is implemented as an addition to the dpkg codebase itself (to be turned on by passing <tt>--with-lsb</tt> to <tt>./configure</tt>). The RPM backend uses librpm instead and can thus be deployed separately from RPM itself.
+
 
+
== To Be Done ==
+
 
+
* The values allowed in the <tt>version</tt> and <tt>arch</tt> attributes are yet completely unspecified. For the former, this is needed to guarantee a reliable version comparision scheme (there are differences about how versions are sorted across packaging system IIRC), and for the latter because it is completely useless otherwise.
+
 
+
* A simple dependency system should be added. At least dependencies to particular versions of the LSB should be specifiable. Dependencies between packages of the same provider could also be supported. Contrary to the [[Berlin  Packagaing API]] proposal, I think dependencies should not be expressed as interface function but in the package manifest file, as (a) they can be verified by the package service then and (b) they can be added to the package system's database (so that the user is warned if he/she attempts to remove a dependency).
+
 
+
* Internationalization of the package metadata was not taken into account yet. It should be possible to define <tt><description></tt>, <tt><displayed-name></tt> and <tt><displayed-provider></tt> for multiple languages. A possible solution would be to have multiple of these elements with different <tt>xml:lang</tt> attribute values like with the MIME type files, e.g. <code><displayed-name xml:lang='de'>LSB Hallo Welt</displayed-name></code>.
+
 
+
* More sufficiently safe [[#package file locations]] should be added when needed. There may also be a need to provide a "system" location that maps to the root directory (/) as some low-level applications might need this; however, attempting to install to such a location should at least cause the LSB Package Service to show a BIG FAT WARNING to the user to inform him/her about possible interference with the base system.
+
** Generally, it is preferable to have as much installed into /opt as possible. For instance, it would be great if the [http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html XDG Base Directory Specification] would be extended to include /opt/share in <tt>$XDG_DATA_DIRS</tt>. We could also install .desktop files to /opt/applications then, for instance.
+
 
+
* Uninstalling and updating an application is not handled yet in the API. While uninstalling is not that critical as this can be done with the package manager, a way of updating is desperately needed and would have to be added to this API.
+
 
+
== Download ==
+
 
+
'''''Note:''' Make sure you read the descriptions on the linked file pages, they contain important information about getting everything working!''
+
 
+
* [[Image:lsb_package.tar.gz|lsb_packaged and liblsb_package]]
+
* [[Image:Lsb_package_rpm.tar.gz|RPM backend]]
+
* [[Image:dpkg-1.15.0_plus_lsb_backend.tar.gz|dpkg backend]]
+
== Discussion ==
+
 
+
Comments and criticism are very welcome. Please add any commentary to the [[Talk:LSB Package API|Discussion page]].
+

Latest revision as of 15:13, 4 July 2008

The API formally proposed here has been renamed to "Burgdorf Packaging API" to emphasize it's unofficial state. Follow the link for the specification.


[Article] [Discussion] [View source] [History]