• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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