mirror of
https://github.com/systemd/systemd.git
synced 2024-12-26 03:22:00 +03:00
Merge pull request #27357 from bluca/example_logcontrol
man: add working example to LogControl1 manpage
This commit is contained in:
commit
3539b3a9f1
232
man/logcontrol-example.c
Normal file
232
man/logcontrol-example.c
Normal file
@ -0,0 +1,232 @@
|
||||
/* 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 'cc logcontrol-example.c $(pkg-config --libs --cflags libsystemd)'
|
||||
*
|
||||
* To get and set properties via busctl:
|
||||
*
|
||||
* $ busctl --user get-property org.freedesktop.Example \
|
||||
* /org/freedesktop/LogControl1 \
|
||||
* org.freedesktop.LogControl1 \
|
||||
* SyslogIdentifier
|
||||
* s "example"
|
||||
* $ busctl --user get-property org.freedesktop.Example \
|
||||
* /org/freedesktop/LogControl1 \
|
||||
* org.freedesktop.LogControl1 \
|
||||
* LogTarget
|
||||
* s "journal"
|
||||
* $ busctl --user get-property org.freedesktop.Example \
|
||||
* /org/freedesktop/LogControl1 \
|
||||
* org.freedesktop.LogControl1 \
|
||||
* LogLevel
|
||||
* s "info"
|
||||
* $ busctl --user set-property org.freedesktop.Example \
|
||||
* /org/freedesktop/LogControl1 \
|
||||
* org.freedesktop.LogControl1 \
|
||||
* LogLevel \
|
||||
* "s" debug
|
||||
* $ busctl --user get-property org.freedesktop.Example \
|
||||
* /org/freedesktop/LogControl1 \
|
||||
* org.freedesktop.LogControl1 \
|
||||
* LogLevel
|
||||
* s "debug"
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <syslog.h>
|
||||
#include <systemd/sd-bus.h>
|
||||
#include <systemd/sd-journal.h>
|
||||
|
||||
#define _cleanup_(f) __attribute__((cleanup(f)))
|
||||
|
||||
#define check(log_level, x) ({ \
|
||||
int _r = (x); \
|
||||
errno = _r < 0 ? -_r : 0; \
|
||||
sd_journal_print((log_level), #x ": %m"); \
|
||||
if (_r < 0) \
|
||||
return EXIT_FAILURE; \
|
||||
})
|
||||
|
||||
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_INVALID_ARGS,
|
||||
"Unknown property '%s'",
|
||||
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) {
|
||||
for (int i = 0; i < LOG_DEBUG + 1; i++)
|
||||
if (strcmp(value, log_level_table[i]) == 0) {
|
||||
o->log_level = i;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return sd_bus_error_setf(error,
|
||||
SD_BUS_ERROR_INVALID_ARGS,
|
||||
"Invalid value for LogLevel: '%s'",
|
||||
value);
|
||||
}
|
||||
|
||||
if (strcmp(value, "LogTarget") == 0) {
|
||||
for (LogTarget 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: '%s'",
|
||||
value);
|
||||
}
|
||||
|
||||
return sd_bus_error_setf(error,
|
||||
SD_BUS_ERROR_INVALID_ARGS,
|
||||
"Unknown property '%s'",
|
||||
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;
|
||||
|
||||
/* Acquire a connection to the bus, letting the library work out the details.
|
||||
* https://www.freedesktop.org/software/systemd/man/sd_bus_default.html
|
||||
*/
|
||||
sd_bus_default(&bus);
|
||||
|
||||
object o = {
|
||||
.log_level = LOG_INFO,
|
||||
.log_target = LOG_TARGET_JOURNAL,
|
||||
.syslog_identifier = "example",
|
||||
};
|
||||
|
||||
/* 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
|
||||
*/
|
||||
check(o.log_level, sd_bus_add_object_vtable(bus, NULL,
|
||||
"/org/freedesktop/LogControl1",
|
||||
"org.freedesktop.LogControl1",
|
||||
vtable,
|
||||
&o));
|
||||
|
||||
/* 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
|
||||
*/
|
||||
check(o.log_level, sd_bus_request_name(bus, "org.freedesktop.Example", 0));
|
||||
|
||||
for (;;) {
|
||||
/* https://www.freedesktop.org/software/systemd/man/sd_bus_wait.html
|
||||
*/
|
||||
check(o.log_level, sd_bus_wait(bus, UINT64_MAX));
|
||||
/* https://www.freedesktop.org/software/systemd/man/sd_bus_process.html
|
||||
*/
|
||||
check(o.log_level, sd_bus_process(bus, NULL));
|
||||
}
|
||||
|
||||
/* https://www.freedesktop.org/software/systemd/man/sd_bus_release_name.html
|
||||
*/
|
||||
check(o.log_level, sd_bus_release_name(bus, "org.freedesktop.Example"));
|
||||
|
||||
return 0;
|
||||
}
|
@ -116,6 +116,20 @@ node /org/freedesktop/LogControl1 {
|
||||
for details about <varname>BusName=</varname>.)</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Example</title>
|
||||
|
||||
<example>
|
||||
<title>Create a simple listener on the bus that implements LogControl1</title>
|
||||
|
||||
<programlisting><xi:include href="logcontrol-example.c" parse="text"/></programlisting>
|
||||
|
||||
<para>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
|
||||
<citerefentry><refentrytitle>sd_journal_print</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para>
|
||||
</example>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>See Also</title>
|
||||
<para>
|
||||
|
@ -110,13 +110,14 @@
|
||||
<title>Description</title>
|
||||
|
||||
<para><function>sd_bus_default()</function> acquires a bus
|
||||
connection object to the user bus when invoked in user context, or
|
||||
to the system bus otherwise. The connection object is associated
|
||||
with the calling thread. Each time the function is invoked from
|
||||
the same thread, the same object is returned, but its reference
|
||||
count is increased by one, as long as at least one reference is
|
||||
kept. When the last reference to the connection is dropped (using
|
||||
the
|
||||
connection object to the user bus when invoked from within a user
|
||||
slice (any session under <literal>user-*.slice</literal>, e.g.:
|
||||
<literal>user@1000.service</literal>), or to the system bus
|
||||
otherwise. The connection object is associated with the calling
|
||||
thread. Each time the function is invoked from the same thread,
|
||||
the same object is returned, but its reference count is increased
|
||||
by one, as long as at least one reference is kept. When the last
|
||||
reference to the connection is dropped (using the
|
||||
<citerefentry><refentrytitle>sd_bus_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>
|
||||
call), the connection is terminated. Note that the connection is
|
||||
not automatically terminated when the associated thread ends. It
|
||||
|
Loading…
Reference in New Issue
Block a user