diff options
Diffstat (limited to 'man/logcontrol-example.c')
-rw-r--r-- | man/logcontrol-example.c | 239 |
1 files changed, 239 insertions, 0 deletions
diff --git a/man/logcontrol-example.c b/man/logcontrol-example.c new file mode 100644 index 0000000..c199ec7 --- /dev/null +++ b/man/logcontrol-example.c @@ -0,0 +1,239 @@ +/* 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))) + +#define check(log_level, x) ({ \ + int _r = (x); \ + errno = _r < 0 ? -_r : 0; \ + sd_journal_print((log_level), #x ": %m"); \ + if (_r < 0) \ + return EXIT_FAILURE; \ + }) + +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) { + for (int 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) { + for (LogTarget 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", + }; + + /* 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 + */ + check(o.log_level, sd_bus_default(&bus)); + + /* 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 + */ + check(o.log_level, sd_bus_add_object_vtable(bus, NULL, + "/org/freedesktop/LogControl1", + "org.freedesktop.LogControl1", + vtable, + &o)); + + /* 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 + */ + check(o.log_level, sd_bus_request_name(bus, "org.freedesktop.Example", 0)); + + for (;;) { + /* https://www.freedesktop.org/software/systemd/man/sd_bus_wait.html + */ + check(o.log_level, sd_bus_wait(bus, UINT64_MAX)); + /* https://www.freedesktop.org/software/systemd/man/sd_bus_process.html + */ + check(o.log_level, sd_bus_process(bus, NULL)); + } + + /* https://www.freedesktop.org/software/systemd/man/sd_bus_release_name.html + */ + check(o.log_level, sd_bus_release_name(bus, "org.freedesktop.Example")); + + return 0; +} |