diff options
Diffstat (limited to 'src/pulsecore/usergroup.c')
-rw-r--r-- | src/pulsecore/usergroup.c | 362 |
1 files changed, 362 insertions, 0 deletions
diff --git a/src/pulsecore/usergroup.c b/src/pulsecore/usergroup.c new file mode 100644 index 0000000..40dad28 --- /dev/null +++ b/src/pulsecore/usergroup.c @@ -0,0 +1,362 @@ +/*** + This file is part of PulseAudio. + + Copyright 2009 Ted Percival + + PulseAudio is free software; you can redistribute it and/or modify + 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. + + PulseAudio 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <sys/types.h> +#include <errno.h> + +#ifdef HAVE_PWD_H +#include <pwd.h> +#endif + +#ifdef HAVE_GRP_H +#include <grp.h> +#endif + +#include <pulse/xmalloc.h> +#include <pulsecore/macro.h> + +#include "usergroup.h" + +#ifdef HAVE_GRP_H + +/* Returns a suitable starting size for a getgrnam_r() or getgrgid_r() buffer, + plus the size of a struct group. + */ +static size_t starting_getgr_buflen(void) { + size_t full_size; + long n; +#ifdef _SC_GETGR_R_SIZE_MAX + n = sysconf(_SC_GETGR_R_SIZE_MAX); +#else + n = -1; +#endif + if (n <= 0) + n = 512; + + full_size = (size_t) n + sizeof(struct group); + + if (full_size < (size_t) n) /* check for integer overflow */ + return (size_t) n; + + return full_size; +} + +/* Returns a suitable starting size for a getpwnam_r() or getpwuid_r() buffer, + plus the size of a struct passwd. + */ +static size_t starting_getpw_buflen(void) { + long n; + size_t full_size; + +#ifdef _SC_GETPW_R_SIZE_MAX + n = sysconf(_SC_GETPW_R_SIZE_MAX); +#else + n = -1; +#endif + if (n <= 0) + n = 512; + + full_size = (size_t) n + sizeof(struct passwd); + + if (full_size < (size_t) n) /* check for integer overflow */ + return (size_t) n; + + return full_size; +} + +/* Given a memory allocation (*bufptr) and its length (*buflenptr), + double the size of the allocation, updating the given buffer and length + arguments. This function should be used in conjunction with the pa_*alloc + and pa_xfree functions. + + Unlike realloc(), this function does *not* retain the original buffer's + contents. + + Returns 0 on success, nonzero on error. The error cause is indicated by + errno. + */ +static int expand_buffer_trashcontents(void **bufptr, size_t *buflenptr) { + size_t newlen; + + if (!bufptr || !*bufptr || !buflenptr) { + errno = EINVAL; + return -1; + } + + newlen = *buflenptr * 2; + + if (newlen < *buflenptr) { + errno = EOVERFLOW; + return -1; + } + + /* Don't bother retaining memory contents; free & alloc anew */ + pa_xfree(*bufptr); + + *bufptr = pa_xmalloc(newlen); + *buflenptr = newlen; + + return 0; +} + +#ifdef HAVE_GETGRGID_R +/* Thread-safe getgrgid() replacement. + Returned value should be freed using pa_getgrgid_free() when the caller is + finished with the returned group data. + + API is the same as getgrgid(), errors are indicated by a NULL return; + consult errno for the error cause (zero it before calling). + */ +struct group *pa_getgrgid_malloc(gid_t gid) { + size_t buflen, getgr_buflen; + int err; + void *buf; + void *getgr_buf; + struct group *result = NULL; + + buflen = starting_getgr_buflen(); + buf = pa_xmalloc(buflen); + + getgr_buflen = buflen - sizeof(struct group); + getgr_buf = (char *)buf + sizeof(struct group); + + while ((err = getgrgid_r(gid, (struct group *)buf, getgr_buf, getgr_buflen, &result)) == ERANGE) { + if (expand_buffer_trashcontents(&buf, &buflen)) + break; + + getgr_buflen = buflen - sizeof(struct group); + getgr_buf = (char *)buf + sizeof(struct group); + } + + if (err || !result) { + result = NULL; + if (buf) { + pa_xfree(buf); + buf = NULL; + } + } + + pa_assert(result == buf || result == NULL); + + return result; +} + +void pa_getgrgid_free(struct group *grp) { + pa_xfree(grp); +} + +#else /* !HAVE_GETGRGID_R */ + +struct group *pa_getgrgid_malloc(gid_t gid) { + return getgrgid(gid); +} + +void pa_getgrgid_free(struct group *grp) { + /* nothing */ + return; +} + +#endif /* !HAVE_GETGRGID_R */ + +#ifdef HAVE_GETGRNAM_R +/* Thread-safe getgrnam() function. + Returned value should be freed using pa_getgrnam_free() when the caller is + finished with the returned group data. + + API is the same as getgrnam(), errors are indicated by a NULL return; + consult errno for the error cause (zero it before calling). + */ +struct group *pa_getgrnam_malloc(const char *name) { + size_t buflen, getgr_buflen; + int err; + void *buf; + void *getgr_buf; + struct group *result = NULL; + + buflen = starting_getgr_buflen(); + buf = pa_xmalloc(buflen); + + getgr_buflen = buflen - sizeof(struct group); + getgr_buf = (char *)buf + sizeof(struct group); + + while ((err = getgrnam_r(name, (struct group *)buf, getgr_buf, getgr_buflen, &result)) == ERANGE) { + if (expand_buffer_trashcontents(&buf, &buflen)) + break; + + getgr_buflen = buflen - sizeof(struct group); + getgr_buf = (char *)buf + sizeof(struct group); + } + + if (err || !result) { + result = NULL; + if (buf) { + pa_xfree(buf); + buf = NULL; + } + } + + pa_assert(result == buf || result == NULL); + + return result; +} + +void pa_getgrnam_free(struct group *group) { + pa_xfree(group); +} + +#else /* !HAVE_GETGRNAM_R */ + +struct group *pa_getgrnam_malloc(const char *name) { + return getgrnam(name); +} + +void pa_getgrnam_free(struct group *group) { + /* nothing */ + return; +} + +#endif /* HAVE_GETGRNAM_R */ + +#endif /* HAVE_GRP_H */ + +#ifdef HAVE_PWD_H + +#ifdef HAVE_GETPWNAM_R +/* Thread-safe getpwnam() function. + Returned value should be freed using pa_getpwnam_free() when the caller is + finished with the returned passwd data. + + API is the same as getpwnam(), errors are indicated by a NULL return; + consult errno for the error cause (zero it before calling). + */ +struct passwd *pa_getpwnam_malloc(const char *name) { + size_t buflen, getpw_buflen; + int err; + void *buf; + void *getpw_buf; + struct passwd *result = NULL; + + buflen = starting_getpw_buflen(); + buf = pa_xmalloc(buflen); + + getpw_buflen = buflen - sizeof(struct passwd); + getpw_buf = (char *)buf + sizeof(struct passwd); + + while ((err = getpwnam_r(name, (struct passwd *)buf, getpw_buf, getpw_buflen, &result)) == ERANGE) { + if (expand_buffer_trashcontents(&buf, &buflen)) + break; + + getpw_buflen = buflen - sizeof(struct passwd); + getpw_buf = (char *)buf + sizeof(struct passwd); + } + + if (err || !result) { + result = NULL; + if (buf) { + pa_xfree(buf); + buf = NULL; + } + } + + pa_assert(result == buf || result == NULL); + + return result; +} + +void pa_getpwnam_free(struct passwd *passwd) { + pa_xfree(passwd); +} + +#else /* !HAVE_GETPWNAM_R */ + +struct passwd *pa_getpwnam_malloc(const char *name) { + return getpwnam(name); +} + +void pa_getpwnam_free(struct passwd *passwd) { + /* nothing */ + return; +} + +#endif /* !HAVE_GETPWNAM_R */ + +#ifdef HAVE_GETPWUID_R +/* Thread-safe getpwuid() function. + Returned value should be freed using pa_getpwuid_free() when the caller is + finished with the returned group data. + + API is the same as getpwuid(), errors are indicated by a NULL return; + consult errno for the error cause (zero it before calling). + */ +struct passwd *pa_getpwuid_malloc(uid_t uid) { + size_t buflen, getpw_buflen; + int err; + void *buf; + void *getpw_buf; + struct passwd *result = NULL; + + buflen = starting_getpw_buflen(); + buf = pa_xmalloc(buflen); + + getpw_buflen = buflen - sizeof(struct passwd); + getpw_buf = (char *)buf + sizeof(struct passwd); + + while ((err = getpwuid_r(uid, (struct passwd *)buf, getpw_buf, getpw_buflen, &result)) == ERANGE) { + if (expand_buffer_trashcontents(&buf, &buflen)) + break; + + getpw_buflen = buflen - sizeof(struct passwd); + getpw_buf = (char *)buf + sizeof(struct passwd); + } + + if (err || !result) { + result = NULL; + if (buf) { + pa_xfree(buf); + buf = NULL; + } + } + + pa_assert(result == buf || result == NULL); + + return result; +} + +void pa_getpwuid_free(struct passwd *passwd) { + pa_xfree(passwd); +} + +#else /* !HAVE_GETPWUID_R */ + +struct passwd *pa_getpwuid_malloc(uid_t uid) { + return getpwuid(uid); +} + +void pa_getpwuid_free(struct passwd *passwd) { + /* nothing */ + return; +} + +#endif /* !HAVE_GETPWUID_R */ + +#endif /* HAVE_PWD_H */ |