xref: /onnv-gate/usr/src/cmd/fs.d/autofs/auto_subr.c (revision 1676:37f4a3e2bd99)
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*1676Sjpk  * Common Development and Distribution License (the "License").
6*1676Sjpk  * 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*1676Sjpk  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23*1676Sjpk  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
270Sstevel@tonic-gate 
280Sstevel@tonic-gate #include <ctype.h>
290Sstevel@tonic-gate #include <stdio.h>
300Sstevel@tonic-gate #include <stdlib.h>
310Sstevel@tonic-gate #include <unistd.h>
320Sstevel@tonic-gate #include <locale.h>
330Sstevel@tonic-gate #include <syslog.h>
340Sstevel@tonic-gate #include <errno.h>
350Sstevel@tonic-gate #include <string.h>
360Sstevel@tonic-gate #include <stdarg.h>
370Sstevel@tonic-gate #include <dirent.h>
380Sstevel@tonic-gate #include <thread.h>
390Sstevel@tonic-gate #include <sys/param.h>
400Sstevel@tonic-gate #include <sys/time.h>
410Sstevel@tonic-gate #include <sys/vfs.h>
420Sstevel@tonic-gate #include <sys/types.h>
430Sstevel@tonic-gate #include <sys/stat.h>
440Sstevel@tonic-gate #include <sys/mnttab.h>
450Sstevel@tonic-gate #include <sys/mntent.h>
460Sstevel@tonic-gate #include <sys/mount.h>
470Sstevel@tonic-gate #include <sys/signal.h>
480Sstevel@tonic-gate #include <sys/utsname.h>
490Sstevel@tonic-gate #include <sys/systeminfo.h>
500Sstevel@tonic-gate #include <sys/tiuser.h>
510Sstevel@tonic-gate #include <sys/utsname.h>
520Sstevel@tonic-gate #include <rpc/rpc.h>
530Sstevel@tonic-gate #include <rpcsvc/nfs_prot.h>
540Sstevel@tonic-gate #include <assert.h>
550Sstevel@tonic-gate #include "automount.h"
56*1676Sjpk #include <zone.h>
57*1676Sjpk #include <priv.h>
58*1676Sjpk #include <fcntl.h>
590Sstevel@tonic-gate 
600Sstevel@tonic-gate static char *check_hier(char *);
610Sstevel@tonic-gate static int natisa(char *, size_t);
620Sstevel@tonic-gate 
630Sstevel@tonic-gate struct mntlist *current_mounts;
640Sstevel@tonic-gate 
650Sstevel@tonic-gate static bool_t nodirect_map = FALSE;
660Sstevel@tonic-gate 
67*1676Sjpk /*
68*1676Sjpk  * If the system is labeled then we need to
69*1676Sjpk  * have a uniquely-named auto_home map for each zone.
70*1676Sjpk  * The maps are made unique by appending the zonename.
71*1676Sjpk  * The home directory is made unique by prepending /zone/<zonename>
72*1676Sjpk  * for each zone that is dominated by the current zone.
73*1676Sjpk  * The current zone's home directory mount point is not changed.
74*1676Sjpk  *
75*1676Sjpk  * For each auto_home_<zonename> a default template map is created
76*1676Sjpk  * only if it doesn't exist yet. The default entry is used to declare
77*1676Sjpk  * local home directories created within each zone. For example:
78*1676Sjpk  *
79*1676Sjpk  *	+auto_home_public
80*1676Sjpk  *      * -fstype=lofs :/zone/public/export/home/&
81*1676Sjpk  */
82*1676Sjpk static void
83*1676Sjpk loadzone_maps(char *mntpnt, char *map, char *opts, char **stack, char ***stkptr)
84*1676Sjpk {
85*1676Sjpk 	zoneid_t *zids = NULL;
86*1676Sjpk 	zoneid_t my_zoneid;
87*1676Sjpk 	uint_t nzents_saved;
88*1676Sjpk 	uint_t nzents;
89*1676Sjpk 	int i;
90*1676Sjpk 
91*1676Sjpk 	if (!priv_ineffect(PRIV_SYS_MOUNT))
92*1676Sjpk 		return;
93*1676Sjpk 
94*1676Sjpk 	if (zone_list(NULL, &nzents) != 0) {
95*1676Sjpk 		return;
96*1676Sjpk 	}
97*1676Sjpk 	my_zoneid = getzoneid();
98*1676Sjpk again:
99*1676Sjpk 	if (nzents == 0)
100*1676Sjpk 		return;
101*1676Sjpk 
102*1676Sjpk 	zids = malloc(nzents * sizeof (zoneid_t));
103*1676Sjpk 	nzents_saved = nzents;
104*1676Sjpk 
105*1676Sjpk 	if (zone_list(zids, &nzents) != 0) {
106*1676Sjpk 		free(zids);
107*1676Sjpk 		return;
108*1676Sjpk 	}
109*1676Sjpk 	if (nzents != nzents_saved) {
110*1676Sjpk 		/* list changed, try again */
111*1676Sjpk 		free(zids);
112*1676Sjpk 		goto again;
113*1676Sjpk 	}
114*1676Sjpk 
115*1676Sjpk 	for (i = 0; i < nzents; i++) {
116*1676Sjpk 		char zonename[ZONENAME_MAX];
117*1676Sjpk 		char zoneroot[MAXPATHLEN];
118*1676Sjpk 
119*1676Sjpk 		if (getzonenamebyid(zids[i], zonename, ZONENAME_MAX) != -1) {
120*1676Sjpk 			char appended_map[MAXPATHLEN];
121*1676Sjpk 			char prepended_mntpnt[MAXPATHLEN];
122*1676Sjpk 			char map_path[MAXPATHLEN];
123*1676Sjpk 			int fd;
124*1676Sjpk 
125*1676Sjpk 			(void) snprintf(appended_map, sizeof (appended_map),
126*1676Sjpk 			    "%s_%s", map, zonename);
127*1676Sjpk 
128*1676Sjpk 			/* for current zone, leave mntpnt alone */
129*1676Sjpk 			if (zids[i] != my_zoneid) {
130*1676Sjpk 				(void) snprintf(prepended_mntpnt,
131*1676Sjpk 				    sizeof (prepended_mntpnt),
132*1676Sjpk 				    "/zone/%s%s", zonename, mntpnt);
133*1676Sjpk 				if (zone_getattr(zids[i], ZONE_ATTR_ROOT,
134*1676Sjpk 				    zoneroot, sizeof (zoneroot)) == -1)
135*1676Sjpk 					continue;
136*1676Sjpk 			} else {
137*1676Sjpk 				(void) strcpy(prepended_mntpnt, mntpnt);
138*1676Sjpk 				zoneroot[0] = '\0';
139*1676Sjpk 			}
140*1676Sjpk 
141*1676Sjpk 			dirinit(prepended_mntpnt, appended_map, opts, 0, stack,
142*1676Sjpk 			    stkptr);
143*1676Sjpk 			/*
144*1676Sjpk 			 * Next create auto_home_<zone> maps for each zone
145*1676Sjpk 			 */
146*1676Sjpk 
147*1676Sjpk 			(void) snprintf(map_path, sizeof (map_path),
148*1676Sjpk 			    "/etc/%s", appended_map);
149*1676Sjpk 			/*
150*1676Sjpk 			 * If the map file doesn't exist create a template
151*1676Sjpk 			 */
152*1676Sjpk 			if ((fd = open(map_path, O_RDWR | O_CREAT | O_EXCL,
153*1676Sjpk 			    S_IRUSR | S_IWUSR | S_IRGRP| S_IROTH)) != -1) {
154*1676Sjpk 				int len;
155*1676Sjpk 				char map_rec[MAXPATHLEN];
156*1676Sjpk 
157*1676Sjpk 				len = snprintf(map_rec, sizeof (map_rec),
158*1676Sjpk 				    "+%s\n*\t-fstype=lofs\t:%s/export/home/&\n",
159*1676Sjpk 				    appended_map, zoneroot);
160*1676Sjpk 				if (len <= sizeof (map_rec))
161*1676Sjpk 					(void) write(fd, map_rec, len);
162*1676Sjpk 				(void) close(fd);
163*1676Sjpk 			}
164*1676Sjpk 		}
165*1676Sjpk 	}
166*1676Sjpk 	free(zids);
167*1676Sjpk }
168*1676Sjpk 
1690Sstevel@tonic-gate void
1700Sstevel@tonic-gate dirinit(char *mntpnt, char *map, char *opts, int direct, char **stack,
171*1676Sjpk     char ***stkptr)
1720Sstevel@tonic-gate {
1730Sstevel@tonic-gate 	struct autodir *dir;
1740Sstevel@tonic-gate 	char *p;
1750Sstevel@tonic-gate 
1760Sstevel@tonic-gate 	if (strcmp(map, "-null") == 0) {
1770Sstevel@tonic-gate 		if (strcmp(mntpnt, "/-") == 0)
1780Sstevel@tonic-gate 			nodirect_map = TRUE;
1790Sstevel@tonic-gate 		goto enter;
1800Sstevel@tonic-gate 	}
1810Sstevel@tonic-gate 
1820Sstevel@tonic-gate 	p = mntpnt + (strlen(mntpnt) - 1);
1830Sstevel@tonic-gate 	if (*p == '/')
1840Sstevel@tonic-gate 		*p = '\0';	/* trim trailing / */
1850Sstevel@tonic-gate 	if (*mntpnt != '/') {
1860Sstevel@tonic-gate 		pr_msg("dir %s must start with '/'", mntpnt);
1870Sstevel@tonic-gate 		return;
1880Sstevel@tonic-gate 	}
1890Sstevel@tonic-gate 	if (p = check_hier(mntpnt)) {
1900Sstevel@tonic-gate 		pr_msg("hierarchical mountpoint: %s and %s",
1910Sstevel@tonic-gate 			p, mntpnt);
1920Sstevel@tonic-gate 		return;
1930Sstevel@tonic-gate 	}
1940Sstevel@tonic-gate 
1950Sstevel@tonic-gate 	/*
1960Sstevel@tonic-gate 	 * If it's a direct map then call dirinit
1970Sstevel@tonic-gate 	 * for every map entry.
1980Sstevel@tonic-gate 	 */
1990Sstevel@tonic-gate 	if ((strcmp(mntpnt, "/-") == 0) && !(nodirect_map)) {
2000Sstevel@tonic-gate 		(void) loaddirect_map(map, map, opts, stack, stkptr);
2010Sstevel@tonic-gate 		return;
2020Sstevel@tonic-gate 	}
2030Sstevel@tonic-gate 
204*1676Sjpk 	/*
205*1676Sjpk 	 * Home directories are polyinstantiated on
206*1676Sjpk 	 * labeled systems.
207*1676Sjpk 	 */
208*1676Sjpk 	if (is_system_labeled() &&
209*1676Sjpk 	    (strcmp(mntpnt, "/home") == 0) &&
210*1676Sjpk 	    (strcmp(map, "auto_home") == 0)) {
211*1676Sjpk 		(void) loadzone_maps(mntpnt, map, opts, stack, stkptr);
212*1676Sjpk 		return;
213*1676Sjpk 	}
2140Sstevel@tonic-gate enter:
2150Sstevel@tonic-gate 	dir = (struct autodir *)malloc(sizeof (*dir));
2160Sstevel@tonic-gate 	if (dir == NULL)
2170Sstevel@tonic-gate 		goto alloc_failed;
2180Sstevel@tonic-gate 	dir->dir_name = strdup(mntpnt);
2190Sstevel@tonic-gate 	if (dir->dir_name == NULL)
2200Sstevel@tonic-gate 		goto alloc_failed;
2210Sstevel@tonic-gate 	dir->dir_map = strdup(map);
2220Sstevel@tonic-gate 	if (dir->dir_map == NULL)
2230Sstevel@tonic-gate 		goto alloc_failed;
2240Sstevel@tonic-gate 	dir->dir_opts = strdup(opts);
2250Sstevel@tonic-gate 	if (dir->dir_opts == NULL)
2260Sstevel@tonic-gate 		goto alloc_failed;
2270Sstevel@tonic-gate 	dir->dir_direct = direct;
2280Sstevel@tonic-gate 	dir->dir_remount = 0;
2290Sstevel@tonic-gate 	dir->dir_next = NULL;
2300Sstevel@tonic-gate 
2310Sstevel@tonic-gate 	/*
2320Sstevel@tonic-gate 	 * Append to dir chain
2330Sstevel@tonic-gate 	 */
2340Sstevel@tonic-gate 	if (dir_head == NULL)
2350Sstevel@tonic-gate 		dir_head = dir;
2360Sstevel@tonic-gate 	else
2370Sstevel@tonic-gate 		dir_tail->dir_next = dir;
2380Sstevel@tonic-gate 
2390Sstevel@tonic-gate 	dir->dir_prev = dir_tail;
2400Sstevel@tonic-gate 	dir_tail = dir;
2410Sstevel@tonic-gate 
2420Sstevel@tonic-gate 	return;
2430Sstevel@tonic-gate 
2440Sstevel@tonic-gate alloc_failed:
2450Sstevel@tonic-gate 	if (dir != NULL) {
2460Sstevel@tonic-gate 		if (dir->dir_opts)
2470Sstevel@tonic-gate 			free(dir->dir_opts);
2480Sstevel@tonic-gate 		if (dir->dir_map)
2490Sstevel@tonic-gate 			free(dir->dir_map);
2500Sstevel@tonic-gate 		if (dir->dir_name)
2510Sstevel@tonic-gate 			free(dir->dir_name);
2520Sstevel@tonic-gate 		free(dir);
2530Sstevel@tonic-gate 	}
2540Sstevel@tonic-gate 	pr_msg("dirinit: memory allocation failed");
2550Sstevel@tonic-gate }
2560Sstevel@tonic-gate 
2570Sstevel@tonic-gate /*
2580Sstevel@tonic-gate  *  Check whether the mount point is a
2590Sstevel@tonic-gate  *  subdirectory or a parent directory
2600Sstevel@tonic-gate  *  of any previously mounted automount
2610Sstevel@tonic-gate  *  mount point.
2620Sstevel@tonic-gate  */
2630Sstevel@tonic-gate static char *
2640Sstevel@tonic-gate check_hier(mntpnt)
2650Sstevel@tonic-gate 	char *mntpnt;
2660Sstevel@tonic-gate {
2670Sstevel@tonic-gate 	register struct autodir *dir;
2680Sstevel@tonic-gate 	register char *p, *q;
2690Sstevel@tonic-gate 
2700Sstevel@tonic-gate 	for (dir = dir_head; dir; dir = dir->dir_next) {
2710Sstevel@tonic-gate 		p = dir->dir_name;
2720Sstevel@tonic-gate 		q = mntpnt;
2730Sstevel@tonic-gate 		for (; *p == *q; p++, q++)
2740Sstevel@tonic-gate 			if (*p == '\0')
2750Sstevel@tonic-gate 				break;
2760Sstevel@tonic-gate 		if (*p == '/' && *q == '\0')
2770Sstevel@tonic-gate 			return (dir->dir_name);
2780Sstevel@tonic-gate 		if (*p == '\0' && *q == '/')
2790Sstevel@tonic-gate 			return (dir->dir_name);
2800Sstevel@tonic-gate 		if (*p == '\0' && *q == '\0')
2810Sstevel@tonic-gate 			return (NULL);
2820Sstevel@tonic-gate 	}
2830Sstevel@tonic-gate 	return (NULL);	/* it's not a subdir or parent */
2840Sstevel@tonic-gate }
2850Sstevel@tonic-gate 
2860Sstevel@tonic-gate /*
2870Sstevel@tonic-gate  * Gets the next token from the string "p" and copies
2880Sstevel@tonic-gate  * it into "w".  Both "wq" and "w" are quote vectors
2890Sstevel@tonic-gate  * for "w" and "p".  Delim is the character to be used
2900Sstevel@tonic-gate  * as a delimiter for the scan.  A space means "whitespace".
2910Sstevel@tonic-gate  * The call to getword must provide buffers w and wq of size at
2920Sstevel@tonic-gate  * least wordsz. getword() will pass strings of maximum length
2930Sstevel@tonic-gate  * (wordsz-1), since it needs to null terminate the string.
2940Sstevel@tonic-gate  * Returns 0 on ok and -1 on error.
2950Sstevel@tonic-gate  */
2960Sstevel@tonic-gate int
2970Sstevel@tonic-gate getword(char *w, char *wq, char **p, char **pq, char delim, int wordsz)
2980Sstevel@tonic-gate {
2990Sstevel@tonic-gate 	char *tmp = w;
3000Sstevel@tonic-gate 	char *tmpq = wq;
3010Sstevel@tonic-gate 	int count = wordsz;
3020Sstevel@tonic-gate 
3030Sstevel@tonic-gate 	if (wordsz <= 0) {
3040Sstevel@tonic-gate 		if (verbose)
3050Sstevel@tonic-gate 			syslog(LOG_ERR,
3060Sstevel@tonic-gate 			"getword: input word size %d must be > 0", wordsz);
3070Sstevel@tonic-gate 		return (-1);
3080Sstevel@tonic-gate 	}
3090Sstevel@tonic-gate 
3100Sstevel@tonic-gate 	while ((delim == ' ' ? isspace(**p) : **p == delim) && **pq == ' ')
3110Sstevel@tonic-gate 		(*p)++, (*pq)++;
3120Sstevel@tonic-gate 
3130Sstevel@tonic-gate 	while (**p &&
3140Sstevel@tonic-gate 		!((delim == ' ' ? isspace(**p) : **p == delim) &&
3150Sstevel@tonic-gate 			**pq == ' ')) {
3160Sstevel@tonic-gate 		if (--count <= 0) {
3170Sstevel@tonic-gate 			*tmp = '\0';
3180Sstevel@tonic-gate 			*tmpq = '\0';
3190Sstevel@tonic-gate 			syslog(LOG_ERR,
3200Sstevel@tonic-gate 			"maximum word length (%d) exceeded", wordsz);
3210Sstevel@tonic-gate 			return (-1);
3220Sstevel@tonic-gate 		}
3230Sstevel@tonic-gate 		*w++  = *(*p)++;
3240Sstevel@tonic-gate 		*wq++ = *(*pq)++;
3250Sstevel@tonic-gate 	}
3260Sstevel@tonic-gate 	*w  = '\0';
3270Sstevel@tonic-gate 	*wq = '\0';
3280Sstevel@tonic-gate 
3290Sstevel@tonic-gate 	return (0);
3300Sstevel@tonic-gate }
3310Sstevel@tonic-gate 
3320Sstevel@tonic-gate /*
3330Sstevel@tonic-gate  * get_line attempts to get a line from the map, upto LINESZ. A line in
3340Sstevel@tonic-gate  * the map is a concatenation of lines if the continuation symbol '\'
3350Sstevel@tonic-gate  * is used at the end of the line. Returns line on success, a NULL on
3360Sstevel@tonic-gate  * EOF, and an empty string on lines > linesz.
3370Sstevel@tonic-gate  */
3380Sstevel@tonic-gate char *
3390Sstevel@tonic-gate get_line(FILE *fp, char *map, char *line, int linesz)
3400Sstevel@tonic-gate {
3410Sstevel@tonic-gate 	register char *p = line;
3420Sstevel@tonic-gate 	register int len;
3430Sstevel@tonic-gate 	int excess = 0;
3440Sstevel@tonic-gate 
3450Sstevel@tonic-gate 	*p = '\0';
3460Sstevel@tonic-gate 
3470Sstevel@tonic-gate 	for (;;) {
3480Sstevel@tonic-gate 		if (fgets(p, linesz - (p-line), fp) == NULL) {
3490Sstevel@tonic-gate 			return (*line ? line : NULL);	/* EOF */
3500Sstevel@tonic-gate 		}
3510Sstevel@tonic-gate 
3520Sstevel@tonic-gate 		len = strlen(line);
3530Sstevel@tonic-gate 		if (len <= 0) {
3540Sstevel@tonic-gate 			p = line;
3550Sstevel@tonic-gate 			continue;
3560Sstevel@tonic-gate 		}
3570Sstevel@tonic-gate 		p = &line[len - 1];
3580Sstevel@tonic-gate 
3590Sstevel@tonic-gate 		/*
3600Sstevel@tonic-gate 		 * Is input line too long?
3610Sstevel@tonic-gate 		 */
3620Sstevel@tonic-gate 		if (*p != '\n') {
3630Sstevel@tonic-gate 			excess = 1;
3640Sstevel@tonic-gate 			/*
3650Sstevel@tonic-gate 			 * Perhaps last char read was '\'. Reinsert it
3660Sstevel@tonic-gate 			 * into the stream to ease the parsing when we
3670Sstevel@tonic-gate 			 * read the rest of the line to discard.
3680Sstevel@tonic-gate 			 */
3690Sstevel@tonic-gate 			(void) ungetc(*p, fp);
3700Sstevel@tonic-gate 			break;
3710Sstevel@tonic-gate 		}
3720Sstevel@tonic-gate trim:
3730Sstevel@tonic-gate 		/* trim trailing white space */
3740Sstevel@tonic-gate 		while (p >= line && isspace(*(uchar_t *)p))
3750Sstevel@tonic-gate 			*p-- = '\0';
3760Sstevel@tonic-gate 		if (p < line) {			/* empty line */
3770Sstevel@tonic-gate 			p = line;
3780Sstevel@tonic-gate 			continue;
3790Sstevel@tonic-gate 		}
3800Sstevel@tonic-gate 
3810Sstevel@tonic-gate 		if (*p == '\\') {		/* continuation */
3820Sstevel@tonic-gate 			*p = '\0';
3830Sstevel@tonic-gate 			continue;
3840Sstevel@tonic-gate 		}
3850Sstevel@tonic-gate 
3860Sstevel@tonic-gate 		/*
3870Sstevel@tonic-gate 		 * Ignore comments. Comments start with '#'
3880Sstevel@tonic-gate 		 * which must be preceded by a whitespace, unless
3890Sstevel@tonic-gate 		 * if '#' is the first character in the line.
3900Sstevel@tonic-gate 		 */
3910Sstevel@tonic-gate 		p = line;
3920Sstevel@tonic-gate 		while (p = strchr(p, '#')) {
3930Sstevel@tonic-gate 			if (p == line || isspace(*(p-1))) {
3940Sstevel@tonic-gate 				*p-- = '\0';
3950Sstevel@tonic-gate 				goto trim;
3960Sstevel@tonic-gate 			}
3970Sstevel@tonic-gate 			p++;
3980Sstevel@tonic-gate 		}
3990Sstevel@tonic-gate 		break;
4000Sstevel@tonic-gate 	}
4010Sstevel@tonic-gate 	if (excess) {
4020Sstevel@tonic-gate 		int c;
4030Sstevel@tonic-gate 
4040Sstevel@tonic-gate 		/*
4050Sstevel@tonic-gate 		 * discard rest of line and return an empty string.
4060Sstevel@tonic-gate 		 * done to set the stream to the correct place when
4070Sstevel@tonic-gate 		 * we are done with this line.
4080Sstevel@tonic-gate 		 */
4090Sstevel@tonic-gate 		while ((c = getc(fp)) != EOF) {
4100Sstevel@tonic-gate 			*p = c;
4110Sstevel@tonic-gate 			if (*p == '\n')		/* end of the long line */
4120Sstevel@tonic-gate 				break;
4130Sstevel@tonic-gate 			else if (*p == '\\') {		/* continuation */
4140Sstevel@tonic-gate 				if (getc(fp) == EOF)	/* ignore next char */
4150Sstevel@tonic-gate 					break;
4160Sstevel@tonic-gate 			}
4170Sstevel@tonic-gate 		}
4180Sstevel@tonic-gate 		syslog(LOG_ERR,
4190Sstevel@tonic-gate 			"map %s: line too long (max %d chars)",
4200Sstevel@tonic-gate 			map, linesz-1);
4210Sstevel@tonic-gate 		*line = '\0';
4220Sstevel@tonic-gate 	}
4230Sstevel@tonic-gate 
4240Sstevel@tonic-gate 	return (line);
4250Sstevel@tonic-gate }
4260Sstevel@tonic-gate 
4270Sstevel@tonic-gate /*
4280Sstevel@tonic-gate  * Gets the retry=n entry from opts.
4290Sstevel@tonic-gate  * Returns 0 if retry=n is not present in option string,
4300Sstevel@tonic-gate  * retry=n is invalid, or when option string is NULL.
4310Sstevel@tonic-gate  */
4320Sstevel@tonic-gate int
4330Sstevel@tonic-gate get_retry(char *opts)
4340Sstevel@tonic-gate {
4350Sstevel@tonic-gate 	int retry = 0;
4360Sstevel@tonic-gate 	char buf[MAXOPTSLEN];
4370Sstevel@tonic-gate 	char *p, *pb, *lasts;
4380Sstevel@tonic-gate 
4390Sstevel@tonic-gate 	if (opts == NULL)
4400Sstevel@tonic-gate 		return (retry);
4410Sstevel@tonic-gate 
4420Sstevel@tonic-gate 	(void) strcpy(buf, opts);
4430Sstevel@tonic-gate 	pb = buf;
4440Sstevel@tonic-gate 	while (p = (char *)strtok_r(pb, ",", &lasts)) {
4450Sstevel@tonic-gate 		pb = NULL;
4460Sstevel@tonic-gate 		if (strncmp(p, "retry=", 6) == 0)
4470Sstevel@tonic-gate 			retry = atoi(p+6);
4480Sstevel@tonic-gate 	}
4490Sstevel@tonic-gate 	return (retry > 0 ? retry : 0);
4500Sstevel@tonic-gate }
4510Sstevel@tonic-gate 
4520Sstevel@tonic-gate /*
4530Sstevel@tonic-gate  * Returns zero if "opt" is found in mnt->mnt_opts, setting
4540Sstevel@tonic-gate  * *sval to whatever follows the equal sign after "opt".
4550Sstevel@tonic-gate  * str_opt allocates a string long enough to store the value of
4560Sstevel@tonic-gate  * "opt" plus a terminating null character and returns it as *sval.
4570Sstevel@tonic-gate  * It is the responsability of the caller to deallocate *sval.
4580Sstevel@tonic-gate  * *sval will be equal to NULL upon return if either "opt=" is not found,
4590Sstevel@tonic-gate  * or "opt=" has no value associated with it.
4600Sstevel@tonic-gate  *
4610Sstevel@tonic-gate  * stropt will return -1 on error.
4620Sstevel@tonic-gate  */
4630Sstevel@tonic-gate int
4640Sstevel@tonic-gate str_opt(struct mnttab *mnt, char *opt, char **sval)
4650Sstevel@tonic-gate {
4660Sstevel@tonic-gate 	char *str, *comma;
4670Sstevel@tonic-gate 
4680Sstevel@tonic-gate 	/*
4690Sstevel@tonic-gate 	 * is "opt" in the options field?
4700Sstevel@tonic-gate 	 */
4710Sstevel@tonic-gate 	if (str = hasmntopt(mnt, opt)) {
4720Sstevel@tonic-gate 		str += strlen(opt);
4730Sstevel@tonic-gate 		if (*str++ != '=' ||
4740Sstevel@tonic-gate 		    (*str == ',' || *str == '\0')) {
4750Sstevel@tonic-gate 			syslog(LOG_ERR, "Bad option field");
4760Sstevel@tonic-gate 			return (-1);
4770Sstevel@tonic-gate 		}
4780Sstevel@tonic-gate 		comma = strchr(str, ',');
4790Sstevel@tonic-gate 		if (comma != NULL)
4800Sstevel@tonic-gate 			*comma = '\0';
4810Sstevel@tonic-gate 		*sval = strdup(str);
4820Sstevel@tonic-gate 		if (comma != NULL)
4830Sstevel@tonic-gate 			*comma = ',';
4840Sstevel@tonic-gate 		if (*sval == NULL)
4850Sstevel@tonic-gate 			return (-1);
4860Sstevel@tonic-gate 	} else
4870Sstevel@tonic-gate 		*sval = NULL;
4880Sstevel@tonic-gate 
4890Sstevel@tonic-gate 	return (0);
4900Sstevel@tonic-gate }
4910Sstevel@tonic-gate 
4920Sstevel@tonic-gate /*
4930Sstevel@tonic-gate  * Performs text expansions in the string "pline".
4940Sstevel@tonic-gate  * "plineq" is the quote vector for "pline".
4950Sstevel@tonic-gate  * An identifier prefixed by "$" is replaced by the
4960Sstevel@tonic-gate  * corresponding environment variable string.  A "&"
4970Sstevel@tonic-gate  * is replaced by the key string for the map entry.
4980Sstevel@tonic-gate  *
4990Sstevel@tonic-gate  * This routine will return an error (non-zero) if *size* would be
5000Sstevel@tonic-gate  * exceeded after expansion, indicating that the macro_expand failed.
5010Sstevel@tonic-gate  * This is to prevent writing past the end of pline and plineq.
5020Sstevel@tonic-gate  * Both pline and plineq are left untouched in such error case.
5030Sstevel@tonic-gate  */
5040Sstevel@tonic-gate int
5050Sstevel@tonic-gate macro_expand(key, pline, plineq, size)
5060Sstevel@tonic-gate 	char *key, *pline, *plineq;
5070Sstevel@tonic-gate 	int size;
5080Sstevel@tonic-gate {
5090Sstevel@tonic-gate 	register char *p,  *q;
5100Sstevel@tonic-gate 	register char *bp, *bq;
5110Sstevel@tonic-gate 	register char *s;
5120Sstevel@tonic-gate 	char buffp[LINESZ], buffq[LINESZ];
5130Sstevel@tonic-gate 	char namebuf[64], *pn;
5140Sstevel@tonic-gate 	int expand = 0;
5150Sstevel@tonic-gate 	struct utsname name;
5160Sstevel@tonic-gate 	char isaname[64];
5170Sstevel@tonic-gate 
5180Sstevel@tonic-gate 	p = pline;  q = plineq;
5190Sstevel@tonic-gate 	bp = buffp; bq = buffq;
5200Sstevel@tonic-gate 
5210Sstevel@tonic-gate 	while (*p) {
5220Sstevel@tonic-gate 		if (*p == '&' && *q == ' ') {	/* insert key */
5230Sstevel@tonic-gate 			/*
5240Sstevel@tonic-gate 			 * make sure we don't overflow buffer
5250Sstevel@tonic-gate 			 */
5260Sstevel@tonic-gate 			if ((int)((bp - buffp) + strlen(key)) < size) {
5270Sstevel@tonic-gate 				for (s = key; *s; s++) {
5280Sstevel@tonic-gate 					*bp++ = *s;
5290Sstevel@tonic-gate 					*bq++ = ' ';
5300Sstevel@tonic-gate 				}
5310Sstevel@tonic-gate 				expand++;
5320Sstevel@tonic-gate 				p++; q++;
5330Sstevel@tonic-gate 				continue;
5340Sstevel@tonic-gate 			} else {
5350Sstevel@tonic-gate 				/*
5360Sstevel@tonic-gate 				 * line too long...
5370Sstevel@tonic-gate 				 */
5380Sstevel@tonic-gate 				return (1);
5390Sstevel@tonic-gate 			}
5400Sstevel@tonic-gate 		}
5410Sstevel@tonic-gate 
5420Sstevel@tonic-gate 		if (*p == '$' && *q == ' ') {	/* insert env var */
5430Sstevel@tonic-gate 			p++; q++;
5440Sstevel@tonic-gate 			pn = namebuf;
5450Sstevel@tonic-gate 			if (*p == '{') {
5460Sstevel@tonic-gate 				p++; q++;
5470Sstevel@tonic-gate 				while (*p && *p != '}') {
5480Sstevel@tonic-gate 					*pn++ = *p++;
5490Sstevel@tonic-gate 					q++;
5500Sstevel@tonic-gate 				}
5510Sstevel@tonic-gate 				if (*p) {
5520Sstevel@tonic-gate 					p++; q++;
5530Sstevel@tonic-gate 				}
5540Sstevel@tonic-gate 			} else {
5550Sstevel@tonic-gate 				while (*p && (*p == '_' || isalnum(*p))) {
5560Sstevel@tonic-gate 					*pn++ = *p++;
5570Sstevel@tonic-gate 					q++;
5580Sstevel@tonic-gate 				}
5590Sstevel@tonic-gate 			}
5600Sstevel@tonic-gate 			*pn = '\0';
5610Sstevel@tonic-gate 
5620Sstevel@tonic-gate 			s = getenv(namebuf);
5630Sstevel@tonic-gate 			if (!s) {
5640Sstevel@tonic-gate 				/* not found in env */
5650Sstevel@tonic-gate 				if (strcmp(namebuf, "HOST") == 0) {
5660Sstevel@tonic-gate 					(void) uname(&name);
5670Sstevel@tonic-gate 					s = name.nodename;
5680Sstevel@tonic-gate 				} else if (strcmp(namebuf, "OSREL") == 0) {
5690Sstevel@tonic-gate 					(void) uname(&name);
5700Sstevel@tonic-gate 					s = name.release;
5710Sstevel@tonic-gate 				} else if (strcmp(namebuf, "OSNAME") == 0) {
5720Sstevel@tonic-gate 					(void) uname(&name);
5730Sstevel@tonic-gate 					s = name.sysname;
5740Sstevel@tonic-gate 				} else if (strcmp(namebuf, "OSVERS") == 0) {
5750Sstevel@tonic-gate 					(void) uname(&name);
5760Sstevel@tonic-gate 					s = name.version;
5770Sstevel@tonic-gate 				} else if (strcmp(namebuf, "NATISA") == 0) {
5780Sstevel@tonic-gate 					if (natisa(isaname, sizeof (isaname)))
5790Sstevel@tonic-gate 						s = isaname;
5800Sstevel@tonic-gate 				}
5810Sstevel@tonic-gate 			}
5820Sstevel@tonic-gate 
5830Sstevel@tonic-gate 			if (s) {
5840Sstevel@tonic-gate 				if ((int)((bp - buffp) + strlen(s)) < size) {
5850Sstevel@tonic-gate 					while (*s) {
5860Sstevel@tonic-gate 						*bp++ = *s++;
5870Sstevel@tonic-gate 						*bq++ = ' ';
5880Sstevel@tonic-gate 					}
5890Sstevel@tonic-gate 				} else {
5900Sstevel@tonic-gate 					/*
5910Sstevel@tonic-gate 					 * line too long...
5920Sstevel@tonic-gate 					 */
5930Sstevel@tonic-gate 					return (1);
5940Sstevel@tonic-gate 				}
5950Sstevel@tonic-gate 			}
5960Sstevel@tonic-gate 			expand++;
5970Sstevel@tonic-gate 			continue;
5980Sstevel@tonic-gate 		}
5990Sstevel@tonic-gate 		/*
6000Sstevel@tonic-gate 		 * Since buffp needs to be null terminated, we need to
6010Sstevel@tonic-gate 		 * check that there's still room in the buffer to
6020Sstevel@tonic-gate 		 * place at least two more characters, *p and the
6030Sstevel@tonic-gate 		 * terminating null.
6040Sstevel@tonic-gate 		 */
6050Sstevel@tonic-gate 		if (bp - buffp == size - 1) {
6060Sstevel@tonic-gate 			/*
6070Sstevel@tonic-gate 			 * There was not enough room for at least two more
6080Sstevel@tonic-gate 			 * characters, return with an error.
6090Sstevel@tonic-gate 			 */
6100Sstevel@tonic-gate 			return (1);
6110Sstevel@tonic-gate 		}
6120Sstevel@tonic-gate 		/*
6130Sstevel@tonic-gate 		 * The total number of characters so far better be less
6140Sstevel@tonic-gate 		 * than the size of buffer passed in.
6150Sstevel@tonic-gate 		 */
6160Sstevel@tonic-gate 		*bp++ = *p++;
6170Sstevel@tonic-gate 		*bq++ = *q++;
6180Sstevel@tonic-gate 
6190Sstevel@tonic-gate 	}
6200Sstevel@tonic-gate 	if (!expand)
6210Sstevel@tonic-gate 		return (0);
6220Sstevel@tonic-gate 	*bp = '\0';
6230Sstevel@tonic-gate 	*bq = '\0';
6240Sstevel@tonic-gate 	/*
6250Sstevel@tonic-gate 	 * We know buffp/buffq will fit in pline/plineq since we
6260Sstevel@tonic-gate 	 * processed at most size characters.
6270Sstevel@tonic-gate 	 */
6280Sstevel@tonic-gate 	(void) strcpy(pline, buffp);
6290Sstevel@tonic-gate 	(void) strcpy(plineq, buffq);
6300Sstevel@tonic-gate 
6310Sstevel@tonic-gate 	return (0);
6320Sstevel@tonic-gate }
6330Sstevel@tonic-gate 
6340Sstevel@tonic-gate /*
6350Sstevel@tonic-gate  * Removes quotes from the string "str" and returns
6360Sstevel@tonic-gate  * the quoting information in "qbuf". e.g.
6370Sstevel@tonic-gate  * original str: 'the "quick brown" f\ox'
6380Sstevel@tonic-gate  * unquoted str: 'the quick brown fox'
6390Sstevel@tonic-gate  * and the qbuf: '    ^^^^^^^^^^^  ^ '
6400Sstevel@tonic-gate  */
6410Sstevel@tonic-gate void
6420Sstevel@tonic-gate unquote(str, qbuf)
6430Sstevel@tonic-gate 	char *str, *qbuf;
6440Sstevel@tonic-gate {
6450Sstevel@tonic-gate 	register int escaped, inquote, quoted;
6460Sstevel@tonic-gate 	register char *ip, *bp, *qp;
6470Sstevel@tonic-gate 	char buf[LINESZ];
6480Sstevel@tonic-gate 
6490Sstevel@tonic-gate 	escaped = inquote = quoted = 0;
6500Sstevel@tonic-gate 
6510Sstevel@tonic-gate 	for (ip = str, bp = buf, qp = qbuf; *ip; ip++) {
6520Sstevel@tonic-gate 		if (!escaped) {
6530Sstevel@tonic-gate 			if (*ip == '\\') {
6540Sstevel@tonic-gate 				escaped = 1;
6550Sstevel@tonic-gate 				quoted++;
6560Sstevel@tonic-gate 				continue;
6570Sstevel@tonic-gate 			} else
6580Sstevel@tonic-gate 			if (*ip == '"') {
6590Sstevel@tonic-gate 				inquote = !inquote;
6600Sstevel@tonic-gate 				quoted++;
6610Sstevel@tonic-gate 				continue;
6620Sstevel@tonic-gate 			}
6630Sstevel@tonic-gate 		}
6640Sstevel@tonic-gate 
6650Sstevel@tonic-gate 		*bp++ = *ip;
6660Sstevel@tonic-gate 		*qp++ = (inquote || escaped) ? '^' : ' ';
6670Sstevel@tonic-gate 		escaped = 0;
6680Sstevel@tonic-gate 	}
6690Sstevel@tonic-gate 	*bp = '\0';
6700Sstevel@tonic-gate 	*qp = '\0';
6710Sstevel@tonic-gate 	if (quoted)
6720Sstevel@tonic-gate 		(void) strcpy(str, buf);
6730Sstevel@tonic-gate }
6740Sstevel@tonic-gate 
6750Sstevel@tonic-gate /*
6760Sstevel@tonic-gate  * Removes trailing spaces from string "s".
6770Sstevel@tonic-gate  */
6780Sstevel@tonic-gate void
6790Sstevel@tonic-gate trim(s)
6800Sstevel@tonic-gate 	char *s;
6810Sstevel@tonic-gate {
6820Sstevel@tonic-gate 	char *p = &s[strlen(s) - 1];
6830Sstevel@tonic-gate 
6840Sstevel@tonic-gate 	while (p >= s && isspace(*(uchar_t *)p))
6850Sstevel@tonic-gate 		*p-- = '\0';
6860Sstevel@tonic-gate }
6870Sstevel@tonic-gate 
6880Sstevel@tonic-gate /*
6890Sstevel@tonic-gate  * try to allocate memory using malloc, if malloc fails, then flush the
6900Sstevel@tonic-gate  * rddir caches, and retry. If the second allocation after the readdir
6910Sstevel@tonic-gate  * caches have been flushed fails too, then return NULL to indicate
6920Sstevel@tonic-gate  * memory could not be allocated.
6930Sstevel@tonic-gate  */
6940Sstevel@tonic-gate char *
6950Sstevel@tonic-gate auto_rddir_malloc(unsigned nbytes)
6960Sstevel@tonic-gate {
6970Sstevel@tonic-gate 	char *p;
6980Sstevel@tonic-gate 	int again = 0;
6990Sstevel@tonic-gate 
7000Sstevel@tonic-gate 	if ((p = malloc(nbytes)) == NULL) {
7010Sstevel@tonic-gate 		/*
7020Sstevel@tonic-gate 		 * No memory, free rddir caches and try again
7030Sstevel@tonic-gate 		 */
7040Sstevel@tonic-gate 		mutex_lock(&cleanup_lock);
7050Sstevel@tonic-gate 		cond_signal(&cleanup_start_cv);
7060Sstevel@tonic-gate 		if (cond_wait(&cleanup_done_cv, &cleanup_lock)) {
7070Sstevel@tonic-gate 			mutex_unlock(&cleanup_lock);
7080Sstevel@tonic-gate 			syslog(LOG_ERR, "auto_rddir_malloc interrupted\n");
7090Sstevel@tonic-gate 		} else {
7100Sstevel@tonic-gate 			mutex_unlock(&cleanup_lock);
7110Sstevel@tonic-gate 			again = 1;
7120Sstevel@tonic-gate 		}
7130Sstevel@tonic-gate 	}
7140Sstevel@tonic-gate 
7150Sstevel@tonic-gate 	if (again)
7160Sstevel@tonic-gate 		p = malloc(nbytes);
7170Sstevel@tonic-gate 
7180Sstevel@tonic-gate 	return (p);
7190Sstevel@tonic-gate }
7200Sstevel@tonic-gate 
7210Sstevel@tonic-gate /*
7220Sstevel@tonic-gate  * try to strdup a string, if it fails, then flush the rddir caches,
7230Sstevel@tonic-gate  * and retry. If the second strdup fails, return NULL to indicate failure.
7240Sstevel@tonic-gate  */
7250Sstevel@tonic-gate char *
7260Sstevel@tonic-gate auto_rddir_strdup(const char *s1)
7270Sstevel@tonic-gate {
7280Sstevel@tonic-gate 	char *s2;
7290Sstevel@tonic-gate 	int again = 0;
7300Sstevel@tonic-gate 
7310Sstevel@tonic-gate 	if ((s2 = strdup(s1)) == NULL) {
7320Sstevel@tonic-gate 		/*
7330Sstevel@tonic-gate 		 * No memory, free rddir caches and try again
7340Sstevel@tonic-gate 		 */
7350Sstevel@tonic-gate 		mutex_lock(&cleanup_lock);
7360Sstevel@tonic-gate 		cond_signal(&cleanup_start_cv);
7370Sstevel@tonic-gate 		if (cond_wait(&cleanup_done_cv, &cleanup_lock)) {
7380Sstevel@tonic-gate 			mutex_unlock(&cleanup_lock);
7390Sstevel@tonic-gate 			syslog(LOG_ERR, "auto_rddir_strdup interrupted\n");
7400Sstevel@tonic-gate 		} else {
7410Sstevel@tonic-gate 			mutex_unlock(&cleanup_lock);
7420Sstevel@tonic-gate 			again = 1;
7430Sstevel@tonic-gate 		}
7440Sstevel@tonic-gate 	}
7450Sstevel@tonic-gate 
7460Sstevel@tonic-gate 	if (again)
7470Sstevel@tonic-gate 		s2 = strdup(s1);
7480Sstevel@tonic-gate 
7490Sstevel@tonic-gate 	return (s2);
7500Sstevel@tonic-gate }
7510Sstevel@tonic-gate 
7520Sstevel@tonic-gate /*
7530Sstevel@tonic-gate  * Returns a pointer to the entry corresponding to 'name' if found,
7540Sstevel@tonic-gate  * otherwise it returns NULL.
7550Sstevel@tonic-gate  */
7560Sstevel@tonic-gate struct dir_entry *
7570Sstevel@tonic-gate btree_lookup(struct dir_entry *head, char *name)
7580Sstevel@tonic-gate {
7590Sstevel@tonic-gate 	register struct dir_entry *p;
7600Sstevel@tonic-gate 	register int direction;
7610Sstevel@tonic-gate 
7620Sstevel@tonic-gate 	for (p = head; p != NULL; ) {
7630Sstevel@tonic-gate 		direction = strcmp(name, p->name);
7640Sstevel@tonic-gate 		if (direction == 0)
7650Sstevel@tonic-gate 			return (p);
7660Sstevel@tonic-gate 		if (direction > 0)
7670Sstevel@tonic-gate 			p = p->right;
7680Sstevel@tonic-gate 		else p = p->left;
7690Sstevel@tonic-gate 	}
7700Sstevel@tonic-gate 	return (NULL);
7710Sstevel@tonic-gate }
7720Sstevel@tonic-gate 
7730Sstevel@tonic-gate /*
7740Sstevel@tonic-gate  * Add entry to binary tree
7750Sstevel@tonic-gate  * Duplicate entries are not added
7760Sstevel@tonic-gate  */
7770Sstevel@tonic-gate void
7780Sstevel@tonic-gate btree_enter(struct dir_entry **head, struct dir_entry *ent)
7790Sstevel@tonic-gate {
7800Sstevel@tonic-gate 	register struct dir_entry *p, *prev = NULL;
7810Sstevel@tonic-gate 	register int direction;
7820Sstevel@tonic-gate 
7830Sstevel@tonic-gate 	ent->right = ent->left = NULL;
7840Sstevel@tonic-gate 	if (*head == NULL) {
7850Sstevel@tonic-gate 		*head = ent;
7860Sstevel@tonic-gate 		return;
7870Sstevel@tonic-gate 	}
7880Sstevel@tonic-gate 
7890Sstevel@tonic-gate 	for (p = *head; p != NULL; ) {
7900Sstevel@tonic-gate 		prev = p;
7910Sstevel@tonic-gate 		direction = strcmp(ent->name, p->name);
7920Sstevel@tonic-gate 		if (direction == 0) {
7930Sstevel@tonic-gate 			/*
7940Sstevel@tonic-gate 			 * entry already in btree
7950Sstevel@tonic-gate 			 */
7960Sstevel@tonic-gate 			return;
7970Sstevel@tonic-gate 		}
7980Sstevel@tonic-gate 		if (direction > 0)
7990Sstevel@tonic-gate 			p = p->right;
8000Sstevel@tonic-gate 		else p = p->left;
8010Sstevel@tonic-gate 	}
8020Sstevel@tonic-gate 	assert(prev != NULL);
8030Sstevel@tonic-gate 	if (direction > 0)
8040Sstevel@tonic-gate 		prev->right = ent;
8050Sstevel@tonic-gate 	else prev->left = ent;
8060Sstevel@tonic-gate }
8070Sstevel@tonic-gate 
8080Sstevel@tonic-gate /*
8090Sstevel@tonic-gate  * If entry doesn't exist already, add it to the linear list
8100Sstevel@tonic-gate  * after '*last' and to the binary tree list.
8110Sstevel@tonic-gate  * If '*last == NULL' then the list is walked till the end.
8120Sstevel@tonic-gate  * *last is always set to the new element after successful completion.
8130Sstevel@tonic-gate  * if entry already exists '*last' is only updated if not previously
8140Sstevel@tonic-gate  * provided.
8150Sstevel@tonic-gate  */
8160Sstevel@tonic-gate int
8170Sstevel@tonic-gate add_dir_entry(char *name, struct dir_entry **list, struct dir_entry **last)
8180Sstevel@tonic-gate {
8190Sstevel@tonic-gate 	struct dir_entry *e, *l;
8200Sstevel@tonic-gate 
8210Sstevel@tonic-gate 	if ((*list != NULL) && (*last == NULL)) {
8220Sstevel@tonic-gate 		/*
8230Sstevel@tonic-gate 		 * walk the list to find last element
8240Sstevel@tonic-gate 		 */
8250Sstevel@tonic-gate 		for (l = *list; l != NULL; l = l->next)
8260Sstevel@tonic-gate 			*last = l;
8270Sstevel@tonic-gate 	}
8280Sstevel@tonic-gate 
8290Sstevel@tonic-gate 	if (btree_lookup(*list, name) == NULL) {
8300Sstevel@tonic-gate 		/*
8310Sstevel@tonic-gate 		 * not a duplicate, add it to list
8320Sstevel@tonic-gate 		 */
8330Sstevel@tonic-gate 		/* LINTED pointer alignment */
8340Sstevel@tonic-gate 		e = (struct dir_entry *)
8350Sstevel@tonic-gate 			auto_rddir_malloc(sizeof (struct dir_entry));
8360Sstevel@tonic-gate 		if (e == NULL)
8370Sstevel@tonic-gate 			return (ENOMEM);
8380Sstevel@tonic-gate 		(void) memset((char *)e, 0, sizeof (*e));
8390Sstevel@tonic-gate 		e->name = auto_rddir_strdup(name);
8400Sstevel@tonic-gate 		if (e->name == NULL) {
8410Sstevel@tonic-gate 			free(e);
8420Sstevel@tonic-gate 			return (ENOMEM);
8430Sstevel@tonic-gate 		}
8440Sstevel@tonic-gate 		e->next = NULL;
8450Sstevel@tonic-gate 		if (*list == NULL) {
8460Sstevel@tonic-gate 			/*
8470Sstevel@tonic-gate 			 * list is empty
8480Sstevel@tonic-gate 			 */
8490Sstevel@tonic-gate 			*list = *last = e;
8500Sstevel@tonic-gate 		} else {
8510Sstevel@tonic-gate 			/*
8520Sstevel@tonic-gate 			 * append to end of list
8530Sstevel@tonic-gate 			 */
8540Sstevel@tonic-gate 			assert(*last != NULL);
8550Sstevel@tonic-gate 			(*last)->next = e;
8560Sstevel@tonic-gate 			*last = e;
8570Sstevel@tonic-gate 		}
8580Sstevel@tonic-gate 		/*
8590Sstevel@tonic-gate 		 * add to binary tree
8600Sstevel@tonic-gate 		 */
8610Sstevel@tonic-gate 		btree_enter(list, e);
8620Sstevel@tonic-gate 	}
8630Sstevel@tonic-gate 	return (0);
8640Sstevel@tonic-gate }
8650Sstevel@tonic-gate 
8660Sstevel@tonic-gate /*
8670Sstevel@tonic-gate  * Print trace output.
8680Sstevel@tonic-gate  * Like fprintf(stderr, fmt, ...) except that if "id" is nonzero, the output
8690Sstevel@tonic-gate  * is preceeded by the ID of the calling thread.
8700Sstevel@tonic-gate  */
8710Sstevel@tonic-gate #define	FMT_BUFSIZ 1024
8720Sstevel@tonic-gate 
8730Sstevel@tonic-gate void
8740Sstevel@tonic-gate trace_prt(int id, char *fmt, ...)
8750Sstevel@tonic-gate {
8760Sstevel@tonic-gate 	va_list args;
8770Sstevel@tonic-gate 
8780Sstevel@tonic-gate 	char buf[FMT_BUFSIZ];
8790Sstevel@tonic-gate 
8800Sstevel@tonic-gate 	if (id) {
8810Sstevel@tonic-gate 		(void) sprintf(buf, "t%u\t%s", thr_self(), fmt);
8820Sstevel@tonic-gate 		fmt = buf;
8830Sstevel@tonic-gate 	}
8840Sstevel@tonic-gate 	va_start(args, fmt);
8850Sstevel@tonic-gate 	(void) vfprintf(stderr, fmt, args);
8860Sstevel@tonic-gate 	va_end(args);
8870Sstevel@tonic-gate }
8880Sstevel@tonic-gate 
8890Sstevel@tonic-gate /*
8900Sstevel@tonic-gate  * Extract the isalist(5) for userland from the kernel.
8910Sstevel@tonic-gate  */
8920Sstevel@tonic-gate static char *
8930Sstevel@tonic-gate isalist(void)
8940Sstevel@tonic-gate {
8950Sstevel@tonic-gate 	char *buf;
8960Sstevel@tonic-gate 	size_t bufsize = BUFSIZ;	/* wild guess */
8970Sstevel@tonic-gate 	long ret;
8980Sstevel@tonic-gate 
8990Sstevel@tonic-gate 	buf = malloc(bufsize);
9000Sstevel@tonic-gate 	do {
9010Sstevel@tonic-gate 		ret = sysinfo(SI_ISALIST, buf, bufsize);
9020Sstevel@tonic-gate 		if (ret == -1l)
9030Sstevel@tonic-gate 			return (NULL);
9040Sstevel@tonic-gate 		if (ret > bufsize) {
9050Sstevel@tonic-gate 			bufsize = ret;
9060Sstevel@tonic-gate 			buf = realloc(buf, bufsize);
9070Sstevel@tonic-gate 		} else
9080Sstevel@tonic-gate 			break;
9090Sstevel@tonic-gate 	} while (buf != NULL);
9100Sstevel@tonic-gate 
9110Sstevel@tonic-gate 	return (buf);
9120Sstevel@tonic-gate }
9130Sstevel@tonic-gate 
9140Sstevel@tonic-gate /*
9150Sstevel@tonic-gate  * Classify isa's as to bitness of the corresponding ABIs.
9160Sstevel@tonic-gate  * isa's which have no "official" system ABI are returned
9170Sstevel@tonic-gate  * unrecognised i.e. zero bits.
9180Sstevel@tonic-gate  */
9190Sstevel@tonic-gate static int
9200Sstevel@tonic-gate bitness(char *isaname)
9210Sstevel@tonic-gate {
9220Sstevel@tonic-gate 	if (strcmp(isaname, "sparc") == 0 ||
9230Sstevel@tonic-gate 	    strcmp(isaname, "i386") == 0)
9240Sstevel@tonic-gate 		return (32);
9250Sstevel@tonic-gate 
9260Sstevel@tonic-gate 	if (strcmp(isaname, "sparcv9") == 0)
9270Sstevel@tonic-gate 		return (64);
9280Sstevel@tonic-gate 
9290Sstevel@tonic-gate 	return (0);
9300Sstevel@tonic-gate }
9310Sstevel@tonic-gate 
9320Sstevel@tonic-gate /*
9330Sstevel@tonic-gate  * Find the left-most element in the isalist that matches our idea of a
9340Sstevel@tonic-gate  * system ABI.
9350Sstevel@tonic-gate  *
9360Sstevel@tonic-gate  * On machines with only one ABI, this is usually the same as uname -p.
9370Sstevel@tonic-gate  */
9380Sstevel@tonic-gate static int
9390Sstevel@tonic-gate natisa(char *buf, size_t bufsize)
9400Sstevel@tonic-gate {
9410Sstevel@tonic-gate 	int bits;
9420Sstevel@tonic-gate 	char *isa, *list;
9430Sstevel@tonic-gate 	char *lasts;
9440Sstevel@tonic-gate 
9450Sstevel@tonic-gate 	if ((list = isalist()) == NULL)
9460Sstevel@tonic-gate 		return (0);
9470Sstevel@tonic-gate 
9480Sstevel@tonic-gate 	for (isa = strtok_r(list, " ", &lasts);
9490Sstevel@tonic-gate 	    isa; isa = strtok_r(0, " ", &lasts))
9500Sstevel@tonic-gate 		if ((bits = bitness(isa)) != 0)
9510Sstevel@tonic-gate 			break;	/* ignore "extension" architectures */
9520Sstevel@tonic-gate 
9530Sstevel@tonic-gate 	if (isa == 0 || bits == 0) {
9540Sstevel@tonic-gate 		free(list);
9550Sstevel@tonic-gate 		return (0);	/* can't figure it out :( */
9560Sstevel@tonic-gate 	}
9570Sstevel@tonic-gate 
9580Sstevel@tonic-gate 	(void) strncpy(buf, isa, bufsize);
9590Sstevel@tonic-gate 	free(list);
9600Sstevel@tonic-gate 
9610Sstevel@tonic-gate 	return (1);
9620Sstevel@tonic-gate }
963