]> code.delx.au - pulseaudio/blobdiff - src/pulsecore/core-util.c
core-util: Call fchown() only when necessary
[pulseaudio] / src / pulsecore / core-util.c
index a3c601303878fd8c0a1a232fe6a2c61ac6a14579..3e2c615ffa67c024e6d0e573e0009c0838223ba6 100644 (file)
 #include <fcntl.h>
 #include <unistd.h>
 #include <limits.h>
-#include <time.h>
 #include <ctype.h>
 #include <sys/types.h>
 #include <sys/stat.h>
-#include <sys/time.h>
 #include <dirent.h>
-#include <regex.h>
+
+#ifdef HAVE_LANGINFO_H
 #include <langinfo.h>
+#endif
+
+#ifdef HAVE_UNAME
 #include <sys/utsname.h>
-#include <sys/socket.h>
+#endif
+
+#if defined(HAVE_REGEX_H)
+#include <regex.h>
+#elif defined(HAVE_PCREPOSIX_H)
+#include <pcreposix.h>
+#endif
 
 #ifdef HAVE_STRTOF_L
 #include <locale.h>
 #include <windows.h>
 #endif
 
+#ifndef ENOTSUP
+#define ENOTSUP   135
+#endif
+
 #ifdef HAVE_PWD_H
 #include <pwd.h>
 #endif
 
 #ifdef __APPLE__
 #include <xlocale.h>
+#include <mach/mach_init.h>
+#include <mach/thread_act.h>
+#include <mach/thread_policy.h>
+#include <sys/sysctl.h>
 #endif
 
 #ifdef HAVE_DBUS
 #include "rtkit.h"
 #endif
 
-#ifdef __linux__
+#if defined(__linux__) && !defined(__ANDROID__)
 #include <sys/personality.h>
 #endif
 
 #include <pulse/utf8.h>
 
 #include <pulsecore/core-error.h>
-#include <pulsecore/winsock.h>
+#include <pulsecore/socket.h>
 #include <pulsecore/log.h>
 #include <pulsecore/macro.h>
 #include <pulsecore/thread.h>
 #include <pulsecore/strbuf.h>
 #include <pulsecore/usergroup.h>
 #include <pulsecore/strlist.h>
+#include <pulsecore/cpu-x86.h>
+#include <pulsecore/pipe.h>
 
 #include "core-util.h"
 
 #define MSG_NOSIGNAL 0
 #endif
 
+#define NEWLINE "\r\n"
+#define WHITESPACE "\n\r \t"
+
 static pa_strlist *recorded_env = NULL;
 
 #ifdef OS_IS_WIN32
+static fd_set nonblocking_fds;
+#endif
 
-#define PULSE_ROOTENV "PULSE_ROOT"
+#ifdef OS_IS_WIN32
 
-int pa_set_root(HANDLE handle) {
-    char library_path[MAX_PATH + sizeof(PULSE_ROOTENV) + 1], *sep;
+/* Returns the directory of the current DLL, with '/bin/' removed if it is the last component */
+char *pa_win32_get_toplevel(HANDLE handle) {
+    static char *toplevel = NULL;
 
-    strcpy(library_path, PULSE_ROOTENV "=");
+    if (!toplevel) {
+        char library_path[MAX_PATH];
+        char *p;
 
-    /* FIXME: Needs to set errno */
+        if (!GetModuleFileName(handle, library_path, MAX_PATH))
+            return NULL;
 
-    if (!GetModuleFileName(handle, library_path + sizeof(PULSE_ROOTENV), MAX_PATH))
-        return 0;
+        toplevel = pa_xstrdup(library_path);
 
-    sep = strrchr(library_path, PA_PATH_SEP_CHAR);
-    if (sep)
-        *sep = '\0';
+        p = strrchr(toplevel, PA_PATH_SEP_CHAR);
+        if (p)
+            *p = '\0';
 
-    if (_putenv(library_path) < 0)
-        return 0;
+        p = strrchr(toplevel, PA_PATH_SEP_CHAR);
+        if (p && pa_streq(p + 1, "bin"))
+            *p = '\0';
+    }
 
-    return 1;
+    return toplevel;
 }
 
 #endif
 
-/** Make a file descriptor nonblock. Doesn't do any error checking */
-void pa_make_fd_nonblock(int fd) {
+static void set_nonblock(int fd, bool nonblock) {
 
 #ifdef O_NONBLOCK
-    int v;
+    int v, nv;
     pa_assert(fd >= 0);
 
     pa_assert_se((v = fcntl(fd, F_GETFL)) >= 0);
 
-    if (!(v & O_NONBLOCK))
+    if (nonblock)
+        nv = v | O_NONBLOCK;
+    else
+        nv = v & ~O_NONBLOCK;
+
+    if (v != nv)
         pa_assert_se(fcntl(fd, F_SETFL, v|O_NONBLOCK) >= 0);
 
 #elif defined(OS_IS_WIN32)
-    u_long arg = 1;
+    u_long arg;
+
+    if (nonblock)
+        arg = 1;
+    else
+        arg = 0;
+
     if (ioctlsocket(fd, FIONBIO, &arg) < 0) {
         pa_assert_se(WSAGetLastError() == WSAENOTSOCK);
         pa_log_warn("Only sockets can be made non-blocking!");
+        return;
     }
+
+    /* There is no method to query status, so we remember all fds */
+    if (nonblock)
+        FD_SET(fd, &nonblocking_fds);
+    else
+        FD_CLR(fd, &nonblocking_fds);
 #else
     pa_log_warn("Non-blocking I/O not supported.!");
 #endif
 
 }
 
+/** Make a file descriptor nonblock. Doesn't do any error checking */
+void pa_make_fd_nonblock(int fd) {
+    set_nonblock(fd, true);
+}
+
+/** Make a file descriptor blocking. Doesn't do any error checking */
+void pa_make_fd_block(int fd) {
+    set_nonblock(fd, false);
+}
+
+/** Query if a file descriptor is non-blocking */
+bool pa_is_fd_nonblock(int fd) {
+
+#ifdef O_NONBLOCK
+    int v;
+    pa_assert(fd >= 0);
+
+    pa_assert_se((v = fcntl(fd, F_GETFL)) >= 0);
+
+    return !!(v & O_NONBLOCK);
+
+#elif defined(OS_IS_WIN32)
+    return !!FD_ISSET(fd, &nonblocking_fds);
+#else
+    return false;
+#endif
+
+}
+
 /* Set the FD_CLOEXEC flag for a fd */
 void pa_make_fd_cloexec(int fd) {
 
@@ -192,37 +266,88 @@ void pa_make_fd_cloexec(int fd) {
 
 }
 
-/** Creates a directory securely */
-int pa_make_secure_dir(const char* dir, mode_t m, uid_t uid, gid_t gid) {
+/** Creates a directory securely. Will create parent directories recursively if
+ * required. This will not update permissions on parent directories if they
+ * already exist, however. */
+int pa_make_secure_dir(const char* dir, mode_t m, uid_t uid, gid_t gid, bool update_perms) {
     struct stat st;
     int r, saved_errno;
+    bool retry = true;
 
     pa_assert(dir);
 
+again:
 #ifdef OS_IS_WIN32
     r = mkdir(dir);
 #else
-    {
+{
     mode_t u;
     u = umask((~m) & 0777);
     r = mkdir(dir, m);
     umask(u);
-    }
+}
 #endif
 
+    if (r < 0 && errno == ENOENT && retry) {
+        /* If a parent directory in the path doesn't exist, try to create that
+         * first, then try again. */
+        pa_make_secure_parent_dir(dir, m, uid, gid, false);
+        retry = false;
+        goto again;
+    }
+
     if (r < 0 && errno != EEXIST)
         return -1;
 
-#ifdef HAVE_CHOWN
-    if (uid == (uid_t)-1)
+#if defined(HAVE_FSTAT) && !defined(OS_IS_WIN32)
+{
+    int fd;
+    if ((fd = open(dir,
+#ifdef O_CLOEXEC
+                   O_CLOEXEC|
+#endif
+#ifdef O_NOCTTY
+                   O_NOCTTY|
+#endif
+#ifdef O_NOFOLLOW
+                   O_NOFOLLOW|
+#endif
+                   O_RDONLY)) < 0)
+        goto fail;
+
+    if (fstat(fd, &st) < 0) {
+        pa_assert_se(pa_close(fd) >= 0);
+        goto fail;
+    }
+
+    if (!S_ISDIR(st.st_mode)) {
+        pa_assert_se(pa_close(fd) >= 0);
+        errno = EEXIST;
+        goto fail;
+    }
+
+    if (!update_perms) {
+        pa_assert_se(pa_close(fd) >= 0);
+        return 0;
+    }
+
+#ifdef HAVE_FCHOWN
+    if (uid == (uid_t) -1)
         uid = getuid();
-    if (gid == (gid_t)-1)
+    if (gid == (gid_t) -1)
         gid = getgid();
-    (void) chown(dir, uid, gid);
+    if (((st.st_uid != uid) || (st.st_gid != gid)) && fchown(fd, uid, gid) < 0) {
+        pa_assert_se(pa_close(fd) >= 0);
+        goto fail;
+    }
 #endif
 
-#ifdef HAVE_CHMOD
-    chmod(dir, m);
+#ifdef HAVE_FCHMOD
+    (void) fchmod(fd, m);
+#endif
+
+    pa_assert_se(pa_close(fd) >= 0);
+}
 #endif
 
 #ifdef HAVE_LSTAT
@@ -269,14 +394,14 @@ char *pa_parent_dir(const char *fn) {
 }
 
 /* Creates a the parent directory of the specified path securely */
-int pa_make_secure_parent_dir(const char *fn, mode_t m, uid_t uid, gid_t gid) {
+int pa_make_secure_parent_dir(const char *fn, mode_t m, uid_t uid, gid_t gid, bool update_perms) {
     int ret = -1;
     char *dir;
 
     if (!(dir = pa_parent_dir(fn)))
         goto finish;
 
-    if (pa_make_secure_dir(dir, m, uid, gid) < 0)
+    if (pa_make_secure_dir(dir, m, uid, gid, update_perms) < 0)
         goto finish;
 
     ret = 0;
@@ -430,13 +555,15 @@ ssize_t pa_loop_write(int fd, const void*data, size_t size, int *type) {
     return ret;
 }
 
-/** Platform independent read function. Necessary since not all
+/** Platform independent close function. Necessary since not all
  * systems treat all file descriptors equal. */
 int pa_close(int fd) {
 
 #ifdef OS_IS_WIN32
     int ret;
 
+    FD_CLR(fd, &nonblocking_fds);
+
     if ((ret = closesocket(fd)) == 0)
         return 0;
 
@@ -501,7 +628,7 @@ void pa_check_signal_is_blocked(int sig) {
 /* The following function is based on an example from the GNU libc
  * documentation. This function is similar to GNU's asprintf(). */
 char *pa_sprintf_malloc(const char *format, ...) {
-    size_t  size = 100;
+    size_t size = 100;
     char *c = NULL;
 
     pa_assert(format);
@@ -531,7 +658,7 @@ char *pa_sprintf_malloc(const char *format, ...) {
 /* Same as the previous function, but use a va_list instead of an
  * ellipsis */
 char *pa_vsprintf_malloc(const char *format, va_list ap) {
-    size_t  size = 100;
+    size_t size = 100;
     char *c = NULL;
 
     pa_assert(format);
@@ -577,10 +704,16 @@ char *pa_strlcpy(char *b, const char *s, size_t l) {
     return b;
 }
 
+#ifdef _POSIX_PRIORITY_SCHEDULING
 static int set_scheduler(int rtprio) {
+#ifdef HAVE_SCHED_H
     struct sched_param sp;
-    int r;
 #ifdef HAVE_DBUS
+    int r;
+    long long rttime;
+#ifdef RLIMIT_RTTIME
+    struct rlimit rl;
+#endif
     DBusError error;
     DBusConnection *bus;
 
@@ -591,49 +724,109 @@ static int set_scheduler(int rtprio) {
     sp.sched_priority = rtprio;
 
 #ifdef SCHED_RESET_ON_FORK
-    if ((r = pthread_setschedparam(pthread_self(), SCHED_RR|SCHED_RESET_ON_FORK, &sp)) == 0) {
+    if (pthread_setschedparam(pthread_self(), SCHED_RR|SCHED_RESET_ON_FORK, &sp) == 0) {
         pa_log_debug("SCHED_RR|SCHED_RESET_ON_FORK worked.");
         return 0;
     }
 #endif
 
-    if ((r = pthread_setschedparam(pthread_self(), SCHED_RR, &sp)) == 0) {
+    if (pthread_setschedparam(pthread_self(), SCHED_RR, &sp) == 0) {
         pa_log_debug("SCHED_RR worked.");
         return 0;
     }
+#endif  /* HAVE_SCHED_H */
 
 #ifdef HAVE_DBUS
     /* Try to talk to RealtimeKit */
 
-    if (!(bus = dbus_bus_get(DBUS_BUS_SYSTEM, &error))) {
+    if (!(bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error))) {
         pa_log("Failed to connect to system bus: %s\n", error.message);
         dbus_error_free(&error);
         errno = -EIO;
         return -1;
     }
 
-    r = rtkit_make_realtime(bus, 0, rtprio);
-    dbus_connection_unref(bus);
+    /* We need to disable exit on disconnect because otherwise
+     * dbus_shutdown will kill us. See
+     * https://bugs.freedesktop.org/show_bug.cgi?id=16924 */
+    dbus_connection_set_exit_on_disconnect(bus, FALSE);
 
-    if (r >= 0) {
-        pa_log_debug("RealtimeKit worked.");
-        return 0;
+    rttime = rtkit_get_rttime_usec_max(bus);
+    if (rttime >= 0) {
+#ifdef RLIMIT_RTTIME
+        r = getrlimit(RLIMIT_RTTIME, &rl);
+
+        if (r >= 0 && (long long) rl.rlim_max > rttime) {
+            pa_log_info("Clamping rlimit-rttime to %lld for RealtimeKit\n", rttime);
+            rl.rlim_cur = rl.rlim_max = rttime;
+            r = setrlimit(RLIMIT_RTTIME, &rl);
+
+            if (r < 0)
+                pa_log("setrlimit() failed: %s", pa_cstrerror(errno));
+        }
+#endif
+        r = rtkit_make_realtime(bus, 0, rtprio);
+        dbus_connection_close(bus);
+        dbus_connection_unref(bus);
+
+        if (r >= 0) {
+            pa_log_debug("RealtimeKit worked.");
+            return 0;
+        }
+
+        errno = -r;
+    } else {
+        dbus_connection_close(bus);
+        dbus_connection_unref(bus);
+        errno = -rttime;
     }
 
-    errno = -r;
 #else
-    errno = r;
+    errno = 0;
 #endif
 
     return -1;
 }
+#endif
 
 /* Make the current thread a realtime thread, and acquire the highest
  * rtprio we can get that is less or equal the specified parameter. If
  * the thread is already realtime, don't do anything. */
 int pa_make_realtime(int rtprio) {
 
-#ifdef _POSIX_PRIORITY_SCHEDULING
+#if defined(OS_IS_DARWIN)
+    struct thread_time_constraint_policy ttcpolicy;
+    uint64_t freq = 0;
+    size_t size = sizeof(freq);
+    int ret;
+
+    ret = sysctlbyname("hw.cpufrequency", &freq, &size, NULL, 0);
+    if (ret < 0) {
+        pa_log_info("Unable to read CPU frequency, acquisition of real-time scheduling failed.");
+        return -1;
+    }
+
+    pa_log_debug("sysctl for hw.cpufrequency: %llu", freq);
+
+    /* See http://developer.apple.com/library/mac/#documentation/Darwin/Conceptual/KernelProgramming/scheduler/scheduler.html */
+    ttcpolicy.period = freq / 160;
+    ttcpolicy.computation = freq / 3300;
+    ttcpolicy.constraint = freq / 2200;
+    ttcpolicy.preemptible = 1;
+
+    ret = thread_policy_set(mach_thread_self(),
+                            THREAD_TIME_CONSTRAINT_POLICY,
+                            (thread_policy_t) &ttcpolicy,
+                            THREAD_TIME_CONSTRAINT_POLICY_COUNT);
+    if (ret) {
+        pa_log_info("Unable to set real-time thread priority (%08x).", ret);
+        return -1;
+    }
+
+    pa_log_info("Successfully acquired real-time thread priority.");
+    return 0;
+
+#elif defined(_POSIX_PRIORITY_SCHEDULING)
     int p;
 
     if (set_scheduler(rtprio) >= 0) {
@@ -642,20 +835,28 @@ int pa_make_realtime(int rtprio) {
     }
 
     for (p = rtprio-1; p >= 1; p--)
-        if (set_scheduler(p)) {
+        if (set_scheduler(p) >= 0) {
             pa_log_info("Successfully enabled SCHED_RR scheduling for thread, with priority %i, which is lower than the requested %i.", p, rtprio);
             return 0;
         }
+#elif defined(OS_IS_WIN32)
+    /* Windows only allows realtime scheduling to be set on a per process basis.
+     * Therefore, instead of making the thread realtime, just give it the highest non-realtime priority. */
+    if (SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL)) {
+        pa_log_info("Successfully enabled THREAD_PRIORITY_TIME_CRITICAL scheduling for thread.");
+        return 0;
+    }
 
-    pa_log_info("Failed to acquire real-time scheduling: %s", pa_cstrerror(errno));
-    return -1;
+    pa_log_warn("SetThreadPriority() failed: 0x%08X", GetLastError());
+    errno = EPERM;
 #else
-
     errno = ENOTSUP;
-    return -1;
 #endif
+    pa_log_info("Failed to acquire real-time scheduling: %s", pa_cstrerror(errno));
+    return -1;
 }
 
+#ifdef HAVE_SYS_RESOURCE_H
 static int set_nice(int nice_level) {
 #ifdef HAVE_DBUS
     DBusError error;
@@ -665,10 +866,12 @@ static int set_nice(int nice_level) {
     dbus_error_init(&error);
 #endif
 
+#ifdef HAVE_SYS_RESOURCE_H
     if (setpriority(PRIO_PROCESS, 0, nice_level) >= 0) {
         pa_log_debug("setpriority() worked.");
         return 0;
     }
+#endif
 
 #ifdef HAVE_DBUS
     /* Try to talk to RealtimeKit */
@@ -680,6 +883,11 @@ static int set_nice(int nice_level) {
         return -1;
     }
 
+    /* We need to disable exit on disconnect because otherwise
+     * dbus_shutdown will kill us. See
+     * https://bugs.freedesktop.org/show_bug.cgi?id=16924 */
+    dbus_connection_set_exit_on_disconnect(bus, FALSE);
+
     r = rtkit_make_high_priority(bus, 0, nice_level);
     dbus_connection_unref(bus);
 
@@ -693,6 +901,7 @@ static int set_nice(int nice_level) {
 
     return -1;
 }
+#endif
 
 /* Raise the priority of the current process as much as possible that
  * is <= the specified nice level..*/
@@ -707,7 +916,7 @@ int pa_raise_priority(int nice_level) {
     }
 
     for (n = nice_level+1; n < 0; n++)
-        if (set_nice(n) > 0) {
+        if (set_nice(n) >= 0) {
             pa_log_info("Successfully acquired nice level %i, which is lower than the requested %i.", n, nice_level);
             return 0;
         }
@@ -749,6 +958,7 @@ void pa_reset_priority(void) {
 }
 
 int pa_match(const char *expr, const char *v) {
+#if defined(HAVE_REGEX_H) || defined(HAVE_PCREPOSIX_H)
     int k;
     regex_t re;
     int r;
@@ -771,39 +981,90 @@ int pa_match(const char *expr, const char *v) {
         errno = EINVAL;
 
     return r;
+#else
+    errno = ENOSYS;
+    return -1;
+#endif
 }
 
 /* Try to parse a boolean string value.*/
 int pa_parse_boolean(const char *v) {
-    const char *expr;
-    int r;
     pa_assert(v);
 
-    /* First we check language independant */
-    if (!strcmp(v, "1") || v[0] == 'y' || v[0] == 'Y' || v[0] == 't' || v[0] == 'T' || !strcasecmp(v, "on"))
+    /* First we check language independent */
+    if (pa_streq(v, "1") || !strcasecmp(v, "y") || !strcasecmp(v, "t")
+            || !strcasecmp(v, "yes") || !strcasecmp(v, "true") || !strcasecmp(v, "on"))
         return 1;
-    else if (!strcmp(v, "0") || v[0] == 'n' || v[0] == 'N' || v[0] == 'f' || v[0] == 'F' || !strcasecmp(v, "off"))
+    else if (pa_streq(v, "0") || !strcasecmp(v, "n") || !strcasecmp(v, "f")
+                 || !strcasecmp(v, "no") || !strcasecmp(v, "false") || !strcasecmp(v, "off"))
         return 0;
 
-    /* And then we check language dependant */
+#ifdef HAVE_LANGINFO_H
+{
+    const char *expr;
+    /* And then we check language dependent */
     if ((expr = nl_langinfo(YESEXPR)))
         if (expr[0])
-            if ((r = pa_match(expr, v)) > 0)
+            if (pa_match(expr, v) > 0)
                 return 1;
 
     if ((expr = nl_langinfo(NOEXPR)))
         if (expr[0])
-            if ((r = pa_match(expr, v)) > 0)
+            if (pa_match(expr, v) > 0)
                 return 0;
+}
+#endif
 
     errno = EINVAL;
     return -1;
 }
 
+/* Try to parse a volume string to pa_volume_t. The allowed formats are:
+ * db, % and unsigned integer */
+int pa_parse_volume(const char *v, pa_volume_t *volume) {
+    int len, ret = -1;
+    uint32_t i;
+    double d;
+    char str[64];
+
+    pa_assert(v);
+    pa_assert(volume);
+
+    len = strlen(v);
+
+    if (len >= 64)
+        return -1;
+
+    memcpy(str, v, len + 1);
+
+    if (str[len - 1] == '%') {
+        str[len - 1] = '\0';
+        if (pa_atou(str, &i) == 0) {
+            *volume = PA_CLAMP_VOLUME((uint64_t) PA_VOLUME_NORM * i / 100);
+            ret = 0;
+        }
+    } else if (len > 2 && (str[len - 1] == 'b' || str[len - 1] == 'B') &&
+               (str[len - 2] == 'd' || str[len - 2] == 'D')) {
+        str[len - 2] = '\0';
+        if (pa_atod(str, &d) == 0) {
+            *volume = pa_sw_volume_from_dB(d);
+            ret = 0;
+        }
+    } else {
+        if (pa_atou(v, &i) == 0) {
+            *volume= PA_CLAMP_VOLUME(i);
+            ret = 0;
+        }
+
+    }
+
+    return ret;
+}
+
 /* Split the specified string wherever one of the strings in delimiter
  * occurs. Each time it is called returns a newly allocated string
  * with pa_xmalloc(). The variable state points to, should be
- * initiallized to NULL before the first call. */
+ * initialized to NULL before the first call. */
 char *pa_split(const char *c, const char *delimiter, const char**state) {
     const char *current = *state ? *state : c;
     size_t l;
@@ -820,8 +1081,28 @@ char *pa_split(const char *c, const char *delimiter, const char**state) {
     return pa_xstrndup(current, l);
 }
 
-/* What is interpreted as whitespace? */
-#define WHITESPACE " \t\n"
+/* Split the specified string wherever one of the strings in delimiter
+ * occurs. Each time it is called returns a pointer to the substring within the
+ * string and the length in 'n'. Note that the resultant string cannot be used
+ * as-is without the length parameter, since it is merely pointing to a point
+ * within the original string. The variable state points to, should be
+ * initialized to NULL before the first call. */
+const char *pa_split_in_place(const char *c, const char *delimiter, int *n, const char**state) {
+    const char *current = *state ? *state : c;
+    size_t l;
+
+    if (!*current)
+        return NULL;
+
+    l = strcspn(current, delimiter);
+    *state = current+l;
+
+    if (**state)
+        (*state)++;
+
+    *n = l;
+    return current;
+}
 
 /* Split a string into words. Otherwise similar to pa_split(). */
 char *pa_split_spaces(const char *c, const char **state) {
@@ -866,7 +1147,7 @@ const char *pa_sig2str(int sig) {
     }
 #else
 
-    switch(sig) {
+    switch (sig) {
 #ifdef SIGHUP
         case SIGHUP:    return "SIGHUP";
 #endif
@@ -977,8 +1258,7 @@ static int is_group(gid_t gid, const char *name) {
     int r = -1;
 
     errno = 0;
-    if (!(group = pa_getgrgid_malloc(gid)))
-    {
+    if (!(group = pa_getgrgid_malloc(gid))) {
         if (!errno)
             errno = ENOENT;
 
@@ -987,7 +1267,7 @@ static int is_group(gid_t gid, const char *name) {
         goto finish;
     }
 
-    r = strcmp(name, group->gr_name) == 0;
+    r = pa_streq(name, group->gr_name);
 
 finish:
     pa_getgrgid_free(group);
@@ -1037,15 +1317,14 @@ finish:
     return r;
 }
 
-/* Check whether the specifc user id is a member of the specified group */
+/* Check whether the specific user id is a member of the specified group */
 int pa_uid_in_group(uid_t uid, const char *name) {
     struct group *group = NULL;
     char **i;
     int r = -1;
 
     errno = 0;
-    if (!(group = pa_getgrnam_malloc(name)))
-    {
+    if (!(group = pa_getgrnam_malloc(name))) {
         if (!errno)
             errno = ENOENT;
         goto finish;
@@ -1074,14 +1353,13 @@ finish:
     return r;
 }
 
-/* Get the GID of a gfiven group, return (gid_t) -1 on failure. */
+/* Get the GID of a given group, return (gid_t) -1 on failure. */
 gid_t pa_get_gid_of_group(const char *name) {
     gid_t ret = (gid_t) -1;
     struct group *gr = NULL;
 
     errno = 0;
-    if (!(gr = pa_getgrnam_malloc(name)))
-    {
+    if (!(gr = pa_getgrnam_malloc(name))) {
         if (!errno)
             errno = ENOENT;
         goto finish;
@@ -1111,23 +1389,23 @@ int pa_check_in_group(gid_t g) {
 #else /* HAVE_GRP_H */
 
 int pa_own_uid_in_group(const char *name, gid_t *gid) {
-    errno = ENOSUP;
+    errno = ENOTSUP;
     return -1;
 
 }
 
 int pa_uid_in_group(uid_t uid, const char *name) {
-    errno = ENOSUP;
+    errno = ENOTSUP;
     return -1;
 }
 
 gid_t pa_get_gid_of_group(const char *name) {
-    errno = ENOSUP;
+    errno = ENOTSUP;
     return (gid_t) -1;
 }
 
 int pa_check_in_group(gid_t g) {
-    errno = ENOSUP;
+    errno = ENOTSUP;
     return -1;
 }
 
@@ -1149,18 +1427,18 @@ int pa_lock_fd(int fd, int b) {
     if (fcntl(fd, F_SETLKW, &f_lock) >= 0)
         return 0;
 
-    /* Perhaps the file descriptor qas opened for read only, than try again with a read lock. */
+    /* Perhaps the file descriptor was opened for read only, than try again with a read lock. */
     if (b && errno == EBADF) {
         f_lock.l_type = F_RDLCK;
         if (fcntl(fd, F_SETLKW, &f_lock) >= 0)
             return 0;
     }
 
-    pa_log("%slock: %s", !b? "un" : "", pa_cstrerror(errno));
+    pa_log("%slock: %s", !b ? "un" : "", pa_cstrerror(errno));
 #endif
 
 #ifdef OS_IS_WIN32
-    HANDLE h = (HANDLE)_get_osfhandle(fd);
+    HANDLE h = (HANDLE) _get_osfhandle(fd);
 
     if (b && LockFile(h, 0, 0, 0xFFFFFFFF, 0xFFFFFFFF))
         return 0;
@@ -1179,22 +1457,39 @@ int pa_lock_fd(int fd, int b) {
 char* pa_strip_nl(char *s) {
     pa_assert(s);
 
-    s[strcspn(s, "\r\n")] = 0;
+    s[strcspn(s, NEWLINE)] = 0;
+    return s;
+}
+
+char *pa_strip(char *s) {
+    char *e, *l = NULL;
+
+    /* Drops trailing whitespace. Modifies the string in
+     * place. Returns pointer to first non-space character */
+
+    s += strspn(s, WHITESPACE);
+
+    for (e = s; *e; e++)
+        if (!strchr(WHITESPACE, *e))
+            l = e;
+
+    if (l)
+        *(l+1) = 0;
+    else
+        *s = 0;
+
     return s;
 }
 
 /* Create a temporary lock file and lock it. */
 int pa_lock_lockfile(const char *fn) {
-    int fd = -1;
+    int fd;
     pa_assert(fn);
 
     for (;;) {
         struct stat st;
 
-        if ((fd = open(fn, O_CREAT|O_RDWR
-#ifdef O_NOCTTY
-                       |O_NOCTTY
-#endif
+        if ((fd = pa_open_cloexec(fn, O_CREAT|O_RDWR
 #ifdef O_NOFOLLOW
                        |O_NOFOLLOW
 #endif
@@ -1228,8 +1523,6 @@ int pa_lock_lockfile(const char *fn) {
             fd = -1;
             goto fail;
         }
-
-        fd = -1;
     }
 
     return fd;
@@ -1245,7 +1538,7 @@ fail:
     return -1;
 }
 
-/* Unlock a temporary lcok file */
+/* Unlock a temporary lock file */
 int pa_unlock_lockfile(const char *fn, int fd) {
     int r = 0;
     pa_assert(fd >= 0);
@@ -1270,31 +1563,62 @@ int pa_unlock_lockfile(const char *fn, int fd) {
     return r;
 }
 
-static char *get_pulse_home(void) {
-    char *h;
+static char *get_config_home(char *home) {
+    char *t;
+
+    t = getenv("XDG_CONFIG_HOME");
+    if (t)
+        return pa_xstrdup(t);
+
+    return pa_sprintf_malloc("%s" PA_PATH_SEP ".config", home);
+}
+
+static int check_ours(const char *p) {
     struct stat st;
-    char *ret = NULL;
 
-    if (!(h = pa_get_home_dir_malloc())) {
+    pa_assert(p);
+
+    if (stat(p, &st) < 0)
+        return -errno;
+
+#ifdef HAVE_GETUID
+    if (st.st_uid != getuid())
+        return -EACCES;
+#endif
+
+    return 0;
+}
+
+static char *get_pulse_home(void) {
+    char *h, *ret, *config_home;
+    int t;
+
+    h = pa_get_home_dir_malloc();
+    if (!h) {
         pa_log_error("Failed to get home directory.");
         return NULL;
     }
 
-    if (stat(h, &st) < 0) {
-        pa_log_error("Failed to stat home directory %s: %s", h, pa_cstrerror(errno));
-        goto finish;
-    }
-
-    if (st.st_uid != getuid()) {
-        pa_log_error("Home directory %s not ours.", h);
-        errno = EACCES;
-        goto finish;
+    t = check_ours(h);
+    if (t < 0 && t != -ENOENT) {
+        pa_log_error("Home directory not accessible: %s", pa_cstrerror(-t));
+        pa_xfree(h);
+        return NULL;
     }
 
+    /* If the old directory exists, use it. */
     ret = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse", h);
+    if (access(ret, F_OK) >= 0) {
+        free(h);
+        return ret;
+    }
+    free(ret);
 
-finish:
-    pa_xfree(h);
+    /* Otherwise go for the XDG compliant directory. */
+    config_home = get_config_home(h);
+    free(h);
+    ret = pa_sprintf_malloc("%s" PA_PATH_SEP "pulse", config_home);
+    free(config_home);
 
     return ret;
 }
@@ -1312,8 +1636,8 @@ char *pa_get_state_dir(void) {
     /* If PULSE_STATE_PATH and PULSE_RUNTIME_PATH point to the same
      * dir then this will break. */
 
-    if (pa_make_secure_dir(d, 0700U, (uid_t) -1, (gid_t) -1) < 0)  {
-        pa_log_error("Failed to create secure directory: %s", pa_cstrerror(errno));
+    if (pa_make_secure_dir(d, 0700U, (uid_t) -1, (gid_t) -1, true) < 0) {
+        pa_log_error("Failed to create secure directory (%s): %s", d, pa_cstrerror(errno));
         pa_xfree(d);
         return NULL;
     }
@@ -1371,19 +1695,10 @@ static char* make_random_dir(mode_t m) {
         "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
         "0123456789";
 
-    const char *tmpdir;
     char *fn;
     size_t pathlen;
 
-    if (!(tmpdir = getenv("TMPDIR")))
-        if (!(tmpdir = getenv("TMP")))
-            if (!(tmpdir = getenv("TEMP")))
-                tmpdir = getenv("TEMPDIR");
-
-    if (!tmpdir || !pa_is_path_absolute(tmpdir))
-        tmpdir = "/tmp";
-
-    fn = pa_sprintf_malloc("%s/pulse-XXXXXXXXXXXX", tmpdir);
+    fn = pa_sprintf_malloc("%s" PA_PATH_SEP "pulse-XXXXXXXXXXXX", pa_get_temp_dir());
     pathlen = strlen(fn);
 
     for (;;) {
@@ -1396,7 +1711,11 @@ static char* make_random_dir(mode_t m) {
             fn[i] = table[rand() % (sizeof(table)-1)];
 
         u = umask((~m) & 0777);
+#ifndef OS_IS_WIN32
         r = mkdir(fn, m);
+#else
+        r = mkdir(fn);
+#endif
 
         saved_errno = errno;
         umask(u);
@@ -1419,6 +1738,7 @@ static int make_random_dir_and_link(mode_t m, const char *k) {
     if (!(p = make_random_dir(m)))
         return -1;
 
+#ifdef HAVE_SYMLINK
     if (symlink(p, k) < 0) {
         int saved_errno = errno;
 
@@ -1431,6 +1751,10 @@ static int make_random_dir_and_link(mode_t m, const char *k) {
         errno = saved_errno;
         return -1;
     }
+#else
+    pa_xfree(p);
+    return -1;
+#endif
 
     pa_xfree(p);
     return 0;
@@ -1438,38 +1762,56 @@ static int make_random_dir_and_link(mode_t m, const char *k) {
 
 char *pa_get_runtime_dir(void) {
     char *d, *k = NULL, *p = NULL, *t = NULL, *mid;
-    struct stat st;
     mode_t m;
 
     /* The runtime directory shall contain dynamic data that needs NOT
-     * to be kept accross reboots and is usuallly private to the user,
+     * to be kept across reboots and is usually private to the user,
      * except in system mode, where it might be accessible by other
      * users, too. Since we need POSIX locking and UNIX sockets in
-     * this directory, we link it to a random subdir in /tmp, if it
-     * was not explicitly configured. */
+     * this directory, we try XDG_RUNTIME_DIR first, and if that isn't
+     * set create a directory in $HOME and link it to a random subdir
+     * in /tmp, if it was not explicitly configured. */
 
     m = pa_in_system_mode() ? 0755U : 0700U;
 
-    if ((d = getenv("PULSE_RUNTIME_PATH"))) {
+    /* Use the explicitly configured value if it is set */
+    d = getenv("PULSE_RUNTIME_PATH");
+    if (d) {
 
-        if (pa_make_secure_dir(d, m, (uid_t) -1, (gid_t) -1) < 0)  {
-            pa_log_error("Failed to create secure directory: %s", pa_cstrerror(errno));
+        if (pa_make_secure_dir(d, m, (uid_t) -1, (gid_t) -1, true) < 0) {
+            pa_log_error("Failed to create secure directory (%s): %s", d, pa_cstrerror(errno));
             goto fail;
         }
 
         return pa_xstrdup(d);
     }
 
-    if (!(d = get_pulse_home()))
+    /* Use the XDG standard for the runtime directory. */
+    d = getenv("XDG_RUNTIME_DIR");
+    if (d) {
+        k = pa_sprintf_malloc("%s" PA_PATH_SEP "pulse", d);
+
+        if (pa_make_secure_dir(k, m, (uid_t) -1, (gid_t) -1, true) < 0) {
+            pa_log_error("Failed to create secure directory (%s): %s", k, pa_cstrerror(errno));
+            goto fail;
+        }
+
+        return k;
+    }
+
+    /* XDG_RUNTIME_DIR wasn't set, use the old legacy fallback */
+    d = get_pulse_home();
+    if (!d)
         goto fail;
 
-    if (pa_make_secure_dir(d, m, (uid_t) -1, (gid_t) -1) < 0)  {
-        pa_log_error("Failed to create secure directory: %s", pa_cstrerror(errno));
+    if (pa_make_secure_dir(d, m, (uid_t) -1, (gid_t) -1, true) < 0) {
+        pa_log_error("Failed to create secure directory (%s): %s", d, pa_cstrerror(errno));
         pa_xfree(d);
         goto fail;
     }
 
-    if (!(mid = pa_machine_id())) {
+    mid = pa_machine_id();
+    if (!mid) {
         pa_xfree(d);
         goto fail;
     }
@@ -1479,16 +1821,17 @@ char *pa_get_runtime_dir(void) {
     pa_xfree(mid);
 
     for (;;) {
-        /* OK, first let's check if the "runtime" symlink is already
-         * existant */
+        /* OK, first let's check if the "runtime" symlink already exists */
 
-        if (!(p = pa_readlink(k))) {
+        p = pa_readlink(k);
+        if (!p) {
 
             if (errno != ENOENT) {
                 pa_log_error("Failed to stat runtime directory %s: %s", k, pa_cstrerror(errno));
                 goto fail;
             }
 
+#ifdef HAVE_SYMLINK
             /* Hmm, so the runtime directory didn't exist yet, so let's
              * create one in /tmp and symlink that to it */
 
@@ -1501,6 +1844,12 @@ char *pa_get_runtime_dir(void) {
 
                 goto fail;
             }
+#else
+            /* No symlink possible, so let's just create the runtime directly
+             * Do not check again if it exists since it cannot be a symlink */
+            if (mkdir(k) < 0 && errno != EEXIST)
+                goto fail;
+#endif
 
             return k;
         }
@@ -1512,9 +1861,10 @@ char *pa_get_runtime_dir(void) {
             goto fail;
         }
 
-        /* Hmm, so this symlink is still around, make sure nobody fools
-         * us */
-
+        /* Hmm, so this symlink is still around, make sure nobody fools us */
+#ifdef HAVE_LSTAT
+{
+        struct stat st;
         if (lstat(p, &st) < 0) {
 
             if (errno != ENOENT) {
@@ -1534,6 +1884,8 @@ char *pa_get_runtime_dir(void) {
 
             pa_log_info("Hmm, runtime path exists, but points to an invalid directory. Changing runtime directory.");
         }
+}
+#endif
 
         pa_xfree(p);
         p = NULL;
@@ -1555,7 +1907,7 @@ char *pa_get_runtime_dir(void) {
             pa_xfree(t);
             t = NULL;
 
-            /* Hmm, someone lese was quicker then us. Let's give
+            /* Hmm, someone else was quicker then us. Let's give
              * him some time to finish, and retry. */
             pa_msleep(10);
             continue;
@@ -1588,24 +1940,10 @@ fail:
  * stored there.*/
 FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result) {
     const char *fn;
-#ifdef OS_IS_WIN32
-    char buf[PATH_MAX];
-
-    if (!getenv(PULSE_ROOTENV))
-        pa_set_root(NULL);
-#endif
+    FILE *f;
 
     if (env && (fn = getenv(env))) {
-        FILE *f;
-
-#ifdef OS_IS_WIN32
-        if (!ExpandEnvironmentStrings(fn, buf, PATH_MAX))
-            /* FIXME: Needs to set errno! */
-            return NULL;
-        fn = buf;
-#endif
-
-        if ((f = fopen(fn, "r"))) {
+        if ((f = pa_fopen_cloexec(fn, "r"))) {
             if (result)
                 *result = pa_xstrdup(fn);
 
@@ -1620,26 +1958,23 @@ FILE *pa_open_config_file(const char *global, const char *local, const char *env
         const char *e;
         char *lfn;
         char *h;
-        FILE *f;
 
-        if ((e = getenv("PULSE_CONFIG_PATH")))
+        if ((e = getenv("PULSE_CONFIG_PATH"))) {
             fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", e, local);
-        else if ((h = pa_get_home_dir_malloc())) {
+            f = pa_fopen_cloexec(fn, "r");
+        } else if ((h = pa_get_home_dir_malloc())) {
             fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse" PA_PATH_SEP "%s", h, local);
+            f = pa_fopen_cloexec(fn, "r");
+            if (!f) {
+                free(lfn);
+                fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP ".config/pulse" PA_PATH_SEP "%s", h, local);
+                f = pa_fopen_cloexec(fn, "r");
+            }
             pa_xfree(h);
         } else
             return NULL;
 
-#ifdef OS_IS_WIN32
-        if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX)) {
-            /* FIXME: Needs to set errno! */
-            pa_xfree(lfn);
-            return NULL;
-        }
-        fn = buf;
-#endif
-
-        if ((f = fopen(fn, "r"))) {
+        if (f) {
             if (result)
                 *result = pa_xstrdup(fn);
 
@@ -1657,22 +1992,26 @@ FILE *pa_open_config_file(const char *global, const char *local, const char *env
     }
 
     if (global) {
-        FILE *f;
+        char *gfn;
 
 #ifdef OS_IS_WIN32
-        if (!ExpandEnvironmentStrings(global, buf, PATH_MAX))
-            /* FIXME: Needs to set errno! */
-            return NULL;
-        global = buf;
+        if (strncmp(global, PA_DEFAULT_CONFIG_DIR, strlen(PA_DEFAULT_CONFIG_DIR)) == 0)
+            gfn = pa_sprintf_malloc("%s" PA_PATH_SEP "etc" PA_PATH_SEP "pulse%s",
+                                    pa_win32_get_toplevel(NULL),
+                                    global + strlen(PA_DEFAULT_CONFIG_DIR));
+        else
 #endif
+        gfn = pa_xstrdup(global);
 
-        if ((f = fopen(global, "r"))) {
-
+        if ((f = pa_fopen_cloexec(gfn, "r"))) {
             if (result)
-                *result = pa_xstrdup(global);
+                *result = gfn;
+            else
+                pa_xfree(gfn);
 
             return f;
         }
+        pa_xfree(gfn);
     }
 
     errno = ENOENT;
@@ -1681,22 +2020,8 @@ FILE *pa_open_config_file(const char *global, const char *local, const char *env
 
 char *pa_find_config_file(const char *global, const char *local, const char *env) {
     const char *fn;
-#ifdef OS_IS_WIN32
-    char buf[PATH_MAX];
-
-    if (!getenv(PULSE_ROOTENV))
-        pa_set_root(NULL);
-#endif
 
     if (env && (fn = getenv(env))) {
-
-#ifdef OS_IS_WIN32
-        if (!ExpandEnvironmentStrings(fn, buf, PATH_MAX))
-            /* FIXME: Needs to set errno! */
-            return NULL;
-        fn = buf;
-#endif
-
         if (access(fn, R_OK) == 0)
             return pa_xstrdup(fn);
 
@@ -1717,15 +2042,6 @@ char *pa_find_config_file(const char *global, const char *local, const char *env
         } else
             return NULL;
 
-#ifdef OS_IS_WIN32
-        if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX)) {
-            /* FIXME: Needs to set errno! */
-            pa_xfree(lfn);
-            return NULL;
-        }
-        fn = buf;
-#endif
-
         if (access(fn, R_OK) == 0) {
             char *r = pa_xstrdup(fn);
             pa_xfree(lfn);
@@ -1742,15 +2058,20 @@ char *pa_find_config_file(const char *global, const char *local, const char *env
     }
 
     if (global) {
+        char *gfn;
+
 #ifdef OS_IS_WIN32
-        if (!ExpandEnvironmentStrings(global, buf, PATH_MAX))
-            /* FIXME: Needs to set errno! */
-            return NULL;
-        global = buf;
+        if (strncmp(global, PA_DEFAULT_CONFIG_DIR, strlen(PA_DEFAULT_CONFIG_DIR)) == 0)
+            gfn = pa_sprintf_malloc("%s" PA_PATH_SEP "etc" PA_PATH_SEP "pulse%s",
+                                    pa_win32_get_toplevel(NULL),
+                                    global + strlen(PA_DEFAULT_CONFIG_DIR));
+        else
 #endif
+        gfn = pa_xstrdup(global);
 
-        if (access(global, R_OK) == 0)
-            return pa_xstrdup(global);
+        if (access(gfn, R_OK) == 0)
+            return gfn;
+        pa_xfree(gfn);
     }
 
     errno = ENOENT;
@@ -1767,7 +2088,7 @@ char *pa_hexstr(const uint8_t* d, size_t dlength, char *s, size_t slength) {
     pa_assert(s);
     pa_assert(slength > 0);
 
-    while (i < dlength && j+3 <= slength) {
+    while (j+2 < slength && i < dlength) {
         s[j++] = hex[*d >> 4];
         s[j++] = hex[*d & 0xF];
 
@@ -1823,7 +2144,7 @@ size_t pa_parsehex(const char *p, uint8_t *d, size_t dlength) {
 }
 
 /* Returns nonzero when *s starts with *pfx */
-pa_bool_t pa_startswith(const char *s, const char *pfx) {
+bool pa_startswith(const char *s, const char *pfx) {
     size_t l;
 
     pa_assert(s);
@@ -1835,7 +2156,7 @@ pa_bool_t pa_startswith(const char *s, const char *pfx) {
 }
 
 /* Returns nonzero when *s ends with *sfx */
-pa_bool_t pa_endswith(const char *s, const char *sfx) {
+bool pa_endswith(const char *s, const char *sfx) {
     size_t l1, l2;
 
     pa_assert(s);
@@ -1844,10 +2165,10 @@ pa_bool_t pa_endswith(const char *s, const char *sfx) {
     l1 = strlen(s);
     l2 = strlen(sfx);
 
-    return l1 >= l2 && strcmp(s+l1-l2, sfx) == 0;
+    return l1 >= l2 && pa_streq(s + l1 - l2, sfx);
 }
 
-pa_bool_t pa_is_path_absolute(const char *fn) {
+bool pa_is_path_absolute(const char *fn) {
     pa_assert(fn);
 
 #ifndef OS_IS_WIN32
@@ -1874,66 +2195,72 @@ char *pa_make_path_absolute(const char *p) {
     return r;
 }
 
-/* if fn is null return the PulseAudio run time path in s (~/.pulse)
- * if fn is non-null and starts with / return fn
- * otherwise append fn to the run time path and return it */
-static char *get_path(const char *fn, pa_bool_t prependmid, pa_bool_t rt) {
+/* If fn is NULL, return the PulseAudio runtime or state dir (depending on the
+ * rt parameter). If fn is non-NULL and starts with /, return fn. Otherwise,
+ * append fn to the runtime/state dir and return it. */
+static char *get_path(const char *fn, bool prependmid, bool rt) {
     char *rtp;
 
     rtp = rt ? pa_get_runtime_dir() : pa_get_state_dir();
 
     if (fn) {
-        char *r;
+        char *r, *canonical_rtp;
 
-        if (pa_is_path_absolute(fn))
+        if (pa_is_path_absolute(fn)) {
+            pa_xfree(rtp);
             return pa_xstrdup(fn);
+        }
 
         if (!rtp)
             return NULL;
 
+        /* Hopefully make the path smaller to avoid 108 char limit (fdo#44680) */
+        if ((canonical_rtp = pa_realpath(rtp))) {
+            if (strlen(rtp) >= strlen(canonical_rtp))
+                pa_xfree(rtp);
+            else {
+                pa_xfree(canonical_rtp);
+                canonical_rtp = rtp;
+            }
+        } else
+            canonical_rtp = rtp;
+
         if (prependmid) {
             char *mid;
 
             if (!(mid = pa_machine_id())) {
-                pa_xfree(rtp);
+                pa_xfree(canonical_rtp);
                 return NULL;
             }
 
-            r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s-%s", rtp, mid, fn);
+            r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s-%s", canonical_rtp, mid, fn);
             pa_xfree(mid);
         } else
-            r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", rtp, fn);
+            r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", canonical_rtp, fn);
 
-        pa_xfree(rtp);
+        pa_xfree(canonical_rtp);
         return r;
     } else
         return rtp;
 }
 
 char *pa_runtime_path(const char *fn) {
-    return get_path(fn, FALSE, TRUE);
+    return get_path(fn, false, true);
 }
 
-char *pa_state_path(const char *fn, pa_bool_t appendmid) {
-    return get_path(fn, appendmid, FALSE);
+char *pa_state_path(const char *fn, bool appendmid) {
+    return get_path(fn, appendmid, false);
 }
 
 /* Convert the string s to a signed integer in *ret_i */
 int pa_atoi(const char *s, int32_t *ret_i) {
-    char *x = NULL;
     long l;
 
     pa_assert(s);
     pa_assert(ret_i);
 
-    errno = 0;
-    l = strtol(s, &x, 0);
-
-    if (!x || *x || errno) {
-        if (!errno)
-            errno = EINVAL;
+    if (pa_atol(s, &l) < 0)
         return -1;
-    }
 
     if ((int32_t) l != l) {
         errno = ERANGE;
@@ -1972,6 +2299,28 @@ int pa_atou(const char *s, uint32_t *ret_u) {
     return 0;
 }
 
+/* Convert the string s to a signed long integer in *ret_l. */
+int pa_atol(const char *s, long *ret_l) {
+    char *x = NULL;
+    long l;
+
+    pa_assert(s);
+    pa_assert(ret_l);
+
+    errno = 0;
+    l = strtol(s, &x, 0);
+
+    if (!x || *x || errno) {
+        if (!errno)
+            errno = EINVAL;
+        return -1;
+    }
+
+    *ret_l = l;
+
+    return 0;
+}
+
 #ifdef HAVE_STRTOF_L
 static locale_t c_locale = NULL;
 
@@ -2095,7 +2444,7 @@ void *pa_will_need(const void *p, size_t l) {
 #endif
     const void *a;
     size_t size;
-    int r;
+    int r = ENOTSUP;
     size_t bs;
 
     pa_assert(p);
@@ -2173,6 +2522,7 @@ void pa_close_pipe(int fds[2]) {
 }
 
 char *pa_readlink(const char *p) {
+#ifdef HAVE_READLINK
     size_t l = 100;
 
     for (;;) {
@@ -2194,6 +2544,9 @@ char *pa_readlink(const char *p) {
         pa_xfree(c);
         l *= 2;
     }
+#else
+    return NULL;
+#endif
 }
 
 int pa_close_all(int except_fd, ...) {
@@ -2232,6 +2585,7 @@ int pa_close_all(int except_fd, ...) {
 }
 
 int pa_close_allv(const int except_fds[]) {
+#ifndef OS_IS_WIN32
     struct rlimit rl;
     int maxfd, fd;
 
@@ -2244,7 +2598,7 @@ int pa_close_allv(const int except_fds[]) {
         struct dirent *de;
 
         while ((de = readdir(d))) {
-            pa_bool_t found;
+            bool found;
             long l;
             char *e = NULL;
             int i;
@@ -2274,10 +2628,10 @@ int pa_close_allv(const int except_fds[]) {
             if (fd == dirfd(d))
                 continue;
 
-            found = FALSE;
+            found = false;
             for (i = 0; except_fds[i] >= 0; i++)
                 if (except_fds[i] == fd) {
-                    found = TRUE;
+                    found = true;
                     break;
                 }
 
@@ -2306,12 +2660,12 @@ int pa_close_allv(const int except_fds[]) {
 
     for (fd = 3; fd < maxfd; fd++) {
         int i;
-        pa_bool_t found;
+        bool found;
 
-        found = FALSE;
+        found = false;
         for (i = 0; except_fds[i] >= 0; i++)
             if (except_fds[i] == fd) {
-                found = TRUE;
+                found = true;
                 break;
             }
 
@@ -2321,6 +2675,7 @@ int pa_close_allv(const int except_fds[]) {
         if (pa_close(fd) < 0 && errno != EBADF)
             return -1;
     }
+#endif  /* !OS_IS_WIN32 */
 
     return 0;
 }
@@ -2361,6 +2716,7 @@ int pa_unblock_sigs(int except, ...) {
 }
 
 int pa_unblock_sigsv(const int except[]) {
+#ifndef OS_IS_WIN32
     int i;
     sigset_t ss;
 
@@ -2372,6 +2728,9 @@ int pa_unblock_sigsv(const int except[]) {
             return -1;
 
     return sigprocmask(SIG_SETMASK, &ss, NULL);
+#else
+    return 0;
+#endif
 }
 
 int pa_reset_sigs(int except, ...) {
@@ -2397,7 +2756,7 @@ int pa_reset_sigs(int except, ...) {
         p[i++] = except;
 
         while ((sig = va_arg(ap, int)) >= 0)
-            sig = p[i++];
+            p[i++] = sig;
     }
     p[i] = -1;
 
@@ -2410,15 +2769,16 @@ int pa_reset_sigs(int except, ...) {
 }
 
 int pa_reset_sigsv(const int except[]) {
+#ifndef OS_IS_WIN32
     int sig;
 
     for (sig = 1; sig < NSIG; sig++) {
-        pa_bool_t reset = TRUE;
+        bool reset = true;
 
         switch (sig) {
             case SIGKILL:
             case SIGSTOP:
-                reset = FALSE;
+                reset = false;
                 break;
 
             default: {
@@ -2426,7 +2786,7 @@ int pa_reset_sigsv(const int except[]) {
 
                 for (i = 0; except[i] > 0; i++) {
                     if (sig == except[i]) {
-                        reset = FALSE;
+                        reset = false;
                         break;
                     }
                 }
@@ -2446,6 +2806,7 @@ int pa_reset_sigsv(const int except[]) {
                     return -1;
         }
     }
+#endif
 
     return 0;
 }
@@ -2456,7 +2817,11 @@ void pa_set_env(const char *key, const char *value) {
 
     /* This is not thread-safe */
 
-    putenv(pa_sprintf_malloc("%s=%s", key, value));
+#ifdef OS_IS_WIN32
+    SetEnvironmentVariable(key, value);
+#else
+    setenv(key, value, 1);
+#endif
 }
 
 void pa_set_env_and_record(const char *key, const char *value) {
@@ -2481,20 +2846,44 @@ void pa_unset_env_recorded(void) {
         if (!s)
             break;
 
+#ifdef OS_IS_WIN32
+        SetEnvironmentVariable(s, NULL);
+#else
         unsetenv(s);
+#endif
         pa_xfree(s);
     }
 }
 
-pa_bool_t pa_in_system_mode(void) {
+bool pa_in_system_mode(void) {
     const char *e;
 
     if (!(e = getenv("PULSE_SYSTEM")))
-        return FALSE;
+        return false;
 
     return !!atoi(e);
 }
 
+/* Checks a whitespace-separated list of words in haystack for needle */
+bool pa_str_in_list_spaces(const char *haystack, const char *needle) {
+    char *s;
+    const char *state = NULL;
+
+    if (!haystack || !needle)
+        return false;
+
+    while ((s = pa_split_spaces(haystack, &state))) {
+        if (pa_streq(needle, s)) {
+            pa_xfree(s);
+            return true;
+        }
+
+        pa_xfree(s);
+    }
+
+    return false;
+}
+
 char *pa_get_user_name_malloc(void) {
     ssize_t k;
     char *u;
@@ -2561,11 +2950,12 @@ char *pa_machine_id(void) {
     /* The returned value is supposed be some kind of ascii identifier
      * that is unique and stable across reboots. */
 
-    /* First we try the D-Bus UUID, which is the best option we have,
-     * since it fits perfectly our needs and is not as volatile as the
-     * hostname which might be set from dhcp. */
+    /* First we try the /etc/machine-id, which is the best option we
+     * have, since it fits perfectly our needs and is not as volatile
+     * as the hostname which might be set from dhcp. */
 
-    if ((f = fopen(PA_MACHINE_ID, "r"))) {
+    if ((f = pa_fopen_cloexec(PA_MACHINE_ID, "r")) ||
+        (f = pa_fopen_cloexec(PA_MACHINE_ID_FALLBACK, "r"))) {
         char ln[34] = "", *r;
 
         r = fgets(ln, sizeof(ln)-1, f);
@@ -2580,30 +2970,46 @@ char *pa_machine_id(void) {
     if ((h = pa_get_host_name_malloc()))
         return h;
 
+#if !defined(OS_IS_WIN32) && !defined(__ANDROID__)
     /* If no hostname was set we use the POSIX hostid. It's usually
      * the IPv4 address.  Might not be that stable. */
-    return pa_sprintf_malloc("%08lx", (unsigned long) gethostid);
+    return pa_sprintf_malloc("%08lx", (unsigned long) gethostid());
+#else
+    return NULL;
+#endif
 }
 
 char *pa_session_id(void) {
     const char *e;
 
-    if (!(e = getenv("XDG_SESSION_COOKIE")))
+    e = getenv("XDG_SESSION_ID");
+    if (!e)
         return NULL;
 
     return pa_utf8_filter(e);
 }
 
 char *pa_uname_string(void) {
+#ifdef HAVE_UNAME
     struct utsname u;
 
     pa_assert_se(uname(&u) >= 0);
 
     return pa_sprintf_malloc("%s %s %s %s", u.sysname, u.machine, u.release, u.version);
+#endif
+#ifdef OS_IS_WIN32
+    OSVERSIONINFO i;
+
+    pa_zero(i);
+    i.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+    pa_assert_se(GetVersionEx(&i));
+
+    return pa_sprintf_malloc("Windows %d.%d (%d) %s", i.dwMajorVersion, i.dwMinorVersion, i.dwBuildNumber, i.szCSDVersion);
+#endif
 }
 
 #ifdef HAVE_VALGRIND_MEMCHECK_H
-pa_bool_t pa_in_valgrind(void) {
+bool pa_in_valgrind(void) {
     static int b = 0;
 
     /* To make heisenbugs a bit simpler to find we check for $VALGRIND
@@ -2680,18 +3086,40 @@ char *pa_replace(const char*s, const char*a, const char *b) {
     return pa_strbuf_tostring_free(sb);
 }
 
+char *pa_escape(const char *p, const char *chars) {
+    const char *s;
+    const char *c;
+    pa_strbuf *buf = pa_strbuf_new();
+
+    for (s = p; *s; ++s) {
+        if (*s == '\\')
+            pa_strbuf_putc(buf, '\\');
+        else if (chars) {
+            for (c = chars; *c; ++c) {
+                if (*s == *c) {
+                    pa_strbuf_putc(buf, '\\');
+                    break;
+                }
+            }
+        }
+        pa_strbuf_putc(buf, *s);
+    }
+
+    return pa_strbuf_tostring_free(buf);
+}
+
 char *pa_unescape(char *p) {
     char *s, *d;
-    pa_bool_t escaped = FALSE;
+    bool escaped = false;
 
     for (s = p, d = p; *s; s++) {
         if (!escaped && *s == '\\') {
-            escaped = TRUE;
+            escaped = true;
             continue;
         }
 
         *(d++) = *s;
-        escaped = FALSE;
+        escaped = false;
     }
 
     *d = 0;
@@ -2703,13 +3131,13 @@ char *pa_realpath(const char *path) {
     char *t;
     pa_assert(path);
 
-    /* We want only abolsute paths */
+    /* We want only absolute paths */
     if (path[0] != '/') {
         errno = EINVAL;
         return NULL;
     }
 
-#if defined(__GLIBC__) || defined(__APPLE__)
+#if defined(__GLIBC__)
     {
         char *r;
 
@@ -2726,10 +3154,17 @@ char *pa_realpath(const char *path) {
         char *path_buf;
         path_buf = pa_xmalloc(PATH_MAX);
 
+#if defined(OS_IS_WIN32)
+        if (!(t = _fullpath(path_buf, path, _MAX_PATH))) {
+            pa_xfree(path_buf);
+            return NULL;
+        }
+#else
         if (!(t = realpath(path, path_buf))) {
             pa_xfree(path_buf);
             return NULL;
         }
+#endif
     }
 #else
 #error "It's not clear whether this system supports realpath(..., NULL) like GNU libc does. If it doesn't we need a private version of realpath() here."
@@ -2822,28 +3257,266 @@ size_t pa_pipe_buf(int fd) {
 
 void pa_reset_personality(void) {
 
-#ifdef __linux__
+#if defined(__linux__) && !defined(__ANDROID__)
     if (personality(PER_LINUX) < 0)
         pa_log_warn("Uh, personality() failed: %s", pa_cstrerror(errno));
 #endif
 
 }
 
-#if defined(__linux__) && !defined(__OPTIMIZE__)
-
-pa_bool_t pa_run_from_build_tree(void) {
+bool pa_run_from_build_tree(void) {
     char *rp;
-    pa_bool_t b = FALSE;
+    static bool b = false;
+
+    PA_ONCE_BEGIN {
+        if ((rp = pa_readlink("/proc/self/exe"))) {
+            b = pa_startswith(rp, PA_BUILDDIR);
+            pa_xfree(rp);
+        }
+    } PA_ONCE_END;
+
+    return b;
+}
+
+const char *pa_get_temp_dir(void) {
+    const char *t;
+
+    if ((t = getenv("TMPDIR")) &&
+        pa_is_path_absolute(t))
+        return t;
+
+    if ((t = getenv("TMP")) &&
+        pa_is_path_absolute(t))
+        return t;
+
+    if ((t = getenv("TEMP")) &&
+        pa_is_path_absolute(t))
+        return t;
+
+    if ((t = getenv("TEMPDIR")) &&
+        pa_is_path_absolute(t))
+        return t;
+
+    return "/tmp";
+}
+
+int pa_open_cloexec(const char *fn, int flags, mode_t mode) {
+    int fd;
+
+#ifdef O_NOCTTY
+    flags |= O_NOCTTY;
+#endif
+
+#ifdef O_CLOEXEC
+    if ((fd = open(fn, flags|O_CLOEXEC, mode)) >= 0)
+        goto finish;
+
+    if (errno != EINVAL)
+        return fd;
+#endif
+
+    if ((fd = open(fn, flags, mode)) < 0)
+        return fd;
+
+finish:
+    /* Some implementations might simply ignore O_CLOEXEC if it is not
+     * understood, make sure FD_CLOEXEC is enabled anyway */
+
+    pa_make_fd_cloexec(fd);
+    return fd;
+}
+
+int pa_socket_cloexec(int domain, int type, int protocol) {
+    int fd;
 
-    /* We abuse __OPTIMIZE__ as a check whether we are a debug build
-     * or not. */
+#ifdef SOCK_CLOEXEC
+    if ((fd = socket(domain, type | SOCK_CLOEXEC, protocol)) >= 0)
+        goto finish;
+
+    if (errno != EINVAL)
+        return fd;
+#endif
+
+    if ((fd = socket(domain, type, protocol)) < 0)
+        return fd;
+
+finish:
+    /* Some implementations might simply ignore SOCK_CLOEXEC if it is
+     * not understood, make sure FD_CLOEXEC is enabled anyway */
+
+    pa_make_fd_cloexec(fd);
+    return fd;
+}
+
+int pa_pipe_cloexec(int pipefd[2]) {
+    int r;
+
+#ifdef HAVE_PIPE2
+    if ((r = pipe2(pipefd, O_CLOEXEC)) >= 0)
+        goto finish;
+
+    if (errno != EINVAL && errno != ENOSYS)
+        return r;
+
+#endif
+
+    if ((r = pipe(pipefd)) < 0)
+        return r;
+
+finish:
+    pa_make_fd_cloexec(pipefd[0]);
+    pa_make_fd_cloexec(pipefd[1]);
+
+    return 0;
+}
+
+int pa_accept_cloexec(int sockfd, struct sockaddr *addr, socklen_t *addrlen) {
+    int fd;
+
+#ifdef HAVE_ACCEPT4
+    if ((fd = accept4(sockfd, addr, addrlen, SOCK_CLOEXEC)) >= 0)
+        goto finish;
+
+    if (errno != EINVAL && errno != ENOSYS)
+        return fd;
+
+#endif
 
-    if ((rp = pa_readlink("/proc/self/exe"))) {
-        b = pa_startswith(rp, PA_BUILDDIR);
-        pa_xfree(rp);
+    if ((fd = accept(sockfd, addr, addrlen)) < 0)
+        return fd;
+
+finish:
+    pa_make_fd_cloexec(fd);
+    return fd;
+}
+
+FILE* pa_fopen_cloexec(const char *path, const char *mode) {
+    FILE *f;
+    char *m;
+
+    m = pa_sprintf_malloc("%se", mode);
+
+    errno = 0;
+    if ((f = fopen(path, m))) {
+        pa_xfree(m);
+        goto finish;
     }
 
-    return b;
+    pa_xfree(m);
+
+    if (errno != EINVAL)
+        return NULL;
+
+    if (!(f = fopen(path, mode)))
+        return NULL;
+
+finish:
+    pa_make_fd_cloexec(fileno(f));
+    return f;
 }
 
+void pa_nullify_stdfds(void) {
+
+#ifndef OS_IS_WIN32
+        pa_close(STDIN_FILENO);
+        pa_close(STDOUT_FILENO);
+        pa_close(STDERR_FILENO);
+
+        pa_assert_se(open("/dev/null", O_RDONLY) == STDIN_FILENO);
+        pa_assert_se(open("/dev/null", O_WRONLY) == STDOUT_FILENO);
+        pa_assert_se(open("/dev/null", O_WRONLY) == STDERR_FILENO);
+#else
+        FreeConsole();
 #endif
+
+}
+
+char *pa_read_line_from_file(const char *fn) {
+    FILE *f;
+    char ln[256] = "", *r;
+
+    if (!(f = pa_fopen_cloexec(fn, "r")))
+        return NULL;
+
+    r = fgets(ln, sizeof(ln)-1, f);
+    fclose(f);
+
+    if (!r) {
+        errno = EIO;
+        return NULL;
+    }
+
+    pa_strip_nl(ln);
+    return pa_xstrdup(ln);
+}
+
+bool pa_running_in_vm(void) {
+
+#if defined(__i386__) || defined(__x86_64__)
+
+    /* Both CPUID and DMI are x86 specific interfaces... */
+
+    uint32_t eax = 0x40000000;
+    union {
+        uint32_t sig32[3];
+        char text[13];
+    } sig;
+
+#ifdef __linux__
+    const char *const dmi_vendors[] = {
+        "/sys/class/dmi/id/sys_vendor",
+        "/sys/class/dmi/id/board_vendor",
+        "/sys/class/dmi/id/bios_vendor"
+    };
+
+    unsigned i;
+
+    for (i = 0; i < PA_ELEMENTSOF(dmi_vendors); i++) {
+        char *s;
+
+        if ((s = pa_read_line_from_file(dmi_vendors[i]))) {
+
+            if (pa_startswith(s, "QEMU") ||
+                /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
+                pa_startswith(s, "VMware") ||
+                pa_startswith(s, "VMW") ||
+                pa_startswith(s, "Microsoft Corporation") ||
+                pa_startswith(s, "innotek GmbH") ||
+                pa_startswith(s, "Xen")) {
+
+                pa_xfree(s);
+                return true;
+            }
+
+            pa_xfree(s);
+        }
+    }
+
+#endif
+
+    /* http://lwn.net/Articles/301888/ */
+    pa_zero(sig);
+
+    __asm__ __volatile__ (
+        /* ebx/rbx is being used for PIC! */
+        "  push %%"PA_REG_b"         \n\t"
+        "  cpuid                     \n\t"
+        "  mov %%ebx, %1             \n\t"
+        "  pop %%"PA_REG_b"          \n\t"
+
+        : "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2])
+        : "0" (eax)
+    );
+
+    if (pa_streq(sig.text, "XenVMMXenVMM") ||
+        pa_streq(sig.text, "KVMKVMKVM") ||
+        /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
+        pa_streq(sig.text, "VMwareVMware") ||
+        /* http://msdn.microsoft.com/en-us/library/bb969719.aspx */
+        pa_streq(sig.text, "Microsoft Hv"))
+        return true;
+
+#endif
+
+    return false;
+}