Apply Joe Marcus Clarke's FreeBSD patches
[profile/ivi/pulseaudio-panda.git] / polyp / 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 General Public License as published
8   by the Free Software Foundation; either version 2 of the License,
9   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   General Public License for more details.
15  
16   You should have received a copy of the GNU General Public License
17   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 <sys/types.h>
36 #include <sys/stat.h>
37 #include <pwd.h>
38 #include <signal.h>
39 #include <pthread.h>
40 #include <sys/time.h>
41 #include <sched.h>
42 #include <sys/resource.h>
43 #include <limits.h>
44 #include <unistd.h>
45 #include <grp.h>
46
47 #include <samplerate.h>
48
49 #include "util.h"
50 #include "xmalloc.h"
51 #include "log.h"
52
53 void pa_make_nonblock_fd(int fd) {
54     int v;
55
56     if ((v = fcntl(fd, F_GETFL)) >= 0)
57         if (!(v & O_NONBLOCK))
58             fcntl(fd, F_SETFL, v|O_NONBLOCK);
59 }
60
61 int pa_make_secure_dir(const char* dir) {
62     struct stat st;
63
64     if (mkdir(dir, 0700) < 0) 
65         if (errno != EEXIST)
66             return -1;
67     
68     if (lstat(dir, &st) < 0) 
69         goto fail;
70     
71     if (!S_ISDIR(st.st_mode) || (st.st_uid != getuid()) || ((st.st_mode & 0777) != 0700))
72         goto fail;
73     
74     return 0;
75     
76 fail:
77     rmdir(dir);
78     return -1;
79 }
80
81 ssize_t pa_loop_read(int fd, void*data, size_t size) {
82     ssize_t ret = 0;
83     assert(fd >= 0 && data && size);
84
85     while (size > 0) {
86         ssize_t r;
87
88         if ((r = read(fd, data, size)) < 0)
89             return r;
90
91         if (r == 0)
92             break;
93         
94         ret += r;
95         data = (uint8_t*) data + r;
96         size -= r;
97     }
98
99     return ret;
100 }
101
102 ssize_t pa_loop_write(int fd, const void*data, size_t size) {
103     ssize_t ret = 0;
104     assert(fd >= 0 && data && size);
105
106     while (size > 0) {
107         ssize_t r;
108
109         if ((r = write(fd, data, size)) < 0)
110             return r;
111
112         if (r == 0)
113             break;
114         
115         ret += r;
116         data = (uint8_t*) data + r;
117         size -= r;
118     }
119
120     return ret;
121 }
122
123 void pa_check_signal_is_blocked(int sig) {
124     struct sigaction sa;
125     sigset_t set;
126
127 #ifdef HAVE_PTHREAD    
128     if (pthread_sigmask(SIG_SETMASK, NULL, &set) < 0) {
129 #endif
130         if (sigprocmask(SIG_SETMASK, NULL, &set) < 0) {
131             pa_log(__FILE__": sigprocmask() failed: %s\n", strerror(errno));
132             return;
133         }
134 #ifdef HAVE_PTHREAD
135     }
136 #endif
137
138     if (sigismember(&set, sig))
139         return;
140     
141     if (sigaction(sig, NULL, &sa) < 0) {
142         pa_log(__FILE__": sigaction() failed: %s\n", strerror(errno));
143         return;
144     }
145         
146     if (sa.sa_handler != SIG_DFL)
147         return;
148     
149     pa_log(__FILE__": WARNING: %s is not trapped. This might cause malfunction!\n", pa_strsignal(sig));
150 }
151
152 /* The following is based on an example from the GNU libc documentation */
153 char *pa_sprintf_malloc(const char *format, ...) {
154     int  size = 100;
155     char *c = NULL;
156     
157     assert(format);
158     
159     for(;;) {
160         int r;
161         va_list ap;
162
163         c = pa_xrealloc(c, size);
164
165         va_start(ap, format);
166         r = vsnprintf(c, size, format, ap);
167         va_end(ap);
168         
169         if (r > -1 && r < size)
170             return c;
171
172         if (r > -1)    /* glibc 2.1 */
173             size = r+1; 
174         else           /* glibc 2.0 */
175             size *= 2;
176     }
177 }
178
179 char *pa_vsprintf_malloc(const char *format, va_list ap) {
180     int  size = 100;
181     char *c = NULL;
182     
183     assert(format);
184     
185     for(;;) {
186         int r;
187         va_list ap;
188
189         c = pa_xrealloc(c, size);
190         r = vsnprintf(c, size, format, ap);
191         
192         if (r > -1 && r < size)
193             return c;
194
195         if (r > -1)    /* glibc 2.1 */
196             size = r+1; 
197         else           /* glibc 2.0 */
198             size *= 2;
199     }
200 }
201
202
203 char *pa_get_user_name(char *s, size_t l) {
204     struct passwd pw, *r;
205     char buf[1024];
206     char *p;
207
208     if (!(p = getenv("USER")))
209         if (!(p = getenv("LOGNAME")))
210             if (!(p = getenv("USERNAME"))) {
211                 
212 #ifdef HAVE_GETPWUID_R
213                 if (getpwuid_r(getuid(), &pw, buf, sizeof(buf), &r) != 0 || !r) {
214 #else
215                 /* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X)
216                  * that do not support getpwuid_r. */
217                 if ((r = getpwuid(getuid())) == NULL) {
218 #endif
219                     snprintf(s, l, "%lu", (unsigned long) getuid());
220                     return s;
221                 }
222                 
223                 p = r->pw_name;
224             }
225     
226     snprintf(s, l, "%s", p);
227     return s;
228 }
229
230 char *pa_get_host_name(char *s, size_t l) {
231     gethostname(s, l);
232     s[l-1] = 0;
233     return s;
234 }
235
236 pa_usec_t pa_timeval_diff(const struct timeval *a, const struct timeval *b) {
237     pa_usec_t r;
238     assert(a && b);
239
240     if (pa_timeval_cmp(a, b) < 0) {
241         const struct timeval *c;
242         c = a;
243         a = b;
244         b = c;
245     }
246
247     r = ((pa_usec_t) a->tv_sec - b->tv_sec)* 1000000;
248
249     if (a->tv_usec > b->tv_usec)
250         r += ((pa_usec_t) a->tv_usec - b->tv_usec);
251     else if (a->tv_usec < b->tv_usec)
252         r -= ((pa_usec_t) b->tv_usec - a->tv_usec);
253
254     return r;
255 }
256
257 int pa_timeval_cmp(const struct timeval *a, const struct timeval *b) {
258     assert(a && b);
259
260     if (a->tv_sec < b->tv_sec)
261         return -1;
262
263     if (a->tv_sec > b->tv_sec)
264         return 1;
265
266     if (a->tv_usec < b->tv_usec)
267         return -1;
268
269     if (a->tv_usec > b->tv_usec)
270         return 1;
271
272     return 0;
273 }
274
275 pa_usec_t pa_timeval_age(const struct timeval *tv) {
276     struct timeval now;
277     assert(tv);
278     gettimeofday(&now, NULL);
279     return pa_timeval_diff(&now, tv);
280 }
281
282 void pa_timeval_add(struct timeval *tv, pa_usec_t v) {
283     unsigned long secs;
284     assert(tv);
285     
286     secs = (v/1000000);
287     tv->tv_sec += (unsigned long) secs;
288     v -= secs*1000000;
289
290     tv->tv_usec += v;
291
292     while (tv->tv_usec >= 1000000) {
293         tv->tv_sec++;
294         tv->tv_usec -= 1000000;
295     }
296 }
297
298 #define NICE_LEVEL (-15)
299
300 void pa_raise_priority(void) {
301     if (setpriority(PRIO_PROCESS, 0, NICE_LEVEL) < 0)
302         pa_log(__FILE__": setpriority() failed: %s\n", strerror(errno));
303     else
304         pa_log(__FILE__": Successfully gained nice level %i.\n", NICE_LEVEL);
305
306 #ifdef _POSIX_PRIORITY_SCHEDULING
307     {
308         struct sched_param sp;
309
310         if (sched_getparam(0, &sp) < 0) {
311             pa_log(__FILE__": sched_getparam() failed: %s\n", strerror(errno));
312             return;
313         }
314         
315         sp.sched_priority = 1;
316         if (sched_setscheduler(0, SCHED_FIFO, &sp) < 0) {
317             pa_log(__FILE__": sched_setscheduler() failed: %s\n", strerror(errno));
318             return;
319         }
320
321         pa_log(__FILE__": Successfully enabled SCHED_FIFO scheduling.\n");
322     }
323 #endif
324 }
325
326 void pa_reset_priority(void) {
327 #ifdef _POSIX_PRIORITY_SCHEDULING
328     {
329         struct sched_param sp;
330         sched_getparam(0, &sp);
331         sp.sched_priority = 0;
332         sched_setscheduler(0, SCHED_OTHER, &sp);
333     }
334 #endif
335
336     setpriority(PRIO_PROCESS, 0, 0);
337 }
338
339 int pa_fd_set_cloexec(int fd, int b) {
340     int v;
341     assert(fd >= 0);
342
343     if ((v = fcntl(fd, F_GETFD, 0)) < 0)
344         return -1;
345     
346     v = (v & ~FD_CLOEXEC) | (b ? FD_CLOEXEC : 0);
347     
348     if (fcntl(fd, F_SETFD, v) < 0)
349         return -1;
350     
351     return 0;
352 }
353
354 char *pa_get_binary_name(char *s, size_t l) {
355     char path[PATH_MAX];
356     int i;
357     assert(s && l);
358
359     /* This works on Linux only */
360     
361     snprintf(path, sizeof(path), "/proc/%u/exe", (unsigned) getpid());
362     if ((i = readlink(path, s, l-1)) < 0)
363         return NULL;
364
365     s[i] = 0;
366     return s;
367 }
368
369 char *pa_path_get_filename(const char *p) {
370     char *fn;
371
372     if ((fn = strrchr(p, '/')))
373         return fn+1;
374
375     return (char*) p;
376 }
377
378 int pa_parse_boolean(const char *v) {
379     
380     if (!strcmp(v, "1") || v[0] == 'y' || v[0] == 'Y' || v[0] == 't' || v[0] == 'T' || !strcasecmp(v, "on"))
381         return 1;
382     else if (!strcmp(v, "0") || v[0] == 'n' || v[0] == 'N' || v[0] == 'f' || v[0] == 'F' || !strcasecmp(v, "off"))
383         return 0;
384
385     return -1;
386 }
387
388 char *pa_split(const char *c, const char *delimiter, const char**state) {
389     const char *current = *state ? *state : c;
390     size_t l;
391
392     if (!*current)
393         return NULL;
394     
395     l = strcspn(current, delimiter);
396     *state = current+l;
397
398     if (**state)
399         (*state)++;
400
401     return pa_xstrndup(current, l);
402 }
403
404 #define WHITESPACE " \t\n"
405
406 char *pa_split_spaces(const char *c, const char **state) {
407     const char *current = *state ? *state : c;
408     size_t l;
409
410     if (!*current)
411         return NULL;
412
413     current += strspn(current, WHITESPACE);
414     l = strcspn(current, WHITESPACE);
415
416     *state = current+l;
417
418     return pa_xstrndup(current, l);
419 }
420
421 const char *pa_strsignal(int sig) {
422     switch(sig) {
423         case SIGINT: return "SIGINT";
424         case SIGTERM: return "SIGTERM";
425         case SIGUSR1: return "SIGUSR1";
426         case SIGUSR2: return "SIGUSR2";
427         case SIGXCPU: return "SIGXCPU";
428         case SIGPIPE: return "SIGPIPE";
429         case SIGCHLD: return "SIGCHLD";
430         case SIGHUP: return "SIGHUP";
431         default: return "UNKNOWN SIGNAL";
432     }
433 }
434
435 int pa_parse_resample_method(const char *string) {
436     assert(string);
437
438     if (!strcmp(string, "sinc-best-quality"))
439         return SRC_SINC_BEST_QUALITY;
440     else if (!strcmp(string, "sinc-medium-quality"))
441         return SRC_SINC_MEDIUM_QUALITY;
442     else if (!strcmp(string, "sinc-fastest"))
443         return SRC_SINC_FASTEST;
444     else if (!strcmp(string, "zero-order-hold"))
445         return SRC_ZERO_ORDER_HOLD;
446     else if (!strcmp(string, "linear"))
447         return SRC_LINEAR;
448     else
449         return -1;
450 }
451
452 static int is_group(gid_t gid, const char *name) {
453     struct group group, *result = NULL;
454     long n;
455     void *data;
456     int r = -1;
457
458 #ifdef HAVE_GETGRGID_R
459 #ifdef _SC_GETGR_R_SIZE_MAX
460     n = sysconf(_SC_GETGR_R_SIZE_MAX);
461 #else
462     n = -1;
463 #endif
464     if (n < 0) n = 512;
465     data = pa_xmalloc(n);
466
467     if (getgrgid_r(gid, &group, data, n, &result) < 0 || !result) {
468         pa_log(__FILE__ ": getgrgid_r(%u) failed: %s\n", gid, strerror(errno));
469         goto finish;
470     }
471
472     
473     r = strcmp(name, result->gr_name) == 0;
474     
475 finish:
476     pa_xfree(data);
477 #else
478     /* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X) that do not
479      * support getgrgid_r. */
480     if ((result = getgrgid(gid)) == NULL) {
481         pa_log(__FILE__ ": getgrgid(%u) failed: %s\n", gid, strerror(errno));
482         goto finish;
483     }
484
485     r = strcmp(name, result->gr_name) == 0;
486
487 finish:
488 #endif
489     
490     return r;
491 }
492
493 int pa_uid_in_group(const char *name, gid_t *gid) {
494     gid_t *gids, tgid;
495     long n = sysconf(_SC_NGROUPS_MAX);
496     int r = -1, i;
497
498     assert(n > 0);
499     
500     gids = pa_xmalloc(sizeof(gid_t)*n);
501     
502     if ((n = getgroups(n, gids)) < 0) {
503         pa_log(__FILE__": getgroups() failed: %s\n", strerror(errno));
504         goto finish;
505     }
506
507     for (i = 0; i < n; i++) {
508         if (is_group(gids[i], name) > 0) {
509             *gid = gids[i];
510             r = 1;
511             goto finish;
512         }
513     }
514
515     if (is_group(tgid = getgid(), name) > 0) {
516         *gid = tgid;
517         r = 1;
518         goto finish;
519     }
520
521     r = 0;
522     
523 finish:
524
525     pa_xfree(gids);
526     return r;
527 }
528
529 int pa_lock_fd(int fd, int b) {
530
531     struct flock flock;
532
533     flock.l_type = b ? F_WRLCK : F_UNLCK;
534     flock.l_whence = SEEK_SET;
535     flock.l_start = 0;
536     flock.l_len = 0;
537
538     if (fcntl(fd, F_SETLKW, &flock) < 0) {
539         pa_log(__FILE__": %slock failed: %s\n", !b ? "un" : "", strerror(errno));
540         return -1;
541     }
542
543     return 0;
544 }
545
546 char* pa_strip_nl(char *s) {
547     assert(s);
548
549     s[strcspn(s, "\r\n")] = 0;
550     return s;
551 }
552
553 int pa_lock_lockfile(const char *fn) {
554     int fd;
555     assert(fn);
556
557     if ((fd = open(fn, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR)) < 0) {
558         pa_log(__FILE__": failed to create lock file '%s'\n", fn);
559         goto fail;
560     }
561
562     if (pa_lock_fd(fd, 1) < 0)
563         goto fail;
564
565     return fd;
566
567 fail:
568
569     if (fd >= 0)
570         close(fd);
571
572     return -1;
573 }
574
575
576 int pa_unlock_lockfile(int fd) {
577     int r = 0;
578     assert(fd >= 0);
579
580     if (pa_lock_fd(fd, 0) < 0) {
581         pa_log(__FILE__": WARNING: failed to unlock file.\n");
582         r = -1;
583     }
584
585     if (close(fd) < 0) {
586         pa_log(__FILE__": WARNING: failed to close lock file.\n");
587         r = -1;
588     }
589
590     return r;
591 }
592