1 /* passwd.cc: getpwnam () and friends
3 Copyright 1996, 1997, 1998 Cygnus Solutions.
5 This file is part of Cygwin.
7 This software is a copyrighted work licensed under the terms of the
8 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
25 #include <sys/termios.h>
27 /* Read /etc/passwd only once for better performance. This is done
28 on the first call that needs information from it. */
30 static struct passwd
*passwd_buf
; /* passwd contents in memory */
31 static int curr_lines
;
34 /* Set to loaded when /etc/passwd has been read in by read_etc_passwd ().
35 Set to emulated if passwd is emulated. */
36 /* Functions in this file need to check the value of passwd_state
37 and read in the password file if it isn't set. */
46 FILETIME last_modified
;
47 char pwd_w32
[MAX_PATH
];
50 pwd_check () : state (uninitialized
)
52 last_modified
.dwLowDateTime
= last_modified
.dwHighDateTime
= 0;
60 if (!pwd_w32
[0]) /* First call. */
62 path_conv
p ("/etc/passwd", PC_SYM_FOLLOW
| PC_FULL
);
64 strcpy (pwd_w32
, p
.get_win32 ());
67 if ((h
= FindFirstFile (pwd_w32
, &data
)) != INVALID_HANDLE_VALUE
)
69 if (CompareFileTime (&data
.ftLastWriteTime
, &last_modified
) > 0)
71 state
= uninitialized
;
72 last_modified
= data
.ftLastWriteTime
;
78 void operator = (pwd_state nstate
)
84 static pwd_check passwd_state
;
87 /* Position in the passwd cache */
89 #define pw_pos _reent_winsup ()->_pw_pos
91 static int pw_pos
= 0;
94 /* Remove a : teminated string from the buffer, and increment the pointer */
96 grab_string (char **p
)
101 while (*src
&& *src
!= ':' && *src
!= '\n')
118 int val
= strtol (src
, NULL
, 10);
119 while (*src
&& *src
!= ':' && *src
!= '\n')
127 /* Parse /etc/passwd line into passwd structure. */
129 parse_pwd (struct passwd
&res
, char *buf
)
131 /* Allocate enough room for the passwd struct and all the strings
133 size_t len
= strlen (buf
);
134 char *mybuf
= (char *) malloc (len
+ 1);
135 (void) memcpy (mybuf
, buf
, len
+ 1);
136 if (mybuf
[--len
] == '\n')
139 res
.pw_name
= grab_string (&mybuf
);
140 res
.pw_passwd
= grab_string (&mybuf
);
141 res
.pw_uid
= grab_int (&mybuf
);
142 res
.pw_gid
= grab_int (&mybuf
);
144 res
.pw_gecos
= grab_string (&mybuf
);
145 res
.pw_dir
= grab_string (&mybuf
);
146 res
.pw_shell
= grab_string (&mybuf
);
149 /* Add one line from /etc/passwd into the password cache */
151 add_pwd_line (char *line
)
153 if (curr_lines
>= max_lines
)
156 passwd_buf
= (struct passwd
*) realloc (passwd_buf
, max_lines
* sizeof (struct passwd
));
158 parse_pwd (passwd_buf
[curr_lines
++], line
);
161 /* Read in /etc/passwd and save contents in the password cache.
162 This sets passwd_state to loaded or emulated so functions in this file can
163 tell that /etc/passwd has been read in or will be emulated. */
168 /* A mutex is ok for speed here - pthreads will use critical sections not mutex's
169 * for non-shared mutexs in the future. Also, this function will at most be called
170 * once from each thread, after that the passwd_state test will succeed
172 static NO_COPY pthread_mutex_t etc_passwd_mutex
= (pthread_mutex_t
) PTHREAD_MUTEX_INITIALIZER
;
173 pthread_mutex_lock (&etc_passwd_mutex
);
175 /* if we got blocked by the mutex, then etc_passwd may have been processed */
176 if (passwd_state
!= uninitialized
)
178 pthread_mutex_unlock(&etc_passwd_mutex
);
182 if (passwd_state
!= initializing
)
184 passwd_state
= initializing
;
185 if (max_lines
) /* When rereading, free allocated memory first. */
187 for (int i
= 0; i
< curr_lines
; ++i
)
188 free (passwd_buf
[i
].pw_name
);
190 curr_lines
= max_lines
= 0;
193 FILE *f
= fopen ("/etc/passwd", "rt");
197 while (fgets (linebuf
, sizeof (linebuf
), f
) != NULL
)
199 if (strlen (linebuf
))
200 add_pwd_line (linebuf
);
204 passwd_state
= loaded
;
208 debug_printf ("Emulating /etc/passwd");
209 snprintf (linebuf
, sizeof (linebuf
), "%s::%u:%u::%s:/bin/sh", cygheap
->user
.name (),
210 DEFAULT_UID
, DEFAULT_GID
, getenv ("HOME") ?: "/");
211 add_pwd_line (linebuf
);
212 passwd_state
= emulated
;
217 pthread_mutex_unlock (&etc_passwd_mutex
);
220 /* Cygwin internal */
221 /* If this ever becomes non-reentrant, update all the getpw*_r functions */
222 static struct passwd
*
223 search_for (uid_t uid
, const char *name
)
225 struct passwd
*res
= 0;
226 struct passwd
*default_pw
= 0;
228 for (int i
= 0; i
< curr_lines
; i
++)
230 res
= passwd_buf
+ i
;
231 if (res
->pw_uid
== DEFAULT_UID
)
233 /* on Windows NT user names are case-insensitive */
236 if (strcasematch (name
, res
->pw_name
))
239 else if (uid
== res
->pw_uid
)
243 /* Return default passwd entry if passwd is emulated or it's a
244 request for the current user. */
245 if (passwd_state
!= loaded
246 || (!name
&& uid
== myself
->uid
)
247 || (name
&& strcasematch (name
, cygheap
->user
.name ())))
253 extern "C" struct passwd
*
256 if (passwd_state
<= initializing
)
259 pthread_testcancel();
261 return search_for (uid
, 0);
265 getpwuid_r (uid_t uid
, struct passwd
*pwd
, char *buffer
, size_t bufsize
, struct passwd
**result
)
272 if (passwd_state
<= initializing
)
275 pthread_testcancel();
277 struct passwd
*temppw
= search_for (uid
, 0);
282 /* check needed buffer size. */
283 size_t needsize
= strlen (temppw
->pw_name
) + strlen (temppw
->pw_dir
) +
284 strlen (temppw
->pw_shell
) + strlen (temppw
->pw_gecos
) +
285 strlen (temppw
->pw_passwd
) + 5;
286 if (needsize
> bufsize
)
289 /* make a copy of temppw */
291 pwd
->pw_uid
= temppw
->pw_uid
;
292 pwd
->pw_gid
= temppw
->pw_gid
;
293 pwd
->pw_name
= buffer
;
294 pwd
->pw_dir
= pwd
->pw_name
+ strlen (temppw
->pw_name
) + 1;
295 pwd
->pw_shell
= pwd
->pw_dir
+ strlen (temppw
->pw_dir
) + 1;
296 pwd
->pw_gecos
= pwd
->pw_shell
+ strlen (temppw
->pw_shell
) + 1;
297 pwd
->pw_passwd
= pwd
->pw_gecos
+ strlen (temppw
->pw_gecos
) + 1;
298 strcpy (pwd
->pw_name
, temppw
->pw_name
);
299 strcpy (pwd
->pw_dir
, temppw
->pw_dir
);
300 strcpy (pwd
->pw_shell
, temppw
->pw_shell
);
301 strcpy (pwd
->pw_gecos
, temppw
->pw_gecos
);
302 strcpy (pwd
->pw_passwd
, temppw
->pw_passwd
);
306 extern "C" struct passwd
*
307 getpwnam (const char *name
)
309 if (passwd_state
<= initializing
)
312 pthread_testcancel();
314 return search_for (0, name
);
318 /* the max size buffer we can expect to
319 * use is returned via sysconf with _SC_GETPW_R_SIZE_MAX.
320 * This may need updating! - Rob Collins April 2001.
323 getpwnam_r (const char *nam
, struct passwd
*pwd
, char *buffer
, size_t bufsize
, struct passwd
**result
)
327 if (!pwd
|| !buffer
|| !nam
)
330 if (passwd_state
<= initializing
)
333 pthread_testcancel();
335 struct passwd
*temppw
= search_for (0, nam
);
340 /* check needed buffer size. */
341 size_t needsize
= strlen (temppw
->pw_name
) + strlen (temppw
->pw_dir
) +
342 strlen (temppw
->pw_shell
) + strlen (temppw
->pw_gecos
) +
343 strlen (temppw
->pw_passwd
) + 5;
344 if (needsize
> bufsize
)
347 /* make a copy of temppw */
349 pwd
->pw_uid
= temppw
->pw_uid
;
350 pwd
->pw_gid
= temppw
->pw_gid
;
351 pwd
->pw_name
= buffer
;
352 pwd
->pw_dir
= pwd
->pw_name
+ strlen (temppw
->pw_name
) + 1;
353 pwd
->pw_shell
= pwd
->pw_dir
+ strlen (temppw
->pw_dir
) + 1;
354 pwd
->pw_gecos
= pwd
->pw_shell
+ strlen (temppw
->pw_shell
) + 1;
355 pwd
->pw_passwd
= pwd
->pw_gecos
+ strlen (temppw
->pw_gecos
) + 1;
356 strcpy (pwd
->pw_name
, temppw
->pw_name
);
357 strcpy (pwd
->pw_dir
, temppw
->pw_dir
);
358 strcpy (pwd
->pw_shell
, temppw
->pw_shell
);
359 strcpy (pwd
->pw_gecos
, temppw
->pw_gecos
);
360 strcpy (pwd
->pw_passwd
, temppw
->pw_passwd
);
364 extern "C" struct passwd
*
367 if (passwd_state
<= initializing
)
370 if (pw_pos
< curr_lines
)
371 return passwd_buf
+ pw_pos
++;
376 extern "C" struct passwd
*
400 /* Internal function. ONLY USE THIS INTERNALLY, NEVER `getpwent'!!! */
402 internal_getpwent (int pos
)
404 if (passwd_state
<= initializing
)
407 if (pos
< curr_lines
)
408 return passwd_buf
+ pos
;
413 getpass (const char * prompt
)
416 char *pass
=_reent_winsup ()->_pass
;
418 static char pass
[_PASSWORD_LEN
];
420 struct termios ti
, newti
;
422 if (passwd_state
<= initializing
)
425 if (cygheap
->fdtab
.not_open (0))
432 fhandler_base
*fhstdin
= cygheap
->fdtab
[0];
433 fhstdin
->tcgetattr (&ti
);
435 newti
.c_lflag
&= ~ECHO
;
436 fhstdin
->tcsetattr (TCSANOW
, &newti
);
437 fputs (prompt
, stderr
);
438 fgets (pass
, _PASSWORD_LEN
, stdin
);
439 fprintf (stderr
, "\n");
440 for (int i
=0; pass
[i
]; i
++)
441 if (pass
[i
] == '\r' || pass
[i
] == '\n')
443 fhstdin
->tcsetattr (TCSANOW
, &ti
);