1/*** 2 This file is part of PulseAudio. 3 4 Copyright 2004-2006 Lennart Poettering 5 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB 6 7 PulseAudio is free software; you can redistribute it and/or modify 8 it under the terms of the GNU Lesser General Public License as 9 published by the Free Software Foundation; either version 2.1 of the 10 License, or (at your option) any later version. 11 12 PulseAudio is distributed in the hope that it will be useful, but 13 WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 Lesser General Public License for more details. 16 17 You should have received a copy of the GNU Lesser General Public 18 License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 19***/ 20 21#ifdef HAVE_CONFIG_H 22#include <config.h> 23#endif 24 25#include <errno.h> 26#include <limits.h> 27#include <stdio.h> 28#include <stdlib.h> 29#include <string.h> 30#include <time.h> 31#include <unistd.h> 32#include <sys/types.h> 33 34#ifdef HAVE_PWD_H 35#include <pwd.h> 36#endif 37 38#ifdef HAVE_NETDB_H 39#include <netdb.h> 40#endif 41 42#ifdef HAVE_WINDOWS_H 43#include <windows.h> 44#endif 45 46#ifdef HAVE_SYS_PRCTL_H 47#include <sys/prctl.h> 48#endif 49 50#ifdef OS_IS_DARWIN 51#include <libgen.h> 52#include <sys/sysctl.h> 53#endif 54 55#include <pulse/xmalloc.h> 56#include <pulse/timeval.h> 57 58#include <pulsecore/socket.h> 59#include <pulsecore/core-error.h> 60#include <pulsecore/core-util.h> 61#include <pulsecore/macro.h> 62#include <pulsecore/usergroup.h> 63 64#include "util.h" 65 66#if defined(HAVE_DLADDR) && defined(PA_GCC_WEAKREF) 67#ifndef _GNU_SOURCE 68#define _GNU_SOURCE 1 69#endif 70#include <dlfcn.h> 71 72static int _main() PA_GCC_WEAKREF(main); 73#endif 74 75#ifdef HAVE_PTHREAD 76#include <pthread.h> 77#endif 78 79#ifdef HAVE_SCHED_H 80#include <sched.h> 81 82#if defined(__linux__) && !defined(SCHED_RESET_ON_FORK) 83#define SCHED_RESET_ON_FORK 0x40000000 84#endif 85#endif 86 87#ifdef __APPLE__ 88#include <mach/mach_init.h> 89#include <mach/thread_act.h> 90#include <mach/thread_policy.h> 91#include <sys/sysctl.h> 92#endif 93 94#ifdef __FreeBSD__ 95#include <sys/sysctl.h> 96#endif 97 98#ifdef HAVE_DBUS 99#include <pulsecore/rtkit.h> 100#endif 101 102char *pa_get_user_name(char *s, size_t l) { 103 const char *p; 104 char *name = NULL; 105#ifdef OS_IS_WIN32 106 char buf[1024]; 107#endif 108 109#ifdef HAVE_PWD_H 110 struct passwd *r; 111#endif 112 113 pa_assert(s); 114 pa_assert(l > 0); 115 116 p = NULL; 117#ifdef HAVE_GETUID 118 p = getuid() == 0 ? "root" : NULL; 119#endif 120 if (!p) p = getenv("USER"); 121 if (!p) p = getenv("LOGNAME"); 122 if (!p) p = getenv("USERNAME"); 123 124 if (p) { 125 name = pa_strlcpy(s, p, l); 126 } else { 127#ifdef HAVE_PWD_H 128 129 if ((r = pa_getpwuid_malloc(getuid())) == NULL) { 130 pa_snprintf(s, l, "%lu", (unsigned long) getuid()); 131 return s; 132 } 133 134 name = pa_strlcpy(s, r->pw_name, l); 135 pa_getpwuid_free(r); 136 137#elif defined(OS_IS_WIN32) /* HAVE_PWD_H */ 138 DWORD size = sizeof(buf); 139 140 if (!GetUserName(buf, &size)) { 141 errno = ENOENT; 142 return NULL; 143 } 144 145 name = pa_strlcpy(s, buf, l); 146 147#else /* HAVE_PWD_H */ 148 149 return NULL; 150#endif /* HAVE_PWD_H */ 151 } 152 153 return name; 154} 155 156char *pa_get_host_name(char *s, size_t l) { 157 158 pa_assert(s); 159 pa_assert(l > 0); 160 161 if (gethostname(s, l) < 0) 162 return NULL; 163 164 s[l-1] = 0; 165 return s; 166} 167 168char *pa_get_home_dir(char *s, size_t l) { 169 char *e; 170 char *dir; 171#ifdef HAVE_PWD_H 172 struct passwd *r; 173#endif 174 175 pa_assert(s); 176 pa_assert(l > 0); 177 178 if ((e = getenv("HOME"))) { 179 dir = pa_strlcpy(s, e, l); 180 goto finish; 181 } 182 183 if ((e = getenv("USERPROFILE"))) { 184 dir = pa_strlcpy(s, e, l); 185 goto finish; 186 } 187 188#ifdef HAVE_PWD_H 189 errno = 0; 190 if ((r = pa_getpwuid_malloc(getuid())) == NULL) { 191 if (!errno) 192 errno = ENOENT; 193 194 return NULL; 195 } 196 197 dir = pa_strlcpy(s, r->pw_dir, l); 198 199 pa_getpwuid_free(r); 200#endif /* HAVE_PWD_H */ 201 202finish: 203 if (!dir) { 204 errno = ENOENT; 205 return NULL; 206 } 207 208 if (!pa_is_path_absolute(dir)) { 209 pa_log("Failed to get the home directory, not an absolute path: %s", dir); 210 errno = ENOENT; 211 return NULL; 212 } 213 214 return dir; 215} 216 217char *pa_get_binary_name(char *s, size_t l) { 218 219 pa_assert(s); 220 pa_assert(l > 0); 221 222#if defined(OS_IS_WIN32) 223 { 224 char path[PATH_MAX]; 225 226 if (GetModuleFileName(NULL, path, PATH_MAX)) 227 return pa_strlcpy(s, pa_path_get_filename(path), l); 228 } 229#endif 230 231#if defined(__linux__) || (defined(__FreeBSD_kernel__) && !defined(__FreeBSD__)) || defined(__GNU__) 232 { 233 char *rp; 234 /* This works on Linux and Debian/kFreeBSD */ 235 236 if ((rp = pa_readlink("/proc/self/exe"))) { 237 pa_strlcpy(s, pa_path_get_filename(rp), l); 238 pa_xfree(rp); 239 return s; 240 } 241 } 242#endif 243 244#ifdef __FreeBSD__ 245 { 246 char path[PATH_MAX + 1]; 247 size_t len = PATH_MAX; 248 int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; 249 250 if (sysctl(mib, 4, &path, &len, NULL, 0) == 0) { 251 pa_strlcpy(s, pa_path_get_filename(path), l); 252 return s; 253 } 254 } 255#endif 256 257#if defined(HAVE_DLADDR) && defined(PA_GCC_WEAKREF) 258 { 259 Dl_info info; 260 if(_main) { 261 int err = dladdr(&_main, &info); 262 if (err != 0) { 263 char *p = pa_realpath(info.dli_fname); 264 if (p) 265 return p; 266 } 267 } 268 } 269#endif 270 271#if defined(HAVE_SYS_PRCTL_H) && defined(PR_GET_NAME) 272 { 273 274 #ifndef TASK_COMM_LEN 275 /* Actually defined in linux/sched.h */ 276 #define TASK_COMM_LEN 16 277 #endif 278 279 char tcomm[TASK_COMM_LEN+1]; 280 memset(tcomm, 0, sizeof(tcomm)); 281 282 /* This works on Linux only */ 283 if (prctl(PR_GET_NAME, (unsigned long) tcomm, 0, 0, 0) == 0) 284 return pa_strlcpy(s, tcomm, l); 285 286 } 287#endif 288 289#ifdef OS_IS_DARWIN 290 { 291 int mib[] = { CTL_KERN, KERN_PROCARGS, getpid(), 0 }; 292 size_t len, nmib = (sizeof(mib) / sizeof(mib[0])) - 1; 293 char *buf; 294 295 sysctl(mib, nmib, NULL, &len, NULL, 0); 296 buf = (char *) pa_xmalloc(len); 297 298 if (sysctl(mib, nmib, buf, &len, NULL, 0) == 0) { 299 pa_strlcpy(s, basename(buf), l); 300 pa_xfree(buf); 301 return s; 302 } 303 304 pa_xfree(buf); 305 306 /* fall thru */ 307 } 308#endif /* OS_IS_DARWIN */ 309 310 errno = ENOENT; 311 return NULL; 312} 313 314char *pa_path_get_filename(const char *p) { 315 char *fn; 316 317 if (!p) 318 return NULL; 319 320 if ((fn = strrchr(p, PA_PATH_SEP_CHAR))) 321 return fn+1; 322 323 return (char*) p; 324} 325 326char *pa_get_fqdn(char *s, size_t l) { 327 char hn[256]; 328#ifdef HAVE_GETADDRINFO 329 struct addrinfo *a = NULL, hints; 330#endif 331 332 pa_assert(s); 333 pa_assert(l > 0); 334 335 if (!pa_get_host_name(hn, sizeof(hn))) 336 return NULL; 337 338#ifdef HAVE_GETADDRINFO 339 memset(&hints, 0, sizeof(hints)); 340 hints.ai_family = AF_UNSPEC; 341 hints.ai_flags = AI_CANONNAME; 342 343 if (getaddrinfo(hn, NULL, &hints, &a)) 344 return pa_strlcpy(s, hn, l); 345 346 if (!a->ai_canonname || !*a->ai_canonname) { 347 freeaddrinfo(a); 348 return pa_strlcpy(s, hn, l); 349 } 350 351 pa_strlcpy(s, a->ai_canonname, l); 352 freeaddrinfo(a); 353 return s; 354#else 355 return pa_strlcpy(s, hn, l); 356#endif 357} 358 359int pa_msleep(unsigned long t) { 360#ifdef OS_IS_WIN32 361 Sleep(t); 362 return 0; 363#elif defined(HAVE_NANOSLEEP) 364 struct timespec ts; 365 366 ts.tv_sec = (time_t) (t / PA_MSEC_PER_SEC); 367 ts.tv_nsec = (long) ((t % PA_MSEC_PER_SEC) * PA_NSEC_PER_MSEC); 368 369 return nanosleep(&ts, NULL); 370#else 371#error "Platform lacks a sleep function." 372#endif 373} 374 375#ifdef _POSIX_PRIORITY_SCHEDULING 376static int set_scheduler(int rtprio) { 377#ifdef HAVE_SCHED_H 378 struct sched_param sp; 379#ifdef HAVE_DBUS 380 int r; 381 long long rttime; 382#ifdef RLIMIT_RTTIME 383 struct rlimit rl; 384#endif 385 DBusError error; 386 DBusConnection *bus; 387 388 dbus_error_init(&error); 389#endif 390 391 pa_zero(sp); 392 sp.sched_priority = rtprio; 393 394#ifdef SCHED_RESET_ON_FORK 395 if (pthread_setschedparam(pthread_self(), SCHED_RR|SCHED_RESET_ON_FORK, &sp) == 0) { 396 pa_log_debug("SCHED_RR|SCHED_RESET_ON_FORK worked."); 397 return 0; 398 } 399#endif 400 401 if (pthread_setschedparam(pthread_self(), SCHED_RR, &sp) == 0) { 402 pa_log_debug("SCHED_RR worked."); 403 return 0; 404 } 405#endif /* HAVE_SCHED_H */ 406 407#ifdef HAVE_DBUS 408 /* Try to talk to RealtimeKit */ 409 410 if (!(bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error))) { 411 pa_log("Failed to connect to system bus: %s", error.message); 412 dbus_error_free(&error); 413 errno = -EIO; 414 return -1; 415 } 416 417 /* We need to disable exit on disconnect because otherwise 418 * dbus_shutdown will kill us. See 419 * https://bugs.freedesktop.org/show_bug.cgi?id=16924 */ 420 dbus_connection_set_exit_on_disconnect(bus, FALSE); 421 422 rttime = rtkit_get_rttime_usec_max(bus); 423 if (rttime >= 0) { 424#ifdef RLIMIT_RTTIME 425 r = getrlimit(RLIMIT_RTTIME, &rl); 426 427 if (r >= 0 && (long long) rl.rlim_max > rttime) { 428 pa_log_info("Clamping rlimit-rttime to %lld for RealtimeKit", rttime); 429 rl.rlim_cur = rl.rlim_max = rttime; 430 r = setrlimit(RLIMIT_RTTIME, &rl); 431 432 if (r < 0) 433 pa_log("setrlimit() failed: %s", pa_cstrerror(errno)); 434 } 435#endif 436 r = rtkit_make_realtime(bus, 0, rtprio); 437 dbus_connection_close(bus); 438 dbus_connection_unref(bus); 439 440 if (r >= 0) { 441 pa_log_debug("RealtimeKit worked."); 442 return 0; 443 } 444 445 errno = -r; 446 } else { 447 dbus_connection_close(bus); 448 dbus_connection_unref(bus); 449 errno = -rttime; 450 } 451 452#else 453 errno = 0; 454#endif 455 456 return -1; 457} 458#endif 459 460/* Make the current thread a realtime thread, and acquire the highest 461 * rtprio we can get that is less or equal the specified parameter. If 462 * the thread is already realtime, don't do anything. */ 463int pa_thread_make_realtime(int rtprio) { 464 465#if defined(OS_IS_DARWIN) 466 struct thread_time_constraint_policy ttcpolicy; 467 uint64_t freq = 0; 468 size_t size = sizeof(freq); 469 int ret; 470 471 ret = sysctlbyname("hw.cpufrequency", &freq, &size, NULL, 0); 472 if (ret < 0) { 473 pa_log_info("Unable to read CPU frequency, acquisition of real-time scheduling failed."); 474 return -1; 475 } 476 477 pa_log_debug("sysctl for hw.cpufrequency: %llu", freq); 478 479 /* See http://developer.apple.com/library/mac/#documentation/Darwin/Conceptual/KernelProgramming/scheduler/scheduler.html */ 480 ttcpolicy.period = freq / 160; 481 ttcpolicy.computation = freq / 3300; 482 ttcpolicy.constraint = freq / 2200; 483 ttcpolicy.preemptible = 1; 484 485 ret = thread_policy_set(mach_thread_self(), 486 THREAD_TIME_CONSTRAINT_POLICY, 487 (thread_policy_t) &ttcpolicy, 488 THREAD_TIME_CONSTRAINT_POLICY_COUNT); 489 if (ret) { 490 pa_log_info("Unable to set real-time thread priority (%08x).", ret); 491 return -1; 492 } 493 494 pa_log_info("Successfully acquired real-time thread priority."); 495 return 0; 496 497#elif defined(_POSIX_PRIORITY_SCHEDULING) 498 int p; 499 500 if (set_scheduler(rtprio) >= 0) { 501 pa_log_info("Successfully enabled SCHED_RR scheduling for thread, with priority %i.", rtprio); 502 return 0; 503 } 504 505 for (p = rtprio-1; p >= 1; p--) 506 if (set_scheduler(p) >= 0) { 507 pa_log_info("Successfully enabled SCHED_RR scheduling for thread, with priority %i, which is lower than the requested %i.", p, rtprio); 508 return 0; 509 } 510#elif defined(OS_IS_WIN32) 511 /* Windows only allows realtime scheduling to be set on a per process basis. 512 * Therefore, instead of making the thread realtime, just give it the highest non-realtime priority. */ 513 if (SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL)) { 514 pa_log_info("Successfully enabled THREAD_PRIORITY_TIME_CRITICAL scheduling for thread."); 515 return 0; 516 } 517 518 pa_log_warn("SetThreadPriority() failed: 0x%08lX", GetLastError()); 519 errno = EPERM; 520#else 521 errno = ENOTSUP; 522#endif 523 pa_log_info("Failed to acquire real-time scheduling: %s", pa_cstrerror(errno)); 524 return -1; 525} 526