• 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 __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