Browse Source

Prep v228: Add remaining updates from upstream (1/3)

The util.[hc] files have been stripped of a lot of functions, that
got sorted into various new files representing the type of
utility.

This commit adds the missing files.
keep-around/964a6d9fb555cc86528eb1cc1f6d044f85584842
Sven Eden 6 years ago
parent
commit
b96ed50e34
  1. 18
      .gitignore
  2. 9
      Makefile-man.am
  3. 48
      Makefile.am
  4. 81
      src/basic/alloc-util.c
  5. 108
      src/basic/alloc-util.h
  6. 13
      src/basic/audit-util.c
  7. 2
      src/basic/audit-util.h
  8. 24
      src/basic/capability-util.c
  9. 10
      src/basic/capability-util.h
  10. 81
      src/basic/dirent-util.c
  11. 51
      src/basic/dirent-util.h
  12. 482
      src/basic/escape.c
  13. 48
      src/basic/escape.h
  14. 292
      src/basic/extract-word.c
  15. 37
      src/basic/extract-word.h
  16. 351
      src/basic/fd-util.c
  17. 75
      src/basic/fd-util.h
  18. 509
      src/basic/fs-util.c
  19. 75
      src/basic/fs-util.h
  20. 698
      src/basic/hexdecoct.c
  21. 54
      src/basic/hexdecoct.h
  22. 261
      src/basic/io-util.c
  23. 76
      src/basic/io-util.h
  24. 312
      src/basic/locale-util.c
  25. 75
      src/basic/locale-util.h
  26. 532
      src/basic/mount-util.c
  27. 52
      src/basic/mount-util.h
  28. 495
      src/basic/parse-util.c
  29. 92
      src/basic/parse-util.h
  30. 177
      src/basic/proc-cmdline.c
  31. 11
      src/basic/proc-cmdline.h
  32. 937
      src/basic/socket-util.c
  33. 231
      src/basic/stat-util.c
  34. 73
      src/basic/stat-util.h
  35. 78
      src/basic/stdio-util.h
  36. 35
      src/basic/string-table.c
  37. 88
      src/basic/string-table.h
  38. 800
      src/basic/string-util.c
  39. 184
      src/basic/string-util.h
  40. 124
      src/basic/syslog-util.c
  41. 34
      src/basic/syslog-util.h
  42. 48
      src/basic/umask-util.h
  43. 472
      src/basic/user-util.c
  44. 67
      src/basic/user-util.h
  45. 198
      src/basic/xattr-util.c
  46. 38
      src/basic/xattr-util.h

18
.gitignore

@ -297,10 +297,14 @@ stamp-*
# Local Helper Scripts and Tools - Not for distribution
check_tree.sh
cleanup*.sh
elogind.*
get_build_file_diff.sh
pwx_*.*
rebuild_all.sh
patches/
/check_*.*
/cleanup*.sh
/elogind.*
/get_build_file_diff.sh
/pwx_*.*
/rebuild_all.sh
*.orig
*.rej
*.remote
*.bak

9
Makefile-man.am

@ -163,15 +163,6 @@ man/sd_session_is_remote.html: man/sd_session_is_active.html
endif
if HAVE_PYTHON
MANPAGES += \
man/elogind.index.7
MANPAGES_ALIAS += \
#
endif
# Really, do not edit this file.
EXTRA_DIST += \

48
Makefile.am

@ -297,10 +297,11 @@ libbasic_la_SOURCES = \
src/basic/missing.h \
src/basic/musl_missing.h \
src/basic/musl_missing.c \
src/basic/capability.c \
src/basic/capability.h \
src/basic/capability-util.c \
src/basic/capability-util.h \
src/basic/conf-files.c \
src/basic/conf-files.h \
src/basic/stdio-util.h \
src/basic/hostname-util.h \
src/basic/hostname-util.c \
src/basic/unit-name.c \
@ -308,12 +309,47 @@ libbasic_la_SOURCES = \
src/basic/unaligned.h \
src/basic/util.c \
src/basic/util.h \
src/basic/io-util.c \
src/basic/io-util.h \
src/basic/string-util.c \
src/basic/string-util.h \
src/basic/parse-util.c \
src/basic/parse-util.h \
src/basic/fd-util.c \
src/basic/fd-util.h \
src/basic/user-util.c \
src/basic/user-util.h \
src/basic/dirent-util.c \
src/basic/dirent-util.h \
src/basic/xattr-util.c \
src/basic/xattr-util.h \
src/basic/proc-cmdline.c \
src/basic/proc-cmdline.h \
src/basic/fs-util.c \
src/basic/fs-util.h \
src/basic/syslog-util.c \
src/basic/syslog-util.h \
src/basic/stat-util.c \
src/basic/stat-util.h \
src/basic/mount-util.c \
src/basic/mount-util.h \
src/basic/hexdecoct.c \
src/basic/hexdecoct.h \
src/basic/extract-word.c \
src/basic/extract-word.h \
src/basic/escape.c \
src/basic/escape.h \
src/basic/path-util.c \
src/basic/path-util.h \
src/basic/time-util.c \
src/basic/time-util.h \
src/basic/locale-util.c \
src/basic/locale-util.h \
src/basic/umask-util.h \
src/basic/signal-util.c \
src/basic/signal-util.h \
src/basic/string-table.c \
src/basic/string-table.h \
src/basic/mempool.c \
src/basic/mempool.h \
src/basic/hashmap.c \
@ -336,6 +372,8 @@ libbasic_la_SOURCES = \
src/basic/utf8.h \
src/basic/gunicode.c \
src/basic/gunicode.h \
src/basic/socket-util.c \
src/basic/socket-util.h \
src/basic/fileio.c \
src/basic/fileio.h \
src/basic/mkdir.c \
@ -348,8 +386,8 @@ libbasic_la_SOURCES = \
src/basic/terminal-util.h \
src/basic/login-util.h \
src/basic/login-util.c \
src/basic/audit.c \
src/basic/audit.h \
src/basic/audit-util.c \
src/basic/audit-util.h \
src/basic/memfd-util.c \
src/basic/memfd-util.h \
src/basic/process-util.c \
@ -369,6 +407,8 @@ libbasic_la_SOURCES = \
src/basic/rm-rf.h \
src/basic/copy.c \
src/basic/copy.h \
src/basic/alloc-util.h \
src/basic/alloc-util.c \
src/basic/parse-printf-format.c \
src/basic/parse-printf-format.h

81
src/basic/alloc-util.c

@ -0,0 +1,81 @@
/*-*- 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 "alloc-util.h"
#include "util.h"
void* memdup(const void *p, size_t l) {
void *r;
assert(p);
r = malloc(l);
if (!r)
return NULL;
memcpy(r, p, l);
return r;
}
void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size) {
size_t a, newalloc;
void *q;
assert(p);
assert(allocated);
if (*allocated >= need)
return *p;
newalloc = MAX(need * 2, 64u / size);
a = newalloc * size;
/* check for overflows */
if (a < size * need)
return NULL;
q = realloc(*p, a);
if (!q)
return NULL;
*p = q;
*allocated = newalloc;
return q;
}
void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size) {
size_t prev;
uint8_t *q;
assert(p);
assert(allocated);
prev = *allocated;
q = greedy_realloc(p, allocated, need, size);
if (!q)
return NULL;
if (*allocated > prev)
memzero(q + prev * size, (*allocated - prev) * size);
return q;
}

108
src/basic/alloc-util.h

@ -0,0 +1,108 @@
/*-*- 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 <alloca.h>
#include <stdlib.h>
#include <string.h>
#include "macro.h"
#define new(t, n) ((t*) malloc_multiply(sizeof(t), (n)))
#define new0(t, n) ((t*) calloc((n), sizeof(t)))
#define newa(t, n) ((t*) alloca(sizeof(t)*(n)))
#define newa0(t, n) ((t*) alloca0(sizeof(t)*(n)))
#define newdup(t, p, n) ((t*) memdup_multiply(p, sizeof(t), (n)))
#define malloc0(n) (calloc(1, (n)))
static inline void *mfree(void *memory) {
free(memory);
return NULL;
}
void* memdup(const void *p, size_t l) _alloc_(2);
static inline void freep(void *p) {
free(*(void**) p);
}
#define _cleanup_free_ _cleanup_(freep)
_malloc_ _alloc_(1, 2) static inline void *malloc_multiply(size_t a, size_t b) {
if (_unlikely_(b != 0 && a > ((size_t) -1) / b))
return NULL;
return malloc(a * b);
}
_alloc_(2, 3) static inline void *realloc_multiply(void *p, size_t a, size_t b) {
if (_unlikely_(b != 0 && a > ((size_t) -1) / b))
return NULL;
return realloc(p, a * b);
}
_alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t a, size_t b) {
if (_unlikely_(b != 0 && a > ((size_t) -1) / b))
return NULL;
return memdup(p, a * b);
}
void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size);
void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size);
#define GREEDY_REALLOC(array, allocated, need) \
greedy_realloc((void**) &(array), &(allocated), (need), sizeof((array)[0]))
#define GREEDY_REALLOC0(array, allocated, need) \
greedy_realloc0((void**) &(array), &(allocated), (need), sizeof((array)[0]))
#define alloca0(n) \
({ \
char *_new_; \
size_t _len_ = n; \
_new_ = alloca(_len_); \
(void *) memset(_new_, 0, _len_); \
})
/* It's not clear what alignment glibc/gcc alloca() guarantee, hence provide a guaranteed safe version */
#define alloca_align(size, align) \
({ \
void *_ptr_; \
size_t _mask_ = (align) - 1; \
_ptr_ = alloca((size) + _mask_); \
(void*)(((uintptr_t)_ptr_ + _mask_) & ~_mask_); \
})
#define alloca0_align(size, align) \
({ \
void *_new_; \
size_t _size_ = (size); \
_new_ = alloca_align(_size_, (align)); \
(void*)memset(_new_, 0, _size_); \
})

13
src/basic/audit.c → src/basic/audit-util.c

@ -22,11 +22,15 @@
#include <errno.h>
#include <stdio.h>
#include "alloc-util.h"
#include "audit-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "macro.h"
#include "audit.h"
#include "util.h"
#include "parse-util.h"
#include "process-util.h"
#include "fileio.h"
#include "user-util.h"
#include "util.h"
int audit_session_from_pid(pid_t pid, uint32_t *id) {
_cleanup_free_ char *s = NULL;
@ -82,8 +86,6 @@ int audit_loginuid_from_pid(pid_t pid, uid_t *uid) {
return 0;
}
/// UNNEEDED by elogind
#if 0
bool use_audit(void) {
static int cached_use = -1;
@ -101,4 +103,3 @@ bool use_audit(void) {
return cached_use;
}
#endif // 0

2
src/basic/audit.h → src/basic/audit-util.h

@ -30,4 +30,4 @@
int audit_session_from_pid(pid_t pid, uint32_t *id);
int audit_loginuid_from_pid(pid_t pid, uid_t *uid);
// UNNEEDED bool use_audit(void);
bool use_audit(void);

24
src/basic/capability.c → src/basic/capability-util.c

@ -19,21 +19,21 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <unistd.h>
#include <errno.h>
#include <grp.h>
#include <stdio.h>
#include <sys/capability.h>
#include <sys/prctl.h>
#include "grp.h"
#include <unistd.h>
#include "alloc-util.h"
#include "capability-util.h"
#include "fileio.h"
#include "log.h"
#include "macro.h"
#include "parse-util.h"
#include "util.h"
#include "log.h"
#include "fileio.h"
#include "capability.h"
/// UNNEEDED by elogind
#if 0
int have_effective_cap(int value) {
_cleanup_cap_free_ cap_t cap;
cap_flag_value_t fv;
@ -47,7 +47,6 @@ int have_effective_cap(int value) {
else
return fv == CAP_SET;
}
#endif // 0
unsigned long cap_last_cap(void) {
static thread_local unsigned long saved;
@ -96,8 +95,6 @@ unsigned long cap_last_cap(void) {
return p;
}
/// UNNEEDED by elogind
#if 0
int capability_bounding_set_drop(uint64_t drop, bool right_now) {
_cleanup_cap_free_ cap_t after_cap = NULL;
cap_flag_value_t fv;
@ -281,10 +278,8 @@ int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities) {
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;
}
cap_set_flag(d, CAP_PERMITTED, j, bits, CAP_SET) < 0)
return log_error_errno(errno, "Failed to enable capabilities bits: %m");
if (cap_set_proc(d) < 0)
return log_error_errno(errno, "Failed to increase capabilities: %m");
@ -310,4 +305,3 @@ int drop_capability(cap_value_t cv) {
return 0;
}
#endif // 0

10
src/basic/capability.h → src/basic/capability-util.h

@ -27,13 +27,13 @@
#include "util.h"
unsigned long cap_last_cap(void);
// UNNEEDED int have_effective_cap(int value);
// UNNEEDED int capability_bounding_set_drop(uint64_t drop, bool right_now);
// UNNEEDED int capability_bounding_set_drop_usermode(uint64_t drop);
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);
// UNNEEDED int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities);
int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities);
// UNNEEDED int drop_capability(cap_value_t cv);
int drop_capability(cap_value_t cv);
DEFINE_TRIVIAL_CLEANUP_FUNC(cap_t, cap_free);
#define _cleanup_cap_free_ _cleanup_(cap_freep)

81
src/basic/dirent-util.c

@ -0,0 +1,81 @@
/*-*- 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 <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "dirent-util.h"
#include "string-util.h"
int dirent_ensure_type(DIR *d, struct dirent *de) {
struct stat st;
assert(d);
assert(de);
if (de->d_type != DT_UNKNOWN)
return 0;
if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0)
return -errno;
de->d_type =
S_ISREG(st.st_mode) ? DT_REG :
S_ISDIR(st.st_mode) ? DT_DIR :
S_ISLNK(st.st_mode) ? DT_LNK :
S_ISFIFO(st.st_mode) ? DT_FIFO :
S_ISSOCK(st.st_mode) ? DT_SOCK :
S_ISCHR(st.st_mode) ? DT_CHR :
S_ISBLK(st.st_mode) ? DT_BLK :
DT_UNKNOWN;
return 0;
}
bool dirent_is_file(const struct dirent *de) {
assert(de);
if (hidden_file(de->d_name))
return false;
if (de->d_type != DT_REG &&
de->d_type != DT_LNK &&
de->d_type != DT_UNKNOWN)
return false;
return true;
}
bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) {
assert(de);
if (de->d_type != DT_REG &&
de->d_type != DT_LNK &&
de->d_type != DT_UNKNOWN)
return false;
if (hidden_file_allow_backup(de->d_name))
return false;
return endswith(de->d_name, suffix);
}

51
src/basic/dirent-util.h

@ -0,0 +1,51 @@
/*-*- 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 <dirent.h>
#include "path-util.h"
int dirent_ensure_type(DIR *d, struct dirent *de);
bool dirent_is_file(const struct dirent *de) _pure_;
bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) _pure_;
#define FOREACH_DIRENT(de, d, on_error) \
for (errno = 0, de = readdir(d);; errno = 0, de = readdir(d)) \
if (!de) { \
if (errno > 0) { \
on_error; \
} \
break; \
} else if (hidden_file((de)->d_name)) \
continue; \
else
#define FOREACH_DIRENT_ALL(de, d, on_error) \
for (errno = 0, de = readdir(d);; errno = 0, de = readdir(d)) \
if (!de) { \
if (errno > 0) { \
on_error; \
} \
break; \
} else

482
src/basic/escape.c

@ -0,0 +1,482 @@
/*-*- 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 "alloc-util.h"
#include "escape.h"
#include "hexdecoct.h"
#include "string-util.h"
#include "utf8.h"
#include "util.h"
size_t cescape_char(char c, char *buf) {
char * buf_old = buf;
switch (c) {
case '\a':
*(buf++) = '\\';
*(buf++) = 'a';
break;
case '\b':
*(buf++) = '\\';
*(buf++) = 'b';
break;
case '\f':
*(buf++) = '\\';
*(buf++) = 'f';
break;
case '\n':
*(buf++) = '\\';
*(buf++) = 'n';
break;
case '\r':
*(buf++) = '\\';
*(buf++) = 'r';
break;
case '\t':
*(buf++) = '\\';
*(buf++) = 't';
break;
case '\v':
*(buf++) = '\\';
*(buf++) = 'v';
break;
case '\\':
*(buf++) = '\\';
*(buf++) = '\\';
break;
case '"':
*(buf++) = '\\';
*(buf++) = '"';
break;
case '\'':
*(buf++) = '\\';
*(buf++) = '\'';
break;
default:
/* For special chars we prefer octal over
* hexadecimal encoding, simply because glib's
* g_strescape() does the same */
if ((c < ' ') || (c >= 127)) {
*(buf++) = '\\';
*(buf++) = octchar((unsigned char) c >> 6);
*(buf++) = octchar((unsigned char) c >> 3);
*(buf++) = octchar((unsigned char) c);
} else
*(buf++) = c;
break;
}
return buf - buf_old;
}
char *cescape(const char *s) {
char *r, *t;
const char *f;
assert(s);
/* Does C style string escaping. May be reversed with
* cunescape(). */
r = new(char, strlen(s)*4 + 1);
if (!r)
return NULL;
for (f = s, t = r; *f; f++)
t += cescape_char(*f, t);
*t = 0;
return r;
}
int cunescape_one(const char *p, size_t length, char *ret, uint32_t *ret_unicode) {
int r = 1;
assert(p);
assert(*p);
assert(ret);
/* Unescapes C style. Returns the unescaped character in ret,
* unless we encountered a \u sequence in which case the full
* unicode character is returned in ret_unicode, instead. */
if (length != (size_t) -1 && length < 1)
return -EINVAL;
switch (p[0]) {
case 'a':
*ret = '\a';
break;
case 'b':
*ret = '\b';
break;
case 'f':
*ret = '\f';
break;
case 'n':
*ret = '\n';
break;
case 'r':
*ret = '\r';
break;
case 't':
*ret = '\t';
break;
case 'v':
*ret = '\v';
break;
case '\\':
*ret = '\\';
break;
case '"':
*ret = '"';
break;
case '\'':
*ret = '\'';
break;
case 's':
/* This is an extension of the XDG syntax files */
*ret = ' ';
break;
case 'x': {
/* hexadecimal encoding */
int a, b;
if (length != (size_t) -1 && length < 3)
return -EINVAL;
a = unhexchar(p[1]);
if (a < 0)
return -EINVAL;
b = unhexchar(p[2]);
if (b < 0)
return -EINVAL;
/* Don't allow NUL bytes */
if (a == 0 && b == 0)
return -EINVAL;
*ret = (char) ((a << 4U) | b);
r = 3;
break;
}
case 'u': {
/* C++11 style 16bit unicode */
int a[4];
unsigned i;
uint32_t c;
if (length != (size_t) -1 && length < 5)
return -EINVAL;
for (i = 0; i < 4; i++) {
a[i] = unhexchar(p[1 + i]);
if (a[i] < 0)
return a[i];
}
c = ((uint32_t) a[0] << 12U) | ((uint32_t) a[1] << 8U) | ((uint32_t) a[2] << 4U) | (uint32_t) a[3];
/* Don't allow 0 chars */
if (c == 0)
return -EINVAL;
if (c < 128)
*ret = c;
else {
if (!ret_unicode)
return -EINVAL;
*ret = 0;
*ret_unicode = c;
}
r = 5;
break;
}
case 'U': {
/* C++11 style 32bit unicode */
int a[8];
unsigned i;
uint32_t c;
if (length != (size_t) -1 && length < 9)
return -EINVAL;
for (i = 0; i < 8; i++) {
a[i] = unhexchar(p[1 + i]);
if (a[i] < 0)
return a[i];
}
c = ((uint32_t) a[0] << 28U) | ((uint32_t) a[1] << 24U) | ((uint32_t) a[2] << 20U) | ((uint32_t) a[3] << 16U) |
((uint32_t) a[4] << 12U) | ((uint32_t) a[5] << 8U) | ((uint32_t) a[6] << 4U) | (uint32_t) a[7];
/* Don't allow 0 chars */
if (c == 0)
return -EINVAL;
/* Don't allow invalid code points */
if (!unichar_is_valid(c))
return -EINVAL;
if (c < 128)
*ret = c;
else {
if (!ret_unicode)
return -EINVAL;
*ret = 0;
*ret_unicode = c;
}
r = 9;
break;
}
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7': {
/* octal encoding */
int a, b, c;
uint32_t m;
if (length != (size_t) -1 && length < 3)
return -EINVAL;
a = unoctchar(p[0]);
if (a < 0)
return -EINVAL;
b = unoctchar(p[1]);
if (b < 0)
return -EINVAL;
c = unoctchar(p[2]);
if (c < 0)
return -EINVAL;
/* don't allow NUL bytes */
if (a == 0 && b == 0 && c == 0)
return -EINVAL;
/* Don't allow bytes above 255 */
m = ((uint32_t) a << 6U) | ((uint32_t) b << 3U) | (uint32_t) c;
if (m > 255)
return -EINVAL;
*ret = m;
r = 3;
break;
}
default:
return -EINVAL;
}
return r;
}
int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret) {
char *r, *t;
const char *f;
size_t pl;
assert(s);
assert(ret);
/* Undoes C style string escaping, and optionally prefixes it. */
pl = prefix ? strlen(prefix) : 0;
r = new(char, pl+length+1);
if (!r)
return -ENOMEM;
if (prefix)
memcpy(r, prefix, pl);
for (f = s, t = r + pl; f < s + length; f++) {
size_t remaining;
uint32_t u;
char c;
int k;
remaining = s + length - f;
assert(remaining > 0);
if (*f != '\\') {
/* A literal literal, copy verbatim */
*(t++) = *f;
continue;
}
if (remaining == 1) {
if (flags & UNESCAPE_RELAX) {
/* A trailing backslash, copy verbatim */
*(t++) = *f;
continue;
}
free(r);
return -EINVAL;
}
k = cunescape_one(f + 1, remaining - 1, &c, &u);
if (k < 0) {
if (flags & UNESCAPE_RELAX) {
/* Invalid escape code, let's take it literal then */
*(t++) = '\\';
continue;
}
free(r);
return k;
}
if (c != 0)
/* Non-Unicode? Let's encode this directly */
*(t++) = c;
else
/* Unicode? Then let's encode this in UTF-8 */
t += utf8_encode_unichar(t, u);
f += k;
}
*t = 0;
*ret = r;
return t - r;
}
int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret) {
return cunescape_length_with_prefix(s, length, NULL, flags, ret);
}
int cunescape(const char *s, UnescapeFlags flags, char **ret) {
return cunescape_length(s, strlen(s), flags, ret);
}
char *xescape(const char *s, const char *bad) {
char *r, *t;
const char *f;
/* Escapes all chars in bad, in addition to \ and all special
* chars, in \xFF style escaping. May be reversed with
* cunescape(). */
r = new(char, strlen(s) * 4 + 1);
if (!r)
return NULL;
for (f = s, t = r; *f; f++) {
if ((*f < ' ') || (*f >= 127) ||
(*f == '\\') || strchr(bad, *f)) {
*(t++) = '\\';
*(t++) = 'x';
*(t++) = hexchar(*f >> 4);
*(t++) = hexchar(*f);
} else
*(t++) = *f;
}
*t = 0;
return r;
}
static char *strcpy_backslash_escaped(char *t, const char *s, const char *bad) {
assert(bad);
for (; *s; s++) {
if (*s == '\\' || strchr(bad, *s))
*(t++) = '\\';
*(t++) = *s;
}
return t;
}
char *shell_escape(const char *s, const char *bad) {
char *r, *t;
r = new(char, strlen(s)*2+1);
if (!r)
return NULL;
t = strcpy_backslash_escaped(r, s, bad);
*t = 0;
return r;
}
char *shell_maybe_quote(const char *s) {
const char *p;
char *r, *t;
assert(s);
/* Encloses a string in double quotes if necessary to make it
* OK as shell string. */
for (p = s; *p; p++)
if (*p <= ' ' ||
*p >= 127 ||
strchr(SHELL_NEED_QUOTES, *p))
break;
if (!*p)
return strdup(s);
r = new(char, 1+strlen(s)*2+1+1);
if (!r)
return NULL;
t = r;
*(t++) = '"';
t = mempcpy(t, s, p - s);
t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE);
*(t++)= '"';
*t = 0;
return r;
}

48
src/basic/escape.h

@ -0,0 +1,48 @@
/*-*- 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 <inttypes.h>
/* What characters are special in the shell? */
/* must be escaped outside and inside double-quotes */
#define SHELL_NEED_ESCAPE "\"\\`$"
/* can be escaped or double-quoted */
#define SHELL_NEED_QUOTES SHELL_NEED_ESCAPE GLOB_CHARS "'()<>|&;"
typedef enum UnescapeFlags {
UNESCAPE_RELAX = 1,
} UnescapeFlags;
char *cescape(const char *s);
size_t cescape_char(char c, char *buf);
int cunescape(const char *s, UnescapeFlags flags, char **ret);
int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret);
int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret);
int cunescape_one(const char *p, size_t length, char *ret, uint32_t *ret_unicode);
char *xescape(const char *s, const char *bad);
char *shell_escape(const char *s, const char *bad);
char *shell_maybe_quote(const char *s);

292
src/basic/extract-word.c

@ -0,0 +1,292 @@
/*-*- 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 "alloc-util.h"
#include "escape.h"
#include "extract-word.h"
#include "string-util.h"
#include "utf8.h"
#include "util.h"
int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags) {
_cleanup_free_ char *s = NULL;
size_t allocated = 0, sz = 0;
char c;
int r;
char quote = 0; /* 0 or ' or " */
bool backslash = false; /* whether we've just seen a backslash */
assert(p);
assert(ret);
/* Bail early if called after last value or with no input */
if (!*p)
goto finish_force_terminate;
c = **p;
if (!separators)
separators = WHITESPACE;
/* Parses the first word of a string, and returns it in
* *ret. Removes all quotes in the process. When parsing fails
* (because of an uneven number of quotes or similar), leaves
* the pointer *p at the first invalid character. */
if (flags & EXTRACT_DONT_COALESCE_SEPARATORS)
if (!GREEDY_REALLOC(s, allocated, sz+1))
return -ENOMEM;
for (;; (*p) ++, c = **p) {
if (c == 0)
goto finish_force_terminate;
else if (strchr(separators, c)) {
if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) {
(*p) ++;
goto finish_force_next;
}
} else {
/* We found a non-blank character, so we will always
* want to return a string (even if it is empty),
* allocate it here. */
if (!GREEDY_REALLOC(s, allocated, sz+1))
return -ENOMEM;
break;
}
}
for (;; (*p) ++, c = **p) {
if (backslash) {
if (!GREEDY_REALLOC(s, allocated, sz+7))
return -ENOMEM;
if (c == 0) {
if ((flags & EXTRACT_CUNESCAPE_RELAX) &&
(!quote || flags & EXTRACT_RELAX)) {
/* If we find an unquoted trailing backslash and we're in
* EXTRACT_CUNESCAPE_RELAX mode, keep it verbatim in the
* output.
*
* Unbalanced quotes will only be allowed in EXTRACT_RELAX
* mode, EXTRACT_CUNESCAPE_RELAX mode does not allow them.
*/
s[sz++] = '\\';
goto finish_force_terminate;
}
if (flags & EXTRACT_RELAX)
goto finish_force_terminate;
return -EINVAL;
}
if (flags & EXTRACT_CUNESCAPE) {
uint32_t u;
r = cunescape_one(*p, (size_t) -1, &c, &u);
if (r < 0) {
if (flags & EXTRACT_CUNESCAPE_RELAX) {
s[sz++] = '\\';
s[sz++] = c;
} else
return -EINVAL;
} else {
(*p) += r - 1;
if (c != 0)
s[sz++] = c; /* normal explicit char */
else
sz += utf8_encode_unichar(s + sz, u); /* unicode chars we'll encode as utf8 */
}
} else
s[sz++] = c;
backslash = false;
} else if (quote) { /* inside either single or double quotes */
for (;; (*p) ++, c = **p) {
if (c == 0) {
if (flags & EXTRACT_RELAX)
goto finish_force_terminate;
return -EINVAL;
} else if (c == quote) { /* found the end quote */
quote = 0;
break;
} else if (c == '\\' && !(flags & EXTRACT_RETAIN_ESCAPE)) {
backslash = true;
break;
} else {
if (!GREEDY_REALLOC(s, allocated, sz+2))
return -ENOMEM;
s[sz++] = c;
}
}
} else {
for (;; (*p) ++, c = **p) {
if (c == 0)
goto finish_force_terminate;
else if ((c == '\'' || c == '"') && (flags & EXTRACT_QUOTES)) {
quote = c;
break;
} else if (c == '\\' && !(flags & EXTRACT_RETAIN_ESCAPE)) {
backslash = true;
break;
} else if (strchr(separators, c)) {
if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) {
(*p) ++;
goto finish_force_next;
}
/* Skip additional coalesced separators. */
for (;; (*p) ++, c = **p) {
if (c == 0)
goto finish_force_terminate;
if (!strchr(separators, c))
break;
}
goto finish;
} else {
if (!GREEDY_REALLOC(s, allocated, sz+2))
return -ENOMEM;
s[sz++] = c;
}
}
}
}
finish_force_terminate:
*p = NULL;
finish:
if (!s) {
*p = NULL;
*ret = NULL;
return 0;
}
finish_force_next:
s[sz] = 0;
*ret = s;
s = NULL;
return 1;
}
/// UNNEEDED by elogind
#if 0
int extract_first_word_and_warn(
const char **p,
char **ret,
const char *separators,
ExtractFlags flags,
const char *unit,
const char *filename,
unsigned line,
const char *rvalue) {
/* Try to unquote it, if it fails, warn about it and try again
* but this time using EXTRACT_CUNESCAPE_RELAX to keep the
* backslashes verbatim in invalid escape sequences. */
const char *save;
int r;
save = *p;
r = extract_first_word(p, ret, separators, flags);
if (r >= 0)
return r;
if (r == -EINVAL && !(flags & EXTRACT_CUNESCAPE_RELAX)) {
/* Retry it with EXTRACT_CUNESCAPE_RELAX. */
*p = save;
r = extract_first_word(p, ret, separators, flags|EXTRACT_CUNESCAPE_RELAX);
if (r >= 0) {
/* It worked this time, hence it must have been an invalid escape sequence we could correct. */
log_syntax(unit, LOG_WARNING, filename, line, EINVAL, "Invalid escape sequences in line, correcting: \"%s\"", rvalue);
return r;
}
/* If it's still EINVAL; then it must be unbalanced quoting, report this. */
if (r == -EINVAL)
return log_syntax(unit, LOG_ERR, filename, line, r, "Unbalanced quoting, ignoring: \"%s\"", rvalue);
}
/* Can be any error, report it */
return log_syntax(unit, LOG_ERR, filename, line, r, "Unable to decode word \"%s\", ignoring: %m", rvalue);
}
int extract_many_words(const char **p, const char *separators, ExtractFlags flags, ...) {
va_list ap;
char **l;
int n = 0, i, c, r;
/* Parses a number of words from a string, stripping any
* quotes if necessary. */
assert(p);
/* Count how many words are expected */
va_start(ap, flags);
for (;;) {
if (!va_arg(ap, char **))
break;
n++;
}
va_end(ap);
if (n <= 0)
return 0;
/* Read all words into a temporary array */
l = newa0(char*, n);
for (c = 0; c < n; c++) {
r = extract_first_word(p, &l[c], separators, flags);
if (r < 0) {
int j;
for (j = 0; j < c; j++)
free(l[j]);
return r;
}
if (r == 0)
break;
}
/* If we managed to parse all words, return them in the passed
* in parameters */
va_start(ap, flags);
for (i = 0; i < n; i++) {
char **v;
v = va_arg(ap, char **);
assert(v);
*v = l[i];
}
va_end(ap);
return c;
}
#endif // 0

37
src/basic/extract-word.h

@ -0,0 +1,37 @@
/*-*- 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 "macro.h"
typedef enum ExtractFlags {
EXTRACT_RELAX = 1,
EXTRACT_CUNESCAPE = 2,
EXTRACT_CUNESCAPE_RELAX = 4,
EXTRACT_QUOTES = 8,
EXTRACT_DONT_COALESCE_SEPARATORS = 16,
EXTRACT_RETAIN_ESCAPE = 32,
} ExtractFlags;
int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags);
// UNNEEDED int extract_first_word_and_warn(const char **p, char **ret, const char *separators, ExtractFlags flags, const char *unit, const char *filename, unsigned line, const char *rvalue);
// UNNEEDED int extract_many_words(const char **p, const char *separators, ExtractFlags flags, ...) _sentinel_;

351
src/basic/fd-util.c

@ -0,0 +1,351 @@
/*-*- 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 "dirent-util.h"
#include "fd-util.h"
#include "parse-util.h"
#include "socket-util.h"
#include "util.h"
int close_nointr(int fd) {
assert(fd >= 0);
if (close(fd) >= 0)
return 0;
/*
* Just ignore EINTR; a retry loop is the wrong thing to do on
* Linux.
*
* http://lkml.indiana.edu/hypermail/linux/kernel/0509.1/0877.html
* https://bugzilla.gnome.org/show_bug.cgi?id=682819
* http://utcc.utoronto.ca/~cks/space/blog/unix/CloseEINTR
* https://sites.google.com/site/michaelsafyan/software-engineering/checkforeintrwheninvokingclosethinkagain
*/
if (errno == EINTR)
return 0;
return -errno;
}
int safe_close(int fd) {
/*
* Like close_nointr() but cannot fail. Guarantees errno is
* unchanged. Is a NOP with negative fds passed, and returns
* -1, so that it can be used in this syntax:
*
* fd = safe_close(fd);
*/
if (fd >= 0) {
PROTECT_ERRNO;
/* The kernel might return pretty much any error code
* via close(), but the fd will be closed anyway. The
* only condition we want to check for here is whether
* the fd was invalid at all... */
assert_se(close_nointr(fd) != -EBADF);
}
return -1;
}
void safe_close_pair(int p[]) {
assert(p);
if (p[0] == p[1]) {
/* Special case pairs which use the same fd in both
* directions... */
p[0] = p[1] = safe_close(p[0]);
return;
}
p[0] = safe_close(p[0]);
p[1] = safe_close(p[1]);
}
void close_many(const int fds[], unsigned n_fd) {
unsigned i;
assert(fds || n_fd <= 0)