/* * tclUnixCompat.c * * Written by: Zoran Vasiljevic (vasiljevic@users.sourceforge.net). * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * RCS: @(#) $Id: tclUnixCompat.c,v 1.1.1.2 2009/03/23 15:10:41 duncan Exp $ * */ #include "tclInt.h" #include "tclPort.h" #include #include #include #include /* * Used to pad structures at size'd boundaries * * This macro assumes that the pointer 'buffer' was created from an * aligned pointer by adding the 'length'. If this 'length' was not a * multiple of the 'size' the result is unaligned and PadBuffer * corrects both the pointer, _and_ the 'length'. The latter means * that future increments of 'buffer' by 'length' stay aligned. */ #define PadBuffer(buffer, length, size) \ if (((length) % (size))) { \ (buffer) += ((size) - ((length) % (size))); \ (length) += ((size) - ((length) % (size))); \ } /* * Per-thread private storage used to store values * returned from MT-unsafe library calls. */ #ifdef TCL_THREADS typedef struct ThreadSpecificData { struct passwd pwd; char pbuf[2048]; struct group grp; char gbuf[2048]; #if !defined(HAVE_MTSAFE_GETHOSTBYNAME) || !defined(HAVE_MTSAFE_GETHOSTBYADDR) struct hostent hent; char hbuf[2048]; #endif } ThreadSpecificData; static Tcl_ThreadDataKey dataKey; #if ((!defined(HAVE_GETHOSTBYNAME_R) || !defined(HAVE_GETHOSTBYADDR_R)) && \ (!defined(HAVE_MTSAFE_GETHOSTBYNAME) || !defined(HAVE_MTSAFE_GETHOSTBYADDR))) || \ !defined(HAVE_GETPWNAM_R) || !defined(HAVE_GETPWUID_R) || \ !defined(HAVE_GETGRNAM_R) || !defined(HAVE_GETGRGID_R) /* * Mutex to lock access to MT-unsafe calls. This is just to protect * our own usage. It does not protect us from others calling the * same functions without (or using some different) lock. */ static Tcl_Mutex compatLock; /* *--------------------------------------------------------------------------- * * CopyArray -- * * Copies array of NULL-terminated or fixed-length strings * to the private buffer, honouring the size of the buffer. * * Results: * Number of bytes copied on success or -1 on error (errno = ERANGE) * * Side effects: * None. * *--------------------------------------------------------------------------- */ static int CopyArray(char **src, int elsize, char *buf, int buflen) { int i, j, len = 0; char *p, **new; if (src == NULL) { return 0; } for (i = 0; src[i] != NULL; i++) { /* Empty loop to count howmany */ } if ((sizeof(char *)*(i + 1)) > buflen) { return -1; } len = (sizeof(char *)*(i + 1)); /* Leave place for the array */ new = (char **)buf; p = buf + (sizeof(char *)*(i + 1)); for (j = 0; j < i; j++) { if (elsize < 0) { len += strlen(src[j]) + 1; } else { len += elsize; } if (len > buflen) { return -1; } if (elsize < 0) { strcpy(p, src[j]); } else { memcpy(p, src[j], elsize); } new[j] = p; p = buf + len; } new[j] = NULL; return len; } /* *--------------------------------------------------------------------------- * * CopyString -- * * Copies a NULL-terminated string to the private buffer, * honouring the size of the buffer * * Results: * 0 success or -1 on error (errno = ERANGE) * * Side effects: * None * *--------------------------------------------------------------------------- */ static int CopyString(char *src, char *buf, int buflen) { int len = 0; if (src != NULL) { len += strlen(src) + 1; if (len > buflen) { return -1; } strcpy(buf, src); } return len; } #endif /* ((!defined(HAVE_GETHOSTBYNAME_R) || !defined(HAVE_GETHOSTBYADDR_R)) && \ (!defined(HAVE_MTSAFE_GETHOSTBYNAME) || !defined(HAVE_MTSAFE_GETHOSTBYADDR))) || \ !defined(HAVE_GETPWNAM_R) || !defined(HAVE_GETPWUID_R) || \ !defined(HAVE_GETGRNAM_R) || !defined(HAVE_GETGRGID_R) */ #if (!defined(HAVE_GETHOSTBYNAME_R) || !defined(HAVE_GETHOSTBYADDR_R)) && \ (!defined(HAVE_MTSAFE_GETHOSTBYNAME) || !defined(HAVE_MTSAFE_GETHOSTBYADDR)) /* *--------------------------------------------------------------------------- * * CopyHostnent -- * * Copies string fields of the hostnent structure to the * private buffer, honouring the size of the buffer. * * Results: * Number of bytes copied on success or -1 on error (errno = ERANGE) * * Side effects: * None * *--------------------------------------------------------------------------- */ static int CopyHostent(struct hostent *tgtPtr, char *buf, int buflen) { char *p = buf; int copied, len = 0; copied = CopyString(tgtPtr->h_name, p, buflen - len); if (copied == -1) { range: errno = ERANGE; return -1; } tgtPtr->h_name = (copied > 0) ? p : NULL; len += copied; p = buf + len; PadBuffer(p, len, sizeof(char *)); copied = CopyArray(tgtPtr->h_aliases, -1, p, buflen - len); if (copied == -1) { goto range; } tgtPtr->h_aliases = (copied > 0) ? (char **)p : NULL; len += copied; p += len; PadBuffer(p, len, sizeof(char *)); copied = CopyArray(tgtPtr->h_addr_list, tgtPtr->h_length, p, buflen - len); if (copied == -1) { goto range; } tgtPtr->h_addr_list = (copied > 0) ? (char **)p : NULL; return 0; } #endif /* (!defined(HAVE_GETHOSTBYNAME_R) || !defined(HAVE_GETHOSTBYADDR_R)) && \ (!defined(HAVE_MTSAFE_GETHOSTBYNAME) || !defined(HAVE_MTSAFE_GETHOSTBYADDR)) */ #if !defined(HAVE_GETPWNAM_R) || !defined(HAVE_GETPWUID_R) /* *--------------------------------------------------------------------------- * * CopyPwd -- * * Copies string fields of the passwd structure to the * private buffer, honouring the size of the buffer. * * Results: * 0 on success or -1 on error (errno = ERANGE) * * Side effects: * We are not copying the gecos field as it may not be supported * on all platforms. * *--------------------------------------------------------------------------- */ static int CopyPwd(struct passwd *tgtPtr, char *buf, int buflen) { char *p = buf; int copied, len = 0; copied = CopyString(tgtPtr->pw_name, p, buflen - len); if (copied == -1) { range: errno = ERANGE; return -1; } tgtPtr->pw_name = (copied > 0) ? p : NULL; len += copied; p = buf + len; copied = CopyString(tgtPtr->pw_passwd, p, buflen - len); if (copied == -1) { goto range; } tgtPtr->pw_passwd = (copied > 0) ? p : NULL; len += copied; p = buf + len; copied = CopyString(tgtPtr->pw_dir, p, buflen - len); if (copied == -1) { goto range; } tgtPtr->pw_dir = (copied > 0) ? p : NULL; len += copied; p = buf + len; copied = CopyString(tgtPtr->pw_shell, p, buflen - len); if (copied == -1) { goto range; } tgtPtr->pw_shell = (copied > 0) ? p : NULL; return 0; } #endif /* !defined(HAVE_GETPWNAM_R) || !defined(HAVE_GETPWUID_R) */ #if !defined(HAVE_GETGRNAM_R) || !defined(HAVE_GETGRGID_R) /* *--------------------------------------------------------------------------- * * CopyGrp -- * * Copies string fields of the group structure to the * private buffer, honouring the size of the buffer. * * Results: * 0 on success or -1 on error (errno = ERANGE) * * Side effects: * None. * *--------------------------------------------------------------------------- */ static int CopyGrp(struct group *tgtPtr, char *buf, int buflen) { register char *p = buf; register int copied, len = 0; /* Copy username */ copied = CopyString(tgtPtr->gr_name, p, buflen - len); if (copied == -1) { range: errno = ERANGE; return -1; } tgtPtr->gr_name = (copied > 0) ? p : NULL; len += copied; p = buf + len; /* Copy password */ copied = CopyString(tgtPtr->gr_passwd, p, buflen - len); if (copied == -1) { goto range; } tgtPtr->gr_passwd = (copied > 0) ? p : NULL; len += copied; p = buf + len; /* Copy group members */ PadBuffer(p, len, sizeof(char *)); copied = CopyArray((char **)tgtPtr->gr_mem, -1, p, buflen - len); if (copied == -1) { goto range; } tgtPtr->gr_mem = (copied > 0) ? (char **)p : NULL; return 0; } #endif /* !defined(HAVE_GETGRNAM_R) || !defined(HAVE_GETGRGID_R) */ #endif /* TCL_THREADS */ /* *--------------------------------------------------------------------------- * * TclpGetPwNam -- * * Thread-safe wrappers for getpwnam(). * See "man getpwnam" for more details. * * Results: * Pointer to struct passwd on success or NULL on error. * * Side effects: * None. * *--------------------------------------------------------------------------- */ struct passwd * TclpGetPwNam(const char *name) { #if !defined(TCL_THREADS) return getpwnam(name); #else ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); #if defined(HAVE_GETPWNAM_R_5) struct passwd *pwPtr = NULL; return (getpwnam_r(name, &tsdPtr->pwd, tsdPtr->pbuf, sizeof(tsdPtr->pbuf), &pwPtr) == 0 && pwPtr != NULL) ? &tsdPtr->pwd : NULL; #elif defined(HAVE_GETPWNAM_R_4) return getpwnam_r(name, &tsdPtr->pwd, tsdPtr->pbuf, sizeof(tsdPtr->pbuf)); #else struct passwd *pwPtr; Tcl_MutexLock(&compatLock); pwPtr = getpwnam(name); if (pwPtr != NULL) { tsdPtr->pwd = *pwPtr; pwPtr = &tsdPtr->pwd; if (CopyPwd(&tsdPtr->pwd, tsdPtr->pbuf, sizeof(tsdPtr->pbuf)) == -1) { pwPtr = NULL; } } Tcl_MutexUnlock(&compatLock); return pwPtr; #endif return NULL; /* Not reached */ #endif /* TCL_THREADS */ } /* *--------------------------------------------------------------------------- * * TclpGetPwUid -- * * Thread-safe wrappers for getpwuid(). * See "man getpwuid" for more details. * * Results: * Pointer to struct passwd on success or NULL on error. * * Side effects: * None. * *--------------------------------------------------------------------------- */ struct passwd * TclpGetPwUid(uid_t uid) { #if !defined(TCL_THREADS) return getpwuid(uid); #else ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); #if defined(HAVE_GETPWUID_R_5) struct passwd *pwPtr = NULL; return (getpwuid_r(uid, &tsdPtr->pwd, tsdPtr->pbuf, sizeof(tsdPtr->pbuf), &pwPtr) == 0 && pwPtr != NULL) ? &tsdPtr->pwd : NULL; #elif defined(HAVE_GETPWUID_R_4) return getpwuid_r(uid, &tsdPtr->pwd, tsdPtr->pbuf, sizeof(tsdPtr->pbuf)); #else struct passwd *pwPtr; Tcl_MutexLock(&compatLock); pwPtr = getpwuid(uid); if (pwPtr != NULL) { tsdPtr->pwd = *pwPtr; pwPtr = &tsdPtr->pwd; if (CopyPwd(&tsdPtr->pwd, tsdPtr->pbuf, sizeof(tsdPtr->pbuf)) == -1) { pwPtr = NULL; } } Tcl_MutexUnlock(&compatLock); return pwPtr; #endif return NULL; /* Not reached */ #endif /* TCL_THREADS */ } /* *--------------------------------------------------------------------------- * * TclpGetGrNam -- * * Thread-safe wrappers for getgrnam(). * See "man getgrnam" for more details. * * Results: * Pointer to struct group on success or NULL on error. * * Side effects: * None. * *--------------------------------------------------------------------------- */ struct group * TclpGetGrNam(const char *name) { #if !defined(TCL_THREADS) return getgrnam(name); #else ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); #if defined(HAVE_GETGRNAM_R_5) struct group *grPtr = NULL; return (getgrnam_r(name, &tsdPtr->grp, tsdPtr->gbuf, sizeof(tsdPtr->gbuf), &grPtr) == 0 && grPtr != NULL) ? &tsdPtr->grp : NULL; #elif defined(HAVE_GETGRNAM_R_4) return getgrnam_r(name, &tsdPtr->grp, tsdPtr->gbuf, sizeof(tsdPtr->gbuf)); #else struct group *grPtr; Tcl_MutexLock(&compatLock); grPtr = getgrnam(name); if (grPtr != NULL) { tsdPtr->grp = *grPtr; grPtr = &tsdPtr->grp; if (CopyGrp(&tsdPtr->grp, tsdPtr->gbuf, sizeof(tsdPtr->gbuf)) == -1) { grPtr = NULL; } } Tcl_MutexUnlock(&compatLock); return grPtr; #endif return NULL; /* Not reached */ #endif /* TCL_THREADS */ } /* *--------------------------------------------------------------------------- * * TclpGetGrGid -- * * Thread-safe wrappers for getgrgid(). * See "man getgrgid" for more details. * * Results: * Pointer to struct group on success or NULL on error. * * Side effects: * None. * *--------------------------------------------------------------------------- */ struct group * TclpGetGrGid(gid_t gid) { #if !defined(TCL_THREADS) return getgrgid(gid); #else ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); #if defined(HAVE_GETGRGID_R_5) struct group *grPtr = NULL; return (getgrgid_r(gid, &tsdPtr->grp, tsdPtr->gbuf, sizeof(tsdPtr->gbuf), &grPtr) == 0 && grPtr != NULL) ? &tsdPtr->grp : NULL; #elif defined(HAVE_GETGRGID_R_4) return getgrgid_r(gid, &tsdPtr->grp, tsdPtr->gbuf, sizeof(tsdPtr->gbuf)); #else struct group *grPtr; Tcl_MutexLock(&compatLock); grPtr = getgrgid(gid); if (grPtr != NULL) { tsdPtr->grp = *grPtr; grPtr = &tsdPtr->grp; if (CopyGrp(&tsdPtr->grp, tsdPtr->gbuf, sizeof(tsdPtr->gbuf)) == -1) { grPtr = NULL; } } Tcl_MutexUnlock(&compatLock); return grPtr; #endif return NULL; /* Not reached */ #endif /* TCL_THREADS */ } /* *--------------------------------------------------------------------------- * * TclpGetHostByName -- * * Thread-safe wrappers for gethostbyname(). * See "man gethostbyname" for more details. * * Results: * Pointer to struct hostent on success or NULL on error. * * Side effects: * None. * *--------------------------------------------------------------------------- */ struct hostent * TclpGetHostByName(const char *name) { #if !defined(TCL_THREADS) || defined(HAVE_MTSAFE_GETHOSTBYNAME) return gethostbyname(name); #else ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); #if defined(HAVE_GETHOSTBYNAME_R_5) int h_errno; return gethostbyname_r(name, &tsdPtr->hent, tsdPtr->hbuf, sizeof(tsdPtr->hbuf), &h_errno); #elif defined(HAVE_GETHOSTBYNAME_R_6) struct hostent *hePtr; int result, h_errno; result = gethostbyname_r(name, &tsdPtr->hent, tsdPtr->hbuf, sizeof(tsdPtr->hbuf), &hePtr, &h_errno); return (result == 0) ? hePtr : NULL; #elif defined(HAVE_GETHOSTBYNAME_R_3) struct hostent_data data; return (gethostbyname_r(name, &tsdPtr->hent, &data) == 0) ? &tsdPtr->hent : NULL; #else struct hostent *hePtr; Tcl_MutexLock(&compatLock); hePtr = gethostbyname(name); if (hePtr != NULL) { tsdPtr->hent = *hePtr; hePtr = &tsdPtr->hent; if (CopyHostent(&tsdPtr->hent, tsdPtr->hbuf, sizeof(tsdPtr->hbuf)) == -1) { hePtr = NULL; } } Tcl_MutexUnlock(&compatLock); return hePtr; #endif return NULL; /* Not reached */ #endif /* TCL_THREADS */ } /* *--------------------------------------------------------------------------- * * TclpGetHostByAddr -- * * Thread-safe wrappers for gethostbyaddr(). * See "man gethostbyaddr" for more details. * * Results: * Pointer to struct hostent on success or NULL on error. * * Side effects: * None. * *--------------------------------------------------------------------------- */ struct hostent * TclpGetHostByAddr(const char *addr, int length, int type) { #if !defined(TCL_THREADS) || defined(HAVE_MTSAFE_GETHOSTBYADDR) return gethostbyaddr(addr, length, type); #else ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); #if defined(HAVE_GETHOSTBYADDR_R_7) int h_errno; return gethostbyaddr_r(addr, length, type, &tsdPtr->hent, tsdPtr->hbuf, sizeof(tsdPtr->hbuf), &h_errno); #elif defined(HAVE_GETHOSTBYADDR_R_8) struct hostent *hePtr; int h_errno; return (gethostbyaddr_r(addr, length, type, &tsdPtr->hent, tsdPtr->hbuf, sizeof(tsdPtr->hbuf), &hePtr, &h_errno) == 0) ? &tsdPtr->hent : NULL; #else struct hostent *hePtr; Tcl_MutexLock(&compatLock); hePtr = gethostbyaddr(addr, length, type); if (hePtr != NULL) { tsdPtr->hent = *hePtr; hePtr = &tsdPtr->hent; if (CopyHostent(&tsdPtr->hent, tsdPtr->hbuf, sizeof(tsdPtr->hbuf)) == -1) { hePtr = NULL; } } Tcl_MutexUnlock(&compatLock); return hePtr; #endif return NULL; /* Not reached */ #endif /* TCL_THREADS */ }