'\" t .TH "ORG\&.FREEDESKTOP\&.LOGCONTROL1" "5" "" "systemd 256.8" "org.freedesktop.LogControl1" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" org.freedesktop.LogControl1 \- D\-Bus interface to query and set logging configuration .SH "INTRODUCTION" .PP 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 \fBsystemd\fR(1) suite\&. .PP 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\&. .SH "DESCRIPTION" .PP The following interface is exposed: .sp .if n \{\ .RS 4 .\} .nf node /org/freedesktop/LogControl1 { interface org\&.freedesktop\&.LogControl1 { properties: @org\&.freedesktop\&.DBus\&.Property\&.EmitsChangedSignal("false") @org\&.freedesktop\&.systemd1\&.Privileged("true") readwrite s LogLevel = \*(Aq\&.\&.\&.\*(Aq; @org\&.freedesktop\&.DBus\&.Property\&.EmitsChangedSignal("false") @org\&.freedesktop\&.systemd1\&.Privileged("true") readwrite s LogTarget = \*(Aq\&.\&.\&.\*(Aq; @org\&.freedesktop\&.DBus\&.Property\&.EmitsChangedSignal("false") readonly s SyslogIdentifier = \*(Aq\&.\&.\&.\*(Aq; }; interface org\&.freedesktop\&.DBus\&.Peer { \&.\&.\&. }; interface org\&.freedesktop\&.DBus\&.Introspectable { \&.\&.\&. }; interface org\&.freedesktop\&.DBus\&.Properties { \&.\&.\&. }; }; .fi .if n \{\ .RE .\} .SS "Properties" .PP \fILogLevel\fR describes the \fBsyslog\fR(3)\-style log\-level, and should be one of "emerg", "alert", "crit", "err", "warning", "notice", "info", "debug", in order of increasing verbosity\&. .PP \fILogTarget\fR 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 \fBsystemd-journald.service\fR(8)), "syslog" (log using the \fBsyslog\fR(3) call)\&. .PP Those two properties are writable, so they may be set by sufficiently privileged users\&. .PP \fISyslogIdentifier\fR 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 \fBsyslog\fR(3) call\&. .SH "TOOLS" .PP \fBjournalctl\fR option \fB\-p\fR/\fB\-\-priority=\fR may be used to filter log messages by log level, option \fB\-t\fR/\fB\-\-identifier=\fR 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 \fBsystemd\-journald\fR\&. .PP \fBsystemctl log\-level\fR and \fBsystemctl log\-target\fR verbs may be used to query and set the \fILogLevel\fR and \fILogTarget\fR properties of the service manager\&. \fBsystemctl service\-log\-level\fR and \fBsystemctl service\-log\-target\fR may similarly be used for individual services\&. (Services must have the \fIBusName=\fR property set and must implement the interface described here\&. See \fBsystemd.service\fR(5) for details about \fIBusName=\fR\&.) .SH "EXAMPLE" .PP \fBExample\ \&1.\ \&Create a simple listener on the bus that implements LogControl1\fR .sp .if n \{\ .RS 4 .\} .nf /* 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 \*(Aqcc logcontrol\-example\&.c $(pkg\-config \-\-libs \-\-cflags libsystemd)\*(Aq * * To get and set properties via busctl: * * $ busctl \-\-user get\-property org\&.freedesktop\&.Example \e * /org/freedesktop/LogControl1 \e * org\&.freedesktop\&.LogControl1 \e * SyslogIdentifier * s "example" * $ busctl \-\-user get\-property org\&.freedesktop\&.Example \e * /org/freedesktop/LogControl1 \e * org\&.freedesktop\&.LogControl1 \e * LogTarget * s "journal" * $ busctl \-\-user get\-property org\&.freedesktop\&.Example \e * /org/freedesktop/LogControl1 \e * org\&.freedesktop\&.LogControl1 \e * LogLevel * s "info" * $ busctl \-\-user set\-property org\&.freedesktop\&.Example \e * /org/freedesktop/LogControl1 \e * org\&.freedesktop\&.LogControl1 \e * LogLevel \e * "s" debug * $ busctl \-\-user get\-property org\&.freedesktop\&.Example \e * /org/freedesktop/LogControl1 \e * org\&.freedesktop\&.LogControl1 \e * LogLevel * s "debug" */ #include #include #include #include #include #include #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 \*(Aq%s\*(Aq", 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: \*(Aq%s\*(Aq", 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: \*(Aq%s\*(Aq", value); } return sd_bus_error_setf(error, SD_BUS_ERROR_UNKNOWN_PROPERTY, "Unknown property \*(Aq%s\*(Aq", 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; } .fi .if n \{\ .RE .\} .PP 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 \fBsd_journal_print\fR(3)\&. .SH "SEE ALSO" .PP \fBsystemd\fR(1), \fBjournalctl\fR(1), \fBsystemctl\fR(1), \fBsystemd.service\fR(5), \fBsyslog\fR(3)