org.freedesktop.LogControl1 - Man Page

D-Bus interface to query and set logging configuration

Introduction

org​.freedesktop​.LogControl1 is a generic interface that is intended to be used by any daemon which allows the log level and target to be set over D-Bus​. It is implemented by various daemons that are part of the systemd(1) suite​.

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 /org/freedesktop/LogControl1​.

Description

The following interface is exposed:

node /org/freedesktop/LogControl1 {
  interface org​.freedesktop​.LogControl1 {
    properties:
      @org​.freedesktop​.DBus​.Property​.EmitsChangedSignal("false")
      @org​.freedesktop​.systemd1​.Privileged("true")
      readwrite s LogLevel = '​.​.​.';
      @org​.freedesktop​.DBus​.Property​.EmitsChangedSignal("false")
      @org​.freedesktop​.systemd1​.Privileged("true")
      readwrite s LogTarget = '​.​.​.';
      @org​.freedesktop​.DBus​.Property​.EmitsChangedSignal("false")
      readonly s SyslogIdentifier = '​.​.​.';
  };
  interface org​.freedesktop​.DBus​.Peer { ​.​.​. };
  interface org​.freedesktop​.DBus​.Introspectable { ​.​.​. };
  interface org​.freedesktop​.DBus​.Properties { ​.​.​. };
};

Properties

LogLevel describes the syslog(3)-style log-level, and should be one of "emerg", "alert", "crit", "err", "warning", "notice", "info", "debug", in order of increasing verbosity​.

LogTarget describes the log target (mechanism)​. It should be one of "console" (log to the console or standard output), "kmsg" (log to the kernel ring buffer), "journal" (log to the journal natively, see systemd-journald.service(8)), "syslog" (log using the syslog(3) call)​.

Write Access

The LogLevel and LogTarget properties are supposed to be writable​. Care should be taken to ensure that only appropriately privileged clients can modify them​.

SyslogIdentifier 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 syslog(3) call​.

Tools

journalctl option -p/--priority= may be used to filter log messages by log level, option -t/--identifier= may be used to by the syslog identifier, and filters like "_TRANSPORT=syslog", "_TRANSPORT=journal", and "_TRANSPORT=kernel" may be used to filter messages by the mechanism through which they reached systemd-journald​.

systemctl log-level and systemctl log-target verbs may be used to query and set the LogLevel and LogTarget properties of the service manager​. systemctl service-log-level and systemctl service-log-target may similarly be used for individual services​. (Services must have the BusName= property set and must implement the interface described here​. See systemd.service(5) for details about BusName=​.)

Example

Example ​1. ​Create a simple listener on the bus that implements LogControl1

/* SPDX-License-Identifier: MIT-0 */

/* Implements the LogControl1 interface as per specification:
 * https://www​.freedesktop​.org/software/systemd/man/org​.freedesktop​.LogControl1​.html
 *
 * Compile with 'cc logcontrol-example​.c $(pkg-config --libs --cflags libsystemd)'
 *
 * To get and set properties via busctl:
 *
 * $ busctl --user get-property org​.freedesktop​.Example \
 *                              /org/freedesktop/LogControl1 \
 *                              org​.freedesktop​.LogControl1 \
 *                              SyslogIdentifier
 *   s "example"
 * $ busctl --user get-property org​.freedesktop​.Example \
 *                              /org/freedesktop/LogControl1 \
 *                              org​.freedesktop​.LogControl1 \
 *                              LogTarget
 *   s "journal"
 * $ busctl --user get-property org​.freedesktop​.Example \
 *                              /org/freedesktop/LogControl1 \
 *                              org​.freedesktop​.LogControl1 \
 *                              LogLevel
 *   s "info"
 * $ busctl --user set-property org​.freedesktop​.Example \
 *                              /org/freedesktop/LogControl1 \
 *                              org​.freedesktop​.LogControl1 \
 *                              LogLevel \
 *                              "s" debug
 * $ busctl --user get-property org​.freedesktop​.Example \
 *                              /org/freedesktop/LogControl1 \
 *                              org​.freedesktop​.LogControl1 \
 *                              LogLevel
 *   s "debug"
 */

#include <errno​.h>
#include <stdlib​.h>
#include <stdio​.h>
#include <syslog​.h>
#include <systemd/sd-bus​.h>
#include <systemd/sd-journal​.h>

#define _cleanup_(f) __attribute__((cleanup(f)))

static int log_error(int log_level, int error, const char *str) {
  sd_journal_print(log_level, "%s failed: %s", str, strerror(-error));
  return error;
}

typedef enum LogTarget {
  LOG_TARGET_JOURNAL,
  LOG_TARGET_KMSG,
  LOG_TARGET_SYSLOG,
  LOG_TARGET_CONSOLE,
  _LOG_TARGET_MAX,
} LogTarget;

static const char* const log_target_table[_LOG_TARGET_MAX] = {
  [LOG_TARGET_JOURNAL] = "journal",
  [LOG_TARGET_KMSG]    = "kmsg",
  [LOG_TARGET_SYSLOG]  = "syslog",
  [LOG_TARGET_CONSOLE] = "console",
};

static const char* const log_level_table[LOG_DEBUG + 1] = {
  [LOG_EMERG]   = "emerg",
  [LOG_ALERT]   = "alert",
  [LOG_CRIT]    = "crit",
  [LOG_ERR]     = "err",
  [LOG_WARNING] = "warning",
  [LOG_NOTICE]  = "notice",
  [LOG_INFO]    = "info",
  [LOG_DEBUG]   = "debug",
};

typedef struct object {
  const char *syslog_identifier;
  LogTarget log_target;
  int log_level;
} object;

static int property_get(
                sd_bus *bus,
                const char *path,
                const char *interface,
                const char *property,
                sd_bus_message *reply,
                void *userdata,
                sd_bus_error *error) {

  object *o = userdata;

  if (strcmp(property, "LogLevel") == 0)
    return sd_bus_message_append(reply, "s", log_level_table[o->log_level]);

  if (strcmp(property, "LogTarget") == 0)
    return sd_bus_message_append(reply, "s", log_target_table[o->log_target]);

  if (strcmp(property, "SyslogIdentifier") == 0)
    return sd_bus_message_append(reply, "s", o->syslog_identifier);

  return sd_bus_error_setf(error,
                           SD_BUS_ERROR_UNKNOWN_PROPERTY,
                           "Unknown property '%s'",
                           property);
}

static int property_set(
                sd_bus *bus,
                const char *path,
                const char *interface,
                const char *property,
                sd_bus_message *message,
                void *userdata,
                sd_bus_error *error) {

  object *o = userdata;
  const char *value;
  int r;

  r = sd_bus_message_read(message, "s", &value);
  if (r < 0)
    return r;

  if (strcmp(property, "LogLevel") == 0) {
    int i;
    for (i = 0; i < LOG_DEBUG + 1; i++)
      if (strcmp(value, log_level_table[i]) == 0) {
        o->log_level = i;
        setlogmask(LOG_UPTO(i));
        return 0;
      }

    return sd_bus_error_setf(error,
                             SD_BUS_ERROR_INVALID_ARGS,
                             "Invalid value for LogLevel: '%s'",
                             value);
  }

  if (strcmp(property, "LogTarget") == 0) {
    LogTarget i;
    for (i = 0; i < _LOG_TARGET_MAX; i++)
      if (strcmp(value, log_target_table[i]) == 0) {
        o->log_target = i;
        return 0;
      }

    return sd_bus_error_setf(error,
                             SD_BUS_ERROR_INVALID_ARGS,
                             "Invalid value for LogTarget: '%s'",
                             value);
  }

  return sd_bus_error_setf(error,
                           SD_BUS_ERROR_UNKNOWN_PROPERTY,
                           "Unknown property '%s'",
                           property);
}

/* https://www​.freedesktop​.org/software/systemd/man/sd_bus_add_object​.html
 */
static const sd_bus_vtable vtable[] = {
  SD_BUS_VTABLE_START(0),
  SD_BUS_WRITABLE_PROPERTY(
    "LogLevel", "s",
    property_get, property_set,
    0,
    0),
  SD_BUS_WRITABLE_PROPERTY(
    "LogTarget", "s",
    property_get, property_set,
    0,
    0),
  SD_BUS_PROPERTY(
    "SyslogIdentifier", "s",
    property_get,
    0,
    SD_BUS_VTABLE_PROPERTY_CONST),
  SD_BUS_VTABLE_END
};

int main(int argc, char **argv) {
  /* The bus should be relinquished before the program terminates​. The cleanup
   * attribute allows us to do it nicely and cleanly whenever we exit the
   * block​.
   */
  _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;

  object o = {
    ​.log_level = LOG_INFO,
    ​.log_target = LOG_TARGET_JOURNAL,
    ​.syslog_identifier = "example",
  };
  int r;

  /* https://man7​.org/linux/man-pages/man3/setlogmask​.3​.html
   * Programs using syslog() instead of sd_journal can use this API to cut logs
   * emission at the source​.
   */
  setlogmask(LOG_UPTO(o​.log_level));

  /* Acquire a connection to the bus, letting the library work out the details​.
   * https://www​.freedesktop​.org/software/systemd/man/sd_bus_default​.html
   */
  r = sd_bus_default(&bus);
  if (r < 0)
    return log_error(o​.log_level, r, "sd_bus_default()");

  /* Publish an interface on the bus, specifying our well-known object access
   * path and public interface name​.
   * https://www​.freedesktop​.org/software/systemd/man/sd_bus_add_object​.html
   * https://dbus​.freedesktop​.org/doc/dbus-tutorial​.html
   */
  r = sd_bus_add_object_vtable(bus, NULL,
                               "/org/freedesktop/LogControl1",
                               "org​.freedesktop​.LogControl1",
                               vtable,
                               &o);
  if (r < 0)
    return log_error(o​.log_level, r, "sd_bus_add_object_vtable()");

  /* By default, the service is assigned an ephemeral name​. Also add a fixed
   * one, so that clients know whom to call​.
   * https://www​.freedesktop​.org/software/systemd/man/sd_bus_request_name​.html
   */
  r = sd_bus_request_name(bus, "org​.freedesktop​.Example", 0);
  if (r < 0)
    return log_error(o​.log_level, r, "sd_bus_request_name()");

  for (;;) {
    /* https://www​.freedesktop​.org/software/systemd/man/sd_bus_wait​.html
     */
    r = sd_bus_wait(bus, UINT64_MAX);
    if (r < 0)
      return log_error(o​.log_level, r, "sd_bus_wait()");
    /* https://www​.freedesktop​.org/software/systemd/man/sd_bus_process​.html
     */
    r = sd_bus_process(bus, NULL);
    if (r < 0)
      return log_error(o​.log_level, r, "sd_bus_process()");
  }

  /* https://www​.freedesktop​.org/software/systemd/man/sd_bus_release_name​.html
   */
  r = sd_bus_release_name(bus, "org​.freedesktop​.Example");
  if (r < 0)
    return log_error(o​.log_level, r, "sd_bus_release_name()");

  return 0;
}

This creates a simple server on the bus​. It implements the LogControl1 interface by providing the required properties and allowing to set the writable ones​. It logs at the configured log level using sd_journal_print(3)​.

Note that when porting this example to other D-Bus libraries it might be necessary to add manual client privilege checks, as they typically do not default to the restrictive defaults of sd-bus, where unprivileged access to properties is controlled via the SD_BUS_VTABLE_UNPRIVILEGED flag that is opt-in rather than opt-out​.

See Also

systemd(1), journalctl(1), systemctl(1), systemd.service(5), syslog(3)

Referenced By

systemctl(1), systemd(1), systemd.directives(7), systemd-hostnamed.service(8), systemd-importd.service(8), systemd.index(7), systemd-localed.service(8), systemd-logind.service(8), systemd-machined.service(8), systemd-networkd.service(8), systemd-oomd.service(8), systemd-portabled.service(8), systemd-resolved.service(8), systemd-sysupdated.service(8), systemd-timedated.service(8).

systemd 262~rc2