2 * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd.
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, and/or sell copies of the Software, and to permit persons
11 * to whom the Software is furnished to do so, provided that the above
12 * copyright notice(s) and this permission notice appear in all copies of
13 * the Software and that both the above copyright notice(s) and this
14 * permission notice appear in supporting documentation.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
19 * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
20 * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
21 * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
22 * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
23 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
24 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
26 * Except as contained in this notice, the name of a copyright holder
27 * shall not be used in advertising or otherwise to promote the sale, use
28 * or other dealings in this Software without prior written authorization
29 * of the copyright holder.
33 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
34 * Use is subject to license terms.
38 * If file-system access is to be excluded, this module has no function,
39 * so all of its code should be excluded.
41 #ifndef WITHOUT_FILE_SYSTEM
49 #include <sys/types.h>
58 * Use the reentrant POSIX threads versions of the password lookup functions?
60 #if defined(PREFER_REENTRANT) && defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199506L
61 #define THREAD_COMPATIBLE 1
63 * Under Solaris we can use thr_main() to determine whether
64 * threads are actually running, and thus when it is necessary
65 * to avoid non-reentrant features.
67 #if defined __sun && defined __SVR4
68 #include <thread.h> /* Solaris thr_main() */
73 * Provide a password buffer size fallback in case the max size reported
74 * by sysconf() is said to be indeterminate.
76 #define DEF_GETPW_R_SIZE_MAX 1024
79 * The resources needed to lookup and record a home directory are
80 * maintained in objects of the following type.
83 ErrMsg
*err
; /* The error message report buffer */
84 char *buffer
; /* A buffer for reading password entries and */
85 /* directory paths. */
86 int buflen
; /* The allocated size of buffer[] */
87 #ifdef THREAD_COMPATIBLE
88 struct passwd pwd
; /* The password entry of a user */
92 static const char *hd_getpwd(HomeDir
*home
);
94 /*.......................................................................
95 * Create a new HomeDir object.
98 * return HomeDir * The new object, or NULL on error.
100 HomeDir
*_new_HomeDir(void)
102 HomeDir
*home
; /* The object to be returned */
103 size_t pathlen
; /* The estimated maximum size of a pathname */
105 * Allocate the container.
107 home
= (HomeDir
*) malloc(sizeof(HomeDir
));
113 * Before attempting any operation that might fail, initialize the
114 * container at least up to the point at which it can safely be passed
121 * Allocate a place to record error messages.
123 home
->err
= _new_ErrMsg();
125 return _del_HomeDir(home
);
127 * Allocate the buffer that is used by the reentrant POSIX password-entry
130 #ifdef THREAD_COMPATIBLE
132 * Get the length of the buffer needed by the reentrant version
135 #ifndef _SC_GETPW_R_SIZE_MAX
136 home
->buflen
= DEF_GETPW_R_SIZE_MAX
;
139 home
->buflen
= sysconf(_SC_GETPW_R_SIZE_MAX
);
141 * If the limit isn't available, substitute a suitably large fallback value.
143 if(home
->buflen
< 0 || errno
)
144 home
->buflen
= DEF_GETPW_R_SIZE_MAX
;
148 * If the existing buffer length requirement is too restrictive to record
149 * a pathname, increase its length.
151 pathlen
= _pu_pathname_dim();
152 if(pathlen
> home
->buflen
)
153 home
->buflen
= pathlen
;
155 * Allocate a work buffer.
157 home
->buffer
= (char *) malloc(home
->buflen
);
160 return _del_HomeDir(home
);
165 /*.......................................................................
166 * Delete a HomeDir object.
169 * home HomeDir * The object to be deleted.
171 * return HomeDir * The deleted object (always NULL).
173 HomeDir
*_del_HomeDir(HomeDir
*home
)
176 home
->err
= _del_ErrMsg(home
->err
);
183 /*.......................................................................
184 * Lookup the home directory of a given user in the password file.
187 * home HomeDir * The resources needed to lookup the home directory.
188 * user const char * The name of the user to lookup, or "" to lookup
189 * the home directory of the person running the
192 * return const char * The home directory. If the library was compiled
193 * with threads, this string is part of the HomeDir
194 * object and will change on subsequent calls. If
195 * the library wasn't compiled to be reentrant,
196 * then the string is a pointer into a static string
197 * in the C library and will change not only on
198 * subsequent calls to this function, but also if
199 * any calls are made to the C library password
200 * file lookup functions. Thus to be safe, you should
201 * make a copy of this string before calling any
202 * other function that might do a password file
205 * On error, NULL is returned and a description
206 * of the error can be acquired by calling
207 * _hd_last_home_dir_error().
209 const char *_hd_lookup_home_dir(HomeDir
*home
, const char *user
)
211 const char *home_dir
; /* A pointer to the home directory of the user */
213 * If no username has been specified, arrange to lookup the current
216 int login_user
= !user
|| *user
=='\0';
218 * Check the arguments.
225 * Handle the ksh "~+". This expands to the absolute path of the
226 * current working directory.
228 if(!login_user
&& strcmp(user
, "+") == 0) {
229 home_dir
= hd_getpwd(home
);
231 _err_record_msg(home
->err
, "Can't determine current directory",
238 * When looking up the home directory of the current user, see if the
239 * HOME environment variable is set, and if so, return its value.
242 home_dir
= getenv("HOME");
247 * Look up the password entry of the user.
248 * First the POSIX threads version - this is painful!
250 #ifdef THREAD_COMPATIBLE
252 struct passwd
*ret
; /* The returned pointer to pwd */
253 int status
; /* The return value of getpwnam_r() */
255 * Look up the password entry of the specified user.
258 status
= getpwuid_r(geteuid(), &home
->pwd
, home
->buffer
, home
->buflen
,
261 status
= getpwnam_r(user
, &home
->pwd
, home
->buffer
, home
->buflen
, &ret
);
263 _err_record_msg(home
->err
, "User '", user
, "' doesn't exist.",
268 * Get a pointer to the string that holds the home directory.
270 home_dir
= home
->pwd
.pw_dir
;
273 * Now the classic unix version.
277 struct passwd
*pwd
= login_user
? getpwuid(geteuid()) : getpwnam(user
);
279 _err_record_msg(home
->err
, "User '", user
, "' doesn't exist.",
284 * Get a pointer to the home directory.
286 home_dir
= pwd
->pw_dir
;
292 /*.......................................................................
293 * Return a description of the last error that caused _hd_lookup_home_dir()
297 * home HomeDir * The resources needed to record the home directory.
299 * return char * The description of the last error.
301 const char *_hd_last_home_dir_error(HomeDir
*home
)
303 return home
? _err_get_msg(home
->err
) : "NULL HomeDir argument";
306 /*.......................................................................
307 * The _hd_scan_user_home_dirs() function calls a user-provided function
308 * for each username known by the system, passing the function both
309 * the name and the home directory of the user.
312 * home HomeDir * The resource object for reading home
314 * prefix const char * Only information for usernames that
315 * start with this prefix will be
316 * returned. Note that the empty
317 & string "", matches all usernames.
318 * data void * Anonymous data to be passed to the
320 * callback_fn HOME_DIR_FN(*) The function to call for each user.
322 * return int 0 - Successful completion.
323 * 1 - An error occurred. A description
324 * of the error can be obtained by
325 * calling _hd_last_home_dir_error().
327 int _hd_scan_user_home_dirs(HomeDir
*home
, const char *prefix
,
328 void *data
, HOME_DIR_FN(*callback_fn
))
330 int waserr
= 0; /* True after errors */
331 int prefix_len
; /* The length of prefix[] */
333 * Check the arguments.
335 if(!home
|| !prefix
|| !callback_fn
) {
337 _err_record_msg(home
->err
,
338 "_hd_scan_user_home_dirs: Missing callback function",
344 * Get the length of the username prefix.
346 prefix_len
= strlen(prefix
);
348 * There are no reentrant versions of getpwent() etc for scanning
349 * the password file, so disable username completion when the
350 * library is compiled to be reentrant.
352 #if defined(PREFER_REENTRANT) && defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199506L
353 #if defined __sun && defined __SVR4
359 struct passwd pwd_buffer
; /* A returned password entry */
360 struct passwd
*pwd
; /* A pointer to pwd_buffer */
361 char buffer
[512]; /* The buffer in which the string members of */
362 /* pwd_buffer are stored. */
364 * See if the prefix that is being completed is a complete username.
366 if(!waserr
&& getpwnam_r(prefix
, &pwd_buffer
, buffer
, sizeof(buffer
),
367 &pwd
) == 0 && pwd
!= NULL
) {
368 waserr
= callback_fn(data
, pwd
->pw_name
, pwd
->pw_dir
,
369 _err_get_msg(home
->err
), ERR_MSG_LEN
);
372 * See if the username of the current user minimally matches the prefix.
374 if(!waserr
&& getpwuid_r(getuid(), &pwd_buffer
, buffer
, sizeof(buffer
),
375 &pwd
) == 0 && pwd
!= NULL
&&
376 strncmp(prefix
, pwd
->pw_name
, prefix_len
)==0) {
377 waserr
= callback_fn(data
, pwd
->pw_name
, pwd
->pw_dir
,
378 _err_get_msg(home
->err
), ERR_MSG_LEN
);
381 * Reentrancy not required?
386 struct passwd
*pwd
; /* The pointer to the latest password entry */
388 * Open the password file.
392 * Read the contents of the password file, looking for usernames
393 * that start with the specified prefix, and adding them to the
396 while((pwd
= getpwent()) != NULL
&& !waserr
) {
397 if(strncmp(prefix
, pwd
->pw_name
, prefix_len
) == 0) {
398 waserr
= callback_fn(data
, pwd
->pw_name
, pwd
->pw_dir
,
399 _err_get_msg(home
->err
), ERR_MSG_LEN
);
403 * Close the password file.
408 * Under ksh ~+ stands for the absolute pathname of the current working
411 if(!waserr
&& strncmp(prefix
, "+", prefix_len
) == 0) {
412 const char *pwd
= hd_getpwd(home
);
414 waserr
= callback_fn(data
, "+", pwd
, _err_get_msg(home
->err
),ERR_MSG_LEN
);
417 _err_record_msg(home
->err
, "Can't determine current directory.",
424 /*.......................................................................
425 * Return the value of getenv("PWD") if this points to the current
426 * directory, or the return value of getcwd() otherwise. The reason for
427 * prefering PWD over getcwd() is that the former preserves the history
428 * of symbolic links that have been traversed to reach the current
429 * directory. This function is designed to provide the equivalent
430 * expansion of the ksh ~+ directive, which normally returns its value
434 * home HomeDir * The resource object for reading home directories.
436 * return const char * A pointer to either home->buffer, where the
437 * pathname is recorded, the string returned by
438 * getenv("PWD"), or NULL on error.
440 static const char *hd_getpwd(HomeDir
*home
)
443 * Get the absolute path of the current working directory.
445 char *cwd
= getcwd(home
->buffer
, home
->buflen
);
447 * Some shells set PWD with the path of the current working directory.
448 * This will differ from cwd in that it won't have had symbolic links
451 const char *pwd
= getenv("PWD");
453 * If PWD was set, and it points to the same directory as cwd, return
454 * its value. Note that it won't be the same if the current shell or
455 * the current program has changed directories, after inheriting PWD
456 * from a parent shell.
458 struct stat cwdstat
, pwdstat
;
459 if(pwd
&& cwd
&& stat(cwd
, &cwdstat
)==0 && stat(pwd
, &pwdstat
)==0 &&
460 cwdstat
.st_dev
== pwdstat
.st_dev
&& cwdstat
.st_ino
== pwdstat
.st_ino
)
463 * Also return pwd if getcwd() failed, since it represents the best
464 * information that we have access to.
469 * In the absence of a valid PWD, return cwd.
474 #endif /* ifndef WITHOUT_FILE_SYSTEM */