xref: /third_party/pulseaudio/src/pulse/util.c (revision 53a5a1b3)
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
72static 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
102char *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
156char *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
168char *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
202finish:
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
217char *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
314char *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
326char *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
359int 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
376static 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. */
463int 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