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 72 static 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 HAVE_DBUS 95 #include <pulsecore/rtkit.h> 96 #endif 97 pa_get_user_name(char * s,size_t l)98 char *pa_get_user_name(char *s, size_t l) { 99 const char *p; 100 char *name = NULL; 101 #ifdef OS_IS_WIN32 102 char buf[1024]; 103 #endif 104 105 #ifdef HAVE_PWD_H 106 struct passwd *r; 107 #endif 108 109 pa_assert(s); 110 pa_assert(l > 0); 111 112 p = NULL; 113 #ifdef HAVE_GETUID 114 p = getuid() == 0 ? "root" : NULL; 115 #endif 116 if (!p) p = getenv("USER"); 117 if (!p) p = getenv("LOGNAME"); 118 if (!p) p = getenv("USERNAME"); 119 120 if (p) { 121 name = pa_strlcpy(s, p, l); 122 } else { 123 #ifdef HAVE_PWD_H 124 125 if ((r = pa_getpwuid_malloc(getuid())) == NULL) { 126 pa_snprintf(s, l, "%lu", (unsigned long) getuid()); 127 return s; 128 } 129 130 name = pa_strlcpy(s, r->pw_name, l); 131 pa_getpwuid_free(r); 132 133 #elif defined(OS_IS_WIN32) /* HAVE_PWD_H */ 134 DWORD size = sizeof(buf); 135 136 if (!GetUserName(buf, &size)) { 137 errno = ENOENT; 138 return NULL; 139 } 140 141 name = pa_strlcpy(s, buf, l); 142 143 #else /* HAVE_PWD_H */ 144 145 return NULL; 146 #endif /* HAVE_PWD_H */ 147 } 148 149 return name; 150 } 151 pa_get_host_name(char * s,size_t l)152 char *pa_get_host_name(char *s, size_t l) { 153 154 pa_assert(s); 155 pa_assert(l > 0); 156 157 if (gethostname(s, l) < 0) 158 return NULL; 159 160 s[l-1] = 0; 161 return s; 162 } 163 pa_get_home_dir(char * s,size_t l)164 char *pa_get_home_dir(char *s, size_t l) { 165 char *e; 166 char *dir; 167 #ifdef HAVE_PWD_H 168 struct passwd *r; 169 #endif 170 171 pa_assert(s); 172 pa_assert(l > 0); 173 174 if ((e = getenv("HOME"))) { 175 dir = pa_strlcpy(s, e, l); 176 goto finish; 177 } 178 179 if ((e = getenv("USERPROFILE"))) { 180 dir = pa_strlcpy(s, e, l); 181 goto finish; 182 } 183 184 #ifdef HAVE_PWD_H 185 errno = 0; 186 if ((r = pa_getpwuid_malloc(getuid())) == NULL) { 187 if (!errno) 188 errno = ENOENT; 189 190 return NULL; 191 } 192 193 dir = pa_strlcpy(s, r->pw_dir, l); 194 195 pa_getpwuid_free(r); 196 #endif /* HAVE_PWD_H */ 197 198 finish: 199 if (!dir) { 200 errno = ENOENT; 201 return NULL; 202 } 203 204 if (!pa_is_path_absolute(dir)) { 205 pa_log("Failed to get the home directory, not an absolute path: %s", dir); 206 errno = ENOENT; 207 return NULL; 208 } 209 210 return dir; 211 } 212 pa_get_binary_name(char * s,size_t l)213 char *pa_get_binary_name(char *s, size_t l) { 214 215 pa_assert(s); 216 pa_assert(l > 0); 217 218 #if defined(OS_IS_WIN32) 219 { 220 char path[PATH_MAX]; 221 222 if (GetModuleFileName(NULL, path, PATH_MAX)) 223 return pa_strlcpy(s, pa_path_get_filename(path), l); 224 } 225 #endif 226 227 #if defined(__linux__) || defined(__FreeBSD_kernel__) 228 { 229 char *rp; 230 /* This works on Linux and Debian/kFreeBSD */ 231 232 if ((rp = pa_readlink("/proc/self/exe"))) { 233 pa_strlcpy(s, pa_path_get_filename(rp), l); 234 pa_xfree(rp); 235 return s; 236 } 237 } 238 #endif 239 240 #ifdef __FreeBSD__ 241 { 242 char *rp; 243 244 if ((rp = pa_readlink("/proc/curproc/file"))) { 245 pa_strlcpy(s, pa_path_get_filename(rp), l); 246 pa_xfree(rp); 247 return s; 248 } 249 } 250 #endif 251 252 #if defined(HAVE_DLADDR) && defined(PA_GCC_WEAKREF) 253 { 254 Dl_info info; 255 if(_main) { 256 int err = dladdr(&_main, &info); 257 if (err != 0) { 258 char *p = pa_realpath(info.dli_fname); 259 if (p) 260 return p; 261 } 262 } 263 } 264 #endif 265 266 #if defined(HAVE_SYS_PRCTL_H) && defined(PR_GET_NAME) 267 { 268 269 #ifndef TASK_COMM_LEN 270 /* Actually defined in linux/sched.h */ 271 #define TASK_COMM_LEN 16 272 #endif 273 274 char tcomm[TASK_COMM_LEN+1]; 275 memset(tcomm, 0, sizeof(tcomm)); 276 277 /* This works on Linux only */ 278 if (prctl(PR_GET_NAME, (unsigned long) tcomm, 0, 0, 0) == 0) 279 return pa_strlcpy(s, tcomm, l); 280 281 } 282 #endif 283 284 #ifdef OS_IS_DARWIN 285 { 286 int mib[] = { CTL_KERN, KERN_PROCARGS, getpid(), 0 }; 287 size_t len, nmib = (sizeof(mib) / sizeof(mib[0])) - 1; 288 char *buf; 289 290 sysctl(mib, nmib, NULL, &len, NULL, 0); 291 buf = (char *) pa_xmalloc(len); 292 293 if (sysctl(mib, nmib, buf, &len, NULL, 0) == 0) { 294 pa_strlcpy(s, basename(buf), l); 295 pa_xfree(buf); 296 return s; 297 } 298 299 pa_xfree(buf); 300 301 /* fall thru */ 302 } 303 #endif /* OS_IS_DARWIN */ 304 305 errno = ENOENT; 306 return NULL; 307 } 308 pa_path_get_filename(const char * p)309 char *pa_path_get_filename(const char *p) { 310 char *fn; 311 312 if (!p) 313 return NULL; 314 315 if ((fn = strrchr(p, PA_PATH_SEP_CHAR))) 316 return fn+1; 317 318 return (char*) p; 319 } 320 pa_get_fqdn(char * s,size_t l)321 char *pa_get_fqdn(char *s, size_t l) { 322 char hn[256]; 323 #ifdef HAVE_GETADDRINFO 324 struct addrinfo *a = NULL, hints; 325 #endif 326 327 pa_assert(s); 328 pa_assert(l > 0); 329 330 if (!pa_get_host_name(hn, sizeof(hn))) 331 return NULL; 332 333 #ifdef HAVE_GETADDRINFO 334 memset(&hints, 0, sizeof(hints)); 335 hints.ai_family = AF_UNSPEC; 336 hints.ai_flags = AI_CANONNAME; 337 338 if (getaddrinfo(hn, NULL, &hints, &a)) 339 return pa_strlcpy(s, hn, l); 340 341 if (!a->ai_canonname || !*a->ai_canonname) { 342 freeaddrinfo(a); 343 return pa_strlcpy(s, hn, l); 344 } 345 346 pa_strlcpy(s, a->ai_canonname, l); 347 freeaddrinfo(a); 348 return s; 349 #else 350 return pa_strlcpy(s, hn, l); 351 #endif 352 } 353 pa_msleep(unsigned long t)354 int pa_msleep(unsigned long t) { 355 #ifdef OS_IS_WIN32 356 Sleep(t); 357 return 0; 358 #elif defined(HAVE_NANOSLEEP) 359 struct timespec ts; 360 361 ts.tv_sec = (time_t) (t / PA_MSEC_PER_SEC); 362 ts.tv_nsec = (long) ((t % PA_MSEC_PER_SEC) * PA_NSEC_PER_MSEC); 363 364 return nanosleep(&ts, NULL); 365 #else 366 #error "Platform lacks a sleep function." 367 #endif 368 } 369 370 #ifdef _POSIX_PRIORITY_SCHEDULING set_scheduler(int rtprio)371 static int set_scheduler(int rtprio) { 372 #ifdef HAVE_SCHED_H 373 struct sched_param sp; 374 #ifdef HAVE_DBUS 375 int r; 376 long long rttime; 377 #ifdef RLIMIT_RTTIME 378 struct rlimit rl; 379 #endif 380 DBusError error; 381 DBusConnection *bus; 382 383 dbus_error_init(&error); 384 #endif 385 386 pa_zero(sp); 387 sp.sched_priority = rtprio; 388 389 #ifdef SCHED_RESET_ON_FORK 390 if (pthread_setschedparam(pthread_self(), SCHED_RR|SCHED_RESET_ON_FORK, &sp) == 0) { 391 pa_log_debug("SCHED_RR|SCHED_RESET_ON_FORK worked."); 392 return 0; 393 } 394 #endif 395 396 if (pthread_setschedparam(pthread_self(), SCHED_RR, &sp) == 0) { 397 pa_log_debug("SCHED_RR worked."); 398 return 0; 399 } 400 #endif /* HAVE_SCHED_H */ 401 402 #ifdef HAVE_DBUS 403 /* Try to talk to RealtimeKit */ 404 405 if (!(bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error))) { 406 pa_log("Failed to connect to system bus: %s", error.message); 407 dbus_error_free(&error); 408 errno = -EIO; 409 return -1; 410 } 411 412 /* We need to disable exit on disconnect because otherwise 413 * dbus_shutdown will kill us. See 414 * https://bugs.freedesktop.org/show_bug.cgi?id=16924 */ 415 dbus_connection_set_exit_on_disconnect(bus, FALSE); 416 417 rttime = rtkit_get_rttime_usec_max(bus); 418 if (rttime >= 0) { 419 #ifdef RLIMIT_RTTIME 420 r = getrlimit(RLIMIT_RTTIME, &rl); 421 422 if (r >= 0 && (long long) rl.rlim_max > rttime) { 423 pa_log_info("Clamping rlimit-rttime to %lld for RealtimeKit", rttime); 424 rl.rlim_cur = rl.rlim_max = rttime; 425 r = setrlimit(RLIMIT_RTTIME, &rl); 426 427 if (r < 0) 428 pa_log("setrlimit() failed: %s", pa_cstrerror(errno)); 429 } 430 #endif 431 r = rtkit_make_realtime(bus, 0, rtprio); 432 dbus_connection_close(bus); 433 dbus_connection_unref(bus); 434 435 if (r >= 0) { 436 pa_log_debug("RealtimeKit worked."); 437 return 0; 438 } 439 440 errno = -r; 441 } else { 442 dbus_connection_close(bus); 443 dbus_connection_unref(bus); 444 errno = -rttime; 445 } 446 447 #else 448 errno = 0; 449 #endif 450 451 return -1; 452 } 453 #endif 454 455 /* Make the current thread a realtime thread, and acquire the highest 456 * rtprio we can get that is less or equal the specified parameter. If 457 * the thread is already realtime, don't do anything. */ pa_thread_make_realtime(int rtprio)458 int pa_thread_make_realtime(int rtprio) { 459 460 #if defined(OS_IS_DARWIN) 461 struct thread_time_constraint_policy ttcpolicy; 462 uint64_t freq = 0; 463 size_t size = sizeof(freq); 464 int ret; 465 466 ret = sysctlbyname("hw.cpufrequency", &freq, &size, NULL, 0); 467 if (ret < 0) { 468 pa_log_info("Unable to read CPU frequency, acquisition of real-time scheduling failed."); 469 return -1; 470 } 471 472 pa_log_debug("sysctl for hw.cpufrequency: %llu", freq); 473 474 /* See http://developer.apple.com/library/mac/#documentation/Darwin/Conceptual/KernelProgramming/scheduler/scheduler.html */ 475 ttcpolicy.period = freq / 160; 476 ttcpolicy.computation = freq / 3300; 477 ttcpolicy.constraint = freq / 2200; 478 ttcpolicy.preemptible = 1; 479 480 ret = thread_policy_set(mach_thread_self(), 481 THREAD_TIME_CONSTRAINT_POLICY, 482 (thread_policy_t) &ttcpolicy, 483 THREAD_TIME_CONSTRAINT_POLICY_COUNT); 484 if (ret) { 485 pa_log_info("Unable to set real-time thread priority (%08x).", ret); 486 return -1; 487 } 488 489 pa_log_info("Successfully acquired real-time thread priority."); 490 return 0; 491 492 #elif defined(_POSIX_PRIORITY_SCHEDULING) 493 int p; 494 495 if (set_scheduler(rtprio) >= 0) { 496 pa_log_info("Successfully enabled SCHED_RR scheduling for thread, with priority %i.", rtprio); 497 return 0; 498 } 499 500 for (p = rtprio-1; p >= 1; p--) 501 if (set_scheduler(p) >= 0) { 502 pa_log_info("Successfully enabled SCHED_RR scheduling for thread, with priority %i, which is lower than the requested %i.", p, rtprio); 503 return 0; 504 } 505 #elif defined(OS_IS_WIN32) 506 /* Windows only allows realtime scheduling to be set on a per process basis. 507 * Therefore, instead of making the thread realtime, just give it the highest non-realtime priority. */ 508 if (SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL)) { 509 pa_log_info("Successfully enabled THREAD_PRIORITY_TIME_CRITICAL scheduling for thread."); 510 return 0; 511 } 512 513 pa_log_warn("SetThreadPriority() failed: 0x%08X", GetLastError()); 514 errno = EPERM; 515 #else 516 errno = ENOTSUP; 517 #endif 518 pa_log_info("Failed to acquire real-time scheduling: %s", pa_cstrerror(errno)); 519 return -1; 520 } 521