X-Git-Url: https://code.delx.au/pulseaudio/blobdiff_plain/2ee9276d97f15ea965fb8a88f2aa184355b9903a..8ad8e393f135f294942347dd66ec5e6c53329764:/src/pulsecore/core-util.c?ds=sidebyside diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c index dde34d7b..8e98e857 100644 --- a/src/pulsecore/core-util.c +++ b/src/pulsecore/core-util.c @@ -43,6 +43,7 @@ #include #include #include +#include #ifdef HAVE_STRTOF_L #include @@ -50,6 +51,10 @@ #ifdef HAVE_SCHED_H #include + +#if defined(__linux__) && !defined(SCHED_RESET_ON_FORK) +#define SCHED_RESET_ON_FORK 0x40000000 +#endif #endif #ifdef HAVE_SYS_RESOURCE_H @@ -88,6 +93,18 @@ #include #endif +#ifdef __APPLE__ +#include +#endif + +#ifdef HAVE_DBUS +#include "rtkit.h" +#endif + +#ifdef __linux__ +#include +#endif + #include #include #include @@ -97,6 +114,9 @@ #include #include #include +#include +#include +#include #include "core-util.h" @@ -105,6 +125,8 @@ #define MSG_NOSIGNAL 0 #endif +static pa_strlist *recorded_env = NULL; + #ifdef OS_IS_WIN32 #define PULSE_ROOTENV "PULSE_ROOT" @@ -291,7 +313,15 @@ ssize_t pa_read(int fd, void *buf, size_t count, int *type) { #endif - return read(fd, buf, count); + for (;;) { + ssize_t r; + + if ((r = read(fd, buf, count)) < 0) + if (errno == EINTR) + continue; + + return r; + } } /** Similar to pa_read(), but handles writes */ @@ -300,8 +330,17 @@ ssize_t pa_write(int fd, const void *buf, size_t count, int *type) { if (!type || *type == 0) { ssize_t r; - if ((r = send(fd, buf, count, MSG_NOSIGNAL)) >= 0) + for (;;) { + if ((r = send(fd, buf, count, MSG_NOSIGNAL)) < 0) { + + if (errno == EINTR) + continue; + + break; + } + return r; + } #ifdef OS_IS_WIN32 if (WSAGetLastError() != WSAENOTSOCK) { @@ -317,11 +356,19 @@ ssize_t pa_write(int fd, const void *buf, size_t count, int *type) { *type = 1; } - return write(fd, buf, count); + for (;;) { + ssize_t r; + + if ((r = write(fd, buf, count)) < 0) + if (errno == EINTR) + continue; + + return r; + } } /** Calls read() in a loop. Makes sure that as much as 'size' bytes, - * unless EOF is reached or an error occured */ + * unless EOF is reached or an error occurred */ ssize_t pa_loop_read(int fd, void*data, size_t size, int *type) { ssize_t ret = 0; int _type; @@ -402,11 +449,11 @@ int pa_close(int fd) { for (;;) { int r; - if ((r = close(fd)) >= 0) - return r; + if ((r = close(fd)) < 0) + if (errno == EINTR) + continue; - if (errno != EINTR) - return r; + return r; } } @@ -513,136 +560,148 @@ 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) { + size_t k; + pa_assert(b); pa_assert(s); pa_assert(l > 0); - strncpy(b, s, l); - b[l-1] = 0; + k = strlen(s); + + if (k > l-1) + k = l-1; + + memcpy(b, s, k); + b[k] = 0; + return b; } -/* 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 +static int set_scheduler(int rtprio) { struct sched_param sp; - int r, policy; + int r; +#ifdef HAVE_DBUS + DBusError error; + DBusConnection *bus; - memset(&sp, 0, sizeof(sp)); - policy = 0; + dbus_error_init(&error); +#endif - if ((r = pthread_getschedparam(pthread_self(), &policy, &sp)) != 0) { - pa_log("pthread_getschedgetparam(): %s", pa_cstrerror(r)); - return -1; + pa_zero(sp); + sp.sched_priority = rtprio; + +#ifdef SCHED_RESET_ON_FORK + 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 (policy == SCHED_FIFO && sp.sched_priority >= rtprio) { - pa_log_info("Thread already being scheduled with SCHED_FIFO with priority %i.", sp.sched_priority); + if (pthread_setschedparam(pthread_self(), SCHED_RR, &sp) == 0) { + pa_log_debug("SCHED_RR worked."); return 0; } - sp.sched_priority = rtprio; - if ((r = pthread_setschedparam(pthread_self(), SCHED_FIFO, &sp)) != 0) { +#ifdef HAVE_DBUS + /* Try to talk to RealtimeKit */ - while (sp.sched_priority > 1) { - sp.sched_priority --; + if (!(bus = dbus_bus_get(DBUS_BUS_SYSTEM, &error))) { + pa_log("Failed to connect to system bus: %s\n", error.message); + dbus_error_free(&error); + errno = -EIO; + return -1; + } - 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; - } - } + /* 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); - pa_log_warn("pthread_setschedparam(): %s", pa_cstrerror(r)); - return -1; + r = rtkit_make_realtime(bus, 0, rtprio); + dbus_connection_unref(bus); + + if (r >= 0) { + pa_log_debug("RealtimeKit worked."); + return 0; } - pa_log_info("Successfully enabled SCHED_FIFO scheduling for thread, with priority %i.", sp.sched_priority); - return 0; + errno = -r; #else + errno = r; +#endif - errno = ENOTSUP; return -1; -#endif } -/* 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; +/* 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) { -#if defined(HAVE_SYS_RESOURCE_H) && defined(RLIMIT_RTPRIO) - { - struct rlimit rl; +#ifdef _POSIX_PRIORITY_SCHEDULING + int p; - if (getrlimit(RLIMIT_RTPRIO, &rl) >= 0) - if (rl.rlim_cur > 0 || rl.rlim_cur == RLIM_INFINITY) - return TRUE; + if (set_scheduler(rtprio) >= 0) { + pa_log_info("Successfully enabled SCHED_RR scheduling for thread, with priority %i.", rtprio); + return 0; } -#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; + for (p = rtprio-1; p >= 1; p--) + if (set_scheduler(p)) { + pa_log_info("Successfully enabled SCHED_RR scheduling for thread, with priority %i, which is lower than the requested %i.", p, rtprio); + return 0; + } - if (cap_get_flag(cap, CAP_SYS_NICE, CAP_EFFECTIVE, &flag) >= 0) - if (flag == CAP_SET) { - cap_free(cap); - return TRUE; - } + pa_log_info("Failed to acquire real-time scheduling: %s", pa_cstrerror(errno)); + return -1; +#else - cap_free(cap); - } - } + errno = ENOTSUP; + return -1; #endif - - 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; +static int set_nice(int nice_level) { +#ifdef HAVE_DBUS + DBusError error; + DBusConnection *bus; + int r; -#if defined(HAVE_SYS_RESOURCE_H) && defined(RLIMIT_RTPRIO) - { - struct rlimit rl; + dbus_error_init(&error); +#endif - if (getrlimit(RLIMIT_NICE, &rl) >= 0) - if (rl.rlim_cur >= 21 || rl.rlim_cur == RLIM_INFINITY) - return TRUE; + if (setpriority(PRIO_PROCESS, 0, nice_level) >= 0) { + pa_log_debug("setpriority() worked."); + return 0; } -#endif -#if defined(HAVE_SYS_CAPABILITY_H) && defined(CAP_SYS_NICE) - { - cap_t cap; +#ifdef HAVE_DBUS + /* Try to talk to RealtimeKit */ - if ((cap = cap_get_proc())) { - cap_flag_value_t flag = CAP_CLEAR; + if (!(bus = dbus_bus_get(DBUS_BUS_SYSTEM, &error))) { + pa_log("Failed to connect to system bus: %s\n", error.message); + dbus_error_free(&error); + errno = -EIO; + return -1; + } - if (cap_get_flag(cap, CAP_SYS_NICE, CAP_EFFECTIVE, &flag) >= 0) - if (flag == CAP_SET) { - cap_free(cap); - return TRUE; - } + /* 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); - cap_free(cap); - } + r = rtkit_make_high_priority(bus, 0, nice_level); + dbus_connection_unref(bus); + + if (r >= 0) { + pa_log_debug("RealtimeKit worked."); + return 0; } + + errno = -r; #endif - return FALSE; + return -1; } /* Raise the priority of the current process as much as possible that @@ -650,22 +709,21 @@ pa_bool_t pa_can_high_priority(void) { int pa_raise_priority(int nice_level) { #ifdef HAVE_SYS_RESOURCE_H - if (setpriority(PRIO_PROCESS, 0, nice_level) < 0) { - int n; + int n; - for (n = nice_level+1; n < 0; n++) { + if (set_nice(nice_level) >= 0) { + pa_log_info("Successfully gained nice level %i.", nice_level); + return 0; + } - 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; - } + for (n = nice_level+1; n < 0; n++) + 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; } - pa_log_warn("setpriority(): %s", pa_cstrerror(errno)); - return -1; - } - - pa_log_info("Successfully gained nice level %i.", nice_level); + pa_log_info("Failed to acquire high-priority scheduling: %s", pa_cstrerror(errno)); + return -1; #endif #ifdef OS_IS_WIN32 @@ -673,9 +731,10 @@ int pa_raise_priority(int nice_level) { if (!SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS)) { pa_log_warn("SetPriorityClass() failed: 0x%08X", GetLastError()); errno = EPERM; - return .-1; - } else - pa_log_info("Successfully gained high priority class."); + return -1; + } + + pa_log_info("Successfully gained high priority class."); } #endif @@ -690,8 +749,8 @@ void pa_reset_priority(void) { setpriority(PRIO_PROCESS, 0, 0); - memset(&sp, 0, sizeof(sp)); - pa_assert_se(pthread_setschedparam(pthread_self(), SCHED_OTHER, &sp) == 0); + pa_zero(sp); + pthread_setschedparam(pthread_self(), SCHED_OTHER, &sp); #endif #ifdef OS_IS_WIN32 @@ -699,7 +758,7 @@ void pa_reset_priority(void) { #endif } -static int match(const char *expr, const char *v) { +int pa_match(const char *expr, const char *v) { int k; regex_t re; int r; @@ -727,7 +786,6 @@ static int match(const char *expr, const char *v) { /* 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 */ @@ -739,12 +797,12 @@ int pa_parse_boolean(const char *v) { /* And then we check language dependant */ if ((expr = nl_langinfo(YESEXPR))) if (expr[0]) - if ((r = match(expr, v)) > 0) + if (pa_match(expr, v) > 0) return 1; if ((expr = nl_langinfo(NOEXPR))) if (expr[0]) - if ((r = match(expr, v)) > 0) + if (pa_match(expr, v) > 0) return 0; errno = EINVAL; @@ -924,54 +982,24 @@ fail: /* Check whether the specified GID and the group name match */ static int is_group(gid_t gid, const char *name) { - struct group group, *result = NULL; - long n; - void *data; + struct group *group = NULL; int r = -1; -#ifdef HAVE_GETGRGID_R -#ifdef _SC_GETGR_R_SIZE_MAX - n = sysconf(_SC_GETGR_R_SIZE_MAX); -#else - n = -1; -#endif - if (n < 0) - n = 512; - - data = pa_xmalloc((size_t) n); - errno = 0; - if (getgrgid_r(gid, &group, data, (size_t) n, &result) < 0 || !result) { - pa_log("getgrgid_r(%u): %s", (unsigned) gid, pa_cstrerror(errno)); - + if (!(group = pa_getgrgid_malloc(gid))) + { if (!errno) errno = ENOENT; - goto finish; - } - - r = strcmp(name, result->gr_name) == 0; - -finish: - pa_xfree(data); -#else - /* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X) that do not - * support getgrgid_r. */ - - errno = 0; - if ((result = getgrgid(gid)) == NULL) { - pa_log("getgrgid(%u): %s", gid, pa_cstrerror(errno)); - - if (!errno) - errno = ENOENT; + pa_log("pa_getgrgid_malloc(%u): %s", gid, pa_cstrerror(errno)); goto finish; } - r = strcmp(name, result->gr_name) == 0; + r = strcmp(name, group->gr_name) == 0; finish: -#endif + pa_getgrgid_free(group); return r; } @@ -1020,44 +1048,37 @@ finish: /* Check whether the specifc user id is a member of the specified group */ int pa_uid_in_group(uid_t uid, const char *name) { - char *g_buf, *p_buf; - long g_n, p_n; - struct group grbuf, *gr; + struct group *group = NULL; char **i; int r = -1; - g_n = sysconf(_SC_GETGR_R_SIZE_MAX); - g_buf = pa_xmalloc((size_t) g_n); - - p_n = sysconf(_SC_GETPW_R_SIZE_MAX); - p_buf = pa_xmalloc((size_t) p_n); - errno = 0; - if (getgrnam_r(name, &grbuf, g_buf, (size_t) g_n, &gr) != 0 || !gr) { - + if (!(group = pa_getgrnam_malloc(name))) + { if (!errno) errno = ENOENT; - goto finish; } r = 0; - for (i = gr->gr_mem; *i; i++) { - struct passwd pwbuf, *pw; + for (i = group->gr_mem; *i; i++) { + struct passwd *pw = NULL; errno = 0; - if (getpwnam_r(*i, &pwbuf, p_buf, (size_t) p_n, &pw) != 0 || !pw) + if (!(pw = pa_getpwnam_malloc(*i))) continue; - if (pw->pw_uid == uid) { + if (pw->pw_uid == uid) r = 1; + + pa_getpwnam_free(pw); + + if (r == 1) break; - } } finish: - pa_xfree(g_buf); - pa_xfree(p_buf); + pa_getgrnam_free(group); return r; } @@ -1065,26 +1086,20 @@ finish: /* Get the GID of a gfiven group, return (gid_t) -1 on failure. */ gid_t pa_get_gid_of_group(const char *name) { gid_t ret = (gid_t) -1; - char *g_buf; - long g_n; - struct group grbuf, *gr; - - g_n = sysconf(_SC_GETGR_R_SIZE_MAX); - g_buf = pa_xmalloc((size_t) g_n); + struct group *gr = NULL; errno = 0; - if (getgrnam_r(name, &grbuf, g_buf, (size_t) g_n, &gr) != 0 || !gr) { - + if (!(gr = pa_getgrnam_malloc(name))) + { if (!errno) errno = ENOENT; - goto finish; } ret = gr->gr_gid; finish: - pa_xfree(g_buf); + pa_getgrnam_free(gr); return ret; } @@ -1131,22 +1146,22 @@ int pa_check_in_group(gid_t g) { (advisory on UNIX, mandatory on Windows) */ int pa_lock_fd(int fd, int b) { #ifdef F_SETLKW - struct flock flock; + struct flock f_lock; /* Try a R/W lock first */ - flock.l_type = (short) (b ? F_WRLCK : F_UNLCK); - flock.l_whence = SEEK_SET; - flock.l_start = 0; - flock.l_len = 0; + f_lock.l_type = (short) (b ? F_WRLCK : F_UNLCK); + f_lock.l_whence = SEEK_SET; + f_lock.l_start = 0; + f_lock.l_len = 0; - if (fcntl(fd, F_SETLKW, &flock) >= 0) + 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. */ if (b && errno == EBADF) { - flock.l_type = F_RDLCK; - if (fcntl(fd, F_SETLKW, &flock) >= 0) + f_lock.l_type = F_RDLCK; + if (fcntl(fd, F_SETLKW, &f_lock) >= 0) return 0; } @@ -1179,7 +1194,7 @@ char* pa_strip_nl(char *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 (;;) { @@ -1207,7 +1222,7 @@ int pa_lock_lockfile(const char *fn) { goto fail; } - /* Check wheter the file has been removed meanwhile. When yes, + /* Check whether the file has been removed meanwhile. When yes, * restart this loop, otherwise, we're done */ if (st.st_nlink >= 1) break; @@ -1222,8 +1237,6 @@ int pa_lock_lockfile(const char *fn) { fd = -1; goto fail; } - - fd = -1; } return fd; @@ -1265,26 +1278,32 @@ int pa_unlock_lockfile(const char *fn, int fd) { } static char *get_pulse_home(void) { - char h[PATH_MAX]; + char *h; struct stat st; + char *ret = NULL; - if (!pa_get_home_dir(h, sizeof(h))) { + if (!(h = pa_get_home_dir_malloc())) { 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; + goto finish; } if (st.st_uid != getuid()) { pa_log_error("Home directory %s not ours.", h); errno = EACCES; - return NULL; + goto finish; } - return pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse", h); + ret = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse", h); + +finish: + pa_xfree(h); + + return ret; } char *pa_get_state_dir(void) { @@ -1309,25 +1328,60 @@ char *pa_get_state_dir(void) { return d; } +char *pa_get_home_dir_malloc(void) { + char *homedir; + size_t allocated = 128; + + for (;;) { + homedir = pa_xmalloc(allocated); + + if (!pa_get_home_dir(homedir, allocated)) { + pa_xfree(homedir); + return NULL; + } + + if (strlen(homedir) < allocated - 1) + break; + + pa_xfree(homedir); + allocated *= 2; + } + + return homedir; +} + +char *pa_get_binary_name_malloc(void) { + char *t; + size_t allocated = 128; + + for (;;) { + t = pa_xmalloc(allocated); + + if (!pa_get_binary_name(t, allocated)) { + pa_xfree(t); + return NULL; + } + + if (strlen(t) < allocated - 1) + break; + + pa_xfree(t); + allocated *= 2; + } + + return t; +} + static char* make_random_dir(mode_t m) { static const char table[] = "abcdefghijklmnopqrstuvwxyz" "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 (;;) { @@ -1376,6 +1430,7 @@ static int make_random_dir_and_link(mode_t m, const char *k) { return -1; } + pa_xfree(p); return 0; } @@ -1408,6 +1463,7 @@ char *pa_get_runtime_dir(void) { 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)); + pa_xfree(d); goto fail; } @@ -1416,7 +1472,7 @@ char *pa_get_runtime_dir(void) { goto fail; } - k = pa_sprintf_malloc("%s" PA_PATH_SEP "%s:runtime", d, mid); + k = pa_sprintf_malloc("%s" PA_PATH_SEP "%s-runtime", d, mid); pa_xfree(d); pa_xfree(mid); @@ -1561,14 +1617,15 @@ FILE *pa_open_config_file(const char *global, const char *local, const char *env if (local) { const char *e; char *lfn; - char h[PATH_MAX]; + char *h; FILE *f; 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))) + else if ((h = pa_get_home_dir_malloc())) { fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse" PA_PATH_SEP "%s", h, local); - else + pa_xfree(h); + } else return NULL; #ifdef OS_IS_WIN32 @@ -1648,13 +1705,14 @@ char *pa_find_config_file(const char *global, const char *local, const char *env if (local) { const char *e; char *lfn; - char h[PATH_MAX]; + char *h; 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))) + else if ((h = pa_get_home_dir_malloc())) { fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse" PA_PATH_SEP "%s", h, local); - else + pa_xfree(h); + } else return NULL; #ifdef OS_IS_WIN32 @@ -1820,17 +1878,17 @@ char *pa_make_path_absolute(const char *p) { static char *get_path(const char *fn, pa_bool_t prependmid, 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; + if (pa_is_path_absolute(fn)) + return pa_xstrdup(fn); + + if (!rtp) + return NULL; + if (prependmid) { char *mid; @@ -1839,7 +1897,7 @@ static char *get_path(const char *fn, pa_bool_t prependmid, pa_bool_t rt) { 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", rtp, mid, fn); pa_xfree(mid); } else r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", rtp, fn); @@ -2166,18 +2224,17 @@ int pa_close_all(int except_fd, ...) { va_end(ap); r = pa_close_allv(p); - free(p); + pa_xfree(p); return r; } int pa_close_allv(const int except_fds[]) { struct rlimit rl; - int fd; - int saved_errno; + int maxfd, fd; #ifdef __linux__ - + int saved_errno; DIR *d; if ((d = opendir("/proc/self/fd"))) { @@ -2240,10 +2297,12 @@ int pa_close_allv(const int except_fds[]) { #endif - if (getrlimit(RLIMIT_NOFILE, &rl) < 0) - return -1; + if (getrlimit(RLIMIT_NOFILE, &rl) >= 0) + maxfd = (int) rl.rlim_max; + else + maxfd = sysconf(_SC_OPEN_MAX); - for (fd = 3; fd < (int) rl.rlim_max; fd++) { + for (fd = 3; fd < maxfd; fd++) { int i; pa_bool_t found; @@ -2336,7 +2395,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; @@ -2393,9 +2452,38 @@ void pa_set_env(const char *key, const char *value) { pa_assert(key); pa_assert(value); + /* This is not thread-safe */ + putenv(pa_sprintf_malloc("%s=%s", key, value)); } +void pa_set_env_and_record(const char *key, const char *value) { + pa_assert(key); + pa_assert(value); + + /* This is not thread-safe */ + + pa_set_env(key, value); + recorded_env = pa_strlist_prepend(recorded_env, key); +} + +void pa_unset_env_recorded(void) { + + /* This is not thread-safe */ + + for (;;) { + char *s; + + recorded_env = pa_strlist_pop(recorded_env, &s); + + if (!s) + break; + + unsetenv(s); + pa_xfree(s); + } +} + pa_bool_t pa_in_system_mode(void) { const char *e; @@ -2405,31 +2493,29 @@ pa_bool_t pa_in_system_mode(void) { return !!atoi(e); } -char *pa_machine_id(void) { - FILE *f; - size_t l; - - /* 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. */ +char *pa_get_user_name_malloc(void) { + ssize_t k; + char *u; - if ((f = fopen(PA_MACHINE_ID, "r"))) { - char ln[34] = "", *r; +#ifdef _SC_LOGIN_NAME_MAX + k = (ssize_t) sysconf(_SC_LOGIN_NAME_MAX); - r = fgets(ln, sizeof(ln)-1, f); - fclose(f); + if (k <= 0) +#endif + k = 32; - pa_strip_nl(ln); + u = pa_xnew(char, k+1); - if (ln[0]) - return pa_xstrdup(ln); + if (!(pa_get_user_name(u, k))) { + pa_xfree(u); + return NULL; } - /* The we fall back to the host name. It supposed to be somewhat - * unique, at least in a network, but may change. */ + return u; +} + +char *pa_get_host_name_malloc(void) { + size_t l; l = 100; for (;;) { @@ -2443,13 +2529,16 @@ char *pa_machine_id(void) { break; } else if (strlen(c) < l-1) { + char *u; if (*c == 0) { pa_xfree(c); break; } - return c; + u = pa_utf8_filter(c); + pa_xfree(c); + return u; } /* Hmm, the hostname is as long the space we offered the @@ -2460,15 +2549,53 @@ char *pa_machine_id(void) { l *= 2; } + return NULL; +} + +char *pa_machine_id(void) { + FILE *f; + char *h; + + /* 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. */ + + if ((f = fopen(PA_MACHINE_ID, "r"))) { + char ln[34] = "", *r; + + r = fgets(ln, sizeof(ln)-1, f); + fclose(f); + + pa_strip_nl(ln); + + if (r && ln[0]) + return pa_utf8_filter(ln); + } + + if ((h = pa_get_host_name_malloc())) + return h; + /* If no hostname was set we use the POSIX hostid. It's usually - * the IPv4 address. Mit not be that stable. */ + * the IPv4 address. Might not be that stable. */ return pa_sprintf_malloc("%08lx", (unsigned long) gethostid); } +char *pa_session_id(void) { + const char *e; + + if (!(e = getenv("XDG_SESSION_COOKIE"))) + return NULL; + + return pa_utf8_filter(e); +} + char *pa_uname_string(void) { struct utsname u; - pa_assert_se(uname(&u) == 0); + pa_assert_se(uname(&u) >= 0); return pa_sprintf_malloc("%s %s %s %s", u.sysname, u.machine, u.release, u.version); } @@ -2487,3 +2614,256 @@ pa_bool_t pa_in_valgrind(void) { return b > 1; } #endif + +unsigned pa_gcd(unsigned a, unsigned b) { + + while (b > 0) { + unsigned t = b; + b = a % b; + a = t; + } + + return a; +} + +void pa_reduce(unsigned *num, unsigned *den) { + + unsigned gcd = pa_gcd(*num, *den); + + if (gcd <= 0) + return; + + *num /= gcd; + *den /= gcd; + + pa_assert(pa_gcd(*num, *den) == 1); +} + +unsigned pa_ncpus(void) { + long ncpus; + +#ifdef _SC_NPROCESSORS_CONF + ncpus = sysconf(_SC_NPROCESSORS_CONF); +#else + ncpus = 1; +#endif + + return ncpus <= 0 ? 1 : (unsigned) ncpus; +} + +char *pa_replace(const char*s, const char*a, const char *b) { + pa_strbuf *sb; + size_t an; + + pa_assert(s); + pa_assert(a); + pa_assert(b); + + an = strlen(a); + sb = pa_strbuf_new(); + + for (;;) { + const char *p; + + if (!(p = strstr(s, a))) + break; + + pa_strbuf_putsn(sb, s, p-s); + pa_strbuf_puts(sb, b); + s = p + an; + } + + pa_strbuf_puts(sb, s); + + return pa_strbuf_tostring_free(sb); +} + +char *pa_unescape(char *p) { + char *s, *d; + pa_bool_t escaped = FALSE; + + for (s = p, d = p; *s; s++) { + if (!escaped && *s == '\\') { + escaped = TRUE; + continue; + } + + *(d++) = *s; + escaped = FALSE; + } + + *d = 0; + + return p; +} + +char *pa_realpath(const char *path) { + char *t; + pa_assert(path); + + /* We want only abolsute paths */ + if (path[0] != '/') { + errno = EINVAL; + return NULL; + } + +#if defined(__GLIBC__) || defined(__APPLE__) + { + char *r; + + if (!(r = realpath(path, NULL))) + return NULL; + + /* We copy this here in case our pa_xmalloc() is not + * implemented on top of libc malloc() */ + t = pa_xstrdup(r); + pa_xfree(r); + } +#elif defined(PATH_MAX) + { + char *path_buf; + path_buf = pa_xmalloc(PATH_MAX); + + if (!(t = realpath(path, path_buf))) { + pa_xfree(path_buf); + return NULL; + } + } +#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." +#endif + + return t; +} + +void pa_disable_sigpipe(void) { + +#ifdef SIGPIPE + struct sigaction sa; + + pa_zero(sa); + + if (sigaction(SIGPIPE, NULL, &sa) < 0) { + pa_log("sigaction(): %s", pa_cstrerror(errno)); + return; + } + + sa.sa_handler = SIG_IGN; + + if (sigaction(SIGPIPE, &sa, NULL) < 0) { + pa_log("sigaction(): %s", pa_cstrerror(errno)); + return; + } +#endif +} + +void pa_xfreev(void**a) { + void **p; + + if (!a) + return; + + for (p = a; *p; p++) + pa_xfree(*p); + + pa_xfree(a); +} + +char **pa_split_spaces_strv(const char *s) { + char **t, *e; + unsigned i = 0, n = 8; + const char *state = NULL; + + t = pa_xnew(char*, n); + while ((e = pa_split_spaces(s, &state))) { + t[i++] = e; + + if (i >= n) { + n *= 2; + t = pa_xrenew(char*, t, n); + } + } + + if (i <= 0) { + pa_xfree(t); + return NULL; + } + + t[i] = NULL; + return t; +} + +char* pa_maybe_prefix_path(const char *path, const char *prefix) { + pa_assert(path); + + if (pa_is_path_absolute(path)) + return pa_xstrdup(path); + + return pa_sprintf_malloc("%s" PA_PATH_SEP "%s", prefix, path); +} + +size_t pa_pipe_buf(int fd) { + +#ifdef _PC_PIPE_BUF + long n; + + if ((n = fpathconf(fd, _PC_PIPE_BUF)) >= 0) + return (size_t) n; +#endif + +#ifdef PIPE_BUF + return PIPE_BUF; +#else + return 4096; +#endif +} + +void pa_reset_personality(void) { + +#ifdef __linux__ + 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) { + char *rp; + pa_bool_t 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); + } + + return b; +} + +#endif + +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"; +}