1/* 2 Unix SMB/CIFS implementation. 3 Broadcom implementation of GNU C Library functions used by Samba 4 system utilities. 5 6 Copyright (C) 7 Copyright (C) Broadcom 2004 8 9 This program is free software; you can redistribute it and/or modify 10 it under the terms of the GNU General Public License as published by 11 the Free Software Foundation; either version 2 of the License, or 12 (at your option) any later version. 13 14 This program is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 GNU General Public License for more details. 18 19 You should have received a copy of the GNU General Public License 20 along with this program; if not, write to the Free Software 21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 22*/ 23 24/* 25 We've had some issues with the standard system calls, so at least 26 temporarily, we've implemented our own versions. 27 */ 28 29#include "includes.h" 30#include <unistd.h> 31#include <stdlib.h> 32#include <string.h> 33#include <errno.h> 34 35 36#define PASSWD_FILENAME "/tmp/passwd" 37#define GROUP_FILENAME "/tmp/group" 38 39 40static BOOL 41ug_lock( const char * filename, int open_flags, int *file_descriptor ) 42{ 43 int result; 44 45 46 *file_descriptor = open( filename, open_flags ); 47 if (*file_descriptor == -1) 48 { 49 DEBUG(0, ("ERROR: open of %s for lock failed with %u: %s.\n", filename, errno, strerror(errno))); 50 return False; 51 } 52 result = flock(*file_descriptor, LOCK_EX); 53 if (result == -1) 54 { 55 DEBUG(0, ("ERROR: lock of %s failed with %u: %s.\n", filename, errno, strerror(errno))); 56 close(*file_descriptor); 57 return False; 58 } 59 return True; 60} 61 62 63struct passwd * brcm_getpwnam(const char *name) 64{ 65 int fd; 66 static struct passwd result; 67 static char line[256]; 68 FILE *fp; 69 char *tok; 70 71 if (name == NULL || *name == '\0') 72 return NULL; 73 74 //DEBUG(1, ("entered name=%s\n", name)); 75 76 if (!ug_lock(PASSWD_FILENAME, O_RDONLY, &fd)) 77 { 78 return NULL; 79 } 80 81 if ((fp = fdopen(fd, "r")) == NULL) 82 { 83 DEBUG(1, ("open failed, errno=%u, str='%s'\n", errno, strerror(errno))); 84 close(fd); 85 return NULL; 86 } 87 88 while (fgets(line, 256, fp) != NULL) 89 { 90 char *ptr = line; 91 //DEBUG(1, ("line='%s'\n", line)); 92 93 tok = strsep( &ptr, ":" ); 94 if (tok == NULL) 95 return NULL; 96 if (strcmp(name, tok) == 0) 97 { 98 /* Yeah, we found it. Parse the rest. */ 99 result.pw_name = tok; 100 result.pw_passwd = strsep( &ptr, ":" ); 101 result.pw_uid = strtol( strsep( &ptr, ":" ), NULL, 10 ); 102 result.pw_gid = strtol( strsep( &ptr, ":" ), NULL, 10 ); 103 result.pw_gecos = strsep( &ptr, ":" ); 104 result.pw_dir = strsep( &ptr, ":" ); 105 result.pw_shell = strsep( &ptr, ":" ); 106 107 //DEBUG(1, ("match! %s:%s:%u:%u:%s:%s:%s\n", result.pw_name,result.pw_passwd,result.pw_uid,result.pw_gid,result.pw_gecos,result.pw_dir,result.pw_shell)); 108 109 fclose( fp ); 110 return &result; 111 } 112 } 113 fclose( fp ); 114 return NULL; 115} 116 117 118struct passwd * brcm_getpwuid(uid_t uid) 119{ 120 int fd; 121 static struct passwd result; 122 static char line[256]; 123 FILE *fp; 124 char *tok; 125 126 //DEBUG(1, ("gew_getpwuid:1: entered uid=%u\n",uid)); 127 128 if (!ug_lock(PASSWD_FILENAME, O_RDONLY, &fd)) 129 { 130 return NULL; 131 } 132 133 if ((fp = fdopen(fd, "r")) == NULL) 134 { 135 DEBUG(1, ("gew_getpwuid:1: open failed, errno=%u, str='%s'\n", errno, strerror(errno))); 136 close(fd); 137 return NULL; 138 } 139 140 while (fgets(line, 256, fp) != NULL) 141 { 142 char *ptr = line; 143 //DEBUG(1, ("gew_getpwuid:1: line='%s'\n", line)); 144 145 tok = strsep( &ptr, ":" ); 146 if (tok == NULL) 147 return NULL; 148 149 /* Use 'result' as a temporary. */ 150 result.pw_name = tok; 151 result.pw_passwd = strsep( &ptr, ":" ); 152 result.pw_uid = strtol( strsep( &ptr, ":" ), NULL, 10 ); 153 result.pw_gid = strtol( strsep( &ptr, ":" ), NULL, 10 ); 154 result.pw_gecos = strsep( &ptr, ":" ); 155 result.pw_dir = strsep( &ptr, ":" ); 156 result.pw_shell = strsep( &ptr, ":" ); 157 158 if (uid == result.pw_uid) 159 { 160 //DEBUG(1, ("gew_getpwuid:1: match! %s:%s:%u:%u:%s:%s:%s\n", result.pw_name,result.pw_passwd,result.pw_uid,result.pw_gid,result.pw_gecos,result.pw_dir,result.pw_shell)); 161 162 fclose( fp ); 163 return &result; 164 } 165 } 166 fclose( fp ); 167 return NULL; 168} 169 170static int brcm_pw_fd = -1; 171 172void brcm_setpwent(void) 173{ 174 if (brcm_pw_fd != -1) 175 close(brcm_pw_fd); 176 177 brcm_pw_fd = open(GROUP_FILENAME, O_RDONLY); 178} 179 180void brcm_endpwent(void) 181{ 182 if (brcm_pw_fd != -1) 183 close(brcm_pw_fd); 184 brcm_pw_fd = -1; 185} 186 187struct passwd *brcm_getpwent(void) 188{ 189 if (brcm_pw_fd != -1) 190 return (brcm__getpwent(brcm_pw_fd)); 191 return NULL; 192} 193 194 195 196#if 1 197struct group * brcm_getgrgid(gid_t gid) 198{ 199 int fd; 200 static struct group *group; 201 FILE *fp; 202 203// DEBUG(0, ("brcm_getgrgid: entered gid=%d\n",gid)); 204 /* 205 if (!ug_lock(GROUP_FILENAME, O_RDONLY, &fd)) 206 { 207 return NULL; 208 } 209 210 if ((fp = fdopen(fd, "r")) == NULL) 211 { 212 DEBUG(1, ("vkp_getgrgid:1: open failed, errno=%u, str='%s'\n", errno, strerror(errno))); 213 close(fd); 214 return NULL; 215 } 216 */ 217 218 if ((fd = open(GROUP_FILENAME, O_RDONLY)) < 0) 219 return NULL; 220 221 while ((group = (struct group *) brcm_getgrent(fd)) != NULL) 222 if (group->gr_gid == gid) { 223// DEBUG(0, ("brcm_getgrgid: found group with gid=%d\n",gid)); 224 close(fd); 225 return group; 226 } 227 228 close(fd); 229 return NULL; 230} 231#endif 232 233#if 1 234struct group * brcm_getgrnam(const char *name) 235{ 236 static struct group *group; 237 FILE *fp; 238 int fd; 239 240 if (name == NULL || *name == '\0') 241 return NULL; 242 243//DEBUG(0, ("brcm_getgrnam enterred name=%s\n", name)); 244/* 245 if (!ug_lock(GROUP_FILENAME, O_RDONLY, &fd)) 246 { 247 return NULL; 248 } 249 250 if ((fp = fdopen(fd, "r")) == NULL) 251 { 252 DEBUG(1, ("vkp_getgrgnam:1: open failed, errno=%u, str='%s'\n", errno, strerror(errno))); 253 close(fd); 254 return NULL; 255 } 256*/ 257 258 if ((fd = open(GROUP_FILENAME, O_RDONLY)) < 0) 259 return NULL; 260 261 while ((group = (struct group *) brcm_getgrent(fd)) != NULL) 262 if (!strcmp(group->gr_name,name)) { 263// DEBUG(0, ("brcm_getgrnam found group with name=%s\n", name)); 264 close(fd); 265 return group; 266 } 267 268 close(fd); 269 return NULL; 270} 271#endif 272 273#if 0 274struct passwd * brcm_getpwuid(uid_t uid) 275{ 276 static struct passwd result; 277 static char line[256]; 278 FILE *fp; 279 char *tok; 280 281DEBUG(1, ("gew_getpwuid:1: entered uid=%u\n",uid)); 282 283 if ((fp = fopen(PASSWD_FILENAME, "r")) == NULL) 284 { 285DEBUG(1, ("gew_getpwuid:1: open failed, errno=%u, str='%s'\n", errno, strerror(errno))); 286 return NULL; 287 } 288 289 while (fgets(line, 256, fp) != NULL) 290 { 291 char *ptr = line; 292DEBUG(1, ("gew_getpwuid:1: line='%s'\n", line)); 293 294 tok = strsep( &ptr, ":" ); 295 if (tok == NULL) 296 return NULL; 297 298 /* Use 'result' as a temporary. */ 299 result.pw_name = tok; 300 result.pw_passwd = strsep( &ptr, ":" ); 301 result.pw_uid = strtol( strsep( &ptr, ":" ), NULL, 10 ); 302 result.pw_gid = strtol( strsep( &ptr, ":" ), NULL, 10 ); 303 result.pw_gecos = strsep( &ptr, ":" ); 304 result.pw_dir = strsep( &ptr, ":" ); 305 result.pw_shell = strsep( &ptr, ":" ); 306 307 if (uid == result.pw_uid) 308 { 309DEBUG(1, ("gew_getpwuid:1: match! %s:%s:%u:%u:%s:%s:%s\n", result.pw_name,result.pw_passwd,result.pw_uid,result.pw_gid,result.pw_gecos,result.pw_dir,result.pw_shell)); 310 311 fclose( fp ); 312 return &result; 313 } 314 } 315 fclose( fp ); 316 return NULL; 317} 318#endif 319 320 321/* 322 * Define GR_SCALE_DYNAMIC if you want grp to dynamically scale its read buffer 323 * so that lines of any length can be used. On very very small systems, 324 * you may want to leave this undefined becasue it will make the grp functions 325 * somewhat larger (because of the inclusion of malloc and the code necessary). 326 * On larger systems, you will want to define this, because grp will _not_ 327 * deal with long lines gracefully (they will be skipped). 328 */ 329#undef GR_SCALE_DYNAMIC 330 331#ifndef GR_SCALE_DYNAMIC 332/* 333 * If scaling is not dynamic, the buffers will be statically allocated, and 334 * maximums must be chosen. GR_MAX_LINE_LEN is the maximum number of 335 * characters per line in the group file. GR_MAX_MEMBERS is the maximum 336 * number of members of any given group. 337 */ 338#define GR_MAX_LINE_LEN 128 339/* GR_MAX_MEMBERS = (GR_MAX_LINE_LEN-(24+3+6))/9 */ 340#define GR_MAX_MEMBERS 11 341 342#endif /* !GR_SCALE_DYNAMIC */ 343 344/* 345 * This is the core group-file read function. It behaves exactly like 346 * getgrent() except that it is passed a file descriptor. getgrent() 347 * is just a wrapper for this function. 348 */ 349struct group *brcm_getgrent(int grp_fd) 350{ 351#ifndef GR_SCALE_DYNAMIC 352 static char line_buff[GR_MAX_LINE_LEN]; 353 static char *members[GR_MAX_MEMBERS]; 354#else 355 static char *line_buff = NULL; 356 static char **members = NULL; 357 short line_index; 358 short buff_size; 359#endif 360 static struct group group; 361 register char *ptr; 362 char *field_begin; 363 short member_num; 364 char *endptr; 365 int line_len; 366 367 368 /* We use the restart label to handle malformatted lines */ 369 restart: 370#ifdef GR_SCALE_DYNAMIC 371 line_index = 0; 372 buff_size = 256; 373#endif 374 375#ifndef GR_SCALE_DYNAMIC 376 /* Read the line into the static buffer */ 377 if ((line_len = read(grp_fd, line_buff, GR_MAX_LINE_LEN)) <= 0) 378 return NULL; 379 field_begin = strchr(line_buff, '\n'); 380 if (field_begin != NULL) 381 lseek(grp_fd, (long) (1 + field_begin - (line_buff + line_len)), 382 SEEK_CUR); 383 else { /* The line is too long - skip it :-\ */ 384 385 do { 386 if ((line_len = read(grp_fd, line_buff, GR_MAX_LINE_LEN)) <= 0) 387 return NULL; 388 } while (!(field_begin = strchr(line_buff, '\n'))); 389 lseek(grp_fd, (long) ((field_begin - line_buff) - line_len + 1), 390 SEEK_CUR); 391 goto restart; 392 } 393 if (*line_buff == '#' || *line_buff == ' ' || *line_buff == '\n' || 394 *line_buff == '\t') 395 goto restart; 396 *field_begin = '\0'; 397 398#else /* !GR_SCALE_DYNAMIC */ 399 line_buff = realloc(line_buff, buff_size); 400 while (1) { 401 if ((line_len = read(grp_fd, line_buff + line_index, 402 buff_size - line_index)) <= 0) 403 return NULL; 404 field_begin = strchr(line_buff, '\n'); 405 if (field_begin != NULL) { 406 lseek(grp_fd, 407 (long) (1 + field_begin - 408 (line_len + line_index + line_buff)), SEEK_CUR); 409 *field_begin = '\0'; 410 if (*line_buff == '#' || *line_buff == ' ' 411 || *line_buff == '\n' || *line_buff == '\t') 412 goto restart; 413 break; 414 } else { /* Allocate some more space */ 415 416 line_index = buff_size; 417 buff_size += 256; 418 line_buff = realloc(line_buff, buff_size); 419 } 420 } 421#endif /* GR_SCALE_DYNAMIC */ 422 423 /* Now parse the line */ 424 group.gr_name = line_buff; 425 ptr = strchr(line_buff, ':'); 426 if (ptr == NULL) 427 goto restart; 428 *ptr++ = '\0'; 429 430 group.gr_passwd = ptr; 431 ptr = strchr(ptr, ':'); 432 if (ptr == NULL) 433 goto restart; 434 *ptr++ = '\0'; 435 436 field_begin = ptr; 437 ptr = strchr(ptr, ':'); 438 if (ptr == NULL) 439 goto restart; 440 *ptr++ = '\0'; 441 442 group.gr_gid = (gid_t) strtoul(field_begin, &endptr, 10); 443 if (*endptr != '\0') 444 goto restart; 445 446 member_num = 0; 447 field_begin = ptr; 448 449#ifndef GR_SCALE_DYNAMIC 450 while ((ptr = strchr(ptr, ',')) != NULL) { 451 *ptr = '\0'; 452 ptr++; 453 members[member_num] = field_begin; 454 field_begin = ptr; 455 member_num++; 456 } 457 if (*field_begin == '\0') 458 members[member_num] = NULL; 459 else { 460 members[member_num] = field_begin; 461 members[member_num + 1] = NULL; 462 } 463#else /* !GR_SCALE_DYNAMIC */ 464 free(members); 465 members = (char **) malloc((member_num + 1) * sizeof(char *)); 466 for ( ; field_begin && *field_begin != '\0'; field_begin = ptr) { 467 if ((ptr = strchr(field_begin, ',')) != NULL) 468 *ptr++ = '\0'; 469 members[member_num++] = field_begin; 470 members = (char **) realloc(members, 471 (member_num + 1) * sizeof(char *)); 472 } 473 members[member_num] = NULL; 474#endif /* GR_SCALE_DYNAMIC */ 475 476 group.gr_mem = members; 477 return &group; 478} 479 480 481#if 0 482struct group *brcm_getgrent(int grp_fd) 483{ 484#ifndef GR_SCALE_DYNAMIC 485 static char line_buff[GR_MAX_LINE_LEN]; 486 static char *members[GR_MAX_MEMBERS]; 487#else 488 static char *line_buff = NULL; 489 static char **members = NULL; 490 short line_index; 491 short buff_size; 492#endif 493 static struct group group; 494 register char *ptr; 495 char *field_begin; 496 short member_num; 497 char *endptr; 498 int line_len; 499 int count=0; 500 501 502 /* We use the restart label to handle malformatted lines */ 503 504 restart: 505 DEBUG(0, ("vkp_getgrent top: count='%d'\n", count++)); 506 // return NULL; 507#ifdef GR_SCALE_DYNAMIC 508 line_index = 0; 509 buff_size = 256; 510#endif 511 512#ifndef GR_SCALE_DYNAMIC 513 /* Read the line into the static buffer */ 514 if ((line_len = read(grp_fd, line_buff, GR_MAX_LINE_LEN)) <= 0) 515 return NULL; 516 DEBUG(0, ("vkp_getgrent: read line into buffer \n")); 517 field_begin = strchr(line_buff, '\n'); 518 if (field_begin != NULL) 519 { 520 DEBUG(0, ("vkp_getgrent: before lseek \n")); 521 lseek(grp_fd, (long) (1 + field_begin - (line_buff + line_len)), 522 SEEK_CUR); 523 DEBUG(0, ("vkp_getgrent: after lseek \n")); 524 } 525 else { /* The line is too long - skip it :-\ */ 526 527 do { 528 if ((line_len = read(grp_fd, line_buff, GR_MAX_LINE_LEN)) <= 0) 529 return NULL; 530 } while (!(field_begin = strchr(line_buff, '\n'))); 531 lseek(grp_fd, (long) ((field_begin - line_buff) - line_len + 1), 532 SEEK_CUR); 533 DEBUG(0, ("vkp_getgrent: restart line too long\n")); 534 goto restart; 535 } 536 if (*line_buff == '#' || *line_buff == ' ' || *line_buff == '\n' || 537 *line_buff == '\t') 538 { 539 DEBUG(0, ("vkp_getgrent: restart line has iffy chars\n")); 540 goto restart; 541 } 542 *field_begin = '\0'; 543 544#else /* !GR_SCALE_DYNAMIC */ 545 line_buff = realloc(line_buff, buff_size); 546 while (1) { 547 if ((line_len = read(grp_fd, line_buff + line_index, 548 buff_size - line_index)) <= 0) 549 return NULL; 550 field_begin = strchr(line_buff, '\n'); 551 if (field_begin != NULL) { 552 lseek(grp_fd, 553 (long) (1 + field_begin - 554 (line_len + line_index + line_buff)), SEEK_CUR); 555 *field_begin = '\0'; 556 if (*line_buff == '#' || *line_buff == ' ' 557 || *line_buff == '\n' || *line_buff == '\t') 558 goto restart; 559 break; 560 } else { /* Allocate some more space */ 561 562 line_index = buff_size; 563 buff_size += 256; 564 line_buff = realloc(line_buff, buff_size); 565 } 566 } 567#endif /* GR_SCALE_DYNAMIC */ 568 DEBUG(0,("vkp_getgrent: parsing the line\n")); 569 DEBUG(0,("vkp_getgrent: line is %s\n",line_buff)); 570 /* Now parse the line */ 571 group.gr_name = line_buff; 572 DEBUG(0,("vkp_getgrent: gr_name is %s\n",group.gr_name)); 573 ptr = strchr(line_buff, ':'); 574 if (ptr == NULL) 575 { 576 DEBUG(0, ("vkp_getgrent: restart parsing gr_name failed\n")); 577 goto restart; 578 } 579 *ptr++ = '\0'; 580 group.gr_passwd = ptr; 581 DEBUG(0,("vkp_getgrent: gr_pwd is %s\n",group.gr_passwd)); 582 ptr = strchr(ptr, ':'); 583 if (ptr == NULL) 584 { 585 DEBUG(0, ("vkp_getgrent: restart parsing gr_pwd failed\n")); 586 goto restart; 587 } 588 *ptr++ = '\0'; 589 590 field_begin = ptr; 591 ptr = strchr(ptr, ':'); 592 if (ptr == NULL) 593 { 594 DEBUG(0, ("vkp_getgrent: restart parsing gr_gid failed\n")); 595 goto restart; 596 } 597 *ptr++ = '\0'; 598 599 group.gr_gid = (gid_t) strtoul(field_begin, &endptr, 10); 600 DEBUG(0,("vkp_getgrent: gr_gid is %d\n",group.gr_gid)); 601 if (*endptr != '\0') 602 goto restart; 603 604 member_num = 0; 605 field_begin = ptr; 606 607#ifndef GR_SCALE_DYNAMIC 608 DEBUG(0,("vkp_getgrent: parse members\n")); 609 while ((ptr = strchr(ptr, ',')) != NULL) { 610 *ptr = '\0'; 611 ptr++; 612 members[member_num] = field_begin; 613 field_begin = ptr; 614 member_num++; 615 } 616 DEBUG(0,("vkp_getgrent: parsed members\n")); 617 // return(NULL); 618 if (*field_begin == '\0') 619 members[member_num] = NULL; 620 else { 621 members[member_num] = field_begin; 622 members[member_num + 1] = NULL; 623 } 624#else /* !GR_SCALE_DYNAMIC */ 625 free(members); 626 members = (char **) malloc((member_num + 1) * sizeof(char *)); 627 for ( ; field_begin && *field_begin != '\0'; field_begin = ptr) { 628 if ((ptr = strchr(field_begin, ',')) != NULL) 629 *ptr++ = '\0'; 630 members[member_num++] = field_begin; 631 members = (char **) realloc(members, 632 (member_num + 1) * sizeof(char *)); 633 } 634 members[member_num] = NULL; 635#endif /* GR_SCALE_DYNAMIC */ 636 637 group.gr_mem = members; 638 DEBUG(0,("vkp_getgrent: parsed members are %s\n",group.gr_mem)); 639 return(NULL); 640 return &group; 641} 642#endif 643 644#define PWD_BUFFER_SIZE 256 645 646/* This isn't as flash as my previous version -- it doesn't dynamically 647 scale down the gecos on too-long lines, but it also makes fewer syscalls, 648 so it's probably nicer. Write me if you want the old version. Maybe I 649 should include it as a build-time option... ? 650 -Nat <ndf@linux.mit.edu> */ 651 652struct passwd *brcm__getpwent(int pwd_fd) 653{ 654 static char line_buff[PWD_BUFFER_SIZE]; 655 static struct passwd passwd; 656 char *field_begin; 657 char *endptr; 658 char *gid_ptr=NULL; 659 char *uid_ptr=NULL; 660 int line_len; 661 int i; 662 663 /* We use the restart label to handle malformatted lines */ 664 restart: 665 /* Read the passwd line into the static buffer using a minimal of 666 syscalls. */ 667 if ((line_len = read(pwd_fd, line_buff, PWD_BUFFER_SIZE)) <= 0) 668 return NULL; 669 field_begin = strchr(line_buff, '\n'); 670 if (field_begin != NULL) 671 lseek(pwd_fd, (long) (1 + field_begin - (line_buff + line_len)), 672 SEEK_CUR); 673 else { /* The line is too long - skip it. :-\ */ 674 675 do { 676 if ((line_len = read(pwd_fd, line_buff, PWD_BUFFER_SIZE)) <= 0) 677 return NULL; 678 } while (!(field_begin = strchr(line_buff, '\n'))); 679 lseek(pwd_fd, (long) (field_begin - line_buff) - line_len + 1, 680 SEEK_CUR); 681 goto restart; 682 } 683 if (*line_buff == '#' || *line_buff == ' ' || *line_buff == '\n' || 684 *line_buff == '\t') 685 goto restart; 686 *field_begin = '\0'; 687 688 /* We've read the line; now parse it. */ 689 field_begin = line_buff; 690 for (i = 0; i < 7; i++) { 691 switch (i) { 692 case 0: 693 passwd.pw_name = field_begin; 694 break; 695 case 1: 696 passwd.pw_passwd = field_begin; 697 break; 698 case 2: 699 uid_ptr = field_begin; 700 break; 701 case 3: 702 gid_ptr = field_begin; 703 break; 704 case 4: 705 passwd.pw_gecos = field_begin; 706 break; 707 case 5: 708 passwd.pw_dir = field_begin; 709 break; 710 case 6: 711 passwd.pw_shell = field_begin; 712 break; 713 } 714 if (i < 6) { 715 field_begin = strchr(field_begin, ':'); 716 if (field_begin == NULL) 717 goto restart; 718 *field_begin++ = '\0'; 719 } 720 } 721 passwd.pw_gid = (gid_t) strtoul(gid_ptr, &endptr, 10); 722 if (*endptr != '\0') 723 goto restart; 724 725 passwd.pw_uid = (uid_t) strtoul(uid_ptr, &endptr, 10); 726 if (*endptr != '\0') 727 goto restart; 728 729 return &passwd; 730} 731 732