===============================
 Service-Level Settings Format
===============================
:Author: Jacob Peddicord <jpeddicord@ubuntu.com>
:Description: SLS format 0.8 introduction and specification

.. contents:: :depth: 3

Introduction
============

Are you the maintainer (or interested developer) of a system service? Do you want to make certain aspects of your service easily configurable by system administrators and users, without the need to edit config files? You've come to the right place.

Service-Level Settings (SLS) is a small XML format that describes tunable settings about a service. It contains information on setting names and types, where and how they are stored, and how they should be presented to a user. This information is all loaded by jobservice, a system job management daemon. jobservice_ parses this information and sends details to client programs, such as jobs-admin_, for rendering to the user.

This document describes the format of an SLS definition with examples and further instructions. After reading this, you should be able to write your own in no time. If you can't, then I've failed as a documentation writer and you should email me directly for help. My email is located at the top of this document.

If you've written a SLS file and want me to look over it or give some pointers, feel free to contact me.

Examples are provided throughout the document. This is intended to be read from the top, but if you just want to start hacking you may be able to jump straight to the `Type Reference`_ for a quick reference.

XML Format
==========

I'm going to assume you have at least basic knowledge of XML. If you don't know what XML is, just think of HTML, but more mundane. A basic SLS XML file looks something like this::

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE settings PUBLIC
     "-//Ubuntu//DTD jobservice Service-Level Settings 0.8//EN"
     "http://people.ubuntu.com/~jpeddicord/SLS/0.8/sls.dtd">
    <settings>
        <setting name="setting_name" type="setting_type">
            <description>Some descriptive name</description>
            <data>
                <parse file="some_filename">%n=%s</parse>
            </data>
            <values>
                <value name="true">
                    <raw>1</raw>
                    <description>A description for this value</description>
                </value>
                <value name="false">
                    <raw>0</raw>
                </value>
                ...
            </values>
        </setting>
        
        ...
    </settings>

Let's break this down a little. The first four lines are standard XML practice: the XML and DOCTYPE tags. Always keep these present in your file. Nothing bad should happen with jobservice_ if they're not there, but the standards police may arrive on your doorstep and take you away.

Next up is the <settings> element. This **must** be present or nothing's going to read your file.

Inside the <settings> tag is a <setting> (singular) tag. Any number of <setting> tags can be present inside <settings>. A setting tag has two required attributes: ``name`` and ``type``. ``name`` must be a unique name for this setting within the file. ``type`` tells jobservice_ what it should be expecting. We'll talk more about types shortly.

Inside an individual <setting> tag are three elements: <description>, <data>, and <values>. Only <description> is actually required, meaning this is valid::

    <setting name="group_warning" type="label">
        <description>The following settings may break things.</description>
    </setting>

That's usually not very useful, though, as it doesn't really do anything. (It's really only for the ``label`` type, which I'll describe later.)

<data> tells jobservice_ where it can find and save the actual settings it changes. This will be covered in the Data_ section below.

<values> describes possible values a particular setting can take. Covered in the `Setting Values`_ section.

Data
----

Single File
~~~~~~~~~~~

The data section tells jobservice_ where to look for the setting value and where to store it when it changes. 90% of the time, it will look something like this::

    <data>
        <parse file="/etc/config/file">SOME_OPTION = %s</parse>
    </data>

This tells the parser to look in ``/etc/config/file`` for a line that has ``SOME_OPTION =``, followed by the actual value it will pick up. So, if that file has ``SOME_OPTION = sega`` in it, then the value returned by jobservice_ will be ``sega``. When the user changes this value, it is written back to the file in the same place.

Keep in mind that values are only parsed on a single line. If you need multiple line support, you may want to use a helper script (see `External Helpers`_).

At this point, unless your configuration is more complex, you may want to skip the rest of this section and continue to `Setting Values`_.

Multiple Locations
~~~~~~~~~~~~~~~~~~

Multiple <parse> tags are accepted. In ``apache2``, for example, changing the server port requires it to be configured in multiple locations. Such a setup could be written as the following (simplified, this isn't actually correct for apache)::

    <data>
        <parse file="/etc/apache2/ports.conf">Listen %s</parse>
        <parse file="/etc/apache2/sites-available/000-default.conf">VirtualHost *:%s</parse>
    </data>

With the above, when a value is changed, it is written to both of those files. (Note that jobservice_ only *reads* the value from the first, however.)

External Helpers
~~~~~~~~~~~~~~~~

There are *very rare* cases where simply writing to a file is not enough. For that, there is the ability to call on external programs or helper scripts to fetch and set the value::

    <data>
        <parse get="/usr/lib/jobservice/my-helper">SomeSetting %s</parse>
        <parse set="/usr/lib/jobservice/my-helper">SomeSetting=%s</parse>
    </data>

This calls ``my-helper`` and parses STDOUT for ``SomeSetting %s`` as is done for normal files.
When saving, ``my-helper`` is called with STDIN set to ``SomeSetting=%s`` (with %s replaced with the value, of course), again much like how things are written with normal files. The scripts given in get/set **must have absolute paths** or jobservice_ may not be able to find them.

Other than that, there are no real limitations: you can use extra arguments to scripts, get and set can be different programs, and you can even mix this with regular files as in the above examples. These are all rather advanced, however, and using regular files should cover most use cases.

Additionally, any instance of ``%j`` in the get/set attributes or parse body will be replaced with the full job name. This can be useful when multiple instances of a job use the same SLS file and you need to differentiate between them.

The "%n" shortcut
~~~~~~~~~~~~~~~~~

One very handy shortcut you can use inside <parse> tags is ``%n``, which is replaced with the name of the setting before parsing. So::

    <setting name="MySetting" type="str">
        <data>
            <parse file="/some/file">MySetting = "%s"</parse>
        ...

is identical to::

    <setting name="MySetting" type="str">
        <data>
            <parse file="/some/file">%n = "%s"</parse>
        ...

This can make your setting files more concise and reduce the work needed to write them (even if just by a small amount).

Static values
~~~~~~~~~~~~~

Lastly, a data tag can also have a fixed value. This is really only useful for the ``exec`` type, but you may find other uses for it::

    <data val="gufw" />

In this case the tag is self-closing. As previously mentioned, if you're using a ``label`` type, you can omit the <data> tag entirely.

Setting Values
--------------

The <values> tag is a tricky thing to get, but is a very powerful tool to use once you know how to wield it. Similar to <settings>, <values> contains any number of <value> tags, including none at all.

The usage of a <value> tag varies greatly with the setting type (see the `Type Reference`_), but I'll give you a basic overview here. Let's start by example::

    <values>
        <value name="true">
            <raw>1</raw>
        </value>
        <value name="false">
            <raw>0</raw>
        </value>
    </values>

This is common to see for a ``bool`` type. Basically, this states that when the value for this is determined to be ``true`` (i.e. the checkbox in the interface is active), then a value of ``1`` will be written to the file (see Data_ for information on how). When the value is ``false``, a ``0`` is written.

The key point to understand here is that the value parser is a **translator** between the user and the raw file data. The text in the ``name`` attribute is what is sent to and received from the client, and the ``raw`` tag text is what is written to or read from the file. Any value sent from the user or read from a file can be translated. This allows you to do some neat things, but also very nasty things. While you could translate ``str`` values from the user, this would be a *very bad* thing to do.

We'll call this **value translation** in other parts of this document.

<description> can be used inside value tags when necessary (depends on the type)::

    <value name="two">
        <description>Second item</description>
        <raw>2</raw>
    </value>

When writing value sections, be sure to pay close attention to what is needed for the type of setting you are using. Don't stray from that and you should be fine.

Value Constraints
~~~~~~~~~~~~~~~~~

Values allow some constraints to be suggested. This is the responsibility of the client program to implement, and can even be ignored -- but that might compromise the stability of your system if an invalid value is entered.

Constraints are just attributes on the main <values> tag::

    <values min="80" max="600" />
    
The above would be useful for an ``int`` setting. Note that constraints can be used regardless if there are values present or if the tag is empty (and thus self-closing, as in this example).

Again, pay attention to what constraints are available for the specific type you are working with and you should have no problems.

Type Reference
--------------

This is the main portion of this document. Different types have different requirements, and types effect the display of information to the user in client programs. If you're looking for a specific type, just locate the relevant section and read the details for information on how to use it.

.. contents:: :local:
              :depth: 1

bool
~~~~

A ``bool`` type is a simple true/false setting. This is represented in jobs-admin_ as a checkbox.

Values
''''''

``true`` and ``false`` are the client values, and need to be translated into raw values for writing.

Example
'''''''

::

    <setting name="enabled" type="bool">
        <description>Enable this setting</description>
        <data>
            <parse file="/etc/myfile">SettingEnabled %s</parse>
        </data>
        <values>
            <value name="true">
                <raw>yes</raw>
            </value>
            <value name="false">
                <raw>no</raw>
            </value>
        </values>
    </setting>

This creates a setting of type ``bool`` named ``enabled`` with a description of "Enable this setting." The data is stored in ``/etc/myfile`` in the format ``SettingEnabled %s``. ``true`` is translated into ``yes`` (``SettingEnabled yes`` is written) and ``false`` becomes ``no``.

int/float
~~~~~~~~~

An ``int`` corresponds to an unsigned integer, and ``float`` corresponds to a decimal. jobs-admin_ renders these as GtkSpinButton widgets.

Values
''''''

It is not necessary to provide translated values for these types. However, constraints may be used on an empty <values> tag.

Constraints
```````````

min
    Minimum value accepted.

max
    Maximum value accepted.

Example
'''''''

::

    <setting type="int" name="Port">
        <description>Port</description>
        <data>
            <parse file="/etc/ssh/sshd_config">%n %s</parse>
        </data>
        <values min="0" max="65535"/>
    </setting>

A setting named ``Port`` of type ``int`` is created. Its description is simply "Port." The value is written to ``/etc/ssh/sshd_config`` using the format ``%n %s`` (or: ``Port %s``). A minimum value of ``0`` and a maximum of ``65535`` is set.

str
~~~

A ``str`` is a simple text string. jobs-admin_ renders this as a text box. If a client program isn't sure how to render another type, it may treat it as a ``str``.

Value translation should almost *never* be used here.

Example
'''''''

::

    <setting type="str" name="server">
        <description>NTP Server</description>
        <data>
            <parse file="/etc/ntp.conf">%n %s</parse>
        </data>
    </setting>

Hopefully simple enough: a ``str`` setting named ``server`` with a description of ``NTP Server``. Stored in ``/etc/ntp.conf`` as ``server %s``.

label
~~~~~

Perhaps the easiest setting to use, the ``label`` just displays some text. This cannot be configured by the user: it is for informational purposes.

Example
'''''''

::

    <setting type="label" name="some_info">
        <description>Careful: fire is dangerous.</description>
    </setting>
    
This will insert "Careful: fire is dangerous." as a setting. Because settings are displayed in the order present in the XML, you can use this to group some settings or show additional information about a setting.

choice
~~~~~~

A ``choice`` setting requires one of its values be selected. Rendered in jobs-admin_ as a combo box.

Values
''''''

Value translation isn't important here, as most clients will render the description instead of the client value. You can make the value name the same as the raw text or different; it shouldn't matter for most cases.

The <description> tag inside a <value> is strongly suggested here, as it will likely be rendered by the client::

    <value name="some_choice">
        <description>Some choice option</description>
        <raw>choice_rawdata</raw>
    </value>

Example
'''''''

::

    <setting type="choice" name="default_input_policy">
        <description>Default inbound policy</description>
        <data>
            <parse file="/etc/default/ufw">DEFAULT_INPUT_POLICY="%s"</parse>
        </data>
        <values>
            <value name="drop">
                <raw>DROP</raw>
                <description>Drop</description>
            </value>
            <value name="accept">
                <raw>ACCEPT</raw>
                <description>Accept</description>
            </value>
        </values>
    </setting>

Renders a ``choice`` setting named ``default_input_policy``. Stored in ``/etc/default/ufw`` in ``DEFAULT_INPUT_POLICY="%s"``. Two choices are available: Drop, and Accept. Selecting Drop will store ``DEFAULT_INPUT_POLICY="DROP"`` in the file, and ``DEFAULT_INPUT_POLICY="ACCEPT"`` for Accept.

file/dir
~~~~~~~~

A ``file`` or ``dir`` setting points to a file or directory on the system. Normally, this is rendered as a GtkFileChooserButton in jobs-admin_, however it may render as a textbox if the ``exist`` constraint is ``false``.

Values
''''''

Value translation should not be used. However, constraints are available:

Constraints
```````````

exist
    If ``true``, requires that the selected file or directory already exists on the system.
    If ``false``, any filesystem location may be specified, existing or not.
    Defaults to ``true``.

Example
'''''''

::

    <setting type="dir" name="log_location">
        <description>Log location</description>
        <data>
            <parse file="/etc/someservice.conf">%n %s</parse>
        </data>
        <values exist="false" />
    </setting>

Creates a ``dir`` setting named ``log_location``, stored in ``/etc/someservice.conf`` as ``log_location %s``. The specified location doesn't have to exist; which implies that it is created on-demand by the service.

user/group
~~~~~~~~~~

Much like a ``choice`` setting, but using system users or groups instead of configurable choices. 

Values
''''''

Value translation should not be used, but constraints are available:

Constraints
```````````

useid
    If ``true``, the client should send and recieve values as numerical user IDs.
    If ``false``, user or group names should be used.
    Defaults to ``false``.

Example
'''''''

::

    <setting type="group" name="SystemGroup">
        <description>System Group</description>
        <data>
            <parse file="/etc/cups/cupsd.conf">%n %s</parse>
        </data>
        <values useid="false" />
    </setting>

A ``group`` setting named ``SystemGroup``. Stored in ``/etc/cups/cupsd.conf`` as ``SystemGroup %s``. Because ``useid`` is false, the entire <values> tag may be omitted.

exec
~~~~

Suggests to the client program that another application should be run to manage this particular service. The client program is responsible for launching this process, in fact, it may choose not to show settings of this type at all. jobs-admin_ presents these as a description followed by a "Launch" button to start the application.

Uses a static value with the shell-interpreted command to launch::

    <data val="someapplication" />

Alternatively, if values are provided, the client program should check for the existence of each, deferring to the next if one is not available.

Values
''''''

If not using a static value in the data section, use values to list the commands that should be run. If one is not available, the next will be presented, until there are no more options left.

Example
'''''''

::

    <setting type="exec" name="manage">
        <_description>Manage CUPS</_description>
        <data val="xdg-open 'http://localhost:631/'"/>
        <values>
            <value name="system-config-printer">
                <_description>Configure printers</_description>
            </value>
            <value name="xdg-open 'http://localhost:631/'">
                <_description>Launch web interface</_description>
            </value>
        </values>
    </setting>

An ``exec`` setting named ``manage`` that suggests that the user launch ``system-config-printer`` to manage printers. If that is not available, the web interface will be suggested instead. Note that the action to launch is in the value name. The text of <raw> tags is not passed over D-Bus, so you need to specify the action in the name.

Translation
===========

Because <description> tags contain text that is displayed to the user, this text may be translated. Translations must be shipped inside the SLS file using xml:lang notation for alternate languages::

    <setting name="stuff" type="label">
        <description>Hello!</description>
        <description xml:lang="es_ES">¡Hola!</description>
        ...

The **intltool** suite is capable of generating translatable XML files from gettext .po files. If you can't support the translation infrastructure needed here, we may be able to ship the SLS definition and translations with jobservice_ itself. Read on.

Distribution
============

Once you've gotten your SLS definition written, working, and hopefully translated, it's time to ship it out to your users.

With your service
-----------------

If you are able to, I recommend simply shipping the SLS definition *with* your system service. It won't introduce any additional dependencies, as it is just a single XML file that is loaded only when needed.

To do so, just have your installation script or package install your SLS file to ``/usr/share/jobservice/sls``. So, if you want to ship your ``apache2.xml`` SLS, it should go in ``/usr/share/jobservice/sls/apache2.xml``. If the ``sls`` directory doesn't already exist, it should be created.

When jobservice_ starts up, it will read in your SLS file and display your settings. That's it!

With jobservice
---------------

If, for some reason, you don't want to ship your SLS file with your service, we may be able to ship it with jobservice_ directly. This may be useful if you are unable to properly support the translation infrastructure needed for internationalizing SLS description tags. Email your SLS definition to me (my email is at the top of this document) or branch lp:jobservice and add your SLS definition to it for merging.

Your SLS file will then be shipped as a "default" set of settings. If you choose to ship these in your own service later, these will be automatically overriden. We'll add the description tags for translation.

End
===

If you've survived this far, congratulations! You are now an SLS guru. If you need me to look over your SLS file, need assistance, or just want to chat, feel free to email me, ping me on twitter (@jpeddicord), identi.ca (@jacob), or on irc.freenode.net (nick jacob). Other contact methods are on `my website <http://jacob.peddicord.net>`_.

Keep tabs on jobservice_ and jobs-admin_ for updates and new features, as well!

.. _jobservice: https://launchpad.net/jobservice
.. _jobs-admin: https://launchpad.net/jobsadmin

