4 This file is part of polypaudio.
6 polypaudio is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as
8 published by the Free Software Foundation; either version 2.1 of the
9 License, or (at your option) any later version.
11 polypaudio is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with polypaudio; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
38 #include <sys/types.h>
46 #ifdef HAVE_SYS_RESOURCE_H
47 #include <sys/resource.h>
70 #include <samplerate.h>
72 #include <polyp/xmalloc.h>
73 #include <polyp/util.h>
75 #include <polypcore/winsock.h>
76 #include <polypcore/log.h>
78 #include "core-util.h"
81 #define PA_RUNTIME_PATH_PREFIX "/tmp/polypaudio-"
84 #define PA_RUNTIME_PATH_PREFIX "%TEMP%\\polypaudio-"
90 #define POLYP_ROOTENV "POLYP_ROOT"
92 int pa_set_root(HANDLE handle) {
93 char library_path[MAX_PATH + sizeof(POLYP_ROOTENV) + 1], *sep;
95 strcpy(library_path, POLYP_ROOTENV "=");
97 if (!GetModuleFileName(handle, library_path + sizeof(POLYP_ROOTENV), MAX_PATH))
100 sep = strrchr(library_path, '\\');
104 if (_putenv(library_path) < 0)
112 /** Make a file descriptor nonblock. Doesn't do any error checking */
113 void pa_make_nonblock_fd(int fd) {
118 if ((v = fcntl(fd, F_GETFL)) >= 0)
119 if (!(v & O_NONBLOCK))
120 fcntl(fd, F_SETFL, v|O_NONBLOCK);
121 #elif defined(OS_IS_WIN32)
123 if (ioctlsocket(fd, FIONBIO, &arg) < 0) {
124 if (WSAGetLastError() == WSAENOTSOCK)
125 pa_log_warn(__FILE__": WARNING: Only sockets can be made non-blocking!");
128 pa_log_warn(__FILE__": WARNING: Non-blocking I/O not supported.!");
132 /** Creates a directory securely */
133 int pa_make_secure_dir(const char* dir) {
140 if (mkdir(dir, 0700) < 0)
146 chown(dir, getuid(), getgid());
153 if (lstat(dir, &st) < 0)
155 if (stat(dir, &st) < 0)
160 if (!S_ISDIR(st.st_mode) || (st.st_uid != getuid()) || ((st.st_mode & 0777) != 0700))
163 fprintf(stderr, "FIXME: pa_make_secure_dir()\n");
173 /* Return a newly allocated sting containing the parent directory of the specified file */
174 char *pa_parent_dir(const char *fn) {
175 char *slash, *dir = pa_xstrdup(fn);
177 slash = (char*) pa_path_get_filename(dir);
185 /* Creates a the parent directory of the specified path securely */
186 int pa_make_secure_parent_dir(const char *fn) {
190 if (!(dir = pa_parent_dir(fn)))
193 if (pa_make_secure_dir(dir) < 0)
203 /** Platform independent read function. Necessary since not all systems
204 * treat all file descriptors equal. */
205 ssize_t pa_read(int fd, void *buf, size_t count) {
209 r = recv(fd, buf, count, 0);
211 if (WSAGetLastError() != WSAENOTSOCK) {
212 errno = WSAGetLastError();
219 r = read(fd, buf, count);
224 /** Similar to pa_read(), but handles writes */
225 ssize_t pa_write(int fd, const void *buf, size_t count) {
229 r = send(fd, buf, count, 0);
231 if (WSAGetLastError() != WSAENOTSOCK) {
232 errno = WSAGetLastError();
239 r = write(fd, buf, count);
244 /** Calls read() in a loop. Makes sure that as much as 'size' bytes,
245 * unless EOF is reached or an error occured */
246 ssize_t pa_loop_read(int fd, void*data, size_t size) {
248 assert(fd >= 0 && data && size);
253 if ((r = pa_read(fd, data, size)) < 0)
260 data = (uint8_t*) data + r;
267 /** Similar to pa_loop_read(), but wraps write() */
268 ssize_t pa_loop_write(int fd, const void*data, size_t size) {
270 assert(fd >= 0 && data && size);
275 if ((r = pa_write(fd, data, size)) < 0)
282 data = (const uint8_t*) data + r;
289 /* Print a warning messages in case that the given signal is not
290 * blocked or trapped */
291 void pa_check_signal_is_blocked(int sig) {
292 #ifdef HAVE_SIGACTION
296 /* If POSIX threads are supported use thread-aware
297 * pthread_sigmask() function, to check if the signal is
298 * blocked. Otherwise fall back to sigprocmask() */
301 if (pthread_sigmask(SIG_SETMASK, NULL, &set) < 0) {
303 if (sigprocmask(SIG_SETMASK, NULL, &set) < 0) {
304 pa_log(__FILE__": sigprocmask() failed: %s", strerror(errno));
311 if (sigismember(&set, sig))
314 /* Check whether the signal is trapped */
316 if (sigaction(sig, NULL, &sa) < 0) {
317 pa_log(__FILE__": sigaction() failed: %s", strerror(errno));
321 if (sa.sa_handler != SIG_DFL)
324 pa_log(__FILE__": WARNING: %s is not trapped. This might cause malfunction!", pa_strsignal(sig));
325 #else /* HAVE_SIGACTION */
326 pa_log(__FILE__": WARNING: %s might not be trapped. This might cause malfunction!", pa_strsignal(sig));
330 /* The following function is based on an example from the GNU libc
331 * documentation. This function is similar to GNU's asprintf(). */
332 char *pa_sprintf_malloc(const char *format, ...) {
342 c = pa_xrealloc(c, size);
344 va_start(ap, format);
345 r = vsnprintf(c, size, format, ap);
348 if (r > -1 && r < size)
351 if (r > -1) /* glibc 2.1 */
358 /* Same as the previous function, but use a va_list instead of an
360 char *pa_vsprintf_malloc(const char *format, va_list ap) {
368 c = pa_xrealloc(c, size);
369 r = vsnprintf(c, size, format, ap);
371 if (r > -1 && r < size)
374 if (r > -1) /* glibc 2.1 */
381 /* Similar to OpenBSD's strlcpy() function */
382 char *pa_strlcpy(char *b, const char *s, size_t l) {
383 assert(b && s && l > 0);
390 #define NICE_LEVEL (-15)
392 /* Raise the priority of the current process as much as possible and
393 sensible: set the nice level to -15 and enable realtime scheduling if
395 void pa_raise_priority(void) {
397 #ifdef HAVE_SYS_RESOURCE_H
398 if (setpriority(PRIO_PROCESS, 0, NICE_LEVEL) < 0)
399 pa_log_warn(__FILE__": setpriority() failed: %s", strerror(errno));
401 pa_log_info(__FILE__": Successfully gained nice level %i.", NICE_LEVEL);
404 #ifdef _POSIX_PRIORITY_SCHEDULING
406 struct sched_param sp;
408 if (sched_getparam(0, &sp) < 0) {
409 pa_log(__FILE__": sched_getparam() failed: %s", strerror(errno));
413 sp.sched_priority = 1;
414 if (sched_setscheduler(0, SCHED_FIFO, &sp) < 0) {
415 pa_log_warn(__FILE__": sched_setscheduler() failed: %s", strerror(errno));
419 pa_log_info(__FILE__": Successfully enabled SCHED_FIFO scheduling.");
424 if (!SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS))
425 pa_log_warn(__FILE__": SetPriorityClass() failed: 0x%08X", GetLastError());
427 pa_log_info(__FILE__": Successfully gained high priority class.");
431 /* Reset the priority to normal, inverting the changes made by pa_raise_priority() */
432 void pa_reset_priority(void) {
434 SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
437 #ifdef _POSIX_PRIORITY_SCHEDULING
439 struct sched_param sp;
440 sched_getparam(0, &sp);
441 sp.sched_priority = 0;
442 sched_setscheduler(0, SCHED_OTHER, &sp);
446 #ifdef HAVE_SYS_RESOURCE_H
447 setpriority(PRIO_PROCESS, 0, 0);
451 /* Set the FD_CLOEXEC flag for a fd */
452 int pa_fd_set_cloexec(int fd, int b) {
458 if ((v = fcntl(fd, F_GETFD, 0)) < 0)
461 v = (v & ~FD_CLOEXEC) | (b ? FD_CLOEXEC : 0);
463 if (fcntl(fd, F_SETFD, v) < 0)
470 /* Try to parse a boolean string value.*/
471 int pa_parse_boolean(const char *v) {
473 if (!strcmp(v, "1") || v[0] == 'y' || v[0] == 'Y' || v[0] == 't' || v[0] == 'T' || !strcasecmp(v, "on"))
475 else if (!strcmp(v, "0") || v[0] == 'n' || v[0] == 'N' || v[0] == 'f' || v[0] == 'F' || !strcasecmp(v, "off"))
481 /* Split the specified string wherever one of the strings in delimiter
482 * occurs. Each time it is called returns a newly allocated string
483 * with pa_xmalloc(). The variable state points to, should be
484 * initiallized to NULL before the first call. */
485 char *pa_split(const char *c, const char *delimiter, const char**state) {
486 const char *current = *state ? *state : c;
492 l = strcspn(current, delimiter);
498 return pa_xstrndup(current, l);
501 /* What is interpreted as whitespace? */
502 #define WHITESPACE " \t\n"
504 /* Split a string into words. Otherwise similar to pa_split(). */
505 char *pa_split_spaces(const char *c, const char **state) {
506 const char *current = *state ? *state : c;
509 if (!*current || *c == 0)
512 current += strspn(current, WHITESPACE);
513 l = strcspn(current, WHITESPACE);
517 return pa_xstrndup(current, l);
520 /* Return the name of an UNIX signal. Similar to GNU's strsignal() */
521 const char *pa_strsignal(int sig) {
523 case SIGINT: return "SIGINT";
524 case SIGTERM: return "SIGTERM";
526 case SIGUSR1: return "SIGUSR1";
529 case SIGUSR2: return "SIGUSR2";
532 case SIGXCPU: return "SIGXCPU";
535 case SIGPIPE: return "SIGPIPE";
538 case SIGCHLD: return "SIGCHLD";
541 case SIGHUP: return "SIGHUP";
543 default: return "UNKNOWN SIGNAL";
549 /* Check whether the specified GID and the group name match */
550 static int is_group(gid_t gid, const char *name) {
551 struct group group, *result = NULL;
556 #ifdef HAVE_GETGRGID_R
557 #ifdef _SC_GETGR_R_SIZE_MAX
558 n = sysconf(_SC_GETGR_R_SIZE_MAX);
563 data = pa_xmalloc(n);
565 if (getgrgid_r(gid, &group, data, n, &result) < 0 || !result) {
566 pa_log(__FILE__ ": getgrgid_r(%u) failed: %s", gid, strerror(errno));
570 r = strcmp(name, result->gr_name) == 0;
575 /* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X) that do not
576 * support getgrgid_r. */
577 if ((result = getgrgid(gid)) == NULL) {
578 pa_log(__FILE__ ": getgrgid(%u) failed: %s", gid, strerror(errno));
582 r = strcmp(name, result->gr_name) == 0;
590 /* Check the current user is member of the specified group */
591 int pa_own_uid_in_group(const char *name, gid_t *gid) {
592 GETGROUPS_T *gids, tgid;
593 int n = sysconf(_SC_NGROUPS_MAX);
598 gids = pa_xmalloc(sizeof(GETGROUPS_T)*n);
600 if ((n = getgroups(n, gids)) < 0) {
601 pa_log(__FILE__": getgroups() failed: %s", strerror(errno));
605 for (i = 0; i < n; i++) {
606 if (is_group(gids[i], name) > 0) {
613 if (is_group(tgid = getgid(), name) > 0) {
627 int pa_uid_in_group(uid_t uid, const char *name) {
630 struct group grbuf, *gr;
634 g_n = sysconf(_SC_GETGR_R_SIZE_MAX);
635 g_buf = pa_xmalloc(g_n);
637 p_n = sysconf(_SC_GETPW_R_SIZE_MAX);
638 p_buf = pa_xmalloc(p_n);
640 if (getgrnam_r(name, &grbuf, g_buf, (size_t) g_n, &gr) != 0 || !gr)
644 for (i = gr->gr_mem; *i; i++) {
645 struct passwd pwbuf, *pw;
647 if (getpwnam_r(*i, &pwbuf, p_buf, (size_t) p_n, &pw) != 0 || !pw)
650 if (pw->pw_uid == uid) {
663 #else /* HAVE_GRP_H */
665 int pa_own_uid_in_group(const char *name, gid_t *gid) {
670 int pa_uid_in_group(uid_t uid, const char *name) {
676 /* Lock or unlock a file entirely.
677 (advisory on UNIX, mandatory on Windows) */
678 int pa_lock_fd(int fd, int b) {
682 /* Try a R/W lock first */
684 flock.l_type = b ? F_WRLCK : F_UNLCK;
685 flock.l_whence = SEEK_SET;
689 if (fcntl(fd, F_SETLKW, &flock) >= 0)
692 /* Perhaps the file descriptor qas opened for read only, than try again with a read lock. */
693 if (b && errno == EBADF) {
694 flock.l_type = F_RDLCK;
695 if (fcntl(fd, F_SETLKW, &flock) >= 0)
699 pa_log(__FILE__": %slock failed: %s", !b ? "un" : "", strerror(errno));
703 HANDLE h = (HANDLE)_get_osfhandle(fd);
705 if (b && LockFile(h, 0, 0, 0xFFFFFFFF, 0xFFFFFFFF))
707 if (!b && UnlockFile(h, 0, 0, 0xFFFFFFFF, 0xFFFFFFFF))
710 pa_log(__FILE__": %slock failed: 0x%08X", !b ? "un" : "", GetLastError());
716 /* Remove trailing newlines from a string */
717 char* pa_strip_nl(char *s) {
720 s[strcspn(s, "\r\n")] = 0;
724 /* Create a temporary lock file and lock it. */
725 int pa_lock_lockfile(const char *fn) {
732 if ((fd = open(fn, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR)) < 0) {
733 pa_log(__FILE__": failed to create lock file '%s': %s", fn, strerror(errno));
737 if (pa_lock_fd(fd, 1) < 0) {
738 pa_log(__FILE__": failed to lock file '%s'.", fn);
742 if (fstat(fd, &st) < 0) {
743 pa_log(__FILE__": failed to fstat() file '%s'.", fn);
747 /* Check wheter the file has been removed meanwhile. When yes, restart this loop, otherwise, we're done */
748 if (st.st_nlink >= 1)
751 if (pa_lock_fd(fd, 0) < 0) {
752 pa_log(__FILE__": failed to unlock file '%s'.", fn);
757 pa_log(__FILE__": failed to close file '%s'.", fn);
774 /* Unlock a temporary lcok file */
775 int pa_unlock_lockfile(const char *fn, int fd) {
777 assert(fn && fd >= 0);
779 if (unlink(fn) < 0) {
780 pa_log_warn(__FILE__": WARNING: unable to remove lock file '%s': %s", fn, strerror(errno));
784 if (pa_lock_fd(fd, 0) < 0) {
785 pa_log_warn(__FILE__": WARNING: failed to unlock file '%s'.", fn);
790 pa_log_warn(__FILE__": WARNING: failed to close lock file '%s': %s", fn, strerror(errno));
797 /* Try to open a configuration file. If "env" is specified, open the
798 * value of the specified environment variable. Otherwise look for a
799 * file "local" in the home directory or a file "global" in global
800 * file system. If "result" is non-NULL, a pointer to a newly
801 * allocated buffer containing the used configuration file is
803 FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result, const char *mode) {
810 if (!getenv(POLYP_ROOTENV))
814 if (env && (fn = getenv(env))) {
816 if (!ExpandEnvironmentStrings(fn, buf, PATH_MAX))
822 *result = pa_xstrdup(fn);
824 return fopen(fn, mode);
827 if (local && pa_get_home_dir(h, sizeof(h))) {
831 fn = lfn = pa_sprintf_malloc("%s/%s", h, local);
834 if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX))
841 if (f || errno != ENOENT) {
843 *result = pa_xstrdup(fn);
859 if (!ExpandEnvironmentStrings(global, buf, PATH_MAX))
865 *result = pa_xstrdup(global);
867 return fopen(global, mode);
870 /* Format the specified data as a hexademical string */
871 char *pa_hexstr(const uint8_t* d, size_t dlength, char *s, size_t slength) {
873 const char hex[] = "0123456789abcdef";
874 assert(d && s && slength > 0);
876 while (i < dlength && j+3 <= slength) {
877 s[j++] = hex[*d >> 4];
878 s[j++] = hex[*d & 0xF];
884 s[j < slength ? j : slength] = 0;
888 /* Convert a hexadecimal digit to a number or -1 if invalid */
889 static int hexc(char c) {
890 if (c >= '0' && c <= '9')
893 if (c >= 'A' && c <= 'F')
896 if (c >= 'a' && c <= 'f')
902 /* Parse a hexadecimal string as created by pa_hexstr() to a BLOB */
903 size_t pa_parsehex(const char *p, uint8_t *d, size_t dlength) {
907 while (j < dlength && *p) {
910 if ((b = hexc(*(p++))) < 0)
913 d[j] = (uint8_t) (b << 4);
918 if ((b = hexc(*(p++))) < 0)
928 /* Returns nonzero when *s starts with *pfx */
929 int pa_startswith(const char *s, const char *pfx) {
937 return strlen(s) >= l && strncmp(s, pfx, l) == 0;
940 /* Returns nonzero when *s ends with *sfx */
941 int pa_endswith(const char *s, const char *sfx) {
950 return l1 >= l2 && strcmp(s+l1-l2, sfx) == 0;
953 /* if fn is null return the polypaudio run time path in s (/tmp/polypaudio)
954 * if fn is non-null and starts with / return fn in s
955 * otherwise append fn to the run time path and return it in s */
956 char *pa_runtime_path(const char *fn, char *s, size_t l) {
960 if (fn && *fn == '/')
962 if (fn && strlen(fn) >= 3 && isalpha(fn[0]) && fn[1] == ':' && fn[2] == '\\')
964 return pa_strlcpy(s, fn, l);
967 snprintf(s, l, "%s%s%c%s", PA_RUNTIME_PATH_PREFIX, pa_get_user_name(u, sizeof(u)), PATH_SEP, fn);
969 snprintf(s, l, "%s%s", PA_RUNTIME_PATH_PREFIX, pa_get_user_name(u, sizeof(u)));
975 ExpandEnvironmentStrings(buf, s, l);
982 /* Convert the string s to a signed integer in *ret_i */
983 int pa_atoi(const char *s, int32_t *ret_i) {
988 l = strtol(s, &x, 0);
993 *ret_i = (int32_t) l;
998 /* Convert the string s to an unsigned integer in *ret_u */
999 int pa_atou(const char *s, uint32_t *ret_u) {
1004 l = strtoul(s, &x, 0);
1009 *ret_u = (uint32_t) l;