mirror of
				https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
				synced 2025-11-03 09:01:50 -05:00 
			
		
		
		
	If ~/.pulse/ already exists we stick to it in order not to lose configuration and so that pulse configuration may still be shared across the network with different PA versions.
		
			
				
	
	
		
			3350 lines
		
	
	
	
		
			71 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			3350 lines
		
	
	
	
		
			71 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/***
 | 
						|
  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
 | 
						|
  USA.
 | 
						|
***/
 | 
						|
 | 
						|
#ifdef HAVE_CONFIG_H
 | 
						|
#include <config.h>
 | 
						|
#endif
 | 
						|
 | 
						|
#include <stdarg.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <signal.h>
 | 
						|
#include <errno.h>
 | 
						|
#include <string.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <fcntl.h>
 | 
						|
#include <unistd.h>
 | 
						|
#include <limits.h>
 | 
						|
#include <ctype.h>
 | 
						|
#include <sys/types.h>
 | 
						|
#include <sys/stat.h>
 | 
						|
#include <dirent.h>
 | 
						|
 | 
						|
#ifdef HAVE_LANGINFO_H
 | 
						|
#include <langinfo.h>
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef HAVE_UNAME
 | 
						|
#include <sys/utsname.h>
 | 
						|
#endif
 | 
						|
 | 
						|
#if defined(HAVE_REGEX_H)
 | 
						|
#include <regex.h>
 | 
						|
#elif defined(HAVE_PCREPOSIX_H)
 | 
						|
#include <pcreposix.h>
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef HAVE_STRTOF_L
 | 
						|
#include <locale.h>
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef HAVE_SCHED_H
 | 
						|
#include <sched.h>
 | 
						|
 | 
						|
#if defined(__linux__) && !defined(SCHED_RESET_ON_FORK)
 | 
						|
#define SCHED_RESET_ON_FORK 0x40000000
 | 
						|
#endif
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef HAVE_SYS_RESOURCE_H
 | 
						|
#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
 | 
						|
 | 
						|
#ifdef HAVE_NETDB_H
 | 
						|
#include <netdb.h>
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef HAVE_WINDOWS_H
 | 
						|
#include <windows.h>
 | 
						|
#endif
 | 
						|
 | 
						|
#ifndef ENOTSUP
 | 
						|
#define ENOTSUP   135
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef HAVE_PWD_H
 | 
						|
#include <pwd.h>
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef HAVE_GRP_H
 | 
						|
#include <grp.h>
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef HAVE_LIBSAMPLERATE
 | 
						|
#include <samplerate.h>
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef __APPLE__
 | 
						|
#include <xlocale.h>
 | 
						|
#include <mach/mach_init.h>
 | 
						|
#include <mach/thread_act.h>
 | 
						|
#include <mach/thread_policy.h>
 | 
						|
#include <sys/sysctl.h>
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef HAVE_DBUS
 | 
						|
#include "rtkit.h"
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef __linux__
 | 
						|
#include <sys/personality.h>
 | 
						|
#endif
 | 
						|
 | 
						|
#include <pulse/xmalloc.h>
 | 
						|
#include <pulse/util.h>
 | 
						|
#include <pulse/utf8.h>
 | 
						|
 | 
						|
#include <pulsecore/core-error.h>
 | 
						|
#include <pulsecore/socket.h>
 | 
						|
#include <pulsecore/log.h>
 | 
						|
#include <pulsecore/macro.h>
 | 
						|
#include <pulsecore/thread.h>
 | 
						|
#include <pulsecore/strbuf.h>
 | 
						|
#include <pulsecore/usergroup.h>
 | 
						|
#include <pulsecore/strlist.h>
 | 
						|
#include <pulsecore/cpu-x86.h>
 | 
						|
#include <pulsecore/pipe.h>
 | 
						|
 | 
						|
#include "core-util.h"
 | 
						|
 | 
						|
/* Not all platforms have this */
 | 
						|
#ifndef MSG_NOSIGNAL
 | 
						|
#define MSG_NOSIGNAL 0
 | 
						|
#endif
 | 
						|
 | 
						|
#define NEWLINE "\r\n"
 | 
						|
#define WHITESPACE "\n\r \t"
 | 
						|
 | 
						|
static pa_strlist *recorded_env = NULL;
 | 
						|
 | 
						|
#ifdef OS_IS_WIN32
 | 
						|
 | 
						|
/* 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;
 | 
						|
 | 
						|
    if (!toplevel) {
 | 
						|
        char library_path[MAX_PATH];
 | 
						|
        char *p;
 | 
						|
 | 
						|
        if (!GetModuleFileName(handle, library_path, MAX_PATH))
 | 
						|
            return NULL;
 | 
						|
 | 
						|
        toplevel = pa_xstrdup(library_path);
 | 
						|
 | 
						|
        p = strrchr(toplevel, PA_PATH_SEP_CHAR);
 | 
						|
        if (p)
 | 
						|
            *p = '\0';
 | 
						|
 | 
						|
        p = strrchr(toplevel, PA_PATH_SEP_CHAR);
 | 
						|
        if (p && (strcmp(p + 1, "bin") == 0))
 | 
						|
            *p = '\0';
 | 
						|
    }
 | 
						|
 | 
						|
    return toplevel;
 | 
						|
}
 | 
						|
 | 
						|
#endif
 | 
						|
 | 
						|
/** Make a file descriptor nonblock. Doesn't do any error checking */
 | 
						|
void pa_make_fd_nonblock(int fd) {
 | 
						|
 | 
						|
#ifdef O_NONBLOCK
 | 
						|
    int v;
 | 
						|
    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);
 | 
						|
 | 
						|
#elif defined(OS_IS_WIN32)
 | 
						|
    u_long arg = 1;
 | 
						|
    if (ioctlsocket(fd, FIONBIO, &arg) < 0) {
 | 
						|
        pa_assert_se(WSAGetLastError() == WSAENOTSOCK);
 | 
						|
        pa_log_warn("Only sockets can be made non-blocking!");
 | 
						|
    }
 | 
						|
#else
 | 
						|
    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, saved_errno;
 | 
						|
 | 
						|
    pa_assert(dir);
 | 
						|
 | 
						|
#ifdef OS_IS_WIN32
 | 
						|
    r = mkdir(dir);
 | 
						|
#else
 | 
						|
{
 | 
						|
    mode_t u;
 | 
						|
    u = umask((~m) & 0777);
 | 
						|
    r = mkdir(dir, m);
 | 
						|
    umask(u);
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
    if (r < 0 && errno != EEXIST)
 | 
						|
        return -1;
 | 
						|
 | 
						|
#if defined(HAVE_FSTAT) && !defined(OS_IS_WIN32)
 | 
						|
{
 | 
						|
    int fd;
 | 
						|
    if ((fd = open(dir,
 | 
						|
#ifdef O_CLOEXEC
 | 
						|
                   O_CLOEXEC|
 | 
						|
#endif
 | 
						|
#ifdef O_NOCTTY
 | 
						|
                   O_NOCTTY|
 | 
						|
#endif
 | 
						|
#ifdef O_NOFOLLOW
 | 
						|
                   O_NOFOLLOW|
 | 
						|
#endif
 | 
						|
                   O_RDONLY)) < 0)
 | 
						|
        goto fail;
 | 
						|
 | 
						|
    if (fstat(fd, &st) < 0) {
 | 
						|
        pa_assert_se(pa_close(fd) >= 0);
 | 
						|
        goto fail;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!S_ISDIR(st.st_mode)) {
 | 
						|
        pa_assert_se(pa_close(fd) >= 0);
 | 
						|
        errno = EEXIST;
 | 
						|
        goto fail;
 | 
						|
    }
 | 
						|
 | 
						|
#ifdef HAVE_FCHOWN
 | 
						|
    if (uid == (uid_t) -1)
 | 
						|
        uid = getuid();
 | 
						|
    if (gid == (gid_t) -1)
 | 
						|
        gid = getgid();
 | 
						|
    if (fchown(fd, uid, gid) < 0)
 | 
						|
        goto fail;
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef HAVE_FCHMOD
 | 
						|
    (void) fchmod(fd, m);
 | 
						|
#endif
 | 
						|
 | 
						|
    pa_assert_se(pa_close(fd) >= 0);
 | 
						|
}
 | 
						|
#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) ||
 | 
						|
        (st.st_gid != gid) ||
 | 
						|
        ((st.st_mode & 0777) != m)) {
 | 
						|
        errno = EACCES;
 | 
						|
        goto fail;
 | 
						|
    }
 | 
						|
#else
 | 
						|
    pa_log_warn("Secure directory creation not supported on Win32.");
 | 
						|
#endif
 | 
						|
 | 
						|
    return 0;
 | 
						|
 | 
						|
fail:
 | 
						|
    saved_errno = errno;
 | 
						|
    rmdir(dir);
 | 
						|
    errno = saved_errno;
 | 
						|
 | 
						|
    return -1;
 | 
						|
}
 | 
						|
 | 
						|
/* Return a newly allocated sting containing the parent directory of the specified file */
 | 
						|
char *pa_parent_dir(const char *fn) {
 | 
						|
    char *slash, *dir = pa_xstrdup(fn);
 | 
						|
 | 
						|
    if ((slash = (char*) pa_path_get_filename(dir)) == dir) {
 | 
						|
        pa_xfree(dir);
 | 
						|
        errno = ENOENT;
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    *(slash-1) = 0;
 | 
						|
    return dir;
 | 
						|
}
 | 
						|
 | 
						|
/* 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 ret = -1;
 | 
						|
    char *dir;
 | 
						|
 | 
						|
    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;
 | 
						|
}
 | 
						|
 | 
						|
/** Platform independent read function. Necessary since not all
 | 
						|
 * systems treat all file descriptors equal. If type is
 | 
						|
 * non-NULL it is used to cache the type of the fd. This is
 | 
						|
 * useful for making sure that only a single syscall is executed per
 | 
						|
 * function call. The variable pointed to should be initialized to 0
 | 
						|
 * by the caller. */
 | 
						|
ssize_t pa_read(int fd, void *buf, size_t count, int *type) {
 | 
						|
 | 
						|
#ifdef OS_IS_WIN32
 | 
						|
 | 
						|
    if (!type || *type == 0) {
 | 
						|
        ssize_t r;
 | 
						|
 | 
						|
        if ((r = recv(fd, buf, count, 0)) >= 0)
 | 
						|
            return r;
 | 
						|
 | 
						|
        if (WSAGetLastError() != WSAENOTSOCK) {
 | 
						|
            errno = WSAGetLastError();
 | 
						|
            return r;
 | 
						|
        }
 | 
						|
 | 
						|
        if (type)
 | 
						|
            *type = 1;
 | 
						|
    }
 | 
						|
 | 
						|
#endif
 | 
						|
 | 
						|
    for (;;) {
 | 
						|
        ssize_t r;
 | 
						|
 | 
						|
        if ((r = read(fd, buf, count)) < 0)
 | 
						|
            if (errno == EINTR)
 | 
						|
                continue;
 | 
						|
 | 
						|
        return r;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/** Similar to pa_read(), but handles writes */
 | 
						|
ssize_t pa_write(int fd, const void *buf, size_t count, int *type) {
 | 
						|
 | 
						|
    if (!type || *type == 0) {
 | 
						|
        ssize_t r;
 | 
						|
 | 
						|
        for (;;) {
 | 
						|
            if ((r = send(fd, buf, count, MSG_NOSIGNAL)) < 0) {
 | 
						|
 | 
						|
                if (errno == EINTR)
 | 
						|
                    continue;
 | 
						|
 | 
						|
                break;
 | 
						|
            }
 | 
						|
 | 
						|
            return r;
 | 
						|
        }
 | 
						|
 | 
						|
#ifdef OS_IS_WIN32
 | 
						|
        if (WSAGetLastError() != WSAENOTSOCK) {
 | 
						|
            errno = WSAGetLastError();
 | 
						|
            return r;
 | 
						|
        }
 | 
						|
#else
 | 
						|
        if (errno != ENOTSOCK)
 | 
						|
            return r;
 | 
						|
#endif
 | 
						|
 | 
						|
        if (type)
 | 
						|
            *type = 1;
 | 
						|
    }
 | 
						|
 | 
						|
    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 occurred */
 | 
						|
ssize_t pa_loop_read(int fd, void*data, size_t size, int *type) {
 | 
						|
    ssize_t ret = 0;
 | 
						|
    int _type;
 | 
						|
 | 
						|
    pa_assert(fd >= 0);
 | 
						|
    pa_assert(data);
 | 
						|
    pa_assert(size);
 | 
						|
 | 
						|
    if (!type) {
 | 
						|
        _type = 0;
 | 
						|
        type = &_type;
 | 
						|
    }
 | 
						|
 | 
						|
    while (size > 0) {
 | 
						|
        ssize_t r;
 | 
						|
 | 
						|
        if ((r = pa_read(fd, data, size, type)) < 0)
 | 
						|
            return r;
 | 
						|
 | 
						|
        if (r == 0)
 | 
						|
            break;
 | 
						|
 | 
						|
        ret += r;
 | 
						|
        data = (uint8_t*) data + r;
 | 
						|
        size -= (size_t) r;
 | 
						|
    }
 | 
						|
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
/** Similar to pa_loop_read(), but wraps write() */
 | 
						|
ssize_t pa_loop_write(int fd, const void*data, size_t size, int *type) {
 | 
						|
    ssize_t ret = 0;
 | 
						|
    int _type;
 | 
						|
 | 
						|
    pa_assert(fd >= 0);
 | 
						|
    pa_assert(data);
 | 
						|
    pa_assert(size);
 | 
						|
 | 
						|
    if (!type) {
 | 
						|
        _type = 0;
 | 
						|
        type = &_type;
 | 
						|
    }
 | 
						|
 | 
						|
    while (size > 0) {
 | 
						|
        ssize_t r;
 | 
						|
 | 
						|
        if ((r = pa_write(fd, data, size, type)) < 0)
 | 
						|
            return r;
 | 
						|
 | 
						|
        if (r == 0)
 | 
						|
            break;
 | 
						|
 | 
						|
        ret += r;
 | 
						|
        data = (const uint8_t*) data + r;
 | 
						|
        size -= (size_t) r;
 | 
						|
    }
 | 
						|
 | 
						|
    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
 | 
						|
 | 
						|
    for (;;) {
 | 
						|
        int r;
 | 
						|
 | 
						|
        if ((r = close(fd)) < 0)
 | 
						|
            if (errno == EINTR)
 | 
						|
                continue;
 | 
						|
 | 
						|
        return r;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* Print a warning messages in case that the given signal is not
 | 
						|
 * blocked or trapped */
 | 
						|
void pa_check_signal_is_blocked(int sig) {
 | 
						|
#ifdef HAVE_SIGACTION
 | 
						|
    struct sigaction sa;
 | 
						|
    sigset_t set;
 | 
						|
 | 
						|
    /* 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
 | 
						|
    if (pthread_sigmask(SIG_SETMASK, NULL, &set) < 0) {
 | 
						|
#endif
 | 
						|
        if (sigprocmask(SIG_SETMASK, NULL, &set) < 0) {
 | 
						|
            pa_log("sigprocmask(): %s", pa_cstrerror(errno));
 | 
						|
            return;
 | 
						|
        }
 | 
						|
#ifdef HAVE_PTHREAD
 | 
						|
    }
 | 
						|
#endif
 | 
						|
 | 
						|
    if (sigismember(&set, 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_warn("%s is not trapped. This might cause malfunction!", pa_sig2str(sig));
 | 
						|
#else /* HAVE_SIGACTION */
 | 
						|
    pa_log_warn("%s might not be trapped. This might cause malfunction!", pa_sig2str(sig));
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
/* The following function is based on an example from the GNU libc
 | 
						|
 * documentation. This function is similar to GNU's asprintf(). */
 | 
						|
char *pa_sprintf_malloc(const char *format, ...) {
 | 
						|
    size_t size = 100;
 | 
						|
    char *c = NULL;
 | 
						|
 | 
						|
    pa_assert(format);
 | 
						|
 | 
						|
    for(;;) {
 | 
						|
        int r;
 | 
						|
        va_list ap;
 | 
						|
 | 
						|
        c = pa_xrealloc(c, size);
 | 
						|
 | 
						|
        va_start(ap, format);
 | 
						|
        r = vsnprintf(c, size, format, ap);
 | 
						|
        va_end(ap);
 | 
						|
 | 
						|
        c[size-1] = 0;
 | 
						|
 | 
						|
        if (r > -1 && (size_t) r < size)
 | 
						|
            return c;
 | 
						|
 | 
						|
        if (r > -1)    /* glibc 2.1 */
 | 
						|
            size = (size_t) r+1;
 | 
						|
        else           /* glibc 2.0 */
 | 
						|
            size *= 2;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* Same as the previous function, but use a va_list instead of an
 | 
						|
 * ellipsis */
 | 
						|
char *pa_vsprintf_malloc(const char *format, va_list ap) {
 | 
						|
    size_t size = 100;
 | 
						|
    char *c = NULL;
 | 
						|
 | 
						|
    pa_assert(format);
 | 
						|
 | 
						|
    for(;;) {
 | 
						|
        int r;
 | 
						|
        va_list aq;
 | 
						|
 | 
						|
        c = pa_xrealloc(c, size);
 | 
						|
 | 
						|
        va_copy(aq, ap);
 | 
						|
        r = vsnprintf(c, size, format, aq);
 | 
						|
        va_end(aq);
 | 
						|
 | 
						|
        c[size-1] = 0;
 | 
						|
 | 
						|
        if (r > -1 && (size_t) r < size)
 | 
						|
            return c;
 | 
						|
 | 
						|
        if (r > -1)    /* glibc 2.1 */
 | 
						|
            size = (size_t) r+1;
 | 
						|
        else           /* glibc 2.0 */
 | 
						|
            size *= 2;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* 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);
 | 
						|
 | 
						|
    k = strlen(s);
 | 
						|
 | 
						|
    if (k > l-1)
 | 
						|
        k = l-1;
 | 
						|
 | 
						|
    memcpy(b, s, k);
 | 
						|
    b[k] = 0;
 | 
						|
 | 
						|
    return b;
 | 
						|
}
 | 
						|
 | 
						|
#ifdef _POSIX_PRIORITY_SCHEDULING
 | 
						|
static int set_scheduler(int rtprio) {
 | 
						|
#ifdef HAVE_SCHED_H
 | 
						|
    struct sched_param sp;
 | 
						|
#ifdef HAVE_DBUS
 | 
						|
    int r;
 | 
						|
    DBusError error;
 | 
						|
    DBusConnection *bus;
 | 
						|
 | 
						|
    dbus_error_init(&error);
 | 
						|
#endif
 | 
						|
 | 
						|
    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 (pthread_setschedparam(pthread_self(), SCHED_RR, &sp) == 0) {
 | 
						|
        pa_log_debug("SCHED_RR worked.");
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
#endif  /* HAVE_SCHED_H */
 | 
						|
 | 
						|
#ifdef HAVE_DBUS
 | 
						|
    /* Try to talk to RealtimeKit */
 | 
						|
 | 
						|
    if (!(bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error))) {
 | 
						|
        pa_log("Failed to connect to system bus: %s\n", error.message);
 | 
						|
        dbus_error_free(&error);
 | 
						|
        errno = -EIO;
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    /* We need to disable exit on disconnect because otherwise
 | 
						|
     * dbus_shutdown will kill us. See
 | 
						|
     * https://bugs.freedesktop.org/show_bug.cgi?id=16924 */
 | 
						|
    dbus_connection_set_exit_on_disconnect(bus, FALSE);
 | 
						|
 | 
						|
    r = rtkit_make_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
 | 
						|
    errno = 0;
 | 
						|
#endif
 | 
						|
 | 
						|
    return -1;
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
/* Make the current thread a realtime thread, and acquire the highest
 | 
						|
 * rtprio we can get that is less or equal the specified parameter. If
 | 
						|
 * the thread is already realtime, don't do anything. */
 | 
						|
int pa_make_realtime(int rtprio) {
 | 
						|
 | 
						|
#if defined(OS_IS_DARWIN)
 | 
						|
    struct thread_time_constraint_policy ttcpolicy;
 | 
						|
    uint64_t freq = 0;
 | 
						|
    size_t size = sizeof(freq);
 | 
						|
    int ret;
 | 
						|
 | 
						|
    ret = sysctlbyname("hw.cpufrequency", &freq, &size, NULL, 0);
 | 
						|
    if (ret < 0) {
 | 
						|
        pa_log_info("Unable to read CPU frequency, acquisition of real-time scheduling failed.");
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    pa_log_debug("sysctl for hw.cpufrequency: %llu", freq);
 | 
						|
 | 
						|
    /* See http://developer.apple.com/library/mac/#documentation/Darwin/Conceptual/KernelProgramming/scheduler/scheduler.html */
 | 
						|
    ttcpolicy.period = freq / 160;
 | 
						|
    ttcpolicy.computation = freq / 3300;
 | 
						|
    ttcpolicy.constraint = freq / 2200;
 | 
						|
    ttcpolicy.preemptible = 1;
 | 
						|
 | 
						|
    ret = thread_policy_set(mach_thread_self(),
 | 
						|
                            THREAD_TIME_CONSTRAINT_POLICY,
 | 
						|
                            (thread_policy_t) &ttcpolicy,
 | 
						|
                            THREAD_TIME_CONSTRAINT_POLICY_COUNT);
 | 
						|
    if (ret) {
 | 
						|
        pa_log_info("Unable to set real-time thread priority (%08x).", ret);
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    pa_log_info("Successfully acquired real-time thread priority.");
 | 
						|
    return 0;
 | 
						|
 | 
						|
#elif defined(_POSIX_PRIORITY_SCHEDULING)
 | 
						|
    int p;
 | 
						|
 | 
						|
    if (set_scheduler(rtprio) >= 0) {
 | 
						|
        pa_log_info("Successfully enabled SCHED_RR scheduling for thread, with priority %i.", rtprio);
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    for (p = rtprio-1; p >= 1; p--)
 | 
						|
        if (set_scheduler(p) >= 0) {
 | 
						|
            pa_log_info("Successfully enabled SCHED_RR scheduling for thread, with priority %i, which is lower than the requested %i.", p, rtprio);
 | 
						|
            return 0;
 | 
						|
        }
 | 
						|
#elif defined(OS_IS_WIN32)
 | 
						|
    /* Windows only allows realtime scheduling to be set on a per process basis.
 | 
						|
     * Therefore, instead of making the thread realtime, just give it the highest non-realtime priority. */
 | 
						|
    if (SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL)) {
 | 
						|
        pa_log_info("Successfully enabled THREAD_PRIORITY_TIME_CRITICAL scheduling for thread.");
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    pa_log_warn("SetThreadPriority() failed: 0x%08X", GetLastError());
 | 
						|
    errno = EPERM;
 | 
						|
#else
 | 
						|
    errno = ENOTSUP;
 | 
						|
#endif
 | 
						|
    pa_log_info("Failed to acquire real-time scheduling: %s", pa_cstrerror(errno));
 | 
						|
    return -1;
 | 
						|
}
 | 
						|
 | 
						|
#ifdef HAVE_SYS_RESOURCE_H
 | 
						|
static int set_nice(int nice_level) {
 | 
						|
#ifdef HAVE_DBUS
 | 
						|
    DBusError error;
 | 
						|
    DBusConnection *bus;
 | 
						|
    int r;
 | 
						|
 | 
						|
    dbus_error_init(&error);
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef HAVE_SYS_RESOURCE_H
 | 
						|
    if (setpriority(PRIO_PROCESS, 0, nice_level) >= 0) {
 | 
						|
        pa_log_debug("setpriority() worked.");
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef HAVE_DBUS
 | 
						|
    /* Try to talk to RealtimeKit */
 | 
						|
 | 
						|
    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;
 | 
						|
    }
 | 
						|
 | 
						|
    /* We need to disable exit on disconnect because otherwise
 | 
						|
     * dbus_shutdown will kill us. See
 | 
						|
     * https://bugs.freedesktop.org/show_bug.cgi?id=16924 */
 | 
						|
    dbus_connection_set_exit_on_disconnect(bus, FALSE);
 | 
						|
 | 
						|
    r = rtkit_make_high_priority(bus, 0, nice_level);
 | 
						|
    dbus_connection_unref(bus);
 | 
						|
 | 
						|
    if (r >= 0) {
 | 
						|
        pa_log_debug("RealtimeKit worked.");
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    errno = -r;
 | 
						|
#endif
 | 
						|
 | 
						|
    return -1;
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
/* 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 HAVE_SYS_RESOURCE_H
 | 
						|
    int n;
 | 
						|
 | 
						|
    if (set_nice(nice_level) >= 0) {
 | 
						|
        pa_log_info("Successfully gained nice level %i.", 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_info("Failed to acquire high-priority scheduling: %s", pa_cstrerror(errno));
 | 
						|
    return -1;
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef OS_IS_WIN32
 | 
						|
    if (nice_level < 0) {
 | 
						|
        if (!SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS)) {
 | 
						|
            pa_log_warn("SetPriorityClass() failed: 0x%08X", GetLastError());
 | 
						|
            errno = EPERM;
 | 
						|
            return -1;
 | 
						|
        }
 | 
						|
 | 
						|
        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);
 | 
						|
 | 
						|
    pa_zero(sp);
 | 
						|
    pthread_setschedparam(pthread_self(), SCHED_OTHER, &sp);
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef OS_IS_WIN32
 | 
						|
    SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
int pa_match(const char *expr, const char *v) {
 | 
						|
    int k;
 | 
						|
    regex_t re;
 | 
						|
    int r;
 | 
						|
 | 
						|
    if (regcomp(&re, expr, REG_NOSUB|REG_EXTENDED) != 0) {
 | 
						|
        errno = EINVAL;
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    if ((k = regexec(&re, v, 0, NULL, 0)) == 0)
 | 
						|
        r = 1;
 | 
						|
    else if (k == REG_NOMATCH)
 | 
						|
        r = 0;
 | 
						|
    else
 | 
						|
        r = -1;
 | 
						|
 | 
						|
    regfree(&re);
 | 
						|
 | 
						|
    if (r < 0)
 | 
						|
        errno = EINVAL;
 | 
						|
 | 
						|
    return r;
 | 
						|
}
 | 
						|
 | 
						|
/* Try to parse a boolean string value.*/
 | 
						|
int pa_parse_boolean(const char *v) {
 | 
						|
    pa_assert(v);
 | 
						|
 | 
						|
    /* First we check language independent */
 | 
						|
    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"))
 | 
						|
        return 0;
 | 
						|
 | 
						|
#ifdef HAVE_LANGINFO_H
 | 
						|
{
 | 
						|
    const char *expr;
 | 
						|
    /* And then we check language dependent */
 | 
						|
    if ((expr = nl_langinfo(YESEXPR)))
 | 
						|
        if (expr[0])
 | 
						|
            if (pa_match(expr, v) > 0)
 | 
						|
                return 1;
 | 
						|
 | 
						|
    if ((expr = nl_langinfo(NOEXPR)))
 | 
						|
        if (expr[0])
 | 
						|
            if (pa_match(expr, v) > 0)
 | 
						|
                return 0;
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
    errno = EINVAL;
 | 
						|
    return -1;
 | 
						|
}
 | 
						|
 | 
						|
/* 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
 | 
						|
 * 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;
 | 
						|
 | 
						|
    if (!*current)
 | 
						|
        return NULL;
 | 
						|
 | 
						|
    l = strcspn(current, delimiter);
 | 
						|
    *state = current+l;
 | 
						|
 | 
						|
    if (**state)
 | 
						|
        (*state)++;
 | 
						|
 | 
						|
    return pa_xstrndup(current, l);
 | 
						|
}
 | 
						|
 | 
						|
/* 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;
 | 
						|
    size_t l;
 | 
						|
 | 
						|
    if (!*current || *c == 0)
 | 
						|
        return NULL;
 | 
						|
 | 
						|
    current += strspn(current, WHITESPACE);
 | 
						|
    l = strcspn(current, WHITESPACE);
 | 
						|
 | 
						|
    *state = current+l;
 | 
						|
 | 
						|
    return pa_xstrndup(current, l);
 | 
						|
}
 | 
						|
 | 
						|
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) {
 | 
						|
#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";
 | 
						|
#endif
 | 
						|
        case SIGSEGV:   return "SIGSEGV";
 | 
						|
#ifdef SIGUSR2
 | 
						|
        case SIGUSR2:   return "SIGUSR2";
 | 
						|
#endif
 | 
						|
#ifdef 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";
 | 
						|
#endif
 | 
						|
#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
 | 
						|
#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
 | 
						|
 | 
						|
/* Check whether the specified GID and the group name match */
 | 
						|
static int is_group(gid_t gid, const char *name) {
 | 
						|
    struct group *group = NULL;
 | 
						|
    int r = -1;
 | 
						|
 | 
						|
    errno = 0;
 | 
						|
    if (!(group = pa_getgrgid_malloc(gid))) {
 | 
						|
        if (!errno)
 | 
						|
            errno = ENOENT;
 | 
						|
 | 
						|
        pa_log("pa_getgrgid_malloc(%u): %s", gid, pa_cstrerror(errno));
 | 
						|
 | 
						|
        goto finish;
 | 
						|
    }
 | 
						|
 | 
						|
    r = strcmp(name, group->gr_name) == 0;
 | 
						|
 | 
						|
finish:
 | 
						|
    pa_getgrgid_free(group);
 | 
						|
 | 
						|
    return r;
 | 
						|
}
 | 
						|
 | 
						|
/* Check the current user is member of the specified group */
 | 
						|
int pa_own_uid_in_group(const char *name, gid_t *gid) {
 | 
						|
    GETGROUPS_T *gids, tgid;
 | 
						|
    long n = sysconf(_SC_NGROUPS_MAX);
 | 
						|
    int r = -1, i, k;
 | 
						|
 | 
						|
    pa_assert(n > 0);
 | 
						|
 | 
						|
    gids = pa_xmalloc(sizeof(GETGROUPS_T) * (size_t) n);
 | 
						|
 | 
						|
    if ((n = getgroups((int) n, gids)) < 0) {
 | 
						|
        pa_log("getgroups(): %s", pa_cstrerror(errno));
 | 
						|
        goto finish;
 | 
						|
    }
 | 
						|
 | 
						|
    for (i = 0; i < n; i++) {
 | 
						|
 | 
						|
        if ((k = is_group(gids[i], name)) < 0)
 | 
						|
            goto finish;
 | 
						|
        else if (k > 0) {
 | 
						|
            *gid = gids[i];
 | 
						|
            r = 1;
 | 
						|
            goto finish;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    if ((k = is_group(tgid = getgid(), name)) < 0)
 | 
						|
        goto finish;
 | 
						|
    else if (k > 0) {
 | 
						|
        *gid = tgid;
 | 
						|
        r = 1;
 | 
						|
        goto finish;
 | 
						|
    }
 | 
						|
 | 
						|
    r = 0;
 | 
						|
 | 
						|
finish:
 | 
						|
 | 
						|
    pa_xfree(gids);
 | 
						|
    return r;
 | 
						|
}
 | 
						|
 | 
						|
/* Check whether the specific user id is a member of the specified group */
 | 
						|
int pa_uid_in_group(uid_t uid, const char *name) {
 | 
						|
    struct group *group = NULL;
 | 
						|
    char **i;
 | 
						|
    int r = -1;
 | 
						|
 | 
						|
    errno = 0;
 | 
						|
    if (!(group = pa_getgrnam_malloc(name))) {
 | 
						|
        if (!errno)
 | 
						|
            errno = ENOENT;
 | 
						|
        goto finish;
 | 
						|
    }
 | 
						|
 | 
						|
    r = 0;
 | 
						|
    for (i = group->gr_mem; *i; i++) {
 | 
						|
        struct passwd *pw = NULL;
 | 
						|
 | 
						|
        errno = 0;
 | 
						|
        if (!(pw = pa_getpwnam_malloc(*i)))
 | 
						|
            continue;
 | 
						|
 | 
						|
        if (pw->pw_uid == uid)
 | 
						|
            r = 1;
 | 
						|
 | 
						|
        pa_getpwnam_free(pw);
 | 
						|
 | 
						|
        if (r == 1)
 | 
						|
            break;
 | 
						|
    }
 | 
						|
 | 
						|
finish:
 | 
						|
    pa_getgrnam_free(group);
 | 
						|
 | 
						|
    return r;
 | 
						|
}
 | 
						|
 | 
						|
/* Get the GID of a given group, return (gid_t) -1 on failure. */
 | 
						|
gid_t pa_get_gid_of_group(const char *name) {
 | 
						|
    gid_t ret = (gid_t) -1;
 | 
						|
    struct group *gr = NULL;
 | 
						|
 | 
						|
    errno = 0;
 | 
						|
    if (!(gr = pa_getgrnam_malloc(name))) {
 | 
						|
        if (!errno)
 | 
						|
            errno = ENOENT;
 | 
						|
        goto finish;
 | 
						|
    }
 | 
						|
 | 
						|
    ret = gr->gr_gid;
 | 
						|
 | 
						|
finish:
 | 
						|
    pa_getgrnam_free(gr);
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
int pa_check_in_group(gid_t g) {
 | 
						|
    gid_t gids[NGROUPS_MAX];
 | 
						|
    int r;
 | 
						|
 | 
						|
    if ((r = getgroups(NGROUPS_MAX, gids)) < 0)
 | 
						|
        return -1;
 | 
						|
 | 
						|
    for (; r > 0; r--)
 | 
						|
        if (gids[r-1] == g)
 | 
						|
            return 1;
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
#else /* HAVE_GRP_H */
 | 
						|
 | 
						|
int pa_own_uid_in_group(const char *name, gid_t *gid) {
 | 
						|
    errno = ENOTSUP;
 | 
						|
    return -1;
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
int pa_uid_in_group(uid_t uid, const char *name) {
 | 
						|
    errno = ENOTSUP;
 | 
						|
    return -1;
 | 
						|
}
 | 
						|
 | 
						|
gid_t pa_get_gid_of_group(const char *name) {
 | 
						|
    errno = ENOTSUP;
 | 
						|
    return (gid_t) -1;
 | 
						|
}
 | 
						|
 | 
						|
int pa_check_in_group(gid_t g) {
 | 
						|
    errno = ENOTSUP;
 | 
						|
    return -1;
 | 
						|
}
 | 
						|
 | 
						|
#endif
 | 
						|
 | 
						|
/* Lock or unlock a file entirely.
 | 
						|
  (advisory on UNIX, mandatory on Windows) */
 | 
						|
int pa_lock_fd(int fd, int b) {
 | 
						|
#ifdef F_SETLKW
 | 
						|
    struct flock f_lock;
 | 
						|
 | 
						|
    /* Try a R/W lock first */
 | 
						|
 | 
						|
    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, &f_lock) >= 0)
 | 
						|
        return 0;
 | 
						|
 | 
						|
    /* 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));
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef OS_IS_WIN32
 | 
						|
    HANDLE h = (HANDLE) _get_osfhandle(fd);
 | 
						|
 | 
						|
    if (b && LockFile(h, 0, 0, 0xFFFFFFFF, 0xFFFFFFFF))
 | 
						|
        return 0;
 | 
						|
    if (!b && UnlockFile(h, 0, 0, 0xFFFFFFFF, 0xFFFFFFFF))
 | 
						|
        return 0;
 | 
						|
 | 
						|
    pa_log("%slock failed: 0x%08X", !b ? "un" : "", GetLastError());
 | 
						|
 | 
						|
    /* FIXME: Needs to set errno! */
 | 
						|
#endif
 | 
						|
 | 
						|
    return -1;
 | 
						|
}
 | 
						|
 | 
						|
/* Remove trailing newlines from a string */
 | 
						|
char* pa_strip_nl(char *s) {
 | 
						|
    pa_assert(s);
 | 
						|
 | 
						|
    s[strcspn(s, NEWLINE)] = 0;
 | 
						|
    return s;
 | 
						|
}
 | 
						|
 | 
						|
char *pa_strip(char *s) {
 | 
						|
    char *e, *l = NULL;
 | 
						|
 | 
						|
    /* Drops trailing whitespace. Modifies the string in
 | 
						|
     * place. Returns pointer to first non-space character */
 | 
						|
 | 
						|
    s += strspn(s, WHITESPACE);
 | 
						|
 | 
						|
    for (e = s; *e; e++)
 | 
						|
        if (!strchr(WHITESPACE, *e))
 | 
						|
            l = e;
 | 
						|
 | 
						|
    if (l)
 | 
						|
        *(l+1) = 0;
 | 
						|
    else
 | 
						|
        *s = 0;
 | 
						|
 | 
						|
    return s;
 | 
						|
}
 | 
						|
 | 
						|
/* Create a temporary lock file and lock it. */
 | 
						|
int pa_lock_lockfile(const char *fn) {
 | 
						|
    int fd;
 | 
						|
    pa_assert(fn);
 | 
						|
 | 
						|
    for (;;) {
 | 
						|
        struct stat st;
 | 
						|
 | 
						|
        if ((fd = pa_open_cloexec(fn, O_CREAT|O_RDWR
 | 
						|
#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_warn("Failed to lock file '%s'.", fn);
 | 
						|
            goto fail;
 | 
						|
        }
 | 
						|
 | 
						|
        if (fstat(fd, &st) < 0) {
 | 
						|
            pa_log_warn("Failed to fstat() file '%s': %s", fn, pa_cstrerror(errno));
 | 
						|
            goto fail;
 | 
						|
        }
 | 
						|
 | 
						|
        /* Check whether 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_warn("Failed to unlock file '%s'.", fn);
 | 
						|
            goto fail;
 | 
						|
        }
 | 
						|
 | 
						|
        if (pa_close(fd) < 0) {
 | 
						|
            pa_log_warn("Failed to close file '%s': %s", fn, pa_cstrerror(errno));
 | 
						|
            fd = -1;
 | 
						|
            goto fail;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return fd;
 | 
						|
 | 
						|
fail:
 | 
						|
 | 
						|
    if (fd >= 0) {
 | 
						|
        int saved_errno = errno;
 | 
						|
        pa_close(fd);
 | 
						|
        errno = saved_errno;
 | 
						|
    }
 | 
						|
 | 
						|
    return -1;
 | 
						|
}
 | 
						|
 | 
						|
/* Unlock a temporary lock file */
 | 
						|
int pa_unlock_lockfile(const char *fn, int fd) {
 | 
						|
    int r = 0;
 | 
						|
    pa_assert(fd >= 0);
 | 
						|
 | 
						|
    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("Failed to unlock file '%s'.", fn);
 | 
						|
        r = -1;
 | 
						|
    }
 | 
						|
 | 
						|
    if (pa_close(fd) < 0) {
 | 
						|
        pa_log_warn("Failed to close '%s': %s", fn, pa_cstrerror(errno));
 | 
						|
        r = -1;
 | 
						|
    }
 | 
						|
 | 
						|
    return r;
 | 
						|
}
 | 
						|
 | 
						|
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;
 | 
						|
 | 
						|
    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;
 | 
						|
    }
 | 
						|
 | 
						|
    t = check_ours(h);
 | 
						|
    if (t < 0 && t != -ENOENT) {
 | 
						|
        pa_log_error("Home directory not accessible: %s", pa_cstrerror(-t));
 | 
						|
        pa_xfree(h);
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    /* If the old directory exists, use it. */
 | 
						|
    ret = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse", h);
 | 
						|
    if (access(ret, F_OK) >= 0) {
 | 
						|
        free(h);
 | 
						|
        return ret;
 | 
						|
    }
 | 
						|
    free(ret);
 | 
						|
 | 
						|
    /* 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;
 | 
						|
}
 | 
						|
 | 
						|
char *pa_get_state_dir(void) {
 | 
						|
    char *d;
 | 
						|
 | 
						|
    /* The state directory shall contain dynamic data that should be
 | 
						|
     * kept across reboots, and is private to this user */
 | 
						|
 | 
						|
    if (!(d = pa_xstrdup(getenv("PULSE_STATE_PATH"))))
 | 
						|
        if (!(d = get_pulse_home()))
 | 
						|
            return NULL;
 | 
						|
 | 
						|
    /* 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));
 | 
						|
        pa_xfree(d);
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    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";
 | 
						|
 | 
						|
    char *fn;
 | 
						|
    size_t pathlen;
 | 
						|
 | 
						|
    fn = pa_sprintf_malloc("%s" PA_PATH_SEP "pulse-XXXXXXXXXXXX", pa_get_temp_dir());
 | 
						|
    pathlen = strlen(fn);
 | 
						|
 | 
						|
    for (;;) {
 | 
						|
        size_t i;
 | 
						|
        int r;
 | 
						|
        mode_t u;
 | 
						|
        int saved_errno;
 | 
						|
 | 
						|
        for (i = pathlen - 12; i < pathlen; i++)
 | 
						|
            fn[i] = table[rand() % (sizeof(table)-1)];
 | 
						|
 | 
						|
        u = umask((~m) & 0777);
 | 
						|
#ifndef OS_IS_WIN32
 | 
						|
        r = mkdir(fn, m);
 | 
						|
#else
 | 
						|
        r = mkdir(fn);
 | 
						|
#endif
 | 
						|
 | 
						|
        saved_errno = errno;
 | 
						|
        umask(u);
 | 
						|
        errno = saved_errno;
 | 
						|
 | 
						|
        if (r >= 0)
 | 
						|
            return fn;
 | 
						|
 | 
						|
        if (errno != EEXIST) {
 | 
						|
            pa_log_error("Failed to create random directory %s: %s", fn, pa_cstrerror(errno));
 | 
						|
            pa_xfree(fn);
 | 
						|
            return NULL;
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static int make_random_dir_and_link(mode_t m, const char *k) {
 | 
						|
    char *p;
 | 
						|
 | 
						|
    if (!(p = make_random_dir(m)))
 | 
						|
        return -1;
 | 
						|
 | 
						|
#ifdef HAVE_SYMLINK
 | 
						|
    if (symlink(p, k) < 0) {
 | 
						|
        int saved_errno = errno;
 | 
						|
 | 
						|
        if (errno != EEXIST)
 | 
						|
            pa_log_error("Failed to symlink %s to %s: %s", k, p, pa_cstrerror(errno));
 | 
						|
 | 
						|
        rmdir(p);
 | 
						|
        pa_xfree(p);
 | 
						|
 | 
						|
        errno = saved_errno;
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
#else
 | 
						|
    pa_xfree(p);
 | 
						|
    return -1;
 | 
						|
#endif
 | 
						|
 | 
						|
    pa_xfree(p);
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
char *pa_get_runtime_dir(void) {
 | 
						|
    char *d, *k = NULL, *p = NULL, *t = NULL, *mid;
 | 
						|
    mode_t m;
 | 
						|
 | 
						|
    /* The runtime directory shall contain dynamic data that needs NOT
 | 
						|
     * 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 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;
 | 
						|
 | 
						|
    /* 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));
 | 
						|
            goto fail;
 | 
						|
        }
 | 
						|
 | 
						|
        return pa_xstrdup(d);
 | 
						|
    }
 | 
						|
 | 
						|
    /* 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) < 0) {
 | 
						|
            free(k);
 | 
						|
            pa_log_error("Failed to create secure directory: %s", 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));
 | 
						|
        pa_xfree(d);
 | 
						|
        goto fail;
 | 
						|
    }
 | 
						|
 | 
						|
    mid = pa_machine_id();
 | 
						|
    if (!mid) {
 | 
						|
        pa_xfree(d);
 | 
						|
        goto fail;
 | 
						|
    }
 | 
						|
 | 
						|
    k = pa_sprintf_malloc("%s" PA_PATH_SEP "%s-runtime", d, mid);
 | 
						|
    pa_xfree(d);
 | 
						|
    pa_xfree(mid);
 | 
						|
 | 
						|
    for (;;) {
 | 
						|
        /* OK, first let's check if the "runtime" symlink already exists */
 | 
						|
 | 
						|
        p = pa_readlink(k);
 | 
						|
        if (!p) {
 | 
						|
 | 
						|
            if (errno != ENOENT) {
 | 
						|
                pa_log_error("Failed to stat runtime directory %s: %s", k, pa_cstrerror(errno));
 | 
						|
                goto fail;
 | 
						|
            }
 | 
						|
 | 
						|
#ifdef HAVE_SYMLINK
 | 
						|
            /* Hmm, so the runtime directory didn't exist yet, so let's
 | 
						|
             * create one in /tmp and symlink that to it */
 | 
						|
 | 
						|
            if (make_random_dir_and_link(0700, k) < 0) {
 | 
						|
 | 
						|
                /* Mhmm, maybe another process was quicker than us,
 | 
						|
                 * let's check if that was valid */
 | 
						|
                if (errno == EEXIST)
 | 
						|
                    continue;
 | 
						|
 | 
						|
                goto fail;
 | 
						|
            }
 | 
						|
#else
 | 
						|
            /* No symlink possible, so let's just create the runtime directly */
 | 
						|
            if (mkdir(k) < 0)
 | 
						|
                goto fail;
 | 
						|
#endif
 | 
						|
 | 
						|
            return k;
 | 
						|
        }
 | 
						|
 | 
						|
        /* Make sure that this actually makes sense */
 | 
						|
        if (!pa_is_path_absolute(p)) {
 | 
						|
            pa_log_error("Path %s in link %s is not absolute.", p, k);
 | 
						|
            errno = ENOENT;
 | 
						|
            goto fail;
 | 
						|
        }
 | 
						|
 | 
						|
        /* 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) {
 | 
						|
                pa_log_error("Failed to stat runtime directory %s: %s", p, pa_cstrerror(errno));
 | 
						|
                goto fail;
 | 
						|
            }
 | 
						|
 | 
						|
        } else {
 | 
						|
 | 
						|
            if (S_ISDIR(st.st_mode) &&
 | 
						|
                (st.st_uid == getuid()) &&
 | 
						|
                ((st.st_mode & 0777) == 0700)) {
 | 
						|
 | 
						|
                pa_xfree(p);
 | 
						|
                return k;
 | 
						|
            }
 | 
						|
 | 
						|
            pa_log_info("Hmm, runtime path exists, but points to an invalid directory. Changing runtime directory.");
 | 
						|
        }
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
        pa_xfree(p);
 | 
						|
        p = NULL;
 | 
						|
 | 
						|
        /* Hmm, so the link points to some nonexisting or invalid
 | 
						|
         * dir. Let's replace it by a new link. We first create a
 | 
						|
         * temporary link and then rename that to allow concurrent
 | 
						|
         * execution of this function. */
 | 
						|
 | 
						|
        t = pa_sprintf_malloc("%s.tmp", k);
 | 
						|
 | 
						|
        if (make_random_dir_and_link(0700, t) < 0) {
 | 
						|
 | 
						|
            if (errno != EEXIST) {
 | 
						|
                pa_log_error("Failed to symlink %s: %s", t, pa_cstrerror(errno));
 | 
						|
                goto fail;
 | 
						|
            }
 | 
						|
 | 
						|
            pa_xfree(t);
 | 
						|
            t = NULL;
 | 
						|
 | 
						|
            /* Hmm, someone else was quicker then us. Let's give
 | 
						|
             * him some time to finish, and retry. */
 | 
						|
            pa_msleep(10);
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
 | 
						|
        /* OK, we succeeded in creating the temporary symlink, so
 | 
						|
         * let's rename it */
 | 
						|
        if (rename(t, k) < 0) {
 | 
						|
            pa_log_error("Failed to rename %s to %s: %s", t, k, pa_cstrerror(errno));
 | 
						|
            goto fail;
 | 
						|
        }
 | 
						|
 | 
						|
        pa_xfree(t);
 | 
						|
        return k;
 | 
						|
    }
 | 
						|
 | 
						|
fail:
 | 
						|
    pa_xfree(p);
 | 
						|
    pa_xfree(k);
 | 
						|
    pa_xfree(t);
 | 
						|
 | 
						|
    return NULL;
 | 
						|
}
 | 
						|
 | 
						|
/* 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 *fn;
 | 
						|
    FILE *f;
 | 
						|
 | 
						|
    if (env && (fn = getenv(env))) {
 | 
						|
        if ((f = pa_fopen_cloexec(fn, "r"))) {
 | 
						|
            if (result)
 | 
						|
                *result = pa_xstrdup(fn);
 | 
						|
 | 
						|
            return f;
 | 
						|
        }
 | 
						|
 | 
						|
        pa_log_warn("Failed to open configuration file '%s': %s", fn, pa_cstrerror(errno));
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    if (local) {
 | 
						|
        const char *e;
 | 
						|
        char *lfn;
 | 
						|
        char *h;
 | 
						|
 | 
						|
        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())) {
 | 
						|
            fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse" PA_PATH_SEP "%s", h, local);
 | 
						|
            pa_xfree(h);
 | 
						|
        } else
 | 
						|
            return NULL;
 | 
						|
 | 
						|
        if ((f = pa_fopen_cloexec(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) {
 | 
						|
        char *gfn;
 | 
						|
 | 
						|
#ifdef OS_IS_WIN32
 | 
						|
        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(gfn, "r"))) {
 | 
						|
            if (result)
 | 
						|
                *result = gfn;
 | 
						|
            else
 | 
						|
                pa_xfree(gfn);
 | 
						|
 | 
						|
            return f;
 | 
						|
        }
 | 
						|
        pa_xfree(gfn);
 | 
						|
    }
 | 
						|
 | 
						|
    errno = ENOENT;
 | 
						|
    return NULL;
 | 
						|
}
 | 
						|
 | 
						|
char *pa_find_config_file(const char *global, const char *local, const char *env) {
 | 
						|
    const char *fn;
 | 
						|
 | 
						|
    if (env && (fn = getenv(env))) {
 | 
						|
        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;
 | 
						|
 | 
						|
        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())) {
 | 
						|
            fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse" PA_PATH_SEP "%s", h, local);
 | 
						|
            pa_xfree(h);
 | 
						|
        } else
 | 
						|
            return NULL;
 | 
						|
 | 
						|
        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) {
 | 
						|
        char *gfn;
 | 
						|
 | 
						|
#ifdef OS_IS_WIN32
 | 
						|
        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(gfn, R_OK) == 0)
 | 
						|
            return gfn;
 | 
						|
        pa_xfree(gfn);
 | 
						|
    }
 | 
						|
 | 
						|
    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";
 | 
						|
 | 
						|
    pa_assert(d);
 | 
						|
    pa_assert(s);
 | 
						|
    pa_assert(slength > 0);
 | 
						|
 | 
						|
    while (j+2 < slength && i < dlength) {
 | 
						|
        s[j++] = hex[*d >> 4];
 | 
						|
        s[j++] = hex[*d & 0xF];
 | 
						|
 | 
						|
        d++;
 | 
						|
        i++;
 | 
						|
    }
 | 
						|
 | 
						|
    s[j < slength ? j : slength] = 0;
 | 
						|
    return s;
 | 
						|
}
 | 
						|
 | 
						|
/* Convert a hexadecimal digit to a number or -1 if invalid */
 | 
						|
static int hexc(char c) {
 | 
						|
    if (c >= '0' && c <= '9')
 | 
						|
        return c - '0';
 | 
						|
 | 
						|
    if (c >= 'A' && c <= 'F')
 | 
						|
        return c - 'A' + 10;
 | 
						|
 | 
						|
    if (c >= 'a' && c <= 'f')
 | 
						|
        return c - 'a' + 10;
 | 
						|
 | 
						|
    errno = EINVAL;
 | 
						|
    return -1;
 | 
						|
}
 | 
						|
 | 
						|
/* 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;
 | 
						|
 | 
						|
    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)
 | 
						|
            return (size_t) -1;
 | 
						|
 | 
						|
        if ((b = hexc(*(p++))) < 0)
 | 
						|
            return (size_t) -1;
 | 
						|
 | 
						|
        d[j] |= (uint8_t) b;
 | 
						|
        j++;
 | 
						|
    }
 | 
						|
 | 
						|
    return j;
 | 
						|
}
 | 
						|
 | 
						|
/* Returns nonzero when *s starts with *pfx */
 | 
						|
pa_bool_t pa_startswith(const char *s, const char *pfx) {
 | 
						|
    size_t l;
 | 
						|
 | 
						|
    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 */
 | 
						|
pa_bool_t pa_endswith(const char *s, const char *sfx) {
 | 
						|
    size_t l1, l2;
 | 
						|
 | 
						|
    pa_assert(s);
 | 
						|
    pa_assert(sfx);
 | 
						|
 | 
						|
    l1 = strlen(s);
 | 
						|
    l2 = strlen(sfx);
 | 
						|
 | 
						|
    return l1 >= l2 && strcmp(s+l1-l2, sfx) == 0;
 | 
						|
}
 | 
						|
 | 
						|
pa_bool_t pa_is_path_absolute(const char *fn) {
 | 
						|
    pa_assert(fn);
 | 
						|
 | 
						|
#ifndef OS_IS_WIN32
 | 
						|
    return *fn == '/';
 | 
						|
#else
 | 
						|
    return strlen(fn) >= 3 && isalpha(fn[0]) && fn[1] == ':' && fn[2] == '\\';
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
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 prependmid, pa_bool_t rt) {
 | 
						|
    char *rtp;
 | 
						|
 | 
						|
    rtp = rt ? pa_get_runtime_dir() : pa_get_state_dir();
 | 
						|
 | 
						|
    if (fn) {
 | 
						|
        char *r, *canonical_rtp;
 | 
						|
 | 
						|
        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(canonical_rtp);
 | 
						|
                return NULL;
 | 
						|
            }
 | 
						|
 | 
						|
            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", canonical_rtp, fn);
 | 
						|
 | 
						|
        pa_xfree(canonical_rtp);
 | 
						|
        return r;
 | 
						|
    } else
 | 
						|
        return rtp;
 | 
						|
}
 | 
						|
 | 
						|
char *pa_runtime_path(const char *fn) {
 | 
						|
    return get_path(fn, FALSE, TRUE);
 | 
						|
}
 | 
						|
 | 
						|
char *pa_state_path(const char *fn, pa_bool_t 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) {
 | 
						|
    long l;
 | 
						|
 | 
						|
    pa_assert(s);
 | 
						|
    pa_assert(ret_i);
 | 
						|
 | 
						|
    if (pa_atol(s, &l) < 0)
 | 
						|
        return -1;
 | 
						|
 | 
						|
    if ((int32_t) l != l) {
 | 
						|
        errno = ERANGE;
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    *ret_i = (int32_t) l;
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* Convert the string s to an unsigned integer in *ret_u */
 | 
						|
int pa_atou(const char *s, uint32_t *ret_u) {
 | 
						|
    char *x = NULL;
 | 
						|
    unsigned long l;
 | 
						|
 | 
						|
    pa_assert(s);
 | 
						|
    pa_assert(ret_u);
 | 
						|
 | 
						|
    errno = 0;
 | 
						|
    l = strtoul(s, &x, 0);
 | 
						|
 | 
						|
    if (!x || *x || errno) {
 | 
						|
        if (!errno)
 | 
						|
            errno = EINVAL;
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    if ((uint32_t) l != l) {
 | 
						|
        errno = ERANGE;
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    *ret_u = (uint32_t) l;
 | 
						|
 | 
						|
    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;
 | 
						|
 | 
						|
static void c_locale_destroy(void) {
 | 
						|
    freelocale(c_locale);
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
int pa_atod(const char *s, double *ret_d) {
 | 
						|
    char *x = NULL;
 | 
						|
    double f;
 | 
						|
 | 
						|
    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) {
 | 
						|
        if (!errno)
 | 
						|
            errno = EINVAL;
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    *ret_d = f;
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* Same as snprintf, but guarantees NUL-termination on every platform */
 | 
						|
size_t pa_snprintf(char *str, size_t size, const char *format, ...) {
 | 
						|
    size_t 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 */
 | 
						|
size_t 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)
 | 
						|
        return strlen(str);
 | 
						|
 | 
						|
    if ((size_t) ret > size-1)
 | 
						|
        return size-1;
 | 
						|
 | 
						|
    return (size_t) 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_xmalloc(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 = ENOTSUP;
 | 
						|
    size_t bs;
 | 
						|
 | 
						|
    pa_assert(p);
 | 
						|
    pa_assert(l > 0);
 | 
						|
 | 
						|
    a = PA_PAGE_ALIGN_PTR(p);
 | 
						|
    size = (size_t) ((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));
 | 
						|
        errno = EPERM;
 | 
						|
        return (void*) p;
 | 
						|
    }
 | 
						|
 | 
						|
    bs = PA_PAGE_ALIGN((size_t) 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) {
 | 
						|
#ifdef HAVE_READLINK
 | 
						|
    size_t l = 100;
 | 
						|
 | 
						|
    for (;;) {
 | 
						|
        char *c;
 | 
						|
        ssize_t n;
 | 
						|
 | 
						|
        c = pa_xmalloc(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;
 | 
						|
    }
 | 
						|
#else
 | 
						|
    return NULL;
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
int pa_close_all(int except_fd, ...) {
 | 
						|
    va_list ap;
 | 
						|
    unsigned n = 0, i;
 | 
						|
    int r, *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);
 | 
						|
    pa_xfree(p);
 | 
						|
 | 
						|
    return r;
 | 
						|
}
 | 
						|
 | 
						|
int pa_close_allv(const int except_fds[]) {
 | 
						|
#ifndef OS_IS_WIN32
 | 
						|
    struct rlimit rl;
 | 
						|
    int maxfd, fd;
 | 
						|
 | 
						|
#ifdef __linux__
 | 
						|
    int saved_errno;
 | 
						|
    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)
 | 
						|
        maxfd = (int) rl.rlim_max;
 | 
						|
    else
 | 
						|
        maxfd = sysconf(_SC_OPEN_MAX);
 | 
						|
 | 
						|
    for (fd = 3; fd < maxfd; fd++) {
 | 
						|
        int i;
 | 
						|
        pa_bool_t found;
 | 
						|
 | 
						|
        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 && errno != EBADF)
 | 
						|
            return -1;
 | 
						|
    }
 | 
						|
#endif  /* !OS_IS_WIN32 */
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
int pa_unblock_sigs(int except, ...) {
 | 
						|
    va_list ap;
 | 
						|
    unsigned n = 0, i;
 | 
						|
    int r, *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[]) {
 | 
						|
#ifndef OS_IS_WIN32
 | 
						|
    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);
 | 
						|
#else
 | 
						|
    return 0;
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
int pa_reset_sigs(int except, ...) {
 | 
						|
    va_list ap;
 | 
						|
    unsigned n = 0, i;
 | 
						|
    int *p, r;
 | 
						|
 | 
						|
    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_reset_sigsv(p);
 | 
						|
    pa_xfree(p);
 | 
						|
 | 
						|
    return r;
 | 
						|
}
 | 
						|
 | 
						|
int pa_reset_sigsv(const int except[]) {
 | 
						|
#ifndef OS_IS_WIN32
 | 
						|
    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;
 | 
						|
        }
 | 
						|
    }
 | 
						|
#endif
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
void pa_set_env(const char *key, const char *value) {
 | 
						|
    pa_assert(key);
 | 
						|
    pa_assert(value);
 | 
						|
 | 
						|
    /* This is not thread-safe */
 | 
						|
 | 
						|
#ifdef OS_IS_WIN32
 | 
						|
    SetEnvironmentVariable(key, value);
 | 
						|
#else
 | 
						|
    setenv(key, value, 1);
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
void pa_set_env_and_record(const char *key, const char *value) {
 | 
						|
    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;
 | 
						|
 | 
						|
#ifdef OS_IS_WIN32
 | 
						|
        SetEnvironmentVariable(s, NULL);
 | 
						|
#else
 | 
						|
        unsetenv(s);
 | 
						|
#endif
 | 
						|
        pa_xfree(s);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
pa_bool_t pa_in_system_mode(void) {
 | 
						|
    const char *e;
 | 
						|
 | 
						|
    if (!(e = getenv("PULSE_SYSTEM")))
 | 
						|
        return FALSE;
 | 
						|
 | 
						|
    return !!atoi(e);
 | 
						|
}
 | 
						|
 | 
						|
/* Checks a whitespace-separated list of words in haystack for needle */
 | 
						|
pa_bool_t 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;
 | 
						|
 | 
						|
#ifdef _SC_LOGIN_NAME_MAX
 | 
						|
    k = (ssize_t) sysconf(_SC_LOGIN_NAME_MAX);
 | 
						|
 | 
						|
    if (k <= 0)
 | 
						|
#endif
 | 
						|
        k = 32;
 | 
						|
 | 
						|
    u = pa_xnew(char, k+1);
 | 
						|
 | 
						|
    if (!(pa_get_user_name(u, k))) {
 | 
						|
        pa_xfree(u);
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    return u;
 | 
						|
}
 | 
						|
 | 
						|
char *pa_get_host_name_malloc(void) {
 | 
						|
    size_t l;
 | 
						|
 | 
						|
    l = 100;
 | 
						|
    for (;;) {
 | 
						|
        char *c;
 | 
						|
 | 
						|
        c = pa_xmalloc(l);
 | 
						|
 | 
						|
        if (!pa_get_host_name(c, l)) {
 | 
						|
 | 
						|
            if (errno != EINVAL && errno != ENAMETOOLONG)
 | 
						|
                break;
 | 
						|
 | 
						|
        } else if (strlen(c) < l-1) {
 | 
						|
            char *u;
 | 
						|
 | 
						|
            if (*c == 0) {
 | 
						|
                pa_xfree(c);
 | 
						|
                break;
 | 
						|
            }
 | 
						|
 | 
						|
            u = pa_utf8_filter(c);
 | 
						|
            pa_xfree(c);
 | 
						|
            return u;
 | 
						|
        }
 | 
						|
 | 
						|
        /* Hmm, the hostname is as long the space we offered the
 | 
						|
         * function, we cannot know if it fully fit in, so let's play
 | 
						|
         * safe and retry. */
 | 
						|
 | 
						|
        pa_xfree(c);
 | 
						|
        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 /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")) ||
 | 
						|
        (f = pa_fopen_cloexec(PA_MACHINE_ID_FALLBACK, "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;
 | 
						|
 | 
						|
#ifndef OS_IS_WIN32
 | 
						|
    /* 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());
 | 
						|
#else
 | 
						|
    return NULL;
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
char *pa_session_id(void) {
 | 
						|
    const char *e;
 | 
						|
 | 
						|
    e = getenv("XDG_SESSION_ID");
 | 
						|
    if (!e)
 | 
						|
        return NULL;
 | 
						|
 | 
						|
    return pa_utf8_filter(e);
 | 
						|
}
 | 
						|
 | 
						|
char *pa_uname_string(void) {
 | 
						|
#ifdef HAVE_UNAME
 | 
						|
    struct utsname u;
 | 
						|
 | 
						|
    pa_assert_se(uname(&u) >= 0);
 | 
						|
 | 
						|
    return pa_sprintf_malloc("%s %s %s %s", u.sysname, u.machine, u.release, u.version);
 | 
						|
#endif
 | 
						|
#ifdef OS_IS_WIN32
 | 
						|
    OSVERSIONINFO i;
 | 
						|
 | 
						|
    pa_zero(i);
 | 
						|
    i.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
 | 
						|
    pa_assert_se(GetVersionEx(&i));
 | 
						|
 | 
						|
    return pa_sprintf_malloc("Windows %d.%d (%d) %s", i.dwMajorVersion, i.dwMinorVersion, i.dwBuildNumber, i.szCSDVersion);
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
#ifdef HAVE_VALGRIND_MEMCHECK_H
 | 
						|
pa_bool_t pa_in_valgrind(void) {
 | 
						|
    static int b = 0;
 | 
						|
 | 
						|
    /* To make heisenbugs a bit simpler to find we check for $VALGRIND
 | 
						|
     * here instead of really checking whether we run in valgrind or
 | 
						|
     * not. */
 | 
						|
 | 
						|
    if (b < 1)
 | 
						|
        b = getenv("VALGRIND") ? 2 : 1;
 | 
						|
 | 
						|
    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_escape(const char *p, const char *chars) {
 | 
						|
    const char *s;
 | 
						|
    const char *c;
 | 
						|
    pa_strbuf *buf = pa_strbuf_new();
 | 
						|
 | 
						|
    for (s = p; *s; ++s) {
 | 
						|
        if (*s == '\\')
 | 
						|
            pa_strbuf_putc(buf, '\\');
 | 
						|
        else if (chars) {
 | 
						|
            for (c = chars; *c; ++c) {
 | 
						|
                if (*s == *c) {
 | 
						|
                    pa_strbuf_putc(buf, '\\');
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
        pa_strbuf_putc(buf, *s);
 | 
						|
    }
 | 
						|
 | 
						|
    return pa_strbuf_tostring_free(buf);
 | 
						|
}
 | 
						|
 | 
						|
char *pa_unescape(char *p) {
 | 
						|
    char *s, *d;
 | 
						|
    pa_bool_t escaped = FALSE;
 | 
						|
 | 
						|
    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 absolute 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 defined(OS_IS_WIN32)
 | 
						|
        if (!(t = _fullpath(path_buf, path, _MAX_PATH))) {
 | 
						|
            pa_xfree(path_buf);
 | 
						|
            return NULL;
 | 
						|
        }
 | 
						|
#else
 | 
						|
        if (!(t = realpath(path, path_buf))) {
 | 
						|
            pa_xfree(path_buf);
 | 
						|
            return NULL;
 | 
						|
        }
 | 
						|
#endif
 | 
						|
    }
 | 
						|
#else
 | 
						|
#error "It's not clear whether this system supports realpath(..., NULL) like GNU libc does. If it doesn't we need a private version of realpath() here."
 | 
						|
#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;
 | 
						|
 | 
						|
    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";
 | 
						|
}
 | 
						|
 | 
						|
int pa_open_cloexec(const char *fn, int flags, mode_t mode) {
 | 
						|
    int fd;
 | 
						|
 | 
						|
#ifdef O_NOCTTY
 | 
						|
    flags |= O_NOCTTY;
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef O_CLOEXEC
 | 
						|
    if ((fd = open(fn, flags|O_CLOEXEC, mode)) >= 0)
 | 
						|
        goto finish;
 | 
						|
 | 
						|
    if (errno != EINVAL)
 | 
						|
        return fd;
 | 
						|
#endif
 | 
						|
 | 
						|
    if ((fd = open(fn, flags, mode)) < 0)
 | 
						|
        return fd;
 | 
						|
 | 
						|
finish:
 | 
						|
    /* Some implementations might simply ignore O_CLOEXEC if it is not
 | 
						|
     * understood, make sure FD_CLOEXEC is enabled anyway */
 | 
						|
 | 
						|
    pa_make_fd_cloexec(fd);
 | 
						|
    return fd;
 | 
						|
}
 | 
						|
 | 
						|
int pa_socket_cloexec(int domain, int type, int protocol) {
 | 
						|
    int fd;
 | 
						|
 | 
						|
#ifdef SOCK_CLOEXEC
 | 
						|
    if ((fd = socket(domain, type | SOCK_CLOEXEC, protocol)) >= 0)
 | 
						|
        goto finish;
 | 
						|
 | 
						|
    if (errno != EINVAL)
 | 
						|
        return fd;
 | 
						|
#endif
 | 
						|
 | 
						|
    if ((fd = socket(domain, type, protocol)) < 0)
 | 
						|
        return fd;
 | 
						|
 | 
						|
finish:
 | 
						|
    /* Some implementations might simply ignore SOCK_CLOEXEC if it is
 | 
						|
     * not understood, make sure FD_CLOEXEC is enabled anyway */
 | 
						|
 | 
						|
    pa_make_fd_cloexec(fd);
 | 
						|
    return fd;
 | 
						|
}
 | 
						|
 | 
						|
int pa_pipe_cloexec(int pipefd[2]) {
 | 
						|
    int r;
 | 
						|
 | 
						|
#ifdef HAVE_PIPE2
 | 
						|
    if ((r = pipe2(pipefd, O_CLOEXEC)) >= 0)
 | 
						|
        goto finish;
 | 
						|
 | 
						|
    if (errno != EINVAL && errno != ENOSYS)
 | 
						|
        return r;
 | 
						|
 | 
						|
#endif
 | 
						|
 | 
						|
    if ((r = pipe(pipefd)) < 0)
 | 
						|
        return r;
 | 
						|
 | 
						|
finish:
 | 
						|
    pa_make_fd_cloexec(pipefd[0]);
 | 
						|
    pa_make_fd_cloexec(pipefd[1]);
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
int pa_accept_cloexec(int sockfd, struct sockaddr *addr, socklen_t *addrlen) {
 | 
						|
    int fd;
 | 
						|
 | 
						|
#ifdef HAVE_ACCEPT4
 | 
						|
    if ((fd = accept4(sockfd, addr, addrlen, SOCK_CLOEXEC)) >= 0)
 | 
						|
        goto finish;
 | 
						|
 | 
						|
    if (errno != EINVAL && errno != ENOSYS)
 | 
						|
        return fd;
 | 
						|
 | 
						|
#endif
 | 
						|
 | 
						|
    if ((fd = accept(sockfd, addr, addrlen)) < 0)
 | 
						|
        return fd;
 | 
						|
 | 
						|
finish:
 | 
						|
    pa_make_fd_cloexec(fd);
 | 
						|
    return fd;
 | 
						|
}
 | 
						|
 | 
						|
FILE* pa_fopen_cloexec(const char *path, const char *mode) {
 | 
						|
    FILE *f;
 | 
						|
    char *m;
 | 
						|
 | 
						|
    m = pa_sprintf_malloc("%se", mode);
 | 
						|
 | 
						|
    errno = 0;
 | 
						|
    if ((f = fopen(path, m))) {
 | 
						|
        pa_xfree(m);
 | 
						|
        goto finish;
 | 
						|
    }
 | 
						|
 | 
						|
    pa_xfree(m);
 | 
						|
 | 
						|
    if (errno != EINVAL)
 | 
						|
        return NULL;
 | 
						|
 | 
						|
    if (!(f = fopen(path, mode)))
 | 
						|
        return NULL;
 | 
						|
 | 
						|
finish:
 | 
						|
    pa_make_fd_cloexec(fileno(f));
 | 
						|
    return f;
 | 
						|
}
 | 
						|
 | 
						|
void pa_nullify_stdfds(void) {
 | 
						|
 | 
						|
#ifndef OS_IS_WIN32
 | 
						|
        pa_close(STDIN_FILENO);
 | 
						|
        pa_close(STDOUT_FILENO);
 | 
						|
        pa_close(STDERR_FILENO);
 | 
						|
 | 
						|
        pa_assert_se(open("/dev/null", O_RDONLY) == STDIN_FILENO);
 | 
						|
        pa_assert_se(open("/dev/null", O_WRONLY) == STDOUT_FILENO);
 | 
						|
        pa_assert_se(open("/dev/null", O_WRONLY) == STDERR_FILENO);
 | 
						|
#else
 | 
						|
        FreeConsole();
 | 
						|
#endif
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
char *pa_read_line_from_file(const char *fn) {
 | 
						|
    FILE *f;
 | 
						|
    char ln[256] = "", *r;
 | 
						|
 | 
						|
    if (!(f = pa_fopen_cloexec(fn, "r")))
 | 
						|
        return NULL;
 | 
						|
 | 
						|
    r = fgets(ln, sizeof(ln)-1, f);
 | 
						|
    fclose(f);
 | 
						|
 | 
						|
    if (!r) {
 | 
						|
        errno = EIO;
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    pa_strip_nl(ln);
 | 
						|
    return pa_xstrdup(ln);
 | 
						|
}
 | 
						|
 | 
						|
pa_bool_t pa_running_in_vm(void) {
 | 
						|
 | 
						|
#if defined(__i386__) || defined(__x86_64__)
 | 
						|
 | 
						|
    /* Both CPUID and DMI are x86 specific interfaces... */
 | 
						|
 | 
						|
    uint32_t eax = 0x40000000;
 | 
						|
    union {
 | 
						|
        uint32_t sig32[3];
 | 
						|
        char text[13];
 | 
						|
    } sig;
 | 
						|
 | 
						|
#ifdef __linux__
 | 
						|
    const char *const dmi_vendors[] = {
 | 
						|
        "/sys/class/dmi/id/sys_vendor",
 | 
						|
        "/sys/class/dmi/id/board_vendor",
 | 
						|
        "/sys/class/dmi/id/bios_vendor"
 | 
						|
    };
 | 
						|
 | 
						|
    unsigned i;
 | 
						|
 | 
						|
    for (i = 0; i < PA_ELEMENTSOF(dmi_vendors); i++) {
 | 
						|
        char *s;
 | 
						|
 | 
						|
        if ((s = pa_read_line_from_file(dmi_vendors[i]))) {
 | 
						|
 | 
						|
            if (pa_startswith(s, "QEMU") ||
 | 
						|
                /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
 | 
						|
                pa_startswith(s, "VMware") ||
 | 
						|
                pa_startswith(s, "VMW") ||
 | 
						|
                pa_startswith(s, "Microsoft Corporation") ||
 | 
						|
                pa_startswith(s, "innotek GmbH") ||
 | 
						|
                pa_startswith(s, "Xen")) {
 | 
						|
 | 
						|
                pa_xfree(s);
 | 
						|
                return TRUE;
 | 
						|
            }
 | 
						|
 | 
						|
            pa_xfree(s);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
#endif
 | 
						|
 | 
						|
    /* http://lwn.net/Articles/301888/ */
 | 
						|
    pa_zero(sig);
 | 
						|
 | 
						|
    __asm__ __volatile__ (
 | 
						|
        /* ebx/rbx is being used for PIC! */
 | 
						|
        "  push %%"PA_REG_b"         \n\t"
 | 
						|
        "  cpuid                     \n\t"
 | 
						|
        "  mov %%ebx, %1             \n\t"
 | 
						|
        "  pop %%"PA_REG_b"          \n\t"
 | 
						|
 | 
						|
        : "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2])
 | 
						|
        : "0" (eax)
 | 
						|
    );
 | 
						|
 | 
						|
    if (pa_streq(sig.text, "XenVMMXenVMM") ||
 | 
						|
        pa_streq(sig.text, "KVMKVMKVM") ||
 | 
						|
        /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
 | 
						|
        pa_streq(sig.text, "VMwareVMware") ||
 | 
						|
        /* http://msdn.microsoft.com/en-us/library/bb969719.aspx */
 | 
						|
        pa_streq(sig.text, "Microsoft Hv"))
 | 
						|
        return TRUE;
 | 
						|
 | 
						|
#endif
 | 
						|
 | 
						|
    return FALSE;
 | 
						|
}
 |