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 /*
30 * An implementation that used mmap() sensibly would be a wonderful thing,
31 * but this here is just yer standard fgets() thang.
32 */
33
34 #include "user_common.h"
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <ctype.h>
38 #include <fcntl.h>
39 #include <poll.h>
40 #include <unistd.h>
41 #include <sys/stat.h>
42 #include <string.h>
43
44 /*ARGSUSED*/
45 nss_status_t
_nss_user_setent(be,dummy)46 _nss_user_setent(be, dummy)
47 user_backend_ptr_t be;
48 void *dummy;
49 {
50 if (be->f == 0) {
51 if (be->filename == 0) {
52 /* Backend isn't initialized properly? */
53 return (NSS_UNAVAIL);
54 }
55 if ((be->f = fopen(be->filename, "rF")) == 0) {
56 return (NSS_UNAVAIL);
57 }
58 } else {
59 rewind(be->f);
60 }
61 return (NSS_SUCCESS);
62 }
63
64 /*ARGSUSED*/
65 nss_status_t
_nss_user_endent(be,dummy)66 _nss_user_endent(be, dummy)
67 user_backend_ptr_t be;
68 void *dummy;
69 {
70 if (be->f != 0) {
71 (void) fclose(be->f);
72 be->f = 0;
73 }
74 if (be->buf != 0) {
75 free(be->buf);
76 be->buf = 0;
77 }
78 return (NSS_SUCCESS);
79 }
80
81 /*
82 * This routine reads a line, including the processing of continuation
83 * characters. It always leaves (or inserts) \n\0 at the end of the line.
84 * It returns the length of the line read, excluding the \n\0. Who's idea
85 * was this?
86 * Returns -1 on EOF.
87 *
88 * Note that since each concurrent call to _nss_user_read_line has
89 * it's own FILE pointer, we can use getc_unlocked w/o difficulties,
90 * a substantial performance win.
91 */
92 int
_nss_user_read_line(f,buffer,buflen)93 _nss_user_read_line(f, buffer, buflen)
94 FILE *f;
95 char *buffer;
96 int buflen;
97 {
98 int linelen; /* 1st unused slot in buffer */
99 int c;
100
101 /*CONSTCOND*/
102 while (1) {
103 linelen = 0;
104 while (linelen < buflen - 1) { /* "- 1" saves room for \n\0 */
105 switch (c = getc_unlocked(f)) {
106 case EOF:
107 if (linelen == 0 ||
108 buffer[linelen - 1] == '\\') {
109 return (-1);
110 } else {
111 buffer[linelen ] = '\n';
112 buffer[linelen + 1] = '\0';
113 return (linelen);
114 }
115 case '\n':
116 if (linelen > 0 &&
117 buffer[linelen - 1] == '\\') {
118 --linelen; /* remove the '\\' */
119 } else {
120 buffer[linelen ] = '\n';
121 buffer[linelen + 1] = '\0';
122 return (linelen);
123 }
124 break;
125 default:
126 buffer[linelen++] = c;
127 }
128 }
129 /* Buffer overflow -- eat rest of line and loop again */
130 /* ===> Should syslog() */
131 do {
132 c = getc_unlocked(f);
133 if (c == EOF) {
134 return (-1);
135 }
136 } while (c != '\n');
137 }
138 /*NOTREACHED*/
139 }
140
141
142 /*
143 * Could implement this as an iterator function on top of _nss_user_do_all(),
144 * but the shared code is small enough that it'd be pretty silly.
145 */
146 nss_status_t
_nss_user_XY_all(be,args,netdb,filter,check)147 _nss_user_XY_all(be, args, netdb, filter, check)
148 user_backend_ptr_t be;
149 nss_XbyY_args_t *args;
150 int netdb; /* whether it uses netdb */
151 /* format or not */
152 const char *filter; /* advisory, to speed up */
153 /* string search */
154 user_XY_check_func check; /* NULL means one-shot, for getXXent */
155 {
156 nss_status_t res;
157 int parsestat;
158
159 if (be->buf == 0 &&
160 (be->buf = malloc(be->minbuf)) == 0) {
161 return (NSS_UNAVAIL); /* really panic, malloc failed */
162 }
163
164 if (check != 0 || be->f == 0) {
165 if ((res = _nss_user_setent(be, 0)) != NSS_SUCCESS) {
166 return (res);
167 }
168 }
169
170 res = NSS_NOTFOUND;
171
172 /*CONSTCOND*/
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 /*ARGSUSED*/
256 nss_status_t
_nss_user_destr(be,dummy)257 _nss_user_destr(be, dummy)
258 user_backend_ptr_t be;
259 void *dummy;
260 {
261 if (be != 0) {
262 if (be->f != 0) {
263 (void) _nss_user_endent(be, 0);
264 }
265 free((char *)be->filename);
266 free(be);
267 }
268 return (NSS_SUCCESS); /* In case anyone is dumb enough to check */
269 }
270
271 nss_backend_t *
_nss_user_constr(ops,n_ops,filename,min_bufsize)272 _nss_user_constr(ops, n_ops, filename, min_bufsize)
273 user_backend_op_t ops[];
274 int n_ops;
275 const char *filename;
276 int min_bufsize;
277 {
278 user_backend_ptr_t be;
279
280 if ((be = (user_backend_ptr_t)malloc(sizeof (*be))) == 0) {
281 return (0);
282 }
283 be->ops = ops;
284 be->n_ops = n_ops;
285 if ((be->filename = strdup(filename)) == NULL) {
286 free(be);
287 return (NULL);
288 }
289 be->minbuf = min_bufsize;
290 be->f = 0;
291 be->buf = 0;
292
293 return ((nss_backend_t *)be);
294 }
295