xref: /illumos-gate/usr/src/lib/nsswitch/files/common/files_common.c (revision 5328fc53d11d7151861fa272e4fb0248b8f0e145)
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 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 /*
26  * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
27  * Copyright (c) 2017, Joyent, Inc.
28  */
29 
30 /*
31  * Common code and structures used by name-service-switch "files" backends.
32  */
33 
34 /*
35  * An implementation that used mmap() sensibly would be a wonderful thing,
36  *   but this here is just yer standard fgets() thang.
37  */
38 
39 #include "files_common.h"
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <ctype.h>
44 #include <fcntl.h>
45 #include <poll.h>
46 #include <unistd.h>
47 #include <sys/stat.h>
48 #include <sys/mman.h>
49 
50 /*ARGSUSED*/
51 nss_status_t
52 _nss_files_setent(be, dummy)
53 	files_backend_ptr_t	be;
54 	void			*dummy;
55 {
56 	if (be->f == 0) {
57 		if (be->filename == 0) {
58 			/* Backend isn't initialized properly? */
59 			return (NSS_UNAVAIL);
60 		}
61 		if ((be->f = fopen(be->filename, "rF")) == 0) {
62 			return (NSS_UNAVAIL);
63 		}
64 	} else {
65 		rewind(be->f);
66 	}
67 	return (NSS_SUCCESS);
68 }
69 
70 /*ARGSUSED*/
71 nss_status_t
72 _nss_files_endent(be, dummy)
73 	files_backend_ptr_t	be;
74 	void			*dummy;
75 {
76 	if (be->f != 0) {
77 		(void) fclose(be->f);
78 		be->f = 0;
79 	}
80 	if (be->buf != 0) {
81 		free(be->buf);
82 		be->buf = 0;
83 	}
84 	return (NSS_SUCCESS);
85 }
86 
87 /*
88  * This routine reads a line, including the processing of continuation
89  * characters.  It always leaves (or inserts) \n\0 at the end of the line.
90  * It returns the length of the line read, excluding the \n\0.  Who's idea
91  * was this?
92  * Returns -1 on EOF.
93  *
94  * Note that since each concurrent call to _nss_files_read_line has
95  * it's own FILE pointer, we can use getc_unlocked w/o difficulties,
96  * a substantial performance win.
97  */
98 int
99 _nss_files_read_line(f, buffer, buflen)
100 	FILE			*f;
101 	char			*buffer;
102 	int			buflen;
103 {
104 	int			linelen;	/* 1st unused slot in buffer */
105 	int			c;
106 
107 	/*CONSTCOND*/
108 	while (1) {
109 		linelen = 0;
110 		while (linelen < buflen - 1) {	/* "- 1" saves room for \n\0 */
111 			switch (c = getc_unlocked(f)) {
112 			case EOF:
113 				if (linelen == 0 ||
114 				    buffer[linelen - 1] == '\\') {
115 					return (-1);
116 				} else {
117 					buffer[linelen    ] = '\n';
118 					buffer[linelen + 1] = '\0';
119 					return (linelen);
120 				}
121 			case '\n':
122 				if (linelen > 0 &&
123 				    buffer[linelen - 1] == '\\') {
124 					--linelen;  /* remove the '\\' */
125 				} else {
126 					buffer[linelen    ] = '\n';
127 					buffer[linelen + 1] = '\0';
128 					return (linelen);
129 				}
130 				break;
131 			default:
132 				buffer[linelen++] = c;
133 			}
134 		}
135 		/* Buffer overflow -- eat rest of line and loop again */
136 		/* ===> Should syslog() */
137 		do {
138 			c = getc_unlocked(f);
139 			if (c == EOF) {
140 				return (-1);
141 			}
142 		} while (c != '\n');
143 	}
144 	/*NOTREACHED*/
145 }
146 
147 /*
148  * used only for getgroupbymem() now.
149  */
150 nss_status_t
151 _nss_files_do_all(be, args, filter, func)
152 	files_backend_ptr_t	be;
153 	void			*args;
154 	const char		*filter;
155 	files_do_all_func_t	func;
156 {
157 	long			grlen;
158 	char			*buffer;
159 	int			buflen;
160 	nss_status_t		res;
161 
162 	if (be->buf == 0) {
163 		if ((grlen = sysconf(_SC_GETGR_R_SIZE_MAX)) > 0)
164 			be->minbuf = grlen;
165 		if ((be->buf = malloc(be->minbuf)) == 0)
166 			return (NSS_UNAVAIL);
167 	}
168 	buffer = be->buf;
169 	buflen = be->minbuf;
170 
171 	if ((res = _nss_files_setent(be, 0)) != NSS_SUCCESS) {
172 		return (res);
173 	}
174 
175 	res = NSS_NOTFOUND;
176 
177 	do {
178 		int		linelen;
179 
180 		if ((linelen = _nss_files_read_line(be->f, buffer,
181 		    buflen)) < 0) {
182 			/* End of file */
183 			break;
184 		}
185 		if (filter != 0 && strstr(buffer, filter) == 0) {
186 			/*
187 			 * Optimization:  if the entry doesn't contain the
188 			 *   filter string then it can't be the entry we want,
189 			 *   so don't bother looking more closely at it.
190 			 */
191 			continue;
192 		}
193 		res = (*func)(buffer, linelen, args);
194 
195 	} while (res == NSS_NOTFOUND);
196 
197 	(void) _nss_files_endent(be, 0);
198 	return (res);
199 }
200 
201 /*
202  * Could implement this as an iterator function on top of _nss_files_do_all(),
203  *   but the shared code is small enough that it'd be pretty silly.
204  */
205 nss_status_t
206 _nss_files_XY_all(be, args, netdb, filter, check)
207 	files_backend_ptr_t	be;
208 	nss_XbyY_args_t		*args;
209 	int			netdb;		/* whether it uses netdb */
210 						/* format or not */
211 	const char		*filter;	/* advisory, to speed up */
212 						/* string search */
213 	files_XY_check_func	check;	/* NULL means one-shot, for getXXent */
214 {
215 	char			*r;
216 	nss_status_t		res;
217 	int	parsestat;
218 	int (*func)();
219 
220 	if (filter != NULL && *filter == '\0')
221 		return (NSS_NOTFOUND);
222 	if (be->buf == 0 || (be->minbuf < args->buf.buflen)) {
223 		if (be->minbuf < args->buf.buflen) {
224 			if (be->buf == 0) {
225 				be->minbuf = args->buf.buflen;
226 			} else if (
227 			    (r = realloc(be->buf, args->buf.buflen)) != NULL) {
228 				be->buf = r;
229 				be->minbuf = args->buf.buflen;
230 			}
231 		}
232 		if (be->buf == 0 &&
233 			(be->buf = malloc(be->minbuf)) == 0)
234 				return (NSS_UNAVAIL);
235 	}
236 
237 	if (check != 0 || be->f == 0) {
238 		if ((res = _nss_files_setent(be, 0)) != NSS_SUCCESS) {
239 			return (res);
240 		}
241 	}
242 
243 	res = NSS_NOTFOUND;
244 
245 	/*CONSTCOND*/
246 	while (1) {
247 		char		*instr	= be->buf;
248 		int		linelen;
249 
250 		if ((linelen = _nss_files_read_line(be->f, instr,
251 		    be->minbuf)) < 0) {
252 			/* End of file */
253 			args->returnval = 0;
254 			args->returnlen = 0;
255 			break;
256 		}
257 		if (filter != 0 && strstr(instr, filter) == 0) {
258 			/*
259 			 * Optimization:  if the entry doesn't contain the
260 			 *   filter string then it can't be the entry we want,
261 			 *   so don't bother looking more closely at it.
262 			 */
263 			continue;
264 		}
265 		if (netdb) {
266 			char		*first;
267 			char		*last;
268 
269 			if ((last = strchr(instr, '#')) == 0) {
270 				last = instr + linelen;
271 			}
272 			*last-- = '\0';		/* Nuke '\n' or #comment */
273 
274 			/*
275 			 * Skip leading whitespace.  Normally there isn't
276 			 *   any, so it's not worth calling strspn().
277 			 */
278 			for (first = instr;  isspace(*first);  first++) {
279 				;
280 			}
281 			if (*first == '\0') {
282 				continue;
283 			}
284 			/*
285 			 * Found something non-blank on the line.  Skip back
286 			 * over any trailing whitespace;  since we know
287 			 * there's non-whitespace earlier in the line,
288 			 * checking for termination is easy.
289 			 */
290 			while (isspace(*last)) {
291 				--last;
292 			}
293 
294 			linelen = last - first + 1;
295 			if (first != instr) {
296 					instr = first;
297 			}
298 		}
299 
300 		args->returnval = 0;
301 		args->returnlen = 0;
302 
303 		if (check != NULL && (*check)(args, instr, linelen) == 0)
304 			continue;
305 
306 		parsestat = NSS_STR_PARSE_SUCCESS;
307 		if (be->filename != NULL) {
308 			/*
309 			 * Special case for passwd and group wherein we
310 			 * replace uids/gids > MAXUID by ID_NOBODY
311 			 * because files backend does not support
312 			 * ephemeral ids.
313 			 */
314 			if (strcmp(be->filename, PF_PATH) == 0)
315 				parsestat = validate_passwd_ids(instr,
316 				    &linelen, be->minbuf, 2);
317 			else if (strcmp(be->filename, GF_PATH) == 0)
318 				parsestat = validate_group_ids(instr,
319 				    &linelen, be->minbuf, 2, check);
320 		}
321 
322 		if (parsestat == NSS_STR_PARSE_SUCCESS) {
323 			func = args->str2ent;
324 			parsestat = (*func)(instr, linelen, args->buf.result,
325 			    args->buf.buffer, args->buf.buflen);
326 		}
327 
328 		if (parsestat == NSS_STR_PARSE_SUCCESS) {
329 			args->returnval = (args->buf.result != NULL)?
330 					args->buf.result : args->buf.buffer;
331 			args->returnlen = linelen;
332 			res = NSS_SUCCESS;
333 			break;
334 		} else if (parsestat == NSS_STR_PARSE_ERANGE) {
335 			args->erange = 1;
336 			break;
337 		} /* else if (parsestat == NSS_STR_PARSE_PARSE) don't care ! */
338 	}
339 
340 	/*
341 	 * stayopen is set to 0 by default in order to close the opened
342 	 * file.  Some applications may break if it is set to 1.
343 	 */
344 	if (check != 0 && !args->stayopen) {
345 		(void) _nss_files_endent(be, 0);
346 	}
347 
348 	return (res);
349 }
350 
351 /*
352  * File hashing support.  Critical for sites with large (e.g. 1000+ lines)
353  * /etc/passwd or /etc/group files.  Currently only used by getpw*() and
354  * getgr*() routines, but any files backend can use this stuff.
355  */
356 static void
357 _nss_files_hash_destroy(files_hash_t *fhp)
358 {
359 	free(fhp->fh_table);
360 	fhp->fh_table = NULL;
361 	free(fhp->fh_line);
362 	fhp->fh_line = NULL;
363 	free(fhp->fh_file_start);
364 	fhp->fh_file_start = NULL;
365 }
366 #ifdef PIC
367 /*
368  * It turns out the hashing stuff really needs to be disabled for processes
369  * other than the nscd; the consumption of swap space and memory is otherwise
370  * unacceptable when the nscd is killed w/ a large passwd file (4M) active.
371  * See 4031930 for details.
372  * So we just use this psuedo function to enable the hashing feature.  Since
373  * this function name is private, we just create a function w/ the name
374  *  __nss_use_files_hash in the nscd itself and everyone else uses the old
375  * interface.
376  * We also disable hashing for .a executables to avoid problems with large
377  * files....
378  */
379 
380 #pragma weak __nss_use_files_hash
381 
382 extern void  __nss_use_files_hash(void);
383 #endif /* pic */
384 
385 /*ARGSUSED*/
386 nss_status_t
387 _nss_files_XY_hash(files_backend_ptr_t be, nss_XbyY_args_t *args,
388 	int netdb, files_hash_t *fhp, int hashop, files_XY_check_func check)
389 {
390 	/* LINTED E_FUNC_VAR_UNUSED */
391 	int fd, retries, ht, stat;
392 	/* LINTED E_FUNC_VAR_UNUSED */
393 	uint_t hash, line, f;
394 	/* LINTED E_FUNC_VAR_UNUSED */
395 	files_hashent_t *hp, *htab;
396 	/* LINTED E_FUNC_VAR_UNUSED */
397 	char *cp, *first, *last;
398 	/* LINTED E_FUNC_VAR_UNUSED */
399 	nss_XbyY_args_t xargs;
400 	/* LINTED E_FUNC_VAR_UNUSED */
401 	struct stat64 st;
402 
403 #ifndef PIC
404 	return (_nss_files_XY_all(be, args, netdb, 0, check));
405 }
406 #else
407 	if (__nss_use_files_hash == 0)
408 		return (_nss_files_XY_all(be, args, netdb, 0, check));
409 
410 	mutex_lock(&fhp->fh_lock);
411 retry:
412 	retries = 100;
413 	while (stat64(be->filename, &st) < 0) {
414 		/*
415 		 * This can happen only in two cases: Either the file is
416 		 * completely missing and we were not able to read it yet
417 		 * (fh_table is NULL), or there is some brief period when the
418 		 * file is being modified/renamed.  Keep trying until things
419 		 * settle down, but eventually give up.
420 		 */
421 		if (fhp->fh_table == NULL || --retries == 0)
422 			goto unavail;
423 		poll(0, 0, 100);
424 	}
425 
426 	if (st.st_mtim.tv_sec == fhp->fh_mtime.tv_sec &&
427 	    st.st_mtim.tv_nsec == fhp->fh_mtime.tv_nsec &&
428 	    fhp->fh_table != NULL) {
429 		htab = &fhp->fh_table[hashop * fhp->fh_size];
430 		hash = fhp->fh_hash_func[hashop](args, 1, NULL, 0);
431 		for (hp = htab[hash % fhp->fh_size].h_first; hp != NULL;
432 		    hp = hp->h_next) {
433 			if (hp->h_hash != hash)
434 				continue;
435 			line = hp - htab;
436 			if ((*check)(args, fhp->fh_line[line].l_start,
437 					fhp->fh_line[line].l_len) == 0)
438 				continue;
439 
440 			if (be->filename != NULL) {
441 				stat = NSS_STR_PARSE_SUCCESS;
442 				if (strcmp(be->filename, PF_PATH) == 0)
443 					stat = validate_passwd_ids(
444 					    fhp->fh_line[line].l_start,
445 					    &fhp->fh_line[line].l_len,
446 					    fhp->fh_line[line].l_len + 1,
447 					    1);
448 				else if (strcmp(be->filename, GF_PATH) == 0)
449 					stat = validate_group_ids(
450 					    fhp->fh_line[line].l_start,
451 					    &fhp->fh_line[line].l_len,
452 					    fhp->fh_line[line].l_len + 1,
453 					    1, check);
454 				if (stat != NSS_STR_PARSE_SUCCESS) {
455 					if (stat == NSS_STR_PARSE_ERANGE)
456 						args->erange = 1;
457 					continue;
458 				}
459 			}
460 
461 			if ((*args->str2ent)(fhp->fh_line[line].l_start,
462 			    fhp->fh_line[line].l_len, args->buf.result,
463 			    args->buf.buffer, args->buf.buflen) ==
464 			    NSS_STR_PARSE_SUCCESS) {
465 				args->returnval = (args->buf.result)?
466 					args->buf.result:args->buf.buffer;
467 				args->returnlen = fhp->fh_line[line].l_len;
468 				mutex_unlock(&fhp->fh_lock);
469 				return (NSS_SUCCESS);
470 			} else {
471 				args->erange = 1;
472 			}
473 		}
474 		args->returnval = 0;
475 		args->returnlen = 0;
476 		mutex_unlock(&fhp->fh_lock);
477 		return (NSS_NOTFOUND);
478 	}
479 
480 	_nss_files_hash_destroy(fhp);
481 
482 	if (st.st_size > SSIZE_MAX)
483 		goto unavail;
484 
485 	if ((fhp->fh_file_start = malloc((ssize_t)st.st_size + 1)) == NULL)
486 		goto unavail;
487 
488 	if ((fd = open(be->filename, O_RDONLY)) < 0)
489 		goto unavail;
490 
491 	if (read(fd, fhp->fh_file_start, (ssize_t)st.st_size) !=
492 	    (ssize_t)st.st_size) {
493 		close(fd);
494 		goto retry;
495 	}
496 
497 	close(fd);
498 
499 	fhp->fh_file_end = fhp->fh_file_start + (off_t)st.st_size;
500 	*fhp->fh_file_end = '\n';
501 	fhp->fh_mtime = st.st_mtim;
502 
503 	/*
504 	 * If the file changed since we read it, or if it's less than
505 	 * 1-2 seconds old, don't trust it; its modification may still
506 	 * be in progress.  The latter is a heuristic hack to minimize
507 	 * the likelihood of damage if someone modifies /etc/mumble
508 	 * directly (as opposed to editing and renaming a temp file).
509 	 *
510 	 * Note: the cast to u_int is there in case (1) someone rdated
511 	 * the system backwards since the last modification of /etc/mumble
512 	 * or (2) this is a diskless client whose time is badly out of sync
513 	 * with its server.  The 1-2 second age hack doesn't cover these
514 	 * cases -- oh well.
515 	 */
516 	if (stat64(be->filename, &st) < 0 ||
517 	    st.st_mtim.tv_sec != fhp->fh_mtime.tv_sec ||
518 	    st.st_mtim.tv_nsec != fhp->fh_mtime.tv_nsec ||
519 	    (uint_t)(time(0) - st.st_mtim.tv_sec + 2) < 4) {
520 		poll(0, 0, 1000);
521 		goto retry;
522 	}
523 
524 	line = 1;
525 	for (cp = fhp->fh_file_start; cp < fhp->fh_file_end; cp++)
526 		if (*cp == '\n')
527 			line++;
528 
529 	for (f = 2; f * f <= line; f++) {	/* find next largest prime */
530 		if (line % f == 0) {
531 			f = 1;
532 			line++;
533 		}
534 	}
535 
536 	fhp->fh_size = line;
537 	fhp->fh_line = malloc(line * sizeof (files_linetab_t));
538 	fhp->fh_table = calloc(line * fhp->fh_nhtab, sizeof (files_hashent_t));
539 	if (fhp->fh_line == NULL || fhp->fh_table == NULL)
540 		goto unavail;
541 
542 	line = 0;
543 	cp = fhp->fh_file_start;
544 	while (cp < fhp->fh_file_end) {
545 		first = cp;
546 		while (*cp != '\n')
547 			cp++;
548 		if (cp > first && *(cp - 1) == '\\') {
549 			memmove(first + 2, first, cp - first - 1);
550 			cp = first + 2;
551 			continue;
552 		}
553 		last = cp;
554 		*cp++ = '\0';
555 		if (netdb) {
556 			if ((last = strchr(first, '#')) == 0)
557 				last = cp - 1;
558 			*last-- = '\0';		/* nuke '\n' or #comment */
559 			while (isspace(*first))	/* nuke leading whitespace */
560 				first++;
561 			if (*first == '\0')	/* skip content-free lines */
562 				continue;
563 			while (isspace(*last))	/* nuke trailing whitespace */
564 				--last;
565 			*++last = '\0';
566 		}
567 		for (ht = 0; ht < fhp->fh_nhtab; ht++) {
568 			hp = &fhp->fh_table[ht * fhp->fh_size + line];
569 			hp->h_hash = fhp->fh_hash_func[ht](&xargs, 0, first,
570 					last - first);
571 		}
572 		fhp->fh_line[line].l_start = first;
573 		fhp->fh_line[line++].l_len = last - first;
574 	}
575 
576 	/*
577 	 * Populate the hash tables in reverse order so that the hash chains
578 	 * end up in forward order.  This ensures that hashed lookups find
579 	 * things in the same order that a linear search of the file would.
580 	 * This is essential in cases where there could be multiple matches.
581 	 * For example: until 2.7, root and smtp both had uid 0; but we
582 	 * certainly wouldn't want getpwuid(0) to return smtp.
583 	 */
584 	for (ht = 0; ht < fhp->fh_nhtab; ht++) {
585 		htab = &fhp->fh_table[ht * fhp->fh_size];
586 		for (hp = &htab[line - 1]; hp >= htab; hp--) {
587 			uint_t bucket = hp->h_hash % fhp->fh_size;
588 			hp->h_next = htab[bucket].h_first;
589 			htab[bucket].h_first = hp;
590 		}
591 	}
592 
593 	goto retry;
594 
595 unavail:
596 	_nss_files_hash_destroy(fhp);
597 	mutex_unlock(&fhp->fh_lock);
598 	return (NSS_UNAVAIL);
599 }
600 #endif /* PIC */
601 
602 nss_status_t
603 _nss_files_getent_rigid(be, a)
604 	files_backend_ptr_t	be;
605 	void			*a;
606 {
607 	nss_XbyY_args_t		*args = (nss_XbyY_args_t *)a;
608 
609 	return (_nss_files_XY_all(be, args, 0, 0, 0));
610 }
611 
612 nss_status_t
613 _nss_files_getent_netdb(be, a)
614 	files_backend_ptr_t	be;
615 	void			*a;
616 {
617 	nss_XbyY_args_t		*args = (nss_XbyY_args_t *)a;
618 
619 	return (_nss_files_XY_all(be, args, 1, 0, 0));
620 }
621 
622 /*ARGSUSED*/
623 nss_status_t
624 _nss_files_destr(be, dummy)
625 	files_backend_ptr_t	be;
626 	void			*dummy;
627 {
628 	if (be != 0) {
629 		if (be->f != 0) {
630 			(void) _nss_files_endent(be, 0);
631 		}
632 		if (be->hashinfo != NULL) {
633 			(void) mutex_lock(&be->hashinfo->fh_lock);
634 			if (--be->hashinfo->fh_refcnt == 0)
635 				_nss_files_hash_destroy(be->hashinfo);
636 			(void) mutex_unlock(&be->hashinfo->fh_lock);
637 		}
638 		free(be);
639 	}
640 	return (NSS_SUCCESS);	/* In case anyone is dumb enough to check */
641 }
642 
643 nss_backend_t *
644 _nss_files_constr(ops, n_ops, filename, min_bufsize, fhp)
645 	files_backend_op_t	ops[];
646 	int			n_ops;
647 	const char		*filename;
648 	int			min_bufsize;
649 	files_hash_t		*fhp;
650 {
651 	files_backend_ptr_t	be;
652 
653 	if ((be = (files_backend_ptr_t)malloc(sizeof (*be))) == 0) {
654 		return (0);
655 	}
656 	be->ops		= ops;
657 	be->n_ops	= n_ops;
658 	be->filename	= filename;
659 	be->minbuf	= min_bufsize;
660 	be->f		= 0;
661 	be->buf		= 0;
662 	be->hashinfo	= fhp;
663 
664 	if (fhp != NULL) {
665 		(void) mutex_lock(&fhp->fh_lock);
666 		fhp->fh_refcnt++;
667 		(void) mutex_unlock(&fhp->fh_lock);
668 	}
669 
670 	return ((nss_backend_t *)be);
671 }
672 
673 int
674 _nss_files_check_name_colon(nss_XbyY_args_t *argp, const char *line,
675 	int linelen)
676 {
677 	const char	*linep, *limit;
678 	const char	*keyp = argp->key.name;
679 
680 	linep = line;
681 	limit = line + linelen;
682 	while (*keyp && linep < limit && *keyp == *linep) {
683 		keyp++;
684 		linep++;
685 	}
686 	return (linep < limit && *keyp == '\0' && *linep == ':');
687 }
688 
689 /*
690  * This routine is used to parse lines of the form:
691  * 	name number aliases
692  * It returns 1 if the key in argp matches any one of the
693  * names in the line, otherwise 0
694  * Used by rpc, networks, protocols
695  */
696 int
697 _nss_files_check_name_aliases(nss_XbyY_args_t *argp, const char *line,
698 	int linelen)
699 {
700 	const char	*limit, *linep, *keyp;
701 
702 	linep = line;
703 	limit = line + linelen;
704 	keyp = argp->key.name;
705 
706 	/* compare name */
707 	while (*keyp && linep < limit && !isspace(*linep) && *keyp == *linep) {
708 		keyp++;
709 		linep++;
710 	}
711 	if (*keyp == '\0' && linep < limit && isspace(*linep))
712 		return (1);
713 	/* skip remainder of the name, if any */
714 	while (linep < limit && !isspace(*linep))
715 		linep++;
716 	/* skip the delimiting spaces */
717 	while (linep < limit && isspace(*linep))
718 		linep++;
719 	/* compare with the aliases */
720 	while (linep < limit) {
721 		/*
722 		 * 1st pass: skip number
723 		 * Other passes: skip remainder of the alias name, if any
724 		 */
725 		while (linep < limit && !isspace(*linep))
726 			linep++;
727 		/* skip the delimiting spaces */
728 		while (linep < limit && isspace(*linep))
729 			linep++;
730 		/* compare with the alias name */
731 		keyp = argp->key.name;
732 		while (*keyp && linep < limit && !isspace(*linep) &&
733 		    *keyp == *linep) {
734 			keyp++;
735 			linep++;
736 		}
737 		if (*keyp == '\0' && (linep == limit || isspace(*linep)))
738 			return (1);
739 	}
740 	return (0);
741 }
742 
743 /*
744  * A few NSS modules hold onto data for the duration of their module. In this
745  * case, when that module goes away, we must free that data. This is a place
746  * that allows for modules to register items to take care of.
747  */
748 #pragma fini(_nss_files_fini)
749 static void
750 _nss_files_fini(void)
751 {
752 	getexecattr_fini();
753 }
754