/*** 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, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. ***/ #ifdef HAVE_CONFIG_H #include #endif #include #include #ifdef HAVE_PWD_H #include #endif #ifdef HAVE_GRP_H #include #endif #include #include #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 */