]> git.ipfire.org Git - thirdparty/systemd.git/blob - man/logcontrol-example.c
systemctl: do not fall back to StartUnit automatically for sleep operations
[thirdparty/systemd.git] / man / logcontrol-example.c
1 /* SPDX-License-Identifier: MIT-0 */
2
3 /* Implements the LogControl1 interface as per specification:
4 * https://www.freedesktop.org/software/systemd/man/org.freedesktop.LogControl1.html
5 *
6 * Compile with 'cc logcontrol-example.c $(pkg-config --libs --cflags libsystemd)'
7 *
8 * To get and set properties via busctl:
9 *
10 * $ busctl --user get-property org.freedesktop.Example \
11 * /org/freedesktop/LogControl1 \
12 * org.freedesktop.LogControl1 \
13 * SyslogIdentifier
14 * s "example"
15 * $ busctl --user get-property org.freedesktop.Example \
16 * /org/freedesktop/LogControl1 \
17 * org.freedesktop.LogControl1 \
18 * LogTarget
19 * s "journal"
20 * $ busctl --user get-property org.freedesktop.Example \
21 * /org/freedesktop/LogControl1 \
22 * org.freedesktop.LogControl1 \
23 * LogLevel
24 * s "info"
25 * $ busctl --user set-property org.freedesktop.Example \
26 * /org/freedesktop/LogControl1 \
27 * org.freedesktop.LogControl1 \
28 * LogLevel \
29 * "s" debug
30 * $ busctl --user get-property org.freedesktop.Example \
31 * /org/freedesktop/LogControl1 \
32 * org.freedesktop.LogControl1 \
33 * LogLevel
34 * s "debug"
35 */
36
37 #include <errno.h>
38 #include <stdlib.h>
39 #include <stdio.h>
40 #include <syslog.h>
41 #include <systemd/sd-bus.h>
42 #include <systemd/sd-journal.h>
43
44 #define _cleanup_(f) __attribute__((cleanup(f)))
45
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));
48 return error;
49 }
50
51 typedef enum LogTarget {
52 LOG_TARGET_JOURNAL,
53 LOG_TARGET_KMSG,
54 LOG_TARGET_SYSLOG,
55 LOG_TARGET_CONSOLE,
56 _LOG_TARGET_MAX,
57 } LogTarget;
58
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",
64 };
65
66 static const char* const log_level_table[LOG_DEBUG + 1] = {
67 [LOG_EMERG] = "emerg",
68 [LOG_ALERT] = "alert",
69 [LOG_CRIT] = "crit",
70 [LOG_ERR] = "err",
71 [LOG_WARNING] = "warning",
72 [LOG_NOTICE] = "notice",
73 [LOG_INFO] = "info",
74 [LOG_DEBUG] = "debug",
75 };
76
77 typedef struct object {
78 const char *syslog_identifier;
79 LogTarget log_target;
80 int log_level;
81 } object;
82
83 static int property_get(
84 sd_bus *bus,
85 const char *path,
86 const char *interface,
87 const char *property,
88 sd_bus_message *reply,
89 void *userdata,
90 sd_bus_error *error) {
91
92 object *o = userdata;
93
94 if (strcmp(property, "LogLevel") == 0)
95 return sd_bus_message_append(reply, "s", log_level_table[o->log_level]);
96
97 if (strcmp(property, "LogTarget") == 0)
98 return sd_bus_message_append(reply, "s", log_target_table[o->log_target]);
99
100 if (strcmp(property, "SyslogIdentifier") == 0)
101 return sd_bus_message_append(reply, "s", o->syslog_identifier);
102
103 return sd_bus_error_setf(error,
104 SD_BUS_ERROR_UNKNOWN_PROPERTY,
105 "Unknown property '%s'",
106 property);
107 }
108
109 static int property_set(
110 sd_bus *bus,
111 const char *path,
112 const char *interface,
113 const char *property,
114 sd_bus_message *message,
115 void *userdata,
116 sd_bus_error *error) {
117
118 object *o = userdata;
119 const char *value;
120 int r;
121
122 r = sd_bus_message_read(message, "s", &value);
123 if (r < 0)
124 return r;
125
126 if (strcmp(property, "LogLevel") == 0) {
127 int i;
128 for (i = 0; i < LOG_DEBUG + 1; i++)
129 if (strcmp(value, log_level_table[i]) == 0) {
130 o->log_level = i;
131 setlogmask(LOG_UPTO(i));
132 return 0;
133 }
134
135 return sd_bus_error_setf(error,
136 SD_BUS_ERROR_INVALID_ARGS,
137 "Invalid value for LogLevel: '%s'",
138 value);
139 }
140
141 if (strcmp(property, "LogTarget") == 0) {
142 LogTarget i;
143 for (i = 0; i < _LOG_TARGET_MAX; i++)
144 if (strcmp(value, log_target_table[i]) == 0) {
145 o->log_target = i;
146 return 0;
147 }
148
149 return sd_bus_error_setf(error,
150 SD_BUS_ERROR_INVALID_ARGS,
151 "Invalid value for LogTarget: '%s'",
152 value);
153 }
154
155 return sd_bus_error_setf(error,
156 SD_BUS_ERROR_UNKNOWN_PROPERTY,
157 "Unknown property '%s'",
158 property);
159 }
160
161 /* https://www.freedesktop.org/software/systemd/man/sd_bus_add_object.html
162 */
163 static const sd_bus_vtable vtable[] = {
164 SD_BUS_VTABLE_START(0),
165 SD_BUS_WRITABLE_PROPERTY(
166 "LogLevel", "s",
167 property_get, property_set,
168 0,
169 0),
170 SD_BUS_WRITABLE_PROPERTY(
171 "LogTarget", "s",
172 property_get, property_set,
173 0,
174 0),
175 SD_BUS_PROPERTY(
176 "SyslogIdentifier", "s",
177 property_get,
178 0,
179 SD_BUS_VTABLE_PROPERTY_CONST),
180 SD_BUS_VTABLE_END
181 };
182
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
186 * block.
187 */
188 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
189
190 object o = {
191 .log_level = LOG_INFO,
192 .log_target = LOG_TARGET_JOURNAL,
193 .syslog_identifier = "example",
194 };
195 int r;
196
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.
200 */
201 setlogmask(LOG_UPTO(o.log_level));
202
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
205 */
206 r = sd_bus_default(&bus);
207 if (r < 0)
208 return log_error(o.log_level, r, "sd_bus_default()");
209
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
214 */
215 r = sd_bus_add_object_vtable(bus, NULL,
216 "/org/freedesktop/LogControl1",
217 "org.freedesktop.LogControl1",
218 vtable,
219 &o);
220 if (r < 0)
221 return log_error(o.log_level, r, "sd_bus_add_object_vtable()");
222
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
226 */
227 r = sd_bus_request_name(bus, "org.freedesktop.Example", 0);
228 if (r < 0)
229 return log_error(o.log_level, r, "sd_bus_request_name()");
230
231 for (;;) {
232 /* https://www.freedesktop.org/software/systemd/man/sd_bus_wait.html
233 */
234 r = sd_bus_wait(bus, UINT64_MAX);
235 if (r < 0)
236 return log_error(o.log_level, r, "sd_bus_wait()");
237 /* https://www.freedesktop.org/software/systemd/man/sd_bus_process.html
238 */
239 r = sd_bus_process(bus, NULL);
240 if (r < 0)
241 return log_error(o.log_level, r, "sd_bus_process()");
242 }
243
244 /* https://www.freedesktop.org/software/systemd/man/sd_bus_release_name.html
245 */
246 r = sd_bus_release_name(bus, "org.freedesktop.Example");
247 if (r < 0)
248 return log_error(o.log_level, r, "sd_bus_release_name()");
249
250 return 0;
251 }