xref: /netbsd-src/lib/libc/gen/getusershell.c (revision 23c8222edbfb0f0932d88a8351d3a0cf817dfb9e)
1 /*	$NetBSD: getusershell.c,v 1.23 2003/08/07 16:42:51 agc Exp $	*/
2 
3 /*
4  * Copyright (c) 1985, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #if defined(LIBC_SCCS) && !defined(lint)
34 #if 0
35 static char sccsid[] = "@(#)getusershell.c	8.1 (Berkeley) 6/4/93";
36 #else
37 __RCSID("$NetBSD: getusershell.c,v 1.23 2003/08/07 16:42:51 agc Exp $");
38 #endif
39 #endif /* LIBC_SCCS and not lint */
40 
41 #include "namespace.h"
42 #include <sys/param.h>
43 #include <sys/file.h>
44 
45 #include <ctype.h>
46 #include <errno.h>
47 #include <nsswitch.h>
48 #include <paths.h>
49 #include <stdarg.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <stringlist.h>
54 #include <unistd.h>
55 
56 #ifdef HESIOD
57 #include <hesiod.h>
58 #endif
59 #ifdef YP
60 #include <rpc/rpc.h>
61 #include <rpcsvc/ypclnt.h>
62 #include <rpcsvc/yp_prot.h>
63 #endif
64 
65 #ifdef __weak_alias
66 __weak_alias(endusershell,_endusershell)
67 __weak_alias(getusershell,_getusershell)
68 __weak_alias(setusershell,_setusershell)
69 #endif
70 
71 /*
72  * Local shells should NOT be added here.  They should be added in
73  * /etc/shells.
74  */
75 
76 static const char *const okshells[] = { _PATH_BSHELL, _PATH_CSHELL, NULL };
77 static const char *const *curshell;
78 static StringList	 *sl;
79 
80 static const char *const *initshells __P((void));
81 
82 /*
83  * Get a list of shells from "shells" nsswitch database
84  */
85 __aconst char *
86 getusershell()
87 {
88 	__aconst char *ret;
89 
90 	if (curshell == NULL)
91 		curshell = initshells();
92 	/*LINTED*/
93 	ret = (__aconst char *)*curshell;
94 	if (ret != NULL)
95 		curshell++;
96 	return (ret);
97 }
98 
99 void
100 endusershell()
101 {
102 	if (sl)
103 		sl_free(sl, 1);
104 	sl = NULL;
105 	curshell = NULL;
106 }
107 
108 void
109 setusershell()
110 {
111 
112 	curshell = initshells();
113 }
114 
115 
116 static int	_local_initshells __P((void *, void *, va_list));
117 
118 /*ARGSUSED*/
119 static int
120 _local_initshells(rv, cb_data, ap)
121 	void	*rv;
122 	void	*cb_data;
123 	va_list	 ap;
124 {
125 	char	*sp, *cp;
126 	FILE	*fp;
127 	char	 line[MAXPATHLEN + 2];
128 
129 	if (sl)
130 		sl_free(sl, 1);
131 	sl = sl_init();
132 	if (!sl)
133 		return (NS_UNAVAIL);
134 
135 	if ((fp = fopen(_PATH_SHELLS, "r")) == NULL)
136 		return (NS_UNAVAIL);
137 
138 	sp = cp = line;
139 	while (fgets(cp, MAXPATHLEN + 1, fp) != NULL) {
140 		while (*cp != '#' && *cp != '/' && *cp != '\0')
141 			cp++;
142 		if (*cp == '#' || *cp == '\0')
143 			continue;
144 		sp = cp;
145 		while (!isspace((unsigned char) *cp) && *cp != '#'
146 		    && *cp != '\0')
147 			cp++;
148 		*cp++ = '\0';
149 		if (sl_add(sl, strdup(sp)) == -1)
150 			return (NS_UNAVAIL);
151 	}
152 	(void)fclose(fp);
153 	return (NS_SUCCESS);
154 }
155 
156 #ifdef HESIOD
157 static int	_dns_initshells __P((void *, void *, va_list));
158 
159 /*ARGSUSED*/
160 static int
161 _dns_initshells(rv, cb_data, ap)
162 	void	*rv;
163 	void	*cb_data;
164 	va_list	 ap;
165 {
166 	char	  shellname[] = "shells-XXXXX";
167 	int	  hsindex, hpi, r;
168 	char	**hp;
169 	void	 *context;
170 
171 	if (sl)
172 		sl_free(sl, 1);
173 	sl = sl_init();
174 	if (!sl)
175 		return (NS_UNAVAIL);
176 
177 	r = NS_UNAVAIL;
178 	if (hesiod_init(&context) == -1)
179 		return (r);
180 
181 	for (hsindex = 0; ; hsindex++) {
182 		snprintf(shellname, sizeof(shellname)-1, "shells-%d", hsindex);
183 		hp = hesiod_resolve(context, shellname, "shells");
184 		if (hp == NULL) {
185 			if (errno == ENOENT) {
186 				if (hsindex == 0)
187 					r = NS_NOTFOUND;
188 				else
189 					r = NS_SUCCESS;
190 			}
191 			break;
192 		} else {
193 			int bad = 0;
194 
195 			for (hpi = 0; hp[hpi]; hpi++)
196 				if (sl_add(sl, hp[hpi]) == -1) {
197 					bad = 1;
198 					break;
199 				}
200 			free(hp);
201 			if (bad)
202 				break;
203 		}
204 	}
205 	hesiod_end(context);
206 	return (r);
207 }
208 #endif /* HESIOD */
209 
210 #ifdef YP
211 static int	_nis_initshells __P((void *, void *, va_list));
212 
213 /*ARGSUSED*/
214 static int
215 _nis_initshells(rv, cb_data, ap)
216 	void	*rv;
217 	void	*cb_data;
218 	va_list	 ap;
219 {
220 	static char *ypdomain;
221 
222 	if (sl)
223 		sl_free(sl, 1);
224 	sl = sl_init();
225 	if (!sl)
226 		return (NS_UNAVAIL);
227 
228 	if (ypdomain == NULL) {
229 		switch (yp_get_default_domain(&ypdomain)) {
230 		case 0:
231 			break;
232 		case YPERR_RESRC:
233 			return (NS_TRYAGAIN);
234 		default:
235 			return (NS_UNAVAIL);
236 		}
237 	}
238 
239 	for (;;) {
240 		char	*ypcur = NULL;
241 		int	 ypcurlen = 0;	/* XXX: GCC */
242 		char	*key, *data;
243 		int	 keylen, datalen;
244 		int	 r;
245 
246 		key = data = NULL;
247 		if (ypcur) {
248 			r = yp_next(ypdomain, "shells", ypcur, ypcurlen,
249 					&key, &keylen, &data, &datalen);
250 			free(ypcur);
251 			switch (r) {
252 			case 0:
253 				break;
254 			case YPERR_NOMORE:
255 				free(key);
256 				free(data);
257 				return (NS_SUCCESS);
258 			default:
259 				free(key);
260 				free(data);
261 				return (NS_UNAVAIL);
262 			}
263 			ypcur = key;
264 			ypcurlen = keylen;
265 		} else {
266 			if (yp_first(ypdomain, "shells", &ypcur,
267 				    &ypcurlen, &data, &datalen)) {
268 				free(data);
269 				return (NS_UNAVAIL);
270 			}
271 		}
272 		data[datalen] = '\0';		/* clear trailing \n */
273 		if (sl_add(sl, data) == -1)
274 			return (NS_UNAVAIL);
275 	}
276 }
277 #endif /* YP */
278 
279 static const char *const *
280 initshells()
281 {
282 	static const ns_dtab dtab[] = {
283 		NS_FILES_CB(_local_initshells, NULL)
284 		NS_DNS_CB(_dns_initshells, NULL)
285 		NS_NIS_CB(_nis_initshells, NULL)
286 		{ 0 }
287 	};
288 	if (sl)
289 		sl_free(sl, 1);
290 	sl = sl_init();
291 	if (!sl)
292 		goto badinitshells;
293 
294 	if (nsdispatch(NULL, dtab, NSDB_SHELLS, "initshells", __nsdefaultsrc)
295 	    != NS_SUCCESS) {
296  badinitshells:
297 		if (sl)
298 			sl_free(sl, 1);
299 		sl = NULL;
300 		return (okshells);
301 	}
302 	if (sl_add(sl, NULL) == -1)
303 		goto badinitshells;
304 
305 	return (const char *const *)(sl->sl_str);
306 }
307