X-Git-Url: https://code.delx.au/pulseaudio/blobdiff_plain/fc339a608e0a295312a1887b6cfb78883f7c556c..5610d41482df995baaf510308e07ccbe04c9e18b:/src/pulsecore/core-util.c diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c index 7d239186..3e2c615f 100644 --- a/src/pulsecore/core-util.c +++ b/src/pulsecore/core-util.c @@ -117,7 +117,7 @@ #include "rtkit.h" #endif -#ifdef __linux__ +#if defined(__linux__) && !defined(__ANDROID__) #include #endif @@ -149,53 +149,108 @@ 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], *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; - /* FIXME: Needs to set errno */ + if (!toplevel) { + char library_path[MAX_PATH]; + char *p; - if (!GetModuleFileName(handle, library_path, MAX_PATH)) - return 0; + if (!GetModuleFileName(handle, library_path, MAX_PATH)) + return NULL; - sep = strrchr(library_path, PA_PATH_SEP_CHAR); - if (sep) - *sep = '\0'; + toplevel = pa_xstrdup(library_path); - if (!SetEnvironmentVariable(PULSE_ROOTENV, library_path)) - return 0; + p = strrchr(toplevel, PA_PATH_SEP_CHAR); + if (p) + *p = '\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) { @@ -211,13 +266,17 @@ 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, fd; + int r, saved_errno; + bool retry = true; pa_assert(dir); +again: #ifdef OS_IS_WIN32 r = mkdir(dir); #else @@ -229,10 +288,20 @@ int pa_make_secure_dir(const char* dir, mode_t m, uid_t uid, gid_t gid) { } #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; #if defined(HAVE_FSTAT) && !defined(OS_IS_WIN32) +{ + int fd; if ((fd = open(dir, #ifdef O_CLOEXEC O_CLOEXEC| @@ -257,12 +326,20 @@ int pa_make_secure_dir(const char* dir, mode_t m, uid_t uid, gid_t gid) { goto fail; } + if (!update_perms) { + pa_assert_se(pa_close(fd) >= 0); + return 0; + } + #ifdef HAVE_FCHOWN - if (uid == (uid_t)-1) + if (uid == (uid_t) -1) uid = getuid(); - if (gid == (gid_t)-1) + if (gid == (gid_t) -1) gid = getgid(); - (void) fchown(fd, 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_FCHMOD @@ -270,6 +347,7 @@ int pa_make_secure_dir(const char* dir, mode_t m, uid_t uid, gid_t gid) { #endif pa_assert_se(pa_close(fd) >= 0); +} #endif #ifdef HAVE_LSTAT @@ -316,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; @@ -477,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; @@ -630,6 +710,10 @@ static int set_scheduler(int rtprio) { struct sched_param sp; #ifdef HAVE_DBUS int r; + long long rttime; +#ifdef RLIMIT_RTTIME + struct rlimit rl; +#endif DBusError error; DBusConnection *bus; @@ -655,7 +739,7 @@ static int set_scheduler(int rtprio) { #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; @@ -667,15 +751,36 @@ static int set_scheduler(int rtprio) { * https://bugs.freedesktop.org/show_bug.cgi?id=16924 */ dbus_connection_set_exit_on_disconnect(bus, FALSE); - r = rtkit_make_realtime(bus, 0, rtprio); - dbus_connection_unref(bus); + rttime = rtkit_get_rttime_usec_max(bus); + if (rttime >= 0) { +#ifdef RLIMIT_RTTIME + r = getrlimit(RLIMIT_RTTIME, &rl); - if (r >= 0) { - pa_log_debug("RealtimeKit worked."); - return 0; + 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 = 0; #endif @@ -721,7 +826,7 @@ int pa_make_realtime(int rtprio) { pa_log_info("Successfully acquired real-time thread priority."); return 0; -#elif _POSIX_PRIORITY_SCHEDULING +#elif defined(_POSIX_PRIORITY_SCHEDULING) int p; if (set_scheduler(rtprio) >= 0) { @@ -737,7 +842,7 @@ int pa_make_realtime(int rtprio) { #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)) { + if (SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL)) { pa_log_info("Successfully enabled THREAD_PRIORITY_TIME_CRITICAL scheduling for thread."); return 0; } @@ -853,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; @@ -875,21 +981,28 @@ 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; 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; #ifdef HAVE_LANGINFO_H - /* And then we check language dependant */ +{ + const char *expr; + /* And then we check language dependent */ if ((expr = nl_langinfo(YESEXPR))) if (expr[0]) if (pa_match(expr, v) > 0) @@ -899,16 +1012,59 @@ int pa_parse_boolean(const char *v) { if (expr[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; @@ -925,6 +1081,29 @@ char *pa_split(const char *c, const char *delimiter, const char**state) { return pa_xstrndup(current, l); } +/* 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) { const char *current = *state ? *state : c; @@ -968,7 +1147,7 @@ const char *pa_sig2str(int sig) { } #else - switch(sig) { + switch (sig) { #ifdef SIGHUP case SIGHUP: return "SIGHUP"; #endif @@ -1088,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); @@ -1138,7 +1317,7 @@ 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; @@ -1174,7 +1353,7 @@ 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; @@ -1248,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; @@ -1359,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); @@ -1384,33 +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; - } - -#ifdef HAVE_GETUID - 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; } -#endif + /* 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; } @@ -1428,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; } @@ -1554,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; } @@ -1595,10 +1821,10 @@ 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)); @@ -1619,8 +1845,9 @@ char *pa_get_runtime_dir(void) { goto fail; } #else - /* No symlink possible, so let's just create the runtime directly */ - if (!mkdir(k)) + /* 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 @@ -1634,10 +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) { @@ -1657,6 +1884,7 @@ 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); @@ -1679,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; @@ -1712,23 +1940,9 @@ 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 = pa_fopen_cloexec(fn, "r"))) { if (result) *result = pa_xstrdup(fn); @@ -1744,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 = pa_fopen_cloexec(fn, "r"))) { + if (f) { if (result) *result = pa_xstrdup(fn); @@ -1781,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 = pa_fopen_cloexec(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; @@ -1805,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); @@ -1841,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); @@ -1866,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; @@ -1891,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]; @@ -1947,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); @@ -1959,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); @@ -1968,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 @@ -1998,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; @@ -2096,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; @@ -2219,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); @@ -2373,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; @@ -2403,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; } @@ -2435,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; } @@ -2548,12 +2773,12 @@ int pa_reset_sigsv(const int except[]) { 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: { @@ -2561,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; } } @@ -2630,15 +2855,35 @@ void pa_unset_env_recorded(void) { } } -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; @@ -2705,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 = pa_fopen_cloexec(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); @@ -2724,10 +2970,10 @@ char *pa_machine_id(void) { if ((h = pa_get_host_name_malloc())) return h; -#ifndef OS_IS_WIN32 +#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 @@ -2736,7 +2982,8 @@ char *pa_machine_id(void) { 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); @@ -2762,7 +3009,7 @@ char *pa_uname_string(void) { } #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 @@ -2863,16 +3110,16 @@ char *pa_escape(const char *p, const char *chars) { 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; @@ -2884,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; @@ -3010,32 +3257,27 @@ 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; - /* We abuse __OPTIMIZE__ as a check whether we are a debug build - * or not. */ - - if ((rp = pa_readlink("/proc/self/exe"))) { - b = pa_startswith(rp, PA_BUILDDIR); - pa_xfree(rp); - } + PA_ONCE_BEGIN { + if ((rp = pa_readlink("/proc/self/exe"))) { + b = pa_startswith(rp, PA_BUILDDIR); + pa_xfree(rp); + } + } PA_ONCE_END; return b; } -#endif - const char *pa_get_temp_dir(void) { const char *t; @@ -3208,7 +3450,7 @@ char *pa_read_line_from_file(const char *fn) { return pa_xstrdup(ln); } -pa_bool_t pa_running_in_vm(void) { +bool pa_running_in_vm(void) { #if defined(__i386__) || defined(__x86_64__) @@ -3243,7 +3485,7 @@ pa_bool_t pa_running_in_vm(void) { pa_startswith(s, "Xen")) { pa_xfree(s); - return TRUE; + return true; } pa_xfree(s); @@ -3272,9 +3514,9 @@ pa_bool_t pa_running_in_vm(void) { pa_streq(sig.text, "VMwareVMware") || /* http://msdn.microsoft.com/en-us/library/bb969719.aspx */ pa_streq(sig.text, "Microsoft Hv")) - return TRUE; + return true; #endif - return FALSE; + return false; }