1 /* $NetBSD: getusershell.c,v 1.29 2012/03/13 21:13:36 christos 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.29 2012/03/13 21:13:36 christos 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
, "re");
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
, (int)sizeof(curshell
) - 1, _files_state
.fp
)
195 while (*cp
!= '#' && *cp
!= '/' && *cp
!= '\0')
197 if (*cp
== '#' || *cp
== '\0')
200 while (!isspace((unsigned char) *cp
) && *cp
!= '#'
217 /* state shared between dns methods */
219 void *context
; /* Hesiod context */
220 int num
; /* shell index, -1 if no more */
223 static struct dns_state _dns_state
;
226 _dns_start(struct dns_state
*state
)
229 _DIAGASSERT(state
!= NULL
);
232 if (state
->context
== NULL
) { /* setup Hesiod */
233 if (hesiod_init(&state
->context
) == -1)
241 _dns_end(struct dns_state
*state
)
244 _DIAGASSERT(state
!= NULL
);
247 if (state
->context
) {
248 hesiod_end(state
->context
);
249 state
->context
= NULL
;
256 _dns_setusershell(void *nsrv
, void *nscb
, va_list ap
)
259 return _dns_start(&_dns_state
);
264 _dns_endusershell(void *nsrv
, void *nscb
, va_list ap
)
267 return _dns_end(&_dns_state
);
272 _dns_getusershell(void *nsrv
, void *nscb
, va_list ap
)
274 char **retval
= va_arg(ap
, char **);
276 char shellname
[] = "shells-NNNNNNNNNN";
280 _DIAGASSERT(retval
!= NULL
);
284 if (_dns_state
.num
== -1) /* exhausted search */
287 if (_dns_state
.context
== NULL
) {
288 /* only start if Hesiod not setup */
289 rv
= _dns_start(&_dns_state
);
290 if (rv
!= NS_SUCCESS
)
297 /* find shells-NNN */
298 snprintf(shellname
, sizeof(shellname
), "shells-%d", _dns_state
.num
);
301 hp
= hesiod_resolve(_dns_state
.context
, shellname
, "shells");
308 if ((ep
= strchr(hp
[0], '\n')) != NULL
)
309 *ep
= '\0'; /* clear trailing \n */
310 /* only use first result */
311 strlcpy(curshell
, hp
[0], sizeof(curshell
));
317 hesiod_free_list(_dns_state
.context
, hp
);
318 if (rv
!= NS_SUCCESS
)
319 _dns_state
.num
= -1; /* any failure halts search */
330 /* state shared between nis methods */
332 char *domain
; /* NIS domain */
333 int done
; /* non-zero if search exhausted */
334 char *current
; /* current first/next match */
335 int currentlen
; /* length of _nis_current */
338 static struct nis_state _nis_state
;
341 _nis_start(struct nis_state
*state
)
344 _DIAGASSERT(state
!= NULL
);
347 if (state
->current
) {
348 free(state
->current
);
349 state
->current
= NULL
;
351 if (state
->domain
== NULL
) { /* setup NIS */
352 switch (yp_get_default_domain(&state
->domain
)) {
365 _nis_end(struct nis_state
*state
)
368 _DIAGASSERT(state
!= NULL
);
371 state
->domain
= NULL
;
374 free(state
->current
);
375 state
->current
= NULL
;
381 _nis_setusershell(void *nsrv
, void *nscb
, va_list ap
)
384 return _nis_start(&_nis_state
);
389 _nis_endusershell(void *nsrv
, void *nscb
, va_list ap
)
392 return _nis_end(&_nis_state
);
397 _nis_getusershell(void *nsrv
, void *nscb
, va_list ap
)
399 char **retval
= va_arg(ap
, char **);
402 int keylen
, datalen
, rv
, nisr
;
404 _DIAGASSERT(retval
!= NULL
);
408 if (_nis_state
.done
) /* exhausted search */
410 if (_nis_state
.domain
== NULL
) {
411 /* only start if NIS not setup */
412 rv
= _nis_start(&_nis_state
);
413 if (rv
!= NS_SUCCESS
)
421 if (_nis_state
.current
) { /* already searching */
422 nisr
= yp_next(_nis_state
.domain
, "shells",
423 _nis_state
.current
, _nis_state
.currentlen
,
424 &key
, &keylen
, &data
, &datalen
);
425 free(_nis_state
.current
);
426 _nis_state
.current
= NULL
;
429 _nis_state
.current
= key
;
430 _nis_state
.currentlen
= keylen
;
440 } else { /* new search */
441 if (yp_first(_nis_state
.domain
, "shells",
442 &_nis_state
.current
, &_nis_state
.currentlen
,
449 data
[datalen
] = '\0'; /* clear trailing \n */
450 strlcpy(curshell
, data
, sizeof(curshell
));
459 if (rv
!= NS_SUCCESS
) /* any failure halts search */
474 static const ns_dtab dtab
[] = {
475 NS_FILES_CB(_files_endusershell
, NULL
)
476 NS_DNS_CB(_dns_endusershell
, NULL
)
477 NS_NIS_CB(_nis_endusershell
, NULL
)
481 mutex_lock(&__shellmutex
);
483 curokshell
= okshells
; /* reset okshells fallback state */
486 /* force all endusershell() methods */
487 (void) nsdispatch(NULL
, dtab
, NSDB_SHELLS
, "endusershell",
488 __nsdefaultfiles_forceall
);
489 mutex_unlock(&__shellmutex
);
496 __aconst
char *retval
;
498 static const ns_dtab dtab
[] = {
499 NS_FILES_CB(_files_getusershell
, NULL
)
500 NS_DNS_CB(_dns_getusershell
, NULL
)
501 NS_NIS_CB(_nis_getusershell
, NULL
)
505 mutex_lock(&__shellmutex
);
509 rv
= nsdispatch(NULL
, dtab
, NSDB_SHELLS
, "getusershell",
510 __nsdefaultsrc
, &retval
);
511 /* loop until failure or non-blank result */
512 } while (rv
== NS_SUCCESS
&& retval
[0] == '\0');
514 if (rv
== NS_SUCCESS
) {
516 } else if (shellsfound
== 0) { /* no shells; fall back to okshells */
517 if (curokshell
!= NULL
) {
518 retval
= __UNCONST(*curokshell
);
524 mutex_unlock(&__shellmutex
);
525 return (rv
== NS_SUCCESS
) ? retval
: NULL
;
531 static const ns_dtab dtab
[] = {
532 NS_FILES_CB(_files_setusershell
, NULL
)
533 NS_DNS_CB(_dns_setusershell
, NULL
)
534 NS_NIS_CB(_nis_setusershell
, NULL
)
538 mutex_lock(&__shellmutex
);
540 curokshell
= okshells
; /* reset okshells fallback state */
543 /* force all setusershell() methods */
544 (void) nsdispatch(NULL
, dtab
, NSDB_SHELLS
, "setusershell",
545 __nsdefaultfiles_forceall
);
546 mutex_unlock(&__shellmutex
);