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 __FreeBSD__
95 #include <sys/sysctl.h>
96 #endif
97
98 #ifdef HAVE_DBUS
99 #include <pulsecore/rtkit.h>
100 #endif
101
pa_get_user_name(char * s,size_t l)102 char *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
pa_get_host_name(char * s,size_t l)156 char *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
pa_get_home_dir(char * s,size_t l)168 char *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
202 finish:
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
pa_get_binary_name(char * s,size_t l)217 char *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
pa_path_get_filename(const char * p)314 char *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
pa_get_fqdn(char * s,size_t l)326 char *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
pa_msleep(unsigned long t)359 int 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
set_scheduler(int rtprio)376 static 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. */
pa_thread_make_realtime(int rtprio)463 int 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