diff --git a/src/basic/namespace-util.h b/src/basic/namespace-util.h index 0a7616443d..c9e1da39ab 100644 --- a/src/basic/namespace-util.h +++ b/src/basic/namespace-util.h @@ -3,9 +3,11 @@ #include +typedef enum NamespaceType NamespaceType; + #include "pidref.h" -typedef enum NamespaceType { +enum NamespaceType { NAMESPACE_CGROUP, NAMESPACE_IPC, NAMESPACE_NET, @@ -16,7 +18,7 @@ typedef enum NamespaceType { NAMESPACE_TIME, _NAMESPACE_TYPE_MAX, _NAMESPACE_TYPE_INVALID = -EINVAL, -} NamespaceType; +}; extern const struct namespace_info { const char *proc_name; diff --git a/src/basic/pidref.c b/src/basic/pidref.c index 8529236c5e..1defe863e2 100644 --- a/src/basic/pidref.c +++ b/src/basic/pidref.c @@ -428,6 +428,10 @@ int pidref_wait_for_terminate(const PidRef *pidref, siginfo_t *ret) { } } +bool pidref_is_automatic(const PidRef *pidref) { + return pidref && pid_is_automatic(pidref->pid); +} + static void pidref_hash_func(const PidRef *pidref, struct siphash *state) { siphash24_compress_typesafe(pidref->pid, state); } diff --git a/src/basic/pidref.h b/src/basic/pidref.h index 4738c6eb5d..8647f4bd23 100644 --- a/src/basic/pidref.h +++ b/src/basic/pidref.h @@ -1,18 +1,26 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once +typedef struct PidRef PidRef; + #include "macro.h" +#include "process-util.h" /* An embeddable structure carrying a reference to a process. Supposed to be used when tracking processes continuously. */ -typedef struct PidRef { +struct PidRef { pid_t pid; /* always valid */ int fd; /* only valid if pidfd are available in the kernel, and we manage to get an fd */ uint64_t fd_id; /* the inode number of pidfd. only useful in kernel 6.9+ where pidfds live in their own pidfs and each process comes with a unique inode number */ -} PidRef; +}; #define PIDREF_NULL (const PidRef) { .fd = -EBADF } +/* A special pidref value that we are using when a PID shall be automatically acquired from some surrounding + * context, for example connection peer. Much like PIDREF_NULL it will be considerd unset by + * pidref_is_set().*/ +#define PIDREF_AUTOMATIC (const PidRef) { .pid = PID_AUTOMATIC, .fd = -EBADF } + /* Turns a pid_t into a PidRef structure on-the-fly *without* acquiring a pidfd for it. (As opposed to * pidref_set_pid() which does so *with* acquiring one, see below) */ #define PIDREF_MAKE_FROM_PID(x) (PidRef) { .pid = (x), .fd = -EBADF } @@ -21,6 +29,8 @@ static inline bool pidref_is_set(const PidRef *pidref) { return pidref && pidref->pid > 0; } +bool pidref_is_automatic(const PidRef *pidref); + int pidref_acquire_pidfd_id(PidRef *pidref); bool pidref_equal(PidRef *a, PidRef *b); diff --git a/src/basic/process-util.h b/src/basic/process-util.h index 2efe89e135..05b7a69fc6 100644 --- a/src/basic/process-util.h +++ b/src/basic/process-util.h @@ -15,6 +15,7 @@ #include "format-util.h" #include "macro.h" #include "namespace-util.h" +#include "pidref.h" #include "time-util.h" #define procfs_file_alloca(pid, field) \ @@ -148,8 +149,7 @@ static inline bool sched_priority_is_valid(int i) { return i >= 0 && i <= sched_get_priority_max(SCHED_RR); } -#define PID_AUTOMATIC ((pid_t) INT_MIN) /* special value indicating "acquire pid from connection peer */ -#define PID_INVALID ((pid_t) 0) /* default value for "invalid pid" */ +#define PID_AUTOMATIC ((pid_t) INT_MIN) /* special value indicating "acquire pid from connection peer" */ static inline bool pid_is_valid(pid_t p) { return p > 0; @@ -159,10 +159,6 @@ static inline bool pid_is_automatic(pid_t p) { return p == PID_AUTOMATIC; } -static inline bool pid_is_valid_or_automatic(pid_t p) { - return pid_is_valid(p) || pid_is_automatic(p); -} - pid_t getpid_cached(void); void reset_cached_pid(void); diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym index d642ef2581..b3f46f90f1 100644 --- a/src/libsystemd/libsystemd.sym +++ b/src/libsystemd/libsystemd.sym @@ -864,7 +864,6 @@ global: sd_json_dispatch_strv; sd_json_dispatch_tristate; sd_json_dispatch_uid_gid; - sd_json_dispatch_pid; sd_json_dispatch_uint16; sd_json_dispatch_uint32; sd_json_dispatch_uint64; diff --git a/src/libsystemd/sd-json/json-util.c b/src/libsystemd/sd-json/json-util.c index 2ff4587565..0c1dea566a 100644 --- a/src/libsystemd/sd-json/json-util.c +++ b/src/libsystemd/sd-json/json-util.c @@ -185,7 +185,13 @@ int json_dispatch_pidref(const char *name, sd_json_variant *variant, sd_json_dis * above. If SD_JSON_STRICT is set this will acquire a pidfd for the process, and validate that the * auxiliary fields match it. Otherwise, this will just store the pid and the pidfd inode number (the * latter not if the provided boot id differs from the local one), and not attempt to get a pidfd for - * it, or authenticate it. */ + * it, or authenticate it. + * + * If SD_JSON_RELAX is specified, a specified but zero/empty PID will be mapped to PIDREF_AUTOMATIC, + * which is supposed to indicate that the PID shall be automatically derived, typically from the + * connection peer. + * + * Note that SD_JSON_RELAX and SD_JSON_STRICT can be combined. */ if (sd_json_variant_is_null(variant)) { pidref_done(p); @@ -220,6 +226,13 @@ int json_dispatch_pidref(const char *name, sd_json_variant *variant, sd_json_dis } else return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is neither a numeric PID nor a PID object.", strna(name)); + /* If SD_JSON_RELAX is set then we'll take a specified but zero field as request for "automic" PID derivation */ + if ((flags & SD_JSON_RELAX) && data.pid == 0 && data.fd_id == 0 && sd_id128_is_null(data.boot_id)) { + pidref_done(p); + *p = PIDREF_AUTOMATIC; + return 0; + } + /* Before casting the 64bit data.pid field to pid_t, let's ensure it fits the pid_t range. */ if (data.pid > PID_T_MAX || !pid_is_valid(data.pid)) return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' does not contain a valid PID.", strna(name)); diff --git a/src/libsystemd/sd-json/sd-json.c b/src/libsystemd/sd-json/sd-json.c index 5ff1b584a6..2028f222cb 100644 --- a/src/libsystemd/sd-json/sd-json.c +++ b/src/libsystemd/sd-json/sd-json.c @@ -5564,44 +5564,6 @@ _public_ int sd_json_dispatch_uid_gid(const char *name, sd_json_variant *variant return 0; } -_public_ int sd_json_dispatch_pid(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) { - /* pid_t is a signed type, but we don't consider negative values as valid. - * There is a special treatment for 0 if SD_JSON_RELAX flag present. */ - - pid_t *pid = userdata; - uint32_t k; - int r; - - assert_return(variant, -EINVAL); - assert_cc(sizeof(pid_t) == sizeof(uint32_t)); - - if (sd_json_variant_is_null(variant)) { - *pid = PID_INVALID; - return 0; - } - - r = sd_json_dispatch_uint32(name, variant, flags, &k); - if (r < 0) - return r; - - if (k == 0) { - if (!FLAGS_SET(flags, SD_JSON_RELAX)) - goto invalid_value; - - *pid = PID_AUTOMATIC; - return 0; - } - - if (!pid_is_valid(k)) - goto invalid_value; - - *pid = k; - return 0; - -invalid_value: - return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid PID.", strna(name)); -} - _public_ int sd_json_dispatch_id128(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) { sd_id128_t *uuid = userdata; int r; diff --git a/src/machine/machine-varlink.c b/src/machine/machine-varlink.c index 3affc5265c..c9f44f20ab 100644 --- a/src/machine/machine-varlink.c +++ b/src/machine/machine-varlink.c @@ -200,8 +200,8 @@ static int lookup_machine_by_name(sd_varlink *link, Manager *manager, const char return 0; } -static int lookup_machine_by_pid(sd_varlink *link, Manager *manager, pid_t pid, Machine **ret_machine) { - _cleanup_(pidref_done) PidRef pidref = PIDREF_MAKE_FROM_PID(pid); +static int lookup_machine_by_pidref(sd_varlink *link, Manager *manager, const PidRef *pidref, Machine **ret_machine) { + _cleanup_(pidref_done) PidRef peer = PIDREF_NULL; Machine *machine; int r; @@ -209,16 +209,16 @@ static int lookup_machine_by_pid(sd_varlink *link, Manager *manager, pid_t pid, assert(manager); assert(ret_machine); - if (pid == PID_AUTOMATIC) { - r = varlink_get_peer_pidref(link, &pidref); + if (pidref_is_automatic(pidref)) { + r = varlink_get_peer_pidref(link, &peer); if (r < 0) return log_debug_errno(r, "Failed to get peer pidref: %m"); - } - if (!pidref_is_set(&pidref)) + pidref = &peer; + } else if (!pidref_is_set(pidref)) return -EINVAL; - r = manager_get_machine_by_pidref(manager, &pidref, &machine); + r = manager_get_machine_by_pidref(manager, pidref, &machine); if (r < 0) return r; if (!machine) @@ -228,7 +228,7 @@ static int lookup_machine_by_pid(sd_varlink *link, Manager *manager, pid_t pid, return 0; } -int lookup_machine_by_name_or_pid(sd_varlink *link, Manager *manager, const char *machine_name, pid_t pid, Machine **ret_machine) { +int lookup_machine_by_name_or_pidref(sd_varlink *link, Manager *manager, const char *machine_name, const PidRef *pidref, Machine **ret_machine) { Machine *machine = NULL, *pid_machine = NULL; int r; @@ -246,8 +246,8 @@ int lookup_machine_by_name_or_pid(sd_varlink *link, Manager *manager, const char return r; } - if (pid_is_valid_or_automatic(pid)) { - r = lookup_machine_by_pid(link, manager, pid, &pid_machine); + if (pidref_is_set(pidref) || pidref_is_automatic(pidref)) { + r = lookup_machine_by_pidref(link, manager, pidref, &pid_machine); if (r == -EINVAL) return sd_varlink_error_invalid_parameter_name(link, "pid"); if (r < 0) @@ -255,7 +255,7 @@ int lookup_machine_by_name_or_pid(sd_varlink *link, Manager *manager, const char } 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); + return log_debug_errno(SYNTHETIC_ERRNO(ESRCH), "Search by machine name '%s' and pid " PID_FMT " resulted in two different machines", machine_name, pidref->pid); if (machine) *ret_machine = machine; else if (pid_machine) @@ -310,24 +310,32 @@ 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; - }; +typedef struct MachineKillParameters { + const char *name; + PidRef pidref; + const char *swhom; + int32_t signo; +} MachineKillParameters; +static void machine_kill_paramaters_done(MachineKillParameters *p) { + assert(p); + + pidref_done(&p->pidref); +} + +int vl_method_kill(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) { 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_signal, offsetof(struct params, signo), SD_JSON_MANDATORY }, + VARLINK_DISPATCH_MACHINE_LOOKUP_FIELDS(MachineKillParameters), + { "whom", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(MachineKillParameters, swhom), 0 }, + { "signal", _SD_JSON_VARIANT_TYPE_INVALID , sd_json_dispatch_signal, offsetof(MachineKillParameters, signo), SD_JSON_MANDATORY }, VARLINK_DISPATCH_POLKIT_FIELD, {} }; Manager *manager = ASSERT_PTR(userdata); - struct params p = {}; + _cleanup_(machine_kill_paramaters_done) MachineKillParameters p = { + .pidref = PIDREF_NULL, + }; KillWhom whom; int r; @@ -339,7 +347,7 @@ int vl_method_kill(sd_varlink *link, sd_json_variant *parameters, sd_varlink_met return r; Machine *machine; - r = lookup_machine_by_name_or_pid(link, manager, p.machine_name, p.pid, &machine); + r = lookup_machine_by_name_or_pidref(link, manager, p.name, &p.pidref, &machine); if (r == -ESRCH) return sd_varlink_error(link, "io.systemd.Machine.NoSuchMachine", NULL); if (r != 0) diff --git a/src/machine/machine-varlink.h b/src/machine/machine-varlink.h index 77db7b14ed..5b58b35d80 100644 --- a/src/machine/machine-varlink.h +++ b/src/machine/machine-varlink.h @@ -5,20 +5,20 @@ #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_pid, \ - .offset = offsetof(t, pid), \ - .flags = SD_JSON_RELAX /* allows pid=0 */ \ +#define VARLINK_DISPATCH_MACHINE_LOOKUP_FIELDS(t) { \ + .name = "name", \ + .type = SD_JSON_VARIANT_STRING, \ + .callback = sd_json_dispatch_const_string, \ + .offset = offsetof(t, name) \ + }, { \ + .name = "pid", \ + .type = _SD_JSON_VARIANT_TYPE_INVALID, \ + .callback = json_dispatch_pidref, \ + .offset = offsetof(t, pidref), \ + .flags = SD_JSON_RELAX /* allows PID_AUTOMATIC */ \ } -int lookup_machine_by_name_or_pid(sd_varlink *link, Manager *manager, const char *machine_name, pid_t pid, Machine **ret_machine); +int lookup_machine_by_name_or_pidref(sd_varlink *link, Manager *manager, const char *machine_name, const PidRef *pidref, 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 536aee507d..5932d5edab 100644 --- a/src/machine/machined-varlink.c +++ b/src/machine/machined-varlink.c @@ -421,10 +421,16 @@ static int list_machine_one(sd_varlink *link, Machine *m, bool more) { } typedef struct MachineLookupParameters { - const char *machine_name; - pid_t pid; + const char *name; + PidRef pidref; } MachineLookupParameters; +static void machine_lookup_parameters_done(MachineLookupParameters *p) { + assert(p); + + pidref_done(&p->pidref); +} + 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[] = { VARLINK_DISPATCH_MACHINE_LOOKUP_FIELDS(MachineLookupParameters), @@ -433,7 +439,9 @@ static int vl_method_list(sd_varlink *link, sd_json_variant *parameters, sd_varl }; Manager *m = ASSERT_PTR(userdata); - MachineLookupParameters p = {}; + _cleanup_(machine_lookup_parameters_done) MachineLookupParameters p = { + .pidref = PIDREF_NULL, + }; Machine *machine; int r; @@ -444,8 +452,8 @@ static int vl_method_list(sd_varlink *link, sd_json_variant *parameters, sd_varl if (r != 0) return r; - if (p.machine_name || pid_is_valid_or_automatic(p.pid)) { - r = lookup_machine_by_name_or_pid(link, m, p.machine_name, p.pid, &machine); + if (p.name || pidref_is_set(&p.pidref) || pidref_is_automatic(&p.pidref)) { + r = lookup_machine_by_name_or_pidref(link, m, p.name, &p.pidref, &machine); if (r == -ESRCH) return sd_varlink_error(link, "io.systemd.Machine.NoSuchMachine", NULL); if (r != 0) @@ -482,7 +490,9 @@ static int lookup_machine_and_call_method(sd_varlink *link, sd_json_variant *par }; Manager *manager = ASSERT_PTR(userdata); - MachineLookupParameters p = {}; + _cleanup_(machine_lookup_parameters_done) MachineLookupParameters p = { + .pidref = PIDREF_NULL, + }; Machine *machine; int r; @@ -493,7 +503,7 @@ static int lookup_machine_and_call_method(sd_varlink *link, sd_json_variant *par if (r != 0) return r; - r = lookup_machine_by_name_or_pid(link, manager, p.machine_name, p.pid, &machine); + r = lookup_machine_by_name_or_pidref(link, manager, p.name, &p.pidref, &machine); if (r == -ESRCH) return sd_varlink_error(link, "io.systemd.Machine.NoSuchMachine", NULL); if (r != 0) diff --git a/src/shared/varlink-io.systemd.Machine.c b/src/shared/varlink-io.systemd.Machine.c index 7b2bb4969f..6d90f1e6ce 100644 --- a/src/shared/varlink-io.systemd.Machine.c +++ b/src/shared/varlink-io.systemd.Machine.c @@ -10,7 +10,7 @@ SD_VARLINK_FIELD_COMMENT("If non-null the name of a machine."), \ SD_VARLINK_DEFINE_INPUT(name, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), \ SD_VARLINK_FIELD_COMMENT("If non-null the PID of a machine. Special value 0 means to take pid of the machine the caller is part of."), \ - SD_VARLINK_DEFINE_INPUT(pid, SD_VARLINK_INT, SD_VARLINK_NULLABLE), \ + SD_VARLINK_DEFINE_INPUT_BY_TYPE(pid, ProcessId, SD_VARLINK_NULLABLE), \ VARLINK_DEFINE_POLKIT_INPUT static SD_VARLINK_DEFINE_METHOD( diff --git a/src/systemd/sd-json.h b/src/systemd/sd-json.h index a73eeb8dd6..f3c9a27257 100644 --- a/src/systemd/sd-json.h +++ b/src/systemd/sd-json.h @@ -328,7 +328,6 @@ int sd_json_dispatch_int8(const char *name, sd_json_variant *variant, sd_json_di int sd_json_dispatch_uint8(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata); int sd_json_dispatch_double(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata); int sd_json_dispatch_uid_gid(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata); -int sd_json_dispatch_pid(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata); int sd_json_dispatch_id128(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata); int sd_json_dispatch_signal(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata); int sd_json_dispatch_unsupported(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata); diff --git a/src/test/test-pidref.c b/src/test/test-pidref.c index 53ed10d153..7298b36596 100644 --- a/src/test/test-pidref.c +++ b/src/test/test-pidref.c @@ -224,4 +224,20 @@ TEST(pidref_verify) { assert_se(pidref_verify(&pidref) == (pidref.fd >= 0)); } +TEST(pidref_is_automatic) { + assert_se(!pidref_is_automatic(NULL)); + assert_se(!pidref_is_automatic(&PIDREF_NULL)); + assert_se(!pidref_is_automatic(&PIDREF_MAKE_FROM_PID(1))); + assert_se(!pidref_is_automatic(&PIDREF_MAKE_FROM_PID(getpid_cached()))); + assert_se(pidref_is_automatic(&PIDREF_AUTOMATIC)); + + assert_se(!pid_is_automatic(0)); + assert_se(!pid_is_automatic(1)); + assert_se(!pid_is_automatic(getpid_cached())); + assert_se(pid_is_automatic(PID_AUTOMATIC)); + + assert_se(!pidref_is_set(&PIDREF_AUTOMATIC)); + assert_se(!pid_is_valid(PID_AUTOMATIC)); +} + DEFINE_TEST_MAIN(LOG_DEBUG); diff --git a/test/units/TEST-13-NSPAWN.machined.sh b/test/units/TEST-13-NSPAWN.machined.sh index 987ecfee90..176fe6fb69 100755 --- a/test/units/TEST-13-NSPAWN.machined.sh +++ b/test/units/TEST-13-NSPAWN.machined.sh @@ -261,7 +261,7 @@ varlinkctl --more call /run/systemd/machine/io.systemd.Machine io.systemd.Machin 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.pid') +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}" | diff /tmp/expected - varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List "{\"name\":\"long-running\", \"pid\":$pid}" | diff /tmp/expected -