xref: /onnv-gate/usr/src/lib/nsswitch/user/common/user_common.c (revision 1914:8a8c5f225b1b)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*1914Scasper  * Common Development and Distribution License (the "License").
6*1914Scasper  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*1914Scasper  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  *
250Sstevel@tonic-gate  * Common code and structures used by name-service-switch "user" backends.
260Sstevel@tonic-gate  * Much of this was taken directly from the files_common.c source.
270Sstevel@tonic-gate  */
280Sstevel@tonic-gate 
290Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
300Sstevel@tonic-gate 
310Sstevel@tonic-gate /*
320Sstevel@tonic-gate  * An implementation that used mmap() sensibly would be a wonderful thing,
330Sstevel@tonic-gate  *   but this here is just yer standard fgets() thang.
340Sstevel@tonic-gate  */
350Sstevel@tonic-gate 
36*1914Scasper #include "user_common.h"
370Sstevel@tonic-gate #include <stdio.h>
380Sstevel@tonic-gate #include <stdlib.h>
390Sstevel@tonic-gate #include <ctype.h>
400Sstevel@tonic-gate #include <fcntl.h>
410Sstevel@tonic-gate #include <poll.h>
420Sstevel@tonic-gate #include <unistd.h>
430Sstevel@tonic-gate #include <sys/stat.h>
440Sstevel@tonic-gate #include <string.h>
450Sstevel@tonic-gate 
460Sstevel@tonic-gate nss_status_t
470Sstevel@tonic-gate _nss_user_setent(be, dummy)
480Sstevel@tonic-gate 	user_backend_ptr_t	be;
490Sstevel@tonic-gate 	void			*dummy;
500Sstevel@tonic-gate {
510Sstevel@tonic-gate 	if (be->f == 0) {
520Sstevel@tonic-gate 		if (be->filename == 0) {
530Sstevel@tonic-gate 			/* Backend isn't initialized properly? */
540Sstevel@tonic-gate 			return (NSS_UNAVAIL);
550Sstevel@tonic-gate 		}
56*1914Scasper 		if ((be->f = fopen(be->filename, "rF")) == 0) {
570Sstevel@tonic-gate 			return (NSS_UNAVAIL);
580Sstevel@tonic-gate 		}
590Sstevel@tonic-gate 	} else {
60*1914Scasper 		rewind(be->f);
610Sstevel@tonic-gate 	}
620Sstevel@tonic-gate 	return (NSS_SUCCESS);
630Sstevel@tonic-gate }
640Sstevel@tonic-gate 
650Sstevel@tonic-gate nss_status_t
660Sstevel@tonic-gate _nss_user_endent(be, dummy)
670Sstevel@tonic-gate 	user_backend_ptr_t	be;
680Sstevel@tonic-gate 	void			*dummy;
690Sstevel@tonic-gate {
700Sstevel@tonic-gate 	if (be->f != 0) {
71*1914Scasper 		fclose(be->f);
720Sstevel@tonic-gate 		be->f = 0;
730Sstevel@tonic-gate 	}
740Sstevel@tonic-gate 	if (be->buf != 0) {
750Sstevel@tonic-gate 		free(be->buf);
760Sstevel@tonic-gate 		be->buf = 0;
770Sstevel@tonic-gate 	}
780Sstevel@tonic-gate 	return (NSS_SUCCESS);
790Sstevel@tonic-gate }
800Sstevel@tonic-gate 
810Sstevel@tonic-gate /*
820Sstevel@tonic-gate  * This routine reads a line, including the processing of continuation
830Sstevel@tonic-gate  * characters.  It always leaves (or inserts) \n\0 at the end of the line.
840Sstevel@tonic-gate  * It returns the length of the line read, excluding the \n\0.  Who's idea
850Sstevel@tonic-gate  * was this?
860Sstevel@tonic-gate  * Returns -1 on EOF.
870Sstevel@tonic-gate  *
880Sstevel@tonic-gate  * Note that since each concurrent call to _nss_user_read_line has
890Sstevel@tonic-gate  * it's own FILE pointer, we can use getc_unlocked w/o difficulties,
900Sstevel@tonic-gate  * a substantial performance win.
910Sstevel@tonic-gate  */
920Sstevel@tonic-gate int
930Sstevel@tonic-gate _nss_user_read_line(f, buffer, buflen)
94*1914Scasper 	FILE			*f;
950Sstevel@tonic-gate 	char			*buffer;
960Sstevel@tonic-gate 	int			buflen;
970Sstevel@tonic-gate {
980Sstevel@tonic-gate 	int			linelen;	/* 1st unused slot in buffer */
990Sstevel@tonic-gate 	int			c;
1000Sstevel@tonic-gate 
1010Sstevel@tonic-gate 	while (1) {
1020Sstevel@tonic-gate 		linelen = 0;
1030Sstevel@tonic-gate 		while (linelen < buflen - 1) {	/* "- 1" saves room for \n\0 */
104*1914Scasper 			switch (c = getc_unlocked(f)) {
1050Sstevel@tonic-gate 			case EOF:
1060Sstevel@tonic-gate 				if (linelen == 0 ||
1070Sstevel@tonic-gate 				    buffer[linelen - 1] == '\\') {
1080Sstevel@tonic-gate 					return (-1);
1090Sstevel@tonic-gate 				} else {
1100Sstevel@tonic-gate 					buffer[linelen    ] = '\n';
1110Sstevel@tonic-gate 					buffer[linelen + 1] = '\0';
1120Sstevel@tonic-gate 					return (linelen);
1130Sstevel@tonic-gate 				}
1140Sstevel@tonic-gate 			case '\n':
1150Sstevel@tonic-gate 				if (linelen > 0 &&
1160Sstevel@tonic-gate 				    buffer[linelen - 1] == '\\') {
1170Sstevel@tonic-gate 					--linelen;  /* remove the '\\' */
1180Sstevel@tonic-gate 				} else {
1190Sstevel@tonic-gate 					buffer[linelen    ] = '\n';
1200Sstevel@tonic-gate 					buffer[linelen + 1] = '\0';
1210Sstevel@tonic-gate 					return (linelen);
1220Sstevel@tonic-gate 				}
1230Sstevel@tonic-gate 				break;
1240Sstevel@tonic-gate 			default:
1250Sstevel@tonic-gate 				buffer[linelen++] = c;
1260Sstevel@tonic-gate 			}
1270Sstevel@tonic-gate 		}
1280Sstevel@tonic-gate 		/* Buffer overflow -- eat rest of line and loop again */
1290Sstevel@tonic-gate 		/* ===> Should syslog() */
1300Sstevel@tonic-gate 		do {
131*1914Scasper 			c = getc_unlocked(f);
1320Sstevel@tonic-gate 			if (c == EOF) {
1330Sstevel@tonic-gate 				return (-1);
1340Sstevel@tonic-gate 			}
1350Sstevel@tonic-gate 		} while (c != '\n');
1360Sstevel@tonic-gate 	}
1370Sstevel@tonic-gate 	/*NOTREACHED*/
1380Sstevel@tonic-gate }
1390Sstevel@tonic-gate 
1400Sstevel@tonic-gate 
1410Sstevel@tonic-gate /*
1420Sstevel@tonic-gate  * Could implement this as an iterator function on top of _nss_user_do_all(),
1430Sstevel@tonic-gate  *   but the shared code is small enough that it'd be pretty silly.
1440Sstevel@tonic-gate  */
1450Sstevel@tonic-gate nss_status_t
1460Sstevel@tonic-gate _nss_user_XY_all(be, args, netdb, filter, check)
1470Sstevel@tonic-gate 	user_backend_ptr_t	be;
1480Sstevel@tonic-gate 	nss_XbyY_args_t		*args;
1490Sstevel@tonic-gate 	int			netdb;		/* whether it uses netdb */
1500Sstevel@tonic-gate 						/* format or not */
1510Sstevel@tonic-gate 	const char		*filter;	/* advisory, to speed up */
1520Sstevel@tonic-gate 						/* string search */
1530Sstevel@tonic-gate 	user_XY_check_func	check;	/* NULL means one-shot, for getXXent */
1540Sstevel@tonic-gate {
1550Sstevel@tonic-gate 	nss_status_t		res;
1560Sstevel@tonic-gate 	int	parsestat;
1570Sstevel@tonic-gate 
1580Sstevel@tonic-gate 	if (be->buf == 0 &&
1590Sstevel@tonic-gate 		(be->buf = malloc(be->minbuf)) == 0) {
1600Sstevel@tonic-gate 		return (NSS_UNAVAIL); /* really panic, malloc failed */
1610Sstevel@tonic-gate 	}
1620Sstevel@tonic-gate 
1630Sstevel@tonic-gate 	if (check != 0 || be->f == 0) {
1640Sstevel@tonic-gate 		if ((res = _nss_user_setent(be, 0)) != NSS_SUCCESS) {
1650Sstevel@tonic-gate 			return (res);
1660Sstevel@tonic-gate 		}
1670Sstevel@tonic-gate 	}
1680Sstevel@tonic-gate 
1690Sstevel@tonic-gate 	res = NSS_NOTFOUND;
1700Sstevel@tonic-gate 
1710Sstevel@tonic-gate 	while (1) {
1720Sstevel@tonic-gate 		char		*instr	= be->buf;
1730Sstevel@tonic-gate 		int		linelen;
1740Sstevel@tonic-gate 
1750Sstevel@tonic-gate 		if ((linelen = _nss_user_read_line(be->f, instr,
1760Sstevel@tonic-gate 		    be->minbuf)) < 0) {
1770Sstevel@tonic-gate 			/* End of file */
1780Sstevel@tonic-gate 			args->returnval = 0;
1790Sstevel@tonic-gate 			args->erange    = 0;
1800Sstevel@tonic-gate 			break;
1810Sstevel@tonic-gate 		}
1820Sstevel@tonic-gate 		if (filter != 0 && strstr(instr, filter) == 0) {
1830Sstevel@tonic-gate 			/*
1840Sstevel@tonic-gate 			 * Optimization:  if the entry doesn't contain the
1850Sstevel@tonic-gate 			 *   filter string then it can't be the entry we want,
1860Sstevel@tonic-gate 			 *   so don't bother looking more closely at it.
1870Sstevel@tonic-gate 			 */
1880Sstevel@tonic-gate 			continue;
1890Sstevel@tonic-gate 		}
1900Sstevel@tonic-gate 		if (netdb) {
1910Sstevel@tonic-gate 			char		*first;
1920Sstevel@tonic-gate 			char		*last;
1930Sstevel@tonic-gate 
1940Sstevel@tonic-gate 			if ((last = strchr(instr, '#')) == 0) {
1950Sstevel@tonic-gate 				last = instr + linelen;
1960Sstevel@tonic-gate 			}
1970Sstevel@tonic-gate 			*last-- = '\0';		/* Nuke '\n' or #comment */
1980Sstevel@tonic-gate 
1990Sstevel@tonic-gate 			/*
2000Sstevel@tonic-gate 			 * Skip leading whitespace.  Normally there isn't
2010Sstevel@tonic-gate 			 *   any, so it's not worth calling strspn().
2020Sstevel@tonic-gate 			 */
2030Sstevel@tonic-gate 			for (first = instr;  isspace(*first);  first++) {
2040Sstevel@tonic-gate 				;
2050Sstevel@tonic-gate 			}
2060Sstevel@tonic-gate 			if (*first == '\0') {
2070Sstevel@tonic-gate 				continue;
2080Sstevel@tonic-gate 			}
2090Sstevel@tonic-gate 			/*
2100Sstevel@tonic-gate 			 * Found something non-blank on the line.  Skip back
2110Sstevel@tonic-gate 			 * over any trailing whitespace;  since we know
2120Sstevel@tonic-gate 			 * there's non-whitespace earlier in the line,
2130Sstevel@tonic-gate 			 * checking for termination is easy.
2140Sstevel@tonic-gate 			 */
2150Sstevel@tonic-gate 			while (isspace(*last)) {
2160Sstevel@tonic-gate 				--last;
2170Sstevel@tonic-gate 			}
2180Sstevel@tonic-gate 
2190Sstevel@tonic-gate 			linelen = last - first + 1;
2200Sstevel@tonic-gate 			if (first != instr) {
2210Sstevel@tonic-gate 					instr = first;
2220Sstevel@tonic-gate 			}
2230Sstevel@tonic-gate 		}
2240Sstevel@tonic-gate 
2250Sstevel@tonic-gate 		args->returnval = 0;
2260Sstevel@tonic-gate 		parsestat = (*args->str2ent)(instr, linelen, args->buf.result,
2270Sstevel@tonic-gate 				args->buf.buffer, args->buf.buflen);
2280Sstevel@tonic-gate 
2290Sstevel@tonic-gate 		if (parsestat == NSS_STR_PARSE_SUCCESS) {
2300Sstevel@tonic-gate 			args->returnval = args->buf.result;
2310Sstevel@tonic-gate 			if (check == 0 || (*check)(args)) {
2320Sstevel@tonic-gate 				res = NSS_SUCCESS;
2330Sstevel@tonic-gate 				break;
2340Sstevel@tonic-gate 			}
2350Sstevel@tonic-gate 		} else if (parsestat == NSS_STR_PARSE_ERANGE) {
2360Sstevel@tonic-gate 			args->erange = 1;	/* should we just skip this */
2370Sstevel@tonic-gate 						/* one long line ?? */
2380Sstevel@tonic-gate 		} /* else if (parsestat == NSS_STR_PARSE_PARSE) don't care ! */
2390Sstevel@tonic-gate 	}
2400Sstevel@tonic-gate 
2410Sstevel@tonic-gate 	/*
2420Sstevel@tonic-gate 	 * stayopen is set to 0 by default in order to close the opened
2430Sstevel@tonic-gate 	 * file.  Some applications may break if it is set to 1.
2440Sstevel@tonic-gate 	 */
2450Sstevel@tonic-gate 	if (check != 0 && !args->stayopen) {
2460Sstevel@tonic-gate 		(void) _nss_user_endent(be, 0);
2470Sstevel@tonic-gate 	}
2480Sstevel@tonic-gate 
2490Sstevel@tonic-gate 	return (res);
2500Sstevel@tonic-gate }
2510Sstevel@tonic-gate 
2520Sstevel@tonic-gate 
2530Sstevel@tonic-gate nss_status_t
2540Sstevel@tonic-gate _nss_user_destr(be, dummy)
2550Sstevel@tonic-gate 	user_backend_ptr_t	be;
2560Sstevel@tonic-gate 	void			*dummy;
2570Sstevel@tonic-gate {
2580Sstevel@tonic-gate 	if (be != 0) {
2590Sstevel@tonic-gate 		if (be->f != 0) {
2600Sstevel@tonic-gate 			_nss_user_endent(be, 0);
2610Sstevel@tonic-gate 		}
2620Sstevel@tonic-gate 		free((char *)be->filename);
2630Sstevel@tonic-gate 		free(be);
2640Sstevel@tonic-gate 	}
2650Sstevel@tonic-gate 	return (NSS_SUCCESS);	/* In case anyone is dumb enough to check */
2660Sstevel@tonic-gate }
2670Sstevel@tonic-gate 
2680Sstevel@tonic-gate nss_backend_t *
2690Sstevel@tonic-gate _nss_user_constr(ops, n_ops, filename, min_bufsize)
2700Sstevel@tonic-gate 	user_backend_op_t	ops[];
2710Sstevel@tonic-gate 	int			n_ops;
2720Sstevel@tonic-gate 	const char		*filename;
2730Sstevel@tonic-gate 	int			min_bufsize;
2740Sstevel@tonic-gate {
2750Sstevel@tonic-gate 	user_backend_ptr_t	be;
2760Sstevel@tonic-gate 
277*1914Scasper 	if ((be = (user_backend_ptr_t)malloc(sizeof (*be))) == 0) {
2780Sstevel@tonic-gate 		return (0);
2790Sstevel@tonic-gate 	}
2800Sstevel@tonic-gate 	be->ops		= ops;
2810Sstevel@tonic-gate 	be->n_ops	= n_ops;
2820Sstevel@tonic-gate 	if ((be->filename = strdup(filename)) == NULL) {
2830Sstevel@tonic-gate 		free(be);
2840Sstevel@tonic-gate 		return (NULL);
2850Sstevel@tonic-gate 	}
2860Sstevel@tonic-gate 	be->minbuf	= min_bufsize;
2870Sstevel@tonic-gate 	be->f		= 0;
2880Sstevel@tonic-gate 	be->buf		= 0;
2890Sstevel@tonic-gate 
290*1914Scasper 	return ((nss_backend_t *)be);
2910Sstevel@tonic-gate }
292