elogind Documentation

repository·main·Indexed 18 days ago

https://github.com/elogind/elogind

A standalone implementation of the systemd 'logind' component. It provides the org.freedesktop.login1 D-Bus interface and libelogind library, enabling desktop environments like GNOME to run on init systems other than systemd, such as GNU Shepherd or OpenRC.

Tokens
13.3K
Snippets
37
Records
74
Agent score
62%

What's inside elogind

  1. Understand elogind's resource management and scope model

    main

    Elogind is a standalone daemon and does not rely on a global systemd instance. This results in several key behavioral differences regarding resource management:

    • No Global Cgroups: Unlike systemd, elogind does not use a global cgroup-based resource management system. The directory /run/systemd/slices is not used and will remain empty or non-existent.
    • Scopes vs. Sessions: Elogind does not have a distinct concept of a "scope" internally; a scope is treated the same as a session. Any API calls attempting to refer to scopes will return an error code.
    • Process Organization: Elogind places processes in private cgroups for organizational purposes without installing controllers. This allows it to map processes to sessions even if they undergo a double-fork and are reparented to PID 1.
    • Virtual Terminals: Elogind does not manage virtual terminals.
  2. Code organization and language standards

    main

    Language Standards

    • Internal Code: Uses ISO C11 with GNU extensions (gnu11).
    • Public APIs (elogind/sd-*.h): Must use ISO C89 with a limited set of conservative extensions (e.g., fixed-size integer types from <inttypes.h>) to ensure compatibility for consumers not using C11.

    Naming Conventions

    • Structures: PascalCase (except for public API structs).
    • Variables and Functions: snake_case.

    Thread Safety and Globals

    • Avoid Static Variables: Use them only for caches. To ensure thread safety, prefer Thread Local Storage (TLS) for small, fixed-size cache objects or use is_main_thread() to disable caching for non-main threads.
    • Avoid Global Variables: They hinder reusability and break thread safety. If necessary, make them static to limit scope. Data inherently global (like command-line parsed data) or specific logging targets are acceptable exceptions.
  3. Configure systemd activation context for NSS/PAM

    main

    When NSS and PAM modules are invoked by the service manager on behalf of a specific unit, the following variables are provided to help modules avoid deadlocks (e.g., by bypassing IPC calls to the unit being started):

    • $SYSTEMD_ACTIVATION_UNIT: Contains the full unit name (e.g., foobar.service).
    • $SYSTEMD_ACTIVATION_SCOPE: Set to either system or user depending on whether the module was called in --system or --user mode.
  4. Implement destructors and reference counting

    main

    When implementing object lifecycles in elogind, follow these naming and implementation patterns:

    Naming Conventions

    • xyz_free(): Destroys an object in full, freeing all memory and invalidating the pointer.
    • xyz_done(): Destroys only the referenced content but leaves the object itself allocated.
    • xyz_clear(): Resets all fields so the object can be reused.
    • xyz_ref(): Increases the reference counter by one.
    • xyz_unref(): Decreases the reference counter by one.

    Implementation Best Practices

    • Deregistration Order: Destructors must always deregister the object from its parent (the next bigger object) first.
    • Robustness: Destructors must be able to handle half-initialized objects.
    • NULL Safety: Destructors and unref() calls must accept NULL and treat it as a NOP (no-operation).
    • Chained Unref: To simplify code, make unref() return the same type it takes and always return NULL. This allows the pattern p = xyz_unref(p); to safely handle both initialized and uninitialized pointers in a single line.
    p = foobar_unref(p);
  5. Prevent deadlocks in PID 1

    main

    To avoid system deadlocks, follow these restrictions when running as PID 1:

    • NSS Requests: Do not issue Name Service Switch (NSS) requests (such as user name or hostname lookups) from PID 1, as these may synchronously trigger services required for startup.
    • Synchronous Communication: Do not communicate synchronously with any other service from PID 1.
  6. Avoid using threads in the service manager

    main

    To prevent deadlocks, avoid using threads in the service manager/PID 1. This is because mixing memory allocation in threads with clone() or clone3() system calls can lead to a child process being cloned in a locked state (e.g., if a thread holds the malloc() lock), making the lock unacquirable in the child.

    Instead of worker threads, use worker processes. For better performance and to avoid Copy-on-Write (CoW) traps, it is recommended to use execve() after forking or to use posix_spawn(), which combines clone() and execve() and uses CLONE_VFORK and CLONE_VM to avoid CoW issues.

  7. Manage file descriptors with CLOEXEC and Non-blocking modes

    main

    To prevent file descriptor leaks and blocking, follow these rules:

    • CLOEXEC by default: All file descriptors and sockets must be created with the CLOEXEC flag immediately to prevent leaking to forked binaries.
      • Use O_CLOEXEC with open().
      • Use SOCK_CLOEXEC with socket() and socketpair().
      • Use MSG_CMSG_CLOEXEC with recvmsg().
      • Use F_DUPFD_CLOEXEC instead of F_DUPFD.
      • Use the e variant of fopen().
    • Non-blocking for foreign files: Use O_NONBLOCK when opening files whose paths are user-specified. This prevents blocking on special file types like FIFOs or device nodes.
    • Openat-style APIs: Prefer openat()-style APIs. When implementing library calls in this style, imply AT_EMPTY_PATH if an empty or NULL path is provided (converting NULL to an empty string).
  8. Use correct types and avoid kernel-specific types

    main

    elogind follows specific type conventions to ensure portability and ABI stability:

    • Integers: Use unsigned (preferred over unsigned int) for non-negative values. Use fixed-size types like uint8_t, uint16_t, uint32_t, uint64_t, int8_t, int16_t, int32_t, etc. Avoid short and kernel-specific types like u32.
    • Bytes: Use uint8_t for generic bytes. Use char only for actual characters.
    • Time: Stay uniform; for example, always use usec_t for time values (do not mix usec and msec).
    • File Offsets: Never use off_t, especially in public APIs. Use uint64_t directly to avoid ABI corruption and ensure consistent parsing across architectures and D-Bus.
    • Floating Point: Use double instead of float unless allocating an array, as processors handle double natively.
    • Booleans: Use the bool type. Exception: In public headers (e.g., src/elogind/sd-*.h), use integers to maintain C89 compatibility.
  9. Why dynamic linking is required for clang sanitizers

    main

    When using clang, sanitizer libraries are statically linked by default. This causes issues during integration tests where a standard (uninstrumented) application might load an instrumented libelogind. This leads to unresolved symbol errors.

    To resolve this, you must use dynamic sanitizer libraries and potentially pre-load the ASan DSO using LD_PRELOAD=/path/to/asan/dso. This allows the instrumented library to function correctly when loaded by uninstrumented processes. The elogind integration test suite handles these requirements automatically via the create_asan_wrapper function in test/test-functions.

  10. Write thread-safe code for libelogind.so

    main
    While the main service manager avoids threads, code intended to run inside libelogind.so should be thread-safe. Use Thread Local Storage (TLS) via the thread_local keyword and pthread_once() where appropriate to ensure compatibility with multi-threaded environments.
  11. Manage memory allocation safely

    main

    When performing memory management in elogind, follow these safety rules:

    Safe Stack Allocation

    • Avoid alloca(): Do not use alloca(), strdupa(), or strndupa() directly. Use the safe wrappers: alloca_safe(), strdupa_safe(), or strndupa_safe(). These include assertions to prevent stack overruns.
    • Loop Warning: Never call alloca_safe() inside a loop. Since memory is released only at the end of the function (not the block), calling it in a loop will cause the stack to grow indefinitely.

    String Handling

    • Avoid Fixed Buffers: Avoid char buf[256]. Use dynamic memory, alloca_safe(), or VLAs. If you must use fixed-size buffers, use defined macros like LINE_MAX.
    • Concatenation: Use strjoina() or strjoin() instead of asprintf() for better performance, especially in inner loops.

    Best Practices

    • OOM Checks: Always check for Out-of-Memory. In application code, use log_oom(); in library code, do not log.
    • Automatic Cleanup: Use _cleanup_free_ and related macros to simplify resource management and improve readability.