build-sys: split internal basic/ library from shared/

basic/      can be used by everything
            cannot use anything outside of basic/

libsystemd/ can use basic/
            cannot use shared/

shared/     can use libsystemd/
This commit is contained in:
Kay Sievers
2015-06-11 10:52:46 +02:00
parent 37c47e5e5b
commit a095315b3c
153 changed files with 600 additions and 809 deletions
+16
View File
@@ -0,0 +1,16 @@
/cap-from-name.gperf
/cap-from-name.h
/cap-list.txt
/cap-to-name.h
/errno-from-name.gperf
/errno-from-name.h
/errno-list.txt
/errno-to-name.h
/af-from-name.gperf
/af-from-name.h
/af-list.txt
/af-to-name.h
/arphrd-from-name.gperf
/arphrd-from-name.h
/arphrd-list.txt
/arphrd-to-name.h
+1
View File
@@ -0,0 +1 @@
../Makefile
+86
View File
@@ -0,0 +1,86 @@
//-----------------------------------------------------------------------------
// MurmurHash2 was written by Austin Appleby, and is placed in the public
// domain. The author hereby disclaims copyright to this source code.
// Note - This code makes a few assumptions about how your machine behaves -
// 1. We can read a 4-byte value from any address without crashing
// 2. sizeof(int) == 4
// And it has a few limitations -
// 1. It will not work incrementally.
// 2. It will not produce the same results on little-endian and big-endian
// machines.
#include "MurmurHash2.h"
//-----------------------------------------------------------------------------
// Platform-specific functions and macros
// Microsoft Visual Studio
#if defined(_MSC_VER)
#define BIG_CONSTANT(x) (x)
// Other compilers
#else // defined(_MSC_VER)
#define BIG_CONSTANT(x) (x##LLU)
#endif // !defined(_MSC_VER)
//-----------------------------------------------------------------------------
uint32_t MurmurHash2 ( const void * key, int len, uint32_t seed )
{
// 'm' and 'r' are mixing constants generated offline.
// They're not really 'magic', they just happen to work well.
const uint32_t m = 0x5bd1e995;
const int r = 24;
// Initialize the hash to a 'random' value
uint32_t h = seed ^ len;
// Mix 4 bytes at a time into the hash
const unsigned char * data = (const unsigned char *)key;
while(len >= 4)
{
uint32_t k = *(uint32_t*)data;
k *= m;
k ^= k >> r;
k *= m;
h *= m;
h ^= k;
data += 4;
len -= 4;
}
// Handle the last few bytes of the input array
switch(len)
{
case 3: h ^= data[2] << 16;
case 2: h ^= data[1] << 8;
case 1: h ^= data[0];
h *= m;
};
// Do a few final mixes of the hash to ensure the last few
// bytes are well-incorporated.
h ^= h >> 13;
h *= m;
h ^= h >> 15;
return h;
}
+33
View File
@@ -0,0 +1,33 @@
//-----------------------------------------------------------------------------
// MurmurHash2 was written by Austin Appleby, and is placed in the public
// domain. The author hereby disclaims copyright to this source code.
#ifndef _MURMURHASH2_H_
#define _MURMURHASH2_H_
//-----------------------------------------------------------------------------
// Platform-specific functions and macros
// Microsoft Visual Studio
#if defined(_MSC_VER)
typedef unsigned char uint8_t;
typedef unsigned long uint32_t;
typedef unsigned __int64 uint64_t;
// Other compilers
#else // defined(_MSC_VER)
#include <stdint.h>
#endif // !defined(_MSC_VER)
//-----------------------------------------------------------------------------
uint32_t MurmurHash2 ( const void * key, int len, uint32_t seed );
//-----------------------------------------------------------------------------
#endif // _MURMURHASH2_H_
+58
View File
@@ -0,0 +1,58 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2013 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <sys/socket.h>
#include <string.h>
#include "util.h"
#include "af-list.h"
static const struct af_name* lookup_af(register const char *str, register unsigned int len);
#include "af-to-name.h"
#include "af-from-name.h"
const char *af_to_name(int id) {
if (id <= 0)
return NULL;
if (id >= (int) ELEMENTSOF(af_names))
return NULL;
return af_names[id];
}
int af_from_name(const char *name) {
const struct af_name *sc;
assert(name);
sc = lookup_af(name, strlen(name));
if (!sc)
return AF_UNSPEC;
return sc->id;
}
int af_max(void) {
return ELEMENTSOF(af_names);
}
+27
View File
@@ -0,0 +1,27 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2014 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
const char *af_to_name(int id);
int af_from_name(const char *name);
int af_max(void);
+58
View File
@@ -0,0 +1,58 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2014 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <net/if_arp.h>
#include <string.h>
#include "util.h"
#include "arphrd-list.h"
static const struct arphrd_name* lookup_arphrd(register const char *str, register unsigned int len);
#include "arphrd-to-name.h"
#include "arphrd-from-name.h"
const char *arphrd_to_name(int id) {
if (id <= 0)
return NULL;
if (id >= (int) ELEMENTSOF(arphrd_names))
return NULL;
return arphrd_names[id];
}
int arphrd_from_name(const char *name) {
const struct arphrd_name *sc;
assert(name);
sc = lookup_arphrd(name, strlen(name));
if (!sc)
return 0;
return sc->id;
}
int arphrd_max(void) {
return ELEMENTSOF(arphrd_names);
}
+27
View File
@@ -0,0 +1,27 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2014 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
const char *arphrd_to_name(int id);
int arphrd_from_name(const char *name);
int arphrd_max(void);
+92
View File
@@ -0,0 +1,92 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2013 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <pthread.h>
#include <unistd.h>
#include "async.h"
#include "log.h"
#include "util.h"
int asynchronous_job(void* (*func)(void *p), void *arg) {
pthread_attr_t a;
pthread_t t;
int r;
/* It kinda sucks that we have to resort to threads to
* implement an asynchronous sync(), but well, such is
* life.
*
* Note that issuing this command right before exiting a
* process will cause the process to wait for the sync() to
* complete. This function hence is nicely asynchronous really
* only in long running processes. */
r = pthread_attr_init(&a);
if (r > 0)
return -r;
r = pthread_attr_setdetachstate(&a, PTHREAD_CREATE_DETACHED);
if (r > 0)
goto finish;
r = pthread_create(&t, &a, func, arg);
finish:
pthread_attr_destroy(&a);
return -r;
}
static void *sync_thread(void *p) {
sync();
return NULL;
}
int asynchronous_sync(void) {
log_debug("Spawning new thread for sync");
return asynchronous_job(sync_thread, NULL);
}
static void *close_thread(void *p) {
assert_se(close_nointr(PTR_TO_INT(p)) != -EBADF);
return NULL;
}
int asynchronous_close(int fd) {
int r;
/* This is supposed to behave similar to safe_close(), but
* actually invoke close() asynchronously, so that it will
* never block. Ideally the kernel would have an API for this,
* but it doesn't, so we work around it, and hide this as a
* far away as we can. */
if (fd >= 0) {
PROTECT_ERRNO;
r = asynchronous_job(close_thread, INT_TO_PTR(fd));
if (r < 0)
assert_se(close_nointr(fd) != -EBADF);
}
return -1;
}
+27
View File
@@ -0,0 +1,27 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2013 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
int asynchronous_job(void* (*func)(void *p), void *arg);
int asynchronous_sync(void);
int asynchronous_close(int fd);
+94
View File
@@ -0,0 +1,94 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <errno.h>
#include <stdio.h>
#include "macro.h"
#include "audit.h"
#include "util.h"
#include "process-util.h"
#include "fileio.h"
int audit_session_from_pid(pid_t pid, uint32_t *id) {
_cleanup_free_ char *s = NULL;
const char *p;
uint32_t u;
int r;
assert(id);
p = procfs_file_alloca(pid, "sessionid");
r = read_one_line_file(p, &s);
if (r < 0)
return r;
r = safe_atou32(s, &u);
if (r < 0)
return r;
if (u == AUDIT_SESSION_INVALID || u <= 0)
return -ENXIO;
*id = u;
return 0;
}
int audit_loginuid_from_pid(pid_t pid, uid_t *uid) {
_cleanup_free_ char *s = NULL;
const char *p;
uid_t u;
int r;
assert(uid);
p = procfs_file_alloca(pid, "loginuid");
r = read_one_line_file(p, &s);
if (r < 0)
return r;
r = parse_uid(s, &u);
if (r < 0)
return r;
*uid = (uid_t) u;
return 0;
}
bool use_audit(void) {
static int cached_use = -1;
if (cached_use < 0) {
int fd;
fd = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_AUDIT);
if (fd < 0)
cached_use = errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT;
else {
cached_use = true;
safe_close(fd);
}
}
return cached_use;
}
+33
View File
@@ -0,0 +1,33 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <stdint.h>
#include <stdbool.h>
#include <sys/types.h>
#define AUDIT_SESSION_INVALID ((uint32_t) -1)
int audit_session_from_pid(pid_t pid, uint32_t *id);
int audit_loginuid_from_pid(pid_t pid, uid_t *uid);
bool use_audit(void);
+416
View File
@@ -0,0 +1,416 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2014 David Herrmann <dh.herrmann@gmail.com>
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#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 "macro.h"
#include "util.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 int role) {
int fd;
assert(b);
assert(role == BARRIER_PARENT || role == 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;
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);
}
+91
View File
@@ -0,0 +1,91 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2014 David Herrmann <dh.herrmann@gmail.com>
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#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 int 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 b->barriers == BARRIER_I_ABORTED || b->barriers == BARRIER_WE_ABORTED;
}
static inline bool barrier_they_aborted(Barrier *b) {
return b->barriers == BARRIER_THEY_ABORTED || b->barriers == 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 b->barriers == BARRIER_I_ABORTED || b->barriers == BARRIER_THEY_ABORTED || b->barriers == BARRIER_WE_ABORTED;
}
static inline bool barrier_place_and_sync(Barrier *b) {
(void) barrier_place(b);
return barrier_sync(b);
}
+33
View File
@@ -0,0 +1,33 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2014 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#ifdef HAVE_BLKID
#include <blkid/blkid.h>
#endif
#include "util.h"
#ifdef HAVE_BLKID
DEFINE_TRIVIAL_CLEANUP_FUNC(blkid_probe, blkid_free_probe);
#define _cleanup_blkid_free_probe_ _cleanup_(blkid_free_probep)
#endif
+98
View File
@@ -0,0 +1,98 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
#include "macro.h"
#include "sparse-endian.h"
/* Stolen from btrfs' ctree.h */
struct btrfs_timespec {
le64_t sec;
le32_t nsec;
} _packed_;
struct btrfs_disk_key {
le64_t objectid;
uint8_t type;
le64_t offset;
} _packed_;
struct btrfs_inode_item {
le64_t generation;
le64_t transid;
le64_t size;
le64_t nbytes;
le64_t block_group;
le32_t nlink;
le32_t uid;
le32_t gid;
le32_t mode;
le64_t rdev;
le64_t flags;
le64_t sequence;
le64_t reserved[4];
struct btrfs_timespec atime;
struct btrfs_timespec ctime;
struct btrfs_timespec mtime;
struct btrfs_timespec otime;
} _packed_;
struct btrfs_root_item {
struct btrfs_inode_item inode;
le64_t generation;
le64_t root_dirid;
le64_t bytenr;
le64_t byte_limit;
le64_t bytes_used;
le64_t last_snapshot;
le64_t flags;
le32_t refs;
struct btrfs_disk_key drop_progress;
uint8_t drop_level;
uint8_t level;
le64_t generation_v2;
uint8_t uuid[BTRFS_UUID_SIZE];
uint8_t parent_uuid[BTRFS_UUID_SIZE];
uint8_t received_uuid[BTRFS_UUID_SIZE];
le64_t ctransid;
le64_t otransid;
le64_t stransid;
le64_t rtransid;
struct btrfs_timespec ctime;
struct btrfs_timespec otime;
struct btrfs_timespec stime;
struct btrfs_timespec rtime;
le64_t reserved[8];
} _packed_;
#define BTRFS_ROOT_SUBVOL_RDONLY (1ULL << 0)
struct btrfs_qgroup_info_item {
le64_t generation;
le64_t rfer;
le64_t rfer_cmpr;
le64_t excl;
le64_t excl_cmpr;
} _packed_;
#define BTRFS_QGROUP_LIMIT_MAX_RFER (1ULL << 0)
#define BTRFS_QGROUP_LIMIT_MAX_EXCL (1ULL << 1)
#define BTRFS_QGROUP_LIMIT_RSV_RFER (1ULL << 2)
#define BTRFS_QGROUP_LIMIT_RSV_EXCL (1ULL << 3)
#define BTRFS_QGROUP_LIMIT_RFER_CMPR (1ULL << 4)
#define BTRFS_QGROUP_LIMIT_EXCL_CMPR (1ULL << 5)
struct btrfs_qgroup_limit_item {
le64_t flags;
le64_t max_rfer;
le64_t max_excl;
le64_t rsv_rfer;
le64_t rsv_excl;
} _packed_;
struct btrfs_root_ref {
le64_t dirid;
le64_t sequence;
le16_t name_len;
} _packed_;
File diff suppressed because it is too large Load Diff
+87
View File
@@ -0,0 +1,87 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2014 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#pragma once
#include <stdbool.h>
#include <sys/types.h>
#include "time-util.h"
typedef struct BtrfsSubvolInfo {
uint64_t subvol_id;
usec_t otime;
sd_id128_t uuid;
sd_id128_t parent_uuid;
bool read_only;
} BtrfsSubvolInfo;
typedef struct BtrfsQuotaInfo {
uint64_t referenced;
uint64_t exclusive;
uint64_t referenced_max;
uint64_t exclusive_max;
} BtrfsQuotaInfo;
typedef enum BtrfsSnapshotFlags {
BTRFS_SNAPSHOT_FALLBACK_COPY = 1,
BTRFS_SNAPSHOT_READ_ONLY = 2,
BTRFS_SNAPSHOT_RECURSIVE = 4,
} BtrfsSnapshotFlags;
int btrfs_is_filesystem(int fd);
int btrfs_is_subvol(int fd);
int btrfs_subvol_make(const char *path);
int btrfs_subvol_make_label(const char *path);
int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlags flags);
int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags);
int btrfs_subvol_set_read_only_fd(int fd, bool b);
int btrfs_subvol_set_read_only(const char *path, bool b);
int btrfs_subvol_get_read_only_fd(int fd);
int btrfs_subvol_get_id_fd(int fd, uint64_t *ret);
int btrfs_subvol_get_info_fd(int fd, BtrfsSubvolInfo *info);
int btrfs_subvol_get_quota_fd(int fd, BtrfsQuotaInfo *quota);
int btrfs_reflink(int infd, int outfd);
int btrfs_clone_range(int infd, uint64_t in_offset, int ofd, uint64_t out_offset, uint64_t sz);
int btrfs_get_block_device_fd(int fd, dev_t *dev);
int btrfs_get_block_device(const char *path, dev_t *dev);
int btrfs_defrag_fd(int fd);
int btrfs_defrag(const char *p);
int btrfs_quota_enable_fd(int fd, bool b);
int btrfs_quota_enable(const char *path, bool b);
int btrfs_quota_limit_fd(int fd, uint64_t referenced_max);
int btrfs_quota_limit(const char *path, uint64_t referenced_max);
int btrfs_resize_loopback_fd(int fd, uint64_t size, bool grow_only);
int btrfs_resize_loopback(const char *path, uint64_t size, bool grow_only);
int btrfs_subvol_remove(const char *path, bool recursive);
int btrfs_subvol_remove_fd(int fd, const char *subvolume, bool recursive);
+157
View File
@@ -0,0 +1,157 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#ifdef HAVE_PAM
#define _PAM_FEATURE_ "+PAM"
#else
#define _PAM_FEATURE_ "-PAM"
#endif
#ifdef HAVE_AUDIT
#define _AUDIT_FEATURE_ "+AUDIT"
#else
#define _AUDIT_FEATURE_ "-AUDIT"
#endif
#ifdef HAVE_SELINUX
#define _SELINUX_FEATURE_ "+SELINUX"
#else
#define _SELINUX_FEATURE_ "-SELINUX"
#endif
#ifdef HAVE_APPARMOR
#define _APPARMOR_FEATURE_ "+APPARMOR"
#else
#define _APPARMOR_FEATURE_ "-APPARMOR"
#endif
#ifdef HAVE_IMA
#define _IMA_FEATURE_ "+IMA"
#else
#define _IMA_FEATURE_ "-IMA"
#endif
#ifdef HAVE_SMACK
#define _SMACK_FEATURE_ "+SMACK"
#else
#define _SMACK_FEATURE_ "-SMACK"
#endif
#ifdef HAVE_SYSV_COMPAT
#define _SYSVINIT_FEATURE_ "+SYSVINIT"
#else
#define _SYSVINIT_FEATURE_ "-SYSVINIT"
#endif
#ifdef HAVE_UTMP
#define _UTMP_FEATURE_ "+UTMP"
#else
#define _UTMP_FEATURE_ "-UTMP"
#endif
#ifdef HAVE_LIBCRYPTSETUP
#define _LIBCRYPTSETUP_FEATURE_ "+LIBCRYPTSETUP"
#else
#define _LIBCRYPTSETUP_FEATURE_ "-LIBCRYPTSETUP"
#endif
#ifdef HAVE_GCRYPT
#define _GCRYPT_FEATURE_ "+GCRYPT"
#else
#define _GCRYPT_FEATURE_ "-GCRYPT"
#endif
#ifdef HAVE_GNUTLS
#define _GNUTLS_FEATURE_ "+GNUTLS"
#else
#define _GNUTLS_FEATURE_ "-GNUTLS"
#endif
#ifdef HAVE_ACL
#define _ACL_FEATURE_ "+ACL"
#else
#define _ACL_FEATURE_ "-ACL"
#endif
#ifdef HAVE_XZ
#define _XZ_FEATURE_ "+XZ"
#else
#define _XZ_FEATURE_ "-XZ"
#endif
#ifdef HAVE_LZ4
#define _LZ4_FEATURE_ "+LZ4"
#else
#define _LZ4_FEATURE_ "-LZ4"
#endif
#ifdef HAVE_SECCOMP
#define _SECCOMP_FEATURE_ "+SECCOMP"
#else
#define _SECCOMP_FEATURE_ "-SECCOMP"
#endif
#ifdef HAVE_BLKID
#define _BLKID_FEATURE_ "+BLKID"
#else
#define _BLKID_FEATURE_ "-BLKID"
#endif
#ifdef HAVE_ELFUTILS
#define _ELFUTILS_FEATURE_ "+ELFUTILS"
#else
#define _ELFUTILS_FEATURE_ "-ELFUTILS"
#endif
#ifdef HAVE_KMOD
#define _KMOD_FEATURE_ "+KMOD"
#else
#define _KMOD_FEATURE_ "-KMOD"
#endif
#ifdef HAVE_LIBIDN
#define _IDN_FEATURE_ "+IDN"
#else
#define _IDN_FEATURE_ "-IDN"
#endif
#define SYSTEMD_FEATURES \
_PAM_FEATURE_ " " \
_AUDIT_FEATURE_ " " \
_SELINUX_FEATURE_ " " \
_IMA_FEATURE_ " " \
_APPARMOR_FEATURE_ " " \
_SMACK_FEATURE_ " " \
_SYSVINIT_FEATURE_ " " \
_UTMP_FEATURE_ " " \
_LIBCRYPTSETUP_FEATURE_ " " \
_GCRYPT_FEATURE_ " " \
_GNUTLS_FEATURE_ " " \
_ACL_FEATURE_ " " \
_XZ_FEATURE_ " " \
_LZ4_FEATURE_ " " \
_SECCOMP_FEATURE_ " " \
_BLKID_FEATURE_ " " \
_ELFUTILS_FEATURE_ " " \
_KMOD_FEATURE_ " " \
_IDN_FEATURE_
+100
View File
@@ -0,0 +1,100 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2013 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <stdlib.h>
#include "util.h"
#include "macro.h"
#include "bus-label.h"
char *bus_label_escape(const char *s) {
char *r, *t;
const char *f;
assert_return(s, NULL);
/* Escapes all chars that D-Bus' object path cannot deal
* with. Can be reversed with bus_path_unescape(). We special
* case the empty string. */
if (*s == 0)
return strdup("_");
r = new(char, strlen(s)*3 + 1);
if (!r)
return NULL;
for (f = s, t = r; *f; f++) {
/* Escape everything that is not a-zA-Z0-9. We also
* escape 0-9 if it's the first character */
if (!(*f >= 'A' && *f <= 'Z') &&
!(*f >= 'a' && *f <= 'z') &&
!(f > s && *f >= '0' && *f <= '9')) {
*(t++) = '_';
*(t++) = hexchar(*f >> 4);
*(t++) = hexchar(*f);
} else
*(t++) = *f;
}
*t = 0;
return r;
}
char *bus_label_unescape_n(const char *f, size_t l) {
char *r, *t;
size_t i;
assert_return(f, NULL);
/* Special case for the empty string */
if (l == 1 && *f == '_')
return strdup("");
r = new(char, l + 1);
if (!r)
return NULL;
for (i = 0, t = r; i < l; ++i) {
if (f[i] == '_') {
int a, b;
if (l - i < 3 ||
(a = unhexchar(f[i + 1])) < 0 ||
(b = unhexchar(f[i + 2])) < 0) {
/* Invalid escape code, let's take it literal then */
*(t++) = '_';
} else {
*(t++) = (char) ((a << 4) | b);
i += 2;
}
} else
*(t++) = f[i];
}
*t = 0;
return r;
}
+32
View File
@@ -0,0 +1,32 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2013 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <stdlib.h>
#include <string.h>
char *bus_label_escape(const char *s);
char *bus_label_unescape_n(const char *f, size_t l);
static inline char *bus_label_unescape(const char *f) {
return bus_label_unescape_n(f, f ? strlen(f) : 0);
}
File diff suppressed because it is too large Load Diff
+57
View File
@@ -0,0 +1,57 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2012 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
/* A structure for specifying (possibly repetitive) points in calendar
* time, a la cron */
#include <stdbool.h>
#include "util.h"
typedef struct CalendarComponent {
int value;
int repeat;
struct CalendarComponent *next;
} CalendarComponent;
typedef struct CalendarSpec {
int weekdays_bits;
CalendarComponent *year;
CalendarComponent *month;
CalendarComponent *day;
CalendarComponent *hour;
CalendarComponent *minute;
CalendarComponent *second;
} CalendarSpec;
void 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);
+65
View File
@@ -0,0 +1,65 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2014 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <string.h>
#include "util.h"
#include "cap-list.h"
#include "missing.h"
static const struct capability_name* lookup_capability(register const char *str, register unsigned int len);
#include "cap-to-name.h"
#include "cap-from-name.h"
const char *capability_to_name(int id) {
if (id < 0)
return NULL;
if (id >= (int) ELEMENTSOF(capability_names))
return NULL;
return capability_names[id];
}
int capability_from_name(const char *name) {
const struct capability_name *sc;
int r, i;
assert(name);
/* Try to parse numeric capability */
r = safe_atoi(name, &i);
if (r >= 0 && i >= 0)
return i;
/* Try to parse string capability */
sc = lookup_capability(name, strlen(name));
if (!sc)
return -EINVAL;
return sc->id;
}
int capability_list_length(void) {
return (int) ELEMENTSOF(capability_names);
}
+26
View File
@@ -0,0 +1,26 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2014 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
const char *capability_to_name(int id);
int capability_from_name(const char *name);
int capability_list_length(void);
+307
View File
@@ -0,0 +1,307 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <sys/capability.h>
#include <sys/prctl.h>
#include "grp.h"
#include "macro.h"
#include "util.h"
#include "log.h"
#include "fileio.h"
#include "capability.h"
int have_effective_cap(int value) {
_cleanup_cap_free_ cap_t cap;
cap_flag_value_t fv;
cap = cap_get_proc();
if (!cap)
return -errno;
if (cap_get_flag(cap, value, CAP_EFFECTIVE, &fv) < 0)
return -errno;
else
return fv == CAP_SET;
}
unsigned long cap_last_cap(void) {
static thread_local unsigned long saved;
static thread_local bool valid = false;
_cleanup_free_ char *content = NULL;
unsigned long p = 0;
int r;
if (valid)
return saved;
/* available since linux-3.2 */
r = read_one_line_file("/proc/sys/kernel/cap_last_cap", &content);
if (r >= 0) {
r = safe_atolu(content, &p);
if (r >= 0) {
saved = p;
valid = true;
return p;
}
}
/* fall back to syscall-probing for pre linux-3.2 */
p = (unsigned long) CAP_LAST_CAP;
if (prctl(PR_CAPBSET_READ, p) < 0) {
/* Hmm, look downwards, until we find one that
* works */
for (p--; p > 0; p --)
if (prctl(PR_CAPBSET_READ, p) >= 0)
break;
} else {
/* Hmm, look upwards, until we find one that doesn't
* work */
for (;; p++)
if (prctl(PR_CAPBSET_READ, p+1) < 0)
break;
}
saved = p;
valid = true;
return p;
}
int capability_bounding_set_drop(uint64_t drop, bool right_now) {
_cleanup_cap_free_ cap_t after_cap = NULL;
cap_flag_value_t fv;
unsigned long i;
int r;
/* If we are run as PID 1 we will lack CAP_SETPCAP by default
* in the effective set (yes, the kernel drops that when
* executing init!), so get it back temporarily so that we can
* call PR_CAPBSET_DROP. */
after_cap = cap_get_proc();
if (!after_cap)
return -errno;
if (cap_get_flag(after_cap, CAP_SETPCAP, CAP_EFFECTIVE, &fv) < 0)
return -errno;
if (fv != CAP_SET) {
_cleanup_cap_free_ cap_t temp_cap = NULL;
static const cap_value_t v = CAP_SETPCAP;
temp_cap = cap_dup(after_cap);
if (!temp_cap) {
r = -errno;
goto finish;
}
if (cap_set_flag(temp_cap, CAP_EFFECTIVE, 1, &v, CAP_SET) < 0) {
r = -errno;
goto finish;
}
if (cap_set_proc(temp_cap) < 0) {
r = -errno;
goto finish;
}
}
for (i = 0; i <= cap_last_cap(); i++) {
if (drop & ((uint64_t) 1ULL << (uint64_t) i)) {
cap_value_t v;
/* Drop it from the bounding set */
if (prctl(PR_CAPBSET_DROP, i) < 0) {
r = -errno;
goto finish;
}
v = (cap_value_t) i;
/* Also drop it from the inheritable set, so
* that anything we exec() loses the
* capability for good. */
if (cap_set_flag(after_cap, CAP_INHERITABLE, 1, &v, CAP_CLEAR) < 0) {
r = -errno;
goto finish;
}
/* If we shall apply this right now drop it
* also from our own capability sets. */
if (right_now) {
if (cap_set_flag(after_cap, CAP_PERMITTED, 1, &v, CAP_CLEAR) < 0 ||
cap_set_flag(after_cap, CAP_EFFECTIVE, 1, &v, CAP_CLEAR) < 0) {
r = -errno;
goto finish;
}
}
}
}
r = 0;
finish:
if (cap_set_proc(after_cap) < 0)
return -errno;
return r;
}
static int drop_from_file(const char *fn, uint64_t drop) {
int r, k;
uint32_t hi, lo;
uint64_t current, after;
char *p;
r = read_one_line_file(fn, &p);
if (r < 0)
return r;
assert_cc(sizeof(hi) == sizeof(unsigned));
assert_cc(sizeof(lo) == sizeof(unsigned));
k = sscanf(p, "%u %u", &lo, &hi);
free(p);
if (k != 2)
return -EIO;
current = (uint64_t) lo | ((uint64_t) hi << 32ULL);
after = current & ~drop;
if (current == after)
return 0;
lo = (unsigned) (after & 0xFFFFFFFFULL);
hi = (unsigned) ((after >> 32ULL) & 0xFFFFFFFFULL);
if (asprintf(&p, "%u %u", lo, hi) < 0)
return -ENOMEM;
r = write_string_file(fn, p);
free(p);
return r;
}
int capability_bounding_set_drop_usermode(uint64_t drop) {
int r;
r = drop_from_file("/proc/sys/kernel/usermodehelper/inheritable", drop);
if (r < 0)
return r;
r = drop_from_file("/proc/sys/kernel/usermodehelper/bset", drop);
if (r < 0)
return r;
return r;
}
int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities) {
_cleanup_cap_free_ cap_t d = NULL;
unsigned i, j = 0;
int r;
/* Unfortunately we cannot leave privilege dropping to PID 1
* here, since we want to run as user but want to keep some
* capabilities. Since file capabilities have been introduced
* this cannot be done across exec() anymore, unless our
* binary has the capability configured in the file system,
* which we want to avoid. */
if (setresgid(gid, gid, gid) < 0)
return log_error_errno(errno, "Failed to change group ID: %m");
if (setgroups(0, NULL) < 0)
return log_error_errno(errno, "Failed to drop auxiliary groups list: %m");
/* Ensure we keep the permitted caps across the setresuid() */
if (prctl(PR_SET_KEEPCAPS, 1) < 0)
return log_error_errno(errno, "Failed to enable keep capabilities flag: %m");
r = setresuid(uid, uid, uid);
if (r < 0)
return log_error_errno(errno, "Failed to change user ID: %m");
if (prctl(PR_SET_KEEPCAPS, 0) < 0)
return log_error_errno(errno, "Failed to disable keep capabilities flag: %m");
/* Drop all caps from the bounding set, except the ones we want */
r = capability_bounding_set_drop(~keep_capabilities, true);
if (r < 0)
return log_error_errno(r, "Failed to drop capabilities: %m");
/* Now upgrade the permitted caps we still kept to effective caps */
d = cap_init();
if (!d)
return log_oom();
if (keep_capabilities) {
cap_value_t bits[u64log2(keep_capabilities) + 1];
for (i = 0; i < ELEMENTSOF(bits); i++)
if (keep_capabilities & (1ULL << i))
bits[j++] = i;
/* use enough bits */
assert(i == 64 || (keep_capabilities >> i) == 0);
/* don't use too many bits */
assert(keep_capabilities & (1ULL << (i - 1)));
if (cap_set_flag(d, CAP_EFFECTIVE, j, bits, CAP_SET) < 0 ||
cap_set_flag(d, CAP_PERMITTED, j, bits, CAP_SET) < 0) {
log_error_errno(errno, "Failed to enable capabilities bits: %m");
return -errno;
}
if (cap_set_proc(d) < 0)
return log_error_errno(errno, "Failed to increase capabilities: %m");
}
return 0;
}
int drop_capability(cap_value_t cv) {
_cleanup_cap_free_ cap_t tmp_cap = NULL;
tmp_cap = cap_get_proc();
if (!tmp_cap)
return -errno;
if ((cap_set_flag(tmp_cap, CAP_INHERITABLE, 1, &cv, CAP_CLEAR) < 0) ||
(cap_set_flag(tmp_cap, CAP_PERMITTED, 1, &cv, CAP_CLEAR) < 0) ||
(cap_set_flag(tmp_cap, CAP_EFFECTIVE, 1, &cv, CAP_CLEAR) < 0))
return -errno;
if (cap_set_proc(tmp_cap) < 0)
return -errno;
return 0;
}
+45
View File
@@ -0,0 +1,45 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <stdbool.h>
#include <sys/capability.h>
#include "util.h"
unsigned long cap_last_cap(void);
int have_effective_cap(int value);
int capability_bounding_set_drop(uint64_t drop, bool right_now);
int capability_bounding_set_drop_usermode(uint64_t drop);
int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities);
int drop_capability(cap_value_t cv);
DEFINE_TRIVIAL_CLEANUP_FUNC(cap_t, cap_free);
#define _cleanup_cap_free_ _cleanup_(cap_freep)
static inline void cap_free_charpp(char **p) {
if (*p)
cap_free(*p);
}
#define _cleanup_cap_free_charp_ _cleanup_(cap_free_charpp)
File diff suppressed because it is too large Load Diff
+139
View File
@@ -0,0 +1,139 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <sys/types.h>
#include <stdio.h>
#include <dirent.h>
#include "set.h"
#include "def.h"
/* A bit mask of well known cgroup controllers */
typedef enum CGroupControllerMask {
CGROUP_CPU = 1,
CGROUP_CPUACCT = 2,
CGROUP_BLKIO = 4,
CGROUP_MEMORY = 8,
CGROUP_DEVICE = 16,
_CGROUP_CONTROLLER_MASK_ALL = 31
} CGroupControllerMask;
/*
* General rules:
*
* We accept named hierarchies in the syntax "foo" and "name=foo".
*
* We expect that named hierarchies do not conflict in name with a
* kernel hierarchy, modulo the "name=" prefix.
*
* We always generate "normalized" controller names, i.e. without the
* "name=" prefix.
*
* We require absolute cgroup paths. When returning, we will always
* generate paths with multiple adjacent / removed.
*/
int cg_enumerate_processes(const char *controller, const char *path, FILE **_f);
int cg_read_pid(FILE *f, pid_t *_pid);
int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d);
int cg_read_subgroup(DIR *d, char **fn);
int cg_kill(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, Set *s);
int cg_kill_recursive(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, bool remove, Set *s);
int cg_migrate(const char *cfrom, const char *pfrom, const char *cto, const char *pto, bool ignore_self);
int cg_migrate_recursive(const char *cfrom, const char *pfrom, const char *cto, const char *pto, bool ignore_self, bool remove);
int cg_migrate_recursive_fallback(const char *cfrom, const char *pfrom, const char *cto, const char *pto, bool ignore_self, bool rem);
int cg_split_spec(const char *spec, char **controller, char **path);
int cg_mangle_path(const char *path, char **result);
int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs);
int cg_get_path_and_check(const char *controller, const char *path, const char *suffix, char **fs);
int cg_pid_get_path(const char *controller, pid_t pid, char **path);
int cg_trim(const char *controller, const char *path, bool delete_root);
int cg_rmdir(const char *controller, const char *path);
int cg_delete(const char *controller, const char *path);
int cg_create(const char *controller, const char *path);
int cg_attach(const char *controller, const char *path, pid_t pid);
int cg_attach_fallback(const char *controller, const char *path, pid_t pid);
int cg_create_and_attach(const char *controller, const char *path, pid_t pid);
int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value);
int cg_get_attribute(const char *controller, const char *path, const char *attribute, char **ret);
int cg_set_group_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid);
int cg_set_task_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid);
int cg_install_release_agent(const char *controller, const char *agent);
int cg_uninstall_release_agent(const char *controller);
int cg_is_empty(const char *controller, const char *path, bool ignore_self);
int cg_is_empty_recursive(const char *controller, const char *path, bool ignore_self);
int cg_get_root_path(char **path);
int cg_path_get_session(const char *path, char **session);
int cg_path_get_owner_uid(const char *path, uid_t *uid);
int cg_path_get_unit(const char *path, char **unit);
int cg_path_get_user_unit(const char *path, char **unit);
int cg_path_get_machine_name(const char *path, char **machine);
int cg_path_get_slice(const char *path, char **slice);
int cg_path_get_user_slice(const char *path, char **slice);
int cg_shift_path(const char *cgroup, const char *cached_root, const char **shifted);
int cg_pid_get_path_shifted(pid_t pid, const char *cached_root, char **cgroup);
int cg_pid_get_session(pid_t pid, char **session);
int cg_pid_get_owner_uid(pid_t pid, uid_t *uid);
int cg_pid_get_unit(pid_t pid, char **unit);
int cg_pid_get_user_unit(pid_t pid, char **unit);
int cg_pid_get_machine_name(pid_t pid, char **machine);
int cg_pid_get_slice(pid_t pid, char **slice);
int cg_pid_get_user_slice(pid_t pid, char **slice);
int cg_path_decode_unit(const char *cgroup, char **unit);
char *cg_escape(const char *p);
char *cg_unescape(const char *p) _pure_;
bool cg_controller_is_valid(const char *p);
int cg_slice_to_path(const char *unit, char **ret);
typedef const char* (*cg_migrate_callback_t)(CGroupControllerMask mask, void *userdata);
int cg_create_everywhere(CGroupControllerMask supported, CGroupControllerMask mask, const char *path);
int cg_attach_everywhere(CGroupControllerMask supported, const char *path, pid_t pid, cg_migrate_callback_t callback, void *userdata);
int cg_attach_many_everywhere(CGroupControllerMask supported, const char *path, Set* pids, cg_migrate_callback_t callback, void *userdata);
int cg_migrate_everywhere(CGroupControllerMask supported, const char *from, const char *to, cg_migrate_callback_t callback, void *userdata);
int cg_trim_everywhere(CGroupControllerMask supported, const char *path, bool delete_root);
CGroupControllerMask cg_mask_supported(void);
int cg_kernel_controllers(Set *controllers);
+142
View File
@@ -0,0 +1,142 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2010-2012 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <linux/rtc.h>
#include "macro.h"
#include "util.h"
#include "clock-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(void) {
_cleanup_fclose_ FILE *f;
/*
* The third line of adjtime is "UTC" or "LOCAL" or nothing.
* # /etc/adjtime
* 0.0 0 0
* 0
* UTC
*/
f = fopen("/etc/adjtime", "re");
if (f) {
char line[LINE_MAX];
bool b;
b = fgets(line, sizeof(line), f) &&
fgets(line, sizeof(line), f) &&
fgets(line, sizeof(line), f);
if (!b)
return -EIO;
truncate_nl(line);
return streq(line, "LOCAL");
} else if (errno != ENOENT)
return -errno;
return 0;
}
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(tm = localtime(&ts.tv_sec));
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 -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;
}
+29
View File
@@ -0,0 +1,29 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2010-2012 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
int clock_is_localtime(void);
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);
+174
View File
@@ -0,0 +1,174 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <dirent.h>
#include "macro.h"
#include "util.h"
#include "missing.h"
#include "log.h"
#include "strv.h"
#include "path-util.h"
#include "hashmap.h"
#include "conf-files.h"
static int files_add(Hashmap *h, const char *root, const char *path, const char *suffix) {
_cleanup_closedir_ DIR *dir = NULL;
const char *dirpath;
int r;
assert(path);
assert(suffix);
dirpath = prefix_roota(root, path);
dir = opendir(dirpath);
if (!dir) {
if (errno == ENOENT)
return 0;
return -errno;
}
for (;;) {
struct dirent *de;
char *p;
errno = 0;
de = readdir(dir);
if (!de && errno != 0)
return -errno;
if (!de)
break;
if (!dirent_is_file_with_suffix(de, suffix))
continue;
p = strjoin(dirpath, "/", de->d_name, NULL);
if (!p)
return -ENOMEM;
r = hashmap_put(h, basename(p), p);
if (r == -EEXIST) {
log_debug("Skipping overridden file: %s.", p);
free(p);
} else if (r < 0) {
free(p);
return r;
} else if (r == 0) {
log_debug("Duplicate file %s", p);
free(p);
}
}
return 0;
}
static int base_cmp(const void *a, const void *b) {
const char *s1, *s2;
s1 = *(char * const *)a;
s2 = *(char * const *)b;
return strcmp(basename(s1), basename(s2));
}
static int conf_files_list_strv_internal(char ***strv, const char *suffix, const char *root, char **dirs) {
_cleanup_hashmap_free_ Hashmap *fh = NULL;
char **files, **p;
int r;
assert(strv);
assert(suffix);
/* This alters the dirs string array */
if (!path_strv_resolve_uniq(dirs, root))
return -ENOMEM;
fh = hashmap_new(&string_hash_ops);
if (!fh)
return -ENOMEM;
STRV_FOREACH(p, dirs) {
r = files_add(fh, root, *p, suffix);
if (r == -ENOMEM) {
return r;
} else if (r < 0)
log_debug_errno(r, "Failed to search for files in %s: %m",
*p);
}
files = hashmap_get_strv(fh);
if (files == NULL) {
return -ENOMEM;
}
qsort_safe(files, hashmap_size(fh), sizeof(char *), base_cmp);
*strv = files;
return 0;
}
int conf_files_list_strv(char ***strv, const char *suffix, const char *root, const char* const* dirs) {
_cleanup_strv_free_ char **copy = NULL;
assert(strv);
assert(suffix);
copy = strv_copy((char**) dirs);
if (!copy)
return -ENOMEM;
return conf_files_list_strv_internal(strv, suffix, root, copy);
}
int conf_files_list(char ***strv, const char *suffix, const char *root, const char *dir, ...) {
_cleanup_strv_free_ char **dirs = NULL;
va_list ap;
assert(strv);
assert(suffix);
va_start(ap, dir);
dirs = strv_new_ap(dir, ap);
va_end(ap);
if (!dirs)
return -ENOMEM;
return conf_files_list_strv_internal(strv, suffix, root, dirs);
}
int conf_files_list_nulstr(char ***strv, const char *suffix, const char *root, const char *d) {
_cleanup_strv_free_ char **dirs = NULL;
assert(strv);
assert(suffix);
dirs = strv_split_nulstr(d);
if (!dirs)
return -ENOMEM;
return conf_files_list_strv_internal(strv, suffix, root, dirs);
}
+28
View File
@@ -0,0 +1,28 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2010-2012 Lennart Poettering
Copyright 2010-2012 Kay Sievers
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
int conf_files_list(char ***strv, const char *suffix, const char *root, const char *dir, ...);
int conf_files_list_strv(char ***strv, const char *suffix, const char *root, const char* const* dirs);
int conf_files_list_nulstr(char ***strv, const char *suffix, const char *root, const char *dirs);
+507
View File
@@ -0,0 +1,507 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2014 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <sys/sendfile.h>
#include <sys/xattr.h>
#include "util.h"
#include "btrfs-util.h"
#include "copy.h"
#define COPY_BUFFER_SIZE (16*1024)
int copy_bytes(int fdf, int fdt, off_t max_bytes, bool try_reflink) {
bool try_sendfile = true;
int r;
assert(fdf >= 0);
assert(fdt >= 0);
/* Try btrfs reflinks first. */
if (try_reflink && max_bytes == (off_t) -1) {
r = btrfs_reflink(fdf, fdt);
if (r >= 0)
return r;
}
for (;;) {
size_t m = COPY_BUFFER_SIZE;
ssize_t n;
if (max_bytes != (off_t) -1) {
if (max_bytes <= 0)
return -EFBIG;
if ((off_t) m > max_bytes)
m = (size_t) max_bytes;
}
/* First try sendfile(), unless we already tried */
if (try_sendfile) {
n = sendfile(fdt, fdf, NULL, m);
if (n < 0) {
if (errno != EINVAL && errno != ENOSYS)
return -errno;
try_sendfile = false;
/* use fallback below */
} else if (n == 0) /* EOF */
break;
else if (n > 0)
/* Succcess! */
goto next;
}
/* As a fallback just copy bits by hand */
{
char buf[m];
n = read(fdf, buf, m);
if (n < 0)
return -errno;
if (n == 0) /* EOF */
break;
r = loop_write(fdt, buf, (size_t) n, false);
if (r < 0)
return r;
}
next:
if (max_bytes != (off_t) -1) {
assert(max_bytes >= n);
max_bytes -= n;
}
}
return 0;
}
static int fd_copy_symlink(int df, const char *from, const struct stat *st, int dt, const char *to) {
_cleanup_free_ char *target = NULL;
int r;
assert(from);
assert(st);
assert(to);
r = readlinkat_malloc(df, from, &target);
if (r < 0)
return r;
if (symlinkat(target, dt, to) < 0)
return -errno;
if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
return -errno;
return 0;
}
static int fd_copy_regular(int df, const char *from, const struct stat *st, int dt, const char *to) {
_cleanup_close_ int fdf = -1, fdt = -1;
struct timespec ts[2];
int r, q;
assert(from);
assert(st);
assert(to);
fdf = openat(df, from, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
if (fdf < 0)
return -errno;
fdt = openat(dt, to, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, st->st_mode & 07777);
if (fdt < 0)
return -errno;
r = copy_bytes(fdf, fdt, (off_t) -1, true);
if (r < 0) {
unlinkat(dt, to, 0);
return r;
}
if (fchown(fdt, st->st_uid, st->st_gid) < 0)
r = -errno;
if (fchmod(fdt, st->st_mode & 07777) < 0)
r = -errno;
ts[0] = st->st_atim;
ts[1] = st->st_mtim;
(void) futimens(fdt, ts);
(void) copy_xattr(fdf, fdt);
q = close(fdt);
fdt = -1;
if (q < 0) {
r = -errno;
unlinkat(dt, to, 0);
}
return r;
}
static int fd_copy_fifo(int df, const char *from, const struct stat *st, int dt, const char *to) {
int r;
assert(from);
assert(st);
assert(to);
r = mkfifoat(dt, to, st->st_mode & 07777);
if (r < 0)
return -errno;
if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
r = -errno;
if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
r = -errno;
return r;
}
static int fd_copy_node(int df, const char *from, const struct stat *st, int dt, const char *to) {
int r;
assert(from);
assert(st);
assert(to);
r = mknodat(dt, to, st->st_mode, st->st_rdev);
if (r < 0)
return -errno;
if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
r = -errno;
if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
r = -errno;
return r;
}
static int fd_copy_directory(
int df,
const char *from,
const struct stat *st,
int dt,
const char *to,
dev_t original_device,
bool merge) {
_cleanup_close_ int fdf = -1, fdt = -1;
_cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
bool created;
int r;
assert(st);
assert(to);
if (from)
fdf = openat(df, from, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
else
fdf = fcntl(df, F_DUPFD_CLOEXEC, 3);
d = fdopendir(fdf);
if (!d)
return -errno;
fdf = -1;
r = mkdirat(dt, to, st->st_mode & 07777);
if (r >= 0)
created = true;
else if (errno == EEXIST && merge)
created = false;
else
return -errno;
fdt = openat(dt, to, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
if (fdt < 0)
return -errno;
r = 0;
if (created) {
struct timespec ut[2] = {
st->st_atim,
st->st_mtim
};
if (fchown(fdt, st->st_uid, st->st_gid) < 0)
r = -errno;
if (fchmod(fdt, st->st_mode & 07777) < 0)
r = -errno;
(void) futimens(fdt, ut);
(void) copy_xattr(dirfd(d), fdt);
}
FOREACH_DIRENT(de, d, return -errno) {
struct stat buf;
int q;
if (fstatat(dirfd(d), de->d_name, &buf, AT_SYMLINK_NOFOLLOW) < 0) {
r = -errno;
continue;
}
if (buf.st_dev != original_device)
continue;
if (S_ISREG(buf.st_mode))
q = fd_copy_regular(dirfd(d), de->d_name, &buf, fdt, de->d_name);
else if (S_ISDIR(buf.st_mode))
q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, merge);
else if (S_ISLNK(buf.st_mode))
q = fd_copy_symlink(dirfd(d), de->d_name, &buf, fdt, de->d_name);
else if (S_ISFIFO(buf.st_mode))
q = fd_copy_fifo(dirfd(d), de->d_name, &buf, fdt, de->d_name);
else if (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode))
q = fd_copy_node(dirfd(d), de->d_name, &buf, fdt, de->d_name);
else
q = -EOPNOTSUPP;
if (q == -EEXIST && merge)
q = 0;
if (q < 0)
r = q;
}
return r;
}
int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge) {
struct stat st;
assert(from);
assert(to);
if (fstatat(fdf, from, &st, AT_SYMLINK_NOFOLLOW) < 0)
return -errno;
if (S_ISREG(st.st_mode))
return fd_copy_regular(fdf, from, &st, fdt, to);
else if (S_ISDIR(st.st_mode))
return fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, merge);
else if (S_ISLNK(st.st_mode))
return fd_copy_symlink(fdf, from, &st, fdt, to);
else if (S_ISFIFO(st.st_mode))
return fd_copy_fifo(fdf, from, &st, fdt, to);
else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode))
return fd_copy_node(fdf, from, &st, fdt, to);
else
return -EOPNOTSUPP;
}
int copy_tree(const char *from, const char *to, bool merge) {
return copy_tree_at(AT_FDCWD, from, AT_FDCWD, to, merge);
}
int copy_directory_fd(int dirfd, const char *to, bool merge) {
struct stat st;
assert(dirfd >= 0);
assert(to);
if (fstat(dirfd, &st) < 0)
return -errno;
if (!S_ISDIR(st.st_mode))
return -ENOTDIR;
return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, merge);
}
int copy_file_fd(const char *from, int fdt, bool try_reflink) {
_cleanup_close_ int fdf = -1;
int r;
assert(from);
assert(fdt >= 0);
fdf = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fdf < 0)
return -errno;
r = copy_bytes(fdf, fdt, (off_t) -1, try_reflink);
(void) copy_times(fdf, fdt);
(void) copy_xattr(fdf, fdt);
return r;
}
int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned chattr_flags) {
int fdt = -1, r;
assert(from);
assert(to);
RUN_WITH_UMASK(0000) {
fdt = open(to, flags|O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, mode);
if (fdt < 0)
return -errno;
}
if (chattr_flags != 0)
(void) chattr_fd(fdt, chattr_flags, (unsigned) -1);
r = copy_file_fd(from, fdt, true);
if (r < 0) {
close(fdt);
unlink(to);
return r;
}
if (close(fdt) < 0) {
unlink_noerrno(to);
return -errno;
}
return 0;
}
int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace, unsigned chattr_flags) {
_cleanup_free_ char *t = NULL;
int r;
assert(from);
assert(to);
r = tempfn_random(to, &t);
if (r < 0)
return r;
r = copy_file(from, t, O_NOFOLLOW|O_EXCL, mode, chattr_flags);
if (r < 0)
return r;
if (replace) {
r = renameat(AT_FDCWD, t, AT_FDCWD, to);
if (r < 0)
r = -errno;
} else
r = rename_noreplace(AT_FDCWD, t, AT_FDCWD, to);
if (r < 0) {
(void) unlink_noerrno(t);
return r;
}
return 0;
}
int copy_times(int fdf, int fdt) {
struct timespec ut[2];
struct stat st;
usec_t crtime = 0;
assert(fdf >= 0);
assert(fdt >= 0);
if (fstat(fdf, &st) < 0)
return -errno;
ut[0] = st.st_atim;
ut[1] = st.st_mtim;
if (futimens(fdt, ut) < 0)
return -errno;
if (fd_getcrtime(fdf, &crtime) >= 0)
(void) fd_setcrtime(fdt, crtime);
return 0;
}
int copy_xattr(int fdf, int fdt) {
_cleanup_free_ char *bufa = NULL, *bufb = NULL;
size_t sza = 100, szb = 100;
ssize_t n;
int ret = 0;
const char *p;
for (;;) {
bufa = malloc(sza);
if (!bufa)
return -ENOMEM;
n = flistxattr(fdf, bufa, sza);
if (n == 0)
return 0;
if (n > 0)
break;
if (errno != ERANGE)
return -errno;
sza *= 2;
free(bufa);
bufa = NULL;
}
p = bufa;
while (n > 0) {
size_t l;
l = strlen(p);
assert(l < (size_t) n);
if (startswith(p, "user.")) {
ssize_t m;
if (!bufb) {
bufb = malloc(szb);
if (!bufb)
return -ENOMEM;
}
m = fgetxattr(fdf, p, bufb, szb);
if (m < 0) {
if (errno == ERANGE) {
szb *= 2;
free(bufb);
bufb = NULL;
continue;
}
return -errno;
}
if (fsetxattr(fdt, p, bufb, m, 0) < 0)
ret = -errno;
}
p += l + 1;
n -= l + 1;
}
return ret;
}
+35
View File
@@ -0,0 +1,35 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2014 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <stdbool.h>
#include <sys/types.h>
int copy_file_fd(const char *from, int to, bool try_reflink);
int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned chattr_flags);
int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace, unsigned chattr_flags);
int copy_tree(const char *from, const char *to, bool merge);
int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge);
int copy_directory_fd(int dirfd, const char *to, bool merge);
int copy_bytes(int fdf, int fdt, off_t max_bytes, bool try_reflink);
int copy_times(int fdf, int fdt);
int copy_xattr(int fdf, int fdt);
+86
View File
@@ -0,0 +1,86 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "util.h"
#define DEFAULT_TIMEOUT_USEC (90*USEC_PER_SEC)
#define DEFAULT_RESTART_USEC (100*USEC_PER_MSEC)
#define DEFAULT_CONFIRM_USEC (30*USEC_PER_SEC)
#define DEFAULT_START_LIMIT_INTERVAL (10*USEC_PER_SEC)
#define DEFAULT_START_LIMIT_BURST 5
/* The default time after which exit-on-idle services exit. This
* should be kept lower than the watchdog timeout, because otherwise
* the watchdog pings will keep the loop busy. */
#define DEFAULT_EXIT_USEC (30*USEC_PER_SEC)
#define SYSTEMD_CGROUP_CONTROLLER "systemd"
#define SIGNALS_CRASH_HANDLER SIGSEGV,SIGILL,SIGFPE,SIGBUS,SIGQUIT,SIGABRT
#define SIGNALS_IGNORE SIGPIPE
#define DIGITS "0123456789"
#define LOWERCASE_LETTERS "abcdefghijklmnopqrstuvwxyz"
#define UPPERCASE_LETTERS "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
#define LETTERS LOWERCASE_LETTERS UPPERCASE_LETTERS
#define ALPHANUMERICAL LETTERS DIGITS
#define REBOOT_PARAM_FILE "/run/systemd/reboot-param"
#ifdef HAVE_SPLIT_USR
#define KBD_KEYMAP_DIRS \
"/usr/share/keymaps/\0" \
"/usr/share/kbd/keymaps/\0" \
"/usr/lib/kbd/keymaps/\0" \
"/lib/kbd/keymaps/\0"
#else
#define KBD_KEYMAP_DIRS \
"/usr/share/keymaps/\0" \
"/usr/share/kbd/keymaps/\0" \
"/usr/lib/kbd/keymaps/\0"
#endif
#define UNIX_SYSTEM_BUS_ADDRESS "unix:path=/var/run/dbus/system_bus_socket"
#define KERNEL_SYSTEM_BUS_ADDRESS "kernel:path=/sys/fs/kdbus/0-system/bus"
#ifdef ENABLE_KDBUS
# define DEFAULT_SYSTEM_BUS_ADDRESS KERNEL_SYSTEM_BUS_ADDRESS ";" UNIX_SYSTEM_BUS_ADDRESS
#else
# define DEFAULT_SYSTEM_BUS_ADDRESS UNIX_SYSTEM_BUS_ADDRESS
#endif
#define UNIX_USER_BUS_ADDRESS_FMT "unix:path=%s/bus"
#define KERNEL_USER_BUS_ADDRESS_FMT "kernel:path=/sys/fs/kdbus/"UID_FMT"-user/bus"
#define PLYMOUTH_SOCKET { \
.un.sun_family = AF_UNIX, \
.un.sun_path = "\0/org/freedesktop/plymouthd", \
}
#ifndef TTY_GID
#define TTY_GID 5
#endif
#define NOTIFY_FD_MAX 768
#define NOTIFY_BUFFER_MAX PIPE_BUF
+80
View File
@@ -0,0 +1,80 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2008-2011 Kay Sievers
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <stdio.h>
#include "device-nodes.h"
#include "utf8.h"
int whitelisted_char_for_devnode(char c, const char *white) {
if ((c >= '0' && c <= '9') ||
(c >= 'A' && c <= 'Z') ||
(c >= 'a' && c <= 'z') ||
strchr("#+-.:=@_", c) != NULL ||
(white != NULL && strchr(white, c) != NULL))
return 1;
return 0;
}
int encode_devnode_name(const char *str, char *str_enc, size_t len) {
size_t i, j;
if (str == NULL || str_enc == NULL)
return -EINVAL;
for (i = 0, j = 0; str[i] != '\0'; i++) {
int seqlen;
seqlen = utf8_encoded_valid_unichar(&str[i]);
if (seqlen > 1) {
if (len-j < (size_t)seqlen)
return -EINVAL;
memcpy(&str_enc[j], &str[i], seqlen);
j += seqlen;
i += (seqlen-1);
} else if (str[i] == '\\' || !whitelisted_char_for_devnode(str[i], NULL)) {
if (len-j < 4)
return -EINVAL;
sprintf(&str_enc[j], "\\x%02x", (unsigned char) str[i]);
j += 4;
} else {
if (len-j < 1)
return -EINVAL;
str_enc[j] = str[i];
j++;
}
}
if (len-j < 1)
return -EINVAL;
str_enc[j] = '\0';
return 0;
}
+25
View File
@@ -0,0 +1,25 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2012 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
int encode_devnode_name(const char *str, char *str_enc, size_t len);
int whitelisted_char_for_devnode(char c, const char *additional);
+594
View File
@@ -0,0 +1,594 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2012 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <limits.h>
#include <unistd.h>
#include "strv.h"
#include "utf8.h"
#include "util.h"
#include "env-util.h"
#include "def.h"
#define VALID_CHARS_ENV_NAME \
DIGITS LETTERS \
"_"
#ifndef ARG_MAX
#define ARG_MAX ((size_t) sysconf(_SC_ARG_MAX))
#endif
static bool env_name_is_valid_n(const char *e, size_t n) {
const char *p;
if (!e)
return false;
if (n <= 0)
return false;
if (e[0] >= '0' && e[0] <= '9')
return false;
/* POSIX says the overall size of the environment block cannot
* be > ARG_MAX, an individual assignment hence cannot be
* either. Discounting the equal sign and trailing NUL this
* hence leaves ARG_MAX-2 as longest possible variable
* name. */
if (n > ARG_MAX - 2)
return false;
for (p = e; p < e + n; p++)
if (!strchr(VALID_CHARS_ENV_NAME, *p))
return false;
return true;
}
bool env_name_is_valid(const char *e) {
if (!e)
return false;
return env_name_is_valid_n(e, strlen(e));
}
bool env_value_is_valid(const char *e) {
if (!e)
return false;
if (!utf8_is_valid(e))
return false;
/* bash allows tabs in environment variables, and so should
* we */
if (string_has_cc(e, "\t"))
return false;
/* POSIX says the overall size of the environment block cannot
* be > ARG_MAX, an individual assignment hence cannot be
* either. Discounting the shortest possible variable name of
* length 1, the equal sign and trailing NUL this hence leaves
* ARG_MAX-3 as longest possible variable value. */
if (strlen(e) > ARG_MAX - 3)
return false;
return true;
}
bool env_assignment_is_valid(const char *e) {
const char *eq;
eq = strchr(e, '=');
if (!eq)
return false;
if (!env_name_is_valid_n(e, eq - e))
return false;
if (!env_value_is_valid(eq + 1))
return false;
/* POSIX says the overall size of the environment block cannot
* be > ARG_MAX, hence the individual variable assignments
* cannot be either, but let's leave room for one trailing NUL
* byte. */
if (strlen(e) > ARG_MAX - 1)
return false;
return true;
}
bool strv_env_is_valid(char **e) {
char **p, **q;
STRV_FOREACH(p, e) {
size_t k;
if (!env_assignment_is_valid(*p))
return false;
/* Check if there are duplicate assginments */
k = strcspn(*p, "=");
STRV_FOREACH(q, p + 1)
if (strneq(*p, *q, k) && (*q)[k] == '=')
return false;
}
return true;
}
bool strv_env_name_or_assignment_is_valid(char **l) {
char **p, **q;
STRV_FOREACH(p, l) {
if (!env_assignment_is_valid(*p) && !env_name_is_valid(*p))
return false;
STRV_FOREACH(q, p + 1)
if (streq(*p, *q))
return false;
}
return true;
}
static int env_append(char **r, char ***k, char **a) {
assert(r);
assert(k);
if (!a)
return 0;
/* Add the entries of a to *k unless they already exist in *r
* in which case they are overridden instead. This assumes
* there is enough space in the r array. */
for (; *a; a++) {
char **j;
size_t n;
n = strcspn(*a, "=");
if ((*a)[n] == '=')
n++;
for (j = r; j < *k; j++)
if (strneq(*j, *a, n))
break;
if (j >= *k)
(*k)++;
else
free(*j);
*j = strdup(*a);
if (!*j)
return -ENOMEM;
}
return 0;
}
char **strv_env_merge(unsigned n_lists, ...) {
size_t n = 0;
char **l, **k, **r;
va_list ap;
unsigned i;
/* Merges an arbitrary number of environment sets */
va_start(ap, n_lists);
for (i = 0; i < n_lists; i++) {
l = va_arg(ap, char**);
n += strv_length(l);
}
va_end(ap);
r = new(char*, n+1);
if (!r)
return NULL;
k = r;
va_start(ap, n_lists);
for (i = 0; i < n_lists; i++) {
l = va_arg(ap, char**);
if (env_append(r, &k, l) < 0)
goto fail;
}
va_end(ap);
*k = NULL;
return r;
fail:
va_end(ap);
strv_free(r);
return NULL;
}
_pure_ static bool env_match(const char *t, const char *pattern) {
assert(t);
assert(pattern);
/* pattern a matches string a
* a matches a=
* a matches a=b
* a= matches a=
* a=b matches a=b
* a= does not match a
* a=b does not match a=
* a=b does not match a
* a=b does not match a=c */
if (streq(t, pattern))
return true;
if (!strchr(pattern, '=')) {
size_t l = strlen(pattern);
return strneq(t, pattern, l) && t[l] == '=';
}
return false;
}
char **strv_env_delete(char **x, unsigned n_lists, ...) {
size_t n, i = 0;
char **k, **r;
va_list ap;
/* Deletes every entry from x that is mentioned in the other
* string lists */
n = strv_length(x);
r = new(char*, n+1);
if (!r)
return NULL;
STRV_FOREACH(k, x) {
unsigned v;
va_start(ap, n_lists);
for (v = 0; v < n_lists; v++) {
char **l, **j;
l = va_arg(ap, char**);
STRV_FOREACH(j, l)
if (env_match(*k, *j))
goto skip;
}
va_end(ap);
r[i] = strdup(*k);
if (!r[i]) {
strv_free(r);
return NULL;
}
i++;
continue;
skip:
va_end(ap);
}
r[i] = NULL;
assert(i <= n);
return r;
}
char **strv_env_unset(char **l, const char *p) {
char **f, **t;
if (!l)
return NULL;
assert(p);
/* Drops every occurrence of the env var setting p in the
* string list. Edits in-place. */
for (f = t = l; *f; f++) {
if (env_match(*f, p)) {
free(*f);
continue;
}
*(t++) = *f;
}
*t = NULL;
return l;
}
char **strv_env_unset_many(char **l, ...) {
char **f, **t;
if (!l)
return NULL;
/* Like strv_env_unset() but applies many at once. Edits in-place. */
for (f = t = l; *f; f++) {
bool found = false;
const char *p;
va_list ap;
va_start(ap, l);
while ((p = va_arg(ap, const char*))) {
if (env_match(*f, p)) {
found = true;
break;
}
}
va_end(ap);
if (found) {
free(*f);
continue;
}
*(t++) = *f;
}
*t = NULL;
return l;
}
char **strv_env_set(char **x, const char *p) {
char **k, **r;
char* m[2] = { (char*) p, NULL };
/* Overrides the env var setting of p, returns a new copy */
r = new(char*, strv_length(x)+2);
if (!r)
return NULL;
k = r;
if (env_append(r, &k, x) < 0)
goto fail;
if (env_append(r, &k, m) < 0)
goto fail;
*k = NULL;
return r;
fail:
strv_free(r);
return NULL;
}
char *strv_env_get_n(char **l, const char *name, size_t k) {
char **i;
assert(name);
if (k <= 0)
return NULL;
STRV_FOREACH(i, l)
if (strneq(*i, name, k) &&
(*i)[k] == '=')
return *i + k + 1;
return NULL;
}
char *strv_env_get(char **l, const char *name) {
assert(name);
return strv_env_get_n(l, name, strlen(name));
}
char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) {
char **p, **q;
int k = 0;
STRV_FOREACH(p, e) {
size_t n;
bool duplicate = false;
if (!env_assignment_is_valid(*p)) {
if (invalid_callback)
invalid_callback(*p, userdata);
free(*p);
continue;
}
n = strcspn(*p, "=");
STRV_FOREACH(q, p + 1)
if (strneq(*p, *q, n) && (*q)[n] == '=') {
duplicate = true;
break;
}
if (duplicate) {
free(*p);
continue;
}
e[k++] = *p;
}
if (e)
e[k] = NULL;
return e;
}
char *replace_env(const char *format, char **env) {
enum {
WORD,
CURLY,
VARIABLE
} state = WORD;
const char *e, *word = format;
char *r = NULL, *k;
assert(format);
for (e = format; *e; e ++) {
switch (state) {
case WORD:
if (*e == '$')
state = CURLY;
break;
case CURLY:
if (*e == '{') {
k = strnappend(r, word, e-word-1);
if (!k)
goto fail;
free(r);
r = k;
word = e-1;
state = VARIABLE;
} else if (*e == '$') {
k = strnappend(r, word, e-word);
if (!k)
goto fail;
free(r);
r = k;
word = e+1;
state = WORD;
} else
state = WORD;
break;
case VARIABLE:
if (*e == '}') {
const char *t;
t = strempty(strv_env_get_n(env, word+2, e-word-2));
k = strappend(r, t);
if (!k)
goto fail;
free(r);
r = k;
word = e+1;
state = WORD;
}
break;
}
}
k = strnappend(r, word, e-word);
if (!k)
goto fail;
free(r);
return k;
fail:
free(r);
return NULL;
}
char **replace_env_argv(char **argv, char **env) {
char **ret, **i;
unsigned k = 0, l = 0;
l = strv_length(argv);
ret = new(char*, l+1);
if (!ret)
return NULL;
STRV_FOREACH(i, argv) {
/* If $FOO appears as single word, replace it by the split up variable */
if ((*i)[0] == '$' && (*i)[1] != '{') {
char *e;
char **w, **m = NULL;
unsigned q;
e = strv_env_get(env, *i+1);
if (e) {
int r;
r = strv_split_quoted(&m, e, UNQUOTE_RELAX);
if (r < 0) {
ret[k] = NULL;
strv_free(ret);
return NULL;
}
} else
m = NULL;
q = strv_length(m);
l = l + q - 1;
w = realloc(ret, sizeof(char*) * (l+1));
if (!w) {
ret[k] = NULL;
strv_free(ret);
strv_free(m);
return NULL;
}
ret = w;
if (m) {
memcpy(ret + k, m, q * sizeof(char*));
free(m);
}
k += q;
continue;
}
/* If ${FOO} appears as part of a word, replace it by the variable as-is */
ret[k] = replace_env(*i, env);
if (!ret[k]) {
strv_free(ret);
return NULL;
}
k++;
}
ret[k] = NULL;
return ret;
}
+49
View File
@@ -0,0 +1,49 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2013 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <stdbool.h>
#include "macro.h"
bool env_name_is_valid(const char *e);
bool env_value_is_valid(const char *e);
bool env_assignment_is_valid(const char *e);
char *replace_env(const char *format, char **env);
char **replace_env_argv(char **argv, char **env);
bool strv_env_is_valid(char **e);
#define strv_env_clean(l) strv_env_clean_with_callback(l, NULL, NULL)
char **strv_env_clean_with_callback(char **l, void (*invalid_callback)(const char *p, void *userdata), void *userdata);
bool strv_env_name_or_assignment_is_valid(char **l);
char **strv_env_merge(unsigned n_lists, ...);
char **strv_env_delete(char **x, unsigned n_lists, ...); /* New copy */
char **strv_env_set(char **x, const char *p); /* New copy ... */
char **strv_env_unset(char **l, const char *p); /* In place ... */
char **strv_env_unset_many(char **l, ...) _sentinel_;
char *strv_env_get_n(char **l, const char *name, size_t k) _pure_;
char *strv_env_get(char **x, const char *n) _pure_;
+58
View File
@@ -0,0 +1,58 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2013 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <string.h>
#include "util.h"
#include "errno-list.h"
static const struct errno_name* lookup_errno(register const char *str,
register unsigned int len);
#include "errno-to-name.h"
#include "errno-from-name.h"
const char *errno_to_name(int id) {
if (id < 0)
id = -id;
if (id >= (int) ELEMENTSOF(errno_names))
return NULL;
return errno_names[id];
}
int errno_from_name(const char *name) {
const struct errno_name *sc;
assert(name);
sc = lookup_errno(name, strlen(name));
if (!sc)
return 0;
return sc->id;
}
int errno_max(void) {
return ELEMENTSOF(errno_names);
}
+27
View File
@@ -0,0 +1,27 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2013 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
const char *errno_to_name(int id);
int errno_from_name(const char *name);
int errno_max(void);
+27
View File
@@ -0,0 +1,27 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2014 Tom Gundersen
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <net/ethernet.h>
#define ETHER_ADDR_FORMAT_STR "%02X%02X%02X%02X%02X%02X"
#define ETHER_ADDR_FORMAT_VAL(x) (x).ether_addr_octet[0], (x).ether_addr_octet[1], (x).ether_addr_octet[2], (x).ether_addr_octet[3], (x).ether_addr_octet[4], (x).ether_addr_octet[5]
+241
View File
@@ -0,0 +1,241 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <stdlib.h>
#include "exit-status.h"
#include "set.h"
#include "macro.h"
const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) {
/* We cast to int here, so that -Wenum doesn't complain that
* EXIT_SUCCESS/EXIT_FAILURE aren't in the enum */
switch ((int) status) {
case EXIT_SUCCESS:
return "SUCCESS";
case EXIT_FAILURE:
return "FAILURE";
}
if (level == EXIT_STATUS_SYSTEMD || level == EXIT_STATUS_LSB) {
switch ((int) status) {
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_MAKE_STARTER:
return "MAKE_STARTER";
case EXIT_BUS_ENDPOINT:
return "BUS_ENDPOINT";
}
}
if (level == EXIT_STATUS_LSB) {
switch ((int) status) {
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";
}
}
return NULL;
}
bool is_clean_exit(int code, int status, 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
status == SIGHUP ||
status == SIGINT ||
status == SIGTERM ||
status == SIGPIPE ||
(success_status &&
set_contains(success_status->signal, INT_TO_PTR(status)));
return false;
}
bool is_clean_exit_lsb(int code, int status, ExitStatusSet *success_status) {
if (is_clean_exit(code, status, success_status))
return true;
return
code == CLD_EXITED &&
(status == EXIT_NOTINSTALLED || status == EXIT_NOTCONFIGURED);
}
void exit_status_set_free(ExitStatusSet *x) {
assert(x);
set_free(x->status);
set_free(x->signal);
x->status = x->signal = NULL;
}
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;
}
+103
View File
@@ -0,0 +1,103 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <stdbool.h>
#include "set.h"
typedef enum ExitStatus {
/* 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,
/* The LSB suggests that error codes >= 200 are "reserved". We
* use them here under the assumption that they hence are
* unused by init scripts.
*
* http://refspecs.linuxfoundation.org/LSB_3.2.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html */
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_MAKE_STARTER,
EXIT_CHOWN,
EXIT_BUS_ENDPOINT,
EXIT_SMACK_PROCESS_LABEL,
} ExitStatus;
typedef enum ExitStatusLevel {
EXIT_STATUS_MINIMAL,
EXIT_STATUS_SYSTEMD,
EXIT_STATUS_LSB,
EXIT_STATUS_FULL = EXIT_STATUS_LSB
} ExitStatusLevel;
typedef struct ExitStatusSet {
Set *status;
Set *signal;
} ExitStatusSet;
const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) _const_;
bool is_clean_exit(int code, int status, ExitStatusSet *success_status);
bool is_clean_exit_lsb(int code, int status, 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);
+285
View File
@@ -0,0 +1,285 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <errno.h>
#include <dirent.h>
#include <fcntl.h>
#include "set.h"
#include "util.h"
#include "macro.h"
#include "fdset.h"
#include "sd-daemon.h"
#define MAKE_SET(s) ((Set*) s)
#define MAKE_FDSET(s) ((FDSet*) s)
/* Make sure we can distinguish fd 0 and NULL */
#define FD_TO_PTR(fd) INT_TO_PTR((fd)+1)
#define PTR_TO_FD(p) (PTR_TO_INT(p)-1)
FDSet *fdset_new(void) {
return MAKE_FDSET(set_new(NULL));
}
int fdset_new_array(FDSet **ret, int *fds, unsigned n_fds) {
unsigned i;
FDSet *s;
int r;
assert(ret);
s = fdset_new();
if (!s)
return -ENOMEM;
for (i = 0; i < n_fds; i++) {
r = fdset_put(s, fds[i]);
if (r < 0) {
set_free(MAKE_SET(s));
return r;
}
}
*ret = s;
return 0;
}
FDSet* fdset_free(FDSet *s) {
void *p;
while ((p = set_steal_first(MAKE_SET(s)))) {
/* Valgrind's fd might have ended up in this set here,
* due to fdset_new_fill(). We'll ignore all failures
* here, so that the EBADFD that valgrind will return
* us on close() doesn't influence us */
/* When reloading duplicates of the private bus
* connection fds and suchlike are closed here, which
* has no effect at all, since they are only
* duplicates. So don't be surprised about these log
* messages. */
log_debug("Closing left-over fd %i", PTR_TO_FD(p));
close_nointr(PTR_TO_FD(p));
}
set_free(MAKE_SET(s));
return NULL;
}
int fdset_put(FDSet *s, int fd) {
assert(s);
assert(fd >= 0);
return set_put(MAKE_SET(s), FD_TO_PTR(fd));
}
int fdset_consume(FDSet *s, int fd) {
int r;
assert(s);
assert(fd >= 0);
r = fdset_put(s, fd);
if (r <= 0)
safe_close(fd);
return r;
}
int fdset_put_dup(FDSet *s, int fd) {
int copy, r;
assert(s);
assert(fd >= 0);
copy = fcntl(fd, F_DUPFD_CLOEXEC, 3);
if (copy < 0)
return -errno;
r = fdset_put(s, copy);
if (r < 0) {
safe_close(copy);
return r;
}
return copy;
}
bool fdset_contains(FDSet *s, int fd) {
assert(s);
assert(fd >= 0);
return !!set_get(MAKE_SET(s), FD_TO_PTR(fd));
}
int fdset_remove(FDSet *s, int fd) {
assert(s);
assert(fd >= 0);
return set_remove(MAKE_SET(s), FD_TO_PTR(fd)) ? fd : -ENOENT;
}
int fdset_new_fill(FDSet **_s) {
_cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
int r = 0;
FDSet *s;
assert(_s);
/* Creates an fdset and fills in all currently open file
* descriptors. */
d = opendir("/proc/self/fd");
if (!d)
return -errno;
s = fdset_new();
if (!s) {
r = -ENOMEM;
goto finish;
}
while ((de = readdir(d))) {
int fd = -1;
if (hidden_file(de->d_name))
continue;
r = safe_atoi(de->d_name, &fd);
if (r < 0)
goto finish;
if (fd < 3)
continue;
if (fd == dirfd(d))
continue;
r = fdset_put(s, fd);
if (r < 0)
goto finish;
}
r = 0;
*_s = s;
s = NULL;
finish:
/* We won't close the fds here! */
if (s)
set_free(MAKE_SET(s));
return r;
}
int fdset_cloexec(FDSet *fds, bool b) {
Iterator i;
void *p;
int r;
assert(fds);
SET_FOREACH(p, MAKE_SET(fds), i)
if ((r = fd_cloexec(PTR_TO_FD(p), b)) < 0)
return r;
return 0;
}
int fdset_new_listen_fds(FDSet **_s, bool unset) {
int n, fd, r;
FDSet *s;
assert(_s);
/* Creates an fdset and fills in all passed file descriptors */
s = fdset_new();
if (!s) {
r = -ENOMEM;
goto fail;
}
n = sd_listen_fds(unset);
for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd ++) {
r = fdset_put(s, fd);
if (r < 0)
goto fail;
}
*_s = s;
return 0;
fail:
if (s)
set_free(MAKE_SET(s));
return r;
}
int fdset_close_others(FDSet *fds) {
void *e;
Iterator i;
int *a;
unsigned j, m;
j = 0, m = fdset_size(fds);
a = alloca(sizeof(int) * m);
SET_FOREACH(e, MAKE_SET(fds), i)
a[j++] = PTR_TO_FD(e);
assert(j == m);
return close_all_fds(a, j);
}
unsigned fdset_size(FDSet *fds) {
return set_size(MAKE_SET(fds));
}
bool fdset_isempty(FDSet *fds) {
return set_isempty(MAKE_SET(fds));
}
int fdset_iterate(FDSet *s, Iterator *i) {
void *p;
p = set_iterate(MAKE_SET(s), i);
if (!p)
return -ENOENT;
return PTR_TO_FD(p);
}
int fdset_steal_first(FDSet *fds) {
void *p;
p = set_steal_first(MAKE_SET(fds));
if (!p)
return -ENOENT;
return PTR_TO_FD(p);
}
+57
View File
@@ -0,0 +1,57 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "set.h"
typedef struct FDSet FDSet;
FDSet* fdset_new(void);
FDSet* fdset_free(FDSet *s);
int fdset_put(FDSet *s, int fd);
int fdset_put_dup(FDSet *s, int fd);
int fdset_consume(FDSet *s, int fd);
bool fdset_contains(FDSet *s, int fd);
int fdset_remove(FDSet *s, int fd);
int fdset_new_array(FDSet **ret, int *fds, unsigned n_fds);
int fdset_new_fill(FDSet **ret);
int fdset_new_listen_fds(FDSet **ret, bool unset);
int fdset_cloexec(FDSet *fds, bool b);
int fdset_close_others(FDSet *fds);
unsigned fdset_size(FDSet *fds);
bool fdset_isempty(FDSet *fds);
int fdset_iterate(FDSet *s, Iterator *i);
int fdset_steal_first(FDSet *fds);
#define FDSET_FOREACH(fd, fds, i) \
for ((i) = ITERATOR_FIRST, (fd) = fdset_iterate((fds), &(i)); (fd) >= 0; (fd) = fdset_iterate((fds), &(i)))
DEFINE_TRIVIAL_CLEANUP_FUNC(FDSet*, fdset_free);
#define _cleanup_fdset_free_ _cleanup_(fdset_freep)
+68
View File
@@ -0,0 +1,68 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
Copyright 2010 Harald Hoyer
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "util.h"
#include "selinux-util.h"
#include "fileio-label.h"
int write_string_file_atomic_label(const char *fn, const char *line) {
int r;
r = mac_selinux_create_file_prepare(fn, S_IFREG);
if (r < 0)
return r;
r = write_string_file_atomic(fn, line);
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;
}
+31
View File
@@ -0,0 +1,31 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
Copyright 2010 Harald Hoyer
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <stdio.h>
#include "fileio.h"
int write_string_file_atomic_label(const char *fn, const char *line);
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);
+817
View File
@@ -0,0 +1,817 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <unistd.h>
#include "util.h"
#include "strv.h"
#include "utf8.h"
#include "ctype.h"
#include "fileio.h"
int write_string_stream(FILE *f, const char *line) {
assert(f);
assert(line);
errno = 0;
fputs(line, f);
if (!endswith(line, "\n"))
fputc('\n', f);
fflush(f);
if (ferror(f))
return errno ? -errno : -EIO;
return 0;
}
int write_string_file(const char *fn, const char *line) {
_cleanup_fclose_ FILE *f = NULL;
assert(fn);
assert(line);
f = fopen(fn, "we");
if (!f)
return -errno;
return write_string_stream(f, line);
}
int write_string_file_no_create(const char *fn, const char *line) {
_cleanup_fclose_ FILE *f = NULL;
int fd;
assert(fn);
assert(line);
/* We manually build our own version of fopen(..., "we") that
* works without O_CREAT */
fd = open(fn, O_WRONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0)
return -errno;
f = fdopen(fd, "we");
if (!f) {
safe_close(fd);
return -errno;
}
return write_string_stream(f, line);
}
int write_string_file_atomic(const char *fn, const char *line) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *p = NULL;
int r;
assert(fn);
assert(line);
r = fopen_temporary(fn, &f, &p);
if (r < 0)
return r;
fchmod_umask(fileno(f), 0644);
r = write_string_stream(f, line);
if (r >= 0) {
if (rename(p, fn) < 0)
r = -errno;
}
if (r < 0)
unlink(p);
return r;
}
int read_one_line_file(const char *fn, char **line) {
_cleanup_fclose_ FILE *f = NULL;
char t[LINE_MAX], *c;
assert(fn);
assert(line);
f = fopen(fn, "re");
if (!f)
return -errno;
if (!fgets(t, sizeof(t), f)) {
if (ferror(f))
return errno ? -errno : -EIO;
t[0] = 0;
}
c = strdup(t);
if (!c)
return -ENOMEM;
truncate_nl(c);
*line = c;
return 0;
}
int read_full_stream(FILE *f, char **contents, size_t *size) {
size_t n, l;
_cleanup_free_ char *buf = NULL;
struct stat st;
assert(f);
assert(contents);
if (fstat(fileno(f), &st) < 0)
return -errno;
n = LINE_MAX;
if (S_ISREG(st.st_mode)) {
/* Safety check */
if (st.st_size > 4*1024*1024)
return -E2BIG;
/* Start with the right file size, but be prepared for
* files from /proc which generally report a file size
* of 0 */
if (st.st_size > 0)
n = st.st_size;
}
l = 0;
for (;;) {
char *t;
size_t k;
t = realloc(buf, n+1);
if (!t)
return -ENOMEM;
buf = t;
k = fread(buf + l, 1, n - l, f);
if (k <= 0) {
if (ferror(f))
return -errno;
break;
}
l += k;
n *= 2;
/* Safety check */
if (n > 4*1024*1024)
return -E2BIG;
}
buf[l] = 0;
*contents = buf;
buf = NULL; /* do not free */
if (size)
*size = l;
return 0;
}
int read_full_file(const char *fn, char **contents, size_t *size) {
_cleanup_fclose_ FILE *f = NULL;
assert(fn);
assert(contents);
f = fopen(fn, "re");
if (!f)
return -errno;
return read_full_stream(f, contents, size);
}
static int parse_env_file_internal(
FILE *f,
const char *fname,
const char *newline,
int (*push) (const char *filename, unsigned line,
const char *key, char *value, void *userdata, int *n_pushed),
void *userdata,
int *n_pushed) {
_cleanup_free_ char *contents = NULL, *key = NULL;
size_t key_alloc = 0, n_key = 0, value_alloc = 0, n_value = 0, last_value_whitespace = (size_t) -1, last_key_whitespace = (size_t) -1;
char *p, *value = NULL;
int r;
unsigned line = 1;
enum {
PRE_KEY,
KEY,
PRE_VALUE,
VALUE,
VALUE_ESCAPE,
SINGLE_QUOTE_VALUE,
SINGLE_QUOTE_VALUE_ESCAPE,
DOUBLE_QUOTE_VALUE,
DOUBLE_QUOTE_VALUE_ESCAPE,
COMMENT,
COMMENT_ESCAPE
} state = PRE_KEY;
assert(newline);
if (f)
r = read_full_stream(f, &contents, NULL);
else
r = read_full_file(fname, &contents, NULL);
if (r < 0)
return r;
for (p = contents; *p; p++) {
char c = *p;
switch (state) {
case PRE_KEY:
if (strchr(COMMENTS, c))
state = COMMENT;
else if (!strchr(WHITESPACE, c)) {
state = KEY;
last_key_whitespace = (size_t) -1;
if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) {
r = -ENOMEM;
goto fail;
}
key[n_key++] = c;
}
break;
case KEY:
if (strchr(newline, c)) {
state = PRE_KEY;
line ++;
n_key = 0;
} else if (c == '=') {
state = PRE_VALUE;
last_value_whitespace = (size_t) -1;
} else {
if (!strchr(WHITESPACE, c))
last_key_whitespace = (size_t) -1;
else if (last_key_whitespace == (size_t) -1)
last_key_whitespace = n_key;
if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) {
r = -ENOMEM;
goto fail;
}
key[n_key++] = c;
}
break;
case PRE_VALUE:
if (strchr(newline, c)) {
state = PRE_KEY;
line ++;
key[n_key] = 0;
if (value)
value[n_value] = 0;
/* strip trailing whitespace from key */
if (last_key_whitespace != (size_t) -1)
key[last_key_whitespace] = 0;
r = push(fname, line, key, value, userdata, n_pushed);
if (r < 0)
goto fail;
n_key = 0;
value = NULL;
value_alloc = n_value = 0;
} else if (c == '\'')
state = SINGLE_QUOTE_VALUE;
else if (c == '\"')
state = DOUBLE_QUOTE_VALUE;
else if (c == '\\')
state = VALUE_ESCAPE;
else if (!strchr(WHITESPACE, c)) {
state = VALUE;
if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
r = -ENOMEM;
goto fail;
}
value[n_value++] = c;
}
break;
case VALUE:
if (strchr(newline, c)) {
state = PRE_KEY;
line ++;
key[n_key] = 0;
if (value)
value[n_value] = 0;
/* Chomp off trailing whitespace from value */
if (last_value_whitespace != (size_t) -1)
value[last_value_whitespace] = 0;
/* strip trailing whitespace from key */
if (last_key_whitespace != (size_t) -1)
key[last_key_whitespace] = 0;
r = push(fname, line, key, value, userdata, n_pushed);
if (r < 0)
goto fail;
n_key = 0;
value = NULL;
value_alloc = n_value = 0;
} else if (c == '\\') {
state = VALUE_ESCAPE;
last_value_whitespace = (size_t) -1;
} else {
if (!strchr(WHITESPACE, c))
last_value_whitespace = (size_t) -1;
else if (last_value_whitespace == (size_t) -1)
last_value_whitespace = n_value;
if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
r = -ENOMEM;
goto fail;
}
value[n_value++] = c;
}
break;
case VALUE_ESCAPE:
state = VALUE;
if (!strchr(newline, c)) {
/* Escaped newlines we eat up entirely */
if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
r = -ENOMEM;
goto fail;
}
value[n_value++] = c;
}
break;
case SINGLE_QUOTE_VALUE:
if (c == '\'')
state = PRE_VALUE;
else if (c == '\\')
state = SINGLE_QUOTE_VALUE_ESCAPE;
else {
if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
r = -ENOMEM;
goto fail;
}
value[n_value++] = c;
}
break;
case SINGLE_QUOTE_VALUE_ESCAPE:
state = SINGLE_QUOTE_VALUE;
if (!strchr(newline, c)) {
if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
r = -ENOMEM;
goto fail;
}
value[n_value++] = c;
}
break;
case DOUBLE_QUOTE_VALUE:
if (c == '\"')
state = PRE_VALUE;
else if (c == '\\')
state = DOUBLE_QUOTE_VALUE_ESCAPE;
else {
if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
r = -ENOMEM;
goto fail;
}
value[n_value++] = c;
}
break;
case DOUBLE_QUOTE_VALUE_ESCAPE:
state = DOUBLE_QUOTE_VALUE;
if (!strchr(newline, c)) {
if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
r = -ENOMEM;
goto fail;
}
value[n_value++] = c;
}
break;
case COMMENT:
if (c == '\\')
state = COMMENT_ESCAPE;
else if (strchr(newline, c)) {
state = PRE_KEY;
line ++;
}
break;
case COMMENT_ESCAPE:
state = COMMENT;
break;
}
}
if (state == PRE_VALUE ||
state == VALUE ||
state == VALUE_ESCAPE ||
state == SINGLE_QUOTE_VALUE ||
state == SINGLE_QUOTE_VALUE_ESCAPE ||
state == DOUBLE_QUOTE_VALUE ||
state == DOUBLE_QUOTE_VALUE_ESCAPE) {
key[n_key] = 0;
if (value)
value[n_value] = 0;
if (state == VALUE)
if (last_value_whitespace != (size_t) -1)
value[last_value_whitespace] = 0;
/* strip trailing whitespace from key */
if (last_key_whitespace != (size_t) -1)
key[last_key_whitespace] = 0;
r = push(fname, line, key, value, userdata, n_pushed);
if (r < 0)
goto fail;
}
return 0;
fail:
free(value);
return r;
}
static int parse_env_file_push(
const char *filename, unsigned line,
const char *key, char *value,
void *userdata,
int *n_pushed) {
const char *k;
va_list aq, *ap = userdata;
if (!utf8_is_valid(key)) {
_cleanup_free_ char *p;
p = utf8_escape_invalid(key);
log_error("%s:%u: invalid UTF-8 in key '%s', ignoring.", strna(filename), line, p);
return -EINVAL;
}
if (value && !utf8_is_valid(value)) {
_cleanup_free_ char *p;
p = utf8_escape_invalid(value);
log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, p);
return -EINVAL;
}
va_copy(aq, *ap);
while ((k = va_arg(aq, const char *))) {
char **v;
v = va_arg(aq, char **);
if (streq(key, k)) {
va_end(aq);
free(*v);
*v = value;
if (n_pushed)
(*n_pushed)++;
return 1;
}
}
va_end(aq);
free(value);
return 0;
}
int parse_env_file(
const char *fname,
const char *newline, ...) {
va_list ap;
int r, n_pushed = 0;
if (!newline)
newline = NEWLINE;
va_start(ap, newline);
r = parse_env_file_internal(NULL, fname, newline, parse_env_file_push, &ap, &n_pushed);
va_end(ap);
return r < 0 ? r : n_pushed;
}
static int load_env_file_push(
const char *filename, unsigned line,
const char *key, char *value,
void *userdata,
int *n_pushed) {
char ***m = userdata;
char *p;
int r;
if (!utf8_is_valid(key)) {
_cleanup_free_ char *t = utf8_escape_invalid(key);
log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename), line, t);
return -EINVAL;
}
if (value && !utf8_is_valid(value)) {
_cleanup_free_ char *t = utf8_escape_invalid(value);
log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, t);
return -EINVAL;
}
p = strjoin(key, "=", strempty(value), NULL);
if (!p)
return -ENOMEM;
r = strv_consume(m, p);
if (r < 0)
return r;
if (n_pushed)
(*n_pushed)++;
free(value);
return 0;
}
int load_env_file(FILE *f, const char *fname, const char *newline, char ***rl) {
char **m = NULL;
int r;
if (!newline)
newline = NEWLINE;
r = parse_env_file_internal(f, fname, newline, load_env_file_push, &m, NULL);
if (r < 0) {
strv_free(m);
return r;
}
*rl = m;
return 0;
}
static int load_env_file_push_pairs(
const char *filename, unsigned line,
const char *key, char *value,
void *userdata,
int *n_pushed) {
char ***m = userdata;
int r;
if (!utf8_is_valid(key)) {
_cleanup_free_ char *t = utf8_escape_invalid(key);
log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename), line, t);
return -EINVAL;
}
if (value && !utf8_is_valid(value)) {
_cleanup_free_ char *t = utf8_escape_invalid(value);
log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, t);
return -EINVAL;
}
r = strv_extend(m, key);
if (r < 0)
return -ENOMEM;
if (!value) {
r = strv_extend(m, "");
if (r < 0)
return -ENOMEM;
} else {
r = strv_push(m, value);
if (r < 0)
return r;
}
if (n_pushed)
(*n_pushed)++;
return 0;
}
int load_env_file_pairs(FILE *f, const char *fname, const char *newline, char ***rl) {
char **m = NULL;
int r;
if (!newline)
newline = NEWLINE;
r = parse_env_file_internal(f, fname, newline, load_env_file_push_pairs, &m, NULL);
if (r < 0) {
strv_free(m);
return r;
}
*rl = m;
return 0;
}
static void write_env_var(FILE *f, const char *v) {
const char *p;
p = strchr(v, '=');
if (!p) {
/* Fallback */
fputs(v, f);
fputc('\n', f);
return;
}
p++;
fwrite(v, 1, p-v, f);
if (string_has_cc(p, NULL) || chars_intersect(p, WHITESPACE SHELL_NEED_QUOTES)) {
fputc('\"', f);
for (; *p; p++) {
if (strchr(SHELL_NEED_ESCAPE, *p))
fputc('\\', f);
fputc(*p, f);
}
fputc('\"', f);
} else
fputs(p, f);
fputc('\n', f);
}
int write_env_file(const char *fname, char **l) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *p = NULL;
char **i;
int r;
assert(fname);
r = fopen_temporary(fname, &f, &p);
if (r < 0)
return r;
fchmod_umask(fileno(f), 0644);
STRV_FOREACH(i, l)
write_env_var(f, *i);
r = fflush_and_check(f);
if (r >= 0) {
if (rename(p, fname) >= 0)
return 0;
r = -errno;
}
unlink(p);
return r;
}
int executable_is_script(const char *path, char **interpreter) {
int r;
_cleanup_free_ char *line = NULL;
int len;
char *ans;
assert(path);
r = read_one_line_file(path, &line);
if (r < 0)
return r;
if (!startswith(line, "#!"))
return 0;
ans = strstrip(line + 2);
len = strcspn(ans, " \t");
if (len == 0)
return 0;
ans = strndup(ans, len);
if (!ans)
return -ENOMEM;
*interpreter = ans;
return 1;
}
/**
* Retrieve one field from a file like /proc/self/status. pattern
* should start with '\n' and end with a ':'. Whitespace and zeros
* after the ':' will be skipped. field must be freed afterwards.
*/
int get_status_field(const char *filename, const char *pattern, char **field) {
_cleanup_free_ char *status = NULL;
char *t;
size_t len;
int r;
assert(filename);
assert(pattern);
assert(field);
r = read_full_file(filename, &status, NULL);
if (r < 0)
return r;
t = strstr(status, pattern);
if (!t)
return -ENOENT;
t += strlen(pattern);
if (*t) {
t += strspn(t, " \t");
/* Also skip zeros, because when this is used for
* capabilities, we don't want the zeros. This way the
* same capability set always maps to the same string,
* irrespective of the total capability set size. For
* other numbers it shouldn't matter. */
t += strspn(t, "0");
/* Back off one char if there's nothing but whitespace
and zeros */
if (!*t || isspace(*t))
t --;
}
len = strcspn(t, WHITESPACE);
*field = strndup(t, len);
if (!*field)
return -ENOMEM;
return 0;
}
+45
View File
@@ -0,0 +1,45 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <stddef.h>
#include <stdio.h>
#include "macro.h"
int write_string_stream(FILE *f, const char *line);
int write_string_file(const char *fn, const char *line);
int write_string_file_no_create(const char *fn, const char *line);
int write_string_file_atomic(const char *fn, const char *line);
int read_one_line_file(const char *fn, char **line);
int read_full_file(const char *fn, char **contents, size_t *size);
int read_full_stream(FILE *f, char **contents, size_t *size);
int parse_env_file(const char *fname, const char *separator, ...) _sentinel_;
int load_env_file(FILE *f, const char *fname, const char *separator, char ***l);
int load_env_file_pairs(FILE *f, const char *fname, const char *separator, char ***l);
int write_env_file(const char *fname, char **l);
int executable_is_script(const char *path, char **interpreter);
int get_status_field(const char *filename, const char *pattern, char **field);
+110
View File
@@ -0,0 +1,110 @@
/* gunicode.c - Unicode manipulation functions
*
* Copyright (C) 1999, 2000 Tom Tromey
* Copyright 2000, 2005 Red Hat, Inc.
*/
#include "gunicode.h"
#define unichar uint32_t
/**
* g_utf8_prev_char:
* @p: a pointer to a position within a UTF-8 encoded string
*
* Finds the previous UTF-8 character in the string before @p.
*
* @p does not have to be at the beginning of a UTF-8 character. No check
* is made to see if the character found is actually valid other than
* it starts with an appropriate byte. If @p might be the first
* character of the string, you must use g_utf8_find_prev_char() instead.
*
* Return value: a pointer to the found character.
**/
char *
utf8_prev_char (const char *p)
{
while (1)
{
p--;
if ((*p & 0xc0) != 0x80)
return (char *)p;
}
}
struct Interval
{
unichar start, end;
};
static int
interval_compare (const void *key, const void *elt)
{
unichar c = (unichar) (long) (key);
struct Interval *interval = (struct Interval *)elt;
if (c < interval->start)
return -1;
if (c > interval->end)
return +1;
return 0;
}
/*
* NOTE:
*
* The tables for g_unichar_iswide() and g_unichar_iswide_cjk() are
* generated from the Unicode Character Database's file
* extracted/DerivedEastAsianWidth.txt using the gen-iswide-table.py
* in this way:
*
* ./gen-iswide-table.py < path/to/ucd/extracted/DerivedEastAsianWidth.txt | fmt
*
* Last update for Unicode 6.0.
*/
/**
* g_unichar_iswide:
* @c: a Unicode character
*
* Determines if a character is typically rendered in a double-width
* cell.
*
* Return value: %TRUE if the character is wide
**/
bool
unichar_iswide (unichar c)
{
/* See NOTE earlier for how to update this table. */
static const struct Interval wide[] = {
{0x1100, 0x115F}, {0x2329, 0x232A}, {0x2E80, 0x2E99}, {0x2E9B, 0x2EF3},
{0x2F00, 0x2FD5}, {0x2FF0, 0x2FFB}, {0x3000, 0x303E}, {0x3041, 0x3096},
{0x3099, 0x30FF}, {0x3105, 0x312D}, {0x3131, 0x318E}, {0x3190, 0x31BA},
{0x31C0, 0x31E3}, {0x31F0, 0x321E}, {0x3220, 0x3247}, {0x3250, 0x32FE},
{0x3300, 0x4DBF}, {0x4E00, 0xA48C}, {0xA490, 0xA4C6}, {0xA960, 0xA97C},
{0xAC00, 0xD7A3}, {0xF900, 0xFAFF}, {0xFE10, 0xFE19}, {0xFE30, 0xFE52},
{0xFE54, 0xFE66}, {0xFE68, 0xFE6B}, {0xFF01, 0xFF60}, {0xFFE0, 0xFFE6},
{0x1B000, 0x1B001}, {0x1F200, 0x1F202}, {0x1F210, 0x1F23A},
{0x1F240, 0x1F248}, {0x1F250, 0x1F251},
{0x1F300, 0x1F567}, /* Miscellaneous Symbols and Pictographs */
{0x20000, 0x2FFFD}, {0x30000, 0x3FFFD},
};
if (bsearch ((void *)(uintptr_t)c, wide, (sizeof (wide) / sizeof ((wide)[0])), sizeof wide[0],
interval_compare))
return true;
return false;
}
const char utf8_skip_data[256] = {
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1
};
+30
View File
@@ -0,0 +1,30 @@
/* gunicode.h - Unicode manipulation functions
*
* Copyright (C) 1999, 2000 Tom Tromey
* Copyright 2000, 2005 Red Hat, Inc.
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
char *utf8_prev_char (const char *p);
extern const char utf8_skip_data[256];
/**
* g_utf8_next_char:
* @p: Pointer to the start of a valid UTF-8 character
*
* Skips to the next character in a UTF-8 string. The string must be
* valid; this macro is as fast as possible, and has no error-checking.
* You would use this macro to iterate over a string character by
* character. The macro returns the start of the next UTF-8 character.
* Before using this macro, use g_utf8_validate() to validate strings
* that may contain invalid UTF-8.
*/
#define utf8_next_char(p) (char *)((p) + utf8_skip_data[*(const unsigned char *)(p)])
bool unichar_iswide (uint32_t c);
+1854
View File
File diff suppressed because it is too large Load Diff
+420
View File
@@ -0,0 +1,420 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
Copyright 2014 Michal Schmidt
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <stdbool.h>
#include "macro.h"
#include "util.h"
/*
* A hash table implementation. As a minor optimization a NULL hashmap object
* will be treated as empty hashmap for all read operations. That way it is not
* necessary to instantiate an object for each Hashmap use.
*
* If ENABLE_DEBUG_HASHMAP is defined (by configuring with --enable-debug=hashmap),
* the implemention will:
* - store extra data for debugging and statistics (see tools/gdb-sd_dump_hashmaps.py)
* - perform extra checks for invalid use of iterators
*/
#define HASH_KEY_SIZE 16
/* The base type for all hashmap and set types. Many functions in the
* implementation take (HashmapBase*) parameters and are run-time polymorphic,
* though the API is not meant to be polymorphic (do not call functions
* internal_*() directly). */
typedef struct HashmapBase HashmapBase;
/* Specific hashmap/set types */
typedef struct Hashmap Hashmap; /* Maps keys to values */
typedef struct OrderedHashmap OrderedHashmap; /* Like Hashmap, but also remembers entry insertion order */
typedef struct Set Set; /* Stores just keys */
/* Ideally the Iterator would be an opaque struct, but it is instantiated
* by hashmap users, so the definition has to be here. Do not use its fields
* directly. */
typedef struct {
unsigned idx; /* index of an entry to be iterated next */
const void *next_key; /* expected value of that entry's key pointer */
#ifdef ENABLE_DEBUG_HASHMAP
unsigned put_count; /* hashmap's put_count recorded at start of iteration */
unsigned rem_count; /* hashmap's rem_count in previous iteration */
unsigned prev_idx; /* idx in previous iteration */
#endif
} Iterator;
#define _IDX_ITERATOR_FIRST (UINT_MAX - 1)
#define ITERATOR_FIRST ((Iterator) { .idx = _IDX_ITERATOR_FIRST, .next_key = NULL })
typedef unsigned long (*hash_func_t)(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]);
typedef int (*compare_func_t)(const void *a, const void *b);
struct hash_ops {
hash_func_t hash;
compare_func_t compare;
};
unsigned long string_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) _pure_;
int string_compare_func(const void *a, const void *b) _pure_;
extern const struct hash_ops string_hash_ops;
/* This will compare the passed pointers directly, and will not
* dereference them. This is hence not useful for strings or
* suchlike. */
unsigned long trivial_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) _pure_;
int trivial_compare_func(const void *a, const void *b) _const_;
extern const struct hash_ops trivial_hash_ops;
/* 32bit values we can always just embedd in the pointer itself, but
* in order to support 32bit archs we need store 64bit values
* indirectly, since they don't fit in a pointer. */
unsigned long uint64_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) _pure_;
int uint64_compare_func(const void *a, const void *b) _pure_;
extern const struct hash_ops uint64_hash_ops;
/* On some archs dev_t is 32bit, and on others 64bit. And sometimes
* it's 64bit on 32bit archs, and sometimes 32bit on 64bit archs. Yuck! */
#if SIZEOF_DEV_T != 8
unsigned long devt_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) _pure_;
int devt_compare_func(const void *a, const void *b) _pure_;
extern const struct hash_ops devt_hash_ops = {
.hash = devt_hash_func,
.compare = devt_compare_func
};
#else
#define devt_hash_func uint64_hash_func
#define devt_compare_func uint64_compare_func
#define devt_hash_ops uint64_hash_ops
#endif
/* Macros for type checking */
#define PTR_COMPATIBLE_WITH_HASHMAP_BASE(h) \
(__builtin_types_compatible_p(typeof(h), HashmapBase*) || \
__builtin_types_compatible_p(typeof(h), Hashmap*) || \
__builtin_types_compatible_p(typeof(h), OrderedHashmap*) || \
__builtin_types_compatible_p(typeof(h), Set*))
#define PTR_COMPATIBLE_WITH_PLAIN_HASHMAP(h) \
(__builtin_types_compatible_p(typeof(h), Hashmap*) || \
__builtin_types_compatible_p(typeof(h), OrderedHashmap*)) \
#define HASHMAP_BASE(h) \
__builtin_choose_expr(PTR_COMPATIBLE_WITH_HASHMAP_BASE(h), \
(HashmapBase*)(h), \
(void)0)
#define PLAIN_HASHMAP(h) \
__builtin_choose_expr(PTR_COMPATIBLE_WITH_PLAIN_HASHMAP(h), \
(Hashmap*)(h), \
(void)0)
#ifdef ENABLE_DEBUG_HASHMAP
# define HASHMAP_DEBUG_PARAMS , const char *func, const char *file, int line
# define HASHMAP_DEBUG_SRC_ARGS , __func__, __FILE__, __LINE__
# define HASHMAP_DEBUG_PASS_ARGS , func, file, line
#else
# define HASHMAP_DEBUG_PARAMS
# define HASHMAP_DEBUG_SRC_ARGS
# define HASHMAP_DEBUG_PASS_ARGS
#endif
Hashmap *internal_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
#define hashmap_new(ops) internal_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS)
#define ordered_hashmap_new(ops) internal_ordered_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS)
HashmapBase *internal_hashmap_free(HashmapBase *h);
static inline Hashmap *hashmap_free(Hashmap *h) {
return (void*)internal_hashmap_free(HASHMAP_BASE(h));
}
static inline OrderedHashmap *ordered_hashmap_free(OrderedHashmap *h) {
return (void*)internal_hashmap_free(HASHMAP_BASE(h));
}
HashmapBase *internal_hashmap_free_free(HashmapBase *h);
static inline Hashmap *hashmap_free_free(Hashmap *h) {
return (void*)internal_hashmap_free_free(HASHMAP_BASE(h));
}
static inline OrderedHashmap *ordered_hashmap_free_free(OrderedHashmap *h) {
return (void*)internal_hashmap_free_free(HASHMAP_BASE(h));
}
Hashmap *hashmap_free_free_free(Hashmap *h);
static inline OrderedHashmap *ordered_hashmap_free_free_free(OrderedHashmap *h) {
return (void*)hashmap_free_free_free(PLAIN_HASHMAP(h));
}
HashmapBase *internal_hashmap_copy(HashmapBase *h);
static inline Hashmap *hashmap_copy(Hashmap *h) {
return (Hashmap*) internal_hashmap_copy(HASHMAP_BASE(h));
}
static inline OrderedHashmap *ordered_hashmap_copy(OrderedHashmap *h) {
return (OrderedHashmap*) internal_hashmap_copy(HASHMAP_BASE(h));
}
int internal_hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
#define hashmap_ensure_allocated(h, ops) internal_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
#define ordered_hashmap_ensure_allocated(h, ops) internal_ordered_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
int hashmap_put(Hashmap *h, const void *key, void *value);
static inline int ordered_hashmap_put(OrderedHashmap *h, const void *key, void *value) {
return hashmap_put(PLAIN_HASHMAP(h), key, value);
}
int hashmap_update(Hashmap *h, const void *key, void *value);
static inline int ordered_hashmap_update(OrderedHashmap *h, const void *key, void *value) {
return hashmap_update(PLAIN_HASHMAP(h), key, value);
}
int hashmap_replace(Hashmap *h, const void *key, void *value);
static inline int ordered_hashmap_replace(OrderedHashmap *h, const void *key, void *value) {
return hashmap_replace(PLAIN_HASHMAP(h), key, value);
}
void *internal_hashmap_get(HashmapBase *h, const void *key);
static inline void *hashmap_get(Hashmap *h, const void *key) {
return internal_hashmap_get(HASHMAP_BASE(h), key);
}
static inline void *ordered_hashmap_get(OrderedHashmap *h, const void *key) {
return internal_hashmap_get(HASHMAP_BASE(h), key);
}
void *hashmap_get2(Hashmap *h, const void *key, void **rkey);
static inline void *ordered_hashmap_get2(OrderedHashmap *h, const void *key, void **rkey) {
return hashmap_get2(PLAIN_HASHMAP(h), key, rkey);
}
bool internal_hashmap_contains(HashmapBase *h, const void *key);
static inline bool hashmap_contains(Hashmap *h, const void *key) {
return internal_hashmap_contains(HASHMAP_BASE(h), key);
}
static inline bool ordered_hashmap_contains(OrderedHashmap *h, const void *key) {
return internal_hashmap_contains(HASHMAP_BASE(h), key);
}
void *internal_hashmap_remove(HashmapBase *h, const void *key);
static inline void *hashmap_remove(Hashmap *h, const void *key) {
return internal_hashmap_remove(HASHMAP_BASE(h), key);
}
static inline void *ordered_hashmap_remove(OrderedHashmap *h, const void *key) {
return internal_hashmap_remove(HASHMAP_BASE(h), key);
}
void *hashmap_remove2(Hashmap *h, const void *key, void **rkey);
static inline void *ordered_hashmap_remove2(OrderedHashmap *h, const void *key, void **rkey) {
return hashmap_remove2(PLAIN_HASHMAP(h), key, rkey);
}
void *hashmap_remove_value(Hashmap *h, const void *key, void *value);
static inline void *ordered_hashmap_remove_value(OrderedHashmap *h, const void *key, void *value) {
return hashmap_remove_value(PLAIN_HASHMAP(h), key, value);
}
int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value);
static inline int ordered_hashmap_remove_and_put(OrderedHashmap *h, const void *old_key, const void *new_key, void *value) {
return hashmap_remove_and_put(PLAIN_HASHMAP(h), old_key, new_key, value);
}
int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value);
static inline int ordered_hashmap_remove_and_replace(OrderedHashmap *h, const void *old_key, const void *new_key, void *value) {
return hashmap_remove_and_replace(PLAIN_HASHMAP(h), old_key, new_key, value);
}
/* Since merging data from a OrderedHashmap into a Hashmap or vice-versa
* should just work, allow this by having looser type-checking here. */
int internal_hashmap_merge(Hashmap *h, Hashmap *other);
#define hashmap_merge(h, other) internal_hashmap_merge(PLAIN_HASHMAP(h), PLAIN_HASHMAP(other))
#define ordered_hashmap_merge(h, other) hashmap_merge(h, other)
int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add);
static inline int hashmap_reserve(Hashmap *h, unsigned entries_add) {
return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add);
}
static inline int ordered_hashmap_reserve(OrderedHashmap *h, unsigned entries_add) {
return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add);
}
int internal_hashmap_move(HashmapBase *h, HashmapBase *other);
/* Unlike hashmap_merge, hashmap_move does not allow mixing the types. */
static inline int hashmap_move(Hashmap *h, Hashmap *other) {
return internal_hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other));
}
static inline int ordered_hashmap_move(OrderedHashmap *h, OrderedHashmap *other) {
return internal_hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other));
}
int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key);
static inline int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key) {
return internal_hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key);
}
static inline int ordered_hashmap_move_one(OrderedHashmap *h, OrderedHashmap *other, const void *key) {
return internal_hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key);
}
unsigned internal_hashmap_size(HashmapBase *h) _pure_;
static inline unsigned hashmap_size(Hashmap *h) {
return internal_hashmap_size(HASHMAP_BASE(h));
}
static inline unsigned ordered_hashmap_size(OrderedHashmap *h) {
return internal_hashmap_size(HASHMAP_BASE(h));
}
static inline bool hashmap_isempty(Hashmap *h) {
return hashmap_size(h) == 0;
}
static inline bool ordered_hashmap_isempty(OrderedHashmap *h) {
return ordered_hashmap_size(h) == 0;
}
unsigned internal_hashmap_buckets(HashmapBase *h) _pure_;
static inline unsigned hashmap_buckets(Hashmap *h) {
return internal_hashmap_buckets(HASHMAP_BASE(h));
}
static inline unsigned ordered_hashmap_buckets(OrderedHashmap *h) {
return internal_hashmap_buckets(HASHMAP_BASE(h));
}
void *internal_hashmap_iterate(HashmapBase *h, Iterator *i, const void **key);
static inline void *hashmap_iterate(Hashmap *h, Iterator *i, const void **key) {
return internal_hashmap_iterate(HASHMAP_BASE(h), i, key);
}
static inline void *ordered_hashmap_iterate(OrderedHashmap *h, Iterator *i, const void **key) {
return internal_hashmap_iterate(HASHMAP_BASE(h), i, key);
}
void internal_hashmap_clear(HashmapBase *h);
static inline void hashmap_clear(Hashmap *h) {
internal_hashmap_clear(HASHMAP_BASE(h));
}
static inline void ordered_hashmap_clear(OrderedHashmap *h) {
internal_hashmap_clear(HASHMAP_BASE(h));
}
void internal_hashmap_clear_free(HashmapBase *h);
static inline void hashmap_clear_free(Hashmap *h) {
internal_hashmap_clear_free(HASHMAP_BASE(h));
}
static inline void ordered_hashmap_clear_free(OrderedHashmap *h) {
internal_hashmap_clear_free(HASHMAP_BASE(h));
}
void hashmap_clear_free_free(Hashmap *h);
static inline void ordered_hashmap_clear_free_free(OrderedHashmap *h) {
hashmap_clear_free_free(PLAIN_HASHMAP(h));
}
/*
* Note about all *_first*() functions
*
* For plain Hashmaps and Sets the order of entries is undefined.
* The functions find whatever entry is first in the implementation
* internal order.
*
* Only for OrderedHashmaps the order is well defined and finding
* the first entry is O(1).
*/
void *internal_hashmap_steal_first(HashmapBase *h);
static inline void *hashmap_steal_first(Hashmap *h) {
return internal_hashmap_steal_first(HASHMAP_BASE(h));
}
static inline void *ordered_hashmap_steal_first(OrderedHashmap *h) {
return internal_hashmap_steal_first(HASHMAP_BASE(h));
}
void *internal_hashmap_steal_first_key(HashmapBase *h);
static inline void *hashmap_steal_first_key(Hashmap *h) {
return internal_hashmap_steal_first_key(HASHMAP_BASE(h));
}
static inline void *ordered_hashmap_steal_first_key(OrderedHashmap *h) {
return internal_hashmap_steal_first_key(HASHMAP_BASE(h));
}
void *internal_hashmap_first_key(HashmapBase *h) _pure_;
static inline void *hashmap_first_key(Hashmap *h) {
return internal_hashmap_first_key(HASHMAP_BASE(h));
}
static inline void *ordered_hashmap_first_key(OrderedHashmap *h) {
return internal_hashmap_first_key(HASHMAP_BASE(h));
}
void *internal_hashmap_first(HashmapBase *h) _pure_;
static inline void *hashmap_first(Hashmap *h) {
return internal_hashmap_first(HASHMAP_BASE(h));
}
static inline void *ordered_hashmap_first(OrderedHashmap *h) {
return internal_hashmap_first(HASHMAP_BASE(h));
}
/* no hashmap_next */
void *ordered_hashmap_next(OrderedHashmap *h, const void *key);
char **internal_hashmap_get_strv(HashmapBase *h);
static inline char **hashmap_get_strv(Hashmap *h) {
return internal_hashmap_get_strv(HASHMAP_BASE(h));
}
static inline char **ordered_hashmap_get_strv(OrderedHashmap *h) {
return internal_hashmap_get_strv(HASHMAP_BASE(h));
}
/*
* Hashmaps are iterated in unpredictable order.
* OrderedHashmaps are an exception to this. They are iterated in the order
* the entries were inserted.
* It is safe to remove the current entry.
*/
#define HASHMAP_FOREACH(e, h, i) \
for ((i) = ITERATOR_FIRST, (e) = hashmap_iterate((h), &(i), NULL); \
(e); \
(e) = hashmap_iterate((h), &(i), NULL))
#define ORDERED_HASHMAP_FOREACH(e, h, i) \
for ((i) = ITERATOR_FIRST, (e) = ordered_hashmap_iterate((h), &(i), NULL); \
(e); \
(e) = ordered_hashmap_iterate((h), &(i), NULL))
#define HASHMAP_FOREACH_KEY(e, k, h, i) \
for ((i) = ITERATOR_FIRST, (e) = hashmap_iterate((h), &(i), (const void**) &(k)); \
(e); \
(e) = hashmap_iterate((h), &(i), (const void**) &(k)))
#define ORDERED_HASHMAP_FOREACH_KEY(e, k, h, i) \
for ((i) = ITERATOR_FIRST, (e) = ordered_hashmap_iterate((h), &(i), (const void**) &(k)); \
(e); \
(e) = ordered_hashmap_iterate((h), &(i), (const void**) &(k)))
DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free_free);
#define _cleanup_hashmap_free_ _cleanup_(hashmap_freep)
#define _cleanup_hashmap_free_free_ _cleanup_(hashmap_free_freep)
#define _cleanup_hashmap_free_free_free_ _cleanup_(hashmap_free_free_freep)
#define _cleanup_ordered_hashmap_free_ _cleanup_(ordered_hashmap_freep)
#define _cleanup_ordered_hashmap_free_free_ _cleanup_(ordered_hashmap_free_freep)
#define _cleanup_ordered_hashmap_free_free_free_ _cleanup_(ordered_hashmap_free_free_freep)
+193
View File
@@ -0,0 +1,193 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2015 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <sys/utsname.h>
#include <ctype.h>
#include "util.h"
#include "hostname-util.h"
bool hostname_is_set(void) {
struct utsname u;
assert_se(uname(&u) >= 0);
if (isempty(u.nodename))
return false;
/* This is the built-in kernel default host name */
if (streq(u.nodename, "(none)"))
return false;
return true;
}
char* gethostname_malloc(void) {
struct utsname u;
assert_se(uname(&u) >= 0);
if (isempty(u.nodename) || streq(u.nodename, "(none)"))
return strdup(u.sysname);
return strdup(u.nodename);
}
static bool hostname_valid_char(char c) {
return
(c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z') ||
(c >= '0' && c <= '9') ||
c == '-' ||
c == '_' ||
c == '.';
}
bool hostname_is_valid(const char *s) {
const char *p;
bool dot;
if (isempty(s))
return false;
/* Doesn't accept empty hostnames, hostnames with trailing or
* leading dots, and hostnames with multiple dots in a
* sequence. Also ensures that the length stays below
* HOST_NAME_MAX. */
for (p = s, dot = true; *p; p++) {
if (*p == '.') {
if (dot)
return false;
dot = true;
} else {
if (!hostname_valid_char(*p))
return false;
dot = false;
}
}
if (dot)
return false;
if (p-s > HOST_NAME_MAX)
return false;
return true;
}
char* hostname_cleanup(char *s, bool lowercase) {
char *p, *d;
bool dot;
assert(s);
for (p = s, d = s, dot = true; *p; p++) {
if (*p == '.') {
if (dot)
continue;
*(d++) = '.';
dot = true;
} else if (hostname_valid_char(*p)) {
*(d++) = lowercase ? tolower(*p) : *p;
dot = false;
}
}
if (dot && d > s)
d[-1] = 0;
else
*d = 0;
strshorten(s, HOST_NAME_MAX);
return s;
}
bool is_localhost(const char *hostname) {
assert(hostname);
/* This tries to identify local host and domain names
* described in RFC6761 plus the redhatism of .localdomain */
return streq(hostname, "localhost") ||
streq(hostname, "localhost.") ||
streq(hostname, "localdomain.") ||
streq(hostname, "localdomain") ||
endswith(hostname, ".localhost") ||
endswith(hostname, ".localhost.") ||
endswith(hostname, ".localdomain") ||
endswith(hostname, ".localdomain.");
}
int sethostname_idempotent(const char *s) {
char buf[HOST_NAME_MAX + 1] = {};
assert(s);
if (gethostname(buf, sizeof(buf)) < 0)
return -errno;
if (streq(buf, s))
return 0;
if (sethostname(s, strlen(s)) < 0)
return -errno;
return 1;
}
int read_hostname_config(const char *path, char **hostname) {
_cleanup_fclose_ FILE *f = NULL;
char l[LINE_MAX];
char *name = NULL;
assert(path);
assert(hostname);
f = fopen(path, "re");
if (!f)
return -errno;
/* may have comments, ignore them */
FOREACH_LINE(l, f, return -errno) {
truncate_nl(l);
if (l[0] != '\0' && l[0] != '#') {
/* found line with value */
name = hostname_cleanup(l, false);
name = strdup(name);
if (!name)
return -ENOMEM;
break;
}
}
if (!name)
/* no non-empty line found */
return -ENOENT;
*hostname = name;
return 0;
}
+39
View File
@@ -0,0 +1,39 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2010-2015 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <stdbool.h>
#include "macro.h"
bool hostname_is_set(void);
char* gethostname_malloc(void);
bool hostname_is_valid(const char *s) _pure_;
char* hostname_cleanup(char *s, bool lowercase);
bool is_localhost(const char *hostname);
int sethostname_idempotent(const char *s);
int read_hostname_config(const char *path, char **hostname);
+338
View File
@@ -0,0 +1,338 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2014 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <arpa/inet.h>
#include "in-addr-util.h"
int in_addr_is_null(int family, const union in_addr_union *u) {
assert(u);
if (family == AF_INET)
return u->in.s_addr == 0;
if (family == AF_INET6)
return
u->in6.s6_addr32[0] == 0 &&
u->in6.s6_addr32[1] == 0 &&
u->in6.s6_addr32[2] == 0 &&
u->in6.s6_addr32[3] == 0;
return -EAFNOSUPPORT;
}
int in_addr_is_link_local(int family, const union in_addr_union *u) {
assert(u);
if (family == AF_INET)
return (be32toh(u->in.s_addr) & 0xFFFF0000) == (169U << 24 | 254U << 16);
if (family == AF_INET6)
return IN6_IS_ADDR_LINKLOCAL(&u->in6);
return -EAFNOSUPPORT;
}
int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b) {
assert(a);
assert(b);
if (family == AF_INET)
return a->in.s_addr == b->in.s_addr;
if (family == AF_INET6)
return
a->in6.s6_addr32[0] == b->in6.s6_addr32[0] &&
a->in6.s6_addr32[1] == b->in6.s6_addr32[1] &&
a->in6.s6_addr32[2] == b->in6.s6_addr32[2] &&
a->in6.s6_addr32[3] == b->in6.s6_addr32[3];
return -EAFNOSUPPORT;
}
int in_addr_prefix_intersect(
int family,
const union in_addr_union *a,
unsigned aprefixlen,
const union in_addr_union *b,
unsigned bprefixlen) {
unsigned m;
assert(a);
assert(b);
/* Checks whether there are any addresses that are in both
* networks */
m = MIN(aprefixlen, bprefixlen);
if (family == AF_INET) {
uint32_t x, nm;
x = be32toh(a->in.s_addr ^ b->in.s_addr);
nm = (m == 0) ? 0 : 0xFFFFFFFFUL << (32 - m);
return (x & nm) == 0;
}
if (family == AF_INET6) {
unsigned i;
if (m > 128)
m = 128;
for (i = 0; i < 16; i++) {
uint8_t x, nm;
x = a->in6.s6_addr[i] ^ b->in6.s6_addr[i];
if (m < 8)
nm = 0xFF << (8 - m);
else
nm = 0xFF;
if ((x & nm) != 0)
return 0;
if (m > 8)
m -= 8;
else
m = 0;
}
return 1;
}
return -EAFNOSUPPORT;
}
int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen) {
assert(u);
/* Increases the network part of an address by one. Returns
* positive it that succeeds, or 0 if this overflows. */
if (prefixlen <= 0)
return 0;
if (family == AF_INET) {
uint32_t c, n;
if (prefixlen > 32)
prefixlen = 32;
c = be32toh(u->in.s_addr);
n = c + (1UL << (32 - prefixlen));
if (n < c)
return 0;
n &= 0xFFFFFFFFUL << (32 - prefixlen);
u->in.s_addr = htobe32(n);
return 1;
}
if (family == AF_INET6) {
struct in6_addr add = {}, result;
uint8_t overflow = 0;
unsigned i;
if (prefixlen > 128)
prefixlen = 128;
/* First calculate what we have to add */
add.s6_addr[(prefixlen-1) / 8] = 1 << (7 - (prefixlen-1) % 8);
for (i = 16; i > 0; i--) {
unsigned j = i - 1;
result.s6_addr[j] = u->in6.s6_addr[j] + add.s6_addr[j] + overflow;
overflow = (result.s6_addr[j] < u->in6.s6_addr[j]);
}
if (overflow)
return 0;
u->in6 = result;
return 1;
}
return -EAFNOSUPPORT;
}
int in_addr_to_string(int family, const union in_addr_union *u, char **ret) {
char *x;
size_t l;
assert(u);
assert(ret);
if (family == AF_INET)
l = INET_ADDRSTRLEN;
else if (family == AF_INET6)
l = INET6_ADDRSTRLEN;
else
return -EAFNOSUPPORT;
x = new(char, l);
if (!x)
return -ENOMEM;
errno = 0;
if (!inet_ntop(family, u, x, l)) {
free(x);
return errno ? -errno : -EINVAL;
}
*ret = x;
return 0;
}
int in_addr_from_string(int family, const char *s, union in_addr_union *ret) {
assert(s);
assert(ret);
if (!IN_SET(family, AF_INET, AF_INET6))
return -EAFNOSUPPORT;
errno = 0;
if (inet_pton(family, s, ret) <= 0)
return errno ? -errno : -EINVAL;
return 0;
}
int in_addr_from_string_auto(const char *s, int *family, union in_addr_union *ret) {
int r;
assert(s);
assert(family);
assert(ret);
r = in_addr_from_string(AF_INET, s, ret);
if (r >= 0) {
*family = AF_INET;
return 0;
}
r = in_addr_from_string(AF_INET6, s, ret);
if (r >= 0) {
*family = AF_INET6;
return 0;
}
return -EINVAL;
}
unsigned char in_addr_netmask_to_prefixlen(const struct in_addr *addr) {
assert(addr);
return 32 - u32ctz(be32toh(addr->s_addr));
}
struct in_addr* in_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen) {
assert(addr);
assert(prefixlen <= 32);
/* Shifting beyond 32 is not defined, handle this specially. */
if (prefixlen == 0)
addr->s_addr = 0;
else
addr->s_addr = htobe32((0xffffffff << (32 - prefixlen)) & 0xffffffff);
return addr;
}
int in_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen) {
uint8_t msb_octet = *(uint8_t*) addr;
/* addr may not be aligned, so make sure we only access it byte-wise */
assert(addr);
assert(prefixlen);
if (msb_octet < 128)
/* class A, leading bits: 0 */
*prefixlen = 8;
else if (msb_octet < 192)
/* class B, leading bits 10 */
*prefixlen = 16;
else if (msb_octet < 224)
/* class C, leading bits 110 */
*prefixlen = 24;
else
/* class D or E, no default prefixlen */
return -ERANGE;
return 0;
}
int in_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask) {
unsigned char prefixlen;
int r;
assert(addr);
assert(mask);
r = in_addr_default_prefixlen(addr, &prefixlen);
if (r < 0)
return r;
in_addr_prefixlen_to_netmask(mask, prefixlen);
return 0;
}
int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen) {
assert(addr);
if (family == AF_INET) {
struct in_addr mask;
if (!in_addr_prefixlen_to_netmask(&mask, prefixlen))
return -EINVAL;
addr->in.s_addr &= mask.s_addr;
return 0;
}
if (family == AF_INET6) {
unsigned i;
for (i = 0; i < 16; i++) {
uint8_t mask;
if (prefixlen >= 8) {
mask = 0xFF;
prefixlen -= 8;
} else {
mask = 0xFF << (8 - prefixlen);
prefixlen = 0;
}
addr->in6.s6_addr[i] &= mask;
}
return 0;
}
return -EAFNOSUPPORT;
}
+53
View File
@@ -0,0 +1,53 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2014 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <netinet/in.h>
#include "macro.h"
#include "util.h"
union in_addr_union {
struct in_addr in;
struct in6_addr in6;
};
int in_addr_is_null(int family, const union in_addr_union *u);
int in_addr_is_link_local(int family, const union in_addr_union *u);
int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b);
int in_addr_prefix_intersect(int family, const union in_addr_union *a, unsigned aprefixlen, const union in_addr_union *b, unsigned bprefixlen);
int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen);
int in_addr_to_string(int family, const union in_addr_union *u, char **ret);
int in_addr_from_string(int family, const char *s, union in_addr_union *ret);
int in_addr_from_string_auto(const char *s, int *family, union in_addr_union *ret);
unsigned char in_addr_netmask_to_prefixlen(const struct in_addr *addr);
struct in_addr* in_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen);
int in_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen);
int in_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask);
int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen);
static inline size_t FAMILY_ADDRESS_SIZE(int family) {
assert(family == AF_INET || family == AF_INET6);
return family == AF_INET6 ? 16 : 4;
}
#define IN_ADDR_NULL ((union in_addr_union) {})
+55
View File
@@ -0,0 +1,55 @@
#ifndef IOPRIO_H
#define IOPRIO_H
/* This is minimal version of Linux' linux/ioprio.h header file, which
* is licensed GPL2 */
#include <unistd.h>
#include <sys/syscall.h>
/*
* Gives us 8 prio classes with 13-bits of data for each class
*/
#define IOPRIO_BITS (16)
#define IOPRIO_CLASS_SHIFT (13)
#define IOPRIO_PRIO_MASK ((1UL << IOPRIO_CLASS_SHIFT) - 1)
#define IOPRIO_PRIO_CLASS(mask) ((mask) >> IOPRIO_CLASS_SHIFT)
#define IOPRIO_PRIO_DATA(mask) ((mask) & IOPRIO_PRIO_MASK)
#define IOPRIO_PRIO_VALUE(class, data) (((class) << IOPRIO_CLASS_SHIFT) | data)
#define ioprio_valid(mask) (IOPRIO_PRIO_CLASS((mask)) != IOPRIO_CLASS_NONE)
/*
* These are the io priority groups as implemented by CFQ. RT is the realtime
* class, it always gets premium service. BE is the best-effort scheduling
* class, the default for any process. IDLE is the idle scheduling class, it
* is only served when no one else is using the disk.
*/
enum {
IOPRIO_CLASS_NONE,
IOPRIO_CLASS_RT,
IOPRIO_CLASS_BE,
IOPRIO_CLASS_IDLE,
};
/*
* 8 best effort priority levels are supported
*/
#define IOPRIO_BE_NR (8)
enum {
IOPRIO_WHO_PROCESS = 1,
IOPRIO_WHO_PGRP,
IOPRIO_WHO_USER,
};
static inline int ioprio_set(int which, int who, int ioprio) {
return syscall(__NR_ioprio_set, which, who, ioprio);
}
static inline int ioprio_get(int which, int who) {
return syscall(__NR_ioprio_get, which, who);
}
#endif
+866
View File
@@ -0,0 +1,866 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2014 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <sys/types.h>
#include <math.h>
#include "macro.h"
#include "utf8.h"
#include "json.h"
int json_variant_new(JsonVariant **ret, JsonVariantType type) {
JsonVariant *v;
v = new0(JsonVariant, 1);
if (!v)
return -ENOMEM;
v->type = type;
*ret = v;
return 0;
}
static int json_variant_deep_copy(JsonVariant *ret, JsonVariant *variant) {
int r;
assert(ret);
assert(variant);
ret->type = variant->type;
ret->size = variant->size;
if (variant->type == JSON_VARIANT_STRING) {
ret->string = memdup(variant->string, variant->size+1);
if (!ret->string)
return -ENOMEM;
} else if (variant->type == JSON_VARIANT_ARRAY || variant->type == JSON_VARIANT_OBJECT) {
size_t i;
ret->objects = new0(JsonVariant, variant->size);
if (!ret->objects)
return -ENOMEM;
for (i = 0; i < variant->size; ++i) {
r = json_variant_deep_copy(&ret->objects[i], &variant->objects[i]);
if (r < 0)
return r;
}
} else
ret->value = variant->value;
return 0;
}
static JsonVariant *json_object_unref(JsonVariant *variant);
static JsonVariant *json_variant_unref_inner(JsonVariant *variant) {
if (!variant)
return NULL;
if (variant->type == JSON_VARIANT_ARRAY || variant->type == JSON_VARIANT_OBJECT)
return json_object_unref(variant);
else if (variant->type == JSON_VARIANT_STRING)
free(variant->string);
return NULL;
}
static JsonVariant *json_raw_unref(JsonVariant *variant, size_t size) {
if (!variant)
return NULL;
for (size_t i = 0; i < size; ++i)
json_variant_unref_inner(&variant[i]);
free(variant);
return NULL;
}
static JsonVariant *json_object_unref(JsonVariant *variant) {
size_t i;
assert(variant);
if (!variant->objects)
return NULL;
for (i = 0; i < variant->size; ++i)
json_variant_unref_inner(&variant->objects[i]);
free(variant->objects);
return NULL;
}
static JsonVariant **json_variant_array_unref(JsonVariant **variant) {
size_t i = 0;
JsonVariant *p = NULL;
if (!variant)
return NULL;
while((p = (variant[i++])) != NULL) {
if (p->type == JSON_VARIANT_STRING)
free(p->string);
free(p);
}
free(variant);
return NULL;
}
DEFINE_TRIVIAL_CLEANUP_FUNC(JsonVariant **, json_variant_array_unref);
JsonVariant *json_variant_unref(JsonVariant *variant) {
if (!variant)
return NULL;
if (variant->type == JSON_VARIANT_ARRAY || variant->type == JSON_VARIANT_OBJECT)
json_object_unref(variant);
else if (variant->type == JSON_VARIANT_STRING)
free(variant->string);
free(variant);
return NULL;
}
char *json_variant_string(JsonVariant *variant){
assert(variant);
assert(variant->type == JSON_VARIANT_STRING);
return variant->string;
}
bool json_variant_bool(JsonVariant *variant) {
assert(variant);
assert(variant->type == JSON_VARIANT_BOOLEAN);
return variant->value.boolean;
}
intmax_t json_variant_integer(JsonVariant *variant) {
assert(variant);
assert(variant->type == JSON_VARIANT_INTEGER);
return variant->value.integer;
}
double json_variant_real(JsonVariant *variant) {
assert(variant);
assert(variant->type == JSON_VARIANT_REAL);
return variant->value.real;
}
JsonVariant *json_variant_element(JsonVariant *variant, unsigned index) {
assert(variant);
assert(variant->type == JSON_VARIANT_ARRAY || variant->type == JSON_VARIANT_OBJECT);
assert(index < variant->size);
assert(variant->objects);
return &variant->objects[index];
}
JsonVariant *json_variant_value(JsonVariant *variant, const char *key) {
size_t i;
assert(variant);
assert(variant->type == JSON_VARIANT_OBJECT);
assert(variant->objects);
for (i = 0; i < variant->size; i += 2) {
JsonVariant *p = &variant->objects[i];
if (p->type == JSON_VARIANT_STRING && streq(key, p->string))
return &variant->objects[i + 1];
}
return NULL;
}
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)++;
}
}
static int unhex_ucs2(const char *c, uint16_t *ret) {
int aa, bb, cc, dd;
uint16_t x;
assert(c);
assert(ret);
aa = unhexchar(c[0]);
if (aa < 0)
return -EINVAL;
bb = unhexchar(c[1]);
if (bb < 0)
return -EINVAL;
cc = unhexchar(c[2]);
if (cc < 0)
return -EINVAL;
dd = unhexchar(c[3]);
if (dd < 0)
return -EINVAL;
x = ((uint16_t) aa << 12) |
((uint16_t) bb << 8) |
((uint16_t) cc << 4) |
((uint16_t) dd);
if (x <= 0)
return -EINVAL;
*ret = x;
return 0;
}
static int json_parse_string(const char **p, char **ret) {
_cleanup_free_ char *s = NULL;
size_t n = 0, allocated = 0;
const char *c;
assert(p);
assert(*p);
assert(ret);
c = *p;
if (*c != '"')
return -EINVAL;
c++;
for (;;) {
int len;
/* Check for EOF */
if (*c == 0)
return -EINVAL;
/* Check for control characters 0x00..0x1f */
if (*c > 0 && *c < ' ')
return -EINVAL;
/* Check for control character 0x7f */
if (*c == 0x7f)
return -EINVAL;
if (*c == '"') {
if (!s) {
s = strdup("");
if (!s)
return -ENOMEM;
} else
s[n] = 0;
*p = c + 1;
*ret = s;
s = NULL;
return JSON_STRING;
}
if (*c == '\\') {
char ch = 0;
c++;
if (*c == 0)
return -EINVAL;
if (IN_SET(*c, '"', '\\', '/'))
ch = *c;
else if (*c == 'b')
ch = '\b';
else if (*c == 'f')
ch = '\f';
else if (*c == 'n')
ch = '\n';
else if (*c == 'r')
ch = '\r';
else if (*c == 't')
ch = '\t';
else if (*c == 'u') {
uint16_t x;
int r;
r = unhex_ucs2(c + 1, &x);
if (r < 0)
return r;
c += 5;
if (!GREEDY_REALLOC(s, allocated, n + 4))
return -ENOMEM;
if (!utf16_is_surrogate(x))
n += utf8_encode_unichar(s + n, x);
else if (utf16_is_trailing_surrogate(x))
return -EINVAL;
else {
uint16_t y;
if (c[0] != '\\' || c[1] != 'u')
return -EINVAL;
r = unhex_ucs2(c + 2, &y);
if (r < 0)
return r;
c += 6;
if (!utf16_is_trailing_surrogate(y))
return -EINVAL;
n += utf8_encode_unichar(s + n, utf16_surrogate_pair_to_unichar(x, y));
}
continue;
} else
return -EINVAL;
if (!GREEDY_REALLOC(s, allocated, n + 2))
return -ENOMEM;
s[n++] = ch;
c ++;
continue;
}
len = utf8_encoded_valid_unichar(c);
if (len < 0)
return len;
if (!GREEDY_REALLOC(s, allocated, n + len + 1))
return -ENOMEM;
memcpy(s + n, c, len);
n += len;
c += len;
}
}
static int json_parse_number(const char **p, union json_value *ret) {
bool negative = false, exponent_negative = false, is_double = false;
double x = 0.0, y = 0.0, exponent = 0.0, shift = 1.0;
intmax_t i = 0;
const char *c;
assert(p);
assert(*p);
assert(ret);
c = *p;
if (*c == '-') {
negative = true;
c++;
}
if (*c == '0')
c++;
else {
if (!strchr("123456789", *c) || *c == 0)
return -EINVAL;
do {
if (!is_double) {
int64_t t;
t = 10 * i + (*c - '0');
if (t < i) /* overflow */
is_double = false;
else
i = t;
}
x = 10.0 * x + (*c - '0');
c++;
} while (strchr("0123456789", *c) && *c != 0);
}
if (*c == '.') {
is_double = true;
c++;
if (!strchr("0123456789", *c) || *c == 0)
return -EINVAL;
do {
y = 10.0 * y + (*c - '0');
shift = 10.0 * shift;
c++;
} while (strchr("0123456789", *c) && *c != 0);
}
if (*c == 'e' || *c == 'E') {
is_double = true;
c++;
if (*c == '-') {
exponent_negative = true;
c++;
} else if (*c == '+')
c++;
if (!strchr("0123456789", *c) || *c == 0)
return -EINVAL;
do {
exponent = 10.0 * exponent + (*c - '0');
c++;
} while (strchr("0123456789", *c) && *c != 0);
}
*p = c;
if (is_double) {
ret->real = ((negative ? -1.0 : 1.0) * (x + (y / shift))) * exp10((exponent_negative ? -1.0 : 1.0) * exponent);
return JSON_REAL;
} else {
ret->integer = negative ? -i : i;
return JSON_INTEGER;
}
}
int json_tokenize(
const char **p,
char **ret_string,
union json_value *ret_value,
void **state,
unsigned *line) {
const char *c;
int t;
int r;
enum {
STATE_NULL,
STATE_VALUE,
STATE_VALUE_POST,
};
assert(p);
assert(*p);
assert(ret_string);
assert(ret_value);
assert(state);
t = PTR_TO_INT(*state);
c = *p;
if (t == STATE_NULL) {
if (line)
*line = 1;
t = STATE_VALUE;
}
for (;;) {
const char *b;
b = c + strspn(c, WHITESPACE);
if (*b == 0)
return JSON_END;
inc_lines(line, c, b - c);
c = b;
switch (t) {
case STATE_VALUE:
if (*c == '{') {
*ret_string = NULL;
*ret_value = JSON_VALUE_NULL;
*p = c + 1;
*state = INT_TO_PTR(STATE_VALUE);
return JSON_OBJECT_OPEN;
} else if (*c == '}') {
*ret_string = NULL;
*ret_value = JSON_VALUE_NULL;
*p = c + 1;
*state = INT_TO_PTR(STATE_VALUE_POST);
return JSON_OBJECT_CLOSE;
} else if (*c == '[') {
*ret_string = NULL;
*ret_value = JSON_VALUE_NULL;
*p = c + 1;
*state = INT_TO_PTR(STATE_VALUE);
return JSON_ARRAY_OPEN;
} else if (*c == ']') {
*ret_string = NULL;
*ret_value = JSON_VALUE_NULL;
*p = c + 1;
*state = INT_TO_PTR(STATE_VALUE_POST);
return JSON_ARRAY_CLOSE;
} else if (*c == '"') {
r = json_parse_string(&c, ret_string);
if (r < 0)
return r;
*ret_value = JSON_VALUE_NULL;
*p = c;
*state = INT_TO_PTR(STATE_VALUE_POST);
return r;
} else if (strchr("-0123456789", *c)) {
r = json_parse_number(&c, ret_value);
if (r < 0)
return r;
*ret_string = NULL;
*p = c;
*state = INT_TO_PTR(STATE_VALUE_POST);
return r;
} else if (startswith(c, "true")) {
*ret_string = NULL;
ret_value->boolean = true;
*p = c + 4;
*state = INT_TO_PTR(STATE_VALUE_POST);
return JSON_BOOLEAN;
} else if (startswith(c, "false")) {
*ret_string = NULL;
ret_value->boolean = false;
*p = c + 5;
*state = INT_TO_PTR(STATE_VALUE_POST);
return JSON_BOOLEAN;
} else if (startswith(c, "null")) {
*ret_string = NULL;
*ret_value = JSON_VALUE_NULL;
*p = c + 4;
*state = INT_TO_PTR(STATE_VALUE_POST);
return JSON_NULL;
} else
return -EINVAL;
case STATE_VALUE_POST:
if (*c == ':') {
*ret_string = NULL;
*ret_value = JSON_VALUE_NULL;
*p = c + 1;
*state = INT_TO_PTR(STATE_VALUE);
return JSON_COLON;
} else if (*c == ',') {
*ret_string = NULL;
*ret_value = JSON_VALUE_NULL;
*p = c + 1;
*state = INT_TO_PTR(STATE_VALUE);
return JSON_COMMA;
} else if (*c == '}') {
*ret_string = NULL;
*ret_value = JSON_VALUE_NULL;
*p = c + 1;
*state = INT_TO_PTR(STATE_VALUE_POST);
return JSON_OBJECT_CLOSE;
} else if (*c == ']') {
*ret_string = NULL;
*ret_value = JSON_VALUE_NULL;
*p = c + 1;
*state = INT_TO_PTR(STATE_VALUE_POST);
return JSON_ARRAY_CLOSE;
} else
return -EINVAL;
}
}
}
static bool json_is_value(JsonVariant *var) {
assert(var);
return var->type != JSON_VARIANT_CONTROL;
}
static int json_scoped_parse(JsonVariant **tokens, size_t *i, size_t n, JsonVariant *scope) {
bool arr = scope->type == JSON_VARIANT_ARRAY;
int terminator = arr ? JSON_ARRAY_CLOSE : JSON_OBJECT_CLOSE;
size_t allocated = 0, size = 0;
JsonVariant *key = NULL, *value = NULL, *var = NULL, *items = NULL;
enum {
STATE_KEY,
STATE_COLON,
STATE_COMMA,
STATE_VALUE
} state = arr ? STATE_VALUE : STATE_KEY;
assert(tokens);
assert(i);
assert(scope);
while((var = *i < n ? tokens[(*i)++] : NULL) != NULL) {
bool stopper;
int r;
stopper = !json_is_value(var) && var->value.integer == terminator;
if (stopper) {
if (state != STATE_COMMA && size > 0)
goto error;
goto out;
}
if (state == STATE_KEY) {
if (var->type != JSON_VARIANT_STRING)
goto error;
else {
key = var;
state = STATE_COLON;
}
}
else if (state == STATE_COLON) {
if (key == NULL)
goto error;
if (json_is_value(var))
goto error;
if (var->value.integer != JSON_COLON)
goto error;
state = STATE_VALUE;
}
else if (state == STATE_VALUE) {
_cleanup_json_variant_unref_ JsonVariant *v = NULL;
size_t toadd = arr ? 1 : 2;
if (!json_is_value(var)) {
int type = (var->value.integer == JSON_ARRAY_OPEN) ? JSON_VARIANT_ARRAY : JSON_VARIANT_OBJECT;
r = json_variant_new(&v, type);
if (r < 0)
goto error;
r = json_scoped_parse(tokens, i, n, v);
if (r < 0)
goto error;
value = v;
}
else
value = var;
if(!GREEDY_REALLOC(items, allocated, size + toadd))
goto error;
if (arr) {
r = json_variant_deep_copy(&items[size], value);
if (r < 0)
goto error;
} else {
r = json_variant_deep_copy(&items[size], key);
if (r < 0)
goto error;
r = json_variant_deep_copy(&items[size+1], value);
if (r < 0)
goto error;
}
size += toadd;
state = STATE_COMMA;
}
else if (state == STATE_COMMA) {
if (json_is_value(var))
goto error;
if (var->value.integer != JSON_COMMA)
goto error;
key = NULL;
value = NULL;
state = arr ? STATE_VALUE : STATE_KEY;
}
}
error:
json_raw_unref(items, size);
return -EBADMSG;
out:
scope->size = size;
scope->objects = items;
return scope->type;
}
static int json_parse_tokens(JsonVariant **tokens, size_t ntokens, JsonVariant **rv) {
size_t it = 0;
int r;
JsonVariant *e;
_cleanup_json_variant_unref_ JsonVariant *p = NULL;
assert(tokens);
assert(ntokens);
e = tokens[it++];
r = json_variant_new(&p, JSON_VARIANT_OBJECT);
if (r < 0)
return r;
if (e->type != JSON_VARIANT_CONTROL && e->value.integer != JSON_OBJECT_OPEN)
return -EBADMSG;
r = json_scoped_parse(tokens, &it, ntokens, p);
if (r < 0)
return r;
*rv = p;
p = NULL;
return 0;
}
static int json_tokens(const char *string, size_t size, JsonVariant ***tokens, size_t *n) {
_cleanup_free_ char *buf = NULL;
_cleanup_(json_variant_array_unrefp) JsonVariant **items = NULL;
union json_value v = {};
void *json_state = NULL;
const char *p;
int t, r;
size_t allocated = 0, s = 0;
assert(string);
assert(n);
if (size <= 0)
return -EBADMSG;
buf = strndup(string, size);
if (!buf)
return -ENOMEM;
p = buf;
for (;;) {
_cleanup_json_variant_unref_ JsonVariant *var = NULL;
_cleanup_free_ char *rstr = NULL;
t = json_tokenize(&p, &rstr, &v, &json_state, NULL);
if (t < 0)
return t;
else if (t == JSON_END)
break;
if (t <= JSON_ARRAY_CLOSE) {
r = json_variant_new(&var, JSON_VARIANT_CONTROL);
if (r < 0)
return r;
var->value.integer = t;
} else {
switch (t) {
case JSON_STRING:
r = json_variant_new(&var, JSON_VARIANT_STRING);
if (r < 0)
return r;
var->size = strlen(rstr);
var->string = strdup(rstr);
if (!var->string) {
return -ENOMEM;
}
break;
case JSON_INTEGER:
r = json_variant_new(&var, JSON_VARIANT_INTEGER);
if (r < 0)
return r;
var->value = v;
break;
case JSON_REAL:
r = json_variant_new(&var, JSON_VARIANT_REAL);
if (r < 0)
return r;
var->value = v;
break;
case JSON_BOOLEAN:
r = json_variant_new(&var, JSON_VARIANT_BOOLEAN);
if (r < 0)
return r;
var->value = v;
break;
case JSON_NULL:
r = json_variant_new(&var, JSON_VARIANT_NULL);
if (r < 0)
return r;
break;
}
}
if (!GREEDY_REALLOC(items, allocated, s+2))
return -ENOMEM;
items[s++] = var;
items[s] = NULL;
var = NULL;
}
*n = s;
*tokens = items;
items = NULL;
return 0;
}
int json_parse(const char *string, JsonVariant **rv) {
_cleanup_(json_variant_array_unrefp) JsonVariant **s = NULL;
JsonVariant *v = NULL;
size_t n = 0;
int r;
assert(string);
assert(rv);
r = json_tokens(string, strlen(string), &s, &n);
if (r < 0)
return r;
r = json_parse_tokens(s, n, &v);
if (r < 0)
return r;
*rv = v;
return 0;
}
+88
View File
@@ -0,0 +1,88 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2014 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <stdbool.h>
#include "util.h"
enum {
JSON_END,
JSON_COLON,
JSON_COMMA,
JSON_OBJECT_OPEN,
JSON_OBJECT_CLOSE,
JSON_ARRAY_OPEN,
JSON_ARRAY_CLOSE,
JSON_STRING,
JSON_REAL,
JSON_INTEGER,
JSON_BOOLEAN,
JSON_NULL,
};
typedef enum {
JSON_VARIANT_CONTROL,
JSON_VARIANT_STRING,
JSON_VARIANT_INTEGER,
JSON_VARIANT_BOOLEAN,
JSON_VARIANT_REAL,
JSON_VARIANT_ARRAY,
JSON_VARIANT_OBJECT,
JSON_VARIANT_NULL
} JsonVariantType;
union json_value {
bool boolean;
double real;
intmax_t integer;
};
typedef struct JsonVariant {
JsonVariantType type;
size_t size;
union {
char *string;
struct JsonVariant *objects;
union json_value value;
};
} JsonVariant;
int json_variant_new(JsonVariant **ret, JsonVariantType type);
JsonVariant *json_variant_unref(JsonVariant *v);
DEFINE_TRIVIAL_CLEANUP_FUNC(JsonVariant *, json_variant_unref);
#define _cleanup_json_variant_unref_ _cleanup_(json_variant_unrefp)
char *json_variant_string(JsonVariant *v);
bool json_variant_bool(JsonVariant *v);
intmax_t json_variant_integer(JsonVariant *v);
double json_variant_real(JsonVariant *v);
JsonVariant *json_variant_element(JsonVariant *v, unsigned index);
JsonVariant *json_variant_value(JsonVariant *v, const char *key);
#define JSON_VALUE_NULL ((union json_value) {})
int json_tokenize(const char **p, char **ret_string, union json_value *ret_value, void **state, unsigned *line);
int json_parse(const char *string, JsonVariant **rv);
int json_parse_measure(const char *string, size_t *size);
+80
View File
@@ -0,0 +1,80 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "selinux-util.h"
#include "smack-util.h"
#include "util.h"
#include "label.h"
int label_fix(const char *path, bool ignore_enoent, bool ignore_erofs) {
int r, q;
r = mac_selinux_fix(path, ignore_enoent, ignore_erofs);
q = mac_smack_fix(path, ignore_enoent, ignore_erofs);
if (r < 0)
return r;
if (q < 0)
return q;
return 0;
}
int mkdir_label(const char *path, mode_t mode) {
int r;
assert(path);
r = mac_selinux_create_file_prepare(path, S_IFDIR);
if (r < 0)
return r;
if (mkdir(path, mode) < 0)
r = -errno;
mac_selinux_create_file_clear();
if (r < 0)
return r;
return mac_smack_fix(path, false, false);
}
int symlink_label(const char *old_path, const char *new_path) {
int r;
assert(old_path);
assert(new_path);
r = mac_selinux_create_file_prepare(new_path, S_IFLNK);
if (r < 0)
return r;
if (symlink(old_path, new_path) < 0)
r = -errno;
mac_selinux_create_file_clear();
if (r < 0)
return r;
return mac_smack_fix(new_path, false, false);
}
+30
View File
@@ -0,0 +1,30 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <stdbool.h>
#include <sys/types.h>
int label_fix(const char *path, bool ignore_enoent, bool ignore_erofs);
int mkdir_label(const char *path, mode_t mode);
int symlink_label(const char *old_path, const char *new_path);
+1
View File
@@ -0,0 +1 @@
../Makefile
+158
View File
@@ -0,0 +1,158 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
/* The head of the linked list. Use this in the structure that shall
* contain the head of the linked list */
#define LIST_HEAD(t,name) \
t *name
/* The pointers in the linked list's items. Use this in the item structure */
#define LIST_FIELDS(t,name) \
t *name##_next, *name##_prev
/* Initialize the list's head */
#define LIST_HEAD_INIT(head) \
do { \
(head) = NULL; } \
while(false)
/* Initialize a list item */
#define LIST_INIT(name,item) \
do { \
typeof(*(item)) *_item = (item); \
assert(_item); \
_item->name##_prev = _item->name##_next = NULL; \
} while(false)
/* Prepend an item to the list */
#define LIST_PREPEND(name,head,item) \
do { \
typeof(*(head)) **_head = &(head), *_item = (item); \
assert(_item); \
if ((_item->name##_next = *_head)) \
_item->name##_next->name##_prev = _item; \
_item->name##_prev = NULL; \
*_head = _item; \
} while(false)
/* Append an item to the list */
#define LIST_APPEND(name,head,item) \
do { \
typeof(*(head)) *_tail; \
LIST_FIND_TAIL(name,head,_tail); \
LIST_INSERT_AFTER(name,head,_tail,item); \
} while(false)
/* Remove an item from the list */
#define LIST_REMOVE(name,head,item) \
do { \
typeof(*(head)) **_head = &(head), *_item = (item); \
assert(_item); \
if (_item->name##_next) \
_item->name##_next->name##_prev = _item->name##_prev; \
if (_item->name##_prev) \
_item->name##_prev->name##_next = _item->name##_next; \
else { \
assert(*_head == _item); \
*_head = _item->name##_next; \
} \
_item->name##_next = _item->name##_prev = NULL; \
} while(false)
/* Find the head of the list */
#define LIST_FIND_HEAD(name,item,head) \
do { \
typeof(*(item)) *_item = (item); \
if (!_item) \
(head) = NULL; \
else { \
while (_item->name##_prev) \
_item = _item->name##_prev; \
(head) = _item; \
} \
} while (false)
/* Find the tail of the list */
#define LIST_FIND_TAIL(name,item,tail) \
do { \
typeof(*(item)) *_item = (item); \
if (!_item) \
(tail) = NULL; \
else { \
while (_item->name##_next) \
_item = _item->name##_next; \
(tail) = _item; \
} \
} while (false)
/* Insert an item after another one (a = where, b = what) */
#define LIST_INSERT_AFTER(name,head,a,b) \
do { \
typeof(*(head)) **_head = &(head), *_a = (a), *_b = (b); \
assert(_b); \
if (!_a) { \
if ((_b->name##_next = *_head)) \
_b->name##_next->name##_prev = _b; \
_b->name##_prev = NULL; \
*_head = _b; \
} else { \
if ((_b->name##_next = _a->name##_next)) \
_b->name##_next->name##_prev = _b; \
_b->name##_prev = _a; \
_a->name##_next = _b; \
} \
} while(false)
#define LIST_JUST_US(name,item) \
(!(item)->name##_prev && !(item)->name##_next) \
#define LIST_FOREACH(name,i,head) \
for ((i) = (head); (i); (i) = (i)->name##_next)
#define LIST_FOREACH_SAFE(name,i,n,head) \
for ((i) = (head); (i) && (((n) = (i)->name##_next), 1); (i) = (n))
#define LIST_FOREACH_BEFORE(name,i,p) \
for ((i) = (p)->name##_prev; (i); (i) = (i)->name##_prev)
#define LIST_FOREACH_AFTER(name,i,p) \
for ((i) = (p)->name##_next; (i); (i) = (i)->name##_next)
/* Iterate through all the members of the list p is included in, but skip over p */
#define LIST_FOREACH_OTHERS(name,i,p) \
for (({ \
(i) = (p); \
while ((i) && (i)->name##_prev) \
(i) = (i)->name##_prev; \
if ((i) == (p)) \
(i) = (p)->name##_next; \
}); \
(i); \
(i) = (i)->name##_next == (p) ? (p)->name##_next : (i)->name##_next)
/* Loop starting from p->next until p->prev.
p can be adjusted meanwhile. */
#define LIST_LOOP_BUT_ONE(name,i,head,p) \
for ((i) = (p)->name##_next ? (p)->name##_next : (head); \
(i) != (p); \
(i) = (i)->name##_next ? (i)->name##_next : (head))
+224
View File
@@ -0,0 +1,224 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2014 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <sys/mman.h>
#include "set.h"
#include "util.h"
#include "utf8.h"
#include "strv.h"
#include "locale-util.h"
static int add_locales_from_archive(Set *locales) {
/* Stolen from glibc... */
struct locarhead {
uint32_t magic;
/* Serial number. */
uint32_t serial;
/* Name hash table. */
uint32_t namehash_offset;
uint32_t namehash_used;
uint32_t namehash_size;
/* String table. */
uint32_t string_offset;
uint32_t string_used;
uint32_t string_size;
/* Table with locale records. */
uint32_t locrectab_offset;
uint32_t locrectab_used;
uint32_t locrectab_size;
/* MD5 sum hash table. */
uint32_t sumhash_offset;
uint32_t sumhash_used;
uint32_t sumhash_size;
};
struct namehashent {
/* Hash value of the name. */
uint32_t hashval;
/* Offset of the name in the string table. */
uint32_t name_offset;
/* Offset of the locale record. */
uint32_t locrec_offset;
};
const struct locarhead *h;
const struct namehashent *e;
const void *p = MAP_FAILED;
_cleanup_close_ int fd = -1;
size_t sz = 0;
struct stat st;
unsigned i;
int r;
fd = open("/usr/lib/locale/locale-archive", O_RDONLY|O_NOCTTY|O_CLOEXEC);
if (fd < 0)
return errno == ENOENT ? 0 : -errno;
if (fstat(fd, &st) < 0)
return -errno;
if (!S_ISREG(st.st_mode))
return -EBADMSG;
if (st.st_size < (off_t) sizeof(struct locarhead))
return -EBADMSG;
p = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
if (p == MAP_FAILED)
return -errno;
h = (const struct locarhead *) p;
if (h->magic != 0xde020109 ||
h->namehash_offset + h->namehash_size > st.st_size ||
h->string_offset + h->string_size > st.st_size ||
h->locrectab_offset + h->locrectab_size > st.st_size ||
h->sumhash_offset + h->sumhash_size > st.st_size) {
r = -EBADMSG;
goto finish;
}
e = (const struct namehashent*) ((const uint8_t*) p + h->namehash_offset);
for (i = 0; i < h->namehash_size; i++) {
char *z;
if (e[i].locrec_offset == 0)
continue;
if (!utf8_is_valid((char*) p + e[i].name_offset))
continue;
z = strdup((char*) p + e[i].name_offset);
if (!z) {
r = -ENOMEM;
goto finish;
}
r = set_consume(locales, z);
if (r < 0)
goto finish;
}
r = 0;
finish:
if (p != MAP_FAILED)
munmap((void*) p, sz);
return r;
}
static int add_locales_from_libdir (Set *locales) {
_cleanup_closedir_ DIR *dir = NULL;
struct dirent *entry;
int r;
dir = opendir("/usr/lib/locale");
if (!dir)
return errno == ENOENT ? 0 : -errno;
FOREACH_DIRENT(entry, dir, return -errno) {
char *z;
if (entry->d_type != DT_DIR)
continue;
z = strdup(entry->d_name);
if (!z)
return -ENOMEM;
r = set_consume(locales, z);
if (r < 0 && r != -EEXIST)
return r;
}
return 0;
}
int get_locales(char ***ret) {
_cleanup_set_free_ Set *locales = NULL;
_cleanup_strv_free_ char **l = NULL;
int r;
locales = set_new(&string_hash_ops);
if (!locales)
return -ENOMEM;
r = add_locales_from_archive(locales);
if (r < 0 && r != -ENOENT)
return r;
r = add_locales_from_libdir(locales);
if (r < 0)
return r;
l = set_get_strv(locales);
if (!l)
return -ENOMEM;
strv_sort(l);
*ret = l;
l = NULL;
return 0;
}
bool locale_is_valid(const char *name) {
if (isempty(name))
return false;
if (strlen(name) >= 128)
return false;
if (!utf8_is_valid(name))
return false;
if (!filename_is_valid(name))
return false;
if (!string_is_safe(name))
return false;
return true;
}
static const char * const locale_variable_table[_VARIABLE_LC_MAX] = {
[VARIABLE_LANG] = "LANG",
[VARIABLE_LANGUAGE] = "LANGUAGE",
[VARIABLE_LC_CTYPE] = "LC_CTYPE",
[VARIABLE_LC_NUMERIC] = "LC_NUMERIC",
[VARIABLE_LC_TIME] = "LC_TIME",
[VARIABLE_LC_COLLATE] = "LC_COLLATE",
[VARIABLE_LC_MONETARY] = "LC_MONETARY",
[VARIABLE_LC_MESSAGES] = "LC_MESSAGES",
[VARIABLE_LC_PAPER] = "LC_PAPER",
[VARIABLE_LC_NAME] = "LC_NAME",
[VARIABLE_LC_ADDRESS] = "LC_ADDRESS",
[VARIABLE_LC_TELEPHONE] = "LC_TELEPHONE",
[VARIABLE_LC_MEASUREMENT] = "LC_MEASUREMENT",
[VARIABLE_LC_IDENTIFICATION] = "LC_IDENTIFICATION"
};
DEFINE_STRING_TABLE_LOOKUP(locale_variable, LocaleVariable);
+54
View File
@@ -0,0 +1,54 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2014 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <stdbool.h>
#include "macro.h"
typedef enum LocaleVariable {
/* We don't list LC_ALL here on purpose. People should be
* using LANG instead. */
VARIABLE_LANG,
VARIABLE_LANGUAGE,
VARIABLE_LC_CTYPE,
VARIABLE_LC_NUMERIC,
VARIABLE_LC_TIME,
VARIABLE_LC_COLLATE,
VARIABLE_LC_MONETARY,
VARIABLE_LC_MESSAGES,
VARIABLE_LC_PAPER,
VARIABLE_LC_NAME,
VARIABLE_LC_ADDRESS,
VARIABLE_LC_TELEPHONE,
VARIABLE_LC_MEASUREMENT,
VARIABLE_LC_IDENTIFICATION,
_VARIABLE_LC_MAX,
_VARIABLE_LC_INVALID = -1
} LocaleVariable;
int get_locales(char ***l);
bool locale_is_valid(const char *name);
const char* locale_variable_to_string(LocaleVariable i) _const_;
LocaleVariable locale_variable_from_string(const char *s) _pure_;
+154
View File
@@ -0,0 +1,154 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <stdlib.h>
#include <stdbool.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <limits.h>
#include <sys/file.h>
#include "util.h"
#include "lockfile-util.h"
#include "fileio.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);
free(f->path);
f->path = NULL;
}
f->fd = safe_close(f->fd);
f->operation = 0;
}
+39
View File
@@ -0,0 +1,39 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2011 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#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 _cleanup_release_lock_file_ _cleanup_(release_lock_file)
#define LOCK_FILE_INIT { .fd = -1, .path = NULL }
+1138
View File
File diff suppressed because it is too large Load Diff
+229
View File
@@ -0,0 +1,229 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <stdbool.h>
#include <stdarg.h>
#include <stdlib.h>
#include <syslog.h>
#include <sys/signalfd.h>
#include <errno.h>
#include "sd-id128.h"
#include "macro.h"
typedef enum LogTarget{
LOG_TARGET_CONSOLE,
LOG_TARGET_CONSOLE_PREFIXED,
LOG_TARGET_KMSG,
LOG_TARGET_JOURNAL,
LOG_TARGET_JOURNAL_OR_KMSG,
LOG_TARGET_SYSLOG,
LOG_TARGET_SYSLOG_OR_KMSG,
LOG_TARGET_AUTO, /* console if stderr is tty, JOURNAL_OR_KMSG otherwise */
LOG_TARGET_SAFE, /* console if stderr is tty, KMSG otherwise */
LOG_TARGET_NULL,
_LOG_TARGET_MAX,
_LOG_TARGET_INVALID = -1
} LogTarget;
void log_set_target(LogTarget target);
void log_set_max_level(int level);
void log_set_facility(int facility);
int log_set_target_from_string(const char *e);
int log_set_max_level_from_string(const char *e);
void log_show_color(bool b);
bool log_get_show_color(void) _pure_;
void log_show_location(bool b);
bool log_get_show_location(void) _pure_;
int log_show_color_from_string(const char *e);
int log_show_location_from_string(const char *e);
LogTarget log_get_target(void) _pure_;
int log_get_max_level(void) _pure_;
int log_open(void);
void log_close(void);
void log_forget_fds(void);
void log_close_syslog(void);
void log_close_journal(void);
void log_close_kmsg(void);
void log_close_console(void);
void log_parse_environment(void);
int log_internal(
int level,
int error,
const char *file,
int line,
const char *func,
const char *format, ...) _printf_(6,7);
int log_internalv(
int level,
int error,
const char *file,
int line,
const char *func,
const char *format,
va_list ap) _printf_(6,0);
int log_object_internal(
int level,
int error,
const char *file,
int line,
const char *func,
const char *object_field,
const char *object,
const char *format, ...) _printf_(8,9);
int log_object_internalv(
int level,
int error,
const char*file,
int line,
const char *func,
const char *object_field,
const char *object,
const char *format,
va_list ap) _printf_(8,0);
int log_struct_internal(
int level,
int error,
const char *file,
int line,
const char *func,
const char *format, ...) _printf_(6,0) _sentinel_;
int log_oom_internal(
const char *file,
int line,
const char *func);
/* This modifies the buffer passed! */
int log_dump_internal(
int level,
int error,
const char *file,
int line,
const char *func,
char *buffer);
/* Logging for various assertions */
noreturn void log_assert_failed(
const char *text,
const char *file,
int line,
const char *func);
noreturn void log_assert_failed_unreachable(
const char *text,
const char *file,
int line,
const char *func);
void log_assert_failed_return(
const char *text,
const char *file,
int line,
const char *func);
/* Logging with level */
#define log_full_errno(level, error, ...) \
({ \
int _level = (level), _e = (error); \
(log_get_max_level() >= LOG_PRI(_level)) \
? log_internal(_level, _e, __FILE__, __LINE__, __func__, __VA_ARGS__) \
: -abs(_e); \
})
#define log_full(level, ...) log_full_errno(level, 0, __VA_ARGS__)
/* Normal logging */
#define log_debug(...) log_full(LOG_DEBUG, __VA_ARGS__)
#define log_info(...) log_full(LOG_INFO, __VA_ARGS__)
#define log_notice(...) log_full(LOG_NOTICE, __VA_ARGS__)
#define log_warning(...) log_full(LOG_WARNING, __VA_ARGS__)
#define log_error(...) log_full(LOG_ERR, __VA_ARGS__)
#define log_emergency(...) log_full(getpid() == 1 ? LOG_EMERG : LOG_ERR, __VA_ARGS__)
/* Logging triggered by an errno-like error */
#define log_debug_errno(error, ...) log_full_errno(LOG_DEBUG, error, __VA_ARGS__)
#define log_info_errno(error, ...) log_full_errno(LOG_INFO, error, __VA_ARGS__)
#define log_notice_errno(error, ...) log_full_errno(LOG_NOTICE, error, __VA_ARGS__)
#define log_warning_errno(error, ...) log_full_errno(LOG_WARNING, error, __VA_ARGS__)
#define log_error_errno(error, ...) log_full_errno(LOG_ERR, error, __VA_ARGS__)
#define log_emergency_errno(error, ...) log_full_errno(getpid() == 1 ? LOG_EMERG : LOG_ERR, error, __VA_ARGS__)
#ifdef LOG_TRACE
# define log_trace(...) log_debug(__VA_ARGS__)
#else
# define log_trace(...) do {} while(0)
#endif
/* Structured logging */
#define log_struct(level, ...) log_struct_internal(level, 0, __FILE__, __LINE__, __func__, __VA_ARGS__)
#define log_struct_errno(level, error, ...) log_struct_internal(level, error, __FILE__, __LINE__, __func__, __VA_ARGS__)
/* This modifies the buffer passed! */
#define log_dump(level, buffer) log_dump_internal(level, 0, __FILE__, __LINE__, __func__, buffer)
#define log_oom() log_oom_internal(__FILE__, __LINE__, __func__)
bool log_on_console(void) _pure_;
const char *log_target_to_string(LogTarget target) _const_;
LogTarget log_target_from_string(const char *s) _pure_;
/* Helpers to prepare various fields for structured logging */
#define LOG_MESSAGE(fmt, ...) "MESSAGE=" fmt, ##__VA_ARGS__
#define LOG_MESSAGE_ID(x) "MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(x)
void log_received_signal(int level, const struct signalfd_siginfo *si);
void log_set_upgrade_syslog_to_journal(bool b);
int log_syntax_internal(
const char *unit,
int level,
const char *config_file,
unsigned config_line,
int error,
const char *file,
int line,
const char *func,
const char *format, ...) _printf_(9, 10);
#define log_syntax(unit, level, config_file, config_line, error, ...) \
({ \
int _level = (level), _e = (error); \
(log_get_max_level() >= LOG_PRI(_level)) \
? log_syntax_internal(unit, _level, config_file, config_line, _e, __FILE__, __LINE__, __func__, __VA_ARGS__) \
: -abs(_e); \
})
+31
View File
@@ -0,0 +1,31 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2013 Zbigniew Jędrzejewski-Szmek
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "login-util.h"
#include "def.h"
bool session_id_valid(const char *id) {
if (isempty(id))
return false;
return id[strspn(id, LETTERS DIGITS)] == '\0';
}
+26
View File
@@ -0,0 +1,26 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2013 Zbigniew Jędrzejewski-Szmek
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#pragma once
#include <stdbool.h>
bool session_id_valid(const char *id);
+473
View File
@@ -0,0 +1,473 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <assert.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <inttypes.h>
#define _printf_(a,b) __attribute__ ((format (printf, a, b)))
#define _alloc_(...) __attribute__ ((alloc_size(__VA_ARGS__)))
#define _sentinel_ __attribute__ ((sentinel))
#define _unused_ __attribute__ ((unused))
#define _destructor_ __attribute__ ((destructor))
#define _pure_ __attribute__ ((pure))
#define _const_ __attribute__ ((const))
#define _deprecated_ __attribute__ ((deprecated))
#define _packed_ __attribute__ ((packed))
#define _malloc_ __attribute__ ((malloc))
#define _weak_ __attribute__ ((weak))
#define _likely_(x) (__builtin_expect(!!(x),1))
#define _unlikely_(x) (__builtin_expect(!!(x),0))
#define _public_ __attribute__ ((visibility("default")))
#define _hidden_ __attribute__ ((visibility("hidden")))
#define _weakref_(x) __attribute__((weakref(#x)))
#define _alignas_(x) __attribute__((aligned(__alignof(x))))
#define _cleanup_(x) __attribute__((cleanup(x)))
/* Temporarily disable some warnings */
#define DISABLE_WARNING_DECLARATION_AFTER_STATEMENT \
_Pragma("GCC diagnostic push"); \
_Pragma("GCC diagnostic ignored \"-Wdeclaration-after-statement\"")
#define DISABLE_WARNING_FORMAT_NONLITERAL \
_Pragma("GCC diagnostic push"); \
_Pragma("GCC diagnostic ignored \"-Wformat-nonliteral\"")
#define DISABLE_WARNING_MISSING_PROTOTYPES \
_Pragma("GCC diagnostic push"); \
_Pragma("GCC diagnostic ignored \"-Wmissing-prototypes\"")
#define DISABLE_WARNING_NONNULL \
_Pragma("GCC diagnostic push"); \
_Pragma("GCC diagnostic ignored \"-Wnonnull\"")
#define DISABLE_WARNING_SHADOW \
_Pragma("GCC diagnostic push"); \
_Pragma("GCC diagnostic ignored \"-Wshadow\"")
#define DISABLE_WARNING_INCOMPATIBLE_POINTER_TYPES \
_Pragma("GCC diagnostic push"); \
_Pragma("GCC diagnostic ignored \"-Wincompatible-pointer-types\"")
#define REENABLE_WARNING \
_Pragma("GCC diagnostic pop")
/* automake test harness */
#define EXIT_TEST_SKIP 77
#define XSTRINGIFY(x) #x
#define STRINGIFY(x) XSTRINGIFY(x)
#define XCONCATENATE(x, y) x ## y
#define CONCATENATE(x, y) XCONCATENATE(x, y)
#define UNIQ_T(x, uniq) CONCATENATE(__unique_prefix_, CONCATENATE(x, uniq))
#define UNIQ __COUNTER__
/* Rounds up */
#define ALIGN4(l) (((l) + 3) & ~3)
#define ALIGN8(l) (((l) + 7) & ~7)
#if __SIZEOF_POINTER__ == 8
#define ALIGN(l) ALIGN8(l)
#elif __SIZEOF_POINTER__ == 4
#define ALIGN(l) ALIGN4(l)
#else
#error "Wut? Pointers are neither 4 nor 8 bytes long?"
#endif
#define ALIGN_PTR(p) ((void*) ALIGN((unsigned long) (p)))
#define ALIGN4_PTR(p) ((void*) ALIGN4((unsigned long) (p)))
#define ALIGN8_PTR(p) ((void*) ALIGN8((unsigned long) (p)))
static inline size_t ALIGN_TO(size_t l, size_t ali) {
return ((l + ali - 1) & ~(ali - 1));
}
#define ALIGN_TO_PTR(p, ali) ((void*) ALIGN_TO((unsigned long) (p), (ali)))
/* align to next higher power-of-2 (except for: 0 => 0, overflow => 0) */
static inline unsigned long ALIGN_POWER2(unsigned long u) {
/* clz(0) is undefined */
if (u == 1)
return 1;
/* left-shift overflow is undefined */
if (__builtin_clzl(u - 1UL) < 1)
return 0;
return 1UL << (sizeof(u) * 8 - __builtin_clzl(u - 1UL));
}
#define ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0]))
/*
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*/
#define container_of(ptr, type, member) __container_of(UNIQ, (ptr), type, member)
#define __container_of(uniq, ptr, type, member) \
__extension__ ({ \
const typeof( ((type*)0)->member ) *UNIQ_T(A, uniq) = (ptr); \
(type*)( (char *)UNIQ_T(A, uniq) - offsetof(type,member) ); \
})
#undef MAX
#define MAX(a, b) __MAX(UNIQ, (a), UNIQ, (b))
#define __MAX(aq, a, bq, b) \
__extension__ ({ \
const typeof(a) UNIQ_T(A, aq) = (a); \
const typeof(b) UNIQ_T(B, bq) = (b); \
UNIQ_T(A,aq) > UNIQ_T(B,bq) ? UNIQ_T(A,aq) : UNIQ_T(B,bq); \
})
/* evaluates to (void) if _A or _B are not constant or of different types */
#define CONST_MAX(_A, _B) \
__extension__ (__builtin_choose_expr( \
__builtin_constant_p(_A) && \
__builtin_constant_p(_B) && \
__builtin_types_compatible_p(typeof(_A), typeof(_B)), \
((_A) > (_B)) ? (_A) : (_B), \
(void)0))
/* takes two types and returns the size of the larger one */
#define MAXSIZE(A, B) (sizeof(union _packed_ { typeof(A) a; typeof(B) b; }))
#define MAX3(x,y,z) \
__extension__ ({ \
const typeof(x) _c = MAX(x,y); \
MAX(_c, z); \
})
#undef MIN
#define MIN(a, b) __MIN(UNIQ, (a), UNIQ, (b))
#define __MIN(aq, a, bq, b) \
__extension__ ({ \
const typeof(a) UNIQ_T(A, aq) = (a); \
const typeof(b) UNIQ_T(B, bq) = (b); \
UNIQ_T(A,aq) < UNIQ_T(B,bq) ? UNIQ_T(A,aq) : UNIQ_T(B,bq); \
})
#define MIN3(x,y,z) \
__extension__ ({ \
const typeof(x) _c = MIN(x,y); \
MIN(_c, z); \
})
#define LESS_BY(a, b) __LESS_BY(UNIQ, (a), UNIQ, (b))
#define __LESS_BY(aq, a, bq, b) \
__extension__ ({ \
const typeof(a) UNIQ_T(A, aq) = (a); \
const typeof(b) UNIQ_T(B, bq) = (b); \
UNIQ_T(A,aq) > UNIQ_T(B,bq) ? UNIQ_T(A,aq) - UNIQ_T(B,bq) : 0; \
})
#undef CLAMP
#define CLAMP(x, low, high) __CLAMP(UNIQ, (x), UNIQ, (low), UNIQ, (high))
#define __CLAMP(xq, x, lowq, low, highq, high) \
__extension__ ({ \
const typeof(x) UNIQ_T(X,xq) = (x); \
const typeof(low) UNIQ_T(LOW,lowq) = (low); \
const typeof(high) UNIQ_T(HIGH,highq) = (high); \
UNIQ_T(X,xq) > UNIQ_T(HIGH,highq) ? \
UNIQ_T(HIGH,highq) : \
UNIQ_T(X,xq) < UNIQ_T(LOW,lowq) ? \
UNIQ_T(LOW,lowq) : \
UNIQ_T(X,xq); \
})
/* [(x + y - 1) / y] suffers from an integer overflow, even though the
* computation should be possible in the given type. Therefore, we use
* [x / y + !!(x % y)]. Note that on "Real CPUs" a division returns both the
* quotient and the remainder, so both should be equally fast. */
#define DIV_ROUND_UP(_x, _y) \
__extension__ ({ \
const typeof(_x) __x = (_x); \
const typeof(_y) __y = (_y); \
(__x / __y + !!(__x % __y)); \
})
#define assert_se(expr) \
do { \
if (_unlikely_(!(expr))) \
log_assert_failed(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
} while (false) \
/* We override the glibc assert() here. */
#undef assert
#ifdef NDEBUG
#define assert(expr) do {} while(false)
#else
#define assert(expr) assert_se(expr)
#endif
#define assert_not_reached(t) \
do { \
log_assert_failed_unreachable(t, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
} while (false)
#if defined(static_assert)
/* static_assert() is sometimes defined in a way that trips up
* -Wdeclaration-after-statement, hence let's temporarily turn off
* this warning around it. */
#define assert_cc(expr) \
DISABLE_WARNING_DECLARATION_AFTER_STATEMENT; \
static_assert(expr, #expr); \
REENABLE_WARNING
#else
#define assert_cc(expr) \
DISABLE_WARNING_DECLARATION_AFTER_STATEMENT; \
struct CONCATENATE(_assert_struct_, __COUNTER__) { \
char x[(expr) ? 0 : -1]; \
}; \
REENABLE_WARNING
#endif
#define assert_return(expr, r) \
do { \
if (_unlikely_(!(expr))) { \
log_assert_failed_return(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
return (r); \
} \
} while (false)
#define assert_return_errno(expr, r, err) \
do { \
if (_unlikely_(!(expr))) { \
log_assert_failed_return(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
errno = err; \
return (r); \
} \
} while (false)
#define PTR_TO_INT(p) ((int) ((intptr_t) (p)))
#define INT_TO_PTR(u) ((void *) ((intptr_t) (u)))
#define PTR_TO_UINT(p) ((unsigned int) ((uintptr_t) (p)))
#define UINT_TO_PTR(u) ((void *) ((uintptr_t) (u)))
#define PTR_TO_LONG(p) ((long) ((intptr_t) (p)))
#define LONG_TO_PTR(u) ((void *) ((intptr_t) (u)))
#define PTR_TO_ULONG(p) ((unsigned long) ((uintptr_t) (p)))
#define ULONG_TO_PTR(u) ((void *) ((uintptr_t) (u)))
#define PTR_TO_INT32(p) ((int32_t) ((intptr_t) (p)))
#define INT32_TO_PTR(u) ((void *) ((intptr_t) (u)))
#define PTR_TO_UINT32(p) ((uint32_t) ((uintptr_t) (p)))
#define UINT32_TO_PTR(u) ((void *) ((uintptr_t) (u)))
#define PTR_TO_INT64(p) ((int64_t) ((intptr_t) (p)))
#define INT64_TO_PTR(u) ((void *) ((intptr_t) (u)))
#define PTR_TO_UINT64(p) ((uint64_t) ((uintptr_t) (p)))
#define UINT64_TO_PTR(u) ((void *) ((uintptr_t) (u)))
#define PTR_TO_SIZE(p) ((size_t) ((uintptr_t) (p)))
#define SIZE_TO_PTR(u) ((void *) ((uintptr_t) (u)))
/* The following macros add 1 when converting things, since UID 0 is a
* valid UID, while the pointer NULL is special */
#define PTR_TO_UID(p) ((uid_t) (((uintptr_t) (p))-1))
#define UID_TO_PTR(u) ((void*) (((uintptr_t) (u))+1))
#define PTR_TO_GID(p) ((gid_t) (((uintptr_t) (p))-1))
#define GID_TO_PTR(u) ((void*) (((uintptr_t) (u))+1))
#define memzero(x,l) (memset((x), 0, (l)))
#define zero(x) (memzero(&(x), sizeof(x)))
#define CHAR_TO_STR(x) ((char[2]) { x, 0 })
#define char_array_0(x) x[sizeof(x)-1] = 0;
#define IOVEC_SET_STRING(i, s) \
do { \
struct iovec *_i = &(i); \
char *_s = (char *)(s); \
_i->iov_base = _s; \
_i->iov_len = strlen(_s); \
} while(false)
static inline size_t IOVEC_TOTAL_SIZE(const struct iovec *i, unsigned n) {
unsigned j;
size_t r = 0;
for (j = 0; j < n; j++)
r += i[j].iov_len;
return r;
}
static inline size_t IOVEC_INCREMENT(struct iovec *i, unsigned n, size_t k) {
unsigned j;
for (j = 0; j < n; j++) {
size_t sub;
if (_unlikely_(k <= 0))
break;
sub = MIN(i[j].iov_len, k);
i[j].iov_len -= sub;
i[j].iov_base = (uint8_t*) i[j].iov_base + sub;
k -= sub;
}
return k;
}
#define VA_FORMAT_ADVANCE(format, ap) \
do { \
int _argtypes[128]; \
size_t _i, _k; \
_k = parse_printf_format((format), ELEMENTSOF(_argtypes), _argtypes); \
assert(_k < ELEMENTSOF(_argtypes)); \
for (_i = 0; _i < _k; _i++) { \
if (_argtypes[_i] & PA_FLAG_PTR) { \
(void) va_arg(ap, void*); \
continue; \
} \
\
switch (_argtypes[_i]) { \
case PA_INT: \
case PA_INT|PA_FLAG_SHORT: \
case PA_CHAR: \
(void) va_arg(ap, int); \
break; \
case PA_INT|PA_FLAG_LONG: \
(void) va_arg(ap, long int); \
break; \
case PA_INT|PA_FLAG_LONG_LONG: \
(void) va_arg(ap, long long int); \
break; \
case PA_WCHAR: \
(void) va_arg(ap, wchar_t); \
break; \
case PA_WSTRING: \
case PA_STRING: \
case PA_POINTER: \
(void) va_arg(ap, void*); \
break; \
case PA_FLOAT: \
case PA_DOUBLE: \
(void) va_arg(ap, double); \
break; \
case PA_DOUBLE|PA_FLAG_LONG_DOUBLE: \
(void) va_arg(ap, long double); \
break; \
default: \
assert_not_reached("Unknown format string argument."); \
} \
} \
} while(false)
/* Because statfs.t_type can be int on some architectures, we have to cast
* the const magic to the type, otherwise the compiler warns about
* signed/unsigned comparison, because the magic can be 32 bit unsigned.
*/
#define F_TYPE_EQUAL(a, b) (a == (typeof(a)) b)
/* Returns the number of chars needed to format variables of the
* specified type as a decimal string. Adds in extra space for a
* negative '-' prefix (hence works correctly on signed
* types). Includes space for the trailing NUL. */
#define DECIMAL_STR_MAX(type) \
(2+(sizeof(type) <= 1 ? 3 : \
sizeof(type) <= 2 ? 5 : \
sizeof(type) <= 4 ? 10 : \
sizeof(type) <= 8 ? 20 : sizeof(int[-2*(sizeof(type) > 8)])))
#define SET_FLAG(v, flag, b) \
(v) = (b) ? ((v) | (flag)) : ((v) & ~(flag))
#define IN_SET(x, y, ...) \
({ \
const typeof(y) _y = (y); \
const typeof(_y) _x = (x); \
unsigned _i; \
bool _found = false; \
for (_i = 0; _i < 1 + sizeof((const typeof(_x)[]) { __VA_ARGS__ })/sizeof(const typeof(_x)); _i++) \
if (((const typeof(_x)[]) { _y, __VA_ARGS__ })[_i] == _x) { \
_found = true; \
break; \
} \
_found; \
})
/* Return a nulstr for a standard cascade of configuration directories,
* suitable to pass to conf_files_list_nulstr or config_parse_many. */
#define CONF_DIRS_NULSTR(n) \
"/etc/" n ".d\0" \
"/run/" n ".d\0" \
"/usr/local/lib/" n ".d\0" \
"/usr/lib/" n ".d\0" \
CONF_DIR_SPLIT_USR(n)
#ifdef HAVE_SPLIT_USR
#define CONF_DIR_SPLIT_USR(n) "/lib/" n ".d\0"
#else
#define CONF_DIR_SPLIT_USR(n)
#endif
/* Define C11 thread_local attribute even on older gcc compiler
* version */
#ifndef thread_local
/*
* Don't break on glibc < 2.16 that doesn't define __STDC_NO_THREADS__
* see http://gcc.gnu.org/bugzilla/show_bug.cgi?id=53769
*/
#if __STDC_VERSION__ >= 201112L && !(defined(__STDC_NO_THREADS__) || (defined(__GNU_LIBRARY__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 16))
#define thread_local _Thread_local
#else
#define thread_local __thread
#endif
#endif
/* Define C11 noreturn without <stdnoreturn.h> and even on older gcc
* compiler versions */
#ifndef noreturn
#if __STDC_VERSION__ >= 201112L
#define noreturn _Noreturn
#else
#define noreturn __attribute__((noreturn))
#endif
#endif
#define UID_INVALID ((uid_t) -1)
#define GID_INVALID ((gid_t) -1)
#define MODE_INVALID ((mode_t) -1)
#define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func) \
static inline void func##p(type *p) { \
if (*p) \
func(*p); \
} \
struct __useless_struct_to_allow_trailing_semicolon__
#define CMSG_FOREACH(cmsg, mh) \
for ((cmsg) = CMSG_FIRSTHDR(mh); (cmsg); (cmsg) = CMSG_NXTHDR((mh), (cmsg)))
#include "log.h"
+171
View File
@@ -0,0 +1,171 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2013 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#ifdef HAVE_LINUX_MEMFD_H
# include <linux/memfd.h>
#endif
#include "util.h"
#include "memfd-util.h"
#include "utf8.h"
#include "missing.h"
int memfd_new(const char *name) {
_cleanup_free_ char *g = NULL;
int fd;
if (!name) {
char pr[17] = {};
/* If no name is specified we generate one. We include
* a hint indicating our library implementation, and
* add the thread name to it */
assert_se(prctl(PR_GET_NAME, (unsigned long) pr) >= 0);
if (isempty(pr))
name = "sd";
else {
_cleanup_free_ char *e = NULL;
e = utf8_escape_invalid(pr);
if (!e)
return -ENOMEM;
g = strappend("sd-", e);
if (!g)
return -ENOMEM;
name = g;
}
}
fd = memfd_create(name, MFD_ALLOW_SEALING | MFD_CLOEXEC);
if (fd < 0)
return -errno;
return fd;
}
int memfd_map(int fd, uint64_t offset, size_t size, void **p) {
void *q;
int sealed;
assert(fd >= 0);
assert(size > 0);
assert(p);
sealed = memfd_get_sealed(fd);
if (sealed < 0)
return sealed;
if (sealed)
q = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, offset);
else
q = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset);
if (q == MAP_FAILED)
return -errno;
*p = q;
return 0;
}
int memfd_set_sealed(int fd) {
int r;
assert(fd >= 0);
r = fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_SEAL);
if (r < 0)
return -errno;
return 0;
}
int memfd_get_sealed(int fd) {
int r;
assert(fd >= 0);
r = fcntl(fd, F_GET_SEALS);
if (r < 0)
return -errno;
return r == (F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_SEAL);
}
int memfd_get_size(int fd, uint64_t *sz) {
struct stat stat;
int r;
assert(fd >= 0);
assert(sz);
r = fstat(fd, &stat);
if (r < 0)
return -errno;
*sz = stat.st_size;
return 0;
}
int memfd_set_size(int fd, uint64_t sz) {
int r;
assert(fd >= 0);
r = ftruncate(fd, sz);
if (r < 0)
return -errno;
return 0;
}
int memfd_new_and_map(const char *name, size_t sz, void **p) {
_cleanup_close_ int fd = -1;
int r;
assert(sz > 0);
assert(p);
fd = memfd_new(name);
if (fd < 0)
return fd;
r = memfd_set_size(fd, sz);
if (r < 0)
return r;
r = memfd_map(fd, 0, sz, p);
if (r < 0)
return r;
r = fd;
fd = -1;
return r;
}
+35
View File
@@ -0,0 +1,35 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2013 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
int memfd_new(const char *name);
int memfd_new_and_map(const char *name, size_t sz, void **p);
int memfd_map(int fd, uint64_t offset, size_t size, void **p);
int memfd_set_sealed(int fd);
int memfd_get_sealed(int fd);
int memfd_get_size(int fd, uint64_t *sz);
int memfd_set_size(int fd, uint64_t sz);
+103
View File
@@ -0,0 +1,103 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2010-2014 Lennart Poettering
Copyright 2014 Michal Schmidt
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "mempool.h"
#include "macro.h"
#include "util.h"
struct pool {
struct pool *next;
unsigned n_tiles;
unsigned n_used;
};
void* mempool_alloc_tile(struct mempool *mp) {
unsigned i;
/* When a tile is released we add it to the list and simply
* place the next pointer at its offset 0. */
assert(mp->tile_size >= sizeof(void*));
assert(mp->at_least > 0);
if (mp->freelist) {
void *r;
r = mp->freelist;
mp->freelist = * (void**) mp->freelist;
return r;
}
if (_unlikely_(!mp->first_pool) ||
_unlikely_(mp->first_pool->n_used >= mp->first_pool->n_tiles)) {
unsigned n;
size_t size;
struct pool *p;
n = mp->first_pool ? mp->first_pool->n_tiles : 0;
n = MAX(mp->at_least, n * 2);
size = PAGE_ALIGN(ALIGN(sizeof(struct pool)) + n*mp->tile_size);
n = (size - ALIGN(sizeof(struct pool))) / mp->tile_size;
p = malloc(size);
if (!p)
return NULL;
p->next = mp->first_pool;
p->n_tiles = n;
p->n_used = 0;
mp->first_pool = p;
}
i = mp->first_pool->n_used++;
return ((uint8_t*) mp->first_pool) + ALIGN(sizeof(struct pool)) + i*mp->tile_size;
}
void* mempool_alloc0_tile(struct mempool *mp) {
void *p;
p = mempool_alloc_tile(mp);
if (p)
memzero(p, mp->tile_size);
return p;
}
void mempool_free_tile(struct mempool *mp, void *p) {
* (void**) p = mp->freelist;
mp->freelist = p;
}
#ifdef VALGRIND
void mempool_drop(struct mempool *mp) {
struct pool *p = mp->first_pool;
while (p) {
struct pool *n;
n = p->next;
free(p);
p = n;
}
}
#endif
+49
View File
@@ -0,0 +1,49 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2011-2014 Lennart Poettering
Copyright 2014 Michal Schmidt
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <stddef.h>
struct pool;
struct mempool {
struct pool *first_pool;
void *freelist;
size_t tile_size;
unsigned at_least;
};
void* mempool_alloc_tile(struct mempool *mp);
void* mempool_alloc0_tile(struct mempool *mp);
void mempool_free_tile(struct mempool *mp, void *p);
#define DEFINE_MEMPOOL(pool_name, tile_type, alloc_at_least) \
struct mempool pool_name = { \
.tile_size = sizeof(tile_type), \
.at_least = alloc_at_least, \
}
#ifdef VALGRIND
void mempool_drop(struct mempool *mp);
#endif
+1001
View File
File diff suppressed because it is too large Load Diff
+39
View File
@@ -0,0 +1,39 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
Copyright 2013 Kay Sievers
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <unistd.h>
#include <stdio.h>
#include "label.h"
#include "mkdir.h"
int mkdir_safe_label(const char *path, mode_t mode, uid_t uid, gid_t gid) {
return mkdir_safe_internal(path, mode, uid, gid, mkdir_label);
}
int mkdir_parents_label(const char *path, mode_t mode) {
return mkdir_parents_internal(NULL, path, mode, mkdir_label);
}
int mkdir_p_label(const char *path, mode_t mode) {
return mkdir_p_internal(NULL, path, mode, mkdir_label);
}
+125
View File
@@ -0,0 +1,125 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <string.h>
#include <errno.h>
#include "util.h"
#include "path-util.h"
#include "mkdir.h"
int mkdir_safe_internal(const char *path, mode_t mode, uid_t uid, gid_t gid, mkdir_func_t _mkdir) {
struct stat st;
if (_mkdir(path, mode) >= 0)
if (chmod_and_chown(path, mode, uid, gid) < 0)
return -errno;
if (lstat(path, &st) < 0)
return -errno;
if ((st.st_mode & 0007) > (mode & 0007) ||
(st.st_mode & 0070) > (mode & 0070) ||
(st.st_mode & 0700) > (mode & 0700) ||
(uid != UID_INVALID && st.st_uid != uid) ||
(gid != GID_INVALID && st.st_gid != gid) ||
!S_ISDIR(st.st_mode))
return -EEXIST;
return 0;
}
int mkdir_safe(const char *path, mode_t mode, uid_t uid, gid_t gid) {
return mkdir_safe_internal(path, mode, uid, gid, mkdir);
}
int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir) {
const char *p, *e;
int r;
assert(path);
if (prefix && !path_startswith(path, prefix))
return -ENOTDIR;
/* return immediately if directory exists */
e = strrchr(path, '/');
if (!e)
return -EINVAL;
if (e == path)
return 0;
p = strndupa(path, e - path);
r = is_dir(p, true);
if (r > 0)
return 0;
if (r == 0)
return -ENOTDIR;
/* create every parent directory in the path, except the last component */
p = path + strspn(path, "/");
for (;;) {
char t[strlen(path) + 1];
e = p + strcspn(p, "/");
p = e + strspn(e, "/");
/* Is this the last component? If so, then we're
* done */
if (*p == 0)
return 0;
memcpy(t, path, e - path);
t[e-path] = 0;
if (prefix && path_startswith(prefix, t))
continue;
r = _mkdir(t, mode);
if (r < 0 && errno != EEXIST)
return -errno;
}
}
int mkdir_parents(const char *path, mode_t mode) {
return mkdir_parents_internal(NULL, path, mode, mkdir);
}
int mkdir_p_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir) {
int r;
/* Like mkdir -p */
r = mkdir_parents_internal(prefix, path, mode, _mkdir);
if (r < 0)
return r;
r = _mkdir(path, mode);
if (r < 0 && (errno != EEXIST || is_dir(path, true) <= 0))
return -errno;
return 0;
}
int mkdir_p(const char *path, mode_t mode) {
return mkdir_p_internal(NULL, path, mode, mkdir);
}
+40
View File
@@ -0,0 +1,40 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
Copyright 2013 Kay Sievers
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <sys/types.h>
int mkdir_safe(const char *path, mode_t mode, uid_t uid, gid_t gid);
int mkdir_parents(const char *path, mode_t mode);
int mkdir_p(const char *path, mode_t mode);
/* mandatory access control(MAC) versions */
int mkdir_safe_label(const char *path, mode_t mode, uid_t uid, gid_t gid);
int mkdir_parents_label(const char *path, mode_t mode);
int mkdir_p_label(const char *path, mode_t mode);
/* internally used */
typedef int (*mkdir_func_t)(const char *pathname, mode_t mode);
int mkdir_safe_internal(const char *path, mode_t mode, uid_t uid, gid_t gid, mkdir_func_t _mkdir);
int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir);
int mkdir_p_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir);
+59
View File
@@ -0,0 +1,59 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2015 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "hashmap.h"
typedef struct OrderedSet OrderedSet;
static inline OrderedSet* ordered_set_new(const struct hash_ops *ops) {
return (OrderedSet*) ordered_hashmap_new(ops);
}
static inline OrderedSet* ordered_set_free(OrderedSet *s) {
ordered_hashmap_free((OrderedHashmap*) s);
return NULL;
}
static inline OrderedSet* ordered_set_free_free(OrderedSet *s) {
ordered_hashmap_free_free((OrderedHashmap*) s);
return NULL;
}
static inline int ordered_set_put(OrderedSet *s, void *p) {
return ordered_hashmap_put((OrderedHashmap*) s, p, p);
}
static inline bool ordered_set_isempty(OrderedSet *s) {
return ordered_hashmap_isempty((OrderedHashmap*) s);
}
static inline void *ordered_set_iterate(OrderedSet *s, Iterator *i) {
return ordered_hashmap_iterate((OrderedHashmap*) s, i, NULL);
}
#define ORDERED_SET_FOREACH(e, s, i) \
for ((i) = ITERATOR_FIRST, (e) = ordered_set_iterate((s), &(i)); (e); (e) = ordered_set_iterate((s), &(i)))
DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedSet*, ordered_set_free);
#define _cleanup_ordered_set_free_ _cleanup_(ordered_set_freep)
+853
View File
@@ -0,0 +1,853 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2010-2012 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/statvfs.h>
#include "macro.h"
#include "util.h"
#include "log.h"
#include "strv.h"
#include "path-util.h"
#include "missing.h"
#include "fileio.h"
bool path_is_absolute(const char *p) {
return p[0] == '/';
}
bool is_path(const char *p) {
return !!strchr(p, '/');
}
int path_get_parent(const char *path, char **_r) {
const char *e, *a = NULL, *b = NULL, *p;
char *r;
bool slash = false;
assert(path);
assert(_r);
if (!*path)
return -EINVAL;
for (e = path; *e; e++) {
if (!slash && *e == '/') {
a = b;
b = e;
slash = true;
} else if (slash && *e != '/')
slash = false;
}
if (*(e-1) == '/')
p = a;
else
p = b;
if (!p)
return -EINVAL;
if (p == path)
r = strdup("/");
else
r = strndup(path, p-path);
if (!r)
return -ENOMEM;
*_r = r;
return 0;
}
char **path_split_and_make_absolute(const char *p) {
char **l;
assert(p);
l = strv_split(p, ":");
if (!l)
return NULL;
if (!path_strv_make_absolute_cwd(l)) {
strv_free(l);
return NULL;
}
return l;
}
char *path_make_absolute(const char *p, const char *prefix) {
assert(p);
/* Makes every item in the list an absolute path by prepending
* the prefix, if specified and necessary */
if (path_is_absolute(p) || !prefix)
return strdup(p);
return strjoin(prefix, "/", p, NULL);
}
char *path_make_absolute_cwd(const char *p) {
_cleanup_free_ char *cwd = NULL;
assert(p);
/* Similar to path_make_absolute(), but prefixes with the
* current working directory. */
if (path_is_absolute(p))
return strdup(p);
cwd = get_current_dir_name();
if (!cwd)
return NULL;
return strjoin(cwd, "/", p, NULL);
}
int path_make_relative(const char *from_dir, const char *to_path, char **_r) {
char *r, *p;
unsigned n_parents;
assert(from_dir);
assert(to_path);
assert(_r);
/* Strips the common part, and adds ".." elements as necessary. */
if (!path_is_absolute(from_dir))
return -EINVAL;
if (!path_is_absolute(to_path))
return -EINVAL;
/* Skip the common part. */
for (;;) {
size_t a;
size_t b;
from_dir += strspn(from_dir, "/");
to_path += strspn(to_path, "/");
if (!*from_dir) {
if (!*to_path)
/* from_dir equals to_path. */
r = strdup(".");
else
/* from_dir is a parent directory of to_path. */
r = strdup(to_path);
if (!r)
return -ENOMEM;
path_kill_slashes(r);
*_r = r;
return 0;
}
if (!*to_path)
break;
a = strcspn(from_dir, "/");
b = strcspn(to_path, "/");
if (a != b)
break;
if (memcmp(from_dir, to_path, a) != 0)
break;
from_dir += a;
to_path += b;
}
/* If we're here, then "from_dir" has one or more elements that need to
* be replaced with "..". */
/* Count the number of necessary ".." elements. */
for (n_parents = 0;;) {
from_dir += strspn(from_dir, "/");
if (!*from_dir)
break;
from_dir += strcspn(from_dir, "/");
n_parents++;
}
r = malloc(n_parents * 3 + strlen(to_path) + 1);
if (!r)
return -ENOMEM;
for (p = r; n_parents > 0; n_parents--, p += 3)
memcpy(p, "../", 3);
strcpy(p, to_path);
path_kill_slashes(r);
*_r = r;
return 0;
}
char **path_strv_make_absolute_cwd(char **l) {
char **s;
/* Goes through every item in the string list and makes it
* absolute. This works in place and won't rollback any
* changes on failure. */
STRV_FOREACH(s, l) {
char *t;
t = path_make_absolute_cwd(*s);
if (!t)
return NULL;
free(*s);
*s = t;
}
return l;
}
char **path_strv_resolve(char **l, const char *prefix) {
char **s;
unsigned k = 0;
bool enomem = false;
if (strv_isempty(l))
return l;
/* Goes through every item in the string list and canonicalize
* the path. This works in place and won't rollback any
* changes on failure. */
STRV_FOREACH(s, l) {
char *t, *u;
_cleanup_free_ char *orig = NULL;
if (!path_is_absolute(*s)) {
free(*s);
continue;
}
if (prefix) {
orig = *s;
t = strappend(prefix, orig);
if (!t) {
enomem = true;
continue;
}
} else
t = *s;
errno = 0;
u = canonicalize_file_name(t);
if (!u) {
if (errno == ENOENT) {
if (prefix) {
u = orig;
orig = NULL;
free(t);
} else
u = t;
} else {
free(t);
if (errno == ENOMEM || errno == 0)
enomem = true;
continue;
}
} else if (prefix) {
char *x;
free(t);
x = path_startswith(u, prefix);
if (x) {
/* restore the slash if it was lost */
if (!startswith(x, "/"))
*(--x) = '/';
t = strdup(x);
free(u);
if (!t) {
enomem = true;
continue;
}
u = t;
} else {
/* canonicalized path goes outside of
* prefix, keep the original path instead */
free(u);
u = orig;
orig = NULL;
}
} else
free(t);
l[k++] = u;
}
l[k] = NULL;
if (enomem)
return NULL;
return l;
}
char **path_strv_resolve_uniq(char **l, const char *prefix) {
if (strv_isempty(l))
return l;
if (!path_strv_resolve(l, prefix))
return NULL;
return strv_uniq(l);
}
char *path_kill_slashes(char *path) {
char *f, *t;
bool slash = false;
/* Removes redundant inner and trailing slashes. Modifies the
* passed string in-place.
*
* ///foo///bar/ becomes /foo/bar
*/
for (f = path, t = path; *f; f++) {
if (*f == '/') {
slash = true;
continue;
}
if (slash) {
slash = false;
*(t++) = '/';
}
*(t++) = *f;
}
/* Special rule, if we are talking of the root directory, a
trailing slash is good */
if (t == path && slash)
*(t++) = '/';
*t = 0;
return path;
}
char* path_startswith(const char *path, const char *prefix) {
assert(path);
assert(prefix);
if ((path[0] == '/') != (prefix[0] == '/'))
return NULL;
for (;;) {
size_t a, b;
path += strspn(path, "/");
prefix += strspn(prefix, "/");
if (*prefix == 0)
return (char*) path;
if (*path == 0)
return NULL;
a = strcspn(path, "/");
b = strcspn(prefix, "/");
if (a != b)
return NULL;
if (memcmp(path, prefix, a) != 0)
return NULL;
path += a;
prefix += b;
}
}
int path_compare(const char *a, const char *b) {
int d;
assert(a);
assert(b);
/* A relative path and an abolute path must not compare as equal.
* Which one is sorted before the other does not really matter.
* Here a relative path is ordered before an absolute path. */
d = (a[0] == '/') - (b[0] == '/');
if (d)
return d;
for (;;) {
size_t j, k;
a += strspn(a, "/");
b += strspn(b, "/");
if (*a == 0 && *b == 0)
return 0;
/* Order prefixes first: "/foo" before "/foo/bar" */
if (*a == 0)
return -1;
if (*b == 0)
return 1;
j = strcspn(a, "/");
k = strcspn(b, "/");
/* Alphabetical sort: "/foo/aaa" before "/foo/b" */
d = memcmp(a, b, MIN(j, k));
if (d)
return (d > 0) - (d < 0); /* sign of d */
/* Sort "/foo/a" before "/foo/aaa" */
d = (j > k) - (j < k); /* sign of (j - k) */
if (d)
return d;
a += j;
b += k;
}
}
bool path_equal(const char *a, const char *b) {
return path_compare(a, b) == 0;
}
bool path_equal_or_files_same(const char *a, const char *b) {
return path_equal(a, b) || files_same(a, b) > 0;
}
char* path_join(const char *root, const char *path, const char *rest) {
assert(path);
if (!isempty(root))
return strjoin(root, endswith(root, "/") ? "" : "/",
path[0] == '/' ? path+1 : path,
rest ? (endswith(path, "/") ? "" : "/") : NULL,
rest && rest[0] == '/' ? rest+1 : rest,
NULL);
else
return strjoin(path,
rest ? (endswith(path, "/") ? "" : "/") : NULL,
rest && rest[0] == '/' ? rest+1 : rest,
NULL);
}
static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *mnt_id) {
char path[strlen("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
_cleanup_free_ char *fdinfo = NULL;
_cleanup_close_ int subfd = -1;
char *p;
int r;
if ((flags & AT_EMPTY_PATH) && isempty(filename))
xsprintf(path, "/proc/self/fdinfo/%i", fd);
else {
subfd = openat(fd, filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH);
if (subfd < 0)
return -errno;
xsprintf(path, "/proc/self/fdinfo/%i", subfd);
}
r = read_full_file(path, &fdinfo, NULL);
if (r == -ENOENT) /* The fdinfo directory is a relatively new addition */
return -EOPNOTSUPP;
if (r < 0)
return -errno;
p = startswith(fdinfo, "mnt_id:");
if (!p) {
p = strstr(fdinfo, "\nmnt_id:");
if (!p) /* The mnt_id field is a relatively new addition */
return -EOPNOTSUPP;
p += 8;
}
p += strspn(p, WHITESPACE);
p[strcspn(p, WHITESPACE)] = 0;
return safe_atoi(p, mnt_id);
}
int fd_is_mount_point(int fd, const char *filename, int flags) {
union file_handle_union h = FILE_HANDLE_INIT, h_parent = FILE_HANDLE_INIT;
int mount_id = -1, mount_id_parent = -1;
bool nosupp = false, check_st_dev = true;
struct stat a, b;
int r;
assert(fd >= 0);
assert(filename);
/* First we will try the name_to_handle_at() syscall, which
* tells us the mount id and an opaque file "handle". It is
* not supported everywhere though (kernel compile-time
* option, not all file systems are hooked up). If it works
* the mount id is usually good enough to tell us whether
* something is a mount point.
*
* If that didn't work we will try to read the mount id from
* /proc/self/fdinfo/<fd>. This is almost as good as
* name_to_handle_at(), however, does not return the the
* opaque file handle. The opaque file handle is pretty useful
* to detect the root directory, which we should always
* consider a mount point. Hence we use this only as
* fallback. Exporting the mnt_id in fdinfo is a pretty recent
* kernel addition.
*
* As last fallback we do traditional fstat() based st_dev
* comparisons. This is how things were traditionally done,
* but unionfs breaks breaks this since it exposes file
* systems with a variety of st_dev reported. Also, btrfs
* subvolumes have different st_dev, even though they aren't
* real mounts of their own. */
r = name_to_handle_at(fd, filename, &h.handle, &mount_id, flags);
if (r < 0) {
if (errno == ENOSYS)
/* This kernel does not support name_to_handle_at()
* fall back to simpler logic. */
goto fallback_fdinfo;
else if (errno == EOPNOTSUPP)
/* This kernel or file system does not support
* name_to_handle_at(), hence let's see if the
* upper fs supports it (in which case it is a
* mount point), otherwise fallback to the
* traditional stat() logic */
nosupp = true;
else
return -errno;
}
r = name_to_handle_at(fd, "", &h_parent.handle, &mount_id_parent, AT_EMPTY_PATH);
if (r < 0) {
if (errno == EOPNOTSUPP) {
if (nosupp)
/* Neither parent nor child do name_to_handle_at()?
We have no choice but to fall back. */
goto fallback_fdinfo;
else
/* The parent can't do name_to_handle_at() but the
* directory we are interested in can?
* If so, it must be a mount point. */
return 1;
} else
return -errno;
}
/* The parent can do name_to_handle_at() but the
* directory we are interested in can't? If so, it
* must be a mount point. */
if (nosupp)
return 1;
/* If the file handle for the directory we are
* interested in and its parent are identical, we
* assume this is the root directory, which is a mount
* point. */
if (h.handle.handle_bytes == h_parent.handle.handle_bytes &&
h.handle.handle_type == h_parent.handle.handle_type &&
memcmp(h.handle.f_handle, h_parent.handle.f_handle, h.handle.handle_bytes) == 0)
return 1;
return mount_id != mount_id_parent;
fallback_fdinfo:
r = fd_fdinfo_mnt_id(fd, filename, flags, &mount_id);
if (r == -EOPNOTSUPP)
goto fallback_fstat;
if (r < 0)
return r;
r = fd_fdinfo_mnt_id(fd, "", AT_EMPTY_PATH, &mount_id_parent);
if (r < 0)
return r;
if (mount_id != mount_id_parent)
return 1;
/* Hmm, so, the mount ids are the same. This leaves one
* special case though for the root file system. For that,
* let's see if the parent directory has the same inode as we
* are interested in. Hence, let's also do fstat() checks now,
* too, but avoid the st_dev comparisons, since they aren't
* that useful on unionfs mounts. */
check_st_dev = false;
fallback_fstat:
/* yay for fstatat() taking a different set of flags than the other
* _at() above */
if (flags & AT_SYMLINK_FOLLOW)
flags &= ~AT_SYMLINK_FOLLOW;
else
flags |= AT_SYMLINK_NOFOLLOW;
if (fstatat(fd, filename, &a, flags) < 0)
return -errno;
if (fstatat(fd, "", &b, AT_EMPTY_PATH) < 0)
return -errno;
/* A directory with same device and inode as its parent? Must
* be the root directory */
if (a.st_dev == b.st_dev &&
a.st_ino == b.st_ino)
return 1;
return check_st_dev && (a.st_dev != b.st_dev);
}
/* flags can be AT_SYMLINK_FOLLOW or 0 */
int path_is_mount_point(const char *t, int flags) {
_cleanup_close_ int fd = -1;
_cleanup_free_ char *canonical = NULL, *parent = NULL;
int r;
assert(t);
if (path_equal(t, "/"))
return 1;
/* we need to resolve symlinks manually, we can't just rely on
* fd_is_mount_point() to do that for us; if we have a structure like
* /bin -> /usr/bin/ and /usr is a mount point, then the parent that we
* look at needs to be /usr, not /. */
if (flags & AT_SYMLINK_FOLLOW) {
canonical = canonicalize_file_name(t);
if (!canonical)
return -errno;
}
r = path_get_parent(canonical ?: t, &parent);
if (r < 0)
return r;
fd = openat(AT_FDCWD, parent, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_PATH);
if (fd < 0)
return -errno;
return fd_is_mount_point(fd, basename(canonical ?: t), flags);
}
int path_is_read_only_fs(const char *path) {
struct statvfs st;
assert(path);
if (statvfs(path, &st) < 0)
return -errno;
if (st.f_flag & ST_RDONLY)
return true;
/* On NFS, statvfs() might not reflect whether we can actually
* write to the remote share. Let's try again with
* access(W_OK) which is more reliable, at least sometimes. */
if (access(path, W_OK) < 0 && errno == EROFS)
return true;
return false;
}
int path_is_os_tree(const char *path) {
char *p;
int r;
/* We use /usr/lib/os-release as flag file if something is an OS */
p = strjoina(path, "/usr/lib/os-release");
r = access(p, F_OK);
if (r >= 0)
return 1;
/* Also check for the old location in /etc, just in case. */
p = strjoina(path, "/etc/os-release");
r = access(p, F_OK);
return r >= 0;
}
int find_binary(const char *name, bool local, char **filename) {
assert(name);
if (is_path(name)) {
if (local && access(name, X_OK) < 0)
return -errno;
if (filename) {
char *p;
p = path_make_absolute_cwd(name);
if (!p)
return -ENOMEM;
*filename = p;
}
return 0;
} else {
const char *path;
const char *word, *state;
size_t l;
/**
* Plain getenv, not secure_getenv, because we want
* to actually allow the user to pick the binary.
*/
path = getenv("PATH");
if (!path)
path = DEFAULT_PATH;
FOREACH_WORD_SEPARATOR(word, l, path, ":", state) {
_cleanup_free_ char *p = NULL;
if (asprintf(&p, "%.*s/%s", (int) l, word, name) < 0)
return -ENOMEM;
if (access(p, X_OK) < 0)
continue;
if (filename) {
*filename = path_kill_slashes(p);
p = NULL;
}
return 0;
}
return -ENOENT;
}
}
bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) {
bool changed = false;
const char* const* i;
assert(timestamp);
if (paths == NULL)
return false;
STRV_FOREACH(i, paths) {
struct stat stats;
usec_t u;
if (stat(*i, &stats) < 0)
continue;
u = timespec_load(&stats.st_mtim);
/* first check */
if (*timestamp >= u)
continue;
log_debug("timestamp of '%s' changed", *i);
/* update timestamp */
if (update) {
*timestamp = u;
changed = true;
} else
return true;
}
return changed;
}
int fsck_exists(const char *fstype) {
_cleanup_free_ char *p = NULL, *d = NULL;
const char *checker;
int r;
checker = strjoina("fsck.", fstype);
r = find_binary(checker, true, &p);
if (r < 0)
return r;
/* An fsck that is linked to /bin/true is a non-existent
* fsck */
r = readlink_malloc(p, &d);
if (r >= 0 &&
(path_equal(d, "/bin/true") ||
path_equal(d, "/usr/bin/true") ||
path_equal(d, "/dev/null")))
return -ENOENT;
return 0;
}
char *prefix_root(const char *root, const char *path) {
char *n, *p;
size_t l;
/* If root is passed, prefixes path with it. Otherwise returns
* it as is. */
assert(path);
/* First, drop duplicate prefixing slashes from the path */
while (path[0] == '/' && path[1] == '/')
path++;
if (isempty(root) || path_equal(root, "/"))
return strdup(path);
l = strlen(root) + 1 + strlen(path) + 1;
n = new(char, l);
if (!n)
return NULL;
p = stpcpy(n, root);
while (p > n && p[-1] == '/')
p--;
if (path[0] != '/')
*(p++) = '/';
strcpy(p, path);
return n;
}
+102
View File
@@ -0,0 +1,102 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2010-2012 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <stdbool.h>
#include "macro.h"
#include "time-util.h"
#define DEFAULT_PATH_NORMAL "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin"
#define DEFAULT_PATH_SPLIT_USR DEFAULT_PATH_NORMAL ":/sbin:/bin"
#ifdef HAVE_SPLIT_USR
# define DEFAULT_PATH DEFAULT_PATH_SPLIT_USR
#else
# define DEFAULT_PATH DEFAULT_PATH_NORMAL
#endif
bool is_path(const char *p) _pure_;
char** path_split_and_make_absolute(const char *p);
int path_get_parent(const char *path, char **parent);
bool path_is_absolute(const char *p) _pure_;
char* path_make_absolute(const char *p, const char *prefix);
char* path_make_absolute_cwd(const char *p);
int path_make_relative(const char *from_dir, const char *to_path, char **_r);
char* path_kill_slashes(char *path);
char* path_startswith(const char *path, const char *prefix) _pure_;
int path_compare(const char *a, const char *b) _pure_;
bool path_equal(const char *a, const char *b) _pure_;
bool path_equal_or_files_same(const char *a, const char *b);
char* path_join(const char *root, const char *path, const char *rest);
char** path_strv_make_absolute_cwd(char **l);
char** path_strv_resolve(char **l, const char *prefix);
char** path_strv_resolve_uniq(char **l, const char *prefix);
int fd_is_mount_point(int fd, const char *filename, int flags);
int path_is_mount_point(const char *path, int flags);
int path_is_read_only_fs(const char *path);
int path_is_os_tree(const char *path);
int find_binary(const char *name, bool local, char **filename);
bool paths_check_timestamp(const char* const* paths, usec_t *paths_ts_usec, bool update);
int fsck_exists(const char *fstype);
/* Iterates through the path prefixes of the specified path, going up
* the tree, to root. Also returns "" (and not "/"!) for the root
* directory. Excludes the specified directory itself */
#define PATH_FOREACH_PREFIX(prefix, path) \
for (char *_slash = ({ path_kill_slashes(strcpy(prefix, path)); streq(prefix, "/") ? NULL : strrchr(prefix, '/'); }); _slash && ((*_slash = 0), true); _slash = strrchr((prefix), '/'))
/* Same as PATH_FOREACH_PREFIX but also includes the specified path itself */
#define PATH_FOREACH_PREFIX_MORE(prefix, path) \
for (char *_slash = ({ path_kill_slashes(strcpy(prefix, path)); if (streq(prefix, "/")) prefix[0] = 0; strrchr(prefix, 0); }); _slash && ((*_slash = 0), true); _slash = strrchr((prefix), '/'))
char *prefix_root(const char *root, const char *path);
/* Similar to prefix_root(), but returns an alloca() buffer, or
* possibly a const pointer into the path parameter */
#define prefix_roota(root, path) \
({ \
const char* _path = (path), *_root = (root), *_ret; \
char *_p, *_n; \
size_t _l; \
while (_path[0] == '/' && _path[1] == '/') \
_path ++; \
if (isempty(_root) || path_equal(_root, "/")) \
_ret = _path; \
else { \
_l = strlen(_root) + 1 + strlen(_path) + 1; \
_n = alloca(_l); \
_p = stpcpy(_n, _root); \
while (_p > _n && _p[-1] == '/') \
_p--; \
if (_path[0] != '/') \
*(_p++) = '/'; \
strcpy(_p, _path); \
_ret = _n; \
} \
_ret; \
})
+308
View File
@@ -0,0 +1,308 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2013 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "util.h"
#include "prioq.h"
struct prioq_item {
void *data;
unsigned *idx;
};
struct Prioq {
compare_func_t compare_func;
unsigned n_items, n_allocated;
struct prioq_item *items;
};
Prioq *prioq_new(compare_func_t compare_func) {
Prioq *q;
q = new0(Prioq, 1);
if (!q)
return q;
q->compare_func = compare_func;
return q;
}
Prioq* prioq_free(Prioq *q) {
if (!q)
return NULL;
free(q->items);
free(q);
return NULL;
}
int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func) {
assert(q);
if (*q)
return 0;
*q = prioq_new(compare_func);
if (!*q)
return -ENOMEM;
return 0;
}
static void swap(Prioq *q, unsigned j, unsigned k) {
void *saved_data;
unsigned *saved_idx;
assert(q);
assert(j < q->n_items);
assert(k < q->n_items);
assert(!q->items[j].idx || *(q->items[j].idx) == j);
assert(!q->items[k].idx || *(q->items[k].idx) == k);
saved_data = q->items[j].data;
saved_idx = q->items[j].idx;
q->items[j].data = q->items[k].data;
q->items[j].idx = q->items[k].idx;
q->items[k].data = saved_data;
q->items[k].idx = saved_idx;
if (q->items[j].idx)
*q->items[j].idx = j;
if (q->items[k].idx)
*q->items[k].idx = k;
}
static unsigned shuffle_up(Prioq *q, unsigned idx) {
assert(q);
while (idx > 0) {
unsigned k;
k = (idx-1)/2;
if (q->compare_func(q->items[k].data, q->items[idx].data) < 0)
break;
swap(q, idx, k);
idx = k;
}
return idx;
}
static unsigned shuffle_down(Prioq *q, unsigned idx) {
assert(q);
for (;;) {
unsigned j, k, s;
k = (idx+1)*2; /* right child */
j = k-1; /* left child */
if (j >= q->n_items)
break;
if (q->compare_func(q->items[j].data, q->items[idx].data) < 0)
/* So our left child is smaller than we are, let's
* remember this fact */
s = j;
else
s = idx;
if (k < q->n_items &&
q->compare_func(q->items[k].data, q->items[s].data) < 0)
/* So our right child is smaller than we are, let's
* remember this fact */
s = k;
/* s now points to the smallest of the three items */
if (s == idx)
/* No swap necessary, we're done */
break;
swap(q, idx, s);
idx = s;
}
return idx;
}
int prioq_put(Prioq *q, void *data, unsigned *idx) {
struct prioq_item *i;
unsigned k;
assert(q);
if (q->n_items >= q->n_allocated) {
unsigned n;
struct prioq_item *j;
n = MAX((q->n_items+1) * 2, 16u);
j = realloc(q->items, sizeof(struct prioq_item) * n);
if (!j)
return -ENOMEM;
q->items = j;
q->n_allocated = n;
}
k = q->n_items++;
i = q->items + k;
i->data = data;
i->idx = idx;
if (idx)
*idx = k;
shuffle_up(q, k);
return 0;
}
static void remove_item(Prioq *q, struct prioq_item *i) {
struct prioq_item *l;
assert(q);
assert(i);
l = q->items + q->n_items - 1;
if (i == l)
/* Last entry, let's just remove it */
q->n_items--;
else {
unsigned k;
/* Not last entry, let's replace the last entry with
* this one, and reshuffle */
k = i - q->items;
i->data = l->data;
i->idx = l->idx;
if (i->idx)
*i->idx = k;
q->n_items--;
k = shuffle_down(q, k);
shuffle_up(q, k);
}
}
_pure_ static struct prioq_item* find_item(Prioq *q, void *data, unsigned *idx) {
struct prioq_item *i;
assert(q);
if (idx) {
if (*idx == PRIOQ_IDX_NULL ||
*idx > q->n_items)
return NULL;
i = q->items + *idx;
if (i->data != data)
return NULL;
return i;
} else {
for (i = q->items; i < q->items + q->n_items; i++)
if (i->data == data)
return i;
return NULL;
}
}
int prioq_remove(Prioq *q, void *data, unsigned *idx) {
struct prioq_item *i;
if (!q)
return 0;
i = find_item(q, data, idx);
if (!i)
return 0;
remove_item(q, i);
return 1;
}
int prioq_reshuffle(Prioq *q, void *data, unsigned *idx) {
struct prioq_item *i;
unsigned k;
assert(q);
i = find_item(q, data, idx);
if (!i)
return 0;
k = i - q->items;
k = shuffle_down(q, k);
shuffle_up(q, k);
return 1;
}
void *prioq_peek(Prioq *q) {
if (!q)
return NULL;
if (q->n_items <= 0)
return NULL;
return q->items[0].data;
}
void *prioq_pop(Prioq *q) {
void *data;
if (!q)
return NULL;
if (q->n_items <= 0)
return NULL;
data = q->items[0].data;
remove_item(q, q->items);
return data;
}
unsigned prioq_size(Prioq *q) {
if (!q)
return 0;
return q->n_items;
}
bool prioq_isempty(Prioq *q) {
if (!q)
return true;
return q->n_items <= 0;
}
+42
View File
@@ -0,0 +1,42 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2013 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "hashmap.h"
typedef struct Prioq Prioq;
#define PRIOQ_IDX_NULL ((unsigned) -1)
Prioq *prioq_new(compare_func_t compare);
Prioq *prioq_free(Prioq *q);
int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func);
int prioq_put(Prioq *q, void *data, unsigned *idx);
int prioq_remove(Prioq *q, void *data, unsigned *idx);
int prioq_reshuffle(Prioq *q, void *data, unsigned *idx);
void *prioq_peek(Prioq *q) _pure_;
void *prioq_pop(Prioq *q);
unsigned prioq_size(Prioq *q) _pure_;
bool prioq_isempty(Prioq *q) _pure_;
+539
View File
@@ -0,0 +1,539 @@
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <stdbool.h>
#include <sys/types.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
#include <ctype.h>
#include "fileio.h"
#include "util.h"
#include "log.h"
#include "signal-util.h"
#include "process-util.h"
int get_process_state(pid_t pid) {
const char *p;
char state;
int r;
_cleanup_free_ char *line = NULL;
assert(pid >= 0);
p = procfs_file_alloca(pid, "stat");
r = read_one_line_file(p, &line);
if (r < 0)
return r;
p = strrchr(line, ')');
if (!p)
return -EIO;
p++;
if (sscanf(p, " %c", &state) != 1)
return -EIO;
return (unsigned char) state;
}
int get_process_comm(pid_t pid, char **name) {
const char *p;
int r;
assert(name);
assert(pid >= 0);
p = procfs_file_alloca(pid, "comm");
r = read_one_line_file(p, name);
if (r == -ENOENT)
return -ESRCH;
return r;
}
int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line) {
_cleanup_fclose_ FILE *f = NULL;
char *r = NULL, *k;
const char *p;
int c;
assert(line);
assert(pid >= 0);
p = procfs_file_alloca(pid, "cmdline");
f = fopen(p, "re");
if (!f)
return -errno;
if (max_length == 0) {
size_t len = 0, allocated = 0;
while ((c = getc(f)) != EOF) {
if (!GREEDY_REALLOC(r, allocated, len+2)) {
free(r);
return -ENOMEM;
}
r[len++] = isprint(c) ? c : ' ';
}
if (len > 0)
r[len-1] = 0;
} else {
bool space = false;
size_t left;
r = new(char, max_length);
if (!r)
return -ENOMEM;
k = r;
left = max_length;
while ((c = getc(f)) != EOF) {
if (isprint(c)) {
if (space) {
if (left <= 4)
break;
*(k++) = ' ';
left--;
space = false;
}
if (left <= 4)
break;
*(k++) = (char) c;
left--;
} else
space = true;
}
if (left <= 4) {
size_t n = MIN(left-1, 3U);
memcpy(k, "...", n);
k[n] = 0;
} else
*k = 0;
}
/* Kernel threads have no argv[] */
if (isempty(r)) {
_cleanup_free_ char *t = NULL;
int h;
free(r);
if (!comm_fallback)
return -ENOENT;
h = get_process_comm(pid, &t);
if (h < 0)
return h;
r = strjoin("[", t, "]", NULL);
if (!r)
return -ENOMEM;
}
*line = r;
return 0;
}
int is_kernel_thread(pid_t pid) {
const char *p;
size_t count;
char c;
bool eof;
FILE *f;
if (pid == 0)
return 0;
assert(pid > 0);
p = procfs_file_alloca(pid, "cmdline");
f = fopen(p, "re");
if (!f)
return -errno;
count = fread(&c, 1, 1, f);
eof = feof(f);
fclose(f);
/* Kernel threads have an empty cmdline */
if (count <= 0)
return eof ? 1 : -errno;
return 0;
}
int get_process_capeff(pid_t pid, char **capeff) {
const char *p;
assert(capeff);
assert(pid >= 0);
p = procfs_file_alloca(pid, "status");
return get_status_field(p, "\nCapEff:", capeff);
}
static int get_process_link_contents(const char *proc_file, char **name) {
int r;
assert(proc_file);
assert(name);
r = readlink_malloc(proc_file, name);
if (r < 0)
return r == -ENOENT ? -ESRCH : r;
return 0;
}
int get_process_exe(pid_t pid, char **name) {
const char *p;
char *d;
int r;
assert(pid >= 0);
p = procfs_file_alloca(pid, "exe");
r = get_process_link_contents(p, name);
if (r < 0)
return r;
d = endswith(*name, " (deleted)");
if (d)
*d = '\0';
return 0;
}
static int get_process_id(pid_t pid, const char *field, uid_t *uid) {
_cleanup_fclose_ FILE *f = NULL;
char line[LINE_MAX];
const char *p;
assert(field);
assert(uid);
if (pid == 0)
return getuid();
p = procfs_file_alloca(pid, "status");
f = fopen(p, "re");
if (!f)
return -errno;
FOREACH_LINE(line, f, return -errno) {
char *l;
l = strstrip(line);
if (startswith(l, field)) {
l += strlen(field);
l += strspn(l, WHITESPACE);
l[strcspn(l, WHITESPACE)] = 0;
return parse_uid(l, uid);
}
}
return -EIO;
}
int get_process_uid(pid_t pid, uid_t *uid) {
return get_process_id(pid, "Uid:", uid);
}
int get_process_gid(pid_t pid, gid_t *gid) {
assert_cc(sizeof(uid_t) == sizeof(gid_t));
return get_process_id(pid, "Gid:", gid);
}
int get_process_cwd(pid_t pid, char **cwd) {
const char *p;
assert(pid >= 0);
p = procfs_file_alloca(pid, "cwd");
return get_process_link_contents(p, cwd);
}
int get_process_root(pid_t pid, char **root) {
const char *p;
assert(pid >= 0);
p = procfs_file_alloca(pid, "root");
return get_process_link_contents(p, root);
}
int get_process_environ(pid_t pid, char **env) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *outcome = NULL;
int c;
const char *p;
size_t allocated = 0, sz = 0;
assert(pid >= 0);
assert(env);
p = procfs_file_alloca(pid, "environ");
f = fopen(p, "re");
if (!f)
return -errno;
while ((c = fgetc(f)) != EOF) {
if (!GREEDY_REALLOC(outcome, allocated, sz + 5))
return -ENOMEM;
if (c == '\0')
outcome[sz++] = '\n';
else
sz += cescape_char(c, outcome + sz);
}
outcome[sz] = '\0';
*env = outcome;
outcome = NULL;
return 0;
}
int get_parent_of_pid(pid_t pid, pid_t *_ppid) {
int r;
_cleanup_free_ char *line = NULL;
long unsigned ppid;
const char *p;
assert(pid >= 0);
assert(_ppid);
if (pid == 0) {
*_ppid = getppid();
return 0;
}
p = procfs_file_alloca(pid, "stat");
r = read_one_line_file(p, &line);
if (r < 0)
return r;
/* Let's skip the pid and comm fields. The latter is enclosed
* in () but does not escape any () in its value, so let's
* skip over it manually */
p = strrchr(line, ')');
if (!p)
return -EIO;
p++;
if (sscanf(p, " "
"%*c " /* state */
"%lu ", /* ppid */
&ppid) != 1)
return -EIO;
if ((long unsigned) (pid_t) ppid != ppid)
return -ERANGE;
*_ppid = (pid_t) ppid;
return 0;
}
int wait_for_terminate(pid_t pid, siginfo_t *status) {
siginfo_t dummy;
assert(pid >= 1);
if (!status)
status = &dummy;
for (;;) {
zero(*status);
if (waitid(P_PID, pid, status, WEXITED) < 0) {
if (errno == EINTR)
continue;
return -errno;
}
return 0;
}
}
/*
* Return values:
* < 0 : wait_for_terminate() failed to get the state of the
* process, the process was terminated by a signal, or
* failed for an unknown reason.
* >=0 : The process terminated normally, and its exit code is
* returned.
*
* That is, success is indicated by a return value of zero, and an
* error is indicated by a non-zero value.
*
* A warning is emitted if the process terminates abnormally,
* and also if it returns non-zero unless check_exit_code is true.
*/
int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code) {
int r;
siginfo_t status;
assert(name);
assert(pid > 1);
r = wait_for_terminate(pid, &status);
if (r < 0)
return log_warning_errno(r, "Failed to wait for %s: %m", name);
if (status.si_code == CLD_EXITED) {
if (status.si_status != 0)
log_full(check_exit_code ? LOG_WARNING : LOG_DEBUG,
"%s failed with error code %i.", name, status.si_status);
else
log_debug("%s succeeded.", name);
return status.si_status;
} else if (status.si_code == CLD_KILLED ||
status.si_code == CLD_DUMPED) {
log_warning("%s terminated by signal %s.", name, signal_to_string(status.si_status));
return -EPROTO;
}
log_warning("%s failed due to unknown reason.", name);
return -EPROTO;
}
int kill_and_sigcont(pid_t pid, int sig) {
int r;
r = kill(pid, sig) < 0 ? -errno : 0;
if (r >= 0)
kill(pid, SIGCONT);
return r;
}
int getenv_for_pid(pid_t pid, const char *field, char **_value) {
_cleanup_fclose_ FILE *f = NULL;
char *value = NULL;
int r;
bool done = false;
size_t l;
const char *path;
assert(pid >= 0);
assert(field);
assert(_value);
path = procfs_file_alloca(pid, "environ");
f = fopen(path, "re");
if (!f)
return -errno;
l = strlen(field);
r = 0;
do {
char line[LINE_MAX];
unsigned i;
for (i = 0; i < sizeof(line)-1; i++) {
int c;
c = getc(f);
if (_unlikely_(c == EOF)) {
done = true;
break;
} else if (c == 0)
break;
line[i] = c;
}
line[i] = 0;
if (memcmp(line, field, l) == 0 && line[l] == '=') {
value = strdup(line + l + 1);
if (!value)
return -ENOMEM;
r = 1;
break;
}
} while (!done);
*_value = value;
return r;
}
bool pid_is_unwaited(pid_t pid) {
/* Checks whether a PID is still valid at all, including a zombie */
if (pid <= 0)
return false;
if (kill(pid, 0) >= 0)
return true;
return errno != ESRCH;
}
bool pid_is_alive(pid_t pid) {
int r;
/* Checks whether a PID is still valid and not a zombie */
if (pid <= 0)
return false;
r = get_process_state(pid);
if (r == -ENOENT || r == 'Z')
return false;
return true;
}
+65
View File
@@ -0,0 +1,65 @@
#pragma once
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <stdbool.h>
#include <sys/types.h>
#include <alloca.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include "formats-util.h"
#define procfs_file_alloca(pid, field) \
({ \
pid_t _pid_ = (pid); \
const char *_r_; \
if (_pid_ == 0) { \
_r_ = ("/proc/self/" field); \
} else { \
_r_ = alloca(strlen("/proc/") + DECIMAL_STR_MAX(pid_t) + 1 + sizeof(field)); \
sprintf((char*) _r_, "/proc/"PID_FMT"/" field, _pid_); \
} \
_r_; \
})
int get_process_state(pid_t pid);
int get_process_comm(pid_t pid, char **name);
int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line);
int get_process_exe(pid_t pid, char **name);
int get_process_uid(pid_t pid, uid_t *uid);
int get_process_gid(pid_t pid, gid_t *gid);
int get_process_capeff(pid_t pid, char **capeff);
int get_process_cwd(pid_t pid, char **cwd);
int get_process_root(pid_t pid, char **root);
int get_process_environ(pid_t pid, char **environ);
int wait_for_terminate(pid_t pid, siginfo_t *status);
int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code);
int kill_and_sigcont(pid_t pid, int sig);
pid_t get_parent_of_pid(pid_t pid, pid_t *ppid);
void rename_process(const char name[8]);
int is_kernel_thread(pid_t pid);
int getenv_for_pid(pid_t pid, const char *field, char **_value);
bool pid_is_alive(pid_t pid);
bool pid_is_unwaited(pid_t pid);
+129
View File
@@ -0,0 +1,129 @@
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <stdint.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#ifdef HAVE_SYS_AUXV_H
#include <sys/auxv.h>
#endif
#include <linux/random.h>
#include "random-util.h"
#include "time-util.h"
#include "missing.h"
#include "util.h"
int dev_urandom(void *p, size_t n) {
static int have_syscall = -1;
_cleanup_close_ int fd = -1;
int r;
/* Gathers some randomness from the kernel. This call will
* never block, and will always return some data from the
* kernel, regardless if the random pool is fully initialized
* or not. It thus makes no guarantee for the quality of the
* returned entropy, but is good enough for or usual usecases
* of seeding the hash functions for hashtable */
/* Use the getrandom() syscall unless we know we don't have
* it, or when the requested size is too large for it. */
if (have_syscall != 0 || (size_t) (int) n != n) {
r = getrandom(p, n, GRND_NONBLOCK);
if (r == (int) n) {
have_syscall = true;
return 0;
}
if (r < 0) {
if (errno == ENOSYS)
/* we lack the syscall, continue with
* reading from /dev/urandom */
have_syscall = false;
else if (errno == EAGAIN)
/* not enough entropy for now. Let's
* remember to use the syscall the
* next time, again, but also read
* from /dev/urandom for now, which
* doesn't care about the current
* amount of entropy. */
have_syscall = true;
else
return -errno;
} else
/* too short read? */
return -ENODATA;
}
fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0)
return errno == ENOENT ? -ENOSYS : -errno;
return loop_read_exact(fd, p, n, true);
}
void initialize_srand(void) {
static bool srand_called = false;
unsigned x;
#ifdef HAVE_SYS_AUXV_H
void *auxv;
#endif
if (srand_called)
return;
x = 0;
#ifdef HAVE_SYS_AUXV_H
/* The kernel provides us with a bit of entropy in auxv, so
* let's try to make use of that to seed the pseudo-random
* generator. It's better than nothing... */
auxv = (void*) getauxval(AT_RANDOM);
if (auxv)
x ^= *(unsigned*) auxv;
#endif
x ^= (unsigned) now(CLOCK_REALTIME);
x ^= (unsigned) gettid();
srand(x);
srand_called = true;
}
void random_bytes(void *p, size_t n) {
uint8_t *q;
int r;
r = dev_urandom(p, n);
if (r >= 0)
return;
/* If some idiot made /dev/urandom unavailable to us, he'll
* get a PRNG instead. */
initialize_srand();
for (q = p; q < (uint8_t*) p + n; q ++)
*q = rand();
}
+38
View File
@@ -0,0 +1,38 @@
#pragma once
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <stdint.h>
int dev_urandom(void *p, size_t n);
void random_bytes(void *p, size_t n);
void initialize_srand(void);
static inline uint64_t random_u64(void) {
uint64_t u;
random_bytes(&u, sizeof(u));
return u;
}
static inline uint32_t random_u32(void) {
uint32_t u;
random_bytes(&u, sizeof(u));
return u;
}
+55
View File
@@ -0,0 +1,55 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "ratelimit.h"
/* Modelled after Linux' lib/ratelimit.c by Dave Young
* <hidave.darkstar@gmail.com>, which is licensed GPLv2. */
bool ratelimit_test(RateLimit *r) {
usec_t ts;
assert(r);
if (r->interval <= 0 || r->burst <= 0)
return true;
ts = now(CLOCK_MONOTONIC);
if (r->begin <= 0 ||
r->begin + r->interval < ts) {
r->begin = ts;
/* Reset counter */
r->num = 0;
goto good;
}
if (r->num < r->burst)
goto good;
return false;
good:
r->num++;
return true;
}
+57
View File
@@ -0,0 +1,57 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "util.h"
typedef struct RateLimit {
usec_t interval;
usec_t begin;
unsigned burst;
unsigned num;
} RateLimit;
#define RATELIMIT_DEFINE(_name, _interval, _burst) \
RateLimit _name = { \
.interval = (_interval), \
.burst = (_burst), \
.num = 0, \
.begin = 0 \
}
#define RATELIMIT_INIT(v, _interval, _burst) \
do { \
RateLimit *_r = &(v); \
_r->interval = (_interval); \
_r->burst = (_burst); \
_r->num = 0; \
_r->begin = 0; \
} while (false)
#define RATELIMIT_RESET(v) \
do { \
RateLimit *_r = &(v); \
_r->num = 0; \
_r->begin = 0; \
} while (false)
bool ratelimit_test(RateLimit *r);
+34
View File
@@ -0,0 +1,34 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2013 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
/* A type-safe atomic refcounter */
typedef struct {
volatile unsigned _value;
} RefCount;
#define REFCNT_GET(r) ((r)._value)
#define REFCNT_INC(r) (__sync_add_and_fetch(&(r)._value, 1))
#define REFCNT_DEC(r) (__sync_sub_and_fetch(&(r)._value, 1))
#define REFCNT_INIT ((RefCount) { ._value = 1 })
+111
View File
@@ -0,0 +1,111 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2012 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <string.h>
#include "macro.h"
#include "util.h"
#include "replace-var.h"
#include "def.h"
/*
* Generic infrastructure for replacing @FOO@ style variables in
* strings. Will call a callback for each replacement.
*/
static int get_variable(const char *b, char **r) {
size_t k;
char *t;
assert(b);
assert(r);
if (*b != '@')
return 0;
k = strspn(b + 1, UPPERCASE_LETTERS "_");
if (k <= 0 || b[k+1] != '@')
return 0;
t = strndup(b + 1, k);
if (!t)
return -ENOMEM;
*r = t;
return 1;
}
char *replace_var(const char *text, char *(*lookup)(const char *variable, void*userdata), void *userdata) {
char *r, *t;
const char *f;
size_t l;
assert(text);
assert(lookup);
l = strlen(text);
r = new(char, l+1);
if (!r)
return NULL;
f = text;
t = r;
while (*f) {
_cleanup_free_ char *v = NULL, *n = NULL;
char *a;
int k;
size_t skip, d, nl;
k = get_variable(f, &v);
if (k < 0)
goto oom;
if (k == 0) {
*(t++) = *(f++);
continue;
}
n = lookup(v, userdata);
if (!n)
goto oom;
skip = strlen(v) + 2;
d = t - r;
nl = l - skip + strlen(n);
a = realloc(r, nl + 1);
if (!a)
goto oom;
l = nl;
r = a;
t = r + d;
t = stpcpy(t, n);
f += skip;
}
*t = 0;
return r;
oom:
free(r);
return NULL;
}
+24
View File
@@ -0,0 +1,24 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2012 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
char *replace_var(const char *text, char *(*lookup)(const char *variable, void*userdata), void *userdata);
+209
View File
@@ -0,0 +1,209 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2014 David Herrmann <dh.herrmann@gmail.com>
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/uio.h>
#include "macro.h"
#include "ring.h"
#define RING_MASK(_r, _v) ((_v) & ((_r)->size - 1))
void ring_flush(Ring *r) {
assert(r);
r->start = 0;
r->used = 0;
}
void ring_clear(Ring *r) {
assert(r);
free(r->buf);
zero(*r);
}
/*
* Get data pointers for current ring-buffer data. @vec must be an array of 2
* iovec objects. They are filled according to the data available in the
* ring-buffer. 0, 1 or 2 is returned according to the number of iovec objects
* that were filled (0 meaning buffer is empty).
*
* Hint: "struct iovec" is defined in <sys/uio.h> and looks like this:
* struct iovec {
* void *iov_base;
* size_t iov_len;
* };
*/
size_t ring_peek(Ring *r, struct iovec *vec) {
assert(r);
if (r->used == 0) {
return 0;
} else if (r->start + r->used <= r->size) {
if (vec) {
vec[0].iov_base = &r->buf[r->start];
vec[0].iov_len = r->used;
}
return 1;
} else {
if (vec) {
vec[0].iov_base = &r->buf[r->start];
vec[0].iov_len = r->size - r->start;
vec[1].iov_base = r->buf;
vec[1].iov_len = r->used - (r->size - r->start);
}
return 2;
}
}
/*
* Copy data from the ring buffer into the linear external buffer @buf. Copy
* at most @size bytes. If the ring buffer size is smaller, copy less bytes and
* return the number of bytes copied.
*/
size_t ring_copy(Ring *r, void *buf, size_t size) {
size_t l;
assert(r);
assert(buf);
if (size > r->used)
size = r->used;
if (size > 0) {
l = r->size - r->start;
if (size <= l) {
memcpy(buf, &r->buf[r->start], size);
} else {
memcpy(buf, &r->buf[r->start], l);
memcpy((uint8_t*)buf + l, r->buf, size - l);
}
}
return size;
}
/*
* Resize ring-buffer to size @nsize. @nsize must be a power-of-2, otherwise
* ring operations will behave incorrectly.
*/
static int ring_resize(Ring *r, size_t nsize) {
uint8_t *buf;
size_t l;
assert(r);
assert(nsize > 0);
buf = malloc(nsize);
if (!buf)
return -ENOMEM;
if (r->used > 0) {
l = r->size - r->start;
if (r->used <= l) {
memcpy(buf, &r->buf[r->start], r->used);
} else {
memcpy(buf, &r->buf[r->start], l);
memcpy(&buf[l], r->buf, r->used - l);
}
}
free(r->buf);
r->buf = buf;
r->size = nsize;
r->start = 0;
return 0;
}
/*
* Resize ring-buffer to provide enough room for @add bytes of new data. This
* resizes the buffer if it is too small. It returns -ENOMEM on OOM and 0 on
* success.
*/
static int ring_grow(Ring *r, size_t add) {
size_t need;
assert(r);
if (r->size - r->used >= add)
return 0;
need = r->used + add;
if (need <= r->used)
return -ENOMEM;
else if (need < 4096)
need = 4096;
need = ALIGN_POWER2(need);
if (need == 0)
return -ENOMEM;
return ring_resize(r, need);
}
/*
* Push @len bytes from @u8 into the ring buffer. The buffer is resized if it
* is too small. -ENOMEM is returned on OOM, 0 on success.
*/
int ring_push(Ring *r, const void *u8, size_t size) {
int err;
size_t pos, l;
assert(r);
assert(u8);
if (size == 0)
return 0;
err = ring_grow(r, size);
if (err < 0)
return err;
pos = RING_MASK(r, r->start + r->used);
l = r->size - pos;
if (l >= size) {
memcpy(&r->buf[pos], u8, size);
} else {
memcpy(&r->buf[pos], u8, l);
memcpy(r->buf, (const uint8_t*)u8 + l, size - l);
}
r->used += size;
return 0;
}
/*
* Remove @len bytes from the start of the ring-buffer. Note that we protect
* against overflows so removing more bytes than available is safe.
*/
void ring_pull(Ring *r, size_t size) {
assert(r);
if (size > r->used)
size = r->used;
r->start = RING_MASK(r, r->start + size);
r->used -= size;
}
+56
View File
@@ -0,0 +1,56 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2014 David Herrmann <dh.herrmann@gmail.com>
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
typedef struct Ring Ring;
struct Ring {
uint8_t *buf; /* buffer or NULL */
size_t size; /* actual size of @buf */
size_t start; /* start position of ring */
size_t used; /* number of actually used bytes */
};
/* flush buffer so it is empty again */
void ring_flush(Ring *r);
/* flush buffer, free allocated data and reset to initial state */
void ring_clear(Ring *r);
/* get pointers to buffer data and their length */
size_t ring_peek(Ring *r, struct iovec *vec);
/* copy data into external linear buffer */
size_t ring_copy(Ring *r, void *buf, size_t size);
/* push data to the end of the buffer */
int ring_push(Ring *r, const void *u8, size_t size);
/* pull data from the front of the buffer */
void ring_pull(Ring *r, size_t size);
/* return size of occupied buffer in bytes */
static inline size_t ring_get_size(Ring *r)
{
return r->used;
}
+224
View File
@@ -0,0 +1,224 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2015 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "util.h"
#include "path-util.h"
#include "btrfs-util.h"
#include "rm-rf.h"
int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) {
_cleanup_closedir_ DIR *d = NULL;
int ret = 0, r;
assert(fd >= 0);
/* This returns the first error we run into, but nevertheless
* tries to go on. This closes the passed fd. */
if (!(flags & REMOVE_PHYSICAL)) {
r = fd_is_temporary_fs(fd);
if (r < 0) {
safe_close(fd);
return r;
}
if (!r) {
/* We refuse to clean physical file systems
* with this call, unless explicitly
* requested. This is extra paranoia just to
* be sure we never ever remove non-state
* data */
log_error("Attempted to remove disk file system, and we can't allow that.");
safe_close(fd);
return -EPERM;
}
}
d = fdopendir(fd);
if (!d) {
safe_close(fd);
return errno == ENOENT ? 0 : -errno;
}
for (;;) {
struct dirent *de;
bool is_dir;
struct stat st;
errno = 0;
de = readdir(d);
if (!de) {
if (errno != 0 && ret == 0)
ret = -errno;
return ret;
}
if (streq(de->d_name, ".") || streq(de->d_name, ".."))
continue;
if (de->d_type == DT_UNKNOWN ||
(de->d_type == DT_DIR && (root_dev || (flags & REMOVE_SUBVOLUME)))) {
if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
if (ret == 0 && errno != ENOENT)
ret = -errno;
continue;
}
is_dir = S_ISDIR(st.st_mode);
} else
is_dir = de->d_type == DT_DIR;
if (is_dir) {
int subdir_fd;
/* if root_dev is set, remove subdirectories only if device is same */
if (root_dev && st.st_dev != root_dev->st_dev)
continue;
subdir_fd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
if (subdir_fd < 0) {
if (ret == 0 && errno != ENOENT)
ret = -errno;
continue;
}
/* Stop at mount points */
r = fd_is_mount_point(fd, de->d_name, 0);
if (r < 0) {
if (ret == 0 && r != -ENOENT)
ret = r;
safe_close(subdir_fd);
continue;
}
if (r) {
safe_close(subdir_fd);
continue;
}
if ((flags & REMOVE_SUBVOLUME) && st.st_ino == 256) {
/* This could be a subvolume, try to remove it */
r = btrfs_subvol_remove_fd(fd, de->d_name, true);
if (r < 0) {
if (r != -ENOTTY && r != -EINVAL) {
if (ret == 0)
ret = r;
safe_close(subdir_fd);
continue;
}
/* ENOTTY, then it wasn't a
* btrfs subvolume, continue
* below. */
} else {
/* It was a subvolume, continue. */
safe_close(subdir_fd);
continue;
}
}
/* We pass REMOVE_PHYSICAL here, to avoid
* doing the fstatfs() to check the file
* system type again for each directory */
r = rm_rf_children(subdir_fd, flags | REMOVE_PHYSICAL, root_dev);
if (r < 0 && ret == 0)
ret = r;
if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) {
if (ret == 0 && errno != ENOENT)
ret = -errno;
}
} else if (!(flags & REMOVE_ONLY_DIRECTORIES)) {
if (unlinkat(fd, de->d_name, 0) < 0) {
if (ret == 0 && errno != ENOENT)
ret = -errno;
}
}
}
}
int rm_rf(const char *path, RemoveFlags flags) {
int fd, r;
struct statfs s;
assert(path);
/* We refuse to clean the root file system with this
* call. This is extra paranoia to never cause a really
* seriously broken system. */
if (path_equal(path, "/")) {
log_error("Attempted to remove entire root file system, and we can't allow that.");
return -EPERM;
}
if ((flags & (REMOVE_SUBVOLUME|REMOVE_ROOT|REMOVE_PHYSICAL)) == (REMOVE_SUBVOLUME|REMOVE_ROOT|REMOVE_PHYSICAL)) {
/* Try to remove as subvolume first */
r = btrfs_subvol_remove(path, true);
if (r >= 0)
return r;
if (r != -ENOTTY && r != -EINVAL)
return r;
/* Not btrfs or not a subvolume */
}
fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
if (fd < 0) {
if (errno != ENOTDIR && errno != ELOOP)
return -errno;
if (!(flags & REMOVE_PHYSICAL)) {
if (statfs(path, &s) < 0)
return -errno;
if (!is_temporary_fs(&s)) {
log_error("Attempted to remove disk file system, and we can't allow that.");
return -EPERM;
}
}
if ((flags & REMOVE_ROOT) && !(flags & REMOVE_ONLY_DIRECTORIES))
if (unlink(path) < 0 && errno != ENOENT)
return -errno;
return 0;
}
r = rm_rf_children(fd, flags, NULL);
if (flags & REMOVE_ROOT) {
if (rmdir(path) < 0) {
if (r == 0 && errno != ENOENT)
r = -errno;
}
}
return r;
}

Some files were not shown because too many files have changed in this diff Show More