/*
* 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.1 2006/09/06 13:24:32 vasiljevic Exp $
*
*/
#include "tclInt.h"
#include "tclPort.h"
#include <pwd.h>
#include <grp.h>
#include <errno.h>
#include <string.h>
/*
* 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.
*/
Tcl_Mutex compatLock;
#if !defined(HAVE_GETHOSTBYNAME_R) || !defined(HAVE_GETHOSTBYADDR_R) || \
!defined(HAVE_GETPWNAM_R) || !defined(HAVE_GETGRNAM_R)
/*
*---------------------------------------------------------------------------
*
* CopyArray --
*
* Copies array of strings (or fixed values) 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 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_GETPWNAM_R) || !defined(HAVE_GETGRNAM_R) */
/*
*---------------------------------------------------------------------------
*
* 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
*
*---------------------------------------------------------------------------
*/
#if !defined(HAVE_GETHOSTBYNAME_R) || !defined(HAVE_GETHOSTBYADDR_R)
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;
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;
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) */
/*
*---------------------------------------------------------------------------
*
* 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.
*
*---------------------------------------------------------------------------
*/
#if !defined(HAVE_GETPWNAM_R) || !defined(HAVE_GETPWUID_R)
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 /* HAVE_GETPWNAM_R || HAVE_GETPWUID_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.
*
*---------------------------------------------------------------------------
*/
#if !defined(HAVE_GETGRNAM_R) || !defined(HAVE_GETGRGID_R)
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 */
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 /* HAVE_GETGRNAM_R || HAVE_GETGRGID_R*/
/*
*---------------------------------------------------------------------------
*
* TclpGetPwNam --
*
* Thread-safe wrappers for getpwnam().
* See "man getpwnam" for more details.
*
* Results:
* 0 on success or -1 on error.
*
* Side effects:
* None.
*
*---------------------------------------------------------------------------
*/
int
TclpGetPwNam(const char *name, struct passwd *pwbuf, char *buf, size_t buflen,
struct passwd **pwbufp)
{
int result = 0;
#if defined(HAVE_GETPWNAM_R_5)
result = getpwnam_r(name, pwbuf, buf, buflen, pwbufp);
#elif defined(HAVE_GETPWNAM_R_4)
*pwbufp = getpwnam_r(name, pwbuf, buf, buflen);
if (*pwbufp == NULL) {
result = -1;
}
#else
struct passwd *pwPtr = NULL;
Tcl_MutexLock(&compatLock);
pwPtr = getpwnam(name);
if (pwPtr == NULL) {
result = -1;
} else {
*pwbuf = *pwPtr;
*pwbufp = pwbuf;
result = CopyPwd(pwbuf, buf, buflen);
}
Tcl_MutexUnlock(&compatLock);
#endif
return result;
}
/*
*---------------------------------------------------------------------------
*
* TclpGetPwUid --
*
* Thread-safe wrappers for getpwuid().
* See "man getpwuid" for more details.
*
* Results:
* 0 on success or -1 on error.
*
* Side effects:
* None.
*
*---------------------------------------------------------------------------
*/
int
TclpGetPwUid(uid_t uid, struct passwd *pwbuf, char *buf, size_t buflen,
struct passwd **pwbufp)
{
int result = 0;
#if defined(HAVE_GETPWUID_R_5)
result = getpwuid_r(uid, pwbuf, buf, buflen, pwbufp);
#elif defined(HAVE_GETPWUID_R_4)
*pwbufp = getpwuid_r(uid, pwbuf, buf, buflen);
if (*pwbufp == NULL) {
result = -1;
}
#else
struct passwd *pwPtr = NULL;
Tcl_MutexLock(&compatLock);
pwPtr = getpwuid(uid);
if (pwPtr == NULL) {
result = -1;
} else {
*pwbuf = *pwPtr;
*pwbufp = pwbuf;
result = CopyPwd(pwbuf, buf, buflen);
}
Tcl_MutexUnlock(&compatLock);
#endif
return result;
}
/*
*---------------------------------------------------------------------------
*
* TclpGetGrNam --
*
* Thread-safe wrappers for getgrnam().
* See "man getgrnam" for more details.
*
* Results:
* 0 on success or -1 on error.
*
* Side effects:
* None.
*
*---------------------------------------------------------------------------
*/
int
TclpGetGrNam(const char *name, struct group *gbuf, char *buf, size_t buflen,
struct group **gbufp)
{
int result = 0;
#if defined(HAVE_GETGRNAM_R_5)
result = getgrnam_r(name, gbuf, buf, buflen, gbufp);
#elif defined(HAVE_GETGRNAM_R_4)
*gbufp = getgrgid_r(name, gbuf, buf, buflen);
if (*gbufp == NULL) {
result = -1;
}
#else
struct group *grPtr = NULL;
Tcl_MutexLock(&compatLock);
grPtr = getgrnam(name);
if (grPtr == NULL) {
result = -1;
} else {
*gbuf = *grPtr;
*gbufp = gbuf;
result = CopyGrp(gbuf, buf, buflen);
}
Tcl_MutexUnlock(&compatLock);
#endif
return result;
}
/*
*---------------------------------------------------------------------------
*
* TclpGetGrGid --
*
* Thread-safe wrappers for getgrgid().
* See "man getgrgid" for more details.
*
* Results:
* 0 on success or -1 on error.
*
* Side effects:
* None.
*
*---------------------------------------------------------------------------
*/
int
TclpGetGrGid(gid_t gid, struct group *gbuf, char *buf, size_t buflen,
struct group **gbufp)
{
int result = 0;
#if defined(HAVE_GETGRGID_R_5)
result = getgrgid_r(gid, gbuf, buf, buflen, gbufp);
#elif defined(HAVE_GETGRGID_R_4)
*gbufp = getgrgid_r(gid, gbuf, buf, buflen);
if (*gbufp == NULL) {
result = -1;
}
#else
struct group *grPtr = NULL;
Tcl_MutexLock(&compatLock);
grPtr = getgrgid(gid);
if (grPtr == NULL) {
result = -1;
} else {
*gbuf = *grPtr;
*gbufp = gbuf;
result = CopyGrp(gbuf, buf, buflen);
}
Tcl_MutexUnlock(&compatLock);
#endif
return result;
}
/*
*---------------------------------------------------------------------------
*
* 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, struct hostent *hbuf, char *buf,
size_t buflen, int *h_errnop)
{
#if defined(HAVE_GETHOSTBYNAME_R_5)
return gethostbyname_r(name, hbuf, buf, buflen, h_errnop);
#elif defined(HAVE_GETHOSTBYNAME_R_6)
struct hostent *hePtr;
return (gethostbyname_r(name, hbuf, buf, buflen, &hePtr,
h_errnop) == 0) ? hbuf : NULL;
#elif defined(HAVE_GETHOSTBYNAME_R_3)
struct hostent_data data;
if (-1 == gethostbyname_r(host, hbuf, &data)) {
*h_errnop = h_errno;
return NULL;
} else {
return &hbuf;
}
#else
struct hostent *hePtr = NULL;
Tcl_MutexLock(&compatLock);
hePtr = gethostbyname(name);
if (hePtr != NULL) {
*hbuf = *hePtr;
if (-1 == CopyHostent(hbuf, buf, buflen)) {
hePtr = NULL;
} else {
hePtr = hbuf;
}
}
Tcl_MutexUnlock(&compatLock);
return hePtr;
#endif
return NULL; /* Not reached */
}
/*
*---------------------------------------------------------------------------
*
* 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, struct hostent *hbuf,
char *buf, size_t buflen, int *h_errnop)
{
#if defined(HAVE_GETHOSTBYADDR_R_7)
return gethostbyaddr_r(addr, length, type, hbuf, buf, buflen, h_errnop);
#elif defined(HAVE_GETHOSTBYADDR_R_8)
struct hostent *hePtr;
return (gethostbyaddr_r(addr, length, type, hbuf, buf, buflen,
&hePtr, h_errnop) == 0) ? hbuf : NULL;
#else
struct hostent *hePtr = NULL;
Tcl_MutexLock(&compatLock);
hePtr = gethostbyaddr(addr, length, type);
if (hePtr != NULL) {
*hbuf = *hePtr;
if (-1 == CopyHostent(hbuf, buf, buflen)) {
hePtr = NULL;
} else {
hePtr = hbuf;
}
}
Tcl_MutexUnlock(&compatLock);
return hePtr;
#endif
return NULL; /* Not reached */
}