~ubuntu-branches/ubuntu/quantal/genometools/quantal-backports

« back to all changes in this revision

Viewing changes to src/external/libtecla-1.6.1/homedir.c

  • Committer: Package Import Robot
  • Author(s): Sascha Steinbiss
  • Date: 2012-07-09 14:10:23 UTC
  • Revision ID: package-import@ubuntu.com-20120709141023-juuu4spm6chqsf9o
Tags: upstream-1.4.1
ImportĀ upstreamĀ versionĀ 1.4.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd.
 
3
 * 
 
4
 * All rights reserved.
 
5
 * 
 
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.
 
15
 * 
 
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.
 
25
 * 
 
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.
 
30
 */
 
31
 
 
32
/*
 
33
 * If file-system access is to be excluded, this module has no function,
 
34
 * so all of its code should be excluded.
 
35
 */
 
36
#ifndef WITHOUT_FILE_SYSTEM
 
37
 
 
38
#include <stdio.h>
 
39
#include <stdlib.h>
 
40
#include <string.h>
 
41
#include <errno.h>
 
42
 
 
43
#include <unistd.h>
 
44
#include <sys/types.h>
 
45
#include <sys/stat.h>
 
46
#include <pwd.h>
 
47
 
 
48
#include "pathutil.h"
 
49
#include "homedir.h"
 
50
#include "errmsg.h"
 
51
 
 
52
/*
 
53
 * Use the reentrant POSIX threads versions of the password lookup functions?
 
54
 */
 
55
#if defined(PREFER_REENTRANT) && defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199506L
 
56
#define THREAD_COMPATIBLE 1
 
57
/*
 
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.
 
61
 */
 
62
#if defined __sun && defined __SVR4
 
63
#include <thread.h>                      /* Solaris thr_main() */
 
64
#endif
 
65
#endif
 
66
 
 
67
/*
 
68
 * Provide a password buffer size fallback in case the max size reported
 
69
 * by sysconf() is said to be indeterminate.
 
70
 */
 
71
#define DEF_GETPW_R_SIZE_MAX 1024
 
72
 
 
73
/*
 
74
 * The resources needed to lookup and record a home directory are
 
75
 * maintained in objects of the following type.
 
76
 */
 
77
struct HomeDir {
 
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 */
 
84
#endif
 
85
};
 
86
 
 
87
static const char *hd_getpwd(HomeDir *home);
 
88
 
 
89
/*.......................................................................
 
90
 * Create a new HomeDir object.
 
91
 *
 
92
 * Output:
 
93
 *  return  HomeDir *  The new object, or NULL on error.
 
94
 */
 
95
HomeDir *_new_HomeDir(void)
 
96
{
 
97
  HomeDir *home;  /* The object to be returned */
 
98
  size_t pathlen; /* The estimated maximum size of a pathname */
 
99
/*
 
100
 * Allocate the container.
 
101
 */
 
102
  home = (HomeDir *) malloc(sizeof(HomeDir));
 
103
  if(!home) {
 
104
    errno = ENOMEM;
 
105
    return NULL;
 
106
  };
 
107
/*
 
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
 
110
 * to _del_HomeDir().
 
111
 */
 
112
  home->err = NULL;
 
113
  home->buffer = NULL;
 
114
  home->buflen = 0;
 
115
/*
 
116
 * Allocate a place to record error messages.
 
117
 */
 
118
  home->err = _new_ErrMsg();
 
119
  if(!home->err)
 
120
    return _del_HomeDir(home);
 
121
/*
 
122
 * Allocate the buffer that is used by the reentrant POSIX password-entry
 
123
 * lookup functions.
 
124
 */
 
125
#ifdef THREAD_COMPATIBLE
 
126
/*
 
127
 * Get the length of the buffer needed by the reentrant version
 
128
 * of getpwnam().
 
129
 */
 
130
#ifndef _SC_GETPW_R_SIZE_MAX
 
131
  home->buflen = DEF_GETPW_R_SIZE_MAX;
 
132
#else
 
133
  errno = 0;
 
134
  home->buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
 
135
/*
 
136
 * If the limit isn't available, substitute a suitably large fallback value.
 
137
 */
 
138
  if(home->buflen < 0 || errno)
 
139
    home->buflen = DEF_GETPW_R_SIZE_MAX;
 
140
#endif
 
141
#endif
 
142
/*
 
143
 * If the existing buffer length requirement is too restrictive to record
 
144
 * a pathname, increase its length.
 
145
 */
 
146
  pathlen = _pu_pathname_dim();
 
147
  if(pathlen > home->buflen)
 
148
    home->buflen = pathlen;
 
149
/*
 
150
 * Allocate a work buffer.
 
151
 */
 
152
  home->buffer = (char *) malloc(home->buflen);
 
153
  if(!home->buffer) {
 
154
    errno = ENOMEM;
 
155
    return _del_HomeDir(home);
 
156
  };
 
157
  return home;
 
158
}
 
159
 
 
160
/*.......................................................................
 
161
 * Delete a HomeDir object.
 
162
 *
 
163
 * Input:
 
164
 *  home   HomeDir *  The object to be deleted.
 
165
 * Output:
 
166
 *  return HomeDir *  The deleted object (always NULL).
 
167
 */
 
168
HomeDir *_del_HomeDir(HomeDir *home)
 
169
{
 
170
  if(home) {
 
171
    home->err = _del_ErrMsg(home->err);
 
172
    if(home->buffer)
 
173
      free(home->buffer);
 
174
    free(home);
 
175
  };
 
176
  return NULL;
 
177
}
 
178
 
 
179
/*.......................................................................
 
180
 * Lookup the home directory of a given user in the password file.
 
181
 *
 
182
 * Input:
 
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
 
186
 *                        program.
 
187
 * Output:
 
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
 
199
 *                        lookup.
 
200
 *
 
201
 *                        On error, NULL is returned and a description
 
202
 *                        of the error can be acquired by calling
 
203
 *                        _hd_last_home_dir_error().
 
204
 */
 
205
const char *_hd_lookup_home_dir(HomeDir *home, const char *user)
 
206
{
 
207
  const char *home_dir;   /* A pointer to the home directory of the user */
 
208
/*
 
209
 * If no username has been specified, arrange to lookup the current
 
210
 * user.
 
211
 */
 
212
  int login_user = !user || *user=='\0';
 
213
/*
 
214
 * Check the arguments.
 
215
 */
 
216
  if(!home) {
 
217
    errno = EINVAL;
 
218
    return NULL;
 
219
  };
 
220
/*
 
221
 * Handle the ksh "~+". This expands to the absolute path of the
 
222
 * current working directory.
 
223
 */
 
224
  if(!login_user && strcmp(user, "+") == 0) {
 
225
    home_dir = hd_getpwd(home);
 
226
    if(!home_dir) {
 
227
      _err_record_msg(home->err, "Can't determine current directory",
 
228
                      END_ERR_MSG);
 
229
      return NULL;
 
230
    }
 
231
    return home_dir;
 
232
  };
 
233
/*
 
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.
 
236
 */
 
237
  if(login_user) {
 
238
    home_dir = getenv("HOME");
 
239
    if(home_dir)
 
240
      return home_dir;
 
241
  };
 
242
/*
 
243
 * Look up the password entry of the user.
 
244
 * First the POSIX threads version - this is painful!
 
245
 */
 
246
#ifdef THREAD_COMPATIBLE
 
247
  {
 
248
    struct passwd *ret; /* The returned pointer to pwd */
 
249
    int status;         /* The return value of getpwnam_r() */
 
250
/*
 
251
 * Look up the password entry of the specified user.
 
252
 */
 
253
    if(login_user)
 
254
      status = getpwuid_r(geteuid(), &home->pwd, home->buffer, home->buflen,
 
255
                          &ret);
 
256
    else
 
257
      status = getpwnam_r(user, &home->pwd, home->buffer, home->buflen, &ret);
 
258
    if(status || !ret) {
 
259
      _err_record_msg(home->err, "User '", user, "' doesn't exist.",
 
260
                      END_ERR_MSG);
 
261
      return NULL;
 
262
    };
 
263
/*
 
264
 * Get a pointer to the string that holds the home directory.
 
265
 */
 
266
    home_dir = home->pwd.pw_dir;
 
267
  };
 
268
/*
 
269
 * Now the classic unix version.
 
270
 */
 
271
#else
 
272
  {
 
273
    struct passwd *pwd = login_user ? getpwuid(geteuid()) : getpwnam(user);
 
274
    if(!pwd) {
 
275
      _err_record_msg(home->err, "User '", user, "' doesn't exist.",
 
276
                      END_ERR_MSG);
 
277
      return NULL;
 
278
    };
 
279
/*
 
280
 * Get a pointer to the home directory.
 
281
 */
 
282
    home_dir = pwd->pw_dir;
 
283
  };
 
284
#endif
 
285
  return home_dir;
 
286
}
 
287
 
 
288
/*.......................................................................
 
289
 * Return a description of the last error that caused _hd_lookup_home_dir()
 
290
 * to return NULL.
 
291
 *
 
292
 * Input:
 
293
 *  home   HomeDir *  The resources needed to record the home directory.
 
294
 * Output:
 
295
 *  return    char *  The description of the last error.
 
296
 */
 
297
const char *_hd_last_home_dir_error(HomeDir *home)
 
298
{
 
299
  return home ? _err_get_msg(home->err) : "NULL HomeDir argument";
 
300
}
 
301
 
 
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.
 
306
 *
 
307
 * Input:
 
308
 *  home             HomeDir *  The resource object for reading home
 
309
 *                              directories.
 
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
 
315
 *                              callback function.
 
316
 *  callback_fn  HOME_DIR_FN(*) The function to call for each user.
 
317
 * Output:
 
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().
 
322
 */
 
323
int _hd_scan_user_home_dirs(HomeDir *home, const char *prefix,
 
324
                            void *data, HOME_DIR_FN(*callback_fn))
 
325
{
 
326
  int waserr = 0;       /* True after errors */
 
327
  int prefix_len;       /* The length of prefix[] */
 
328
/*
 
329
 * Check the arguments.
 
330
 */
 
331
  if(!home || !prefix || !callback_fn) {
 
332
    if(home) {
 
333
      _err_record_msg(home->err,
 
334
                      "_hd_scan_user_home_dirs: Missing callback function",
 
335
                      END_ERR_MSG);
 
336
    };
 
337
    return 1;
 
338
  };
 
339
/*
 
340
 * Get the length of the username prefix.
 
341
 */
 
342
  prefix_len = strlen(prefix);
 
343
/*
 
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.
 
347
 */
 
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 */
 
351
#else
 
352
  if(1)
 
353
#endif
 
354
  {
 
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. */
 
359
/*
 
360
 * See if the prefix that is being completed is a complete username.
 
361
 */
 
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);
 
366
    };
 
367
/*
 
368
 * See if the username of the current user minimally matches the prefix.
 
369
 */
 
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);
 
375
    };
 
376
/*
 
377
 * Reentrancy not required?
 
378
 */
 
379
  } else
 
380
#endif
 
381
  {
 
382
    struct passwd *pwd;   /* The pointer to the latest password entry */
 
383
/*
 
384
 * Open the password file.
 
385
 */
 
386
    setpwent();
 
387
/*
 
388
 * Read the contents of the password file, looking for usernames
 
389
 * that start with the specified prefix, and adding them to the
 
390
 * list of matches.
 
391
 */
 
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);
 
396
      };
 
397
    };
 
398
/*
 
399
 * Close the password file.
 
400
 */
 
401
    endpwent();
 
402
  };
 
403
/*
 
404
 * Under ksh ~+ stands for the absolute pathname of the current working
 
405
 * directory.
 
406
 */
 
407
  if(!waserr && strncmp(prefix, "+", prefix_len) == 0) {
 
408
    const char *pwd = hd_getpwd(home);
 
409
    if(pwd) {
 
410
      waserr = callback_fn(data, "+", pwd, _err_get_msg(home->err),ERR_MSG_LEN);
 
411
    } else {
 
412
      waserr = 1;
 
413
      _err_record_msg(home->err, "Can't determine current directory.",
 
414
                      END_ERR_MSG);
 
415
    };
 
416
  };
 
417
  return waserr;
 
418
}
 
419
 
 
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
 
427
 * of PWD.
 
428
 *
 
429
 * Input:
 
430
 *  home      HomeDir *  The resource object for reading home directories.
 
431
 * Output:
 
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.
 
435
 */
 
436
static const char *hd_getpwd(HomeDir *home)
 
437
{
 
438
/*
 
439
 * Get the absolute path of the current working directory.
 
440
 */
 
441
  char *cwd = getcwd(home->buffer, home->buflen);
 
442
/*
 
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
 
445
 * expanded.
 
446
 */
 
447
  const char *pwd = getenv("PWD");
 
448
/*
 
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.
 
453
 */
 
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)
 
457
    return pwd;
 
458
/*
 
459
 * Also return pwd if getcwd() failed, since it represents the best
 
460
 * information that we have access to.
 
461
 */
 
462
  if(!cwd)
 
463
    return pwd;
 
464
/*
 
465
 * In the absence of a valid PWD, return cwd.
 
466
 */
 
467
  return cwd;
 
468
}
 
469
 
 
470
#endif  /* ifndef WITHOUT_FILE_SYSTEM */