Browse Source

Remove doc and man files for stuff elogind doesn't have.

Signed-off-by: Sven Eden <>
Sven Eden 1 year ago
  1. 652
  2. 168
  3. 106
  4. 9400
  5. 367


@ -1,652 +0,0 @@
title: Journal File Format
category: Interfaces
layout: default
# Journal File Format
_Note that this document describes the binary on-disk format of journals
only. For interfacing with web technologies there's the [Journal JSON
Format]( For transfer
of journal data across the network there's the [Journal Export
The elogind journal stores log data in a binary format with several features:
* Fully indexed by all fields
* Can store binary data, up to 2^64-1 in size
* Seekable
* Primarily append-based, hence robust to corruption
* Support for in-line compression
* Support for in-line Forward Secure Sealing
This document explains the basic structure of the file format on disk. We are
making this available primarily to allow review and provide documentation. Note
that the actual implementation in the [elogind
codebase]( is the
only ultimately authoritative description of the format, so if this document
and the code disagree, the code is right. That said we'll of course try hard to
keep this document up-to-date and accurate.
Instead of implementing your own reader or writer for journal files we ask you
to use the [Journal's native C
API]( to access
these files. It provides you with full access to the files, and will not
withhold any data. If you find a limitation, please ping us and we might add
some additional interfaces for you.
If you need access to the raw journal data in serialized stream form without C
API our recommendation is to make use of the [Journal Export
Format](, which you can
get via "journalctl -o export" or via elogind-journal-gatewayd. The export
format is much simpler to parse, but complete and accurate. Due to its
stream-based nature it is not indexed.
_Or, to put this in other words: this low-level document is probably not what
you want to use as base of your project. You want our [C
API]( instead!
And if you really don't want the C API, then you want the [Journal Export
Format]( instead! This
document is primarily for your entertainment and education. Thank you!_
This document assumes you have a basic understanding of the journal concepts,
the properties of a journal entry and so on. If not, please go and read up,
then come back! This is a good opportunity to read about the [basic properties
of journal
in particular realize that they may include binary non-text data (though
usually don't), and the same field might have multiple values assigned within
the same entry.
This document describes the current format of elogind 195. The documented
format is compatible with the format used in the first versions of the journal,
but received various compatible additions since.
If you are wondering why the journal file format has been created in the first
place instead of adopting an existing database implementation, please have a
look [at this
## Basics
* All offsets, sizes, time values, hashes (and most other numeric values) are 64bit unsigned integers in LE format.
* Offsets are always relative to the beginning of the file.
* The 64bit hash function used is [Jenkins lookup3](, more specifically jenkins_hashlittle2() with the first 32bit integer it returns as higher 32bit part of the 64bit value, and the second one uses as lower 32bit part.
* All structures are aligned to 64bit boundaries and padded to multiples of 64bit
* The format is designed to be read and written via memory mapping using multiple mapped windows.
* All time values are stored in usec since the respective epoch.
* Wall clock time values are relative to the Unix time epoch, i.e. January 1st, 1970. (CLOCK_REALTIME)
* Monotonic time values are always stored jointly with the kernel boot ID value (i.e. /proc/sys/kernel/random/boot_id) they belong to. They tend to be relative to the start of the boot, but aren't for containers. (CLOCK_MONOTONIC)
* Randomized, unique 128bit IDs are used in various locations. These are generally UUID v4 compatible, but this is not a requirement.
## General Rules
If any kind of corruption is noticed by a writer it should immediately rotate
the file and start a new one. No further writes should be attempted to the
original file, but it should be left around so that as little data as possible
is lost.
If any kind of corruption is noticed by a reader it should try hard to handle
this gracefully, such as skipping over the corrupted data, but allowing access
to as much data around it as possible.
A reader should verify all offsets and other data as it reads it. This includes
checking for alignment and range of offsets in the file, especially before
trying to read it via a memory map.
A reader must interleave rotated and corrupted files as good as possible and
present them as single stream to the user.
All fields marked as "reserved" must be initialized with 0 when writing and be
ignored on reading. They are currently not used but might be used later on.
## Structure
The file format's data structures are declared in
The file format begins with a header structure. After the header structure
object structures follow. Objects are appended to the end as time
progresses. Most data stored in these objects is not altered anymore after
having been written once, with the exception of records necessary for
indexing. When new data is appended to a file the writer first writes all new
objects to the end of the file, and then links them up at front after that's
done. Currently, seven different object types are known:
enum {
* A **DATA** object, which encapsulates the contents of one field of an entry, i.e. a string such as `_SYSTEMD_UNIT=avahi-daemon.service`, or `MESSAGE=Foobar made a booboo.` but possibly including large or binary data, and always prefixed by the field name and "=".
* A **FIELD** object, which encapsulates a field name, i.e. a string such as `_SYSTEMD_UNIT` or `MESSAGE`, without any `=` or even value.
* An **ENTRY** object, which binds several **DATA** objects together into a log entry.
* A **DATA_HASH_TABLE** object, which encapsulates a hash table for finding existing **DATA** objects.
* A **FIELD_HASH_TABLE** object, which encapsulates a hash table for finding existing **FIELD** objects.
* An **ENTRY_ARRAY** object, which encapsulates a sorted array of offsets to entries, used for seeking by binary search.
* A **TAG** object, consisting of an FSS sealing tag for all data from the beginning of the file or the last tag written (whichever is later).
## Header
The Header struct defines, well, you guessed it, the file header:
_packed_ struct Header {
uint8_t signature[8]; /* "LPKSHHRH" */
le32_t compatible_flags;
le32_t incompatible_flags;
uint8_t state;
uint8_t reserved[7];
sd_id128_t file_id;
sd_id128_t machine_id;
sd_id128_t boot_id; /* last writer */
sd_id128_t seqnum_id;
le64_t header_size;
le64_t arena_size;
le64_t data_hash_table_offset;
le64_t data_hash_table_size;
le64_t field_hash_table_offset;
le64_t field_hash_table_size;
le64_t tail_object_offset;
le64_t n_objects;
le64_t n_entries;
le64_t tail_entry_seqnum;
le64_t head_entry_seqnum;
le64_t entry_array_offset;
le64_t head_entry_realtime;
le64_t tail_entry_realtime;
le64_t tail_entry_monotonic;
/* Added in 187 */
le64_t n_data;
le64_t n_fields;
/* Added in 189 */
le64_t n_tags;
le64_t n_entry_arrays;
The first 8 bytes of Journal files must contain the ASCII characters LPKSHHRH.
If a writer finds that the **machine_id** of a file to write to does not match
the machine it is running on it should immediately rotate the file and start a
new one.
When journal file is first created the **file_id** is randomly and uniquely
When a writer opens a file it shall initialize the **boot_id** to the current
boot id of the system.
The currently used part of the file is the **header_size** plus the
**arena_size** field of the header. If a writer needs to write to a file where
the actual file size on disk is smaller than the reported value it shall
immediately rotate the file and start a new one. If a writer is asked to write
to a file with a header that is shorter than his own definition of the struct
Header, he shall immediately rotate the file and start a new one.
The **n_objects** field contains a counter for objects currently available in
this file. As objects are appended to the end of the file this counter is
The first object in the file starts immediately after the header. The last
object in the file is at the offset **tail_object_offset**, which may be 0 if
no object is in the file yet.
The **n_entries**, **n_data**, **n_fields**, **n_tags**, **n_entry_arrays** are
counters of the objects of the specific types.
**tail_entry_seqnum** and **head_entry_seqnum** contain the sequential number
(see below) of the last or first entry in the file, respectively, or 0 if no
entry has been written yet.
**tail_entry_realtime** and **head_entry_realtime** contain the wallclock
timestamp of the last or first entry in the file, respectively, or 0 if no
entry has been written yet.
**tail_entry_monotonic** is the monotonic timestamp of the last entry in the
file, referring to monotonic time of the boot identified by **boot_id**.
## Extensibility
The format is supposed to be extensible in order to enable future additions of
features. Readers should simply skip objects of unknown types as they read
them. If a compatible feature extension is made a new bit is registered in the
header's 'compatible_flags' field. If a feature extension is used that makes
the format incompatible a new bit is registered in the header's
'incompatible_flags' field. Readers should check these two bit fields, if they
find a flag they don't understand in compatible_flags they should continue to
read the file, but if they find one in 'incompatible_flags' they should fail,
asking for an update of the software. Writers should refuse writing if there's
an unknown bit flag in either of these fields.
The file header may be extended as new features are added. The size of the file
header is stored in the header. All header fields up to "n_data" are known to
unconditionally exist in all revisions of the file format, all fields starting
with "n_data" needs to be explicitly checked for via a size check, since they
were additions after the initial release.
Currently only two extensions flagged in the flags fields are known:
enum {
enum {
HEADER_INCOMPATIBLE_COMPRESSED indicates that the file includes DATA objects
that are compressed using XZ.
HEADER_COMPATIBLE_SEALED indicates that the file includes TAG objects required
for Forward Secure Sealing.
## Dirty Detection
enum {
If a file is opened for writing the **state** field should be set to
STATE_ONLINE. If a file is closed after writing the **state** field should be
set to STATE_OFFLINE. After a file has been rotated it should be set to
STATE_ARCHIVED. If a writer is asked to write to a file that is not in
STATE_OFFLINE it should immediately rotate the file and start a new one,
without changing the file.
After and before the state field is changed fdatasync() should be executed on
the file to ensure the dirty state hits disk.
## Sequence Numbers
All entries carry sequence numbers that are monotonically counted up for each
entry (starting at 1) and are unique among all files which carry the same
**seqnum_id** field. This field is randomly generated when the journal daemon
creates its first file. All files generated by the same journal daemon instance
should hence carry the same seqnum_id. This should guarantee a monotonic stream
of sequential numbers for easy interleaving even if entries are distributed
among several files, such as the system journal and many per-user journals.
## Concurrency
The file format is designed to be usable in a simultaneous
single-writer/multiple-reader scenario. The synchronization model is very weak
in order to facilitate storage on the most basic of file systems (well, the
most basic ones that provide us with mmap() that is), and allow good
performance. No file locking is used. The only time where disk synchronization
via fdatasync() should be enforced is after and before changing the **state**
field in the file header (see below). It is recommended to execute a memory
barrier after appending and initializing new objects at the end of the file,
and before linking them up in the earlier objects.
This weak synchronization model means that it is crucial that readers verify
the structural integrity of the file as they read it and handle invalid
structure gracefully. (Checking what you read is a pretty good idea out of
security considerations anyway.) This specifically includes checking offset
values, and that they point to valid objects, with valid sizes and of the type
and hash value expected. All code must be written with the fact in mind that a
file with inconsistent structure file might just be inconsistent temporarily,
and might become consistent later on. Payload OTOH requires less scrutiny, as
it should only be linked up (and hence visible to readers) after it was
successfully written to memory (though not necessarily to disk). On non-local
file systems it is a good idea to verify the payload hashes when reading, in
order to avoid annoyances with mmap() inconsistencies.
Clients intending to show a live view of the journal should use inotify() for
this to watch for files changes. Since file writes done via mmap() do not
result in inotify() writers shall truncate the file to its current size after
writing one or more entries, which results in inotify events being
generated. Note that this is not used as transaction scheme (it doesn't protect
anything), but merely for triggering wakeups.
Note that inotify will not work on network file systems if reader and writer
reside on different hosts. Readers which detect they are run on journal files
on a non-local file system should hence not rely on inotify for live views but
fall back to simple time based polling of the files (maybe recheck every 2s).
## Objects
All objects carry a common header:
enum {
_packed_ struct ObjectHeader {
uint8_t type;
uint8_t flags;
uint8_t reserved[6];
le64_t size;
uint8_t payload[];
The **type** field is one of the object types listed above. The **flags** field
currently knows one flag: OBJECT_COMPRESSED. It is only valid for DATA objects
and indicates that the data payload is compressed with XZ. If OBJECT_COMPRESSED
is set for an object HEADER_INCOMPATIBLE_COMPRESSED must be set for the file as
well. The **size** field encodes the size of the object including all its
headers and payload.
## Data Objects
_packed_ struct DataObject {
ObjectHeader object;
le64_t hash;
le64_t next_hash_offset;
le64_t next_field_offset;
le64_t entry_offset; /* the first array entry we store inline */
le64_t entry_array_offset;
le64_t n_entries;
uint8_t payload[];
Data objects carry actual field data in the **payload[]** array, including a
field name, a '=' and the field data. Example:
`_SYSTEMD_UNIT=foobar.service`. The **hash** field is a hash value of the
**next_hash_offset** is used to link up DATA objects in the DATA_HASH_TABLE if
a hash collision happens (in a singly linked list, with an offset of 0
indicating the end). **next_field_offset** is used to link up data objects with
the same field name from the FIELD object of the field used.
**entry_offset** is an offset to the first ENTRY object referring to this DATA
object. **entry_array_offset** is an offset to an ENTRY_ARRAY object with
offsets to other entries referencing this DATA object. Storing the offset to
the first ENTRY object in-line is an optimization given that many DATA objects
will be referenced from a single entry only (for example, `MESSAGE=` frequently
includes a practically unique string). **n_entries** is a counter of the total
number of ENTRY objects that reference this object, i.e. the sum of all
ENTRY_ARRAYS chained up from this object, plus 1.
The **payload[]** field contains the field name and date unencoded, unless
OBJECT_COMPRESSED is set in the `ObjectHeader`, in which case the payload is
LZMA compressed.
## Field Objects
_packed_ struct FieldObject {
ObjectHeader object;
le64_t hash;
le64_t next_hash_offset;
le64_t head_data_offset;
uint8_t payload[];
Field objects are used to enumerate all possible values a certain field name
can take in the entire journal file.
The **payload[]** array contains the actual field name, without '=' or any
field value. Example: `_SYSTEMD_UNIT`. The **hash** field is a hash value of
the payload. As for the DATA objects, this too is either the `.file_id` keyed
siphash24 hash of the payload, or the non-keyed Jenkins hash.
**next_hash_offset** is used to link up FIELD objects in the FIELD_HASH_TABLE
if a hash collision happens (in singly linked list, offset 0 indicating the
end). **head_data_offset** points to the first DATA object that shares this
field name. It is the head of a singly linked list using DATA's
**next_field_offset** offset.
## Entry Objects
_packed_ struct EntryItem {
le64_t object_offset;
le64_t hash;
_packed_ struct EntryObject {
ObjectHeader object;
le64_t seqnum;
le64_t realtime;
le64_t monotonic;
sd_id128_t boot_id;
le64_t xor_hash;
EntryItem items[];
An ENTRY object binds several DATA objects together into one log entry, and
includes other metadata such as various timestamps.
The **seqnum** field contains the sequence number of the entry, **realtime**
the realtime timestamp, and **monotonic** the monotonic timestamp for the boot
identified by **boot_id**.
The **xor_hash** field contains a binary XOR of the hashes of the payload of
all DATA objects referenced by this ENTRY. This value is usable to check the
contents of the entry, being independent of the order of the DATA objects in
the array.
The **items[]** array contains references to all DATA objects of this entry,
plus their respective hashes.
In the file ENTRY objects are written ordered monotonically by sequence
number. For continuous parts of the file written during the same boot
(i.e. with the same boot_id) the monotonic timestamp is monotonic too. Modulo
wallclock time jumps (due to incorrect clocks being corrected) the realtime
timestamps are monotonic too.
## Hash Table Objects
_packed_ struct HashItem {
le64_t head_hash_offset;
le64_t tail_hash_offset;
_packed_ struct HashTableObject {
ObjectHeader object;
HashItem items[];
The structure of both DATA_HASH_TABLE and FIELD_HASH_TABLE objects are
identical. They implement a simple hash table, which each cell containing
offsets to the head and tail of the singly linked list of the DATA and FIELD
objects, respectively. DATA's and FIELD's next_hash_offset field are used to
chain up the objects. Empty cells have both offsets set to 0.
Each file contains exactly one DATA_HASH_TABLE and one FIELD_HASH_TABLE
objects. Their payload is directly referred to by the file header in the
**data_hash_table_offset**, **data_hash_table_size**,
**field_hash_table_offset**, **field_hash_table_size** fields. These offsets do
_not_ point to the object headers but directly to the payloads. When a new
journal file is created the two hash table objects need to be created right
away as first two objects in the stream.
If the hash table fill level is increasing over a certain fill level (Learning
from Java's Hashtable for example: > 75%), the writer should rotate the file
and create a new one.
The DATA_HASH_TABLE should be sized taking into account to the maximum size the
file is expected to grow, as configured by the administrator or disk space
considerations. The FIELD_HASH_TABLE should be sized to a fixed size, as the
number of fields should be pretty static it depends only on developers'
creativity rather than runtime parameters.
## Entry Array Objects
_packed_ struct EntryArrayObject {
ObjectHeader object;
le64_t next_entry_array_offset;
le64_t items[];
Entry Arrays are used to store a sorted array of offsets to entries. Entry
arrays are strictly sorted by offsets on disk, and hence by their timestamps
and sequence numbers (with some restrictions, see above).
Entry Arrays are chained up. If one entry array is full another one is
allocated and the **next_entry_array_offset** field of the old one pointed to
it. An Entry Array with **next_entry_array_offset** set to 0 is the last in the
list. To optimize allocation and seeking, as entry arrays are appended to a
chain of entry arrays they should increase in size (double).
Due to being monotonically ordered entry arrays may be searched with a binary
search (bisection).
One chain of entry arrays links up all entries written to the journal. The
first entry array is referenced in the **entry_array_offset** field of the
Each DATA object also references an entry array chain listing all entries
referencing a specific DATA object. Since many DATA objects are only referenced
by a single ENTRY the first offset of the list is stored inside the DATA object
itself, an ENTRY_ARRAY object is only needed if it is referenced by more than
one ENTRY.
## Tag Object
#define TAG_LENGTH (256/8)
_packed_ struct TagObject {
ObjectHeader object;
le64_t seqnum;
le64_t epoch;
uint8_t tag[TAG_LENGTH]; /* SHA-256 HMAC */
Tag objects are used to seal off the journal for alteration. In regular
intervals a tag object is appended to the file. The tag object consists of a
SHA-256 HMAC tag that is calculated from the objects stored in the file since
the last tag was written, or from the beginning if no tag was written yet. The
key for the HMAC is calculated via the externally maintained FSPRG logic for
the epoch that is written into **epoch**. The sequence number **seqnum** is
increased with each tag. When calculating the HMAC of objects header fields
that are volatile are excluded (skipped). More specifically all fields that
might validly be altered to maintain a consistent file structure (such as
offsets to objects added later for the purpose of linked lists and suchlike)
after an object has been written are not protected by the tag. This means a
verifier has to independently check these fields for consistency of
structure. For the fields excluded from the HMAC please consult the source code
directly. A verifier should read the file from the beginning to the end, always
calculating the HMAC for the objects it reads. Each time a tag object is
encountered the HMAC should be verified and restarted. The tag object sequence
numbers need to increase strictly monotonically. Tag objects themselves are
partially protected by the HMAC (i.e. seqnum and epoch is included, the tag
itself not).
## Algorithms
### Reading
Given an offset to an entry all data fields are easily found by following the
offsets in the data item array of the entry.
Listing entries without filter is done by traversing the list of entry arrays
starting with the headers' **entry_array_offset** field.
Seeking to an entry by timestamp or sequence number (without any matches) is
done via binary search in the entry arrays starting with the header's
**entry_array_offset** field. Since these arrays double in size as more are
added the time cost of seeking is O(log(n)*log(n)) if n is the number of
entries in the file.
When seeking or listing with one field match applied the DATA object of the
match is first identified, and then its data entry array chain traversed. The
time cost is the same as for seeks/listings with no match.
If multiple matches are applied, multiple chains of entry arrays should be
traversed in parallel. Since they all are strictly monotonically ordered by
offset of the entries, advancing in one can be directly applied to the others,
until an entry matching all matches is found. In the worst case seeking like
this is O(n) where n is the number of matching entries of the "loosest" match,
but in the common case should be much more efficient at least for the
well-known fields, where the set of possible field values tend to be closely
related. Checking whether an entry matches a number of matches is efficient
since the item array of the entry contains hashes of all data fields
referenced, and the number of data fields of an entry is generally small (<
When interleaving multiple journal files seeking tends to be a frequently used
operation, but in this case can be effectively suppressed by caching results
from previous entries.
When listing all possible values a certain field can take it is sufficient to
look up the FIELD object and follow the chain of links to all DATA it includes.
### Writing
When an entry is appended to the journal for each of its data fields the data
hash table should be checked. If the data field does not yet exist in the file
it should be appended and added to the data hash table. When a field data
object is added the field hash table should be checked for the field name of
the data field, and a field object be added if necessary. After all data fields
(and recursively all field names) of the new entry are appended and linked up
in the hashtables the entry object should be appended and linked up too.
In regular intervals a tag object should be written if sealing is enabled (see
above). Before the file is closed a tag should be written too, to seal it off.
Before writing an object, time and disk space limits should be checked and
rotation triggered if necessary.
## Optimizing Disk IO
_A few general ideas to keep in mind:_
The hash tables for looking up fields and data should be quickly in the memory
cache and not hurt performance. All entries and entry arrays are ordered
strictly by time on disk, and hence should expose an OK access pattern on
rotating media, when read sequentially (which should be the most common case,
given the nature of log data).
The disk access patterns of the binary search for entries needed for seeking
are problematic on rotating disks. This should not be a major issue though,
since seeking should not be a frequent operation.
When reading, collecting data fields for presenting entries to the user is
problematic on rotating disks. In order to optimize these patterns the item
array of entry objects should be sorted by disk offset before
writing. Effectively, frequently used data objects should be in the memory
cache quickly. Non-frequently used data objects are likely to be located
between the previous and current entry when reading and hence should expose an
OK access pattern. Problematic are data objects that are neither frequently nor
infrequently referenced, which will cost seek time.
And that's all there is to it.
Thanks for your interest!


@ -1,168 +0,0 @@
title: elogind-homed and JSON User/Group Record Support in Desktop Environments
category: Interfaces
layout: default
# `elogind-homed` and JSON User/Group Record Support in Desktop Environments
Starting with version 245, elogind supports a new subsystem
for managing regular ("human") users and their home directories. Along with it
a new concept `userdb` got merged that brings rich, extensible JSON user/group
records, extending the classic UNIX/glibc NSS `struct passwd`/`struct group`
structures. Both additions are added in a fully backwards compatible way,
accessible through `getpwnam()`/`getgrnam()`/… (i.e. libc NSS) and PAM as
usual, meaning that for basic support no changes in the upper layers of the
stack (in particular desktop environments, such as GNOME or KDE) have to be
made. However, for better support a number of changes to desktop environments
are recommended. A few areas where that applies are discussed below.
Before reading on, please read up on the basic concepts, specifically:
* [Home Directories](
* [JSON User Records](
* [JSON Group Records](
* [User/Group Record Lookup API via Varlink](
## Support for Suspending Home Directory Access during System Suspend
One key feature of `elogind-homed` managed encrypted home directories is the
ability that access to them can be suspended automatically during system sleep,
removing any cryptographic key material from memory while doing so. This is
important in a world where most laptop users seldom shut down their computers
but most of the time just suspend them instead. Previously, the encryption keys
for the home directories remained in memory during system suspend, so that
sufficiently equipped attackers could read them from there and gain full access
to the device. By removing the key material from memory before suspend, and
re-requesting it on resume this attack vector can be closed down effectively.
Supporting this mechanism requires support in the desktop environment, since
the encryption keys (i.e. the user's login password) need to be reacquired on
system resume, from a lock screen or similar. This lock screen must run in
system context, and cannot run in the user's own context, since otherwise it
might end up accessing the home directory of the user even though access to it
is temporarily suspended and thus will hang if attempted.
It is suggested that desktop environments that implement lock screens run them
from system context, for example by switching back to the display manager, and
only revert back to the session after re-authentication via this system lock
screen (re-authentication in this case refers to passing the user's login
credentials to the usual PAM authentication hooks). Or in other words, when
going into system suspend it is recommended that GNOME Shell switches back to
the GNOME Display Manager login screen which now should double as screen lock,
and only switches back to the shell's UI after the user re-authenticated there.
Note that this change in behavior is a good idea in any case, and does not
create any dependencies on `elogind-homed` or elogind-specific APIs. It's
simply a change of behavior regarding use of existing APIs, not a suggested
hook-up to a any new API.
A display manager which supports this kind of out-of-context screen lock
operation needs to inform elogind-homed about this so that elogind-homed knows
that it is safe to suspend the user's home directory on suspend. This is done
via the `suspend=` argument to the
PAM module. A display manager should hence change its PAM stack configuration
to set this parameter to on. `elogind-homed` will not suspend home directories
if there's at least one active session of the user that does not support
suspending, as communicated via this parameter.
## User Management UIs
The rich user/group records `userdb` and `elogind-homed` support carry various
fields of relevance to UIs that manage the local user database or parts
thereof. In particular, most of the metadata `accounts-daemon` (also see below)
supports is directly available in these JSON records. Hence it makes sense for
any user management UI to expose them directly.
`elogind-homed` exposes APIs to add, remove and make changes to local users via
D-Bus, with full PolicyKit hook-up. On the command line this is exposed via the
`homectl` command. A graphical UI that exposes similar functionality would be
very useful, exposing the various new account settings, and in particular
providing a stream-lined UI for enrolling new-style authentication tokens such
as PKCS#11/YubiKey-style devices. (Ideally, if the user plugs in an
uninitialized YubiKey during operation it might be nice if the Desktop would
automatically ask if a key pair shall be written to it and the local account be
bound to it, `elogind-homed` provides enough YubiKey/PKCS#11 support to make
this a reality today; except that it will not take care of token
A strong point of `elogind-homed` is per-user resource management. In
particular disk space assignments are something that most likely should be
exposed in a user management UI. Various metadata fields are supplied allowing
exposure of disk space assignment "slider" UI. Note however that the file system
back-ends of `elogind-homed.service` have different feature sets. Specifically,
only btrfs has online file system shrinking support, ext4 only offline file
system shrinking support, and xfs no shrinking support at all (all three file
systems support online file system growing however). This means if the LUKS
back-end is used, disk space assignment cannot be instant for logged in users,
unless btrfs is used.
Note that only `elogind-homed` provides an API for modifying/creating/deleting
users. The generic `userdb` subsystem (which might have other back-ends, besides
`elogind-homed`, for example LDAP or Windows) exclusively provides a read-only
interface. (This is unlikely to change, as the other back-ends might have very
different concepts of adding or modifying users, i.e. might not even have any
local concept for that at all). This means any user management UI that intends
to change (and not just view) user accounts should talk directly to
`elogind-homed` to make use of its features; there's no abstraction available
to support other back-ends under the same API.
Unfortunately there's currently no documentation for the `elogind-homed` D-Bus
API. Consider using the `homectl` sources as guidelines for implementing a user
management UI. The JSON user/records are well documented however, see above,
and the D-Bus API provides limited introspection.
## Relationship to `accounts-daemon`
For a long time `accounts-daemon` has been included in Linux distributions
providing richer user accounts. The functionality of this daemon overlaps in
many areas with the functionality of `elogind-homed` or `userdb`, but there are
systematic differences, which means that `elogind-homed` cannot replace
`accounts-daemon` fully. Most importantly: `accounts-daemon` provides
"side-car" metadata for *any* type of user account, while `elogind-homed` only
provides additional metadata for the users it defines itself. In other words:
`accounts-daemon` will augment foreign accounts; `elogind-homed` cannot be used
to augment users defined elsewhere, for example in LDAP or as classic
`/etc/passwd` records.
This probably means that for the time being, a user management UI (or other UI)
that wants to support rich user records with compatibility with the status quo
ante should probably talk to both `elogind-homed` and `accounts-daemon` at the
same time, and ignore `accounts-daemon`'s records if `elogind-homed` defines
them. While I (Lennart) personally believe in the long run `elogind-homed` is
the way to go for rich user records, any UI that wants to manage and support
rich records for classic records has to support `accounts-daemon` in parallel
for the time being.
In the short term, it might make sense to also expose the `userdb` provided
records via `accounts-daemon`, so that clients of the latter can consume them
without changes. However, I think in the long run `accounts-daemon` should
probably be removed from the general stack, hence this sounds like a temporary
solution only.
In case you wonder, there's no automatic mechanism for converting existing
users registered in `/etc/passwd` or LDAP to users managed by
`elogind-homed`. There's documentation for doing this manually though, see
[Converting Existing Users to elogind-homed managed
## Future Additions
JSON user/group records are extensible, hence we can easily add any additional
fields desktop environments require. For example pattern-based authentication
is likely very useful on touch-based devices, and the user records should hence
learn them natively. Fields for other authentication mechanisms, such as
fingerprint authentication should be provided as well, eventually.
It is planned to extend the `userdb` Varlink API to support look-ups by partial
user name and real name (GECOS) data, so that log-in screens can optionally
implement simple complete-as-you-type login screens.
It is planned to extend the `elogind-homed` D-Bus API to instantly inform clients
about hardware associated with a specific user being plugged in, to which login
screens can listen in order to initiate authentication. Specifically, any
YubiKey-like security token plugged in that is associated with a local user
record should initiate authentication for that user, making typing in of the
username unnecessary.


@ -1,106 +0,0 @@
<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"" >
<!-- SPDX-License-Identifier: LGPL-2.1+ -->
<refentry id="org.freedesktop.LogControl1"
<refpurpose>D-Bus interface to query and set logging configuration</refpurpose>
<para><interfacename>org.freedesktop.LogControl1</interfacename> is a generic interface that is intended
to be used by any daemon which should allow setting the log level and target over D-Bus. It is implemented
by various daemons that are part of the
<citerefentry><refentrytitle>elogind</refentrytitle><manvolnum>1</manvolnum></citerefentry> suite.</para>
<para>It is assumed that those settings are global for the whole program, so a fixed object path is
used. The interface should always be available under the path
<para>The following interface is exposed:</para>
<programlisting executable="elogind" node="/org/freedesktop/LogControl1" interface="org.freedesktop.LogControl1">
node /org/freedesktop/LogControl1 {
interface org.freedesktop.LogControl1 {
readwrite s LogLevel = '...';
readwrite s LogTarget = '...';
readonly s SyslogIdentifier = '...';
interface org.freedesktop.DBus.Peer { ... };
interface org.freedesktop.DBus.Introspectable { ... };
interface org.freedesktop.DBus.Properties { ... };
<!--Autogenerated cross-references for elogind.directives, do not edit-->
<variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.LogControl1"/>
<variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.LogControl1"/>
<variablelist class="dbus-property" generated="True" extra-ref="LogLevel"/>
<variablelist class="dbus-property" generated="True" extra-ref="LogTarget"/>
<variablelist class="dbus-property" generated="True" extra-ref="SyslogIdentifier"/>
<!--End of Autogenerated section-->
<para><varname>LogLevel</varname> describes the
<citerefentry project='man-pages'><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry>-style
log-level, and should be one of <literal>emerg</literal>, <literal>alert</literal>,
<literal>crit</literal>, <literal>err</literal>, <literal>warning</literal>, <literal>notice</literal>,
<literal>info</literal>, <literal>debug</literal>, in order of increasing verbosity.</para>
<para><varname>LogTarget</varname> describes the log target (mechanism). It should be one of
<literal>console</literal> (log to the console or standard output),
<literal>kmsg</literal> (log to the kernel ring buffer),
<literal>journal</literal> (log to the journal natively, see
<literal>syslog</literal> (log using the
<citerefentry project='man-pages'><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry> call).
<para>Those two properties are writable, so they may be set by sufficiently privileged users.</para>
<para><varname>SyslogIdentifier</varname> is a read-only property that shows the "syslog identifier".
It is a short string that identifies the program that is the source of log messages that is passed to
the <citerefentry project='man-pages'><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry> call.
<para>Note: <command>journalctl</command> option <option>-p</option>/<option>--priority=</option> may
be used to filter log messages by log level, option <option>-t</option>/<option>--identifier=</option>
may be used to by the syslog identifier, and filters like <literal>_TRANSPORT=syslog</literal>,
<literal>_TRANSPORT=journal</literal>, and <literal>_TRANSPORT=kernel</literal> may be used to filter
messages by the mechanism through which they reached <command>elogind-journald</command>.</para>


File diff suppressed because it is too large


@ -1,367 +0,0 @@
<?xml version="1.0"?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"" [
<!ENTITY % entities SYSTEM "custom-entities.ent" >
<!-- SPDX-License-Identifier: LGPL-2.1+ -->
<refentry id="org.freedesktop.hostname1" conditional='ENABLE_HOSTNAMED'
<refpurpose>The D-Bus interface of elogind-hostnamed</refpurpose>
is a system service that can be used to control the hostname and related machine metadata from user
programs. This page describes the hostname semantics and the D-Bus interface.</para>
<title>The D-Bus API</title>
<para>The service exposes the following interfaces on the bus:</para>
<programlisting executable="elogind-hostnamed" node="/org/freedesktop/hostname1" interface="org.freedesktop.hostname1">
node /org/freedesktop/hostname1 {
interface org.freedesktop.hostname1 {
SetHostname(in s hostname,
in b interactive);
SetStaticHostname(in s hostname,
in b interactive);
SetPrettyHostname(in s hostname,
in b interactive);
SetIconName(in s icon,
in b interactive);
SetChassis(in s chassis,
in b interactive);
SetDeployment(in s deployment,
in b interactive);
SetLocation(in s location,
in b interactive);
GetProductUUID(in b interactive,
out ay uuid);
readonly s Hostname = '...';
readonly s StaticHostname = '...';
readonly s PrettyHostname = '...';
readonly s IconName = '...';
readonly s Chassis = '...';
readonly s Deployment = '...';
readonly s Location = '...';
readonly s KernelName = '...';
readonly s KernelRelease = '...';
readonly s KernelVersion = '...';
readonly s OperatingSystemPrettyName = '...';
readonly s OperatingSystemCPEName = '...';
readonly s HomeURL = '...';
interface org.freedesktop.DBus.Peer { ... };
interface org.freedesktop.DBus.Introspectable { ... };
interface org.freedesktop.DBus.Properties { ... };
<!--Autogenerated cross-references for elogind.directives, do not edit-->
<variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.hostname1"/>
<variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.hostname1"/>
<variablelist class="dbus-method" generated="True" extra-ref="SetHostname()"/>
<variablelist class="dbus-method" generated="True" extra-ref="SetStaticHostname()"/>
<variablelist class="dbus-method" generated="True" extra-ref="SetPrettyHostname()"/>
<variablelist class="dbus-method" generated="True" extra-ref="SetIconName()"/>
<variablelist class="dbus-method" generated="True" extra-ref="SetChassis()"/>
<variablelist class="dbus-method" generated="True" extra-ref="SetDeployment()"/>
<variablelist class="dbus-method" generated="True" extra-ref="SetLocation()"/>
<variablelist class="dbus-method" generated="True" extra-ref="GetProductUUID()"/>
<variablelist class="dbus-property" generated="True" extra-ref="Hostname"/>
<variablelist class="dbus-property" generated="True" extra-ref="StaticHostname"/>
<variablelist class="dbus-property" generated="True" extra-ref="PrettyHostname"/>
<variablelist class="dbus-property" generated="True" extra-ref="IconName"/>
<variablelist class="dbus-property" generated="True" extra-ref="Chassis"/>
<variablelist class="dbus-property" generated="True" extra-ref="Deployment"/>
<variablelist class="dbus-property" generated="True" extra-ref="Location"/>
<variablelist class="dbus-property" generated="True" extra-ref="KernelName"/>
<variablelist class="dbus-property" generated="True" extra-ref="KernelRelease"/>
<variablelist class="dbus-property" generated="True" extra-ref="KernelVersion"/>
<variablelist class="dbus-property" generated="True" extra-ref="OperatingSystemPrettyName"/>
<variablelist class="dbus-property" generated="True" extra-ref="OperatingSystemCPEName"/>
<variablelist class="dbus-property" generated="True" extra-ref="HomeURL"/>
<!--End of Autogenerated section-->
<para>Whenever the hostname or other metadata is changed via the daemon,
<function>PropertyChanged</function> signals are sent out to subscribed clients. Changing a hostname
using this interface is authenticated via
<ulink url="">polkit</ulink>.</para>
<para>The <emphasis>static (configured) hostname</emphasis> is the one configured in
<filename>/etc/hostname</filename>. It is chosen by the local user. It is not always in sync with the
current hostname as returned by the
<citerefentry project="man-pages"><refentrytitle>gethostname</refentrytitle><manvolnum>3</manvolnum></citerefentry>
system call. If no hostname is configured this property will be the empty string. Setting this property
to the empty string will remove <filename>/etc/hostname</filename>. This property should be an
internet-style hostname, 7-bit lowercase ASCII, no special chars/spaces.</para>
<para>The <emphasis>transient (dynamic) hostname</emphasis> is the one configured via the kernel's
<citerefentry project="man-pages"><refentrytitle>sethostname</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
It can be different from the static hostname if DHCP or mDNS have been configured to change the name
based on network information. <!-- FIXME: it's not DHCP that configures this... -->
This property is never empty. If no hostname is set this will default to
<literal>&FALLBACK_HOSTNAME;</literal> (configurable at compilation time). Setting this property to the
empty string will reset the dynamic hostname to the static hostname. If no static hostname is
configured the dynamic hostname will be reset to <literal>&FALLBACK_HOSTNAME;</literal>. This property
should be an internet-style hostname, 7-bit lowercase ASCII, no special chars/spaces.</para>
<para>The <emphasis>pretty hostname</emphasis> is a free-form UTF-8 hostname for presentation to the
user. User interfaces should ensure that the pretty hostname and the static hostname stay in sync.
I.e. when the former is <literal>Lennart’s Computer</literal> the latter should be
<literal>lennarts-computer</literal>. If no pretty hostname is set this setting will be the empty
string. Applications should then find a suitable fallback, such as the dynamic hostname.</para>
<para>The <emphasis>icon name</emphasis> is a name following the XDG icon naming spec. If not set,
information such as the chassis type (see below) is used to find a suitable fallback icon name
(i.e. <literal>computer-laptop</literal> vs. <literal>computer-desktop</literal> is picked based on the
chassis information). If no such data is available, the empty string is returned. In that case an application
should fall back to a replacement icon, for example <literal>computer</literal>. If this property is set
to the empty string, the automatic fallback name selection is enabled again.</para>
<para>The <emphasis>chassis type</emphasis> should be one of the currently defined chassis types:
<literal>desktop</literal>, <literal>laptop</literal>, <literal>server</literal>,
<literal>tablet</literal>, <literal>handset</literal>, as well as the special chassis types
<literal>vm</literal> and <literal>container</literal> for virtualized systems. Note that in most cases
the chassis type will be determined automatically from DMI/SMBIOS/ACPI firmware information. Writing to
this setting is hence useful only to override misdetected chassis types, or to configure the chassis type if
it could not be auto-detected. Set this property to the empty string to reenable the automatic detection of
the chassis type from firmware information.</para>
<para>Note that <filename>elogind-hostnamed</filename> starts only on request and terminates after a
short idle period. This effectively means that <function>PropertyChanged</function> messages are not sent
out for changes made directly on the files (as in: administrator edits the files with vi). This is
the intended behavior: manual configuration changes should require manual reloading.</para>
<para>The transient (dynamic) hostname maps directly to the kernel hostname. This hostname should be
assumed to be highly dynamic, and hence should be watched directly, without depending on
<function>PropertyChanged</function> messages from <filename>elogind-hostnamed</filename>. To accomplish
this, open <filename>/proc/sys/kernel/hostname</filename> and
<citerefentry project="man-pages"><refentrytitle>poll</refentrytitle><manvolnum>3</manvolnum></citerefentry>
for <constant>SIGHUP</constant> which is triggered by the kernel every time the hostname changes. Again:
this is special for the transient (dynamic) hostname, and does not apply to the configured (fixed)
<para>Applications may read the hostname data directly if hostname change notifications
are not necessary. Use
<citerefentry project="man-pages"><refentrytitle>gethostname</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<filename>/etc/hostname</filename> (possibly with per-distribution fallbacks), and
for that. For more information on these files and syscalls see the respective man pages.</para>
<title>Methods and Properties</title>
<para><function>SetHostname()</function> sets the transient (dynamic) hostname which is exposed by the
<varname>Hostname</varname> property. If empty, the transient hostname is set to the static hostname.
<para><function>SetStaticHostname()</function> sets the static hostname which is exposed by the
<varname>StaticHostname</varname> property. If empty, the built-in default of
<literal>&FALLBACK_HOSTNAME;</literal> is used.</para>
<para><function>SetPrettyHostname()</function> sets the pretty hostname which is exposed by the
<varname>PrettyHostname</varname> property.</para>
<para><function>SetIconName()</function>, <function>SetChassis()</function>,
<function>SetDeployment()</function>, and <function>SetLocation()</function> set the properties
<varname>IconName</varname> (the name of the icon representing for the machine),
<varname>Chassis</varname> (the machine form factor), <varname>Deployment</varname> (the system
deployment environment), and <varname>Location</varname> (physical system location), respectively.
<para><varname>PrettyHostname</varname>, <varname>IconName</varname>, <varname>Chassis</varname>,
<varname>Deployment</varname>, and <varname>Location</varname> are stored in
<filename>/etc/machine-info</filename>. See
<citerefentry><refentrytitle>machine-info</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
the semantics of those settings.</para>
<para><function>GetProductUUID()</function> returns the "product uuid" as exposed by the kernel based
on DMI information in <filename>/sys/class/dmi/id/product_uuid</filename>. Reading the file directly
requires root privileges, and this method allows access to unprivileged clients through the polkit
<para><varname>KernelName</varname>, <varname>KernelRelease</varname>, and
<varname>KernelVersion</varname> expose the kernel name (e.g. <literal>Linux</literal>), release
(e.g. <literal>5.0.0-11</literal>), and version (i.e. the build number, e.g. <literal>#11</literal>) as
reported by
<citerefentry project="man-pages"><refentrytitle>uname</refentrytitle><manvolnum>2</manvolnum></citerefentry>.
<varname>OperatingSystemPrettyName</varname>, <varname>OperatingSystemCPEName</varname>, and
<varname>HomeURL</varname> expose the <varname>PRETTY_NAME=</varname>, <varname>CPE_NAME=</varname> and
<varname>HOME_URL=</varname> fields from
<citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry>. The
purpose of those properties is to allow remote clients to access this information over D-Bus. Local
clients can access the information directly.</para>
<para>The <varname>interactive</varname> boolean parameters can be used to control whether polkit
should interactively ask the user for authentication credentials if required.</para>
<para>The polkit action for <function>SetHostname()</function> is
<interfacename>org.freedesktop.hostname1.set-hostname</interfacename>. For
<function>SetStaticHostname()</function> and <function>SetPrettyHostname()</function> it is
<interfacename>org.freedesktop.hostname1.set-static-hostname</interfacename>. For
<function>SetIconName()</function> and <function>SetChassis()</function> it is
<para>Here are three examples that show how the pretty hostname and the icon name should be used:
<listitem><para>When registering DNS-SD services: use the pretty hostname in the service name, and pass
the icon name in the TXT data, if there is an icon name. Browsing clients can then show the server icon
on each service. This is especially useful for WebDAV applications or UPnP media sharing.
<listitem><para>Set the bluetooth name to the pretty hostname.</para></listitem>
<listitem><para>When your file browser has a "Computer" icon, replace the name with the pretty hostname
if set, and the icon with the icon name, if it is set.</para></listitem>
<para>To properly handle name lookups with changing local hostnames without having to edit
<filename>/etc/hosts</filename>, we recommend using <filename>elogind-hostnamed</filename> in combination
with <citerefentry><refentrytitle>nss-myhostname</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
<para>A client that wants to change the local hostname for DHCP/mDNS should invoke
<code>SetHostname("newname", false)</code> as soon as the name is available and afterwards reset it via
<para>Here are some recommendations to follow when generating a static (internet) hostname from a pretty
<listitem><para>Generate a single DNS label only, not an FQDN. That means no dots allowed. Strip them,
or replace them with <literal>-</literal>.</para></listitem>
<listitem><para>It's probably safer to not use any non-ASCII chars, even if DNS allows this in some way
these days. In fact, restrict your charset to <literal>a-zA-Z0-9</literal> and <literal>-</literal>.
Strip other chars, or try to replace them in some smart way with chars from this set, for example
<literal>ä</literal><literal>ae</literal>, and use <literal>-</literal> as the replacement for all
punctuation characters and whitespace.</para></listitem>
<listitem><para>Try to avoid creating repeated <literal>-</literal>, as well as <literal>-</literal> as
the first or last char.</para></listitem>
<listitem><para>Limit the hostname to 63 chars, which is the length of a DNS label.</para></listitem>
<listitem><para>If after stripping special chars the empty string is the result, you can pass this
as-is to <filename>elogind-hostnamed</filename> in which case it will automatically use
<listitem><para>Uppercase charaacters should be replaced with their lowercase equivalents.
<para>Note that while <filename>elogind-hostnamed</filename> applies some checks to the hostname you pass
they are much looser than the recommendations above. For example, <filename>elogind-hostnamed</filename>
with DNS-SD service types. Also <filename>elogind-hostnamed</filename> allows longer hostnames, but
will also accept <literal>_</literal> in the hostname, but we recommend not using this to avoid clashes
because of the DNS label limitations, we recommend not making use of this.</para>
<para>Here are a couple of example conversions:
<listitem><para><literal>Lennart's PC</literal><literal>lennarts-pc</literal></para></listitem>
<listitem><para><literal>Müllers Computer</literal><literal>muellers-computer</literal></para></listitem>
<listitem><para><literal>Es war einmal ein Männlein</literal><literal>es-war-einmal-ein-maennlein</literal></para></listitem>
<listitem><para><literal>Jawoll. Ist doch wahr!</literal><literal>jawoll-ist-doch-wahr</literal></para></listitem>
<listitem><para><literal>...zack!!! zack!...</literal><literal>zack-zack</literal></para></listitem>
<para>Of course, an already valid internet hostname label you enter and pass through this
conversion should stay unmodified, so that users have direct control of it, if they want — by simply
ignoring the fact that the pretty hostname is pretty and just edit it as if it was the normal internet
<para>These D-Bus interfaces follow <ulink url="">
the usual interface versioning guidelines</ulink>.</para>
<title>Introspect <interfacename>org.freedesktop.hostname1</interfacename> on the bus</title>
<programlisting>$ gdbus introspect --system \
--dest org.freedesktop.hostname1 \
--object-path /org/freedesktop/hostname1
<title>See also</title>
<para>David Zeuthen's original Fedora
<ulink url="">Feature page about xdg-hostname</ulink></para>