summaryrefslogtreecommitdiff
path: root/src/basic
diff options
context:
space:
mode:
Diffstat (limited to 'src/basic')
-rw-r--r--src/basic/alloc-util.h15
-rw-r--r--src/basic/env-util.h1
-rw-r--r--src/basic/errno-list.c2
-rw-r--r--src/basic/exec-util.c16
-rw-r--r--src/basic/exec-util.h3
-rw-r--r--src/basic/fd-util.c24
-rw-r--r--src/basic/fd-util.h8
-rw-r--r--src/basic/fileio.c8
-rw-r--r--src/basic/fileio.h2
-rw-r--r--src/basic/format-table.c14
-rw-r--r--src/basic/fs-util.c2
-rw-r--r--src/basic/hashmap.c17
-rw-r--r--src/basic/hexdecoct.c6
-rw-r--r--src/basic/mempool.c18
-rw-r--r--src/basic/mempool.h5
-rw-r--r--src/basic/meson.build3
-rw-r--r--src/basic/missing.h11
-rw-r--r--src/basic/parse-util.c7
-rw-r--r--src/basic/proc-cmdline.c25
-rw-r--r--src/basic/proc-cmdline.h1
-rw-r--r--src/basic/process-util.c40
-rw-r--r--src/basic/process-util.h6
-rw-r--r--src/basic/raw-clone.h2
-rw-r--r--src/basic/selinux-util.c4
-rw-r--r--src/basic/sigbus.c4
-rw-r--r--src/basic/socket-util.c59
-rw-r--r--src/basic/socket-util.h7
-rw-r--r--src/basic/string-util.c28
-rw-r--r--src/basic/string-util.h27
-rw-r--r--src/basic/terminal-util.c16
-rw-r--r--src/basic/time-util.c19
-rw-r--r--src/basic/unit-def.c2
-rw-r--r--src/basic/unit-def.h2
-rw-r--r--src/basic/unit-name.c4
-rw-r--r--src/basic/unit-name.h6
-rw-r--r--src/basic/user-util.h2
-rw-r--r--src/basic/util.c19
-rw-r--r--src/basic/util.h2
-rw-r--r--src/basic/virt.c38
-rw-r--r--src/basic/xattr-util.c1
-rw-r--r--src/basic/xml.c238
-rw-r--r--src/basic/xml.h14
42 files changed, 580 insertions, 148 deletions
diff --git a/src/basic/alloc-util.h b/src/basic/alloc-util.h
index ebe42889e..2a6deb12c 100644
--- a/src/basic/alloc-util.h
+++ b/src/basic/alloc-util.h
@@ -46,6 +46,21 @@ static inline void *mfree(void *memory) {
void* memdup(const void *p, size_t l) _alloc_(2);
void* memdup_suffix0(const void *p, size_t l) _alloc_(2);
+#define memdupa(p, l) \
+ ({ \
+ void *_q_; \
+ _q_ = alloca(l); \
+ memcpy(_q_, p, l); \
+ })
+
+#define memdupa_suffix0(p, l) \
+ ({ \
+ void *_q_; \
+ _q_ = alloca(l + 1); \
+ ((uint8_t*) _q_)[l] = 0; \
+ memcpy(_q_, p, l); \
+ })
+
static inline void freep(void *p) {
free(*(void**) p);
}
diff --git a/src/basic/env-util.h b/src/basic/env-util.h
index da76e60ca..486056a10 100644
--- a/src/basic/env-util.h
+++ b/src/basic/env-util.h
@@ -4,6 +4,7 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
+//#include <string.h>
#include "macro.h"
diff --git a/src/basic/errno-list.c b/src/basic/errno-list.c
index 0549fcead..1b72bbf3a 100644
--- a/src/basic/errno-list.c
+++ b/src/basic/errno-list.c
@@ -35,5 +35,3 @@ int errno_from_name(const char *name) {
assert(sc->id > 0);
return sc->id;
}
-#if 0 /// UNNEEDED by elogind
-#endif // 0
diff --git a/src/basic/exec-util.c b/src/basic/exec-util.c
index 7896f9369..1d45924e4 100644
--- a/src/basic/exec-util.c
+++ b/src/basic/exec-util.c
@@ -71,11 +71,12 @@ static int do_execute(
gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
void* const callback_args[_STDOUT_CONSUME_MAX],
int output_fd,
- char *argv[]) {
+ char *argv[],
+ char *envp[]) {
_cleanup_hashmap_free_free_ Hashmap *pids = NULL;
_cleanup_strv_free_ char **paths = NULL;
- char **path;
+ char **path, **e;
int r;
/* We fork this all off from a child process so that we can somewhat cleanly make
@@ -86,7 +87,7 @@ static int do_execute(
r = conf_files_list_strv(&paths, NULL, NULL, CONF_FILES_EXECUTABLE|CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED, (const char* const*) directories);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to enumerate executables: %m");
if (!callbacks) {
pids = hashmap_new(NULL);
@@ -100,6 +101,10 @@ static int do_execute(
if (timeout != USEC_INFINITY)
alarm(DIV_ROUND_UP(timeout, USEC_PER_SEC));
+ STRV_FOREACH(e, envp)
+ if (putenv(*e) != 0)
+ return log_error_errno(errno, "Failed to set environment variable: %m");
+
STRV_FOREACH(path, paths) {
_cleanup_free_ char *t = NULL;
_cleanup_close_ int fd = -1;
@@ -170,7 +175,8 @@ int execute_directories(
usec_t timeout,
gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
void* const callback_args[_STDOUT_CONSUME_MAX],
- char *argv[]) {
+ char *argv[],
+ char *envp[]) {
char **dirs = (char**) directories;
_cleanup_close_ int fd = -1;
@@ -201,7 +207,7 @@ int execute_directories(
if (r < 0)
return r;
if (r == 0) {
- r = do_execute(dirs, timeout, callbacks, callback_args, fd, argv);
+ r = do_execute(dirs, timeout, callbacks, callback_args, fd, argv, envp);
_exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
}
diff --git a/src/basic/exec-util.h b/src/basic/exec-util.h
index f81019f86..2c745d501 100644
--- a/src/basic/exec-util.h
+++ b/src/basic/exec-util.h
@@ -18,7 +18,8 @@ int execute_directories(
usec_t timeout,
gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
void* const callback_args[_STDOUT_CONSUME_MAX],
- char *argv[]);
+ char *argv[],
+ char *envp[]);
#if 0 /// UNNEEDED by elogind
extern const gather_stdout_callback_t gather_environment[_STDOUT_CONSUME_MAX];
diff --git a/src/basic/fd-util.c b/src/basic/fd-util.c
index 122b59a68..229b4a899 100644
--- a/src/basic/fd-util.c
+++ b/src/basic/fd-util.c
@@ -357,22 +357,22 @@ bool fdname_is_valid(const char *s) {
#endif // 0
int fd_get_path(int fd, char **ret) {
- _cleanup_close_ int dir = -1;
- char fdname[DECIMAL_STR_MAX(int)];
+ char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
int r;
- dir = open("/proc/self/fd/", O_CLOEXEC | O_DIRECTORY | O_PATH);
- if (dir < 0)
- /* /proc is not available or not set up properly, we're most likely
- * in some chroot environment. */
- return errno == ENOENT ? -EOPNOTSUPP : -errno;
+ xsprintf(procfs_path, "/proc/self/fd/%i", fd);
+ r = readlink_malloc(procfs_path, ret);
+ if (r == -ENOENT) {
+ /* ENOENT can mean two things: that the fd does not exist or that /proc is not mounted. Let's make
+ * things debuggable and distuingish the two. */
- xsprintf(fdname, "%i", fd);
+ if (access("/proc/self/fd/", F_OK) < 0)
+ /* /proc is not available or not set up properly, we're most likely in some chroot
+ * environment. */
+ return errno == ENOENT ? -EOPNOTSUPP : -errno;
- r = readlinkat_malloc(dir, fdname, ret);
- if (r == -ENOENT)
- /* If the file doesn't exist the fd is invalid */
- return -EBADF;
+ return -EBADF; /* The directory exists, hence it's the fd that doesn't. */
+ }
return r;
}
diff --git a/src/basic/fd-util.h b/src/basic/fd-util.h
index 2e5616cb7..b2d5b11a9 100644
--- a/src/basic/fd-util.h
+++ b/src/basic/fd-util.h
@@ -84,8 +84,12 @@ int fd_duplicate_data_fd(int fd);
#endif // 0
/* Hint: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5 */
-#define ERRNO_IS_DISCONNECT(r) \
- IN_SET(r, ENOTCONN, ECONNRESET, ECONNREFUSED, ECONNABORTED, EPIPE, ENETUNREACH)
+/* The kernel sends e.g., EHOSTUNREACH or ENONET to userspace in some ICMP error cases.
+ * See the icmp_err_convert[] in net/ipv4/icmp.c in the kernel sources */
+#define ERRNO_IS_DISCONNECT(r) \
+ IN_SET(r, \
+ ENOTCONN, ECONNRESET, ECONNREFUSED, ECONNABORTED, EPIPE, \
+ ENETUNREACH, EHOSTUNREACH, ENOPROTOOPT, EHOSTDOWN, ENONET)
/* Resource exhaustion, could be our fault or general system trouble */
#define ERRNO_IS_RESOURCE(r) \
diff --git a/src/basic/fileio.c b/src/basic/fileio.c
index 7b1014b1d..41c38197e 100644
--- a/src/basic/fileio.c
+++ b/src/basic/fileio.c
@@ -780,7 +780,6 @@ int load_env_file(FILE *f, const char *fname, const char *newline, char ***rl) {
*rl = m;
return 0;
}
-#endif // 0
static int load_env_file_push_pairs(
const char *filename, unsigned line,
@@ -830,7 +829,6 @@ int load_env_file_pairs(FILE *f, const char *fname, const char *newline, char **
*rl = m;
return 0;
}
-#if 0 /// UNNEEDED by elogind
static int merge_env_file_push(
const char *filename, unsigned line,
@@ -1499,6 +1497,7 @@ int open_serialization_fd(const char *ident) {
#if 0 /// UNNEEDED by elogind
int link_tmpfile(int fd, const char *path, const char *target) {
+ int r;
assert(fd >= 0);
assert(target);
@@ -1511,8 +1510,9 @@ int link_tmpfile(int fd, const char *path, const char *target) {
* operation currently (renameat2() does), and there is no nice way to emulate this. */
if (path) {
- if (rename_noreplace(AT_FDCWD, path, AT_FDCWD, target) < 0)
- return -errno;
+ r = rename_noreplace(AT_FDCWD, path, AT_FDCWD, target);
+ if (r < 0)
+ return r;
} else {
char proc_fd_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1];
diff --git a/src/basic/fileio.h b/src/basic/fileio.h
index eac22ff76..117e8c488 100644
--- a/src/basic/fileio.h
+++ b/src/basic/fileio.h
@@ -45,8 +45,8 @@ int parse_env_filev(FILE *f, const char *fname, const char *separator, va_list a
int parse_env_file(FILE *f, const char *fname, const char *separator, ...) _sentinel_;
#if 0 /// UNNEEDED by elogind
int load_env_file(FILE *f, const char *fname, const char *separator, char ***l);
-#endif // 0
int load_env_file_pairs(FILE *f, const char *fname, const char *separator, char ***l);
+#endif // 0
#if 0 /// UNNEEDED by elogind
int merge_env_file(char ***env, FILE *f, const char *fname);
diff --git a/src/basic/format-table.c b/src/basic/format-table.c
index 00473a2e8..d0305207e 100644
--- a/src/basic/format-table.c
+++ b/src/basic/format-table.c
@@ -36,7 +36,7 @@
that. The first row is always the header row. If header display is turned off we simply skip outputting the first
row. Also, when sorting rows we always leave the first row where it is, as the header shouldn't move.
- - Note because there's no row and no column object some properties that might be approproate as row/column properties
+ - Note because there's no row and no column object some properties that might be appropriate as row/column properties
are exposed as cell properties instead. For example, the "weight" of a column (which is used to determine where to
add/remove space preferable when expanding/compressing tables horizontally) is actually made the "weight" of a
cell. Given that we usually need it per-column though we will calculate the average across every cell of the column
@@ -73,11 +73,11 @@ typedef struct TableData {
} TableData;
static size_t TABLE_CELL_TO_INDEX(TableCell *cell) {
- unsigned i;
+ size_t i;
assert(cell);
- i = PTR_TO_UINT(cell);
+ i = PTR_TO_SIZE(cell);
assert(i > 0);
return i-1;
@@ -85,7 +85,7 @@ static size_t TABLE_CELL_TO_INDEX(TableCell *cell) {
static TableCell* TABLE_INDEX_TO_CELL(size_t index) {
assert(index != (size_t) -1);
- return UINT_TO_PTR((unsigned) (index + 1));
+ return SIZE_TO_PTR(index + 1);
}
struct Table {
@@ -1140,14 +1140,12 @@ int table_print(Table *t, FILE *f) {
assert(weight_sum >= column_weight[j]);
weight_sum -= column_weight[j];
- if (restart)
+ if (restart && !finalize)
break;
}
- if (finalize) {
- assert(!restart);
+ if (finalize)
break;
- }
if (!restart)
finalize = true;
diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c
index 562fe8277..0518c2012 100644
--- a/src/basic/fs-util.c
+++ b/src/basic/fs-util.c
@@ -134,7 +134,7 @@ int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char
#endif // 0
int readlinkat_malloc(int fd, const char *p, char **ret) {
- size_t l = 100;
+ size_t l = FILENAME_MAX+1;
int r;
assert(p);
diff --git a/src/basic/hashmap.c b/src/basic/hashmap.c
index 15c8c8816..40ae2c384 100644
--- a/src/basic/hashmap.c
+++ b/src/basic/hashmap.c
@@ -6,8 +6,8 @@
#include <string.h>
#include "alloc-util.h"
-#include "hashmap.h"
#include "fileio.h"
+#include "hashmap.h"
#include "macro.h"
#include "mempool.h"
#include "process-util.h"
@@ -769,17 +769,16 @@ static void reset_direct_storage(HashmapBase *h) {
static struct HashmapBase *hashmap_base_new(const struct hash_ops *hash_ops, enum HashmapType type HASHMAP_DEBUG_PARAMS) {
HashmapBase *h;
const struct hashmap_type_info *hi = &hashmap_type_info[type];
- bool use_pool;
-
- use_pool = is_main_thread();
+ bool up;
- h = use_pool ? mempool_alloc0_tile(hi->mempool) : malloc0(hi->head_size);
+ up = mempool_enabled();
+ h = up ? mempool_alloc0_tile(hi->mempool) : malloc0(hi->head_size);
if (!h)
return NULL;
h->type = type;
- h->from_pool = use_pool;
+ h->from_pool = up;
h->hash_ops = hash_ops ? hash_ops : &trivial_hash_ops;
if (type == HASHMAP_TYPE_ORDERED) {
@@ -857,9 +856,11 @@ static void hashmap_free_no_clear(HashmapBase *h) {
assert_se(pthread_mutex_unlock(&hashmap_debug_list_mutex) == 0);
#endif
- if (h->from_pool)
+ if (h->from_pool) {
+ /* Ensure that the object didn't get migrated between threads. */
+ assert_se(is_main_thread());
mempool_free_tile(hashmap_type_info[h->type].mempool, h);
- else
+ } else
free(h);
}
diff --git a/src/basic/hexdecoct.c b/src/basic/hexdecoct.c
index 7748e8352..e402ba82e 100644
--- a/src/basic/hexdecoct.c
+++ b/src/basic/hexdecoct.c
@@ -592,8 +592,7 @@ static int base64_append_width(
_cleanup_free_ char *x = NULL;
char *t, *s;
- ssize_t slen, len, avail;
- int line, lines;
+ ssize_t len, slen, avail, line, lines;
len = base64mem(p, l, &x);
if (len <= 0)
@@ -602,6 +601,9 @@ static int base64_append_width(
lines = DIV_ROUND_UP(len, width);
slen = strlen_ptr(sep);
+ if (lines > (SSIZE_MAX - plen - 1 - slen) / (indent + width + 1))
+ return -ENOMEM;
+
t = realloc(*prefix, plen + 1 + slen + (indent + width + 1) * lines);
if (!t)
return -ENOMEM;
diff --git a/src/basic/mempool.c b/src/basic/mempool.c
index a5ec8a102..159c96337 100644
--- a/src/basic/mempool.c
+++ b/src/basic/mempool.c
@@ -3,8 +3,10 @@
#include <stdint.h>
#include <stdlib.h>
+#include "env-util.h"
#include "macro.h"
#include "mempool.h"
+#include "process-util.h"
#include "util.h"
struct pool {
@@ -70,8 +72,21 @@ void mempool_free_tile(struct mempool *mp, void *p) {
mp->freelist = p;
}
-#if VALGRIND
+bool mempool_enabled(void) {
+ static int b = -1;
+
+ if (!is_main_thread())
+ return false;
+ if (!mempool_use_allowed)
+ b = false;
+ if (b < 0)
+ b = getenv_bool("SYSTEMD_MEMPOOL") != 0;
+
+ return b;
+}
+
+#if VALGRIND
void mempool_drop(struct mempool *mp) {
struct pool *p = mp->first_pool;
while (p) {
@@ -81,5 +96,4 @@ void mempool_drop(struct mempool *mp) {
p = n;
}
}
-
#endif
diff --git a/src/basic/mempool.h b/src/basic/mempool.h
index 2a41cb358..0eecca0f9 100644
--- a/src/basic/mempool.h
+++ b/src/basic/mempool.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
+#include <stdbool.h>
#include <stddef.h>
struct pool;
@@ -23,6 +23,9 @@ static struct mempool pool_name = { \
.at_least = alloc_at_least, \
}
+extern const bool mempool_use_allowed;
+bool mempool_enabled(void);
+
#if VALGRIND
void mempool_drop(struct mempool *mp);
#endif
diff --git a/src/basic/meson.build b/src/basic/meson.build
index c199e78c8..9b36a1e0d 100644
--- a/src/basic/meson.build
+++ b/src/basic/meson.build
@@ -122,7 +122,6 @@
# mkdir-label.c
# mkdir.c
# mkdir.h
-# module-util.h
# mount-util.c
# mount-util.h
# nss-util.h
@@ -368,6 +367,8 @@ basic_sources = files('''
virt.h
xattr-util.c
xattr-util.h
+ xml.c
+ xml.h
'''.split())
#endif // 0
diff --git a/src/basic/missing.h b/src/basic/missing.h
index fb1478548..37d1c2a77 100644
--- a/src/basic/missing.h
+++ b/src/basic/missing.h
@@ -15,18 +15,18 @@
#include <linux/neighbour.h>
#include <linux/oom.h>
#include <linux/rtnetlink.h>
-//#include <linux/stat.h>
#include <net/ethernet.h>
#include <stdlib.h>
#include <sys/resource.h>
#include <sys/socket.h>
-//#include <sys/stat.h>
+#include <sys/stat.h>
#include <sys/syscall.h>
#include <uchar.h>
#include <unistd.h>
-/// Additional includes needed by elogind
-#include "musl_missing.h"
+#if WANT_LINUX_STAT_H
+#include <linux/stat.h>
+#endif
#if HAVE_AUDIT
#include <libaudit.h>
@@ -57,6 +57,9 @@ struct sockaddr_vm {
};
#endif /* !HAVE_LINUX_VM_SOCKETS_H */
+/// Additional includes needed by elogind
+#include "musl_missing.h"
+
#ifndef RLIMIT_RTTIME
#define RLIMIT_RTTIME 15
#endif
diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c
index 3935d8a3e..e25e8212d 100644
--- a/src/basic/parse-util.c
+++ b/src/basic/parse-util.c
@@ -642,6 +642,8 @@ int parse_permille_unbounded(const char *p) {
r = safe_atoi(n, &v);
if (r < 0)
return r;
+ if (v < 0)
+ return -ERANGE;
} else {
pc = endswith(p, "%");
if (!pc)
@@ -662,15 +664,14 @@ int parse_permille_unbounded(const char *p) {
r = safe_atoi(n, &v);
if (r < 0)
return r;
+ if (v < 0)
+ return -ERANGE;
if (v > (INT_MAX - q) / 10)
return -ERANGE;
v = v * 10 + q;
}
- if (v < 0)
- return -ERANGE;
-
return v;
}
diff --git a/src/basic/proc-cmdline.c b/src/basic/proc-cmdline.c
index ae3323379..67da8d3cd 100644
--- a/src/basic/proc-cmdline.c
+++ b/src/basic/proc-cmdline.c
@@ -39,18 +39,12 @@ int proc_cmdline(char **ret) {
return read_one_line_file("/proc/cmdline", ret);
}
-int proc_cmdline_parse(proc_cmdline_parse_t parse_item, void *data, unsigned flags) {
-
- _cleanup_free_ char *line = NULL;
+int proc_cmdline_parse_given(const char *line, proc_cmdline_parse_t parse_item, void *data, unsigned flags) {
const char *p;
int r;
assert(parse_item);
- r = proc_cmdline(&line);
- if (r < 0)
- return r;
-
p = line;
for (;;) {
_cleanup_free_ char *word = NULL;
@@ -67,11 +61,15 @@ int proc_cmdline_parse(proc_cmdline_parse_t parse_item, void *data, unsigned fla
/* Filter out arguments that are intended only for the initrd */
q = startswith(word, "rd.");
if (q) {
+#if 0 /// elogind is never in initrd. Doesn't make any sense.
if (!in_initrd())
continue;
if (flags & PROC_CMDLINE_STRIP_RD_PREFIX)
key = q;
+#else
+ continue;
+#endif // 0
}
value = strchr(key, '=');
@@ -86,6 +84,19 @@ int proc_cmdline_parse(proc_cmdline_parse_t parse_item, void *data, unsigned fla
return 0;
}
+int proc_cmdline_parse(proc_cmdline_parse_t parse_item, void *data, unsigned flags) {
+ _cleanup_free_ char *line = NULL;
+ int r;
+
+ assert(parse_item);
+
+ r = proc_cmdline(&line);
+ if (r < 0)
+ return r;
+
+ return proc_cmdline_parse_given(line, parse_item, data, flags);
+}
+
static bool relaxed_equal_char(char a, char b) {
return a == b ||
diff --git a/src/basic/proc-cmdline.h b/src/basic/proc-cmdline.h
index 5cb323f79..de4978f2d 100644
--- a/src/basic/proc-cmdline.h
+++ b/src/basic/proc-cmdline.h
@@ -14,6 +14,7 @@ typedef int (*proc_cmdline_parse_t)(const char *key, const char *value, void *da
int proc_cmdline(char **ret);
+int proc_cmdline_parse_given(const char *line, proc_cmdline_parse_t parse_item, void *data, unsigned flags);
int proc_cmdline_parse(const proc_cmdline_parse_t parse, void *userdata, unsigned flags);
#if 0 /// UNNEEDED by elogind
diff --git a/src/basic/process-util.c b/src/basic/process-util.c
index 77d76958c..3930aa62b 100644
--- a/src/basic/process-util.c
+++ b/src/basic/process-util.c
@@ -308,6 +308,12 @@ int rename_process(const char name[]) {
strncpy(program_invocation_name, name, k);
if (l > k)
truncated = true;
+
+#if 1 /// elogind takes care of situations where the short name points into the long.
+ if ( (program_invocation_short_name >= program_invocation_name)
+ && (program_invocation_short_name < program_invocation_name + k) )
+ program_invocation_short_name = program_invocation_name;
+#endif // 1
}
/* Third step, completely replace the argv[] array the kernel maintains for us. This requires privileges, but
@@ -336,15 +342,33 @@ int rename_process(const char name[]) {
/* Now, let's tell the kernel about this new memory */
if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) {
- log_debug_errno(errno, "PR_SET_MM_ARG_START failed, proceeding without: %m");
- (void) munmap(nn, nn_size);
- goto use_saved_argv;
- }
+ /* HACK: prctl() API is kind of dumb on this point. The existing end address may already be
+ * below the desired start address, in which case the kernel may have kicked this back due
+ * to a range-check failure (see linux/kernel/sys.c:validate_prctl_map() to see this in
+ * action). The proper solution would be to have a prctl() API that could set both start+end
+ * simultaneously, or at least let us query the existing address to anticipate this condition
+ * and respond accordingly. For now, we can only guess at the cause of this failure and try
+ * a workaround--which will briefly expand the arg space to something potentially huge before
+ * resizing it to what we want. */
+ log_debug_errno(errno, "PR_SET_MM_ARG_START failed, attempting PR_SET_MM_ARG_END hack: %m");
+
+ if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0) {
+ log_debug_errno(errno, "PR_SET_MM_ARG_END hack failed, proceeding without: %m");
+ (void) munmap(nn, nn_size);
+ goto use_saved_argv;
+ }
- /* And update the end pointer to the new end, too. If this fails, we don't really know what to do, it's
- * pretty unlikely that we can rollback, hence we'll just accept the failure, and continue. */
- if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0)
- log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m");
+ if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) {
+ log_debug_errno(errno, "PR_SET_MM_ARG_START still failed, proceeding without: %m");
+ goto use_saved_argv;
+ }
+ } else {
+ /* And update the end pointer to the new end, too. If this fails, we don't really know what
+ * to do, it's pretty unlikely that we can rollback, hence we'll just accept the failure,
+ * and continue. */
+ if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0)
+ log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m");
+ }
if (mm)
(void) munmap(mm, mm_size);
diff --git a/src/basic/process-util.h b/src/basic/process-util.h
index f73055218..e7e197517 100644
--- a/src/basic/process-util.h
+++ b/src/basic/process-util.h
@@ -150,12 +150,6 @@ static inline bool pid_is_valid(pid_t p) {
return p > 0;
}
-static inline int sched_policy_to_string_alloc_with_check(int n, char **s) {
- if (!sched_policy_is_valid(n))
- return -EINVAL;
-
- return sched_policy_to_string_alloc(n, s);
-}
#if 0 /// UNNEEDED by elogind
int ioprio_parse_priority(const char *s, int *ret);
diff --git a/src/basic/raw-clone.h b/src/basic/raw-clone.h
index fbd2d8c51..caca34cd0 100644
--- a/src/basic/raw-clone.h
+++ b/src/basic/raw-clone.h
@@ -60,7 +60,7 @@ static inline pid_t raw_clone(unsigned long flags) {
"mov %%o0, %1" :
"=r"(in_child), "=r"(child_pid), "=r"(error) :
"i"(__NR_clone), "r"(flags) :
- "%o1", "%o0", "%g1" "cc" );
+ "%o1", "%o0", "%g1", "cc" );
if (error) {
errno = child_pid;
diff --git a/src/basic/selinux-util.c b/src/basic/selinux-util.c
index 15e62c491..674b2fbf0 100644
--- a/src/basic/selinux-util.c
+++ b/src/basic/selinux-util.c
@@ -16,12 +16,12 @@
#endif
#include "alloc-util.h"
-//#include "fd-util.h"
+#include "fd-util.h"
#include "log.h"
#include "macro.h"
#include "path-util.h"
#include "selinux-util.h"
-//#include "stdio-util.h"
+#include "stdio-util.h"
#include "time-util.h"
#include "util.h"
diff --git a/src/basic/sigbus.c b/src/basic/sigbus.c
index 70afba6bc..d5254eab9 100644
--- a/src/basic/sigbus.c
+++ b/src/basic/sigbus.c
@@ -113,6 +113,10 @@ void sigbus_install(void) {
.sa_flags = SA_SIGINFO,
};
+ /* make sure that sysconf() is not called from a signal handler because
+ * it is not guaranteed to be async-signal-safe since POSIX.1-2008 */
+ (void) page_size();
+
n_installed++;
if (n_installed == 1)
diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c
index 7958e815b..dcc3a5452 100644
--- a/src/basic/socket-util.c
+++ b/src/basic/socket-util.c
@@ -53,6 +53,8 @@ DEFINE_STRING_TABLE_LOOKUP(socket_address_type, int);
int socket_address_parse(SocketAddress *a, const char *s) {
char *e, *n;
unsigned u;
+ _cleanup_free_ char *n = NULL;
+ char *e;
int r;
assert(a);
@@ -71,6 +73,9 @@ int socket_address_parse(SocketAddress *a, const char *s) {
return -EINVAL;
n = strndupa(s+1, e-s-1);
+ n = strndup(s+1, e-s-1);
+ if (!n)
+ return -ENOMEM;
errno = 0;
if (inet_pton(AF_INET6, n, &a->sockaddr.in6.sin6_addr) <= 0)
@@ -134,6 +139,10 @@ int socket_address_parse(SocketAddress *a, const char *s) {
return r;
n = strndupa(cid_start, e - cid_start);
+ n = strndup(cid_start, e - cid_start);
+ if (!n)
+ return -ENOMEM;
+
if (!isempty(n)) {
r = safe_atou(n, &a->sockaddr.vm.svm_cid);
if (r < 0)
@@ -160,6 +169,9 @@ int socket_address_parse(SocketAddress *a, const char *s) {
return -EINVAL;
n = strndupa(s, e-s);
+ n = strndup(s, e-s);
+ if (!n)
+ return -ENOMEM;
/* IPv4 in w.x.y.z:p notation? */
r = inet_pton(AF_INET, n, &a->sockaddr.in.sin_addr);
@@ -762,21 +774,6 @@ int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret)
return 0;
}
-int socket_address_unlink(SocketAddress *a) {
- assert(a);
-
- if (socket_address_family(a) != AF_UNIX)
- return 0;
-
- if (a->sockaddr.un.sun_path[0] == 0)
- return 0;
-
- if (unlink(a->sockaddr.un.sun_path) < 0)
- return -errno;
-
- return 1;
-}
-
static const char* const netlink_family_table[] = {
[NETLINK_ROUTE] = "route",
[NETLINK_FIREWALL] = "firewall",
@@ -851,8 +848,8 @@ int fd_inc_sndbuf(int fd, size_t n) {
/* If we have the privileges we will ignore the kernel limit. */
value = (int) n;
- if (setsockopt(fd, SOL_SOCKET, SO_SNDBUFFORCE, &value, sizeof(value)) < 0)
- if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)) < 0)
+ if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)) < 0)
+ if (setsockopt(fd, SOL_SOCKET, SO_SNDBUFFORCE, &value, sizeof(value)) < 0)
return -errno;
return 1;
@@ -869,8 +866,8 @@ int fd_inc_rcvbuf(int fd, size_t n) {
/* If we have the privileges we will ignore the kernel limit. */
value = (int) n;
- if (setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &value, sizeof(value)) < 0)
- if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value)) < 0)
+ if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value)) < 0)
+ if (setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &value, sizeof(value)) < 0)
return -errno;
return 1;
}
@@ -1208,4 +1205,28 @@ int socket_ioctl_fd(void) {
return fd;
}
+
+int sockaddr_un_unlink(const struct sockaddr_un *sa) {
+ const char *p, * nul;
+
+ assert(sa);
+
+ if (sa->sun_family != AF_UNIX)
+ return -EPROTOTYPE;
+
+ if (sa->sun_path[0] == 0) /* Nothing to do for abstract sockets */
+ return 0;
+
+ /* The path in .sun_path is not necessarily NUL terminated. Let's fix that. */
+ nul = memchr(sa->sun_path, 0, sizeof(sa->sun_path));
+ if (nul)
+ p = sa->sun_path;
+ else
+ p = memdupa_suffix0(sa->sun_path, sizeof(sa->sun_path));
+
+ if (unlink(p) < 0)
+ return -errno;
+
+ return 1;
+}
#endif // 0
diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h
index 35191616f..84c024a37 100644
--- a/src/basic/socket-util.h
+++ b/src/basic/socket-util.h
@@ -74,7 +74,12 @@ int socket_address_parse_and_warn(SocketAddress *a, const char *s);
int socket_address_parse_netlink(SocketAddress *a, const char *s);
int socket_address_print(const SocketAddress *a, char **p);
int socket_address_verify(const SocketAddress *a) _pure_;
-int socket_address_unlink(SocketAddress *a);
+
+int sockaddr_un_unlink(const struct sockaddr_un *sa);
+
+static inline int socket_address_unlink(const SocketAddress *a) {
+ return socket_address_family(a) == AF_UNIX ? sockaddr_un_unlink(&a->sockaddr.un) : 0;
+}
bool socket_address_can_accept(const SocketAddress *a) _pure_;
diff --git a/src/basic/string-util.c b/src/basic/string-util.c
index d7ba43541..38adaaf45 100644
--- a/src/basic/string-util.c
+++ b/src/basic/string-util.c
@@ -1006,7 +1006,7 @@ int free_and_strdup(char **p, const char *s) {
assert(p);
- /* Replaces a string pointer with an strdup()ed new string,
+ /* Replaces a string pointer with a strdup()ed new string,
* possibly freeing the old one. */
if (streq_ptr(*p, s))
@@ -1025,6 +1025,32 @@ int free_and_strdup(char **p, const char *s) {
return 1;
}
+int free_and_strndup(char **p, const char *s, size_t l) {
+ char *t;
+
+ assert(p);
+ assert(s || l == 0);
+
+ /* Replaces a string pointer with a strndup()ed new string,
+ * freeing the old one. */
+
+ if (!*p && !s)
+ return 0;
+
+ if (*p && s && strneq(*p, s, l) && (l > strlen(*p) || (*p)[l] == '\0'))
+ return 0;
+
+ if (s) {
+ t = strndup(s, l);
+ if (!t)
+ return -ENOMEM;
+ } else
+ t = NULL;
+
+ free_and_replace(*p, t);
+ return 1;
+}
+
#if !HAVE_EXPLICIT_BZERO
/*
* Pointer to memset is volatile so that compiler must de-reference
diff --git a/src/basic/string-util.h b/src/basic/string-util.h
index d3a01a65e..f0f73dd47 100644
--- a/src/basic/string-util.h
+++ b/src/basic/string-util.h
@@ -52,11 +52,9 @@ static inline const char *empty_to_null(const char *p) {
return isempty(p) ? NULL : p;
}
-#if 0 /// UNNEEDED by elogind
static inline const char *empty_to_dash(const char *str) {
return isempty(str) ? "-" : str;
}
-#endif // 0
static inline char *startswith(const char *s, const char *prefix) {
size_t l;
@@ -182,6 +180,7 @@ char *strrep(const char *s, unsigned n);
int split_pair(const char *s, const char *sep, char **l, char **r);
int free_and_strdup(char **p, const char *s);
+int free_and_strndup(char **p, const char *s, size_t l);
/* Normal memmem() requires haystack to be nonnull, which is annoying for zero-length buffers */
static inline void *memmem_safe(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) {
@@ -234,3 +233,27 @@ static inline void *memory_startswith(const void *p, size_t sz, const char *toke
return (uint8_t*) p + n;
}
+
+#if 0 /// Not needed by elogind, only test-string-util uses this.
+/* Like startswith_no_case(), but operates on arbitrary memory blocks.
+ * It works only for ASCII strings.
+ */
+static inline void *memory_startswith_no_case(const void *p, size_t sz, const char *token) {
+ size_t n, i;
+
+ assert(token);
+
+ n = strlen(token);
+ if (sz < n)
+ return NULL;
+
+ assert(p);
+
+ for (i = 0; i < n; i++) {
+ if (ascii_tolower(((char *)p)[i]) != ascii_tolower(token[i]))
+ return NULL;
+ }
+
+ return (uint8_t*) p + n;
+}
+#endif // 0
diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c
index f8623f9fd..d8e5d0d2e 100644
--- a/src/basic/terminal-util.c
+++ b/src/basic/terminal-util.c
@@ -830,11 +830,11 @@ unsigned columns(void) {
if (e)
(void) safe_atoi(e, &c);
- if (c <= 0)
+ if (c <= 0 || c > USHRT_MAX) {
c = fd_columns(STDOUT_FILENO);
-
- if (c <= 0)
- c = 80;
+ if (c <= 0)
+ c = 80;
+ }
cached_columns = c;
return cached_columns;
@@ -864,11 +864,11 @@ unsigned lines(void) {
if (e)
(void) safe_atoi(e, &l);
- if (l <= 0)
+ if (l <= 0 || l > USHRT_MAX) {
l = fd_lines(STDOUT_FILENO);
-
- if (l <= 0)
- l = 24;
+ if (l <= 0)
+ l = 24;
+ }
cached_lines = l;
return cached_lines;
diff --git a/src/basic/time-util.c b/src/basic/time-util.c
index 6bd86bf4d..fce250a9c 100644
--- a/src/basic/time-util.c
+++ b/src/basic/time-util.c
@@ -1073,18 +1073,19 @@ int parse_sec(const char *t, usec_t *usec) {
}
#if 0 /// UNNEEDED by elogind
-int parse_sec_fix_0(const char *t, usec_t *usec) {
- assert(t);
- assert(usec);
+int parse_sec_fix_0(const char *t, usec_t *ret) {
+ usec_t k;
+ int r;
- t += strspn(t, WHITESPACE);
+ assert(t);
+ assert(ret);
- if (streq(t, "0")) {
- *usec = USEC_INFINITY;
- return 0;
- }
+ r = parse_sec(t, &k);
+ if (r < 0)
+ return r;
- return parse_sec(t, usec);
+ *ret = k == 0 ? USEC_INFINITY : k;
+ return r;
}
int parse_nsec(const char *t, nsec_t *nsec) {
diff --git a/src/basic/unit-def.c b/src/basic/unit-def.c
index ac6a9b37e..2987a173d 100644
--- a/src/basic/unit-def.c
+++ b/src/basic/unit-def.c
@@ -34,6 +34,7 @@ int unit_name_from_dbus_path(const char *path, char **name) {
return 0;
}
+#if 0 /// elogind does not support systemd units
const char* unit_dbus_interface_from_type(UnitType t) {
static const char *const table[_UNIT_TYPE_MAX] = {
@@ -67,6 +68,7 @@ const char *unit_dbus_interface_from_name(const char *name) {
return unit_dbus_interface_from_type(t);
}
+#endif // 0
static const char* const unit_type_table[_UNIT_TYPE_MAX] = {
[UNIT_SERVICE] = "service",
diff --git a/src/basic/unit-def.h b/src/basic/unit-def.h
index d7e2d7466..415f06e8d 100644
--- a/src/basic/unit-def.h
+++ b/src/basic/unit-def.h
@@ -232,8 +232,10 @@ typedef enum NotifyAccess {
char *unit_dbus_path_from_name(const char *name);
int unit_name_from_dbus_path(const char *path, char **name);
+#if 0 /// elogind does not support systemd units
const char* unit_dbus_interface_from_type(UnitType t);
const char *unit_dbus_interface_from_name(const char *name);
+#endif // 0
const char *unit_type_to_string(UnitType i) _const_;
UnitType unit_type_from_string(const char *s) _pure_;
diff --git a/src/basic/unit-name.c b/src/basic/unit-name.c
index bd0e3f428..13c86e30f 100644
--- a/src/basic/unit-name.c
+++ b/src/basic/unit-name.c
@@ -102,6 +102,7 @@ bool unit_instance_is_valid(const char *i) {
return in_charset(i, "@" VALID_CHARS);
}
+#if 0 /// UNNEEDED by elogind
bool unit_suffix_is_valid(const char *s) {
if (isempty(s))
@@ -116,7 +117,6 @@ bool unit_suffix_is_valid(const char *s) {
return true;
}
-#if 0 /// UNNEEDED by elogind
int unit_name_to_prefix(const char *n, char **ret) {
const char *p;
char *s;
@@ -193,7 +193,6 @@ int unit_name_to_prefix_and_instance(const char *n, char **ret) {
*ret = s;
return 0;
}
-#endif // 0
UnitType unit_name_to_type(const char *n) {
const char *e;
@@ -208,7 +207,6 @@ UnitType unit_name_to_type(const char *n) {
return unit_type_from_string(e + 1);
}
-#if 0 /// UNNEEDED by elogind
int unit_name_change_suffix(const char *n, const char *suffix, char **ret) {
char *e, *s;
size_t a, b;
diff --git a/src/basic/unit-name.h b/src/basic/unit-name.h
index a9a2923fd..d625df0f9 100644
--- a/src/basic/unit-name.h
+++ b/src/basic/unit-name.h
@@ -8,8 +8,6 @@
#define UNIT_NAME_MAX 256
-#if 0 /// UNNEEDED by elogind
-#endif // 0
typedef enum UnitNameFlags {
UNIT_NAME_PLAIN = 1, /* Allow foo.service */
UNIT_NAME_INSTANCE = 2, /* Allow foo@bar.service */
@@ -20,9 +18,9 @@ typedef enum UnitNameFlags {
bool unit_name_is_valid(const char *n, UnitNameFlags flags) _pure_;
bool unit_prefix_is_valid(const char *p) _pure_;
bool unit_instance_is_valid(const char *i) _pure_;
+#if 0 /// UNNEEDED by elogind
bool unit_suffix_is_valid(const char *s) _pure_;
-#if 0 /// UNNEEDED by elogind
static inline int unit_prefix_and_instance_is_valid(const char *p) {
/* For prefix+instance and instance the same rules apply */
return unit_instance_is_valid(p);
@@ -31,11 +29,9 @@ static inline int unit_prefix_and_instance_is_valid(const char *p) {
int unit_name_to_prefix(const char *n, char **prefix);
int unit_name_to_instance(const char *n, char **instance);
int unit_name_to_prefix_and_instance(const char *n, char **ret);
-#endif // 0
UnitType unit_name_to_type(const char *n) _pure_;
-#if 0 /// UNNEEDED by elogind
int unit_name_change_suffix(const char *n, const char *suffix, char **ret);
#endif // 0
diff --git a/src/basic/user-util.h b/src/basic/user-util.h
index 14a4d180c..484395b23 100644
--- a/src/basic/user-util.h
+++ b/src/basic/user-util.h
@@ -113,7 +113,7 @@ int fgetgrent_sane(FILE *stream, struct group **gr);
int putpwent_sane(const struct passwd *pw, FILE *stream);
int putspent_sane(const struct spwd *sp, FILE *stream);
int putgrent_sane(const struct group *gr, FILE *stream);
-#ifdef ENABLE_GSHADOW
+#if ENABLE_GSHADOW
int fgetsgent_sane(FILE *stream, struct sgrp **sg);
int putsgent_sane(const struct sgrp *sg, FILE *stream);
#endif
diff --git a/src/basic/util.c b/src/basic/util.c
index d2ebb4516..f00bf30be 100644
--- a/src/basic/util.c
+++ b/src/basic/util.c
@@ -23,6 +23,7 @@
//#include "def.h"
//#include "device-nodes.h"
#include "dirent-util.h"
+//#include "env-util.h"
#include "fd-util.h"
#include "fileio.h"
//#include "format-util.h"
@@ -48,7 +49,9 @@
int saved_argc = 0;
char **saved_argv = NULL;
+#if 0 /// UNNEEDED by elogind
static int saved_in_initrd = -1;
+#endif // 0
size_t page_size(void) {
static thread_local size_t pgsz = 0;
@@ -131,10 +134,10 @@ int prot_from_flags(int flags) {
return -EINVAL;
}
}
-#endif // 0
bool in_initrd(void) {
struct statfs s;
+ int r;
if (saved_in_initrd >= 0)
return saved_in_initrd;
@@ -149,14 +152,20 @@ bool in_initrd(void) {
* emptying when transititioning to the main systemd.
*/
- saved_in_initrd = access("/etc/initrd-release", F_OK) >= 0 &&
- statfs("/", &s) >= 0 &&
- is_temporary_fs(&s);
+ r = getenv_bool_secure("SYSTEMD_IN_INITRD");
+ if (r < 0 && r != -ENXIO)
+ log_debug_errno(r, "Failed to parse $SYSTEMD_IN_INITRD, ignoring: %m");
+
+ if (r >= 0)
+ saved_in_initrd = r > 0;
+ else
+ saved_in_initrd = access("/etc/initrd-release", F_OK) >= 0 &&
+ statfs("/", &s) >= 0 &&
+ is_temporary_fs(&s);
return saved_in_initrd;
}
-#if 0 /// UNNEEDED by elogind
void in_initrd_force(bool value) {
saved_in_initrd = value;
}
diff --git a/src/basic/util.h b/src/basic/util.h
index ff21a99b9..8e8ef68c4 100644
--- a/src/basic/util.h
+++ b/src/basic/util.h
@@ -69,10 +69,8 @@ extern char **saved_argv;
bool kexec_loaded(void);
int prot_from_flags(int flags) _const_;
-#endif // 0
bool in_initrd(void);
-#if 0 /// UNNEEDED by elogind
void in_initrd_force(bool value);
void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
diff --git a/src/basic/virt.c b/src/basic/virt.c
index ac42947a4..94c791ca3 100644
--- a/src/basic/virt.c
+++ b/src/basic/virt.c
@@ -11,6 +11,7 @@
#include "alloc-util.h"
#include "dirent-util.h"
+//#include "def.h"
#include "env-util.h"
#include "fd-util.h"
#include "fileio.h"
@@ -260,21 +261,38 @@ static int detect_vm_hypervisor(void) {
}
static int detect_vm_uml(void) {
- _cleanup_free_ char *cpuinfo_contents = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
int r;
/* Detect User-Mode Linux by reading /proc/cpuinfo */
- r = read_full_file("/proc/cpuinfo", &cpuinfo_contents, NULL);
- if (r == -ENOENT) {
- log_debug("/proc/cpuinfo not found, assuming no UML virtualization.");
- return VIRTUALIZATION_NONE;
+ f = fopen("/proc/cpuinfo", "re");
+ if (!f) {
+ if (errno == ENOENT) {
+ log_debug("/proc/cpuinfo not found, assuming no UML virtualization.");
+ return VIRTUALIZATION_NONE;
+ }
+ return -errno;
}
- if (r < 0)
- return r;
- if (strstr(cpuinfo_contents, "\nvendor_id\t: User Mode Linux\n")) {
- log_debug("UML virtualization found in /proc/cpuinfo");
- return VIRTUALIZATION_UML;
+ for (;;) {
+ _cleanup_free_ char *line = NULL;
+ const char *t;
+
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ t = startswith(line, "vendor_id\t: ");
+ if (t) {
+ if (startswith(t, "User Mode Linux")) {
+ log_debug("UML virtualization found in /proc/cpuinfo");
+ return VIRTUALIZATION_UML;
+ }
+
+ break;
+ }
}
log_debug("UML virtualization not found in /proc/cpuinfo.");
diff --git a/src/basic/xattr-util.c b/src/basic/xattr-util.c
index a9ec8e106..d3d0194c4 100644
--- a/src/basic/xattr-util.c
+++ b/src/basic/xattr-util.c
@@ -2,7 +2,6 @@
#include <errno.h>
#include <fcntl.h>
-//#include <linux/stat.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
diff --git a/src/basic/xml.c b/src/basic/xml.c
new file mode 100644
index 000000000..cb34d870c
--- /dev/null
+++ b/src/basic/xml.c
@@ -0,0 +1,238 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <errno.h>
+#include <stddef.h>
+#include <string.h>
+
+#include "macro.h"
+#include "string-util.h"
+#include "xml.h"
+
+enum {
+ STATE_NULL,
+ STATE_TEXT,
+ STATE_TAG,
+ STATE_ATTRIBUTE,
+};
+
+static void inc_lines(unsigned *line, const char *s, size_t n) {
+ const char *p = s;
+
+ if (!line)
+ return;
+
+ for (;;) {
+ const char *f;
+
+ f = memchr(p, '\n', n);
+ if (!f)
+ return;
+
+ n -= (f - p) + 1;
+ p = f + 1;
+ (*line)++;
+ }
+}
+
+/* We don't actually do real XML here. We only read a simplistic
+ * subset, that is a bit less strict that XML and lacks all the more
+ * complex features, like entities, or namespaces. However, we do
+ * support some HTML5-like simplifications */
+
+int xml_tokenize(const char **p, char **name, void **state, unsigned *line) {
+ const char *c, *e, *b;
+ char *ret;
+ int t;
+
+ assert(p);
+ assert(*p);
+ assert(name);
+ assert(state);
+
+ t = PTR_TO_INT(*state);
+ c = *p;
+
+ if (t == STATE_NULL) {
+ if (line)
+ *line = 1;
+ t = STATE_TEXT;
+ }
+
+ for (;;) {
+ if (*c == 0)
+ return XML_END;
+
+ switch (t) {
+
+ case STATE_TEXT: {
+ int x;
+
+ e = strchrnul(c, '<');
+ if (e > c) {
+ /* More text... */
+ ret = strndup(c, e - c);
+ if (!ret)
+ return -ENOMEM;
+
+ inc_lines(line, c, e - c);
+
+ *name = ret;
+ *p = e;
+ *state = INT_TO_PTR(STATE_TEXT);
+
+ return XML_TEXT;
+ }
+
+ assert(*e == '<');
+ b = c + 1;
+
+ if (startswith(b, "!--")) {
+ /* A comment */
+ e = strstr(b + 3, "-->");
+ if (!e)
+ return -EINVAL;
+
+ inc_lines(line, b, e + 3 - b);
+
+ c = e + 3;
+ continue;
+ }
+
+ if (*b == '?') {
+ /* Processing instruction */
+
+ e = strstr(b + 1, "?>");
+ if (!e)
+ return -EINVAL;
+
+ inc_lines(line, b, e + 2 - b);
+
+ c = e + 2;
+ continue;
+ }
+
+ if (*b == '!') {
+ /* DTD */
+
+ e = strchr(b + 1, '>');
+ if (!e)
+ return -EINVAL;
+
+ inc_lines(line, b, e + 1 - b);
+
+ c = e + 1;
+ continue;
+ }
+
+ if (*b == '/') {
+ /* A closing tag */
+ x = XML_TAG_CLOSE;
+ b++;
+ } else
+ x = XML_TAG_OPEN;
+
+ e = strpbrk(b, WHITESPACE "/>");
+ if (!e)
+ return -EINVAL;
+
+ ret = strndup(b, e - b);
+ if (!ret)
+ return -ENOMEM;
+
+ *name = ret;
+ *p = e;
+ *state = INT_TO_PTR(STATE_TAG);
+
+ return x;
+ }
+
+ case STATE_TAG:
+
+ b = c + strspn(c, WHITESPACE);
+ if (*b == 0)
+ return -EINVAL;
+
+ inc_lines(line, c, b - c);
+
+ e = b + strcspn(b, WHITESPACE "=/>");
+ if (e > b) {
+ /* An attribute */
+
+ ret = strndup(b, e - b);
+ if (!ret)
+ return -ENOMEM;
+
+ *name = ret;
+ *p = e;
+ *state = INT_TO_PTR(STATE_ATTRIBUTE);
+
+ return XML_ATTRIBUTE_NAME;
+ }
+
+ if (startswith(b, "/>")) {
+ /* An empty tag */
+
+ *name = NULL; /* For empty tags we return a NULL name, the caller must be prepared for that */
+ *p = b + 2;
+ *state = INT_TO_PTR(STATE_TEXT);
+
+ return XML_TAG_CLOSE_EMPTY;
+ }
+
+ if (*b != '>')
+ return -EINVAL;
+
+ c = b + 1;
+ t = STATE_TEXT;
+ continue;
+
+ case STATE_ATTRIBUTE:
+
+ if (*c == '=') {
+ c++;
+
+ if (IN_SET(*c, '\'', '\"')) {
+ /* Tag with a quoted value */
+
+ e = strchr(c+1, *c);
+ if (!e)
+ return -EINVAL;
+
+ inc_lines(line, c, e - c);
+
+ ret = strndup(c+1, e - c - 1);
+ if (!ret)
+ return -ENOMEM;
+
+ *name = ret;
+ *p = e + 1;
+ *state = INT_TO_PTR(STATE_TAG);
+
+ return XML_ATTRIBUTE_VALUE;
+
+ }
+
+ /* Tag with a value without quotes */
+
+ b = strpbrk(c, WHITESPACE ">");
+ if (!b)
+ b = c;
+
+ ret = strndup(c, b - c);
+ if (!ret)
+ return -ENOMEM;
+
+ *name = ret;
+ *p = b;
+ *state = INT_TO_PTR(STATE_TAG);
+ return XML_ATTRIBUTE_VALUE;
+ }
+
+ t = STATE_TAG;
+ continue;
+ }
+
+ }
+
+ assert_not_reached("Bad state");
+}
diff --git a/src/basic/xml.h b/src/basic/xml.h
new file mode 100644
index 000000000..8da2ff5f7
--- /dev/null
+++ b/src/basic/xml.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+enum {
+ XML_END,
+ XML_TEXT,
+ XML_TAG_OPEN,
+ XML_TAG_CLOSE,
+ XML_TAG_CLOSE_EMPTY,
+ XML_ATTRIBUTE_NAME,
+ XML_ATTRIBUTE_VALUE,
+};
+
+int xml_tokenize(const char **p, char **name, void **state, unsigned *line);