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