17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd.
3*1da57d55SToomas Soome  *
47c478bd9Sstevel@tonic-gate  * All rights reserved.
5*1da57d55SToomas Soome  *
67c478bd9Sstevel@tonic-gate  * Permission is hereby granted, free of charge, to any person obtaining a
77c478bd9Sstevel@tonic-gate  * copy of this software and associated documentation files (the
87c478bd9Sstevel@tonic-gate  * "Software"), to deal in the Software without restriction, including
97c478bd9Sstevel@tonic-gate  * without limitation the rights to use, copy, modify, merge, publish,
107c478bd9Sstevel@tonic-gate  * distribute, and/or sell copies of the Software, and to permit persons
117c478bd9Sstevel@tonic-gate  * to whom the Software is furnished to do so, provided that the above
127c478bd9Sstevel@tonic-gate  * copyright notice(s) and this permission notice appear in all copies of
137c478bd9Sstevel@tonic-gate  * the Software and that both the above copyright notice(s) and this
147c478bd9Sstevel@tonic-gate  * permission notice appear in supporting documentation.
15*1da57d55SToomas Soome  *
167c478bd9Sstevel@tonic-gate  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
177c478bd9Sstevel@tonic-gate  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
187c478bd9Sstevel@tonic-gate  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
197c478bd9Sstevel@tonic-gate  * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
207c478bd9Sstevel@tonic-gate  * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
217c478bd9Sstevel@tonic-gate  * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
227c478bd9Sstevel@tonic-gate  * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
237c478bd9Sstevel@tonic-gate  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
247c478bd9Sstevel@tonic-gate  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25*1da57d55SToomas Soome  *
267c478bd9Sstevel@tonic-gate  * Except as contained in this notice, the name of a copyright holder
277c478bd9Sstevel@tonic-gate  * shall not be used in advertising or otherwise to promote the sale, use
287c478bd9Sstevel@tonic-gate  * or other dealings in this Software without prior written authorization
297c478bd9Sstevel@tonic-gate  * of the copyright holder.
307c478bd9Sstevel@tonic-gate  */
317c478bd9Sstevel@tonic-gate 
327c478bd9Sstevel@tonic-gate /*
337c478bd9Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
347c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
357c478bd9Sstevel@tonic-gate  */
367c478bd9Sstevel@tonic-gate 
377c478bd9Sstevel@tonic-gate /*
387c478bd9Sstevel@tonic-gate  * If file-system access is to be excluded, this module has no function,
397c478bd9Sstevel@tonic-gate  * so all of its code should be excluded.
407c478bd9Sstevel@tonic-gate  */
417c478bd9Sstevel@tonic-gate #ifndef WITHOUT_FILE_SYSTEM
427c478bd9Sstevel@tonic-gate 
437c478bd9Sstevel@tonic-gate #include <stdio.h>
447c478bd9Sstevel@tonic-gate #include <stdlib.h>
457c478bd9Sstevel@tonic-gate #include <string.h>
467c478bd9Sstevel@tonic-gate #include <errno.h>
477c478bd9Sstevel@tonic-gate 
487c478bd9Sstevel@tonic-gate #include <unistd.h>
497c478bd9Sstevel@tonic-gate #include <sys/types.h>
507c478bd9Sstevel@tonic-gate #include <sys/stat.h>
517c478bd9Sstevel@tonic-gate #include <pwd.h>
527c478bd9Sstevel@tonic-gate 
537c478bd9Sstevel@tonic-gate #include "pathutil.h"
547c478bd9Sstevel@tonic-gate #include "homedir.h"
557c478bd9Sstevel@tonic-gate #include "errmsg.h"
567c478bd9Sstevel@tonic-gate 
577c478bd9Sstevel@tonic-gate /*
587c478bd9Sstevel@tonic-gate  * Use the reentrant POSIX threads versions of the password lookup functions?
597c478bd9Sstevel@tonic-gate  */
607c478bd9Sstevel@tonic-gate #if defined(PREFER_REENTRANT) && defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199506L
617c478bd9Sstevel@tonic-gate #define THREAD_COMPATIBLE 1
627c478bd9Sstevel@tonic-gate /*
637c478bd9Sstevel@tonic-gate  * Under Solaris we can use thr_main() to determine whether
647c478bd9Sstevel@tonic-gate  * threads are actually running, and thus when it is necessary
657c478bd9Sstevel@tonic-gate  * to avoid non-reentrant features.
667c478bd9Sstevel@tonic-gate  */
677c478bd9Sstevel@tonic-gate #if defined __sun && defined __SVR4
687c478bd9Sstevel@tonic-gate #include <thread.h>                      /* Solaris thr_main() */
697c478bd9Sstevel@tonic-gate #endif
707c478bd9Sstevel@tonic-gate #endif
717c478bd9Sstevel@tonic-gate 
727c478bd9Sstevel@tonic-gate /*
737c478bd9Sstevel@tonic-gate  * Provide a password buffer size fallback in case the max size reported
747c478bd9Sstevel@tonic-gate  * by sysconf() is said to be indeterminate.
757c478bd9Sstevel@tonic-gate  */
767c478bd9Sstevel@tonic-gate #define DEF_GETPW_R_SIZE_MAX 1024
777c478bd9Sstevel@tonic-gate 
787c478bd9Sstevel@tonic-gate /*
797c478bd9Sstevel@tonic-gate  * The resources needed to lookup and record a home directory are
807c478bd9Sstevel@tonic-gate  * maintained in objects of the following type.
817c478bd9Sstevel@tonic-gate  */
827c478bd9Sstevel@tonic-gate struct HomeDir {
837c478bd9Sstevel@tonic-gate   ErrMsg *err;             /* The error message report buffer */
847c478bd9Sstevel@tonic-gate   char *buffer;            /* A buffer for reading password entries and */
857c478bd9Sstevel@tonic-gate                            /*  directory paths. */
867c478bd9Sstevel@tonic-gate   int buflen;              /* The allocated size of buffer[] */
877c478bd9Sstevel@tonic-gate #ifdef THREAD_COMPATIBLE
887c478bd9Sstevel@tonic-gate   struct passwd pwd;       /* The password entry of a user */
897c478bd9Sstevel@tonic-gate #endif
907c478bd9Sstevel@tonic-gate };
917c478bd9Sstevel@tonic-gate 
927c478bd9Sstevel@tonic-gate static const char *hd_getpwd(HomeDir *home);
937c478bd9Sstevel@tonic-gate 
947c478bd9Sstevel@tonic-gate /*.......................................................................
957c478bd9Sstevel@tonic-gate  * Create a new HomeDir object.
967c478bd9Sstevel@tonic-gate  *
977c478bd9Sstevel@tonic-gate  * Output:
987c478bd9Sstevel@tonic-gate  *  return  HomeDir *  The new object, or NULL on error.
997c478bd9Sstevel@tonic-gate  */
_new_HomeDir(void)1007c478bd9Sstevel@tonic-gate HomeDir *_new_HomeDir(void)
1017c478bd9Sstevel@tonic-gate {
1027c478bd9Sstevel@tonic-gate   HomeDir *home;  /* The object to be returned */
1037c478bd9Sstevel@tonic-gate   size_t pathlen; /* The estimated maximum size of a pathname */
1047c478bd9Sstevel@tonic-gate /*
1057c478bd9Sstevel@tonic-gate  * Allocate the container.
1067c478bd9Sstevel@tonic-gate  */
1077c478bd9Sstevel@tonic-gate   home = (HomeDir *) malloc(sizeof(HomeDir));
1087c478bd9Sstevel@tonic-gate   if(!home) {
1097c478bd9Sstevel@tonic-gate     errno = ENOMEM;
1107c478bd9Sstevel@tonic-gate     return NULL;
1117c478bd9Sstevel@tonic-gate   };
1127c478bd9Sstevel@tonic-gate /*
1137c478bd9Sstevel@tonic-gate  * Before attempting any operation that might fail, initialize the
1147c478bd9Sstevel@tonic-gate  * container at least up to the point at which it can safely be passed
1157c478bd9Sstevel@tonic-gate  * to _del_HomeDir().
1167c478bd9Sstevel@tonic-gate  */
1177c478bd9Sstevel@tonic-gate   home->err = NULL;
1187c478bd9Sstevel@tonic-gate   home->buffer = NULL;
1197c478bd9Sstevel@tonic-gate   home->buflen = 0;
1207c478bd9Sstevel@tonic-gate /*
1217c478bd9Sstevel@tonic-gate  * Allocate a place to record error messages.
1227c478bd9Sstevel@tonic-gate  */
1237c478bd9Sstevel@tonic-gate   home->err = _new_ErrMsg();
1247c478bd9Sstevel@tonic-gate   if(!home->err)
1257c478bd9Sstevel@tonic-gate     return _del_HomeDir(home);
1267c478bd9Sstevel@tonic-gate /*
1277c478bd9Sstevel@tonic-gate  * Allocate the buffer that is used by the reentrant POSIX password-entry
1287c478bd9Sstevel@tonic-gate  * lookup functions.
1297c478bd9Sstevel@tonic-gate  */
1307c478bd9Sstevel@tonic-gate #ifdef THREAD_COMPATIBLE
1317c478bd9Sstevel@tonic-gate /*
1327c478bd9Sstevel@tonic-gate  * Get the length of the buffer needed by the reentrant version
1337c478bd9Sstevel@tonic-gate  * of getpwnam().
1347c478bd9Sstevel@tonic-gate  */
1357c478bd9Sstevel@tonic-gate #ifndef _SC_GETPW_R_SIZE_MAX
1367c478bd9Sstevel@tonic-gate   home->buflen = DEF_GETPW_R_SIZE_MAX;
1377c478bd9Sstevel@tonic-gate #else
1387c478bd9Sstevel@tonic-gate   errno = 0;
1397c478bd9Sstevel@tonic-gate   home->buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
1407c478bd9Sstevel@tonic-gate /*
1417c478bd9Sstevel@tonic-gate  * If the limit isn't available, substitute a suitably large fallback value.
1427c478bd9Sstevel@tonic-gate  */
1437c478bd9Sstevel@tonic-gate   if(home->buflen < 0 || errno)
1447c478bd9Sstevel@tonic-gate     home->buflen = DEF_GETPW_R_SIZE_MAX;
1457c478bd9Sstevel@tonic-gate #endif
1467c478bd9Sstevel@tonic-gate #endif
1477c478bd9Sstevel@tonic-gate /*
1487c478bd9Sstevel@tonic-gate  * If the existing buffer length requirement is too restrictive to record
1497c478bd9Sstevel@tonic-gate  * a pathname, increase its length.
1507c478bd9Sstevel@tonic-gate  */
1517c478bd9Sstevel@tonic-gate   pathlen = _pu_pathname_dim();
1527c478bd9Sstevel@tonic-gate   if(pathlen > home->buflen)
1537c478bd9Sstevel@tonic-gate     home->buflen = pathlen;
1547c478bd9Sstevel@tonic-gate /*
1557c478bd9Sstevel@tonic-gate  * Allocate a work buffer.
1567c478bd9Sstevel@tonic-gate  */
1577c478bd9Sstevel@tonic-gate   home->buffer = (char *) malloc(home->buflen);
1587c478bd9Sstevel@tonic-gate   if(!home->buffer) {
1597c478bd9Sstevel@tonic-gate     errno = ENOMEM;
1607c478bd9Sstevel@tonic-gate     return _del_HomeDir(home);
1617c478bd9Sstevel@tonic-gate   };
1627c478bd9Sstevel@tonic-gate   return home;
1637c478bd9Sstevel@tonic-gate }
1647c478bd9Sstevel@tonic-gate 
1657c478bd9Sstevel@tonic-gate /*.......................................................................
1667c478bd9Sstevel@tonic-gate  * Delete a HomeDir object.
1677c478bd9Sstevel@tonic-gate  *
1687c478bd9Sstevel@tonic-gate  * Input:
1697c478bd9Sstevel@tonic-gate  *  home   HomeDir *  The object to be deleted.
1707c478bd9Sstevel@tonic-gate  * Output:
1717c478bd9Sstevel@tonic-gate  *  return HomeDir *  The deleted object (always NULL).
1727c478bd9Sstevel@tonic-gate  */
_del_HomeDir(HomeDir * home)1737c478bd9Sstevel@tonic-gate HomeDir *_del_HomeDir(HomeDir *home)
1747c478bd9Sstevel@tonic-gate {
1757c478bd9Sstevel@tonic-gate   if(home) {
1767c478bd9Sstevel@tonic-gate     home->err = _del_ErrMsg(home->err);
1777c478bd9Sstevel@tonic-gate     if(home->buffer)
1787c478bd9Sstevel@tonic-gate       free(home->buffer);
1797c478bd9Sstevel@tonic-gate     free(home);
1807c478bd9Sstevel@tonic-gate   };
1817c478bd9Sstevel@tonic-gate   return NULL;
1827c478bd9Sstevel@tonic-gate }
1837c478bd9Sstevel@tonic-gate 
1847c478bd9Sstevel@tonic-gate /*.......................................................................
1857c478bd9Sstevel@tonic-gate  * Lookup the home directory of a given user in the password file.
1867c478bd9Sstevel@tonic-gate  *
1877c478bd9Sstevel@tonic-gate  * Input:
1887c478bd9Sstevel@tonic-gate  *  home      HomeDir *   The resources needed to lookup the home directory.
1897c478bd9Sstevel@tonic-gate  *  user   const char *   The name of the user to lookup, or "" to lookup
1907c478bd9Sstevel@tonic-gate  *                        the home directory of the person running the
1917c478bd9Sstevel@tonic-gate  *                        program.
1927c478bd9Sstevel@tonic-gate  * Output:
1937c478bd9Sstevel@tonic-gate  *  return const char *   The home directory. If the library was compiled
1947c478bd9Sstevel@tonic-gate  *                        with threads, this string is part of the HomeDir
1957c478bd9Sstevel@tonic-gate  *                        object and will change on subsequent calls. If
1967c478bd9Sstevel@tonic-gate  *                        the library wasn't compiled to be reentrant,
1977c478bd9Sstevel@tonic-gate  *                        then the string is a pointer into a static string
1987c478bd9Sstevel@tonic-gate  *                        in the C library and will change not only on
1997c478bd9Sstevel@tonic-gate  *                        subsequent calls to this function, but also if
2007c478bd9Sstevel@tonic-gate  *                        any calls are made to the C library password
2017c478bd9Sstevel@tonic-gate  *                        file lookup functions. Thus to be safe, you should
2027c478bd9Sstevel@tonic-gate  *                        make a copy of this string before calling any
2037c478bd9Sstevel@tonic-gate  *                        other function that might do a password file
2047c478bd9Sstevel@tonic-gate  *                        lookup.
2057c478bd9Sstevel@tonic-gate  *
2067c478bd9Sstevel@tonic-gate  *                        On error, NULL is returned and a description
2077c478bd9Sstevel@tonic-gate  *                        of the error can be acquired by calling
2087c478bd9Sstevel@tonic-gate  *                        _hd_last_home_dir_error().
2097c478bd9Sstevel@tonic-gate  */
_hd_lookup_home_dir(HomeDir * home,const char * user)2107c478bd9Sstevel@tonic-gate const char *_hd_lookup_home_dir(HomeDir *home, const char *user)
2117c478bd9Sstevel@tonic-gate {
2127c478bd9Sstevel@tonic-gate   const char *home_dir;   /* A pointer to the home directory of the user */
2137c478bd9Sstevel@tonic-gate /*
2147c478bd9Sstevel@tonic-gate  * If no username has been specified, arrange to lookup the current
2157c478bd9Sstevel@tonic-gate  * user.
2167c478bd9Sstevel@tonic-gate  */
2177c478bd9Sstevel@tonic-gate   int login_user = !user || *user=='\0';
2187c478bd9Sstevel@tonic-gate /*
2197c478bd9Sstevel@tonic-gate  * Check the arguments.
2207c478bd9Sstevel@tonic-gate  */
2217c478bd9Sstevel@tonic-gate   if(!home) {
2227c478bd9Sstevel@tonic-gate     errno = EINVAL;
2237c478bd9Sstevel@tonic-gate     return NULL;
2247c478bd9Sstevel@tonic-gate   };
2257c478bd9Sstevel@tonic-gate /*
2267c478bd9Sstevel@tonic-gate  * Handle the ksh "~+". This expands to the absolute path of the
2277c478bd9Sstevel@tonic-gate  * current working directory.
2287c478bd9Sstevel@tonic-gate  */
2297c478bd9Sstevel@tonic-gate   if(!login_user && strcmp(user, "+") == 0) {
2307c478bd9Sstevel@tonic-gate     home_dir = hd_getpwd(home);
2317c478bd9Sstevel@tonic-gate     if(!home_dir) {
2327c478bd9Sstevel@tonic-gate       _err_record_msg(home->err, "Can't determine current directory",
2337c478bd9Sstevel@tonic-gate 		      END_ERR_MSG);
2347c478bd9Sstevel@tonic-gate       return NULL;
2357c478bd9Sstevel@tonic-gate     }
2367c478bd9Sstevel@tonic-gate     return home_dir;
2377c478bd9Sstevel@tonic-gate   };
2387c478bd9Sstevel@tonic-gate /*
2397c478bd9Sstevel@tonic-gate  * When looking up the home directory of the current user, see if the
2407c478bd9Sstevel@tonic-gate  * HOME environment variable is set, and if so, return its value.
2417c478bd9Sstevel@tonic-gate  */
2427c478bd9Sstevel@tonic-gate   if(login_user) {
2437c478bd9Sstevel@tonic-gate     home_dir = getenv("HOME");
2447c478bd9Sstevel@tonic-gate     if(home_dir)
2457c478bd9Sstevel@tonic-gate       return home_dir;
2467c478bd9Sstevel@tonic-gate   };
2477c478bd9Sstevel@tonic-gate /*
2487c478bd9Sstevel@tonic-gate  * Look up the password entry of the user.
2497c478bd9Sstevel@tonic-gate  * First the POSIX threads version - this is painful!
2507c478bd9Sstevel@tonic-gate  */
2517c478bd9Sstevel@tonic-gate #ifdef THREAD_COMPATIBLE
2527c478bd9Sstevel@tonic-gate   {
2537c478bd9Sstevel@tonic-gate     struct passwd *ret; /* The returned pointer to pwd */
2547c478bd9Sstevel@tonic-gate     int status;         /* The return value of getpwnam_r() */
2557c478bd9Sstevel@tonic-gate /*
2567c478bd9Sstevel@tonic-gate  * Look up the password entry of the specified user.
2577c478bd9Sstevel@tonic-gate  */
2587c478bd9Sstevel@tonic-gate     if(login_user)
2597c478bd9Sstevel@tonic-gate       status = getpwuid_r(geteuid(), &home->pwd, home->buffer, home->buflen,
2607c478bd9Sstevel@tonic-gate 			  &ret);
2617c478bd9Sstevel@tonic-gate     else
2627c478bd9Sstevel@tonic-gate       status = getpwnam_r(user, &home->pwd, home->buffer, home->buflen, &ret);
2637c478bd9Sstevel@tonic-gate     if(status || !ret) {
2647c478bd9Sstevel@tonic-gate       _err_record_msg(home->err, "User '", user, "' doesn't exist.",
2657c478bd9Sstevel@tonic-gate 		      END_ERR_MSG);
2667c478bd9Sstevel@tonic-gate       return NULL;
2677c478bd9Sstevel@tonic-gate     };
2687c478bd9Sstevel@tonic-gate /*
2697c478bd9Sstevel@tonic-gate  * Get a pointer to the string that holds the home directory.
2707c478bd9Sstevel@tonic-gate  */
2717c478bd9Sstevel@tonic-gate     home_dir = home->pwd.pw_dir;
2727c478bd9Sstevel@tonic-gate   };
2737c478bd9Sstevel@tonic-gate /*
2747c478bd9Sstevel@tonic-gate  * Now the classic unix version.
2757c478bd9Sstevel@tonic-gate  */
2767c478bd9Sstevel@tonic-gate #else
2777c478bd9Sstevel@tonic-gate   {
2787c478bd9Sstevel@tonic-gate     struct passwd *pwd = login_user ? getpwuid(geteuid()) : getpwnam(user);
2797c478bd9Sstevel@tonic-gate     if(!pwd) {
2807c478bd9Sstevel@tonic-gate       _err_record_msg(home->err, "User '", user, "' doesn't exist.",
2817c478bd9Sstevel@tonic-gate 		      END_ERR_MSG);
2827c478bd9Sstevel@tonic-gate       return NULL;
2837c478bd9Sstevel@tonic-gate     };
2847c478bd9Sstevel@tonic-gate /*
2857c478bd9Sstevel@tonic-gate  * Get a pointer to the home directory.
2867c478bd9Sstevel@tonic-gate  */
2877c478bd9Sstevel@tonic-gate     home_dir = pwd->pw_dir;
2887c478bd9Sstevel@tonic-gate   };
2897c478bd9Sstevel@tonic-gate #endif
2907c478bd9Sstevel@tonic-gate   return home_dir;
2917c478bd9Sstevel@tonic-gate }
2927c478bd9Sstevel@tonic-gate 
2937c478bd9Sstevel@tonic-gate /*.......................................................................
2947c478bd9Sstevel@tonic-gate  * Return a description of the last error that caused _hd_lookup_home_dir()
2957c478bd9Sstevel@tonic-gate  * to return NULL.
2967c478bd9Sstevel@tonic-gate  *
2977c478bd9Sstevel@tonic-gate  * Input:
2987c478bd9Sstevel@tonic-gate  *  home   HomeDir *  The resources needed to record the home directory.
2997c478bd9Sstevel@tonic-gate  * Output:
3007c478bd9Sstevel@tonic-gate  *  return    char *  The description of the last error.
3017c478bd9Sstevel@tonic-gate  */
_hd_last_home_dir_error(HomeDir * home)3027c478bd9Sstevel@tonic-gate const char *_hd_last_home_dir_error(HomeDir *home)
3037c478bd9Sstevel@tonic-gate {
3047c478bd9Sstevel@tonic-gate   return home ? _err_get_msg(home->err) : "NULL HomeDir argument";
3057c478bd9Sstevel@tonic-gate }
3067c478bd9Sstevel@tonic-gate 
3077c478bd9Sstevel@tonic-gate /*.......................................................................
3087c478bd9Sstevel@tonic-gate  * The _hd_scan_user_home_dirs() function calls a user-provided function
3097c478bd9Sstevel@tonic-gate  * for each username known by the system, passing the function both
3107c478bd9Sstevel@tonic-gate  * the name and the home directory of the user.
3117c478bd9Sstevel@tonic-gate  *
3127c478bd9Sstevel@tonic-gate  * Input:
3137c478bd9Sstevel@tonic-gate  *  home             HomeDir *  The resource object for reading home
3147c478bd9Sstevel@tonic-gate  *                              directories.
3157c478bd9Sstevel@tonic-gate  *  prefix        const char *  Only information for usernames that
3167c478bd9Sstevel@tonic-gate  *                              start with this prefix will be
3177c478bd9Sstevel@tonic-gate  *                              returned. Note that the empty
3187c478bd9Sstevel@tonic-gate  &                              string "", matches all usernames.
3197c478bd9Sstevel@tonic-gate  *  data                void *  Anonymous data to be passed to the
3207c478bd9Sstevel@tonic-gate  *                              callback function.
3217c478bd9Sstevel@tonic-gate  *  callback_fn  HOME_DIR_FN(*) The function to call for each user.
3227c478bd9Sstevel@tonic-gate  * Output:
3237c478bd9Sstevel@tonic-gate  *  return               int    0 - Successful completion.
3247c478bd9Sstevel@tonic-gate  *                              1 - An error occurred. A description
3257c478bd9Sstevel@tonic-gate  *                                  of the error can be obtained by
3267c478bd9Sstevel@tonic-gate  *                                  calling _hd_last_home_dir_error().
3277c478bd9Sstevel@tonic-gate  */
_hd_scan_user_home_dirs(HomeDir * home,const char * prefix,void * data,HOME_DIR_FN (* callback_fn))3287c478bd9Sstevel@tonic-gate int _hd_scan_user_home_dirs(HomeDir *home, const char *prefix,
3297c478bd9Sstevel@tonic-gate 			    void *data, HOME_DIR_FN(*callback_fn))
3307c478bd9Sstevel@tonic-gate {
3317c478bd9Sstevel@tonic-gate   int waserr = 0;       /* True after errors */
3327c478bd9Sstevel@tonic-gate   int prefix_len;       /* The length of prefix[] */
3337c478bd9Sstevel@tonic-gate /*
3347c478bd9Sstevel@tonic-gate  * Check the arguments.
3357c478bd9Sstevel@tonic-gate  */
3367c478bd9Sstevel@tonic-gate   if(!home || !prefix || !callback_fn) {
3377c478bd9Sstevel@tonic-gate     if(home) {
3387c478bd9Sstevel@tonic-gate       _err_record_msg(home->err,
3397c478bd9Sstevel@tonic-gate 		      "_hd_scan_user_home_dirs: Missing callback function",
3407c478bd9Sstevel@tonic-gate 		      END_ERR_MSG);
3417c478bd9Sstevel@tonic-gate     };
3427c478bd9Sstevel@tonic-gate     return 1;
3437c478bd9Sstevel@tonic-gate   };
3447c478bd9Sstevel@tonic-gate /*
3457c478bd9Sstevel@tonic-gate  * Get the length of the username prefix.
3467c478bd9Sstevel@tonic-gate  */
3477c478bd9Sstevel@tonic-gate   prefix_len = strlen(prefix);
3487c478bd9Sstevel@tonic-gate /*
3497c478bd9Sstevel@tonic-gate  * There are no reentrant versions of getpwent() etc for scanning
3507c478bd9Sstevel@tonic-gate  * the password file, so disable username completion when the
3517c478bd9Sstevel@tonic-gate  * library is compiled to be reentrant.
3527c478bd9Sstevel@tonic-gate  */
3537c478bd9Sstevel@tonic-gate #if defined(PREFER_REENTRANT) && defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199506L
3547c478bd9Sstevel@tonic-gate #if defined __sun && defined __SVR4
3557c478bd9Sstevel@tonic-gate   if(0)
3567c478bd9Sstevel@tonic-gate #else
3577c478bd9Sstevel@tonic-gate   if(1)
3587c478bd9Sstevel@tonic-gate #endif
3597c478bd9Sstevel@tonic-gate   {
3607c478bd9Sstevel@tonic-gate     struct passwd pwd_buffer;  /* A returned password entry */
3617c478bd9Sstevel@tonic-gate     struct passwd *pwd;        /* A pointer to pwd_buffer */
3627c478bd9Sstevel@tonic-gate     char buffer[512];          /* The buffer in which the string members of */
3637c478bd9Sstevel@tonic-gate                                /* pwd_buffer are stored. */
3647c478bd9Sstevel@tonic-gate /*
3657c478bd9Sstevel@tonic-gate  * See if the prefix that is being completed is a complete username.
3667c478bd9Sstevel@tonic-gate  */
3677c478bd9Sstevel@tonic-gate     if(!waserr && getpwnam_r(prefix, &pwd_buffer, buffer, sizeof(buffer),
3687c478bd9Sstevel@tonic-gate 			     &pwd) == 0 && pwd != NULL) {
3697c478bd9Sstevel@tonic-gate       waserr = callback_fn(data, pwd->pw_name, pwd->pw_dir,
3707c478bd9Sstevel@tonic-gate 			   _err_get_msg(home->err), ERR_MSG_LEN);
3717c478bd9Sstevel@tonic-gate     };
3727c478bd9Sstevel@tonic-gate /*
3737c478bd9Sstevel@tonic-gate  * See if the username of the current user minimally matches the prefix.
3747c478bd9Sstevel@tonic-gate  */
3757c478bd9Sstevel@tonic-gate     if(!waserr && getpwuid_r(getuid(), &pwd_buffer, buffer, sizeof(buffer),
3767c478bd9Sstevel@tonic-gate 			     &pwd) == 0 && pwd != NULL &&
3777c478bd9Sstevel@tonic-gate                              strncmp(prefix, pwd->pw_name, prefix_len)==0) {
3787c478bd9Sstevel@tonic-gate       waserr = callback_fn(data, pwd->pw_name, pwd->pw_dir,
3797c478bd9Sstevel@tonic-gate 			   _err_get_msg(home->err), ERR_MSG_LEN);
3807c478bd9Sstevel@tonic-gate     };
3817c478bd9Sstevel@tonic-gate /*
3827c478bd9Sstevel@tonic-gate  * Reentrancy not required?
3837c478bd9Sstevel@tonic-gate  */
3847c478bd9Sstevel@tonic-gate   } else
3857c478bd9Sstevel@tonic-gate #endif
3867c478bd9Sstevel@tonic-gate   {
3877c478bd9Sstevel@tonic-gate     struct passwd pwd_buffer;  /* A returned password entry */
3887c478bd9Sstevel@tonic-gate     struct passwd *pwd;   /* The pointer to the latest password entry */
3897c478bd9Sstevel@tonic-gate /*
3907c478bd9Sstevel@tonic-gate  * Open the password file.
3917c478bd9Sstevel@tonic-gate  */
3927c478bd9Sstevel@tonic-gate     setpwent();
3937c478bd9Sstevel@tonic-gate /*
3947c478bd9Sstevel@tonic-gate  * Read the contents of the password file, looking for usernames
3957c478bd9Sstevel@tonic-gate  * that start with the specified prefix, and adding them to the
3967c478bd9Sstevel@tonic-gate  * list of matches.
3977c478bd9Sstevel@tonic-gate  */
3987c478bd9Sstevel@tonic-gate #if defined __sun && defined __SVR4
3997c478bd9Sstevel@tonic-gate     while((pwd = getpwent_r(&pwd_buffer, home->buffer, home->buflen)) != NULL && !waserr) {
4007c478bd9Sstevel@tonic-gate #else
4017c478bd9Sstevel@tonic-gate     while((pwd = getpwent()) != NULL && !waserr) {
4027c478bd9Sstevel@tonic-gate #endif
4037c478bd9Sstevel@tonic-gate       if(strncmp(prefix, pwd->pw_name, prefix_len) == 0) {
4047c478bd9Sstevel@tonic-gate 	waserr = callback_fn(data, pwd->pw_name, pwd->pw_dir,
4057c478bd9Sstevel@tonic-gate 			     _err_get_msg(home->err), ERR_MSG_LEN);
4067c478bd9Sstevel@tonic-gate       };
4077c478bd9Sstevel@tonic-gate     };
4087c478bd9Sstevel@tonic-gate /*
4097c478bd9Sstevel@tonic-gate  * Close the password file.
4107c478bd9Sstevel@tonic-gate  */
4117c478bd9Sstevel@tonic-gate     endpwent();
4127c478bd9Sstevel@tonic-gate   };
4137c478bd9Sstevel@tonic-gate /*
4147c478bd9Sstevel@tonic-gate  * Under ksh ~+ stands for the absolute pathname of the current working
4157c478bd9Sstevel@tonic-gate  * directory.
4167c478bd9Sstevel@tonic-gate  */
4177c478bd9Sstevel@tonic-gate   if(!waserr && strncmp(prefix, "+", prefix_len) == 0) {
4187c478bd9Sstevel@tonic-gate     const char *pwd = hd_getpwd(home);
4197c478bd9Sstevel@tonic-gate     if(pwd) {
4207c478bd9Sstevel@tonic-gate       waserr = callback_fn(data, "+", pwd, _err_get_msg(home->err),ERR_MSG_LEN);
4217c478bd9Sstevel@tonic-gate     } else {
4227c478bd9Sstevel@tonic-gate       waserr = 1;
4237c478bd9Sstevel@tonic-gate       _err_record_msg(home->err, "Can't determine current directory.",
4247c478bd9Sstevel@tonic-gate 		      END_ERR_MSG);
4257c478bd9Sstevel@tonic-gate     };
4267c478bd9Sstevel@tonic-gate   };
4277c478bd9Sstevel@tonic-gate   return waserr;
4287c478bd9Sstevel@tonic-gate }
4297c478bd9Sstevel@tonic-gate 
4307c478bd9Sstevel@tonic-gate /*.......................................................................
4317c478bd9Sstevel@tonic-gate  * Return the value of getenv("PWD") if this points to the current
4327c478bd9Sstevel@tonic-gate  * directory, or the return value of getcwd() otherwise. The reason for
4337c478bd9Sstevel@tonic-gate  * prefering PWD over getcwd() is that the former preserves the history
4347c478bd9Sstevel@tonic-gate  * of symbolic links that have been traversed to reach the current
4357c478bd9Sstevel@tonic-gate  * directory. This function is designed to provide the equivalent
4367c478bd9Sstevel@tonic-gate  * expansion of the ksh ~+ directive, which normally returns its value
4377c478bd9Sstevel@tonic-gate  * of PWD.
4387c478bd9Sstevel@tonic-gate  *
4397c478bd9Sstevel@tonic-gate  * Input:
4407c478bd9Sstevel@tonic-gate  *  home      HomeDir *  The resource object for reading home directories.
4417c478bd9Sstevel@tonic-gate  * Output:
4427c478bd9Sstevel@tonic-gate  *  return const char *  A pointer to either home->buffer, where the
4437c478bd9Sstevel@tonic-gate  *                       pathname is recorded, the string returned by
4447c478bd9Sstevel@tonic-gate  *                       getenv("PWD"), or NULL on error.
4457c478bd9Sstevel@tonic-gate  */
4467c478bd9Sstevel@tonic-gate static const char *hd_getpwd(HomeDir *home)
4477c478bd9Sstevel@tonic-gate {
4487c478bd9Sstevel@tonic-gate /*
4497c478bd9Sstevel@tonic-gate  * Get the absolute path of the current working directory.
4507c478bd9Sstevel@tonic-gate  */
4517c478bd9Sstevel@tonic-gate   char *cwd = getcwd(home->buffer, home->buflen);
4527c478bd9Sstevel@tonic-gate /*
4537c478bd9Sstevel@tonic-gate  * Some shells set PWD with the path of the current working directory.
4547c478bd9Sstevel@tonic-gate  * This will differ from cwd in that it won't have had symbolic links
4557c478bd9Sstevel@tonic-gate  * expanded.
4567c478bd9Sstevel@tonic-gate  */
4577c478bd9Sstevel@tonic-gate   const char *pwd = getenv("PWD");
4587c478bd9Sstevel@tonic-gate /*
4597c478bd9Sstevel@tonic-gate  * If PWD was set, and it points to the same directory as cwd, return
4607c478bd9Sstevel@tonic-gate  * its value. Note that it won't be the same if the current shell or
4617c478bd9Sstevel@tonic-gate  * the current program has changed directories, after inheriting PWD
4627c478bd9Sstevel@tonic-gate  * from a parent shell.
4637c478bd9Sstevel@tonic-gate  */
4647c478bd9Sstevel@tonic-gate   struct stat cwdstat, pwdstat;
4657c478bd9Sstevel@tonic-gate   if(pwd && cwd && stat(cwd, &cwdstat)==0 && stat(pwd, &pwdstat)==0 &&
4667c478bd9Sstevel@tonic-gate      cwdstat.st_dev == pwdstat.st_dev && cwdstat.st_ino == pwdstat.st_ino)
4677c478bd9Sstevel@tonic-gate     return pwd;
4687c478bd9Sstevel@tonic-gate /*
4697c478bd9Sstevel@tonic-gate  * Also return pwd if getcwd() failed, since it represents the best
4707c478bd9Sstevel@tonic-gate  * information that we have access to.
4717c478bd9Sstevel@tonic-gate  */
4727c478bd9Sstevel@tonic-gate   if(!cwd)
4737c478bd9Sstevel@tonic-gate     return pwd;
4747c478bd9Sstevel@tonic-gate /*
4757c478bd9Sstevel@tonic-gate  * In the absence of a valid PWD, return cwd.
4767c478bd9Sstevel@tonic-gate  */
4777c478bd9Sstevel@tonic-gate   return cwd;
4787c478bd9Sstevel@tonic-gate }
4797c478bd9Sstevel@tonic-gate 
4807c478bd9Sstevel@tonic-gate #endif  /* ifndef WITHOUT_FILE_SYSTEM */
481