X-Git-Url: https://code.delx.au/pulseaudio/blobdiff_plain/6f59ae1763ee48f27448a7de9d635f61886052e1..fa499dad06ba6558111cdef64c18f2401e803cff:/polyp/util.c diff --git a/polyp/util.c b/polyp/util.c index 70ec120d..e8a47df0 100644 --- a/polyp/util.c +++ b/polyp/util.c @@ -4,17 +4,17 @@ This file is part of polypaudio. polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. polypaudio is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. + Lesser General Public License for more details. - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. ***/ @@ -43,6 +43,7 @@ #include #include #include +#include #include @@ -50,16 +51,22 @@ #include "xmalloc.h" #include "log.h" +#define PA_RUNTIME_PATH_PREFIX "/tmp/polypaudio-" + +/** Make a file descriptor nonblock. Doesn't do any error checking */ void pa_make_nonblock_fd(int fd) { int v; + assert(fd >= 0); if ((v = fcntl(fd, F_GETFL)) >= 0) if (!(v & O_NONBLOCK)) fcntl(fd, F_SETFL, v|O_NONBLOCK); } +/** Creates a directory securely */ int pa_make_secure_dir(const char* dir) { struct stat st; + assert(dir); if (mkdir(dir, 0700) < 0) if (errno != EEXIST) @@ -78,6 +85,28 @@ fail: return -1; } +/* Creates a the parent directory of the specified path securely */ +int pa_make_secure_parent_dir(const char *fn) { + int ret = -1; + char *slash, *dir = pa_xstrdup(fn); + + if (!(slash = strrchr(dir, '/'))) + goto finish; + *slash = 0; + + if (pa_make_secure_dir(dir) < 0) + goto finish; + + ret = 0; + +finish: + pa_xfree(dir); + return ret; +} + + +/** Calls read() in a loop. Makes sure that as much as 'size' bytes, + * unless EOF is reached or an error occured */ ssize_t pa_loop_read(int fd, void*data, size_t size) { ssize_t ret = 0; assert(fd >= 0 && data && size); @@ -99,6 +128,7 @@ ssize_t pa_loop_read(int fd, void*data, size_t size) { return ret; } +/** Similar to pa_loop_read(), but wraps write() */ ssize_t pa_loop_write(int fd, const void*data, size_t size) { ssize_t ret = 0; assert(fd >= 0 && data && size); @@ -120,10 +150,16 @@ ssize_t pa_loop_write(int fd, const void*data, size_t size) { return ret; } +/* Print a warning messages in case that the given signal is not + * blocked or trapped */ void pa_check_signal_is_blocked(int sig) { struct sigaction sa; sigset_t set; + /* If POSIX threads are supported use thread-aware + * pthread_sigmask() function, to check if the signal is + * blocked. Otherwise fall back to sigprocmask() */ + #ifdef HAVE_PTHREAD if (pthread_sigmask(SIG_SETMASK, NULL, &set) < 0) { #endif @@ -137,6 +173,8 @@ void pa_check_signal_is_blocked(int sig) { if (sigismember(&set, sig)) return; + + /* Check whether the signal is trapped */ if (sigaction(sig, NULL, &sa) < 0) { pa_log(__FILE__": sigaction() failed: %s\n", strerror(errno)); @@ -149,7 +187,8 @@ void pa_check_signal_is_blocked(int sig) { pa_log(__FILE__": WARNING: %s is not trapped. This might cause malfunction!\n", pa_strsignal(sig)); } -/* The following is based on an example from the GNU libc documentation */ +/* The following function is based on an example from the GNU libc + * documentation. This function is similar to GNU's asprintf(). */ char *pa_sprintf_malloc(const char *format, ...) { int size = 100; char *c = NULL; @@ -176,6 +215,8 @@ char *pa_sprintf_malloc(const char *format, ...) { } } +/* Same as the previous function, but use a va_list instead of an + * ellipsis */ char *pa_vsprintf_malloc(const char *format, va_list ap) { int size = 100; char *c = NULL; @@ -199,38 +240,77 @@ char *pa_vsprintf_malloc(const char *format, va_list ap) { } } - +/* Return the current username in the specified string buffer. */ char *pa_get_user_name(char *s, size_t l) { struct passwd pw, *r; char buf[1024]; char *p; + assert(s && l > 0); - if (!(p = getenv("USER"))) - if (!(p = getenv("LOGNAME"))) - if (!(p = getenv("USERNAME"))) { - - if (getpwuid_r(getuid(), &pw, buf, sizeof(buf), &r) != 0 || !r) { - snprintf(s, l, "%lu", (unsigned long) getuid()); - return s; - } - - p = r->pw_name; + if (!(p = getenv("USER")) && !(p = getenv("LOGNAME")) && !(p = getenv("USERNAME"))) { + +#ifdef HAVE_GETPWUID_R + if (getpwuid_r(getuid(), &pw, buf, sizeof(buf), &r) != 0 || !r) { +#else + /* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X) + * that do not support getpwuid_r. */ + if ((r = getpwuid(getuid())) == NULL) { +#endif + snprintf(s, l, "%lu", (unsigned long) getuid()); + return s; } - - snprintf(s, l, "%s", p); - return s; -} + + p = r->pw_name; + } + + return pa_strlcpy(s, p, l); + } +/* Return the current hostname in the specified buffer. */ char *pa_get_host_name(char *s, size_t l) { - gethostname(s, l); + assert(s && l > 0); + if (gethostname(s, l) < 0) { + pa_log(__FILE__": gethostname(): %s\n", strerror(errno)); + return NULL; + } s[l-1] = 0; return s; } +/* Return the home directory of the current user */ +char *pa_get_home_dir(char *s, size_t l) { + char *e; + char buf[1024]; + struct passwd pw, *r; + assert(s && l); + + if ((e = getenv("HOME"))) + return pa_strlcpy(s, e, l); + + if (getpwuid_r(getuid(), &pw, buf, sizeof(buf), &r) != 0 || !r) { + pa_log(__FILE__": getpwuid_r() failed\n"); + return NULL; + } + + return pa_strlcpy(s, r->pw_dir, l); +} + +/* Similar to OpenBSD's strlcpy() function */ +char *pa_strlcpy(char *b, const char *s, size_t l) { + assert(b && s && l > 0); + + strncpy(b, s, l); + b[l-1] = 0; + return b; +} + +/* Calculate the difference between the two specfified timeval + * timestamsps. */ pa_usec_t pa_timeval_diff(const struct timeval *a, const struct timeval *b) { pa_usec_t r; assert(a && b); + /* Check which whan is the earlier time and swap the two arguments if reuqired. */ if (pa_timeval_cmp(a, b) < 0) { const struct timeval *c; c = a; @@ -238,8 +318,10 @@ pa_usec_t pa_timeval_diff(const struct timeval *a, const struct timeval *b) { b = c; } + /* Calculate the second difference*/ r = ((pa_usec_t) a->tv_sec - b->tv_sec)* 1000000; + /* Calculate the microsecond difference */ if (a->tv_usec > b->tv_usec) r += ((pa_usec_t) a->tv_usec - b->tv_usec); else if (a->tv_usec < b->tv_usec) @@ -248,6 +330,7 @@ pa_usec_t pa_timeval_diff(const struct timeval *a, const struct timeval *b) { return r; } +/* Compare the two timeval structs and return 0 when equal, negative when a < b, positive otherwse */ int pa_timeval_cmp(const struct timeval *a, const struct timeval *b) { assert(a && b); @@ -266,13 +349,15 @@ int pa_timeval_cmp(const struct timeval *a, const struct timeval *b) { return 0; } -pa_usec_t pa_age(const struct timeval *tv) { +/* Return the time difference between now and the specified timestamp */ +pa_usec_t pa_timeval_age(const struct timeval *tv) { struct timeval now; assert(tv); gettimeofday(&now, NULL); return pa_timeval_diff(&now, tv); } +/* Add the specified time inmicroseconds to the specified timeval structure */ void pa_timeval_add(struct timeval *tv, pa_usec_t v) { unsigned long secs; assert(tv); @@ -283,6 +368,7 @@ void pa_timeval_add(struct timeval *tv, pa_usec_t v) { tv->tv_usec += v; + /* Normalize */ while (tv->tv_usec >= 1000000) { tv->tv_sec++; tv->tv_usec -= 1000000; @@ -291,12 +377,15 @@ void pa_timeval_add(struct timeval *tv, pa_usec_t v) { #define NICE_LEVEL (-15) +/* Raise the priority of the current process as much as possible and +sensible: set the nice level to -15 and enable realtime scheduling if +supported.*/ void pa_raise_priority(void) { + if (setpriority(PRIO_PROCESS, 0, NICE_LEVEL) < 0) pa_log(__FILE__": setpriority() failed: %s\n", strerror(errno)); - else - pa_log(__FILE__": Successfully gained nice level %i.\n", NICE_LEVEL); - + else pa_log(__FILE__": Successfully gained nice level %i.\n", NICE_LEVEL); + #ifdef _POSIX_PRIORITY_SCHEDULING { struct sched_param sp; @@ -317,6 +406,7 @@ void pa_raise_priority(void) { #endif } +/* Reset the priority to normal, inverting the changes made by pa_raise_priority() */ void pa_reset_priority(void) { #ifdef _POSIX_PRIORITY_SCHEDULING { @@ -330,6 +420,7 @@ void pa_reset_priority(void) { setpriority(PRIO_PROCESS, 0, 0); } +/* Set the FD_CLOEXEC flag for a fd */ int pa_fd_set_cloexec(int fd, int b) { int v; assert(fd >= 0); @@ -345,6 +436,9 @@ int pa_fd_set_cloexec(int fd, int b) { return 0; } +/* Return the binary file name of the current process. Works on Linux + * only. This shoul be used for eyecandy only, don't rely on return + * non-NULL! */ char *pa_get_binary_name(char *s, size_t l) { char path[PATH_MAX]; int i; @@ -360,6 +454,8 @@ char *pa_get_binary_name(char *s, size_t l) { return s; } +/* Return a pointer to the filename inside a path (which is the last + * component). */ char *pa_path_get_filename(const char *p) { char *fn; @@ -369,6 +465,7 @@ char *pa_path_get_filename(const char *p) { return (char*) p; } +/* Try to parse a boolean string value.*/ int pa_parse_boolean(const char *v) { if (!strcmp(v, "1") || v[0] == 'y' || v[0] == 'Y' || v[0] == 't' || v[0] == 'T' || !strcasecmp(v, "on")) @@ -379,6 +476,10 @@ int pa_parse_boolean(const char *v) { return -1; } +/* Split the specified string wherever one of the strings in delimiter + * occurs. Each time it is called returns a newly allocated string + * with pa_xmalloc(). The variable state points to, should be + * initiallized to NULL before the first call. */ char *pa_split(const char *c, const char *delimiter, const char**state) { const char *current = *state ? *state : c; size_t l; @@ -395,13 +496,15 @@ char *pa_split(const char *c, const char *delimiter, const char**state) { return pa_xstrndup(current, l); } +/* What is interpreted as whitespace? */ #define WHITESPACE " \t\n" +/* Split a string into words. Otherwise similar to pa_split(). */ char *pa_split_spaces(const char *c, const char **state) { const char *current = *state ? *state : c; size_t l; - if (!*current) + if (!*current || *c == 0) return NULL; current += strspn(current, WHITESPACE); @@ -412,6 +515,7 @@ char *pa_split_spaces(const char *c, const char **state) { return pa_xstrndup(current, l); } +/* Return the name of an UNIX signal. Similar to GNU's strsignal() */ const char *pa_strsignal(int sig) { switch(sig) { case SIGINT: return "SIGINT"; @@ -426,6 +530,7 @@ const char *pa_strsignal(int sig) { } } +/* Parse a libsamplrate compatible resampling implementation */ int pa_parse_resample_method(const char *string) { assert(string); @@ -443,13 +548,20 @@ int pa_parse_resample_method(const char *string) { return -1; } +/* Check whether the specified GID and the group name match */ static int is_group(gid_t gid, const char *name) { struct group group, *result = NULL; - long n = sysconf(_SC_GETGR_R_SIZE_MAX); + long n; void *data; int r = -1; - - assert(n > 0); + +#ifdef HAVE_GETGRGID_R +#ifdef _SC_GETGR_R_SIZE_MAX + n = sysconf(_SC_GETGR_R_SIZE_MAX); +#else + n = -1; +#endif + if (n < 0) n = 512; data = pa_xmalloc(n); if (getgrgid_r(gid, &group, data, n, &result) < 0 || !result) { @@ -462,10 +574,23 @@ static int is_group(gid_t gid, const char *name) { finish: pa_xfree(data); +#else + /* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X) that do not + * support getgrgid_r. */ + if ((result = getgrgid(gid)) == NULL) { + pa_log(__FILE__ ": getgrgid(%u) failed: %s\n", gid, strerror(errno)); + goto finish; + } + + r = strcmp(name, result->gr_name) == 0; + +finish: +#endif return r; } +/* Check the current user is member of the specified group */ int pa_uid_in_group(const char *name, gid_t *gid) { gid_t *gids, tgid; long n = sysconf(_SC_NGROUPS_MAX); @@ -502,6 +627,7 @@ finish: return r; } +/* Lock or unlock a file entirely. (advisory) */ int pa_lock_fd(int fd, int b) { struct flock flock; @@ -519,6 +645,7 @@ int pa_lock_fd(int fd, int b) { return 0; } +/* Remove trailing newlines from a string */ char* pa_strip_nl(char *s) { assert(s); @@ -526,6 +653,7 @@ char* pa_strip_nl(char *s) { return s; } +/* Create a temporary lock file and lock it. */ int pa_lock_lockfile(const char *fn) { int fd; assert(fn); @@ -548,7 +676,7 @@ fail: return -1; } - +/* Unlock a temporary lcok file */ int pa_unlock_lockfile(int fd) { int r = 0; assert(fd >= 0); @@ -566,3 +694,149 @@ int pa_unlock_lockfile(int fd) { return r; } +/* Try to open a configuration file. If "env" is specified, open the + * value of the specified environment variable. Otherwise look for a + * file "local" in the home directory or a file "global" in global + * file system. If "result" is non-NULL, a pointer to a newly + * allocated buffer containing the used configuration file is + * stored there.*/ +FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result) { + const char *e; + char h[PATH_MAX]; + + if (env && (e = getenv(env))) { + if (result) + *result = pa_xstrdup(e); + return fopen(e, "r"); + } + + if (local && pa_get_home_dir(h, sizeof(h))) { + FILE *f; + char *l; + + l = pa_sprintf_malloc("%s/%s", h, local); + f = fopen(l, "r"); + + if (f || errno != ENOENT) { + if (result) + *result = l; + else + pa_xfree(l); + return f; + } + + pa_xfree(l); + } + + if (!global) { + if (result) + *result = NULL; + errno = ENOENT; + return NULL; + } + + if (result) + *result = pa_xstrdup(global); + + return fopen(global, "r"); +} + +/* Format the specified data as a hexademical string */ +char *pa_hexstr(const uint8_t* d, size_t dlength, char *s, size_t slength) { + size_t i = 0, j = 0; + const char hex[] = "0123456789abcdef"; + assert(d && s && slength > 0); + + while (i < dlength && j+3 <= slength) { + s[j++] = hex[*d >> 4]; + s[j++] = hex[*d & 0xF]; + + d++; + i++; + } + + s[j < slength ? j : slength] = 0; + return s; +} + +/* Convert a hexadecimal digit to a number or -1 if invalid */ +static int hexc(char c) { + if (c >= '0' && c <= '9') + return c - '0'; + + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + + return -1; +} + +/* Parse a hexadecimal string as created by pa_hexstr() to a BLOB */ +size_t pa_parsehex(const char *p, uint8_t *d, size_t dlength) { + size_t j = 0; + assert(p && d); + + while (j < dlength && *p) { + int b; + + if ((b = hexc(*(p++))) < 0) + return (size_t) -1; + + d[j] = (uint8_t) (b << 4); + + if (!*p) + return (size_t) -1; + + if ((b = hexc(*(p++))) < 0) + return (size_t) -1; + + d[j] |= (uint8_t) b; + j++; + } + + return j; +} + +/* Return the fully qualified domain name in *s */ +char *pa_get_fqdn(char *s, size_t l) { + char hn[256]; + struct addrinfo *a, hints; + + if (!pa_get_host_name(hn, sizeof(hn))) + return NULL; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_flags = AI_CANONNAME; + + if (getaddrinfo(hn, NULL, &hints, &a) < 0 || !a || !a->ai_canonname || !*a->ai_canonname) + return pa_strlcpy(s, hn, l); + + pa_strlcpy(s, a->ai_canonname, l); + freeaddrinfo(a); + return s; +} + +/* Returns nonzero when *s starts with *pfx */ +int pa_startswith(const char *s, const char *pfx) { + size_t l; + assert(s && pfx); + l = strlen(pfx); + + return strlen(s) >= l && strncmp(s, pfx, l) == 0; +} + +/* if fn is null return the polypaudio run time path in s (/tmp/polypaudio) + * if fn is non-null and starts with / return fn in s + * otherwise append fn to the run time path and return it in s */ +char *pa_runtime_path(const char *fn, char *s, size_t l) { + char u[256]; + + if (fn && *fn == '/') + return pa_strlcpy(s, fn, l); + + snprintf(s, l, PA_RUNTIME_PATH_PREFIX"%s%s%s", pa_get_user_name(u, sizeof(u)), fn ? "/" : "", fn ? fn : ""); + return s; +}