From fa3137f9d05a9574d795bd0db7ba3abc595f06c4 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 7 Jan 2025 14:24:33 +0100 Subject: [PATCH 1/8] json-util: fine tune json_dispatch_log_level() Let's give the user control on how to handle JSON "null" assignments of the log level. As one of three cases: as failure, as LOG_NULL (i.e. to turn off logging) or as LOG_INFO (as our usual default log level). Let's then use that in the generic SetLogLevel() call, so that callers can use it to explicitly turn off logging in a service. Note that this was (probably accidentally) already implemented, except that the introspection enforcement blocked it. Let's clean this up and make this officially a thing, since it's generally useful to turn off logging I think. --- src/basic/log.h | 1 + src/libsystemd/sd-json/json-util.c | 14 +++++++++++--- src/shared/varlink-io.systemd.service.c | 4 ++-- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/basic/log.h b/src/basic/log.h index 3859a6c41d..02f3f509d1 100644 --- a/src/basic/log.h +++ b/src/basic/log.h @@ -34,6 +34,7 @@ typedef enum LogTarget{ /* This log level disables logging completely. It can only be passed to log_set_max_level() and cannot be * used as a regular log level. */ #define LOG_NULL (LOG_EMERG - 1) +assert_cc(LOG_NULL == -1); #define SYNTHETIC_ERRNO(num) (abs(num) | (1 << 30)) #define IS_SYNTHETIC_ERRNO(val) (((val) >> 30) == 1) diff --git a/src/libsystemd/sd-json/json-util.c b/src/libsystemd/sd-json/json-util.c index ea64205c14..b7990411ea 100644 --- a/src/libsystemd/sd-json/json-util.c +++ b/src/libsystemd/sd-json/json-util.c @@ -344,8 +344,15 @@ int json_dispatch_ifindex(const char *name, sd_json_variant *variant, sd_json_di int json_dispatch_log_level(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) { int *log_level = ASSERT_PTR(userdata), r, t; + /* If SD_JSON_STRICT is set, we'll refuse attempts to set the log level to null. If SD_JSON_RELAX is + * set we'll turn null (and any negative log level) into LOG_NULL (which when used as max log level + * means: no logging). Otherwise we turn null into LOG_INFO (which is typically our default). */ + if (sd_json_variant_is_null(variant)) { - *log_level = -1; + if (FLAGS_SET(flags, SD_JSON_STRICT)) + return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' may not be null.", strna(name)); + + *log_level = FLAGS_SET(flags, SD_JSON_RELAX) ? LOG_NULL : LOG_INFO; return 0; } @@ -353,8 +360,9 @@ int json_dispatch_log_level(const char *name, sd_json_variant *variant, sd_json_ if (r < 0) return r; - /* If SD_JSON_RELAX is set allow a zero interface index, otherwise refuse. */ - if (LOG_PRI(t) != t) + if (FLAGS_SET(flags, SD_JSON_RELAX) && t < 0) + t = LOG_NULL; + else if (LOG_PRI(t) != t) return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid log level.", strna(name)); *log_level = t; diff --git a/src/shared/varlink-io.systemd.service.c b/src/shared/varlink-io.systemd.service.c index f34d5cfead..d029fda41b 100644 --- a/src/shared/varlink-io.systemd.service.c +++ b/src/shared/varlink-io.systemd.service.c @@ -11,8 +11,8 @@ static SD_VARLINK_DEFINE_METHOD(Reload); static SD_VARLINK_DEFINE_METHOD( SetLogLevel, - SD_VARLINK_FIELD_COMMENT("The maximum log level."), - SD_VARLINK_DEFINE_INPUT(level, SD_VARLINK_INT, 0)); + SD_VARLINK_FIELD_COMMENT("The maximum log level, using BSD syslog log level integers."), + SD_VARLINK_DEFINE_INPUT(level, SD_VARLINK_INT, SD_VARLINK_NULLABLE)); SD_VARLINK_DEFINE_INTERFACE( io_systemd_service, From a7cc7038ac06d2c02cbaa5a749aa47b4f7cbe786 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 7 Jan 2025 14:48:22 +0100 Subject: [PATCH 2/8] varlink: rely on sd_varlink_dispatch() for validating zero-argument method calls, too --- src/shared/varlink-io.systemd.service.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/shared/varlink-io.systemd.service.c b/src/shared/varlink-io.systemd.service.c index d029fda41b..c369ad2646 100644 --- a/src/shared/varlink-io.systemd.service.c +++ b/src/shared/varlink-io.systemd.service.c @@ -25,11 +25,16 @@ SD_VARLINK_DEFINE_INTERFACE( SD_VARLINK_SYMBOL_COMMENT("Sets the maximum log level."), &vl_method_SetLogLevel); +/* Generic implementations for some of the method calls above */ + int varlink_method_ping(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) { + int r; + assert(link); - if (sd_json_variant_elements(parameters) > 0) - return sd_varlink_error_invalid_parameter(link, parameters); + r = sd_varlink_dispatch(link, parameters, /* dispatch_table= */ NULL, /* userdata= */ NULL); + if (r != 0) + return r; log_debug("Received io.systemd.service.Ping"); From 36e7aa70107cc87369c6d21d0cb47e9fc9ca7cb5 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 7 Jan 2025 14:46:00 +0100 Subject: [PATCH 3/8] varlink: drop redundant check sd_varlink_dispatch() alredy validates all this, no need to check this manually. --- src/shared/varlink-io.systemd.service.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/shared/varlink-io.systemd.service.c b/src/shared/varlink-io.systemd.service.c index c369ad2646..27c270b8e9 100644 --- a/src/shared/varlink-io.systemd.service.c +++ b/src/shared/varlink-io.systemd.service.c @@ -53,11 +53,6 @@ int varlink_method_set_log_level(sd_varlink *link, sd_json_variant *parameters, assert(link); assert(parameters); - /* NOTE: The method does have 1 parameter, but we must compare to 2 here, because - * sd_json_variant_elements() breaks abstraction and exposes internal structure of JsonObject. */ - if (sd_json_variant_elements(parameters) != 2) - return sd_varlink_error_invalid_parameter(link, parameters); - r = sd_varlink_dispatch(link, parameters, dispatch_table, &level); if (r != 0) return r; From 211012f54483cb7aa180a36bf6133fc6d6f9b6ea Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 7 Jan 2025 14:45:06 +0100 Subject: [PATCH 4/8] varlink: tweak introspection description for Reload() --- src/shared/varlink-io.systemd.service.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/varlink-io.systemd.service.c b/src/shared/varlink-io.systemd.service.c index 27c270b8e9..32ed12fd19 100644 --- a/src/shared/varlink-io.systemd.service.c +++ b/src/shared/varlink-io.systemd.service.c @@ -20,7 +20,7 @@ SD_VARLINK_DEFINE_INTERFACE( SD_VARLINK_INTERFACE_COMMENT("An interface to control basic properties of systemd services."), SD_VARLINK_SYMBOL_COMMENT("Checks if the service is running."), &vl_method_Ping, - SD_VARLINK_SYMBOL_COMMENT("Reloads configurations."), + SD_VARLINK_SYMBOL_COMMENT("Reloads configuration files."), &vl_method_Reload, SD_VARLINK_SYMBOL_COMMENT("Sets the maximum log level."), &vl_method_SetLogLevel); From fef9eb4e6af5c224258959dbdd266eb53bac057f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 7 Jan 2025 15:23:28 +0100 Subject: [PATCH 5/8] varlink: add generic GetEnvironment() call to the Varlink "service" interface It's just so useful being able to retrieve the current env block from our services. Add a concept for that. It's really simple, and dumb. In many ways it's like /proc/$PID/environ, but shows the actual environ[] array visible to the app, not just some memory that was originally used for the env block passed in, but might have been rearranged. --- src/shared/varlink-io.systemd.service.c | 61 ++++++++++++++++++++++++- src/shared/varlink-io.systemd.service.h | 1 + 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/src/shared/varlink-io.systemd.service.c b/src/shared/varlink-io.systemd.service.c index 32ed12fd19..dd1329f878 100644 --- a/src/shared/varlink-io.systemd.service.c +++ b/src/shared/varlink-io.systemd.service.c @@ -2,7 +2,9 @@ #include +#include "env-util.h" #include "json-util.h" +#include "strv.h" #include "varlink-io.systemd.service.h" static SD_VARLINK_DEFINE_METHOD(Ping); @@ -14,6 +16,14 @@ static SD_VARLINK_DEFINE_METHOD( SD_VARLINK_FIELD_COMMENT("The maximum log level, using BSD syslog log level integers."), SD_VARLINK_DEFINE_INPUT(level, SD_VARLINK_INT, SD_VARLINK_NULLABLE)); +static SD_VARLINK_DEFINE_METHOD( + GetEnvironment, + SD_VARLINK_FIELD_COMMENT("Returns the current environment block, i.e. the contents of environ[]."), + SD_VARLINK_DEFINE_OUTPUT(environment, SD_VARLINK_STRING, SD_VARLINK_NULLABLE|SD_VARLINK_ARRAY)); + +static SD_VARLINK_DEFINE_ERROR( + InconsistentEnvironment); + SD_VARLINK_DEFINE_INTERFACE( io_systemd_service, "io.systemd.service", @@ -23,7 +33,11 @@ SD_VARLINK_DEFINE_INTERFACE( SD_VARLINK_SYMBOL_COMMENT("Reloads configuration files."), &vl_method_Reload, SD_VARLINK_SYMBOL_COMMENT("Sets the maximum log level."), - &vl_method_SetLogLevel); + &vl_method_SetLogLevel, + SD_VARLINK_SYMBOL_COMMENT("Get current environment block."), + &vl_method_GetEnvironment, + SD_VARLINK_SYMBOL_COMMENT("Returned if the environment block is currently not in a valid state."), + &vl_error_InconsistentEnvironment); /* Generic implementations for some of the method calls above */ @@ -70,3 +84,48 @@ int varlink_method_set_log_level(sd_varlink *link, sd_json_variant *parameters, return sd_varlink_reply(link, NULL); } + +int varlink_method_get_environment(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) { + uid_t uid; + int r; + + assert(link); + assert(parameters); + + /* This is a lot like /proc/$PID/environ, but can properly report the actual environment block as + * seen from the process itself, which might be quite different from the contents of the memory that + * was originally passed in. This is particularly relevant for cases where the environ[] block has + * been enlarged and similar. */ + + r = sd_varlink_dispatch(link, parameters, /* dispatch_table= */ NULL, /* userdata= */ NULL); + if (r != 0) + return r; + + r = sd_varlink_get_peer_uid(link, &uid); + if (r < 0) + return r; + + /* Don't hand out environment block to arbitrary clients, in some cases people might make the mistake + * of passing secrets via env vars */ + if (uid != 0 && uid != getuid()) + return sd_varlink_error(link, SD_VARLINK_ERROR_PERMISSION_DENIED, parameters); + + log_debug("Received io.systemd.service.GetEnvironment()"); + + _cleanup_strv_free_ char **l = NULL; + STRV_FOREACH(e, environ) { + if (!env_assignment_is_valid(*e)) + goto invalid; + if (!utf8_is_valid(*e)) + goto invalid; + + r = strv_env_replace_strdup(&l, *e); + if (r < 0) + return r; + } + + return sd_varlink_replybo(link, SD_JSON_BUILD_PAIR_STRV("environment", l)); + +invalid: + return sd_varlink_error(link, "io.systemd.service.InconsistentEnvironment", parameters); +} diff --git a/src/shared/varlink-io.systemd.service.h b/src/shared/varlink-io.systemd.service.h index 3f164783f5..a68d58d6d4 100644 --- a/src/shared/varlink-io.systemd.service.h +++ b/src/shared/varlink-io.systemd.service.h @@ -8,3 +8,4 @@ extern const sd_varlink_interface vl_interface_io_systemd_service; int varlink_method_ping(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata); int varlink_method_set_log_level(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata); +int varlink_method_get_environment(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata); From c8127075ae22ad0f64d3072f400818f6e1a9ab48 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 7 Jan 2025 15:21:53 +0100 Subject: [PATCH 6/8] tree-wide: implement generic io.systemd.service Varlink interface in all long-running services Given we have the generic interface, let's hook it up everywhere. This doesnt bother with the Reload() call usually, since that's more involved, but hooks up the other relevant functions where applicable. --- src/core/core-varlink.c | 8 ++++++-- src/home/homed-manager.c | 11 +++++++++-- src/hostname/hostnamed.c | 11 +++++++++-- src/import/importd.c | 13 ++++++++++--- src/journal/journald-server.c | 17 ++++++++++++----- src/login/logind-varlink.c | 13 ++++++++++--- src/machine/machined-varlink.c | 9 +++++++-- src/network/networkd-manager-varlink.c | 17 ++++++++++++----- src/oom/oomd-manager.c | 17 +++++++++++++---- src/resolve/resolved-varlink.c | 11 +++++++++-- 10 files changed, 97 insertions(+), 30 deletions(-) diff --git a/src/core/core-varlink.c b/src/core/core-varlink.c index 4f0563a1c0..92cfd36e71 100644 --- a/src/core/core-varlink.c +++ b/src/core/core-varlink.c @@ -10,6 +10,7 @@ #include "varlink-internal.h" #include "varlink-io.systemd.UserDatabase.h" #include "varlink-io.systemd.ManagedOOM.h" +#include "varlink-io.systemd.service.h" #include "varlink-util.h" typedef struct LookupParameters { @@ -589,7 +590,8 @@ int manager_setup_varlink_server(Manager *m) { r = sd_varlink_server_add_interface_many( s, &vl_interface_io_systemd_UserDatabase, - &vl_interface_io_systemd_ManagedOOM); + &vl_interface_io_systemd_ManagedOOM, + &vl_interface_io_systemd_service); if (r < 0) return log_debug_errno(r, "Failed to add interfaces to varlink server: %m"); @@ -598,7 +600,9 @@ int manager_setup_varlink_server(Manager *m) { "io.systemd.UserDatabase.GetUserRecord", vl_method_get_user_record, "io.systemd.UserDatabase.GetGroupRecord", vl_method_get_group_record, "io.systemd.UserDatabase.GetMemberships", vl_method_get_memberships, - "io.systemd.ManagedOOM.SubscribeManagedOOMCGroups", vl_method_subscribe_managed_oom_cgroups); + "io.systemd.ManagedOOM.SubscribeManagedOOMCGroups", vl_method_subscribe_managed_oom_cgroups, + "io.systemd.service.Ping", varlink_method_ping, + "io.systemd.service.GetEnvironment", varlink_method_get_environment); if (r < 0) return log_debug_errno(r, "Failed to register varlink methods: %m"); diff --git a/src/home/homed-manager.c b/src/home/homed-manager.c index bb4ad1f99f..a0d38eb701 100644 --- a/src/home/homed-manager.c +++ b/src/home/homed-manager.c @@ -54,6 +54,7 @@ #include "user-record-util.h" #include "user-record.h" #include "user-util.h" +#include "varlink-io.systemd.service.h" #include "varlink-io.systemd.UserDatabase.h" #include "varlink-util.h" @@ -1012,7 +1013,10 @@ static int manager_bind_varlink(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to allocate varlink server: %m"); - r = sd_varlink_server_add_interface(m->varlink_server, &vl_interface_io_systemd_UserDatabase); + r = sd_varlink_server_add_interface_many( + m->varlink_server, + &vl_interface_io_systemd_UserDatabase, + &vl_interface_io_systemd_service); if (r < 0) return log_error_errno(r, "Failed to add UserDatabase interface to varlink server: %m"); @@ -1020,7 +1024,10 @@ static int manager_bind_varlink(Manager *m) { m->varlink_server, "io.systemd.UserDatabase.GetUserRecord", vl_method_get_user_record, "io.systemd.UserDatabase.GetGroupRecord", vl_method_get_group_record, - "io.systemd.UserDatabase.GetMemberships", vl_method_get_memberships); + "io.systemd.UserDatabase.GetMemberships", vl_method_get_memberships, + "io.systemd.service.Ping", varlink_method_ping, + "io.systemd.service.SetLogLevel", varlink_method_set_log_level, + "io.systemd.service.GetEnvironment", varlink_method_get_environment); if (r < 0) return log_error_errno(r, "Failed to register varlink methods: %m"); diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c index 2af9c68d98..a4549da826 100644 --- a/src/hostname/hostnamed.c +++ b/src/hostname/hostnamed.c @@ -42,6 +42,7 @@ #include "user-util.h" #include "utf8.h" #include "varlink-io.systemd.Hostname.h" +#include "varlink-io.systemd.service.h" #include "varlink-util.h" #include "virt.h" @@ -1771,13 +1772,19 @@ static int connect_varlink(Context *c) { if (r < 0) return log_error_errno(r, "Failed to allocate Varlink server: %m"); - r = sd_varlink_server_add_interface(c->varlink_server, &vl_interface_io_systemd_Hostname); + r = sd_varlink_server_add_interface_many( + c->varlink_server, + &vl_interface_io_systemd_Hostname, + &vl_interface_io_systemd_service); if (r < 0) return log_error_errno(r, "Failed to add Hostname interface to Varlink server: %m"); r = sd_varlink_server_bind_method_many( c->varlink_server, - "io.systemd.Hostname.Describe", vl_method_describe); + "io.systemd.Hostname.Describe", vl_method_describe, + "io.systemd.service.Ping", varlink_method_ping, + "io.systemd.service.SetLogLevel", varlink_method_set_log_level, + "io.systemd.service.GetEnvironment", varlink_method_get_environment); if (r < 0) return log_error_errno(r, "Failed to bind Varlink method calls: %m"); diff --git a/src/import/importd.c b/src/import/importd.c index ea8257a833..72c0e4a95a 100644 --- a/src/import/importd.c +++ b/src/import/importd.c @@ -42,6 +42,7 @@ #include "syslog-util.h" #include "user-util.h" #include "varlink-io.systemd.Import.h" +#include "varlink-io.systemd.service.h" #include "varlink-util.h" #include "web-util.h" @@ -1979,14 +1980,20 @@ static int manager_connect_varlink(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to allocate varlink server object: %m"); - r = sd_varlink_server_add_interface(m->varlink_server, &vl_interface_io_systemd_Import); + r = sd_varlink_server_add_interface_many( + m->varlink_server, + &vl_interface_io_systemd_Import, + &vl_interface_io_systemd_service); if (r < 0) return log_error_errno(r, "Failed to add Import interface to varlink server: %m"); r = sd_varlink_server_bind_method_many( m->varlink_server, - "io.systemd.Import.ListTransfers", vl_method_list_transfers, - "io.systemd.Import.Pull", vl_method_pull); + "io.systemd.Import.ListTransfers", vl_method_list_transfers, + "io.systemd.Import.Pull", vl_method_pull, + "io.systemd.service.Ping", varlink_method_ping, + "io.systemd.service.SetLogLevel", varlink_method_set_log_level, + "io.systemd.service.GetEnvironment", varlink_method_get_environment); if (r < 0) return log_error_errno(r, "Failed to bind Varlink method calls: %m"); diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index 8fcfa0c57b..3469a90fef 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -64,6 +64,7 @@ #include "uid-classification.h" #include "user-util.h" #include "varlink-io.systemd.Journal.h" +#include "varlink-io.systemd.service.h" #include "varlink-util.h" #define USER_JOURNALS_MAX 1024 @@ -2334,16 +2335,22 @@ static int server_open_varlink(Server *s, const char *socket, int fd) { if (r < 0) return log_error_errno(r, "Failed to allocate varlink server object: %m"); - r = sd_varlink_server_add_interface(s->varlink_server, &vl_interface_io_systemd_Journal); + r = sd_varlink_server_add_interface_many( + s->varlink_server, + &vl_interface_io_systemd_Journal, + &vl_interface_io_systemd_service); if (r < 0) return log_error_errno(r, "Failed to add Journal interface to varlink server: %m"); r = sd_varlink_server_bind_method_many( s->varlink_server, - "io.systemd.Journal.Synchronize", vl_method_synchronize, - "io.systemd.Journal.Rotate", vl_method_rotate, - "io.systemd.Journal.FlushToVar", vl_method_flush_to_var, - "io.systemd.Journal.RelinquishVar", vl_method_relinquish_var); + "io.systemd.Journal.Synchronize", vl_method_synchronize, + "io.systemd.Journal.Rotate", vl_method_rotate, + "io.systemd.Journal.FlushToVar", vl_method_flush_to_var, + "io.systemd.Journal.RelinquishVar", vl_method_relinquish_var, + "io.systemd.service.Ping", varlink_method_ping, + "io.systemd.service.SetLogLevel", varlink_method_set_log_level, + "io.systemd.service.GetEnvironment", varlink_method_get_environment); if (r < 0) return r; diff --git a/src/login/logind-varlink.c b/src/login/logind-varlink.c index a8a2542e18..a757033a55 100644 --- a/src/login/logind-varlink.c +++ b/src/login/logind-varlink.c @@ -10,6 +10,7 @@ #include "terminal-util.h" #include "user-util.h" #include "varlink-io.systemd.Login.h" +#include "varlink-io.systemd.service.h" #include "varlink-util.h" static int manager_varlink_get_session_by_peer( @@ -355,14 +356,20 @@ int manager_varlink_init(Manager *m) { sd_varlink_server_set_userdata(s, m); - r = sd_varlink_server_add_interface(s, &vl_interface_io_systemd_Login); + r = sd_varlink_server_add_interface_many( + s, + &vl_interface_io_systemd_Login, + &vl_interface_io_systemd_service); if (r < 0) return log_error_errno(r, "Failed to add Login interface to varlink server: %m"); r = sd_varlink_server_bind_method_many( s, - "io.systemd.Login.CreateSession", vl_method_create_session, - "io.systemd.Login.ReleaseSession", vl_method_release_session); + "io.systemd.Login.CreateSession", vl_method_create_session, + "io.systemd.Login.ReleaseSession", vl_method_release_session, + "io.systemd.service.Ping", varlink_method_ping, + "io.systemd.service.SetLogLevel", varlink_method_set_log_level, + "io.systemd.service.GetEnvironment", varlink_method_get_environment); if (r < 0) return log_error_errno(r, "Failed to register varlink methods: %m"); diff --git a/src/machine/machined-varlink.c b/src/machine/machined-varlink.c index dc293f5895..5beec14ccd 100644 --- a/src/machine/machined-varlink.c +++ b/src/machine/machined-varlink.c @@ -17,6 +17,7 @@ #include "varlink-io.systemd.Machine.h" #include "varlink-io.systemd.MachineImage.h" #include "varlink-io.systemd.UserDatabase.h" +#include "varlink-io.systemd.service.h" #include "varlink-util.h" typedef struct LookupParameters { @@ -776,7 +777,8 @@ static int manager_varlink_init_machine(Manager *m) { r = sd_varlink_server_add_interface_many( s, &vl_interface_io_systemd_Machine, - &vl_interface_io_systemd_MachineImage); + &vl_interface_io_systemd_MachineImage, + &vl_interface_io_systemd_service); if (r < 0) return log_error_errno(r, "Failed to add Machine and MachineImage interfaces to varlink server: %m"); @@ -798,7 +800,10 @@ static int manager_varlink_init_machine(Manager *m) { "io.systemd.MachineImage.Update", vl_method_update_image, "io.systemd.MachineImage.Clone", vl_method_clone_image, "io.systemd.MachineImage.Remove", vl_method_remove_image, - "io.systemd.MachineImage.SetPoolLimit", vl_method_set_pool_limit); + "io.systemd.MachineImage.SetPoolLimit", vl_method_set_pool_limit, + "io.systemd.service.Ping", varlink_method_ping, + "io.systemd.service.SetLogLevel", varlink_method_set_log_level, + "io.systemd.service.GetEnvironment", varlink_method_get_environment); if (r < 0) return log_error_errno(r, "Failed to register varlink methods: %m"); diff --git a/src/network/networkd-manager-varlink.c b/src/network/networkd-manager-varlink.c index b2a196c5fc..6206fb6e44 100644 --- a/src/network/networkd-manager-varlink.c +++ b/src/network/networkd-manager-varlink.c @@ -12,6 +12,7 @@ #include "networkd-manager-varlink.h" #include "stat-util.h" #include "varlink-io.systemd.Network.h" +#include "varlink-io.systemd.service.h" #include "varlink-util.h" static int vl_method_get_states(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) { @@ -269,16 +270,22 @@ int manager_connect_varlink(Manager *m) { (void) sd_varlink_server_set_description(s, "varlink-api-network"); - r = sd_varlink_server_add_interface(s, &vl_interface_io_systemd_Network); + r = sd_varlink_server_add_interface_many( + s, + &vl_interface_io_systemd_Network, + &vl_interface_io_systemd_service); if (r < 0) return log_error_errno(r, "Failed to add Network interface to varlink server: %m"); r = sd_varlink_server_bind_method_many( s, - "io.systemd.Network.GetStates", vl_method_get_states, - "io.systemd.Network.GetNamespaceId", vl_method_get_namespace_id, - "io.systemd.Network.GetLLDPNeighbors", vl_method_get_lldp_neighbors, - "io.systemd.Network.SetPersistentStorage", vl_method_set_persistent_storage); + "io.systemd.Network.GetStates", vl_method_get_states, + "io.systemd.Network.GetNamespaceId", vl_method_get_namespace_id, + "io.systemd.Network.GetLLDPNeighbors", vl_method_get_lldp_neighbors, + "io.systemd.Network.SetPersistentStorage", vl_method_set_persistent_storage, + "io.systemd.service.Ping", varlink_method_ping, + "io.systemd.service.SetLogLevel", varlink_method_set_log_level, + "io.systemd.service.GetEnvironment", varlink_method_get_environment); if (r < 0) return log_error_errno(r, "Failed to register varlink methods: %m"); diff --git a/src/oom/oomd-manager.c b/src/oom/oomd-manager.c index ce3674365a..352d558281 100644 --- a/src/oom/oomd-manager.c +++ b/src/oom/oomd-manager.c @@ -20,6 +20,7 @@ #include "path-util.h" #include "percent-util.h" #include "varlink-io.systemd.oom.h" +#include "varlink-io.systemd.service.h" #include "varlink-util.h" typedef struct ManagedOOMMessage { @@ -746,13 +747,21 @@ static int manager_varlink_init(Manager *m, int fd) { if (r < 0) return log_error_errno(r, "Failed to allocate varlink server object: %m"); - r = sd_varlink_server_add_interface(s, &vl_interface_io_systemd_oom); + r = sd_varlink_server_add_interface_many( + s, + &vl_interface_io_systemd_oom, + &vl_interface_io_systemd_service); if (r < 0) - return log_error_errno(r, "Failed to add oom interface to varlink server: %m"); + return log_error_errno(r, "Failed to add Varlink interfaces to varlink server: %m"); - r = sd_varlink_server_bind_method(s, "io.systemd.oom.ReportManagedOOMCGroups", process_managed_oom_request); + r = sd_varlink_server_bind_method_many( + s, + "io.systemd.oom.ReportManagedOOMCGroups", process_managed_oom_request, + "io.systemd.service.Ping", varlink_method_ping, + "io.systemd.service.SetLogLevel", varlink_method_set_log_level, + "io.systemd.service.GetEnvironment", varlink_method_get_environment); if (r < 0) - return log_error_errno(r, "Failed to register varlink method: %m"); + return log_error_errno(r, "Failed to register varlink methods: %m"); if (fd < 0) r = sd_varlink_server_listen_address(s, VARLINK_ADDR_PATH_MANAGED_OOM_USER, 0666); diff --git a/src/resolve/resolved-varlink.c b/src/resolve/resolved-varlink.c index 107f722e7d..c30a2a3fea 100644 --- a/src/resolve/resolved-varlink.c +++ b/src/resolve/resolved-varlink.c @@ -9,6 +9,7 @@ #include "socket-netlink.h" #include "varlink-io.systemd.Resolve.h" #include "varlink-io.systemd.Resolve.Monitor.h" +#include "varlink-io.systemd.service.h" #include "varlink-util.h" typedef struct LookupParameters { @@ -1410,7 +1411,10 @@ static int varlink_main_server_init(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to allocate varlink server object: %m"); - r = sd_varlink_server_add_interface(s, &vl_interface_io_systemd_Resolve); + r = sd_varlink_server_add_interface_many( + s, + &vl_interface_io_systemd_Resolve, + &vl_interface_io_systemd_service); if (r < 0) return log_error_errno(r, "Failed to add Resolve interface to varlink server: %m"); @@ -1419,7 +1423,10 @@ static int varlink_main_server_init(Manager *m) { "io.systemd.Resolve.ResolveHostname", vl_method_resolve_hostname, "io.systemd.Resolve.ResolveAddress", vl_method_resolve_address, "io.systemd.Resolve.ResolveService", vl_method_resolve_service, - "io.systemd.Resolve.ResolveRecord", vl_method_resolve_record); + "io.systemd.Resolve.ResolveRecord", vl_method_resolve_record, + "io.systemd.service.Ping", varlink_method_ping, + "io.systemd.service.SetLogLevel", varlink_method_set_log_level, + "io.systemd.service.GetEnvironment", varlink_method_get_environment); if (r < 0) return log_error_errno(r, "Failed to register varlink methods: %m"); From 078ddcb7cee654fd130496590fe69aeebb2a971e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 7 Jan 2025 23:37:31 +0100 Subject: [PATCH 7/8] test: add superficial test for generic per-service varlink API --- test/units/TEST-13-NSPAWN.importctl.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/units/TEST-13-NSPAWN.importctl.sh b/test/units/TEST-13-NSPAWN.importctl.sh index be31837447..ac4a70e9b0 100755 --- a/test/units/TEST-13-NSPAWN.importctl.sh +++ b/test/units/TEST-13-NSPAWN.importctl.sh @@ -83,3 +83,10 @@ systemctl daemon-reload systemctl start import0.service cmp /var/tmp/importtest /var/lib/confexts/importtest9/importtest + +# Verify generic service calls, too +varlinkctl call --more /run/systemd/io.systemd.Import io.systemd.service.Ping '{}' +varlinkctl call --more /run/systemd/io.systemd.Import io.systemd.service.SetLogLevel '{"level":"7"}' +varlinkctl call --more /run/systemd/io.systemd.Import io.systemd.service.SetLogLevel '{"level":"1"}' +varlinkctl call --more /run/systemd/io.systemd.Import io.systemd.service.SetLogLevel '{"level":"7"}' +varlinkctl call --more /run/systemd/io.systemd.Import io.systemd.service.GetEnvironment '{}' From adb556abd9e98d6c16d9c0ab5ded115923518d62 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 7 Jan 2025 23:44:41 +0100 Subject: [PATCH 8/8] tree-wide: use log_level_is_valid() more --- src/basic/log.c | 2 +- src/libsystemd/sd-json/json-util.c | 3 ++- src/udev/udev-config.c | 2 +- src/udev/udev-manager.c | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/basic/log.c b/src/basic/log.c index cbc6edc95d..2250dfe927 100644 --- a/src/basic/log.c +++ b/src/basic/log.c @@ -397,7 +397,7 @@ void log_forget_fds(void) { } int log_set_max_level(int level) { - assert(level == LOG_NULL || LOG_PRI(level) == level); + assert(level == LOG_NULL || log_level_is_valid(level)); int old = log_max_level; log_max_level = level; diff --git a/src/libsystemd/sd-json/json-util.c b/src/libsystemd/sd-json/json-util.c index b7990411ea..f2d306f781 100644 --- a/src/libsystemd/sd-json/json-util.c +++ b/src/libsystemd/sd-json/json-util.c @@ -13,6 +13,7 @@ #include "path-util.h" #include "process-util.h" #include "string-util.h" +#include "syslog-util.h" #include "user-util.h" int json_dispatch_unbase64_iovec(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) { @@ -362,7 +363,7 @@ int json_dispatch_log_level(const char *name, sd_json_variant *variant, sd_json_ if (FLAGS_SET(flags, SD_JSON_RELAX) && t < 0) t = LOG_NULL; - else if (LOG_PRI(t) != t) + else if (!log_level_is_valid(t)) return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid log level.", strna(name)); *log_level = t; diff --git a/src/udev/udev-config.c b/src/udev/udev-config.c index d511691ab2..e60464360d 100644 --- a/src/udev/udev-config.c +++ b/src/udev/udev-config.c @@ -306,7 +306,7 @@ void manager_set_children_max(Manager *manager, unsigned n) { void manager_set_log_level(Manager *manager, int log_level) { assert(manager); - assert(LOG_PRI(log_level) == log_level); + assert(log_level_is_valid(log_level)); int old = log_get_max_level(); diff --git a/src/udev/udev-manager.c b/src/udev/udev-manager.c index b7f2b185c9..caa4379759 100644 --- a/src/udev/udev-manager.c +++ b/src/udev/udev-manager.c @@ -851,7 +851,7 @@ static int on_ctrl_msg(UdevCtrl *uctrl, UdevCtrlMessageType type, const UdevCtrl switch (type) { case UDEV_CTRL_SET_LOG_LEVEL: - if (LOG_PRI(value->intval) != value->intval) { + if (!log_level_is_valid(value->intval)) { log_debug("Received invalid udev control message (SET_LOG_LEVEL, %i), ignoring.", value->intval); break; }