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