diff options
Diffstat (limited to 'src/pulsecore/core-util.c')
-rw-r--r-- | src/pulsecore/core-util.c | 1441 |
1 files changed, 1173 insertions, 268 deletions
diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c index 5f72b342..d259fb16 100644 --- a/src/pulsecore/core-util.c +++ b/src/pulsecore/core-util.c @@ -1,18 +1,20 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. - + + Copyright 2004-2006 Lennart Poettering + Copyright 2004 Joe Marcus Clarke + Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB + PulseAudio 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. - + PulseAudio 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 PulseAudio; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 @@ -27,7 +29,6 @@ #include <stdlib.h> #include <signal.h> #include <errno.h> -#include <assert.h> #include <string.h> #include <stdio.h> #include <fcntl.h> @@ -38,6 +39,11 @@ #include <sys/types.h> #include <sys/stat.h> #include <sys/time.h> +#include <dirent.h> + +#ifdef HAVE_STRTOF_L +#include <locale.h> +#endif #ifdef HAVE_SCHED_H #include <sched.h> @@ -47,6 +53,14 @@ #include <sys/resource.h> #endif +#ifdef HAVE_SYS_CAPABILITY_H +#include <sys/capability.h> +#endif + +#ifdef HAVE_SYS_MMAN_H +#include <sys/mman.h> +#endif + #ifdef HAVE_PTHREAD #include <pthread.h> #endif @@ -67,14 +81,19 @@ #include <grp.h> #endif +#ifdef HAVE_LIBSAMPLERATE #include <samplerate.h> +#endif #include <pulse/xmalloc.h> #include <pulse/util.h> +#include <pulse/utf8.h> #include <pulsecore/core-error.h> #include <pulsecore/winsock.h> #include <pulsecore/log.h> +#include <pulsecore/macro.h> +#include <pulsecore/thread.h> #include "core-util.h" @@ -83,14 +102,6 @@ #define MSG_NOSIGNAL 0 #endif -#ifndef OS_IS_WIN32 -#define PA_USER_RUNTIME_PATH_PREFIX "/tmp/pulse-" -#define PATH_SEP '/' -#else -#define PA_USER_RUNTIME_PATH_PREFIX "%TEMP%\\pulse-" -#define PATH_SEP '\\' -#endif - #ifdef OS_IS_WIN32 #define PULSE_ROOTENV "PULSE_ROOT" @@ -103,7 +114,7 @@ int pa_set_root(HANDLE handle) { if (!GetModuleFileName(handle, library_path + sizeof(PULSE_ROOTENV), MAX_PATH)) return 0; - sep = strrchr(library_path, '\\'); + sep = strrchr(library_path, PA_PATH_SEP_CHAR); if (sep) *sep = '\0'; @@ -116,31 +127,50 @@ int pa_set_root(HANDLE handle) { #endif /** Make a file descriptor nonblock. Doesn't do any error checking */ -void pa_make_nonblock_fd(int fd) { +void pa_make_fd_nonblock(int fd) { + #ifdef O_NONBLOCK int v; - assert(fd >= 0); + pa_assert(fd >= 0); + + pa_assert_se((v = fcntl(fd, F_GETFL)) >= 0); + + if (!(v & O_NONBLOCK)) + pa_assert_se(fcntl(fd, F_SETFL, v|O_NONBLOCK) >= 0); - if ((v = fcntl(fd, F_GETFL)) >= 0) - if (!(v & O_NONBLOCK)) - fcntl(fd, F_SETFL, v|O_NONBLOCK); #elif defined(OS_IS_WIN32) u_long arg = 1; if (ioctlsocket(fd, FIONBIO, &arg) < 0) { - if (WSAGetLastError() == WSAENOTSOCK) - pa_log_warn("WARNING: Only sockets can be made non-blocking!"); + pa_assert_se(WSAGetLastError() == WSAENOTSOCK); + pa_log_warn("Only sockets can be made non-blocking!"); } #else - pa_log_warn("WARNING: Non-blocking I/O not supported.!"); + pa_log_warn("Non-blocking I/O not supported.!"); +#endif + +} + +/* Set the FD_CLOEXEC flag for a fd */ +void pa_make_fd_cloexec(int fd) { + +#ifdef FD_CLOEXEC + int v; + pa_assert(fd >= 0); + + pa_assert_se((v = fcntl(fd, F_GETFD, 0)) >= 0); + + if (!(v & FD_CLOEXEC)) + pa_assert_se(fcntl(fd, F_SETFD, v|FD_CLOEXEC) >= 0); #endif + } /** Creates a directory securely */ int pa_make_secure_dir(const char* dir, mode_t m, uid_t uid, gid_t gid) { struct stat st; int r; - - assert(dir); + + pa_assert(dir); #ifdef OS_IS_WIN32 r = mkdir(dir); @@ -152,7 +182,7 @@ int pa_make_secure_dir(const char* dir, mode_t m, uid_t uid, gid_t gid) { umask(u); } #endif - + if (r < 0 && errno != EEXIST) return -1; @@ -161,20 +191,20 @@ int pa_make_secure_dir(const char* dir, mode_t m, uid_t uid, gid_t gid) { uid = getuid(); if (gid == (gid_t)-1) gid = getgid(); - chown(dir, uid, gid); + (void) chown(dir, uid, gid); #endif - + #ifdef HAVE_CHMOD chmod(dir, m); #endif - + #ifdef HAVE_LSTAT if (lstat(dir, &st) < 0) #else if (stat(dir, &st) < 0) #endif goto fail; - + #ifndef OS_IS_WIN32 if (!S_ISDIR(st.st_mode) || (st.st_uid != uid) || @@ -184,11 +214,11 @@ int pa_make_secure_dir(const char* dir, mode_t m, uid_t uid, gid_t gid) { goto fail; } #else - pa_log_warn("secure directory creation not supported on Win32."); + pa_log_warn("Secure directory creation not supported on Win32."); #endif - + return 0; - + fail: rmdir(dir); return -1; @@ -214,12 +244,12 @@ int pa_make_secure_parent_dir(const char *fn, mode_t m, uid_t uid, gid_t gid) { if (!(dir = pa_parent_dir(fn))) goto finish; - + if (pa_make_secure_dir(dir, m, uid, gid) < 0) goto finish; ret = 0; - + finish: pa_xfree(dir); return ret; @@ -237,7 +267,7 @@ ssize_t pa_read(int fd, void *buf, size_t count, int *type) { if (!type || *type == 0) { ssize_t r; - + if ((r = recv(fd, buf, count, 0)) >= 0) return r; @@ -251,7 +281,7 @@ ssize_t pa_read(int fd, void *buf, size_t count, int *type) { } #endif - + return read(fd, buf, count); } @@ -263,7 +293,7 @@ ssize_t pa_write(int fd, const void *buf, size_t count, int *type) { if ((r = send(fd, buf, count, MSG_NOSIGNAL)) >= 0) return r; - + #ifdef OS_IS_WIN32 if (WSAGetLastError() != WSAENOTSOCK) { errno = WSAGetLastError(); @@ -286,10 +316,10 @@ ssize_t pa_write(int fd, const void *buf, size_t count, int *type) { ssize_t pa_loop_read(int fd, void*data, size_t size, int *type) { ssize_t ret = 0; int _type; - - assert(fd >= 0); - assert(data); - assert(size); + + pa_assert(fd >= 0); + pa_assert(data); + pa_assert(size); if (!type) { _type = 0; @@ -304,7 +334,7 @@ ssize_t pa_loop_read(int fd, void*data, size_t size, int *type) { if (r == 0) break; - + ret += r; data = (uint8_t*) data + r; size -= r; @@ -318,9 +348,9 @@ ssize_t pa_loop_write(int fd, const void*data, size_t size, int *type) { ssize_t ret = 0; int _type; - assert(fd >= 0); - assert(data); - assert(size); + pa_assert(fd >= 0); + pa_assert(data); + pa_assert(size); if (!type) { _type = 0; @@ -335,7 +365,7 @@ ssize_t pa_loop_write(int fd, const void*data, size_t size, int *type) { if (r == 0) break; - + ret += r; data = (const uint8_t*) data + r; size -= r; @@ -344,6 +374,25 @@ 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 + * systems treat all file descriptors equal. */ +int pa_close(int fd) { + +#ifdef OS_IS_WIN32 + int ret; + + if ((ret = closesocket(fd)) == 0) + return 0; + + if (WSAGetLastError() != WSAENOTSOCK) { + errno = WSAGetLastError(); + return ret; + } +#endif + + return close(fd); +} + /* Print a warning messages in case that the given signal is not * blocked or trapped */ void pa_check_signal_is_blocked(int sig) { @@ -354,8 +403,8 @@ void pa_check_signal_is_blocked(int sig) { /* If POSIX threads are supported use thread-aware * pthread_sigmask() function, to check if the signal is * blocked. Otherwise fall back to sigprocmask() */ - -#ifdef HAVE_PTHREAD + +#ifdef HAVE_PTHREAD if (pthread_sigmask(SIG_SETMASK, NULL, &set) < 0) { #endif if (sigprocmask(SIG_SETMASK, NULL, &set) < 0) { @@ -370,18 +419,18 @@ void pa_check_signal_is_blocked(int sig) { return; /* Check whether the signal is trapped */ - + if (sigaction(sig, NULL, &sa) < 0) { pa_log("sigaction(): %s", pa_cstrerror(errno)); return; } - + if (sa.sa_handler != SIG_DFL) return; - - pa_log("WARNING: %s is not trapped. This might cause malfunction!", pa_strsignal(sig)); + + pa_log_warn("%s is not trapped. This might cause malfunction!", pa_sig2str(sig)); #else /* HAVE_SIGACTION */ - pa_log("WARNING: %s might not be trapped. This might cause malfunction!", pa_strsignal(sig)); + pa_log_warn("%s might not be trapped. This might cause malfunction!", pa_sig2str(sig)); #endif } @@ -390,9 +439,9 @@ void pa_check_signal_is_blocked(int sig) { char *pa_sprintf_malloc(const char *format, ...) { int size = 100; char *c = NULL; - - assert(format); - + + pa_assert(format); + for(;;) { int r; va_list ap; @@ -402,12 +451,14 @@ char *pa_sprintf_malloc(const char *format, ...) { va_start(ap, format); r = vsnprintf(c, size, format, ap); va_end(ap); - + + c[size-1] = 0; + if (r > -1 && r < size) return c; if (r > -1) /* glibc 2.1 */ - size = r+1; + size = r+1; else /* glibc 2.0 */ size *= 2; } @@ -418,25 +469,26 @@ char *pa_sprintf_malloc(const char *format, ...) { char *pa_vsprintf_malloc(const char *format, va_list ap) { int size = 100; char *c = NULL; - - assert(format); - + + pa_assert(format); + for(;;) { int r; va_list aq; - va_copy(aq, ap); - c = pa_xrealloc(c, size); - r = vsnprintf(c, size, format, aq); + va_copy(aq, ap); + r = vsnprintf(c, size, format, aq); va_end(aq); - + + c[size-1] = 0; + if (r > -1 && r < size) return c; if (r > -1) /* glibc 2.1 */ - size = r+1; + size = r+1; else /* glibc 2.0 */ size *= 2; } @@ -444,96 +496,193 @@ char *pa_vsprintf_malloc(const char *format, va_list ap) { /* Similar to OpenBSD's strlcpy() function */ char *pa_strlcpy(char *b, const char *s, size_t l) { - assert(b && s && l > 0); + pa_assert(b); + pa_assert(s); + pa_assert(l > 0); strncpy(b, s, l); b[l-1] = 0; return b; } -#define NICE_LEVEL (-15) +/* 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) { -/* Raise the priority of the current process as much as possible and -sensible: set the nice level to -15 and enable realtime scheduling if -supported.*/ -void pa_raise_priority(void) { - -#ifdef HAVE_SYS_RESOURCE_H - if (setpriority(PRIO_PROCESS, 0, NICE_LEVEL) < 0) - pa_log_warn("setpriority(): %s", pa_cstrerror(errno)); - else - pa_log_info("Successfully gained nice level %i.", NICE_LEVEL); -#endif - #ifdef _POSIX_PRIORITY_SCHEDULING - { - struct sched_param sp; + struct sched_param sp; + int r, policy; - if (sched_getparam(0, &sp) < 0) { - pa_log("sched_getparam(): %s", pa_cstrerror(errno)); - return; - } - - sp.sched_priority = 1; - if (sched_setscheduler(0, SCHED_FIFO, &sp) < 0) { - pa_log_warn("sched_setscheduler(): %s", pa_cstrerror(errno)); - return; + memset(&sp, 0, sizeof(sp)); + policy = 0; + + if ((r = pthread_getschedparam(pthread_self(), &policy, &sp)) != 0) { + pa_log("pthread_getschedgetparam(): %s", pa_cstrerror(r)); + return -1; + } + + if (policy == SCHED_FIFO && sp.sched_priority >= rtprio) { + pa_log_info("Thread already being scheduled with SCHED_FIFO with priority %i.", sp.sched_priority); + return 0; + } + + sp.sched_priority = rtprio; + if ((r = pthread_setschedparam(pthread_self(), SCHED_FIFO, &sp)) != 0) { + + while (sp.sched_priority > 1) { + sp.sched_priority --; + + if ((r = pthread_setschedparam(pthread_self(), SCHED_FIFO, &sp)) == 0) { + pa_log_info("Successfully enabled SCHED_FIFO scheduling for thread, with priority %i, which is lower than the requested %i.", sp.sched_priority, rtprio); + return 0; + } } - pa_log_info("Successfully enabled SCHED_FIFO scheduling."); + pa_log_warn("pthread_setschedparam(): %s", pa_cstrerror(r)); + return -1; } -#endif -#ifdef OS_IS_WIN32 - if (!SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS)) - pa_log_warn("SetPriorityClass() failed: 0x%08X", GetLastError()); - else - pa_log_info("Successfully gained high priority class."); + pa_log_info("Successfully enabled SCHED_FIFO scheduling for thread, with priority %i.", sp.sched_priority); + return 0; +#else + return -1; #endif } -/* Reset the priority to normal, inverting the changes made by pa_raise_priority() */ -void pa_reset_priority(void) { -#ifdef OS_IS_WIN32 - SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS); +/* This is merely used for giving the user a hint. This is not correct + * for anything security related */ +pa_bool_t pa_can_realtime(void) { + + if (geteuid() == 0) + return TRUE; + +#if defined(HAVE_SYS_RESOURCE_H) && defined(RLIMIT_RTPRIO) + { + struct rlimit rl; + + if (getrlimit(RLIMIT_RTPRIO, &rl) >= 0) + if (rl.rlim_cur > 0 || rl.rlim_cur == RLIM_INFINITY) + return TRUE; + } #endif -#ifdef _POSIX_PRIORITY_SCHEDULING +#if defined(HAVE_SYS_CAPABILITY_H) && defined(CAP_SYS_NICE) { - struct sched_param sp; - sched_getparam(0, &sp); - sp.sched_priority = 0; - sched_setscheduler(0, SCHED_OTHER, &sp); + cap_t cap; + + if ((cap = cap_get_proc())) { + cap_flag_value_t flag = CAP_CLEAR; + + if (cap_get_flag(cap, CAP_SYS_NICE, CAP_EFFECTIVE, &flag) >= 0) + if (flag == CAP_SET) { + cap_free(cap); + return TRUE; + } + + cap_free(cap); + } } #endif -#ifdef HAVE_SYS_RESOURCE_H - setpriority(PRIO_PROCESS, 0, 0); + return FALSE; +} + +/* This is merely used for giving the user a hint. This is not correct + * for anything security related */ +pa_bool_t pa_can_high_priority(void) { + + if (geteuid() == 0) + return TRUE; + +#if defined(HAVE_SYS_RESOURCE_H) && defined(RLIMIT_RTPRIO) + { + struct rlimit rl; + + if (getrlimit(RLIMIT_NICE, &rl) >= 0) + if (rl.rlim_cur >= 21 || rl.rlim_cur == RLIM_INFINITY) + return TRUE; + } #endif + +#if defined(HAVE_SYS_CAPABILITY_H) && defined(CAP_SYS_NICE) + { + cap_t cap; + + if ((cap = cap_get_proc())) { + cap_flag_value_t flag = CAP_CLEAR; + + if (cap_get_flag(cap, CAP_SYS_NICE, CAP_EFFECTIVE, &flag) >= 0) + if (flag == CAP_SET) { + cap_free(cap); + return TRUE; + } + + cap_free(cap); + } + } +#endif + + return FALSE; } -/* Set the FD_CLOEXEC flag for a fd */ -int pa_fd_set_cloexec(int fd, int b) { +/* Raise the priority of the current process as much as possible that + * is <= the specified nice level..*/ +int pa_raise_priority(int nice_level) { -#ifdef FD_CLOEXEC - int v; - assert(fd >= 0); +#ifdef HAVE_SYS_RESOURCE_H + if (setpriority(PRIO_PROCESS, 0, nice_level) < 0) { + int n; - if ((v = fcntl(fd, F_GETFD, 0)) < 0) - return -1; - - v = (v & ~FD_CLOEXEC) | (b ? FD_CLOEXEC : 0); - - if (fcntl(fd, F_SETFD, v) < 0) + for (n = nice_level+1; n < 0; n++) { + + if (setpriority(PRIO_PROCESS, 0, n) == 0) { + pa_log_info("Successfully acquired nice level %i, which is lower than the requested %i.", n, nice_level); + return 0; + } + } + + pa_log_warn("setpriority(): %s", pa_cstrerror(errno)); return -1; -#endif + } + + pa_log_info("Successfully gained nice level %i.", nice_level); +#endif + +#ifdef OS_IS_WIN32 + if (nice_level < 0) { + if (!SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS)) { + pa_log_warn("SetPriorityClass() failed: 0x%08X", GetLastError()); + return .-1; + } else + pa_log_info("Successfully gained high priority class."); + } +#endif return 0; } +/* Reset the priority to normal, inverting the changes made by + * pa_raise_priority() and pa_make_realtime()*/ +void pa_reset_priority(void) { +#ifdef HAVE_SYS_RESOURCE_H + struct sched_param sp; + + setpriority(PRIO_PROCESS, 0, 0); + + memset(&sp, 0, sizeof(sp)); + pa_assert_se(pthread_setschedparam(pthread_self(), SCHED_OTHER, &sp) == 0); +#endif + +#ifdef OS_IS_WIN32 + SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS); +#endif +} + /* Try to parse a boolean string value.*/ int pa_parse_boolean(const char *v) { - + pa_assert(v); + if (!strcmp(v, "1") || v[0] == 'y' || v[0] == 'Y' || v[0] == 't' || v[0] == 'T' || !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")) @@ -552,7 +701,7 @@ char *pa_split(const char *c, const char *delimiter, const char**state) { if (!*current) return NULL; - + l = strcspn(current, delimiter); *state = current+l; @@ -581,31 +730,134 @@ char *pa_split_spaces(const char *c, const char **state) { return pa_xstrndup(current, l); } -/* Return the name of an UNIX signal. Similar to GNU's strsignal() */ -const char *pa_strsignal(int sig) { +PA_STATIC_TLS_DECLARE(signame, pa_xfree); + +/* Return the name of an UNIX signal. Similar to Solaris sig2str() */ +const char *pa_sig2str(int sig) { + char *t; + + if (sig <= 0) + goto fail; + +#ifdef NSIG + if (sig >= NSIG) + goto fail; +#endif + +#ifdef HAVE_SIG2STR + { + char buf[SIG2STR_MAX]; + + if (sig2str(sig, buf) == 0) { + pa_xfree(PA_STATIC_TLS_GET(signame)); + t = pa_sprintf_malloc("SIG%s", buf); + PA_STATIC_TLS_SET(signame, t); + return t; + } + } +#else + switch(sig) { - case SIGINT: return "SIGINT"; - case SIGTERM: return "SIGTERM"; +#ifdef SIGHUP + case SIGHUP: return "SIGHUP"; +#endif + case SIGINT: return "SIGINT"; +#ifdef SIGQUIT + case SIGQUIT: return "SIGQUIT"; +#endif + case SIGILL: return "SIGULL"; +#ifdef SIGTRAP + case SIGTRAP: return "SIGTRAP"; +#endif + case SIGABRT: return "SIGABRT"; +#ifdef SIGBUS + case SIGBUS: return "SIGBUS"; +#endif + case SIGFPE: return "SIGFPE"; +#ifdef SIGKILL + case SIGKILL: return "SIGKILL"; +#endif #ifdef SIGUSR1 - case SIGUSR1: return "SIGUSR1"; + case SIGUSR1: return "SIGUSR1"; #endif + case SIGSEGV: return "SIGSEGV"; #ifdef SIGUSR2 - case SIGUSR2: return "SIGUSR2"; -#endif -#ifdef SIGXCPU - case SIGXCPU: return "SIGXCPU"; + case SIGUSR2: return "SIGUSR2"; #endif #ifdef SIGPIPE - case SIGPIPE: return "SIGPIPE"; + case SIGPIPE: return "SIGPIPE"; +#endif +#ifdef SIGALRM + case SIGALRM: return "SIGALRM"; +#endif + case SIGTERM: return "SIGTERM"; +#ifdef SIGSTKFLT + case SIGSTKFLT: return "SIGSTKFLT"; #endif #ifdef SIGCHLD - case SIGCHLD: return "SIGCHLD"; + case SIGCHLD: return "SIGCHLD"; #endif -#ifdef SIGHUP - case SIGHUP: return "SIGHUP"; +#ifdef SIGCONT + case SIGCONT: return "SIGCONT"; +#endif +#ifdef SIGSTOP + case SIGSTOP: return "SIGSTOP"; +#endif +#ifdef SIGTSTP + case SIGTSTP: return "SIGTSTP"; +#endif +#ifdef SIGTTIN + case SIGTTIN: return "SIGTTIN"; +#endif +#ifdef SIGTTOU + case SIGTTOU: return "SIGTTOU"; +#endif +#ifdef SIGURG + case SIGURG: return "SIGURG"; +#endif +#ifdef SIGXCPU + case SIGXCPU: return "SIGXCPU"; +#endif +#ifdef SIGXFSZ + case SIGXFSZ: return "SIGXFSZ"; +#endif +#ifdef SIGVTALRM + case SIGVTALRM: return "SIGVTALRM"; +#endif +#ifdef SIGPROF + case SIGPROF: return "SIGPROF"; +#endif +#ifdef SIGWINCH + case SIGWINCH: return "SIGWINCH"; #endif - default: return "UNKNOWN SIGNAL"; +#ifdef SIGIO + case SIGIO: return "SIGIO"; +#endif +#ifdef SIGPWR + case SIGPWR: return "SIGPWR"; +#endif +#ifdef SIGSYS + case SIGSYS: return "SIGSYS"; +#endif + } + +#ifdef SIGRTMIN + if (sig >= SIGRTMIN && sig <= SIGRTMAX) { + pa_xfree(PA_STATIC_TLS_GET(signame)); + t = pa_sprintf_malloc("SIGRTMIN+%i", sig - SIGRTMIN); + PA_STATIC_TLS_SET(signame, t); + return t; } +#endif + +#endif + +fail: + + pa_xfree(PA_STATIC_TLS_GET(signame)); + t = pa_sprintf_malloc("SIG%i", sig); + PA_STATIC_TLS_SET(signame, t); + return t; } #ifdef HAVE_GRP_H @@ -632,7 +884,7 @@ static int is_group(gid_t gid, const char *name) { } r = strcmp(name, result->gr_name) == 0; - + finish: pa_xfree(data); #else @@ -647,7 +899,7 @@ finish: finish: #endif - + return r; } @@ -657,10 +909,10 @@ int pa_own_uid_in_group(const char *name, gid_t *gid) { int n = sysconf(_SC_NGROUPS_MAX); int r = -1, i; - assert(n > 0); - + pa_assert(n > 0); + gids = pa_xmalloc(sizeof(GETGROUPS_T)*n); - + if ((n = getgroups(n, gids)) < 0) { pa_log("getgroups(): %s", pa_cstrerror(errno)); goto finish; @@ -681,7 +933,7 @@ int pa_own_uid_in_group(const char *name, gid_t *gid) { } r = 0; - + finish: pa_xfree(gids); @@ -695,20 +947,20 @@ int pa_uid_in_group(uid_t uid, const char *name) { struct group grbuf, *gr; char **i; int r = -1; - + g_n = sysconf(_SC_GETGR_R_SIZE_MAX); g_buf = pa_xmalloc(g_n); p_n = sysconf(_SC_GETPW_R_SIZE_MAX); p_buf = pa_xmalloc(p_n); - + if (getgrnam_r(name, &grbuf, g_buf, (size_t) g_n, &gr) != 0 || !gr) goto finish; r = 0; for (i = gr->gr_mem; *i; i++) { struct passwd pwbuf, *pw; - + if (getpwnam_r(*i, &pwbuf, p_buf, (size_t) p_n, &pw) != 0 || !pw) continue; @@ -763,7 +1015,7 @@ int pa_check_in_group(gid_t g) { int pa_own_uid_in_group(const char *name, gid_t *gid) { return -1; - + } int pa_uid_in_group(uid_t uid, const char *name) { @@ -787,7 +1039,7 @@ int pa_lock_fd(int fd, int b) { struct flock flock; /* Try a R/W lock first */ - + flock.l_type = b ? F_WRLCK : F_UNLCK; flock.l_whence = SEEK_SET; flock.l_start = 0; @@ -802,9 +1054,8 @@ int pa_lock_fd(int fd, int b) { if (fcntl(fd, F_SETLKW, &flock) >= 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 @@ -823,7 +1074,7 @@ int pa_lock_fd(int fd, int b) { /* Remove trailing newlines from a string */ char* pa_strip_nl(char *s) { - assert(s); + pa_assert(s); s[strcspn(s, "\r\n")] = 0; return s; @@ -832,50 +1083,58 @@ char* pa_strip_nl(char *s) { /* Create a temporary lock file and lock it. */ int pa_lock_lockfile(const char *fn) { int fd = -1; - assert(fn); + pa_assert(fn); for (;;) { struct stat st; - - if ((fd = open(fn, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR)) < 0) { - pa_log("failed to create lock file '%s': %s", fn, - pa_cstrerror(errno)); + + if ((fd = open(fn, O_CREAT|O_RDWR +#ifdef O_NOCTTY + |O_NOCTTY +#endif +#ifdef O_NOFOLLOW + |O_NOFOLLOW +#endif + , S_IRUSR|S_IWUSR)) < 0) { + pa_log_warn("Failed to create lock file '%s': %s", fn, pa_cstrerror(errno)); goto fail; } - + if (pa_lock_fd(fd, 1) < 0) { - pa_log("failed to lock file '%s'.", fn); + pa_log_warn("Failed to lock file '%s'.", fn); goto fail; } - + if (fstat(fd, &st) < 0) { - pa_log("failed to fstat() file '%s'.", fn); + pa_log_warn("Failed to fstat() file '%s': %s", fn, pa_cstrerror(errno)); goto fail; } - /* Check wheter the file has been removed meanwhile. When yes, restart this loop, otherwise, we're done */ + /* Check wheter the file has been removed meanwhile. When yes, + * restart this loop, otherwise, we're done */ if (st.st_nlink >= 1) break; - + if (pa_lock_fd(fd, 0) < 0) { - pa_log("failed to unlock file '%s'.", fn); + pa_log_warn("Failed to unlock file '%s'.", fn); goto fail; } - - if (close(fd) < 0) { - pa_log("failed to close file '%s'.", fn); + + if (pa_close(fd) < 0) { + pa_log_warn("Failed to close file '%s': %s", fn, pa_cstrerror(errno)); + fd = -1; goto fail; } fd = -1; } - + return fd; fail: if (fd >= 0) - close(fd); + pa_close(fd); return -1; } @@ -883,38 +1142,80 @@ fail: /* Unlock a temporary lcok file */ int pa_unlock_lockfile(const char *fn, int fd) { int r = 0; - assert(fn && fd >= 0); + pa_assert(fd >= 0); - if (unlink(fn) < 0) { - pa_log_warn("WARNING: unable to remove lock file '%s': %s", - fn, pa_cstrerror(errno)); - r = -1; + if (fn) { + if (unlink(fn) < 0) { + pa_log_warn("Unable to remove lock file '%s': %s", fn, pa_cstrerror(errno)); + r = -1; + } } - + if (pa_lock_fd(fd, 0) < 0) { - pa_log_warn("WARNING: failed to unlock file '%s'.", fn); + pa_log_warn("Failed to unlock file '%s'.", fn); r = -1; } - if (close(fd) < 0) { - pa_log_warn("WARNING: failed to close lock file '%s': %s", - fn, pa_cstrerror(errno)); + if (pa_close(fd) < 0) { + pa_log_warn("Failed to close '%s': %s", fn, pa_cstrerror(errno)); r = -1; } return r; } +static char *get_dir(mode_t m, const char *env_name) { + const char *e; + char *d; + + if ((e = getenv(env_name))) + d = pa_xstrdup(e); + else { + char h[PATH_MAX]; + struct stat st; + + if (!pa_get_home_dir(h, sizeof(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)); + return NULL; + } + + if (st.st_uid != getuid()) { + pa_log_error("Home directory %s not ours.", d); + return NULL; + } + + d = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse", h); + } + + if (pa_make_secure_dir(d, m, (pid_t) -1, (pid_t) -1) < 0) { + pa_log_error("Failed to create secure directory: %s", pa_cstrerror(errno)); + return NULL; + } + + return d; +} + +char *pa_get_runtime_dir(void) { + return get_dir(pa_in_system_mode() ? 0755 : 0700, "PULSE_RUNTIME_PATH"); +} + +char *pa_get_state_dir(void) { + return get_dir(0700, "PULSE_STATE_PATH"); +} + /* Try to open a configuration file. If "env" is specified, open the * value of the specified environment variable. Otherwise look for a * file "local" in the home directory or a file "global" in global * file system. If "result" is non-NULL, a pointer to a newly * allocated buffer containing the used configuration file is * stored there.*/ -FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result, const char *mode) { +FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result) { const char *fn; - char h[PATH_MAX]; - #ifdef OS_IS_WIN32 char buf[PATH_MAX]; @@ -923,71 +1224,162 @@ FILE *pa_open_config_file(const char *global, const char *local, const char *env #endif if (env && (fn = getenv(env))) { + FILE *f; + #ifdef OS_IS_WIN32 if (!ExpandEnvironmentStrings(fn, buf, PATH_MAX)) return NULL; fn = buf; #endif - if (result) - *result = pa_xstrdup(fn); + if ((f = fopen(fn, "r"))) { + if (result) + *result = pa_xstrdup(fn); + + return f; + } - return fopen(fn, mode); + pa_log_warn("Failed to open configuration file '%s': %s", fn, pa_cstrerror(errno)); + return NULL; } if (local) { const char *e; - char *lfn = NULL; + char *lfn; + char h[PATH_MAX]; + FILE *f; if ((e = getenv("PULSE_CONFIG_PATH"))) - fn = lfn = pa_sprintf_malloc("%s/%s", e, local); + fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", e, local); else if (pa_get_home_dir(h, sizeof(h))) - fn = lfn = pa_sprintf_malloc("%s/.pulse/%s", h, local); + fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse" PA_PATH_SEP "%s", h, local); - if (lfn) { - FILE *f; - #ifdef OS_IS_WIN32 - if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX)) - return NULL; - fn = buf; -#endif - - if ((f = fopen(fn, mode)) || errno != ENOENT) { - if (result) - *result = pa_xstrdup(fn); - pa_xfree(lfn); - return f; - } - + if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX)) { pa_xfree(lfn); + return NULL; } + fn = buf; +#endif + + if ((f = fopen(fn, "r"))) { + if (result) + *result = pa_xstrdup(fn); + + pa_xfree(lfn); + return f; + } + + if (errno != ENOENT) { + pa_log_warn("Failed to open configuration file '%s': %s", fn, pa_cstrerror(errno)); + pa_xfree(lfn); + return NULL; + } + + pa_xfree(lfn); } - if (!global) { - if (result) - *result = NULL; + if (global) { + FILE *f; + +#ifdef OS_IS_WIN32 + if (!ExpandEnvironmentStrings(global, buf, PATH_MAX)) + return NULL; + global = buf; +#endif + + if ((f = fopen(global, "r"))) { + + if (result) + *result = pa_xstrdup(global); + + return f; + } + } else errno = ENOENT; + + return NULL; +} + +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)) + return NULL; + fn = buf; +#endif + + if (access(fn, R_OK) == 0) + return pa_xstrdup(fn); + + pa_log_warn("Failed to access configuration file '%s': %s", fn, pa_cstrerror(errno)); return NULL; } + if (local) { + const char *e; + char *lfn; + char h[PATH_MAX]; + + if ((e = getenv("PULSE_CONFIG_PATH"))) + fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", e, local); + else if (pa_get_home_dir(h, sizeof(h))) + fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse" PA_PATH_SEP "%s", h, local); + #ifdef OS_IS_WIN32 - if (!ExpandEnvironmentStrings(global, buf, PATH_MAX)) - return NULL; - global = buf; + if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX)) { + pa_xfree(lfn); + return NULL; + } + fn = buf; #endif - if (result) - *result = pa_xstrdup(global); - - return fopen(global, mode); + if (access(fn, R_OK) == 0) { + char *r = pa_xstrdup(fn); + pa_xfree(lfn); + return r; + } + + if (errno != ENOENT) { + pa_log_warn("Failed to access configuration file '%s': %s", fn, pa_cstrerror(errno)); + pa_xfree(lfn); + return NULL; + } + + pa_xfree(lfn); + } + + if (global) { +#ifdef OS_IS_WIN32 + if (!ExpandEnvironmentStrings(global, buf, PATH_MAX)) + return NULL; + global = buf; +#endif + + if (access(fn, R_OK) == 0) + return pa_xstrdup(global); + } else + errno = ENOENT; + + return NULL; } - + /* Format the specified data as a hexademical string */ char *pa_hexstr(const uint8_t* d, size_t dlength, char *s, size_t slength) { size_t i = 0, j = 0; const char hex[] = "0123456789abcdef"; - assert(d && s && slength > 0); + + pa_assert(d); + pa_assert(s); + pa_assert(slength > 0); while (i < dlength && j+3 <= slength) { s[j++] = hex[*d >> 4]; @@ -1018,14 +1410,16 @@ static int hexc(char c) { /* Parse a hexadecimal string as created by pa_hexstr() to a BLOB */ size_t pa_parsehex(const char *p, uint8_t *d, size_t dlength) { size_t j = 0; - assert(p && d); + + pa_assert(p); + pa_assert(d); while (j < dlength && *p) { int b; if ((b = hexc(*(p++))) < 0) return (size_t) -1; - + d[j] = (uint8_t) (b << 4); if (!*p) @@ -1042,84 +1436,107 @@ size_t pa_parsehex(const char *p, uint8_t *d, size_t dlength) { } /* Returns nonzero when *s starts with *pfx */ -int pa_startswith(const char *s, const char *pfx) { +pa_bool_t pa_startswith(const char *s, const char *pfx) { size_t l; - - assert(s); - assert(pfx); - + + pa_assert(s); + pa_assert(pfx); + l = strlen(pfx); return strlen(s) >= l && strncmp(s, pfx, l) == 0; } /* Returns nonzero when *s ends with *sfx */ -int pa_endswith(const char *s, const char *sfx) { +pa_bool_t pa_endswith(const char *s, const char *sfx) { size_t l1, l2; - - assert(s); - assert(sfx); - + + pa_assert(s); + pa_assert(sfx); + l1 = strlen(s); l2 = strlen(sfx); return l1 >= l2 && strcmp(s+l1-l2, sfx) == 0; } -/* if fn is null return the PulseAudio run time path in s (/tmp/pulse) - * if fn is non-null and starts with / return fn in s - * otherwise append fn to the run time path and return it in s */ -char *pa_runtime_path(const char *fn, char *s, size_t l) { - const char *e; +pa_bool_t pa_is_path_absolute(const char *fn) { + pa_assert(fn); #ifndef OS_IS_WIN32 - if (fn && *fn == '/') + return *fn == '/'; #else - if (fn && strlen(fn) >= 3 && isalpha(fn[0]) && fn[1] == ':' && fn[2] == '\\') -#endif - return pa_strlcpy(s, fn, l); - - if ((e = getenv("PULSE_RUNTIME_PATH"))) { - - if (fn) - snprintf(s, l, "%s%c%s", e, PATH_SEP, fn); - else - snprintf(s, l, "%s", e); - - } else { - char u[256]; - - if (fn) - snprintf(s, l, "%s%s%c%s", PA_USER_RUNTIME_PATH_PREFIX, pa_get_user_name(u, sizeof(u)), PATH_SEP, fn); - else - snprintf(s, l, "%s%s", PA_USER_RUNTIME_PATH_PREFIX, pa_get_user_name(u, sizeof(u))); - } - - -#ifdef OS_IS_WIN32 - { - char buf[l]; - strcpy(buf, s); - ExpandEnvironmentStrings(buf, s, l); - } + return strlen(fn) >= 3 && isalpha(fn[0]) && fn[1] == ':' && fn[2] == '\\'; #endif +} - return s; +char *pa_make_path_absolute(const char *p) { + char *r; + char *cwd; + + pa_assert(p); + + if (pa_is_path_absolute(p)) + return pa_xstrdup(p); + + if (!(cwd = pa_getcwd())) + return pa_xstrdup(p); + + r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", cwd, p); + pa_xfree(cwd); + 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 rt) { + char *rtp; + + if (pa_is_path_absolute(fn)) + return pa_xstrdup(fn); + + rtp = rt ? pa_get_runtime_dir() : pa_get_state_dir(); + + if (!rtp) + return NULL; + + if (fn) { + char *r; + r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", rtp, fn); + pa_xfree(rtp); + return r; + } else + return rtp; +} + +char *pa_runtime_path(const char *fn) { + return get_path(fn, 1); +} + +char *pa_state_path(const char *fn) { + return get_path(fn, 0); } /* 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; - assert(s && ret_i); + pa_assert(s); + pa_assert(ret_i); + + errno = 0; l = strtol(s, &x, 0); - if (!x || *x) + if (!x || *x || errno != 0) + return -1; + + if ((int32_t) l != l) return -1; *ret_i = (int32_t) l; - + return 0; } @@ -1127,14 +1544,502 @@ int pa_atoi(const char *s, int32_t *ret_i) { int pa_atou(const char *s, uint32_t *ret_u) { char *x = NULL; unsigned long l; - assert(s && ret_u); + pa_assert(s); + pa_assert(ret_u); + + errno = 0; l = strtoul(s, &x, 0); - if (!x || *x) + if (!x || *x || errno != 0) + return -1; + + if ((uint32_t) l != l) return -1; *ret_u = (uint32_t) l; - + + return 0; +} + +#ifdef HAVE_STRTOF_L +static locale_t c_locale = NULL; + +static void c_locale_destroy(void) { + freelocale(c_locale); +} +#endif + +int pa_atod(const char *s, double *ret_d) { + char *x = NULL; + double f; + int r = 0; + + pa_assert(s); + pa_assert(ret_d); + + /* This should be locale independent */ + +#ifdef HAVE_STRTOF_L + + PA_ONCE_BEGIN { + + if ((c_locale = newlocale(LC_ALL_MASK, "C", NULL))) + atexit(c_locale_destroy); + + } PA_ONCE_END; + + if (c_locale) { + errno = 0; + f = strtod_l(s, &x, c_locale); + } else +#endif + { + errno = 0; + f = strtod(s, &x); + } + + if (!x || *x || errno != 0) + r = -1; + else + *ret_d = f; + + return r; +} + +/* Same as snprintf, but guarantees NUL-termination on every platform */ +int pa_snprintf(char *str, size_t size, const char *format, ...) { + int ret; + va_list ap; + + pa_assert(str); + pa_assert(size > 0); + pa_assert(format); + + va_start(ap, format); + ret = pa_vsnprintf(str, size, format, ap); + va_end(ap); + + return ret; +} + +/* Same as vsnprintf, but guarantees NUL-termination on every platform */ +int pa_vsnprintf(char *str, size_t size, const char *format, va_list ap) { + int ret; + + pa_assert(str); + pa_assert(size > 0); + pa_assert(format); + + ret = vsnprintf(str, size, format, ap); + + str[size-1] = 0; + + if (ret < 0) + ret = strlen(str); + + return PA_MIN((int) size-1, ret); +} + +/* Truncate the specified string, but guarantee that the string + * returned still validates as UTF8 */ +char *pa_truncate_utf8(char *c, size_t l) { + pa_assert(c); + pa_assert(pa_utf8_valid(c)); + + if (strlen(c) <= l) + return c; + + c[l] = 0; + + while (l > 0 && !pa_utf8_valid(c)) + c[--l] = 0; + + return c; +} + +char *pa_getcwd(void) { + size_t l = 128; + + for (;;) { + char *p = pa_xnew(char, l); + if (getcwd(p, l)) + return p; + + if (errno != ERANGE) + return NULL; + + pa_xfree(p); + l *= 2; + } +} + +void *pa_will_need(const void *p, size_t l) { +#ifdef RLIMIT_MEMLOCK + struct rlimit rlim; +#endif + const void *a; + size_t size; + int r; + size_t bs; + + pa_assert(p); + pa_assert(l > 0); + + a = PA_PAGE_ALIGN_PTR(p); + size = (const uint8_t*) p + l - (const uint8_t*) a; + +#ifdef HAVE_POSIX_MADVISE + if ((r = posix_madvise((void*) a, size, POSIX_MADV_WILLNEED)) == 0) { + pa_log_debug("posix_madvise() worked fine!"); + return (void*) p; + } +#endif + + /* Most likely the memory was not mmap()ed from a file and thus + * madvise() didn't work, so let's misuse mlock() do page this + * stuff back into RAM. Yeah, let's fuck with the MM! It's so + * inviting, the man page of mlock() tells us: "All pages that + * contain a part of the specified address range are guaranteed to + * be resident in RAM when the call returns successfully." */ + +#ifdef RLIMIT_MEMLOCK + pa_assert_se(getrlimit(RLIMIT_MEMLOCK, &rlim) == 0); + + if (rlim.rlim_cur < PA_PAGE_SIZE) { + pa_log_debug("posix_madvise() failed (or doesn't exist), resource limits don't allow mlock(), can't page in data: %s", pa_cstrerror(r)); + return (void*) p; + } + + bs = PA_PAGE_ALIGN(rlim.rlim_cur); +#else + bs = PA_PAGE_SIZE*4; +#endif + + pa_log_debug("posix_madvise() failed (or doesn't exist), trying mlock(): %s", pa_cstrerror(r)); + +#ifdef HAVE_MLOCK + while (size > 0 && bs > 0) { + + if (bs > size) + bs = size; + + if (mlock(a, bs) < 0) { + bs = PA_PAGE_ALIGN(bs / 2); + continue; + } + + pa_assert_se(munlock(a, bs) == 0); + + a = (const uint8_t*) a + bs; + size -= bs; + } +#endif + + if (bs <= 0) + pa_log_debug("mlock() failed too (or doesn't exist), giving up: %s", pa_cstrerror(errno)); + else + pa_log_debug("mlock() worked fine!"); + + return (void*) p; +} + +void pa_close_pipe(int fds[2]) { + pa_assert(fds); + + if (fds[0] >= 0) + pa_assert_se(pa_close(fds[0]) == 0); + + if (fds[1] >= 0) + pa_assert_se(pa_close(fds[1]) == 0); + + fds[0] = fds[1] = -1; +} + +char *pa_readlink(const char *p) { + size_t l = 100; + + for (;;) { + char *c; + ssize_t n; + + c = pa_xnew(char, l); + + if ((n = readlink(p, c, l-1)) < 0) { + pa_xfree(c); + return NULL; + } + + if ((size_t) n < l-1) { + c[n] = 0; + return c; + } + + pa_xfree(c); + l *= 2; + } +} + +int pa_close_all(int except_fd, ...) { + va_list ap; + int n = 0, i, r; + int *p; + + va_start(ap, except_fd); + + if (except_fd >= 0) + for (n = 1; va_arg(ap, int) >= 0; n++) + ; + + va_end(ap); + + p = pa_xnew(int, n+1); + + va_start(ap, except_fd); + + i = 0; + if (except_fd >= 0) { + int fd; + p[i++] = except_fd; + + while ((fd = va_arg(ap, int)) >= 0) + p[i++] = fd; + } + p[i] = -1; + + va_end(ap); + + r = pa_close_allv(p); + free(p); + + return r; +} + +int pa_close_allv(const int except_fds[]) { + struct rlimit rl; + int fd; + int saved_errno; + +#ifdef __linux__ + + DIR *d; + + if ((d = opendir("/proc/self/fd"))) { + + struct dirent *de; + + while ((de = readdir(d))) { + pa_bool_t found; + long l; + char *e = NULL; + int i; + + if (de->d_name[0] == '.') + continue; + + errno = 0; + l = strtol(de->d_name, &e, 10); + if (errno != 0 || !e || *e) { + closedir(d); + errno = EINVAL; + return -1; + } + + fd = (int) l; + + if ((long) fd != l) { + closedir(d); + errno = EINVAL; + return -1; + } + + if (fd < 3) + continue; + + if (fd == dirfd(d)) + continue; + + found = FALSE; + for (i = 0; except_fds[i] >= 0; i++) + if (except_fds[i] == fd) { + found = TRUE; + break; + } + + if (found) + continue; + + if (pa_close(fd) < 0) { + saved_errno = errno; + closedir(d); + errno = saved_errno; + + return -1; + } + } + + closedir(d); + return 0; + } + +#endif + + if (getrlimit(RLIMIT_NOFILE, &rl) < 0) + return -1; + + for (fd = 0; fd < (int) rl.rlim_max; fd++) { + int i; + + if (fd <= 3) + continue; + + for (i = 0; except_fds[i] >= 0; i++) + if (except_fds[i] == fd) + continue; + + if (close(fd) < 0 && errno != EBADF) + return -1; + } + return 0; } + +int pa_unblock_sigs(int except, ...) { + va_list ap; + int n = 0, i, r; + int *p; + + va_start(ap, except); + + if (except >= 1) + for (n = 1; va_arg(ap, int) >= 0; n++) + ; + + va_end(ap); + + p = pa_xnew(int, n+1); + + va_start(ap, except); + + i = 0; + if (except >= 1) { + int sig; + p[i++] = except; + + while ((sig = va_arg(ap, int)) >= 0) + p[i++] = sig; + } + p[i] = -1; + + va_end(ap); + + r = pa_unblock_sigsv(p); + pa_xfree(p); + + return r; +} + +int pa_unblock_sigsv(const int except[]) { + int i; + sigset_t ss; + + if (sigemptyset(&ss) < 0) + return -1; + + for (i = 0; except[i] > 0; i++) + if (sigaddset(&ss, except[i]) < 0) + return -1; + + return sigprocmask(SIG_SETMASK, &ss, NULL); +} + +int pa_reset_sigs(int except, ...) { + va_list ap; + int n = 0, i, r; + int *p; + + va_start(ap, except); + + if (except >= 1) + for (n = 1; va_arg(ap, int) >= 0; n++) + ; + + va_end(ap); + + p = pa_xnew(int, n+1); + + va_start(ap, except); + + i = 0; + if (except >= 1) { + p[i++] = except; + + while ((p[i++] = va_arg(ap, int)) >= 0) + ; + } + p[i] = -1; + + va_end(ap); + + r = pa_reset_sigsv(p); + pa_xfree(p); + + return r; +} + +int pa_reset_sigsv(const int except[]) { + int sig; + + for (sig = 1; sig < _NSIG; sig++) { + pa_bool_t reset = TRUE; + + switch (sig) { + case SIGKILL: + case SIGSTOP: + reset = FALSE; + break; + + default: { + int i; + + for (i = 0; except[i] > 0; i++) { + if (sig == except[i]) { + reset = FALSE; + break; + } + } + } + } + + if (reset) { + struct sigaction sa; + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_DFL; + + /* On Linux the first two RT signals are reserved by + * glibc, and sigaction() will return EINVAL for them. */ + if ((sigaction(sig, &sa, NULL) < 0)) + if (errno != EINVAL) + return -1; + } + } + + return 0; +} + +void pa_set_env(const char *key, const char *value) { + pa_assert(key); + pa_assert(value); + + putenv(pa_sprintf_malloc("%s=%s", key, value)); +} + +pa_bool_t pa_in_system_mode(void) { + const char *e; + + if (!(e = getenv("PULSE_SYSTEM"))) + return FALSE; + + return !!atoi(e); +} |