From 28744043fbaca39dfc9fd1666a8557fd6d8a690f Mon Sep 17 00:00:00 2001 From: "Anthony G. Basile" Date: Thu, 14 Aug 2014 11:28:47 -0400 Subject: src/shared: import many code cleanups from upstream Signed-off-by: Anthony G. Basile --- src/shared/Makefile.am | 20 +-- src/shared/conf-files.c | 15 +-- src/shared/conf-files.h | 7 +- src/shared/hashmap.h | 4 + src/shared/log.c | 28 ++-- src/shared/macro.h | 13 ++ src/shared/missing.h | 3 + src/shared/path-util.c | 83 ++++++++---- src/shared/path-util.h | 6 +- src/shared/set.h | 1 + src/shared/time-util.c | 28 ++++ src/shared/time-util.h | 3 + src/shared/util.c | 339 ++++++++++++++++++++++++++++-------------------- src/shared/util.h | 32 +++-- src/shared/virt.c | 328 ++++++++++++++++++++++++++++++++++++++++++++++ src/shared/virt.h | 24 ++++ 16 files changed, 710 insertions(+), 224 deletions(-) create mode 100644 src/shared/virt.c create mode 100644 src/shared/virt.h diff --git a/src/shared/Makefile.am b/src/shared/Makefile.am index b4c2000301..4c9f20697b 100644 --- a/src/shared/Makefile.am +++ b/src/shared/Makefile.am @@ -7,41 +7,46 @@ libudev_shared_la_SOURCES=\ cgroup-util.c \ conf-files.c \ device-nodes.c \ + dev-setup.c \ exit-status.c \ fileio.c \ hashmap.c \ + label.c \ log.c \ + mkdir.c \ MurmurHash2.c \ path-util.c \ set.c \ siphash24.c \ + smack-util.c \ strbuf.c \ strv.c \ strxcpyx.c \ time-util.c \ util.c \ utf8.c \ - dev-setup.c \ - label.c \ - mkdir.c \ - smack-util.c + virt.c noinst_HEADERS = \ cgroup-util.h \ conf-files.h \ def.h \ device-nodes.h \ + dev-setup.h \ exit-status.h \ fileio.h \ hashmap.h \ ioprio.h \ + label.h \ log.h \ macro.h \ missing.h \ + mkdir.h \ MurmurHash2.h \ path-util.h \ set.h \ siphash24.h \ + smack-util.h \ socket-util.h \ sparse-endian.h \ strbuf.h \ @@ -50,11 +55,8 @@ noinst_HEADERS = \ time-util.h \ util.h \ utf8.h \ - dev-setup.h \ - label.h \ - mkdir.h \ - smack-util.h \ - udev-util.h + udev-util.h \ + virt.h libudev_shared_la_LDFLAGS = \ $(AM_LDFLAGS) diff --git a/src/shared/conf-files.c b/src/shared/conf-files.c index dc4f970313..2de4fe0a37 100644 --- a/src/shared/conf-files.c +++ b/src/shared/conf-files.c @@ -39,10 +39,12 @@ static int files_add(Hashmap *h, const char *root, const char *path, const char *suffix) { _cleanup_closedir_ DIR *dir = NULL; - _cleanup_free_ char *dirpath = NULL; + char *dirpath; - if (asprintf(&dirpath, "%s%s", root ? root : "", path) < 0) - return -ENOMEM; + assert(path); + assert(suffix); + + dirpath = strappenda(root ? root : "", path); dir = opendir(dirpath); if (!dir) { @@ -96,7 +98,7 @@ static int base_cmp(const void *a, const void *b) { } static int conf_files_list_strv_internal(char ***strv, const char *suffix, const char *root, char **dirs) { - Hashmap *fh; + _cleanup_hashmap_free_ Hashmap *fh = NULL; char **files, **p; int r; @@ -104,7 +106,7 @@ static int conf_files_list_strv_internal(char ***strv, const char *suffix, const assert(suffix); /* This alters the dirs string array */ - if (!path_strv_canonicalize_uniq(dirs)) + if (!path_strv_resolve_uniq(dirs, root)) return -ENOMEM; fh = hashmap_new(string_hash_func, string_compare_func); @@ -114,7 +116,6 @@ static int conf_files_list_strv_internal(char ***strv, const char *suffix, const STRV_FOREACH(p, dirs) { r = files_add(fh, root, *p, suffix); if (r == -ENOMEM) { - hashmap_free_free(fh); return r; } else if (r < 0) log_debug("Failed to search for files in %s: %s", @@ -123,14 +124,12 @@ static int conf_files_list_strv_internal(char ***strv, const char *suffix, const files = hashmap_get_strv(fh); if (files == NULL) { - hashmap_free_free(fh); return -ENOMEM; } qsort_safe(files, hashmap_size(fh), sizeof(char *), base_cmp); *strv = files; - hashmap_free(fh); return 0; } diff --git a/src/shared/conf-files.h b/src/shared/conf-files.h index a0720dc2cc..b769e56845 100644 --- a/src/shared/conf-files.h +++ b/src/shared/conf-files.h @@ -1,7 +1,6 @@ /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ -#ifndef fooconffileshfoo -#define fooconffileshfoo +#pragma once /*** This file is part of systemd. @@ -23,6 +22,6 @@ along with systemd; If not, see . ***/ -int conf_files_list_strv(char ***strv, const char *suffix, const char *root, const char* const* dirs); +#include "macro.h" -#endif +int conf_files_list_strv(char ***strv, const char *suffix, const char *root, const char* const* dirs); diff --git a/src/shared/hashmap.h b/src/shared/hashmap.h index 387c32243e..cfd34750d9 100644 --- a/src/shared/hashmap.h +++ b/src/shared/hashmap.h @@ -24,6 +24,7 @@ #include #include "macro.h" +#include "util.h" /* Pretty straightforward hash table implementation. As a minor * optimization a NULL hashmap object will be treated as empty hashmap @@ -73,3 +74,6 @@ char **hashmap_get_strv(Hashmap *h); #define HASHMAP_FOREACH(e, h, i) \ for ((i) = ITERATOR_FIRST, (e) = hashmap_iterate((h), &(i), NULL); (e); (e) = hashmap_iterate((h), &(i), NULL)) + +DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free); +#define _cleanup_hashmap_free_ _cleanup_(hashmap_freep) diff --git a/src/shared/log.c b/src/shared/log.c index cd496cf6c5..0237d20ff2 100644 --- a/src/shared/log.c +++ b/src/shared/log.c @@ -62,7 +62,7 @@ void log_close_console(void) { if (getpid() == 1) { if (console_fd >= 3) - close_nointr_nofail(console_fd); + safe_close(console_fd); console_fd = -1; } @@ -84,12 +84,7 @@ static int log_open_console(void) { } void log_close_kmsg(void) { - - if (kmsg_fd < 0) - return; - - close_nointr_nofail(kmsg_fd); - kmsg_fd = -1; + kmsg_fd = safe_close(kmsg_fd); } static int log_open_kmsg(void) { @@ -105,16 +100,12 @@ static int log_open_kmsg(void) { } void log_close_syslog(void) { - - if (syslog_fd < 0) - return; - - close_nointr_nofail(syslog_fd); - syslog_fd = -1; + syslog_fd = safe_close(syslog_fd); } static int create_log_socket(int type) { int fd; + struct timeval tv; fd = socket(AF_UNIX, type|SOCK_CLOEXEC, 0); if (fd < 0) @@ -122,6 +113,15 @@ static int create_log_socket(int type) { fd_inc_sndbuf(fd, SNDBUF_SIZE); + /* We need a blocking fd here since we'd otherwise lose + messages way too early. However, let's not hang forever in the + unlikely case of a deadlock. */ + if (getpid() == 1) + timeval_store(&tv, 10 * USEC_PER_MSEC); + else + timeval_store(&tv, 10 * USEC_PER_SEC); + setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); + return fd; } @@ -142,7 +142,7 @@ static int log_open_syslog(void) { } if (connect(syslog_fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) { - close_nointr_nofail(syslog_fd); + safe_close(syslog_fd); /* Some legacy syslog systems still use stream * sockets. They really shouldn't. But what can we diff --git a/src/shared/macro.h b/src/shared/macro.h index 2deaf04092..3e02d86b5c 100644 --- a/src/shared/macro.h +++ b/src/shared/macro.h @@ -173,6 +173,19 @@ static inline size_t IOVEC_INCREMENT(struct iovec *i, unsigned n, size_t k) { sizeof(type) <= 2 ? 5 : \ sizeof(type) <= 4 ? 10 : \ sizeof(type) <= 8 ? 20 : sizeof(int[-2*(sizeof(type) > 8)]))) +#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; \ + }) /* Define C11 thread_local attribute even on older gcc compiler * version */ diff --git a/src/shared/missing.h b/src/shared/missing.h index 471a81120c..3c758956c6 100644 --- a/src/shared/missing.h +++ b/src/shared/missing.h @@ -41,6 +41,9 @@ #define RLIMIT_RTTIME 15 #endif +/* If RLIMIT_RTTIME is not defined, then we cannot use RLIMIT_NLIMITS as is */ +#define _RLIMIT_MAX (RLIMIT_RTTIME+1 > RLIMIT_NLIMITS ? RLIMIT_RTTIME+1 : RLIMIT_NLIMITS) + #ifndef BTRFS_IOCTL_MAGIC #define BTRFS_IOCTL_MAGIC 0x94 #endif diff --git a/src/shared/path-util.c b/src/shared/path-util.c index 41bc20de69..783d130842 100644 --- a/src/shared/path-util.c +++ b/src/shared/path-util.c @@ -34,6 +34,7 @@ #include "util.h" #include "log.h" #include "strv.h" +#include "mkdir.h" #include "path-util.h" #include "missing.h" @@ -41,16 +42,8 @@ bool path_is_absolute(const char *p) { return p[0] == '/'; } -char *path_get_file_name(const char *p) { - char *r; - - assert(p); - - r = strrchr(p, '/'); - if (r) - return r + 1; - - return (char*) p; +bool is_path(const char *p) { + return !!strchr(p, '/'); } int path_get_parent(const char *path, char **_r) { @@ -124,7 +117,7 @@ char *path_make_absolute_cwd(const char *p) { return path_make_absolute(p, cwd); } -char **path_strv_canonicalize(char **l) { +char **path_strv_resolve(char **l, const char *prefix) { char **s; unsigned k = 0; bool enomem = false; @@ -138,28 +131,63 @@ char **path_strv_canonicalize(char **l) { STRV_FOREACH(s, l) { char *t, *u; + _cleanup_free_ char *orig = NULL; - t = path_make_absolute_cwd(*s); - free(*s); - *s = NULL; - - if (!t) { - enomem = true; + 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 = realpath(t, 0); + u = canonicalize_file_name(t); if (!u) { - if (errno == ENOENT) - u = t; - else { + if (errno == ENOENT) { + if (prefix) { + u = orig; + orig = NULL; + free(t); + } else + u = t; + } else { free(t); - if (errno == ENOMEM || !errno) + 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 */ + u = orig; + orig = NULL; + } } else free(t); @@ -174,11 +202,12 @@ char **path_strv_canonicalize(char **l) { return l; } -char **path_strv_canonicalize_uniq(char **l) { +char **path_strv_resolve_uniq(char **l, const char *prefix) { + if (strv_isempty(l)) return l; - if (!path_strv_canonicalize(l)) + if (!path_strv_resolve(l, prefix)) return NULL; return strv_uniq(l); @@ -259,7 +288,7 @@ int path_is_mount_point(const char *t, bool allow_symlink) { }; int mount_id, mount_id_parent; - char *parent; + _cleanup_free_ char *parent = NULL; struct stat a, b; int r; @@ -272,7 +301,7 @@ int path_is_mount_point(const char *t, bool allow_symlink) { r = name_to_handle_at(AT_FDCWD, t, &h.handle, &mount_id, allow_symlink ? AT_SYMLINK_FOLLOW : 0); if (r < 0) { - if (errno == ENOSYS || errno == ENOTSUP) + if (IN_SET(errno, ENOSYS, EOPNOTSUPP)) /* This kernel or file system does not support * name_to_handle_at(), hence fallback to the * traditional stat() logic */ @@ -290,7 +319,6 @@ int path_is_mount_point(const char *t, bool allow_symlink) { h.handle.handle_bytes = MAX_HANDLE_SZ; r = name_to_handle_at(AT_FDCWD, parent, &h.handle, &mount_id_parent, 0); - free(parent); if (r < 0) { /* The parent can't do name_to_handle_at() but the * directory we are interested in can? If so, it must @@ -321,7 +349,6 @@ fallback: return r; r = lstat(parent, &b); - free(parent); if (r < 0) return -errno; diff --git a/src/shared/path-util.h b/src/shared/path-util.h index 8e55131093..a62624c1b3 100644 --- a/src/shared/path-util.h +++ b/src/shared/path-util.h @@ -19,9 +19,9 @@ #include +#include "macro.h" #include "time-util.h" -char* path_get_file_name(const char *p) _pure_; 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); @@ -29,8 +29,8 @@ char* path_make_absolute_cwd(const char *p); char* path_kill_slashes(char *path); bool path_equal(const char *a, const char *b) _pure_; -char** path_strv_canonicalize(char **l); -char** path_strv_canonicalize_uniq(char **l); +char** path_strv_resolve(char **l, const char *prefix); +char** path_strv_resolve_uniq(char **l, const char *prefix); int path_is_mount_point(const char *path, bool allow_symlink); bool paths_check_timestamp(const char* const* paths, usec_t *paths_ts_usec, bool update); diff --git a/src/shared/set.h b/src/shared/set.h index 76383a423a..c6455be7c1 100644 --- a/src/shared/set.h +++ b/src/shared/set.h @@ -28,6 +28,7 @@ * for each set use. */ #include "hashmap.h" +#include "util.h" typedef struct Set Set; diff --git a/src/shared/time-util.c b/src/shared/time-util.c index 6f76e8c19b..3df35b8e24 100644 --- a/src/shared/time-util.c +++ b/src/shared/time-util.c @@ -48,6 +48,34 @@ usec_t timespec_load(const struct timespec *ts) { (usec_t) ts->tv_nsec / NSEC_PER_USEC; } +struct timespec *timespec_store(struct timespec *ts, usec_t u) { + assert(ts); + + if (u == USEC_INFINITY) { + ts->tv_sec = (time_t) -1; + ts->tv_nsec = (long) -1; + return ts; + } + + ts->tv_sec = (time_t) (u / USEC_PER_SEC); + ts->tv_nsec = (long int) ((u % USEC_PER_SEC) * NSEC_PER_USEC); + + return ts; +} +struct timeval *timeval_store(struct timeval *tv, usec_t u) { + assert(tv); + + if (u == USEC_INFINITY) { + tv->tv_sec = (time_t) -1; + tv->tv_usec = (suseconds_t) -1; + } else { + tv->tv_sec = (time_t) (u / USEC_PER_SEC); + tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC); + } + + return tv; +} + char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) { static const struct { const char *suffix; diff --git a/src/shared/time-util.h b/src/shared/time-util.h index 16ef25b523..58bc59ec4d 100644 --- a/src/shared/time-util.h +++ b/src/shared/time-util.h @@ -61,4 +61,7 @@ typedef struct dual_timestamp { usec_t now(clockid_t clock); usec_t timespec_load(const struct timespec *ts) _pure_; +struct timespec *timespec_store(struct timespec *ts, usec_t u); + +struct timeval *timeval_store(struct timeval *tv, usec_t u); char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy); diff --git a/src/shared/util.c b/src/shared/util.c index 5ff767b6a7..d551f87e60 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -68,6 +68,7 @@ #include "exit-status.h" #include "hashmap.h" #include "fileio.h" +#include "virt.h" int saved_argc = 0; char **saved_argv = NULL; @@ -128,32 +129,23 @@ int close_nointr(int fd) { assert(fd >= 0); r = close(fd); - - /* 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 (_unlikely_(r < 0 && errno == EINTR)) - return 0; - else if (r >= 0) + if (r >= 0) return r; + else if (errno == EINTR) + /* + * 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 + */ + return 0; else return -errno; } -void close_nointr_nofail(int fd) { - PROTECT_ERRNO; - - /* like close_nointr() but cannot fail, and guarantees errno - * is unchanged */ - - assert_se(close_nointr(fd) == 0); -} - int safe_close(int fd) { /* @@ -490,6 +482,7 @@ _pure_ static bool ignore_file_allow_backup(const char *filename) { endswith(filename, ".rpmorig") || endswith(filename, ".dpkg-old") || endswith(filename, ".dpkg-new") || + endswith(filename, ".dpkg-tmp") || endswith(filename, ".swp"); } @@ -497,55 +490,11 @@ bool ignore_file(const char *filename) { assert(filename); if (endswith(filename, "~")) - return false; + return true; return ignore_file_allow_backup(filename); } -void random_bytes(void *p, size_t n) { - static bool srand_called = false; - _cleanup_close_ int fd; - ssize_t k; - uint8_t *q; - - fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) - goto fallback; - - k = loop_read(fd, p, n, true); - if (k < 0 || (size_t) k != n) - goto fallback; - - return; - -fallback: - - 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(); -} - int open_terminal(const char *name, int mode) { int fd, r; unsigned c = 0; @@ -582,18 +531,74 @@ int open_terminal(const char *name, int mode) { r = isatty(fd); if (r < 0) { - close_nointr_nofail(fd); + safe_close(fd); return -errno; } if (!r) { - close_nointr_nofail(fd); + safe_close(fd); return -ENOTTY; } return fd; } +int dev_urandom(void *p, size_t n) { + _cleanup_close_ int fd; + ssize_t k; + + fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (fd < 0) + return errno == ENOENT ? -ENOSYS : -errno; + + k = loop_read(fd, p, n, true); + if (k < 0) + return (int) k; + if ((size_t) k != n) + return -EIO; + + return 0; +} + +void random_bytes(void *p, size_t n) { + static bool srand_called = false; + 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. */ + + if (!srand_called) { + unsigned 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... */ + + void *auxv; + + auxv = (void*) getauxval(AT_RANDOM); + if (auxv) + x ^= *(unsigned*) auxv; +#endif + + x ^= (unsigned) now(CLOCK_REALTIME); + x ^= (unsigned) gettid(); + + srand(x); + srand_called = true; + } + + for (q = p; q < (uint8_t*) p + n; q ++) + *q = rand(); +} + _pure_ static int is_temporary_fs(struct statfs *s) { assert(s); @@ -601,6 +606,41 @@ _pure_ static int is_temporary_fs(struct statfs *s) { F_TYPE_EQUAL(s->f_type, RAMFS_MAGIC); } +ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) { + uint8_t *p = buf; + ssize_t n = 0; + + assert(fd >= 0); + assert(buf); + + while (nbytes > 0) { + ssize_t k; + + k = read(fd, p, nbytes); + if (k < 0 && errno == EINTR) + continue; + + if (k < 0 && errno == EAGAIN && do_poll) { + + /* We knowingly ignore any return value here, + * and expect that any error/EOF is reported + * via read() */ + + fd_wait_for_event(fd, POLLIN, USEC_INFINITY); + continue; + } + + if (k <= 0) + return n > 0 ? n : (k < 0 ? -errno : 0); + + p += k; + nbytes -= k; + n += k; + } + + return n; +} + int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) { assert(path); @@ -680,8 +720,7 @@ bool nulstr_contains(const char*nulstr, const char *needle) { return false; } -int execute_command(const char *command, char *const argv[]) -{ +int execute_command(const char *command, char *const argv[]) { pid_t pid; int status; @@ -759,28 +798,20 @@ int flush_fd(int fd) { int fopen_temporary(const char *path, FILE **_f, char **_temp_path) { FILE *f; char *t; - const char *fn; - size_t k; int fd; assert(path); assert(_f); assert(_temp_path); - t = new(char, strlen(path) + 1 + 6 + 1); + t = tempfn_xxxxxx(path); if (!t) return -ENOMEM; - fn = path_get_file_name(path); - k = fn-path; - memcpy(t, path, k); - t[k] = '.'; - stpcpy(stpcpy(t+k+1, fn), "XXXXXX"); - #if HAVE_DECL_MKOSTEMP - fd = mkostemp(t, O_WRONLY|O_CLOEXEC); + fd = mkostemp_safe(t, O_WRONLY|O_CLOEXEC); #else - fd = mkstemp(t); + fd = mkstemp_safe(t); fcntl(fd, F_SETFD, FD_CLOEXEC); #endif if (fd < 0) { @@ -801,55 +832,6 @@ int fopen_temporary(const char *path, FILE **_f, char **_temp_path) { return 0; } -ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) { - uint8_t *p; - ssize_t n = 0; - - assert(fd >= 0); - assert(buf); - - p = buf; - - while (nbytes > 0) { - ssize_t k; - - if ((k = read(fd, p, nbytes)) <= 0) { - - if (k < 0 && errno == EINTR) - continue; - - if (k < 0 && errno == EAGAIN && do_poll) { - struct pollfd pollfd = { - .fd = fd, - .events = POLLIN, - }; - - if (poll(&pollfd, 1, -1) < 0) { - if (errno == EINTR) - continue; - - return n > 0 ? n : -errno; - } - - /* We knowingly ignore the revents value here, - * and expect that any error/EOF is reported - * via read()/write() - */ - - continue; - } - - return n > 0 ? n : (k < 0 ? -errno : 0); - } - - p += k; - nbytes -= k; - n += k; - } - - return n; -} - char *strjoin(const char *x, ...) { va_list ap; size_t l; @@ -908,7 +890,7 @@ 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; @@ -984,7 +966,7 @@ static const char* const sched_policy_table[] = { DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(sched_policy, int, INT_MAX); -static const char* const rlimit_table[] = { +static const char* const rlimit_table[_RLIMIT_MAX] = { [RLIMIT_CPU] = "LimitCPU", [RLIMIT_FSIZE] = "LimitFSIZE", [RLIMIT_DATA] = "LimitDATA", @@ -1053,7 +1035,7 @@ static const char *const __signal_table[] = { 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); @@ -1068,26 +1050,46 @@ const char *signal_to_string(int signo) { return buf; } +int fd_wait_for_event(int fd, int event, usec_t t) { + + struct pollfd pollfd = { + .fd = fd, + .events = event, + }; + + struct timespec ts; + int r; + + r = ppoll(&pollfd, 1, t == USEC_INFINITY ? NULL : timespec_store(&ts, t), NULL); + if (r < 0) + return -errno; + + if (r == 0) + return 0; + + return pollfd.revents; +} + int fd_inc_sndbuf(int fd, size_t n) { int r, value; 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; } bool in_initrd(void) { - static __thread int saved = -1; + static int saved = -1; struct statfs s; if (saved >= 0) @@ -1136,9 +1138,8 @@ void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size, int proc_cmdline(char **ret) { int r; - /* disable containers for now if (detect_container(NULL) > 0) { - char *buf, *p; + char *buf = NULL, *p; size_t sz = 0; r = read_full_file("/proc/1/cmdline", &buf, &sz); @@ -1149,11 +1150,10 @@ int proc_cmdline(char **ret) { if (*p == 0) *p = ' '; - *p = 0; + *p = 0; *ret = buf; return 1; } - */ r = read_one_line_file("/proc/cmdline", ret); if (r < 0) @@ -1185,3 +1185,54 @@ int getpeercred(int fd, struct ucred *ucred) { *ucred = u; return 0; } + +/* This is much like like mkostemp() but is subject to umask(). */ +int mkostemp_safe(char *pattern, int flags) { + _cleanup_umask_ mode_t u; + int fd; + + assert(pattern); + + u = umask(077); + + fd = mkostemp(pattern, flags); + if (fd < 0) + return -errno; + + return fd; +} + +/* This is much like like mkstemp() but is subject to umask(). */ +int mkstemp_safe(char *pattern) { + _cleanup_umask_ mode_t u; + int fd; + + assert(pattern); + + u = umask(077); + + fd = mkstemp(pattern); + if (fd < 0) + return -errno; + + return fd; +} + +char *tempfn_xxxxxx(const char *p) { + const char *fn; + char *t; + size_t k; + + assert(p); + + t = new(char, strlen(p) + 1 + 6 + 1); + if (!t) + return NULL; + + fn = basename(p); + k = fn - p; + + strcpy(stpcpy(stpcpy(mempcpy(t, p, k), "."), fn), "XXXXXX"); + + return t; +} diff --git a/src/shared/util.h b/src/shared/util.h index 5fe724af40..4d05224542 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -32,6 +32,7 @@ #include #include +#include "time-util.h" #include "missing.h" #include "config.h" @@ -127,7 +128,6 @@ char *endswith(const char *s, const char *postfix) _pure_; int close_nointr(int fd); int safe_close(int fd); -void close_nointr_nofail(int fd); int safe_atou(const char *s, unsigned *ret_u); int safe_atoi(const char *s, int *ret_i); @@ -158,6 +158,7 @@ bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) _pu bool ignore_file(const char *filename) _pure_; +int dev_urandom(void *p, size_t n); void random_bytes(void *p, size_t n); /* For basic lookup tables with strictly enumerated entries */ @@ -169,7 +170,8 @@ void random_bytes(void *p, size_t n); } \ scope type name##_from_string(const char *s) { \ type i; \ - assert(s); \ + if (!s) \ + return (type) -1; \ for (i = 0; i < (type)ELEMENTSOF(name##_table); i++) \ if (name##_table[i] && \ streq(name##_table[i], s)) \ @@ -267,6 +269,7 @@ const char *signal_to_string(int i) _const_; extern int saved_argc; extern char **saved_argv; +int fd_wait_for_event(int fd, int event, usec_t timeout); int fd_inc_sndbuf(int fd, size_t n); bool in_initrd(void); @@ -282,28 +285,24 @@ static inline void freep(void *p) { } \ struct __useless_struct_to_allow_trailing_semicolon__ -static inline void fclosep(FILE **f) { - if (*f) - fclose(*f); -} - static inline void closep(int *fd) { - if (*fd >= 0) - close_nointr_nofail(*fd); + safe_close(*fd); } -static inline void closedirp(DIR **d) { - if (*d) - closedir(*d); +static inline void umaskp(mode_t *u) { + umask(*u); } +DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, fclose); +DEFINE_TRIVIAL_CLEANUP_FUNC(DIR*, closedir); #define _cleanup_free_ _cleanup_(freep) -#define _cleanup_fclose_ _cleanup_(fclosep) #define _cleanup_close_ _cleanup_(closep) +#define _cleanup_umask_ _cleanup_(umaskp) +#define _cleanup_fclose_ _cleanup_(fclosep) #define _cleanup_closedir_ _cleanup_(closedirp) _malloc_ _alloc_(1, 2) static inline void *malloc_multiply(size_t a, size_t b) { - if (_unlikely_(b == 0 || a > ((size_t) -1) / b)) + if (_unlikely_(b != 0 && a > ((size_t) -1) / b)) return NULL; return malloc(a * b); @@ -360,7 +359,12 @@ static inline void qsort_safe(void *base, size_t nmemb, size_t size, int proc_cmdline(char **ret); int getpeercred(int fd, struct ucred *ucred); +int mkostemp_safe(char *pattern, int flags); +int mkstemp_safe(char *pattern); + union file_handle_union { struct file_handle handle; char padding[sizeof(struct file_handle) + MAX_HANDLE_SZ]; }; + +char *tempfn_xxxxxx(const char *p); diff --git a/src/shared/virt.c b/src/shared/virt.c new file mode 100644 index 0000000000..2144341e83 --- /dev/null +++ b/src/shared/virt.c @@ -0,0 +1,328 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + 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 . +***/ + +#include +#include +#include + +#include "util.h" +#include "virt.h" +#include "fileio.h" + +#if 0 +static int detect_vm_cpuid(const char **_id) { + + /* Both CPUID and DMI are x86 specific interfaces... */ +#if defined(__i386__) || defined(__x86_64__) + + static const char cpuid_vendor_table[] = + "XenVMMXenVMM\0" "xen\0" + "KVMKVMKVM\0" "kvm\0" + /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */ + "VMwareVMware\0" "vmware\0" + /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */ + "Microsoft Hv\0" "microsoft\0"; + + uint32_t eax, ecx; + union { + uint32_t sig32[3]; + char text[13]; + } sig = {}; + const char *j, *k; + bool hypervisor; + + /* http://lwn.net/Articles/301888/ */ + +#if defined (__i386__) +#define REG_a "eax" +#define REG_b "ebx" +#elif defined (__amd64__) +#define REG_a "rax" +#define REG_b "rbx" +#endif + + /* First detect whether there is a hypervisor */ + eax = 1; + __asm__ __volatile__ ( + /* ebx/rbx is being used for PIC! */ + " push %%"REG_b" \n\t" + " cpuid \n\t" + " pop %%"REG_b" \n\t" + + : "=a" (eax), "=c" (ecx) + : "0" (eax) + ); + + hypervisor = !!(ecx & 0x80000000U); + + if (hypervisor) { + + /* There is a hypervisor, see what it is */ + eax = 0x40000000U; + __asm__ __volatile__ ( + /* ebx/rbx is being used for PIC! */ + " push %%"REG_b" \n\t" + " cpuid \n\t" + " mov %%ebx, %1 \n\t" + " pop %%"REG_b" \n\t" + + : "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2]) + : "0" (eax) + ); + + NULSTR_FOREACH_PAIR(j, k, cpuid_vendor_table) + if (streq(sig.text, j)) { + *_id = k; + return 1; + } + + *_id = "other"; + return 0; + } +#endif + + return 0; +} + +static int detect_vm_dmi(const char **_id) { + + /* Both CPUID and DMI are x86 specific interfaces... */ +#if defined(__i386__) || defined(__x86_64__) + + static const char *const dmi_vendors[] = { + "/sys/class/dmi/id/sys_vendor", + "/sys/class/dmi/id/board_vendor", + "/sys/class/dmi/id/bios_vendor" + }; + + static const char dmi_vendor_table[] = + "QEMU\0" "qemu\0" + /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */ + "VMware\0" "vmware\0" + "VMW\0" "vmware\0" + "innotek GmbH\0" "oracle\0" + "Xen\0" "xen\0" + "Bochs\0" "bochs\0"; + unsigned i; + + for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) { + _cleanup_free_ char *s = NULL; + const char *j, *k; + int r; + + r = read_one_line_file(dmi_vendors[i], &s); + if (r < 0) { + if (r != -ENOENT) + return r; + + continue; + } + + NULSTR_FOREACH_PAIR(j, k, dmi_vendor_table) + if (startswith(s, j)) { + *_id = k; + return 1; + } + } +#endif + + return 0; +} + +/* Returns a short identifier for the various VM implementations */ +int detect_vm(const char **id) { + _cleanup_free_ char *domcap = NULL, *cpuinfo_contents = NULL; + static thread_local int cached_found = -1; + static thread_local const char *cached_id = NULL; + const char *_id = NULL; + int r; + + if (_likely_(cached_found >= 0)) { + + if (id) + *id = cached_id; + + return cached_found; + } + + /* Try xen capabilities file first, if not found try high-level hypervisor sysfs file: + * + * https://bugs.freedesktop.org/show_bug.cgi?id=77271 */ + r = read_one_line_file("/proc/xen/capabilities", &domcap); + if (r >= 0) { + char *cap, *i = domcap; + + while ((cap = strsep(&i, ","))) + if (streq(cap, "control_d")) + break; + + if (!cap) { + _id = "xen"; + r = 1; + } + + goto finish; + + } else if (r == -ENOENT) { + _cleanup_free_ char *hvtype = NULL; + + r = read_one_line_file("/sys/hypervisor/type", &hvtype); + if (r >= 0) { + if (streq(hvtype, "xen")) { + _id = "xen"; + r = 1; + goto finish; + } + } else if (r != -ENOENT) + return r; + } else + return r; + + /* this will set _id to "other" and return 0 for unknown hypervisors */ + r = detect_vm_cpuid(&_id); + if (r != 0) + goto finish; + + r = detect_vm_dmi(&_id); + if (r != 0) + goto finish; + + if (_id) { + /* "other" */ + r = 1; + goto finish; + } + + /* Detect User-Mode Linux by reading /proc/cpuinfo */ + r = read_full_file("/proc/cpuinfo", &cpuinfo_contents, NULL); + if (r < 0) + return r; + if (strstr(cpuinfo_contents, "\nvendor_id\t: User Mode Linux\n")) { + _id = "uml"; + r = 1; + goto finish; + } + +#if defined(__s390__) + { + _cleanup_free_ char *t = NULL; + + r = get_status_field("/proc/sysinfo", "VM00 Control Program:", &t); + if (r >= 0) { + if (streq(t, "z/VM")) + _id = "zvm"; + else + _id = "kvm"; + r = 1; + + goto finish; + } + } +#endif + + r = 0; + +finish: + cached_found = r; + + cached_id = _id; + if (id) + *id = _id; + + return r; +} +#endif + +int detect_container(const char **id) { + + static thread_local int cached_found = -1; + static thread_local const char *cached_id = NULL; + + _cleanup_free_ char *m = NULL; + const char *_id = NULL, *e = NULL; + int r; + + if (_likely_(cached_found >= 0)) { + + if (id) + *id = cached_id; + + return cached_found; + } + + /* /proc/vz exists in container and outside of the container, + * /proc/bc only outside of the container. */ + if (access("/proc/vz", F_OK) >= 0 && + access("/proc/bc", F_OK) < 0) { + _id = "openvz"; + r = 1; + goto finish; + } + + if (getpid() == 1) { + /* If we are PID 1 we can just check our own + * environment variable */ + + e = getenv("container"); + if (isempty(e)) { + r = 0; + goto finish; + } + } else { + + /* Otherwise, PID 1 dropped this information into a + * file in /run. This is better than accessing + * /proc/1/environ, since we don't need CAP_SYS_PTRACE + * for that. */ + + r = read_one_line_file("/run/systemd/container", &m); + if (r == -ENOENT) { + r = 0; + goto finish; + } + if (r < 0) + return r; + + e = m; + } + + /* We only recognize a selected few here, since we want to + * enforce a redacted namespace */ + if (streq(e, "lxc")) + _id ="lxc"; + else if (streq(e, "lxc-libvirt")) + _id = "lxc-libvirt"; + else if (streq(e, "systemd-nspawn")) + _id = "systemd-nspawn"; + else + _id = "other"; + + r = 1; + +finish: + cached_found = r; + + cached_id = _id; + if (id) + *id = _id; + + return r; +} diff --git a/src/shared/virt.h b/src/shared/virt.h new file mode 100644 index 0000000000..571f3a71de --- /dev/null +++ b/src/shared/virt.h @@ -0,0 +1,24 @@ +/*-*- 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 . +***/ + +int detect_container(const char **id); -- cgit v1.2.3-54-g00ecf