Annotation of src/lib/libc/gen/pwcache.c, Revision 1.27
1.27 ! sjg 1: /* $NetBSD: pwcache.c,v 1.26 2004/04/23 02:58:27 simonb Exp $ */
1.4 cgd 2:
1.10 mycroft 3: /*-
1.25 agc 4: * Copyright (c) 1992 Keith Muller.
1.10 mycroft 5: * Copyright (c) 1992, 1993
1.4 cgd 6: * The Regents of the University of California. All rights reserved.
1.1 cgd 7: *
1.10 mycroft 8: * This code is derived from software contributed to Berkeley by
9: * Keith Muller of the University of California, San Diego.
10: *
1.1 cgd 11: * Redistribution and use in source and binary forms, with or without
12: * modification, are permitted provided that the following conditions
13: * are met:
14: * 1. Redistributions of source code must retain the above copyright
15: * notice, this list of conditions and the following disclaimer.
16: * 2. Redistributions in binary form must reproduce the above copyright
17: * notice, this list of conditions and the following disclaimer in the
18: * documentation and/or other materials provided with the distribution.
1.24 agc 19: * 3. Neither the name of the University nor the names of its contributors
20: * may be used to endorse or promote products derived from this software
21: * without specific prior written permission.
22: *
23: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33: * SUCH DAMAGE.
34: */
35:
36: /*-
1.17 lukem 37: * Copyright (c) 2002 The NetBSD Foundation, Inc.
38: * All rights reserved.
39: *
40: * Redistribution and use in source and binary forms, with or without
41: * modification, are permitted provided that the following conditions
42: * are met:
43: * 1. Redistributions of source code must retain the above copyright
44: * notice, this list of conditions and the following disclaimer.
45: * 2. Redistributions in binary form must reproduce the above copyright
46: * notice, this list of conditions and the following disclaimer in the
47: * documentation and/or other materials provided with the distribution.
48: * 3. All advertising materials mentioning features or use of this software
49: * must display the following acknowledgement:
50: * This product includes software developed by the NetBSD
51: * Foundation, Inc. and its contributors.
52: * 4. Neither the name of The NetBSD Foundation nor the names of its
53: * contributors may be used to endorse or promote products derived
54: * from this software without specific prior written permission.
55: *
56: * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
57: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
58: * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
59: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
60: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
61: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
62: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
63: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
64: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
65: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
66: * POSSIBILITY OF SUCH DAMAGE.
67: */
68:
1.27 ! sjg 69: #if HAVE_NBTOOL_CONFIG_H
! 70: #include "nbtool_config.h"
! 71: #endif
! 72:
1.7 christos 73: #include <sys/cdefs.h>
1.15 msaitoh 74: #if defined(LIBC_SCCS) && !defined(lint)
1.4 cgd 75: #if 0
1.10 mycroft 76: static char sccsid[] = "@(#)cache.c 8.1 (Berkeley) 5/31/93";
1.4 cgd 77: #else
1.27 ! sjg 78: __RCSID("$NetBSD: pwcache.c,v 1.26 2004/04/23 02:58:27 simonb Exp $");
1.4 cgd 79: #endif
1.15 msaitoh 80: #endif /* LIBC_SCCS and not lint */
1.1 cgd 81:
1.8 jtc 82: #include "namespace.h"
1.10 mycroft 83:
1.1 cgd 84: #include <sys/types.h>
1.6 sommerfe 85: #include <sys/param.h>
1.4 cgd 86:
1.13 lukem 87: #include <assert.h>
1.4 cgd 88: #include <grp.h>
1.1 cgd 89: #include <pwd.h>
90: #include <stdio.h>
1.10 mycroft 91: #include <stdlib.h>
1.5 jtc 92: #include <string.h>
1.10 mycroft 93: #include <unistd.h>
94:
1.8 jtc 95: #ifdef __weak_alias
1.14 mycroft 96: __weak_alias(user_from_uid,_user_from_uid)
97: __weak_alias(group_from_gid,_group_from_gid)
1.17 lukem 98: __weak_alias(pwcache_userdb,_pwcache_userdb)
99: __weak_alias(pwcache_groupdb,_pwcache_groupdb)
1.8 jtc 100: #endif
1.19 lukem 101:
102: #if !HAVE_PWCACHE_USERDB
103: #include "pwcache.h"
1.1 cgd 104:
1.10 mycroft 105: /*
106: * routines that control user, group, uid and gid caches (for the archive
107: * member print routine).
108: * IMPORTANT:
109: * these routines cache BOTH hits and misses, a major performance improvement
110: */
111:
1.17 lukem 112: /*
113: * function pointers to various name lookup routines.
114: * these may be changed as necessary.
115: */
116: static int (*_pwcache_setgroupent)(int) = setgroupent;
117: static void (*_pwcache_endgrent)(void) = endgrent;
118: static struct group * (*_pwcache_getgrnam)(const char *) = getgrnam;
119: static struct group * (*_pwcache_getgrgid)(gid_t) = getgrgid;
120: static int (*_pwcache_setpassent)(int) = setpassent;
121: static void (*_pwcache_endpwent)(void) = endpwent;
122: static struct passwd * (*_pwcache_getpwnam)(const char *) = getpwnam;
123: static struct passwd * (*_pwcache_getpwuid)(uid_t) = getpwuid;
124:
125: /*
126: * internal state
127: */
128: static int pwopn; /* is password file open */
129: static int gropn; /* is group file open */
130: static UIDC **uidtb; /* uid to name cache */
131: static GIDC **gidtb; /* gid to name cache */
132: static UIDC **usrtb; /* user name to uid cache */
133: static GIDC **grptb; /* group name to gid cache */
134:
135: static int uidtb_fail; /* uidtb_start() failed ? */
136: static int gidtb_fail; /* gidtb_start() failed ? */
137: static int usrtb_fail; /* usrtb_start() failed ? */
138: static int grptb_fail; /* grptb_start() failed ? */
139:
140:
141: static u_int st_hash(const char *, size_t, int);
142: static int uidtb_start(void);
143: static int gidtb_start(void);
144: static int usrtb_start(void);
145: static int grptb_start(void);
146:
1.10 mycroft 147:
148: static u_int
1.16 lukem 149: st_hash(const char *name, size_t len, int tabsz)
1.10 mycroft 150: {
151: u_int key = 0;
152:
1.13 lukem 153: _DIAGASSERT(name != NULL);
154:
1.10 mycroft 155: while (len--) {
156: key += *name++;
157: key = (key << 8) | (key >> 24);
158: }
159:
160: return (key % tabsz);
161: }
162:
163: /*
164: * uidtb_start
165: * creates an an empty uidtb
166: * Return:
167: * 0 if ok, -1 otherwise
168: */
169: static int
170: uidtb_start(void)
171: {
172:
173: if (uidtb != NULL)
174: return (0);
1.17 lukem 175: if (uidtb_fail)
1.10 mycroft 176: return (-1);
177: if ((uidtb = (UIDC **)calloc(UID_SZ, sizeof(UIDC *))) == NULL) {
1.17 lukem 178: ++uidtb_fail;
1.10 mycroft 179: return (-1);
180: }
181: return (0);
182: }
183:
184: /*
185: * gidtb_start
186: * creates an an empty gidtb
187: * Return:
188: * 0 if ok, -1 otherwise
189: */
1.17 lukem 190: static int
1.10 mycroft 191: gidtb_start(void)
192: {
193:
194: if (gidtb != NULL)
195: return (0);
1.17 lukem 196: if (gidtb_fail)
1.10 mycroft 197: return (-1);
198: if ((gidtb = (GIDC **)calloc(GID_SZ, sizeof(GIDC *))) == NULL) {
1.17 lukem 199: ++gidtb_fail;
1.10 mycroft 200: return (-1);
201: }
202: return (0);
203: }
204:
205: /*
206: * usrtb_start
207: * creates an an empty usrtb
208: * Return:
209: * 0 if ok, -1 otherwise
210: */
1.17 lukem 211: static int
1.10 mycroft 212: usrtb_start(void)
213: {
214:
215: if (usrtb != NULL)
216: return (0);
1.17 lukem 217: if (usrtb_fail)
1.10 mycroft 218: return (-1);
219: if ((usrtb = (UIDC **)calloc(UNM_SZ, sizeof(UIDC *))) == NULL) {
1.17 lukem 220: ++usrtb_fail;
1.10 mycroft 221: return (-1);
222: }
223: return (0);
224: }
225:
226: /*
227: * grptb_start
228: * creates an an empty grptb
229: * Return:
230: * 0 if ok, -1 otherwise
231: */
1.17 lukem 232: static int
1.10 mycroft 233: grptb_start(void)
234: {
235:
236: if (grptb != NULL)
237: return (0);
1.17 lukem 238: if (grptb_fail)
1.10 mycroft 239: return (-1);
240: if ((grptb = (GIDC **)calloc(GNM_SZ, sizeof(GIDC *))) == NULL) {
1.17 lukem 241: ++grptb_fail;
1.10 mycroft 242: return (-1);
243: }
244: return (0);
245: }
246:
247: /*
1.11 mycroft 248: * user_from_uid()
1.16 lukem 249: * caches the name (if any) for the uid. If noname clear, we always
1.26 simonb 250: * return the stored name (if valid or invalid match).
1.16 lukem 251: * We use a simple hash table.
1.10 mycroft 252: * Return
253: * Pointer to stored name (or a empty string)
254: */
255: const char *
256: user_from_uid(uid_t uid, int noname)
1.1 cgd 257: {
1.9 perry 258: struct passwd *pw;
1.10 mycroft 259: UIDC *ptr, **pptr;
1.1 cgd 260:
1.10 mycroft 261: if ((uidtb == NULL) && (uidtb_start() < 0))
262: return (NULL);
263:
264: /*
265: * see if we have this uid cached
266: */
267: pptr = uidtb + (uid % UID_SZ);
268: ptr = *pptr;
269:
270: if ((ptr != NULL) && (ptr->valid > 0) && (ptr->uid == uid)) {
271: /*
272: * have an entry for this uid
273: */
274: if (!noname || (ptr->valid == VALID))
275: return (ptr->name);
276: return (NULL);
277: }
278:
279: /*
280: * No entry for this uid, we will add it
281: */
282: if (!pwopn) {
1.17 lukem 283: if (_pwcache_setpassent != NULL)
284: (*_pwcache_setpassent)(1);
1.10 mycroft 285: ++pwopn;
1.1 cgd 286: }
1.10 mycroft 287:
288: if (ptr == NULL)
1.12 mycroft 289: *pptr = ptr = (UIDC *)malloc(sizeof(UIDC));
1.10 mycroft 290:
1.17 lukem 291: if ((pw = (*_pwcache_getpwuid)(uid)) == NULL) {
1.10 mycroft 292: /*
293: * no match for this uid in the local password file
1.23 grant 294: * a string that is the uid in numeric format
1.10 mycroft 295: */
296: if (ptr == NULL)
297: return (NULL);
298: ptr->uid = uid;
299: (void)snprintf(ptr->name, UNMLEN, "%lu", (long) uid);
300: ptr->valid = INVALID;
301: if (noname)
302: return (NULL);
303: } else {
304: /*
305: * there is an entry for this uid in the password file
306: */
307: if (ptr == NULL)
308: return (pw->pw_name);
309: ptr->uid = uid;
1.17 lukem 310: (void)strlcpy(ptr->name, pw->pw_name, UNMLEN);
1.10 mycroft 311: ptr->valid = VALID;
312: }
313: return (ptr->name);
1.1 cgd 314: }
315:
1.10 mycroft 316: /*
317: * group_from_gid()
1.16 lukem 318: * caches the name (if any) for the gid. If noname clear, we always
1.26 simonb 319: * return the stored name (if valid or invalid match).
1.16 lukem 320: * We use a simple hash table.
1.10 mycroft 321: * Return
322: * Pointer to stored name (or a empty string)
323: */
324: const char *
325: group_from_gid(gid_t gid, int noname)
326: {
327: struct group *gr;
328: GIDC *ptr, **pptr;
329:
330: if ((gidtb == NULL) && (gidtb_start() < 0))
331: return (NULL);
332:
333: /*
334: * see if we have this gid cached
335: */
336: pptr = gidtb + (gid % GID_SZ);
337: ptr = *pptr;
338:
339: if ((ptr != NULL) && (ptr->valid > 0) && (ptr->gid == gid)) {
340: /*
341: * have an entry for this gid
342: */
343: if (!noname || (ptr->valid == VALID))
344: return (ptr->name);
345: return (NULL);
346: }
347:
348: /*
349: * No entry for this gid, we will add it
350: */
351: if (!gropn) {
1.17 lukem 352: if (_pwcache_setgroupent != NULL)
353: (*_pwcache_setgroupent)(1);
1.10 mycroft 354: ++gropn;
355: }
356:
357: if (ptr == NULL)
1.12 mycroft 358: *pptr = ptr = (GIDC *)malloc(sizeof(GIDC));
1.10 mycroft 359:
1.17 lukem 360: if ((gr = (*_pwcache_getgrgid)(gid)) == NULL) {
1.10 mycroft 361: /*
362: * no match for this gid in the local group file, put in
363: * a string that is the gid in numberic format
364: */
365: if (ptr == NULL)
366: return (NULL);
367: ptr->gid = gid;
368: (void)snprintf(ptr->name, GNMLEN, "%lu", (long) gid);
369: ptr->valid = INVALID;
370: if (noname)
371: return (NULL);
372: } else {
373: /*
374: * there is an entry for this group in the group file
375: */
376: if (ptr == NULL)
377: return (gr->gr_name);
378: ptr->gid = gid;
1.17 lukem 379: (void)strlcpy(ptr->name, gr->gr_name, GNMLEN);
1.10 mycroft 380: ptr->valid = VALID;
381: }
382: return (ptr->name);
383: }
384:
385: /*
386: * uid_from_user()
387: * caches the uid for a given user name. We use a simple hash table.
388: * Return
389: * the uid (if any) for a user name, or a -1 if no match can be found
390: */
391: int
392: uid_from_user(const char *name, uid_t *uid)
393: {
394: struct passwd *pw;
395: UIDC *ptr, **pptr;
396: size_t namelen;
397:
398: /*
399: * return -1 for mangled names
400: */
1.13 lukem 401: if (name == NULL || ((namelen = strlen(name)) == 0))
1.10 mycroft 402: return (-1);
403: if ((usrtb == NULL) && (usrtb_start() < 0))
404: return (-1);
405:
406: /*
407: * look up in hash table, if found and valid return the uid,
408: * if found and invalid, return a -1
409: */
410: pptr = usrtb + st_hash(name, namelen, UNM_SZ);
411: ptr = *pptr;
412:
413: if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) {
414: if (ptr->valid == INVALID)
415: return (-1);
416: *uid = ptr->uid;
417: return (0);
418: }
419:
420: if (!pwopn) {
1.17 lukem 421: if (_pwcache_setpassent != NULL)
422: (*_pwcache_setpassent)(1);
1.10 mycroft 423: ++pwopn;
424: }
425:
426: if (ptr == NULL)
1.12 mycroft 427: *pptr = ptr = (UIDC *)malloc(sizeof(UIDC));
1.10 mycroft 428:
429: /*
430: * no match, look it up, if no match store it as an invalid entry,
431: * or store the matching uid
432: */
433: if (ptr == NULL) {
1.17 lukem 434: if ((pw = (*_pwcache_getpwnam)(name)) == NULL)
1.10 mycroft 435: return (-1);
436: *uid = pw->pw_uid;
437: return (0);
438: }
1.17 lukem 439: (void)strlcpy(ptr->name, name, UNMLEN);
440: if ((pw = (*_pwcache_getpwnam)(name)) == NULL) {
1.10 mycroft 441: ptr->valid = INVALID;
442: return (-1);
443: }
444: ptr->valid = VALID;
445: *uid = ptr->uid = pw->pw_uid;
446: return (0);
447: }
448:
449: /*
450: * gid_from_group()
451: * caches the gid for a given group name. We use a simple hash table.
452: * Return
453: * the gid (if any) for a group name, or a -1 if no match can be found
454: */
455: int
456: gid_from_group(const char *name, gid_t *gid)
1.1 cgd 457: {
1.4 cgd 458: struct group *gr;
1.10 mycroft 459: GIDC *ptr, **pptr;
460: size_t namelen;
461:
462: /*
463: * return -1 for mangled names
464: */
1.13 lukem 465: if (name == NULL || ((namelen = strlen(name)) == 0))
1.10 mycroft 466: return (-1);
467: if ((grptb == NULL) && (grptb_start() < 0))
468: return (-1);
469:
470: /*
471: * look up in hash table, if found and valid return the uid,
472: * if found and invalid, return a -1
473: */
474: pptr = grptb + st_hash(name, namelen, GID_SZ);
475: ptr = *pptr;
476:
477: if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) {
478: if (ptr->valid == INVALID)
479: return (-1);
480: *gid = ptr->gid;
481: return (0);
482: }
483:
484: if (!gropn) {
1.17 lukem 485: if (_pwcache_setgroupent != NULL)
486: (*_pwcache_setgroupent)(1);
1.10 mycroft 487: ++gropn;
488: }
489:
490: if (ptr == NULL)
1.12 mycroft 491: *pptr = ptr = (GIDC *)malloc(sizeof(GIDC));
1.10 mycroft 492:
493: /*
494: * no match, look it up, if no match store it as an invalid entry,
495: * or store the matching gid
496: */
497: if (ptr == NULL) {
1.17 lukem 498: if ((gr = (*_pwcache_getgrnam)(name)) == NULL)
1.10 mycroft 499: return (-1);
500: *gid = gr->gr_gid;
501: return (0);
502: }
1.1 cgd 503:
1.17 lukem 504: (void)strlcpy(ptr->name, name, GNMLEN);
505: if ((gr = (*_pwcache_getgrnam)(name)) == NULL) {
1.10 mycroft 506: ptr->valid = INVALID;
507: return (-1);
1.1 cgd 508: }
1.10 mycroft 509: ptr->valid = VALID;
510: *gid = ptr->gid = gr->gr_gid;
511: return (0);
1.1 cgd 512: }
1.17 lukem 513:
514: #define FLUSHTB(arr, len, fail) \
515: do { \
516: if (arr != NULL) { \
517: for (i = 0; i < len; i++) \
518: if (arr[i] != NULL) \
519: free(arr[i]); \
520: arr = NULL; \
521: } \
522: fail = 0; \
523: } while (/* CONSTCOND */0);
524:
525: int
526: pwcache_userdb(
527: int (*a_setpassent)(int),
528: void (*a_endpwent)(void),
529: struct passwd * (*a_getpwnam)(const char *),
530: struct passwd * (*a_getpwuid)(uid_t))
531: {
532: int i;
533:
534: /* a_setpassent and a_endpwent may be NULL */
535: if (a_getpwnam == NULL || a_getpwuid == NULL)
536: return (-1);
537:
538: if (_pwcache_endpwent != NULL)
539: (*_pwcache_endpwent)();
540: FLUSHTB(uidtb, UID_SZ, uidtb_fail);
541: FLUSHTB(usrtb, UNM_SZ, usrtb_fail);
542: pwopn = 0;
543: _pwcache_setpassent = a_setpassent;
544: _pwcache_endpwent = a_endpwent;
545: _pwcache_getpwnam = a_getpwnam;
546: _pwcache_getpwuid = a_getpwuid;
547:
548: return (0);
549: }
550:
551: int
552: pwcache_groupdb(
553: int (*a_setgroupent)(int),
554: void (*a_endgrent)(void),
555: struct group * (*a_getgrnam)(const char *),
556: struct group * (*a_getgrgid)(gid_t))
557: {
558: int i;
559:
560: /* a_setgroupent and a_endgrent may be NULL */
561: if (a_getgrnam == NULL || a_getgrgid == NULL)
562: return (-1);
563:
564: if (_pwcache_endgrent != NULL)
565: (*_pwcache_endgrent)();
566: FLUSHTB(gidtb, GID_SZ, gidtb_fail);
567: FLUSHTB(grptb, GNM_SZ, grptb_fail);
568: gropn = 0;
569: _pwcache_setgroupent = a_setgroupent;
570: _pwcache_endgrent = a_endgrent;
571: _pwcache_getgrnam = a_getgrnam;
572: _pwcache_getgrgid = a_getgrgid;
573:
574: return (0);
575: }
576:
577:
578: #ifdef TEST_PWCACHE
579:
580: struct passwd *
581: test_getpwnam(const char *name)
582: {
583: static struct passwd foo;
584:
585: memset(&foo, 0, sizeof(foo));
586: if (strcmp(name, "toor") == 0) {
587: foo.pw_uid = 666;
588: return &foo;
589: }
590: return (getpwnam(name));
591: }
592:
593: int
594: main(int argc, char *argv[])
595: {
596: uid_t u;
597: int r, i;
598:
599: printf("pass 1 (default userdb)\n");
600: for (i = 1; i < argc; i++) {
601: printf("i: %d, pwopn %d usrtb_fail %d usrtb %p\n",
602: i, pwopn, usrtb_fail, usrtb);
603: r = uid_from_user(argv[i], &u);
604: if (r == -1)
605: printf(" uid_from_user %s: failed\n", argv[i]);
606: else
607: printf(" uid_from_user %s: %d\n", argv[i], u);
608: }
609: printf("pass 1 finish: pwopn %d usrtb_fail %d usrtb %p\n",
610: pwopn, usrtb_fail, usrtb);
611:
612: puts("");
613: printf("pass 2 (replacement userdb)\n");
614: printf("pwcache_userdb returned %d\n",
615: pwcache_userdb(setpassent, test_getpwnam, getpwuid));
616: printf("pwopn %d usrtb_fail %d usrtb %p\n", pwopn, usrtb_fail, usrtb);
617:
618: for (i = 1; i < argc; i++) {
619: printf("i: %d, pwopn %d usrtb_fail %d usrtb %p\n",
620: i, pwopn, usrtb_fail, usrtb);
621: u = -1;
622: r = uid_from_user(argv[i], &u);
623: if (r == -1)
624: printf(" uid_from_user %s: failed\n", argv[i]);
625: else
626: printf(" uid_from_user %s: %d\n", argv[i], u);
627: }
628: printf("pass 2 finish: pwopn %d usrtb_fail %d usrtb %p\n",
629: pwopn, usrtb_fail, usrtb);
630:
631: puts("");
632: printf("pass 3 (null pointers)\n");
633: printf("pwcache_userdb returned %d\n",
634: pwcache_userdb(NULL, NULL, NULL));
635:
636: return (0);
637: }
638: #endif /* TEST_PWCACHE */
1.18 lukem 639: #endif /* !HAVE_PWCACHE_USERDB */
CVSweb <webmaster@jp.NetBSD.org>