Merge pull request #29630 from DaanDeMeyer/manager-json

Various refactoring in preparation for adding JSON dump to pid 1
This commit is contained in:
Daan De Meyer 2023-10-20 16:42:12 +02:00 committed by GitHub
commit 26204e1a4a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
31 changed files with 606 additions and 415 deletions

View file

@ -234,16 +234,17 @@ QEMU.
To allow VSCode's debugger to attach to systemd running in a mkosi image, we have to make sure it can access
the virtual machine spawned by mkosi where systemd is running. mkosi makes this possible via a handy SSH
option that makes the generated image accessible via SSH when booted. Thus you must build the image with
`mkosi --ssh`. The easiest way to set the option is to create a file 20-local.conf in mkosi.conf.d/ (in the
directory you ran mkosi in) and add the following contents:
`mkosi --ssh`. The easiest way to set the option is to create a file `mkosi.conf` in the root of the
repository and add the following contents:
```
[Host]
Ssh=yes
RuntimeTrees=.
```
Also make sure that the SSH agent is running on your system and that you've added your SSH key to it with
`ssh-add`.
`ssh-add`. Also make sure that `virtiofsd` is installed.
After rebuilding the image and booting it with `mkosi qemu`, you should now be able to connect to it by
running `mkosi ssh` from the same directory in another terminal window.
@ -284,14 +285,10 @@ the directory, and add the following contents:
},
"MIMode": "gdb",
"sourceFileMap": {
"/work/build/../src": {
"/root/src/systemd": {
"editorPath": "${workspaceFolder}",
"useForBreakpoints": false
},
"/work/build/*": {
"editorPath": "${workspaceFolder}/mkosi.builddir",
"useForBreakpoints": false
}
}
}
]

View file

@ -7,7 +7,6 @@ Dependencies=base
Autologin=yes
BaseTrees=../../mkosi.output/base
ExtraTrees=../../mkosi.output/base-systemd
ExtraTrees=../../src:/usr/src/src
Packages=
acl
bash-completion

View file

@ -1,3 +1,3 @@
set debuginfod enabled off
set build-id-verbose 0
set substitute-path ../src /usr/src
set substitute-path ../src /root/src/systemd

View file

@ -2135,7 +2135,9 @@ int _hashmap_dump_sorted(HashmapBase *h, void ***ret, size_t *ret_n) {
return 0;
}
entries = new(struct hashmap_base_entry*, _hashmap_size(h));
/* We append one more element than needed so that the resulting array can be used as a strv. We
* don't count this entry in the returned size. */
entries = new(struct hashmap_base_entry*, _hashmap_size(h) + 1);
if (!entries)
return -ENOMEM;
@ -2143,6 +2145,7 @@ int _hashmap_dump_sorted(HashmapBase *h, void ***ret, size_t *ret_n) {
entries[n++] = bucket_at(h, idx);
assert(n == _hashmap_size(h));
entries[n] = NULL;
typesafe_qsort_r(entries, n, hashmap_entry_compare, h->hash_ops->compare);

View file

@ -68,6 +68,14 @@ const char *unit_dbus_interface_from_name(const char *name) {
return unit_dbus_interface_from_type(t);
}
const char* unit_type_to_capitalized_string(UnitType t) {
const char *di = unit_dbus_interface_from_type(t);
if (!di)
return NULL;
return ASSERT_PTR(startswith(di, "org.freedesktop.systemd1."));
}
static const char* const unit_type_table[_UNIT_TYPE_MAX] = {
[UNIT_SERVICE] = "service",
[UNIT_SOCKET] = "socket",

View file

@ -287,6 +287,8 @@ const char *unit_dbus_interface_from_name(const char *name);
const char *unit_type_to_string(UnitType i) _const_;
UnitType unit_type_from_string(const char *s) _pure_;
const char* unit_type_to_capitalized_string(UnitType t);
const char *unit_load_state_to_string(UnitLoadState i) _const_;
UnitLoadState unit_load_state_from_string(const char *s) _pure_;

View file

@ -4543,3 +4543,21 @@ static const char* const cgroup_pressure_watch_table[_CGROUP_PRESSURE_WATCH_MAX]
};
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(cgroup_pressure_watch, CGroupPressureWatch, CGROUP_PRESSURE_WATCH_ON);
static const char* const cgroup_ip_accounting_metric_table[_CGROUP_IP_ACCOUNTING_METRIC_MAX] = {
[CGROUP_IP_INGRESS_BYTES] = "IPIngressBytes",
[CGROUP_IP_EGRESS_BYTES] = "IPEgressBytes",
[CGROUP_IP_INGRESS_PACKETS] = "IPIngressPackets",
[CGROUP_IP_EGRESS_PACKETS] = "IPEgressPackets",
};
DEFINE_STRING_TABLE_LOOKUP(cgroup_ip_accounting_metric, CGroupIPAccountingMetric);
static const char* const cgroup_io_accounting_metric_table[_CGROUP_IO_ACCOUNTING_METRIC_MAX] = {
[CGROUP_IO_READ_BYTES] = "IOReadBytes",
[CGROUP_IO_WRITE_BYTES] = "IOWriteBytes",
[CGROUP_IO_READ_OPERATIONS] = "IOReadOperations",
[CGROUP_IO_WRITE_OPERATIONS] = "IOWriteOperations",
};
DEFINE_STRING_TABLE_LOOKUP(cgroup_io_accounting_metric, CGroupIOAccountingMetric);

View file

@ -399,3 +399,9 @@ CGroupPressureWatch cgroup_pressure_watch_from_string(const char *s) _pure_;
const char *cgroup_device_permissions_to_string(CGroupDevicePermissions p) _const_;
CGroupDevicePermissions cgroup_device_permissions_from_string(const char *s) _pure_;
const char* cgroup_ip_accounting_metric_to_string(CGroupIPAccountingMetric m) _const_;
CGroupIPAccountingMetric cgroup_ip_accounting_metric_from_string(const char *s) _pure_;
const char* cgroup_io_accounting_metric_to_string(CGroupIOAccountingMetric m) _const_;
CGroupIOAccountingMetric cgroup_io_accounting_metric_from_string(const char *s) _pure_;

View file

@ -61,6 +61,12 @@ static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_empty_string, "s", NULL);
static BUS_DEFINE_PROPERTY_GET_REF(property_get_syslog_level, "i", int, LOG_PRI);
static BUS_DEFINE_PROPERTY_GET_REF(property_get_syslog_facility, "i", int, LOG_FAC);
static BUS_DEFINE_PROPERTY_GET(property_get_cpu_affinity_from_numa, "b", ExecContext, exec_context_get_cpu_affinity_from_numa);
static BUS_DEFINE_PROPERTY_GET(property_get_oom_score_adjust, "i", ExecContext, exec_context_get_oom_score_adjust);
static BUS_DEFINE_PROPERTY_GET(property_get_nice, "i", ExecContext, exec_context_get_nice);
static BUS_DEFINE_PROPERTY_GET(property_get_cpu_sched_policy, "i", ExecContext, exec_context_get_cpu_sched_policy);
static BUS_DEFINE_PROPERTY_GET(property_get_cpu_sched_priority, "i", ExecContext, exec_context_get_cpu_sched_priority);
static BUS_DEFINE_PROPERTY_GET(property_get_coredump_filter, "t", ExecContext, exec_context_get_coredump_filter);
static BUS_DEFINE_PROPERTY_GET(property_get_timer_slack_nsec, "t", ExecContext, exec_context_get_timer_slack_nsec);
static int property_get_environment_files(
sd_bus *bus,
@ -92,150 +98,6 @@ static int property_get_environment_files(
return sd_bus_message_close_container(reply);
}
static int property_get_oom_score_adjust(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
ExecContext *c = ASSERT_PTR(userdata);
int r, n;
assert(bus);
assert(reply);
if (c->oom_score_adjust_set)
n = c->oom_score_adjust;
else {
n = 0;
r = get_oom_score_adjust(&n);
if (r < 0)
log_debug_errno(r, "Failed to read /proc/self/oom_score_adj, ignoring: %m");
}
return sd_bus_message_append(reply, "i", n);
}
static int property_get_coredump_filter(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
ExecContext *c = ASSERT_PTR(userdata);
uint64_t n;
int r;
assert(bus);
assert(reply);
if (c->coredump_filter_set)
n = c->coredump_filter;
else {
_cleanup_free_ char *t = NULL;
n = COREDUMP_FILTER_MASK_DEFAULT;
r = read_one_line_file("/proc/self/coredump_filter", &t);
if (r < 0)
log_debug_errno(r, "Failed to read /proc/self/coredump_filter, ignoring: %m");
else {
r = safe_atoux64(t, &n);
if (r < 0)
log_debug_errno(r, "Failed to parse \"%s\" from /proc/self/coredump_filter, ignoring: %m", t);
}
}
return sd_bus_message_append(reply, "t", n);
}
static int property_get_nice(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
ExecContext *c = ASSERT_PTR(userdata);
int32_t n;
assert(bus);
assert(reply);
if (c->nice_set)
n = c->nice;
else {
errno = 0;
n = getpriority(PRIO_PROCESS, 0);
if (errno > 0)
n = 0;
}
return sd_bus_message_append(reply, "i", n);
}
static int property_get_cpu_sched_policy(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
ExecContext *c = ASSERT_PTR(userdata);
int32_t n;
assert(bus);
assert(reply);
if (c->cpu_sched_set)
n = c->cpu_sched_policy;
else {
n = sched_getscheduler(0);
if (n < 0)
n = SCHED_OTHER;
}
return sd_bus_message_append(reply, "i", n);
}
static int property_get_cpu_sched_priority(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
ExecContext *c = ASSERT_PTR(userdata);
int32_t n;
assert(bus);
assert(reply);
if (c->cpu_sched_set)
n = c->cpu_sched_priority;
else {
struct sched_param p = {};
if (sched_getparam(0, &p) >= 0)
n = p.sched_priority;
else
n = 0;
}
return sd_bus_message_append(reply, "i", n);
}
static int property_get_cpu_affinity(
sd_bus *bus,
const char *path,
@ -306,29 +168,6 @@ static int property_get_numa_policy(
return sd_bus_message_append_basic(reply, 'i', &policy);
}
static int property_get_timer_slack_nsec(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
ExecContext *c = ASSERT_PTR(userdata);
uint64_t u;
assert(bus);
assert(reply);
if (c->timer_slack_nsec != NSEC_INFINITY)
u = (uint64_t) c->timer_slack_nsec;
else
u = (uint64_t) prctl(PR_GET_TIMERSLACK);
return sd_bus_message_append(reply, "t", u);
}
static int property_get_syscall_filter(
sd_bus *bus,
const char *path,
@ -353,43 +192,9 @@ static int property_get_syscall_filter(
if (r < 0)
return r;
#if HAVE_SECCOMP
void *id, *val;
HASHMAP_FOREACH_KEY(val, id, c->syscall_filter) {
_cleanup_free_ char *name = NULL;
const char *e = NULL;
char *s;
int num = PTR_TO_INT(val);
if (c->syscall_allow_list && num >= 0)
/* syscall with num >= 0 in allow-list is denied. */
continue;
name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
if (!name)
continue;
if (num >= 0) {
e = seccomp_errno_or_action_to_string(num);
if (e) {
s = strjoin(name, ":", e);
if (!s)
return -ENOMEM;
} else {
r = asprintf(&s, "%s:%d", name, num);
if (r < 0)
return -ENOMEM;
}
} else
s = TAKE_PTR(name);
r = strv_consume(&l, s);
if (r < 0)
return r;
}
#endif
strv_sort(l);
l = exec_context_get_syscall_filter(c);
if (!l)
return -ENOMEM;
r = sd_bus_message_append_strv(reply, l);
if (r < 0)
@ -422,22 +227,9 @@ static int property_get_syscall_log(
if (r < 0)
return r;
#if HAVE_SECCOMP
void *id, *val;
HASHMAP_FOREACH_KEY(val, id, c->syscall_log) {
char *name = NULL;
name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
if (!name)
continue;
r = strv_consume(&l, name);
if (r < 0)
return r;
}
#endif
strv_sort(l);
l = exec_context_get_syscall_log(c);
if (!l)
return -ENOMEM;
r = sd_bus_message_append_strv(reply, l);
if (r < 0)
@ -455,28 +247,16 @@ static int property_get_syscall_archs(
void *userdata,
sd_bus_error *error) {
ExecContext *c = ASSERT_PTR(userdata);
_cleanup_strv_free_ char **l = NULL;
int r;
assert(bus);
assert(reply);
#if HAVE_SECCOMP
void *id;
SET_FOREACH(id, ASSERT_PTR((ExecContext*) userdata)->syscall_archs) {
const char *name;
name = seccomp_arch_to_string(PTR_TO_UINT32(id) - 1);
if (!name)
continue;
r = strv_extend(&l, name);
if (r < 0)
return -ENOMEM;
}
#endif
strv_sort(l);
l = exec_context_get_syscall_archs(c);
if (!l)
return -ENOMEM;
r = sd_bus_message_append_strv(reply, l);
if (r < 0)
@ -547,7 +327,6 @@ static int property_get_address_families(
ExecContext *c = ASSERT_PTR(userdata);
_cleanup_strv_free_ char **l = NULL;
void *af;
int r;
assert(bus);
@ -561,19 +340,9 @@ static int property_get_address_families(
if (r < 0)
return r;
SET_FOREACH(af, c->address_families) {
const char *name;
name = af_to_name(PTR_TO_INT(af));
if (!name)
continue;
r = strv_extend(&l, name);
if (r < 0)
return -ENOMEM;
}
strv_sort(l);
l = exec_context_get_address_families(c);
if (!l)
return -ENOMEM;
r = sd_bus_message_append_strv(reply, l);
if (r < 0)
@ -678,13 +447,9 @@ static int property_get_restrict_filesystems(
if (r < 0)
return r;
#if HAVE_LIBBPF
l = set_get_strv(c->restrict_filesystems);
l = exec_context_get_restrict_filesystems(c);
if (!l)
return -ENOMEM;
#endif
strv_sort(l);
r = sd_bus_message_append_strv(reply, l);
if (r < 0)

View file

@ -67,6 +67,7 @@ static BUS_DEFINE_PROPERTY_GET(property_get_default_timeout_abort_usec, "t", Man
static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_watchdog_device, "s", watchdog_get_device());
static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_watchdog_last_ping_realtime, "t", watchdog_get_last_ping(CLOCK_REALTIME));
static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_watchdog_last_ping_monotonic, "t", watchdog_get_last_ping(CLOCK_MONOTONIC));
static BUS_DEFINE_PROPERTY_GET(property_get_progress, "d", Manager, manager_get_progress);
static int property_get_virtualization(
sd_bus *bus,
@ -207,29 +208,6 @@ static int property_set_log_level(
return 0;
}
static int property_get_progress(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
Manager *m = ASSERT_PTR(userdata);
double d;
assert(bus);
assert(reply);
if (MANAGER_IS_FINISHED(m))
d = 1.0;
else
d = 1.0 - ((double) hashmap_size(m->jobs) / (double) m->n_installed_jobs);
return sd_bus_message_append(reply, "d", d);
}
static int property_get_environment(
sd_bus *bus,
const char *path,

View file

@ -22,21 +22,13 @@ static int property_get_what(
_cleanup_free_ char *escaped = NULL;
Mount *m = ASSERT_PTR(userdata);
const char *s = NULL;
assert(bus);
assert(reply);
if (m->from_proc_self_mountinfo && m->parameters_proc_self_mountinfo.what)
s = m->parameters_proc_self_mountinfo.what;
else if (m->from_fragment && m->parameters_fragment.what)
s = m->parameters_fragment.what;
if (s) {
escaped = utf8_escape_invalid(s);
if (!escaped)
return -ENOMEM;
}
escaped = mount_get_what_escaped(m);
if (!escaped)
return -ENOMEM;
return sd_bus_message_append_basic(reply, 's', escaped);
}
@ -52,33 +44,17 @@ static int property_get_options(
_cleanup_free_ char *escaped = NULL;
Mount *m = ASSERT_PTR(userdata);
const char *s = NULL;
assert(bus);
assert(reply);
if (m->from_proc_self_mountinfo && m->parameters_proc_self_mountinfo.options)
s = m->parameters_proc_self_mountinfo.options;
else if (m->from_fragment && m->parameters_fragment.options)
s = m->parameters_fragment.options;
if (s) {
escaped = utf8_escape_invalid(s);
if (!escaped)
return -ENOMEM;
}
escaped = mount_get_options_escaped(m);
if (!escaped)
return -ENOMEM;
return sd_bus_message_append_basic(reply, 's', escaped);
}
static const char *mount_get_fstype(const Mount *m) {
if (m->from_proc_self_mountinfo && m->parameters_proc_self_mountinfo.fstype)
return m->parameters_proc_self_mountinfo.fstype;
else if (m->from_fragment && m->parameters_fragment.fstype)
return m->parameters_fragment.fstype;
return NULL;
}
static BUS_DEFINE_PROPERTY_GET(property_get_type, "s", Mount, mount_get_fstype);
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, mount_result, MountResult);

View file

@ -44,30 +44,12 @@ static int property_get_listen(
LIST_FOREACH(port, p, s->ports) {
_cleanup_free_ char *address = NULL;
const char *a;
switch (p->type) {
case SOCKET_SOCKET: {
r = socket_address_print(&p->address, &address);
if (r)
return r;
r = socket_port_to_address(p, &address);
if (r < 0)
return r;
a = address;
break;
}
case SOCKET_SPECIAL:
case SOCKET_MQUEUE:
case SOCKET_FIFO:
case SOCKET_USB_FUNCTION:
a = p->path;
break;
default:
assert_not_reached();
}
r = sd_bus_message_append(reply, "(ss)", socket_port_type_to_string(p), a);
r = sd_bus_message_append(reply, "(ss)", socket_port_type_to_string(p), address);
if (r < 0)
return r;
}

View file

@ -11,27 +11,6 @@
#include "swap.h"
#include "unit.h"
static int swap_get_priority(Swap *s) {
assert(s);
if (s->from_proc_swaps && s->parameters_proc_swaps.priority_set)
return s->parameters_proc_swaps.priority;
if (s->from_fragment && s->parameters_fragment.priority_set)
return s->parameters_fragment.priority;
return -1;
}
static const char *swap_get_options(Swap *s) {
assert(s);
if (s->from_fragment)
return s->parameters_fragment.options;
return NULL;
}
static BUS_DEFINE_PROPERTY_GET(property_get_priority, "i", Swap, swap_get_priority);
static BUS_DEFINE_PROPERTY_GET(property_get_options, "s", Swap, swap_get_options);
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, swap_result, SwapResult);

View file

@ -30,26 +30,16 @@ static int property_get_monotonic_timers(
return r;
LIST_FOREACH(value, v, t->values) {
_cleanup_free_ char *buf = NULL;
const char *s;
size_t l;
_cleanup_free_ char *usec = NULL;
if (v->base == TIMER_CALENDAR)
continue;
s = timer_base_to_string(v->base);
assert(endswith(s, "Sec"));
/* s/Sec/USec/ */
l = strlen(s);
buf = new(char, l+2);
if (!buf)
usec = timer_base_to_usec_string(v->base);
if (!usec)
return -ENOMEM;
memcpy(buf, s, l-3);
memcpy(buf+l-3, "USec", 5);
r = sd_bus_message_append(reply, "(stt)", buf, v->value, v->next_elapse);
r = sd_bus_message_append(reply, "(stt)", usec, v->value, v->next_elapse);
if (r < 0)
return r;
}
@ -108,9 +98,7 @@ static int property_get_next_elapse_monotonic(
assert(bus);
assert(reply);
return sd_bus_message_append(reply, "t",
(uint64_t) usec_shift_clock(t->next_elapse_monotonic_or_boottime,
TIMER_MONOTONIC_CLOCK(t), CLOCK_MONOTONIC));
return sd_bus_message_append(reply, "t", timer_next_elapse_monotonic(t));
}
const sd_bus_vtable bus_timer_vtable[] = {

View file

@ -30,18 +30,6 @@
#include "user-util.h"
#include "web-util.h"
static bool unit_can_start_refuse_manual(Unit *u) {
return unit_can_start(u) && !u->refuse_manual_start;
}
static bool unit_can_stop_refuse_manual(Unit *u) {
return unit_can_stop(u) && !u->refuse_manual_stop;
}
static bool unit_can_isolate_refuse_manual(Unit *u) {
return unit_can_isolate(u) && !u->refuse_manual_start;
}
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_collect_mode, collect_mode, CollectMode);
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_load_state, unit_load_state, UnitLoadState);
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_job_mode, job_mode, JobMode);
@ -1397,22 +1385,15 @@ static int property_get_ip_counter(
void *userdata,
sd_bus_error *error) {
static const char *const table[_CGROUP_IP_ACCOUNTING_METRIC_MAX] = {
[CGROUP_IP_INGRESS_BYTES] = "IPIngressBytes",
[CGROUP_IP_EGRESS_BYTES] = "IPEgressBytes",
[CGROUP_IP_INGRESS_PACKETS] = "IPIngressPackets",
[CGROUP_IP_EGRESS_PACKETS] = "IPEgressPackets",
};
uint64_t value = UINT64_MAX;
Unit *u = ASSERT_PTR(userdata);
ssize_t metric;
CGroupIPAccountingMetric metric;
assert(bus);
assert(reply);
assert(property);
assert_se((metric = string_table_lookup(table, ELEMENTSOF(table), property)) >= 0);
assert_se((metric = cgroup_ip_accounting_metric_from_string(property)) >= 0);
(void) unit_get_ip_accounting(u, metric, &value);
return sd_bus_message_append(reply, "t", value);
}
@ -1426,13 +1407,6 @@ static int property_get_io_counter(
void *userdata,
sd_bus_error *error) {
static const char *const table[_CGROUP_IO_ACCOUNTING_METRIC_MAX] = {
[CGROUP_IO_READ_BYTES] = "IOReadBytes",
[CGROUP_IO_WRITE_BYTES] = "IOWriteBytes",
[CGROUP_IO_READ_OPERATIONS] = "IOReadOperations",
[CGROUP_IO_WRITE_OPERATIONS] = "IOWriteOperations",
};
uint64_t value = UINT64_MAX;
Unit *u = ASSERT_PTR(userdata);
ssize_t metric;
@ -1441,8 +1415,8 @@ static int property_get_io_counter(
assert(reply);
assert(property);
assert_se((metric = string_table_lookup(table, ELEMENTSOF(table), property)) >= 0);
(void) unit_get_io_accounting(u, metric, false, &value);
assert_se((metric = cgroup_io_accounting_metric_from_string(property)) >= 0);
(void) unit_get_io_accounting(u, metric, /* allow_cache= */ false, &value);
return sd_bus_message_append(reply, "t", value);
}

View file

@ -1542,6 +1542,237 @@ int exec_context_get_clean_mask(ExecContext *c, ExecCleanMask *ret) {
return 0;
}
int exec_context_get_oom_score_adjust(const ExecContext *c) {
int n = 0, r;
assert(c);
if (c->oom_score_adjust_set)
return c->oom_score_adjust;
r = get_oom_score_adjust(&n);
if (r < 0)
log_debug_errno(r, "Failed to read /proc/self/oom_score_adj, ignoring: %m");
return n;
}
uint64_t exec_context_get_coredump_filter(const ExecContext *c) {
_cleanup_free_ char *t = NULL;
uint64_t n = COREDUMP_FILTER_MASK_DEFAULT;
int r;
assert(c);
if (c->coredump_filter_set)
return c->coredump_filter;
r = read_one_line_file("/proc/self/coredump_filter", &t);
if (r < 0)
log_debug_errno(r, "Failed to read /proc/self/coredump_filter, ignoring: %m");
else {
r = safe_atoux64(t, &n);
if (r < 0)
log_debug_errno(r, "Failed to parse \"%s\" from /proc/self/coredump_filter, ignoring: %m", t);
}
return n;
}
int exec_context_get_nice(const ExecContext *c) {
int n;
assert(c);
if (c->nice_set)
return c->nice;
errno = 0;
n = getpriority(PRIO_PROCESS, 0);
if (errno > 0) {
log_debug_errno(errno, "Failed to get process nice value, ignoring: %m");
n = 0;
}
return n;
}
int exec_context_get_cpu_sched_policy(const ExecContext *c) {
int n;
assert(c);
if (c->cpu_sched_set)
return c->cpu_sched_policy;
n = sched_getscheduler(0);
if (n < 0)
log_debug_errno(errno, "Failed to get scheduler policy, ignoring: %m");
return n < 0 ? SCHED_OTHER : n;
}
int exec_context_get_cpu_sched_priority(const ExecContext *c) {
struct sched_param p = {};
int r;
assert(c);
if (c->cpu_sched_set)
return c->cpu_sched_priority;
r = sched_getparam(0, &p);
if (r < 0)
log_debug_errno(errno, "Failed to get scheduler priority, ignoring: %m");
return r >= 0 ? p.sched_priority : 0;
}
uint64_t exec_context_get_timer_slack_nsec(const ExecContext *c) {
int r;
assert(c);
if (c->timer_slack_nsec != NSEC_INFINITY)
return c->timer_slack_nsec;
r = prctl(PR_GET_TIMERSLACK);
if (r < 0)
log_debug_errno(r, "Failed to get timer slack, ignoring: %m");
return (uint64_t) MAX(r, 0);
}
char** exec_context_get_syscall_filter(const ExecContext *c) {
_cleanup_strv_free_ char **l = NULL;
assert(c);
#if HAVE_SECCOMP
void *id, *val;
HASHMAP_FOREACH_KEY(val, id, c->syscall_filter) {
_cleanup_free_ char *name = NULL;
const char *e = NULL;
char *s;
int num = PTR_TO_INT(val);
if (c->syscall_allow_list && num >= 0)
/* syscall with num >= 0 in allow-list is denied. */
continue;
name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
if (!name)
continue;
if (num >= 0) {
e = seccomp_errno_or_action_to_string(num);
if (e) {
s = strjoin(name, ":", e);
if (!s)
return NULL;
} else {
if (asprintf(&s, "%s:%d", name, num) < 0)
return NULL;
}
} else
s = TAKE_PTR(name);
if (strv_consume(&l, s) < 0)
return NULL;
}
strv_sort(l);
#endif
return l ? TAKE_PTR(l) : strv_new(NULL);
}
char** exec_context_get_syscall_archs(const ExecContext *c) {
_cleanup_strv_free_ char **l = NULL;
assert(c);
#if HAVE_SECCOMP
void *id;
SET_FOREACH(id, c->syscall_archs) {
const char *name;
name = seccomp_arch_to_string(PTR_TO_UINT32(id) - 1);
if (!name)
continue;
if (strv_extend(&l, name) < 0)
return NULL;
}
strv_sort(l);
#endif
return l ? TAKE_PTR(l) : strv_new(NULL);
}
char** exec_context_get_syscall_log(const ExecContext *c) {
_cleanup_strv_free_ char **l = NULL;
assert(c);
#if HAVE_SECCOMP
void *id, *val;
HASHMAP_FOREACH_KEY(val, id, c->syscall_log) {
char *name = NULL;
name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
if (!name)
continue;
if (strv_consume(&l, name) < 0)
return NULL;
}
strv_sort(l);
#endif
return l ? TAKE_PTR(l) : strv_new(NULL);
}
char** exec_context_get_address_families(const ExecContext *c) {
_cleanup_strv_free_ char **l = NULL;
void *af;
assert(c);
SET_FOREACH(af, c->address_families) {
const char *name;
name = af_to_name(PTR_TO_INT(af));
if (!name)
continue;
if (strv_extend(&l, name) < 0)
return NULL;
}
strv_sort(l);
return l ? TAKE_PTR(l) : strv_new(NULL);
}
char** exec_context_get_restrict_filesystems(const ExecContext *c) {
_cleanup_strv_free_ char **l = NULL;
assert(c);
#if HAVE_LIBBPF
l = set_get_strv(c->restrict_filesystems);
if (!l)
return NULL;
strv_sort(l);
#endif
return l ? TAKE_PTR(l) : strv_new(NULL);
}
void exec_status_start(ExecStatus *s, pid_t pid) {
assert(s);
@ -2460,6 +2691,16 @@ static const char* const exec_directory_type_symlink_table[_EXEC_DIRECTORY_TYPE_
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_symlink, ExecDirectoryType);
static const char* const exec_directory_type_mode_table[_EXEC_DIRECTORY_TYPE_MAX] = {
[EXEC_DIRECTORY_RUNTIME] = "RuntimeDirectoryMode",
[EXEC_DIRECTORY_STATE] = "StateDirectoryMode",
[EXEC_DIRECTORY_CACHE] = "CacheDirectoryMode",
[EXEC_DIRECTORY_LOGS] = "LogsDirectoryMode",
[EXEC_DIRECTORY_CONFIGURATION] = "ConfigurationDirectoryMode",
};
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_mode, ExecDirectoryType);
/* And this table maps ExecDirectoryType too, but to a generic term identifying the type of resource. This
* one is supposed to be generic enough to be used for unit types that don't use ExecContext and per-unit
* directories, specifically .timer units with their timestamp touch file. */

View file

@ -516,6 +516,19 @@ const char *exec_context_tty_path(const ExecContext *context);
int exec_context_tty_size(const ExecContext *context, unsigned *ret_rows, unsigned *ret_cols);
void exec_context_tty_reset(const ExecContext *context, const ExecParameters *p);
uint64_t exec_context_get_rlimit(const ExecContext *c, const char *name);
int exec_context_get_oom_score_adjust(const ExecContext *c);
uint64_t exec_context_get_coredump_filter(const ExecContext *c);
int exec_context_get_nice(const ExecContext *c);
int exec_context_get_cpu_sched_policy(const ExecContext *c);
int exec_context_get_cpu_sched_priority(const ExecContext *c);
uint64_t exec_context_get_timer_slack_nsec(const ExecContext *c);
char** exec_context_get_syscall_filter(const ExecContext *c);
char** exec_context_get_syscall_archs(const ExecContext *c);
char** exec_context_get_syscall_log(const ExecContext *c);
char** exec_context_get_address_families(const ExecContext *c);
char** exec_context_get_restrict_filesystems(const ExecContext *c);
void exec_status_start(ExecStatus *s, pid_t pid);
void exec_status_exit(ExecStatus *s, const ExecContext *context, pid_t pid, int code, int status);
void exec_status_dump(const ExecStatus *s, FILE *f, const char *prefix);
@ -573,6 +586,9 @@ ExecDirectoryType exec_directory_type_from_string(const char *s) _pure_;
const char* exec_directory_type_symlink_to_string(ExecDirectoryType i) _const_;
ExecDirectoryType exec_directory_type_symlink_from_string(const char *s) _pure_;
const char* exec_directory_type_mode_to_string(ExecDirectoryType i) _const_;
ExecDirectoryType exec_directory_type_mode_from_string(const char *s) _pure_;
const char* exec_resource_type_to_string(ExecDirectoryType i) _const_;
ExecDirectoryType exec_resource_type_from_string(const char *s) _pure_;

View file

@ -866,6 +866,15 @@ void manager_set_switching_root(Manager *m, bool switching_root) {
m->switching_root = MANAGER_IS_SYSTEM(m) && switching_root;
}
double manager_get_progress(Manager *m) {
assert(m);
if (MANAGER_IS_FINISHED(m) || m->n_installed_jobs == 0)
return 1.0;
return 1.0 - ((double) hashmap_size(m->jobs) / (double) m->n_installed_jobs);
}
static int compare_job_priority(const void *a, const void *b) {
const Job *x = a, *y = b;

View file

@ -592,6 +592,8 @@ void manager_override_show_status(Manager *m, ShowStatus mode, const char *reaso
void manager_set_first_boot(Manager *m, bool b);
void manager_set_switching_root(Manager *m, bool switching_root);
double manager_get_progress(Manager *m);
void manager_status_printf(Manager *m, StatusType type, const char *status, const char *format, ...) _printf_(4,5);
Set *manager_get_units_requiring_mounts_for(Manager *m, const char *path);

View file

@ -34,6 +34,7 @@
#include "strv.h"
#include "unit-name.h"
#include "unit.h"
#include "utf8.h"
#define RETRY_UMOUNT_MAX 32
@ -2345,6 +2346,58 @@ static int mount_subsystem_ratelimited(Manager *m) {
return sd_event_source_is_ratelimited(m->mount_event_source);
}
char* mount_get_what_escaped(const Mount *m) {
_cleanup_free_ char *escaped = NULL;
const char *s = NULL;
assert(m);
if (m->from_proc_self_mountinfo && m->parameters_proc_self_mountinfo.what)
s = m->parameters_proc_self_mountinfo.what;
else if (m->from_fragment && m->parameters_fragment.what)
s = m->parameters_fragment.what;
if (s) {
escaped = utf8_escape_invalid(s);
if (!escaped)
return NULL;
}
return escaped ? TAKE_PTR(escaped) : strdup("");
}
char* mount_get_options_escaped(const Mount *m) {
_cleanup_free_ char *escaped = NULL;
const char *s = NULL;
assert(m);
if (m->from_proc_self_mountinfo && m->parameters_proc_self_mountinfo.options)
s = m->parameters_proc_self_mountinfo.options;
else if (m->from_fragment && m->parameters_fragment.options)
s = m->parameters_fragment.options;
if (s) {
escaped = utf8_escape_invalid(s);
if (!escaped)
return NULL;
}
return escaped ? TAKE_PTR(escaped) : strdup("");
}
const char* mount_get_fstype(const Mount *m) {
assert(m);
if (m->from_proc_self_mountinfo && m->parameters_proc_self_mountinfo.fstype)
return m->parameters_proc_self_mountinfo.fstype;
if (m->from_fragment && m->parameters_fragment.fstype)
return m->parameters_fragment.fstype;
return NULL;
}
static const char* const mount_exec_command_table[_MOUNT_EXEC_COMMAND_MAX] = {
[MOUNT_EXEC_MOUNT] = "ExecMount",
[MOUNT_EXEC_UNMOUNT] = "ExecUnmount",

View file

@ -97,6 +97,10 @@ void mount_fd_event(Manager *m, int events);
int mount_invalidate_state_by_path(Manager *manager, const char *path);
char* mount_get_what_escaped(const Mount *m);
char* mount_get_options_escaped(const Mount *m);
const char* mount_get_fstype(const Mount *m);
const char* mount_exec_command_to_string(MountExecCommand i) _const_;
MountExecCommand mount_exec_command_from_string(const char *s) _pure_;

View file

@ -2866,6 +2866,40 @@ static const char *socket_sub_state_to_string(Unit *u) {
return socket_state_to_string(SOCKET(u)->state);
}
int socket_port_to_address(const SocketPort *p, char **ret) {
_cleanup_free_ char *address = NULL;
int r;
assert(p);
assert(ret);
switch (p->type) {
case SOCKET_SOCKET: {
r = socket_address_print(&p->address, &address);
if (r < 0)
return r;
break;
}
case SOCKET_SPECIAL:
case SOCKET_MQUEUE:
case SOCKET_FIFO:
case SOCKET_USB_FUNCTION:
address = strdup(p->path);
if (!address)
return -ENOMEM;
break;
default:
assert_not_reached();
}
*ret = TAKE_PTR(address);
return 0;
}
const char* socket_port_type_to_string(SocketPort *p) {
assert(p);

View file

@ -180,6 +180,8 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(SocketPort*, socket_port_free);
void socket_free_ports(Socket *s);
int socket_port_to_address(const SocketPort *s, char **ret);
int socket_load_service_unit(Socket *s, int cfd, Unit **ret);
char *socket_fdname(Socket *s);

View file

@ -1559,6 +1559,27 @@ static int swap_can_start(Unit *u) {
return 1;
}
int swap_get_priority(const Swap *s) {
assert(s);
if (s->from_proc_swaps && s->parameters_proc_swaps.priority_set)
return s->parameters_proc_swaps.priority;
if (s->from_fragment && s->parameters_fragment.priority_set)
return s->parameters_fragment.priority;
return -1;
}
const char* swap_get_options(const Swap *s) {
assert(s);
if (s->from_fragment)
return s->parameters_fragment.options;
return NULL;
}
static const char* const swap_exec_command_table[_SWAP_EXEC_COMMAND_MAX] = {
[SWAP_EXEC_ACTIVATE] = "ExecActivate",
[SWAP_EXEC_DEACTIVATE] = "ExecDeactivate",

View file

@ -91,6 +91,9 @@ extern const UnitVTable swap_vtable;
int swap_process_device_new(Manager *m, sd_device *dev);
int swap_process_device_remove(Manager *m, sd_device *dev);
int swap_get_priority(const Swap *s);
const char* swap_get_options(const Swap *s);
const char* swap_exec_command_to_string(SwapExecCommand i) _const_;
SwapExecCommand swap_exec_command_from_string(const char *s) _pure_;

View file

@ -1001,6 +1001,13 @@ static int activation_details_timer_append_pair(ActivationDetails *details, char
return 2; /* Return the number of pairs added to the env block */
}
uint64_t timer_next_elapse_monotonic(const Timer *t) {
assert(t);
return (uint64_t) usec_shift_clock(t->next_elapse_monotonic_or_boottime,
TIMER_MONOTONIC_CLOCK(t), CLOCK_MONOTONIC);
}
static const char* const timer_base_table[_TIMER_BASE_MAX] = {
[TIMER_ACTIVE] = "OnActiveSec",
[TIMER_BOOT] = "OnBootSec",
@ -1012,6 +1019,31 @@ static const char* const timer_base_table[_TIMER_BASE_MAX] = {
DEFINE_STRING_TABLE_LOOKUP(timer_base, TimerBase);
char* timer_base_to_usec_string(TimerBase i) {
_cleanup_free_ char *buf = NULL;
const char *s;
size_t l;
s = timer_base_to_string(i);
if (endswith(s, "Sec")) {
/* s/Sec/USec/ */
l = strlen(s);
buf = new(char, l+2);
if (!buf)
return NULL;
memcpy(buf, s, l-3);
memcpy(buf+l-3, "USec", 5);
} else {
buf = strdup(s);
if (!buf)
return NULL;
}
return TAKE_PTR(buf);
}
static const char* const timer_result_table[_TIMER_RESULT_MAX] = {
[TIMER_SUCCESS] = "success",
[TIMER_FAILURE_RESOURCES] = "resources",

View file

@ -72,6 +72,8 @@ struct ActivationDetailsTimer {
#define TIMER_MONOTONIC_CLOCK(t) ((t)->wake_system ? CLOCK_BOOTTIME_ALARM : CLOCK_MONOTONIC)
uint64_t timer_next_elapse_monotonic(const Timer *t);
void timer_free_values(Timer *t);
extern const UnitVTable timer_vtable;
@ -80,6 +82,8 @@ extern const ActivationDetailsVTable activation_details_timer_vtable;
const char *timer_base_to_string(TimerBase i) _const_;
TimerBase timer_base_from_string(const char *s) _pure_;
char* timer_base_to_usec_string(TimerBase i);
const char* timer_result_to_string(TimerResult i) _const_;
TimerResult timer_result_from_string(const char *s) _pure_;

View file

@ -6167,6 +6167,18 @@ int unit_can_clean(Unit *u, ExecCleanMask *ret) {
return UNIT_VTABLE(u)->can_clean(u, ret);
}
bool unit_can_start_refuse_manual(Unit *u) {
return unit_can_start(u) && !u->refuse_manual_start;
}
bool unit_can_stop_refuse_manual(Unit *u) {
return unit_can_stop(u) && !u->refuse_manual_stop;
}
bool unit_can_isolate_refuse_manual(Unit *u) {
return unit_can_isolate(u) && !u->refuse_manual_start;
}
bool unit_can_freeze(Unit *u) {
assert(u);

View file

@ -1078,6 +1078,10 @@ void unit_destroy_runtime_data(Unit *u, const ExecContext *context);
int unit_clean(Unit *u, ExecCleanMask mask);
int unit_can_clean(Unit *u, ExecCleanMask *ret_mask);
bool unit_can_start_refuse_manual(Unit *u);
bool unit_can_stop_refuse_manual(Unit *u);
bool unit_can_isolate_refuse_manual(Unit *u);
bool unit_can_freeze(Unit *u);
int unit_freeze(Unit *u);
void unit_frozen(Unit *u);

View file

@ -21,6 +21,7 @@
#include "math-util.h"
#include "memory-util.h"
#include "memstream-util.h"
#include "set.h"
#include "string-table.h"
#include "string-util.h"
#include "strv.h"
@ -3350,6 +3351,7 @@ int json_parse_file_at(
int json_buildv(JsonVariant **ret, va_list ap) {
JsonStack *stack = NULL;
size_t n_stack = 1;
const char *name = NULL;
int r;
assert_return(ret, -EINVAL);
@ -3877,6 +3879,77 @@ int json_buildv(JsonVariant **ret, va_list ap) {
break;
}
case _JSON_BUILD_STRING_SET: {
Set *set;
if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) {
r = -EINVAL;
goto finish;
}
set = va_arg(ap, Set*);
if (current->n_suppress == 0) {
_cleanup_free_ char **sorted = NULL;
r = set_dump_sorted(set, (void ***) &sorted, NULL);
if (r < 0)
goto finish;
r = json_variant_new_array_strv(&add, sorted);
if (r < 0)
goto finish;
}
n_subtract = 1;
if (current->expect == EXPECT_TOPLEVEL)
current->expect = EXPECT_END;
else if (current->expect == EXPECT_OBJECT_VALUE)
current->expect = EXPECT_OBJECT_KEY;
else
assert(current->expect == EXPECT_ARRAY_ELEMENT);
break;
}
case _JSON_BUILD_CALLBACK: {
JsonBuildCallback cb;
void *userdata;
if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) {
r = -EINVAL;
goto finish;
}
cb = va_arg(ap, JsonBuildCallback);
userdata = va_arg(ap, void *);
if (current->n_suppress == 0) {
if (cb) {
r = cb(&add, name, userdata);
if (r < 0)
goto finish;
}
if (!add)
add = JSON_VARIANT_MAGIC_NULL;
name = NULL;
}
n_subtract = 1;
if (current->expect == EXPECT_TOPLEVEL)
current->expect = EXPECT_END;
else if (current->expect == EXPECT_OBJECT_VALUE)
current->expect = EXPECT_OBJECT_KEY;
else
assert(current->expect == EXPECT_ARRAY_ELEMENT);
break;
}
case _JSON_BUILD_OBJECT_BEGIN:
if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) {
@ -3930,17 +4003,16 @@ int json_buildv(JsonVariant **ret, va_list ap) {
break;
case _JSON_BUILD_PAIR: {
const char *n;
if (current->expect != EXPECT_OBJECT_KEY) {
r = -EINVAL;
goto finish;
}
n = va_arg(ap, const char *);
name = va_arg(ap, const char *);
if (current->n_suppress == 0) {
r = json_variant_new_string(&add, n);
r = json_variant_new_string(&add, name);
if (r < 0)
goto finish;
}
@ -3952,7 +4024,6 @@ int json_buildv(JsonVariant **ret, va_list ap) {
}
case _JSON_BUILD_PAIR_CONDITION: {
const char *n;
bool b;
if (current->expect != EXPECT_OBJECT_KEY) {
@ -3961,10 +4032,10 @@ int json_buildv(JsonVariant **ret, va_list ap) {
}
b = va_arg(ap, int);
n = va_arg(ap, const char *);
name = va_arg(ap, const char *);
if (b && current->n_suppress == 0) {
r = json_variant_new_string(&add, n);
r = json_variant_new_string(&add, name);
if (r < 0)
goto finish;
}

View file

@ -275,6 +275,8 @@ enum {
_JSON_BUILD_UUID,
_JSON_BUILD_BYTE_ARRAY,
_JSON_BUILD_HW_ADDR,
_JSON_BUILD_STRING_SET,
_JSON_BUILD_CALLBACK,
_JSON_BUILD_PAIR_UNSIGNED_NON_ZERO,
_JSON_BUILD_PAIR_FINITE_USEC,
_JSON_BUILD_PAIR_STRING_NON_EMPTY,
@ -289,6 +291,8 @@ enum {
_JSON_BUILD_MAX,
};
typedef int (*JsonBuildCallback)(JsonVariant **ret, const char *name, void *userdata);
#define JSON_BUILD_STRING(s) _JSON_BUILD_STRING, (const char*) { s }
#define JSON_BUILD_INTEGER(i) _JSON_BUILD_INTEGER, (int64_t) { i }
#define JSON_BUILD_UNSIGNED(u) _JSON_BUILD_UNSIGNED, (uint64_t) { u }
@ -319,6 +323,8 @@ enum {
#define JSON_BUILD_IN_ADDR(v, f) JSON_BUILD_BYTE_ARRAY(((const union in_addr_union*) { v })->bytes, FAMILY_ADDRESS_SIZE_SAFE(f))
#define JSON_BUILD_ETHER_ADDR(v) JSON_BUILD_BYTE_ARRAY(((const struct ether_addr*) { v })->ether_addr_octet, sizeof(struct ether_addr))
#define JSON_BUILD_HW_ADDR(v) _JSON_BUILD_HW_ADDR, (const struct hw_addr_data*) { v }
#define JSON_BUILD_STRING_SET(s) _JSON_BUILD_STRING_SET, (Set *) { s }
#define JSON_BUILD_CALLBACK(c, u) _JSON_BUILD_CALLBACK, (JsonBuildCallback) { c }, (void*) { u }
#define JSON_BUILD_PAIR_STRING(name, s) JSON_BUILD_PAIR(name, JSON_BUILD_STRING(s))
#define JSON_BUILD_PAIR_INTEGER(name, i) JSON_BUILD_PAIR(name, JSON_BUILD_INTEGER(i))
@ -344,6 +350,8 @@ enum {
#define JSON_BUILD_PAIR_IN_ADDR(name, v, f) JSON_BUILD_PAIR(name, JSON_BUILD_IN_ADDR(v, f))
#define JSON_BUILD_PAIR_ETHER_ADDR(name, v) JSON_BUILD_PAIR(name, JSON_BUILD_ETHER_ADDR(v))
#define JSON_BUILD_PAIR_HW_ADDR(name, v) JSON_BUILD_PAIR(name, JSON_BUILD_HW_ADDR(v))
#define JSON_BUILD_PAIR_STRING_SET(name, s) JSON_BUILD_PAIR(name, JSON_BUILD_STRING_SET(s))
#define JSON_BUILD_PAIR_CALLBACK(name, c, u) JSON_BUILD_PAIR(name, JSON_BUILD_CALLBACK(c, u))
#define JSON_BUILD_PAIR_UNSIGNED_NON_ZERO(name, u) _JSON_BUILD_PAIR_UNSIGNED_NON_ZERO, (const char*) { name }, (uint64_t) { u }
#define JSON_BUILD_PAIR_FINITE_USEC(name, u) _JSON_BUILD_PAIR_FINITE_USEC, (const char*) { name }, (usec_t) { u }