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
* If file-system access is to be excluded, this module has no function,
34
* so all of its code should be excluded.
36
#ifndef WITHOUT_FILE_SYSTEM
44
#include <sys/types.h>
53
* Use the reentrant POSIX threads versions of the password lookup functions?
55
#if defined(PREFER_REENTRANT) && defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199506L
56
#define THREAD_COMPATIBLE 1
58
* Under Solaris we can use thr_main() to determine whether
59
* threads are actually running, and thus when it is necessary
60
* to avoid non-reentrant features.
62
#if defined __sun && defined __SVR4
63
#include <thread.h> /* Solaris thr_main() */
68
* Provide a password buffer size fallback in case the max size reported
69
* by sysconf() is said to be indeterminate.
71
#define DEF_GETPW_R_SIZE_MAX 1024
74
* The resources needed to lookup and record a home directory are
75
* maintained in objects of the following type.
78
ErrMsg *err; /* The error message report buffer */
79
char *buffer; /* A buffer for reading password entries and */
80
/* directory paths. */
81
int buflen; /* The allocated size of buffer[] */
82
#ifdef THREAD_COMPATIBLE
83
struct passwd pwd; /* The password entry of a user */
87
static const char *hd_getpwd(HomeDir *home);
89
/*.......................................................................
90
* Create a new HomeDir object.
93
* return HomeDir * The new object, or NULL on error.
95
HomeDir *_new_HomeDir(void)
97
HomeDir *home; /* The object to be returned */
98
size_t pathlen; /* The estimated maximum size of a pathname */
100
* Allocate the container.
102
home = (HomeDir *) malloc(sizeof(HomeDir));
108
* Before attempting any operation that might fail, initialize the
109
* container at least up to the point at which it can safely be passed
116
* Allocate a place to record error messages.
118
home->err = _new_ErrMsg();
120
return _del_HomeDir(home);
122
* Allocate the buffer that is used by the reentrant POSIX password-entry
125
#ifdef THREAD_COMPATIBLE
127
* Get the length of the buffer needed by the reentrant version
130
#ifndef _SC_GETPW_R_SIZE_MAX
131
home->buflen = DEF_GETPW_R_SIZE_MAX;
134
home->buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
136
* If the limit isn't available, substitute a suitably large fallback value.
138
if(home->buflen < 0 || errno)
139
home->buflen = DEF_GETPW_R_SIZE_MAX;
143
* If the existing buffer length requirement is too restrictive to record
144
* a pathname, increase its length.
146
pathlen = _pu_pathname_dim();
147
if(pathlen > home->buflen)
148
home->buflen = pathlen;
150
* Allocate a work buffer.
152
home->buffer = (char *) malloc(home->buflen);
155
return _del_HomeDir(home);
160
/*.......................................................................
161
* Delete a HomeDir object.
164
* home HomeDir * The object to be deleted.
166
* return HomeDir * The deleted object (always NULL).
168
HomeDir *_del_HomeDir(HomeDir *home)
171
home->err = _del_ErrMsg(home->err);
179
/*.......................................................................
180
* Lookup the home directory of a given user in the password file.
183
* home HomeDir * The resources needed to lookup the home directory.
184
* user const char * The name of the user to lookup, or "" to lookup
185
* the home directory of the person running the
188
* return const char * The home directory. If the library was compiled
189
* with threads, this string is part of the HomeDir
190
* object and will change on subsequent calls. If
191
* the library wasn't compiled to be reentrant,
192
* then the string is a pointer into a static string
193
* in the C library and will change not only on
194
* subsequent calls to this function, but also if
195
* any calls are made to the C library password
196
* file lookup functions. Thus to be safe, you should
197
* make a copy of this string before calling any
198
* other function that might do a password file
201
* On error, NULL is returned and a description
202
* of the error can be acquired by calling
203
* _hd_last_home_dir_error().
205
const char *_hd_lookup_home_dir(HomeDir *home, const char *user)
207
const char *home_dir; /* A pointer to the home directory of the user */
209
* If no username has been specified, arrange to lookup the current
212
int login_user = !user || *user=='\0';
214
* Check the arguments.
221
* Handle the ksh "~+". This expands to the absolute path of the
222
* current working directory.
224
if(!login_user && strcmp(user, "+") == 0) {
225
home_dir = hd_getpwd(home);
227
_err_record_msg(home->err, "Can't determine current directory",
234
* When looking up the home directory of the current user, see if the
235
* HOME environment variable is set, and if so, return its value.
238
home_dir = getenv("HOME");
243
* Look up the password entry of the user.
244
* First the POSIX threads version - this is painful!
246
#ifdef THREAD_COMPATIBLE
248
struct passwd *ret; /* The returned pointer to pwd */
249
int status; /* The return value of getpwnam_r() */
251
* Look up the password entry of the specified user.
254
status = getpwuid_r(geteuid(), &home->pwd, home->buffer, home->buflen,
257
status = getpwnam_r(user, &home->pwd, home->buffer, home->buflen, &ret);
259
_err_record_msg(home->err, "User '", user, "' doesn't exist.",
264
* Get a pointer to the string that holds the home directory.
266
home_dir = home->pwd.pw_dir;
269
* Now the classic unix version.
273
struct passwd *pwd = login_user ? getpwuid(geteuid()) : getpwnam(user);
275
_err_record_msg(home->err, "User '", user, "' doesn't exist.",
280
* Get a pointer to the home directory.
282
home_dir = pwd->pw_dir;
288
/*.......................................................................
289
* Return a description of the last error that caused _hd_lookup_home_dir()
293
* home HomeDir * The resources needed to record the home directory.
295
* return char * The description of the last error.
297
const char *_hd_last_home_dir_error(HomeDir *home)
299
return home ? _err_get_msg(home->err) : "NULL HomeDir argument";
302
/*.......................................................................
303
* The _hd_scan_user_home_dirs() function calls a user-provided function
304
* for each username known by the system, passing the function both
305
* the name and the home directory of the user.
308
* home HomeDir * The resource object for reading home
310
* prefix const char * Only information for usernames that
311
* start with this prefix will be
312
* returned. Note that the empty
313
& string "", matches all usernames.
314
* data void * Anonymous data to be passed to the
316
* callback_fn HOME_DIR_FN(*) The function to call for each user.
318
* return int 0 - Successful completion.
319
* 1 - An error occurred. A description
320
* of the error can be obtained by
321
* calling _hd_last_home_dir_error().
323
int _hd_scan_user_home_dirs(HomeDir *home, const char *prefix,
324
void *data, HOME_DIR_FN(*callback_fn))
326
int waserr = 0; /* True after errors */
327
int prefix_len; /* The length of prefix[] */
329
* Check the arguments.
331
if(!home || !prefix || !callback_fn) {
333
_err_record_msg(home->err,
334
"_hd_scan_user_home_dirs: Missing callback function",
340
* Get the length of the username prefix.
342
prefix_len = strlen(prefix);
344
* There are no reentrant versions of getpwent() etc for scanning
345
* the password file, so disable username completion when the
346
* library is compiled to be reentrant.
348
#if defined(PREFER_REENTRANT) && defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199506L
349
#if defined __sun && defined __SVR4
350
if(thr_main() >= 0) /* thread library is linked in */
355
struct passwd pwd_buffer; /* A returned password entry */
356
struct passwd *pwd; /* A pointer to pwd_buffer */
357
char buffer[512]; /* The buffer in which the string members of */
358
/* pwd_buffer are stored. */
360
* See if the prefix that is being completed is a complete username.
362
if(!waserr && getpwnam_r(prefix, &pwd_buffer, buffer, sizeof(buffer),
363
&pwd) == 0 && pwd != NULL) {
364
waserr = callback_fn(data, pwd->pw_name, pwd->pw_dir,
365
_err_get_msg(home->err), ERR_MSG_LEN);
368
* See if the username of the current user minimally matches the prefix.
370
if(!waserr && getpwuid_r(getuid(), &pwd_buffer, buffer, sizeof(buffer),
371
&pwd) == 0 && pwd != NULL &&
372
strncmp(prefix, pwd->pw_name, prefix_len)==0) {
373
waserr = callback_fn(data, pwd->pw_name, pwd->pw_dir,
374
_err_get_msg(home->err), ERR_MSG_LEN);
377
* Reentrancy not required?
382
struct passwd *pwd; /* The pointer to the latest password entry */
384
* Open the password file.
388
* Read the contents of the password file, looking for usernames
389
* that start with the specified prefix, and adding them to the
392
while((pwd = getpwent()) != NULL && !waserr) {
393
if(strncmp(prefix, pwd->pw_name, prefix_len) == 0) {
394
waserr = callback_fn(data, pwd->pw_name, pwd->pw_dir,
395
_err_get_msg(home->err), ERR_MSG_LEN);
399
* Close the password file.
404
* Under ksh ~+ stands for the absolute pathname of the current working
407
if(!waserr && strncmp(prefix, "+", prefix_len) == 0) {
408
const char *pwd = hd_getpwd(home);
410
waserr = callback_fn(data, "+", pwd, _err_get_msg(home->err),ERR_MSG_LEN);
413
_err_record_msg(home->err, "Can't determine current directory.",
420
/*.......................................................................
421
* Return the value of getenv("PWD") if this points to the current
422
* directory, or the return value of getcwd() otherwise. The reason for
423
* prefering PWD over getcwd() is that the former preserves the history
424
* of symbolic links that have been traversed to reach the current
425
* directory. This function is designed to provide the equivalent
426
* expansion of the ksh ~+ directive, which normally returns its value
430
* home HomeDir * The resource object for reading home directories.
432
* return const char * A pointer to either home->buffer, where the
433
* pathname is recorded, the string returned by
434
* getenv("PWD"), or NULL on error.
436
static const char *hd_getpwd(HomeDir *home)
439
* Get the absolute path of the current working directory.
441
char *cwd = getcwd(home->buffer, home->buflen);
443
* Some shells set PWD with the path of the current working directory.
444
* This will differ from cwd in that it won't have had symbolic links
447
const char *pwd = getenv("PWD");
449
* If PWD was set, and it points to the same directory as cwd, return
450
* its value. Note that it won't be the same if the current shell or
451
* the current program has changed directories, after inheriting PWD
452
* from a parent shell.
454
struct stat cwdstat, pwdstat;
455
if(pwd && cwd && stat(cwd, &cwdstat)==0 && stat(pwd, &pwdstat)==0 &&
456
cwdstat.st_dev == pwdstat.st_dev && cwdstat.st_ino == pwdstat.st_ino)
459
* Also return pwd if getcwd() failed, since it represents the best
460
* information that we have access to.
465
* In the absence of a valid PWD, return cwd.
470
#endif /* ifndef WITHOUT_FILE_SYSTEM */