#include <stdarg.h>
#include <sys/inotify.h>
#include <sys/poll.h>
-#include <libgen.h>
#include <ctype.h>
#include <sys/prctl.h>
#include <sys/utsname.h>
#include <langinfo.h>
#include <locale.h>
#include <libgen.h>
+#undef basename
+
+#ifdef HAVE_SYS_AUXV_H
+#include <sys/auxv.h>
+#endif
#include "macro.h"
#include "util.h"
#include "hashmap.h"
#include "env-util.h"
#include "fileio.h"
+#include "device-nodes.h"
+#include "utf8.h"
+#include "gunicode.h"
+#include "virt.h"
+#include "def.h"
int saved_argc = 0;
char **saved_argv = NULL;
static volatile unsigned cached_columns = 0;
static volatile unsigned cached_lines = 0;
-#define procfs_file_alloca(pid, field) \
- ({ \
- pid_t _pid_ = (pid); \
- char *_r_; \
- _r_ = alloca(sizeof("/proc/") -1 + DECIMAL_STR_MAX(pid_t) + 1 + sizeof(field)); \
- sprintf(_r_, "/proc/%lu/" field, (unsigned long) _pid_); \
- _r_; \
- })
-
size_t page_size(void) {
- static __thread size_t pgsz = 0;
+ static thread_local size_t pgsz = 0;
long r;
if (_likely_(pgsz > 0))
return (char*) s + sl - pl;
}
-char* startswith(const char *s, const char *prefix) {
- const char *a, *b;
-
- assert(s);
- assert(prefix);
-
- a = s, b = prefix;
- for (;;) {
- if (*b == 0)
- return (char*) a;
- if (*a != *b)
- return NULL;
-
- a++, b++;
- }
-}
-
-char* startswith_no_case(const char *s, const char *prefix) {
- const char *a, *b;
-
- assert(s);
- assert(prefix);
-
- a = s, b = prefix;
- for (;;) {
- if (*b == 0)
- return (char*) a;
- if (tolower(*a) != tolower(*b))
- return NULL;
-
- a++, b++;
- }
-}
-
bool first_word(const char *s, const char *word) {
size_t sl, wl;
int safe_atod(const char *s, double *ret_d) {
char *x = NULL;
- double d;
+ double d = 0;
assert(s);
assert(ret_d);
- errno = 0;
- d = strtod(s, &x);
+ RUN_WITH_LOCALE(LC_NUMERIC_MASK, "C") {
+ errno = 0;
+ d = strtod(s, &x);
+ }
if (!x || x == s || *x || errno)
return errno ? -errno : -EINVAL;
/* Split a string into words, but consider strings enclosed in '' and
* "" as words even if they include spaces. */
char *split_quoted(const char *c, size_t *l, char **state) {
- char *current, *e;
+ const char *current, *e;
bool escaped = false;
- current = *state ? *state : (char*) c;
+ assert(c);
+ assert(l);
+ assert(state);
- if (!*current || *c == 0)
- return NULL;
+ current = *state ? *state : c;
current += strspn(current, WHITESPACE);
- if (*current == '\'') {
+ if (*current == 0)
+ return NULL;
+
+ else if (*current == '\'') {
current ++;
for (e = current; *e; e++) {
}
*l = e-current;
- *state = *e == 0 ? e : e+1;
+ *state = (char*) (*e == 0 ? e : e+1);
+
} else if (*current == '\"') {
current ++;
}
*l = e-current;
- *state = *e == 0 ? e : e+1;
+ *state = (char*) (*e == 0 ? e : e+1);
+
} else {
for (e = current; *e; e++) {
if (escaped)
break;
}
*l = e-current;
- *state = e;
+ *state = (char*) e;
}
return (char*) current;
f = fopen(p, "re");
if (!f)
- return -errno;
+ return errno == ENOENT ? -ESRCH : -errno;
if (!fgets(line, sizeof(line), f)) {
if (ferror(f))
int get_process_comm(pid_t pid, char **name) {
const char *p;
+ int r;
assert(name);
assert(pid >= 0);
else
p = procfs_file_alloca(pid, "comm");
- return read_one_line_file(p, name);
+ 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) {
/* Kernel threads have no argv[] */
if (r == NULL || r[0] == 0) {
- char *t;
+ _cleanup_free_ char *t = NULL;
int h;
free(r);
return h;
r = strjoin("[", t, "]", NULL);
- free(t);
-
if (!r)
return -ENOMEM;
}
return 0;
}
+int get_process_capeff(pid_t pid, char **capeff) {
+ const char *p;
+
+ assert(capeff);
+ assert(pid >= 0);
+
+ if (pid == 0)
+ p = "/proc/self/status";
+ else
+ p = procfs_file_alloca(pid, "status");
+
+ return get_status_field(p, "\nCapEff:", capeff);
+}
int get_process_exe(pid_t pid, char **name) {
const char *p;
+ char *d;
+ int r;
assert(pid >= 0);
assert(name);
else
p = procfs_file_alloca(pid, "exe");
- return readlink_malloc(p, name);
+ r = readlink_malloc(p, name);
+ if (r < 0)
+ return r == -ENOENT ? -ESRCH : 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) {
}
int readlink_and_make_absolute(const char *p, char **r) {
- char *target, *k;
+ _cleanup_free_ char *target = NULL;
+ char *k;
int j;
assert(p);
assert(r);
- if ((j = readlink_malloc(p, &target)) < 0)
+ j = readlink_malloc(p, &target);
+ if (j < 0)
return j;
k = file_in_same_dir(p, target);
- free(target);
-
if (!k)
return -ENOMEM;
return r;
}
-char *bus_path_escape(const char *s) {
- char *r, *t;
- const char *f;
-
- assert(s);
-
- /* Escapes all chars that D-Bus' object path cannot deal
- * with. Can be reverse 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_path_unescape(const char *f) {
- char *r, *t;
-
- assert(f);
-
- /* Special case for the empty string */
- if (streq(f, "_"))
- return strdup("");
-
- r = new(char, strlen(f) + 1);
- if (!r)
- return NULL;
-
- for (t = r; *f; f++) {
-
- if (*f == '_') {
- int a, b;
-
- if ((a = unhexchar(f[1])) < 0 ||
- (b = unhexchar(f[2])) < 0) {
- /* Invalid escape code, let's take it literal then */
- *(t++) = '_';
- } else {
- *(t++) = (char) ((a << 4) | b);
- f += 2;
- }
- } else
- *(t++) = *f;
- }
-
- *t = 0;
-
- return r;
-}
-
char *ascii_strlower(char *t) {
char *p;
return t;
}
-static bool ignore_file_allow_backup(const char *filename) {
+_pure_ static bool ignore_file_allow_backup(const char *filename) {
assert(filename);
return
return 0;
}
-static bool fd_in_set(int fd, const int fdset[], unsigned n_fdset) {
+_pure_ static bool fd_in_set(int fd, const int fdset[], unsigned n_fdset) {
unsigned i;
assert(n_fdset == 0 || fdset);
"cifs\0"
"smbfs\0"
"ncpfs\0"
+ "ncp\0"
"nfs\0"
"nfs4\0"
"gfs\0"
* https://bugs.launchpad.net/ubuntu/+source/linux/+bug/554172/comments/245
*/
+ assert(!(mode & O_CREAT));
+
for (;;) {
- fd = open(name, mode);
+ fd = open(name, mode, 0);
if (fd >= 0)
break;
return n > 0 ? n : -errno;
}
- if (pollfd.revents != POLLIN)
- return n > 0 ? n : -EIO;
+ /* We knowingly ignore the revents value here,
+ * and expect that any error/EOF is reported
+ * via read()/write()
+ */
continue;
}
return n > 0 ? n : -errno;
}
- if (pollfd.revents != POLLOUT)
- return n > 0 ? n : -EIO;
+ /* We knowingly ignore the revents value here,
+ * and expect that any error/EOF is reported
+ * via read()/write()
+ */
continue;
}
int parse_bytes(const char *t, off_t *bytes) {
static const struct {
const char *suffix;
- off_t factor;
+ unsigned long long factor;
} table[] = {
{ "B", 1 },
{ "K", 1024ULL },
};
const char *p;
- off_t r = 0;
+ unsigned long long r = 0;
assert(t);
assert(bytes);
for (i = 0; i < ELEMENTSOF(table); i++)
if (startswith(e, table[i].suffix)) {
- r += (off_t) l * table[i].factor;
+ unsigned long long tmp;
+ if ((unsigned long long) l > ULLONG_MAX / table[i].factor)
+ return -ERANGE;
+ tmp = l * table[i].factor;
+ if (tmp > ULLONG_MAX - r)
+ return -ERANGE;
+
+ r += tmp;
+ if ((unsigned long long) (off_t) r != r)
+ return -ERANGE;
+
p = e + strlen(table[i].suffix);
break;
}
if (i >= ELEMENTSOF(table))
return -EINVAL;
- } while (*p != 0);
+ } while (*p);
*bytes = r;
int dir_is_empty(const char *path) {
_cleanup_closedir_ DIR *d;
- int r;
d = opendir(path);
if (!d)
for (;;) {
struct dirent *de;
- union dirent_storage buf;
- r = readdir_r(d, &buf.de, &de);
- if (r > 0)
- return -r;
+ errno = 0;
+ de = readdir(d);
+ if (!de && errno != 0)
+ return -errno;
if (!de)
return 1;
return dir;
}
-unsigned long long random_ull(void) {
+void random_bytes(void *p, size_t n) {
+ static bool srand_called = false;
_cleanup_close_ int fd;
- uint64_t ull;
- ssize_t r;
+ ssize_t k;
+ uint8_t *q;
fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0)
goto fallback;
- r = loop_read(fd, &ull, sizeof(ull), true);
- if (r != sizeof(ull))
+ k = loop_read(fd, p, n, true);
+ if (k < 0 || (size_t) k != n)
goto fallback;
- return ull;
+ return;
fallback:
- return random() * RAND_MAX + random();
+
+ if (!srand_called) {
+
+#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... */
+
+ void *auxv;
+
+ auxv = (void*) getauxval(AT_RANDOM);
+ if (auxv)
+ srand(*(unsigned*) auxv);
+ else
+#endif
+ srand(time(NULL) + gettid());
+
+ srand_called = true;
+ }
+
+ /* If some idiot made /dev/urandom unavailable to us, he'll
+ * get a PRNG instead. */
+ for (q = p; q < (uint8_t*) p + n; q ++)
+ *q = rand();
}
void rename_process(const char name[8]) {
assert(r);
k = ttyname_r(fd, path, sizeof(path));
- if (k != 0)
+ if (k > 0)
return -k;
char_array_0(path);
char line[LINE_MAX], *p;
unsigned long ttynr;
const char *fn;
- int k;
assert(pid >= 0);
- assert(d);
if (pid == 0)
fn = "/proc/self/stat";
if (!f)
return -errno;
- if (!fgets(line, sizeof(line), f)) {
- k = feof(f) ? -EIO : -errno;
- return k;
- }
+ if (!fgets(line, sizeof(line), f))
+ return feof(f) ? -EIO : -errno;
p = strrchr(line, ')');
if (!p)
if (major(ttynr) == 0 && minor(ttynr) == 0)
return -ENOENT;
- *d = (dev_t) ttynr;
+ if (d)
+ *d = (dev_t) ttynr;
+
return 0;
}
for (;;) {
struct dirent *de;
- union dirent_storage buf;
bool is_dir, keep_around;
struct stat st;
int r;
- r = readdir_r(d, &buf.de, &de);
- if (r != 0 && ret == 0) {
- ret = -r;
+ errno = 0;
+ de = readdir(d);
+ if (!de && errno != 0) {
+ if (ret == 0)
+ ret = -errno;
break;
}
return ret;
}
-static int is_temporary_fs(struct statfs *s) {
+_pure_ static int is_temporary_fs(struct statfs *s) {
assert(s);
- return
- F_TYPE_CMP(s->f_type, TMPFS_MAGIC) ||
- F_TYPE_CMP(s->f_type, RAMFS_MAGIC);
+
+ return F_TYPE_EQUAL(s->f_type, TMPFS_MAGIC) ||
+ F_TYPE_EQUAL(s->f_type, RAMFS_MAGIC);
}
int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev) {
* first change the access mode and only then hand out
* ownership to avoid a window where access is too open. */
- if (fchmod(fd, mode) < 0)
- return -errno;
+ if (mode != (mode_t) -1)
+ if (fchmod(fd, mode) < 0)
+ return -errno;
- if (fchown(fd, uid, gid) < 0)
- return -errno;
+ if (uid != (uid_t) -1 || gid != (gid_t) -1)
+ if (fchown(fd, uid, gid) < 0)
+ return -errno;
return 0;
}
}
int status_welcome(void) {
- int r;
_cleanup_free_ char *pretty_name = NULL, *ansi_color = NULL;
+ int r;
r = parse_env_file("/etc/os-release", NEWLINE,
"PRETTY_NAME", &pretty_name,
"ANSI_COLOR", &ansi_color,
NULL);
+
if (r < 0 && r != -ENOENT)
log_warning("Failed to read /etc/os-release: %s", strerror(-r));
a.st_ino != b.st_ino;
}
-char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
+static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
size_t x;
char *r;
r = new0(char, new_length+1);
if (!r)
- return r;
+ return NULL;
x = (new_length * percent) / 100;
return r;
}
+char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
+ size_t x;
+ char *e;
+ const char *i, *j;
+ unsigned k, len, len2;
+
+ assert(s);
+ assert(percent <= 100);
+ assert(new_length >= 3);
+
+ /* if no multibyte characters use ascii_ellipsize_mem for speed */
+ if (ascii_is_valid(s))
+ return ascii_ellipsize_mem(s, old_length, new_length, percent);
+
+ if (old_length <= 3 || old_length <= new_length)
+ return strndup(s, old_length);
+
+ x = (new_length * percent) / 100;
+
+ if (x > new_length - 3)
+ x = new_length - 3;
+
+ k = 0;
+ for (i = s; k < x && i < s + old_length; i = utf8_next_char(i)) {
+ int c;
+
+ c = utf8_encoded_to_unichar(i);
+ if (c < 0)
+ return NULL;
+ k += unichar_iswide(c) ? 2 : 1;
+ }
+
+ if (k > x) /* last character was wide and went over quota */
+ x ++;
+
+ for (j = s + old_length; k < new_length && j > i; ) {
+ int c;
+
+ j = utf8_prev_char(j);
+ c = utf8_encoded_to_unichar(j);
+ if (c < 0)
+ return NULL;
+ k += unichar_iswide(c) ? 2 : 1;
+ }
+ assert(i <= j);
+
+ /* we don't actually need to ellipsize */
+ if (i == j)
+ return memdup(s, old_length + 1);
+
+ /* make space for ellipsis */
+ j = utf8_next_char(j);
+
+ len = i - s;
+ len2 = s + old_length - j;
+ e = new(char, len + 3 + len2 + 1);
+ if (!e)
+ return NULL;
+
+ /*
+ printf("old_length=%zu new_length=%zu x=%zu len=%u len2=%u k=%u\n",
+ old_length, new_length, x, len, len2, k);
+ */
+
+ memcpy(e, s, len);
+ e[len] = 0xe2; /* tri-dot ellipsis: … */
+ e[len + 1] = 0x80;
+ e[len + 2] = 0xa6;
+
+ memcpy(e + len + 3, j, len2 + 1);
+
+ return e;
+}
+
char *ellipsize(const char *s, size_t length, unsigned percent) {
return ellipsize_mem(s, strlen(s), length, percent);
}
return -EPROTO;
}
-_noreturn_ void freeze(void) {
+noreturn void freeze(void) {
/* Make sure nobody waits for us on a socket anymore */
close_all_fds(NULL, 0);
int nfd;
DIR *d;
- nfd = openat(fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|flags);
+ assert(!(flags & O_CREAT));
+
+ nfd = openat(fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|flags, 0);
if (nfd < 0)
return NULL;
}
static char *tag_to_udev_node(const char *tagvalue, const char *by) {
- char *dn, *t, *u;
- int r;
-
- /* FIXME: to follow udev's logic 100% we need to leave valid
- * UTF8 chars unescaped */
+ _cleanup_free_ char *t = NULL, *u = NULL;
+ char *dn;
+ size_t enc_len;
u = unquote(tagvalue, "\"\'");
if (u == NULL)
return NULL;
- t = xescape(u, "/ ");
- free(u);
-
+ enc_len = strlen(u) * 4 + 1;
+ t = new(char, enc_len);
if (t == NULL)
return NULL;
- r = asprintf(&dn, "/dev/disk/by-%s/%s", by, t);
- free(t);
+ if (encode_devnode_name(u, t, enc_len) < 0)
+ return NULL;
- if (r < 0)
+ if (asprintf(&dn, "/dev/disk/by-%s/%s", by, t) < 0)
return NULL;
return dn;
else
tty = *active;
+ if (streq(tty, "tty0")) {
+ char *tmp;
+
+ /* Get the active VC (e.g. tty1) */
+ if (read_one_line_file("/sys/class/tty/tty0/active", &tmp) >= 0) {
+ free(*active);
+ tty = *active = tmp;
+ }
+ }
+
return tty;
}
bool tty_is_vc_resolve(const char *tty) {
- char *active = NULL;
- bool b;
+ _cleanup_free_ char *active = NULL;
assert(tty);
return false;
}
- b = tty_is_vc(tty);
- free(active);
-
- return b;
+ return tty_is_vc(tty);
}
const char *default_term_for_tty(const char *tty) {
return true;
}
-char* hostname_cleanup(char *s) {
+char* hostname_cleanup(char *s, bool lowercase) {
char *p, *d;
bool dot;
for (p = s, d = s, dot = true; *p; p++) {
if (*p == '.') {
- if (dot || p[1] == 0)
+ if (dot)
continue;
+ *(d++) = '.';
dot = true;
- } else
+ } else if (hostname_valid_char(*p)) {
+ *(d++) = lowercase ? tolower(*p) : *p;
dot = false;
+ }
- if (hostname_valid_char(*p))
- *(d++) = *p;
}
- *d = 0;
+ if (dot && d > s)
+ d[-1] = 0;
+ else
+ *d = 0;
+
strshorten(s, HOST_NAME_MAX);
return s;
if (!t)
return -ENOMEM;
- fn = path_get_file_name(path);
- k = fn-path;
+ fn = basename(path);
+ k = fn - path;
memcpy(t, path, k);
t[k] = '.';
stpcpy(stpcpy(t+k+1, fn), "XXXXXX");
return 0;
}
-int copy_file(const char *from, const char *to) {
- int r, fdf, fdt;
+int copy_file(const char *from, const char *to, int flags) {
+ _cleanup_close_ int fdf = -1;
+ int r, fdt;
assert(from);
assert(to);
if (fdf < 0)
return -errno;
- fdt = open(to, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOCTTY, 0644);
- if (fdt < 0) {
- close_nointr_nofail(fdf);
+ fdt = open(to, flags|O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, 0644);
+ if (fdt < 0)
return -errno;
- }
for (;;) {
char buf[PIPE_BUF];
if (n < 0) {
r = -errno;
- close_nointr_nofail(fdf);
close_nointr(fdt);
unlink(to);
if (n != k) {
r = k < 0 ? k : (errno ? -errno : -EIO);
- close_nointr_nofail(fdf);
close_nointr(fdt);
-
unlink(to);
+
return r;
}
}
- close_nointr_nofail(fdf);
r = close_nointr(fdt);
if (r < 0) {
_cleanup_free_ char *t;
const char *fn;
size_t k;
- unsigned long long ull;
+ uint64_t u;
unsigned i;
int r;
if (!t)
return -ENOMEM;
- fn = path_get_file_name(to);
+ fn = basename(to);
k = fn-to;
memcpy(t, to, k);
t[k] = '.';
x = stpcpy(t+k+1, fn);
- ull = random_ull();
+ u = random_u64();
for (i = 0; i < 16; i++) {
- *(x++) = hexchar(ull & 0xF);
- ull >>= 4;
+ *(x++) = hexchar(u & 0xF);
+ u >>= 4;
}
*x = 0;
int glob_exists(const char *path) {
_cleanup_globfree_ glob_t g = {};
- int r, k;
+ int k;
assert(path);
k = glob(path, GLOB_NOSORT|GLOB_BRACE, NULL, &g);
if (k == GLOB_NOMATCH)
- r = 0;
+ return 0;
else if (k == GLOB_NOSPACE)
- r = -ENOMEM;
+ return -ENOMEM;
else if (k == 0)
- r = !strv_isempty(g.gl_pathv);
+ return !strv_isempty(g.gl_pathv);
else
- r = errno ? -errno : -EIO;
+ return errno ? -errno : -EIO;
+}
- return r;
+int glob_extend(char ***strv, const char *path) {
+ _cleanup_globfree_ glob_t g = {};
+ int k;
+ char **p;
+
+ errno = 0;
+ k = glob(path, GLOB_NOSORT|GLOB_BRACE, NULL, &g);
+
+ if (k == GLOB_NOMATCH)
+ return -ENOENT;
+ else if (k == GLOB_NOSPACE)
+ return -ENOMEM;
+ else if (k != 0 || strv_isempty(g.gl_pathv))
+ return errno ? -errno : -EIO;
+
+ STRV_FOREACH(p, g.gl_pathv) {
+ k = strv_extend(strv, *p);
+ if (k < 0)
+ break;
+ }
+
+ return k;
}
int dirent_ensure_type(DIR *d, struct dirent *de) {
}
int in_search_path(const char *path, char **search) {
- char **i, *parent;
+ char **i;
+ _cleanup_free_ char *parent = NULL;
int r;
r = path_get_parent(path, &parent);
if (r < 0)
return r;
- r = 0;
-
- STRV_FOREACH(i, search) {
- if (path_equal(parent, *i)) {
- r = 1;
- break;
- }
- }
-
- free(parent);
+ STRV_FOREACH(i, search)
+ if (path_equal(parent, *i))
+ return 1;
- return r;
+ return 0;
}
int get_files_in_directory(const char *path, char ***list) {
- DIR *d;
- int r = 0;
- unsigned n = 0;
- char **l = NULL;
+ _cleanup_closedir_ DIR *d = NULL;
+ size_t bufsize = 0, n = 0;
+ _cleanup_strv_free_ char **l = NULL;
assert(path);
/* Returns all files in a directory in *list, and the number
* of files as return value. If list is NULL returns only the
- * number */
+ * number. */
d = opendir(path);
if (!d)
for (;;) {
struct dirent *de;
- union dirent_storage buf;
- int k;
-
- k = readdir_r(d, &buf.de, &de);
- if (k != 0) {
- r = -k;
- goto finish;
- }
+ errno = 0;
+ de = readdir(d);
+ if (!de && errno != 0)
+ return -errno;
if (!de)
break;
continue;
if (list) {
- if ((unsigned) r >= n) {
- char **t;
-
- n = MAX(16, 2*r);
- t = realloc(l, sizeof(char*) * n);
- if (!t) {
- r = -ENOMEM;
- goto finish;
- }
-
- l = t;
- }
-
- assert((unsigned) r < n);
+ /* one extra slot is needed for the terminating NULL */
+ if (!GREEDY_REALLOC(l, bufsize, n + 2))
+ return -ENOMEM;
- l[r] = strdup(de->d_name);
- if (!l[r]) {
- r = -ENOMEM;
- goto finish;
- }
+ l[n] = strdup(de->d_name);
+ if (!l[n])
+ return -ENOMEM;
- l[++r] = NULL;
+ l[++n] = NULL;
} else
- r++;
+ n++;
}
-finish:
- if (d)
- closedir(d);
-
- if (r >= 0) {
- if (list)
- *list = l;
- } else
- strv_free(l);
+ if (list) {
+ *list = l;
+ l = NULL; /* avoid freeing */
+ }
- return r;
+ return n;
}
char *strjoin(const char *x, ...) {
}
bool is_main_thread(void) {
- static __thread int cached = 0;
+ static thread_local int cached = 0;
if (_unlikely_(cached == 0))
cached = getpid() == gettid() ? 1 : -1;
DEFINE_PRIVATE_STRING_TABLE_LOOKUP(__signal, int);
const char *signal_to_string(int signo) {
- static __thread char buf[sizeof("RTMIN+")-1 + DECIMAL_STR_MAX(int) + 1];
+ static thread_local char buf[sizeof("RTMIN+")-1 + DECIMAL_STR_MAX(int) + 1];
const char *name;
name = __signal_to_string(signo);
socklen_t l = sizeof(value);
r = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, &l);
- if (r >= 0 &&
- l == sizeof(value) &&
- (size_t) value >= n*2)
+ if (r >= 0 && l == sizeof(value) && (size_t) value >= n*2)
return 0;
+ /* If we have the privileges we will ignore the kernel limit. */
+
value = (int) n;
- r = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value));
- if (r < 0)
- return -errno;
+ if (setsockopt(fd, SOL_SOCKET, SO_SNDBUFFORCE, &value, sizeof(value)) < 0)
+ if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)) < 0)
+ return -errno;
return 1;
}
socklen_t l = sizeof(value);
r = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, &l);
- if (r >= 0 &&
- l == sizeof(value) &&
- (size_t) value >= n*2)
+ if (r >= 0 && l == sizeof(value) && (size_t) value >= n*2)
return 0;
- value = (int) n;
- r = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value));
- if (r < 0)
- return -errno;
+ /* If we have the privileges we will ignore the kernel limit. */
+ value = (int) n;
+ if (setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &value, sizeof(value)) < 0)
+ if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value)) < 0)
+ return -errno;
return 1;
}
return r;
}
-int can_sleep(const char *type) {
- char *w, *state;
- size_t l, k;
- int r;
- _cleanup_free_ char *p = NULL;
-
- assert(type);
-
- /* If /sys is read-only we cannot sleep */
- if (access("/sys/power/state", W_OK) < 0)
- return false;
-
- r = read_one_line_file("/sys/power/state", &p);
- if (r < 0)
- return false;
-
- k = strlen(type);
- FOREACH_WORD_SEPARATOR(w, l, p, WHITESPACE, state)
- if (l == k && memcmp(w, type, l) == 0)
- return true;
-
- return false;
-}
-
-int can_sleep_disk(const char *type) {
- char *w, *state;
- size_t l, k;
- int r;
- _cleanup_free_ char *p = NULL;
-
- assert(type);
-
- /* If /sys is read-only we cannot sleep */
- if (access("/sys/power/state", W_OK) < 0 ||
- access("/sys/power/disk", W_OK) < 0)
- return false;
-
- r = read_one_line_file("/sys/power/disk", &p);
- if (r < 0)
- return false;
-
- k = strlen(type);
- FOREACH_WORD_SEPARATOR(w, l, p, WHITESPACE, state) {
- if (l == k && memcmp(w, type, l) == 0)
- return true;
-
- if (l == k + 2 && w[0] == '[' && memcmp(w + 1, type, l - 2) == 0 && w[l-1] == ']')
- return true;
- }
-
- return false;
-}
-
bool is_valid_documentation_url(const char *url) {
assert(url);
}
bool in_initrd(void) {
- static __thread int saved = -1;
+ static int saved = -1;
struct statfs s;
if (saved >= 0)
}
int get_home_dir(char **_h) {
- char *h;
+ struct passwd *p;
const char *e;
+ char *h;
uid_t u;
- struct passwd *p;
assert(_h);
return 0;
}
+int get_shell(char **_s) {
+ struct passwd *p;
+ const char *e;
+ char *s;
+ uid_t u;
+
+ assert(_s);
+
+ /* Take the user specified one */
+ e = getenv("SHELL");
+ if (e) {
+ s = strdup(e);
+ if (!s)
+ return -ENOMEM;
+
+ *_s = s;
+ return 0;
+ }
+
+ /* Hardcode home directory for root to avoid NSS */
+ u = getuid();
+ if (u == 0) {
+ s = strdup("/bin/sh");
+ if (!s)
+ return -ENOMEM;
+
+ *_s = s;
+ return 0;
+ }
+
+ /* Check the database... */
+ errno = 0;
+ p = getpwuid(u);
+ if (!p)
+ return errno > 0 ? -errno : -ESRCH;
+
+ if (!path_is_absolute(p->pw_shell))
+ return -EINVAL;
+
+ s = strdup(p->pw_shell);
+ if (!s)
+ return -ENOMEM;
+
+ *_s = s;
+ return 0;
+}
+
bool filename_is_safe(const char *p) {
if (isempty(p))
return true;
}
+/**
+ * Check if a string contains control characters.
+ * Spaces and tabs are not considered control characters.
+ */
bool string_has_cc(const char *p) {
const char *t;
assert(p);
for (t = p; *t; t++)
- if (*t > 0 && *t < ' ')
+ if (*t > 0 && *t < ' ' && *t != '\t')
return true;
return false;
goto out;
}
- if(streq(set, "UTF-8")) {
+ if (streq(set, "UTF-8")) {
cached_answer = true;
goto out;
}
- /* For LC_CTYPE=="C" return true,
- * because CTYPE is effectly unset and
- * everything defaults to UTF-8 nowadays. */
-
+ /* For LC_CTYPE=="C" return true, because CTYPE is effectly
+ * unset and everything can do to UTF-8 nowadays. */
set = setlocale(LC_CTYPE, NULL);
if (!set) {
cached_answer = true;
goto out;
}
- cached_answer = streq(set, "C");
+ /* Check result, but ignore the result if C was set
+ * explicitly. */
+ cached_answer =
+ streq(set, "C") &&
+ !getenv("LC_ALL") &&
+ !getenv("LC_CTYPE") &&
+ !getenv("LANG");
out:
- return (bool)cached_answer;
+ return (bool) cached_answer;
}
const char *draw_special_char(DrawSpecialChar ch) {
[DRAW_TREE_RIGHT] = "\342\224\224\342\224\200", /* └─ */
[DRAW_TREE_SPACE] = " ", /* */
[DRAW_TRIANGULAR_BULLET] = "\342\200\243 ", /* ‣ */
+ [DRAW_BLACK_CIRCLE] = "\342\227\217 ", /* ● */
},
/* ASCII fallback */ {
[DRAW_TREE_VERT] = "| ",
[DRAW_TREE_RIGHT] = "`-",
[DRAW_TREE_SPACE] = " ",
[DRAW_TRIANGULAR_BULLET] = "> ",
+ [DRAW_BLACK_CIRCLE] = "* ",
}
};
for (;;) {
struct dirent *de;
- union dirent_storage buf;
_cleanup_close_ int fd = -1, device = -1;
char contents[6];
ssize_t n;
- int k;
- k = readdir_r(d, &buf.de, &de);
- if (k != 0)
- return -k;
+ errno = 0;
+ de = readdir(d);
+ if (!de && errno != 0)
+ return -errno;
if (!de)
break;
return search_and_fopen_internal(path, mode, s, _f);
}
-int create_tmp_dir(char template[], char** dir_name) {
- int r = 0;
- char *d, *dt;
-
- assert(dir_name);
-
- RUN_WITH_UMASK(0077) {
- d = mkdtemp(template);
- }
- if (!d) {
- log_error("Can't create directory %s: %m", template);
- return -errno;
- }
-
- dt = strjoin(d, "/tmp", NULL);
- if (!dt) {
- r = log_oom();
- goto fail3;
- }
-
- RUN_WITH_UMASK(0000) {
- r = mkdir(dt, 0777);
- }
- if (r < 0) {
- log_error("Can't create directory %s: %m", dt);
- r = -errno;
- goto fail2;
- }
- log_debug("Created temporary directory %s", dt);
-
- r = chmod(dt, 0777 | S_ISVTX);
- if (r < 0) {
- log_error("Failed to chmod %s: %m", dt);
- r = -errno;
- goto fail1;
- }
- log_debug("Set sticky bit on %s", dt);
-
- *dir_name = dt;
-
- return 0;
-fail1:
- rmdir(dt);
-fail2:
- free(dt);
-fail3:
- rmdir(template);
- return r;
-}
-
char *strextend(char **x, ...) {
va_list ap;
size_t f, l;
size_t a;
void *q;
+ assert(p);
+ assert(allocated);
+
if (*allocated >= need)
return *p;
a = MAX(64u, need * 2);
+
+ /* check for overflows */
+ if (a < need)
+ return NULL;
+
q = realloc(*p, a);
if (!q)
return NULL;
*allocated = a;
return q;
}
+
+void* greedy_realloc0(void **p, size_t *allocated, size_t need) {
+ size_t prev;
+ uint8_t *q;
+
+ assert(p);
+ assert(allocated);
+
+ prev = *allocated;
+
+ q = greedy_realloc(p, allocated, need);
+ if (!q)
+ return NULL;
+
+ if (*allocated > prev)
+ memset(&q[prev], 0, *allocated - prev);
+
+ return q;
+}
+
+bool id128_is_valid(const char *s) {
+ size_t i, l;
+
+ l = strlen(s);
+ if (l == 32) {
+
+ /* Simple formatted 128bit hex string */
+
+ for (i = 0; i < l; i++) {
+ char c = s[i];
+
+ if (!(c >= '0' && c <= '9') &&
+ !(c >= 'a' && c <= 'z') &&
+ !(c >= 'A' && c <= 'Z'))
+ return false;
+ }
+
+ } else if (l == 36) {
+
+ /* Formatted UUID */
+
+ for (i = 0; i < l; i++) {
+ char c = s[i];
+
+ if ((i == 8 || i == 13 || i == 18 || i == 23)) {
+ if (c != '-')
+ return false;
+ } else {
+ if (!(c >= '0' && c <= '9') &&
+ !(c >= 'a' && c <= 'z') &&
+ !(c >= 'A' && c <= 'Z'))
+ return false;
+ }
+ }
+
+ } else
+ return false;
+
+ return true;
+}
+
+void parse_user_at_host(char *arg, char **user, char **host) {
+ assert(arg);
+ assert(user);
+ assert(host);
+
+ *host = strchr(arg, '@');
+ if (*host == NULL)
+ *host = arg;
+ else {
+ *host[0]++ = '\0';
+ *user = arg;
+ }
+}
+
+int split_pair(const char *s, const char *sep, char **l, char **r) {
+ char *x, *a, *b;
+
+ assert(s);
+ assert(sep);
+ assert(l);
+ assert(r);
+
+ if (isempty(sep))
+ return -EINVAL;
+
+ x = strstr(s, sep);
+ if (!x)
+ return -EINVAL;
+
+ a = strndup(s, x - s);
+ if (!a)
+ return -ENOMEM;
+
+ b = strdup(x + strlen(sep));
+ if (!b) {
+ free(a);
+ return -ENOMEM;
+ }
+
+ *l = a;
+ *r = b;
+
+ return 0;
+}
+
+int shall_restore_state(void) {
+ _cleanup_free_ char *line;
+ char *w, *state;
+ size_t l;
+ int r;
+
+ r = proc_cmdline(&line);
+ if (r < 0)
+ return r;
+ if (r == 0) /* Container ... */
+ return 1;
+
+ FOREACH_WORD_QUOTED(w, l, line, state)
+ if (l == 23 && strneq(w, "systemd.restore_state=0", 23))
+ return 0;
+
+ return 1;
+}
+
+int proc_cmdline(char **ret) {
+ int r;
+
+ if (detect_container(NULL) > 0) {
+ *ret = NULL;
+ return 0;
+ }
+
+ r = read_one_line_file("/proc/cmdline", ret);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+int container_get_leader(const char *machine, pid_t *pid) {
+ _cleanup_free_ char *s = NULL, *class = NULL;
+ const char *p;
+ pid_t leader;
+ int r;
+
+ assert(machine);
+ assert(pid);
+
+ p = strappenda("/run/systemd/machines/", machine);
+ r = parse_env_file(p, NEWLINE, "LEADER", &s, "CLASS", &class, NULL);
+ if (r == -ENOENT)
+ return -EHOSTDOWN;
+ if (r < 0)
+ return r;
+ if (!s)
+ return -EIO;
+
+ if (!streq_ptr(class, "container"))
+ return -EIO;
+
+ r = parse_pid(s, &leader);
+ if (r < 0)
+ return r;
+ if (leader <= 1)
+ return -EIO;
+
+ *pid = leader;
+ return 0;
+}
+
+int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *root_fd) {
+ _cleanup_close_ int pidnsfd = -1, mntnsfd = -1;
+ const char *pidns, *mntns, *root;
+ int rfd;
+
+ assert(pid >= 0);
+ assert(pidns_fd);
+ assert(mntns_fd);
+ assert(root_fd);
+
+ mntns = procfs_file_alloca(pid, "ns/mnt");
+ mntnsfd = open(mntns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
+ if (mntnsfd < 0)
+ return -errno;
+
+ pidns = procfs_file_alloca(pid, "ns/pid");
+ pidnsfd = open(pidns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
+ if (pidnsfd < 0)
+ return -errno;
+
+ root = procfs_file_alloca(pid, "root");
+ rfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+ if (rfd < 0)
+ return -errno;
+
+ *pidns_fd = pidnsfd;
+ *mntns_fd = mntnsfd;
+ *root_fd = rfd;
+ pidnsfd = -1;
+ mntnsfd = -1;
+
+ return 0;
+}
+
+int namespace_enter(int pidns_fd, int mntns_fd, int root_fd) {
+ assert(pidns_fd >= 0);
+ assert(mntns_fd >= 0);
+ assert(root_fd >= 0);
+
+ if (setns(pidns_fd, CLONE_NEWPID) < 0)
+ return -errno;
+
+ if (setns(mntns_fd, CLONE_NEWNS) < 0)
+ return -errno;
+
+ if (fchdir(root_fd) < 0)
+ return -errno;
+
+ if (chroot(".") < 0)
+ return -errno;
+
+ if (setresgid(0, 0, 0) < 0)
+ return -errno;
+
+ if (setresuid(0, 0, 0) < 0)
+ return -errno;
+
+ return 0;
+}
+
+bool pid_valid(pid_t pid) {
+ if (pid <= 0)
+ return false;
+
+ if (kill(pid, 0) >= 0)
+ return true;
+
+ return errno != ESRCH;
+}