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