1 /* SPDX-License-Identifier: MIT-0 */
3 /* Implements the LogControl1 interface as per specification:
4 * https://www.freedesktop.org/software/systemd/man/org.freedesktop.LogControl1.html
6 * Compile with 'cc logcontrol-example.c $(pkg-config --libs --cflags libsystemd)'
8 * To get and set properties via busctl:
10 * $ busctl --user get-property org.freedesktop.Example \
11 * /org/freedesktop/LogControl1 \
12 * org.freedesktop.LogControl1 \
15 * $ busctl --user get-property org.freedesktop.Example \
16 * /org/freedesktop/LogControl1 \
17 * org.freedesktop.LogControl1 \
20 * $ busctl --user get-property org.freedesktop.Example \
21 * /org/freedesktop/LogControl1 \
22 * org.freedesktop.LogControl1 \
25 * $ busctl --user set-property org.freedesktop.Example \
26 * /org/freedesktop/LogControl1 \
27 * org.freedesktop.LogControl1 \
30 * $ busctl --user get-property org.freedesktop.Example \
31 * /org/freedesktop/LogControl1 \
32 * org.freedesktop.LogControl1 \
41 #include <systemd/sd-bus.h>
42 #include <systemd/sd-journal.h>
44 #define _cleanup_(f) __attribute__((cleanup(f)))
46 #define check(log_level, x) ({ \
48 errno = _r < 0 ? -_r : 0; \
49 sd_journal_print((log_level), #x ": %m"); \
51 return EXIT_FAILURE; \
54 typedef enum LogTarget
{
62 static const char* const log_target_table
[_LOG_TARGET_MAX
] = {
63 [LOG_TARGET_JOURNAL
] = "journal",
64 [LOG_TARGET_KMSG
] = "kmsg",
65 [LOG_TARGET_SYSLOG
] = "syslog",
66 [LOG_TARGET_CONSOLE
] = "console",
69 static const char* const log_level_table
[LOG_DEBUG
+ 1] = {
70 [LOG_EMERG
] = "emerg",
71 [LOG_ALERT
] = "alert",
74 [LOG_WARNING
] = "warning",
75 [LOG_NOTICE
] = "notice",
77 [LOG_DEBUG
] = "debug",
80 typedef struct object
{
81 const char *syslog_identifier
;
86 static int property_get(
89 const char *interface
,
91 sd_bus_message
*reply
,
93 sd_bus_error
*error
) {
97 if (strcmp(property
, "LogLevel") == 0)
98 return sd_bus_message_append(reply
, "s", log_level_table
[o
->log_level
]);
100 if (strcmp(property
, "LogTarget") == 0)
101 return sd_bus_message_append(reply
, "s", log_target_table
[o
->log_target
]);
103 if (strcmp(property
, "SyslogIdentifier") == 0)
104 return sd_bus_message_append(reply
, "s", o
->syslog_identifier
);
106 return sd_bus_error_setf(error
,
107 SD_BUS_ERROR_UNKNOWN_PROPERTY
,
108 "Unknown property '%s'",
112 static int property_set(
115 const char *interface
,
116 const char *property
,
117 sd_bus_message
*message
,
119 sd_bus_error
*error
) {
121 object
*o
= userdata
;
125 r
= sd_bus_message_read(message
, "s", &value
);
129 if (strcmp(property
, "LogLevel") == 0) {
130 for (int i
= 0; i
< LOG_DEBUG
+ 1; i
++)
131 if (strcmp(value
, log_level_table
[i
]) == 0) {
133 setlogmask(LOG_UPTO(i
));
137 return sd_bus_error_setf(error
,
138 SD_BUS_ERROR_INVALID_ARGS
,
139 "Invalid value for LogLevel: '%s'",
143 if (strcmp(property
, "LogTarget") == 0) {
144 for (LogTarget i
= 0; i
< _LOG_TARGET_MAX
; i
++)
145 if (strcmp(value
, log_target_table
[i
]) == 0) {
150 return sd_bus_error_setf(error
,
151 SD_BUS_ERROR_INVALID_ARGS
,
152 "Invalid value for LogTarget: '%s'",
156 return sd_bus_error_setf(error
,
157 SD_BUS_ERROR_UNKNOWN_PROPERTY
,
158 "Unknown property '%s'",
162 /* https://www.freedesktop.org/software/systemd/man/sd_bus_add_object.html
164 static const sd_bus_vtable vtable
[] = {
165 SD_BUS_VTABLE_START(0),
166 SD_BUS_WRITABLE_PROPERTY(
168 property_get
, property_set
,
171 SD_BUS_WRITABLE_PROPERTY(
173 property_get
, property_set
,
177 "SyslogIdentifier", "s",
180 SD_BUS_VTABLE_PROPERTY_CONST
),
184 int main(int argc
, char **argv
) {
185 /* The bus should be relinquished before the program terminates. The cleanup
186 * attribute allows us to do it nicely and cleanly whenever we exit the
189 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
192 .log_level
= LOG_INFO
,
193 .log_target
= LOG_TARGET_JOURNAL
,
194 .syslog_identifier
= "example",
197 /* https://man7.org/linux/man-pages/man3/setlogmask.3.html
198 * Programs using syslog() instead of sd_journal can use this API to cut logs
199 * emission at the source.
201 setlogmask(LOG_UPTO(o
.log_level
));
203 /* Acquire a connection to the bus, letting the library work out the details.
204 * https://www.freedesktop.org/software/systemd/man/sd_bus_default.html
206 check(o
.log_level
, sd_bus_default(&bus
));
208 /* Publish an interface on the bus, specifying our well-known object access
209 * path and public interface name.
210 * https://www.freedesktop.org/software/systemd/man/sd_bus_add_object.html
211 * https://dbus.freedesktop.org/doc/dbus-tutorial.html
213 check(o
.log_level
, sd_bus_add_object_vtable(bus
, NULL
,
214 "/org/freedesktop/LogControl1",
215 "org.freedesktop.LogControl1",
219 /* By default the service is assigned an ephemeral name. Also add a fixed
220 * one, so that clients know whom to call.
221 * https://www.freedesktop.org/software/systemd/man/sd_bus_request_name.html
223 check(o
.log_level
, sd_bus_request_name(bus
, "org.freedesktop.Example", 0));
226 /* https://www.freedesktop.org/software/systemd/man/sd_bus_wait.html
228 check(o
.log_level
, sd_bus_wait(bus
, UINT64_MAX
));
229 /* https://www.freedesktop.org/software/systemd/man/sd_bus_process.html
231 check(o
.log_level
, sd_bus_process(bus
, NULL
));
234 /* https://www.freedesktop.org/software/systemd/man/sd_bus_release_name.html
236 check(o
.log_level
, sd_bus_release_name(bus
, "org.freedesktop.Example"));