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