1/*
2 * tclUnixCompat.c
3 *
4 * Written by: Zoran Vasiljevic (vasiljevic@users.sourceforge.net).
5 *
6 * See the file "license.terms" for information on usage and redistribution
7 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
8 *
9 * RCS: @(#) $Id: tclUnixCompat.c,v 1.1.2.12 2007/12/14 12:45:48 vasiljevic Exp $
10 *
11 */
12
13#include "tclInt.h"
14#include "tclPort.h"
15#include <pwd.h>
16#include <grp.h>
17#include <errno.h>
18#include <string.h>
19
20/*
21 * Used to pad structures at size'd boundaries
22 *
23 * This macro assumes that the pointer 'buffer' was created from an
24 * aligned pointer by adding the 'length'. If this 'length' was not a
25 * multiple of the 'size' the result is unaligned and PadBuffer
26 * corrects both the pointer, _and_ the 'length'. The latter means
27 * that future increments of 'buffer' by 'length' stay aligned.
28 */
29
30#define PadBuffer(buffer, length, size)             \
31    if (((length) % (size))) {                      \
32	(buffer) += ((size) - ((length) % (size))); \
33	(length) += ((size) - ((length) % (size))); \
34    }
35
36/*
37 * Per-thread private storage used to store values
38 * returned from MT-unsafe library calls.
39 */
40
41#ifdef TCL_THREADS
42
43typedef struct ThreadSpecificData {
44
45    struct passwd pwd;
46    char pbuf[2048];
47
48    struct group grp;
49    char gbuf[2048];
50
51#if !defined(HAVE_MTSAFE_GETHOSTBYNAME) || !defined(HAVE_MTSAFE_GETHOSTBYADDR)
52    struct hostent hent;
53    char hbuf[2048];
54#endif
55
56}  ThreadSpecificData;
57
58static Tcl_ThreadDataKey dataKey;
59
60#if ((!defined(HAVE_GETHOSTBYNAME_R) || !defined(HAVE_GETHOSTBYADDR_R)) && \
61     (!defined(HAVE_MTSAFE_GETHOSTBYNAME) || !defined(HAVE_MTSAFE_GETHOSTBYADDR))) || \
62      !defined(HAVE_GETPWNAM_R) || !defined(HAVE_GETPWUID_R) || \
63      !defined(HAVE_GETGRNAM_R) || !defined(HAVE_GETGRGID_R)
64
65/*
66 * Mutex to lock access to MT-unsafe calls. This is just to protect
67 * our own usage. It does not protect us from others calling the
68 * same functions without (or using some different) lock.
69 */
70
71static Tcl_Mutex compatLock;
72
73/*
74 *---------------------------------------------------------------------------
75 *
76 * CopyArray --
77 *
78 *      Copies array of NULL-terminated or fixed-length strings
79 *      to the private buffer, honouring the size of the buffer.
80 *
81 * Results:
82 *      Number of bytes copied on success or -1 on error (errno = ERANGE)
83 *
84 * Side effects:
85 *      None.
86 *
87 *---------------------------------------------------------------------------
88 */
89
90static int
91CopyArray(char **src, int elsize, char *buf, int buflen)
92{
93    int i, j, len = 0;
94    char *p, **new;
95
96    if (src == NULL) {
97	return 0;
98    }
99    for (i = 0; src[i] != NULL; i++) {
100	/* Empty loop to count howmany */
101    }
102    if ((sizeof(char *)*(i + 1)) >  buflen) {
103	return -1;
104    }
105    len = (sizeof(char *)*(i + 1)); /* Leave place for the array */
106    new = (char **)buf;
107    p = buf + (sizeof(char *)*(i + 1));
108    for (j = 0; j < i; j++) {
109	if (elsize < 0) {
110	    len += strlen(src[j]) + 1;
111	} else {
112	    len += elsize;
113	}
114	if (len > buflen) {
115	    return -1;
116	}
117	if (elsize < 0) {
118	    strcpy(p, src[j]);
119	} else {
120	    memcpy(p, src[j], elsize);
121	}
122	new[j] = p;
123	p = buf + len;
124    }
125    new[j] = NULL;
126
127    return len;
128}
129
130
131/*
132 *---------------------------------------------------------------------------
133 *
134 * CopyString --
135 *
136 *      Copies a NULL-terminated string to the private buffer,
137 *      honouring the size of the buffer
138 *
139 * Results:
140 *      0 success or -1 on error (errno = ERANGE)
141 *
142 * Side effects:
143 *      None
144 *
145 *---------------------------------------------------------------------------
146 */
147
148
149static int
150CopyString(char *src, char *buf, int buflen)
151{
152    int len = 0;
153
154    if (src != NULL) {
155	len += strlen(src) + 1;
156	if (len > buflen) {
157	    return -1;
158	}
159	strcpy(buf, src);
160    }
161
162    return len;
163}
164#endif /* ((!defined(HAVE_GETHOSTBYNAME_R) || !defined(HAVE_GETHOSTBYADDR_R)) && \
165	   (!defined(HAVE_MTSAFE_GETHOSTBYNAME) || !defined(HAVE_MTSAFE_GETHOSTBYADDR))) || \
166	    !defined(HAVE_GETPWNAM_R) || !defined(HAVE_GETPWUID_R) || \
167	    !defined(HAVE_GETGRNAM_R) || !defined(HAVE_GETGRGID_R) */
168
169#if (!defined(HAVE_GETHOSTBYNAME_R) || !defined(HAVE_GETHOSTBYADDR_R)) && \
170    (!defined(HAVE_MTSAFE_GETHOSTBYNAME) || !defined(HAVE_MTSAFE_GETHOSTBYADDR))
171
172/*
173 *---------------------------------------------------------------------------
174 *
175 * CopyHostnent --
176 *
177 *      Copies string fields of the hostnent structure to the
178 *      private buffer, honouring the size of the buffer.
179 *
180 * Results:
181 *      Number of bytes copied on success or -1 on error (errno = ERANGE)
182 *
183 * Side effects:
184 *      None
185 *
186 *---------------------------------------------------------------------------
187 */
188
189static int
190CopyHostent(struct hostent *tgtPtr, char *buf, int buflen)
191{
192    char *p = buf;
193    int copied, len = 0;
194
195    copied = CopyString(tgtPtr->h_name, p, buflen - len);
196    if (copied == -1) {
197    range:
198	errno = ERANGE;
199	return -1;
200    }
201    tgtPtr->h_name = (copied > 0) ? p : NULL;
202    len += copied;
203    p = buf + len;
204
205    PadBuffer(p, len, sizeof(char *));
206    copied = CopyArray(tgtPtr->h_aliases, -1, p, buflen - len);
207    if (copied == -1) {
208	goto range;
209    }
210    tgtPtr->h_aliases = (copied > 0) ? (char **)p : NULL;
211    len += copied;
212    p += len;
213
214    PadBuffer(p, len, sizeof(char *));
215    copied = CopyArray(tgtPtr->h_addr_list, tgtPtr->h_length, p, buflen - len);
216    if (copied == -1) {
217	goto range;
218    }
219    tgtPtr->h_addr_list = (copied > 0) ? (char **)p : NULL;
220
221    return 0;
222}
223#endif /* (!defined(HAVE_GETHOSTBYNAME_R) || !defined(HAVE_GETHOSTBYADDR_R)) && \
224	  (!defined(HAVE_MTSAFE_GETHOSTBYNAME) || !defined(HAVE_MTSAFE_GETHOSTBYADDR)) */
225
226#if !defined(HAVE_GETPWNAM_R) || !defined(HAVE_GETPWUID_R)
227
228/*
229 *---------------------------------------------------------------------------
230 *
231 * CopyPwd --
232 *
233 *      Copies string fields of the passwd structure to the
234 *      private buffer, honouring the size of the buffer.
235 *
236 * Results:
237 *      0 on success or -1 on error (errno = ERANGE)
238 *
239 * Side effects:
240 *      We are not copying the gecos field as it may not be supported
241 *      on all platforms.
242 *
243 *---------------------------------------------------------------------------
244 */
245
246static int
247CopyPwd(struct passwd *tgtPtr, char *buf, int buflen)
248{
249    char *p = buf;
250    int copied, len = 0;
251
252    copied = CopyString(tgtPtr->pw_name, p, buflen - len);
253    if (copied == -1) {
254    range:
255	errno = ERANGE;
256	return -1;
257    }
258    tgtPtr->pw_name = (copied > 0) ? p : NULL;
259    len += copied;
260    p = buf + len;
261
262    copied = CopyString(tgtPtr->pw_passwd, p, buflen - len);
263    if (copied == -1) {
264	goto range;
265    }
266    tgtPtr->pw_passwd = (copied > 0) ? p : NULL;
267    len += copied;
268    p = buf + len;
269
270    copied = CopyString(tgtPtr->pw_dir, p, buflen - len);
271    if (copied == -1) {
272	goto range;
273    }
274    tgtPtr->pw_dir = (copied > 0) ? p : NULL;
275    len += copied;
276    p = buf + len;
277
278    copied = CopyString(tgtPtr->pw_shell, p, buflen - len);
279    if (copied == -1) {
280	goto range;
281    }
282    tgtPtr->pw_shell = (copied > 0) ? p : NULL;
283
284    return 0;
285}
286#endif /* !defined(HAVE_GETPWNAM_R) || !defined(HAVE_GETPWUID_R) */
287
288#if !defined(HAVE_GETGRNAM_R) || !defined(HAVE_GETGRGID_R)
289
290/*
291 *---------------------------------------------------------------------------
292 *
293 * CopyGrp --
294 *
295 *      Copies string fields of the group structure to the
296 *      private buffer, honouring the size of the buffer.
297 *
298 * Results:
299 *      0 on success or -1 on error (errno = ERANGE)
300 *
301 * Side effects:
302 *      None.
303 *
304 *---------------------------------------------------------------------------
305 */
306
307static int
308CopyGrp(struct group *tgtPtr, char *buf, int buflen)
309{
310    register char *p = buf;
311    register int copied, len = 0;
312
313    /* Copy username */
314    copied = CopyString(tgtPtr->gr_name, p, buflen - len);
315    if (copied == -1) {
316    range:
317	errno = ERANGE;
318	return -1;
319    }
320    tgtPtr->gr_name = (copied > 0) ? p : NULL;
321    len += copied;
322    p = buf + len;
323
324    /* Copy password */
325    copied = CopyString(tgtPtr->gr_passwd, p, buflen - len);
326    if (copied == -1) {
327	goto range;
328    }
329    tgtPtr->gr_passwd = (copied > 0) ? p : NULL;
330    len += copied;
331    p = buf + len;
332
333    /* Copy group members */
334    PadBuffer(p, len, sizeof(char *));
335    copied = CopyArray((char **)tgtPtr->gr_mem, -1, p, buflen - len);
336    if (copied == -1) {
337	goto range;
338    }
339    tgtPtr->gr_mem = (copied > 0) ? (char **)p : NULL;
340
341    return 0;
342}
343#endif /* !defined(HAVE_GETGRNAM_R) || !defined(HAVE_GETGRGID_R) */
344
345#endif /* TCL_THREADS */
346
347
348/*
349 *---------------------------------------------------------------------------
350 *
351 * TclpGetPwNam --
352 *
353 *      Thread-safe wrappers for getpwnam().
354 *      See "man getpwnam" for more details.
355 *
356 * Results:
357 *      Pointer to struct passwd on success or NULL on error.
358 *
359 * Side effects:
360 *      None.
361 *
362 *---------------------------------------------------------------------------
363 */
364
365struct passwd *
366TclpGetPwNam(const char *name)
367{
368#if !defined(TCL_THREADS)
369    return getpwnam(name);
370#else
371    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
372
373#if defined(HAVE_GETPWNAM_R_5)
374    struct passwd *pwPtr = NULL;
375    return (getpwnam_r(name, &tsdPtr->pwd, tsdPtr->pbuf, sizeof(tsdPtr->pbuf),
376		       &pwPtr) == 0 && pwPtr != NULL) ? &tsdPtr->pwd : NULL;
377
378#elif defined(HAVE_GETPWNAM_R_4)
379    return getpwnam_r(name, &tsdPtr->pwd, tsdPtr->pbuf, sizeof(tsdPtr->pbuf));
380
381#else
382    struct passwd *pwPtr;
383    Tcl_MutexLock(&compatLock);
384    pwPtr = getpwnam(name);
385    if (pwPtr != NULL) {
386	tsdPtr->pwd = *pwPtr;
387	pwPtr = &tsdPtr->pwd;
388	if (CopyPwd(&tsdPtr->pwd, tsdPtr->pbuf, sizeof(tsdPtr->pbuf)) == -1) {
389	    pwPtr = NULL;
390	}
391    }
392    Tcl_MutexUnlock(&compatLock);
393    return pwPtr;
394#endif
395    return NULL; /* Not reached */
396#endif /* TCL_THREADS */
397}
398
399
400/*
401 *---------------------------------------------------------------------------
402 *
403 * TclpGetPwUid --
404 *
405 *      Thread-safe wrappers for getpwuid().
406 *      See "man getpwuid" for more details.
407 *
408 * Results:
409 *      Pointer to struct passwd on success or NULL on error.
410 *
411 * Side effects:
412 *      None.
413 *
414 *---------------------------------------------------------------------------
415 */
416
417struct passwd *
418TclpGetPwUid(uid_t uid)
419{
420#if !defined(TCL_THREADS)
421    return getpwuid(uid);
422#else
423    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
424
425#if defined(HAVE_GETPWUID_R_5)
426    struct passwd *pwPtr = NULL;
427    return (getpwuid_r(uid, &tsdPtr->pwd, tsdPtr->pbuf, sizeof(tsdPtr->pbuf),
428		       &pwPtr) == 0 && pwPtr != NULL) ? &tsdPtr->pwd : NULL;
429
430#elif defined(HAVE_GETPWUID_R_4)
431    return getpwuid_r(uid, &tsdPtr->pwd, tsdPtr->pbuf, sizeof(tsdPtr->pbuf));
432
433#else
434    struct passwd *pwPtr;
435    Tcl_MutexLock(&compatLock);
436    pwPtr = getpwuid(uid);
437    if (pwPtr != NULL) {
438	tsdPtr->pwd = *pwPtr;
439	pwPtr = &tsdPtr->pwd;
440	if (CopyPwd(&tsdPtr->pwd, tsdPtr->pbuf, sizeof(tsdPtr->pbuf)) == -1) {
441	    pwPtr = NULL;
442	}
443    }
444    Tcl_MutexUnlock(&compatLock);
445    return pwPtr;
446#endif
447    return NULL; /* Not reached */
448#endif /* TCL_THREADS */
449}
450
451
452/*
453 *---------------------------------------------------------------------------
454 *
455 * TclpGetGrNam --
456 *
457 *      Thread-safe wrappers for getgrnam().
458 *      See "man getgrnam" for more details.
459 *
460 * Results:
461 *      Pointer to struct group on success or NULL on error.
462 *
463 * Side effects:
464 *      None.
465 *
466 *---------------------------------------------------------------------------
467 */
468
469struct group *
470TclpGetGrNam(const char *name)
471{
472#if !defined(TCL_THREADS)
473    return getgrnam(name);
474#else
475    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
476
477#if defined(HAVE_GETGRNAM_R_5)
478    struct group *grPtr = NULL;
479    return (getgrnam_r(name, &tsdPtr->grp, tsdPtr->gbuf, sizeof(tsdPtr->gbuf),
480		       &grPtr) == 0 && grPtr != NULL) ? &tsdPtr->grp : NULL;
481
482#elif defined(HAVE_GETGRNAM_R_4)
483    return getgrnam_r(name, &tsdPtr->grp, tsdPtr->gbuf, sizeof(tsdPtr->gbuf));
484
485#else
486    struct group *grPtr;
487    Tcl_MutexLock(&compatLock);
488    grPtr = getgrnam(name);
489    if (grPtr != NULL) {
490	tsdPtr->grp = *grPtr;
491	grPtr = &tsdPtr->grp;
492	if (CopyGrp(&tsdPtr->grp, tsdPtr->gbuf, sizeof(tsdPtr->gbuf)) == -1) {
493	    grPtr = NULL;
494	}
495    }
496    Tcl_MutexUnlock(&compatLock);
497    return grPtr;
498#endif
499    return NULL; /* Not reached */
500#endif /* TCL_THREADS */
501}
502
503
504/*
505 *---------------------------------------------------------------------------
506 *
507 * TclpGetGrGid --
508 *
509 *      Thread-safe wrappers for getgrgid().
510 *      See "man getgrgid" for more details.
511 *
512 * Results:
513 *      Pointer to struct group on success or NULL on error.
514 *
515 * Side effects:
516 *      None.
517 *
518 *---------------------------------------------------------------------------
519 */
520
521struct group *
522TclpGetGrGid(gid_t gid)
523{
524#if !defined(TCL_THREADS)
525    return getgrgid(gid);
526#else
527    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
528
529#if defined(HAVE_GETGRGID_R_5)
530    struct group *grPtr = NULL;
531    return (getgrgid_r(gid, &tsdPtr->grp, tsdPtr->gbuf, sizeof(tsdPtr->gbuf),
532		       &grPtr) == 0 && grPtr != NULL) ? &tsdPtr->grp : NULL;
533
534#elif defined(HAVE_GETGRGID_R_4)
535    return getgrgid_r(gid, &tsdPtr->grp, tsdPtr->gbuf, sizeof(tsdPtr->gbuf));
536
537#else
538    struct group *grPtr;
539    Tcl_MutexLock(&compatLock);
540    grPtr = getgrgid(gid);
541    if (grPtr != NULL) {
542	tsdPtr->grp = *grPtr;
543	grPtr = &tsdPtr->grp;
544	if (CopyGrp(&tsdPtr->grp, tsdPtr->gbuf, sizeof(tsdPtr->gbuf)) == -1) {
545	    grPtr = NULL;
546	}
547    }
548    Tcl_MutexUnlock(&compatLock);
549    return grPtr;
550#endif
551    return NULL; /* Not reached */
552#endif /* TCL_THREADS */
553}
554
555
556/*
557 *---------------------------------------------------------------------------
558 *
559 * TclpGetHostByName --
560 *
561 *      Thread-safe wrappers for gethostbyname().
562 *      See "man gethostbyname" for more details.
563 *
564 * Results:
565 *      Pointer to struct hostent on success or NULL on error.
566 *
567 * Side effects:
568 *      None.
569 *
570 *---------------------------------------------------------------------------
571 */
572
573struct hostent *
574TclpGetHostByName(const char *name)
575{
576#if !defined(TCL_THREADS) || defined(HAVE_MTSAFE_GETHOSTBYNAME)
577    return gethostbyname(name);
578#else
579    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
580
581#if defined(HAVE_GETHOSTBYNAME_R_5)
582    int h_errno;
583    return gethostbyname_r(name, &tsdPtr->hent, tsdPtr->hbuf,
584			   sizeof(tsdPtr->hbuf), &h_errno);
585
586#elif defined(HAVE_GETHOSTBYNAME_R_6)
587    struct hostent *hePtr;
588    int result, h_errno;
589
590    result = gethostbyname_r(name, &tsdPtr->hent, tsdPtr->hbuf,
591            sizeof(tsdPtr->hbuf), &hePtr, &h_errno);
592    return (result == 0) ? hePtr : NULL;
593
594#elif defined(HAVE_GETHOSTBYNAME_R_3)
595    struct hostent_data data;
596    return (gethostbyname_r(name, &tsdPtr->hent, &data) == 0) ?
597	&tsdPtr->hent : NULL;
598#else
599    struct hostent *hePtr;
600    Tcl_MutexLock(&compatLock);
601    hePtr = gethostbyname(name);
602    if (hePtr != NULL) {
603	tsdPtr->hent = *hePtr;
604	hePtr = &tsdPtr->hent;
605	if (CopyHostent(&tsdPtr->hent, tsdPtr->hbuf,
606			sizeof(tsdPtr->hbuf)) == -1) {
607	    hePtr = NULL;
608	}
609    }
610    Tcl_MutexUnlock(&compatLock);
611    return hePtr;
612#endif
613    return NULL; /* Not reached */
614#endif /* TCL_THREADS */
615}
616
617
618/*
619 *---------------------------------------------------------------------------
620 *
621 * TclpGetHostByAddr --
622 *
623 *      Thread-safe wrappers for gethostbyaddr().
624 *      See "man gethostbyaddr" for more details.
625 *
626 * Results:
627 *      Pointer to struct hostent on success or NULL on error.
628 *
629 * Side effects:
630 *      None.
631 *
632 *---------------------------------------------------------------------------
633 */
634
635struct hostent *
636TclpGetHostByAddr(const char *addr, int length, int type)
637{
638#if !defined(TCL_THREADS) || defined(HAVE_MTSAFE_GETHOSTBYADDR)
639    return gethostbyaddr(addr, length, type);
640#else
641    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
642
643#if defined(HAVE_GETHOSTBYADDR_R_7)
644    int h_errno;
645    return gethostbyaddr_r(addr, length, type, &tsdPtr->hent, tsdPtr->hbuf,
646			   sizeof(tsdPtr->hbuf), &h_errno);
647
648#elif defined(HAVE_GETHOSTBYADDR_R_8)
649    struct hostent *hePtr;
650    int h_errno;
651    return (gethostbyaddr_r(addr, length, type, &tsdPtr->hent, tsdPtr->hbuf,
652			    sizeof(tsdPtr->hbuf), &hePtr, &h_errno) == 0) ?
653	&tsdPtr->hent : NULL;
654#else
655    struct hostent *hePtr;
656    Tcl_MutexLock(&compatLock);
657    hePtr = gethostbyaddr(addr, length, type);
658    if (hePtr != NULL) {
659	tsdPtr->hent = *hePtr;
660	hePtr = &tsdPtr->hent;
661	if (CopyHostent(&tsdPtr->hent, tsdPtr->hbuf,
662			sizeof(tsdPtr->hbuf)) == -1) {
663	    hePtr = NULL;
664	}
665    }
666    Tcl_MutexUnlock(&compatLock);
667    return hePtr;
668#endif
669    return NULL; /* Not reached */
670#endif /* TCL_THREADS */
671}
672