1/*** 2 This file is part of PulseAudio. 3 4 Copyright 2009 Ted Percival 5 6 PulseAudio 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 PulseAudio 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 PulseAudio; if not, see <http://www.gnu.org/licenses/>. 18***/ 19 20#ifdef HAVE_CONFIG_H 21#include <config.h> 22#endif 23 24#include <sys/types.h> 25#include <errno.h> 26 27#ifdef HAVE_PWD_H 28#include <pwd.h> 29#endif 30 31#ifdef HAVE_GRP_H 32#include <grp.h> 33#endif 34 35#include <pulse/xmalloc.h> 36#include <pulsecore/macro.h> 37 38#include "usergroup.h" 39 40#ifdef HAVE_GRP_H 41 42/* Returns a suitable starting size for a getgrnam_r() or getgrgid_r() buffer, 43 plus the size of a struct group. 44 */ 45static size_t starting_getgr_buflen(void) { 46 size_t full_size; 47 long n; 48#ifdef _SC_GETGR_R_SIZE_MAX 49 n = sysconf(_SC_GETGR_R_SIZE_MAX); 50#else 51 n = -1; 52#endif 53 if (n <= 0) 54 n = 512; 55 56 full_size = (size_t) n + sizeof(struct group); 57 58 if (full_size < (size_t) n) /* check for integer overflow */ 59 return (size_t) n; 60 61 return full_size; 62} 63 64/* Returns a suitable starting size for a getpwnam_r() or getpwuid_r() buffer, 65 plus the size of a struct passwd. 66 */ 67static size_t starting_getpw_buflen(void) { 68 long n; 69 size_t full_size; 70 71#ifdef _SC_GETPW_R_SIZE_MAX 72 n = sysconf(_SC_GETPW_R_SIZE_MAX); 73#else 74 n = -1; 75#endif 76 if (n <= 0) 77 n = 512; 78 79 full_size = (size_t) n + sizeof(struct passwd); 80 81 if (full_size < (size_t) n) /* check for integer overflow */ 82 return (size_t) n; 83 84 return full_size; 85} 86 87/* Given a memory allocation (*bufptr) and its length (*buflenptr), 88 double the size of the allocation, updating the given buffer and length 89 arguments. This function should be used in conjunction with the pa_*alloc 90 and pa_xfree functions. 91 92 Unlike realloc(), this function does *not* retain the original buffer's 93 contents. 94 95 Returns 0 on success, nonzero on error. The error cause is indicated by 96 errno. 97 */ 98static int expand_buffer_trashcontents(void **bufptr, size_t *buflenptr) { 99 size_t newlen; 100 101 if (!bufptr || !*bufptr || !buflenptr) { 102 errno = EINVAL; 103 return -1; 104 } 105 106 newlen = *buflenptr * 2; 107 108 if (newlen < *buflenptr) { 109 errno = EOVERFLOW; 110 return -1; 111 } 112 113 /* Don't bother retaining memory contents; free & alloc anew */ 114 pa_xfree(*bufptr); 115 116 *bufptr = pa_xmalloc(newlen); 117 *buflenptr = newlen; 118 119 return 0; 120} 121 122#ifdef HAVE_GETGRGID_R 123/* Thread-safe getgrgid() replacement. 124 Returned value should be freed using pa_getgrgid_free() when the caller is 125 finished with the returned group data. 126 127 API is the same as getgrgid(), errors are indicated by a NULL return; 128 consult errno for the error cause (zero it before calling). 129 */ 130struct group *pa_getgrgid_malloc(gid_t gid) { 131 size_t buflen, getgr_buflen; 132 int err; 133 void *buf; 134 void *getgr_buf; 135 struct group *result = NULL; 136 137 buflen = starting_getgr_buflen(); 138 buf = pa_xmalloc(buflen); 139 140 getgr_buflen = buflen - sizeof(struct group); 141 getgr_buf = (char *)buf + sizeof(struct group); 142 143 while ((err = getgrgid_r(gid, (struct group *)buf, getgr_buf, getgr_buflen, &result)) == ERANGE) { 144 if (expand_buffer_trashcontents(&buf, &buflen)) 145 break; 146 147 getgr_buflen = buflen - sizeof(struct group); 148 getgr_buf = (char *)buf + sizeof(struct group); 149 } 150 151 if (err || !result) { 152 result = NULL; 153 if (buf) { 154 pa_xfree(buf); 155 buf = NULL; 156 } 157 } 158 159 pa_assert(result == buf || result == NULL); 160 161 return result; 162} 163 164void pa_getgrgid_free(struct group *grp) { 165 pa_xfree(grp); 166} 167 168#else /* !HAVE_GETGRGID_R */ 169 170struct group *pa_getgrgid_malloc(gid_t gid) { 171 return getgrgid(gid); 172} 173 174void pa_getgrgid_free(struct group *grp) { 175 /* nothing */ 176 return; 177} 178 179#endif /* !HAVE_GETGRGID_R */ 180 181#ifdef HAVE_GETGRNAM_R 182/* Thread-safe getgrnam() function. 183 Returned value should be freed using pa_getgrnam_free() when the caller is 184 finished with the returned group data. 185 186 API is the same as getgrnam(), errors are indicated by a NULL return; 187 consult errno for the error cause (zero it before calling). 188 */ 189struct group *pa_getgrnam_malloc(const char *name) { 190 size_t buflen, getgr_buflen; 191 int err; 192 void *buf; 193 void *getgr_buf; 194 struct group *result = NULL; 195 196 buflen = starting_getgr_buflen(); 197 buf = pa_xmalloc(buflen); 198 199 getgr_buflen = buflen - sizeof(struct group); 200 getgr_buf = (char *)buf + sizeof(struct group); 201 202 while ((err = getgrnam_r(name, (struct group *)buf, getgr_buf, getgr_buflen, &result)) == ERANGE) { 203 if (expand_buffer_trashcontents(&buf, &buflen)) 204 break; 205 206 getgr_buflen = buflen - sizeof(struct group); 207 getgr_buf = (char *)buf + sizeof(struct group); 208 } 209 210 if (err || !result) { 211 result = NULL; 212 if (buf) { 213 pa_xfree(buf); 214 buf = NULL; 215 } 216 } 217 218 pa_assert(result == buf || result == NULL); 219 220 return result; 221} 222 223void pa_getgrnam_free(struct group *group) { 224 pa_xfree(group); 225} 226 227#else /* !HAVE_GETGRNAM_R */ 228 229struct group *pa_getgrnam_malloc(const char *name) { 230 return getgrnam(name); 231} 232 233void pa_getgrnam_free(struct group *group) { 234 /* nothing */ 235 return; 236} 237 238#endif /* HAVE_GETGRNAM_R */ 239 240#endif /* HAVE_GRP_H */ 241 242#ifdef HAVE_PWD_H 243 244#ifdef HAVE_GETPWNAM_R 245/* Thread-safe getpwnam() function. 246 Returned value should be freed using pa_getpwnam_free() when the caller is 247 finished with the returned passwd data. 248 249 API is the same as getpwnam(), errors are indicated by a NULL return; 250 consult errno for the error cause (zero it before calling). 251 */ 252struct passwd *pa_getpwnam_malloc(const char *name) { 253 size_t buflen, getpw_buflen; 254 int err; 255 void *buf; 256 void *getpw_buf; 257 struct passwd *result = NULL; 258 259 buflen = starting_getpw_buflen(); 260 buf = pa_xmalloc(buflen); 261 262 getpw_buflen = buflen - sizeof(struct passwd); 263 getpw_buf = (char *)buf + sizeof(struct passwd); 264 265 while ((err = getpwnam_r(name, (struct passwd *)buf, getpw_buf, getpw_buflen, &result)) == ERANGE) { 266 if (expand_buffer_trashcontents(&buf, &buflen)) 267 break; 268 269 getpw_buflen = buflen - sizeof(struct passwd); 270 getpw_buf = (char *)buf + sizeof(struct passwd); 271 } 272 273 if (err || !result) { 274 result = NULL; 275 if (buf) { 276 pa_xfree(buf); 277 buf = NULL; 278 } 279 } 280 281 pa_assert(result == buf || result == NULL); 282 283 return result; 284} 285 286void pa_getpwnam_free(struct passwd *passwd) { 287 pa_xfree(passwd); 288} 289 290#else /* !HAVE_GETPWNAM_R */ 291 292struct passwd *pa_getpwnam_malloc(const char *name) { 293 return getpwnam(name); 294} 295 296void pa_getpwnam_free(struct passwd *passwd) { 297 /* nothing */ 298 return; 299} 300 301#endif /* !HAVE_GETPWNAM_R */ 302 303#ifdef HAVE_GETPWUID_R 304/* Thread-safe getpwuid() function. 305 Returned value should be freed using pa_getpwuid_free() when the caller is 306 finished with the returned group data. 307 308 API is the same as getpwuid(), errors are indicated by a NULL return; 309 consult errno for the error cause (zero it before calling). 310 */ 311struct passwd *pa_getpwuid_malloc(uid_t uid) { 312 size_t buflen, getpw_buflen; 313 int err; 314 void *buf; 315 void *getpw_buf; 316 struct passwd *result = NULL; 317 318 buflen = starting_getpw_buflen(); 319 buf = pa_xmalloc(buflen); 320 321 getpw_buflen = buflen - sizeof(struct passwd); 322 getpw_buf = (char *)buf + sizeof(struct passwd); 323 324 while ((err = getpwuid_r(uid, (struct passwd *)buf, getpw_buf, getpw_buflen, &result)) == ERANGE) { 325 if (expand_buffer_trashcontents(&buf, &buflen)) 326 break; 327 328 getpw_buflen = buflen - sizeof(struct passwd); 329 getpw_buf = (char *)buf + sizeof(struct passwd); 330 } 331 332 if (err || !result) { 333 result = NULL; 334 if (buf) { 335 pa_xfree(buf); 336 buf = NULL; 337 } 338 } 339 340 pa_assert(result == buf || result == NULL); 341 342 return result; 343} 344 345void pa_getpwuid_free(struct passwd *passwd) { 346 pa_xfree(passwd); 347} 348 349#else /* !HAVE_GETPWUID_R */ 350 351struct passwd *pa_getpwuid_malloc(uid_t uid) { 352 return getpwuid(uid); 353} 354 355void pa_getpwuid_free(struct passwd *passwd) { 356 /* nothing */ 357 return; 358} 359 360#endif /* !HAVE_GETPWUID_R */ 361 362#endif /* HAVE_PWD_H */ 363