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 static int log_error(int log_level
, int error
, const char *str
) {
47 sd_journal_print(log_level
, "%s failed: %s", str
, strerror(-error
));
51 typedef enum LogTarget
{
59 static const char* const log_target_table
[_LOG_TARGET_MAX
] = {
60 [LOG_TARGET_JOURNAL
] = "journal",
61 [LOG_TARGET_KMSG
] = "kmsg",
62 [LOG_TARGET_SYSLOG
] = "syslog",
63 [LOG_TARGET_CONSOLE
] = "console",
66 static const char* const log_level_table
[LOG_DEBUG
+ 1] = {
67 [LOG_EMERG
] = "emerg",
68 [LOG_ALERT
] = "alert",
71 [LOG_WARNING
] = "warning",
72 [LOG_NOTICE
] = "notice",
74 [LOG_DEBUG
] = "debug",
77 typedef struct object
{
78 const char *syslog_identifier
;
83 static int property_get(
86 const char *interface
,
88 sd_bus_message
*reply
,
90 sd_bus_error
*error
) {
94 if (strcmp(property
, "LogLevel") == 0)
95 return sd_bus_message_append(reply
, "s", log_level_table
[o
->log_level
]);
97 if (strcmp(property
, "LogTarget") == 0)
98 return sd_bus_message_append(reply
, "s", log_target_table
[o
->log_target
]);
100 if (strcmp(property
, "SyslogIdentifier") == 0)
101 return sd_bus_message_append(reply
, "s", o
->syslog_identifier
);
103 return sd_bus_error_setf(error
,
104 SD_BUS_ERROR_UNKNOWN_PROPERTY
,
105 "Unknown property '%s'",
109 static int property_set(
112 const char *interface
,
113 const char *property
,
114 sd_bus_message
*message
,
116 sd_bus_error
*error
) {
118 object
*o
= userdata
;
122 r
= sd_bus_message_read(message
, "s", &value
);
126 if (strcmp(property
, "LogLevel") == 0) {
128 for (i
= 0; i
< LOG_DEBUG
+ 1; i
++)
129 if (strcmp(value
, log_level_table
[i
]) == 0) {
131 setlogmask(LOG_UPTO(i
));
135 return sd_bus_error_setf(error
,
136 SD_BUS_ERROR_INVALID_ARGS
,
137 "Invalid value for LogLevel: '%s'",
141 if (strcmp(property
, "LogTarget") == 0) {
143 for (i
= 0; i
< _LOG_TARGET_MAX
; i
++)
144 if (strcmp(value
, log_target_table
[i
]) == 0) {
149 return sd_bus_error_setf(error
,
150 SD_BUS_ERROR_INVALID_ARGS
,
151 "Invalid value for LogTarget: '%s'",
155 return sd_bus_error_setf(error
,
156 SD_BUS_ERROR_UNKNOWN_PROPERTY
,
157 "Unknown property '%s'",
161 /* https://www.freedesktop.org/software/systemd/man/sd_bus_add_object.html
163 static const sd_bus_vtable vtable
[] = {
164 SD_BUS_VTABLE_START(0),
165 SD_BUS_WRITABLE_PROPERTY(
167 property_get
, property_set
,
170 SD_BUS_WRITABLE_PROPERTY(
172 property_get
, property_set
,
176 "SyslogIdentifier", "s",
179 SD_BUS_VTABLE_PROPERTY_CONST
),
183 int main(int argc
, char **argv
) {
184 /* The bus should be relinquished before the program terminates. The cleanup
185 * attribute allows us to do it nicely and cleanly whenever we exit the
188 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
191 .log_level
= LOG_INFO
,
192 .log_target
= LOG_TARGET_JOURNAL
,
193 .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 r
= sd_bus_default(&bus
);
208 return log_error(o
.log_level
, r
, "sd_bus_default()");
210 /* Publish an interface on the bus, specifying our well-known object access
211 * path and public interface name.
212 * https://www.freedesktop.org/software/systemd/man/sd_bus_add_object.html
213 * https://dbus.freedesktop.org/doc/dbus-tutorial.html
215 r
= sd_bus_add_object_vtable(bus
, NULL
,
216 "/org/freedesktop/LogControl1",
217 "org.freedesktop.LogControl1",
221 return log_error(o
.log_level
, r
, "sd_bus_add_object_vtable()");
223 /* By default the service is assigned an ephemeral name. Also add a fixed
224 * one, so that clients know whom to call.
225 * https://www.freedesktop.org/software/systemd/man/sd_bus_request_name.html
227 r
= sd_bus_request_name(bus
, "org.freedesktop.Example", 0);
229 return log_error(o
.log_level
, r
, "sd_bus_request_name()");
232 /* https://www.freedesktop.org/software/systemd/man/sd_bus_wait.html
234 r
= sd_bus_wait(bus
, UINT64_MAX
);
236 return log_error(o
.log_level
, r
, "sd_bus_wait()");
237 /* https://www.freedesktop.org/software/systemd/man/sd_bus_process.html
239 r
= sd_bus_process(bus
, NULL
);
241 return log_error(o
.log_level
, r
, "sd_bus_process()");
244 /* https://www.freedesktop.org/software/systemd/man/sd_bus_release_name.html
246 r
= sd_bus_release_name(bus
, "org.freedesktop.Example");
248 return log_error(o
.log_level
, r
, "sd_bus_release_name()");