xref: /titanic_51/usr/src/lib/libtecla/common/homedir.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd.
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * All rights reserved.
5*7c478bd9Sstevel@tonic-gate  *
6*7c478bd9Sstevel@tonic-gate  * Permission is hereby granted, free of charge, to any person obtaining a
7*7c478bd9Sstevel@tonic-gate  * copy of this software and associated documentation files (the
8*7c478bd9Sstevel@tonic-gate  * "Software"), to deal in the Software without restriction, including
9*7c478bd9Sstevel@tonic-gate  * without limitation the rights to use, copy, modify, merge, publish,
10*7c478bd9Sstevel@tonic-gate  * distribute, and/or sell copies of the Software, and to permit persons
11*7c478bd9Sstevel@tonic-gate  * to whom the Software is furnished to do so, provided that the above
12*7c478bd9Sstevel@tonic-gate  * copyright notice(s) and this permission notice appear in all copies of
13*7c478bd9Sstevel@tonic-gate  * the Software and that both the above copyright notice(s) and this
14*7c478bd9Sstevel@tonic-gate  * permission notice appear in supporting documentation.
15*7c478bd9Sstevel@tonic-gate  *
16*7c478bd9Sstevel@tonic-gate  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17*7c478bd9Sstevel@tonic-gate  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18*7c478bd9Sstevel@tonic-gate  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
19*7c478bd9Sstevel@tonic-gate  * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
20*7c478bd9Sstevel@tonic-gate  * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
21*7c478bd9Sstevel@tonic-gate  * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
22*7c478bd9Sstevel@tonic-gate  * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
23*7c478bd9Sstevel@tonic-gate  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
24*7c478bd9Sstevel@tonic-gate  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25*7c478bd9Sstevel@tonic-gate  *
26*7c478bd9Sstevel@tonic-gate  * Except as contained in this notice, the name of a copyright holder
27*7c478bd9Sstevel@tonic-gate  * shall not be used in advertising or otherwise to promote the sale, use
28*7c478bd9Sstevel@tonic-gate  * or other dealings in this Software without prior written authorization
29*7c478bd9Sstevel@tonic-gate  * of the copyright holder.
30*7c478bd9Sstevel@tonic-gate  */
31*7c478bd9Sstevel@tonic-gate 
32*7c478bd9Sstevel@tonic-gate /*
33*7c478bd9Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
34*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
35*7c478bd9Sstevel@tonic-gate  */
36*7c478bd9Sstevel@tonic-gate 
37*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
38*7c478bd9Sstevel@tonic-gate 
39*7c478bd9Sstevel@tonic-gate /*
40*7c478bd9Sstevel@tonic-gate  * If file-system access is to be excluded, this module has no function,
41*7c478bd9Sstevel@tonic-gate  * so all of its code should be excluded.
42*7c478bd9Sstevel@tonic-gate  */
43*7c478bd9Sstevel@tonic-gate #ifndef WITHOUT_FILE_SYSTEM
44*7c478bd9Sstevel@tonic-gate 
45*7c478bd9Sstevel@tonic-gate #include <stdio.h>
46*7c478bd9Sstevel@tonic-gate #include <stdlib.h>
47*7c478bd9Sstevel@tonic-gate #include <string.h>
48*7c478bd9Sstevel@tonic-gate #include <errno.h>
49*7c478bd9Sstevel@tonic-gate 
50*7c478bd9Sstevel@tonic-gate #include <unistd.h>
51*7c478bd9Sstevel@tonic-gate #include <sys/types.h>
52*7c478bd9Sstevel@tonic-gate #include <sys/stat.h>
53*7c478bd9Sstevel@tonic-gate #include <pwd.h>
54*7c478bd9Sstevel@tonic-gate 
55*7c478bd9Sstevel@tonic-gate #include "pathutil.h"
56*7c478bd9Sstevel@tonic-gate #include "homedir.h"
57*7c478bd9Sstevel@tonic-gate #include "errmsg.h"
58*7c478bd9Sstevel@tonic-gate 
59*7c478bd9Sstevel@tonic-gate /*
60*7c478bd9Sstevel@tonic-gate  * Use the reentrant POSIX threads versions of the password lookup functions?
61*7c478bd9Sstevel@tonic-gate  */
62*7c478bd9Sstevel@tonic-gate #if defined(PREFER_REENTRANT) && defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199506L
63*7c478bd9Sstevel@tonic-gate #define THREAD_COMPATIBLE 1
64*7c478bd9Sstevel@tonic-gate /*
65*7c478bd9Sstevel@tonic-gate  * Under Solaris we can use thr_main() to determine whether
66*7c478bd9Sstevel@tonic-gate  * threads are actually running, and thus when it is necessary
67*7c478bd9Sstevel@tonic-gate  * to avoid non-reentrant features.
68*7c478bd9Sstevel@tonic-gate  */
69*7c478bd9Sstevel@tonic-gate #if defined __sun && defined __SVR4
70*7c478bd9Sstevel@tonic-gate #include <thread.h>                      /* Solaris thr_main() */
71*7c478bd9Sstevel@tonic-gate #endif
72*7c478bd9Sstevel@tonic-gate #endif
73*7c478bd9Sstevel@tonic-gate 
74*7c478bd9Sstevel@tonic-gate /*
75*7c478bd9Sstevel@tonic-gate  * Provide a password buffer size fallback in case the max size reported
76*7c478bd9Sstevel@tonic-gate  * by sysconf() is said to be indeterminate.
77*7c478bd9Sstevel@tonic-gate  */
78*7c478bd9Sstevel@tonic-gate #define DEF_GETPW_R_SIZE_MAX 1024
79*7c478bd9Sstevel@tonic-gate 
80*7c478bd9Sstevel@tonic-gate /*
81*7c478bd9Sstevel@tonic-gate  * The resources needed to lookup and record a home directory are
82*7c478bd9Sstevel@tonic-gate  * maintained in objects of the following type.
83*7c478bd9Sstevel@tonic-gate  */
84*7c478bd9Sstevel@tonic-gate struct HomeDir {
85*7c478bd9Sstevel@tonic-gate   ErrMsg *err;             /* The error message report buffer */
86*7c478bd9Sstevel@tonic-gate   char *buffer;            /* A buffer for reading password entries and */
87*7c478bd9Sstevel@tonic-gate                            /*  directory paths. */
88*7c478bd9Sstevel@tonic-gate   int buflen;              /* The allocated size of buffer[] */
89*7c478bd9Sstevel@tonic-gate #ifdef THREAD_COMPATIBLE
90*7c478bd9Sstevel@tonic-gate   struct passwd pwd;       /* The password entry of a user */
91*7c478bd9Sstevel@tonic-gate #endif
92*7c478bd9Sstevel@tonic-gate };
93*7c478bd9Sstevel@tonic-gate 
94*7c478bd9Sstevel@tonic-gate static const char *hd_getpwd(HomeDir *home);
95*7c478bd9Sstevel@tonic-gate 
96*7c478bd9Sstevel@tonic-gate /*.......................................................................
97*7c478bd9Sstevel@tonic-gate  * Create a new HomeDir object.
98*7c478bd9Sstevel@tonic-gate  *
99*7c478bd9Sstevel@tonic-gate  * Output:
100*7c478bd9Sstevel@tonic-gate  *  return  HomeDir *  The new object, or NULL on error.
101*7c478bd9Sstevel@tonic-gate  */
102*7c478bd9Sstevel@tonic-gate HomeDir *_new_HomeDir(void)
103*7c478bd9Sstevel@tonic-gate {
104*7c478bd9Sstevel@tonic-gate   HomeDir *home;  /* The object to be returned */
105*7c478bd9Sstevel@tonic-gate   size_t pathlen; /* The estimated maximum size of a pathname */
106*7c478bd9Sstevel@tonic-gate /*
107*7c478bd9Sstevel@tonic-gate  * Allocate the container.
108*7c478bd9Sstevel@tonic-gate  */
109*7c478bd9Sstevel@tonic-gate   home = (HomeDir *) malloc(sizeof(HomeDir));
110*7c478bd9Sstevel@tonic-gate   if(!home) {
111*7c478bd9Sstevel@tonic-gate     errno = ENOMEM;
112*7c478bd9Sstevel@tonic-gate     return NULL;
113*7c478bd9Sstevel@tonic-gate   };
114*7c478bd9Sstevel@tonic-gate /*
115*7c478bd9Sstevel@tonic-gate  * Before attempting any operation that might fail, initialize the
116*7c478bd9Sstevel@tonic-gate  * container at least up to the point at which it can safely be passed
117*7c478bd9Sstevel@tonic-gate  * to _del_HomeDir().
118*7c478bd9Sstevel@tonic-gate  */
119*7c478bd9Sstevel@tonic-gate   home->err = NULL;
120*7c478bd9Sstevel@tonic-gate   home->buffer = NULL;
121*7c478bd9Sstevel@tonic-gate   home->buflen = 0;
122*7c478bd9Sstevel@tonic-gate /*
123*7c478bd9Sstevel@tonic-gate  * Allocate a place to record error messages.
124*7c478bd9Sstevel@tonic-gate  */
125*7c478bd9Sstevel@tonic-gate   home->err = _new_ErrMsg();
126*7c478bd9Sstevel@tonic-gate   if(!home->err)
127*7c478bd9Sstevel@tonic-gate     return _del_HomeDir(home);
128*7c478bd9Sstevel@tonic-gate /*
129*7c478bd9Sstevel@tonic-gate  * Allocate the buffer that is used by the reentrant POSIX password-entry
130*7c478bd9Sstevel@tonic-gate  * lookup functions.
131*7c478bd9Sstevel@tonic-gate  */
132*7c478bd9Sstevel@tonic-gate #ifdef THREAD_COMPATIBLE
133*7c478bd9Sstevel@tonic-gate /*
134*7c478bd9Sstevel@tonic-gate  * Get the length of the buffer needed by the reentrant version
135*7c478bd9Sstevel@tonic-gate  * of getpwnam().
136*7c478bd9Sstevel@tonic-gate  */
137*7c478bd9Sstevel@tonic-gate #ifndef _SC_GETPW_R_SIZE_MAX
138*7c478bd9Sstevel@tonic-gate   home->buflen = DEF_GETPW_R_SIZE_MAX;
139*7c478bd9Sstevel@tonic-gate #else
140*7c478bd9Sstevel@tonic-gate   errno = 0;
141*7c478bd9Sstevel@tonic-gate   home->buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
142*7c478bd9Sstevel@tonic-gate /*
143*7c478bd9Sstevel@tonic-gate  * If the limit isn't available, substitute a suitably large fallback value.
144*7c478bd9Sstevel@tonic-gate  */
145*7c478bd9Sstevel@tonic-gate   if(home->buflen < 0 || errno)
146*7c478bd9Sstevel@tonic-gate     home->buflen = DEF_GETPW_R_SIZE_MAX;
147*7c478bd9Sstevel@tonic-gate #endif
148*7c478bd9Sstevel@tonic-gate #endif
149*7c478bd9Sstevel@tonic-gate /*
150*7c478bd9Sstevel@tonic-gate  * If the existing buffer length requirement is too restrictive to record
151*7c478bd9Sstevel@tonic-gate  * a pathname, increase its length.
152*7c478bd9Sstevel@tonic-gate  */
153*7c478bd9Sstevel@tonic-gate   pathlen = _pu_pathname_dim();
154*7c478bd9Sstevel@tonic-gate   if(pathlen > home->buflen)
155*7c478bd9Sstevel@tonic-gate     home->buflen = pathlen;
156*7c478bd9Sstevel@tonic-gate /*
157*7c478bd9Sstevel@tonic-gate  * Allocate a work buffer.
158*7c478bd9Sstevel@tonic-gate  */
159*7c478bd9Sstevel@tonic-gate   home->buffer = (char *) malloc(home->buflen);
160*7c478bd9Sstevel@tonic-gate   if(!home->buffer) {
161*7c478bd9Sstevel@tonic-gate     errno = ENOMEM;
162*7c478bd9Sstevel@tonic-gate     return _del_HomeDir(home);
163*7c478bd9Sstevel@tonic-gate   };
164*7c478bd9Sstevel@tonic-gate   return home;
165*7c478bd9Sstevel@tonic-gate }
166*7c478bd9Sstevel@tonic-gate 
167*7c478bd9Sstevel@tonic-gate /*.......................................................................
168*7c478bd9Sstevel@tonic-gate  * Delete a HomeDir object.
169*7c478bd9Sstevel@tonic-gate  *
170*7c478bd9Sstevel@tonic-gate  * Input:
171*7c478bd9Sstevel@tonic-gate  *  home   HomeDir *  The object to be deleted.
172*7c478bd9Sstevel@tonic-gate  * Output:
173*7c478bd9Sstevel@tonic-gate  *  return HomeDir *  The deleted object (always NULL).
174*7c478bd9Sstevel@tonic-gate  */
175*7c478bd9Sstevel@tonic-gate HomeDir *_del_HomeDir(HomeDir *home)
176*7c478bd9Sstevel@tonic-gate {
177*7c478bd9Sstevel@tonic-gate   if(home) {
178*7c478bd9Sstevel@tonic-gate     home->err = _del_ErrMsg(home->err);
179*7c478bd9Sstevel@tonic-gate     if(home->buffer)
180*7c478bd9Sstevel@tonic-gate       free(home->buffer);
181*7c478bd9Sstevel@tonic-gate     free(home);
182*7c478bd9Sstevel@tonic-gate   };
183*7c478bd9Sstevel@tonic-gate   return NULL;
184*7c478bd9Sstevel@tonic-gate }
185*7c478bd9Sstevel@tonic-gate 
186*7c478bd9Sstevel@tonic-gate /*.......................................................................
187*7c478bd9Sstevel@tonic-gate  * Lookup the home directory of a given user in the password file.
188*7c478bd9Sstevel@tonic-gate  *
189*7c478bd9Sstevel@tonic-gate  * Input:
190*7c478bd9Sstevel@tonic-gate  *  home      HomeDir *   The resources needed to lookup the home directory.
191*7c478bd9Sstevel@tonic-gate  *  user   const char *   The name of the user to lookup, or "" to lookup
192*7c478bd9Sstevel@tonic-gate  *                        the home directory of the person running the
193*7c478bd9Sstevel@tonic-gate  *                        program.
194*7c478bd9Sstevel@tonic-gate  * Output:
195*7c478bd9Sstevel@tonic-gate  *  return const char *   The home directory. If the library was compiled
196*7c478bd9Sstevel@tonic-gate  *                        with threads, this string is part of the HomeDir
197*7c478bd9Sstevel@tonic-gate  *                        object and will change on subsequent calls. If
198*7c478bd9Sstevel@tonic-gate  *                        the library wasn't compiled to be reentrant,
199*7c478bd9Sstevel@tonic-gate  *                        then the string is a pointer into a static string
200*7c478bd9Sstevel@tonic-gate  *                        in the C library and will change not only on
201*7c478bd9Sstevel@tonic-gate  *                        subsequent calls to this function, but also if
202*7c478bd9Sstevel@tonic-gate  *                        any calls are made to the C library password
203*7c478bd9Sstevel@tonic-gate  *                        file lookup functions. Thus to be safe, you should
204*7c478bd9Sstevel@tonic-gate  *                        make a copy of this string before calling any
205*7c478bd9Sstevel@tonic-gate  *                        other function that might do a password file
206*7c478bd9Sstevel@tonic-gate  *                        lookup.
207*7c478bd9Sstevel@tonic-gate  *
208*7c478bd9Sstevel@tonic-gate  *                        On error, NULL is returned and a description
209*7c478bd9Sstevel@tonic-gate  *                        of the error can be acquired by calling
210*7c478bd9Sstevel@tonic-gate  *                        _hd_last_home_dir_error().
211*7c478bd9Sstevel@tonic-gate  */
212*7c478bd9Sstevel@tonic-gate const char *_hd_lookup_home_dir(HomeDir *home, const char *user)
213*7c478bd9Sstevel@tonic-gate {
214*7c478bd9Sstevel@tonic-gate   const char *home_dir;   /* A pointer to the home directory of the user */
215*7c478bd9Sstevel@tonic-gate /*
216*7c478bd9Sstevel@tonic-gate  * If no username has been specified, arrange to lookup the current
217*7c478bd9Sstevel@tonic-gate  * user.
218*7c478bd9Sstevel@tonic-gate  */
219*7c478bd9Sstevel@tonic-gate   int login_user = !user || *user=='\0';
220*7c478bd9Sstevel@tonic-gate /*
221*7c478bd9Sstevel@tonic-gate  * Check the arguments.
222*7c478bd9Sstevel@tonic-gate  */
223*7c478bd9Sstevel@tonic-gate   if(!home) {
224*7c478bd9Sstevel@tonic-gate     errno = EINVAL;
225*7c478bd9Sstevel@tonic-gate     return NULL;
226*7c478bd9Sstevel@tonic-gate   };
227*7c478bd9Sstevel@tonic-gate /*
228*7c478bd9Sstevel@tonic-gate  * Handle the ksh "~+". This expands to the absolute path of the
229*7c478bd9Sstevel@tonic-gate  * current working directory.
230*7c478bd9Sstevel@tonic-gate  */
231*7c478bd9Sstevel@tonic-gate   if(!login_user && strcmp(user, "+") == 0) {
232*7c478bd9Sstevel@tonic-gate     home_dir = hd_getpwd(home);
233*7c478bd9Sstevel@tonic-gate     if(!home_dir) {
234*7c478bd9Sstevel@tonic-gate       _err_record_msg(home->err, "Can't determine current directory",
235*7c478bd9Sstevel@tonic-gate 		      END_ERR_MSG);
236*7c478bd9Sstevel@tonic-gate       return NULL;
237*7c478bd9Sstevel@tonic-gate     }
238*7c478bd9Sstevel@tonic-gate     return home_dir;
239*7c478bd9Sstevel@tonic-gate   };
240*7c478bd9Sstevel@tonic-gate /*
241*7c478bd9Sstevel@tonic-gate  * When looking up the home directory of the current user, see if the
242*7c478bd9Sstevel@tonic-gate  * HOME environment variable is set, and if so, return its value.
243*7c478bd9Sstevel@tonic-gate  */
244*7c478bd9Sstevel@tonic-gate   if(login_user) {
245*7c478bd9Sstevel@tonic-gate     home_dir = getenv("HOME");
246*7c478bd9Sstevel@tonic-gate     if(home_dir)
247*7c478bd9Sstevel@tonic-gate       return home_dir;
248*7c478bd9Sstevel@tonic-gate   };
249*7c478bd9Sstevel@tonic-gate /*
250*7c478bd9Sstevel@tonic-gate  * Look up the password entry of the user.
251*7c478bd9Sstevel@tonic-gate  * First the POSIX threads version - this is painful!
252*7c478bd9Sstevel@tonic-gate  */
253*7c478bd9Sstevel@tonic-gate #ifdef THREAD_COMPATIBLE
254*7c478bd9Sstevel@tonic-gate   {
255*7c478bd9Sstevel@tonic-gate     struct passwd *ret; /* The returned pointer to pwd */
256*7c478bd9Sstevel@tonic-gate     int status;         /* The return value of getpwnam_r() */
257*7c478bd9Sstevel@tonic-gate /*
258*7c478bd9Sstevel@tonic-gate  * Look up the password entry of the specified user.
259*7c478bd9Sstevel@tonic-gate  */
260*7c478bd9Sstevel@tonic-gate     if(login_user)
261*7c478bd9Sstevel@tonic-gate       status = getpwuid_r(geteuid(), &home->pwd, home->buffer, home->buflen,
262*7c478bd9Sstevel@tonic-gate 			  &ret);
263*7c478bd9Sstevel@tonic-gate     else
264*7c478bd9Sstevel@tonic-gate       status = getpwnam_r(user, &home->pwd, home->buffer, home->buflen, &ret);
265*7c478bd9Sstevel@tonic-gate     if(status || !ret) {
266*7c478bd9Sstevel@tonic-gate       _err_record_msg(home->err, "User '", user, "' doesn't exist.",
267*7c478bd9Sstevel@tonic-gate 		      END_ERR_MSG);
268*7c478bd9Sstevel@tonic-gate       return NULL;
269*7c478bd9Sstevel@tonic-gate     };
270*7c478bd9Sstevel@tonic-gate /*
271*7c478bd9Sstevel@tonic-gate  * Get a pointer to the string that holds the home directory.
272*7c478bd9Sstevel@tonic-gate  */
273*7c478bd9Sstevel@tonic-gate     home_dir = home->pwd.pw_dir;
274*7c478bd9Sstevel@tonic-gate   };
275*7c478bd9Sstevel@tonic-gate /*
276*7c478bd9Sstevel@tonic-gate  * Now the classic unix version.
277*7c478bd9Sstevel@tonic-gate  */
278*7c478bd9Sstevel@tonic-gate #else
279*7c478bd9Sstevel@tonic-gate   {
280*7c478bd9Sstevel@tonic-gate     struct passwd *pwd = login_user ? getpwuid(geteuid()) : getpwnam(user);
281*7c478bd9Sstevel@tonic-gate     if(!pwd) {
282*7c478bd9Sstevel@tonic-gate       _err_record_msg(home->err, "User '", user, "' doesn't exist.",
283*7c478bd9Sstevel@tonic-gate 		      END_ERR_MSG);
284*7c478bd9Sstevel@tonic-gate       return NULL;
285*7c478bd9Sstevel@tonic-gate     };
286*7c478bd9Sstevel@tonic-gate /*
287*7c478bd9Sstevel@tonic-gate  * Get a pointer to the home directory.
288*7c478bd9Sstevel@tonic-gate  */
289*7c478bd9Sstevel@tonic-gate     home_dir = pwd->pw_dir;
290*7c478bd9Sstevel@tonic-gate   };
291*7c478bd9Sstevel@tonic-gate #endif
292*7c478bd9Sstevel@tonic-gate   return home_dir;
293*7c478bd9Sstevel@tonic-gate }
294*7c478bd9Sstevel@tonic-gate 
295*7c478bd9Sstevel@tonic-gate /*.......................................................................
296*7c478bd9Sstevel@tonic-gate  * Return a description of the last error that caused _hd_lookup_home_dir()
297*7c478bd9Sstevel@tonic-gate  * to return NULL.
298*7c478bd9Sstevel@tonic-gate  *
299*7c478bd9Sstevel@tonic-gate  * Input:
300*7c478bd9Sstevel@tonic-gate  *  home   HomeDir *  The resources needed to record the home directory.
301*7c478bd9Sstevel@tonic-gate  * Output:
302*7c478bd9Sstevel@tonic-gate  *  return    char *  The description of the last error.
303*7c478bd9Sstevel@tonic-gate  */
304*7c478bd9Sstevel@tonic-gate const char *_hd_last_home_dir_error(HomeDir *home)
305*7c478bd9Sstevel@tonic-gate {
306*7c478bd9Sstevel@tonic-gate   return home ? _err_get_msg(home->err) : "NULL HomeDir argument";
307*7c478bd9Sstevel@tonic-gate }
308*7c478bd9Sstevel@tonic-gate 
309*7c478bd9Sstevel@tonic-gate /*.......................................................................
310*7c478bd9Sstevel@tonic-gate  * The _hd_scan_user_home_dirs() function calls a user-provided function
311*7c478bd9Sstevel@tonic-gate  * for each username known by the system, passing the function both
312*7c478bd9Sstevel@tonic-gate  * the name and the home directory of the user.
313*7c478bd9Sstevel@tonic-gate  *
314*7c478bd9Sstevel@tonic-gate  * Input:
315*7c478bd9Sstevel@tonic-gate  *  home             HomeDir *  The resource object for reading home
316*7c478bd9Sstevel@tonic-gate  *                              directories.
317*7c478bd9Sstevel@tonic-gate  *  prefix        const char *  Only information for usernames that
318*7c478bd9Sstevel@tonic-gate  *                              start with this prefix will be
319*7c478bd9Sstevel@tonic-gate  *                              returned. Note that the empty
320*7c478bd9Sstevel@tonic-gate  &                              string "", matches all usernames.
321*7c478bd9Sstevel@tonic-gate  *  data                void *  Anonymous data to be passed to the
322*7c478bd9Sstevel@tonic-gate  *                              callback function.
323*7c478bd9Sstevel@tonic-gate  *  callback_fn  HOME_DIR_FN(*) The function to call for each user.
324*7c478bd9Sstevel@tonic-gate  * Output:
325*7c478bd9Sstevel@tonic-gate  *  return               int    0 - Successful completion.
326*7c478bd9Sstevel@tonic-gate  *                              1 - An error occurred. A description
327*7c478bd9Sstevel@tonic-gate  *                                  of the error can be obtained by
328*7c478bd9Sstevel@tonic-gate  *                                  calling _hd_last_home_dir_error().
329*7c478bd9Sstevel@tonic-gate  */
330*7c478bd9Sstevel@tonic-gate int _hd_scan_user_home_dirs(HomeDir *home, const char *prefix,
331*7c478bd9Sstevel@tonic-gate 			    void *data, HOME_DIR_FN(*callback_fn))
332*7c478bd9Sstevel@tonic-gate {
333*7c478bd9Sstevel@tonic-gate   int waserr = 0;       /* True after errors */
334*7c478bd9Sstevel@tonic-gate   int prefix_len;       /* The length of prefix[] */
335*7c478bd9Sstevel@tonic-gate /*
336*7c478bd9Sstevel@tonic-gate  * Check the arguments.
337*7c478bd9Sstevel@tonic-gate  */
338*7c478bd9Sstevel@tonic-gate   if(!home || !prefix || !callback_fn) {
339*7c478bd9Sstevel@tonic-gate     if(home) {
340*7c478bd9Sstevel@tonic-gate       _err_record_msg(home->err,
341*7c478bd9Sstevel@tonic-gate 		      "_hd_scan_user_home_dirs: Missing callback function",
342*7c478bd9Sstevel@tonic-gate 		      END_ERR_MSG);
343*7c478bd9Sstevel@tonic-gate     };
344*7c478bd9Sstevel@tonic-gate     return 1;
345*7c478bd9Sstevel@tonic-gate   };
346*7c478bd9Sstevel@tonic-gate /*
347*7c478bd9Sstevel@tonic-gate  * Get the length of the username prefix.
348*7c478bd9Sstevel@tonic-gate  */
349*7c478bd9Sstevel@tonic-gate   prefix_len = strlen(prefix);
350*7c478bd9Sstevel@tonic-gate /*
351*7c478bd9Sstevel@tonic-gate  * There are no reentrant versions of getpwent() etc for scanning
352*7c478bd9Sstevel@tonic-gate  * the password file, so disable username completion when the
353*7c478bd9Sstevel@tonic-gate  * library is compiled to be reentrant.
354*7c478bd9Sstevel@tonic-gate  */
355*7c478bd9Sstevel@tonic-gate #if defined(PREFER_REENTRANT) && defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199506L
356*7c478bd9Sstevel@tonic-gate #if defined __sun && defined __SVR4
357*7c478bd9Sstevel@tonic-gate   if(0)
358*7c478bd9Sstevel@tonic-gate #else
359*7c478bd9Sstevel@tonic-gate   if(1)
360*7c478bd9Sstevel@tonic-gate #endif
361*7c478bd9Sstevel@tonic-gate   {
362*7c478bd9Sstevel@tonic-gate     struct passwd pwd_buffer;  /* A returned password entry */
363*7c478bd9Sstevel@tonic-gate     struct passwd *pwd;        /* A pointer to pwd_buffer */
364*7c478bd9Sstevel@tonic-gate     char buffer[512];          /* The buffer in which the string members of */
365*7c478bd9Sstevel@tonic-gate                                /* pwd_buffer are stored. */
366*7c478bd9Sstevel@tonic-gate /*
367*7c478bd9Sstevel@tonic-gate  * See if the prefix that is being completed is a complete username.
368*7c478bd9Sstevel@tonic-gate  */
369*7c478bd9Sstevel@tonic-gate     if(!waserr && getpwnam_r(prefix, &pwd_buffer, buffer, sizeof(buffer),
370*7c478bd9Sstevel@tonic-gate 			     &pwd) == 0 && pwd != NULL) {
371*7c478bd9Sstevel@tonic-gate       waserr = callback_fn(data, pwd->pw_name, pwd->pw_dir,
372*7c478bd9Sstevel@tonic-gate 			   _err_get_msg(home->err), ERR_MSG_LEN);
373*7c478bd9Sstevel@tonic-gate     };
374*7c478bd9Sstevel@tonic-gate /*
375*7c478bd9Sstevel@tonic-gate  * See if the username of the current user minimally matches the prefix.
376*7c478bd9Sstevel@tonic-gate  */
377*7c478bd9Sstevel@tonic-gate     if(!waserr && getpwuid_r(getuid(), &pwd_buffer, buffer, sizeof(buffer),
378*7c478bd9Sstevel@tonic-gate 			     &pwd) == 0 && pwd != NULL &&
379*7c478bd9Sstevel@tonic-gate                              strncmp(prefix, pwd->pw_name, prefix_len)==0) {
380*7c478bd9Sstevel@tonic-gate       waserr = callback_fn(data, pwd->pw_name, pwd->pw_dir,
381*7c478bd9Sstevel@tonic-gate 			   _err_get_msg(home->err), ERR_MSG_LEN);
382*7c478bd9Sstevel@tonic-gate     };
383*7c478bd9Sstevel@tonic-gate /*
384*7c478bd9Sstevel@tonic-gate  * Reentrancy not required?
385*7c478bd9Sstevel@tonic-gate  */
386*7c478bd9Sstevel@tonic-gate   } else
387*7c478bd9Sstevel@tonic-gate #endif
388*7c478bd9Sstevel@tonic-gate   {
389*7c478bd9Sstevel@tonic-gate     struct passwd pwd_buffer;  /* A returned password entry */
390*7c478bd9Sstevel@tonic-gate     struct passwd *pwd;   /* The pointer to the latest password entry */
391*7c478bd9Sstevel@tonic-gate /*
392*7c478bd9Sstevel@tonic-gate  * Open the password file.
393*7c478bd9Sstevel@tonic-gate  */
394*7c478bd9Sstevel@tonic-gate     setpwent();
395*7c478bd9Sstevel@tonic-gate /*
396*7c478bd9Sstevel@tonic-gate  * Read the contents of the password file, looking for usernames
397*7c478bd9Sstevel@tonic-gate  * that start with the specified prefix, and adding them to the
398*7c478bd9Sstevel@tonic-gate  * list of matches.
399*7c478bd9Sstevel@tonic-gate  */
400*7c478bd9Sstevel@tonic-gate #if defined __sun && defined __SVR4
401*7c478bd9Sstevel@tonic-gate     while((pwd = getpwent_r(&pwd_buffer, home->buffer, home->buflen)) != NULL && !waserr) {
402*7c478bd9Sstevel@tonic-gate #else
403*7c478bd9Sstevel@tonic-gate     while((pwd = getpwent()) != NULL && !waserr) {
404*7c478bd9Sstevel@tonic-gate #endif
405*7c478bd9Sstevel@tonic-gate       if(strncmp(prefix, pwd->pw_name, prefix_len) == 0) {
406*7c478bd9Sstevel@tonic-gate 	waserr = callback_fn(data, pwd->pw_name, pwd->pw_dir,
407*7c478bd9Sstevel@tonic-gate 			     _err_get_msg(home->err), ERR_MSG_LEN);
408*7c478bd9Sstevel@tonic-gate       };
409*7c478bd9Sstevel@tonic-gate     };
410*7c478bd9Sstevel@tonic-gate /*
411*7c478bd9Sstevel@tonic-gate  * Close the password file.
412*7c478bd9Sstevel@tonic-gate  */
413*7c478bd9Sstevel@tonic-gate     endpwent();
414*7c478bd9Sstevel@tonic-gate   };
415*7c478bd9Sstevel@tonic-gate /*
416*7c478bd9Sstevel@tonic-gate  * Under ksh ~+ stands for the absolute pathname of the current working
417*7c478bd9Sstevel@tonic-gate  * directory.
418*7c478bd9Sstevel@tonic-gate  */
419*7c478bd9Sstevel@tonic-gate   if(!waserr && strncmp(prefix, "+", prefix_len) == 0) {
420*7c478bd9Sstevel@tonic-gate     const char *pwd = hd_getpwd(home);
421*7c478bd9Sstevel@tonic-gate     if(pwd) {
422*7c478bd9Sstevel@tonic-gate       waserr = callback_fn(data, "+", pwd, _err_get_msg(home->err),ERR_MSG_LEN);
423*7c478bd9Sstevel@tonic-gate     } else {
424*7c478bd9Sstevel@tonic-gate       waserr = 1;
425*7c478bd9Sstevel@tonic-gate       _err_record_msg(home->err, "Can't determine current directory.",
426*7c478bd9Sstevel@tonic-gate 		      END_ERR_MSG);
427*7c478bd9Sstevel@tonic-gate     };
428*7c478bd9Sstevel@tonic-gate   };
429*7c478bd9Sstevel@tonic-gate   return waserr;
430*7c478bd9Sstevel@tonic-gate }
431*7c478bd9Sstevel@tonic-gate 
432*7c478bd9Sstevel@tonic-gate /*.......................................................................
433*7c478bd9Sstevel@tonic-gate  * Return the value of getenv("PWD") if this points to the current
434*7c478bd9Sstevel@tonic-gate  * directory, or the return value of getcwd() otherwise. The reason for
435*7c478bd9Sstevel@tonic-gate  * prefering PWD over getcwd() is that the former preserves the history
436*7c478bd9Sstevel@tonic-gate  * of symbolic links that have been traversed to reach the current
437*7c478bd9Sstevel@tonic-gate  * directory. This function is designed to provide the equivalent
438*7c478bd9Sstevel@tonic-gate  * expansion of the ksh ~+ directive, which normally returns its value
439*7c478bd9Sstevel@tonic-gate  * of PWD.
440*7c478bd9Sstevel@tonic-gate  *
441*7c478bd9Sstevel@tonic-gate  * Input:
442*7c478bd9Sstevel@tonic-gate  *  home      HomeDir *  The resource object for reading home directories.
443*7c478bd9Sstevel@tonic-gate  * Output:
444*7c478bd9Sstevel@tonic-gate  *  return const char *  A pointer to either home->buffer, where the
445*7c478bd9Sstevel@tonic-gate  *                       pathname is recorded, the string returned by
446*7c478bd9Sstevel@tonic-gate  *                       getenv("PWD"), or NULL on error.
447*7c478bd9Sstevel@tonic-gate  */
448*7c478bd9Sstevel@tonic-gate static const char *hd_getpwd(HomeDir *home)
449*7c478bd9Sstevel@tonic-gate {
450*7c478bd9Sstevel@tonic-gate /*
451*7c478bd9Sstevel@tonic-gate  * Get the absolute path of the current working directory.
452*7c478bd9Sstevel@tonic-gate  */
453*7c478bd9Sstevel@tonic-gate   char *cwd = getcwd(home->buffer, home->buflen);
454*7c478bd9Sstevel@tonic-gate /*
455*7c478bd9Sstevel@tonic-gate  * Some shells set PWD with the path of the current working directory.
456*7c478bd9Sstevel@tonic-gate  * This will differ from cwd in that it won't have had symbolic links
457*7c478bd9Sstevel@tonic-gate  * expanded.
458*7c478bd9Sstevel@tonic-gate  */
459*7c478bd9Sstevel@tonic-gate   const char *pwd = getenv("PWD");
460*7c478bd9Sstevel@tonic-gate /*
461*7c478bd9Sstevel@tonic-gate  * If PWD was set, and it points to the same directory as cwd, return
462*7c478bd9Sstevel@tonic-gate  * its value. Note that it won't be the same if the current shell or
463*7c478bd9Sstevel@tonic-gate  * the current program has changed directories, after inheriting PWD
464*7c478bd9Sstevel@tonic-gate  * from a parent shell.
465*7c478bd9Sstevel@tonic-gate  */
466*7c478bd9Sstevel@tonic-gate   struct stat cwdstat, pwdstat;
467*7c478bd9Sstevel@tonic-gate   if(pwd && cwd && stat(cwd, &cwdstat)==0 && stat(pwd, &pwdstat)==0 &&
468*7c478bd9Sstevel@tonic-gate      cwdstat.st_dev == pwdstat.st_dev && cwdstat.st_ino == pwdstat.st_ino)
469*7c478bd9Sstevel@tonic-gate     return pwd;
470*7c478bd9Sstevel@tonic-gate /*
471*7c478bd9Sstevel@tonic-gate  * Also return pwd if getcwd() failed, since it represents the best
472*7c478bd9Sstevel@tonic-gate  * information that we have access to.
473*7c478bd9Sstevel@tonic-gate  */
474*7c478bd9Sstevel@tonic-gate   if(!cwd)
475*7c478bd9Sstevel@tonic-gate     return pwd;
476*7c478bd9Sstevel@tonic-gate /*
477*7c478bd9Sstevel@tonic-gate  * In the absence of a valid PWD, return cwd.
478*7c478bd9Sstevel@tonic-gate  */
479*7c478bd9Sstevel@tonic-gate   return cwd;
480*7c478bd9Sstevel@tonic-gate }
481*7c478bd9Sstevel@tonic-gate 
482*7c478bd9Sstevel@tonic-gate #endif  /* ifndef WITHOUT_FILE_SYSTEM */
483