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