Move various files that don't need to be in basic/ to shared/
This doesn't have much effect on the final build, because we link libbasic.a
into libsystemd-shared.so, so in the end, all the object built from basic/
end up in libsystemd-shared. And when the static library is linked into binaries,
any objects that are included in it but are not used are trimmed. Hence, the
size of output artifacts doesn't change:
$ du -sb /var/tmp/inst*
54181861 /var/tmp/inst1 (old)
54207441 /var/tmp/inst1s (old split-usr)
54182477 /var/tmp/inst2 (new)
54208041 /var/tmp/inst2s (new split-usr)
(The negligible change in size is because libsystemd-shared.so is bigger
by a few hundred bytes. I guess it's because symbols are named differently
or something like that.)
The effect is on the build process, in particular partial builds. This change
effectively moves the requirements on some build steps toward the leaves of the
dependency tree. Two effects:
- when building items that do not depend on libsystemd-shared, we
build less stuff for libbasic.a (which wouldn't be used anyway,
so it's a net win).
- when building items that do depend on libshared, we reduce libbasic.a as a
synchronization point, possibly allowing better parallelism.
Method:
1. copy list of .h files from src/basic/meson.build to /tmp/basic
2. $ for i in $(grep '.h$' /tmp/basic); do echo $i; git --no-pager grep "include \"$i\"" src/basic/ 'src/lib*' 'src/nss-*' 'src/journal/sd-journal.c' |grep -v "${i%.h}.c";echo ;done | less
This commit is contained in:
@@ -1,398 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/eventfd.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "barrier.h"
|
||||
#include "fd-util.h"
|
||||
#include "macro.h"
|
||||
|
||||
/**
|
||||
* Barriers
|
||||
* This barrier implementation provides a simple synchronization method based
|
||||
* on file-descriptors that can safely be used between threads and processes. A
|
||||
* barrier object contains 2 shared counters based on eventfd. Both processes
|
||||
* can now place barriers and wait for the other end to reach a random or
|
||||
* specific barrier.
|
||||
* Barriers are numbered, so you can either wait for the other end to reach any
|
||||
* barrier or the last barrier that you placed. This way, you can use barriers
|
||||
* for one-way *and* full synchronization. Note that even-though barriers are
|
||||
* numbered, these numbers are internal and recycled once both sides reached the
|
||||
* same barrier (implemented as a simple signed counter). It is thus not
|
||||
* possible to address barriers by their ID.
|
||||
*
|
||||
* Barrier-API: Both ends can place as many barriers via barrier_place() as
|
||||
* they want and each pair of barriers on both sides will be implicitly linked.
|
||||
* Each side can use the barrier_wait/sync_*() family of calls to wait for the
|
||||
* other side to place a specific barrier. barrier_wait_next() waits until the
|
||||
* other side calls barrier_place(). No links between the barriers are
|
||||
* considered and this simply serves as most basic asynchronous barrier.
|
||||
* barrier_sync_next() is like barrier_wait_next() and waits for the other side
|
||||
* to place their next barrier via barrier_place(). However, it only waits for
|
||||
* barriers that are linked to a barrier we already placed. If the other side
|
||||
* already placed more barriers than we did, barrier_sync_next() returns
|
||||
* immediately.
|
||||
* barrier_sync() extends barrier_sync_next() and waits until the other end
|
||||
* placed as many barriers via barrier_place() as we did. If they already placed
|
||||
* as many as we did (or more), it returns immediately.
|
||||
*
|
||||
* Additionally to basic barriers, an abortion event is available.
|
||||
* barrier_abort() places an abortion event that cannot be undone. An abortion
|
||||
* immediately cancels all placed barriers and replaces them. Any running and
|
||||
* following wait/sync call besides barrier_wait_abortion() will immediately
|
||||
* return false on both sides (otherwise, they always return true).
|
||||
* barrier_abort() can be called multiple times on both ends and will be a
|
||||
* no-op if already called on this side.
|
||||
* barrier_wait_abortion() can be used to wait for the other side to call
|
||||
* barrier_abort() and is the only wait/sync call that does not return
|
||||
* immediately if we aborted outself. It only returns once the other side
|
||||
* called barrier_abort().
|
||||
*
|
||||
* Barriers can be used for in-process and inter-process synchronization.
|
||||
* However, for in-process synchronization you could just use mutexes.
|
||||
* Therefore, main target is IPC and we require both sides to *not* share the FD
|
||||
* table. If that's given, barriers provide target tracking: If the remote side
|
||||
* exit()s, an abortion event is implicitly queued on the other side. This way,
|
||||
* a sync/wait call will be woken up if the remote side crashed or exited
|
||||
* unexpectedly. However, note that these abortion events are only queued if the
|
||||
* barrier-queue has been drained. Therefore, it is safe to place a barrier and
|
||||
* exit. The other side can safely wait on the barrier even though the exit
|
||||
* queued an abortion event. Usually, the abortion event would overwrite the
|
||||
* barrier, however, that's not true for exit-abortion events. Those are only
|
||||
* queued if the barrier-queue is drained (thus, the receiving side has placed
|
||||
* more barriers than the remote side).
|
||||
*/
|
||||
|
||||
/**
|
||||
* barrier_create() - Initialize a barrier object
|
||||
* @obj: barrier to initialize
|
||||
*
|
||||
* This initializes a barrier object. The caller is responsible of allocating
|
||||
* the memory and keeping it valid. The memory does not have to be zeroed
|
||||
* beforehand.
|
||||
* Two eventfd objects are allocated for each barrier. If allocation fails, an
|
||||
* error is returned.
|
||||
*
|
||||
* If this function fails, the barrier is reset to an invalid state so it is
|
||||
* safe to call barrier_destroy() on the object regardless whether the
|
||||
* initialization succeeded or not.
|
||||
*
|
||||
* The caller is responsible to destroy the object via barrier_destroy() before
|
||||
* releasing the underlying memory.
|
||||
*
|
||||
* Returns: 0 on success, negative error code on failure.
|
||||
*/
|
||||
int barrier_create(Barrier *b) {
|
||||
_cleanup_(barrier_destroyp) Barrier *staging = b;
|
||||
int r;
|
||||
|
||||
assert(b);
|
||||
|
||||
b->me = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
|
||||
if (b->me < 0)
|
||||
return -errno;
|
||||
|
||||
b->them = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
|
||||
if (b->them < 0)
|
||||
return -errno;
|
||||
|
||||
r = pipe2(b->pipe, O_CLOEXEC | O_NONBLOCK);
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
staging = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* barrier_destroy() - Destroy a barrier object
|
||||
* @b: barrier to destroy or NULL
|
||||
*
|
||||
* This destroys a barrier object that has previously been passed to
|
||||
* barrier_create(). The object is released and reset to invalid
|
||||
* state. Therefore, it is safe to call barrier_destroy() multiple
|
||||
* times or even if barrier_create() failed. However, barrier must be
|
||||
* always initialized with BARRIER_NULL.
|
||||
*
|
||||
* If @b is NULL, this is a no-op.
|
||||
*/
|
||||
void barrier_destroy(Barrier *b) {
|
||||
if (!b)
|
||||
return;
|
||||
|
||||
b->me = safe_close(b->me);
|
||||
b->them = safe_close(b->them);
|
||||
safe_close_pair(b->pipe);
|
||||
b->barriers = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* barrier_set_role() - Set the local role of the barrier
|
||||
* @b: barrier to operate on
|
||||
* @role: role to set on the barrier
|
||||
*
|
||||
* This sets the roles on a barrier object. This is needed to know
|
||||
* which side of the barrier you're on. Usually, the parent creates
|
||||
* the barrier via barrier_create() and then calls fork() or clone().
|
||||
* Therefore, the FDs are duplicated and the child retains the same
|
||||
* barrier object.
|
||||
*
|
||||
* Both sides need to call barrier_set_role() after fork() or clone()
|
||||
* are done. If this is not done, barriers will not work correctly.
|
||||
*
|
||||
* Note that barriers could be supported without fork() or clone(). However,
|
||||
* this is currently not needed so it hasn't been implemented.
|
||||
*/
|
||||
void barrier_set_role(Barrier *b, unsigned role) {
|
||||
int fd;
|
||||
|
||||
assert(b);
|
||||
assert(IN_SET(role, BARRIER_PARENT, BARRIER_CHILD));
|
||||
/* make sure this is only called once */
|
||||
assert(b->pipe[0] >= 0 && b->pipe[1] >= 0);
|
||||
|
||||
if (role == BARRIER_PARENT)
|
||||
b->pipe[1] = safe_close(b->pipe[1]);
|
||||
else {
|
||||
b->pipe[0] = safe_close(b->pipe[0]);
|
||||
|
||||
/* swap me/them for children */
|
||||
fd = b->me;
|
||||
b->me = b->them;
|
||||
b->them = fd;
|
||||
}
|
||||
}
|
||||
|
||||
/* places barrier; returns false if we aborted, otherwise true */
|
||||
static bool barrier_write(Barrier *b, uint64_t buf) {
|
||||
ssize_t len;
|
||||
|
||||
/* prevent new sync-points if we already aborted */
|
||||
if (barrier_i_aborted(b))
|
||||
return false;
|
||||
|
||||
assert(b->me >= 0);
|
||||
do {
|
||||
len = write(b->me, &buf, sizeof(buf));
|
||||
} while (len < 0 && IN_SET(errno, EAGAIN, EINTR));
|
||||
|
||||
if (len != sizeof(buf))
|
||||
goto error;
|
||||
|
||||
/* lock if we aborted */
|
||||
if (buf >= (uint64_t)BARRIER_ABORTION) {
|
||||
if (barrier_they_aborted(b))
|
||||
b->barriers = BARRIER_WE_ABORTED;
|
||||
else
|
||||
b->barriers = BARRIER_I_ABORTED;
|
||||
} else if (!barrier_is_aborted(b))
|
||||
b->barriers += buf;
|
||||
|
||||
return !barrier_i_aborted(b);
|
||||
|
||||
error:
|
||||
/* If there is an unexpected error, we have to make this fatal. There
|
||||
* is no way we can recover from sync-errors. Therefore, we close the
|
||||
* pipe-ends and treat this as abortion. The other end will notice the
|
||||
* pipe-close and treat it as abortion, too. */
|
||||
|
||||
safe_close_pair(b->pipe);
|
||||
b->barriers = BARRIER_WE_ABORTED;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* waits for barriers; returns false if they aborted, otherwise true */
|
||||
static bool barrier_read(Barrier *b, int64_t comp) {
|
||||
if (barrier_they_aborted(b))
|
||||
return false;
|
||||
|
||||
while (b->barriers > comp) {
|
||||
struct pollfd pfd[2] = {
|
||||
{ .fd = b->pipe[0] >= 0 ? b->pipe[0] : b->pipe[1],
|
||||
.events = POLLHUP },
|
||||
{ .fd = b->them,
|
||||
.events = POLLIN }};
|
||||
uint64_t buf;
|
||||
int r;
|
||||
|
||||
r = poll(pfd, 2, -1);
|
||||
if (r < 0 && IN_SET(errno, EAGAIN, EINTR))
|
||||
continue;
|
||||
else if (r < 0)
|
||||
goto error;
|
||||
|
||||
if (pfd[1].revents) {
|
||||
ssize_t len;
|
||||
|
||||
/* events on @them signal new data for us */
|
||||
len = read(b->them, &buf, sizeof(buf));
|
||||
if (len < 0 && IN_SET(errno, EAGAIN, EINTR))
|
||||
continue;
|
||||
|
||||
if (len != sizeof(buf))
|
||||
goto error;
|
||||
} else if (pfd[0].revents & (POLLHUP | POLLERR | POLLNVAL))
|
||||
/* POLLHUP on the pipe tells us the other side exited.
|
||||
* We treat this as implicit abortion. But we only
|
||||
* handle it if there's no event on the eventfd. This
|
||||
* guarantees that exit-abortions do not overwrite real
|
||||
* barriers. */
|
||||
buf = BARRIER_ABORTION;
|
||||
else
|
||||
continue;
|
||||
|
||||
/* lock if they aborted */
|
||||
if (buf >= (uint64_t)BARRIER_ABORTION) {
|
||||
if (barrier_i_aborted(b))
|
||||
b->barriers = BARRIER_WE_ABORTED;
|
||||
else
|
||||
b->barriers = BARRIER_THEY_ABORTED;
|
||||
} else if (!barrier_is_aborted(b))
|
||||
b->barriers -= buf;
|
||||
}
|
||||
|
||||
return !barrier_they_aborted(b);
|
||||
|
||||
error:
|
||||
/* If there is an unexpected error, we have to make this fatal. There
|
||||
* is no way we can recover from sync-errors. Therefore, we close the
|
||||
* pipe-ends and treat this as abortion. The other end will notice the
|
||||
* pipe-close and treat it as abortion, too. */
|
||||
|
||||
safe_close_pair(b->pipe);
|
||||
b->barriers = BARRIER_WE_ABORTED;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* barrier_place() - Place a new barrier
|
||||
* @b: barrier object
|
||||
*
|
||||
* This places a new barrier on the barrier object. If either side already
|
||||
* aborted, this is a no-op and returns "false". Otherwise, the barrier is
|
||||
* placed and this returns "true".
|
||||
*
|
||||
* Returns: true if barrier was placed, false if either side aborted.
|
||||
*/
|
||||
bool barrier_place(Barrier *b) {
|
||||
assert(b);
|
||||
|
||||
if (barrier_is_aborted(b))
|
||||
return false;
|
||||
|
||||
barrier_write(b, BARRIER_SINGLE);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* barrier_abort() - Abort the synchronization
|
||||
* @b: barrier object to abort
|
||||
*
|
||||
* This aborts the barrier-synchronization. If barrier_abort() was already
|
||||
* called on this side, this is a no-op. Otherwise, the barrier is put into the
|
||||
* ABORT-state and will stay there. The other side is notified about the
|
||||
* abortion. Any following attempt to place normal barriers or to wait on normal
|
||||
* barriers will return immediately as "false".
|
||||
*
|
||||
* You can wait for the other side to call barrier_abort(), too. Use
|
||||
* barrier_wait_abortion() for that.
|
||||
*
|
||||
* Returns: false if the other side already aborted, true otherwise.
|
||||
*/
|
||||
bool barrier_abort(Barrier *b) {
|
||||
assert(b);
|
||||
|
||||
barrier_write(b, BARRIER_ABORTION);
|
||||
return !barrier_they_aborted(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* barrier_wait_next() - Wait for the next barrier of the other side
|
||||
* @b: barrier to operate on
|
||||
*
|
||||
* This waits until the other side places its next barrier. This is independent
|
||||
* of any barrier-links and just waits for any next barrier of the other side.
|
||||
*
|
||||
* If either side aborted, this returns false.
|
||||
*
|
||||
* Returns: false if either side aborted, true otherwise.
|
||||
*/
|
||||
bool barrier_wait_next(Barrier *b) {
|
||||
assert(b);
|
||||
|
||||
if (barrier_is_aborted(b))
|
||||
return false;
|
||||
|
||||
barrier_read(b, b->barriers - 1);
|
||||
return !barrier_is_aborted(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* barrier_wait_abortion() - Wait for the other side to abort
|
||||
* @b: barrier to operate on
|
||||
*
|
||||
* This waits until the other side called barrier_abort(). This can be called
|
||||
* regardless whether the local side already called barrier_abort() or not.
|
||||
*
|
||||
* If the other side has already aborted, this returns immediately.
|
||||
*
|
||||
* Returns: false if the local side aborted, true otherwise.
|
||||
*/
|
||||
bool barrier_wait_abortion(Barrier *b) {
|
||||
assert(b);
|
||||
|
||||
barrier_read(b, BARRIER_THEY_ABORTED);
|
||||
return !barrier_i_aborted(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* barrier_sync_next() - Wait for the other side to place a next linked barrier
|
||||
* @b: barrier to operate on
|
||||
*
|
||||
* This is like barrier_wait_next() and waits for the other side to call
|
||||
* barrier_place(). However, this only waits for linked barriers. That means, if
|
||||
* the other side already placed more barriers than (or as much as) we did, this
|
||||
* returns immediately instead of waiting.
|
||||
*
|
||||
* If either side aborted, this returns false.
|
||||
*
|
||||
* Returns: false if either side aborted, true otherwise.
|
||||
*/
|
||||
bool barrier_sync_next(Barrier *b) {
|
||||
assert(b);
|
||||
|
||||
if (barrier_is_aborted(b))
|
||||
return false;
|
||||
|
||||
barrier_read(b, MAX((int64_t)0, b->barriers - 1));
|
||||
return !barrier_is_aborted(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* barrier_sync() - Wait for the other side to place as many barriers as we did
|
||||
* @b: barrier to operate on
|
||||
*
|
||||
* This is like barrier_sync_next() but waits for the other side to call
|
||||
* barrier_place() as often as we did (in total). If they already placed as much
|
||||
* as we did (or more), this returns immediately instead of waiting.
|
||||
*
|
||||
* If either side aborted, this returns false.
|
||||
*
|
||||
* Returns: false if either side aborted, true otherwise.
|
||||
*/
|
||||
bool barrier_sync(Barrier *b) {
|
||||
assert(b);
|
||||
|
||||
if (barrier_is_aborted(b))
|
||||
return false;
|
||||
|
||||
barrier_read(b, 0);
|
||||
return !barrier_is_aborted(b);
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "macro.h"
|
||||
|
||||
/* See source file for an API description. */
|
||||
|
||||
typedef struct Barrier Barrier;
|
||||
|
||||
enum {
|
||||
BARRIER_SINGLE = 1LL,
|
||||
BARRIER_ABORTION = INT64_MAX,
|
||||
|
||||
/* bias values to store state; keep @WE < @THEY < @I */
|
||||
BARRIER_BIAS = INT64_MIN,
|
||||
BARRIER_WE_ABORTED = BARRIER_BIAS + 1LL,
|
||||
BARRIER_THEY_ABORTED = BARRIER_BIAS + 2LL,
|
||||
BARRIER_I_ABORTED = BARRIER_BIAS + 3LL,
|
||||
};
|
||||
|
||||
enum {
|
||||
BARRIER_PARENT,
|
||||
BARRIER_CHILD,
|
||||
};
|
||||
|
||||
struct Barrier {
|
||||
int me;
|
||||
int them;
|
||||
int pipe[2];
|
||||
int64_t barriers;
|
||||
};
|
||||
|
||||
#define BARRIER_NULL {-1, -1, {-1, -1}, 0}
|
||||
|
||||
int barrier_create(Barrier *obj);
|
||||
void barrier_destroy(Barrier *b);
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(Barrier*, barrier_destroy);
|
||||
|
||||
void barrier_set_role(Barrier *b, unsigned role);
|
||||
|
||||
bool barrier_place(Barrier *b);
|
||||
bool barrier_abort(Barrier *b);
|
||||
|
||||
bool barrier_wait_next(Barrier *b);
|
||||
bool barrier_wait_abortion(Barrier *b);
|
||||
bool barrier_sync_next(Barrier *b);
|
||||
bool barrier_sync(Barrier *b);
|
||||
|
||||
static inline bool barrier_i_aborted(Barrier *b) {
|
||||
return IN_SET(b->barriers, BARRIER_I_ABORTED, BARRIER_WE_ABORTED);
|
||||
}
|
||||
|
||||
static inline bool barrier_they_aborted(Barrier *b) {
|
||||
return IN_SET(b->barriers, BARRIER_THEY_ABORTED, BARRIER_WE_ABORTED);
|
||||
}
|
||||
|
||||
static inline bool barrier_we_aborted(Barrier *b) {
|
||||
return b->barriers == BARRIER_WE_ABORTED;
|
||||
}
|
||||
|
||||
static inline bool barrier_is_aborted(Barrier *b) {
|
||||
return IN_SET(b->barriers,
|
||||
BARRIER_I_ABORTED, BARRIER_THEY_ABORTED, BARRIER_WE_ABORTED);
|
||||
}
|
||||
|
||||
static inline bool barrier_place_and_sync(Barrier *b) {
|
||||
(void) barrier_place(b);
|
||||
return barrier_sync(b);
|
||||
}
|
||||
@@ -1,218 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "bitmap.h"
|
||||
#include "hashmap.h"
|
||||
#include "macro.h"
|
||||
|
||||
struct Bitmap {
|
||||
uint64_t *bitmaps;
|
||||
size_t n_bitmaps;
|
||||
size_t bitmaps_allocated;
|
||||
};
|
||||
|
||||
/* Bitmaps are only meant to store relatively small numbers
|
||||
* (corresponding to, say, an enum), so it is ok to limit
|
||||
* the max entry. 64k should be plenty. */
|
||||
#define BITMAPS_MAX_ENTRY 0xffff
|
||||
|
||||
/* This indicates that we reached the end of the bitmap */
|
||||
#define BITMAP_END ((unsigned) -1)
|
||||
|
||||
#define BITMAP_NUM_TO_OFFSET(n) ((n) / (sizeof(uint64_t) * 8))
|
||||
#define BITMAP_NUM_TO_REM(n) ((n) % (sizeof(uint64_t) * 8))
|
||||
#define BITMAP_OFFSET_TO_NUM(offset, rem) ((offset) * sizeof(uint64_t) * 8 + (rem))
|
||||
|
||||
Bitmap *bitmap_new(void) {
|
||||
return new0(Bitmap, 1);
|
||||
}
|
||||
|
||||
Bitmap *bitmap_copy(Bitmap *b) {
|
||||
Bitmap *ret;
|
||||
|
||||
ret = bitmap_new();
|
||||
if (!ret)
|
||||
return NULL;
|
||||
|
||||
ret->bitmaps = newdup(uint64_t, b->bitmaps, b->n_bitmaps);
|
||||
if (!ret->bitmaps)
|
||||
return mfree(ret);
|
||||
|
||||
ret->n_bitmaps = ret->bitmaps_allocated = b->n_bitmaps;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void bitmap_free(Bitmap *b) {
|
||||
if (!b)
|
||||
return;
|
||||
|
||||
free(b->bitmaps);
|
||||
free(b);
|
||||
}
|
||||
|
||||
int bitmap_ensure_allocated(Bitmap **b) {
|
||||
Bitmap *a;
|
||||
|
||||
assert(b);
|
||||
|
||||
if (*b)
|
||||
return 0;
|
||||
|
||||
a = bitmap_new();
|
||||
if (!a)
|
||||
return -ENOMEM;
|
||||
|
||||
*b = a;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bitmap_set(Bitmap *b, unsigned n) {
|
||||
uint64_t bitmask;
|
||||
unsigned offset;
|
||||
|
||||
assert(b);
|
||||
|
||||
/* we refuse to allocate huge bitmaps */
|
||||
if (n > BITMAPS_MAX_ENTRY)
|
||||
return -ERANGE;
|
||||
|
||||
offset = BITMAP_NUM_TO_OFFSET(n);
|
||||
|
||||
if (offset >= b->n_bitmaps) {
|
||||
if (!GREEDY_REALLOC0(b->bitmaps, b->bitmaps_allocated, offset + 1))
|
||||
return -ENOMEM;
|
||||
|
||||
b->n_bitmaps = offset + 1;
|
||||
}
|
||||
|
||||
bitmask = UINT64_C(1) << BITMAP_NUM_TO_REM(n);
|
||||
|
||||
b->bitmaps[offset] |= bitmask;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bitmap_unset(Bitmap *b, unsigned n) {
|
||||
uint64_t bitmask;
|
||||
unsigned offset;
|
||||
|
||||
if (!b)
|
||||
return;
|
||||
|
||||
offset = BITMAP_NUM_TO_OFFSET(n);
|
||||
|
||||
if (offset >= b->n_bitmaps)
|
||||
return;
|
||||
|
||||
bitmask = UINT64_C(1) << BITMAP_NUM_TO_REM(n);
|
||||
|
||||
b->bitmaps[offset] &= ~bitmask;
|
||||
}
|
||||
|
||||
bool bitmap_isset(Bitmap *b, unsigned n) {
|
||||
uint64_t bitmask;
|
||||
unsigned offset;
|
||||
|
||||
if (!b)
|
||||
return false;
|
||||
|
||||
offset = BITMAP_NUM_TO_OFFSET(n);
|
||||
|
||||
if (offset >= b->n_bitmaps)
|
||||
return false;
|
||||
|
||||
bitmask = UINT64_C(1) << BITMAP_NUM_TO_REM(n);
|
||||
|
||||
return !!(b->bitmaps[offset] & bitmask);
|
||||
}
|
||||
|
||||
bool bitmap_isclear(Bitmap *b) {
|
||||
unsigned i;
|
||||
|
||||
if (!b)
|
||||
return true;
|
||||
|
||||
for (i = 0; i < b->n_bitmaps; i++)
|
||||
if (b->bitmaps[i] != 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void bitmap_clear(Bitmap *b) {
|
||||
|
||||
if (!b)
|
||||
return;
|
||||
|
||||
b->bitmaps = mfree(b->bitmaps);
|
||||
b->n_bitmaps = 0;
|
||||
b->bitmaps_allocated = 0;
|
||||
}
|
||||
|
||||
bool bitmap_iterate(Bitmap *b, Iterator *i, unsigned *n) {
|
||||
uint64_t bitmask;
|
||||
unsigned offset, rem;
|
||||
|
||||
assert(i);
|
||||
assert(n);
|
||||
|
||||
if (!b || i->idx == BITMAP_END)
|
||||
return false;
|
||||
|
||||
offset = BITMAP_NUM_TO_OFFSET(i->idx);
|
||||
rem = BITMAP_NUM_TO_REM(i->idx);
|
||||
bitmask = UINT64_C(1) << rem;
|
||||
|
||||
for (; offset < b->n_bitmaps; offset ++) {
|
||||
if (b->bitmaps[offset]) {
|
||||
for (; bitmask; bitmask <<= 1, rem ++) {
|
||||
if (b->bitmaps[offset] & bitmask) {
|
||||
*n = BITMAP_OFFSET_TO_NUM(offset, rem);
|
||||
i->idx = *n + 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rem = 0;
|
||||
bitmask = 1;
|
||||
}
|
||||
|
||||
i->idx = BITMAP_END;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bitmap_equal(Bitmap *a, Bitmap *b) {
|
||||
size_t common_n_bitmaps;
|
||||
Bitmap *c;
|
||||
unsigned i;
|
||||
|
||||
if (a == b)
|
||||
return true;
|
||||
|
||||
if (!a != !b)
|
||||
return false;
|
||||
|
||||
if (!a)
|
||||
return true;
|
||||
|
||||
common_n_bitmaps = MIN(a->n_bitmaps, b->n_bitmaps);
|
||||
if (memcmp_safe(a->bitmaps, b->bitmaps, sizeof(uint64_t) * common_n_bitmaps) != 0)
|
||||
return false;
|
||||
|
||||
c = a->n_bitmaps > b->n_bitmaps ? a : b;
|
||||
for (i = common_n_bitmaps; i < c->n_bitmaps; i++)
|
||||
if (c->bitmaps[i] != 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "hashmap.h"
|
||||
#include "macro.h"
|
||||
|
||||
typedef struct Bitmap Bitmap;
|
||||
|
||||
Bitmap *bitmap_new(void);
|
||||
Bitmap *bitmap_copy(Bitmap *b);
|
||||
int bitmap_ensure_allocated(Bitmap **b);
|
||||
void bitmap_free(Bitmap *b);
|
||||
|
||||
int bitmap_set(Bitmap *b, unsigned n);
|
||||
void bitmap_unset(Bitmap *b, unsigned n);
|
||||
bool bitmap_isset(Bitmap *b, unsigned n);
|
||||
bool bitmap_isclear(Bitmap *b);
|
||||
void bitmap_clear(Bitmap *b);
|
||||
|
||||
bool bitmap_iterate(Bitmap *b, Iterator *i, unsigned *n);
|
||||
|
||||
bool bitmap_equal(Bitmap *a, Bitmap *b);
|
||||
|
||||
#define BITMAP_FOREACH(n, b, i) \
|
||||
for ((i).idx = 0; bitmap_iterate((b), &(i), (unsigned*)&(n)); )
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(Bitmap*, bitmap_free);
|
||||
|
||||
#define _cleanup_bitmap_free_ _cleanup_(bitmap_freep)
|
||||
@@ -1,12 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
#pragma once
|
||||
|
||||
#if HAVE_BLKID
|
||||
#include <blkid.h>
|
||||
#endif
|
||||
|
||||
#include "util.h"
|
||||
|
||||
#if HAVE_BLKID
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(blkid_probe, blkid_free_probe);
|
||||
#endif
|
||||
@@ -1,237 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "bpf-program.h"
|
||||
#include "fd-util.h"
|
||||
#include "log.h"
|
||||
#include "missing.h"
|
||||
#include "path-util.h"
|
||||
#include "util.h"
|
||||
|
||||
int bpf_program_new(uint32_t prog_type, BPFProgram **ret) {
|
||||
_cleanup_(bpf_program_unrefp) BPFProgram *p = NULL;
|
||||
|
||||
p = new0(BPFProgram, 1);
|
||||
if (!p)
|
||||
return log_oom();
|
||||
|
||||
p->n_ref = 1;
|
||||
p->prog_type = prog_type;
|
||||
p->kernel_fd = -1;
|
||||
|
||||
*ret = TAKE_PTR(p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static BPFProgram *bpf_program_free(BPFProgram *p) {
|
||||
assert(p);
|
||||
|
||||
/* Unfortunately, the kernel currently doesn't implicitly detach BPF programs from their cgroups when the last
|
||||
* fd to the BPF program is closed. This has nasty side-effects since this means that abnormally terminated
|
||||
* programs that attached one of their BPF programs to a cgroup will leave this programs pinned for good with
|
||||
* zero chance of recovery, until the cgroup is removed. This is particularly problematic if the cgroup in
|
||||
* question is the root cgroup (or any other cgroup belonging to a service that cannot be restarted during
|
||||
* operation, such as dbus), as the memory for the BPF program can only be reclaimed through a reboot. To
|
||||
* counter this, we track closely to which cgroup a program was attached to and will detach it on our own
|
||||
* whenever we close the BPF fd. */
|
||||
(void) bpf_program_cgroup_detach(p);
|
||||
|
||||
safe_close(p->kernel_fd);
|
||||
free(p->instructions);
|
||||
free(p->attached_path);
|
||||
|
||||
return mfree(p);
|
||||
}
|
||||
|
||||
DEFINE_TRIVIAL_REF_UNREF_FUNC(BPFProgram, bpf_program, bpf_program_free);
|
||||
|
||||
int bpf_program_add_instructions(BPFProgram *p, const struct bpf_insn *instructions, size_t count) {
|
||||
|
||||
assert(p);
|
||||
|
||||
if (p->kernel_fd >= 0) /* don't allow modification after we uploaded things to the kernel */
|
||||
return -EBUSY;
|
||||
|
||||
if (!GREEDY_REALLOC(p->instructions, p->allocated, p->n_instructions + count))
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(p->instructions + p->n_instructions, instructions, sizeof(struct bpf_insn) * count);
|
||||
p->n_instructions += count;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bpf_program_load_kernel(BPFProgram *p, char *log_buf, size_t log_size) {
|
||||
union bpf_attr attr;
|
||||
|
||||
assert(p);
|
||||
|
||||
if (p->kernel_fd >= 0) { /* make this idempotent */
|
||||
memzero(log_buf, log_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
attr = (union bpf_attr) {
|
||||
.prog_type = p->prog_type,
|
||||
.insns = PTR_TO_UINT64(p->instructions),
|
||||
.insn_cnt = p->n_instructions,
|
||||
.license = PTR_TO_UINT64("GPL"),
|
||||
.log_buf = PTR_TO_UINT64(log_buf),
|
||||
.log_level = !!log_buf,
|
||||
.log_size = log_size,
|
||||
};
|
||||
|
||||
p->kernel_fd = bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
|
||||
if (p->kernel_fd < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bpf_program_cgroup_attach(BPFProgram *p, int type, const char *path, uint32_t flags) {
|
||||
_cleanup_free_ char *copy = NULL;
|
||||
_cleanup_close_ int fd = -1;
|
||||
union bpf_attr attr;
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
assert(type >= 0);
|
||||
assert(path);
|
||||
|
||||
if (!IN_SET(flags, 0, BPF_F_ALLOW_OVERRIDE, BPF_F_ALLOW_MULTI))
|
||||
return -EINVAL;
|
||||
|
||||
/* We need to track which cgroup the program is attached to, and we can only track one attachment, hence let's
|
||||
* refuse this early. */
|
||||
if (p->attached_path) {
|
||||
if (!path_equal(p->attached_path, path))
|
||||
return -EBUSY;
|
||||
if (p->attached_type != type)
|
||||
return -EBUSY;
|
||||
if (p->attached_flags != flags)
|
||||
return -EBUSY;
|
||||
|
||||
/* Here's a shortcut: if we previously attached this program already, then we don't have to do so
|
||||
* again. Well, with one exception: if we are in BPF_F_ALLOW_OVERRIDE mode then someone else might have
|
||||
* replaced our program since the last time, hence let's reattach it again, just to be safe. In flags
|
||||
* == 0 mode this is not an issue since nobody else can replace our program in that case, and in flags
|
||||
* == BPF_F_ALLOW_MULTI mode any other's program would be installed in addition to ours hence ours
|
||||
* would remain in effect. */
|
||||
if (flags != BPF_F_ALLOW_OVERRIDE)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Ensure we have a kernel object for this. */
|
||||
r = bpf_program_load_kernel(p, NULL, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
copy = strdup(path);
|
||||
if (!copy)
|
||||
return -ENOMEM;
|
||||
|
||||
fd = open(path, O_DIRECTORY|O_RDONLY|O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
attr = (union bpf_attr) {
|
||||
.attach_type = type,
|
||||
.target_fd = fd,
|
||||
.attach_bpf_fd = p->kernel_fd,
|
||||
.attach_flags = flags,
|
||||
};
|
||||
|
||||
if (bpf(BPF_PROG_ATTACH, &attr, sizeof(attr)) < 0)
|
||||
return -errno;
|
||||
|
||||
free_and_replace(p->attached_path, copy);
|
||||
p->attached_type = type;
|
||||
p->attached_flags = flags;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bpf_program_cgroup_detach(BPFProgram *p) {
|
||||
_cleanup_close_ int fd = -1;
|
||||
|
||||
assert(p);
|
||||
|
||||
if (!p->attached_path)
|
||||
return -EUNATCH;
|
||||
|
||||
fd = open(p->attached_path, O_DIRECTORY|O_RDONLY|O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
if (errno != ENOENT)
|
||||
return -errno;
|
||||
|
||||
/* If the cgroup does not exist anymore, then we don't have to explicitly detach, it got detached
|
||||
* implicitly by the removal, hence don't complain */
|
||||
|
||||
} else {
|
||||
union bpf_attr attr;
|
||||
|
||||
attr = (union bpf_attr) {
|
||||
.attach_type = p->attached_type,
|
||||
.target_fd = fd,
|
||||
.attach_bpf_fd = p->kernel_fd,
|
||||
};
|
||||
|
||||
if (bpf(BPF_PROG_DETACH, &attr, sizeof(attr)) < 0)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
p->attached_path = mfree(p->attached_path);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bpf_map_new(enum bpf_map_type type, size_t key_size, size_t value_size, size_t max_entries, uint32_t flags) {
|
||||
union bpf_attr attr = {
|
||||
.map_type = type,
|
||||
.key_size = key_size,
|
||||
.value_size = value_size,
|
||||
.max_entries = max_entries,
|
||||
.map_flags = flags,
|
||||
};
|
||||
int fd;
|
||||
|
||||
fd = bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
int bpf_map_update_element(int fd, const void *key, void *value) {
|
||||
|
||||
union bpf_attr attr = {
|
||||
.map_fd = fd,
|
||||
.key = PTR_TO_UINT64(key),
|
||||
.value = PTR_TO_UINT64(value),
|
||||
};
|
||||
|
||||
if (bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr)) < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bpf_map_lookup_element(int fd, const void *key, void *value) {
|
||||
|
||||
union bpf_attr attr = {
|
||||
.map_fd = fd,
|
||||
.key = PTR_TO_UINT64(key),
|
||||
.value = PTR_TO_UINT64(value),
|
||||
};
|
||||
|
||||
if (bpf(BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr)) < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
#pragma once
|
||||
|
||||
#include <linux/bpf.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/syscall.h>
|
||||
|
||||
#include "list.h"
|
||||
#include "macro.h"
|
||||
|
||||
typedef struct BPFProgram BPFProgram;
|
||||
|
||||
struct BPFProgram {
|
||||
unsigned n_ref;
|
||||
|
||||
int kernel_fd;
|
||||
uint32_t prog_type;
|
||||
|
||||
size_t n_instructions;
|
||||
size_t allocated;
|
||||
struct bpf_insn *instructions;
|
||||
|
||||
char *attached_path;
|
||||
int attached_type;
|
||||
uint32_t attached_flags;
|
||||
};
|
||||
|
||||
int bpf_program_new(uint32_t prog_type, BPFProgram **ret);
|
||||
BPFProgram *bpf_program_unref(BPFProgram *p);
|
||||
BPFProgram *bpf_program_ref(BPFProgram *p);
|
||||
|
||||
int bpf_program_add_instructions(BPFProgram *p, const struct bpf_insn *insn, size_t count);
|
||||
int bpf_program_load_kernel(BPFProgram *p, char *log_buf, size_t log_size);
|
||||
|
||||
int bpf_program_cgroup_attach(BPFProgram *p, int type, const char *path, uint32_t flags);
|
||||
int bpf_program_cgroup_detach(BPFProgram *p);
|
||||
|
||||
int bpf_map_new(enum bpf_map_type type, size_t key_size, size_t value_size, size_t max_entries, uint32_t flags);
|
||||
int bpf_map_update_element(int fd, const void *key, void *value);
|
||||
int bpf_map_lookup_element(int fd, const void *key, void *value);
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(BPFProgram*, bpf_program_unref);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,46 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
#pragma once
|
||||
|
||||
/* A structure for specifying (possibly repetitive) points in calendar
|
||||
* time, a la cron */
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "time-util.h"
|
||||
#include "util.h"
|
||||
|
||||
typedef struct CalendarComponent {
|
||||
int start;
|
||||
int stop;
|
||||
int repeat;
|
||||
|
||||
struct CalendarComponent *next;
|
||||
} CalendarComponent;
|
||||
|
||||
typedef struct CalendarSpec {
|
||||
int weekdays_bits;
|
||||
bool end_of_month;
|
||||
bool utc;
|
||||
int dst;
|
||||
char *timezone;
|
||||
|
||||
CalendarComponent *year;
|
||||
CalendarComponent *month;
|
||||
CalendarComponent *day;
|
||||
|
||||
CalendarComponent *hour;
|
||||
CalendarComponent *minute;
|
||||
CalendarComponent *microsecond;
|
||||
} CalendarSpec;
|
||||
|
||||
CalendarSpec* calendar_spec_free(CalendarSpec *c);
|
||||
|
||||
int calendar_spec_normalize(CalendarSpec *spec);
|
||||
bool calendar_spec_valid(CalendarSpec *spec);
|
||||
|
||||
int calendar_spec_to_string(const CalendarSpec *spec, char **p);
|
||||
int calendar_spec_from_string(const char *p, CalendarSpec **spec);
|
||||
|
||||
int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next);
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(CalendarSpec*, calendar_spec_free);
|
||||
@@ -1,157 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdbool.h>
|
||||
#include <time.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "clock-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "macro.h"
|
||||
#include "string-util.h"
|
||||
#include "util.h"
|
||||
|
||||
int clock_get_hwclock(struct tm *tm) {
|
||||
_cleanup_close_ int fd = -1;
|
||||
|
||||
assert(tm);
|
||||
|
||||
fd = open("/dev/rtc", O_RDONLY|O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
/* This leaves the timezone fields of struct tm
|
||||
* uninitialized! */
|
||||
if (ioctl(fd, RTC_RD_TIME, tm) < 0)
|
||||
return -errno;
|
||||
|
||||
/* We don't know daylight saving, so we reset this in order not
|
||||
* to confuse mktime(). */
|
||||
tm->tm_isdst = -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int clock_set_hwclock(const struct tm *tm) {
|
||||
_cleanup_close_ int fd = -1;
|
||||
|
||||
assert(tm);
|
||||
|
||||
fd = open("/dev/rtc", O_RDONLY|O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
if (ioctl(fd, RTC_SET_TIME, tm) < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int clock_is_localtime(const char* adjtime_path) {
|
||||
_cleanup_fclose_ FILE *f;
|
||||
int r;
|
||||
|
||||
if (!adjtime_path)
|
||||
adjtime_path = "/etc/adjtime";
|
||||
|
||||
/*
|
||||
* The third line of adjtime is "UTC" or "LOCAL" or nothing.
|
||||
* # /etc/adjtime
|
||||
* 0.0 0 0
|
||||
* 0
|
||||
* UTC
|
||||
*/
|
||||
f = fopen(adjtime_path, "re");
|
||||
if (f) {
|
||||
_cleanup_free_ char *line = NULL;
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < 2; i++) { /* skip the first two lines */
|
||||
r = read_line(f, LONG_LINE_MAX, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return false; /* less than three lines → default to UTC */
|
||||
}
|
||||
|
||||
r = read_line(f, LONG_LINE_MAX, &line);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return false; /* less than three lines → default to UTC */
|
||||
|
||||
return streq(line, "LOCAL");
|
||||
|
||||
} else if (errno != ENOENT)
|
||||
return -errno;
|
||||
|
||||
/* adjtime not present → default to UTC */
|
||||
return false;
|
||||
}
|
||||
|
||||
int clock_set_timezone(int *min) {
|
||||
const struct timeval *tv_null = NULL;
|
||||
struct timespec ts;
|
||||
struct tm tm;
|
||||
int minutesdelta;
|
||||
struct timezone tz;
|
||||
|
||||
assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
|
||||
assert_se(localtime_r(&ts.tv_sec, &tm));
|
||||
minutesdelta = tm.tm_gmtoff / 60;
|
||||
|
||||
tz.tz_minuteswest = -minutesdelta;
|
||||
tz.tz_dsttime = 0; /* DST_NONE */
|
||||
|
||||
/*
|
||||
* If the RTC does not run in UTC but in local time, the very first
|
||||
* call to settimeofday() will set the kernel's timezone and will warp the
|
||||
* system clock, so that it runs in UTC instead of the local time we
|
||||
* have read from the RTC.
|
||||
*/
|
||||
if (settimeofday(tv_null, &tz) < 0)
|
||||
return negative_errno();
|
||||
|
||||
if (min)
|
||||
*min = minutesdelta;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int clock_reset_timewarp(void) {
|
||||
const struct timeval *tv_null = NULL;
|
||||
struct timezone tz;
|
||||
|
||||
tz.tz_minuteswest = 0;
|
||||
tz.tz_dsttime = 0; /* DST_NONE */
|
||||
|
||||
/*
|
||||
* The very first call to settimeofday() does time warp magic. Do a
|
||||
* dummy call here, so the time warping is sealed and all later calls
|
||||
* behave as expected.
|
||||
*/
|
||||
if (settimeofday(tv_null, &tz) < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define TIME_EPOCH_USEC ((usec_t) TIME_EPOCH * USEC_PER_SEC)
|
||||
|
||||
int clock_apply_epoch(void) {
|
||||
struct timespec ts;
|
||||
|
||||
if (now(CLOCK_REALTIME) >= TIME_EPOCH_USEC)
|
||||
return 0;
|
||||
|
||||
if (clock_settime(CLOCK_REALTIME, timespec_store(&ts, TIME_EPOCH_USEC)) < 0)
|
||||
return -errno;
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
#pragma once
|
||||
|
||||
#include <time.h>
|
||||
|
||||
int clock_is_localtime(const char* adjtime_path);
|
||||
int clock_set_timezone(int *min);
|
||||
int clock_reset_timewarp(void);
|
||||
int clock_get_hwclock(struct tm *tm);
|
||||
int clock_set_hwclock(const struct tm *tm);
|
||||
int clock_apply_epoch(void);
|
||||
@@ -1,99 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "cpu-set-util.h"
|
||||
#include "extract-word.h"
|
||||
#include "log.h"
|
||||
#include "macro.h"
|
||||
#include "parse-util.h"
|
||||
#include "string-util.h"
|
||||
|
||||
cpu_set_t* cpu_set_malloc(unsigned *ncpus) {
|
||||
cpu_set_t *c;
|
||||
unsigned n = 1024;
|
||||
|
||||
/* Allocates the cpuset in the right size */
|
||||
|
||||
for (;;) {
|
||||
c = CPU_ALLOC(n);
|
||||
if (!c)
|
||||
return NULL;
|
||||
|
||||
if (sched_getaffinity(0, CPU_ALLOC_SIZE(n), c) >= 0) {
|
||||
CPU_ZERO_S(CPU_ALLOC_SIZE(n), c);
|
||||
|
||||
if (ncpus)
|
||||
*ncpus = n;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
CPU_FREE(c);
|
||||
|
||||
if (errno != EINVAL)
|
||||
return NULL;
|
||||
|
||||
n *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
int parse_cpu_set_internal(
|
||||
const char *rvalue,
|
||||
cpu_set_t **cpu_set,
|
||||
bool warn,
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *lvalue) {
|
||||
|
||||
_cleanup_cpu_free_ cpu_set_t *c = NULL;
|
||||
const char *p = rvalue;
|
||||
unsigned ncpus = 0;
|
||||
|
||||
assert(rvalue);
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *word = NULL;
|
||||
unsigned cpu, cpu_lower, cpu_upper;
|
||||
int r;
|
||||
|
||||
r = extract_first_word(&p, &word, WHITESPACE ",", EXTRACT_QUOTES);
|
||||
if (r == -ENOMEM)
|
||||
return warn ? log_oom() : -ENOMEM;
|
||||
if (r < 0)
|
||||
return warn ? log_syntax(unit, LOG_ERR, filename, line, r, "Invalid value for %s: %s", lvalue, rvalue) : r;
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
if (!c) {
|
||||
c = cpu_set_malloc(&ncpus);
|
||||
if (!c)
|
||||
return warn ? log_oom() : -ENOMEM;
|
||||
}
|
||||
|
||||
r = parse_range(word, &cpu_lower, &cpu_upper);
|
||||
if (r < 0)
|
||||
return warn ? log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse CPU affinity '%s'", word) : r;
|
||||
if (cpu_lower >= ncpus || cpu_upper >= ncpus)
|
||||
return warn ? log_syntax(unit, LOG_ERR, filename, line, EINVAL, "CPU out of range '%s' ncpus is %u", word, ncpus) : -EINVAL;
|
||||
|
||||
if (cpu_lower > cpu_upper) {
|
||||
if (warn)
|
||||
log_syntax(unit, LOG_WARNING, filename, line, 0, "Range '%s' is invalid, %u > %u, ignoring", word, cpu_lower, cpu_upper);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (cpu = cpu_lower; cpu <= cpu_upper; cpu++)
|
||||
CPU_SET_S(cpu, CPU_ALLOC_SIZE(ncpus), c);
|
||||
}
|
||||
|
||||
/* On success, sets *cpu_set and returns ncpus for the system. */
|
||||
if (c)
|
||||
*cpu_set = TAKE_PTR(c);
|
||||
|
||||
return (int) ncpus;
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
#pragma once
|
||||
|
||||
#include <sched.h>
|
||||
|
||||
#include "macro.h"
|
||||
|
||||
#ifdef __NCPUBITS
|
||||
#define CPU_SIZE_TO_NUM(n) ((n) * __NCPUBITS)
|
||||
#else
|
||||
#define CPU_SIZE_TO_NUM(n) ((n) * sizeof(cpu_set_t) * 8)
|
||||
#endif
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(cpu_set_t*, CPU_FREE);
|
||||
#define _cleanup_cpu_free_ _cleanup_(CPU_FREEp)
|
||||
|
||||
static inline cpu_set_t* cpu_set_mfree(cpu_set_t *p) {
|
||||
if (p)
|
||||
CPU_FREE(p);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cpu_set_t* cpu_set_malloc(unsigned *ncpus);
|
||||
|
||||
int parse_cpu_set_internal(const char *rvalue, cpu_set_t **cpu_set, bool warn, const char *unit, const char *filename, unsigned line, const char *lvalue);
|
||||
|
||||
static inline int parse_cpu_set_and_warn(const char *rvalue, cpu_set_t **cpu_set, const char *unit, const char *filename, unsigned line, const char *lvalue) {
|
||||
assert(lvalue);
|
||||
|
||||
return parse_cpu_set_internal(rvalue, cpu_set, true, unit, filename, line, lvalue);
|
||||
}
|
||||
|
||||
static inline int parse_cpu_set(const char *rvalue, cpu_set_t **cpu_set){
|
||||
return parse_cpu_set_internal(rvalue, cpu_set, false, NULL, NULL, 0, NULL);
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#if HAVE_LIBCRYPTSETUP
|
||||
#include "crypt-util.h"
|
||||
#include "log.h"
|
||||
|
||||
void cryptsetup_log_glue(int level, const char *msg, void *usrptr) {
|
||||
switch (level) {
|
||||
case CRYPT_LOG_NORMAL:
|
||||
level = LOG_NOTICE;
|
||||
break;
|
||||
case CRYPT_LOG_ERROR:
|
||||
level = LOG_ERR;
|
||||
break;
|
||||
case CRYPT_LOG_VERBOSE:
|
||||
level = LOG_INFO;
|
||||
break;
|
||||
case CRYPT_LOG_DEBUG:
|
||||
level = LOG_DEBUG;
|
||||
break;
|
||||
default:
|
||||
log_error("Unknown libcryptsetup log level: %d", level);
|
||||
level = LOG_ERR;
|
||||
}
|
||||
|
||||
log_full(level, "%s", msg);
|
||||
}
|
||||
#endif
|
||||
@@ -1,17 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
#pragma once
|
||||
|
||||
#if HAVE_LIBCRYPTSETUP
|
||||
#include <libcryptsetup.h>
|
||||
|
||||
#include "macro.h"
|
||||
|
||||
/* libcryptsetup define for any LUKS version, compatible with libcryptsetup 1.x */
|
||||
#ifndef CRYPT_LUKS
|
||||
#define CRYPT_LUKS NULL
|
||||
#endif
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(struct crypt_device *, crypt_free);
|
||||
|
||||
void cryptsetup_log_glue(int level, const char *msg, void *usrptr);
|
||||
#endif
|
||||
@@ -1,348 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "conf-files.h"
|
||||
#include "env-util.h"
|
||||
#include "exec-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "hashmap.h"
|
||||
#include "macro.h"
|
||||
#include "process-util.h"
|
||||
#include "serialize.h"
|
||||
#include "set.h"
|
||||
#include "signal-util.h"
|
||||
#include "stat-util.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "terminal-util.h"
|
||||
#include "util.h"
|
||||
|
||||
/* Put this test here for a lack of better place */
|
||||
assert_cc(EAGAIN == EWOULDBLOCK);
|
||||
|
||||
static int do_spawn(const char *path, char *argv[], int stdout_fd, pid_t *pid) {
|
||||
|
||||
pid_t _pid;
|
||||
int r;
|
||||
|
||||
if (null_or_empty_path(path)) {
|
||||
log_debug("%s is empty (a mask).", path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = safe_fork("(direxec)", FORK_DEATHSIG|FORK_LOG, &_pid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) {
|
||||
char *_argv[2];
|
||||
|
||||
if (stdout_fd >= 0) {
|
||||
r = rearrange_stdio(STDIN_FILENO, stdout_fd, STDERR_FILENO);
|
||||
if (r < 0)
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (!argv) {
|
||||
_argv[0] = (char*) path;
|
||||
_argv[1] = NULL;
|
||||
argv = _argv;
|
||||
} else
|
||||
argv[0] = (char*) path;
|
||||
|
||||
execv(path, argv);
|
||||
log_error_errno(errno, "Failed to execute %s: %m", path);
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
*pid = _pid;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int do_execute(
|
||||
char **directories,
|
||||
usec_t timeout,
|
||||
gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
|
||||
void* const callback_args[_STDOUT_CONSUME_MAX],
|
||||
int output_fd,
|
||||
char *argv[],
|
||||
char *envp[]) {
|
||||
|
||||
_cleanup_hashmap_free_free_ Hashmap *pids = NULL;
|
||||
_cleanup_strv_free_ char **paths = NULL;
|
||||
char **path, **e;
|
||||
int r;
|
||||
|
||||
/* We fork this all off from a child process so that we can somewhat cleanly make
|
||||
* use of SIGALRM to set a time limit.
|
||||
*
|
||||
* If callbacks is nonnull, execution is serial. Otherwise, we default to parallel.
|
||||
*/
|
||||
|
||||
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 log_error_errno(r, "Failed to enumerate executables: %m");
|
||||
|
||||
if (!callbacks) {
|
||||
pids = hashmap_new(NULL);
|
||||
if (!pids)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
/* Abort execution of this process after the timout. We simply rely on SIGALRM as
|
||||
* default action terminating the process, and turn on alarm(). */
|
||||
|
||||
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;
|
||||
pid_t pid;
|
||||
|
||||
t = strdup(*path);
|
||||
if (!t)
|
||||
return log_oom();
|
||||
|
||||
if (callbacks) {
|
||||
fd = open_serialization_fd(basename(*path));
|
||||
if (fd < 0)
|
||||
return log_error_errno(fd, "Failed to open serialization file: %m");
|
||||
}
|
||||
|
||||
r = do_spawn(t, argv, fd, &pid);
|
||||
if (r <= 0)
|
||||
continue;
|
||||
|
||||
if (pids) {
|
||||
r = hashmap_put(pids, PID_TO_PTR(pid), t);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
t = NULL;
|
||||
} else {
|
||||
r = wait_for_terminate_and_check(t, pid, WAIT_LOG);
|
||||
if (r < 0)
|
||||
continue;
|
||||
|
||||
if (lseek(fd, 0, SEEK_SET) < 0)
|
||||
return log_error_errno(errno, "Failed to seek on serialization fd: %m");
|
||||
|
||||
r = callbacks[STDOUT_GENERATE](fd, callback_args[STDOUT_GENERATE]);
|
||||
fd = -1;
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to process output from %s: %m", *path);
|
||||
}
|
||||
}
|
||||
|
||||
if (callbacks) {
|
||||
r = callbacks[STDOUT_COLLECT](output_fd, callback_args[STDOUT_COLLECT]);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Callback two failed: %m");
|
||||
}
|
||||
|
||||
while (!hashmap_isempty(pids)) {
|
||||
_cleanup_free_ char *t = NULL;
|
||||
pid_t pid;
|
||||
|
||||
pid = PTR_TO_PID(hashmap_first_key(pids));
|
||||
assert(pid > 0);
|
||||
|
||||
t = hashmap_remove(pids, PID_TO_PTR(pid));
|
||||
assert(t);
|
||||
|
||||
(void) wait_for_terminate_and_check(t, pid, WAIT_LOG);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int execute_directories(
|
||||
const char* const* directories,
|
||||
usec_t timeout,
|
||||
gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
|
||||
void* const callback_args[_STDOUT_CONSUME_MAX],
|
||||
char *argv[],
|
||||
char *envp[]) {
|
||||
|
||||
char **dirs = (char**) directories;
|
||||
_cleanup_close_ int fd = -1;
|
||||
char *name;
|
||||
int r;
|
||||
|
||||
assert(!strv_isempty(dirs));
|
||||
|
||||
name = basename(dirs[0]);
|
||||
assert(!isempty(name));
|
||||
|
||||
if (callbacks) {
|
||||
assert(callback_args);
|
||||
assert(callbacks[STDOUT_GENERATE]);
|
||||
assert(callbacks[STDOUT_COLLECT]);
|
||||
assert(callbacks[STDOUT_CONSUME]);
|
||||
|
||||
fd = open_serialization_fd(name);
|
||||
if (fd < 0)
|
||||
return log_error_errno(fd, "Failed to open serialization file: %m");
|
||||
}
|
||||
|
||||
/* Executes all binaries in the directories serially or in parallel and waits for
|
||||
* them to finish. Optionally a timeout is applied. If a file with the same name
|
||||
* exists in more than one directory, the earliest one wins. */
|
||||
|
||||
r = safe_fork("(sd-executor)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) {
|
||||
r = do_execute(dirs, timeout, callbacks, callback_args, fd, argv, envp);
|
||||
_exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
if (!callbacks)
|
||||
return 0;
|
||||
|
||||
if (lseek(fd, 0, SEEK_SET) < 0)
|
||||
return log_error_errno(errno, "Failed to rewind serialization fd: %m");
|
||||
|
||||
r = callbacks[STDOUT_CONSUME](fd, callback_args[STDOUT_CONSUME]);
|
||||
fd = -1;
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse returned data: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gather_environment_generate(int fd, void *arg) {
|
||||
char ***env = arg, **x, **y;
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
_cleanup_strv_free_ char **new = NULL;
|
||||
int r;
|
||||
|
||||
/* Read a series of VAR=value assignments from fd, use them to update the list of
|
||||
* variables in env. Also update the exported environment.
|
||||
*
|
||||
* fd is always consumed, even on error.
|
||||
*/
|
||||
|
||||
assert(env);
|
||||
|
||||
f = fdopen(fd, "r");
|
||||
if (!f) {
|
||||
safe_close(fd);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
r = load_env_file_pairs(f, NULL, &new);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
STRV_FOREACH_PAIR(x, y, new) {
|
||||
char *p;
|
||||
|
||||
if (!env_name_is_valid(*x)) {
|
||||
log_warning("Invalid variable assignment \"%s=...\", ignoring.", *x);
|
||||
continue;
|
||||
}
|
||||
|
||||
p = strjoin(*x, "=", *y);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
r = strv_env_replace(env, p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (setenv(*x, *y, true) < 0)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int gather_environment_collect(int fd, void *arg) {
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
char ***env = arg;
|
||||
int r;
|
||||
|
||||
/* Write out a series of env=cescape(VAR=value) assignments to fd. */
|
||||
|
||||
assert(env);
|
||||
|
||||
f = fdopen(fd, "w");
|
||||
if (!f) {
|
||||
safe_close(fd);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
r = serialize_strv(f, "env", *env);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = fflush_and_check(f);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gather_environment_consume(int fd, void *arg) {
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
char ***env = arg;
|
||||
int r = 0;
|
||||
|
||||
/* Read a series of env=cescape(VAR=value) assignments from fd into env. */
|
||||
|
||||
assert(env);
|
||||
|
||||
f = fdopen(fd, "re");
|
||||
if (!f) {
|
||||
safe_close(fd);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *line = NULL;
|
||||
const char *v;
|
||||
int k;
|
||||
|
||||
k = read_line(f, LONG_LINE_MAX, &line);
|
||||
if (k < 0)
|
||||
return k;
|
||||
if (k == 0)
|
||||
break;
|
||||
|
||||
v = startswith(line, "env=");
|
||||
if (!v) {
|
||||
log_debug("Serialization line \"%s\" unexpectedly didn't start with \"env=\".", line);
|
||||
if (r == 0)
|
||||
r = -EINVAL;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
k = deserialize_environment(v, env);
|
||||
if (k < 0) {
|
||||
log_debug_errno(k, "Invalid serialization line \"%s\": %m", line);
|
||||
|
||||
if (r == 0)
|
||||
r = k;
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
const gather_stdout_callback_t gather_environment[] = {
|
||||
gather_environment_generate,
|
||||
gather_environment_collect,
|
||||
gather_environment_consume,
|
||||
};
|
||||
@@ -1,25 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "time-util.h"
|
||||
|
||||
typedef int (*gather_stdout_callback_t) (int fd, void *arg);
|
||||
|
||||
enum {
|
||||
STDOUT_GENERATE, /* from generators to helper process */
|
||||
STDOUT_COLLECT, /* from helper process to main process */
|
||||
STDOUT_CONSUME, /* process data in main process */
|
||||
_STDOUT_CONSUME_MAX,
|
||||
};
|
||||
|
||||
int execute_directories(
|
||||
const char* const* directories,
|
||||
usec_t timeout,
|
||||
gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
|
||||
void* const callback_args[_STDOUT_CONSUME_MAX],
|
||||
char *argv[],
|
||||
char *envp[]);
|
||||
|
||||
extern const gather_stdout_callback_t gather_environment[_STDOUT_CONSUME_MAX];
|
||||
@@ -1,280 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <sysexits.h>
|
||||
|
||||
#include "exit-status.h"
|
||||
#include "macro.h"
|
||||
#include "set.h"
|
||||
|
||||
const char* exit_status_to_string(int status, ExitStatusLevel level) {
|
||||
|
||||
/* Exit status ranges:
|
||||
*
|
||||
* 0…1 │ ISO C, EXIT_SUCCESS + EXIT_FAILURE
|
||||
* 2…7 │ LSB exit codes for init scripts
|
||||
* 8…63 │ (Currently unmapped)
|
||||
* 64…78 │ BSD defined exit codes
|
||||
* 79…199 │ (Currently unmapped)
|
||||
* 200…241 │ systemd's private error codes (might be extended to 254 in future development)
|
||||
* 242…254 │ (Currently unmapped, but see above)
|
||||
* 255 │ (We should probably stay away from that one, it's frequently used by applications to indicate an
|
||||
* │ exit reason that cannot really be expressed in a single exit status value — such as a propagated
|
||||
* │ signal or such)
|
||||
*/
|
||||
|
||||
switch (status) { /* We always cover the ISO C ones */
|
||||
|
||||
case EXIT_SUCCESS:
|
||||
return "SUCCESS";
|
||||
|
||||
case EXIT_FAILURE:
|
||||
return "FAILURE";
|
||||
}
|
||||
|
||||
if (IN_SET(level, EXIT_STATUS_SYSTEMD, EXIT_STATUS_LSB, EXIT_STATUS_FULL)) {
|
||||
switch (status) { /* Optionally we cover our own ones */
|
||||
|
||||
case EXIT_CHDIR:
|
||||
return "CHDIR";
|
||||
|
||||
case EXIT_NICE:
|
||||
return "NICE";
|
||||
|
||||
case EXIT_FDS:
|
||||
return "FDS";
|
||||
|
||||
case EXIT_EXEC:
|
||||
return "EXEC";
|
||||
|
||||
case EXIT_MEMORY:
|
||||
return "MEMORY";
|
||||
|
||||
case EXIT_LIMITS:
|
||||
return "LIMITS";
|
||||
|
||||
case EXIT_OOM_ADJUST:
|
||||
return "OOM_ADJUST";
|
||||
|
||||
case EXIT_SIGNAL_MASK:
|
||||
return "SIGNAL_MASK";
|
||||
|
||||
case EXIT_STDIN:
|
||||
return "STDIN";
|
||||
|
||||
case EXIT_STDOUT:
|
||||
return "STDOUT";
|
||||
|
||||
case EXIT_CHROOT:
|
||||
return "CHROOT";
|
||||
|
||||
case EXIT_IOPRIO:
|
||||
return "IOPRIO";
|
||||
|
||||
case EXIT_TIMERSLACK:
|
||||
return "TIMERSLACK";
|
||||
|
||||
case EXIT_SECUREBITS:
|
||||
return "SECUREBITS";
|
||||
|
||||
case EXIT_SETSCHEDULER:
|
||||
return "SETSCHEDULER";
|
||||
|
||||
case EXIT_CPUAFFINITY:
|
||||
return "CPUAFFINITY";
|
||||
|
||||
case EXIT_GROUP:
|
||||
return "GROUP";
|
||||
|
||||
case EXIT_USER:
|
||||
return "USER";
|
||||
|
||||
case EXIT_CAPABILITIES:
|
||||
return "CAPABILITIES";
|
||||
|
||||
case EXIT_CGROUP:
|
||||
return "CGROUP";
|
||||
|
||||
case EXIT_SETSID:
|
||||
return "SETSID";
|
||||
|
||||
case EXIT_CONFIRM:
|
||||
return "CONFIRM";
|
||||
|
||||
case EXIT_STDERR:
|
||||
return "STDERR";
|
||||
|
||||
case EXIT_PAM:
|
||||
return "PAM";
|
||||
|
||||
case EXIT_NETWORK:
|
||||
return "NETWORK";
|
||||
|
||||
case EXIT_NAMESPACE:
|
||||
return "NAMESPACE";
|
||||
|
||||
case EXIT_NO_NEW_PRIVILEGES:
|
||||
return "NO_NEW_PRIVILEGES";
|
||||
|
||||
case EXIT_SECCOMP:
|
||||
return "SECCOMP";
|
||||
|
||||
case EXIT_SELINUX_CONTEXT:
|
||||
return "SELINUX_CONTEXT";
|
||||
|
||||
case EXIT_PERSONALITY:
|
||||
return "PERSONALITY";
|
||||
|
||||
case EXIT_APPARMOR_PROFILE:
|
||||
return "APPARMOR";
|
||||
|
||||
case EXIT_ADDRESS_FAMILIES:
|
||||
return "ADDRESS_FAMILIES";
|
||||
|
||||
case EXIT_RUNTIME_DIRECTORY:
|
||||
return "RUNTIME_DIRECTORY";
|
||||
|
||||
case EXIT_CHOWN:
|
||||
return "CHOWN";
|
||||
|
||||
case EXIT_SMACK_PROCESS_LABEL:
|
||||
return "SMACK_PROCESS_LABEL";
|
||||
|
||||
case EXIT_KEYRING:
|
||||
return "KEYRING";
|
||||
|
||||
case EXIT_STATE_DIRECTORY:
|
||||
return "STATE_DIRECTORY";
|
||||
|
||||
case EXIT_CACHE_DIRECTORY:
|
||||
return "CACHE_DIRECTORY";
|
||||
|
||||
case EXIT_LOGS_DIRECTORY:
|
||||
return "LOGS_DIRECTORY";
|
||||
|
||||
case EXIT_CONFIGURATION_DIRECTORY:
|
||||
return "CONFIGURATION_DIRECTORY";
|
||||
}
|
||||
}
|
||||
|
||||
if (IN_SET(level, EXIT_STATUS_LSB, EXIT_STATUS_FULL)) {
|
||||
switch (status) { /* Optionally we support LSB ones */
|
||||
|
||||
case EXIT_INVALIDARGUMENT:
|
||||
return "INVALIDARGUMENT";
|
||||
|
||||
case EXIT_NOTIMPLEMENTED:
|
||||
return "NOTIMPLEMENTED";
|
||||
|
||||
case EXIT_NOPERMISSION:
|
||||
return "NOPERMISSION";
|
||||
|
||||
case EXIT_NOTINSTALLED:
|
||||
return "NOTINSTALLED";
|
||||
|
||||
case EXIT_NOTCONFIGURED:
|
||||
return "NOTCONFIGURED";
|
||||
|
||||
case EXIT_NOTRUNNING:
|
||||
return "NOTRUNNING";
|
||||
}
|
||||
}
|
||||
|
||||
if (level == EXIT_STATUS_FULL) {
|
||||
switch (status) { /* Optionally, we support BSD exit statusses */
|
||||
|
||||
case EX_USAGE:
|
||||
return "USAGE";
|
||||
|
||||
case EX_DATAERR:
|
||||
return "DATAERR";
|
||||
|
||||
case EX_NOINPUT:
|
||||
return "NOINPUT";
|
||||
|
||||
case EX_NOUSER:
|
||||
return "NOUSER";
|
||||
|
||||
case EX_NOHOST:
|
||||
return "NOHOST";
|
||||
|
||||
case EX_UNAVAILABLE:
|
||||
return "UNAVAILABLE";
|
||||
|
||||
case EX_SOFTWARE:
|
||||
return "SOFTWARE";
|
||||
|
||||
case EX_OSERR:
|
||||
return "OSERR";
|
||||
|
||||
case EX_OSFILE:
|
||||
return "OSFILE";
|
||||
|
||||
case EX_CANTCREAT:
|
||||
return "CANTCREAT";
|
||||
|
||||
case EX_IOERR:
|
||||
return "IOERR";
|
||||
|
||||
case EX_TEMPFAIL:
|
||||
return "TEMPFAIL";
|
||||
|
||||
case EX_PROTOCOL:
|
||||
return "PROTOCOL";
|
||||
|
||||
case EX_NOPERM:
|
||||
return "NOPERM";
|
||||
|
||||
case EX_CONFIG:
|
||||
return "CONFIG";
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool is_clean_exit(int code, int status, ExitClean clean, ExitStatusSet *success_status) {
|
||||
|
||||
if (code == CLD_EXITED)
|
||||
return status == 0 ||
|
||||
(success_status &&
|
||||
set_contains(success_status->status, INT_TO_PTR(status)));
|
||||
|
||||
/* If a daemon does not implement handlers for some of the signals that's not considered an unclean shutdown */
|
||||
if (code == CLD_KILLED)
|
||||
return
|
||||
(clean == EXIT_CLEAN_DAEMON && IN_SET(status, SIGHUP, SIGINT, SIGTERM, SIGPIPE)) ||
|
||||
(success_status &&
|
||||
set_contains(success_status->signal, INT_TO_PTR(status)));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void exit_status_set_free(ExitStatusSet *x) {
|
||||
assert(x);
|
||||
|
||||
x->status = set_free(x->status);
|
||||
x->signal = set_free(x->signal);
|
||||
}
|
||||
|
||||
bool exit_status_set_is_empty(ExitStatusSet *x) {
|
||||
if (!x)
|
||||
return true;
|
||||
|
||||
return set_isempty(x->status) && set_isempty(x->signal);
|
||||
}
|
||||
|
||||
bool exit_status_set_test(ExitStatusSet *x, int code, int status) {
|
||||
|
||||
if (exit_status_set_is_empty(x))
|
||||
return false;
|
||||
|
||||
if (code == CLD_EXITED && set_contains(x->status, INT_TO_PTR(status)))
|
||||
return true;
|
||||
|
||||
if (IN_SET(code, CLD_KILLED, CLD_DUMPED) && set_contains(x->signal, INT_TO_PTR(status)))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "hashmap.h"
|
||||
#include "macro.h"
|
||||
#include "set.h"
|
||||
|
||||
/* This defines pretty names for the LSB 'start' verb exit codes. Note that they shouldn't be confused with the LSB
|
||||
* 'status' verb exit codes which are defined very differently. For details see:
|
||||
*
|
||||
* https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html
|
||||
*/
|
||||
|
||||
enum {
|
||||
/* EXIT_SUCCESS defined by libc */
|
||||
/* EXIT_FAILURE defined by libc */
|
||||
EXIT_INVALIDARGUMENT = 2,
|
||||
EXIT_NOTIMPLEMENTED = 3,
|
||||
EXIT_NOPERMISSION = 4,
|
||||
EXIT_NOTINSTALLED = 5,
|
||||
EXIT_NOTCONFIGURED = 6,
|
||||
EXIT_NOTRUNNING = 7,
|
||||
|
||||
/* BSD's sysexits.h defines a couple EX_xyz exit codes in the range 64 … 78 */
|
||||
|
||||
/* The LSB suggests that error codes >= 200 are "reserved". We use them here under the assumption that they
|
||||
* hence are unused by init scripts. */
|
||||
EXIT_CHDIR = 200,
|
||||
EXIT_NICE,
|
||||
EXIT_FDS,
|
||||
EXIT_EXEC,
|
||||
EXIT_MEMORY,
|
||||
EXIT_LIMITS,
|
||||
EXIT_OOM_ADJUST,
|
||||
EXIT_SIGNAL_MASK,
|
||||
EXIT_STDIN,
|
||||
EXIT_STDOUT,
|
||||
EXIT_CHROOT, /* 210 */
|
||||
EXIT_IOPRIO,
|
||||
EXIT_TIMERSLACK,
|
||||
EXIT_SECUREBITS,
|
||||
EXIT_SETSCHEDULER,
|
||||
EXIT_CPUAFFINITY,
|
||||
EXIT_GROUP,
|
||||
EXIT_USER,
|
||||
EXIT_CAPABILITIES,
|
||||
EXIT_CGROUP,
|
||||
EXIT_SETSID, /* 220 */
|
||||
EXIT_CONFIRM,
|
||||
EXIT_STDERR,
|
||||
_EXIT_RESERVED, /* used to be tcpwrap, don't reuse! */
|
||||
EXIT_PAM,
|
||||
EXIT_NETWORK,
|
||||
EXIT_NAMESPACE,
|
||||
EXIT_NO_NEW_PRIVILEGES,
|
||||
EXIT_SECCOMP,
|
||||
EXIT_SELINUX_CONTEXT,
|
||||
EXIT_PERSONALITY, /* 230 */
|
||||
EXIT_APPARMOR_PROFILE,
|
||||
EXIT_ADDRESS_FAMILIES,
|
||||
EXIT_RUNTIME_DIRECTORY,
|
||||
_EXIT_RESERVED2, /* used to be used by kdbus, don't reuse */
|
||||
EXIT_CHOWN,
|
||||
EXIT_SMACK_PROCESS_LABEL,
|
||||
EXIT_KEYRING,
|
||||
EXIT_STATE_DIRECTORY,
|
||||
EXIT_CACHE_DIRECTORY,
|
||||
EXIT_LOGS_DIRECTORY, /* 240 */
|
||||
EXIT_CONFIGURATION_DIRECTORY,
|
||||
};
|
||||
|
||||
typedef enum ExitStatusLevel {
|
||||
EXIT_STATUS_MINIMAL, /* only cover libc EXIT_STATUS/EXIT_FAILURE */
|
||||
EXIT_STATUS_SYSTEMD, /* cover libc and systemd's own exit codes */
|
||||
EXIT_STATUS_LSB, /* cover libc, systemd's own and LSB exit codes */
|
||||
EXIT_STATUS_FULL, /* cover libc, systemd's own, LSB and BSD (EX_xyz) exit codes */
|
||||
} ExitStatusLevel;
|
||||
|
||||
typedef struct ExitStatusSet {
|
||||
Set *status;
|
||||
Set *signal;
|
||||
} ExitStatusSet;
|
||||
|
||||
const char* exit_status_to_string(int status, ExitStatusLevel level) _const_;
|
||||
|
||||
typedef enum ExitClean {
|
||||
EXIT_CLEAN_DAEMON,
|
||||
EXIT_CLEAN_COMMAND,
|
||||
} ExitClean;
|
||||
|
||||
bool is_clean_exit(int code, int status, ExitClean clean, ExitStatusSet *success_status);
|
||||
|
||||
void exit_status_set_free(ExitStatusSet *x);
|
||||
bool exit_status_set_is_empty(ExitStatusSet *x);
|
||||
bool exit_status_set_test(ExitStatusSet *x, int code, int status);
|
||||
@@ -1,66 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "fileio-label.h"
|
||||
#include "fileio.h"
|
||||
#include "selinux-util.h"
|
||||
|
||||
int write_string_file_atomic_label_ts(const char *fn, const char *line, struct timespec *ts) {
|
||||
int r;
|
||||
|
||||
r = mac_selinux_create_file_prepare(fn, S_IFREG);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = write_string_file_ts(fn, line, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC, ts);
|
||||
|
||||
mac_selinux_create_file_clear();
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int write_env_file_label(const char *fname, char **l) {
|
||||
int r;
|
||||
|
||||
r = mac_selinux_create_file_prepare(fname, S_IFREG);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = write_env_file(fname, l);
|
||||
|
||||
mac_selinux_create_file_clear();
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int fopen_temporary_label(const char *target,
|
||||
const char *path, FILE **f, char **temp_path) {
|
||||
int r;
|
||||
|
||||
r = mac_selinux_create_file_prepare(target, S_IFREG);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = fopen_temporary(path, f, temp_path);
|
||||
|
||||
mac_selinux_create_file_clear();
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int create_shutdown_run_nologin_or_warn(void) {
|
||||
int r;
|
||||
|
||||
/* This is used twice: once in systemd-user-sessions.service, in order to block logins when we actually go
|
||||
* down, and once in systemd-logind.service when shutdowns are scheduled, and logins are to be turned off a bit
|
||||
* in advance. We use the same wording of the message in both cases. */
|
||||
|
||||
r = write_string_file_atomic_label("/run/nologin",
|
||||
"System is going down. Unprivileged users are not permitted to log in anymore. "
|
||||
"For technical details, see pam_nologin(8).");
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create /run/nologin: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "fileio.h"
|
||||
|
||||
/* These functions are split out of fileio.h (and not for examplement just as flags to the functions they wrap) in
|
||||
* order to optimize linking: This way, -lselinux is needed only for the callers of these functions that need selinux,
|
||||
* but not for all */
|
||||
|
||||
int write_string_file_atomic_label_ts(const char *fn, const char *line, struct timespec *ts);
|
||||
static inline int write_string_file_atomic_label(const char *fn, const char *line) {
|
||||
return write_string_file_atomic_label_ts(fn, line, NULL);
|
||||
}
|
||||
int write_env_file_label(const char *fname, char **l);
|
||||
int fopen_temporary_label(const char *target, const char *path, FILE **f, char **temp_path);
|
||||
|
||||
int create_shutdown_run_nologin_or_warn(void);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,62 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "macro.h"
|
||||
|
||||
typedef enum TableDataType {
|
||||
TABLE_EMPTY,
|
||||
TABLE_STRING,
|
||||
TABLE_BOOLEAN,
|
||||
TABLE_TIMESTAMP,
|
||||
TABLE_TIMESPAN,
|
||||
TABLE_SIZE,
|
||||
TABLE_UINT32,
|
||||
_TABLE_DATA_TYPE_MAX,
|
||||
_TABLE_DATA_TYPE_INVALID = -1,
|
||||
} TableDataType;
|
||||
|
||||
typedef struct Table Table;
|
||||
typedef struct TableCell TableCell;
|
||||
|
||||
Table *table_new_internal(const char *first_header, ...) _sentinel_;
|
||||
#define table_new(...) table_new_internal(__VA_ARGS__, NULL)
|
||||
Table *table_new_raw(size_t n_columns);
|
||||
Table *table_unref(Table *t);
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(Table*, table_unref);
|
||||
|
||||
int table_add_cell_full(Table *t, TableCell **ret_cell, TableDataType type, 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 type, const void *data) {
|
||||
return table_add_cell_full(t, ret_cell, type, data, (size_t) -1, (size_t) -1, (unsigned) -1, (unsigned) -1, (unsigned) -1);
|
||||
}
|
||||
|
||||
int table_dup_cell(Table *t, TableCell *cell);
|
||||
|
||||
int table_set_minimum_width(Table *t, TableCell *cell, size_t minimum_width);
|
||||
int table_set_maximum_width(Table *t, TableCell *cell, size_t maximum_width);
|
||||
int table_set_weight(Table *t, TableCell *cell, unsigned weight);
|
||||
int table_set_align_percent(Table *t, TableCell *cell, unsigned percent);
|
||||
int table_set_ellipsize_percent(Table *t, TableCell *cell, unsigned percent);
|
||||
int table_set_color(Table *t, TableCell *cell, const char *color);
|
||||
|
||||
int table_add_many_internal(Table *t, TableDataType first_type, ...);
|
||||
#define table_add_many(t, ...) table_add_many_internal(t, __VA_ARGS__, _TABLE_DATA_TYPE_MAX)
|
||||
|
||||
void table_set_header(Table *table, bool b);
|
||||
void table_set_width(Table *t, size_t width);
|
||||
int table_set_display(Table *t, size_t first_column, ...);
|
||||
int table_set_sort(Table *t, size_t first_column, ...);
|
||||
|
||||
int table_print(Table *t, FILE *f);
|
||||
int table_format(Table *t, char **ret);
|
||||
|
||||
static inline TableCell* TABLE_HEADER_CELL(size_t i) {
|
||||
return SIZE_TO_PTR(i + 1);
|
||||
}
|
||||
|
||||
size_t table_get_rows(Table *t);
|
||||
size_t table_get_columns(Table *t);
|
||||
@@ -1,502 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "escape.h"
|
||||
#include "fd-util.h"
|
||||
#include "io-util.h"
|
||||
#include "journal-file.h"
|
||||
#include "journal-importer.h"
|
||||
#include "journal-util.h"
|
||||
#include "parse-util.h"
|
||||
#include "string-util.h"
|
||||
#include "unaligned.h"
|
||||
|
||||
enum {
|
||||
IMPORTER_STATE_LINE = 0, /* waiting to read, or reading line */
|
||||
IMPORTER_STATE_DATA_START, /* reading binary data header */
|
||||
IMPORTER_STATE_DATA, /* reading binary data */
|
||||
IMPORTER_STATE_DATA_FINISH, /* expecting newline */
|
||||
IMPORTER_STATE_EOF, /* done */
|
||||
};
|
||||
|
||||
static int iovw_put(struct iovec_wrapper *iovw, void* data, size_t len) {
|
||||
if (!GREEDY_REALLOC(iovw->iovec, iovw->size_bytes, iovw->count + 1))
|
||||
return log_oom();
|
||||
|
||||
iovw->iovec[iovw->count++] = IOVEC_MAKE(data, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void iovw_free_contents(struct iovec_wrapper *iovw) {
|
||||
iovw->iovec = mfree(iovw->iovec);
|
||||
iovw->size_bytes = iovw->count = 0;
|
||||
}
|
||||
|
||||
static void iovw_rebase(struct iovec_wrapper *iovw, char *old, char *new) {
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < iovw->count; i++)
|
||||
iovw->iovec[i].iov_base = (char*) iovw->iovec[i].iov_base - old + new;
|
||||
}
|
||||
|
||||
size_t iovw_size(struct iovec_wrapper *iovw) {
|
||||
size_t n = 0, i;
|
||||
|
||||
for (i = 0; i < iovw->count; i++)
|
||||
n += iovw->iovec[i].iov_len;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
void journal_importer_cleanup(JournalImporter *imp) {
|
||||
if (imp->fd >= 0 && !imp->passive_fd) {
|
||||
log_debug("Closing %s (fd=%d)", imp->name ?: "importer", imp->fd);
|
||||
safe_close(imp->fd);
|
||||
}
|
||||
|
||||
free(imp->name);
|
||||
free(imp->buf);
|
||||
iovw_free_contents(&imp->iovw);
|
||||
}
|
||||
|
||||
static char* realloc_buffer(JournalImporter *imp, size_t size) {
|
||||
char *b, *old = imp->buf;
|
||||
|
||||
b = GREEDY_REALLOC(imp->buf, imp->size, size);
|
||||
if (!b)
|
||||
return NULL;
|
||||
|
||||
iovw_rebase(&imp->iovw, old, imp->buf);
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
static int get_line(JournalImporter *imp, char **line, size_t *size) {
|
||||
ssize_t n;
|
||||
char *c = NULL;
|
||||
|
||||
assert(imp);
|
||||
assert(imp->state == IMPORTER_STATE_LINE);
|
||||
assert(imp->offset <= imp->filled);
|
||||
assert(imp->filled <= imp->size);
|
||||
assert(!imp->buf || imp->size > 0);
|
||||
assert(imp->fd >= 0);
|
||||
|
||||
for (;;) {
|
||||
if (imp->buf) {
|
||||
size_t start = MAX(imp->scanned, imp->offset);
|
||||
|
||||
c = memchr(imp->buf + start, '\n',
|
||||
imp->filled - start);
|
||||
if (c != NULL)
|
||||
break;
|
||||
}
|
||||
|
||||
imp->scanned = imp->filled;
|
||||
if (imp->scanned >= DATA_SIZE_MAX) {
|
||||
log_error("Entry is bigger than %u bytes.", DATA_SIZE_MAX);
|
||||
return -E2BIG;
|
||||
}
|
||||
|
||||
if (imp->passive_fd)
|
||||
/* we have to wait for some data to come to us */
|
||||
return -EAGAIN;
|
||||
|
||||
/* We know that imp->filled is at most DATA_SIZE_MAX, so if
|
||||
we reallocate it, we'll increase the size at least a bit. */
|
||||
assert_cc(DATA_SIZE_MAX < ENTRY_SIZE_MAX);
|
||||
if (imp->size - imp->filled < LINE_CHUNK &&
|
||||
!realloc_buffer(imp, MIN(imp->filled + LINE_CHUNK, ENTRY_SIZE_MAX)))
|
||||
return log_oom();
|
||||
|
||||
assert(imp->buf);
|
||||
assert(imp->size - imp->filled >= LINE_CHUNK ||
|
||||
imp->size == ENTRY_SIZE_MAX);
|
||||
|
||||
n = read(imp->fd,
|
||||
imp->buf + imp->filled,
|
||||
imp->size - imp->filled);
|
||||
if (n < 0) {
|
||||
if (errno != EAGAIN)
|
||||
log_error_errno(errno, "read(%d, ..., %zu): %m",
|
||||
imp->fd,
|
||||
imp->size - imp->filled);
|
||||
return -errno;
|
||||
} else if (n == 0)
|
||||
return 0;
|
||||
|
||||
imp->filled += n;
|
||||
}
|
||||
|
||||
*line = imp->buf + imp->offset;
|
||||
*size = c + 1 - imp->buf - imp->offset;
|
||||
imp->offset += *size;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int fill_fixed_size(JournalImporter *imp, void **data, size_t size) {
|
||||
|
||||
assert(imp);
|
||||
assert(IN_SET(imp->state, IMPORTER_STATE_DATA_START, IMPORTER_STATE_DATA, IMPORTER_STATE_DATA_FINISH));
|
||||
assert(size <= DATA_SIZE_MAX);
|
||||
assert(imp->offset <= imp->filled);
|
||||
assert(imp->filled <= imp->size);
|
||||
assert(imp->buf || imp->size == 0);
|
||||
assert(!imp->buf || imp->size > 0);
|
||||
assert(imp->fd >= 0);
|
||||
assert(data);
|
||||
|
||||
while (imp->filled - imp->offset < size) {
|
||||
int n;
|
||||
|
||||
if (imp->passive_fd)
|
||||
/* we have to wait for some data to come to us */
|
||||
return -EAGAIN;
|
||||
|
||||
if (!realloc_buffer(imp, imp->offset + size))
|
||||
return log_oom();
|
||||
|
||||
n = read(imp->fd, imp->buf + imp->filled,
|
||||
imp->size - imp->filled);
|
||||
if (n < 0) {
|
||||
if (errno != EAGAIN)
|
||||
log_error_errno(errno, "read(%d, ..., %zu): %m", imp->fd,
|
||||
imp->size - imp->filled);
|
||||
return -errno;
|
||||
} else if (n == 0)
|
||||
return 0;
|
||||
|
||||
imp->filled += n;
|
||||
}
|
||||
|
||||
*data = imp->buf + imp->offset;
|
||||
imp->offset += size;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int get_data_size(JournalImporter *imp) {
|
||||
int r;
|
||||
void *data;
|
||||
|
||||
assert(imp);
|
||||
assert(imp->state == IMPORTER_STATE_DATA_START);
|
||||
assert(imp->data_size == 0);
|
||||
|
||||
r = fill_fixed_size(imp, &data, sizeof(uint64_t));
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
imp->data_size = unaligned_read_le64(data);
|
||||
if (imp->data_size > DATA_SIZE_MAX) {
|
||||
log_error("Stream declares field with size %zu > DATA_SIZE_MAX = %u",
|
||||
imp->data_size, DATA_SIZE_MAX);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (imp->data_size == 0)
|
||||
log_warning("Binary field with zero length");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int get_data_data(JournalImporter *imp, void **data) {
|
||||
int r;
|
||||
|
||||
assert(imp);
|
||||
assert(data);
|
||||
assert(imp->state == IMPORTER_STATE_DATA);
|
||||
|
||||
r = fill_fixed_size(imp, data, imp->data_size);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int get_data_newline(JournalImporter *imp) {
|
||||
int r;
|
||||
char *data;
|
||||
|
||||
assert(imp);
|
||||
assert(imp->state == IMPORTER_STATE_DATA_FINISH);
|
||||
|
||||
r = fill_fixed_size(imp, (void**) &data, 1);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
assert(data);
|
||||
if (*data != '\n') {
|
||||
char buf[4];
|
||||
int l;
|
||||
|
||||
l = cescape_char(*data, buf);
|
||||
log_error("Expected newline, got '%.*s'", l, buf);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int process_special_field(JournalImporter *imp, char *line) {
|
||||
const char *value;
|
||||
char buf[CELLESCAPE_DEFAULT_LENGTH];
|
||||
int r;
|
||||
|
||||
assert(line);
|
||||
|
||||
value = startswith(line, "__CURSOR=");
|
||||
if (value)
|
||||
/* ignore __CURSOR */
|
||||
return 1;
|
||||
|
||||
value = startswith(line, "__REALTIME_TIMESTAMP=");
|
||||
if (value) {
|
||||
uint64_t x;
|
||||
|
||||
r = safe_atou64(value, &x);
|
||||
if (r < 0)
|
||||
return log_warning_errno(r, "Failed to parse __REALTIME_TIMESTAMP '%s': %m",
|
||||
cellescape(buf, sizeof buf, value));
|
||||
else if (!VALID_REALTIME(x)) {
|
||||
log_warning("__REALTIME_TIMESTAMP out of range, ignoring: %"PRIu64, x);
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
imp->ts.realtime = x;
|
||||
return 1;
|
||||
}
|
||||
|
||||
value = startswith(line, "__MONOTONIC_TIMESTAMP=");
|
||||
if (value) {
|
||||
uint64_t x;
|
||||
|
||||
r = safe_atou64(value, &x);
|
||||
if (r < 0)
|
||||
return log_warning_errno(r, "Failed to parse __MONOTONIC_TIMESTAMP '%s': %m",
|
||||
cellescape(buf, sizeof buf, value));
|
||||
else if (!VALID_MONOTONIC(x)) {
|
||||
log_warning("__MONOTONIC_TIMESTAMP out of range, ignoring: %"PRIu64, x);
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
imp->ts.monotonic = x;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Just a single underline, but it needs special treatment too. */
|
||||
value = startswith(line, "_BOOT_ID=");
|
||||
if (value) {
|
||||
r = sd_id128_from_string(value, &imp->boot_id);
|
||||
if (r < 0)
|
||||
return log_warning_errno(r, "Failed to parse _BOOT_ID '%s': %m",
|
||||
cellescape(buf, sizeof buf, value));
|
||||
|
||||
/* store the field in the usual fashion too */
|
||||
return 0;
|
||||
}
|
||||
|
||||
value = startswith(line, "__");
|
||||
if (value) {
|
||||
log_notice("Unknown dunder line __%s, ignoring.", cellescape(buf, sizeof buf, value));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* no dunder */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int journal_importer_process_data(JournalImporter *imp) {
|
||||
int r;
|
||||
|
||||
switch(imp->state) {
|
||||
case IMPORTER_STATE_LINE: {
|
||||
char *line, *sep;
|
||||
size_t n = 0;
|
||||
|
||||
assert(imp->data_size == 0);
|
||||
|
||||
r = get_line(imp, &line, &n);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) {
|
||||
imp->state = IMPORTER_STATE_EOF;
|
||||
return 0;
|
||||
}
|
||||
assert(n > 0);
|
||||
assert(line[n-1] == '\n');
|
||||
|
||||
if (n == 1) {
|
||||
log_trace("Received empty line, event is ready");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* MESSAGE=xxx\n
|
||||
or
|
||||
COREDUMP\n
|
||||
LLLLLLLL0011223344...\n
|
||||
*/
|
||||
sep = memchr(line, '=', n);
|
||||
if (sep) {
|
||||
/* chomp newline */
|
||||
n--;
|
||||
|
||||
if (!journal_field_valid(line, sep - line, true)) {
|
||||
char buf[64], *t;
|
||||
|
||||
t = strndupa(line, sep - line);
|
||||
log_debug("Ignoring invalid field: \"%s\"",
|
||||
cellescape(buf, sizeof buf, t));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
line[n] = '\0';
|
||||
r = process_special_field(imp, line);
|
||||
if (r != 0)
|
||||
return r < 0 ? r : 0;
|
||||
|
||||
r = iovw_put(&imp->iovw, line, n);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else {
|
||||
/* replace \n with = */
|
||||
line[n-1] = '=';
|
||||
|
||||
imp->field_len = n;
|
||||
imp->state = IMPORTER_STATE_DATA_START;
|
||||
|
||||
/* we cannot put the field in iovec until we have all data */
|
||||
}
|
||||
|
||||
log_trace("Received: %.*s (%s)", (int) n, line, sep ? "text" : "binary");
|
||||
|
||||
return 0; /* continue */
|
||||
}
|
||||
|
||||
case IMPORTER_STATE_DATA_START:
|
||||
assert(imp->data_size == 0);
|
||||
|
||||
r = get_data_size(imp);
|
||||
// log_debug("get_data_size() -> %d", r);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) {
|
||||
imp->state = IMPORTER_STATE_EOF;
|
||||
return 0;
|
||||
}
|
||||
|
||||
imp->state = imp->data_size > 0 ?
|
||||
IMPORTER_STATE_DATA : IMPORTER_STATE_DATA_FINISH;
|
||||
|
||||
return 0; /* continue */
|
||||
|
||||
case IMPORTER_STATE_DATA: {
|
||||
void *data;
|
||||
char *field;
|
||||
|
||||
assert(imp->data_size > 0);
|
||||
|
||||
r = get_data_data(imp, &data);
|
||||
// log_debug("get_data_data() -> %d", r);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) {
|
||||
imp->state = IMPORTER_STATE_EOF;
|
||||
return 0;
|
||||
}
|
||||
|
||||
assert(data);
|
||||
|
||||
field = (char*) data - sizeof(uint64_t) - imp->field_len;
|
||||
memmove(field + sizeof(uint64_t), field, imp->field_len);
|
||||
|
||||
r = iovw_put(&imp->iovw, field + sizeof(uint64_t), imp->field_len + imp->data_size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
imp->state = IMPORTER_STATE_DATA_FINISH;
|
||||
|
||||
return 0; /* continue */
|
||||
}
|
||||
|
||||
case IMPORTER_STATE_DATA_FINISH:
|
||||
r = get_data_newline(imp);
|
||||
// log_debug("get_data_newline() -> %d", r);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) {
|
||||
imp->state = IMPORTER_STATE_EOF;
|
||||
return 0;
|
||||
}
|
||||
|
||||
imp->data_size = 0;
|
||||
imp->state = IMPORTER_STATE_LINE;
|
||||
|
||||
return 0; /* continue */
|
||||
default:
|
||||
assert_not_reached("wtf?");
|
||||
}
|
||||
}
|
||||
|
||||
int journal_importer_push_data(JournalImporter *imp, const char *data, size_t size) {
|
||||
assert(imp);
|
||||
assert(imp->state != IMPORTER_STATE_EOF);
|
||||
|
||||
if (!realloc_buffer(imp, imp->filled + size)) {
|
||||
log_error("Failed to store received data of size %zu "
|
||||
"(in addition to existing %zu bytes with %zu filled): %s",
|
||||
size, imp->size, imp->filled, strerror(ENOMEM));
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memcpy(imp->buf + imp->filled, data, size);
|
||||
imp->filled += size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void journal_importer_drop_iovw(JournalImporter *imp) {
|
||||
size_t remain, target;
|
||||
|
||||
/* This function drops processed data that along with the iovw that points at it */
|
||||
|
||||
iovw_free_contents(&imp->iovw);
|
||||
|
||||
/* possibly reset buffer position */
|
||||
remain = imp->filled - imp->offset;
|
||||
|
||||
if (remain == 0) /* no brainer */
|
||||
imp->offset = imp->scanned = imp->filled = 0;
|
||||
else if (imp->offset > imp->size - imp->filled &&
|
||||
imp->offset > remain) {
|
||||
memcpy(imp->buf, imp->buf + imp->offset, remain);
|
||||
imp->offset = imp->scanned = 0;
|
||||
imp->filled = remain;
|
||||
}
|
||||
|
||||
target = imp->size;
|
||||
while (target > 16 * LINE_CHUNK && imp->filled < target / 2)
|
||||
target /= 2;
|
||||
if (target < imp->size) {
|
||||
char *tmp;
|
||||
|
||||
tmp = realloc(imp->buf, target);
|
||||
if (!tmp)
|
||||
log_warning("Failed to reallocate buffer to (smaller) size %zu",
|
||||
target);
|
||||
else {
|
||||
log_debug("Reallocated buffer from %zu to %zu bytes",
|
||||
imp->size, target);
|
||||
imp->buf = tmp;
|
||||
imp->size = target;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool journal_importer_eof(const JournalImporter *imp) {
|
||||
return imp->state == IMPORTER_STATE_EOF;
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
#include "sd-id128.h"
|
||||
|
||||
#include "time-util.h"
|
||||
|
||||
/* Make sure not to make this smaller than the maximum coredump size.
|
||||
* See JOURNAL_SIZE_MAX in coredump.c */
|
||||
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
||||
#define ENTRY_SIZE_MAX (1024*1024*770u)
|
||||
#define DATA_SIZE_MAX (1024*1024*768u)
|
||||
#else
|
||||
#define ENTRY_SIZE_MAX (1024*1024*13u)
|
||||
#define DATA_SIZE_MAX (1024*1024*11u)
|
||||
#endif
|
||||
#define LINE_CHUNK 8*1024u
|
||||
|
||||
struct iovec_wrapper {
|
||||
struct iovec *iovec;
|
||||
size_t size_bytes;
|
||||
size_t count;
|
||||
};
|
||||
|
||||
size_t iovw_size(struct iovec_wrapper *iovw);
|
||||
|
||||
typedef struct JournalImporter {
|
||||
int fd;
|
||||
bool passive_fd;
|
||||
char *name;
|
||||
|
||||
char *buf;
|
||||
size_t size; /* total size of the buffer */
|
||||
size_t offset; /* offset to the beginning of live data in the buffer */
|
||||
size_t scanned; /* number of bytes since the beginning of data without a newline */
|
||||
size_t filled; /* total number of bytes in the buffer */
|
||||
|
||||
size_t field_len; /* used for binary fields: the field name length */
|
||||
size_t data_size; /* and the size of the binary data chunk being processed */
|
||||
|
||||
struct iovec_wrapper iovw;
|
||||
|
||||
int state;
|
||||
dual_timestamp ts;
|
||||
sd_id128_t boot_id;
|
||||
} JournalImporter;
|
||||
|
||||
void journal_importer_cleanup(JournalImporter *);
|
||||
int journal_importer_process_data(JournalImporter *);
|
||||
int journal_importer_push_data(JournalImporter *, const char *data, size_t size);
|
||||
void journal_importer_drop_iovw(JournalImporter *);
|
||||
bool journal_importer_eof(const JournalImporter *);
|
||||
|
||||
static inline size_t journal_importer_bytes_remaining(const JournalImporter *imp) {
|
||||
return imp->filled;
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "json.h"
|
||||
|
||||
/* This header should include all prototypes only the JSON parser itself and
|
||||
* its tests need access to. Normal code consuming the JSON parser should not
|
||||
* interface with this. */
|
||||
|
||||
typedef union JsonValue {
|
||||
/* Encodes a simple value. On x86-64 this structure is 16 bytes wide (as long double is 128bit). */
|
||||
bool boolean;
|
||||
long double real;
|
||||
intmax_t integer;
|
||||
uintmax_t unsig;
|
||||
} JsonValue;
|
||||
|
||||
/* Let's protect us against accidental structure size changes on our most relevant arch */
|
||||
#ifdef __x86_64__
|
||||
assert_cc(sizeof(JsonValue) == 16U);
|
||||
#endif
|
||||
|
||||
#define JSON_VALUE_NULL ((JsonValue) {})
|
||||
|
||||
/* We use fake JsonVariant objects for some special values, in order to avoid memory allocations for them. Note that
|
||||
* effectively this means that there are multiple ways to encode the same objects: via these magic values or as
|
||||
* properly allocated JsonVariant. We convert between both on-the-fly as necessary. */
|
||||
#define JSON_VARIANT_MAGIC_TRUE ((JsonVariant*) 1)
|
||||
#define JSON_VARIANT_MAGIC_FALSE ((JsonVariant*) 2)
|
||||
#define JSON_VARIANT_MAGIC_NULL ((JsonVariant*) 3)
|
||||
#define JSON_VARIANT_MAGIC_ZERO_INTEGER ((JsonVariant*) 4)
|
||||
#define JSON_VARIANT_MAGIC_ZERO_UNSIGNED ((JsonVariant*) 5)
|
||||
#define JSON_VARIANT_MAGIC_ZERO_REAL ((JsonVariant*) 6)
|
||||
#define JSON_VARIANT_MAGIC_EMPTY_STRING ((JsonVariant*) 7)
|
||||
#define JSON_VARIANT_MAGIC_EMPTY_ARRAY ((JsonVariant*) 8)
|
||||
#define JSON_VARIANT_MAGIC_EMPTY_OBJECT ((JsonVariant*) 9)
|
||||
#define _JSON_VARIANT_MAGIC_MAX ((JsonVariant*) 10)
|
||||
|
||||
/* This is only safe as long as we don't define more than 4K magic pointers, i.e. the page size of the simplest
|
||||
* architectures we support. That's because we rely on the fact that malloc() will never allocate from the first memory
|
||||
* page, as it is a faulting page for catching NULL pointer dereferences. */
|
||||
assert_cc((uintptr_t) _JSON_VARIANT_MAGIC_MAX < 4096U);
|
||||
|
||||
enum { /* JSON tokens */
|
||||
JSON_TOKEN_END,
|
||||
JSON_TOKEN_COLON,
|
||||
JSON_TOKEN_COMMA,
|
||||
JSON_TOKEN_OBJECT_OPEN,
|
||||
JSON_TOKEN_OBJECT_CLOSE,
|
||||
JSON_TOKEN_ARRAY_OPEN,
|
||||
JSON_TOKEN_ARRAY_CLOSE,
|
||||
JSON_TOKEN_STRING,
|
||||
JSON_TOKEN_REAL,
|
||||
JSON_TOKEN_INTEGER,
|
||||
JSON_TOKEN_UNSIGNED,
|
||||
JSON_TOKEN_BOOLEAN,
|
||||
JSON_TOKEN_NULL,
|
||||
_JSON_TOKEN_MAX,
|
||||
_JSON_TOKEN_INVALID = -1,
|
||||
};
|
||||
|
||||
int json_tokenize(const char **p, char **ret_string, JsonValue *ret_value, unsigned *ret_line, unsigned *ret_column, void **state, unsigned *line, unsigned *column);
|
||||
-3385
File diff suppressed because it is too large
Load Diff
@@ -1,283 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "macro.h"
|
||||
#include "string-util.h"
|
||||
#include "util.h"
|
||||
|
||||
/*
|
||||
In case you wonder why we have our own JSON implementation, here are a couple of reasons why this implementation has
|
||||
benefits over various other implementatins:
|
||||
|
||||
- We need support for 64bit signed and unsigned integers, i.e. the full 64,5bit range of -9223372036854775808…18446744073709551615
|
||||
- All our variants are immutable after creation
|
||||
- Special values such as true, false, zero, null, empty strings, empty array, empty objects require zero dynamic memory
|
||||
- Progressive parsing
|
||||
- Our integer/real type implicitly converts, but only if that's safe and loss-lessly possible
|
||||
- There's a "builder" for putting together objects easily in varargs function calls
|
||||
- There's a "dispatcher" for mapping objects to C data structures
|
||||
- Every variant optionally carries parsing location information, which simplifies debugging and parse log error generation
|
||||
- Formatter has color, line, column support
|
||||
|
||||
Limitations:
|
||||
- Doesn't allow embedded NUL in strings
|
||||
- Can't store integers outside of the -9223372036854775808…18446744073709551615 range (it will use 'long double' for
|
||||
values outside this range, which is lossy)
|
||||
- Can't store negative zero (will be treated identical to positive zero, and not retained across serialization)
|
||||
- Can't store non-integer numbers that can't be stored in "long double" losslessly
|
||||
- Allows creation and parsing of objects with duplicate keys. The "dispatcher" will refuse them however. This means
|
||||
we can parse and pass around such objects, but will carefully refuse them when we convert them into our own data.
|
||||
|
||||
(These limitations should be pretty much in line with those of other JSON implementations, in fact might be less
|
||||
limiting in most cases even.)
|
||||
*/
|
||||
|
||||
typedef struct JsonVariant JsonVariant;
|
||||
|
||||
typedef enum JsonVariantType {
|
||||
JSON_VARIANT_STRING,
|
||||
JSON_VARIANT_INTEGER,
|
||||
JSON_VARIANT_UNSIGNED,
|
||||
JSON_VARIANT_REAL,
|
||||
JSON_VARIANT_NUMBER, /* This a pseudo-type: we can never create variants of this type, but we use it as wildcard check for the above three types */
|
||||
JSON_VARIANT_BOOLEAN,
|
||||
JSON_VARIANT_ARRAY,
|
||||
JSON_VARIANT_OBJECT,
|
||||
JSON_VARIANT_NULL,
|
||||
_JSON_VARIANT_TYPE_MAX,
|
||||
_JSON_VARIANT_TYPE_INVALID = -1
|
||||
} JsonVariantType;
|
||||
|
||||
int json_variant_new_stringn(JsonVariant **ret, const char *s, size_t n);
|
||||
int json_variant_new_integer(JsonVariant **ret, intmax_t i);
|
||||
int json_variant_new_unsigned(JsonVariant **ret, uintmax_t u);
|
||||
int json_variant_new_real(JsonVariant **ret, long double d);
|
||||
int json_variant_new_boolean(JsonVariant **ret, bool b);
|
||||
int json_variant_new_array(JsonVariant **ret, JsonVariant **array, size_t n);
|
||||
int json_variant_new_array_bytes(JsonVariant **ret, const void *p, size_t n);
|
||||
int json_variant_new_array_strv(JsonVariant **ret, char **l);
|
||||
int json_variant_new_object(JsonVariant **ret, JsonVariant **array, size_t n);
|
||||
int json_variant_new_null(JsonVariant **ret);
|
||||
|
||||
static inline int json_variant_new_string(JsonVariant **ret, const char *s) {
|
||||
return json_variant_new_stringn(ret, s, strlen_ptr(s));
|
||||
}
|
||||
|
||||
JsonVariant *json_variant_ref(JsonVariant *v);
|
||||
JsonVariant *json_variant_unref(JsonVariant *v);
|
||||
void json_variant_unref_many(JsonVariant **array, size_t n);
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(JsonVariant *, json_variant_unref);
|
||||
|
||||
const char *json_variant_string(JsonVariant *v);
|
||||
intmax_t json_variant_integer(JsonVariant *v);
|
||||
uintmax_t json_variant_unsigned(JsonVariant *v);
|
||||
long double json_variant_real(JsonVariant *v);
|
||||
bool json_variant_boolean(JsonVariant *v);
|
||||
|
||||
JsonVariantType json_variant_type(JsonVariant *v);
|
||||
bool json_variant_has_type(JsonVariant *v, JsonVariantType type);
|
||||
|
||||
static inline bool json_variant_is_string(JsonVariant *v) {
|
||||
return json_variant_has_type(v, JSON_VARIANT_STRING);
|
||||
}
|
||||
|
||||
static inline bool json_variant_is_integer(JsonVariant *v) {
|
||||
return json_variant_has_type(v, JSON_VARIANT_INTEGER);
|
||||
}
|
||||
|
||||
static inline bool json_variant_is_unsigned(JsonVariant *v) {
|
||||
return json_variant_has_type(v, JSON_VARIANT_UNSIGNED);
|
||||
}
|
||||
|
||||
static inline bool json_variant_is_real(JsonVariant *v) {
|
||||
return json_variant_has_type(v, JSON_VARIANT_REAL);
|
||||
}
|
||||
|
||||
static inline bool json_variant_is_number(JsonVariant *v) {
|
||||
return json_variant_has_type(v, JSON_VARIANT_NUMBER);
|
||||
}
|
||||
|
||||
static inline bool json_variant_is_boolean(JsonVariant *v) {
|
||||
return json_variant_has_type(v, JSON_VARIANT_BOOLEAN);
|
||||
}
|
||||
|
||||
static inline bool json_variant_is_array(JsonVariant *v) {
|
||||
return json_variant_has_type(v, JSON_VARIANT_ARRAY);
|
||||
}
|
||||
|
||||
static inline bool json_variant_is_object(JsonVariant *v) {
|
||||
return json_variant_has_type(v, JSON_VARIANT_OBJECT);
|
||||
}
|
||||
|
||||
static inline bool json_variant_is_null(JsonVariant *v) {
|
||||
return json_variant_has_type(v, JSON_VARIANT_NULL);
|
||||
}
|
||||
|
||||
bool json_variant_is_negative(JsonVariant *v);
|
||||
|
||||
size_t json_variant_elements(JsonVariant *v);
|
||||
JsonVariant *json_variant_by_index(JsonVariant *v, size_t index);
|
||||
JsonVariant *json_variant_by_key(JsonVariant *v, const char *key);
|
||||
JsonVariant *json_variant_by_key_full(JsonVariant *v, const char *key, JsonVariant **ret_key);
|
||||
|
||||
bool json_variant_equal(JsonVariant *a, JsonVariant *b);
|
||||
|
||||
struct json_variant_foreach_state {
|
||||
JsonVariant *variant;
|
||||
size_t idx;
|
||||
};
|
||||
|
||||
#define JSON_VARIANT_ARRAY_FOREACH(i, v) \
|
||||
for (struct json_variant_foreach_state _state = { (v), 0 }; \
|
||||
_state.idx < json_variant_elements(_state.variant) && \
|
||||
({ i = json_variant_by_index(_state.variant, _state.idx); \
|
||||
true; }); \
|
||||
_state.idx++)
|
||||
|
||||
#define JSON_VARIANT_OBJECT_FOREACH(k, e, v) \
|
||||
for (struct json_variant_foreach_state _state = { (v), 0 }; \
|
||||
_state.idx < json_variant_elements(_state.variant) && \
|
||||
({ k = json_variant_by_index(_state.variant, _state.idx); \
|
||||
e = json_variant_by_index(_state.variant, _state.idx + 1); \
|
||||
true; }); \
|
||||
_state.idx += 2)
|
||||
|
||||
int json_variant_get_source(JsonVariant *v, const char **ret_source, unsigned *ret_line, unsigned *ret_column);
|
||||
|
||||
enum {
|
||||
JSON_FORMAT_NEWLINE = 1 << 0, /* suffix with newline */
|
||||
JSON_FORMAT_PRETTY = 1 << 1, /* add internal whitespace to appeal to human readers */
|
||||
JSON_FORMAT_COLOR = 1 << 2, /* insert ANSI color sequences */
|
||||
JSON_FORMAT_SOURCE = 1 << 3, /* prefix with source filename/line/column */
|
||||
JSON_FORMAT_SSE = 1 << 4, /* prefix/suffix with W3C server-sent events */
|
||||
JSON_FORMAT_SEQ = 1 << 5, /* prefix/suffix with RFC 7464 application/json-seq */
|
||||
};
|
||||
|
||||
int json_variant_format(JsonVariant *v, unsigned flags, char **ret);
|
||||
void json_variant_dump(JsonVariant *v, unsigned flags, FILE *f, const char *prefix);
|
||||
|
||||
int json_parse(const char *string, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column);
|
||||
int json_parse_continue(const char **p, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column);
|
||||
int json_parse_file(FILE *f, const char *path, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column);
|
||||
|
||||
enum {
|
||||
_JSON_BUILD_STRING,
|
||||
_JSON_BUILD_INTEGER,
|
||||
_JSON_BUILD_UNSIGNED,
|
||||
_JSON_BUILD_REAL,
|
||||
_JSON_BUILD_BOOLEAN,
|
||||
_JSON_BUILD_ARRAY_BEGIN,
|
||||
_JSON_BUILD_ARRAY_END,
|
||||
_JSON_BUILD_OBJECT_BEGIN,
|
||||
_JSON_BUILD_OBJECT_END,
|
||||
_JSON_BUILD_PAIR,
|
||||
_JSON_BUILD_NULL,
|
||||
_JSON_BUILD_VARIANT,
|
||||
_JSON_BUILD_LITERAL,
|
||||
_JSON_BUILD_STRV,
|
||||
_JSON_BUILD_MAX,
|
||||
};
|
||||
|
||||
#define JSON_BUILD_STRING(s) _JSON_BUILD_STRING, ({ const char *_x = s; _x; })
|
||||
#define JSON_BUILD_INTEGER(i) _JSON_BUILD_INTEGER, ({ intmax_t _x = i; _x; })
|
||||
#define JSON_BUILD_UNSIGNED(u) _JSON_BUILD_UNSIGNED, ({ uintmax_t _x = u; _x; })
|
||||
#define JSON_BUILD_REAL(d) _JSON_BUILD_REAL, ({ long double _x = d; _x; })
|
||||
#define JSON_BUILD_BOOLEAN(b) _JSON_BUILD_BOOLEAN, ({ bool _x = b; _x; })
|
||||
#define JSON_BUILD_ARRAY(...) _JSON_BUILD_ARRAY_BEGIN, __VA_ARGS__, _JSON_BUILD_ARRAY_END
|
||||
#define JSON_BUILD_OBJECT(...) _JSON_BUILD_OBJECT_BEGIN, __VA_ARGS__, _JSON_BUILD_OBJECT_END
|
||||
#define JSON_BUILD_PAIR(n, ...) _JSON_BUILD_PAIR, ({ const char *_x = n; _x; }), __VA_ARGS__
|
||||
#define JSON_BUILD_NULL _JSON_BUILD_NULL
|
||||
#define JSON_BUILD_VARIANT(v) _JSON_BUILD_VARIANT, ({ JsonVariant *_x = v; _x; })
|
||||
#define JSON_BUILD_LITERAL(l) _JSON_BUILD_LITERAL, ({ const char *_x = l; _x; })
|
||||
#define JSON_BUILD_STRV(l) _JSON_BUILD_STRV, ({ char **_x = l; _x; })
|
||||
|
||||
int json_build(JsonVariant **ret, ...);
|
||||
int json_buildv(JsonVariant **ret, va_list ap);
|
||||
|
||||
/* A bitmask of flags used by the dispatch logic. Note that this is a combined bit mask, that is generated from the bit
|
||||
* mask originally passed into json_dispatch(), the individual bitmask associated with the static JsonDispatch callout
|
||||
* entry, as well the bitmask specified for json_log() calls */
|
||||
typedef enum JsonDispatchFlags {
|
||||
/* The following three may be set in JsonDispatch's .flags field or the json_dispatch() flags parameter */
|
||||
JSON_PERMISSIVE = 1 << 0, /* Shall parsing errors be considered fatal for this property? */
|
||||
JSON_MANDATORY = 1 << 1, /* Should existance of this property be mandatory? */
|
||||
JSON_LOG = 1 << 2, /* Should the parser log about errors? */
|
||||
|
||||
/* The following two may be passed into log_json() in addition to the three above */
|
||||
JSON_DEBUG = 1 << 3, /* Indicates that this log message is a debug message */
|
||||
JSON_WARNING = 1 << 4, /* Indicates that this log message is a warning message */
|
||||
} JsonDispatchFlags;
|
||||
|
||||
typedef int (*JsonDispatchCallback)(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
|
||||
|
||||
typedef struct JsonDispatch {
|
||||
const char *name;
|
||||
JsonVariantType type;
|
||||
JsonDispatchCallback callback;
|
||||
size_t offset;
|
||||
JsonDispatchFlags flags;
|
||||
} JsonDispatch;
|
||||
|
||||
int json_dispatch(JsonVariant *v, const JsonDispatch table[], JsonDispatchCallback bad, JsonDispatchFlags flags, void *userdata);
|
||||
|
||||
int json_dispatch_string(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
|
||||
int json_dispatch_strv(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
|
||||
int json_dispatch_boolean(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
|
||||
int json_dispatch_tristate(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
|
||||
int json_dispatch_variant(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
|
||||
int json_dispatch_integer(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
|
||||
int json_dispatch_unsigned(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
|
||||
int json_dispatch_uint32(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
|
||||
int json_dispatch_int32(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
|
||||
|
||||
assert_cc(sizeof(uintmax_t) == sizeof(uint64_t))
|
||||
#define json_dispatch_uint64 json_dispatch_unsigned
|
||||
|
||||
assert_cc(sizeof(intmax_t) == sizeof(int64_t))
|
||||
#define json_dispatch_int64 json_dispatch_integer
|
||||
|
||||
static inline int json_dispatch_level(JsonDispatchFlags flags) {
|
||||
|
||||
/* Did the user request no logging? If so, then never log higher than LOG_DEBUG. Also, if this is marked as
|
||||
* debug message, then also log at debug level. */
|
||||
|
||||
if (!(flags & JSON_LOG) ||
|
||||
(flags & JSON_DEBUG))
|
||||
return LOG_DEBUG;
|
||||
|
||||
/* Are we invoked in permissive mode, or is this explicitly marked as warning message? Then this should be
|
||||
* printed at LOG_WARNING */
|
||||
if (flags & (JSON_PERMISSIVE|JSON_WARNING))
|
||||
return LOG_WARNING;
|
||||
|
||||
/* Otherwise it's an error. */
|
||||
return LOG_ERR;
|
||||
}
|
||||
|
||||
int json_log_internal(JsonVariant *variant, int level, int error, const char *file, int line, const char *func, const char *format, ...) _printf_(7, 8);
|
||||
|
||||
#define json_log(variant, flags, error, ...) \
|
||||
({ \
|
||||
int _level = json_dispatch_level(flags), _e = (error); \
|
||||
(log_get_max_level() >= LOG_PRI(_level)) \
|
||||
? json_log_internal(variant, _level, _e, __FILE__, __LINE__, __func__, __VA_ARGS__) \
|
||||
: -abs(_e); \
|
||||
})
|
||||
|
||||
#define JSON_VARIANT_STRING_CONST(x) _JSON_VARIANT_STRING_CONST(UNIQ, (x))
|
||||
|
||||
#define _JSON_VARIANT_STRING_CONST(xq, x) \
|
||||
({ \
|
||||
__attribute__((__aligned__(2))) static const char UNIQ_T(json_string_const, xq)[] = (x); \
|
||||
assert((((uintptr_t) UNIQ_T(json_string_const, xq)) & 1) == 0); \
|
||||
(JsonVariant*) ((uintptr_t) UNIQ_T(json_string_const, xq) + 1); \
|
||||
})
|
||||
|
||||
const char *json_variant_type_to_string(JsonVariantType t);
|
||||
JsonVariantType json_variant_type_from_string(const char *s);
|
||||
@@ -1,136 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fs-util.h"
|
||||
#include "lockfile-util.h"
|
||||
#include "macro.h"
|
||||
#include "path-util.h"
|
||||
|
||||
int make_lock_file(const char *p, int operation, LockFile *ret) {
|
||||
_cleanup_close_ int fd = -1;
|
||||
_cleanup_free_ char *t = NULL;
|
||||
int r;
|
||||
|
||||
/*
|
||||
* We use UNPOSIX locks if they are available. They have nice
|
||||
* semantics, and are mostly compatible with NFS. However,
|
||||
* they are only available on new kernels. When we detect we
|
||||
* are running on an older kernel, then we fall back to good
|
||||
* old BSD locks. They also have nice semantics, but are
|
||||
* slightly problematic on NFS, where they are upgraded to
|
||||
* POSIX locks, even though locally they are orthogonal to
|
||||
* POSIX locks.
|
||||
*/
|
||||
|
||||
t = strdup(p);
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
|
||||
for (;;) {
|
||||
struct flock fl = {
|
||||
.l_type = (operation & ~LOCK_NB) == LOCK_EX ? F_WRLCK : F_RDLCK,
|
||||
.l_whence = SEEK_SET,
|
||||
};
|
||||
struct stat st;
|
||||
|
||||
fd = open(p, O_CREAT|O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NOCTTY, 0600);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
r = fcntl(fd, (operation & LOCK_NB) ? F_OFD_SETLK : F_OFD_SETLKW, &fl);
|
||||
if (r < 0) {
|
||||
|
||||
/* If the kernel is too old, use good old BSD locks */
|
||||
if (errno == EINVAL)
|
||||
r = flock(fd, operation);
|
||||
|
||||
if (r < 0)
|
||||
return errno == EAGAIN ? -EBUSY : -errno;
|
||||
}
|
||||
|
||||
/* If we acquired the lock, let's check if the file
|
||||
* still exists in the file system. If not, then the
|
||||
* previous exclusive owner removed it and then closed
|
||||
* it. In such a case our acquired lock is worthless,
|
||||
* hence try again. */
|
||||
|
||||
r = fstat(fd, &st);
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
if (st.st_nlink > 0)
|
||||
break;
|
||||
|
||||
fd = safe_close(fd);
|
||||
}
|
||||
|
||||
ret->path = t;
|
||||
ret->fd = fd;
|
||||
ret->operation = operation;
|
||||
|
||||
fd = -1;
|
||||
t = NULL;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int make_lock_file_for(const char *p, int operation, LockFile *ret) {
|
||||
const char *fn;
|
||||
char *t;
|
||||
|
||||
assert(p);
|
||||
assert(ret);
|
||||
|
||||
fn = basename(p);
|
||||
if (!filename_is_valid(fn))
|
||||
return -EINVAL;
|
||||
|
||||
t = newa(char, strlen(p) + 2 + 4 + 1);
|
||||
stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), fn), ".lck");
|
||||
|
||||
return make_lock_file(t, operation, ret);
|
||||
}
|
||||
|
||||
void release_lock_file(LockFile *f) {
|
||||
int r;
|
||||
|
||||
if (!f)
|
||||
return;
|
||||
|
||||
if (f->path) {
|
||||
|
||||
/* If we are the exclusive owner we can safely delete
|
||||
* the lock file itself. If we are not the exclusive
|
||||
* owner, we can try becoming it. */
|
||||
|
||||
if (f->fd >= 0 &&
|
||||
(f->operation & ~LOCK_NB) == LOCK_SH) {
|
||||
static const struct flock fl = {
|
||||
.l_type = F_WRLCK,
|
||||
.l_whence = SEEK_SET,
|
||||
};
|
||||
|
||||
r = fcntl(f->fd, F_OFD_SETLK, &fl);
|
||||
if (r < 0 && errno == EINVAL)
|
||||
r = flock(f->fd, LOCK_EX|LOCK_NB);
|
||||
|
||||
if (r >= 0)
|
||||
f->operation = LOCK_EX|LOCK_NB;
|
||||
}
|
||||
|
||||
if ((f->operation & ~LOCK_NB) == LOCK_EX)
|
||||
unlink_noerrno(f->path);
|
||||
|
||||
f->path = mfree(f->path);
|
||||
}
|
||||
|
||||
f->fd = safe_close(f->fd);
|
||||
f->operation = 0;
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "macro.h"
|
||||
#include "missing.h"
|
||||
|
||||
typedef struct LockFile {
|
||||
char *path;
|
||||
int fd;
|
||||
int operation;
|
||||
} LockFile;
|
||||
|
||||
int make_lock_file(const char *p, int operation, LockFile *ret);
|
||||
int make_lock_file_for(const char *p, int operation, LockFile *ret);
|
||||
void release_lock_file(LockFile *f);
|
||||
|
||||
#define LOCK_FILE_INIT { .fd = -1, .path = NULL }
|
||||
@@ -15,23 +15,14 @@ basic_sources = files('''
|
||||
async.h
|
||||
audit-util.c
|
||||
audit-util.h
|
||||
barrier.c
|
||||
barrier.h
|
||||
bitmap.c
|
||||
bitmap.h
|
||||
blkid-util.h
|
||||
blockdev-util.c
|
||||
blockdev-util.h
|
||||
bpf-program.c
|
||||
bpf-program.h
|
||||
btrfs-ctree.h
|
||||
btrfs-util.c
|
||||
btrfs-util.h
|
||||
build.h
|
||||
bus-label.c
|
||||
bus-label.h
|
||||
calendarspec.c
|
||||
calendarspec.h
|
||||
cap-list.c
|
||||
cap-list.h
|
||||
capability-util.c
|
||||
@@ -40,16 +31,10 @@ basic_sources = files('''
|
||||
cgroup-util.h
|
||||
chattr-util.c
|
||||
chattr-util.h
|
||||
clock-util.c
|
||||
clock-util.h
|
||||
conf-files.c
|
||||
conf-files.h
|
||||
copy.c
|
||||
copy.h
|
||||
cpu-set-util.c
|
||||
cpu-set-util.h
|
||||
crypt-util.c
|
||||
crypt-util.h
|
||||
def.h
|
||||
device-nodes.c
|
||||
device-nodes.h
|
||||
@@ -63,20 +48,12 @@ basic_sources = files('''
|
||||
escape.h
|
||||
ether-addr-util.c
|
||||
ether-addr-util.h
|
||||
exec-util.c
|
||||
exec-util.h
|
||||
exit-status.c
|
||||
exit-status.h
|
||||
extract-word.c
|
||||
extract-word.h
|
||||
fd-util.c
|
||||
fd-util.h
|
||||
fileio-label.c
|
||||
fileio-label.h
|
||||
fileio.c
|
||||
fileio.h
|
||||
format-table.c
|
||||
format-table.h
|
||||
format-util.h
|
||||
fs-util.c
|
||||
fs-util.h
|
||||
@@ -97,11 +74,6 @@ basic_sources = files('''
|
||||
io-util.c
|
||||
io-util.h
|
||||
ioprio.h
|
||||
journal-importer.c
|
||||
journal-importer.h
|
||||
json-internal.h
|
||||
json.c
|
||||
json.h
|
||||
khash.c
|
||||
khash.h
|
||||
label.c
|
||||
@@ -109,8 +81,6 @@ basic_sources = files('''
|
||||
list.h
|
||||
locale-util.c
|
||||
locale-util.h
|
||||
lockfile-util.c
|
||||
lockfile-util.h
|
||||
log.c
|
||||
log.h
|
||||
login-util.c
|
||||
@@ -129,8 +99,6 @@ basic_sources = files('''
|
||||
nss-util.h
|
||||
ordered-set.c
|
||||
ordered-set.h
|
||||
os-util.c
|
||||
os-util.h
|
||||
pager.c
|
||||
pager.h
|
||||
parse-util.c
|
||||
@@ -151,17 +119,11 @@ basic_sources = files('''
|
||||
ratelimit.h
|
||||
raw-clone.h
|
||||
raw-reboot.h
|
||||
reboot-util.c
|
||||
reboot-util.h
|
||||
refcnt.h
|
||||
replace-var.c
|
||||
replace-var.h
|
||||
rlimit-util.c
|
||||
rlimit-util.h
|
||||
rm-rf.c
|
||||
rm-rf.h
|
||||
securebits-util.c
|
||||
securebits-util.h
|
||||
securebits.h
|
||||
selinux-util.c
|
||||
selinux-util.h
|
||||
@@ -175,8 +137,6 @@ basic_sources = files('''
|
||||
smack-util.c
|
||||
smack-util.h
|
||||
socket-label.c
|
||||
socket-protocol-list.c
|
||||
socket-protocol-list.h
|
||||
socket-util.c
|
||||
socket-util.h
|
||||
sparse-endian.h
|
||||
@@ -213,16 +173,10 @@ basic_sources = files('''
|
||||
utf8.h
|
||||
util.c
|
||||
util.h
|
||||
verbs.c
|
||||
verbs.h
|
||||
virt.c
|
||||
virt.h
|
||||
web-util.c
|
||||
web-util.h
|
||||
xattr-util.c
|
||||
xattr-util.h
|
||||
xml.c
|
||||
xml.h
|
||||
'''.split())
|
||||
|
||||
missing_h = files('missing.h')
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fs-util.h"
|
||||
#include "macro.h"
|
||||
#include "os-util.h"
|
||||
#include "strv.h"
|
||||
#include "fileio.h"
|
||||
#include "string-util.h"
|
||||
|
||||
int path_is_os_tree(const char *path) {
|
||||
int r;
|
||||
|
||||
assert(path);
|
||||
|
||||
/* Does the path exist at all? If not, generate an error immediately. This is useful so that a missing root dir
|
||||
* always results in -ENOENT, and we can properly distuingish the case where the whole root doesn't exist from
|
||||
* the case where just the os-release file is missing. */
|
||||
if (laccess(path, F_OK) < 0)
|
||||
return -errno;
|
||||
|
||||
/* We use {/etc|/usr/lib}/os-release as flag file if something is an OS */
|
||||
r = open_os_release(path, NULL, NULL);
|
||||
if (r == -ENOENT) /* We got nothing */
|
||||
return 0;
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int open_os_release(const char *root, char **ret_path, int *ret_fd) {
|
||||
_cleanup_free_ char *q = NULL;
|
||||
const char *p;
|
||||
int k;
|
||||
|
||||
FOREACH_STRING(p, "/etc/os-release", "/usr/lib/os-release") {
|
||||
k = chase_symlinks(p, root, CHASE_PREFIX_ROOT|(ret_fd ? CHASE_OPEN : 0), (ret_path ? &q : NULL));
|
||||
if (k != -ENOENT)
|
||||
break;
|
||||
}
|
||||
if (k < 0)
|
||||
return k;
|
||||
|
||||
if (ret_fd) {
|
||||
int real_fd;
|
||||
|
||||
/* Convert the O_PATH fd into a proper, readable one */
|
||||
real_fd = fd_reopen(k, O_RDONLY|O_CLOEXEC|O_NOCTTY);
|
||||
safe_close(k);
|
||||
if (real_fd < 0)
|
||||
return real_fd;
|
||||
|
||||
*ret_fd = real_fd;
|
||||
}
|
||||
|
||||
if (ret_path)
|
||||
*ret_path = TAKE_PTR(q);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fopen_os_release(const char *root, char **ret_path, FILE **ret_file) {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
_cleanup_close_ int fd = -1;
|
||||
FILE *f;
|
||||
int r;
|
||||
|
||||
if (!ret_file)
|
||||
return open_os_release(root, ret_path, NULL);
|
||||
|
||||
r = open_os_release(root, ret_path ? &p : NULL, &fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
f = fdopen(fd, "re");
|
||||
if (!f)
|
||||
return -errno;
|
||||
fd = -1;
|
||||
|
||||
*ret_file = f;
|
||||
|
||||
if (ret_path)
|
||||
*ret_path = TAKE_PTR(p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int parse_os_release(const char *root, ...) {
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
_cleanup_free_ char *p = NULL;
|
||||
va_list ap;
|
||||
int r;
|
||||
|
||||
r = fopen_os_release(root, &p, &f);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
va_start(ap, root);
|
||||
r = parse_env_filev(f, p, ap);
|
||||
va_end(ap);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int load_os_release_pairs(const char *root, char ***ret) {
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
_cleanup_free_ char *p = NULL;
|
||||
int r;
|
||||
|
||||
r = fopen_os_release(root, &p, &f);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return load_env_file_pairs(f, p, ret);
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
int path_is_os_tree(const char *path);
|
||||
|
||||
int open_os_release(const char *root, char **ret_path, int *ret_fd);
|
||||
int fopen_os_release(const char *root, char **ret_path, FILE **ret_file);
|
||||
|
||||
int parse_os_release(const char *root, ...) _sentinel_;
|
||||
int load_os_release_pairs(const char *root, char ***ret);
|
||||
@@ -1,83 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "fileio.h"
|
||||
#include "log.h"
|
||||
#include "raw-reboot.h"
|
||||
#include "reboot-util.h"
|
||||
#include "string-util.h"
|
||||
#include "umask-util.h"
|
||||
#include "virt.h"
|
||||
|
||||
int update_reboot_parameter_and_warn(const char *parameter) {
|
||||
int r;
|
||||
|
||||
if (isempty(parameter)) {
|
||||
if (unlink("/run/systemd/reboot-param") < 0) {
|
||||
if (errno == ENOENT)
|
||||
return 0;
|
||||
|
||||
return log_warning_errno(errno, "Failed to unlink reboot parameter file: %m");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
RUN_WITH_UMASK(0022) {
|
||||
r = write_string_file("/run/systemd/reboot-param", parameter,
|
||||
WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
|
||||
if (r < 0)
|
||||
return log_warning_errno(r, "Failed to write reboot parameter file: %m");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int reboot_with_parameter(RebootFlags flags) {
|
||||
int r;
|
||||
|
||||
/* Reboots the system with a parameter that is read from /run/systemd/reboot-param. Returns 0 if REBOOT_DRY_RUN
|
||||
* was set and the actual reboot operation was hence skipped. If REBOOT_FALLBACK is set and the reboot with
|
||||
* parameter doesn't work out a fallback to classic reboot() is attempted. If REBOOT_FALLBACK is not set, 0 is
|
||||
* returned instead, which should be considered indication for the caller to fall back to reboot() on its own,
|
||||
* or somehow else deal with this. If REBOOT_LOG is specified will log about what it is going to do, as well as
|
||||
* all errors. */
|
||||
|
||||
if (detect_container() == 0) {
|
||||
_cleanup_free_ char *parameter = NULL;
|
||||
|
||||
r = read_one_line_file("/run/systemd/reboot-param", ¶meter);
|
||||
if (r < 0 && r != -ENOENT)
|
||||
log_full_errno(flags & REBOOT_LOG ? LOG_WARNING : LOG_DEBUG, r,
|
||||
"Failed to read reboot parameter file, ignoring: %m");
|
||||
|
||||
if (!isempty(parameter)) {
|
||||
|
||||
log_full(flags & REBOOT_LOG ? LOG_INFO : LOG_DEBUG,
|
||||
"Rebooting with argument '%s'.", parameter);
|
||||
|
||||
if (flags & REBOOT_DRY_RUN)
|
||||
return 0;
|
||||
|
||||
(void) raw_reboot(LINUX_REBOOT_CMD_RESTART2, parameter);
|
||||
|
||||
log_full_errno(flags & REBOOT_LOG ? LOG_WARNING : LOG_DEBUG, errno,
|
||||
"Failed to reboot with parameter, retrying without: %m");
|
||||
}
|
||||
}
|
||||
|
||||
if (!(flags & REBOOT_FALLBACK))
|
||||
return 0;
|
||||
|
||||
log_full(flags & REBOOT_LOG ? LOG_INFO : LOG_DEBUG, "Rebooting.");
|
||||
|
||||
if (flags & REBOOT_DRY_RUN)
|
||||
return 0;
|
||||
|
||||
(void) reboot(RB_AUTOBOOT);
|
||||
|
||||
return log_full_errno(flags & REBOOT_LOG ? LOG_ERR : LOG_DEBUG, errno, "Failed to reboot: %m");
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
#pragma once
|
||||
|
||||
int update_reboot_parameter_and_warn(const char *parameter);
|
||||
|
||||
typedef enum RebootFlags {
|
||||
REBOOT_LOG = 1 << 0, /* log about what we are going to do and all errors */
|
||||
REBOOT_DRY_RUN = 1 << 1, /* return 0 right before actually doing the reboot */
|
||||
REBOOT_FALLBACK = 1 << 2, /* fallback to plain reboot() if argument-based reboot doesn't work, isn't configured or doesn't apply otherwise */
|
||||
} RebootFlags;
|
||||
|
||||
int reboot_with_parameter(RebootFlags flags);
|
||||
@@ -1,391 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "extract-word.h"
|
||||
#include "fd-util.h"
|
||||
#include "format-util.h"
|
||||
#include "macro.h"
|
||||
#include "missing.h"
|
||||
#include "rlimit-util.h"
|
||||
#include "string-table.h"
|
||||
#include "time-util.h"
|
||||
|
||||
int setrlimit_closest(int resource, const struct rlimit *rlim) {
|
||||
struct rlimit highest, fixed;
|
||||
|
||||
assert(rlim);
|
||||
|
||||
if (setrlimit(resource, rlim) >= 0)
|
||||
return 0;
|
||||
|
||||
if (errno != EPERM)
|
||||
return -errno;
|
||||
|
||||
/* So we failed to set the desired setrlimit, then let's try
|
||||
* to get as close as we can */
|
||||
if (getrlimit(resource, &highest) < 0)
|
||||
return -errno;
|
||||
|
||||
/* If the hard limit is unbounded anyway, then the EPERM had other reasons, let's propagate the original EPERM
|
||||
* then */
|
||||
if (highest.rlim_max == RLIM_INFINITY)
|
||||
return -EPERM;
|
||||
|
||||
fixed = (struct rlimit) {
|
||||
.rlim_cur = MIN(rlim->rlim_cur, highest.rlim_max),
|
||||
.rlim_max = MIN(rlim->rlim_max, highest.rlim_max),
|
||||
};
|
||||
|
||||
/* Shortcut things if we wouldn't change anything. */
|
||||
if (fixed.rlim_cur == highest.rlim_cur &&
|
||||
fixed.rlim_max == highest.rlim_max)
|
||||
return 0;
|
||||
|
||||
if (setrlimit(resource, &fixed) < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int setrlimit_closest_all(const struct rlimit *const *rlim, int *which_failed) {
|
||||
int i, r;
|
||||
|
||||
assert(rlim);
|
||||
|
||||
/* On failure returns the limit's index that failed in *which_failed, but only if non-NULL */
|
||||
|
||||
for (i = 0; i < _RLIMIT_MAX; i++) {
|
||||
if (!rlim[i])
|
||||
continue;
|
||||
|
||||
r = setrlimit_closest(i, rlim[i]);
|
||||
if (r < 0) {
|
||||
if (which_failed)
|
||||
*which_failed = i;
|
||||
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
if (which_failed)
|
||||
*which_failed = -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rlimit_parse_u64(const char *val, rlim_t *ret) {
|
||||
uint64_t u;
|
||||
int r;
|
||||
|
||||
assert(val);
|
||||
assert(ret);
|
||||
|
||||
if (streq(val, "infinity")) {
|
||||
*ret = RLIM_INFINITY;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* setrlimit(2) suggests rlim_t is always 64bit on Linux. */
|
||||
assert_cc(sizeof(rlim_t) == sizeof(uint64_t));
|
||||
|
||||
r = safe_atou64(val, &u);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (u >= (uint64_t) RLIM_INFINITY)
|
||||
return -ERANGE;
|
||||
|
||||
*ret = (rlim_t) u;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rlimit_parse_size(const char *val, rlim_t *ret) {
|
||||
uint64_t u;
|
||||
int r;
|
||||
|
||||
assert(val);
|
||||
assert(ret);
|
||||
|
||||
if (streq(val, "infinity")) {
|
||||
*ret = RLIM_INFINITY;
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = parse_size(val, 1024, &u);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (u >= (uint64_t) RLIM_INFINITY)
|
||||
return -ERANGE;
|
||||
|
||||
*ret = (rlim_t) u;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rlimit_parse_sec(const char *val, rlim_t *ret) {
|
||||
uint64_t u;
|
||||
usec_t t;
|
||||
int r;
|
||||
|
||||
assert(val);
|
||||
assert(ret);
|
||||
|
||||
if (streq(val, "infinity")) {
|
||||
*ret = RLIM_INFINITY;
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = parse_sec(val, &t);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (t == USEC_INFINITY) {
|
||||
*ret = RLIM_INFINITY;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u = (uint64_t) DIV_ROUND_UP(t, USEC_PER_SEC);
|
||||
if (u >= (uint64_t) RLIM_INFINITY)
|
||||
return -ERANGE;
|
||||
|
||||
*ret = (rlim_t) u;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rlimit_parse_usec(const char *val, rlim_t *ret) {
|
||||
usec_t t;
|
||||
int r;
|
||||
|
||||
assert(val);
|
||||
assert(ret);
|
||||
|
||||
if (streq(val, "infinity")) {
|
||||
*ret = RLIM_INFINITY;
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = parse_time(val, &t, 1);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (t == USEC_INFINITY) {
|
||||
*ret = RLIM_INFINITY;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*ret = (rlim_t) t;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rlimit_parse_nice(const char *val, rlim_t *ret) {
|
||||
uint64_t rl;
|
||||
int r;
|
||||
|
||||
/* So, Linux is weird. The range for RLIMIT_NICE is 40..1, mapping to the nice levels -20..19. However, the
|
||||
* RLIMIT_NICE limit defaults to 0 by the kernel, i.e. a value that maps to nice level 20, which of course is
|
||||
* bogus and does not exist. In order to permit parsing the RLIMIT_NICE of 0 here we hence implement a slight
|
||||
* asymmetry: when parsing as positive nice level we permit 0..19. When parsing as negative nice level, we
|
||||
* permit -20..0. But when parsing as raw resource limit value then we also allow the special value 0.
|
||||
*
|
||||
* Yeah, Linux is quality engineering sometimes... */
|
||||
|
||||
if (val[0] == '+') {
|
||||
|
||||
/* Prefixed with "+": Parse as positive user-friendly nice value */
|
||||
r = safe_atou64(val + 1, &rl);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (rl >= PRIO_MAX)
|
||||
return -ERANGE;
|
||||
|
||||
rl = 20 - rl;
|
||||
|
||||
} else if (val[0] == '-') {
|
||||
|
||||
/* Prefixed with "-": Parse as negative user-friendly nice value */
|
||||
r = safe_atou64(val + 1, &rl);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (rl > (uint64_t) (-PRIO_MIN))
|
||||
return -ERANGE;
|
||||
|
||||
rl = 20 + rl;
|
||||
} else {
|
||||
|
||||
/* Not prefixed: parse as raw resource limit value */
|
||||
r = safe_atou64(val, &rl);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (rl > (uint64_t) (20 - PRIO_MIN))
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
*ret = (rlim_t) rl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int (*const rlimit_parse_table[_RLIMIT_MAX])(const char *val, rlim_t *ret) = {
|
||||
[RLIMIT_CPU] = rlimit_parse_sec,
|
||||
[RLIMIT_FSIZE] = rlimit_parse_size,
|
||||
[RLIMIT_DATA] = rlimit_parse_size,
|
||||
[RLIMIT_STACK] = rlimit_parse_size,
|
||||
[RLIMIT_CORE] = rlimit_parse_size,
|
||||
[RLIMIT_RSS] = rlimit_parse_size,
|
||||
[RLIMIT_NOFILE] = rlimit_parse_u64,
|
||||
[RLIMIT_AS] = rlimit_parse_size,
|
||||
[RLIMIT_NPROC] = rlimit_parse_u64,
|
||||
[RLIMIT_MEMLOCK] = rlimit_parse_size,
|
||||
[RLIMIT_LOCKS] = rlimit_parse_u64,
|
||||
[RLIMIT_SIGPENDING] = rlimit_parse_u64,
|
||||
[RLIMIT_MSGQUEUE] = rlimit_parse_size,
|
||||
[RLIMIT_NICE] = rlimit_parse_nice,
|
||||
[RLIMIT_RTPRIO] = rlimit_parse_u64,
|
||||
[RLIMIT_RTTIME] = rlimit_parse_usec,
|
||||
};
|
||||
|
||||
int rlimit_parse_one(int resource, const char *val, rlim_t *ret) {
|
||||
assert(val);
|
||||
assert(ret);
|
||||
|
||||
if (resource < 0)
|
||||
return -EINVAL;
|
||||
if (resource >= _RLIMIT_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
return rlimit_parse_table[resource](val, ret);
|
||||
}
|
||||
|
||||
int rlimit_parse(int resource, const char *val, struct rlimit *ret) {
|
||||
_cleanup_free_ char *hard = NULL, *soft = NULL;
|
||||
rlim_t hl, sl;
|
||||
int r;
|
||||
|
||||
assert(val);
|
||||
assert(ret);
|
||||
|
||||
r = extract_first_word(&val, &soft, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return -EINVAL;
|
||||
|
||||
r = rlimit_parse_one(resource, soft, &sl);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = extract_first_word(&val, &hard, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (!isempty(val))
|
||||
return -EINVAL;
|
||||
if (r == 0)
|
||||
hl = sl;
|
||||
else {
|
||||
r = rlimit_parse_one(resource, hard, &hl);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (sl > hl)
|
||||
return -EILSEQ;
|
||||
}
|
||||
|
||||
*ret = (struct rlimit) {
|
||||
.rlim_cur = sl,
|
||||
.rlim_max = hl,
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rlimit_format(const struct rlimit *rl, char **ret) {
|
||||
char *s = NULL;
|
||||
|
||||
assert(rl);
|
||||
assert(ret);
|
||||
|
||||
if (rl->rlim_cur >= RLIM_INFINITY && rl->rlim_max >= RLIM_INFINITY)
|
||||
s = strdup("infinity");
|
||||
else if (rl->rlim_cur >= RLIM_INFINITY)
|
||||
(void) asprintf(&s, "infinity:" RLIM_FMT, rl->rlim_max);
|
||||
else if (rl->rlim_max >= RLIM_INFINITY)
|
||||
(void) asprintf(&s, RLIM_FMT ":infinity", rl->rlim_cur);
|
||||
else if (rl->rlim_cur == rl->rlim_max)
|
||||
(void) asprintf(&s, RLIM_FMT, rl->rlim_cur);
|
||||
else
|
||||
(void) asprintf(&s, RLIM_FMT ":" RLIM_FMT, rl->rlim_cur, rl->rlim_max);
|
||||
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret = s;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char* const rlimit_table[_RLIMIT_MAX] = {
|
||||
[RLIMIT_AS] = "AS",
|
||||
[RLIMIT_CORE] = "CORE",
|
||||
[RLIMIT_CPU] = "CPU",
|
||||
[RLIMIT_DATA] = "DATA",
|
||||
[RLIMIT_FSIZE] = "FSIZE",
|
||||
[RLIMIT_LOCKS] = "LOCKS",
|
||||
[RLIMIT_MEMLOCK] = "MEMLOCK",
|
||||
[RLIMIT_MSGQUEUE] = "MSGQUEUE",
|
||||
[RLIMIT_NICE] = "NICE",
|
||||
[RLIMIT_NOFILE] = "NOFILE",
|
||||
[RLIMIT_NPROC] = "NPROC",
|
||||
[RLIMIT_RSS] = "RSS",
|
||||
[RLIMIT_RTPRIO] = "RTPRIO",
|
||||
[RLIMIT_RTTIME] = "RTTIME",
|
||||
[RLIMIT_SIGPENDING] = "SIGPENDING",
|
||||
[RLIMIT_STACK] = "STACK",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(rlimit, int);
|
||||
|
||||
int rlimit_from_string_harder(const char *s) {
|
||||
const char *suffix;
|
||||
|
||||
/* The official prefix */
|
||||
suffix = startswith(s, "RLIMIT_");
|
||||
if (suffix)
|
||||
return rlimit_from_string(suffix);
|
||||
|
||||
/* Our own unit file setting prefix */
|
||||
suffix = startswith(s, "Limit");
|
||||
if (suffix)
|
||||
return rlimit_from_string(suffix);
|
||||
|
||||
return rlimit_from_string(s);
|
||||
}
|
||||
|
||||
void rlimit_free_all(struct rlimit **rl) {
|
||||
int i;
|
||||
|
||||
if (!rl)
|
||||
return;
|
||||
|
||||
for (i = 0; i < _RLIMIT_MAX; i++)
|
||||
rl[i] = mfree(rl[i]);
|
||||
}
|
||||
|
||||
int rlimit_nofile_bump(int limit) {
|
||||
int r;
|
||||
|
||||
/* Bumps the (soft) RLIMIT_NOFILE resource limit as close as possible to the specified limit. If a negative
|
||||
* limit is specified, bumps it to the maximum the kernel and the hard resource limit allows. This call should
|
||||
* be used by all our programs that might need a lot of fds, and that know how to deal with high fd numbers
|
||||
* (i.e. do not use select() — which chokes on fds >= 1024) */
|
||||
|
||||
if (limit < 0)
|
||||
limit = read_nr_open();
|
||||
|
||||
if (limit < 3)
|
||||
limit = 3;
|
||||
|
||||
r = setrlimit_closest(RLIMIT_NOFILE, &RLIMIT_MAKE_CONST(limit));
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to set RLIMIT_NOFILE: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
#pragma once
|
||||
|
||||
#include <sys/resource.h>
|
||||
|
||||
#include "macro.h"
|
||||
|
||||
const char *rlimit_to_string(int i) _const_;
|
||||
int rlimit_from_string(const char *s) _pure_;
|
||||
int rlimit_from_string_harder(const char *s) _pure_;
|
||||
|
||||
int setrlimit_closest(int resource, const struct rlimit *rlim);
|
||||
int setrlimit_closest_all(const struct rlimit * const *rlim, int *which_failed);
|
||||
|
||||
int rlimit_parse_one(int resource, const char *val, rlim_t *ret);
|
||||
int rlimit_parse(int resource, const char *val, struct rlimit *ret);
|
||||
|
||||
int rlimit_format(const struct rlimit *rl, char **ret);
|
||||
|
||||
void rlimit_free_all(struct rlimit **rl);
|
||||
|
||||
#define RLIMIT_MAKE_CONST(lim) ((struct rlimit) { lim, lim })
|
||||
|
||||
int rlimit_nofile_bump(int limit);
|
||||
@@ -1,67 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "extract-word.h"
|
||||
#include "securebits.h"
|
||||
#include "securebits-util.h"
|
||||
#include "string-util.h"
|
||||
|
||||
int secure_bits_to_string_alloc(int i, char **s) {
|
||||
_cleanup_free_ char *str = NULL;
|
||||
size_t len;
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
|
||||
r = asprintf(&str, "%s%s%s%s%s%s",
|
||||
(i & (1 << SECURE_KEEP_CAPS)) ? "keep-caps " : "",
|
||||
(i & (1 << SECURE_KEEP_CAPS_LOCKED)) ? "keep-caps-locked " : "",
|
||||
(i & (1 << SECURE_NO_SETUID_FIXUP)) ? "no-setuid-fixup " : "",
|
||||
(i & (1 << SECURE_NO_SETUID_FIXUP_LOCKED)) ? "no-setuid-fixup-locked " : "",
|
||||
(i & (1 << SECURE_NOROOT)) ? "noroot " : "",
|
||||
(i & (1 << SECURE_NOROOT_LOCKED)) ? "noroot-locked " : "");
|
||||
if (r < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
len = strlen(str);
|
||||
if (len != 0)
|
||||
str[len - 1] = '\0';
|
||||
|
||||
*s = TAKE_PTR(str);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int secure_bits_from_string(const char *s) {
|
||||
int secure_bits = 0;
|
||||
const char *p;
|
||||
int r;
|
||||
|
||||
for (p = s;;) {
|
||||
_cleanup_free_ char *word = NULL;
|
||||
|
||||
r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
|
||||
if (r == -ENOMEM)
|
||||
return r;
|
||||
if (r <= 0)
|
||||
break;
|
||||
|
||||
if (streq(word, "keep-caps"))
|
||||
secure_bits |= 1 << SECURE_KEEP_CAPS;
|
||||
else if (streq(word, "keep-caps-locked"))
|
||||
secure_bits |= 1 << SECURE_KEEP_CAPS_LOCKED;
|
||||
else if (streq(word, "no-setuid-fixup"))
|
||||
secure_bits |= 1 << SECURE_NO_SETUID_FIXUP;
|
||||
else if (streq(word, "no-setuid-fixup-locked"))
|
||||
secure_bits |= 1 << SECURE_NO_SETUID_FIXUP_LOCKED;
|
||||
else if (streq(word, "noroot"))
|
||||
secure_bits |= 1 << SECURE_NOROOT;
|
||||
else if (streq(word, "noroot-locked"))
|
||||
secure_bits |= 1 << SECURE_NOROOT_LOCKED;
|
||||
}
|
||||
|
||||
return secure_bits;
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
#pragma once
|
||||
|
||||
#include "securebits.h"
|
||||
|
||||
int secure_bits_to_string_alloc(int i, char **s);
|
||||
int secure_bits_from_string(const char *s);
|
||||
|
||||
static inline bool secure_bits_is_valid(int i) {
|
||||
return ((SECURE_ALL_BITS | SECURE_ALL_LOCKS) & i) == i;
|
||||
}
|
||||
|
||||
static inline int secure_bits_to_string_alloc_with_check(int n, char **s) {
|
||||
if (!secure_bits_is_valid(n))
|
||||
return -EINVAL;
|
||||
|
||||
return secure_bits_to_string_alloc(n, s);
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "socket-protocol-list.h"
|
||||
#include "macro.h"
|
||||
|
||||
static const struct socket_protocol_name* lookup_socket_protocol(register const char *str, register GPERF_LEN_TYPE len);
|
||||
|
||||
#include "socket-protocol-from-name.h"
|
||||
#include "socket-protocol-to-name.h"
|
||||
|
||||
const char *socket_protocol_to_name(int id) {
|
||||
|
||||
if (id < 0)
|
||||
return NULL;
|
||||
|
||||
if (id >= (int) ELEMENTSOF(socket_protocol_names))
|
||||
return NULL;
|
||||
|
||||
return socket_protocol_names[id];
|
||||
}
|
||||
|
||||
int socket_protocol_from_name(const char *name) {
|
||||
const struct socket_protocol_name *sc;
|
||||
|
||||
assert(name);
|
||||
|
||||
sc = lookup_socket_protocol(name, strlen(name));
|
||||
if (!sc)
|
||||
return 0;
|
||||
|
||||
return sc->id;
|
||||
}
|
||||
|
||||
int socket_protocol_max(void) {
|
||||
return ELEMENTSOF(socket_protocol_names);
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
#pragma once
|
||||
|
||||
const char *socket_protocol_to_name(int id);
|
||||
int socket_protocol_from_name(const char *name);
|
||||
|
||||
int socket_protocol_max(void);
|
||||
@@ -1,130 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "env-util.h"
|
||||
#include "log.h"
|
||||
#include "macro.h"
|
||||
#include "process-util.h"
|
||||
#include "string-util.h"
|
||||
#include "verbs.h"
|
||||
#include "virt.h"
|
||||
|
||||
/* Wraps running_in_chroot() which is used in various places, but also adds an environment variable check so external
|
||||
* processes can reliably force this on.
|
||||
*/
|
||||
bool running_in_chroot_or_offline(void) {
|
||||
int r;
|
||||
|
||||
/* Added to support use cases like rpm-ostree, where from %post scripts we only want to execute "preset", but
|
||||
* not "start"/"restart" for example.
|
||||
*
|
||||
* See docs/ENVIRONMENT.md for docs.
|
||||
*/
|
||||
r = getenv_bool("SYSTEMD_OFFLINE");
|
||||
if (r < 0 && r != -ENXIO)
|
||||
log_debug_errno(r, "Failed to parse $SYSTEMD_OFFLINE: %m");
|
||||
else if (r >= 0)
|
||||
return r > 0;
|
||||
|
||||
/* We've had this condition check for a long time which basically checks for legacy chroot case like Fedora's
|
||||
* "mock", which is used for package builds. We don't want to try to start systemd services there, since
|
||||
* without --new-chroot we don't even have systemd running, and even if we did, adding a concept of background
|
||||
* daemons to builds would be an enormous change, requiring considering things like how the journal output is
|
||||
* handled, etc. And there's really not a use case today for a build talking to a service.
|
||||
*
|
||||
* Note this call itself also looks for a different variable SYSTEMD_IGNORE_CHROOT=1.
|
||||
*/
|
||||
r = running_in_chroot();
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "running_in_chroot(): %m");
|
||||
|
||||
return r > 0;
|
||||
}
|
||||
|
||||
int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) {
|
||||
const Verb *verb;
|
||||
const char *name;
|
||||
unsigned i;
|
||||
int left, r;
|
||||
|
||||
assert(verbs);
|
||||
assert(verbs[0].dispatch);
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
assert(argc >= optind);
|
||||
|
||||
left = argc - optind;
|
||||
argv += optind;
|
||||
optind = 0;
|
||||
name = argv[0];
|
||||
|
||||
for (i = 0;; i++) {
|
||||
bool found;
|
||||
|
||||
/* At the end of the list? */
|
||||
if (!verbs[i].dispatch) {
|
||||
if (name)
|
||||
log_error("Unknown operation %s.", name);
|
||||
else
|
||||
log_error("Requires operation parameter.");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (name)
|
||||
found = streq(name, verbs[i].verb);
|
||||
else
|
||||
found = verbs[i].flags & VERB_DEFAULT;
|
||||
|
||||
if (found) {
|
||||
verb = &verbs[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert(verb);
|
||||
|
||||
if (!name)
|
||||
left = 1;
|
||||
|
||||
if (verb->min_args != VERB_ANY &&
|
||||
(unsigned) left < verb->min_args) {
|
||||
log_error("Too few arguments.");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (verb->max_args != VERB_ANY &&
|
||||
(unsigned) left > verb->max_args) {
|
||||
log_error("Too many arguments.");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((verb->flags & VERB_ONLINE_ONLY) && running_in_chroot_or_offline()) {
|
||||
if (name)
|
||||
log_info("Running in chroot, ignoring request: %s", name);
|
||||
else
|
||||
log_info("Running in chroot, ignoring request.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (verb->flags & VERB_MUST_BE_ROOT) {
|
||||
r = must_be_root();
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (name)
|
||||
return verb->dispatch(left, argv, userdata);
|
||||
else {
|
||||
char* fake[2] = {
|
||||
(char*) verb->verb,
|
||||
NULL
|
||||
};
|
||||
|
||||
return verb->dispatch(1, fake, userdata);
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
#pragma once
|
||||
|
||||
#define VERB_ANY ((unsigned) -1)
|
||||
|
||||
typedef enum VerbFlags {
|
||||
VERB_DEFAULT = 1 << 0,
|
||||
VERB_ONLINE_ONLY = 1 << 1,
|
||||
VERB_MUST_BE_ROOT = 1 << 2,
|
||||
} VerbFlags;
|
||||
|
||||
typedef struct {
|
||||
const char *verb;
|
||||
unsigned min_args, max_args;
|
||||
VerbFlags flags;
|
||||
int (* const dispatch)(int argc, char *argv[], void *userdata);
|
||||
} Verb;
|
||||
|
||||
bool running_in_chroot_or_offline(void);
|
||||
|
||||
int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata);
|
||||
@@ -1,59 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "string-util.h"
|
||||
#include "utf8.h"
|
||||
#include "web-util.h"
|
||||
|
||||
bool http_etag_is_valid(const char *etag) {
|
||||
if (isempty(etag))
|
||||
return false;
|
||||
|
||||
if (!endswith(etag, "\""))
|
||||
return false;
|
||||
|
||||
if (!startswith(etag, "\"") && !startswith(etag, "W/\""))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool http_url_is_valid(const char *url) {
|
||||
const char *p;
|
||||
|
||||
if (isempty(url))
|
||||
return false;
|
||||
|
||||
p = startswith(url, "http://");
|
||||
if (!p)
|
||||
p = startswith(url, "https://");
|
||||
if (!p)
|
||||
return false;
|
||||
|
||||
if (isempty(p))
|
||||
return false;
|
||||
|
||||
return ascii_is_valid(p);
|
||||
}
|
||||
|
||||
bool documentation_url_is_valid(const char *url) {
|
||||
const char *p;
|
||||
|
||||
if (isempty(url))
|
||||
return false;
|
||||
|
||||
if (http_url_is_valid(url))
|
||||
return true;
|
||||
|
||||
p = startswith(url, "file:/");
|
||||
if (!p)
|
||||
p = startswith(url, "info:");
|
||||
if (!p)
|
||||
p = startswith(url, "man:");
|
||||
|
||||
if (isempty(p))
|
||||
return false;
|
||||
|
||||
return ascii_is_valid(p);
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "macro.h"
|
||||
|
||||
bool http_url_is_valid(const char *url) _pure_;
|
||||
|
||||
bool documentation_url_is_valid(const char *url) _pure_;
|
||||
|
||||
bool http_etag_is_valid(const char *etag);
|
||||
-238
@@ -1,238 +0,0 @@
|
||||
/* 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");
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
/* 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);
|
||||
Reference in New Issue
Block a user