#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 "core-util.h"
#define MSG_NOSIGNAL 0
#endif
+#define NEWLINE "\r\n"
+#define WHITESPACE "\n\r \t"
+
+static pa_strlist *recorded_env = NULL;
+
#ifdef OS_IS_WIN32
#define PULSE_ROOTENV "PULSE_ROOT"
/** 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;
+ int r, saved_errno, fd;
pa_assert(dir);
if (r < 0 && errno != EEXIST)
return -1;
-#ifdef HAVE_CHOWN
+#ifdef HAVE_FSTAT
+ 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();
- (void) chown(dir, uid, gid);
+ (void) fchown(fd, uid, gid);
#endif
-#ifdef HAVE_CHMOD
- chmod(dir, m);
+#ifdef HAVE_FCHMOD
+ (void) fchmod(fd, m);
+#endif
+
+ pa_assert_se(pa_close(fd) >= 0);
+
#endif
#ifdef HAVE_LSTAT
static int set_scheduler(int rtprio) {
struct sched_param sp;
- int r;
#ifdef HAVE_DBUS
+ int r;
DBusError error;
DBusConnection *bus;
sp.sched_priority = rtprio;
#ifdef SCHED_RESET_ON_FORK
- if ((r = pthread_setschedparam(pthread_self(), SCHED_RR|SCHED_RESET_ON_FORK, &sp)) == 0) {
+ if (pthread_setschedparam(pthread_self(), SCHED_RR|SCHED_RESET_ON_FORK, &sp) == 0) {
pa_log_debug("SCHED_RR|SCHED_RESET_ON_FORK worked.");
return 0;
}
#endif
- if ((r = pthread_setschedparam(pthread_self(), SCHED_RR, &sp)) == 0) {
+ if (pthread_setschedparam(pthread_self(), SCHED_RR, &sp) == 0) {
pa_log_debug("SCHED_RR worked.");
return 0;
}
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_unref(bus);
errno = -r;
#else
- errno = r;
+ errno = 0;
#endif
return -1;
}
for (p = rtprio-1; p >= 1; p--)
- if (set_scheduler(p)) {
+ if (set_scheduler(p) >= 0) {
pa_log_info("Successfully enabled SCHED_RR scheduling for thread, with priority %i, which is lower than the requested %i.", p, rtprio);
return 0;
}
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);
}
for (n = nice_level+1; n < 0; n++)
- if (set_nice(n) > 0) {
+ if (set_nice(n) >= 0) {
pa_log_info("Successfully acquired nice level %i, which is lower than the requested %i.", n, nice_level);
return 0;
}
/* Try to parse a boolean string value.*/
int pa_parse_boolean(const char *v) {
const char *expr;
- int r;
pa_assert(v);
/* First we check language independant */
/* And then we check language dependant */
if ((expr = nl_langinfo(YESEXPR)))
if (expr[0])
- if ((r = pa_match(expr, v)) > 0)
+ if (pa_match(expr, v) > 0)
return 1;
if ((expr = nl_langinfo(NOEXPR)))
if (expr[0])
- if ((r = pa_match(expr, v)) > 0)
+ if (pa_match(expr, v) > 0)
return 0;
errno = EINVAL;
return pa_xstrndup(current, l);
}
-/* What is interpreted as whitespace? */
-#define WHITESPACE " \t\n"
-
/* 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;
/* Check whether the specified GID and the group name match */
static int is_group(gid_t gid, const char *name) {
- struct group group, *result = NULL;
- long n;
- void *data;
+ struct group *group = NULL;
int r = -1;
-#ifdef HAVE_GETGRGID_R
-
-#ifdef _SC_GETGR_R_SIZE_MAX
- n = sysconf(_SC_GETGR_R_SIZE_MAX);
-#else
- n = -1;
-#endif
- if (n <= 0)
- n = 512;
-
- data = pa_xmalloc((size_t) n);
-
- if ((errno = getgrgid_r(gid, &group, data, (size_t) n, &result)) || !result)
-#else
errno = 0;
- if (!(result = getgrgid(gid)))
-#endif
+ if (!(group = pa_getgrgid_malloc(gid)))
{
if (!errno)
errno = ENOENT;
- pa_log("getgrgid(%u): %s", gid, pa_cstrerror(errno));
+ pa_log("pa_getgrgid_malloc(%u): %s", gid, pa_cstrerror(errno));
goto finish;
}
- r = strcmp(name, result->gr_name) == 0;
+ r = strcmp(name, group->gr_name) == 0;
finish:
-
- pa_xfree(data);
+ pa_getgrgid_free(group);
return r;
}
/* Check whether the specifc user id is a member of the specified group */
int pa_uid_in_group(uid_t uid, const char *name) {
- char *g_buf = NULL, *p_buf = NULL;
- long g_n, p_n;
- struct group grbuf, *gr = NULL;
+ struct group *group = NULL;
char **i;
int r = -1;
-#ifdef HAVE_GETGRNAM_R
-
-#ifdef _SC_GETGR_R_SIZE_MAX
- g_n = sysconf(_SC_GETGR_R_SIZE_MAX);
-#else
- g_n = -1;
-#endif
- if (g_n <= 0)
- g_n = 512;
-
- g_buf = pa_xmalloc((size_t) g_n);
-
- if ((errno = getgrnam_r(name, &grbuf, g_buf, (size_t) g_n, &gr)) != 0 || !gr)
-#else
errno = 0;
- if (!(gr = getgrnam(name)))
-#endif
+ if (!(group = pa_getgrnam_malloc(name)))
{
if (!errno)
errno = ENOENT;
goto finish;
}
-#ifdef HAVE_GETPWNAM_R
-
-#ifdef _SC_GETPW_R_SIZE_MAX
- p_n = sysconf(_SC_GETPW_R_SIZE_MAX);
-#else
- p_n = -1;
-#endif
- if (p_n <= 0)
- p_n = 512;
-
- p_buf = pa_xmalloc((size_t) p_n);
-#endif
-
r = 0;
- for (i = gr->gr_mem; *i; i++) {
- struct passwd pwbuf, *pw = NULL;
+ for (i = group->gr_mem; *i; i++) {
+ struct passwd *pw = NULL;
-#ifdef HAVE_GETPWNAM_R
- if ((errno = getpwnam_r(*i, &pwbuf, p_buf, (size_t) p_n, &pw)) != 0 || !pw)
-#else
errno = 0;
- if (!(pw = getpwnam(*i)))
-#endif
+ if (!(pw = pa_getpwnam_malloc(*i)))
continue;
- if (pw->pw_uid == uid) {
+ if (pw->pw_uid == uid)
r = 1;
+
+ pa_getpwnam_free(pw);
+
+ if (r == 1)
break;
- }
}
finish:
- pa_xfree(g_buf);
- pa_xfree(p_buf);
+ pa_getgrnam_free(group);
return r;
}
/* Get the GID of a gfiven group, return (gid_t) -1 on failure. */
gid_t pa_get_gid_of_group(const char *name) {
gid_t ret = (gid_t) -1;
- char *g_buf = NULL;
- long g_n;
- struct group grbuf, *gr = NULL;
-
-#ifdef HAVE_GETGRNAM_R
-
-#ifdef _SC_GETGR_R_SIZE_MAX
- g_n = sysconf(_SC_GETGR_R_SIZE_MAX);
-#else
- g_n = -1;
-#endif
- if (g_n <= 0)
- g_n = 512;
-
- g_buf = pa_xmalloc((size_t) g_n);
+ struct group *gr = NULL;
- if ((errno = getgrnam_r(name, &grbuf, g_buf, (size_t) g_n, &gr)) != 0 || !gr)
-#else
errno = 0;
- if (!(gr = getgrnam(name)))
-#endif
+ if (!(gr = pa_getgrnam_malloc(name)))
{
if (!errno)
errno = ENOENT;
ret = gr->gr_gid;
finish:
- pa_xfree(g_buf);
+ pa_getgrnam_free(gr);
return ret;
}
char* pa_strip_nl(char *s) {
pa_assert(s);
- s[strcspn(s, "\r\n")] = 0;
+ s[strcspn(s, NEWLINE)] = 0;
+ return s;
+}
+
+char *pa_strip(char *s) {
+ char *e, *l = NULL;
+
+ /* Drops trailing whitespace. Modifies the string in
+ * place. Returns pointer to first non-space character */
+
+ s += strspn(s, WHITESPACE);
+
+ for (e = s; *e; e++)
+ if (!strchr(WHITESPACE, *e))
+ l = e;
+
+ if (l)
+ *(l+1) = 0;
+ else
+ *s = 0;
+
return s;
}
/* Create a temporary lock file and lock it. */
int pa_lock_lockfile(const char *fn) {
- int fd = -1;
+ int fd;
pa_assert(fn);
for (;;) {
struct stat st;
- if ((fd = open(fn, O_CREAT|O_RDWR
-#ifdef O_NOCTTY
- |O_NOCTTY
-#endif
+ if ((fd = pa_open_cloexec(fn, O_CREAT|O_RDWR
#ifdef O_NOFOLLOW
|O_NOFOLLOW
#endif
fd = -1;
goto fail;
}
-
- fd = -1;
}
return fd;
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789";
- const char *tmpdir;
char *fn;
size_t pathlen;
- if (!(tmpdir = getenv("TMPDIR")))
- if (!(tmpdir = getenv("TMP")))
- if (!(tmpdir = getenv("TEMP")))
- tmpdir = getenv("TEMPDIR");
-
- if (!tmpdir || !pa_is_path_absolute(tmpdir))
- tmpdir = "/tmp";
-
- fn = pa_sprintf_malloc("%s/pulse-XXXXXXXXXXXX", tmpdir);
+ fn = pa_sprintf_malloc("%s" PA_PATH_SEP "pulse-XXXXXXXXXXXX", pa_get_temp_dir());
pathlen = strlen(fn);
for (;;) {
fn = buf;
#endif
- if ((f = fopen(fn, "r"))) {
+ if ((f = pa_fopen_cloexec(fn, "r"))) {
if (result)
*result = pa_xstrdup(fn);
fn = buf;
#endif
- if ((f = fopen(fn, "r"))) {
+ if ((f = pa_fopen_cloexec(fn, "r"))) {
if (result)
*result = pa_xstrdup(fn);
global = buf;
#endif
- if ((f = fopen(global, "r"))) {
+ if ((f = pa_fopen_cloexec(global, "r"))) {
if (result)
*result = pa_xstrdup(global);
static char *get_path(const char *fn, pa_bool_t prependmid, pa_bool_t rt) {
char *rtp;
- if (pa_is_path_absolute(fn))
- return pa_xstrdup(fn);
-
rtp = rt ? pa_get_runtime_dir() : pa_get_state_dir();
- if (!rtp)
- return NULL;
-
if (fn) {
char *r;
+ if (pa_is_path_absolute(fn))
+ return pa_xstrdup(fn);
+
+ if (!rtp)
+ return NULL;
+
if (prependmid) {
char *mid;
va_end(ap);
r = pa_close_allv(p);
- free(p);
+ pa_xfree(p);
return r;
}
p[i++] = except;
while ((sig = va_arg(ap, int)) >= 0)
- sig = p[i++];
+ p[i++] = sig;
}
p[i] = -1;
pa_assert(key);
pa_assert(value);
+ /* This is not thread-safe */
+
putenv(pa_sprintf_malloc("%s=%s", key, value));
}
+void pa_set_env_and_record(const char *key, const char *value) {
+ pa_assert(key);
+ pa_assert(value);
+
+ /* This is not thread-safe */
+
+ pa_set_env(key, value);
+ recorded_env = pa_strlist_prepend(recorded_env, key);
+}
+
+void pa_unset_env_recorded(void) {
+
+ /* This is not thread-safe */
+
+ for (;;) {
+ char *s;
+
+ recorded_env = pa_strlist_pop(recorded_env, &s);
+
+ if (!s)
+ break;
+
+ unsetenv(s);
+ pa_xfree(s);
+ }
+}
+
pa_bool_t pa_in_system_mode(void) {
const char *e;
* since it fits perfectly our needs and is not as volatile as the
* hostname which might be set from dhcp. */
- if ((f = fopen(PA_MACHINE_ID, "r"))) {
+ if ((f = pa_fopen_cloexec(PA_MACHINE_ID, "r"))) {
char ln[34] = "", *r;
r = fgets(ln, sizeof(ln)-1, f);
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;
}
#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;
+}