2 * Copyright (C) 2003 Manuel Novoa III <mjn3@uclibc.org>
3 * Copyright (C) 2000-2006 Erik Andersen <andersen@uclibc.org>
5 * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
8 /* Nov 6, 2003 Initial version.
10 * NOTE: This implementation is quite strict about requiring all
11 * field seperators. It also does not allow leading whitespace
12 * except when processing the numeric fields. glibc is more
13 * lenient. See the various glibc difference comments below.
16 * Move to dynamic allocation of (currently statically allocated)
17 * buffers; especially for the group-related functions since
18 * large group member lists will cause error returns.
34 #ifdef __UCLIBC_HAS_SHADOW__
37 #include <bits/uClibc_mutex.h>
39 /**********************************************************************/
40 /* Prototypes for internal functions. */
42 extern int __parsepwent(void *pw
, char *line
) attribute_hidden
;
43 extern int __parsegrent(void *gr
, char *line
) attribute_hidden
;
44 extern int __parsespent(void *sp
, char *line
) attribute_hidden
;
46 extern int __pgsreader(int (*__parserfunc
)(void *d
, char *line
), void *data
,
47 char *__restrict line_buff
, size_t buflen
, FILE *f
) attribute_hidden
;
49 extern gid_t
* __getgrouplist_internal(const char *user
, gid_t gid
, int *ngroups
) attribute_hidden
;
51 /**********************************************************************/
52 /* For the various fget??ent_r funcs, return
55 * ENOENT: end-of-file encountered
56 * ERANGE: buflen too small
57 * other error values possible. See __pgsreader.
59 * Also, *result == resultbuf on success and NULL on failure.
61 * NOTE: glibc difference - For the ENOENT case, glibc also sets errno.
62 * We do not, as it really isn't an error if we reach the end-of-file.
63 * Doing so is analogous to having fgetc() set errno on EOF.
65 /**********************************************************************/
69 int fgetpwent_r(FILE *__restrict stream
, struct passwd
*__restrict resultbuf
,
70 char *__restrict buffer
, size_t buflen
,
71 struct passwd
**__restrict result
)
77 if (!(rv
= __pgsreader(__parsepwent
, resultbuf
, buffer
, buflen
, stream
))) {
83 libc_hidden_def(fgetpwent_r
)
87 /**********************************************************************/
91 int fgetgrent_r(FILE *__restrict stream
, struct group
*__restrict resultbuf
,
92 char *__restrict buffer
, size_t buflen
,
93 struct group
**__restrict result
)
99 if (!(rv
= __pgsreader(__parsegrent
, resultbuf
, buffer
, buflen
, stream
))) {
105 libc_hidden_def(fgetgrent_r
)
109 /**********************************************************************/
112 int fgetspent_r(FILE *__restrict stream
, struct spwd
*__restrict resultbuf
,
113 char *__restrict buffer
, size_t buflen
,
114 struct spwd
**__restrict result
)
120 if (!(rv
= __pgsreader(__parsespent
, resultbuf
, buffer
, buflen
, stream
))) {
126 libc_hidden_def(fgetspent_r
)
129 /**********************************************************************/
132 int sgetspent_r(const char *string
, struct spwd
*result_buf
,
133 char *buffer
, size_t buflen
, struct spwd
**result
)
139 if (buflen
< __UCLIBC_PWD_BUFFER_SIZE__
) {
145 if (string
!= buffer
) {
146 if (strlen(string
) >= buflen
) {
149 strcpy(buffer
, string
);
152 if (!(rv
= __parsespent(result_buf
, buffer
))) {
153 *result
= result_buf
;
159 libc_hidden_def(sgetspent_r
)
162 /**********************************************************************/
165 /* This function is non-standard and is currently not built. It seems
166 * to have been created as a reentrant version of the non-standard
167 * functions getspuid. Why getspuid was added, I do not know. */
170 int getspuid_r(uid_t uid
, struct spwd
*__restrict resultbuf
,
171 char *__restrict buffer
, size_t buflen
,
172 struct spwd
**__restrict result
)
176 struct passwd password
;
177 char pwd_buff
[__UCLIBC_PWD_BUFFER_SIZE__
];
180 if (!(rv
= getpwuid_r(uid
, &password
, pwd_buff
, sizeof(pwd_buff
), &pp
))) {
181 rv
= getspnam_r(password
.pw_name
, resultbuf
, buffer
, buflen
, result
);
188 /**********************************************************************/
192 int getpw(uid_t uid
, char *buf
)
194 struct passwd resultbuf
;
195 struct passwd
*result
;
196 char buffer
[__UCLIBC_PWD_BUFFER_SIZE__
];
200 } else if (!getpwuid_r(uid
, &resultbuf
, buffer
, sizeof(buffer
), &result
)) {
201 if (sprintf(buf
, "%s:%s:%lu:%lu:%s:%s:%s\n",
202 resultbuf
.pw_name
, resultbuf
.pw_passwd
,
203 (unsigned long)(resultbuf
.pw_uid
),
204 (unsigned long)(resultbuf
.pw_gid
),
205 resultbuf
.pw_gecos
, resultbuf
.pw_dir
,
206 resultbuf
.pw_shell
) >= 0
216 /**********************************************************************/
219 __UCLIBC_MUTEX_STATIC(mylock
, PTHREAD_MUTEX_INITIALIZER
);
221 static FILE *pwf
/*= NULL*/;
225 __UCLIBC_MUTEX_LOCK(mylock
);
229 __UCLIBC_MUTEX_UNLOCK(mylock
);
234 __UCLIBC_MUTEX_LOCK(mylock
);
239 __UCLIBC_MUTEX_UNLOCK(mylock
);
243 int getpwent_r(struct passwd
*__restrict resultbuf
,
244 char *__restrict buffer
, size_t buflen
,
245 struct passwd
**__restrict result
)
249 __UCLIBC_MUTEX_LOCK(mylock
);
251 *result
= NULL
; /* In case of error... */
254 if (!(pwf
= fopen(_PATH_PASSWD
, "r"))) {
258 __STDIO_SET_USER_LOCKING(pwf
);
261 if (!(rv
= __pgsreader(__parsepwent
, resultbuf
,
262 buffer
, buflen
, pwf
))) {
267 __UCLIBC_MUTEX_UNLOCK(mylock
);
271 libc_hidden_def(getpwent_r
)
274 /**********************************************************************/
276 __UCLIBC_MUTEX_STATIC(mylock
, PTHREAD_MUTEX_INITIALIZER
);
278 static FILE *grf
/*= NULL*/;
282 __UCLIBC_MUTEX_LOCK(mylock
);
286 __UCLIBC_MUTEX_UNLOCK(mylock
);
291 __UCLIBC_MUTEX_LOCK(mylock
);
296 __UCLIBC_MUTEX_UNLOCK(mylock
);
299 int getgrent_r(struct group
*__restrict resultbuf
,
300 char *__restrict buffer
, size_t buflen
,
301 struct group
**__restrict result
)
305 __UCLIBC_MUTEX_LOCK(mylock
);
307 *result
= NULL
; /* In case of error... */
310 if (!(grf
= fopen(_PATH_GROUP
, "r"))) {
314 __STDIO_SET_USER_LOCKING(grf
);
317 if (!(rv
= __pgsreader(__parsegrent
, resultbuf
,
318 buffer
, buflen
, grf
))) {
323 __UCLIBC_MUTEX_UNLOCK(mylock
);
327 libc_hidden_def(getgrent_r
)
330 /**********************************************************************/
332 __UCLIBC_MUTEX_STATIC(mylock
, PTHREAD_MUTEX_INITIALIZER
);
334 static FILE *spf
/*= NULL*/;
338 __UCLIBC_MUTEX_LOCK(mylock
);
342 __UCLIBC_MUTEX_UNLOCK(mylock
);
347 __UCLIBC_MUTEX_LOCK(mylock
);
352 __UCLIBC_MUTEX_UNLOCK(mylock
);
355 int getspent_r(struct spwd
*resultbuf
, char *buffer
,
356 size_t buflen
, struct spwd
**result
)
360 __UCLIBC_MUTEX_LOCK(mylock
);
362 *result
= NULL
; /* In case of error... */
365 if (!(spf
= fopen(_PATH_SHADOW
, "r"))) {
369 __STDIO_SET_USER_LOCKING(spf
);
372 if (!(rv
= __pgsreader(__parsespent
, resultbuf
,
373 buffer
, buflen
, spf
))) {
378 __UCLIBC_MUTEX_UNLOCK(mylock
);
382 libc_hidden_def(getspent_r
)
385 /**********************************************************************/
386 /* For the various fget??ent funcs, return NULL on failure and a
387 * pointer to the appropriate struct (statically allocated) on success.
389 /**********************************************************************/
390 #if defined(GETXXKEY_FUNC) || defined(GETXXKEY_R_FUNC)
391 #include "pwd_grp_internal.c"
394 /**********************************************************************/
395 #ifdef L___getgrouplist_internal
397 gid_t attribute_hidden
*__getgrouplist_internal(const char *user
, gid_t gid
, int *ngroups
)
403 char buff
[__UCLIBC_PWD_BUFFER_SIZE__
];
405 *ngroups
= num_groups
= 1;
407 /* We alloc space for 8 gids at a time. */
408 group_list
= malloc(8 * sizeof(group_list
[0]));
413 grfile
= fopen(_PATH_GROUP
, "r");
414 /* If /etc/group doesn't exist, we still return 1-element vector */
418 __STDIO_SET_USER_LOCKING(grfile
);
420 while (!__pgsreader(__parsegrent
, &group
, buff
, sizeof(buff
), grfile
)) {
423 assert(group
.gr_mem
); /* Must have at least a NULL terminator. */
424 if (group
.gr_gid
== gid
)
426 for (m
= group
.gr_mem
; *m
; m
++) {
427 if (strcmp(*m
, user
) != 0)
429 if (!(num_groups
& 7)) {
430 gid_t
*tmp
= realloc(group_list
, (num_groups
+8) * sizeof(group_list
[0]));
435 group_list
[num_groups
++] = group
.gr_gid
;
442 *ngroups
= num_groups
;
447 /**********************************************************************/
448 #ifdef L_getgrouplist
450 #if defined __USE_BSD || defined __USE_GNU
451 int getgrouplist(const char *user
, gid_t gid
, gid_t
*groups
, int *ngroups
)
454 gid_t
*group_list
= __getgrouplist_internal(user
, gid
, ngroups
);
457 /* malloc failure - what shall we do?
458 * fail with ENOMEM? I bet users never check for that */
459 /* *ngroups = 1; - already done by __getgrouplist_internal */
466 /* *ngroups is non-zero here */
471 memcpy(groups
, group_list
, sz
* sizeof(group_list
[0]));
480 /**********************************************************************/
485 int initgroups(const char *user
, gid_t gid
)
488 int num_groups
= ((unsigned)~0) >> 1; /* INT_MAX */
489 gid_t
*group_list
= __getgrouplist_internal(user
, gid
, &num_groups
);
492 rv
= setgroups(num_groups
, group_list
);
499 /**********************************************************************/
503 int putpwent(const struct passwd
*__restrict p
, FILE *__restrict f
)
510 /* No extra thread locking is needed above what fprintf does. */
511 if (fprintf(f
, "%s:%s:%lu:%lu:%s:%s:%s\n",
512 p
->pw_name
, p
->pw_passwd
,
513 (unsigned long)(p
->pw_uid
),
514 (unsigned long)(p
->pw_gid
),
515 p
->pw_gecos
, p
->pw_dir
, p
->pw_shell
) >= 0
526 /**********************************************************************/
529 int putgrent(const struct group
*__restrict p
, FILE *__restrict f
)
531 static const char format
[] = ",%s";
535 __STDIO_AUTO_THREADLOCK_VAR
;
537 if (!p
|| !f
) { /* Sigh... glibc checks. */
540 __STDIO_AUTO_THREADLOCK(f
);
542 if (fprintf(f
, "%s:%s:%lu:",
543 p
->gr_name
, p
->gr_passwd
,
544 (unsigned long)(p
->gr_gid
)) >= 0
554 if (__fputc_unlocked('\n', f
) >= 0) {
559 if (fprintf(f
, fmt
, *m
) < 0) {
568 __STDIO_AUTO_THREADUNLOCK(f
);
575 /**********************************************************************/
578 static const unsigned char _sp_off
[] = {
579 offsetof(struct spwd
, sp_lstchg
), /* 2 - not a char ptr */
580 offsetof(struct spwd
, sp_min
), /* 3 - not a char ptr */
581 offsetof(struct spwd
, sp_max
), /* 4 - not a char ptr */
582 offsetof(struct spwd
, sp_warn
), /* 5 - not a char ptr */
583 offsetof(struct spwd
, sp_inact
), /* 6 - not a char ptr */
584 offsetof(struct spwd
, sp_expire
), /* 7 - not a char ptr */
587 int putspent(const struct spwd
*p
, FILE *stream
)
589 static const char ld_format
[] = "%ld:";
594 __STDIO_AUTO_THREADLOCK_VAR
;
596 /* Unlike putpwent and putgrent, glibc does not check the args. */
598 __STDIO_AUTO_THREADLOCK(stream
);
600 if (fprintf(stream
, "%s:%s:", p
->sp_namp
,
601 (p
->sp_pwdp
? p
->sp_pwdp
: "")) < 0
606 for (i
=0 ; i
< sizeof(_sp_off
) ; i
++) {
608 if ((x
= *(const long int *)(((const char *) p
) + _sp_off
[i
])) == -1) {
611 if (fprintf(stream
, f
, x
) < 0) {
616 if ((p
->sp_flag
!= ~0UL) && (fprintf(stream
, "%lu", p
->sp_flag
) < 0)) {
620 if (__fputc_unlocked('\n', stream
) > 0) {
625 __STDIO_AUTO_THREADUNLOCK(stream
);
631 /**********************************************************************/
632 /* Internal uClibc functions. */
633 /**********************************************************************/
634 #ifdef L___parsepwent
636 static const unsigned char pw_off
[] = {
637 offsetof(struct passwd
, pw_name
), /* 0 */
638 offsetof(struct passwd
, pw_passwd
), /* 1 */
639 offsetof(struct passwd
, pw_uid
), /* 2 - not a char ptr */
640 offsetof(struct passwd
, pw_gid
), /* 3 - not a char ptr */
641 offsetof(struct passwd
, pw_gecos
), /* 4 */
642 offsetof(struct passwd
, pw_dir
), /* 5 */
643 offsetof(struct passwd
, pw_shell
) /* 6 */
646 int attribute_hidden
__parsepwent(void *data
, char *line
)
654 p
= ((char *) ((struct passwd
*) data
)) + pw_off
[i
];
656 if ((i
& 6) ^ 2) { /* i!=2 and i!=3 */
657 *((char **) p
) = line
;
661 /* NOTE: glibc difference - glibc allows omission of
662 * ':' seperators after the gid field if all remaining
663 * entries are empty. We require all separators. */
664 if (!(line
= strchr(line
, ':'))) {
668 unsigned long t
= strtoul(line
, &endptr
, 10);
669 /* Make sure we had at least one digit, and that the
670 * failing char is the next field seperator ':'. See
671 * glibc difference note above. */
672 /* TODO: Also check for leading whitespace? */
673 if ((endptr
== line
) || (*endptr
!= ':')) {
677 if (i
& 1) { /* i == 3 -- gid */
679 } else { /* i == 2 -- uid */
692 /**********************************************************************/
693 #ifdef L___parsegrent
695 static const unsigned char gr_off
[] = {
696 offsetof(struct group
, gr_name
), /* 0 */
697 offsetof(struct group
, gr_passwd
), /* 1 */
698 offsetof(struct group
, gr_gid
) /* 2 - not a char ptr */
701 int attribute_hidden
__parsegrent(void *data
, char *line
)
709 end_of_buf
= ((struct group
*) data
)->gr_name
; /* Evil hack! */
712 p
= ((char *) ((struct group
*) data
)) + gr_off
[i
];
715 *((char **) p
) = line
;
716 if (!(line
= strchr(line
, ':'))) {
722 *((gid_t
*) p
) = strtoul(line
, &endptr
, 10);
724 /* NOTE: glibc difference - glibc allows omission of the
725 * trailing colon when there is no member list. We treat
726 * this as an error. */
728 /* Make sure we had at least one digit, and that the
729 * failing char is the next field seperator ':'. See
730 * glibc difference note above. */
731 if ((endptr
== line
) || (*endptr
!= ':')) {
735 i
= 1; /* Count terminating NULL ptr. */
738 if (p
[1]) { /* We have a member list to process. */
739 /* Overwrite the last ':' with a ',' before counting.
740 * This allows us to test for initial ',' and adds
741 * one ',' so that the ',' count equals the member
745 /* NOTE: glibc difference - glibc allows and trims leading
746 * (but not trailing) space. We treat this as an error. */
747 /* NOTE: glibc difference - glibc allows consecutive and
748 * trailing commas, and ignores "empty string" users. We
749 * treat this as an error. */
752 *p
= 0; /* nul-terminate each member string. */
753 if (!*++p
|| (*p
== ',') || isspace(*p
)) {
760 /* Now align (p+1), rounding up. */
761 /* Assumes sizeof(char **) is a power of 2. */
762 members
= (char **)( (((intptr_t) p
) + sizeof(char **))
763 & ~((intptr_t)(sizeof(char **) - 1)) );
765 if (((char *)(members
+ i
)) > end_of_buf
) { /* No space. */
769 ((struct group
*) data
)->gr_mem
= members
;
772 p
= endptr
; /* Pointing to char prior to first member. */
790 /**********************************************************************/
791 #ifdef L___parsespent
793 static const unsigned char sp_off
[] = {
794 offsetof(struct spwd
, sp_namp
), /* 0 */
795 offsetof(struct spwd
, sp_pwdp
), /* 1 */
796 offsetof(struct spwd
, sp_lstchg
), /* 2 - not a char ptr */
797 offsetof(struct spwd
, sp_min
), /* 3 - not a char ptr */
798 offsetof(struct spwd
, sp_max
), /* 4 - not a char ptr */
799 offsetof(struct spwd
, sp_warn
), /* 5 - not a char ptr */
800 offsetof(struct spwd
, sp_inact
), /* 6 - not a char ptr */
801 offsetof(struct spwd
, sp_expire
), /* 7 - not a char ptr */
802 offsetof(struct spwd
, sp_flag
) /* 8 - not a char ptr */
805 int attribute_hidden
__parsespent(void *data
, char * line
)
813 p
= ((char *) ((struct spwd
*) data
)) + sp_off
[i
];
815 *((char **) p
) = line
;
816 if (!(line
= strchr(line
, ':'))) {
821 if (i
==5) { /* Support for old format. */
822 while (isspace(*line
)) ++line
; /* glibc eats space here. */
824 ((struct spwd
*) data
)->sp_warn
= -1;
825 ((struct spwd
*) data
)->sp_inact
= -1;
826 ((struct spwd
*) data
)->sp_expire
= -1;
827 ((struct spwd
*) data
)->sp_flag
= ~0UL;
833 *((long *) p
) = (long) strtoul(line
, &endptr
, 10);
835 if (endptr
== line
) {
836 *((long *) p
) = ((i
!= 8) ? -1L : ((long)(~0UL)));
848 if (*endptr
!= ':') {
862 /**********************************************************************/
865 /* Reads until if EOF, or until if finds a line which fits in the buffer
866 * and for which the parser function succeeds.
868 * Returns 0 on success and ENOENT for end-of-file (glibc concession).
871 int attribute_hidden
__pgsreader(int (*__parserfunc
)(void *d
, char *line
), void *data
,
872 char *__restrict line_buff
, size_t buflen
, FILE *f
)
877 __STDIO_AUTO_THREADLOCK_VAR
;
879 if (buflen
< __UCLIBC_PWD_BUFFER_SIZE__
) {
882 __STDIO_AUTO_THREADLOCK(f
);
886 if (!fgets_unlocked(line_buff
, buflen
, f
)) {
887 if (feof_unlocked(f
)) {
893 line_len
= strlen(line_buff
) - 1; /* strlen() must be > 0. */
894 if (line_buff
[line_len
] == '\n') {
895 line_buff
[line_len
] = 0;
896 } else if (line_len
+ 2 == buflen
) { /* line too long */
906 /* NOTE: glibc difference - glibc strips leading whitespace from
907 * records. We do not allow leading whitespace. */
909 /* Skip empty lines, comment lines, and lines with leading
911 if (*line_buff
&& (*line_buff
!= '#') && !isspace(*line_buff
)) {
912 if (__parserfunc
== __parsegrent
) { /* Do evil group hack. */
913 /* The group entry parsing function needs to know where
914 * the end of the buffer is so that it can construct the
915 * group member ptr table. */
916 ((struct group
*) data
)->gr_name
= line_buff
+ buflen
;
919 if (!__parserfunc(data
, line_buff
)) {
926 __STDIO_AUTO_THREADUNLOCK(f
);
933 /**********************************************************************/