1 /* $NetBSD: getusershell.c,v 1.27 2008/04/28 20:22:59 martin Exp $ */
4 * Copyright (c) 1999, 2005 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
33 * Copyright (c) 1985, 1993
34 * The Regents of the University of California. All rights reserved.
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
39 * 1. Redistributions of source code must retain the above copyright
40 * notice, this list of conditions and the following disclaimer.
41 * 2. Redistributions in binary form must reproduce the above copyright
42 * notice, this list of conditions and the following disclaimer in the
43 * documentation and/or other materials provided with the distribution.
44 * 3. Neither the name of the University nor the names of its contributors
45 * may be used to endorse or promote products derived from this software
46 * without specific prior written permission.
48 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
49 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
50 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
51 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
52 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
53 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
54 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
55 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
56 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
57 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
61 #include <sys/cdefs.h>
62 #if defined(LIBC_SCCS) && !defined(lint)
64 static char sccsid
[] = "@(#)getusershell.c 8.1 (Berkeley) 6/4/93";
66 __RCSID("$NetBSD: getusershell.c,v 1.27 2008/04/28 20:22:59 martin Exp $");
68 #endif /* LIBC_SCCS and not lint */
70 #include "namespace.h"
71 #include "reentrant.h"
73 #include <sys/param.h>
92 #include <rpcsvc/ypclnt.h>
93 #include <rpcsvc/yp_prot.h>
97 __weak_alias(endusershell
,_endusershell
)
98 __weak_alias(getusershell
,_getusershell
)
99 __weak_alias(setusershell
,_setusershell
)
103 * Local shells should NOT be added here.
104 * They should be added in /etc/shells.
106 static const char *const okshells
[] = { _PATH_BSHELL
, _PATH_CSHELL
, NULL
};
109 static mutex_t __shellmutex
= MUTEX_INITIALIZER
;
112 static char curshell
[MAXPATHLEN
+ 2];
114 static const char *const *curokshell
= okshells
;
115 static int shellsfound
= 0;
121 /* state shared between files methods */
126 static struct files_state _files_state
;
130 _files_start(struct files_state
*state
)
133 _DIAGASSERT(state
!= NULL
);
135 if (state
->fp
== NULL
) {
136 state
->fp
= fopen(_PATH_SHELLS
, "r");
137 if (state
->fp
== NULL
)
146 _files_end(struct files_state
*state
)
149 _DIAGASSERT(state
!= NULL
);
152 (void) fclose(state
->fp
);
160 _files_setusershell(void *nsrv
, void *nscb
, va_list ap
)
163 return _files_start(&_files_state
);
168 _files_endusershell(void *nsrv
, void *nscb
, va_list ap
)
171 return _files_end(&_files_state
);
176 _files_getusershell(void *nsrv
, void *nscb
, va_list ap
)
178 char **retval
= va_arg(ap
, char **);
183 _DIAGASSERT(retval
!= NULL
);
186 if (_files_state
.fp
== NULL
) { /* only start if file not open yet */
187 rv
= _files_start(&_files_state
);
188 if (rv
!= NS_SUCCESS
)
192 while (fgets(curshell
, sizeof(curshell
) - 1, _files_state
.fp
) != NULL
) {
194 while (*cp
!= '#' && *cp
!= '/' && *cp
!= '\0')
196 if (*cp
== '#' || *cp
== '\0')
199 while (!isspace((unsigned char) *cp
) && *cp
!= '#'
216 /* state shared between dns methods */
218 void *context
; /* Hesiod context */
219 int num
; /* shell index, -1 if no more */
222 static struct dns_state _dns_state
;
225 _dns_start(struct dns_state
*state
)
228 _DIAGASSERT(state
!= NULL
);
231 if (state
->context
== NULL
) { /* setup Hesiod */
232 if (hesiod_init(&state
->context
) == -1)
240 _dns_end(struct dns_state
*state
)
243 _DIAGASSERT(state
!= NULL
);
246 if (state
->context
) {
247 hesiod_end(state
->context
);
248 state
->context
= NULL
;
255 _dns_setusershell(void *nsrv
, void *nscb
, va_list ap
)
258 return _dns_start(&_dns_state
);
263 _dns_endusershell(void *nsrv
, void *nscb
, va_list ap
)
266 return _dns_end(&_dns_state
);
271 _dns_getusershell(void *nsrv
, void *nscb
, va_list ap
)
273 char **retval
= va_arg(ap
, char **);
275 char shellname
[] = "shells-NNNNNNNNNN";
279 _DIAGASSERT(retval
!= NULL
);
283 if (_dns_state
.num
== -1) /* exhausted search */
286 if (_dns_state
.context
== NULL
) {
287 /* only start if Hesiod not setup */
288 rv
= _dns_start(&_dns_state
);
289 if (rv
!= NS_SUCCESS
)
296 /* find shells-NNN */
297 snprintf(shellname
, sizeof(shellname
), "shells-%d", _dns_state
.num
);
300 hp
= hesiod_resolve(_dns_state
.context
, shellname
, "shells");
307 if ((ep
= strchr(hp
[0], '\n')) != NULL
)
308 *ep
= '\0'; /* clear trailing \n */
309 /* only use first result */
310 strlcpy(curshell
, hp
[0], sizeof(curshell
));
316 hesiod_free_list(_dns_state
.context
, hp
);
317 if (rv
!= NS_SUCCESS
)
318 _dns_state
.num
= -1; /* any failure halts search */
329 /* state shared between nis methods */
331 char *domain
; /* NIS domain */
332 int done
; /* non-zero if search exhausted */
333 char *current
; /* current first/next match */
334 int currentlen
; /* length of _nis_current */
337 static struct nis_state _nis_state
;
340 _nis_start(struct nis_state
*state
)
343 _DIAGASSERT(state
!= NULL
);
346 if (state
->current
) {
347 free(state
->current
);
348 state
->current
= NULL
;
350 if (state
->domain
== NULL
) { /* setup NIS */
351 switch (yp_get_default_domain(&state
->domain
)) {
364 _nis_end(struct nis_state
*state
)
367 _DIAGASSERT(state
!= NULL
);
370 state
->domain
= NULL
;
373 free(state
->current
);
374 state
->current
= NULL
;
380 _nis_setusershell(void *nsrv
, void *nscb
, va_list ap
)
383 return _nis_start(&_nis_state
);
388 _nis_endusershell(void *nsrv
, void *nscb
, va_list ap
)
391 return _nis_end(&_nis_state
);
396 _nis_getusershell(void *nsrv
, void *nscb
, va_list ap
)
398 char **retval
= va_arg(ap
, char **);
401 int keylen
, datalen
, rv
, nisr
;
403 _DIAGASSERT(retval
!= NULL
);
407 if (_nis_state
.done
) /* exhausted search */
409 if (_nis_state
.domain
== NULL
) {
410 /* only start if NIS not setup */
411 rv
= _nis_start(&_nis_state
);
412 if (rv
!= NS_SUCCESS
)
420 if (_nis_state
.current
) { /* already searching */
421 nisr
= yp_next(_nis_state
.domain
, "shells",
422 _nis_state
.current
, _nis_state
.currentlen
,
423 &key
, &keylen
, &data
, &datalen
);
424 free(_nis_state
.current
);
425 _nis_state
.current
= NULL
;
428 _nis_state
.current
= key
;
429 _nis_state
.currentlen
= keylen
;
439 } else { /* new search */
440 if (yp_first(_nis_state
.domain
, "shells",
441 &_nis_state
.current
, &_nis_state
.currentlen
,
448 data
[datalen
] = '\0'; /* clear trailing \n */
449 strlcpy(curshell
, data
, sizeof(curshell
));
458 if (rv
!= NS_SUCCESS
) /* any failure halts search */
473 static const ns_dtab dtab
[] = {
474 NS_FILES_CB(_files_endusershell
, NULL
)
475 NS_DNS_CB(_dns_endusershell
, NULL
)
476 NS_NIS_CB(_nis_endusershell
, NULL
)
480 mutex_lock(&__shellmutex
);
482 curokshell
= okshells
; /* reset okshells fallback state */
485 /* force all endusershell() methods */
486 (void) nsdispatch(NULL
, dtab
, NSDB_SHELLS
, "endusershell",
487 __nsdefaultfiles_forceall
);
488 mutex_unlock(&__shellmutex
);
495 __aconst
char *retval
;
497 static const ns_dtab dtab
[] = {
498 NS_FILES_CB(_files_getusershell
, NULL
)
499 NS_DNS_CB(_dns_getusershell
, NULL
)
500 NS_NIS_CB(_nis_getusershell
, NULL
)
504 mutex_lock(&__shellmutex
);
508 rv
= nsdispatch(NULL
, dtab
, NSDB_SHELLS
, "getusershell",
509 __nsdefaultsrc
, &retval
);
510 /* loop until failure or non-blank result */
511 } while (rv
== NS_SUCCESS
&& retval
[0] == '\0');
513 if (rv
== NS_SUCCESS
) {
515 } else if (shellsfound
== 0) { /* no shells; fall back to okshells */
516 if (curokshell
!= NULL
) {
517 retval
= __UNCONST(*curokshell
);
523 mutex_unlock(&__shellmutex
);
524 return (rv
== NS_SUCCESS
) ? retval
: NULL
;
530 static const ns_dtab dtab
[] = {
531 NS_FILES_CB(_files_setusershell
, NULL
)
532 NS_DNS_CB(_dns_setusershell
, NULL
)
533 NS_NIS_CB(_nis_setusershell
, NULL
)
537 mutex_lock(&__shellmutex
);
539 curokshell
= okshells
; /* reset okshells fallback state */
542 /* force all setusershell() methods */
543 (void) nsdispatch(NULL
, dtab
, NSDB_SHELLS
, "setusershell",
544 __nsdefaultfiles_forceall
);
545 mutex_unlock(&__shellmutex
);