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 nss_status_t 47 _nss_user_setent(be, dummy) 48 user_backend_ptr_t be; 49 void *dummy; 50 { 51 if (be->f == 0) { 52 if (be->filename == 0) { 53 /* Backend isn't initialized properly? */ 54 return (NSS_UNAVAIL); 55 } 56 if ((be->f = fopen(be->filename, "rF")) == 0) { 57 return (NSS_UNAVAIL); 58 } 59 } else { 60 rewind(be->f); 61 } 62 return (NSS_SUCCESS); 63 } 64 65 nss_status_t 66 _nss_user_endent(be, dummy) 67 user_backend_ptr_t be; 68 void *dummy; 69 { 70 if (be->f != 0) { 71 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 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 while (1) { 102 linelen = 0; 103 while (linelen < buflen - 1) { /* "- 1" saves room for \n\0 */ 104 switch (c = getc_unlocked(f)) { 105 case EOF: 106 if (linelen == 0 || 107 buffer[linelen - 1] == '\\') { 108 return (-1); 109 } else { 110 buffer[linelen ] = '\n'; 111 buffer[linelen + 1] = '\0'; 112 return (linelen); 113 } 114 case '\n': 115 if (linelen > 0 && 116 buffer[linelen - 1] == '\\') { 117 --linelen; /* remove the '\\' */ 118 } else { 119 buffer[linelen ] = '\n'; 120 buffer[linelen + 1] = '\0'; 121 return (linelen); 122 } 123 break; 124 default: 125 buffer[linelen++] = c; 126 } 127 } 128 /* Buffer overflow -- eat rest of line and loop again */ 129 /* ===> Should syslog() */ 130 do { 131 c = getc_unlocked(f); 132 if (c == EOF) { 133 return (-1); 134 } 135 } while (c != '\n'); 136 } 137 /*NOTREACHED*/ 138 } 139 140 141 /* 142 * Could implement this as an iterator function on top of _nss_user_do_all(), 143 * but the shared code is small enough that it'd be pretty silly. 144 */ 145 nss_status_t 146 _nss_user_XY_all(be, args, netdb, filter, check) 147 user_backend_ptr_t be; 148 nss_XbyY_args_t *args; 149 int netdb; /* whether it uses netdb */ 150 /* format or not */ 151 const char *filter; /* advisory, to speed up */ 152 /* string search */ 153 user_XY_check_func check; /* NULL means one-shot, for getXXent */ 154 { 155 nss_status_t res; 156 int parsestat; 157 158 if (be->buf == 0 && 159 (be->buf = malloc(be->minbuf)) == 0) { 160 return (NSS_UNAVAIL); /* really panic, malloc failed */ 161 } 162 163 if (check != 0 || be->f == 0) { 164 if ((res = _nss_user_setent(be, 0)) != NSS_SUCCESS) { 165 return (res); 166 } 167 } 168 169 res = NSS_NOTFOUND; 170 171 while (1) { 172 char *instr = be->buf; 173 int linelen; 174 175 if ((linelen = _nss_user_read_line(be->f, instr, 176 be->minbuf)) < 0) { 177 /* End of file */ 178 args->returnval = 0; 179 args->erange = 0; 180 break; 181 } 182 if (filter != 0 && strstr(instr, filter) == 0) { 183 /* 184 * Optimization: if the entry doesn't contain the 185 * filter string then it can't be the entry we want, 186 * so don't bother looking more closely at it. 187 */ 188 continue; 189 } 190 if (netdb) { 191 char *first; 192 char *last; 193 194 if ((last = strchr(instr, '#')) == 0) { 195 last = instr + linelen; 196 } 197 *last-- = '\0'; /* Nuke '\n' or #comment */ 198 199 /* 200 * Skip leading whitespace. Normally there isn't 201 * any, so it's not worth calling strspn(). 202 */ 203 for (first = instr; isspace(*first); first++) { 204 ; 205 } 206 if (*first == '\0') { 207 continue; 208 } 209 /* 210 * Found something non-blank on the line. Skip back 211 * over any trailing whitespace; since we know 212 * there's non-whitespace earlier in the line, 213 * checking for termination is easy. 214 */ 215 while (isspace(*last)) { 216 --last; 217 } 218 219 linelen = last - first + 1; 220 if (first != instr) { 221 instr = first; 222 } 223 } 224 225 args->returnval = 0; 226 parsestat = (*args->str2ent)(instr, linelen, args->buf.result, 227 args->buf.buffer, args->buf.buflen); 228 229 if (parsestat == NSS_STR_PARSE_SUCCESS) { 230 args->returnval = args->buf.result; 231 if (check == 0 || (*check)(args)) { 232 res = NSS_SUCCESS; 233 break; 234 } 235 } else if (parsestat == NSS_STR_PARSE_ERANGE) { 236 args->erange = 1; /* should we just skip this */ 237 /* one long line ?? */ 238 } /* else if (parsestat == NSS_STR_PARSE_PARSE) don't care ! */ 239 } 240 241 /* 242 * stayopen is set to 0 by default in order to close the opened 243 * file. Some applications may break if it is set to 1. 244 */ 245 if (check != 0 && !args->stayopen) { 246 (void) _nss_user_endent(be, 0); 247 } 248 249 return (res); 250 } 251 252 253 nss_status_t 254 _nss_user_destr(be, dummy) 255 user_backend_ptr_t be; 256 void *dummy; 257 { 258 if (be != 0) { 259 if (be->f != 0) { 260 _nss_user_endent(be, 0); 261 } 262 free((char *)be->filename); 263 free(be); 264 } 265 return (NSS_SUCCESS); /* In case anyone is dumb enough to check */ 266 } 267 268 nss_backend_t * 269 _nss_user_constr(ops, n_ops, filename, min_bufsize) 270 user_backend_op_t ops[]; 271 int n_ops; 272 const char *filename; 273 int min_bufsize; 274 { 275 user_backend_ptr_t be; 276 277 if ((be = (user_backend_ptr_t)malloc(sizeof (*be))) == 0) { 278 return (0); 279 } 280 be->ops = ops; 281 be->n_ops = n_ops; 282 if ((be->filename = strdup(filename)) == NULL) { 283 free(be); 284 return (NULL); 285 } 286 be->minbuf = min_bufsize; 287 be->f = 0; 288 be->buf = 0; 289 290 return ((nss_backend_t *)be); 291 } 292