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:
@@ -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
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../Makefile
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
@@ -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);
|
||||
@@ -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_
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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);
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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);
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_;
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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]
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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
|
||||
};
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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)
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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) {})
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../Makefile
|
||||
@@ -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))
|
||||
@@ -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);
|
||||
@@ -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_;
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
+229
@@ -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); \
|
||||
})
|
||||
@@ -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';
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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"
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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)
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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; \
|
||||
})
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_;
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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 })
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user