From 1bd979dddbb6ed3ffe410d78a7ff80cbb1c42a64 Mon Sep 17 00:00:00 2001 From: Ivan Kruglov Date: Mon, 7 Oct 2024 11:22:11 +0200 Subject: [PATCH 1/7] machine: io.systemd.Machine.List supports 'pid' filter --- src/machine/machine-varlink.c | 87 +++++++++++++++++++++++++ src/machine/machine-varlink.h | 16 +++++ src/machine/machined-varlink.c | 26 +++++--- src/shared/varlink-io.systemd.Machine.c | 4 +- 4 files changed, 123 insertions(+), 10 deletions(-) diff --git a/src/machine/machine-varlink.c b/src/machine/machine-varlink.c index fb64bde9205..37bcf266f79 100644 --- a/src/machine/machine-varlink.c +++ b/src/machine/machine-varlink.c @@ -188,3 +188,90 @@ int vl_method_register(sd_varlink *link, sd_json_variant *parameters, sd_varlink return sd_varlink_reply(link, NULL); } + +static int lookup_machine_by_name(sd_varlink *link, Manager *manager, const char *machine_name, Machine **ret_machine) { + assert(link); + assert(manager); + assert(ret_machine); + + if (!machine_name) + return -EINVAL; + + if (!hostname_is_valid(machine_name, /* flags= */ VALID_HOSTNAME_DOT_HOST)) + return -EINVAL; + + Machine *machine = hashmap_get(manager->machines, machine_name); + if (!machine) + return -ESRCH; + + *ret_machine = machine; + return 0; +} + +static int lookup_machine_by_pid(sd_varlink *link, Manager *manager, pid_t pid, Machine **ret_machine) { + Machine *machine; + int r; + + assert(link); + assert(manager); + assert(ret_machine); + assert_cc(sizeof(pid_t) == sizeof(uint32_t)); + + if (pid == 0) { + int pidfd = sd_varlink_get_peer_pidfd(link); + if (pidfd < 0) + return log_debug_errno(pidfd, "Failed to get peer pidfd: %m"); + + r = pidfd_get_pid(pidfd, &pid); + if (r < 0) + return log_debug_errno(r, "Failed to get pid from pidfd: %m"); + } + + if (pid <= 0) + return -EINVAL; + + r = manager_get_machine_by_pid(manager, pid, &machine); + if (r < 0) + return r; + if (!machine) + return -ESRCH; + + *ret_machine = machine; + return 0; +} + +int lookup_machine_by_name_or_pid(sd_varlink *link, Manager *manager, const char *machine_name, pid_t pid, Machine **ret_machine) { + Machine *machine = NULL, *pid_machine = NULL; + int r; + + assert(link); + assert(manager); + assert(ret_machine); + + if (machine_name) { + r = lookup_machine_by_name(link, manager, machine_name, &machine); + if (r == -EINVAL) + return sd_varlink_error_invalid_parameter_name(link, "name"); + if (r < 0) + return r; + } + + if (pid >= 0) { + r = lookup_machine_by_pid(link, manager, pid, &pid_machine); + if (r == -EINVAL) + return sd_varlink_error_invalid_parameter_name(link, "pid"); + if (r < 0) + return r; + } + + if (machine && pid_machine && machine != pid_machine) + return log_debug_errno(SYNTHETIC_ERRNO(ESRCH), "Search by machine name '%s' and pid %d resulted in two different machines", machine_name, pid); + else if (machine) + *ret_machine = machine; + else if (pid_machine) + *ret_machine = pid_machine; + else + return -ESRCH; + + return 0; +} diff --git a/src/machine/machine-varlink.h b/src/machine/machine-varlink.h index 3b5eb2c8f72..4ec68d52603 100644 --- a/src/machine/machine-varlink.h +++ b/src/machine/machine-varlink.h @@ -3,4 +3,20 @@ #include "sd-varlink.h" +#include "machine.h" + +#define VARLINK_DISPATCH_MACHINE_LOOKUP_FIELDS(t) { \ + .name = "name", \ + .type = SD_JSON_VARIANT_STRING, \ + .callback = sd_json_dispatch_const_string, \ + .offset = offsetof(t, machine_name) \ + }, { \ + .name = "pid", \ + .type = _SD_JSON_VARIANT_TYPE_INVALID, \ + .callback = sd_json_dispatch_uint32, \ + .offset = offsetof(t, pid) \ + } + +int lookup_machine_by_name_or_pid(sd_varlink *link, Manager *manager, const char *machine_name, pid_t pid, Machine **ret_machine); + int vl_method_register(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata); diff --git a/src/machine/machined-varlink.c b/src/machine/machined-varlink.c index 942b0a96b3e..4189ff2cf24 100644 --- a/src/machine/machined-varlink.c +++ b/src/machine/machined-varlink.c @@ -2,6 +2,7 @@ #include "sd-varlink.h" +#include "bus-polkit.h" #include "format-util.h" #include "hostname-util.h" #include "json-util.h" @@ -415,29 +416,36 @@ static int list_machine_one(sd_varlink *link, Machine *m, bool more) { return sd_varlink_reply(link, v); } +typedef struct MachineLookupParameters { + const char *machine_name; + pid_t pid; +} MachineLookupParameters; + static int vl_method_list(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) { static const sd_json_dispatch_field dispatch_table[] = { - { "name", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, 0, 0 }, + VARLINK_DISPATCH_MACHINE_LOOKUP_FIELDS(MachineLookupParameters), + VARLINK_DISPATCH_POLKIT_FIELD, {} }; Manager *m = ASSERT_PTR(userdata); - const char *mn = NULL; + MachineLookupParameters p = { .pid = -1 }; + Machine *machine; int r; + assert(link); assert(parameters); - r = sd_varlink_dispatch(link, parameters, dispatch_table, &mn); + r = sd_varlink_dispatch(link, parameters, dispatch_table, &p); if (r != 0) return r; - if (mn) { - if (!hostname_is_valid(mn, /* flags= */ VALID_HOSTNAME_DOT_HOST)) - return sd_varlink_error_invalid_parameter_name(link, "name"); - - Machine *machine = hashmap_get(m->machines, mn); - if (!machine) + if (p.machine_name || p.pid >= 0) { + r = lookup_machine_by_name_or_pid(link, m, p.machine_name, p.pid, &machine); + if (r == -ESRCH) return sd_varlink_error(link, "io.systemd.Machine.NoSuchMachine", NULL); + if (r < 0) + return r; return list_machine_one(link, machine, /* more= */ false); } diff --git a/src/shared/varlink-io.systemd.Machine.c b/src/shared/varlink-io.systemd.Machine.c index 5841bf02ced..334e4c7d938 100644 --- a/src/shared/varlink-io.systemd.Machine.c +++ b/src/shared/varlink-io.systemd.Machine.c @@ -31,8 +31,10 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE( static SD_VARLINK_DEFINE_METHOD_FULL( List, SD_VARLINK_SUPPORTS_MORE, - SD_VARLINK_FIELD_COMMENT("If non-null the name of a running machine to report details on. If null/unspecified enumerates all running machines."), + SD_VARLINK_FIELD_COMMENT("If non-null the name of a running machine to report details on. If both 'name' and 'pid' are null/unspecified enumerates all running machines."), SD_VARLINK_DEFINE_INPUT(name, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("If non-null the PID of a running machine to report details on."), + SD_VARLINK_DEFINE_INPUT(pid, SD_VARLINK_INT, SD_VARLINK_NULLABLE), SD_VARLINK_FIELD_COMMENT("Name of the machine"), SD_VARLINK_DEFINE_OUTPUT(name, SD_VARLINK_STRING, 0), SD_VARLINK_FIELD_COMMENT("128bit ID identifying this machine, formatted in hexadecimal"), From 7999aa2c79fb4bcb12f3b52e99668661737a9c54 Mon Sep 17 00:00:00 2001 From: Ivan Kruglov Date: Mon, 7 Oct 2024 11:26:02 +0200 Subject: [PATCH 2/7] machine: introduce io.systemd.Machine.Unregister varlink method --- src/machine/machine-varlink.c | 22 +++++++++++++++ src/machine/machine-varlink.h | 1 + src/machine/machined-varlink.c | 37 +++++++++++++++++++++++-- src/shared/varlink-io.systemd.Machine.c | 6 ++++ 4 files changed, 64 insertions(+), 2 deletions(-) diff --git a/src/machine/machine-varlink.c b/src/machine/machine-varlink.c index 37bcf266f79..810e94925c5 100644 --- a/src/machine/machine-varlink.c +++ b/src/machine/machine-varlink.c @@ -275,3 +275,25 @@ int lookup_machine_by_name_or_pid(sd_varlink *link, Manager *manager, const char return 0; } + +int vl_method_unregister_internal(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) { + Machine *machine = ASSERT_PTR(userdata); + Manager *manager = ASSERT_PTR(machine->manager); + int r; + + r = varlink_verify_polkit_async( + link, + manager->bus, + "org.freedesktop.machine1.manage-machines", + (const char**) STRV_MAKE("name", machine->name, + "verb", "unregister"), + &manager->polkit_registry); + if (r <= 0) + return r; + + r = machine_finalize(machine); + if (r < 0) + return log_debug_errno(r, "Failed to finalize machine: %m"); + + return sd_varlink_reply(link, NULL); +} diff --git a/src/machine/machine-varlink.h b/src/machine/machine-varlink.h index 4ec68d52603..4f772d04b71 100644 --- a/src/machine/machine-varlink.h +++ b/src/machine/machine-varlink.h @@ -20,3 +20,4 @@ int lookup_machine_by_name_or_pid(sd_varlink *link, Manager *manager, const char *machine_name, pid_t pid, Machine **ret_machine); int vl_method_register(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata); +int vl_method_unregister_internal(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata); diff --git a/src/machine/machined-varlink.c b/src/machine/machined-varlink.c index 4189ff2cf24..94d8c16799c 100644 --- a/src/machine/machined-varlink.c +++ b/src/machine/machined-varlink.c @@ -470,6 +470,38 @@ static int vl_method_list(sd_varlink *link, sd_json_variant *parameters, sd_varl return sd_varlink_error(link, "io.systemd.Machine.NoSuchMachine", NULL); } +static int lookup_machine_and_call_method(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata, sd_varlink_method_t method) { + static const sd_json_dispatch_field dispatch_table[] = { + VARLINK_DISPATCH_MACHINE_LOOKUP_FIELDS(MachineLookupParameters), + VARLINK_DISPATCH_POLKIT_FIELD, + {} + }; + + Manager *manager = ASSERT_PTR(userdata); + MachineLookupParameters p = { .pid = -1 }; + Machine *machine; + int r; + + assert(link); + assert(parameters); + + r = sd_varlink_dispatch(link, parameters, dispatch_table, &p); + if (r != 0) + return r; + + r = lookup_machine_by_name_or_pid(link, manager, p.machine_name, p.pid, &machine); + if (r == -ESRCH) + return sd_varlink_error(link, "io.systemd.Machine.NoSuchMachine", NULL); + if (r < 0) + return r; + + return method(link, parameters, flags, machine); +} + +static int vl_method_unregister(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) { + return lookup_machine_and_call_method(link, parameters, flags, userdata, vl_method_unregister_internal); +} + static int manager_varlink_init_userdb(Manager *m) { _cleanup_(sd_varlink_server_unrefp) sd_varlink_server *s = NULL; int r; @@ -532,8 +564,9 @@ static int manager_varlink_init_machine(Manager *m) { r = sd_varlink_server_bind_method_many( s, - "io.systemd.Machine.Register", vl_method_register, - "io.systemd.Machine.List", vl_method_list); + "io.systemd.Machine.Register", vl_method_register, + "io.systemd.Machine.List", vl_method_list, + "io.systemd.Machine.Unregister", vl_method_unregister); if (r < 0) return log_error_errno(r, "Failed to register varlink methods: %m"); diff --git a/src/shared/varlink-io.systemd.Machine.c b/src/shared/varlink-io.systemd.Machine.c index 334e4c7d938..9b65dad363e 100644 --- a/src/shared/varlink-io.systemd.Machine.c +++ b/src/shared/varlink-io.systemd.Machine.c @@ -28,6 +28,11 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE( SD_VARLINK_FIELD_COMMENT("Timestamp in µs in the CLOCK_MONOTONIC clock"), SD_VARLINK_DEFINE_FIELD(monotonic, SD_VARLINK_INT, SD_VARLINK_NULLABLE)); +static SD_VARLINK_DEFINE_METHOD( + Unregister, + SD_VARLINK_FIELD_COMMENT("The name of a machine to unregister."), + SD_VARLINK_DEFINE_INPUT(name, SD_VARLINK_STRING, 0)); + static SD_VARLINK_DEFINE_METHOD_FULL( List, SD_VARLINK_SUPPORTS_MORE, @@ -65,6 +70,7 @@ SD_VARLINK_DEFINE_INTERFACE( SD_VARLINK_SYMBOL_COMMENT("A timestamp object consisting of both CLOCK_REALTIME and CLOCK_MONOTONIC timestamps"), &vl_type_Timestamp, &vl_method_Register, + &vl_method_Unregister, SD_VARLINK_SYMBOL_COMMENT("List running machines"), &vl_method_List, SD_VARLINK_SYMBOL_COMMENT("No matching machine currently running"), From 6bb6354b4fe85090fdfb2b2d8c92f6e2da6b6550 Mon Sep 17 00:00:00 2001 From: Ivan Kruglov Date: Mon, 7 Oct 2024 11:27:16 +0200 Subject: [PATCH 3/7] machine: introduce io.systemd.Machine.Terminate varlink method --- src/machine/machine-varlink.c | 22 ++++++++++++++++++++++ src/machine/machine-varlink.h | 1 + src/machine/machined-varlink.c | 7 ++++++- src/shared/varlink-io.systemd.Machine.c | 7 +++++++ 4 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/machine/machine-varlink.c b/src/machine/machine-varlink.c index 810e94925c5..06c59887aa7 100644 --- a/src/machine/machine-varlink.c +++ b/src/machine/machine-varlink.c @@ -297,3 +297,25 @@ int vl_method_unregister_internal(sd_varlink *link, sd_json_variant *parameters, return sd_varlink_reply(link, NULL); } + +int vl_method_terminate_internal(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) { + Machine *machine = ASSERT_PTR(userdata); + Manager *manager = ASSERT_PTR(machine->manager); + int r; + + r = varlink_verify_polkit_async( + link, + manager->bus, + "org.freedesktop.machine1.manage-machines", + (const char**) STRV_MAKE("name", machine->name, + "verb", "terminate"), + &manager->polkit_registry); + if (r <= 0) + return r; + + r = machine_stop(machine); + if (r < 0) + return log_debug_errno(r, "Failed to stop machine: %m"); + + return sd_varlink_reply(link, NULL); +} diff --git a/src/machine/machine-varlink.h b/src/machine/machine-varlink.h index 4f772d04b71..8b0efc2a639 100644 --- a/src/machine/machine-varlink.h +++ b/src/machine/machine-varlink.h @@ -21,3 +21,4 @@ int lookup_machine_by_name_or_pid(sd_varlink *link, Manager *manager, const char int vl_method_register(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata); int vl_method_unregister_internal(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata); +int vl_method_terminate_internal(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata); diff --git a/src/machine/machined-varlink.c b/src/machine/machined-varlink.c index 94d8c16799c..fbf03ad4a16 100644 --- a/src/machine/machined-varlink.c +++ b/src/machine/machined-varlink.c @@ -502,6 +502,10 @@ static int vl_method_unregister(sd_varlink *link, sd_json_variant *parameters, s return lookup_machine_and_call_method(link, parameters, flags, userdata, vl_method_unregister_internal); } +static int vl_method_terminate(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) { + return lookup_machine_and_call_method(link, parameters, flags, userdata, vl_method_terminate_internal); +} + static int manager_varlink_init_userdb(Manager *m) { _cleanup_(sd_varlink_server_unrefp) sd_varlink_server *s = NULL; int r; @@ -566,7 +570,8 @@ static int manager_varlink_init_machine(Manager *m) { s, "io.systemd.Machine.Register", vl_method_register, "io.systemd.Machine.List", vl_method_list, - "io.systemd.Machine.Unregister", vl_method_unregister); + "io.systemd.Machine.Unregister", vl_method_unregister, + "io.systemd.Machine.Terminate", vl_method_terminate); if (r < 0) return log_error_errno(r, "Failed to register varlink methods: %m"); diff --git a/src/shared/varlink-io.systemd.Machine.c b/src/shared/varlink-io.systemd.Machine.c index 9b65dad363e..d37f31bc3f1 100644 --- a/src/shared/varlink-io.systemd.Machine.c +++ b/src/shared/varlink-io.systemd.Machine.c @@ -33,6 +33,11 @@ static SD_VARLINK_DEFINE_METHOD( SD_VARLINK_FIELD_COMMENT("The name of a machine to unregister."), SD_VARLINK_DEFINE_INPUT(name, SD_VARLINK_STRING, 0)); +static SD_VARLINK_DEFINE_METHOD( + Terminate, + SD_VARLINK_FIELD_COMMENT("The name of a machine to terminate."), + SD_VARLINK_DEFINE_INPUT(name, SD_VARLINK_STRING, 0)); + static SD_VARLINK_DEFINE_METHOD_FULL( List, SD_VARLINK_SUPPORTS_MORE, @@ -71,6 +76,8 @@ SD_VARLINK_DEFINE_INTERFACE( &vl_type_Timestamp, &vl_method_Register, &vl_method_Unregister, + SD_VARLINK_SYMBOL_COMMENT("Terminate machine, killing its processes"), + &vl_method_Terminate, SD_VARLINK_SYMBOL_COMMENT("List running machines"), &vl_method_List, SD_VARLINK_SYMBOL_COMMENT("No matching machine currently running"), From 17d9f172ebbe6ab6f23775a2756696e0ab2f582a Mon Sep 17 00:00:00 2001 From: Ivan Kruglov Date: Mon, 7 Oct 2024 11:27:41 +0200 Subject: [PATCH 4/7] machine: introduce io.systemd.Machine.Kill varlink method --- src/machine/machine-varlink.c | 64 +++++++++++++++++++++++++ src/machine/machine-varlink.h | 1 + src/machine/machined-varlink.c | 3 +- src/shared/varlink-io.systemd.Machine.c | 11 +++++ 4 files changed, 78 insertions(+), 1 deletion(-) diff --git a/src/machine/machine-varlink.c b/src/machine/machine-varlink.c index 06c59887aa7..d565859cae1 100644 --- a/src/machine/machine-varlink.c +++ b/src/machine/machine-varlink.c @@ -14,6 +14,7 @@ #include "path-util.h" #include "pidref.h" #include "process-util.h" +#include "signal-util.h" #include "socket-util.h" #include "string-util.h" #include "varlink-util.h" @@ -319,3 +320,66 @@ int vl_method_terminate_internal(sd_varlink *link, sd_json_variant *parameters, return sd_varlink_reply(link, NULL); } + +int vl_method_kill(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) { + struct params { + const char *machine_name; + pid_t pid; + const char *swhom; + int32_t signo; + }; + + static const sd_json_dispatch_field dispatch_table[] = { + VARLINK_DISPATCH_MACHINE_LOOKUP_FIELDS(struct params), + { "whom", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(struct params, swhom), 0 }, + { "signal", _SD_JSON_VARIANT_TYPE_INVALID , sd_json_dispatch_int32, offsetof(struct params, signo), SD_JSON_MANDATORY }, + VARLINK_DISPATCH_POLKIT_FIELD, + {} + }; + + Manager *manager = ASSERT_PTR(userdata); + struct params p = { .pid = -1 }; + KillWhom whom; + int r; + + assert(link); + assert(parameters); + + r = sd_varlink_dispatch(link, parameters, dispatch_table, &p); + if (r != 0) + return r; + + Machine *machine; + r = lookup_machine_by_name_or_pid(link, manager, p.machine_name, p.pid, &machine); + if (r == -ESRCH) + return sd_varlink_error(link, "io.systemd.Machine.NoSuchMachine", NULL); + if (r < 0) + return r; + + if (isempty(p.swhom)) + whom = KILL_ALL; + else { + whom = kill_whom_from_string(p.swhom); + if (whom < 0) + return sd_varlink_error_invalid_parameter_name(link, "whom"); + } + + if (!SIGNAL_VALID(p.signo)) + return sd_varlink_error_invalid_parameter_name(link, "signal"); + + r = varlink_verify_polkit_async( + link, + manager->bus, + "org.freedesktop.machine1.manage-machines", + (const char**) STRV_MAKE("name", machine->name, + "verb", "kill"), + &manager->polkit_registry); + if (r <= 0) + return r; + + r = machine_kill(machine, whom, p.signo); + if (r < 0) + return log_debug_errno(r, "Failed to send signal to machine: %m"); + + return sd_varlink_reply(link, NULL); +} diff --git a/src/machine/machine-varlink.h b/src/machine/machine-varlink.h index 8b0efc2a639..4ee15ce40b5 100644 --- a/src/machine/machine-varlink.h +++ b/src/machine/machine-varlink.h @@ -22,3 +22,4 @@ int lookup_machine_by_name_or_pid(sd_varlink *link, Manager *manager, const char int vl_method_register(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata); int vl_method_unregister_internal(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata); int vl_method_terminate_internal(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata); +int vl_method_kill(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata); diff --git a/src/machine/machined-varlink.c b/src/machine/machined-varlink.c index fbf03ad4a16..57ef866b2cf 100644 --- a/src/machine/machined-varlink.c +++ b/src/machine/machined-varlink.c @@ -571,7 +571,8 @@ static int manager_varlink_init_machine(Manager *m) { "io.systemd.Machine.Register", vl_method_register, "io.systemd.Machine.List", vl_method_list, "io.systemd.Machine.Unregister", vl_method_unregister, - "io.systemd.Machine.Terminate", vl_method_terminate); + "io.systemd.Machine.Terminate", vl_method_terminate, + "io.systemd.Machine.Kill", vl_method_kill); if (r < 0) return log_error_errno(r, "Failed to register varlink methods: %m"); diff --git a/src/shared/varlink-io.systemd.Machine.c b/src/shared/varlink-io.systemd.Machine.c index d37f31bc3f1..dfcf210b8a2 100644 --- a/src/shared/varlink-io.systemd.Machine.c +++ b/src/shared/varlink-io.systemd.Machine.c @@ -38,6 +38,15 @@ static SD_VARLINK_DEFINE_METHOD( SD_VARLINK_FIELD_COMMENT("The name of a machine to terminate."), SD_VARLINK_DEFINE_INPUT(name, SD_VARLINK_STRING, 0)); +static SD_VARLINK_DEFINE_METHOD( + Kill, + SD_VARLINK_FIELD_COMMENT("The name of a machine to send signal to."), + SD_VARLINK_DEFINE_INPUT(name, SD_VARLINK_STRING, 0), + SD_VARLINK_FIELD_COMMENT("Identifier that specifies what precisely to send the signal to (either 'leader' or 'all')."), + SD_VARLINK_DEFINE_INPUT(whom, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("Numeric UNIX signal integer."), + SD_VARLINK_DEFINE_INPUT(signal, SD_VARLINK_INT, 0)); + static SD_VARLINK_DEFINE_METHOD_FULL( List, SD_VARLINK_SUPPORTS_MORE, @@ -78,6 +87,8 @@ SD_VARLINK_DEFINE_INTERFACE( &vl_method_Unregister, SD_VARLINK_SYMBOL_COMMENT("Terminate machine, killing its processes"), &vl_method_Terminate, + SD_VARLINK_SYMBOL_COMMENT("Send a UNIX signal to the machine's processes"), + &vl_method_Kill, SD_VARLINK_SYMBOL_COMMENT("List running machines"), &vl_method_List, SD_VARLINK_SYMBOL_COMMENT("No matching machine currently running"), From 0c3191a506411401dce169f438632b653c268e85 Mon Sep 17 00:00:00 2001 From: Ivan Kruglov Date: Mon, 7 Oct 2024 14:39:32 +0200 Subject: [PATCH 5/7] machine: use JSON_BUILD_PAIR_STRING_NON_EMPTY() in io.systemd.Machine.List output --- src/machine/machined-varlink.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/machine/machined-varlink.c b/src/machine/machined-varlink.c index 57ef866b2cf..6011780b6ff 100644 --- a/src/machine/machined-varlink.c +++ b/src/machine/machined-varlink.c @@ -400,13 +400,13 @@ static int list_machine_one(sd_varlink *link, Machine *m, bool more) { SD_JSON_BUILD_PAIR("name", SD_JSON_BUILD_STRING(m->name)), SD_JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(m->id), "id", SD_JSON_BUILD_ID128(m->id)), SD_JSON_BUILD_PAIR("class", SD_JSON_BUILD_STRING(machine_class_to_string(m->class))), - SD_JSON_BUILD_PAIR_CONDITION(!!m->service, "service", SD_JSON_BUILD_STRING(m->service)), - SD_JSON_BUILD_PAIR_CONDITION(!!m->root_directory, "rootDirectory", SD_JSON_BUILD_STRING(m->root_directory)), - SD_JSON_BUILD_PAIR_CONDITION(!!m->unit, "unit", SD_JSON_BUILD_STRING(m->unit)), + JSON_BUILD_PAIR_STRING_NON_EMPTY("service", m->service), + JSON_BUILD_PAIR_STRING_NON_EMPTY("rootDirectory", m->root_directory), + JSON_BUILD_PAIR_STRING_NON_EMPTY("unit", m->unit), SD_JSON_BUILD_PAIR_CONDITION(pidref_is_set(&m->leader), "leader", SD_JSON_BUILD_UNSIGNED(m->leader.pid)), SD_JSON_BUILD_PAIR_CONDITION(dual_timestamp_is_set(&m->timestamp), "timestamp", JSON_BUILD_DUAL_TIMESTAMP(&m->timestamp)), SD_JSON_BUILD_PAIR_CONDITION(m->vsock_cid != VMADDR_CID_ANY, "vSockCid", SD_JSON_BUILD_UNSIGNED(m->vsock_cid)), - SD_JSON_BUILD_PAIR_CONDITION(!!m->ssh_address, "sshAddress", SD_JSON_BUILD_STRING(m->ssh_address))); + JSON_BUILD_PAIR_STRING_NON_EMPTY("sshAddress", m->ssh_address)); if (r < 0) return r; From 144128d317029e6890694cc327aa6416b81ae64b Mon Sep 17 00:00:00 2001 From: Ivan Kruglov Date: Tue, 24 Sep 2024 10:21:46 +0200 Subject: [PATCH 6/7] machine: extend io.systemd.Machine.List output with sshPrivateKeyPath field Effectivelly, this is an implementation of GetMachineSSHInfo in dbus. --- src/machine/machined-varlink.c | 3 ++- src/shared/varlink-io.systemd.Machine.c | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/machine/machined-varlink.c b/src/machine/machined-varlink.c index 6011780b6ff..61000241741 100644 --- a/src/machine/machined-varlink.c +++ b/src/machine/machined-varlink.c @@ -406,7 +406,8 @@ static int list_machine_one(sd_varlink *link, Machine *m, bool more) { SD_JSON_BUILD_PAIR_CONDITION(pidref_is_set(&m->leader), "leader", SD_JSON_BUILD_UNSIGNED(m->leader.pid)), SD_JSON_BUILD_PAIR_CONDITION(dual_timestamp_is_set(&m->timestamp), "timestamp", JSON_BUILD_DUAL_TIMESTAMP(&m->timestamp)), SD_JSON_BUILD_PAIR_CONDITION(m->vsock_cid != VMADDR_CID_ANY, "vSockCid", SD_JSON_BUILD_UNSIGNED(m->vsock_cid)), - JSON_BUILD_PAIR_STRING_NON_EMPTY("sshAddress", m->ssh_address)); + JSON_BUILD_PAIR_STRING_NON_EMPTY("sshAddress", m->ssh_address), + JSON_BUILD_PAIR_STRING_NON_EMPTY("sshPrivateKeyPath", m->ssh_private_key_path)); if (r < 0) return r; diff --git a/src/shared/varlink-io.systemd.Machine.c b/src/shared/varlink-io.systemd.Machine.c index dfcf210b8a2..7a18f34df16 100644 --- a/src/shared/varlink-io.systemd.Machine.c +++ b/src/shared/varlink-io.systemd.Machine.c @@ -73,7 +73,9 @@ static SD_VARLINK_DEFINE_METHOD_FULL( SD_VARLINK_FIELD_COMMENT("AF_VSOCK CID of the machine if known and applicable"), SD_VARLINK_DEFINE_OUTPUT(vSockCid, SD_VARLINK_INT, SD_VARLINK_NULLABLE), SD_VARLINK_FIELD_COMMENT("SSH address to connect to"), - SD_VARLINK_DEFINE_OUTPUT(sshAddress, SD_VARLINK_STRING, SD_VARLINK_NULLABLE)); + SD_VARLINK_DEFINE_OUTPUT(sshAddress, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("Path to private SSH key"), + SD_VARLINK_DEFINE_OUTPUT(sshPrivateKeyPath, SD_VARLINK_STRING, SD_VARLINK_NULLABLE)); static SD_VARLINK_DEFINE_ERROR(NoSuchMachine); static SD_VARLINK_DEFINE_ERROR(MachineExists); From 164af66f9abdfd8935098d9f10c746ff29dea7df Mon Sep 17 00:00:00 2001 From: Ivan Kruglov Date: Mon, 7 Oct 2024 11:35:32 +0200 Subject: [PATCH 7/7] machine: tests for varlink interfaces - io.systemd.Machine.List - io.systemd.Machine.Kill - io.systemd.Machine.Terminate - io.systemd.Machine.Register - io.systemd.Machine.Unregister --- ...chinectl.sh => TEST-13-NSPAWN.machined.sh} | 68 +++++++++++++++++-- 1 file changed, 61 insertions(+), 7 deletions(-) rename test/units/{TEST-13-NSPAWN.machinectl.sh => TEST-13-NSPAWN.machined.sh} (68%) diff --git a/test/units/TEST-13-NSPAWN.machinectl.sh b/test/units/TEST-13-NSPAWN.machined.sh similarity index 68% rename from test/units/TEST-13-NSPAWN.machinectl.sh rename to test/units/TEST-13-NSPAWN.machined.sh index 0b0f90fb8fd..b54f6e80d35 100755 --- a/test/units/TEST-13-NSPAWN.machinectl.sh +++ b/test/units/TEST-13-NSPAWN.machined.sh @@ -57,11 +57,19 @@ while :; do done EOF -rm -f /var/lib/machines/long-running/ready -machinectl start long-running -# !!!! DO NOT REMOVE THIS TEST -# The test makes sure that the long-running's init script has enough time to start and registered signal traps -timeout 10 bash -c "until test -e /var/lib/machines/long-running/ready; do sleep .5; done" +long_running_machine_start() { + # shellcheck disable=SC2015 + machinectl status long-running >/dev/null && return 0 || true + + rm -f /var/lib/machines/long-running/ready + # sometime `machinectl start` returns 1 and then do a success + machinectl start long-running || machinectl start long-running + # !!!! DO NOT REMOVE THIS TEST + # The test makes sure that the long-running's init script has enough time to start and registered signal traps + timeout 30 bash -c "until test -e /var/lib/machines/long-running/ready; do sleep .5; done" +} + +long_running_machine_start machinectl machinectl --no-pager --help @@ -232,5 +240,51 @@ done (! machinectl read-only container1 foo) (! machinectl read-only container1 -- -1) -varlinkctl --more call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{}' -varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{"name":".host"}' +#################### +# varlinkctl tests # +# ################## + +long_running_machine_start + +# test io.systemd.Machine.List +varlinkctl --more call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{}' | grep 'long-running' +varlinkctl --more call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{}' | grep '.host' +varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{"name":"long-running"}' + +pid=$(varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{"name":"long-running"}' | jq '.leader') +varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{"name":"long-running"}' >/tmp/expected +varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List "{\"pid\":$pid}" >/tmp/got +diff -u /tmp/expected /tmp/got + +varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List "{\"name\":\"long-running\", \"pid\":$pid}" +(! varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List "{\"name\":\"non-existent\", \"pid\":$pid}") + +# test io.systemd.Machine.Kill +# sending TRAP signal +rm -f /var/lib/machines/long-running/trap +varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.Kill '{"name":"long-running", "whom": "leader", "signal": 5}' +timeout 30 bash -c "until test -e /var/lib/machines/long-running/trap; do sleep .5; done" + +# sending KILL signal +varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.Kill '{"name":"long-running", "signal": 9}' +timeout 30 bash -c "while varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{\"name\":\"long-running\"}'; do sleep 0.5; done" + +# test io.systemd.Machine.Terminate +long_running_machine_start +varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.Terminate '{"name":"long-running"}' +timeout 120 bash -c "while varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{\"name\":\"long-running\"}'; do sleep 0.5; done" + +# test io.systemd.Machine.Register +varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.Register '{"name": "registered-container", "class": "container"}' +timeout 30 bash -c "until varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{\"name\":\"registered-container\"}'; do sleep 0.5; done" + +# test io.systemd.Machine.Unregister +varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.Unregister '{"name": "registered-container"}' +timeout 30 bash -c "while varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{\"name\":\"registered-container\"}'; do sleep 0.5; done" + +# test io.systemd.Machine.List with sshAddress and sshPrivateKeyPath fields +varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.Register '{"name": "registered-container", "class": "container", "sshAddress": "localhost", "sshPrivateKeyPath": "/non-existent"}' +timeout 30 bash -c "until varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{\"name\":\"registered-container\"}'; do sleep 0.5; done" +varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{"name":"registered-container"}' | jq '.sshAddress' | grep -q 'localhost' +varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{"name":"registered-container"}' | jq '.sshPrivateKeyPath' | grep -q 'non-existent' +varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.Unregister '{"name": "registered-container"}'