X-Git-Url: https://code.delx.au/pulseaudio/blobdiff_plain/bacc5ca6f48f280c781af508250a25bb2012327f..8534149fbe87c63a5af85f5610c0f62b45500d90:/src/pulsecore/core-util.c diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c index 4550344f..4e7d0d71 100644 --- a/src/pulsecore/core-util.c +++ b/src/pulsecore/core-util.c @@ -43,6 +43,7 @@ #include #include #include +#include #ifdef HAVE_STRTOF_L #include @@ -100,6 +101,10 @@ #include "rtkit.h" #endif +#ifdef __linux__ +#include +#endif + #include #include #include @@ -110,6 +115,9 @@ #include #include #include +#include +#include +#include #include "core-util.h" @@ -118,6 +126,11 @@ #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" @@ -186,7 +199,7 @@ void pa_make_fd_cloexec(int fd) { /** Creates a directory securely */ int pa_make_secure_dir(const char* dir, mode_t m, uid_t uid, gid_t gid) { struct stat st; - int r, saved_errno; + int r, saved_errno, fd; pa_assert(dir); @@ -204,16 +217,45 @@ int pa_make_secure_dir(const char* dir, mode_t m, uid_t uid, gid_t gid) { 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 @@ -551,19 +593,27 @@ char *pa_vsprintf_malloc(const char *format, va_list ap) { /* Similar to OpenBSD's strlcpy() function */ char *pa_strlcpy(char *b, const char *s, size_t l) { + size_t k; + pa_assert(b); pa_assert(s); pa_assert(l > 0); - strncpy(b, s, l); - b[l-1] = 0; + k = strlen(s); + + if (k > l-1) + k = l-1; + + memcpy(b, s, k); + b[k] = 0; + return b; } static int set_scheduler(int rtprio) { struct sched_param sp; - int r; #ifdef HAVE_DBUS + int r; DBusError error; DBusConnection *bus; @@ -574,13 +624,13 @@ static int set_scheduler(int rtprio) { 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; } @@ -595,6 +645,11 @@ static int set_scheduler(int rtprio) { 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); @@ -605,7 +660,7 @@ static int set_scheduler(int rtprio) { errno = -r; #else - errno = r; + errno = 0; #endif return -1; @@ -625,7 +680,7 @@ int pa_make_realtime(int rtprio) { } 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; } @@ -663,6 +718,11 @@ static int set_nice(int nice_level) { 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); @@ -690,7 +750,7 @@ int pa_raise_priority(int nice_level) { } 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; } @@ -759,7 +819,6 @@ int pa_match(const char *expr, const char *v) { /* Try to parse a boolean string value.*/ int pa_parse_boolean(const char *v) { const char *expr; - int r; pa_assert(v); /* First we check language independant */ @@ -771,12 +830,12 @@ int pa_parse_boolean(const char *v) { /* 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; @@ -803,9 +862,6 @@ char *pa_split(const char *c, const char *delimiter, const char**state) { 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; @@ -956,54 +1012,24 @@ fail: /* Check whether the specified GID and the group name match */ static int is_group(gid_t gid, const char *name) { - struct group group, *result = NULL; - long n; - void *data; + struct group *group = NULL; int r = -1; -#ifdef HAVE_GETGRGID_R -#ifdef _SC_GETGR_R_SIZE_MAX - n = sysconf(_SC_GETGR_R_SIZE_MAX); -#else - n = -1; -#endif - if (n <= 0) - n = 512; - - data = pa_xmalloc((size_t) n); - errno = 0; - if (getgrgid_r(gid, &group, data, (size_t) n, &result) < 0 || !result) { - pa_log("getgrgid_r(%u): %s", (unsigned) gid, pa_cstrerror(errno)); - + if (!(group = pa_getgrgid_malloc(gid))) + { if (!errno) errno = ENOENT; - goto finish; - } - - r = strcmp(name, result->gr_name) == 0; - -finish: - pa_xfree(data); -#else - /* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X) that do not - * support getgrgid_r. */ - - errno = 0; - if (!(result = getgrgid(gid))) { - pa_log("getgrgid(%u): %s", gid, pa_cstrerror(errno)); - - if (!errno) - errno = ENOENT; + pa_log("pa_getgrgid_malloc(%u): %s", gid, pa_cstrerror(errno)); goto finish; } - r = strcmp(name, result->gr_name) == 0; + r = strcmp(name, group->gr_name) == 0; finish: -#endif + pa_getgrgid_free(group); return r; } @@ -1052,38 +1078,12 @@ finish: /* Check whether the specifc user id is a member of the specified group */ int pa_uid_in_group(uid_t uid, const char *name) { - char *g_buf, *p_buf; - long g_n, p_n; - struct group grbuf, *gr; + struct group *group = NULL; char **i; int r = -1; -#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); - -#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); - errno = 0; -#ifdef HAVE_GETGRNAM_R - if (getgrnam_r(name, &grbuf, g_buf, (size_t) g_n, &gr) != 0 || !gr) -#else - if (!(gr = getgrnam(name))) -#endif + if (!(group = pa_getgrnam_malloc(name))) { if (!errno) errno = ENOENT; @@ -1091,25 +1091,24 @@ int pa_uid_in_group(uid_t uid, const char *name) { } r = 0; - for (i = gr->gr_mem; *i; i++) { - struct passwd pwbuf, *pw; + for (i = group->gr_mem; *i; i++) { + struct passwd *pw = NULL; -#ifdef HAVE_GETPWNAM_R - if (getpwnam_r(*i, &pwbuf, p_buf, (size_t) p_n, &pw) != 0 || !pw) -#else - if (!(pw = getpwnam(*i))) -#endif + errno = 0; + 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; } @@ -1117,26 +1116,10 @@ finish: /* Get the GID of a gfiven group, return (gid_t) -1 on failure. */ gid_t pa_get_gid_of_group(const char *name) { gid_t ret = (gid_t) -1; - char *g_buf; - long g_n; - struct group grbuf, *gr; - -#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; errno = 0; -#ifdef HAVE_GETGRNAM_R - if (getgrnam_r(name, &grbuf, g_buf, (size_t) g_n, &gr) != 0 || !gr) -#else - if (!(gr = getgrnam(name))) -#endif + if (!(gr = pa_getgrnam_malloc(name))) { if (!errno) errno = ENOENT; @@ -1146,7 +1129,7 @@ gid_t pa_get_gid_of_group(const char *name) { ret = gr->gr_gid; finish: - pa_xfree(g_buf); + pa_getgrnam_free(gr); return ret; } @@ -1193,22 +1176,22 @@ int pa_check_in_group(gid_t g) { (advisory on UNIX, mandatory on Windows) */ int pa_lock_fd(int fd, int b) { #ifdef F_SETLKW - struct flock flock; + struct flock f_lock; /* Try a R/W lock first */ - flock.l_type = (short) (b ? F_WRLCK : F_UNLCK); - flock.l_whence = SEEK_SET; - flock.l_start = 0; - flock.l_len = 0; + f_lock.l_type = (short) (b ? F_WRLCK : F_UNLCK); + f_lock.l_whence = SEEK_SET; + f_lock.l_start = 0; + f_lock.l_len = 0; - if (fcntl(fd, F_SETLKW, &flock) >= 0) + if (fcntl(fd, F_SETLKW, &f_lock) >= 0) return 0; /* Perhaps the file descriptor qas opened for read only, than try again with a read lock. */ if (b && errno == EBADF) { - flock.l_type = F_RDLCK; - if (fcntl(fd, F_SETLKW, &flock) >= 0) + f_lock.l_type = F_RDLCK; + if (fcntl(fd, F_SETLKW, &f_lock) >= 0) return 0; } @@ -1235,22 +1218,39 @@ int pa_lock_fd(int fd, int b) { 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 @@ -1284,8 +1284,6 @@ int pa_lock_lockfile(const char *fn) { fd = -1; goto fail; } - - fd = -1; } return fd; @@ -1327,26 +1325,32 @@ int pa_unlock_lockfile(const char *fn, int fd) { } static char *get_pulse_home(void) { - char h[PATH_MAX]; + char *h; struct stat st; + char *ret = NULL; - if (!pa_get_home_dir(h, sizeof(h))) { + if (!(h = pa_get_home_dir_malloc())) { pa_log_error("Failed to get home directory."); return NULL; } if (stat(h, &st) < 0) { pa_log_error("Failed to stat home directory %s: %s", h, pa_cstrerror(errno)); - return NULL; + goto finish; } if (st.st_uid != getuid()) { pa_log_error("Home directory %s not ours.", h); errno = EACCES; - return NULL; + goto finish; } - return pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse", h); + ret = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse", h); + +finish: + pa_xfree(h); + + return ret; } char *pa_get_state_dir(void) { @@ -1371,25 +1375,60 @@ char *pa_get_state_dir(void) { return d; } +char *pa_get_home_dir_malloc(void) { + char *homedir; + size_t allocated = 128; + + for (;;) { + homedir = pa_xmalloc(allocated); + + if (!pa_get_home_dir(homedir, allocated)) { + pa_xfree(homedir); + return NULL; + } + + if (strlen(homedir) < allocated - 1) + break; + + pa_xfree(homedir); + allocated *= 2; + } + + return homedir; +} + +char *pa_get_binary_name_malloc(void) { + char *t; + size_t allocated = 128; + + for (;;) { + t = pa_xmalloc(allocated); + + if (!pa_get_binary_name(t, allocated)) { + pa_xfree(t); + return NULL; + } + + if (strlen(t) < allocated - 1) + break; + + pa_xfree(t); + allocated *= 2; + } + + return t; +} + static char* make_random_dir(mode_t m) { static const char table[] = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789"; - const char *tmpdir; char *fn; size_t pathlen; - if (!(tmpdir = getenv("TMPDIR"))) - if (!(tmpdir = getenv("TMP"))) - if (!(tmpdir = getenv("TEMP"))) - tmpdir = getenv("TEMPDIR"); - - if (!tmpdir || !pa_is_path_absolute(tmpdir)) - tmpdir = "/tmp"; - - fn = pa_sprintf_malloc("%s/pulse-XXXXXXXXXXXX", tmpdir); + fn = pa_sprintf_malloc("%s" PA_PATH_SEP "pulse-XXXXXXXXXXXX", pa_get_temp_dir()); pathlen = strlen(fn); for (;;) { @@ -1480,7 +1519,7 @@ char *pa_get_runtime_dir(void) { goto fail; } - k = pa_sprintf_malloc("%s" PA_PATH_SEP "%s:runtime", d, mid); + k = pa_sprintf_malloc("%s" PA_PATH_SEP "%s-runtime", d, mid); pa_xfree(d); pa_xfree(mid); @@ -1611,7 +1650,7 @@ FILE *pa_open_config_file(const char *global, const char *local, const char *env fn = buf; #endif - if ((f = fopen(fn, "r"))) { + if ((f = pa_fopen_cloexec(fn, "r"))) { if (result) *result = pa_xstrdup(fn); @@ -1625,14 +1664,15 @@ FILE *pa_open_config_file(const char *global, const char *local, const char *env if (local) { const char *e; char *lfn; - char h[PATH_MAX]; + char *h; FILE *f; if ((e = getenv("PULSE_CONFIG_PATH"))) fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", e, local); - else if (pa_get_home_dir(h, sizeof(h))) + else if ((h = pa_get_home_dir_malloc())) { fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse" PA_PATH_SEP "%s", h, local); - else + pa_xfree(h); + } else return NULL; #ifdef OS_IS_WIN32 @@ -1644,7 +1684,7 @@ FILE *pa_open_config_file(const char *global, const char *local, const char *env fn = buf; #endif - if ((f = fopen(fn, "r"))) { + if ((f = pa_fopen_cloexec(fn, "r"))) { if (result) *result = pa_xstrdup(fn); @@ -1671,7 +1711,7 @@ FILE *pa_open_config_file(const char *global, const char *local, const char *env global = buf; #endif - if ((f = fopen(global, "r"))) { + if ((f = pa_fopen_cloexec(global, "r"))) { if (result) *result = pa_xstrdup(global); @@ -1712,13 +1752,14 @@ char *pa_find_config_file(const char *global, const char *local, const char *env if (local) { const char *e; char *lfn; - char h[PATH_MAX]; + char *h; if ((e = getenv("PULSE_CONFIG_PATH"))) fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", e, local); - else if (pa_get_home_dir(h, sizeof(h))) + else if ((h = pa_get_home_dir_malloc())) { fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse" PA_PATH_SEP "%s", h, local); - else + pa_xfree(h); + } else return NULL; #ifdef OS_IS_WIN32 @@ -1884,17 +1925,17 @@ char *pa_make_path_absolute(const char *p) { static char *get_path(const char *fn, pa_bool_t prependmid, pa_bool_t rt) { char *rtp; - if (pa_is_path_absolute(fn)) - return pa_xstrdup(fn); - rtp = rt ? pa_get_runtime_dir() : pa_get_state_dir(); - if (!rtp) - return NULL; - if (fn) { char *r; + if (pa_is_path_absolute(fn)) + return pa_xstrdup(fn); + + if (!rtp) + return NULL; + if (prependmid) { char *mid; @@ -1903,7 +1944,7 @@ static char *get_path(const char *fn, pa_bool_t prependmid, pa_bool_t rt) { return NULL; } - r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s:%s", rtp, mid, fn); + r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s-%s", rtp, mid, fn); pa_xfree(mid); } else r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", rtp, fn); @@ -2230,7 +2271,7 @@ int pa_close_all(int except_fd, ...) { va_end(ap); r = pa_close_allv(p); - free(p); + pa_xfree(p); return r; } @@ -2238,10 +2279,9 @@ int pa_close_all(int except_fd, ...) { int pa_close_allv(const int except_fds[]) { struct rlimit rl; int maxfd, fd; - int saved_errno; #ifdef __linux__ - + int saved_errno; DIR *d; if ((d = opendir("/proc/self/fd"))) { @@ -2402,7 +2442,7 @@ int pa_reset_sigs(int except, ...) { p[i++] = except; while ((sig = va_arg(ap, int)) >= 0) - sig = p[i++]; + p[i++] = sig; } p[i] = -1; @@ -2459,9 +2499,38 @@ void pa_set_env(const char *key, const char *value) { pa_assert(key); pa_assert(value); + /* This is not thread-safe */ + putenv(pa_sprintf_malloc("%s=%s", key, value)); } +void pa_set_env_and_record(const char *key, const char *value) { + pa_assert(key); + pa_assert(value); + + /* This is not thread-safe */ + + pa_set_env(key, value); + recorded_env = pa_strlist_prepend(recorded_env, key); +} + +void pa_unset_env_recorded(void) { + + /* This is not thread-safe */ + + for (;;) { + char *s; + + recorded_env = pa_strlist_pop(recorded_env, &s); + + if (!s) + break; + + unsetenv(s); + pa_xfree(s); + } +} + pa_bool_t pa_in_system_mode(void) { const char *e; @@ -2541,7 +2610,7 @@ char *pa_machine_id(void) { * 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); @@ -2656,6 +2725,28 @@ char *pa_replace(const char*s, const char*a, const char *b) { 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; @@ -2779,3 +2870,290 @@ char* pa_maybe_prefix_path(const char *path, const char *prefix) { return pa_sprintf_malloc("%s" PA_PATH_SEP "%s", prefix, path); } + +size_t pa_pipe_buf(int fd) { + +#ifdef _PC_PIPE_BUF + long n; + + if ((n = fpathconf(fd, _PC_PIPE_BUF)) >= 0) + return (size_t) n; +#endif + +#ifdef PIPE_BUF + return PIPE_BUF; +#else + return 4096; +#endif +} + +void pa_reset_personality(void) { + +#ifdef __linux__ + if (personality(PER_LINUX) < 0) + pa_log_warn("Uh, personality() failed: %s", pa_cstrerror(errno)); +#endif + +} + +#if defined(__linux__) && !defined(__OPTIMIZE__) + +pa_bool_t pa_run_from_build_tree(void) { + char *rp; + pa_bool_t b = FALSE; + + /* We abuse __OPTIMIZE__ as a check whether we are a debug build + * or not. */ + + if ((rp = pa_readlink("/proc/self/exe"))) { + b = pa_startswith(rp, PA_BUILDDIR); + pa_xfree(rp); + } + + return b; +} + +#endif + +const char *pa_get_temp_dir(void) { + const char *t; + + if ((t = getenv("TMPDIR")) && + pa_is_path_absolute(t)) + return t; + + if ((t = getenv("TMP")) && + pa_is_path_absolute(t)) + return t; + + if ((t = getenv("TEMP")) && + pa_is_path_absolute(t)) + return t; + + if ((t = getenv("TEMPDIR")) && + pa_is_path_absolute(t)) + return t; + + return "/tmp"; +} + +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; +}