mirror of
https://github.com/morgan9e/systemd
synced 2026-04-15 00:47:10 +09:00
assert_return() exists for gracefully handling programmng errors in public APIs. But these are internal APIs, hence just use a tough assert().
667 lines
26 KiB
C
667 lines
26 KiB
C
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
|
|
#include <netinet/in.h>
|
|
#include <sys/sysmacros.h>
|
|
|
|
#include "alloc-util.h"
|
|
#include "devnum-util.h"
|
|
#include "env-util.h"
|
|
#include "errno-util.h"
|
|
#include "fd-util.h"
|
|
#include "glyph-util.h"
|
|
#include "iovec-util.h"
|
|
#include "json-util.h"
|
|
#include "log.h"
|
|
#include "mountpoint-util.h"
|
|
#include "parse-util.h"
|
|
#include "path-util.h"
|
|
#include "pidref.h"
|
|
#include "process-util.h"
|
|
#include "stat-util.h"
|
|
#include "stdio-util.h"
|
|
#include "string-util.h"
|
|
#include "strv.h"
|
|
#include "syslog-util.h"
|
|
#include "unit-name.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) {
|
|
_cleanup_free_ void *buffer = NULL;
|
|
struct iovec *iov = ASSERT_PTR(userdata);
|
|
size_t sz;
|
|
int r;
|
|
|
|
if (!sd_json_variant_is_string(variant))
|
|
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
|
|
|
|
r = sd_json_variant_unbase64(variant, &buffer, &sz);
|
|
if (r < 0)
|
|
return json_log(variant, flags, r, "JSON field '%s' is not valid Base64 data.", strna(name));
|
|
|
|
free_and_replace(iov->iov_base, buffer);
|
|
iov->iov_len = sz;
|
|
return 0;
|
|
}
|
|
|
|
int json_dispatch_byte_array_iovec(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
|
_cleanup_free_ uint8_t *buffer = NULL;
|
|
struct iovec *iov = ASSERT_PTR(userdata);
|
|
size_t sz, k = 0;
|
|
|
|
assert(variant);
|
|
|
|
if (!sd_json_variant_is_array(variant))
|
|
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array.", strna(name));
|
|
|
|
sz = sd_json_variant_elements(variant);
|
|
|
|
buffer = new(uint8_t, sz + 1);
|
|
if (!buffer)
|
|
return json_log(variant, flags, SYNTHETIC_ERRNO(ENOMEM), "Out of memory.");
|
|
|
|
sd_json_variant *i;
|
|
JSON_VARIANT_ARRAY_FOREACH(i, variant) {
|
|
uint64_t b;
|
|
|
|
if (!sd_json_variant_is_unsigned(i))
|
|
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "Element %zu of JSON field '%s' is not an unsigned integer.", k, strna(name));
|
|
|
|
b = sd_json_variant_unsigned(i);
|
|
if (b > 0xff)
|
|
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL),
|
|
"Element %zu of JSON field '%s' is out of range 0%s255.",
|
|
k, strna(name), glyph(GLYPH_ELLIPSIS));
|
|
|
|
buffer[k++] = (uint8_t) b;
|
|
}
|
|
assert(k == sz);
|
|
|
|
/* Append a NUL byte for safety, like we do in memdup_suffix0() and others. */
|
|
buffer[sz] = 0;
|
|
|
|
free_and_replace(iov->iov_base, buffer);
|
|
iov->iov_len = sz;
|
|
return 0;
|
|
}
|
|
|
|
int json_dispatch_user_group_name(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
|
char **s = ASSERT_PTR(userdata);
|
|
const char *n;
|
|
int r;
|
|
|
|
r = json_dispatch_const_user_group_name(name, variant, flags, &n);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = free_and_strdup(s, n);
|
|
if (r < 0)
|
|
return json_log(variant, flags, r, "Failed to allocate string: %m");
|
|
|
|
return 0;
|
|
}
|
|
|
|
int json_dispatch_const_user_group_name(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
|
const char **s = ASSERT_PTR(userdata), *n;
|
|
|
|
if (sd_json_variant_is_null(variant)) {
|
|
*s = NULL;
|
|
return 0;
|
|
}
|
|
|
|
if (!sd_json_variant_is_string(variant))
|
|
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
|
|
|
|
n = sd_json_variant_string(variant);
|
|
if (!valid_user_group_name(n, FLAGS_SET(flags, SD_JSON_RELAX) ? VALID_USER_RELAX : 0))
|
|
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid user/group name.", strna(name));
|
|
|
|
*s = n;
|
|
return 0;
|
|
}
|
|
|
|
int json_dispatch_const_unit_name(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
|
const char **s = ASSERT_PTR(userdata), *n;
|
|
UnitNameFlags unitname_flags;
|
|
|
|
if (sd_json_variant_is_null(variant)) {
|
|
*s = NULL;
|
|
return 0;
|
|
}
|
|
|
|
if (!sd_json_variant_is_string(variant))
|
|
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
|
|
|
|
if (FLAGS_SET(flags, SD_JSON_STRICT))
|
|
unitname_flags = UNIT_NAME_PLAIN;
|
|
else if (FLAGS_SET(flags, SD_JSON_RELAX))
|
|
unitname_flags = UNIT_NAME_ANY;
|
|
else
|
|
unitname_flags = UNIT_NAME_PLAIN | UNIT_NAME_INSTANCE;
|
|
|
|
n = sd_json_variant_string(variant);
|
|
if (!unit_name_is_valid(n, unitname_flags))
|
|
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid unit name.", strna(name));
|
|
|
|
*s = n;
|
|
return 0;
|
|
}
|
|
|
|
int json_dispatch_in_addr(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
|
struct in_addr *address = ASSERT_PTR(userdata);
|
|
_cleanup_(iovec_done) struct iovec iov = {};
|
|
int r;
|
|
|
|
r = json_dispatch_byte_array_iovec(name, variant, flags, &iov);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
if (iov.iov_len != sizeof(struct in_addr))
|
|
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is array of unexpected size.", strna(name));
|
|
|
|
memcpy(address, iov.iov_base, iov.iov_len);
|
|
return 0;
|
|
}
|
|
|
|
int json_dispatch_const_path(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
|
const char **p = ASSERT_PTR(userdata), *path;
|
|
|
|
assert(variant);
|
|
|
|
if (sd_json_variant_is_null(variant)) {
|
|
*p = NULL;
|
|
return 0;
|
|
}
|
|
|
|
if (!sd_json_variant_is_string(variant))
|
|
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
|
|
|
|
path = sd_json_variant_string(variant);
|
|
if (!((flags & SD_JSON_STRICT) ? path_is_normalized(path) : path_is_valid(path)))
|
|
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a normalized file system path.", strna(name));
|
|
if (!path_is_absolute(path))
|
|
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an absolute file system path.", strna(name));
|
|
|
|
*p = path;
|
|
return 0;
|
|
}
|
|
|
|
int json_dispatch_path(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
|
char **p = ASSERT_PTR(userdata);
|
|
const char *path;
|
|
int r;
|
|
|
|
assert(variant);
|
|
|
|
r = json_dispatch_const_path(name, variant, flags, &path);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
if (free_and_strdup(p, path) < 0)
|
|
return json_log_oom(variant, flags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int json_dispatch_strv_path(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
|
_cleanup_strv_free_ char **n = NULL;
|
|
char ***l = ASSERT_PTR(userdata);
|
|
int r;
|
|
|
|
assert(variant);
|
|
|
|
if (sd_json_variant_is_null(variant)) {
|
|
*l = strv_free(*l);
|
|
return 0;
|
|
}
|
|
|
|
if (!sd_json_variant_is_array(variant))
|
|
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array.", strna(name));
|
|
|
|
sd_json_variant *i;
|
|
JSON_VARIANT_ARRAY_FOREACH(i, variant) {
|
|
const char *a;
|
|
r = json_dispatch_const_path(name, i, flags, &a);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = strv_extend(&n, a);
|
|
if (r < 0)
|
|
return json_log_oom(variant, flags);
|
|
}
|
|
|
|
return strv_free_and_replace(*l, n);
|
|
}
|
|
|
|
int json_dispatch_const_filename(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
|
const char **n = ASSERT_PTR(userdata);
|
|
|
|
if (sd_json_variant_is_null(variant)) {
|
|
*n = NULL;
|
|
return 0;
|
|
}
|
|
|
|
if (!sd_json_variant_is_string(variant))
|
|
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
|
|
|
|
const char *filename = sd_json_variant_string(variant);
|
|
if (!filename_is_valid(filename))
|
|
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid file name.", strna(name));
|
|
|
|
*n = filename;
|
|
return 0;
|
|
}
|
|
|
|
int json_dispatch_filename(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
|
char **n = ASSERT_PTR(userdata);
|
|
const char *filename;
|
|
int r;
|
|
|
|
assert(variant);
|
|
|
|
r = json_dispatch_const_filename(name, variant, flags, &filename);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
if (free_and_strdup(n, filename) < 0)
|
|
return json_log_oom(variant, flags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int json_dispatch_const_version(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
|
const char **n = ASSERT_PTR(userdata);
|
|
|
|
assert(variant);
|
|
|
|
if (sd_json_variant_is_null(variant)) {
|
|
*n = NULL;
|
|
return 0;
|
|
}
|
|
|
|
if (!sd_json_variant_is_string(variant))
|
|
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
|
|
|
|
const char *version = sd_json_variant_string(variant);
|
|
if (!version_is_valid(version))
|
|
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid version string.", strna(name));
|
|
|
|
*n = version;
|
|
return 0;
|
|
}
|
|
|
|
int json_dispatch_version(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
|
char **n = ASSERT_PTR(userdata);
|
|
const char *version;
|
|
int r;
|
|
|
|
assert(variant);
|
|
|
|
r = json_dispatch_const_version(name, variant, flags, &version);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
if (free_and_strdup(n, version) < 0)
|
|
return json_log_oom(variant, flags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int json_variant_new_pidref(sd_json_variant **ret, PidRef *pidref) {
|
|
sd_id128_t boot_id = SD_ID128_NULL;
|
|
int r;
|
|
|
|
/* Turns a PidRef into a triplet of PID, pidfd inode nr, and the boot ID. The triplet should uniquely
|
|
* identify the process globally, and be good enough to turn back into a pidfd + PidRef */
|
|
|
|
if (!pidref_is_set(pidref))
|
|
return sd_json_variant_new_null(ret);
|
|
|
|
if (!pidref_is_remote(pidref)) {
|
|
r = pidref_acquire_pidfd_id(pidref);
|
|
if (r < 0 && !ERRNO_IS_NEG_NOT_SUPPORTED(r) && r != -ENOMEDIUM)
|
|
return r;
|
|
|
|
/* If we have the pidfd inode number, also acquire the boot ID, to make things universally unique */
|
|
if (pidref->fd_id > 0) {
|
|
r = sd_id128_get_boot(&boot_id);
|
|
if (r < 0)
|
|
return r;
|
|
}
|
|
}
|
|
|
|
return sd_json_buildo(
|
|
ret,
|
|
SD_JSON_BUILD_PAIR_INTEGER("pid", pidref->pid),
|
|
SD_JSON_BUILD_PAIR_CONDITION(pidref->fd_id > 0, "pidfdId", SD_JSON_BUILD_INTEGER(pidref->fd_id)),
|
|
SD_JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(boot_id), "bootId", SD_JSON_BUILD_ID128(boot_id)));
|
|
}
|
|
|
|
int json_dispatch_pidref(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
|
PidRef *p = ASSERT_PTR(userdata);
|
|
int r;
|
|
|
|
assert(variant);
|
|
|
|
/* Turns a JSON PID triplet back into a PidRef, i.e. the reverse of json_variant_new_pidref()
|
|
* 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.
|
|
*
|
|
* 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);
|
|
return 0;
|
|
}
|
|
|
|
struct {
|
|
uint64_t pid, fd_id;
|
|
sd_id128_t boot_id;
|
|
} data = {};
|
|
|
|
if (sd_json_variant_is_integer(variant))
|
|
/* Permit a simple classic integer based format */
|
|
data.pid = sd_json_variant_integer(variant);
|
|
else if (sd_json_variant_is_string(variant)) {
|
|
/* As usual, allow integers be encoded as strings too */
|
|
r = safe_atou64(sd_json_variant_string(variant), &data.pid);
|
|
if (r < 0)
|
|
return json_log(variant, flags, r, "JSON field '%s' is not a numeric PID.", strna(name));
|
|
} else if (sd_json_variant_is_object(variant)) {
|
|
|
|
static const sd_json_dispatch_field dispatch_table[] = {
|
|
{ "pid", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, voffsetof(data, pid), SD_JSON_MANDATORY },
|
|
{ "pidfdId", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, voffsetof(data, fd_id), 0 },
|
|
{ "bootId", SD_JSON_VARIANT_STRING, sd_json_dispatch_id128, voffsetof(data, boot_id), 0 },
|
|
{}
|
|
};
|
|
|
|
r = sd_json_dispatch(variant, dispatch_table, flags, &data);
|
|
if (r < 0)
|
|
return r;
|
|
} 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 "automatic" 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));
|
|
|
|
int local_boot_id = -1; /* tristate */
|
|
if (!sd_id128_is_null(data.boot_id)) {
|
|
sd_id128_t my_boot_id;
|
|
|
|
r = sd_id128_get_boot(&my_boot_id);
|
|
if (r < 0) {
|
|
json_log(variant, flags | (FLAGS_SET(flags, SD_JSON_STRICT) ? 0 : SD_JSON_DEBUG), r, "Unable to get local boot ID to validate JSON field '%s': %m", strna(name));
|
|
if (FLAGS_SET(flags, SD_JSON_STRICT))
|
|
return r;
|
|
} else {
|
|
local_boot_id = sd_id128_equal(data.boot_id, my_boot_id);
|
|
if (!local_boot_id) {
|
|
json_log(variant, flags | (FLAGS_SET(flags, SD_JSON_STRICT) ? 0 : SD_JSON_DEBUG), 0, "JSON field '%s' refers to non-local PID%s.", strna(name), FLAGS_SET(flags, SD_JSON_STRICT) ? "" : ", proceeding");
|
|
if (FLAGS_SET(flags, SD_JSON_STRICT))
|
|
return -ESRCH;
|
|
}
|
|
}
|
|
}
|
|
|
|
_cleanup_(pidref_done) PidRef np = PIDREF_NULL;
|
|
if (local_boot_id == 0)
|
|
/* If this is definitely not the local boot ID, then mark the PidRef as remote in the sense of pidref_is_remote() */
|
|
np = (PidRef) {
|
|
.pid = data.pid,
|
|
.fd = -EREMOTE,
|
|
.fd_id = data.fd_id,
|
|
};
|
|
else {
|
|
/* Try to acquire a pidfd if this is or might be a local PID */
|
|
r = pidref_set_pid(&np, data.pid);
|
|
if (r < 0) {
|
|
json_log(variant, flags | (FLAGS_SET(flags, SD_JSON_STRICT) ? 0 : SD_JSON_DEBUG), r, "Unable to get fd for PID in JSON field '%s': %m", strna(name));
|
|
if (FLAGS_SET(flags, SD_JSON_STRICT))
|
|
return r;
|
|
|
|
/* If the PID is dead or we otherwise can't get a pidfd of it, then store at least the PID number */
|
|
np = PIDREF_MAKE_FROM_PID(data.pid);
|
|
}
|
|
|
|
/* If the pidfd inode nr is specified, validate it or at least state */
|
|
if (data.fd_id > 0) {
|
|
if (np.fd >= 0) {
|
|
r = pidref_acquire_pidfd_id(&np);
|
|
if (r < 0 && !ERRNO_IS_NOT_SUPPORTED(r))
|
|
return json_log(variant, flags, r, "Unable to get pidfd ID to validate JSON field '%s': %m", strna(name));
|
|
|
|
if (data.fd_id != np.fd_id) {
|
|
json_log(variant, flags | (FLAGS_SET(flags, SD_JSON_STRICT) ? 0 : SD_JSON_DEBUG), 0, "JSON field '%s' references PID with non-matching inode number.", strna(name));
|
|
if (FLAGS_SET(flags, SD_JSON_STRICT))
|
|
return -ESRCH;
|
|
}
|
|
} else {
|
|
json_log(variant, flags|SD_JSON_DEBUG, 0, "Not validating PID inode number on JSON field '%s', because operating without pidfd.", strna(name));
|
|
np.fd_id = data.fd_id;
|
|
}
|
|
}
|
|
}
|
|
|
|
pidref_done(p);
|
|
*p = TAKE_PIDREF(np);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int json_dispatch_ifindex(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
|
int *ifi = ASSERT_PTR(userdata), r, t;
|
|
|
|
if (sd_json_variant_is_null(variant)) {
|
|
*ifi = 0;
|
|
return 0;
|
|
}
|
|
|
|
r = sd_json_dispatch_int(name, variant, flags, &t);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
/* If SD_JSON_RELAX is set allow a zero interface index, otherwise refuse. */
|
|
if (t < (FLAGS_SET(flags, SD_JSON_RELAX) ? 0 : 1))
|
|
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is out of bounds for an interface index.", strna(name));
|
|
|
|
*ifi = t;
|
|
return 0;
|
|
}
|
|
|
|
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)) {
|
|
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;
|
|
}
|
|
|
|
r = sd_json_dispatch_int(name, variant, flags, &t);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
if (FLAGS_SET(flags, SD_JSON_RELAX) && t < 0)
|
|
t = LOG_NULL;
|
|
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;
|
|
return 0;
|
|
}
|
|
|
|
int json_variant_new_devnum(sd_json_variant **ret, dev_t devnum) {
|
|
if (devnum == 0)
|
|
return sd_json_variant_new_null(ret);
|
|
|
|
return sd_json_buildo(
|
|
ret,
|
|
SD_JSON_BUILD_PAIR_UNSIGNED("major", major(devnum)),
|
|
SD_JSON_BUILD_PAIR_UNSIGNED("minor", minor(devnum)));
|
|
}
|
|
|
|
int json_dispatch_devnum(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
|
dev_t *ret = ASSERT_PTR(userdata);
|
|
int r;
|
|
|
|
assert(variant);
|
|
|
|
if (sd_json_variant_is_null(variant)) {
|
|
*ret = 0;
|
|
return 0;
|
|
}
|
|
|
|
struct {
|
|
uint32_t major;
|
|
uint32_t minor;
|
|
} data;
|
|
|
|
static const sd_json_dispatch_field dispatch_table[] = {
|
|
{ "major", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint32, voffsetof(data, major), SD_JSON_MANDATORY },
|
|
{ "minor", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint32, voffsetof(data, minor), SD_JSON_MANDATORY },
|
|
{}
|
|
};
|
|
|
|
r = sd_json_dispatch(variant, dispatch_table, flags, &data);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
if (!DEVICE_MAJOR_VALID(data.major) || !DEVICE_MINOR_VALID(data.minor))
|
|
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid device number.", strna(name));
|
|
|
|
*ret = makedev(data.major, data.minor);
|
|
return 0;
|
|
}
|
|
|
|
int json_dispatch_strv_environment(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
|
_cleanup_strv_free_ char **n = NULL;
|
|
char ***l = userdata;
|
|
int r;
|
|
|
|
if (sd_json_variant_is_null(variant)) {
|
|
*l = strv_free(*l);
|
|
return 0;
|
|
}
|
|
|
|
if (!sd_json_variant_is_array(variant))
|
|
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array.", strna(name));
|
|
|
|
for (size_t i = 0; i < sd_json_variant_elements(variant); i++) {
|
|
sd_json_variant *e;
|
|
const char *a;
|
|
|
|
e = sd_json_variant_by_index(variant, i);
|
|
if (!sd_json_variant_is_string(e))
|
|
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of strings.", strna(name));
|
|
|
|
assert_se(a = sd_json_variant_string(e));
|
|
|
|
if (!env_assignment_is_valid(a))
|
|
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of environment variables.", strna(name));
|
|
|
|
r = strv_env_replace_strdup(&n, a);
|
|
if (r < 0)
|
|
return json_log_oom(variant, flags);
|
|
}
|
|
|
|
return strv_free_and_replace(*l, n);
|
|
}
|
|
|
|
static int json_variant_new_stat(sd_json_variant **ret, const struct stat *st) {
|
|
char mode[STRLEN("0755")+1];
|
|
|
|
assert(st);
|
|
|
|
if (!stat_is_set(st))
|
|
return sd_json_variant_new_null(ret);
|
|
|
|
xsprintf(mode, "%04o", st->st_mode & ~S_IFMT);
|
|
|
|
return sd_json_buildo(
|
|
ret,
|
|
JSON_BUILD_PAIR_DEVNUM("dev", st->st_dev),
|
|
SD_JSON_BUILD_PAIR_UNSIGNED("inode", st->st_ino),
|
|
JSON_BUILD_PAIR_STRING_NON_EMPTY("type", inode_type_to_string(st->st_mode)),
|
|
SD_JSON_BUILD_PAIR_STRING("mode", mode),
|
|
SD_JSON_BUILD_PAIR_UNSIGNED("linkCount", st->st_nlink),
|
|
SD_JSON_BUILD_PAIR_UNSIGNED("uid", st->st_uid),
|
|
SD_JSON_BUILD_PAIR_UNSIGNED("gid", st->st_gid),
|
|
SD_JSON_BUILD_PAIR_CONDITION(
|
|
S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode),
|
|
"rdev",
|
|
JSON_BUILD_DEVNUM(st->st_rdev)),
|
|
SD_JSON_BUILD_PAIR_UNSIGNED("size", st->st_size),
|
|
SD_JSON_BUILD_PAIR_UNSIGNED("blockSize", st->st_blksize),
|
|
SD_JSON_BUILD_PAIR_UNSIGNED("blocks", st->st_blocks));
|
|
}
|
|
|
|
static int json_variant_new_file_handle(sd_json_variant **ret, const struct file_handle *fid) {
|
|
assert(ret);
|
|
|
|
if (!fid)
|
|
return sd_json_variant_new_null(ret);
|
|
|
|
return sd_json_buildo(
|
|
ret,
|
|
SD_JSON_BUILD_PAIR_INTEGER("type", fid->handle_type),
|
|
SD_JSON_BUILD_PAIR_BASE64("handle", fid->f_handle, fid->handle_bytes));
|
|
}
|
|
|
|
int json_variant_new_fd_info(sd_json_variant **ret, int fd) {
|
|
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL, *w = NULL;
|
|
_cleanup_free_ char *path = NULL;
|
|
_cleanup_free_ struct file_handle *fid = NULL;
|
|
struct stat st;
|
|
int mntid = -1, r;
|
|
|
|
assert(fd >= 0 || fd == AT_FDCWD);
|
|
|
|
r = fd_get_path(fd, &path);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
/* If AT_FDCWD is specified, show information about the current working directory. */
|
|
if (fstatat(fd, "", &st, AT_EMPTY_PATH) < 0)
|
|
return -errno;
|
|
|
|
r = json_variant_new_stat(&v, &st);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = name_to_handle_at_try_fid(fd, "", &fid, &mntid, AT_EMPTY_PATH);
|
|
if (r < 0 && is_name_to_handle_at_fatal_error(r))
|
|
return r;
|
|
|
|
r = json_variant_new_file_handle(&w, fid);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
return sd_json_buildo(
|
|
ret,
|
|
JSON_BUILD_PAIR_INTEGER_NON_NEGATIVE("fd", fd),
|
|
SD_JSON_BUILD_PAIR_STRING("path", path),
|
|
SD_JSON_BUILD_PAIR_VARIANT("stat", v),
|
|
JSON_BUILD_PAIR_INTEGER_NON_NEGATIVE("mountId", mntid),
|
|
SD_JSON_BUILD_PAIR_VARIANT("fileHandle", w));
|
|
}
|