]> code.delx.au - pulseaudio/blob - src/polypcore/util.c
replace memory allocation function calls with pa_xXXXX()
[pulseaudio] / src / polypcore / util.c
1 /* $Id$ */
2
3 /***
4 This file is part of polypaudio.
5
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.
10
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.
15
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
19 USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdarg.h>
27 #include <stdlib.h>
28 #include <signal.h>
29 #include <errno.h>
30 #include <assert.h>
31 #include <string.h>
32 #include <stdio.h>
33 #include <fcntl.h>
34 #include <unistd.h>
35 #include <limits.h>
36 #include <time.h>
37 #include <ctype.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <sys/time.h>
41
42 #ifdef HAVE_SCHED_H
43 #include <sched.h>
44 #endif
45
46 #ifdef HAVE_SYS_RESOURCE_H
47 #include <sys/resource.h>
48 #endif
49
50 #ifdef HAVE_PTHREAD
51 #include <pthread.h>
52 #endif
53
54 #ifdef HAVE_NETDB_H
55 #include <netdb.h>
56 #endif
57
58 #ifdef HAVE_WINDOWS_H
59 #include <windows.h>
60 #endif
61
62 #include <samplerate.h>
63
64 #ifdef HAVE_PWD_H
65 #include <pwd.h>
66 #endif
67 #ifdef HAVE_GRP_H
68 #include <grp.h>
69 #endif
70
71 #include "winsock.h"
72
73 #include <polyp/xmalloc.h>
74
75 #include <polypcore/log.h>
76
77 #include "util.h"
78
79 #ifndef OS_IS_WIN32
80 #define PA_RUNTIME_PATH_PREFIX "/tmp/polypaudio-"
81 #define PATH_SEP '/'
82 #else
83 #define PA_RUNTIME_PATH_PREFIX "%TEMP%\\polypaudio-"
84 #define PATH_SEP '\\'
85 #endif
86
87 #ifdef OS_IS_WIN32
88
89 #define POLYP_ROOTENV "POLYP_ROOT"
90
91 int pa_set_root(HANDLE handle) {
92 char library_path[MAX_PATH + sizeof(POLYP_ROOTENV) + 1], *sep;
93
94 strcpy(library_path, POLYP_ROOTENV "=");
95
96 if (!GetModuleFileName(handle, library_path + sizeof(POLYP_ROOTENV), MAX_PATH))
97 return 0;
98
99 sep = strrchr(library_path, '\\');
100 if (sep)
101 *sep = '\0';
102
103 if (_putenv(library_path) < 0)
104 return 0;
105
106 return 1;
107 }
108
109 #endif
110
111 /** Make a file descriptor nonblock. Doesn't do any error checking */
112 void pa_make_nonblock_fd(int fd) {
113 #ifdef O_NONBLOCK
114 int v;
115 assert(fd >= 0);
116
117 if ((v = fcntl(fd, F_GETFL)) >= 0)
118 if (!(v & O_NONBLOCK))
119 fcntl(fd, F_SETFL, v|O_NONBLOCK);
120 #elif defined(OS_IS_WIN32)
121 u_long arg = 1;
122 if (ioctlsocket(fd, FIONBIO, &arg) < 0) {
123 if (WSAGetLastError() == WSAENOTSOCK)
124 pa_log_warn(__FILE__": WARNING: Only sockets can be made non-blocking!");
125 }
126 #else
127 pa_log_warn(__FILE__": WARNING: Non-blocking I/O not supported.!");
128 #endif
129 }
130
131 /** Creates a directory securely */
132 int pa_make_secure_dir(const char* dir) {
133 struct stat st;
134 assert(dir);
135
136 #ifdef OS_IS_WIN32
137 if (mkdir(dir) < 0)
138 #else
139 if (mkdir(dir, 0700) < 0)
140 #endif
141 if (errno != EEXIST)
142 return -1;
143
144 #ifdef HAVE_CHOWN
145 chown(dir, getuid(), getgid());
146 #endif
147 #ifdef HAVE_CHMOD
148 chmod(dir, 0700);
149 #endif
150
151 #ifdef HAVE_LSTAT
152 if (lstat(dir, &st) < 0)
153 #else
154 if (stat(dir, &st) < 0)
155 #endif
156 goto fail;
157
158 #ifndef OS_IS_WIN32
159 if (!S_ISDIR(st.st_mode) || (st.st_uid != getuid()) || ((st.st_mode & 0777) != 0700))
160 goto fail;
161 #else
162 fprintf(stderr, "FIXME: pa_make_secure_dir()\n");
163 #endif
164
165 return 0;
166
167 fail:
168 rmdir(dir);
169 return -1;
170 }
171
172 /* Return a newly allocated sting containing the parent directory of the specified file */
173 char *pa_parent_dir(const char *fn) {
174 char *slash, *dir = pa_xstrdup(fn);
175
176 slash = (char*) pa_path_get_filename(dir);
177 if (slash == fn)
178 return NULL;
179
180 *(slash-1) = 0;
181 return dir;
182 }
183
184 /* Creates a the parent directory of the specified path securely */
185 int pa_make_secure_parent_dir(const char *fn) {
186 int ret = -1;
187 char *dir;
188
189 if (!(dir = pa_parent_dir(fn)))
190 goto finish;
191
192 if (pa_make_secure_dir(dir) < 0)
193 goto finish;
194
195 ret = 0;
196
197 finish:
198 pa_xfree(dir);
199 return ret;
200 }
201
202 /** Platform independent read function. Necessary since not all systems
203 * treat all file descriptors equal. */
204 ssize_t pa_read(int fd, void *buf, size_t count) {
205 ssize_t r;
206
207 #ifdef OS_IS_WIN32
208 r = recv(fd, buf, count, 0);
209 if (r < 0) {
210 if (WSAGetLastError() != WSAENOTSOCK) {
211 errno = WSAGetLastError();
212 return r;
213 }
214 }
215
216 if (r < 0)
217 #endif
218 r = read(fd, buf, count);
219
220 return r;
221 }
222
223 /** Similar to pa_read(), but handles writes */
224 ssize_t pa_write(int fd, const void *buf, size_t count) {
225 ssize_t r;
226
227 #ifdef OS_IS_WIN32
228 r = send(fd, buf, count, 0);
229 if (r < 0) {
230 if (WSAGetLastError() != WSAENOTSOCK) {
231 errno = WSAGetLastError();
232 return r;
233 }
234 }
235
236 if (r < 0)
237 #endif
238 r = write(fd, buf, count);
239
240 return r;
241 }
242
243 /** Calls read() in a loop. Makes sure that as much as 'size' bytes,
244 * unless EOF is reached or an error occured */
245 ssize_t pa_loop_read(int fd, void*data, size_t size) {
246 ssize_t ret = 0;
247 assert(fd >= 0 && data && size);
248
249 while (size > 0) {
250 ssize_t r;
251
252 if ((r = pa_read(fd, data, size)) < 0)
253 return r;
254
255 if (r == 0)
256 break;
257
258 ret += r;
259 data = (uint8_t*) data + r;
260 size -= r;
261 }
262
263 return ret;
264 }
265
266 /** Similar to pa_loop_read(), but wraps write() */
267 ssize_t pa_loop_write(int fd, const void*data, size_t size) {
268 ssize_t ret = 0;
269 assert(fd >= 0 && data && size);
270
271 while (size > 0) {
272 ssize_t r;
273
274 if ((r = pa_write(fd, data, size)) < 0)
275 return r;
276
277 if (r == 0)
278 break;
279
280 ret += r;
281 data = (const uint8_t*) data + r;
282 size -= r;
283 }
284
285 return ret;
286 }
287
288 /* Print a warning messages in case that the given signal is not
289 * blocked or trapped */
290 void pa_check_signal_is_blocked(int sig) {
291 #ifdef HAVE_SIGACTION
292 struct sigaction sa;
293 sigset_t set;
294
295 /* If POSIX threads are supported use thread-aware
296 * pthread_sigmask() function, to check if the signal is
297 * blocked. Otherwise fall back to sigprocmask() */
298
299 #ifdef HAVE_PTHREAD
300 if (pthread_sigmask(SIG_SETMASK, NULL, &set) < 0) {
301 #endif
302 if (sigprocmask(SIG_SETMASK, NULL, &set) < 0) {
303 pa_log(__FILE__": sigprocmask() failed: %s", strerror(errno));
304 return;
305 }
306 #ifdef HAVE_PTHREAD
307 }
308 #endif
309
310 if (sigismember(&set, sig))
311 return;
312
313 /* Check whether the signal is trapped */
314
315 if (sigaction(sig, NULL, &sa) < 0) {
316 pa_log(__FILE__": sigaction() failed: %s", strerror(errno));
317 return;
318 }
319
320 if (sa.sa_handler != SIG_DFL)
321 return;
322
323 pa_log(__FILE__": WARNING: %s is not trapped. This might cause malfunction!", pa_strsignal(sig));
324 #else /* HAVE_SIGACTION */
325 pa_log(__FILE__": WARNING: %s might not be trapped. This might cause malfunction!", pa_strsignal(sig));
326 #endif
327 }
328
329 /* The following function is based on an example from the GNU libc
330 * documentation. This function is similar to GNU's asprintf(). */
331 char *pa_sprintf_malloc(const char *format, ...) {
332 int size = 100;
333 char *c = NULL;
334
335 assert(format);
336
337 for(;;) {
338 int r;
339 va_list ap;
340
341 c = pa_xrealloc(c, size);
342
343 va_start(ap, format);
344 r = vsnprintf(c, size, format, ap);
345 va_end(ap);
346
347 if (r > -1 && r < size)
348 return c;
349
350 if (r > -1) /* glibc 2.1 */
351 size = r+1;
352 else /* glibc 2.0 */
353 size *= 2;
354 }
355 }
356
357 /* Same as the previous function, but use a va_list instead of an
358 * ellipsis */
359 char *pa_vsprintf_malloc(const char *format, va_list ap) {
360 int size = 100;
361 char *c = NULL;
362
363 assert(format);
364
365 for(;;) {
366 int r;
367 c = pa_xrealloc(c, size);
368 r = vsnprintf(c, size, format, ap);
369
370 if (r > -1 && r < size)
371 return c;
372
373 if (r > -1) /* glibc 2.1 */
374 size = r+1;
375 else /* glibc 2.0 */
376 size *= 2;
377 }
378 }
379
380 /* Return the current username in the specified string buffer. */
381 char *pa_get_user_name(char *s, size_t l) {
382 char *p;
383 char buf[1024];
384
385 #ifdef HAVE_PWD_H
386 struct passwd pw, *r;
387 #endif
388
389 assert(s && l > 0);
390
391 if (!(p = getenv("USER")) && !(p = getenv("LOGNAME")) && !(p = getenv("USERNAME"))) {
392 #ifdef HAVE_PWD_H
393
394 #ifdef HAVE_GETPWUID_R
395 if (getpwuid_r(getuid(), &pw, buf, sizeof(buf), &r) != 0 || !r) {
396 #else
397 /* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X)
398 * that do not support getpwuid_r. */
399 if ((r = getpwuid(getuid())) == NULL) {
400 #endif
401 snprintf(s, l, "%lu", (unsigned long) getuid());
402 return s;
403 }
404
405 p = r->pw_name;
406
407 #elif defined(OS_IS_WIN32) /* HAVE_PWD_H */
408 DWORD size = sizeof(buf);
409
410 if (!GetUserName(buf, &size))
411 return NULL;
412
413 p = buf;
414
415 #else /* HAVE_PWD_H */
416 return NULL;
417 #endif /* HAVE_PWD_H */
418 }
419
420 return pa_strlcpy(s, p, l);
421 }
422
423 /* Return the current hostname in the specified buffer. */
424 char *pa_get_host_name(char *s, size_t l) {
425 assert(s && l > 0);
426 if (gethostname(s, l) < 0) {
427 pa_log(__FILE__": gethostname(): %s", strerror(errno));
428 return NULL;
429 }
430 s[l-1] = 0;
431 return s;
432 }
433
434 /* Return the home directory of the current user */
435 char *pa_get_home_dir(char *s, size_t l) {
436 char *e;
437
438 #ifdef HAVE_PWD_H
439 char buf[1024];
440 struct passwd pw, *r;
441 #endif
442
443 assert(s && l);
444
445 if ((e = getenv("HOME")))
446 return pa_strlcpy(s, e, l);
447
448 if ((e = getenv("USERPROFILE")))
449 return pa_strlcpy(s, e, l);
450
451 #ifdef HAVE_PWD_H
452 #ifdef HAVE_GETPWUID_R
453 if (getpwuid_r(getuid(), &pw, buf, sizeof(buf), &r) != 0 || !r) {
454 pa_log(__FILE__": getpwuid_r() failed");
455 #else
456 /* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X)
457 * that do not support getpwuid_r. */
458 if ((r = getpwuid(getuid())) == NULL) {
459 pa_log(__FILE__": getpwuid_r() failed");
460 #endif
461 return NULL;
462 }
463
464 return pa_strlcpy(s, r->pw_dir, l);
465 #else /* HAVE_PWD_H */
466 return NULL;
467 #endif
468 }
469
470 /* Similar to OpenBSD's strlcpy() function */
471 char *pa_strlcpy(char *b, const char *s, size_t l) {
472 assert(b && s && l > 0);
473
474 strncpy(b, s, l);
475 b[l-1] = 0;
476 return b;
477 }
478
479 struct timeval *pa_gettimeofday(struct timeval *tv) {
480 #ifdef HAVE_GETTIMEOFDAY
481 assert(tv);
482
483 return gettimeofday(tv, NULL) < 0 ? NULL : tv;
484 #elif defined(OS_IS_WIN32)
485 /*
486 * Copied from implementation by Steven Edwards (LGPL).
487 * Found on wine mailing list.
488 */
489
490 #if defined(_MSC_VER) || defined(__BORLANDC__)
491 #define EPOCHFILETIME (116444736000000000i64)
492 #else
493 #define EPOCHFILETIME (116444736000000000LL)
494 #endif
495
496 FILETIME ft;
497 LARGE_INTEGER li;
498 __int64 t;
499
500 assert(tv);
501
502 GetSystemTimeAsFileTime(&ft);
503 li.LowPart = ft.dwLowDateTime;
504 li.HighPart = ft.dwHighDateTime;
505 t = li.QuadPart; /* In 100-nanosecond intervals */
506 t -= EPOCHFILETIME; /* Offset to the Epoch time */
507 t /= 10; /* In microseconds */
508 tv->tv_sec = (long)(t / 1000000);
509 tv->tv_usec = (long)(t % 1000000);
510
511 return tv;
512 #else
513 #error "Platform lacks gettimeofday() or equivalent function."
514 #endif
515 }
516
517 /* Calculate the difference between the two specfified timeval
518 * timestamsps. */
519 pa_usec_t pa_timeval_diff(const struct timeval *a, const struct timeval *b) {
520 pa_usec_t r;
521 assert(a && b);
522
523 /* Check which whan is the earlier time and swap the two arguments if reuqired. */
524 if (pa_timeval_cmp(a, b) < 0) {
525 const struct timeval *c;
526 c = a;
527 a = b;
528 b = c;
529 }
530
531 /* Calculate the second difference*/
532 r = ((pa_usec_t) a->tv_sec - b->tv_sec)* 1000000;
533
534 /* Calculate the microsecond difference */
535 if (a->tv_usec > b->tv_usec)
536 r += ((pa_usec_t) a->tv_usec - b->tv_usec);
537 else if (a->tv_usec < b->tv_usec)
538 r -= ((pa_usec_t) b->tv_usec - a->tv_usec);
539
540 return r;
541 }
542
543 /* Compare the two timeval structs and return 0 when equal, negative when a < b, positive otherwse */
544 int pa_timeval_cmp(const struct timeval *a, const struct timeval *b) {
545 assert(a && b);
546
547 if (a->tv_sec < b->tv_sec)
548 return -1;
549
550 if (a->tv_sec > b->tv_sec)
551 return 1;
552
553 if (a->tv_usec < b->tv_usec)
554 return -1;
555
556 if (a->tv_usec > b->tv_usec)
557 return 1;
558
559 return 0;
560 }
561
562 /* Return the time difference between now and the specified timestamp */
563 pa_usec_t pa_timeval_age(const struct timeval *tv) {
564 struct timeval now;
565 assert(tv);
566
567 return pa_timeval_diff(pa_gettimeofday(&now), tv);
568 }
569
570 /* Add the specified time inmicroseconds to the specified timeval structure */
571 void pa_timeval_add(struct timeval *tv, pa_usec_t v) {
572 unsigned long secs;
573 assert(tv);
574
575 secs = (v/1000000);
576 tv->tv_sec += (unsigned long) secs;
577 v -= secs*1000000;
578
579 tv->tv_usec += v;
580
581 /* Normalize */
582 while (tv->tv_usec >= 1000000) {
583 tv->tv_sec++;
584 tv->tv_usec -= 1000000;
585 }
586 }
587
588 #define NICE_LEVEL (-15)
589
590 /* Raise the priority of the current process as much as possible and
591 sensible: set the nice level to -15 and enable realtime scheduling if
592 supported.*/
593 void pa_raise_priority(void) {
594
595 #ifdef HAVE_SYS_RESOURCE_H
596 if (setpriority(PRIO_PROCESS, 0, NICE_LEVEL) < 0)
597 pa_log_warn(__FILE__": setpriority() failed: %s", strerror(errno));
598 else
599 pa_log_info(__FILE__": Successfully gained nice level %i.", NICE_LEVEL);
600 #endif
601
602 #ifdef _POSIX_PRIORITY_SCHEDULING
603 {
604 struct sched_param sp;
605
606 if (sched_getparam(0, &sp) < 0) {
607 pa_log(__FILE__": sched_getparam() failed: %s", strerror(errno));
608 return;
609 }
610
611 sp.sched_priority = 1;
612 if (sched_setscheduler(0, SCHED_FIFO, &sp) < 0) {
613 pa_log_warn(__FILE__": sched_setscheduler() failed: %s", strerror(errno));
614 return;
615 }
616
617 pa_log_info(__FILE__": Successfully enabled SCHED_FIFO scheduling.");
618 }
619 #endif
620
621 #ifdef OS_IS_WIN32
622 if (!SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS))
623 pa_log_warn(__FILE__": SetPriorityClass() failed: 0x%08X", GetLastError());
624 else
625 pa_log_info(__FILE__": Successfully gained high priority class.");
626 #endif
627 }
628
629 /* Reset the priority to normal, inverting the changes made by pa_raise_priority() */
630 void pa_reset_priority(void) {
631 #ifdef OS_IS_WIN32
632 SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
633 #endif
634
635 #ifdef _POSIX_PRIORITY_SCHEDULING
636 {
637 struct sched_param sp;
638 sched_getparam(0, &sp);
639 sp.sched_priority = 0;
640 sched_setscheduler(0, SCHED_OTHER, &sp);
641 }
642 #endif
643
644 #ifdef HAVE_SYS_RESOURCE_H
645 setpriority(PRIO_PROCESS, 0, 0);
646 #endif
647 }
648
649 /* Set the FD_CLOEXEC flag for a fd */
650 int pa_fd_set_cloexec(int fd, int b) {
651
652 #ifdef FD_CLOEXEC
653 int v;
654 assert(fd >= 0);
655
656 if ((v = fcntl(fd, F_GETFD, 0)) < 0)
657 return -1;
658
659 v = (v & ~FD_CLOEXEC) | (b ? FD_CLOEXEC : 0);
660
661 if (fcntl(fd, F_SETFD, v) < 0)
662 return -1;
663 #endif
664
665 return 0;
666 }
667
668 /* Return the binary file name of the current process. Works on Linux
669 * only. This shoul be used for eyecandy only, don't rely on return
670 * non-NULL! */
671 char *pa_get_binary_name(char *s, size_t l) {
672
673 #ifdef HAVE_READLINK
674 char path[PATH_MAX];
675 int i;
676 assert(s && l);
677
678 /* This works on Linux only */
679
680 snprintf(path, sizeof(path), "/proc/%u/exe", (unsigned) getpid());
681 if ((i = readlink(path, s, l-1)) < 0)
682 return NULL;
683
684 s[i] = 0;
685 return s;
686 #elif defined(OS_IS_WIN32)
687 char path[PATH_MAX];
688 if (!GetModuleFileName(NULL, path, PATH_MAX))
689 return NULL;
690 pa_strlcpy(s, pa_path_get_filename(path), l);
691 return s;
692 #else
693 return NULL;
694 #endif
695 }
696
697 /* Return a pointer to the filename inside a path (which is the last
698 * component). */
699 const char *pa_path_get_filename(const char *p) {
700 char *fn;
701
702 if ((fn = strrchr(p, PATH_SEP)))
703 return fn+1;
704
705 return (const char*) p;
706 }
707
708 /* Try to parse a boolean string value.*/
709 int pa_parse_boolean(const char *v) {
710
711 if (!strcmp(v, "1") || v[0] == 'y' || v[0] == 'Y' || v[0] == 't' || v[0] == 'T' || !strcasecmp(v, "on"))
712 return 1;
713 else if (!strcmp(v, "0") || v[0] == 'n' || v[0] == 'N' || v[0] == 'f' || v[0] == 'F' || !strcasecmp(v, "off"))
714 return 0;
715
716 return -1;
717 }
718
719 /* Split the specified string wherever one of the strings in delimiter
720 * occurs. Each time it is called returns a newly allocated string
721 * with pa_xmalloc(). The variable state points to, should be
722 * initiallized to NULL before the first call. */
723 char *pa_split(const char *c, const char *delimiter, const char**state) {
724 const char *current = *state ? *state : c;
725 size_t l;
726
727 if (!*current)
728 return NULL;
729
730 l = strcspn(current, delimiter);
731 *state = current+l;
732
733 if (**state)
734 (*state)++;
735
736 return pa_xstrndup(current, l);
737 }
738
739 /* What is interpreted as whitespace? */
740 #define WHITESPACE " \t\n"
741
742 /* Split a string into words. Otherwise similar to pa_split(). */
743 char *pa_split_spaces(const char *c, const char **state) {
744 const char *current = *state ? *state : c;
745 size_t l;
746
747 if (!*current || *c == 0)
748 return NULL;
749
750 current += strspn(current, WHITESPACE);
751 l = strcspn(current, WHITESPACE);
752
753 *state = current+l;
754
755 return pa_xstrndup(current, l);
756 }
757
758 /* Return the name of an UNIX signal. Similar to GNU's strsignal() */
759 const char *pa_strsignal(int sig) {
760 switch(sig) {
761 case SIGINT: return "SIGINT";
762 case SIGTERM: return "SIGTERM";
763 #ifdef SIGUSR1
764 case SIGUSR1: return "SIGUSR1";
765 #endif
766 #ifdef SIGUSR2
767 case SIGUSR2: return "SIGUSR2";
768 #endif
769 #ifdef SIGXCPU
770 case SIGXCPU: return "SIGXCPU";
771 #endif
772 #ifdef SIGPIPE
773 case SIGPIPE: return "SIGPIPE";
774 #endif
775 #ifdef SIGCHLD
776 case SIGCHLD: return "SIGCHLD";
777 #endif
778 #ifdef SIGHUP
779 case SIGHUP: return "SIGHUP";
780 #endif
781 default: return "UNKNOWN SIGNAL";
782 }
783 }
784
785 #ifdef HAVE_GRP_H
786
787 /* Check whether the specified GID and the group name match */
788 static int is_group(gid_t gid, const char *name) {
789 struct group group, *result = NULL;
790 long n;
791 void *data;
792 int r = -1;
793
794 #ifdef HAVE_GETGRGID_R
795 #ifdef _SC_GETGR_R_SIZE_MAX
796 n = sysconf(_SC_GETGR_R_SIZE_MAX);
797 #else
798 n = -1;
799 #endif
800 if (n < 0) n = 512;
801 data = pa_xmalloc(n);
802
803 if (getgrgid_r(gid, &group, data, n, &result) < 0 || !result) {
804 pa_log(__FILE__ ": getgrgid_r(%u) failed: %s", gid, strerror(errno));
805 goto finish;
806 }
807
808 r = strcmp(name, result->gr_name) == 0;
809
810 finish:
811 pa_xfree(data);
812 #else
813 /* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X) that do not
814 * support getgrgid_r. */
815 if ((result = getgrgid(gid)) == NULL) {
816 pa_log(__FILE__ ": getgrgid(%u) failed: %s", gid, strerror(errno));
817 goto finish;
818 }
819
820 r = strcmp(name, result->gr_name) == 0;
821
822 finish:
823 #endif
824
825 return r;
826 }
827
828 /* Check the current user is member of the specified group */
829 int pa_own_uid_in_group(const char *name, gid_t *gid) {
830 GETGROUPS_T *gids, tgid;
831 int n = sysconf(_SC_NGROUPS_MAX);
832 int r = -1, i;
833
834 assert(n > 0);
835
836 gids = pa_xmalloc(sizeof(GETGROUPS_T)*n);
837
838 if ((n = getgroups(n, gids)) < 0) {
839 pa_log(__FILE__": getgroups() failed: %s", strerror(errno));
840 goto finish;
841 }
842
843 for (i = 0; i < n; i++) {
844 if (is_group(gids[i], name) > 0) {
845 *gid = gids[i];
846 r = 1;
847 goto finish;
848 }
849 }
850
851 if (is_group(tgid = getgid(), name) > 0) {
852 *gid = tgid;
853 r = 1;
854 goto finish;
855 }
856
857 r = 0;
858
859 finish:
860
861 pa_xfree(gids);
862 return r;
863 }
864
865 int pa_uid_in_group(uid_t uid, const char *name) {
866 char *g_buf, *p_buf;
867 long g_n, p_n;
868 struct group grbuf, *gr;
869 char **i;
870 int r = -1;
871
872 g_n = sysconf(_SC_GETGR_R_SIZE_MAX);
873 g_buf = pa_xmalloc(g_n);
874
875 p_n = sysconf(_SC_GETPW_R_SIZE_MAX);
876 p_buf = pa_xmalloc(p_n);
877
878 if (getgrnam_r(name, &grbuf, g_buf, (size_t) g_n, &gr) != 0 || !gr)
879 goto finish;
880
881 r = 0;
882 for (i = gr->gr_mem; *i; i++) {
883 struct passwd pwbuf, *pw;
884
885 if (getpwnam_r(*i, &pwbuf, p_buf, (size_t) p_n, &pw) != 0 || !pw)
886 continue;
887
888 if (pw->pw_uid == uid) {
889 r = 1;
890 break;
891 }
892 }
893
894 finish:
895 pa_xfree(g_buf);
896 pa_xfree(p_buf);
897
898 return r;
899 }
900
901 #else /* HAVE_GRP_H */
902
903 int pa_own_uid_in_group(const char *name, gid_t *gid) {
904 return -1;
905
906 }
907
908 int pa_uid_in_group(uid_t uid, const char *name) {
909 return -1;
910 }
911
912 #endif
913
914 /* Lock or unlock a file entirely.
915 (advisory on UNIX, mandatory on Windows) */
916 int pa_lock_fd(int fd, int b) {
917 #ifdef F_SETLKW
918 struct flock flock;
919
920 /* Try a R/W lock first */
921
922 flock.l_type = b ? F_WRLCK : F_UNLCK;
923 flock.l_whence = SEEK_SET;
924 flock.l_start = 0;
925 flock.l_len = 0;
926
927 if (fcntl(fd, F_SETLKW, &flock) >= 0)
928 return 0;
929
930 /* Perhaps the file descriptor qas opened for read only, than try again with a read lock. */
931 if (b && errno == EBADF) {
932 flock.l_type = F_RDLCK;
933 if (fcntl(fd, F_SETLKW, &flock) >= 0)
934 return 0;
935 }
936
937 pa_log(__FILE__": %slock failed: %s", !b ? "un" : "", strerror(errno));
938 #endif
939
940 #ifdef OS_IS_WIN32
941 HANDLE h = (HANDLE)_get_osfhandle(fd);
942
943 if (b && LockFile(h, 0, 0, 0xFFFFFFFF, 0xFFFFFFFF))
944 return 0;
945 if (!b && UnlockFile(h, 0, 0, 0xFFFFFFFF, 0xFFFFFFFF))
946 return 0;
947
948 pa_log(__FILE__": %slock failed: 0x%08X", !b ? "un" : "", GetLastError());
949 #endif
950
951 return -1;
952 }
953
954 /* Remove trailing newlines from a string */
955 char* pa_strip_nl(char *s) {
956 assert(s);
957
958 s[strcspn(s, "\r\n")] = 0;
959 return s;
960 }
961
962 /* Create a temporary lock file and lock it. */
963 int pa_lock_lockfile(const char *fn) {
964 int fd = -1;
965 assert(fn);
966
967 for (;;) {
968 struct stat st;
969
970 if ((fd = open(fn, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR)) < 0) {
971 pa_log(__FILE__": failed to create lock file '%s': %s", fn, strerror(errno));
972 goto fail;
973 }
974
975 if (pa_lock_fd(fd, 1) < 0) {
976 pa_log(__FILE__": failed to lock file '%s'.", fn);
977 goto fail;
978 }
979
980 if (fstat(fd, &st) < 0) {
981 pa_log(__FILE__": failed to fstat() file '%s'.", fn);
982 goto fail;
983 }
984
985 /* Check wheter the file has been removed meanwhile. When yes, restart this loop, otherwise, we're done */
986 if (st.st_nlink >= 1)
987 break;
988
989 if (pa_lock_fd(fd, 0) < 0) {
990 pa_log(__FILE__": failed to unlock file '%s'.", fn);
991 goto fail;
992 }
993
994 if (close(fd) < 0) {
995 pa_log(__FILE__": failed to close file '%s'.", fn);
996 goto fail;
997 }
998
999 fd = -1;
1000 }
1001
1002 return fd;
1003
1004 fail:
1005
1006 if (fd >= 0)
1007 close(fd);
1008
1009 return -1;
1010 }
1011
1012 /* Unlock a temporary lcok file */
1013 int pa_unlock_lockfile(const char *fn, int fd) {
1014 int r = 0;
1015 assert(fn && fd >= 0);
1016
1017 if (unlink(fn) < 0) {
1018 pa_log_warn(__FILE__": WARNING: unable to remove lock file '%s': %s", fn, strerror(errno));
1019 r = -1;
1020 }
1021
1022 if (pa_lock_fd(fd, 0) < 0) {
1023 pa_log_warn(__FILE__": WARNING: failed to unlock file '%s'.", fn);
1024 r = -1;
1025 }
1026
1027 if (close(fd) < 0) {
1028 pa_log_warn(__FILE__": WARNING: failed to close lock file '%s': %s", fn, strerror(errno));
1029 r = -1;
1030 }
1031
1032 return r;
1033 }
1034
1035 /* Try to open a configuration file. If "env" is specified, open the
1036 * value of the specified environment variable. Otherwise look for a
1037 * file "local" in the home directory or a file "global" in global
1038 * file system. If "result" is non-NULL, a pointer to a newly
1039 * allocated buffer containing the used configuration file is
1040 * stored there.*/
1041 FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result, const char *mode) {
1042 const char *fn;
1043 char h[PATH_MAX];
1044
1045 #ifdef OS_IS_WIN32
1046 char buf[PATH_MAX];
1047
1048 if (!getenv(POLYP_ROOTENV))
1049 pa_set_root(NULL);
1050 #endif
1051
1052 if (env && (fn = getenv(env))) {
1053 #ifdef OS_IS_WIN32
1054 if (!ExpandEnvironmentStrings(fn, buf, PATH_MAX))
1055 return NULL;
1056 fn = buf;
1057 #endif
1058
1059 if (result)
1060 *result = pa_xstrdup(fn);
1061
1062 return fopen(fn, mode);
1063 }
1064
1065 if (local && pa_get_home_dir(h, sizeof(h))) {
1066 FILE *f;
1067 char *lfn;
1068
1069 fn = lfn = pa_sprintf_malloc("%s/%s", h, local);
1070
1071 #ifdef OS_IS_WIN32
1072 if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX))
1073 return NULL;
1074 fn = buf;
1075 #endif
1076
1077 f = fopen(fn, mode);
1078
1079 if (f || errno != ENOENT) {
1080 if (result)
1081 *result = pa_xstrdup(fn);
1082 pa_xfree(lfn);
1083 return f;
1084 }
1085
1086 pa_xfree(lfn);
1087 }
1088
1089 if (!global) {
1090 if (result)
1091 *result = NULL;
1092 errno = ENOENT;
1093 return NULL;
1094 }
1095
1096 #ifdef OS_IS_WIN32
1097 if (!ExpandEnvironmentStrings(global, buf, PATH_MAX))
1098 return NULL;
1099 global = buf;
1100 #endif
1101
1102 if (result)
1103 *result = pa_xstrdup(global);
1104
1105 return fopen(global, mode);
1106 }
1107
1108 /* Format the specified data as a hexademical string */
1109 char *pa_hexstr(const uint8_t* d, size_t dlength, char *s, size_t slength) {
1110 size_t i = 0, j = 0;
1111 const char hex[] = "0123456789abcdef";
1112 assert(d && s && slength > 0);
1113
1114 while (i < dlength && j+3 <= slength) {
1115 s[j++] = hex[*d >> 4];
1116 s[j++] = hex[*d & 0xF];
1117
1118 d++;
1119 i++;
1120 }
1121
1122 s[j < slength ? j : slength] = 0;
1123 return s;
1124 }
1125
1126 /* Convert a hexadecimal digit to a number or -1 if invalid */
1127 static int hexc(char c) {
1128 if (c >= '0' && c <= '9')
1129 return c - '0';
1130
1131 if (c >= 'A' && c <= 'F')
1132 return c - 'A' + 10;
1133
1134 if (c >= 'a' && c <= 'f')
1135 return c - 'a' + 10;
1136
1137 return -1;
1138 }
1139
1140 /* Parse a hexadecimal string as created by pa_hexstr() to a BLOB */
1141 size_t pa_parsehex(const char *p, uint8_t *d, size_t dlength) {
1142 size_t j = 0;
1143 assert(p && d);
1144
1145 while (j < dlength && *p) {
1146 int b;
1147
1148 if ((b = hexc(*(p++))) < 0)
1149 return (size_t) -1;
1150
1151 d[j] = (uint8_t) (b << 4);
1152
1153 if (!*p)
1154 return (size_t) -1;
1155
1156 if ((b = hexc(*(p++))) < 0)
1157 return (size_t) -1;
1158
1159 d[j] |= (uint8_t) b;
1160 j++;
1161 }
1162
1163 return j;
1164 }
1165
1166 /* Return the fully qualified domain name in *s */
1167 char *pa_get_fqdn(char *s, size_t l) {
1168 char hn[256];
1169 #ifdef HAVE_GETADDRINFO
1170 struct addrinfo *a, hints;
1171 #endif
1172
1173 if (!pa_get_host_name(hn, sizeof(hn)))
1174 return NULL;
1175
1176 #ifdef HAVE_GETADDRINFO
1177 memset(&hints, 0, sizeof(hints));
1178 hints.ai_family = AF_UNSPEC;
1179 hints.ai_flags = AI_CANONNAME;
1180
1181 if (getaddrinfo(hn, NULL, &hints, &a) < 0 || !a || !a->ai_canonname || !*a->ai_canonname)
1182 return pa_strlcpy(s, hn, l);
1183
1184 pa_strlcpy(s, a->ai_canonname, l);
1185 freeaddrinfo(a);
1186 return s;
1187 #else
1188 return pa_strlcpy(s, hn, l);
1189 #endif
1190 }
1191
1192 /* Returns nonzero when *s starts with *pfx */
1193 int pa_startswith(const char *s, const char *pfx) {
1194 size_t l;
1195
1196 assert(s);
1197 assert(pfx);
1198
1199 l = strlen(pfx);
1200
1201 return strlen(s) >= l && strncmp(s, pfx, l) == 0;
1202 }
1203
1204 /* Returns nonzero when *s ends with *sfx */
1205 int pa_endswith(const char *s, const char *sfx) {
1206 size_t l1, l2;
1207
1208 assert(s);
1209 assert(sfx);
1210
1211 l1 = strlen(s);
1212 l2 = strlen(sfx);
1213
1214 return l1 >= l2 && strcmp(s+l1-l2, sfx) == 0;
1215 }
1216
1217 /* if fn is null return the polypaudio run time path in s (/tmp/polypaudio)
1218 * if fn is non-null and starts with / return fn in s
1219 * otherwise append fn to the run time path and return it in s */
1220 char *pa_runtime_path(const char *fn, char *s, size_t l) {
1221 char u[256];
1222
1223 #ifndef OS_IS_WIN32
1224 if (fn && *fn == '/')
1225 #else
1226 if (fn && strlen(fn) >= 3 && isalpha(fn[0]) && fn[1] == ':' && fn[2] == '\\')
1227 #endif
1228 return pa_strlcpy(s, fn, l);
1229
1230 if (fn)
1231 snprintf(s, l, "%s%s%c%s", PA_RUNTIME_PATH_PREFIX, pa_get_user_name(u, sizeof(u)), PATH_SEP, fn);
1232 else
1233 snprintf(s, l, "%s%s", PA_RUNTIME_PATH_PREFIX, pa_get_user_name(u, sizeof(u)));
1234
1235 #ifdef OS_IS_WIN32
1236 {
1237 char buf[l];
1238 strcpy(buf, s);
1239 ExpandEnvironmentStrings(buf, s, l);
1240 }
1241 #endif
1242
1243 return s;
1244 }
1245
1246 /* Wait t milliseconds */
1247 int pa_msleep(unsigned long t) {
1248 #ifdef OS_IS_WIN32
1249 Sleep(t);
1250 return 0;
1251 #elif defined(HAVE_NANOSLEEP)
1252 struct timespec ts;
1253
1254 ts.tv_sec = t/1000;
1255 ts.tv_nsec = (t % 1000) * 1000000;
1256
1257 return nanosleep(&ts, NULL);
1258 #else
1259 #error "Platform lacks a sleep function."
1260 #endif
1261 }
1262
1263 /* Convert the string s to a signed integer in *ret_i */
1264 int pa_atoi(const char *s, int32_t *ret_i) {
1265 char *x = NULL;
1266 long l;
1267 assert(s && ret_i);
1268
1269 l = strtol(s, &x, 0);
1270
1271 if (!x || *x)
1272 return -1;
1273
1274 *ret_i = (int32_t) l;
1275
1276 return 0;
1277 }
1278
1279 /* Convert the string s to an unsigned integer in *ret_u */
1280 int pa_atou(const char *s, uint32_t *ret_u) {
1281 char *x = NULL;
1282 unsigned long l;
1283 assert(s && ret_u);
1284
1285 l = strtoul(s, &x, 0);
1286
1287 if (!x || *x)
1288 return -1;
1289
1290 *ret_u = (uint32_t) l;
1291
1292 return 0;
1293 }