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