1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  *
26  * Common code and structures used by name-service-switch "user" backends.
27  * Much of this was taken directly from the files_common.c source.
28  */
29 
30 #pragma ident	"%Z%%M%	%I%	%E% SMI"
31 
32 /*
33  * An implementation that used mmap() sensibly would be a wonderful thing,
34  *   but this here is just yer standard fgets() thang.
35  */
36 
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <ctype.h>
40 #include <fcntl.h>
41 #include <poll.h>
42 #include <unistd.h>
43 #include "user_common.h"
44 #include <sys/stat.h>
45 #include "../../../libnsl/include/nsl_stdio_prv.h"
46 #include <string.h>
47 
48 nss_status_t
49 _nss_user_setent(be, dummy)
50 	user_backend_ptr_t	be;
51 	void			*dummy;
52 {
53 	if (be->f == 0) {
54 		if (be->filename == 0) {
55 			/* Backend isn't initialized properly? */
56 			return (NSS_UNAVAIL);
57 		}
58 		if ((be->f = __nsl_fopen(be->filename, "r")) == 0) {
59 			return (NSS_UNAVAIL);
60 		}
61 	} else {
62 		__nsl_rewind(be->f);
63 	}
64 	return (NSS_SUCCESS);
65 }
66 
67 nss_status_t
68 _nss_user_endent(be, dummy)
69 	user_backend_ptr_t	be;
70 	void			*dummy;
71 {
72 	if (be->f != 0) {
73 		__nsl_fclose(be->f);
74 		be->f = 0;
75 	}
76 	if (be->buf != 0) {
77 		free(be->buf);
78 		be->buf = 0;
79 	}
80 	return (NSS_SUCCESS);
81 }
82 
83 /*
84  * This routine reads a line, including the processing of continuation
85  * characters.  It always leaves (or inserts) \n\0 at the end of the line.
86  * It returns the length of the line read, excluding the \n\0.  Who's idea
87  * was this?
88  * Returns -1 on EOF.
89  *
90  * Note that since each concurrent call to _nss_user_read_line has
91  * it's own FILE pointer, we can use getc_unlocked w/o difficulties,
92  * a substantial performance win.
93  */
94 int
95 _nss_user_read_line(f, buffer, buflen)
96 	__NSL_FILE		*f;
97 	char			*buffer;
98 	int			buflen;
99 {
100 	int			linelen;	/* 1st unused slot in buffer */
101 	int			c;
102 
103 	while (1) {
104 		linelen = 0;
105 		while (linelen < buflen - 1) {	/* "- 1" saves room for \n\0 */
106 			switch (c = __nsl_getc_unlocked(f)) {
107 			case EOF:
108 				if (linelen == 0 ||
109 				    buffer[linelen - 1] == '\\') {
110 					return (-1);
111 				} else {
112 					buffer[linelen    ] = '\n';
113 					buffer[linelen + 1] = '\0';
114 					return (linelen);
115 				}
116 			case '\n':
117 				if (linelen > 0 &&
118 				    buffer[linelen - 1] == '\\') {
119 					--linelen;  /* remove the '\\' */
120 				} else {
121 					buffer[linelen    ] = '\n';
122 					buffer[linelen + 1] = '\0';
123 					return (linelen);
124 				}
125 				break;
126 			default:
127 				buffer[linelen++] = c;
128 			}
129 		}
130 		/* Buffer overflow -- eat rest of line and loop again */
131 		/* ===> Should syslog() */
132 		do {
133 			c = __nsl_getc_unlocked(f);
134 			if (c == EOF) {
135 				return (-1);
136 			}
137 		} while (c != '\n');
138 	}
139 	/*NOTREACHED*/
140 }
141 
142 
143 /*
144  * Could implement this as an iterator function on top of _nss_user_do_all(),
145  *   but the shared code is small enough that it'd be pretty silly.
146  */
147 nss_status_t
148 _nss_user_XY_all(be, args, netdb, filter, check)
149 	user_backend_ptr_t	be;
150 	nss_XbyY_args_t		*args;
151 	int			netdb;		/* whether it uses netdb */
152 						/* format or not */
153 	const char		*filter;	/* advisory, to speed up */
154 						/* string search */
155 	user_XY_check_func	check;	/* NULL means one-shot, for getXXent */
156 {
157 	nss_status_t		res;
158 	int	parsestat;
159 
160 	if (be->buf == 0 &&
161 		(be->buf = malloc(be->minbuf)) == 0) {
162 		return (NSS_UNAVAIL); /* really panic, malloc failed */
163 	}
164 
165 	if (check != 0 || be->f == 0) {
166 		if ((res = _nss_user_setent(be, 0)) != NSS_SUCCESS) {
167 			return (res);
168 		}
169 	}
170 
171 	res = NSS_NOTFOUND;
172 
173 	while (1) {
174 		char		*instr	= be->buf;
175 		int		linelen;
176 
177 		if ((linelen = _nss_user_read_line(be->f, instr,
178 		    be->minbuf)) < 0) {
179 			/* End of file */
180 			args->returnval = 0;
181 			args->erange    = 0;
182 			break;
183 		}
184 		if (filter != 0 && strstr(instr, filter) == 0) {
185 			/*
186 			 * Optimization:  if the entry doesn't contain the
187 			 *   filter string then it can't be the entry we want,
188 			 *   so don't bother looking more closely at it.
189 			 */
190 			continue;
191 		}
192 		if (netdb) {
193 			char		*first;
194 			char		*last;
195 
196 			if ((last = strchr(instr, '#')) == 0) {
197 				last = instr + linelen;
198 			}
199 			*last-- = '\0';		/* Nuke '\n' or #comment */
200 
201 			/*
202 			 * Skip leading whitespace.  Normally there isn't
203 			 *   any, so it's not worth calling strspn().
204 			 */
205 			for (first = instr;  isspace(*first);  first++) {
206 				;
207 			}
208 			if (*first == '\0') {
209 				continue;
210 			}
211 			/*
212 			 * Found something non-blank on the line.  Skip back
213 			 * over any trailing whitespace;  since we know
214 			 * there's non-whitespace earlier in the line,
215 			 * checking for termination is easy.
216 			 */
217 			while (isspace(*last)) {
218 				--last;
219 			}
220 
221 			linelen = last - first + 1;
222 			if (first != instr) {
223 					instr = first;
224 			}
225 		}
226 
227 		args->returnval = 0;
228 		parsestat = (*args->str2ent)(instr, linelen, args->buf.result,
229 				args->buf.buffer, args->buf.buflen);
230 
231 		if (parsestat == NSS_STR_PARSE_SUCCESS) {
232 			args->returnval = args->buf.result;
233 			if (check == 0 || (*check)(args)) {
234 				res = NSS_SUCCESS;
235 				break;
236 			}
237 		} else if (parsestat == NSS_STR_PARSE_ERANGE) {
238 			args->erange = 1;	/* should we just skip this */
239 						/* one long line ?? */
240 		} /* else if (parsestat == NSS_STR_PARSE_PARSE) don't care ! */
241 	}
242 
243 	/*
244 	 * stayopen is set to 0 by default in order to close the opened
245 	 * file.  Some applications may break if it is set to 1.
246 	 */
247 	if (check != 0 && !args->stayopen) {
248 		(void) _nss_user_endent(be, 0);
249 	}
250 
251 	return (res);
252 }
253 
254 
255 nss_status_t
256 _nss_user_destr(be, dummy)
257 	user_backend_ptr_t	be;
258 	void			*dummy;
259 {
260 	if (be != 0) {
261 		if (be->f != 0) {
262 			_nss_user_endent(be, 0);
263 		}
264 		free((char *)be->filename);
265 		free(be);
266 	}
267 	return (NSS_SUCCESS);	/* In case anyone is dumb enough to check */
268 }
269 
270 nss_backend_t *
271 _nss_user_constr(ops, n_ops, filename, min_bufsize)
272 	user_backend_op_t	ops[];
273 	int			n_ops;
274 	const char		*filename;
275 	int			min_bufsize;
276 {
277 	user_backend_ptr_t	be;
278 
279 	if ((be = (user_backend_ptr_t) malloc(sizeof (*be))) == 0) {
280 		return (0);
281 	}
282 	be->ops		= ops;
283 	be->n_ops	= n_ops;
284 	if ((be->filename = strdup(filename)) == NULL) {
285 		free(be);
286 		return (NULL);
287 	}
288 	be->minbuf	= min_bufsize;
289 	be->f		= 0;
290 	be->buf		= 0;
291 
292 	return ((nss_backend_t *) be);
293 }
294