mirror of
https://git.proxmox.com/git/mirror_lxc
synced 2025-07-19 23:07:46 +00:00

Will fallback to no thinpool if not present or if thin pool provided on the command line does not exist. Signed-off-by: Serge Hallyn <serge.hallyn@ubuntu.com>
979 lines
20 KiB
C
979 lines
20 KiB
C
/*
|
|
* lxc: linux Container library
|
|
*
|
|
* (C) Copyright IBM Corp. 2007, 2008
|
|
*
|
|
* Authors:
|
|
* Daniel Lezcano <daniel.lezcano at free.fr>
|
|
*
|
|
* This library 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.
|
|
*
|
|
* This library 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 this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#define _GNU_SOURCE
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/param.h>
|
|
#include <sys/mount.h>
|
|
#include <dirent.h>
|
|
#include <fcntl.h>
|
|
#include <libgen.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <assert.h>
|
|
|
|
#ifndef HAVE_GETLINE
|
|
#ifdef HAVE_FGETLN
|
|
#include <../include/getline.h>
|
|
#endif
|
|
#endif
|
|
|
|
#include "utils.h"
|
|
#include "log.h"
|
|
#include "lxclock.h"
|
|
|
|
lxc_log_define(lxc_utils, lxc);
|
|
|
|
static int _recursive_rmdir_onedev(char *dirname, dev_t pdev)
|
|
{
|
|
struct dirent dirent, *direntp;
|
|
DIR *dir;
|
|
int ret, failed=0;
|
|
char pathname[MAXPATHLEN];
|
|
|
|
process_lock();
|
|
dir = opendir(dirname);
|
|
process_unlock();
|
|
if (!dir) {
|
|
ERROR("%s: failed to open %s", __func__, dirname);
|
|
return 0;
|
|
}
|
|
|
|
while (!readdir_r(dir, &dirent, &direntp)) {
|
|
struct stat mystat;
|
|
int rc;
|
|
|
|
if (!direntp)
|
|
break;
|
|
|
|
if (!strcmp(direntp->d_name, ".") ||
|
|
!strcmp(direntp->d_name, ".."))
|
|
continue;
|
|
|
|
rc = snprintf(pathname, MAXPATHLEN, "%s/%s", dirname, direntp->d_name);
|
|
if (rc < 0 || rc >= MAXPATHLEN) {
|
|
ERROR("pathname too long");
|
|
failed=1;
|
|
continue;
|
|
}
|
|
ret = lstat(pathname, &mystat);
|
|
if (ret) {
|
|
ERROR("%s: failed to stat %s", __func__, pathname);
|
|
failed=1;
|
|
continue;
|
|
}
|
|
if (mystat.st_dev != pdev)
|
|
continue;
|
|
if (S_ISDIR(mystat.st_mode)) {
|
|
if (!_recursive_rmdir_onedev(pathname, pdev))
|
|
failed=1;
|
|
} else {
|
|
if (unlink(pathname) < 0) {
|
|
ERROR("%s: failed to delete %s", __func__, pathname);
|
|
failed=1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (rmdir(dirname) < 0) {
|
|
ERROR("%s: failed to delete %s", __func__, dirname);
|
|
failed=1;
|
|
}
|
|
|
|
process_lock();
|
|
ret = closedir(dir);
|
|
process_unlock();
|
|
if (ret) {
|
|
ERROR("%s: failed to close directory %s", __func__, dirname);
|
|
failed=1;
|
|
}
|
|
|
|
return !failed;
|
|
}
|
|
|
|
/* returns 1 on success, 0 if there were any failures */
|
|
extern int lxc_rmdir_onedev(char *path)
|
|
{
|
|
struct stat mystat;
|
|
|
|
if (lstat(path, &mystat) < 0) {
|
|
ERROR("%s: failed to stat %s", __func__, path);
|
|
return 0;
|
|
}
|
|
|
|
return _recursive_rmdir_onedev(path, mystat.st_dev);
|
|
}
|
|
|
|
static int mount_fs(const char *source, const char *target, const char *type)
|
|
{
|
|
/* the umount may fail */
|
|
if (umount(target))
|
|
WARN("failed to unmount %s : %s", target, strerror(errno));
|
|
|
|
if (mount(source, target, type, 0, NULL)) {
|
|
ERROR("failed to mount %s : %s", target, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
DEBUG("'%s' mounted on '%s'", source, target);
|
|
|
|
return 0;
|
|
}
|
|
|
|
extern int lxc_setup_fs(void)
|
|
{
|
|
if (mount_fs("proc", "/proc", "proc"))
|
|
return -1;
|
|
|
|
/* if we can't mount /dev/shm, continue anyway */
|
|
if (mount_fs("shmfs", "/dev/shm", "tmpfs"))
|
|
INFO("failed to mount /dev/shm");
|
|
|
|
/* If we were able to mount /dev/shm, then /dev exists */
|
|
/* Sure, but it's read-only per config :) */
|
|
if (access("/dev/mqueue", F_OK) && mkdir("/dev/mqueue", 0666)) {
|
|
DEBUG("failed to create '/dev/mqueue'");
|
|
return 0;
|
|
}
|
|
|
|
/* continue even without posix message queue support */
|
|
if (mount_fs("mqueue", "/dev/mqueue", "mqueue"))
|
|
INFO("failed to mount /dev/mqueue");
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* borrowed from iproute2 */
|
|
extern int get_u16(unsigned short *val, const char *arg, int base)
|
|
{
|
|
unsigned long res;
|
|
char *ptr;
|
|
|
|
if (!arg || !*arg)
|
|
return -1;
|
|
|
|
res = strtoul(arg, &ptr, base);
|
|
if (!ptr || ptr == arg || *ptr || res > 0xFFFF)
|
|
return -1;
|
|
|
|
*val = res;
|
|
|
|
return 0;
|
|
}
|
|
|
|
extern int mkdir_p(const char *dir, mode_t mode)
|
|
{
|
|
const char *tmp = dir;
|
|
const char *orig = dir;
|
|
char *makeme;
|
|
|
|
do {
|
|
dir = tmp + strspn(tmp, "/");
|
|
tmp = dir + strcspn(dir, "/");
|
|
makeme = strndup(orig, dir - orig);
|
|
if (*makeme) {
|
|
if (mkdir(makeme, mode) && errno != EEXIST) {
|
|
SYSERROR("failed to create directory '%s'\n", makeme);
|
|
free(makeme);
|
|
return -1;
|
|
}
|
|
}
|
|
free(makeme);
|
|
} while(tmp != dir);
|
|
|
|
return 0;
|
|
}
|
|
|
|
extern void remove_trailing_slashes(char *p)
|
|
{
|
|
int l = strlen(p);
|
|
while (--l >= 0 && (p[l] == '/' || p[l] == '\n'))
|
|
p[l] = '\0';
|
|
}
|
|
|
|
static char *copy_global_config_value(char *p)
|
|
{
|
|
int len = strlen(p);
|
|
char *retbuf;
|
|
|
|
if (len < 1)
|
|
return NULL;
|
|
if (p[len-1] == '\n') {
|
|
p[len-1] = '\0';
|
|
len--;
|
|
}
|
|
retbuf = malloc(len+1);
|
|
if (!retbuf)
|
|
return NULL;
|
|
strcpy(retbuf, p);
|
|
return retbuf;
|
|
}
|
|
|
|
#define DEFAULT_VG "lxc"
|
|
#define DEFAULT_THIN_POOL "lxc"
|
|
#define DEFAULT_ZFSROOT "lxc"
|
|
|
|
const char *lxc_global_config_value(const char *option_name)
|
|
{
|
|
static const char *options[][2] = {
|
|
{ "lvm_vg", DEFAULT_VG },
|
|
{ "lvm_thin_pool", DEFAULT_THIN_POOL },
|
|
{ "zfsroot", DEFAULT_ZFSROOT },
|
|
{ "lxcpath", LXCPATH },
|
|
{ "cgroup.pattern", DEFAULT_CGROUP_PATTERN },
|
|
{ "cgroup.use", NULL },
|
|
{ NULL, NULL },
|
|
};
|
|
static const char *values[sizeof(options) / sizeof(options[0])] = { 0 };
|
|
const char *(*ptr)[2];
|
|
size_t i;
|
|
char buf[1024], *p, *p2;
|
|
FILE *fin = NULL;
|
|
|
|
for (i = 0, ptr = options; (*ptr)[0]; ptr++, i++) {
|
|
if (!strcmp(option_name, (*ptr)[0]))
|
|
break;
|
|
}
|
|
if (!(*ptr)[0]) {
|
|
errno = EINVAL;
|
|
return NULL;
|
|
}
|
|
if (values[i])
|
|
return values[i];
|
|
|
|
process_lock();
|
|
fin = fopen_cloexec(LXC_GLOBAL_CONF, "r");
|
|
process_unlock();
|
|
if (fin) {
|
|
while (fgets(buf, 1024, fin)) {
|
|
if (buf[0] == '#')
|
|
continue;
|
|
p = strstr(buf, option_name);
|
|
if (!p)
|
|
continue;
|
|
/* see if there was just white space in front
|
|
* of the option name
|
|
*/
|
|
for (p2 = buf; p2 < p; p2++) {
|
|
if (*p2 != ' ' && *p2 != '\t')
|
|
break;
|
|
}
|
|
if (p2 < p)
|
|
continue;
|
|
p = strchr(p, '=');
|
|
if (!p)
|
|
continue;
|
|
/* see if there was just white space after
|
|
* the option name
|
|
*/
|
|
for (p2 += strlen(option_name); p2 < p; p2++) {
|
|
if (*p2 != ' ' && *p2 != '\t')
|
|
break;
|
|
}
|
|
if (p2 < p)
|
|
continue;
|
|
p++;
|
|
while (*p && (*p == ' ' || *p == '\t')) p++;
|
|
if (!*p)
|
|
continue;
|
|
values[i] = copy_global_config_value(p);
|
|
goto out;
|
|
}
|
|
}
|
|
/* could not find value, use default */
|
|
values[i] = (*ptr)[1];
|
|
/* special case: if default value is NULL,
|
|
* and there is no config, don't view that
|
|
* as an error... */
|
|
if (!values[i])
|
|
errno = 0;
|
|
|
|
out:
|
|
process_lock();
|
|
if (fin)
|
|
fclose(fin);
|
|
process_unlock();
|
|
return values[i];
|
|
}
|
|
|
|
const char *default_lvm_vg(void)
|
|
{
|
|
return lxc_global_config_value("lvm_vg");
|
|
}
|
|
|
|
const char *default_lvm_thin_pool(void)
|
|
{
|
|
return lxc_global_config_value("lvm_thin_pool");
|
|
}
|
|
|
|
const char *default_zfs_root(void)
|
|
{
|
|
return lxc_global_config_value("zfsroot");
|
|
}
|
|
const char *default_lxc_path(void)
|
|
{
|
|
return lxc_global_config_value("lxcpath");
|
|
}
|
|
|
|
const char *get_rundir()
|
|
{
|
|
const char *rundir;
|
|
|
|
rundir = getenv("XDG_RUNTIME_DIR");
|
|
if (geteuid() == 0 || rundir == NULL)
|
|
rundir = "/run";
|
|
return rundir;
|
|
}
|
|
|
|
int wait_for_pid(pid_t pid)
|
|
{
|
|
int status, ret;
|
|
|
|
again:
|
|
ret = waitpid(pid, &status, 0);
|
|
if (ret == -1) {
|
|
if (errno == EINTR)
|
|
goto again;
|
|
return -1;
|
|
}
|
|
if (ret != pid)
|
|
goto again;
|
|
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
int lxc_wait_for_pid_status(pid_t pid)
|
|
{
|
|
int status, ret;
|
|
|
|
again:
|
|
ret = waitpid(pid, &status, 0);
|
|
if (ret == -1) {
|
|
if (errno == EINTR)
|
|
goto again;
|
|
return -1;
|
|
}
|
|
if (ret != pid)
|
|
goto again;
|
|
return status;
|
|
}
|
|
|
|
ssize_t lxc_write_nointr(int fd, const void* buf, size_t count)
|
|
{
|
|
ssize_t ret;
|
|
again:
|
|
ret = write(fd, buf, count);
|
|
if (ret < 0 && errno == EINTR)
|
|
goto again;
|
|
return ret;
|
|
}
|
|
|
|
ssize_t lxc_read_nointr(int fd, void* buf, size_t count)
|
|
{
|
|
ssize_t ret;
|
|
again:
|
|
ret = read(fd, buf, count);
|
|
if (ret < 0 && errno == EINTR)
|
|
goto again;
|
|
return ret;
|
|
}
|
|
|
|
ssize_t lxc_read_nointr_expect(int fd, void* buf, size_t count, const void* expected_buf)
|
|
{
|
|
ssize_t ret;
|
|
ret = lxc_read_nointr(fd, buf, count);
|
|
if (ret <= 0)
|
|
return ret;
|
|
if ((size_t)ret != count)
|
|
return -1;
|
|
if (expected_buf && memcmp(buf, expected_buf, count) != 0) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static inline int lock_fclose(FILE *f)
|
|
{
|
|
int ret;
|
|
process_lock();
|
|
ret = fclose(f);
|
|
process_unlock();
|
|
return ret;
|
|
}
|
|
|
|
#if HAVE_LIBGNUTLS
|
|
#include <gnutls/gnutls.h>
|
|
#include <gnutls/crypto.h>
|
|
|
|
__attribute__((constructor))
|
|
static void gnutls_lxc_init(void)
|
|
{
|
|
gnutls_global_init();
|
|
}
|
|
|
|
int sha1sum_file(char *fnam, unsigned char *digest)
|
|
{
|
|
char *buf;
|
|
int ret;
|
|
FILE *f;
|
|
long flen;
|
|
|
|
if (!fnam)
|
|
return -1;
|
|
process_lock();
|
|
f = fopen_cloexec(fnam, "r");
|
|
process_unlock();
|
|
if (f < 0) {
|
|
SYSERROR("Error opening template");
|
|
return -1;
|
|
}
|
|
if (fseek(f, 0, SEEK_END) < 0) {
|
|
SYSERROR("Error seeking to end of template");
|
|
lock_fclose(f);
|
|
return -1;
|
|
}
|
|
if ((flen = ftell(f)) < 0) {
|
|
SYSERROR("Error telling size of template");
|
|
lock_fclose(f);
|
|
return -1;
|
|
}
|
|
if (fseek(f, 0, SEEK_SET) < 0) {
|
|
SYSERROR("Error seeking to start of template");
|
|
lock_fclose(f);
|
|
return -1;
|
|
}
|
|
if ((buf = malloc(flen+1)) == NULL) {
|
|
SYSERROR("Out of memory");
|
|
lock_fclose(f);
|
|
return -1;
|
|
}
|
|
if (fread(buf, 1, flen, f) != flen) {
|
|
SYSERROR("Failure reading template");
|
|
free(buf);
|
|
lock_fclose(f);
|
|
return -1;
|
|
}
|
|
if (lock_fclose(f) < 0) {
|
|
SYSERROR("Failre closing template");
|
|
free(buf);
|
|
return -1;
|
|
}
|
|
buf[flen] = '\0';
|
|
ret = gnutls_hash_fast(GNUTLS_DIG_SHA1, buf, flen, (void *)digest);
|
|
free(buf);
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
char** lxc_va_arg_list_to_argv(va_list ap, size_t skip, int do_strdup)
|
|
{
|
|
va_list ap2;
|
|
size_t count = 1 + skip;
|
|
char **result;
|
|
|
|
/* first determine size of argument list, we don't want to reallocate
|
|
* constantly...
|
|
*/
|
|
va_copy(ap2, ap);
|
|
while (1) {
|
|
char* arg = va_arg(ap2, char*);
|
|
if (!arg)
|
|
break;
|
|
count++;
|
|
}
|
|
va_end(ap2);
|
|
|
|
result = calloc(count, sizeof(char*));
|
|
if (!result)
|
|
return NULL;
|
|
count = skip;
|
|
while (1) {
|
|
char* arg = va_arg(ap, char*);
|
|
if (!arg)
|
|
break;
|
|
arg = do_strdup ? strdup(arg) : arg;
|
|
if (!arg)
|
|
goto oom;
|
|
result[count++] = arg;
|
|
}
|
|
|
|
/* calloc has already set last element to NULL*/
|
|
return result;
|
|
|
|
oom:
|
|
free(result);
|
|
return NULL;
|
|
}
|
|
|
|
const char** lxc_va_arg_list_to_argv_const(va_list ap, size_t skip)
|
|
{
|
|
return (const char**)lxc_va_arg_list_to_argv(ap, skip, 0);
|
|
}
|
|
|
|
/*
|
|
* fopen_cloexec: must be called with process_lock() held
|
|
* if it is needed.
|
|
*/
|
|
FILE *fopen_cloexec(const char *path, const char *mode)
|
|
{
|
|
int open_mode = 0;
|
|
int step = 0;
|
|
int fd;
|
|
int saved_errno = 0;
|
|
FILE *ret;
|
|
|
|
if (!strncmp(mode, "r+", 2)) {
|
|
open_mode = O_RDWR;
|
|
step = 2;
|
|
} else if (!strncmp(mode, "r", 1)) {
|
|
open_mode = O_RDONLY;
|
|
step = 1;
|
|
} else if (!strncmp(mode, "w+", 2)) {
|
|
open_mode = O_RDWR | O_TRUNC | O_CREAT;
|
|
step = 2;
|
|
} else if (!strncmp(mode, "w", 1)) {
|
|
open_mode = O_WRONLY | O_TRUNC | O_CREAT;
|
|
step = 1;
|
|
} else if (!strncmp(mode, "a+", 2)) {
|
|
open_mode = O_RDWR | O_CREAT | O_APPEND;
|
|
step = 2;
|
|
} else if (!strncmp(mode, "a", 1)) {
|
|
open_mode = O_WRONLY | O_CREAT | O_APPEND;
|
|
step = 1;
|
|
}
|
|
for (; mode[step]; step++)
|
|
if (mode[step] == 'x')
|
|
open_mode |= O_EXCL;
|
|
open_mode |= O_CLOEXEC;
|
|
|
|
fd = open(path, open_mode, 0666);
|
|
if (fd < 0)
|
|
return NULL;
|
|
|
|
ret = fdopen(fd, mode);
|
|
saved_errno = errno;
|
|
if (!ret)
|
|
close(fd);
|
|
errno = saved_errno;
|
|
return ret;
|
|
}
|
|
|
|
char *lxc_string_replace(const char *needle, const char *replacement, const char *haystack)
|
|
{
|
|
ssize_t len = -1, saved_len = -1;
|
|
char *result = NULL;
|
|
size_t replacement_len = strlen(replacement);
|
|
size_t needle_len = strlen(needle);
|
|
|
|
/* should be executed exactly twice */
|
|
while (len == -1 || result == NULL) {
|
|
char *p;
|
|
char *last_p;
|
|
ssize_t part_len;
|
|
|
|
if (len != -1) {
|
|
result = calloc(1, len + 1);
|
|
if (!result)
|
|
return NULL;
|
|
saved_len = len;
|
|
}
|
|
|
|
len = 0;
|
|
|
|
for (last_p = (char *)haystack, p = strstr(last_p, needle); p; last_p = p, p = strstr(last_p, needle)) {
|
|
part_len = (ssize_t)(p - last_p);
|
|
if (result && part_len > 0)
|
|
memcpy(&result[len], last_p, part_len);
|
|
len += part_len;
|
|
if (result && replacement_len > 0)
|
|
memcpy(&result[len], replacement, replacement_len);
|
|
len += replacement_len;
|
|
p += needle_len;
|
|
}
|
|
part_len = strlen(last_p);
|
|
if (result && part_len > 0)
|
|
memcpy(&result[len], last_p, part_len);
|
|
len += part_len;
|
|
}
|
|
|
|
/* make sure we did the same thing twice,
|
|
* once for calculating length, the other
|
|
* time for copying data */
|
|
assert(saved_len == len);
|
|
/* make sure we didn't overwrite any buffer,
|
|
* due to calloc the string should be 0-terminated */
|
|
assert(result[len] == '\0');
|
|
|
|
return result;
|
|
}
|
|
|
|
bool lxc_string_in_array(const char *needle, const char **haystack)
|
|
{
|
|
for (; haystack && *haystack; haystack++)
|
|
if (!strcmp(needle, *haystack))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
char *lxc_string_join(const char *sep, const char **parts, bool use_as_prefix)
|
|
{
|
|
char *result;
|
|
char **p;
|
|
size_t sep_len = strlen(sep);
|
|
size_t result_len = use_as_prefix * sep_len;
|
|
|
|
/* calculate new string length */
|
|
for (p = (char **)parts; *p; p++)
|
|
result_len += (p > (char **)parts) * sep_len + strlen(*p);
|
|
|
|
result = calloc(result_len + 1, 1);
|
|
if (!result)
|
|
return NULL;
|
|
|
|
if (use_as_prefix)
|
|
strcpy(result, sep);
|
|
for (p = (char **)parts; *p; p++) {
|
|
if (p > (char **)parts)
|
|
strcat(result, sep);
|
|
strcat(result, *p);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
char **lxc_normalize_path(const char *path)
|
|
{
|
|
char **components;
|
|
char **p;
|
|
size_t components_len = 0;
|
|
size_t pos = 0;
|
|
|
|
components = lxc_string_split(path, '/');
|
|
if (!components)
|
|
return NULL;
|
|
for (p = components; *p; p++)
|
|
components_len++;
|
|
|
|
/* resolve '.' and '..' */
|
|
for (pos = 0; pos < components_len; ) {
|
|
if (!strcmp(components[pos], ".") || (!strcmp(components[pos], "..") && pos == 0)) {
|
|
/* eat this element */
|
|
free(components[pos]);
|
|
memmove(&components[pos], &components[pos+1], sizeof(char *) * (components_len - pos));
|
|
components_len--;
|
|
} else if (!strcmp(components[pos], "..")) {
|
|
/* eat this and the previous element */
|
|
free(components[pos - 1]);
|
|
free(components[pos]);
|
|
memmove(&components[pos-1], &components[pos+1], sizeof(char *) * (components_len - pos));
|
|
components_len -= 2;
|
|
pos--;
|
|
} else {
|
|
pos++;
|
|
}
|
|
}
|
|
|
|
return components;
|
|
}
|
|
|
|
char *lxc_append_paths(const char *first, const char *second)
|
|
{
|
|
size_t len = strlen(first) + strlen(second) + 1;
|
|
const char *pattern = "%s%s";
|
|
char *result = NULL;
|
|
|
|
if (second[0] != '/') {
|
|
len += 1;
|
|
pattern = "%s/%s";
|
|
}
|
|
|
|
result = calloc(1, len);
|
|
if (!result)
|
|
return NULL;
|
|
|
|
snprintf(result, len, pattern, first, second);
|
|
return result;
|
|
}
|
|
|
|
bool lxc_string_in_list(const char *needle, const char *haystack, char _sep)
|
|
{
|
|
char *token, *str, *saveptr = NULL;
|
|
char sep[2] = { _sep, '\0' };
|
|
|
|
if (!haystack || !needle)
|
|
return 0;
|
|
|
|
str = alloca(strlen(haystack)+1);
|
|
strcpy(str, haystack);
|
|
for (; (token = strtok_r(str, sep, &saveptr)); str = NULL) {
|
|
if (strcmp(needle, token) == 0)
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
char **lxc_string_split(const char *string, char _sep)
|
|
{
|
|
char *token, *str, *saveptr = NULL;
|
|
char sep[2] = { _sep, '\0' };
|
|
char **result = NULL;
|
|
size_t result_capacity = 0;
|
|
size_t result_count = 0;
|
|
int r, saved_errno;
|
|
|
|
if (!string)
|
|
return calloc(1, sizeof(char *));
|
|
|
|
str = alloca(strlen(string)+1);
|
|
strcpy(str, string);
|
|
for (; (token = strtok_r(str, sep, &saveptr)); str = NULL) {
|
|
r = lxc_grow_array((void ***)&result, &result_capacity, result_count + 1, 16);
|
|
if (r < 0)
|
|
goto error_out;
|
|
result[result_count] = strdup(token);
|
|
if (!result[result_count])
|
|
goto error_out;
|
|
result_count++;
|
|
}
|
|
|
|
/* if we allocated too much, reduce it */
|
|
return realloc(result, (result_count + 1) * sizeof(char *));
|
|
error_out:
|
|
saved_errno = errno;
|
|
lxc_free_array((void **)result, free);
|
|
errno = saved_errno;
|
|
return NULL;
|
|
}
|
|
|
|
char **lxc_string_split_and_trim(const char *string, char _sep)
|
|
{
|
|
char *token, *str, *saveptr = NULL;
|
|
char sep[2] = { _sep, '\0' };
|
|
char **result = NULL;
|
|
size_t result_capacity = 0;
|
|
size_t result_count = 0;
|
|
int r, saved_errno;
|
|
size_t i = 0;
|
|
|
|
if (!string)
|
|
return calloc(1, sizeof(char *));
|
|
|
|
str = alloca(strlen(string)+1);
|
|
strcpy(str, string);
|
|
for (; (token = strtok_r(str, sep, &saveptr)); str = NULL) {
|
|
while (token[0] == ' ' || token[0] == '\t')
|
|
token++;
|
|
i = strlen(token);
|
|
while (i > 0 && (token[i - 1] == ' ' || token[i - 1] == '\t')) {
|
|
token[i - 1] = '\0';
|
|
i--;
|
|
}
|
|
r = lxc_grow_array((void ***)&result, &result_capacity, result_count + 1, 16);
|
|
if (r < 0)
|
|
goto error_out;
|
|
result[result_count] = strdup(token);
|
|
if (!result[result_count])
|
|
goto error_out;
|
|
result_count++;
|
|
}
|
|
|
|
/* if we allocated too much, reduce it */
|
|
return realloc(result, (result_count + 1) * sizeof(char *));
|
|
error_out:
|
|
saved_errno = errno;
|
|
lxc_free_array((void **)result, free);
|
|
errno = saved_errno;
|
|
return NULL;
|
|
}
|
|
|
|
void lxc_free_array(void **array, lxc_free_fn element_free_fn)
|
|
{
|
|
void **p;
|
|
for (p = array; p && *p; p++)
|
|
element_free_fn(*p);
|
|
free((void*)array);
|
|
}
|
|
|
|
int lxc_grow_array(void ***array, size_t* capacity, size_t new_size, size_t capacity_increment)
|
|
{
|
|
size_t new_capacity;
|
|
void **new_array;
|
|
|
|
/* first time around, catch some trivial mistakes of the user
|
|
* only initializing one of these */
|
|
if (!*array || !*capacity) {
|
|
*array = NULL;
|
|
*capacity = 0;
|
|
}
|
|
|
|
new_capacity = *capacity;
|
|
while (new_size + 1 > new_capacity)
|
|
new_capacity += capacity_increment;
|
|
if (new_capacity != *capacity) {
|
|
/* we have to reallocate */
|
|
new_array = realloc(*array, new_capacity * sizeof(void *));
|
|
if (!new_array)
|
|
return -1;
|
|
memset(&new_array[*capacity], 0, (new_capacity - (*capacity)) * sizeof(void *));
|
|
*array = new_array;
|
|
*capacity = new_capacity;
|
|
}
|
|
|
|
/* array has sufficient elements */
|
|
return 0;
|
|
}
|
|
|
|
size_t lxc_array_len(void **array)
|
|
{
|
|
void **p;
|
|
size_t result = 0;
|
|
|
|
for (p = array; p && *p; p++)
|
|
result++;
|
|
|
|
return result;
|
|
}
|
|
|
|
void **lxc_dup_array(void **array, lxc_dup_fn element_dup_fn, lxc_free_fn element_free_fn)
|
|
{
|
|
size_t l = lxc_array_len(array);
|
|
void **result = calloc(l + 1, sizeof(void *));
|
|
void **pp;
|
|
void *p;
|
|
int saved_errno = 0;
|
|
|
|
if (!result)
|
|
return NULL;
|
|
|
|
for (l = 0, pp = array; pp && *pp; pp++, l++) {
|
|
p = element_dup_fn(*pp);
|
|
if (!p) {
|
|
saved_errno = errno;
|
|
lxc_free_array(result, element_free_fn);
|
|
errno = saved_errno;
|
|
return NULL;
|
|
}
|
|
result[l] = p;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
int lxc_write_to_file(const char *filename, const void* buf, size_t count, bool add_newline)
|
|
{
|
|
int fd, saved_errno;
|
|
ssize_t ret;
|
|
|
|
process_lock();
|
|
fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, 0666);
|
|
process_unlock();
|
|
if (fd < 0)
|
|
return -1;
|
|
ret = lxc_write_nointr(fd, buf, count);
|
|
if (ret < 0)
|
|
goto out_error;
|
|
if ((size_t)ret != count)
|
|
goto out_error;
|
|
if (add_newline) {
|
|
ret = lxc_write_nointr(fd, "\n", 1);
|
|
if (ret != 1)
|
|
goto out_error;
|
|
}
|
|
process_lock();
|
|
close(fd);
|
|
process_unlock();
|
|
return 0;
|
|
|
|
out_error:
|
|
saved_errno = errno;
|
|
process_lock();
|
|
close(fd);
|
|
process_unlock();
|
|
errno = saved_errno;
|
|
return -1;
|
|
}
|
|
|
|
int lxc_read_from_file(const char *filename, void* buf, size_t count)
|
|
{
|
|
int fd = -1, saved_errno;
|
|
ssize_t ret;
|
|
|
|
process_lock();
|
|
fd = open(filename, O_RDONLY | O_CLOEXEC);
|
|
process_unlock();
|
|
if (fd < 0)
|
|
return -1;
|
|
|
|
if (!buf || !count) {
|
|
char buf2[100];
|
|
size_t count2 = 0;
|
|
while ((ret = read(fd, buf2, 100)) > 0)
|
|
count2 += ret;
|
|
if (ret >= 0)
|
|
ret = count2;
|
|
} else {
|
|
memset(buf, 0, count);
|
|
ret = read(fd, buf, count);
|
|
}
|
|
|
|
if (ret < 0)
|
|
ERROR("read %s: %s", filename, strerror(errno));
|
|
|
|
saved_errno = errno;
|
|
process_lock();
|
|
close(fd);
|
|
process_unlock();
|
|
errno = saved_errno;
|
|
return ret;
|
|
}
|
|
|
|
void **lxc_append_null_to_array(void **array, size_t count)
|
|
{
|
|
void **temp;
|
|
|
|
/* Append NULL to the array */
|
|
if (count) {
|
|
temp = realloc(array, (count + 1) * sizeof(*array));
|
|
if (!temp) {
|
|
int i;
|
|
for (i = 0; i < count; i++)
|
|
free(array[i]);
|
|
free(array);
|
|
return NULL;
|
|
}
|
|
array = temp;
|
|
array[count] = NULL;
|
|
}
|
|
return array;
|
|
}
|