mirror of
https://github.com/systemd/systemd
synced 2026-04-11 17:44:58 +02:00
Compare commits
18 Commits
ff6b70fe7e
...
056bc106e1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
056bc106e1 | ||
|
|
1016dd315f | ||
|
|
5cd61c1330 | ||
|
|
6b1a59d594 | ||
|
|
a1813a40ec | ||
|
|
79f90f4ae0 | ||
|
|
2a23e43f70 | ||
|
|
7024c0c38c | ||
|
|
90c6157100 | ||
|
|
5ceaab3d4f | ||
|
|
b8514cff52 | ||
|
|
954243f2bc | ||
|
|
3b478198e9 | ||
|
|
81d2d0270b | ||
|
|
bbc6af9ce3 | ||
|
|
3ed9e7cd23 | ||
|
|
74573799ca | ||
|
|
e2227c8a20 |
@ -18,3 +18,12 @@ Subscription to the Security Advisories and/or systemd-security mailing list is
|
||||
Those conditions should be backed by publicly accessible information (ideally, a track of posts and commits from the mail address in question).
|
||||
If you fall into one of those categories and wish to be subscribed,
|
||||
contact the maintainers or submit a **[subscription request](https://www.redhat.com/mailman/listinfo/systemd-security)**.
|
||||
|
||||
# Requirements for a Valid Report
|
||||
|
||||
- Please ensure the issue is reproducible on main.
|
||||
- Please ensure a fully working, end-to-end reproducer is provided.
|
||||
- Please ensure the reproducer is real-world and not simulated or abstracted.
|
||||
- Please ensure the reproducer demonstrably violates a security boundary.
|
||||
- Please understand that most of our maintainers are volunteers and already have a heavy review burden. While we will try to triage and fix issues in a timely manner, we cannot guarantee any fixed timeline for issue resolution.
|
||||
- While modern industry practices around coordinated disclosures encourage public disclosure to avoid vendors stonewalling researchers, we are an open source project that would gain little from needlessly stonewalling researchers. We thus kindly request that reporters do not publicly disclose issues they have reported to us before an agreed-to disclosure date.
|
||||
|
||||
@ -33,12 +33,7 @@ ConfFile* conf_file_free(ConfFile *c) {
|
||||
return mfree(c);
|
||||
}
|
||||
|
||||
void conf_file_free_many(ConfFile **array, size_t n) {
|
||||
FOREACH_ARRAY(i, array, n)
|
||||
conf_file_free(*i);
|
||||
|
||||
free(array);
|
||||
}
|
||||
DEFINE_POINTER_ARRAY_FREE_FUNC(ConfFile*, conf_file_free);
|
||||
|
||||
static int conf_files_log_level(ConfFilesFlags flags) {
|
||||
return FLAGS_SET(flags, CONF_FILES_WARN) ? LOG_WARNING : LOG_DEBUG;
|
||||
@ -485,7 +480,7 @@ static int dump_files(Hashmap *fh, const char *root, ConfFilesFlags flags, ConfF
|
||||
size_t n_files = 0;
|
||||
int r;
|
||||
|
||||
CLEANUP_ARRAY(files, n_files, conf_file_free_many);
|
||||
CLEANUP_ARRAY(files, n_files, conf_file_free_array);
|
||||
|
||||
assert(ret_files);
|
||||
assert(ret_n_files);
|
||||
@ -528,7 +523,7 @@ static int copy_and_sort_files_from_hashmap(
|
||||
int log_level = conf_files_log_level(flags);
|
||||
|
||||
/* The entries in the array given by hashmap_dump_sorted() are still owned by the hashmap.
|
||||
* Hence, do not use conf_file_free_many() for 'entries' */
|
||||
* Hence, do not use conf_file_free_array() for 'entries' */
|
||||
r = hashmap_dump_sorted(fh, (void***) &files, &n_files);
|
||||
if (r < 0)
|
||||
return log_oom_full(log_level);
|
||||
|
||||
@ -29,7 +29,7 @@ typedef struct ConfFile {
|
||||
|
||||
ConfFile* conf_file_free(ConfFile *c);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(ConfFile*, conf_file_free);
|
||||
void conf_file_free_many(ConfFile **array, size_t n);
|
||||
void conf_file_free_array(ConfFile **array, size_t n);
|
||||
|
||||
int conf_file_new_at(const char *path, const char *root, int rfd, ConfFilesFlags flags, ConfFile **ret);
|
||||
int conf_file_new(const char *path, const char *root, ConfFilesFlags flags, ConfFile **ret);
|
||||
|
||||
@ -412,14 +412,7 @@ static void named_addon_done(NamedAddon *a) {
|
||||
iovec_done(&a->blob);
|
||||
}
|
||||
|
||||
static void named_addon_free_many(NamedAddon *a, size_t n) {
|
||||
assert(a || n == 0);
|
||||
|
||||
FOREACH_ARRAY(i, a, n)
|
||||
named_addon_done(i);
|
||||
|
||||
free(a);
|
||||
}
|
||||
static DEFINE_ARRAY_FREE_FUNC(named_addon_free_array, NamedAddon, named_addon_done);
|
||||
|
||||
static void install_addon_devicetrees(
|
||||
struct devicetree_state *dt_state,
|
||||
@ -1294,9 +1287,9 @@ static EFI_STATUS run(EFI_HANDLE image) {
|
||||
|
||||
/* Now that we have the UKI sections loaded, also load global first and then local (per-UKI)
|
||||
* addons. The data is loaded at once, and then used later. */
|
||||
CLEANUP_ARRAY(dt_addons, n_dt_addons, named_addon_free_many);
|
||||
CLEANUP_ARRAY(initrd_addons, n_initrd_addons, named_addon_free_many);
|
||||
CLEANUP_ARRAY(ucode_addons, n_ucode_addons, named_addon_free_many);
|
||||
CLEANUP_ARRAY(dt_addons, n_dt_addons, named_addon_free_array);
|
||||
CLEANUP_ARRAY(initrd_addons, n_initrd_addons, named_addon_free_array);
|
||||
CLEANUP_ARRAY(ucode_addons, n_ucode_addons, named_addon_free_array);
|
||||
load_all_addons(image, loaded_image, uname, &cmdline_addons, &dt_addons, &n_dt_addons, &initrd_addons, &n_initrd_addons, &ucode_addons, &n_ucode_addons);
|
||||
|
||||
/* If we have any extra command line to add via PE addons, load them now and append, and measure the
|
||||
|
||||
@ -3988,6 +3988,29 @@ bool unit_cgroup_delegate(Unit *u) {
|
||||
return c->delegate;
|
||||
}
|
||||
|
||||
void unit_cgroup_disable_all_controllers(Unit *u) {
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
|
||||
CGroupRuntime *crt = unit_get_cgroup_runtime(u);
|
||||
if (!crt || !crt->cgroup_path)
|
||||
return;
|
||||
|
||||
if (!unit_cgroup_delegate(u))
|
||||
return;
|
||||
|
||||
/* For delegated units, the previous payload may have enabled controllers (e.g. "pids") in
|
||||
* cgroup.subtree_control. These persist after the service stops and turn the cgroup into an
|
||||
* "internal node", causing clone3(CLONE_INTO_CGROUP) to fail with EBUSY. Clear them now, right
|
||||
* before the new start, so that resource control is preserved for lingering processes as long as
|
||||
* possible. Ignore errors — if sub-cgroups still have live processes the write will fail, but so
|
||||
* will the upcoming spawn. */
|
||||
r = cg_enable(u->manager->cgroup_supported, /* mask= */ 0, crt->cgroup_path, &crt->cgroup_enabled_mask);
|
||||
if (r < 0)
|
||||
log_unit_debug_errno(u, r, "Failed to disable controllers on cgroup %s, ignoring: %m", empty_to_root(crt->cgroup_path));
|
||||
}
|
||||
|
||||
void manager_invalidate_startup_units(Manager *m) {
|
||||
Unit *u;
|
||||
|
||||
|
||||
@ -471,6 +471,8 @@ void unit_cgroup_catchup(Unit *u);
|
||||
|
||||
bool unit_cgroup_delegate(Unit *u);
|
||||
|
||||
void unit_cgroup_disable_all_controllers(Unit *u);
|
||||
|
||||
int unit_get_cpuset(Unit *u, CPUSet *cpus, const char *name);
|
||||
|
||||
int unit_cgroup_freezer_action(Unit *u, FreezerAction action);
|
||||
|
||||
@ -4055,7 +4055,7 @@ int bus_exec_context_set_transient_property(
|
||||
MountImage *mount_images = NULL;
|
||||
size_t n_mount_images = 0;
|
||||
|
||||
CLEANUP_ARRAY(mount_images, n_mount_images, mount_image_free_many);
|
||||
CLEANUP_ARRAY(mount_images, n_mount_images, mount_image_free_array);
|
||||
|
||||
r = sd_bus_message_enter_container(message, 'a', "(ssba(ss))");
|
||||
if (r < 0)
|
||||
@ -4127,7 +4127,7 @@ int bus_exec_context_set_transient_property(
|
||||
|
||||
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
|
||||
if (n_mount_images == 0) {
|
||||
mount_image_free_many(c->mount_images, c->n_mount_images);
|
||||
mount_image_free_array(c->mount_images, c->n_mount_images);
|
||||
c->mount_images = NULL;
|
||||
c->n_mount_images = 0;
|
||||
|
||||
@ -4158,7 +4158,7 @@ int bus_exec_context_set_transient_property(
|
||||
MountImage *extension_images = NULL;
|
||||
size_t n_extension_images = 0;
|
||||
|
||||
CLEANUP_ARRAY(extension_images, n_extension_images, mount_image_free_many);
|
||||
CLEANUP_ARRAY(extension_images, n_extension_images, mount_image_free_array);
|
||||
|
||||
r = sd_bus_message_enter_container(message, 'a', "(sba(ss))");
|
||||
if (r < 0)
|
||||
@ -4220,7 +4220,7 @@ int bus_exec_context_set_transient_property(
|
||||
|
||||
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
|
||||
if (n_extension_images == 0) {
|
||||
mount_image_free_many(c->extension_images, c->n_extension_images);
|
||||
mount_image_free_array(c->extension_images, c->n_extension_images);
|
||||
c->extension_images = NULL;
|
||||
c->n_extension_images = 0;
|
||||
|
||||
|
||||
@ -1086,15 +1086,12 @@ static int enforce_user(
|
||||
|
||||
#if HAVE_PAM
|
||||
|
||||
static void pam_response_free_array(struct pam_response *responses, size_t n_responses) {
|
||||
assert(responses || n_responses == 0);
|
||||
|
||||
FOREACH_ARRAY(resp, responses, n_responses)
|
||||
erase_and_free(resp->resp);
|
||||
|
||||
free(responses);
|
||||
static void pam_response_done(struct pam_response *response) {
|
||||
erase_and_free(ASSERT_PTR(response)->resp);
|
||||
}
|
||||
|
||||
static DEFINE_ARRAY_FREE_FUNC(pam_response_free_array, struct pam_response, pam_response_done);
|
||||
|
||||
typedef struct AskPasswordConvData {
|
||||
const ExecContext *context;
|
||||
const ExecParameters *params;
|
||||
|
||||
@ -696,10 +696,10 @@ void exec_context_done(ExecContext *c) {
|
||||
bind_mount_free_many(c->bind_mounts, c->n_bind_mounts);
|
||||
c->bind_mounts = NULL;
|
||||
c->n_bind_mounts = 0;
|
||||
mount_image_free_many(c->mount_images, c->n_mount_images);
|
||||
mount_image_free_array(c->mount_images, c->n_mount_images);
|
||||
c->mount_images = NULL;
|
||||
c->n_mount_images = 0;
|
||||
mount_image_free_many(c->extension_images, c->n_extension_images);
|
||||
mount_image_free_array(c->extension_images, c->n_extension_images);
|
||||
c->extension_images = NULL;
|
||||
c->n_extension_images = 0;
|
||||
c->extension_directories = strv_free(c->extension_directories);
|
||||
|
||||
@ -5235,7 +5235,7 @@ int config_parse_mount_images(
|
||||
|
||||
if (isempty(rvalue)) {
|
||||
/* Empty assignment resets the list */
|
||||
mount_image_free_many(c->mount_images, c->n_mount_images);
|
||||
mount_image_free_array(c->mount_images, c->n_mount_images);
|
||||
c->mount_images = NULL;
|
||||
c->n_mount_images = 0;
|
||||
return 0;
|
||||
@ -5385,7 +5385,7 @@ int config_parse_extension_images(
|
||||
|
||||
if (isempty(rvalue)) {
|
||||
/* Empty assignment resets the list */
|
||||
mount_image_free_many(c->extension_images, c->n_extension_images);
|
||||
mount_image_free_array(c->extension_images, c->n_extension_images);
|
||||
c->extension_images = NULL;
|
||||
c->n_extension_images = 0;
|
||||
return 0;
|
||||
|
||||
@ -3250,17 +3250,14 @@ int bind_mount_add(BindMount **b, size_t *n, const BindMount *item) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mount_image_free_many(MountImage *m, size_t n) {
|
||||
assert(m || n == 0);
|
||||
|
||||
FOREACH_ARRAY(i, m, n) {
|
||||
free(i->source);
|
||||
free(i->destination);
|
||||
mount_options_free_all(i->mount_options);
|
||||
static void mount_image_done(MountImage *m) {
|
||||
assert(m);
|
||||
m->source = mfree(m->source);
|
||||
m->destination = mfree(m->destination);
|
||||
m->mount_options = mount_options_free_all(m->mount_options);
|
||||
}
|
||||
|
||||
free(m);
|
||||
}
|
||||
DEFINE_ARRAY_FREE_FUNC(mount_image_free_array, MountImage, mount_image_done);
|
||||
|
||||
int mount_image_add(MountImage **m, size_t *n, const MountImage *item) {
|
||||
_cleanup_free_ char *s = NULL, *d = NULL;
|
||||
|
||||
@ -303,7 +303,7 @@ DECLARE_STRING_TABLE_LOOKUP(private_pids, PrivatePIDs);
|
||||
void bind_mount_free_many(BindMount *b, size_t n);
|
||||
int bind_mount_add(BindMount **b, size_t *n, const BindMount *item);
|
||||
|
||||
void mount_image_free_many(MountImage *m, size_t n);
|
||||
void mount_image_free_array(MountImage *array, size_t n);
|
||||
int mount_image_add(MountImage **m, size_t *n, const MountImage *item);
|
||||
|
||||
void temporary_filesystem_free_many(TemporaryFileSystem *t, size_t n);
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
#include "bus-common-errors.h"
|
||||
#include "bus-error.h"
|
||||
#include "bus-util.h"
|
||||
#include "cgroup.h"
|
||||
#include "chase.h"
|
||||
#include "dbus-service.h"
|
||||
#include "dbus-unit.h"
|
||||
@ -3174,8 +3175,10 @@ static int service_start(Unit *u) {
|
||||
exec_status_reset(&s->main_exec_status);
|
||||
|
||||
CGroupRuntime *crt = unit_get_cgroup_runtime(u);
|
||||
if (crt)
|
||||
if (crt) {
|
||||
unit_cgroup_disable_all_controllers(u);
|
||||
crt->reset_accounting = true;
|
||||
}
|
||||
|
||||
service_enter_condition(s);
|
||||
return 1;
|
||||
@ -5640,6 +5643,7 @@ static int service_clean(Unit *u, ExecCleanMask mask) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
unit_cgroup_disable_all_controllers(u);
|
||||
r = unit_fork_and_watch_rm_rf(u, l, &s->control_pid);
|
||||
if (r < 0) {
|
||||
log_unit_warning_errno(u, r, "Failed to spawn cleaning task: %m");
|
||||
|
||||
@ -45,6 +45,35 @@
|
||||
#define DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_MACRO(type, macro, empty) \
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_MACRO_RENAME(type, macro, macro##p, empty)
|
||||
|
||||
/* Clean up a NULL-terminated array by dropping all the items in it (up to the first NULL).
|
||||
* The array itself is not deallocated. */
|
||||
#define DEFINE_ARRAY_DONE_FUNC(type, helper) \
|
||||
void helper ## _many(type (*p)[]) { \
|
||||
for (type *t = *ASSERT_PTR(p); *t; t++) \
|
||||
*t = helper(*t); \
|
||||
}
|
||||
|
||||
/* Clean up an array of pointers to objects by dropping all the items in it.
|
||||
* The size of the array is passed in as a parameter, so NULL items may appear in the middle of the array.
|
||||
* Free the array itself afterwards. */
|
||||
#define DEFINE_POINTER_ARRAY_FREE_FUNC(type, helper) \
|
||||
void helper ## _array(type *array, size_t n) { \
|
||||
assert(array || n == 0); \
|
||||
FOREACH_ARRAY(item, array, n) \
|
||||
helper(*item); \
|
||||
free(array); \
|
||||
}
|
||||
|
||||
/* Clean up an array of objects of known size by dropping all the items in it.
|
||||
* Then free the array itself. */
|
||||
#define DEFINE_ARRAY_FREE_FUNC(name, type, helper) \
|
||||
void name(type *array, size_t n) { \
|
||||
assert(array || n == 0); \
|
||||
FOREACH_ARRAY(item, array, n) \
|
||||
helper(item); \
|
||||
free(array); \
|
||||
}
|
||||
|
||||
typedef void (*free_array_func_t)(void *p, size_t n);
|
||||
|
||||
/* An automatic _cleanup_-like logic for destroy arrays (i.e. pointers + size) when leaving scope */
|
||||
|
||||
@ -34,4 +34,4 @@ int dns_resolvers_to_dot_strv(const sd_dns_resolver *resolvers, size_t n_resolve
|
||||
|
||||
void sd_dns_resolver_done(sd_dns_resolver *res);
|
||||
|
||||
void dns_resolver_done_many(sd_dns_resolver *resolvers, size_t n);
|
||||
void dns_resolver_free_array(sd_dns_resolver *array, size_t n);
|
||||
|
||||
@ -169,7 +169,7 @@ int deserialize_dnr(sd_dns_resolver **ret, const char *string) {
|
||||
|
||||
sd_dns_resolver *dnr = NULL;
|
||||
size_t n = 0;
|
||||
CLEANUP_ARRAY(dnr, n, dns_resolver_done_many);
|
||||
CLEANUP_ARRAY(dnr, n, dns_resolver_free_array);
|
||||
int priority = 0;
|
||||
|
||||
for (;;) {
|
||||
|
||||
@ -434,7 +434,7 @@ static sd_dhcp_lease *dhcp_lease_free(sd_dhcp_lease *lease) {
|
||||
for (sd_dhcp_lease_server_type_t i = 0; i < _SD_DHCP_LEASE_SERVER_TYPE_MAX; i++)
|
||||
free(lease->servers[i].addr);
|
||||
|
||||
dns_resolver_done_many(lease->dnr, lease->n_dnr);
|
||||
dns_resolver_free_array(lease->dnr, lease->n_dnr);
|
||||
free(lease->static_routes);
|
||||
free(lease->classless_routes);
|
||||
free(lease->vendor_specific);
|
||||
@ -642,7 +642,7 @@ static int lease_parse_dnr(const uint8_t *option, size_t len, sd_dns_resolver **
|
||||
int r;
|
||||
sd_dns_resolver *res_list = NULL;
|
||||
size_t n_resolvers = 0;
|
||||
CLEANUP_ARRAY(res_list, n_resolvers, dns_resolver_done_many);
|
||||
CLEANUP_ARRAY(res_list, n_resolvers, dns_resolver_free_array);
|
||||
|
||||
assert(option || len == 0);
|
||||
assert(dnr);
|
||||
@ -747,7 +747,7 @@ static int lease_parse_dnr(const uint8_t *option, size_t len, sd_dns_resolver **
|
||||
|
||||
typesafe_qsort(res_list, n_resolvers, dns_resolver_prio_compare);
|
||||
|
||||
dns_resolver_done_many(*dnr, *n_dnr);
|
||||
dns_resolver_free_array(*dnr, *n_dnr);
|
||||
*dnr = TAKE_PTR(res_list);
|
||||
*n_dnr = n_resolvers;
|
||||
|
||||
|
||||
@ -1076,7 +1076,7 @@ static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) {
|
||||
dhcp6_ia_free(lease->ia_na);
|
||||
dhcp6_ia_free(lease->ia_pd);
|
||||
free(lease->dns);
|
||||
dns_resolver_done_many(lease->dnr, lease->n_dnr);
|
||||
dns_resolver_free_array(lease->dnr, lease->n_dnr);
|
||||
free(lease->fqdn);
|
||||
free(lease->captive_portal);
|
||||
strv_free(lease->domains);
|
||||
|
||||
@ -26,14 +26,7 @@ sd_dns_resolver *sd_dns_resolver_unref(sd_dns_resolver *res) {
|
||||
return mfree(res);
|
||||
}
|
||||
|
||||
void dns_resolver_done_many(sd_dns_resolver resolvers[], size_t n) {
|
||||
assert(resolvers || n == 0);
|
||||
|
||||
FOREACH_ARRAY(res, resolvers, n)
|
||||
sd_dns_resolver_done(res);
|
||||
|
||||
free(resolvers);
|
||||
}
|
||||
DEFINE_ARRAY_FREE_FUNC(dns_resolver_free_array, sd_dns_resolver, sd_dns_resolver_done);
|
||||
|
||||
int dns_resolver_prio_compare(const sd_dns_resolver *a, const sd_dns_resolver *b) {
|
||||
return CMP(ASSERT_PTR(a)->priority, ASSERT_PTR(b)->priority);
|
||||
@ -306,7 +299,7 @@ int dns_resolvers_to_dot_addrs(const sd_dns_resolver *resolvers, size_t n_resolv
|
||||
|
||||
struct in_addr_full **addrs = NULL;
|
||||
size_t n = 0;
|
||||
CLEANUP_ARRAY(addrs, n, in_addr_full_array_free);
|
||||
CLEANUP_ARRAY(addrs, n, in_addr_full_free_array);
|
||||
|
||||
FOREACH_ARRAY(res, resolvers, n_resolvers) {
|
||||
if (!FLAGS_SET(res->transports, SD_DNS_ALPN_DOT))
|
||||
@ -347,7 +340,7 @@ int dns_resolvers_to_dot_strv(const sd_dns_resolver *resolvers, size_t n_resolve
|
||||
|
||||
struct in_addr_full **addrs = NULL;
|
||||
size_t n = 0;
|
||||
CLEANUP_ARRAY(addrs, n, in_addr_full_array_free);
|
||||
CLEANUP_ARRAY(addrs, n, in_addr_full_free_array);
|
||||
|
||||
r = dns_resolvers_to_dot_addrs(resolvers, n_resolvers, &addrs, &n);
|
||||
if (r < 0)
|
||||
|
||||
@ -80,6 +80,7 @@ sd_login_sources = files('sd-login/sd-login.c')
|
||||
############################################################
|
||||
|
||||
sd_json_sources = files(
|
||||
'sd-json/json-stream.c',
|
||||
'sd-json/json-util.c',
|
||||
'sd-json/sd-json.c',
|
||||
)
|
||||
|
||||
@ -451,7 +451,7 @@ int catalog_update(const char *database, const char *root, const char* const *di
|
||||
ConfFile **files = NULL;
|
||||
size_t n_files = 0;
|
||||
|
||||
CLEANUP_ARRAY(files, n_files, conf_file_free_many);
|
||||
CLEANUP_ARRAY(files, n_files, conf_file_free_array);
|
||||
|
||||
r = conf_files_list_strv_full(".catalog", root,
|
||||
CONF_FILES_REGULAR | CONF_FILES_FILTER_MASKED | CONF_FILES_WARN,
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
#include "time-util.h"
|
||||
#include "xattr-util.h"
|
||||
|
||||
typedef struct vacuum_info {
|
||||
typedef struct VacuumInfo {
|
||||
uint64_t usage;
|
||||
char *filename;
|
||||
|
||||
@ -30,9 +30,9 @@ typedef struct vacuum_info {
|
||||
sd_id128_t seqnum_id;
|
||||
uint64_t seqnum;
|
||||
bool have_seqnum;
|
||||
} vacuum_info;
|
||||
} VacuumInfo;
|
||||
|
||||
static int vacuum_info_compare(const vacuum_info *a, const vacuum_info *b) {
|
||||
static int vacuum_info_compare(const VacuumInfo *a, const VacuumInfo *b) {
|
||||
int r;
|
||||
|
||||
if (a->have_seqnum && b->have_seqnum &&
|
||||
@ -49,16 +49,13 @@ static int vacuum_info_compare(const vacuum_info *a, const vacuum_info *b) {
|
||||
return strcmp(a->filename, b->filename);
|
||||
}
|
||||
|
||||
static void vacuum_info_array_free(vacuum_info *list, size_t n) {
|
||||
if (!list)
|
||||
return;
|
||||
|
||||
FOREACH_ARRAY(i, list, n)
|
||||
free(i->filename);
|
||||
|
||||
free(list);
|
||||
static void vacuum_info_done(VacuumInfo *info) {
|
||||
assert(info);
|
||||
info->filename = mfree(info->filename);
|
||||
}
|
||||
|
||||
static DEFINE_ARRAY_FREE_FUNC(vacuum_info_array_free, VacuumInfo, vacuum_info_done);
|
||||
|
||||
static void patch_realtime(
|
||||
int fd,
|
||||
const char *fn,
|
||||
@ -137,7 +134,7 @@ int journal_directory_vacuum(
|
||||
uint64_t sum = 0, freed = 0, n_active_files = 0;
|
||||
size_t n_list = 0, i;
|
||||
_cleanup_closedir_ DIR *d = NULL;
|
||||
vacuum_info *list = NULL;
|
||||
VacuumInfo *list = NULL;
|
||||
usec_t retention_limit = 0;
|
||||
int r;
|
||||
|
||||
@ -280,7 +277,7 @@ int journal_directory_vacuum(
|
||||
if (!GREEDY_REALLOC(list, n_list + 1))
|
||||
return -ENOMEM;
|
||||
|
||||
list[n_list++] = (vacuum_info) {
|
||||
list[n_list++] = (VacuumInfo) {
|
||||
.filename = TAKE_PTR(p),
|
||||
.usage = size,
|
||||
.seqnum = seqnum,
|
||||
|
||||
1438
src/libsystemd/sd-json/json-stream.c
Normal file
1438
src/libsystemd/sd-json/json-stream.c
Normal file
File diff suppressed because it is too large
Load Diff
267
src/libsystemd/sd-json/json-stream.h
Normal file
267
src/libsystemd/sd-json/json-stream.h
Normal file
@ -0,0 +1,267 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include "sd-forward.h"
|
||||
|
||||
#include "list.h"
|
||||
|
||||
/* JsonStream provides the transport layer used by sd-varlink (and other consumers like
|
||||
* the QMP client) for exchanging length-delimited JSON messages over a pair of file
|
||||
* descriptors. It owns the input/output buffers, the file-descriptor passing machinery
|
||||
* (SCM_RIGHTS), the deferred output queue, and the read/write/parse step functions. It
|
||||
* does not implement any state machine, dispatch, callback or event-source plumbing —
|
||||
* those concerns belong to the consumer. */
|
||||
|
||||
typedef struct JsonStreamQueueItem JsonStreamQueueItem;
|
||||
|
||||
typedef enum JsonStreamFlags {
|
||||
JSON_STREAM_BOUNDED_READS = 1u << 0,
|
||||
JSON_STREAM_INPUT_SENSITIVE = 1u << 1,
|
||||
JSON_STREAM_ALLOW_FD_PASSING_INPUT = 1u << 2,
|
||||
JSON_STREAM_ALLOW_FD_PASSING_OUTPUT = 1u << 3,
|
||||
JSON_STREAM_CONNECTING = 1u << 4,
|
||||
JSON_STREAM_GOT_POLLHUP = 1u << 5,
|
||||
JSON_STREAM_WRITE_DISCONNECTED = 1u << 6,
|
||||
JSON_STREAM_READ_DISCONNECTED = 1u << 7,
|
||||
JSON_STREAM_PREFER_READ = 1u << 8,
|
||||
JSON_STREAM_PREFER_WRITE = 1u << 9,
|
||||
JSON_STREAM_OUTPUT_BUFFER_SENSITIVE = 1u << 10,
|
||||
} JsonStreamFlags;
|
||||
|
||||
/* What the consumer's high-level state machine is currently doing — used by the various
|
||||
* "what should I do right now?" APIs (get_events, wait, should_disconnect) to decide
|
||||
* whether to ask for read events, whether transport death matters, and whether the idle
|
||||
* timeout deadline is currently in force. */
|
||||
typedef enum JsonStreamPhase {
|
||||
JSON_STREAM_PHASE_READING, /* waiting for the next inbound message, no deadline */
|
||||
JSON_STREAM_PHASE_AWAITING_REPLY, /* waiting for a reply with the idle timeout deadline */
|
||||
JSON_STREAM_PHASE_IDLE_CLIENT, /* idle client, no in-flight call */
|
||||
JSON_STREAM_PHASE_PENDING_OUTPUT, /* has more output queued, waiting to send */
|
||||
JSON_STREAM_PHASE_OTHER, /* none of the above */
|
||||
} JsonStreamPhase;
|
||||
|
||||
/* Consumer hooks supplied at construction time:
|
||||
* • phase — queried by get_events / wait / should_disconnect / attach_event's prepare
|
||||
* callback whenever the consumer's current phase is needed.
|
||||
* • dispatch — invoked by attach_event's io and time callbacks after the stream has
|
||||
* consumed the revents, so the consumer can drive its state machine
|
||||
* forward. Should return 0 on success or a negative errno; the stream logs
|
||||
* the failure and continues running. */
|
||||
typedef JsonStreamPhase (*json_stream_phase_t)(void *userdata);
|
||||
typedef int (*json_stream_dispatch_t)(void *userdata);
|
||||
|
||||
typedef struct JsonStreamParams {
|
||||
const char *delimiter; /* message delimiter; NULL → single NUL byte (varlink), e.g. "\r\n" for QMP */
|
||||
size_t buffer_max; /* maximum bytes buffered before -ENOBUFS; 0 = 16 MiB default */
|
||||
size_t read_chunk; /* per-read chunk size; 0 = 64 KiB default */
|
||||
size_t queue_max; /* maximum number of queued output items; 0 = 64 Ki default */
|
||||
|
||||
/* Consumer hooks (see typedefs above). */
|
||||
json_stream_phase_t phase;
|
||||
json_stream_dispatch_t dispatch;
|
||||
void *userdata;
|
||||
} JsonStreamParams;
|
||||
|
||||
typedef struct JsonStream {
|
||||
char *delimiter; /* message delimiter; NULL → NUL byte (varlink), e.g. "\r\n" for QMP */
|
||||
size_t buffer_max;
|
||||
size_t read_chunk;
|
||||
size_t queue_max;
|
||||
|
||||
char *description;
|
||||
|
||||
int input_fd;
|
||||
int output_fd;
|
||||
|
||||
usec_t timeout; /* relative; USEC_INFINITY = no timeout */
|
||||
usec_t last_activity; /* CLOCK_MONOTONIC */
|
||||
|
||||
/* Cached peer credentials */
|
||||
struct ucred ucred;
|
||||
bool ucred_acquired;
|
||||
int peer_pidfd;
|
||||
|
||||
/* Cached socket address family. -1 = unchecked, AF_UNSPEC = checked-not-socket,
|
||||
* otherwise the resolved family. */
|
||||
int af;
|
||||
|
||||
sd_event *event;
|
||||
sd_event_source *input_event_source;
|
||||
sd_event_source *output_event_source;
|
||||
sd_event_source *time_event_source;
|
||||
|
||||
json_stream_phase_t phase_cb;
|
||||
json_stream_dispatch_t dispatch_cb;
|
||||
void *userdata;
|
||||
|
||||
char *input_buffer;
|
||||
size_t input_buffer_index;
|
||||
size_t input_buffer_size;
|
||||
size_t input_buffer_unscanned;
|
||||
|
||||
void *input_control_buffer;
|
||||
size_t input_control_buffer_size;
|
||||
|
||||
char *output_buffer;
|
||||
size_t output_buffer_index;
|
||||
size_t output_buffer_size;
|
||||
|
||||
int *input_fds;
|
||||
size_t n_input_fds;
|
||||
|
||||
int *output_fds;
|
||||
size_t n_output_fds;
|
||||
|
||||
LIST_HEAD(JsonStreamQueueItem, output_queue);
|
||||
JsonStreamQueueItem *output_queue_tail;
|
||||
size_t n_output_queue;
|
||||
|
||||
int *pushed_fds;
|
||||
size_t n_pushed_fds;
|
||||
|
||||
JsonStreamFlags flags;
|
||||
} JsonStream;
|
||||
|
||||
int json_stream_init(JsonStream *s, const JsonStreamParams *params);
|
||||
void json_stream_done(JsonStream *s);
|
||||
|
||||
/* Optional description used as the prefix for the stream's debug log lines (sent/received
|
||||
* messages, POLLHUP detection, async connect completion, etc.). The string is duped. */
|
||||
int json_stream_set_description(JsonStream *s, const char *description);
|
||||
const char* json_stream_get_description(const JsonStream *s);
|
||||
|
||||
/* fd ownership */
|
||||
int json_stream_attach_fds(JsonStream *s, int input_fd, int output_fd);
|
||||
|
||||
/* Open an AF_UNIX SOCK_STREAM socket and connect to the given filesystem path, attaching
|
||||
* the resulting fd to the stream. Handles paths too long for sockaddr_un by routing through
|
||||
* O_PATH (connect_unix_path()). If the connect() returns EAGAIN/EINPROGRESS the stream's
|
||||
* connecting state is set so that the consumer waits for POLLOUT before treating the
|
||||
* connection as established. Returns 0 on success or successfully started async connect,
|
||||
* negative errno on failure. */
|
||||
int json_stream_connect_address(JsonStream *s, const char *address);
|
||||
|
||||
/* Adopt a pre-connected pair of fds, ensuring both are non-blocking. Equivalent to
|
||||
* json_stream_attach_fds() but does the fd_nonblock() dance up front, so the caller can
|
||||
* pass in fds without having to know whether they were already configured. */
|
||||
int json_stream_connect_fd_pair(JsonStream *s, int input_fd, int output_fd);
|
||||
|
||||
bool json_stream_flags_set(const JsonStream *s, JsonStreamFlags flags);
|
||||
void json_stream_set_flags(JsonStream *s, JsonStreamFlags flags, bool b);
|
||||
|
||||
/* Combines the transport-level disconnect signals (write/read disconnected, buffered
|
||||
* output, POLLHUP, async connect) with the consumer's current phase (queried via the
|
||||
* registered get_phase callback) to answer "should the consumer initiate teardown right
|
||||
* now?". The decision logic mirrors what the original varlink transport did but stays
|
||||
* generic enough for other JSON-line consumers. */
|
||||
bool json_stream_should_disconnect(const JsonStream *s);
|
||||
|
||||
/* Enable/disable fd passing. These verify the underlying fd is an AF_UNIX socket and
|
||||
* (for input) optionally set SO_PASSRIGHTS. */
|
||||
int json_stream_set_allow_fd_passing_input(JsonStream *s, bool enabled, bool with_sockopt);
|
||||
int json_stream_set_allow_fd_passing_output(JsonStream *s, bool enabled);
|
||||
|
||||
/* Output: enqueue a JSON variant. Fast path concatenates into the output buffer; if
|
||||
* pushed_fds are present or the queue is non-empty the message is queued instead, so that
|
||||
* fd-to-message boundaries are preserved. */
|
||||
int json_stream_enqueue(JsonStream *s, sd_json_variant *m);
|
||||
|
||||
/* Allocate a queue item carrying `m` and the currently pushed fds. The pushed fds are
|
||||
* transferred to the new item; on success n_pushed_fds is reset to 0. The caller may
|
||||
* later submit the item via json_stream_enqueue_item() or free it. */
|
||||
int json_stream_make_queue_item(JsonStream *s, sd_json_variant *m, JsonStreamQueueItem **ret);
|
||||
int json_stream_enqueue_item(JsonStream *s, JsonStreamQueueItem *q);
|
||||
JsonStreamQueueItem* json_stream_queue_item_free(JsonStreamQueueItem *q);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(JsonStreamQueueItem*, json_stream_queue_item_free);
|
||||
sd_json_variant** json_stream_queue_item_get_data(JsonStreamQueueItem *q);
|
||||
|
||||
/* fd push/peek/take */
|
||||
int json_stream_push_fd(JsonStream *s, int fd);
|
||||
void json_stream_reset_pushed_fds(JsonStream *s);
|
||||
|
||||
int json_stream_peek_input_fd(const JsonStream *s, size_t i);
|
||||
int json_stream_take_input_fd(JsonStream *s, size_t i);
|
||||
size_t json_stream_get_n_input_fds(const JsonStream *s);
|
||||
|
||||
/* Close and free all currently received input fds (used after consuming a message). */
|
||||
void json_stream_close_input_fds(JsonStream *s);
|
||||
|
||||
/* I/O steps. Same return-value contract as the original varlink_{write,read,parse_message}:
|
||||
* 1 = made progress (call again),
|
||||
* 0 = nothing to do (wait for I/O),
|
||||
* <0 = error. */
|
||||
int json_stream_write(JsonStream *s);
|
||||
int json_stream_read(JsonStream *s);
|
||||
|
||||
/* Extract the next complete JSON message from the input buffer (delimited per
|
||||
* params.delimiter). Returns 1 with *ret set on success, 0 if no full message is
|
||||
* available yet (with *ret == NULL), <0 on parse error. The buffer slot occupied by the
|
||||
* parsed message is erased if input_sensitive was set. */
|
||||
int json_stream_parse(JsonStream *s, sd_json_variant **ret);
|
||||
|
||||
/* Status accessors used by the consumer's state machine. */
|
||||
bool json_stream_has_buffered_input(const JsonStream *s);
|
||||
|
||||
/* Compute the poll events the consumer should wait for. The stream queries the consumer's
|
||||
* phase via the registered get_phase callback. In JSON_STREAM_PHASE_READING the stream asks
|
||||
* for POLLIN (provided the input buffer is empty and the read side is still alive); POLLOUT
|
||||
* is added whenever there's pending output. When connecting we only ask for POLLOUT to
|
||||
* learn when the non-blocking connect() completes. */
|
||||
int json_stream_get_events(const JsonStream *s);
|
||||
|
||||
/* Block on poll() for the configured fds for at most `timeout` µs. Internally updates the
|
||||
* connecting / got_pollhup state based on the seen revents.
|
||||
* 1 = some event was observed (call us again),
|
||||
* 0 = timeout,
|
||||
* <0 = error (negative errno from ppoll_usec). */
|
||||
int json_stream_wait(JsonStream *s, usec_t timeout);
|
||||
|
||||
/* Block until the output buffer is fully drained (or the write side disconnects).
|
||||
* 1 = some bytes were written during the flush,
|
||||
* 0 = nothing to flush,
|
||||
* -ECONNRESET if the write side became disconnected before everything could be sent,
|
||||
* <0 on other I/O errors. */
|
||||
int json_stream_flush(JsonStream *s);
|
||||
|
||||
/* Peer credential helpers. All refuse if the stream uses different input/output fds, since
|
||||
* peer credentials are only meaningful for a bidirectional socket.
|
||||
* • acquire_peer_uid/gid/pid/pidfd() query the kernel on first use, cache the result,
|
||||
* and log failures (using the stream's description). They each return 0 on success
|
||||
* with the value in *ret, or a negative errno on failure (kernel error or invalid
|
||||
* field).
|
||||
* • get_peer_ucred() returns the *already-cached* ucred (set via a prior acquire or via
|
||||
* set_peer_ucred()) without triggering a kernel query — returns -ENODATA if nothing is
|
||||
* cached. Used by consumers that want to react to a previously-known ucred without
|
||||
* forcing a fresh query (e.g. teardown bookkeeping). */
|
||||
int json_stream_acquire_peer_uid(JsonStream *s, uid_t *ret);
|
||||
int json_stream_acquire_peer_gid(JsonStream *s, gid_t *ret);
|
||||
int json_stream_acquire_peer_pid(JsonStream *s, pid_t *ret);
|
||||
int json_stream_acquire_peer_pidfd(JsonStream *s);
|
||||
int json_stream_get_peer_ucred(const JsonStream *s, struct ucred *ret);
|
||||
void json_stream_set_peer_ucred(JsonStream *s, const struct ucred *ucred);
|
||||
|
||||
/* Per-operation idle timeout. The deadline is computed as last_activity + timeout.
|
||||
* Successful writes refresh last_activity automatically; the consumer should also call
|
||||
* json_stream_mark_activity() at operation start (e.g. when initiating a method call) to
|
||||
* reset the deadline.
|
||||
*
|
||||
* When the deadline elapses the time event source attached via json_stream_attach_event()
|
||||
* fires and the consumer's dispatch callback is invoked. The consumer detects the timeout
|
||||
* by comparing now(CLOCK_MONOTONIC) against json_stream_get_timeout(). */
|
||||
void json_stream_set_timeout(JsonStream *s, usec_t timeout);
|
||||
void json_stream_mark_activity(JsonStream *s);
|
||||
|
||||
/* Returns the absolute deadline (in CLOCK_MONOTONIC microseconds) currently in force for
|
||||
* the consumer's phase, or USEC_INFINITY if no timeout applies (no timeout configured, no
|
||||
* activity yet, or the current phase isn't AWAITING_REPLY). */
|
||||
usec_t json_stream_get_timeout(const JsonStream *s);
|
||||
|
||||
/* sd-event integration. JsonStream owns the input/output io event sources and the time
|
||||
* event source for its idle timeout, and installs its own internal prepare and io callbacks
|
||||
* on them. The hooks (get_phase, io_dispatch) supplied via JsonStreamParams at construction
|
||||
* are wired up automatically. */
|
||||
int json_stream_attach_event(JsonStream *s, sd_event *event, int64_t priority);
|
||||
void json_stream_detach_event(JsonStream *s);
|
||||
sd_event* json_stream_get_event(const JsonStream *s);
|
||||
@ -3122,6 +3122,12 @@ static int json_parse_internal(
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* n_stack includes the top level entry, hence > instead of >= */
|
||||
if (n_stack > DEPTH_MAX) {
|
||||
r = -ELNRNG;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (!GREEDY_REALLOC(stack, n_stack+1)) {
|
||||
r = -ENOMEM;
|
||||
goto finish;
|
||||
@ -3178,6 +3184,12 @@ static int json_parse_internal(
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* n_stack includes the top level entry, hence > instead of >= */
|
||||
if (n_stack > DEPTH_MAX) {
|
||||
r = -ELNRNG;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (!GREEDY_REALLOC(stack, n_stack+1)) {
|
||||
r = -ENOMEM;
|
||||
goto finish;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -5,6 +5,7 @@
|
||||
|
||||
#include "sd-varlink.h"
|
||||
|
||||
#include "json-stream.h"
|
||||
#include "list.h"
|
||||
#include "pidref.h"
|
||||
#include "sd-forward.h"
|
||||
@ -70,86 +71,22 @@ typedef enum VarlinkState {
|
||||
VARLINK_PROCESSING_METHOD, \
|
||||
VARLINK_PROCESSING_METHOD_MORE)
|
||||
|
||||
typedef struct VarlinkJsonQueueItem VarlinkJsonQueueItem;
|
||||
|
||||
/* A queued message we shall write into the socket, along with the file descriptors to send at the same
|
||||
* time. This queue item binds them together so that message/fd boundaries are maintained throughout the
|
||||
* whole pipeline. */
|
||||
struct VarlinkJsonQueueItem {
|
||||
LIST_FIELDS(VarlinkJsonQueueItem, queue);
|
||||
sd_json_variant *data;
|
||||
size_t n_fds;
|
||||
int fds[];
|
||||
};
|
||||
|
||||
typedef struct sd_varlink {
|
||||
unsigned n_ref;
|
||||
|
||||
sd_varlink_server *server;
|
||||
|
||||
VarlinkState state;
|
||||
bool connecting; /* This boolean indicates whether the socket fd we are operating on is currently
|
||||
* processing an asynchronous connect(). In that state we watch the socket for
|
||||
* EPOLLOUT, but we refrain from calling read() or write() on the socket as that
|
||||
* will trigger ENOTCONN. Note that this boolean is kept separate from the
|
||||
* VarlinkState above on purpose: while the connect() is still not complete we
|
||||
* already want to allow queuing of messages and similar. Thus it's nice to keep
|
||||
* these two state concepts separate: the VarlinkState encodes what our own view of
|
||||
* the connection is, i.e. whether we think it's a server, a client, and has
|
||||
* something queued already, while 'connecting' tells us a detail about the
|
||||
* transport used below, that should have no effect on how we otherwise accept and
|
||||
* process operations from the user.
|
||||
*
|
||||
* Or to say this differently: VARLINK_STATE_IS_ALIVE(state) tells you whether the
|
||||
* connection is good to use, even if it might not be fully connected
|
||||
* yet. connecting=true then informs you that actually we are still connecting, and
|
||||
* the connection is actually not established yet and thus any requests you enqueue
|
||||
* now will still work fine but will be queued only, not sent yet, but that
|
||||
* shouldn't stop you from using the connection, since eventually whatever you queue
|
||||
* *will* be sent.
|
||||
*
|
||||
* Or to say this even differently: 'state' is a high-level ("application layer"
|
||||
* high, if you so will) state, while 'conecting' is a low-level ("transport layer"
|
||||
* low, if you so will) state, and while they are not entirely unrelated and
|
||||
* sometimes propagate effects to each other they are only asynchronously connected
|
||||
* at most. */
|
||||
|
||||
/* Transport layer: input/output buffers, fd passing, output queue, read/write/parse
|
||||
* step functions, sd-event integration (input/output/time event sources, idle
|
||||
* timeout, description, peer credentials). The varlink-level state machine and
|
||||
* dispatch logic live in sd-varlink.c; everything else about moving bytes is
|
||||
* delegated. */
|
||||
JsonStream stream;
|
||||
|
||||
unsigned n_pending;
|
||||
|
||||
int input_fd;
|
||||
int output_fd;
|
||||
|
||||
char *input_buffer; /* valid data starts at input_buffer_index, ends at input_buffer_index+input_buffer_size */
|
||||
size_t input_buffer_index;
|
||||
size_t input_buffer_size;
|
||||
size_t input_buffer_unscanned;
|
||||
|
||||
void *input_control_buffer;
|
||||
size_t input_control_buffer_size;
|
||||
|
||||
char *output_buffer; /* valid data starts at output_buffer_index, ends at output_buffer_index+output_buffer_size */
|
||||
size_t output_buffer_index;
|
||||
size_t output_buffer_size;
|
||||
|
||||
int *input_fds; /* file descriptors associated with the data in input_buffer (for fd passing) */
|
||||
size_t n_input_fds;
|
||||
|
||||
int *output_fds; /* file descriptors associated with the data in output_buffer (for fd passing) */
|
||||
size_t n_output_fds;
|
||||
|
||||
/* Further messages to output not yet formatted into text, and thus not included in output_buffer
|
||||
* yet. We keep them separate from output_buffer, to not violate fd message boundaries: we want that
|
||||
* each fd that is sent is associated with its fds, and that fds cannot be accidentally associated
|
||||
* with preceding or following messages. */
|
||||
LIST_HEAD(VarlinkJsonQueueItem, output_queue);
|
||||
VarlinkJsonQueueItem *output_queue_tail;
|
||||
size_t n_output_queue;
|
||||
|
||||
/* The fds to associate with the next message that is about to be enqueued. The user first pushes the
|
||||
* fds it intends to send via varlink_push_fd() into this queue, and then once the message data is
|
||||
* submitted we'll combine the fds and the message data into one. */
|
||||
int *pushed_fds;
|
||||
size_t n_pushed_fds;
|
||||
|
||||
sd_varlink_reply_t reply_callback;
|
||||
|
||||
sd_json_variant *current;
|
||||
@ -157,39 +94,18 @@ typedef struct sd_varlink {
|
||||
sd_varlink_reply_flags_t current_reply_flags;
|
||||
sd_varlink_symbol *current_method;
|
||||
|
||||
VarlinkJsonQueueItem *previous;
|
||||
JsonStreamQueueItem *previous;
|
||||
char *sentinel;
|
||||
|
||||
int peer_pidfd;
|
||||
struct ucred ucred;
|
||||
bool ucred_acquired:1;
|
||||
|
||||
bool write_disconnected:1;
|
||||
bool read_disconnected:1;
|
||||
bool prefer_read:1;
|
||||
bool prefer_write:1;
|
||||
bool got_pollhup:1;
|
||||
|
||||
bool protocol_upgrade:1; /* Whether a protocol_upgrade was requested */
|
||||
|
||||
bool output_buffer_sensitive:1; /* whether to erase the output buffer after writing it to the socket */
|
||||
bool input_sensitive:1; /* Whether incoming messages might be sensitive */
|
||||
|
||||
bool allow_fd_passing_output;
|
||||
int allow_fd_passing_input;
|
||||
|
||||
int af; /* address family if socket; AF_UNSPEC if not socket; negative if not known */
|
||||
|
||||
usec_t timestamp;
|
||||
usec_t timeout;
|
||||
/* Per-call protocol-upgrade marker: set when the *current* method call carries the
|
||||
* SD_VARLINK_METHOD_UPGRADE flag. Validated by sd_varlink_reply_and_upgrade() to
|
||||
* ensure the caller's contract is honored. The transport-layer "stop reading at the
|
||||
* next message boundary" behavior is governed independently by the JsonStream's
|
||||
* bounded_reads flag. */
|
||||
bool protocol_upgrade:1;
|
||||
|
||||
void *userdata;
|
||||
char *description;
|
||||
|
||||
sd_event *event;
|
||||
sd_event_source *input_event_source;
|
||||
sd_event_source *output_event_source;
|
||||
sd_event_source *time_event_source;
|
||||
sd_event_source *quit_event_source;
|
||||
sd_event_source *defer_event_source;
|
||||
|
||||
@ -254,7 +170,7 @@ typedef struct sd_varlink_server {
|
||||
log_debug("%s: " fmt, varlink_server_description(s), ##__VA_ARGS__)
|
||||
|
||||
static inline const char* varlink_description(sd_varlink *v) {
|
||||
return (v ? v->description : NULL) ?: "varlink";
|
||||
return (v ? json_stream_get_description(&v->stream) : NULL) ?: "varlink";
|
||||
}
|
||||
|
||||
static inline const char* varlink_server_description(sd_varlink_server *s) {
|
||||
|
||||
@ -417,7 +417,7 @@ static int run(int argc, char *argv[]) {
|
||||
ConfFile **files = NULL;
|
||||
size_t n_files = 0;
|
||||
|
||||
CLEANUP_ARRAY(files, n_files, conf_file_free_many);
|
||||
CLEANUP_ARRAY(files, n_files, conf_file_free_array);
|
||||
|
||||
STRV_FOREACH(i, arg_proc_cmdline_modules)
|
||||
RET_GATHER(ret, modules_list_append_dup(&module_set, *i));
|
||||
|
||||
@ -136,7 +136,7 @@ static int link_put_dns(Link *link, OrderedSet **s) {
|
||||
if (r >= 0) {
|
||||
struct in_addr_full **dot_servers;
|
||||
size_t n = 0;
|
||||
CLEANUP_ARRAY(dot_servers, n, in_addr_full_array_free);
|
||||
CLEANUP_ARRAY(dot_servers, n, in_addr_full_free_array);
|
||||
|
||||
r = dns_resolvers_to_dot_addrs(resolvers, r, &dot_servers, &n);
|
||||
if (r < 0)
|
||||
@ -165,7 +165,7 @@ static int link_put_dns(Link *link, OrderedSet **s) {
|
||||
if (r >= 0) {
|
||||
struct in_addr_full **dot_servers;
|
||||
size_t n = 0;
|
||||
CLEANUP_ARRAY(dot_servers, n, in_addr_full_array_free);
|
||||
CLEANUP_ARRAY(dot_servers, n, in_addr_full_free_array);
|
||||
|
||||
r = dns_resolvers_to_dot_addrs(resolvers, r, &dot_servers, &n);
|
||||
if (r < 0)
|
||||
@ -193,7 +193,7 @@ static int link_put_dns(Link *link, OrderedSet **s) {
|
||||
SET_FOREACH(a, link->ndisc_dnr) {
|
||||
struct in_addr_full **dot_servers = NULL;
|
||||
size_t n = 0;
|
||||
CLEANUP_ARRAY(dot_servers, n, in_addr_full_array_free);
|
||||
CLEANUP_ARRAY(dot_servers, n, in_addr_full_free_array);
|
||||
|
||||
r = dns_resolvers_to_dot_addrs(&a->resolver, 1, &dot_servers, &n);
|
||||
if (r < 0)
|
||||
|
||||
@ -37,7 +37,7 @@ Bearer* bearer_free(Bearer *b) {
|
||||
free(b->name);
|
||||
free(b->apn);
|
||||
|
||||
in_addr_full_array_free(b->dns, b->n_dns);
|
||||
in_addr_full_free_array(b->dns, b->n_dns);
|
||||
|
||||
return mfree(b);
|
||||
}
|
||||
|
||||
@ -364,11 +364,6 @@ static int parse_private_users(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void unref_many_tables(Table* (*tablesp)[]) {
|
||||
for (Table **t = *ASSERT_PTR(tablesp); *t; t++)
|
||||
*t = table_unref(*t);
|
||||
}
|
||||
|
||||
static int help(void) {
|
||||
_cleanup_free_ char *link = NULL;
|
||||
int r;
|
||||
@ -379,7 +374,7 @@ static int help(void) {
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
static const char *groups[] = {
|
||||
static const char* const groups[] = {
|
||||
NULL,
|
||||
"Image",
|
||||
"Execution",
|
||||
@ -396,7 +391,7 @@ static int help(void) {
|
||||
"Other",
|
||||
};
|
||||
|
||||
_cleanup_(unref_many_tables) Table* tables[ELEMENTSOF(groups) + 1] = {};
|
||||
_cleanup_(table_unref_many) Table* tables[ELEMENTSOF(groups) + 1] = {};
|
||||
|
||||
for (size_t i = 0; i < ELEMENTSOF(groups); i++) {
|
||||
r = option_parser_get_help_table_group(groups[i], &tables[i]);
|
||||
@ -1372,7 +1367,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
* been repurposed to optionally set the runtime scope, with --uid= replacing
|
||||
* the old container user functionality. To maintain backwards compatibility
|
||||
* with the space-separated form (--user NAME), if the next arg does not look
|
||||
* like an option, interpret it a user name. */
|
||||
* like an option, interpret it as a user name. */
|
||||
const char *t = option_parser_next_arg(&state);
|
||||
if (t && t[0] != '-') {
|
||||
arg = option_parser_consume_next_arg(&state);
|
||||
|
||||
@ -65,14 +65,7 @@ void delegated_userns_info_done(DelegatedUserNamespaceInfo *info) {
|
||||
info->n_ancestor_userns = 0;
|
||||
}
|
||||
|
||||
void delegated_userns_info_done_many(DelegatedUserNamespaceInfo infos[], size_t n) {
|
||||
assert(infos || n == 0);
|
||||
|
||||
FOREACH_ARRAY(info, infos, n)
|
||||
delegated_userns_info_done(info);
|
||||
|
||||
free(infos);
|
||||
}
|
||||
static DEFINE_ARRAY_FREE_FUNC(delegated_userns_info_free_array, DelegatedUserNamespaceInfo, delegated_userns_info_done);
|
||||
|
||||
UserNamespaceInfo* userns_info_new(void) {
|
||||
UserNamespaceInfo *info = new(UserNamespaceInfo, 1);
|
||||
@ -97,7 +90,7 @@ UserNamespaceInfo *userns_info_free(UserNamespaceInfo *userns) {
|
||||
free(userns->cgroups);
|
||||
free(userns->name);
|
||||
|
||||
delegated_userns_info_done_many(userns->delegates, userns->n_delegates);
|
||||
delegated_userns_info_free_array(userns->delegates, userns->n_delegates);
|
||||
|
||||
strv_free(userns->netifs);
|
||||
|
||||
@ -154,12 +147,10 @@ static int dispatch_delegates_array(const char *name, sd_json_variant *variant,
|
||||
size_t n = 0;
|
||||
int r;
|
||||
|
||||
CLEANUP_ARRAY(delegates, n, delegated_userns_info_done_many);
|
||||
CLEANUP_ARRAY(delegates, n, delegated_userns_info_free_array);
|
||||
|
||||
if (sd_json_variant_is_null(variant)) {
|
||||
delegated_userns_info_done_many(info->delegates, info->n_delegates);
|
||||
info->delegates = NULL;
|
||||
info->n_delegates = 0;
|
||||
CLEANUP_ARRAY(info->delegates, info->n_delegates, delegated_userns_info_free_array);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -199,7 +190,7 @@ static int dispatch_delegates_array(const char *name, sd_json_variant *variant,
|
||||
n++;
|
||||
}
|
||||
|
||||
delegated_userns_info_done_many(info->delegates, info->n_delegates);
|
||||
delegated_userns_info_free_array(info->delegates, info->n_delegates);
|
||||
info->delegates = TAKE_PTR(delegates);
|
||||
info->n_delegates = n;
|
||||
|
||||
|
||||
@ -24,7 +24,6 @@ typedef struct DelegatedUserNamespaceInfo {
|
||||
}
|
||||
|
||||
void delegated_userns_info_done(DelegatedUserNamespaceInfo *info);
|
||||
void delegated_userns_info_done_many(DelegatedUserNamespaceInfo infos[], size_t n);
|
||||
|
||||
typedef struct UserNamespaceInfo {
|
||||
uid_t owner;
|
||||
|
||||
@ -1271,18 +1271,13 @@ static int portable_changes_add_with_prefix(
|
||||
return portable_changes_add(changes, n_changes, type_or_errno, path, source);
|
||||
}
|
||||
|
||||
void portable_changes_free(PortableChange *changes, size_t n_changes) {
|
||||
size_t i;
|
||||
|
||||
assert(changes || n_changes == 0);
|
||||
|
||||
for (i = 0; i < n_changes; i++) {
|
||||
free(changes[i].path);
|
||||
free(changes[i].source);
|
||||
static void portable_change_done(PortableChange *change) {
|
||||
assert(change);
|
||||
change->path = mfree(change->path);
|
||||
change->source = mfree(change->source);
|
||||
}
|
||||
|
||||
free(changes);
|
||||
}
|
||||
DEFINE_ARRAY_FREE_FUNC(portable_changes_free, PortableChange, portable_change_done);
|
||||
|
||||
static const char *root_setting_from_image(ImageType type) {
|
||||
switch (type) {
|
||||
|
||||
@ -112,7 +112,7 @@ int portable_get_state(
|
||||
|
||||
int portable_get_profiles(RuntimeScope scope, char ***ret);
|
||||
|
||||
void portable_changes_free(PortableChange *changes, size_t n_changes);
|
||||
void portable_changes_free(PortableChange *array, size_t n);
|
||||
|
||||
DECLARE_STRING_TABLE_LOOKUP(portable_change_type, int);
|
||||
|
||||
|
||||
@ -145,7 +145,7 @@ static int manager_static_records_read(Manager *m) {
|
||||
|
||||
ConfFile **files = NULL;
|
||||
size_t n_files = 0;
|
||||
CLEANUP_ARRAY(files, n_files, conf_file_free_many);
|
||||
CLEANUP_ARRAY(files, n_files, conf_file_free_array);
|
||||
|
||||
r = conf_files_list_nulstr_full(
|
||||
".rr",
|
||||
|
||||
@ -50,19 +50,7 @@ static const char* dnat_map_name(void) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
static sd_netlink_message** netlink_message_unref_many(sd_netlink_message **m) {
|
||||
if (!m)
|
||||
return NULL;
|
||||
|
||||
/* This does not free array. The end of the array must be NULL. */
|
||||
|
||||
for (sd_netlink_message **p = m; *p; p++)
|
||||
*p = sd_netlink_message_unref(*p);
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(sd_netlink_message**, netlink_message_unref_many);
|
||||
static DEFINE_ARRAY_DONE_FUNC(sd_netlink_message*, sd_netlink_message_unref);
|
||||
|
||||
static int nfnl_open_expr_container(sd_netlink_message *m, const char *name) {
|
||||
int r;
|
||||
@ -736,8 +724,7 @@ static uint32_t concat_types2(enum nft_key_types a, enum nft_key_types b) {
|
||||
}
|
||||
|
||||
static int fw_nftables_init_family(sd_netlink *nfnl, int family) {
|
||||
sd_netlink_message *messages[10] = {};
|
||||
_unused_ _cleanup_(netlink_message_unref_manyp) sd_netlink_message **unref = messages;
|
||||
_cleanup_(sd_netlink_message_unref_many) sd_netlink_message *messages[10] = {};
|
||||
size_t msgcnt = 0, ip_type_size;
|
||||
uint32_t set_id = 0;
|
||||
int ip_type, r;
|
||||
@ -1058,8 +1045,7 @@ static int fw_nftables_add_local_dnat_internal(
|
||||
uint16_t remote_port,
|
||||
const union in_addr_union *previous_remote) {
|
||||
|
||||
sd_netlink_message *messages[3] = {};
|
||||
_unused_ _cleanup_(netlink_message_unref_manyp) sd_netlink_message **unref = messages;
|
||||
_cleanup_(sd_netlink_message_unref_many) sd_netlink_message *messages[3] = {};
|
||||
uint32_t data[5], key[2], dlen;
|
||||
size_t msgcnt = 0;
|
||||
int r;
|
||||
|
||||
@ -100,6 +100,7 @@ Table* table_new_vertical(void);
|
||||
Table* table_unref(Table *t);
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(Table*, table_unref);
|
||||
static inline DEFINE_ARRAY_DONE_FUNC(Table*, table_unref);
|
||||
|
||||
int table_add_cell_full(Table *t, TableCell **ret_cell, TableDataType dt, const void *data, size_t minimum_width, size_t maximum_width, unsigned weight, unsigned align_percent, unsigned ellipsize_percent);
|
||||
static inline int table_add_cell(Table *t, TableCell **ret_cell, TableDataType dt, const void *data) {
|
||||
|
||||
@ -608,7 +608,7 @@ int hwdb_update(const char *root, const char *hwdb_bin_dir, bool strict, bool co
|
||||
ConfFile **files = NULL;
|
||||
size_t n_files = 0;
|
||||
|
||||
CLEANUP_ARRAY(files, n_files, conf_file_free_many);
|
||||
CLEANUP_ARRAY(files, n_files, conf_file_free_array);
|
||||
|
||||
r = conf_files_list_strv_full(".hwdb", root,
|
||||
CONF_FILES_REGULAR | CONF_FILES_FILTER_MASKED | CONF_FILES_WARN,
|
||||
|
||||
@ -319,16 +319,14 @@ InstallChangeType install_changes_add(
|
||||
return type;
|
||||
}
|
||||
|
||||
void install_changes_free(InstallChange *changes, size_t n_changes) {
|
||||
assert(changes || n_changes == 0);
|
||||
static void install_change_done(InstallChange *change) {
|
||||
assert(change);
|
||||
|
||||
FOREACH_ARRAY(i, changes, n_changes) {
|
||||
free(i->path);
|
||||
free(i->source);
|
||||
change->path = mfree(change->path);
|
||||
change->source = mfree(change->source);
|
||||
}
|
||||
|
||||
free(changes);
|
||||
}
|
||||
DEFINE_ARRAY_FREE_FUNC(install_changes_free, InstallChange, install_change_done);
|
||||
|
||||
static void install_change_dump_success(const InstallChange *change) {
|
||||
assert(change);
|
||||
|
||||
@ -202,7 +202,7 @@ static inline int unit_file_exists(RuntimeScope scope, const LookupPaths *lp, co
|
||||
int unit_file_get_list(RuntimeScope scope, const char *root_dir, char * const *states, char * const *patterns, Hashmap **ret);
|
||||
|
||||
InstallChangeType install_changes_add(InstallChange **changes, size_t *n_changes, InstallChangeType type, const char *path, const char *source);
|
||||
void install_changes_free(InstallChange *changes, size_t n_changes);
|
||||
void install_changes_free(InstallChange *array, size_t n);
|
||||
|
||||
int install_change_dump_error(const InstallChange *change, char **ret_errmsg, const char **ret_bus_error);
|
||||
int install_changes_dump(
|
||||
|
||||
@ -1748,14 +1748,7 @@ static void sub_mount_clear(SubMount *s) {
|
||||
s->mount_fd = safe_close(s->mount_fd);
|
||||
}
|
||||
|
||||
void sub_mount_array_free(SubMount *s, size_t n) {
|
||||
assert(s || n == 0);
|
||||
|
||||
for (size_t i = 0; i < n; i++)
|
||||
sub_mount_clear(s + i);
|
||||
|
||||
free(s);
|
||||
}
|
||||
DEFINE_ARRAY_FREE_FUNC(sub_mount_array_free, SubMount, sub_mount_clear);
|
||||
|
||||
#if HAVE_LIBMOUNT
|
||||
static int sub_mount_compare(const SubMount *a, const SubMount *b) {
|
||||
|
||||
@ -8,7 +8,7 @@ typedef struct SubMount {
|
||||
int mount_fd;
|
||||
} SubMount;
|
||||
|
||||
void sub_mount_array_free(SubMount *s, size_t n);
|
||||
void sub_mount_array_free(SubMount *array, size_t n);
|
||||
|
||||
int get_sub_mounts(const char *prefix, SubMount **ret_mounts, size_t *ret_n_mounts);
|
||||
int bind_mount_submounts(
|
||||
|
||||
@ -486,7 +486,7 @@ int conf_files_cat(const char *root, const char *name, CatFlags flags) {
|
||||
/* Then locate the drop-ins, if any */
|
||||
ConfFile **dropins = NULL;
|
||||
size_t n_dropins = 0;
|
||||
CLEANUP_ARRAY(dropins, n_dropins, conf_file_free_many);
|
||||
CLEANUP_ARRAY(dropins, n_dropins, conf_file_free_array);
|
||||
r = conf_files_list_strv_full(extension, root, CONF_FILES_REGULAR | CONF_FILES_FILTER_MASKED | CONF_FILES_WARN, (const char* const*) dirs, &dropins, &n_dropins);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to query file list: %m");
|
||||
|
||||
@ -359,14 +359,7 @@ struct in_addr_full *in_addr_full_free(struct in_addr_full *a) {
|
||||
return mfree(a);
|
||||
}
|
||||
|
||||
void in_addr_full_array_free(struct in_addr_full *addrs[], size_t n) {
|
||||
assert(addrs || n == 0);
|
||||
|
||||
FOREACH_ARRAY(a, addrs, n)
|
||||
in_addr_full_freep(a);
|
||||
|
||||
free(addrs);
|
||||
}
|
||||
DEFINE_POINTER_ARRAY_FREE_FUNC(struct in_addr_full*, in_addr_full_free);
|
||||
|
||||
int in_addr_full_new(
|
||||
int family,
|
||||
|
||||
@ -38,7 +38,7 @@ struct in_addr_full {
|
||||
|
||||
struct in_addr_full* in_addr_full_free(struct in_addr_full *a);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(struct in_addr_full*, in_addr_full_free);
|
||||
void in_addr_full_array_free(struct in_addr_full *addrs[], size_t n);
|
||||
void in_addr_full_free_array(struct in_addr_full **array, size_t n);
|
||||
int in_addr_full_new(int family, const union in_addr_union *a, uint16_t port, int ifindex, const char *server_name, struct in_addr_full **ret);
|
||||
int in_addr_full_new_from_string(const char *s, struct in_addr_full **ret);
|
||||
const char* in_addr_full_to_string(struct in_addr_full *a);
|
||||
|
||||
@ -69,14 +69,7 @@ static void xattr_done(XAttr *xa) {
|
||||
iovec_done(&xa->data);
|
||||
}
|
||||
|
||||
static void xattr_done_many(XAttr *xa, size_t n) {
|
||||
assert(xa || n == 0);
|
||||
|
||||
FOREACH_ARRAY(i, xa, n)
|
||||
xattr_done(i);
|
||||
|
||||
free(xa);
|
||||
}
|
||||
static DEFINE_ARRAY_FREE_FUNC(xattr_free_array, XAttr, xattr_done);
|
||||
|
||||
static void open_inode_done(OpenInode *of) {
|
||||
assert(of);
|
||||
@ -87,7 +80,7 @@ static void open_inode_done(OpenInode *of) {
|
||||
of->fd = safe_close(of->fd);
|
||||
of->path = mfree(of->path);
|
||||
}
|
||||
xattr_done_many(of->xattr, of->n_xattr);
|
||||
xattr_free_array(of->xattr, of->n_xattr);
|
||||
#if HAVE_ACL
|
||||
if (of->acl_access)
|
||||
sym_acl_free(of->acl_access);
|
||||
@ -96,14 +89,7 @@ static void open_inode_done(OpenInode *of) {
|
||||
#endif
|
||||
}
|
||||
|
||||
static void open_inode_done_many(OpenInode *array, size_t n) {
|
||||
assert(array || n == 0);
|
||||
|
||||
FOREACH_ARRAY(i, array, n)
|
||||
open_inode_done(i);
|
||||
|
||||
free(array);
|
||||
}
|
||||
static DEFINE_ARRAY_FREE_FUNC(open_inode_free_array, OpenInode, open_inode_done);
|
||||
|
||||
static int open_inode_apply_acl(OpenInode *of) {
|
||||
int r = 0;
|
||||
@ -792,7 +778,7 @@ int tar_x(int input_fd, int tree_fd, TarFlags flags) {
|
||||
return log_oom();
|
||||
|
||||
size_t n_open_inodes = 0;
|
||||
CLEANUP_ARRAY(open_inodes, n_open_inodes, open_inode_done_many);
|
||||
CLEANUP_ARRAY(open_inodes, n_open_inodes, open_inode_free_array);
|
||||
|
||||
/* Fill in the root inode. (Note: we leave the .path field as NULL to mark it as root inode.) */
|
||||
open_inodes[0] = (OpenInode) {
|
||||
@ -913,7 +899,7 @@ int tar_x(int input_fd, int tree_fd, TarFlags flags) {
|
||||
acl_t acl_access = NULL, acl_default = NULL;
|
||||
XAttr *xa = NULL;
|
||||
size_t n_xa = 0;
|
||||
CLEANUP_ARRAY(xa, n_xa, xattr_done_many);
|
||||
CLEANUP_ARRAY(xa, n_xa, xattr_free_array);
|
||||
|
||||
if (isempty(rest)) {
|
||||
/* This is the final node in the path, create it */
|
||||
|
||||
@ -111,11 +111,7 @@ static Context* context_new(void) {
|
||||
return new0(Context, 1);
|
||||
}
|
||||
|
||||
static void free_transfers(Transfer **array, size_t n) {
|
||||
FOREACH_ARRAY(t, array, n)
|
||||
transfer_free(*t);
|
||||
free(array);
|
||||
}
|
||||
static DEFINE_POINTER_ARRAY_FREE_FUNC(Transfer*, transfer_free);
|
||||
|
||||
static int read_definitions(
|
||||
Context *c,
|
||||
@ -128,9 +124,9 @@ static int read_definitions(
|
||||
size_t n_files = 0, n_transfers = 0, n_disabled = 0;
|
||||
int r;
|
||||
|
||||
CLEANUP_ARRAY(files, n_files, conf_file_free_many);
|
||||
CLEANUP_ARRAY(transfers, n_transfers, free_transfers);
|
||||
CLEANUP_ARRAY(disabled, n_disabled, free_transfers);
|
||||
CLEANUP_ARRAY(files, n_files, conf_file_free_array);
|
||||
CLEANUP_ARRAY(transfers, n_transfers, transfer_free_array);
|
||||
CLEANUP_ARRAY(disabled, n_disabled, transfer_free_array);
|
||||
|
||||
assert(c);
|
||||
assert(dirs);
|
||||
@ -208,7 +204,7 @@ static int context_read_definitions(Context *c, const char* node, bool requires_
|
||||
ConfFile **files = NULL;
|
||||
size_t n_files = 0;
|
||||
|
||||
CLEANUP_ARRAY(files, n_files, conf_file_free_many);
|
||||
CLEANUP_ARRAY(files, n_files, conf_file_free_array);
|
||||
|
||||
r = conf_files_list_strv_full(".feature", arg_root,
|
||||
CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED|CONF_FILES_WARN,
|
||||
@ -1718,7 +1714,7 @@ static int verb_components(int argc, char *argv[], uintptr_t _data, void *userda
|
||||
ConfFile **directories = NULL;
|
||||
size_t n_directories = 0;
|
||||
|
||||
CLEANUP_ARRAY(directories, n_directories, conf_file_free_many);
|
||||
CLEANUP_ARRAY(directories, n_directories, conf_file_free_array);
|
||||
|
||||
r = conf_files_list_strv_full(".d", arg_root, CONF_FILES_DIRECTORY|CONF_FILES_WARN,
|
||||
(const char * const *) CONF_PATHS_STRV(""), &directories, &n_directories);
|
||||
|
||||
@ -556,6 +556,49 @@ TEST(depth) {
|
||||
fputs("\n", stdout);
|
||||
}
|
||||
|
||||
static char *prepare_nested_json(const char *open, unsigned depth) {
|
||||
char *s, *p;
|
||||
size_t olen;
|
||||
|
||||
assert_se(open);
|
||||
|
||||
olen = strlen(open);
|
||||
s = p = new(char, olen * depth + 1);
|
||||
if (!s)
|
||||
return NULL;
|
||||
|
||||
for (unsigned i = 0; i < depth; i++)
|
||||
p = mempcpy(p, open, olen);
|
||||
*p = '\0';
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
TEST(parse_depth) {
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
|
||||
_cleanup_free_ char *s = NULL;
|
||||
|
||||
/* Refuse parsing > DEPTH_MAX (currently 2048) levels of nested arrays */
|
||||
s = prepare_nested_json("[", 2049);
|
||||
ASSERT_ERROR(sd_json_parse(s, 0, &v, NULL, NULL), ELNRNG);
|
||||
s = mfree(s);
|
||||
|
||||
/* Same for nested objects */
|
||||
s = prepare_nested_json("{\"a\":", 2049);
|
||||
ASSERT_ERROR(sd_json_parse(s, 0, &v, NULL, NULL), ELNRNG);
|
||||
s = mfree(s);
|
||||
|
||||
/* <= DEPTH_MAX levels of nested arrays should be refused by EINVAL
|
||||
* later in the parsing process */
|
||||
s = prepare_nested_json("[", 2048);
|
||||
ASSERT_ERROR(sd_json_parse(s, 0, &v, NULL, NULL), EINVAL);
|
||||
s = mfree(s);
|
||||
|
||||
/* And the same for nested objects */
|
||||
s = prepare_nested_json("{\"a\":", 2048);
|
||||
ASSERT_ERROR(sd_json_parse(s, 0, &v, NULL, NULL), EINVAL);
|
||||
}
|
||||
|
||||
TEST(normalize) {
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL, *w = NULL;
|
||||
_cleanup_free_ char *t = NULL;
|
||||
|
||||
@ -1841,7 +1841,7 @@ int udev_rules_load(UdevRules **ret_rules, ResolveNameTiming resolve_name_timing
|
||||
ConfFile **files = NULL;
|
||||
size_t n_files = 0;
|
||||
|
||||
CLEANUP_ARRAY(files, n_files, conf_file_free_many);
|
||||
CLEANUP_ARRAY(files, n_files, conf_file_free_array);
|
||||
|
||||
r = conf_files_list_strv_full(".rules", /* root= */ NULL, CONF_FILES_REGULAR | CONF_FILES_FILTER_MASKED,
|
||||
(const char* const*) directories, &files, &n_files);
|
||||
|
||||
@ -105,7 +105,7 @@ int verb_cat_main(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
ConfFile **files = NULL;
|
||||
size_t n_files = 0;
|
||||
|
||||
CLEANUP_ARRAY(files, n_files, conf_file_free_many);
|
||||
CLEANUP_ARRAY(files, n_files, conf_file_free_array);
|
||||
|
||||
r = search_rules_files(strv_skip(argv, optind), arg_root, &files, &n_files);
|
||||
if (r < 0)
|
||||
|
||||
@ -302,7 +302,7 @@ static int search_rules_file(const char *s, const char *root, ConfFile ***files,
|
||||
ConfFile **f = NULL;
|
||||
size_t n = 0;
|
||||
|
||||
CLEANUP_ARRAY(f, n, conf_file_free_many);
|
||||
CLEANUP_ARRAY(f, n, conf_file_free_array);
|
||||
|
||||
r = conf_files_list_strv_full(".rules", root, CONF_FILES_REGULAR | CONF_FILES_WARN, (const char* const*) STRV_MAKE_CONST(s), &f, &n);
|
||||
if (r < 0)
|
||||
@ -311,7 +311,7 @@ static int search_rules_file(const char *s, const char *root, ConfFile ***files,
|
||||
if (!GREEDY_REALLOC_APPEND(*files, *n_files, f, n))
|
||||
return log_oom();
|
||||
|
||||
f = mfree(f); /* The array elements are owned by 'files'. So, conf_file_free_many() must not be called. */
|
||||
f = mfree(f); /* The array elements are owned by 'files'. So, conf_file_free_array() must not be called. */
|
||||
n = 0;
|
||||
return 0;
|
||||
}
|
||||
@ -321,7 +321,7 @@ int search_rules_files(char * const *a, const char *root, ConfFile ***ret_files,
|
||||
size_t n_files = 0;
|
||||
int r;
|
||||
|
||||
CLEANUP_ARRAY(files, n_files, conf_file_free_many);
|
||||
CLEANUP_ARRAY(files, n_files, conf_file_free_array);
|
||||
|
||||
assert(ret_files);
|
||||
assert(ret_n_files);
|
||||
|
||||
@ -171,7 +171,7 @@ int verb_verify_main(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
ConfFile **files = NULL;
|
||||
size_t n_files = 0;
|
||||
|
||||
CLEANUP_ARRAY(files, n_files, conf_file_free_many);
|
||||
CLEANUP_ARRAY(files, n_files, conf_file_free_array);
|
||||
|
||||
r = search_rules_files(strv_skip(argv, optind), arg_root, &files, &n_files);
|
||||
if (r < 0)
|
||||
|
||||
@ -205,11 +205,6 @@ STATIC_DESTRUCTOR_REGISTER(arg_bind_user, strv_freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_bind_user_shell, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_bind_user_groups, strv_freep);
|
||||
|
||||
static void unref_many_tables(Table* (*tablesp)[]) {
|
||||
for (Table **t = *ASSERT_PTR(tablesp); *t; t++)
|
||||
*t = table_unref(*t);
|
||||
}
|
||||
|
||||
static int help(void) {
|
||||
_cleanup_free_ char *link = NULL;
|
||||
int r;
|
||||
@ -220,7 +215,7 @@ static int help(void) {
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
static const char *groups[] = {
|
||||
static const char* const groups[] = {
|
||||
NULL,
|
||||
"Image",
|
||||
"Host Configuration",
|
||||
@ -234,7 +229,7 @@ static int help(void) {
|
||||
"Credentials",
|
||||
};
|
||||
|
||||
_cleanup_(unref_many_tables) Table* tables[ELEMENTSOF(groups) + 1] = {};
|
||||
_cleanup_(table_unref_many) Table* tables[ELEMENTSOF(groups) + 1] = {};
|
||||
|
||||
for (size_t i = 0; i < ELEMENTSOF(groups); i++) {
|
||||
r = option_parser_get_help_table_group(groups[i], &tables[i]);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user