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 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 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 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 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 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 * 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