Compare commits

...

5 Commits

Author SHA1 Message Date
Yu Watanabe 6161b35d5e
Merge pull request #16048 from poettering/conf-parser-mtime
conf-parser: automatically pick up newest mtime when parsing configuration files
2020-06-03 08:25:28 +09:00
Zbigniew Jędrzejewski-Szmek c9e0695675 core: set source_mtime after load dropins
Dropins may specify SourcePath= too, but we would do the stat only
after loading the main fragment, before loading of the drop-ins.

Fixes #13634.
2020-06-02 22:53:55 +02:00
Lennart Poettering 4f9ff96a55 conf-parser: return mtime in config_parse() and friends
This is a follow-up for 9f83091e3c.

Instead of reading the mtime off the configuration files after reading,
let's do so before reading, but with the fd we read the data from. This
is not only cleaner (as it allows us to save one stat()), but also has
the benefit that we'll detect changes that happen while we read the
files.

This also reworks unit file drop-ins to use the common code for
determining drop-in mtime, instead of reading system clock for that.
2020-06-02 19:32:20 +02:00
Lennart Poettering 5aca2e6733 conf-parse: fix pretty bad typo 2020-06-02 19:32:06 +02:00
Lennart Poettering 22ed4a6d9a fs-util: add stat_warn_permissions() that operates on struct stat instead of fd 2020-06-02 19:31:36 +02:00
30 changed files with 294 additions and 205 deletions

View File

@ -353,26 +353,36 @@ int fchmod_opath(int fd, mode_t m) {
return 0; return 0;
} }
int stat_warn_permissions(const char *path, const struct stat *st) {
assert(path);
assert(st);
/* Don't complain if we are reading something that is not a file, for example /dev/null */
if (!S_ISREG(st->st_mode))
return 0;
if (st->st_mode & 0111)
log_warning("Configuration file %s is marked executable. Please remove executable permission bits. Proceeding anyway.", path);
if (st->st_mode & 0002)
log_warning("Configuration file %s is marked world-writable. Please remove world writability permission bits. Proceeding anyway.", path);
if (getpid_cached() == 1 && (st->st_mode & 0044) != 0044)
log_warning("Configuration file %s is marked world-inaccessible. This has no effect as configuration data is accessible via APIs without restrictions. Proceeding anyway.", path);
return 0;
}
int fd_warn_permissions(const char *path, int fd) { int fd_warn_permissions(const char *path, int fd) {
struct stat st; struct stat st;
assert(path);
assert(fd >= 0);
if (fstat(fd, &st) < 0) if (fstat(fd, &st) < 0)
return -errno; return -errno;
/* Don't complain if we are reading something that is not a file, for example /dev/null */ return stat_warn_permissions(path, &st);
if (!S_ISREG(st.st_mode))
return 0;
if (st.st_mode & 0111)
log_warning("Configuration file %s is marked executable. Please remove executable permission bits. Proceeding anyway.", path);
if (st.st_mode & 0002)
log_warning("Configuration file %s is marked world-writable. Please remove world writability permission bits. Proceeding anyway.", path);
if (getpid_cached() == 1 && (st.st_mode & 0044) != 0044)
log_warning("Configuration file %s is marked world-inaccessible. This has no effect as configuration data is accessible via APIs without restrictions. Proceeding anyway.", path);
return 0;
} }
int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) { int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) {

View File

@ -40,6 +40,7 @@ int fchmod_umask(int fd, mode_t mode);
int fchmod_opath(int fd, mode_t m); int fchmod_opath(int fd, mode_t m);
int fd_warn_permissions(const char *path, int fd); int fd_warn_permissions(const char *path, int fd);
int stat_warn_permissions(const char *path, const struct stat *st);
#define laccess(path, mode) faccessat(AT_FDCWD, (path), (mode), AT_SYMLINK_NOFOLLOW) #define laccess(path, mode) faccessat(AT_FDCWD, (path), (mode), AT_SYMLINK_NOFOLLOW)

View File

@ -114,12 +114,13 @@ int unit_load_dropin(Unit *u) {
} }
STRV_FOREACH(f, u->dropin_paths) STRV_FOREACH(f, u->dropin_paths)
(void) config_parse(u->id, *f, NULL, (void) config_parse(
u->id, *f, NULL,
UNIT_VTABLE(u)->sections, UNIT_VTABLE(u)->sections,
config_item_perf_lookup, load_fragment_gperf_lookup, config_item_perf_lookup, load_fragment_gperf_lookup,
0, u); 0,
u,
u->dropin_mtime = now(CLOCK_REALTIME); &u->dropin_mtime);
return 0; return 0;
} }

View File

@ -4804,7 +4804,6 @@ static int merge_by_names(Unit **u, Set *names, const char *id) {
int unit_load_fragment(Unit *u) { int unit_load_fragment(Unit *u) {
const char *fragment; const char *fragment;
_cleanup_set_free_free_ Set *names = NULL; _cleanup_set_free_free_ Set *names = NULL;
struct stat st;
int r; int r;
assert(u); assert(u);
@ -4836,6 +4835,7 @@ int unit_load_fragment(Unit *u) {
if (fragment) { if (fragment) {
/* Open the file, check if this is a mask, otherwise read. */ /* Open the file, check if this is a mask, otherwise read. */
_cleanup_fclose_ FILE *f = NULL; _cleanup_fclose_ FILE *f = NULL;
struct stat st;
/* Try to open the file name. A symlink is OK, for example for linked files or masks. We /* Try to open the file name. A symlink is OK, for example for linked files or masks. We
* expect that all symlinks within the lookup paths have been already resolved, but we don't * expect that all symlinks within the lookup paths have been already resolved, but we don't
@ -4864,7 +4864,9 @@ int unit_load_fragment(Unit *u) {
r = config_parse(u->id, fragment, f, r = config_parse(u->id, fragment, f,
UNIT_VTABLE(u)->sections, UNIT_VTABLE(u)->sections,
config_item_perf_lookup, load_fragment_gperf_lookup, config_item_perf_lookup, load_fragment_gperf_lookup,
CONFIG_PARSE_ALLOW_INCLUDE, u); CONFIG_PARSE_ALLOW_INCLUDE,
u,
NULL);
if (r == -ENOEXEC) if (r == -ENOEXEC)
log_unit_notice_errno(u, r, "Unit configuration has fatal error, unit will not be started."); log_unit_notice_errno(u, r, "Unit configuration has fatal error, unit will not be started.");
if (r < 0) if (r < 0)
@ -4872,13 +4874,6 @@ int unit_load_fragment(Unit *u) {
} }
} }
if (u->source_path) {
if (stat(u->source_path, &st) >= 0)
u->source_mtime = timespec_load(&st.st_mtim);
else
u->source_mtime = 0;
}
/* We do the merge dance here because for some unit types, the unit might have aliases which are not /* We do the merge dance here because for some unit types, the unit might have aliases which are not
* declared in the file system. In particular, this is true (and frequent) for device and swap units. * declared in the file system. In particular, this is true (and frequent) for device and swap units.
*/ */

View File

@ -657,7 +657,13 @@ static int parse_config_file(void) {
CONF_PATHS_NULSTR("systemd/system.conf.d") : CONF_PATHS_NULSTR("systemd/system.conf.d") :
CONF_PATHS_NULSTR("systemd/user.conf.d"); CONF_PATHS_NULSTR("systemd/user.conf.d");
(void) config_parse_many_nulstr(fn, conf_dirs_nulstr, "Manager\0", config_item_table_lookup, items, CONFIG_PARSE_WARN, NULL); (void) config_parse_many_nulstr(
fn, conf_dirs_nulstr,
"Manager\0",
config_item_table_lookup, items,
CONFIG_PARSE_WARN,
NULL,
NULL);
/* Traditionally "0" was used to turn off the default unit timeouts. Fix this up so that we used USEC_INFINITY /* Traditionally "0" was used to turn off the default unit timeouts. Fix this up so that we used USEC_INFINITY
* like everywhere else. */ * like everywhere else. */

View File

@ -1457,7 +1457,20 @@ int unit_load_fragment_and_dropin(Unit *u, bool fragment_required) {
* target unit needlessly. But we cannot be sure which drops-ins have already * target unit needlessly. But we cannot be sure which drops-ins have already
* been loaded and which not, at least without doing complicated book-keeping, * been loaded and which not, at least without doing complicated book-keeping,
* so let's always reread all drop-ins. */ * so let's always reread all drop-ins. */
return unit_load_dropin(unit_follow_merge(u)); r = unit_load_dropin(unit_follow_merge(u));
if (r < 0)
return r;
if (u->source_path) {
struct stat st;
if (stat(u->source_path, &st) >= 0)
u->source_mtime = timespec_load(&st.st_mtim);
else
u->source_mtime = 0;
}
return 0;
} }
void unit_add_to_target_deps_queue(Unit *u) { void unit_add_to_target_deps_queue(Unit *u) {

View File

@ -155,11 +155,14 @@ static int parse_config(void) {
{} {}
}; };
return config_parse_many_nulstr(PKGSYSCONFDIR "/coredump.conf", return config_parse_many_nulstr(
PKGSYSCONFDIR "/coredump.conf",
CONF_PATHS_NULSTR("systemd/coredump.conf.d"), CONF_PATHS_NULSTR("systemd/coredump.conf.d"),
"Coredump\0", "Coredump\0",
config_item_table_lookup, items, config_item_table_lookup, items,
CONFIG_PARSE_WARN, NULL); CONFIG_PARSE_WARN,
NULL,
NULL);
} }
static uint64_t storage_size_max(void) { static uint64_t storage_size_max(void) {

View File

@ -70,10 +70,13 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
name = strjoina("a.", unit_type_to_string(t)); name = strjoina("a.", unit_type_to_string(t));
assert_se(unit_new_for_name(m, unit_vtable[t]->object_size, name, &u) >= 0); assert_se(unit_new_for_name(m, unit_vtable[t]->object_size, name, &u) >= 0);
(void) config_parse(name, name, f, (void) config_parse(
name, name, f,
UNIT_VTABLE(u)->sections, UNIT_VTABLE(u)->sections,
config_item_perf_lookup, load_fragment_gperf_lookup, config_item_perf_lookup, load_fragment_gperf_lookup,
CONFIG_PARSE_ALLOW_INCLUDE, u); CONFIG_PARSE_ALLOW_INCLUDE,
u,
NULL);
g = open_memstream_unlocked(&out, &out_size); g = open_memstream_unlocked(&out, &out_size);
assert_se(g); assert_se(g);

View File

@ -10,11 +10,14 @@ int manager_parse_config_file(Manager *m) {
assert(m); assert(m);
r = config_parse_many_nulstr(PKGSYSCONFDIR "/homed.conf", r = config_parse_many_nulstr(
PKGSYSCONFDIR "/homed.conf",
CONF_PATHS_NULSTR("systemd/homed.conf.d"), CONF_PATHS_NULSTR("systemd/homed.conf.d"),
"Home\0", "Home\0",
config_item_perf_lookup, homed_gperf_lookup, config_item_perf_lookup, homed_gperf_lookup,
CONFIG_PARSE_WARN, m); CONFIG_PARSE_WARN,
m,
NULL);
if (r < 0) if (r < 0)
return r; return r;

View File

@ -762,10 +762,14 @@ static int parse_config(void) {
{} {}
}; };
return config_parse_many_nulstr(PKGSYSCONFDIR "/journal-remote.conf", return config_parse_many_nulstr(
PKGSYSCONFDIR "/journal-remote.conf",
CONF_PATHS_NULSTR("systemd/journal-remote.conf.d"), CONF_PATHS_NULSTR("systemd/journal-remote.conf.d"),
"Remote\0", config_item_table_lookup, items, "Remote\0",
CONFIG_PARSE_WARN, NULL); config_item_table_lookup, items,
CONFIG_PARSE_WARN,
NULL,
NULL);
} }
static int help(void) { static int help(void) {

View File

@ -562,12 +562,17 @@ static int parse_config(void) {
{ "Upload", "ServerKeyFile", config_parse_path_or_ignore, 0, &arg_key }, { "Upload", "ServerKeyFile", config_parse_path_or_ignore, 0, &arg_key },
{ "Upload", "ServerCertificateFile", config_parse_path_or_ignore, 0, &arg_cert }, { "Upload", "ServerCertificateFile", config_parse_path_or_ignore, 0, &arg_cert },
{ "Upload", "TrustedCertificateFile", config_parse_path_or_ignore, 0, &arg_trust }, { "Upload", "TrustedCertificateFile", config_parse_path_or_ignore, 0, &arg_trust },
{}}; {}
};
return config_parse_many_nulstr(PKGSYSCONFDIR "/journal-upload.conf", return config_parse_many_nulstr(
PKGSYSCONFDIR "/journal-upload.conf",
CONF_PATHS_NULSTR("systemd/journal-upload.conf.d"), CONF_PATHS_NULSTR("systemd/journal-upload.conf.d"),
"Upload\0", config_item_table_lookup, items, "Upload\0",
CONFIG_PARSE_WARN, NULL); config_item_table_lookup, items,
CONFIG_PARSE_WARN,
NULL,
NULL);
} }
static int help(void) { static int help(void) {

View File

@ -1633,23 +1633,24 @@ static int server_parse_config_file(Server *s) {
/* If we are running in namespace mode, load the namespace specific configuration file, and nothing else */ /* If we are running in namespace mode, load the namespace specific configuration file, and nothing else */
namespaced = strjoina(PKGSYSCONFDIR "/journald@", s->namespace, ".conf"); namespaced = strjoina(PKGSYSCONFDIR "/journald@", s->namespace, ".conf");
r = config_parse( r = config_parse(NULL,
NULL,
namespaced, NULL, namespaced, NULL,
"Journal\0", "Journal\0",
config_item_perf_lookup, journald_gperf_lookup, config_item_perf_lookup, journald_gperf_lookup,
CONFIG_PARSE_WARN, s); CONFIG_PARSE_WARN, s,
NULL);
if (r < 0) if (r < 0)
return r; return r;
return 0; return 0;
} }
return config_parse_many_nulstr(PKGSYSCONFDIR "/journald.conf", return config_parse_many_nulstr(
PKGSYSCONFDIR "/journald.conf",
CONF_PATHS_NULSTR("systemd/journald.conf.d"), CONF_PATHS_NULSTR("systemd/journald.conf.d"),
"Journal\0", "Journal\0",
config_item_perf_lookup, journald_gperf_lookup, config_item_perf_lookup, journald_gperf_lookup,
CONFIG_PARSE_WARN, s); CONFIG_PARSE_WARN, s, NULL);
} }
static int server_dispatch_sync(sd_event_source *es, usec_t t, void *userdata) { static int server_dispatch_sync(sd_event_source *es, usec_t t, void *userdata) {

View File

@ -69,11 +69,13 @@ void manager_reset_config(Manager *m) {
int manager_parse_config_file(Manager *m) { int manager_parse_config_file(Manager *m) {
assert(m); assert(m);
return config_parse_many_nulstr(PKGSYSCONFDIR "/logind.conf", return config_parse_many_nulstr(
PKGSYSCONFDIR "/logind.conf",
CONF_PATHS_NULSTR("systemd/logind.conf.d"), CONF_PATHS_NULSTR("systemd/logind.conf.d"),
"Login\0", "Login\0",
config_item_perf_lookup, logind_gperf_lookup, config_item_perf_lookup, logind_gperf_lookup,
CONFIG_PARSE_WARN, m); CONFIG_PARSE_WARN, m,
NULL);
} }
int manager_add_device(Manager *m, const char *sysfs, bool master, Device **ret_device) { int manager_add_device(Manager *m, const char *sysfs, bool master, Device **ret_device) {

View File

@ -686,10 +686,13 @@ int netdev_load_one(Manager *manager, const char *filename) {
}; };
dropin_dirname = strjoina(basename(filename), ".d"); dropin_dirname = strjoina(basename(filename), ".d");
r = config_parse_many(filename, NETWORK_DIRS, dropin_dirname, r = config_parse_many(
filename, NETWORK_DIRS, dropin_dirname,
NETDEV_COMMON_SECTIONS NETDEV_OTHER_SECTIONS, NETDEV_COMMON_SECTIONS NETDEV_OTHER_SECTIONS,
config_item_perf_lookup, network_netdev_gperf_lookup, config_item_perf_lookup, network_netdev_gperf_lookup,
CONFIG_PARSE_WARN, netdev_raw, NULL); CONFIG_PARSE_WARN,
netdev_raw,
NULL);
if (r < 0) if (r < 0)
return r; return r;
@ -726,10 +729,12 @@ int netdev_load_one(Manager *manager, const char *filename) {
if (NETDEV_VTABLE(netdev)->init) if (NETDEV_VTABLE(netdev)->init)
NETDEV_VTABLE(netdev)->init(netdev); NETDEV_VTABLE(netdev)->init(netdev);
r = config_parse_many(filename, NETWORK_DIRS, dropin_dirname, r = config_parse_many(
filename, NETWORK_DIRS, dropin_dirname,
NETDEV_VTABLE(netdev)->sections, NETDEV_VTABLE(netdev)->sections,
config_item_perf_lookup, network_netdev_gperf_lookup, config_item_perf_lookup, network_netdev_gperf_lookup,
CONFIG_PARSE_WARN, netdev, NULL); CONFIG_PARSE_WARN,
netdev, NULL);
if (r < 0) if (r < 0)
return r; return r;

View File

@ -23,11 +23,15 @@ int manager_parse_config_file(Manager *m) {
assert(m); assert(m);
r = config_parse_many_nulstr(PKGSYSCONFDIR "/networkd.conf", r = config_parse_many_nulstr(
PKGSYSCONFDIR "/networkd.conf",
CONF_PATHS_NULSTR("systemd/networkd.conf.d"), CONF_PATHS_NULSTR("systemd/networkd.conf.d"),
"Network\0DHCP\0", "Network\0"
"DHCP\0",
config_item_perf_lookup, networkd_gperf_lookup, config_item_perf_lookup, networkd_gperf_lookup,
CONFIG_PARSE_WARN, m); CONFIG_PARSE_WARN,
m,
NULL);
if (r < 0) if (r < 0)
return r; return r;

View File

@ -335,7 +335,6 @@ int network_verify(Network *network) {
int network_load_one(Manager *manager, OrderedHashmap **networks, const char *filename) { int network_load_one(Manager *manager, OrderedHashmap **networks, const char *filename) {
_cleanup_free_ char *fname = NULL, *name = NULL; _cleanup_free_ char *fname = NULL, *name = NULL;
_cleanup_(network_unrefp) Network *network = NULL; _cleanup_(network_unrefp) Network *network = NULL;
_cleanup_strv_free_ char **dropins = NULL;
_cleanup_fclose_ FILE *file = NULL; _cleanup_fclose_ FILE *file = NULL;
const char *dropin_dirname; const char *dropin_dirname;
char *d; char *d;
@ -477,7 +476,8 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
.ip_service_type = -1, .ip_service_type = -1,
}; };
r = config_parse_many(filename, NETWORK_DIRS, dropin_dirname, r = config_parse_many(
filename, NETWORK_DIRS, dropin_dirname,
"Match\0" "Match\0"
"Link\0" "Link\0"
"Network\0" "Network\0"
@ -524,7 +524,9 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
"TokenBucketFilter\0" "TokenBucketFilter\0"
"TrivialLinkEqualizer\0", "TrivialLinkEqualizer\0",
config_item_perf_lookup, network_network_gperf_lookup, config_item_perf_lookup, network_network_gperf_lookup,
CONFIG_PARSE_WARN, network, &dropins); CONFIG_PARSE_WARN,
network,
&network->timestamp);
if (r < 0) if (r < 0)
return r; return r;
@ -539,24 +541,6 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
log_warning_errno(r, "%s: Failed to add default route on device, ignoring: %m", log_warning_errno(r, "%s: Failed to add default route on device, ignoring: %m",
network->filename); network->filename);
struct stat stats;
if (stat(filename, &stats) >= 0)
network->timestamp = timespec_load(&stats.st_mtim);
char **f;
STRV_FOREACH(f, dropins) {
usec_t t;
if (stat(*f, &stats) < 0) {
network->timestamp = 0;
break;
}
t = timespec_load(&stats.st_mtim);
if (t > network->timestamp)
network->timestamp = t;
}
if (network_verify(network) < 0) if (network_verify(network) < 0)
/* Ignore .network files that do not match the conditions. */ /* Ignore .network files that do not match the conditions. */
return 0; return 0;

View File

@ -78,7 +78,7 @@ int settings_load(FILE *f, const char *path, Settings **ret) {
"Files\0", "Files\0",
config_item_perf_lookup, nspawn_gperf_lookup, config_item_perf_lookup, nspawn_gperf_lookup,
CONFIG_PARSE_WARN, CONFIG_PARSE_WARN,
s); s, NULL);
if (r < 0) if (r < 0)
return r; return r;

View File

@ -976,7 +976,12 @@ static int partition_read_definition(Partition *p, const char *path) {
}; };
int r; int r;
r = config_parse(NULL, path, NULL, "Partition\0", config_item_table_lookup, table, CONFIG_PARSE_WARN, p); r = config_parse(NULL, path, NULL,
"Partition\0",
config_item_table_lookup, table,
CONFIG_PARSE_WARN,
p,
NULL);
if (r < 0) if (r < 0)
return r; return r;

View File

@ -78,11 +78,14 @@ static int parse_config(void) {
{} {}
}; };
return config_parse_many_nulstr(PKGSYSCONFDIR "/pstore.conf", return config_parse_many_nulstr(
PKGSYSCONFDIR "/pstore.conf",
CONF_PATHS_NULSTR("systemd/pstore.conf.d"), CONF_PATHS_NULSTR("systemd/pstore.conf.d"),
"PStore\0", "PStore\0",
config_item_table_lookup, items, config_item_table_lookup, items,
CONFIG_PARSE_WARN, NULL); CONFIG_PARSE_WARN,
NULL,
NULL);
} }
/* File list handling - PStoreEntry is the struct and /* File list handling - PStoreEntry is the struct and

View File

@ -381,11 +381,14 @@ int manager_parse_config_file(Manager *m) {
assert(m); assert(m);
r = config_parse_many_nulstr(PKGSYSCONFDIR "/resolved.conf", r = config_parse_many_nulstr(
PKGSYSCONFDIR "/resolved.conf",
CONF_PATHS_NULSTR("systemd/resolved.conf.d"), CONF_PATHS_NULSTR("systemd/resolved.conf.d"),
"Resolve\0", "Resolve\0",
config_item_perf_lookup, resolved_gperf_lookup, config_item_perf_lookup, resolved_gperf_lookup,
CONFIG_PARSE_WARN, m); CONFIG_PARSE_WARN,
m,
NULL);
if (r < 0) if (r < 0)
return r; return r;

View File

@ -86,10 +86,13 @@ static int dnssd_service_load(Manager *manager, const char *filename) {
dropin_dirname = strjoina(service->name, ".dnssd.d"); dropin_dirname = strjoina(service->name, ".dnssd.d");
r = config_parse_many(filename, DNSSD_SERVICE_DIRS, dropin_dirname, r = config_parse_many(
filename, DNSSD_SERVICE_DIRS, dropin_dirname,
"Service\0", "Service\0",
config_item_perf_lookup, resolved_dnssd_gperf_lookup, config_item_perf_lookup, resolved_dnssd_gperf_lookup,
false, service, NULL); CONFIG_PARSE_WARN,
service,
NULL);
if (r < 0) if (r < 0)
return r; return r;

View File

@ -199,7 +199,7 @@ static int parse_line(
if (!fn) if (!fn)
return -ENOMEM; return -ENOMEM;
return config_parse(unit, fn, NULL, sections, lookup, table, flags, userdata); return config_parse(unit, fn, NULL, sections, lookup, table, flags, userdata, NULL);
} }
if (!utf8_is_valid(l)) if (!utf8_is_valid(l))
@ -289,13 +289,15 @@ int config_parse(const char *unit,
ConfigItemLookup lookup, ConfigItemLookup lookup,
const void *table, const void *table,
ConfigParseFlags flags, ConfigParseFlags flags,
void *userdata) { void *userdata,
usec_t *ret_mtime) {
_cleanup_free_ char *section = NULL, *continuation = NULL; _cleanup_free_ char *section = NULL, *continuation = NULL;
_cleanup_fclose_ FILE *ours = NULL; _cleanup_fclose_ FILE *ours = NULL;
unsigned line = 0, section_line = 0; unsigned line = 0, section_line = 0;
bool section_ignored = false, bom_seen = false; bool section_ignored = false, bom_seen = false;
int r, fd; int r, fd;
usec_t mtime;
assert(filename); assert(filename);
assert(lookup); assert(lookup);
@ -313,8 +315,16 @@ int config_parse(const char *unit,
} }
fd = fileno(f); fd = fileno(f);
if (fd >= 0) /* stream might not have an fd, let's be careful hence */ if (fd >= 0) { /* stream might not have an fd, let's be careful hence */
fd_warn_permissions(filename, fd); struct stat st;
if (fstat(fd, &st) < 0)
return log_full_errno(FLAGS_SET(flags, CONFIG_PARSE_WARN) ? LOG_ERR : LOG_DEBUG, errno,
"Failed to fstat(%s): %m", filename);
(void) stat_warn_permissions(filename, &st);
mtime = timespec_load(&st.st_mtim);
}
for (;;) { for (;;) {
_cleanup_free_ char *buf = NULL; _cleanup_free_ char *buf = NULL;
@ -331,7 +341,7 @@ int config_parse(const char *unit,
return r; return r;
} }
if (r < 0) { if (r < 0) {
if (CONFIG_PARSE_WARN) if (FLAGS_SET(flags, CONFIG_PARSE_WARN))
log_error_errno(r, "%s:%u: Error while reading configuration file: %m", filename, line); log_error_errno(r, "%s:%u: Error while reading configuration file: %m", filename, line);
return r; return r;
@ -434,6 +444,9 @@ int config_parse(const char *unit,
} }
} }
if (ret_mtime)
*ret_mtime = mtime;
return 0; return 0;
} }
@ -444,23 +457,32 @@ static int config_parse_many_files(
ConfigItemLookup lookup, ConfigItemLookup lookup,
const void *table, const void *table,
ConfigParseFlags flags, ConfigParseFlags flags,
void *userdata) { void *userdata,
usec_t *ret_mtime) {
usec_t mtime = 0;
char **fn; char **fn;
int r; int r;
if (conf_file) { if (conf_file) {
r = config_parse(NULL, conf_file, NULL, sections, lookup, table, flags, userdata); r = config_parse(NULL, conf_file, NULL, sections, lookup, table, flags, userdata, &mtime);
if (r < 0) if (r < 0)
return r; return r;
} }
STRV_FOREACH(fn, files) { STRV_FOREACH(fn, files) {
r = config_parse(NULL, *fn, NULL, sections, lookup, table, flags, userdata); usec_t t;
r = config_parse(NULL, *fn, NULL, sections, lookup, table, flags, userdata, &t);
if (r < 0) if (r < 0)
return r; return r;
if (t > mtime) /* Find the newest */
mtime = t;
} }
if (ret_mtime)
*ret_mtime = mtime;
return 0; return 0;
} }
@ -472,7 +494,8 @@ int config_parse_many_nulstr(
ConfigItemLookup lookup, ConfigItemLookup lookup,
const void *table, const void *table,
ConfigParseFlags flags, ConfigParseFlags flags,
void *userdata) { void *userdata,
usec_t *ret_mtime) {
_cleanup_strv_free_ char **files = NULL; _cleanup_strv_free_ char **files = NULL;
int r; int r;
@ -481,7 +504,7 @@ int config_parse_many_nulstr(
if (r < 0) if (r < 0)
return r; return r;
return config_parse_many_files(conf_file, files, sections, lookup, table, flags, userdata); return config_parse_many_files(conf_file, files, sections, lookup, table, flags, userdata, ret_mtime);
} }
/* Parse each config file in the directories specified as strv. */ /* Parse each config file in the directories specified as strv. */
@ -494,7 +517,7 @@ int config_parse_many(
const void *table, const void *table,
ConfigParseFlags flags, ConfigParseFlags flags,
void *userdata, void *userdata,
char ***ret_dropins) { usec_t *ret_mtime) {
_cleanup_strv_free_ char **dropin_dirs = NULL; _cleanup_strv_free_ char **dropin_dirs = NULL;
_cleanup_strv_free_ char **files = NULL; _cleanup_strv_free_ char **files = NULL;
@ -510,14 +533,7 @@ int config_parse_many(
if (r < 0) if (r < 0)
return r; return r;
r = config_parse_many_files(conf_file, files, sections, lookup, table, flags, userdata); return config_parse_many_files(conf_file, files, sections, lookup, table, flags, userdata, ret_mtime);
if (r < 0)
return r;
if (ret_dropins)
*ret_dropins = TAKE_PTR(files);
return 0;
} }
#define DEFINE_PARSER(type, vartype, conv_func) \ #define DEFINE_PARSER(type, vartype, conv_func) \

View File

@ -10,6 +10,7 @@
#include "alloc-util.h" #include "alloc-util.h"
#include "log.h" #include "log.h"
#include "macro.h" #include "macro.h"
#include "time-util.h"
/* An abstract parser for simple, line based, shallow configuration files consisting of variable assignments only. */ /* An abstract parser for simple, line based, shallow configuration files consisting of variable assignments only. */
@ -88,7 +89,8 @@ int config_parse(
ConfigItemLookup lookup, ConfigItemLookup lookup,
const void *table, const void *table,
ConfigParseFlags flags, ConfigParseFlags flags,
void *userdata); void *userdata,
usec_t *ret_mtime); /* possibly NULL */
int config_parse_many_nulstr( int config_parse_many_nulstr(
const char *conf_file, /* possibly NULL */ const char *conf_file, /* possibly NULL */
@ -97,7 +99,8 @@ int config_parse_many_nulstr(
ConfigItemLookup lookup, ConfigItemLookup lookup,
const void *table, const void *table,
ConfigParseFlags flags, ConfigParseFlags flags,
void *userdata); void *userdata,
usec_t *ret_mtime); /* possibly NULL */
int config_parse_many( int config_parse_many(
const char *conf_file, /* possibly NULL */ const char *conf_file, /* possibly NULL */
@ -108,7 +111,7 @@ int config_parse_many(
const void *table, const void *table,
ConfigParseFlags flags, ConfigParseFlags flags,
void *userdata, void *userdata,
char ***ret_dropins); /* possibly NULL */ usec_t *ret_mtime); /* possibly NULL */
CONFIG_PARSER_PROTOTYPE(config_parse_int); CONFIG_PARSER_PROTOTYPE(config_parse_int);
CONFIG_PARSER_PROTOTYPE(config_parse_unsigned); CONFIG_PARSER_PROTOTYPE(config_parse_unsigned);

View File

@ -1304,7 +1304,8 @@ static int unit_file_load(
"-Target\0" "-Target\0"
"-Timer\0", "-Timer\0",
config_item_table_lookup, items, config_item_table_lookup, items,
CONFIG_PARSE_ALLOW_INCLUDE, info); CONFIG_PARSE_ALLOW_INCLUDE, info,
NULL);
if (r < 0) if (r < 0)
return log_debug_errno(r, "Failed to parse %s: %m", info->name); return log_debug_errno(r, "Failed to parse %s: %m", info->name);

View File

@ -59,10 +59,14 @@ int parse_sleep_config(SleepConfig **ret_sleep_config) {
{} {}
}; };
(void) config_parse_many_nulstr(PKGSYSCONFDIR "/sleep.conf", (void) config_parse_many_nulstr(
PKGSYSCONFDIR "/sleep.conf",
CONF_PATHS_NULSTR("systemd/sleep.conf.d"), CONF_PATHS_NULSTR("systemd/sleep.conf.d"),
"Sleep\0", config_item_table_lookup, items, "Sleep\0",
CONFIG_PARSE_WARN, NULL); config_item_table_lookup, items,
CONFIG_PARSE_WARN,
NULL,
NULL);
/* use default values unless set */ /* use default values unless set */
sc->allow_suspend = allow_suspend != 0; sc->allow_suspend = allow_suspend != 0;

View File

@ -335,13 +335,17 @@ static void test_config_parse(unsigned i, const char *s) {
ConfigItemLookup lookup, ConfigItemLookup lookup,
const void *table, const void *table,
ConfigParseFlags flags, ConfigParseFlags flags,
void *userdata) void *userdata,
usec_t *ret_mtime)
*/ */
r = config_parse(NULL, name, f, r = config_parse(NULL, name, f,
"Section\0-NoWarnSection\0", "Section\0"
"-NoWarnSection\0",
config_item_table_lookup, items, config_item_table_lookup, items,
CONFIG_PARSE_WARN, NULL); CONFIG_PARSE_WARN,
NULL,
NULL);
switch (i) { switch (i) {
case 0 ... 4: case 0 ... 4:

View File

@ -102,11 +102,14 @@ int manager_parse_config_file(Manager *m) {
assert(m); assert(m);
r = config_parse_many_nulstr(PKGSYSCONFDIR "/timesyncd.conf", r = config_parse_many_nulstr(
PKGSYSCONFDIR "/timesyncd.conf",
CONF_PATHS_NULSTR("systemd/timesyncd.conf.d"), CONF_PATHS_NULSTR("systemd/timesyncd.conf.d"),
"Time\0", "Time\0",
config_item_perf_lookup, timesyncd_gperf_lookup, config_item_perf_lookup, timesyncd_gperf_lookup,
CONFIG_PARSE_WARN, m); CONFIG_PARSE_WARN,
m,
NULL);
if (r < 0) if (r < 0)
return r; return r;

View File

@ -192,7 +192,9 @@ static int process_one_password_file(const char *filename) {
r = config_parse(NULL, filename, NULL, r = config_parse(NULL, filename, NULL,
NULL, NULL,
config_item_table_lookup, items, config_item_table_lookup, items,
CONFIG_PARSE_RELAXED|CONFIG_PARSE_WARN, NULL); CONFIG_PARSE_RELAXED|CONFIG_PARSE_WARN,
NULL,
NULL);
if (r < 0) if (r < 0)
return r; return r;

View File

@ -161,7 +161,8 @@ int link_load_one(link_config_ctx *ctx, const char *filename) {
r = config_parse(NULL, filename, file, r = config_parse(NULL, filename, file,
"Match\0Link\0", "Match\0Link\0",
config_item_perf_lookup, link_config_gperf_lookup, config_item_perf_lookup, link_config_gperf_lookup,
CONFIG_PARSE_WARN, link); CONFIG_PARSE_WARN, link,
NULL);
if (r < 0) if (r < 0)
return r; return r;

View File

@ -319,7 +319,8 @@ XdgAutostartService *xdg_autostart_service_parse_desktop(const char *path) {
r = config_parse(NULL, service->path, NULL, r = config_parse(NULL, service->path, NULL,
"Desktop Entry\0", "Desktop Entry\0",
xdg_config_item_table_lookup, items, xdg_config_item_table_lookup, items,
CONFIG_PARSE_WARN, service); CONFIG_PARSE_WARN, service,
NULL);
/* If parsing failed, only hide the file so it will still mask others. */ /* If parsing failed, only hide the file so it will still mask others. */
if (r < 0) { if (r < 0) {
log_warning_errno(r, "Failed to parse %s, ignoring it", service->path); log_warning_errno(r, "Failed to parse %s, ignoring it", service->path);