/*
* 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.2.10 2006/09/12 22:05:03 andreas_kupries Exp $
*
*/
#include "tclInt.h"
#include "tclPort.h"
#include <pwd.h>
#include <grp.h>
#include <errno.h>
#include <string.h>
/*
* 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 h_errno;
return (gethostbyname_r(name, &tsdPtr->hent, tsdPtr->hbuf,
sizeof(tsdPtr->hbuf), &hePtr, &h_errno) == 0) ?
&tsdPtr->hent : 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 */
}