• 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