1ORG.FREEDESKTOP.LOGCONTROL1o(r5g).freedesktop.LogContOrRoGl.1FREEDESKTOP.LOGCONTROL1(5)
2
3
4
6 org.freedesktop.LogControl1 - D-Bus interface to query and set logging
7 configuration
8
10 org.freedesktop.LogControl1 is a generic interface that is intended to
11 be used by any daemon which allows the log level and target to be set
12 over D-Bus. It is implemented by various daemons that are part of the
13 systemd(1) suite.
14
15 It is assumed that those settings are global for the whole program, so
16 a fixed object path is used. The interface should always be available
17 under the path /org/freedesktop/LogControl1.
18
20 The following interface is exposed:
21
22 node /org/freedesktop/LogControl1 {
23 interface org.freedesktop.LogControl1 {
24 properties:
25 @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
26 @org.freedesktop.systemd1.Privileged("true")
27 readwrite s LogLevel = '...';
28 @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
29 @org.freedesktop.systemd1.Privileged("true")
30 readwrite s LogTarget = '...';
31 @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
32 readonly s SyslogIdentifier = '...';
33 };
34 interface org.freedesktop.DBus.Peer { ... };
35 interface org.freedesktop.DBus.Introspectable { ... };
36 interface org.freedesktop.DBus.Properties { ... };
37 };
38
39
40
41
42
43
44 Properties
45 LogLevel describes the syslog(3)-style log-level, and should be one of
46 "emerg", "alert", "crit", "err", "warning", "notice", "info", "debug",
47 in order of increasing verbosity.
48
49 LogTarget describes the log target (mechanism). It should be one of
50 "console" (log to the console or standard output), "kmsg" (log to the
51 kernel ring buffer), "journal" (log to the journal natively, see
52 systemd-journald.service(8)), "syslog" (log using the syslog(3) call).
53
54 Those two properties are writable, so they may be set by sufficiently
55 privileged users.
56
57 SyslogIdentifier is a read-only property that shows the "syslog
58 identifier". It is a short string that identifies the program that is
59 the source of log messages that is passed to the syslog(3) call.
60
62 journalctl option -p/--priority= may be used to filter log messages by
63 log level, option -t/--identifier= may be used to by the syslog
64 identifier, and filters like "_TRANSPORT=syslog", "_TRANSPORT=journal",
65 and "_TRANSPORT=kernel" may be used to filter messages by the mechanism
66 through which they reached systemd-journald.
67
68 systemctl log-level and systemctl log-target verbs may be used to query
69 and set the LogLevel and LogTarget properties of the service manager.
70 systemctl service-log-level and systemctl service-log-target may
71 similarly be used for individual services. (Services must have the
72 BusName= property set and must implement the interface described here.
73 See systemd.service(5) for details about BusName=.)
74
76 Example 1. Create a simple listener on the bus that implements
77 LogControl1
78
79 /* SPDX-License-Identifier: MIT-0 */
80
81 /* Implements the LogControl1 interface as per specification:
82 * https://www.freedesktop.org/software/systemd/man/org.freedesktop.LogControl1.html
83 *
84 * Compile with 'cc logcontrol-example.c $(pkg-config --libs --cflags libsystemd)'
85 *
86 * To get and set properties via busctl:
87 *
88 * $ busctl --user get-property org.freedesktop.Example \
89 * /org/freedesktop/LogControl1 \
90 * org.freedesktop.LogControl1 \
91 * SyslogIdentifier
92 * s "example"
93 * $ busctl --user get-property org.freedesktop.Example \
94 * /org/freedesktop/LogControl1 \
95 * org.freedesktop.LogControl1 \
96 * LogTarget
97 * s "journal"
98 * $ busctl --user get-property org.freedesktop.Example \
99 * /org/freedesktop/LogControl1 \
100 * org.freedesktop.LogControl1 \
101 * LogLevel
102 * s "info"
103 * $ busctl --user set-property org.freedesktop.Example \
104 * /org/freedesktop/LogControl1 \
105 * org.freedesktop.LogControl1 \
106 * LogLevel \
107 * "s" debug
108 * $ busctl --user get-property org.freedesktop.Example \
109 * /org/freedesktop/LogControl1 \
110 * org.freedesktop.LogControl1 \
111 * LogLevel
112 * s "debug"
113 */
114
115 #include <errno.h>
116 #include <stdlib.h>
117 #include <stdio.h>
118 #include <syslog.h>
119 #include <systemd/sd-bus.h>
120 #include <systemd/sd-journal.h>
121
122 #define _cleanup_(f) __attribute__((cleanup(f)))
123
124 #define check(log_level, x) ({ \
125 int _r = (x); \
126 errno = _r < 0 ? -_r : 0; \
127 sd_journal_print((log_level), #x ": %m"); \
128 if (_r < 0) \
129 return EXIT_FAILURE; \
130 })
131
132 typedef enum LogTarget {
133 LOG_TARGET_JOURNAL,
134 LOG_TARGET_KMSG,
135 LOG_TARGET_SYSLOG,
136 LOG_TARGET_CONSOLE,
137 _LOG_TARGET_MAX,
138 } LogTarget;
139
140 static const char* const log_target_table[_LOG_TARGET_MAX] = {
141 [LOG_TARGET_JOURNAL] = "journal",
142 [LOG_TARGET_KMSG] = "kmsg",
143 [LOG_TARGET_SYSLOG] = "syslog",
144 [LOG_TARGET_CONSOLE] = "console",
145 };
146
147 static const char* const log_level_table[LOG_DEBUG + 1] = {
148 [LOG_EMERG] = "emerg",
149 [LOG_ALERT] = "alert",
150 [LOG_CRIT] = "crit",
151 [LOG_ERR] = "err",
152 [LOG_WARNING] = "warning",
153 [LOG_NOTICE] = "notice",
154 [LOG_INFO] = "info",
155 [LOG_DEBUG] = "debug",
156 };
157
158 typedef struct object {
159 const char *syslog_identifier;
160 LogTarget log_target;
161 int log_level;
162 } object;
163
164 static int property_get(
165 sd_bus *bus,
166 const char *path,
167 const char *interface,
168 const char *property,
169 sd_bus_message *reply,
170 void *userdata,
171 sd_bus_error *error) {
172
173 object *o = userdata;
174
175 if (strcmp(property, "LogLevel") == 0)
176 return sd_bus_message_append(reply, "s", log_level_table[o->log_level]);
177
178 if (strcmp(property, "LogTarget") == 0)
179 return sd_bus_message_append(reply, "s", log_target_table[o->log_target]);
180
181 if (strcmp(property, "SyslogIdentifier") == 0)
182 return sd_bus_message_append(reply, "s", o->syslog_identifier);
183
184 return sd_bus_error_setf(error,
185 SD_BUS_ERROR_UNKNOWN_PROPERTY,
186 "Unknown property '%s'",
187 property);
188 }
189
190 static int property_set(
191 sd_bus *bus,
192 const char *path,
193 const char *interface,
194 const char *property,
195 sd_bus_message *message,
196 void *userdata,
197 sd_bus_error *error) {
198
199 object *o = userdata;
200 const char *value;
201 int r;
202
203 r = sd_bus_message_read(message, "s", &value);
204 if (r < 0)
205 return r;
206
207 if (strcmp(property, "LogLevel") == 0) {
208 for (int i = 0; i < LOG_DEBUG + 1; i++)
209 if (strcmp(value, log_level_table[i]) == 0) {
210 o->log_level = i;
211 setlogmask(LOG_UPTO(i));
212 return 0;
213 }
214
215 return sd_bus_error_setf(error,
216 SD_BUS_ERROR_INVALID_ARGS,
217 "Invalid value for LogLevel: '%s'",
218 value);
219 }
220
221 if (strcmp(property, "LogTarget") == 0) {
222 for (LogTarget i = 0; i < _LOG_TARGET_MAX; i++)
223 if (strcmp(value, log_target_table[i]) == 0) {
224 o->log_target = i;
225 return 0;
226 }
227
228 return sd_bus_error_setf(error,
229 SD_BUS_ERROR_INVALID_ARGS,
230 "Invalid value for LogTarget: '%s'",
231 value);
232 }
233
234 return sd_bus_error_setf(error,
235 SD_BUS_ERROR_UNKNOWN_PROPERTY,
236 "Unknown property '%s'",
237 property);
238 }
239
240 /* https://www.freedesktop.org/software/systemd/man/sd_bus_add_object.html
241 */
242 static const sd_bus_vtable vtable[] = {
243 SD_BUS_VTABLE_START(0),
244 SD_BUS_WRITABLE_PROPERTY(
245 "LogLevel", "s",
246 property_get, property_set,
247 0,
248 0),
249 SD_BUS_WRITABLE_PROPERTY(
250 "LogTarget", "s",
251 property_get, property_set,
252 0,
253 0),
254 SD_BUS_PROPERTY(
255 "SyslogIdentifier", "s",
256 property_get,
257 0,
258 SD_BUS_VTABLE_PROPERTY_CONST),
259 SD_BUS_VTABLE_END
260 };
261
262 int main(int argc, char **argv) {
263 /* The bus should be relinquished before the program terminates. The cleanup
264 * attribute allows us to do it nicely and cleanly whenever we exit the
265 * block.
266 */
267 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
268
269 object o = {
270 .log_level = LOG_INFO,
271 .log_target = LOG_TARGET_JOURNAL,
272 .syslog_identifier = "example",
273 };
274
275 /* https://man7.org/linux/man-pages/man3/setlogmask.3.html
276 * Programs using syslog() instead of sd_journal can use this API to cut logs
277 * emission at the source.
278 */
279 setlogmask(LOG_UPTO(o.log_level));
280
281 /* Acquire a connection to the bus, letting the library work out the details.
282 * https://www.freedesktop.org/software/systemd/man/sd_bus_default.html
283 */
284 check(o.log_level, sd_bus_default(&bus));
285
286 /* Publish an interface on the bus, specifying our well-known object access
287 * path and public interface name.
288 * https://www.freedesktop.org/software/systemd/man/sd_bus_add_object.html
289 * https://dbus.freedesktop.org/doc/dbus-tutorial.html
290 */
291 check(o.log_level, sd_bus_add_object_vtable(bus, NULL,
292 "/org/freedesktop/LogControl1",
293 "org.freedesktop.LogControl1",
294 vtable,
295 &o));
296
297 /* By default the service is assigned an ephemeral name. Also add a fixed
298 * one, so that clients know whom to call.
299 * https://www.freedesktop.org/software/systemd/man/sd_bus_request_name.html
300 */
301 check(o.log_level, sd_bus_request_name(bus, "org.freedesktop.Example", 0));
302
303 for (;;) {
304 /* https://www.freedesktop.org/software/systemd/man/sd_bus_wait.html
305 */
306 check(o.log_level, sd_bus_wait(bus, UINT64_MAX));
307 /* https://www.freedesktop.org/software/systemd/man/sd_bus_process.html
308 */
309 check(o.log_level, sd_bus_process(bus, NULL));
310 }
311
312 /* https://www.freedesktop.org/software/systemd/man/sd_bus_release_name.html
313 */
314 check(o.log_level, sd_bus_release_name(bus, "org.freedesktop.Example"));
315
316 return 0;
317 }
318
319 This creates a simple server on the bus. It implements the LogControl1
320 interface by providing the required properties and allowing to set the
321 writable ones. It logs at the configured log level using
322 sd_journal_print(3).
323
325 systemd(1), journalctl(1), systemctl(1), systemd.service(5), syslog(3)
326
327
328
329systemd 254 ORG.FREEDESKTOP.LOGCONTROL1(5)