/*** This file is part of PulseAudio. Copyright 2004-2006 Lennart Poettering Copyright 2006 Pierre Ossman 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, see . ***/ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #ifdef HAVE_PWD_H #include #endif #ifdef HAVE_NETDB_H #include #endif #ifdef HAVE_WINDOWS_H #include #endif #ifdef HAVE_SYS_PRCTL_H #include #endif #ifdef OS_IS_DARWIN #include #include #endif #include #include #include #include #include #include #include #include "util.h" #if defined(HAVE_DLADDR) && defined(PA_GCC_WEAKREF) #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include static int _main() PA_GCC_WEAKREF(main); #endif #ifdef HAVE_PTHREAD #include #endif #ifdef HAVE_SCHED_H #include #if defined(__linux__) && !defined(SCHED_RESET_ON_FORK) #define SCHED_RESET_ON_FORK 0x40000000 #endif #endif #ifdef __APPLE__ #include #include #include #include #endif #ifdef HAVE_DBUS #include #endif char *pa_get_user_name(char *s, size_t l) { const char *p; char *name = NULL; #ifdef OS_IS_WIN32 char buf[1024]; #endif #ifdef HAVE_PWD_H struct passwd *r; #endif pa_assert(s); pa_assert(l > 0); p = NULL; #ifdef HAVE_GETUID p = getuid() == 0 ? "root" : NULL; #endif if (!p) p = getenv("USER"); if (!p) p = getenv("LOGNAME"); if (!p) p = getenv("USERNAME"); if (p) { name = pa_strlcpy(s, p, l); } else { #ifdef HAVE_PWD_H if ((r = pa_getpwuid_malloc(getuid())) == NULL) { pa_snprintf(s, l, "%lu", (unsigned long) getuid()); return s; } name = pa_strlcpy(s, r->pw_name, l); pa_getpwuid_free(r); #elif defined(OS_IS_WIN32) /* HAVE_PWD_H */ DWORD size = sizeof(buf); if (!GetUserName(buf, &size)) { errno = ENOENT; return NULL; } name = pa_strlcpy(s, buf, l); #else /* HAVE_PWD_H */ return NULL; #endif /* HAVE_PWD_H */ } return name; } char *pa_get_host_name(char *s, size_t l) { pa_assert(s); pa_assert(l > 0); if (gethostname(s, l) < 0) return NULL; s[l-1] = 0; return s; } char *pa_get_home_dir(char *s, size_t l) { char *e; char *dir; #ifdef HAVE_PWD_H struct passwd *r; #endif pa_assert(s); pa_assert(l > 0); if ((e = getenv("HOME"))) { dir = pa_strlcpy(s, e, l); goto finish; } if ((e = getenv("USERPROFILE"))) { dir = pa_strlcpy(s, e, l); goto finish; } #ifdef HAVE_PWD_H errno = 0; if ((r = pa_getpwuid_malloc(getuid())) == NULL) { if (!errno) errno = ENOENT; return NULL; } dir = pa_strlcpy(s, r->pw_dir, l); pa_getpwuid_free(r); #endif /* HAVE_PWD_H */ finish: if (!dir) { errno = ENOENT; return NULL; } if (!pa_is_path_absolute(dir)) { pa_log("Failed to get the home directory, not an absolute path: %s", dir); errno = ENOENT; return NULL; } return dir; } char *pa_get_binary_name(char *s, size_t l) { pa_assert(s); pa_assert(l > 0); #if defined(OS_IS_WIN32) { char path[PATH_MAX]; if (GetModuleFileName(NULL, path, PATH_MAX)) return pa_strlcpy(s, pa_path_get_filename(path), l); } #endif #if defined(__linux__) || defined(__FreeBSD_kernel__) { char *rp; /* This works on Linux and Debian/kFreeBSD */ if ((rp = pa_readlink("/proc/self/exe"))) { pa_strlcpy(s, pa_path_get_filename(rp), l); pa_xfree(rp); return s; } } #endif #ifdef __FreeBSD__ { char *rp; if ((rp = pa_readlink("/proc/curproc/file"))) { pa_strlcpy(s, pa_path_get_filename(rp), l); pa_xfree(rp); return s; } } #endif #if defined(HAVE_DLADDR) && defined(PA_GCC_WEAKREF) { Dl_info info; if(_main) { int err = dladdr(&_main, &info); if (err != 0) { char *p = pa_realpath(info.dli_fname); if (p) return p; } } } #endif #if defined(HAVE_SYS_PRCTL_H) && defined(PR_GET_NAME) { #ifndef TASK_COMM_LEN /* Actually defined in linux/sched.h */ #define TASK_COMM_LEN 16 #endif char tcomm[TASK_COMM_LEN+1]; memset(tcomm, 0, sizeof(tcomm)); /* This works on Linux only */ if (prctl(PR_GET_NAME, (unsigned long) tcomm, 0, 0, 0) == 0) return pa_strlcpy(s, tcomm, l); } #endif #ifdef OS_IS_DARWIN { int mib[] = { CTL_KERN, KERN_PROCARGS, getpid(), 0 }; size_t len, nmib = (sizeof(mib) / sizeof(mib[0])) - 1; char *buf; sysctl(mib, nmib, NULL, &len, NULL, 0); buf = (char *) pa_xmalloc(len); if (sysctl(mib, nmib, buf, &len, NULL, 0) == 0) { pa_strlcpy(s, basename(buf), l); pa_xfree(buf); return s; } pa_xfree(buf); /* fall thru */ } #endif /* OS_IS_DARWIN */ errno = ENOENT; return NULL; } char *pa_path_get_filename(const char *p) { char *fn; if (!p) return NULL; if ((fn = strrchr(p, PA_PATH_SEP_CHAR))) return fn+1; return (char*) p; } char *pa_get_fqdn(char *s, size_t l) { char hn[256]; #ifdef HAVE_GETADDRINFO struct addrinfo *a = NULL, hints; #endif pa_assert(s); pa_assert(l > 0); if (!pa_get_host_name(hn, sizeof(hn))) return NULL; #ifdef HAVE_GETADDRINFO memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_flags = AI_CANONNAME; if (getaddrinfo(hn, NULL, &hints, &a)) return pa_strlcpy(s, hn, l); if (!a->ai_canonname || !*a->ai_canonname) { freeaddrinfo(a); return pa_strlcpy(s, hn, l); } pa_strlcpy(s, a->ai_canonname, l); freeaddrinfo(a); return s; #else return pa_strlcpy(s, hn, l); #endif } int pa_msleep(unsigned long t) { #ifdef OS_IS_WIN32 Sleep(t); return 0; #elif defined(HAVE_NANOSLEEP) struct timespec ts; ts.tv_sec = (time_t) (t / PA_MSEC_PER_SEC); ts.tv_nsec = (long) ((t % PA_MSEC_PER_SEC) * PA_NSEC_PER_MSEC); return nanosleep(&ts, NULL); #else #error "Platform lacks a sleep function." #endif } #ifdef _POSIX_PRIORITY_SCHEDULING static int set_scheduler(int rtprio) { #ifdef HAVE_SCHED_H struct sched_param sp; #ifdef HAVE_DBUS int r; long long rttime; #ifdef RLIMIT_RTTIME struct rlimit rl; #endif 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", 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); rttime = rtkit_get_rttime_usec_max(bus); if (rttime >= 0) { #ifdef RLIMIT_RTTIME r = getrlimit(RLIMIT_RTTIME, &rl); if (r >= 0 && (long long) rl.rlim_max > rttime) { pa_log_info("Clamping rlimit-rttime to %lld for RealtimeKit", rttime); rl.rlim_cur = rl.rlim_max = rttime; r = setrlimit(RLIMIT_RTTIME, &rl); if (r < 0) pa_log("setrlimit() failed: %s", pa_cstrerror(errno)); } #endif 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 { dbus_connection_close(bus); dbus_connection_unref(bus); errno = -rttime; } #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_thread_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; }