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 (c) 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25
26#include <ctype.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <unistd.h>
30#include <locale.h>
31#include <syslog.h>
32#include <errno.h>
33#include <string.h>
34#include <stdarg.h>
35#include <dirent.h>
36#include <limits.h>
37#include <thread.h>
38#include <sys/param.h>
39#include <sys/time.h>
40#include <sys/vfs.h>
41#include <sys/types.h>
42#include <sys/stat.h>
43#include <sys/mnttab.h>
44#include <sys/mntent.h>
45#include <sys/mount.h>
46#include <sys/signal.h>
47#include <sys/utsname.h>
48#include <sys/systeminfo.h>
49#include <sys/tiuser.h>
50#include <sys/utsname.h>
51#include <rpc/rpc.h>
52#include <rpcsvc/nfs_prot.h>
53#include <rpcsvc/daemon_utils.h>
54#include <assert.h>
55#include "automount.h"
56#include <deflt.h>
57#include <zone.h>
58#include <priv.h>
59#include <fcntl.h>
60#include <libshare.h>
61#include <libscf.h>
62#include "smfcfg.h"
63
64static char *check_hier(char *);
65static int arch(char *, size_t, bool_t);
66static int cpu(char *, size_t);
67static int natisa(char *, size_t);
68static int platform(char *, size_t);
69
70struct mntlist *current_mounts;
71
72static bool_t nodirect_map = FALSE;
73
74/*
75 * If the system is labeled then we need to
76 * have a uniquely-named auto_home map for each zone.
77 * The maps are made unique by appending the zonename.
78 * The home directory is made unique by prepending /zone/<zonename>
79 * for each zone that is dominated by the current zone.
80 * The current zone's home directory mount point is not changed.
81 *
82 * For each auto_home_<zonename> a default template map is created
83 * only if it doesn't exist yet. The default entry is used to declare
84 * local home directories created within each zone. For example:
85 *
86 *	+auto_home_public
87 *      * -fstype=lofs :/zone/public/export/home/&
88 */
89static void
90loadzone_maps(char *mntpnt, char *map, char *opts, char **stack, char ***stkptr)
91{
92	zoneid_t *zids = NULL;
93	zoneid_t my_zoneid;
94	uint_t nzents_saved;
95	uint_t nzents;
96	int i;
97
98	if (!priv_ineffect(PRIV_SYS_MOUNT))
99		return;
100
101	if (zone_list(NULL, &nzents) != 0) {
102		return;
103	}
104	my_zoneid = getzoneid();
105again:
106	if (nzents == 0)
107		return;
108
109	zids = malloc(nzents * sizeof (zoneid_t));
110	nzents_saved = nzents;
111
112	if (zone_list(zids, &nzents) != 0) {
113		free(zids);
114		return;
115	}
116	if (nzents != nzents_saved) {
117		/* list changed, try again */
118		free(zids);
119		goto again;
120	}
121
122	for (i = 0; i < nzents; i++) {
123		char zonename[ZONENAME_MAX];
124		char zoneroot[MAXPATHLEN];
125
126		if (getzonenamebyid(zids[i], zonename, ZONENAME_MAX) != -1) {
127			char appended_map[MAXPATHLEN];
128			char prepended_mntpnt[MAXPATHLEN];
129			char map_path[MAXPATHLEN];
130			int fd;
131
132			(void) snprintf(appended_map, sizeof (appended_map),
133			    "%s_%s", map, zonename);
134
135			/* for current zone, leave mntpnt alone */
136			if (zids[i] != my_zoneid) {
137				(void) snprintf(prepended_mntpnt,
138				    sizeof (prepended_mntpnt),
139				    "/zone/%s%s", zonename, mntpnt);
140				if (zone_getattr(zids[i], ZONE_ATTR_ROOT,
141				    zoneroot, sizeof (zoneroot)) == -1)
142					continue;
143			} else {
144				(void) strcpy(prepended_mntpnt, mntpnt);
145				zoneroot[0] = '\0';
146			}
147
148			dirinit(prepended_mntpnt, appended_map, opts, 0, stack,
149			    stkptr);
150			/*
151			 * Next create auto_home_<zone> maps for each zone
152			 */
153
154			(void) snprintf(map_path, sizeof (map_path),
155			    "/etc/%s", appended_map);
156			/*
157			 * If the map file doesn't exist create a template
158			 */
159			if ((fd = open(map_path, O_RDWR | O_CREAT | O_EXCL,
160			    S_IRUSR | S_IWUSR | S_IRGRP| S_IROTH)) != -1) {
161				int len;
162				char map_rec[MAXPATHLEN];
163
164				len = snprintf(map_rec, sizeof (map_rec),
165				    "+%s\n*\t-fstype=lofs\t:%s/export/home/&\n",
166				    appended_map, zoneroot);
167				if (len <= sizeof (map_rec))
168					(void) write(fd, map_rec, len);
169				(void) close(fd);
170			}
171		}
172	}
173	free(zids);
174}
175
176void
177dirinit(char *mntpnt, char *map, char *opts, int direct, char **stack,
178    char ***stkptr)
179{
180	struct autodir *dir;
181	char *p;
182
183	if (strcmp(map, "-null") == 0) {
184		if (strcmp(mntpnt, "/-") == 0)
185			nodirect_map = TRUE;
186		goto enter;
187	}
188
189	p = mntpnt + (strlen(mntpnt) - 1);
190	if (*p == '/')
191		*p = '\0';	/* trim trailing / */
192	if (*mntpnt != '/') {
193		pr_msg("dir %s must start with '/'", mntpnt);
194		return;
195	}
196	if (p = check_hier(mntpnt)) {
197		pr_msg("hierarchical mountpoint: %s and %s",
198		    p, mntpnt);
199		return;
200	}
201
202	/*
203	 * If it's a direct map then call dirinit
204	 * for every map entry.
205	 */
206	if ((strcmp(mntpnt, "/-") == 0) && !(nodirect_map)) {
207		(void) loaddirect_map(map, map, opts, stack, stkptr);
208		return;
209	}
210
211	/*
212	 * Home directories are polyinstantiated on
213	 * labeled systems.
214	 */
215	if (is_system_labeled() &&
216	    (strcmp(mntpnt, "/home") == 0) &&
217	    (strcmp(map, "auto_home") == 0)) {
218		(void) loadzone_maps(mntpnt, map, opts, stack, stkptr);
219		return;
220	}
221enter:
222	dir = (struct autodir *)malloc(sizeof (*dir));
223	if (dir == NULL)
224		goto alloc_failed;
225	dir->dir_name = strdup(mntpnt);
226	if (dir->dir_name == NULL)
227		goto alloc_failed;
228	dir->dir_map = strdup(map);
229	if (dir->dir_map == NULL)
230		goto alloc_failed;
231	dir->dir_opts = strdup(opts);
232	if (dir->dir_opts == NULL)
233		goto alloc_failed;
234	dir->dir_direct = direct;
235	dir->dir_remount = 0;
236	dir->dir_next = NULL;
237
238	/*
239	 * Append to dir chain
240	 */
241	if (dir_head == NULL)
242		dir_head = dir;
243	else
244		dir_tail->dir_next = dir;
245
246	dir->dir_prev = dir_tail;
247	dir_tail = dir;
248
249	return;
250
251alloc_failed:
252	if (dir != NULL) {
253		if (dir->dir_opts)
254			free(dir->dir_opts);
255		if (dir->dir_map)
256			free(dir->dir_map);
257		if (dir->dir_name)
258			free(dir->dir_name);
259		free(dir);
260	}
261	pr_msg("dirinit: memory allocation failed");
262}
263
264/*
265 *  Check whether the mount point is a
266 *  subdirectory or a parent directory
267 *  of any previously mounted automount
268 *  mount point.
269 */
270static char *
271check_hier(mntpnt)
272	char *mntpnt;
273{
274	register struct autodir *dir;
275	register char *p, *q;
276
277	for (dir = dir_head; dir; dir = dir->dir_next) {
278		p = dir->dir_name;
279		q = mntpnt;
280		for (; *p == *q; p++, q++)
281			if (*p == '\0')
282				break;
283		if (*p == '/' && *q == '\0')
284			return (dir->dir_name);
285		if (*p == '\0' && *q == '/')
286			return (dir->dir_name);
287		if (*p == '\0' && *q == '\0')
288			return (NULL);
289	}
290	return (NULL);	/* it's not a subdir or parent */
291}
292
293/*
294 * Gets the next token from the string "p" and copies it into "w".  The "wq" is
295 * a quote vector for "w" and is derived from "pq", which is a quote vector for
296 * "p". Delim is the character to be used as a delimiter for the scan.  A space
297 * means "whitespace". The call to getword must provide buffers w and wq of size
298 * at least wordsz. getword() will pass strings of maximum length (wordsz-1),
299 * since it needs to null terminate the string.
300 * Returns 0 on ok and -1 on error.
301 */
302int
303getword(char *w, char *wq, char **p, char **pq, char delim, int wordsz)
304{
305	char *tmp = w;
306	char *tmpq = wq;
307	int count = wordsz;
308
309	if (wordsz <= 0) {
310		if (verbose)
311			syslog(LOG_ERR,
312			"getword: input word size %d must be > 0", wordsz);
313		return (-1);
314	}
315
316	while ((delim == ' ' ? isspace(**p) : **p == delim) && **pq == ' ')
317		(*p)++, (*pq)++;
318
319	while (**p &&
320	    !((delim == ' ' ? isspace(**p) : **p == delim) &&
321	    **pq == ' ')) {
322		if (--count <= 0) {
323			*tmp = '\0';
324			*tmpq = '\0';
325			syslog(LOG_ERR,
326			"maximum word length (%d) exceeded", wordsz);
327			return (-1);
328		}
329		*w++  = *(*p)++;
330		*wq++ = *(*pq)++;
331	}
332	*w  = '\0';
333	*wq = '\0';
334
335	return (0);
336}
337
338/*
339 * get_line attempts to get a line from the map, upto LINESZ. A line in
340 * the map is a concatenation of lines if the continuation symbol '\'
341 * is used at the end of the line. Returns line on success, a NULL on
342 * EOF, and an empty string on lines > linesz.
343 */
344char *
345get_line(FILE *fp, char *map, char *line, int linesz)
346{
347	register char *p = line;
348	register int len;
349	int excess = 0;
350
351	*p = '\0';
352
353	for (;;) {
354		if (fgets(p, linesz - (p-line), fp) == NULL) {
355			return (*line ? line : NULL);	/* EOF */
356		}
357
358		len = strlen(line);
359		if (len <= 0) {
360			p = line;
361			continue;
362		}
363		p = &line[len - 1];
364
365		/*
366		 * Is input line too long?
367		 */
368		if (*p != '\n') {
369			excess = 1;
370			/*
371			 * Perhaps last char read was '\'. Reinsert it
372			 * into the stream to ease the parsing when we
373			 * read the rest of the line to discard.
374			 */
375			(void) ungetc(*p, fp);
376			break;
377		}
378trim:
379		/* trim trailing white space */
380		while (p >= line && isspace(*(uchar_t *)p))
381			*p-- = '\0';
382		if (p < line) {			/* empty line */
383			p = line;
384			continue;
385		}
386
387		if (*p == '\\') {		/* continuation */
388			*p = '\0';
389			continue;
390		}
391
392		/*
393		 * Ignore comments. Comments start with '#'
394		 * which must be preceded by a whitespace, unless
395		 * if '#' is the first character in the line.
396		 */
397		p = line;
398		while (p = strchr(p, '#')) {
399			if (p == line || isspace(*(p-1))) {
400				*p-- = '\0';
401				goto trim;
402			}
403			p++;
404		}
405		break;
406	}
407	if (excess) {
408		int c;
409
410		/*
411		 * discard rest of line and return an empty string.
412		 * done to set the stream to the correct place when
413		 * we are done with this line.
414		 */
415		while ((c = getc(fp)) != EOF) {
416			*p = c;
417			if (*p == '\n')		/* end of the long line */
418				break;
419			else if (*p == '\\') {		/* continuation */
420				if (getc(fp) == EOF)	/* ignore next char */
421					break;
422			}
423		}
424		syslog(LOG_ERR,
425		    "map %s: line too long (max %d chars)",
426		    map, linesz-1);
427		*line = '\0';
428	}
429
430	return (line);
431}
432
433/*
434 * Gets the retry=n entry from opts.
435 * Returns 0 if retry=n is not present in option string,
436 * retry=n is invalid, or when option string is NULL.
437 */
438int
439get_retry(char *opts)
440{
441	int retry = 0;
442	char buf[MAXOPTSLEN];
443	char *p, *pb, *lasts;
444
445	if (opts == NULL)
446		return (retry);
447
448	(void) strcpy(buf, opts);
449	pb = buf;
450	while (p = (char *)strtok_r(pb, ",", &lasts)) {
451		pb = NULL;
452		if (strncmp(p, "retry=", 6) == 0)
453			retry = atoi(p+6);
454	}
455	return (retry > 0 ? retry : 0);
456}
457
458/*
459 * Returns zero if "opt" is found in mnt->mnt_opts, setting
460 * *sval to whatever follows the equal sign after "opt".
461 * str_opt allocates a string long enough to store the value of
462 * "opt" plus a terminating null character and returns it as *sval.
463 * It is the responsability of the caller to deallocate *sval.
464 * *sval will be equal to NULL upon return if either "opt=" is not found,
465 * or "opt=" has no value associated with it.
466 *
467 * stropt will return -1 on error.
468 */
469int
470str_opt(struct mnttab *mnt, char *opt, char **sval)
471{
472	char *str, *comma;
473
474	/*
475	 * is "opt" in the options field?
476	 */
477	if (str = hasmntopt(mnt, opt)) {
478		str += strlen(opt);
479		if (*str++ != '=' ||
480		    (*str == ',' || *str == '\0')) {
481			syslog(LOG_ERR, "Bad option field");
482			return (-1);
483		}
484		comma = strchr(str, ',');
485		if (comma != NULL)
486			*comma = '\0';
487		*sval = strdup(str);
488		if (comma != NULL)
489			*comma = ',';
490		if (*sval == NULL)
491			return (-1);
492	} else
493		*sval = NULL;
494
495	return (0);
496}
497
498/*
499 * Performs text expansions in the string "pline".
500 * "plineq" is the quote vector for "pline".
501 * An identifier prefixed by "$" is replaced by the
502 * corresponding environment variable string.  A "&"
503 * is replaced by the key string for the map entry.
504 *
505 * This routine will return an error (non-zero) if *size* would be
506 * exceeded after expansion, indicating that the macro_expand failed.
507 * This is to prevent writing past the end of pline and plineq.
508 * Both pline and plineq are left untouched in such error case.
509 */
510int
511macro_expand(key, pline, plineq, size)
512	char *key, *pline, *plineq;
513	int size;
514{
515	register char *p,  *q;
516	register char *bp, *bq;
517	register char *s;
518	char buffp[LINESZ], buffq[LINESZ];
519	char namebuf[64], *pn;
520	int expand = 0;
521	struct utsname name;
522	char procbuf[SYS_NMLN];
523	char isaname[64];
524
525	p = pline;  q = plineq;
526	bp = buffp; bq = buffq;
527
528	while (*p) {
529		if (*p == '&' && *q == ' ') {	/* insert key */
530			/*
531			 * make sure we don't overflow buffer
532			 */
533			if ((int)((bp - buffp) + strlen(key)) < size) {
534				for (s = key; *s; s++) {
535					*bp++ = *s;
536					*bq++ = ' ';
537				}
538				expand++;
539				p++; q++;
540				continue;
541			} else {
542				/*
543				 * line too long...
544				 */
545				return (1);
546			}
547		}
548
549		if (*p == '$' && *q == ' ') {	/* insert env var */
550			p++; q++;
551			pn = namebuf;
552			if (*p == '{') {
553				p++; q++;
554				while (*p && *p != '}') {
555					*pn++ = *p++;
556					q++;
557				}
558				if (*p) {
559					p++; q++;
560				}
561			} else {
562				while (*p && (*p == '_' || isalnum(*p))) {
563					*pn++ = *p++;
564					q++;
565				}
566			}
567			*pn = '\0';
568
569			s = getenv(namebuf);
570			if (!s) {
571				/* not found in env */
572				if (strcmp(namebuf, "ARCH") == 0) {
573					if (arch(procbuf, sizeof (procbuf),
574					    FALSE))
575						s = procbuf;
576				} else if (strcmp(namebuf, "CPU") == 0) {
577					if (cpu(procbuf, sizeof (procbuf)))
578						s = procbuf;
579				} else if (strcmp(namebuf, "HOST") == 0) {
580					(void) uname(&name);
581					s = name.nodename;
582				} else if (strcmp(namebuf, "KARCH") == 0) {
583					if (arch(procbuf, sizeof (procbuf),
584					    TRUE))
585						s = procbuf;
586				} else if (strcmp(namebuf, "OSREL") == 0) {
587					(void) uname(&name);
588					s = name.release;
589				} else if (strcmp(namebuf, "OSNAME") == 0) {
590					(void) uname(&name);
591					s = name.sysname;
592				} else if (strcmp(namebuf, "OSVERS") == 0) {
593					(void) uname(&name);
594					s = name.version;
595				} else if (strcmp(namebuf, "NATISA") == 0) {
596					if (natisa(isaname, sizeof (isaname)))
597						s = isaname;
598				} else if (strcmp(namebuf, "PLATFORM") == 0) {
599					if (platform(procbuf, sizeof (procbuf)))
600						s = procbuf;
601				}
602			}
603
604			if (s) {
605				if ((int)((bp - buffp) + strlen(s)) < size) {
606					while (*s) {
607						*bp++ = *s++;
608						*bq++ = ' ';
609					}
610				} else {
611					/*
612					 * line too long...
613					 */
614					return (1);
615				}
616			}
617			expand++;
618			continue;
619		}
620		/*
621		 * Since buffp needs to be null terminated, we need to
622		 * check that there's still room in the buffer to
623		 * place at least two more characters, *p and the
624		 * terminating null.
625		 */
626		if (bp - buffp == size - 1) {
627			/*
628			 * There was not enough room for at least two more
629			 * characters, return with an error.
630			 */
631			return (1);
632		}
633		/*
634		 * The total number of characters so far better be less
635		 * than the size of buffer passed in.
636		 */
637		*bp++ = *p++;
638		*bq++ = *q++;
639
640	}
641	if (!expand)
642		return (0);
643	*bp = '\0';
644	*bq = '\0';
645	/*
646	 * We know buffp/buffq will fit in pline/plineq since we
647	 * processed at most size characters.
648	 */
649	(void) strcpy(pline, buffp);
650	(void) strcpy(plineq, buffq);
651
652	return (0);
653}
654
655/*
656 * Removes backslashes, quotes and brackets from the string "str"
657 * and returns the quoting information in "qbuf". Character is
658 * considered escaped when it is
659 *
660 *	preceded with '\'	e.g. \a
661 *	within quotes		e.g. "string"
662 *	a ':' in brackets	e.g. [an:ip:6::ad::d:re:s:s]
663 *
664 * original str: 'the "brown" f\ox said: [fe80::20a:e4ff:fe35:8b0d]'
665 * unquoted str: 'the brown fox said: [fe80::20a:e4ff:fe35:8b0d]'
666 * and the qbuf: '    ^^^^^  ^             ^^   ^    ^    ^     '
667 */
668void
669unquote(str, qbuf)
670	char *str, *qbuf;
671{
672	register int escaped, inquote, inbracket, quoted;
673	register char *ip, *bp, *qp;
674	char buf[LINESZ];
675
676	escaped = inquote = inbracket = quoted = 0;
677
678	for (ip = str, bp = buf, qp = qbuf; *ip; ip++) {
679		if (!escaped) {
680			if (*ip == '\\') {
681				escaped = 1;
682				quoted++;
683				continue;
684			} else
685			if (*ip == '"') {
686				inquote = !inquote;
687				quoted++;
688				continue;
689			} else
690			if (*ip == '[') {
691				inbracket++;
692				quoted++;
693			} else
694			if (*ip == ']') {
695				if (inbracket > 0) inbracket--;
696			}
697		}
698
699		*bp++ = *ip;
700		*qp++ = (inquote || escaped) ? '^'
701				: ((inbracket && (*ip == ':')) ? '^' : ' ');
702		escaped = 0;
703	}
704
705	*bp = '\0';
706	*qp = '\0';
707
708	if (quoted)
709		(void) strcpy(str, buf);
710}
711
712/*
713 * If str is enclosed in [brackets], trim them off.
714 */
715void
716unbracket(s)
717	char **s;
718{
719	char *b = *s + strlen(*s) - 1;
720
721	if (*b == ']')
722		*b = '\0';
723	if (**s == '[')
724		(*s)++;
725}
726
727/*
728 * Removes trailing spaces from string "s".
729 */
730void
731trim(s)
732	char *s;
733{
734	char *p = &s[strlen(s) - 1];
735
736	while (p >= s && isspace(*(uchar_t *)p))
737		*p-- = '\0';
738}
739
740/*
741 * try to allocate memory using malloc, if malloc fails, then flush the
742 * rddir caches, and retry. If the second allocation after the readdir
743 * caches have been flushed fails too, then return NULL to indicate
744 * memory could not be allocated.
745 */
746char *
747auto_rddir_malloc(unsigned nbytes)
748{
749	char *p;
750	int again = 0;
751
752	if ((p = malloc(nbytes)) == NULL) {
753		/*
754		 * No memory, free rddir caches and try again
755		 */
756		mutex_lock(&cleanup_lock);
757		cond_signal(&cleanup_start_cv);
758		if (cond_wait(&cleanup_done_cv, &cleanup_lock)) {
759			mutex_unlock(&cleanup_lock);
760			syslog(LOG_ERR, "auto_rddir_malloc interrupted\n");
761		} else {
762			mutex_unlock(&cleanup_lock);
763			again = 1;
764		}
765	}
766
767	if (again)
768		p = malloc(nbytes);
769
770	return (p);
771}
772
773/*
774 * try to strdup a string, if it fails, then flush the rddir caches,
775 * and retry. If the second strdup fails, return NULL to indicate failure.
776 */
777char *
778auto_rddir_strdup(const char *s1)
779{
780	char *s2;
781	int again = 0;
782
783	if ((s2 = strdup(s1)) == NULL) {
784		/*
785		 * No memory, free rddir caches and try again
786		 */
787		mutex_lock(&cleanup_lock);
788		cond_signal(&cleanup_start_cv);
789		if (cond_wait(&cleanup_done_cv, &cleanup_lock)) {
790			mutex_unlock(&cleanup_lock);
791			syslog(LOG_ERR, "auto_rddir_strdup interrupted\n");
792		} else {
793			mutex_unlock(&cleanup_lock);
794			again = 1;
795		}
796	}
797
798	if (again)
799		s2 = strdup(s1);
800
801	return (s2);
802}
803
804/*
805 * Returns a pointer to the entry corresponding to 'name' if found,
806 * otherwise it returns NULL.
807 */
808struct dir_entry *
809btree_lookup(struct dir_entry *head, char *name)
810{
811	register struct dir_entry *p;
812	register int direction;
813
814	for (p = head; p != NULL; ) {
815		direction = strcmp(name, p->name);
816		if (direction == 0)
817			return (p);
818		if (direction > 0)
819			p = p->right;
820		else p = p->left;
821	}
822	return (NULL);
823}
824
825/*
826 * Add entry to binary tree
827 * Duplicate entries are not added
828 */
829void
830btree_enter(struct dir_entry **head, struct dir_entry *ent)
831{
832	register struct dir_entry *p, *prev = NULL;
833	register int direction;
834
835	ent->right = ent->left = NULL;
836	if (*head == NULL) {
837		*head = ent;
838		return;
839	}
840
841	for (p = *head; p != NULL; ) {
842		prev = p;
843		direction = strcmp(ent->name, p->name);
844		if (direction == 0) {
845			/*
846			 * entry already in btree
847			 */
848			return;
849		}
850		if (direction > 0)
851			p = p->right;
852		else p = p->left;
853	}
854	assert(prev != NULL);
855	if (direction > 0)
856		prev->right = ent;
857	else prev->left = ent;
858}
859
860/*
861 * If entry doesn't exist already, add it to the linear list
862 * after '*last' and to the binary tree list.
863 * If '*last == NULL' then the list is walked till the end.
864 * *last is always set to the new element after successful completion.
865 * if entry already exists '*last' is only updated if not previously
866 * provided.
867 */
868int
869add_dir_entry(char *name, struct dir_entry **list, struct dir_entry **last)
870{
871	struct dir_entry *e, *l;
872
873	if ((*list != NULL) && (*last == NULL)) {
874		/*
875		 * walk the list to find last element
876		 */
877		for (l = *list; l != NULL; l = l->next)
878			*last = l;
879	}
880
881	if (btree_lookup(*list, name) == NULL) {
882		/*
883		 * not a duplicate, add it to list
884		 */
885		/* LINTED pointer alignment */
886		e = (struct dir_entry *)
887		    auto_rddir_malloc(sizeof (struct dir_entry));
888		if (e == NULL)
889			return (ENOMEM);
890		(void) memset((char *)e, 0, sizeof (*e));
891		e->name = auto_rddir_strdup(name);
892		if (e->name == NULL) {
893			free(e);
894			return (ENOMEM);
895		}
896		e->next = NULL;
897		if (*list == NULL) {
898			/*
899			 * list is empty
900			 */
901			*list = *last = e;
902		} else {
903			/*
904			 * append to end of list
905			 */
906			assert(*last != NULL);
907			(*last)->next = e;
908			*last = e;
909		}
910		/*
911		 * add to binary tree
912		 */
913		btree_enter(list, e);
914	}
915	return (0);
916}
917
918/*
919 * Print trace output.
920 * Like fprintf(stderr, fmt, ...) except that if "id" is nonzero, the output
921 * is preceeded by the ID of the calling thread.
922 */
923#define	FMT_BUFSIZ 1024
924
925void
926trace_prt(int id, char *fmt, ...)
927{
928	va_list args;
929
930	char buf[FMT_BUFSIZ];
931
932	if (id) {
933		(void) sprintf(buf, "t%u\t%s", thr_self(), fmt);
934		fmt = buf;
935	}
936	va_start(args, fmt);
937	(void) vfprintf(stderr, fmt, args);
938	va_end(args);
939}
940
941/*
942 * Extract the isalist(5) for userland from the kernel.
943 */
944static char *
945isalist(void)
946{
947	char *buf;
948	size_t bufsize = BUFSIZ;	/* wild guess */
949	long ret;
950
951	buf = malloc(bufsize);
952	do {
953		ret = sysinfo(SI_ISALIST, buf, bufsize);
954		if (ret == -1l)
955			return (NULL);
956		if (ret > bufsize) {
957			bufsize = ret;
958			buf = realloc(buf, bufsize);
959		} else
960			break;
961	} while (buf != NULL);
962
963	return (buf);
964}
965
966/*
967 * Classify isa's as to bitness of the corresponding ABIs.
968 * isa's which have no "official" system ABI are returned
969 * unrecognised i.e. zero bits.
970 */
971static int
972bitness(char *isaname)
973{
974	if (strcmp(isaname, "sparc") == 0 ||
975	    strcmp(isaname, "i386") == 0)
976		return (32);
977
978	if (strcmp(isaname, "sparcv9") == 0 ||
979	    strcmp(isaname, "amd64") == 0)
980		return (64);
981
982	return (0);
983}
984
985/*
986 * Determine the application architecture (derived from uname -m) to expand
987 * the $ARCH and $KARCH macros.
988 *
989 * Like arch(1), we need to substitute "sun4" for "sun4u", "sun4v", ... for
990 * backward compatibility. When kflag is set (like arch -k), the unmodifed
991 * value is returned instead.
992 */
993static int
994arch(char *buf, size_t bufsize, bool_t karch)
995{
996	long ret;
997
998	ret = sysinfo(SI_MACHINE, buf, bufsize);
999	if (ret == -1L)
1000		return (0);
1001	if (!karch && strncmp(buf, "sun4", 4) == 0)
1002		(void) strlcpy(buf, "sun4", bufsize);
1003	return (1);
1004}
1005
1006/*
1007 * Determine the basic ISA (uname -p) to expand the $CPU macro.
1008 */
1009static int
1010cpu(char *buf, size_t bufsize)
1011{
1012	long ret;
1013
1014	ret = sysinfo(SI_ARCHITECTURE, buf, bufsize);
1015	if (ret == -1L)
1016		return (0);
1017	else
1018		return (1);
1019}
1020
1021/*
1022 * Find the left-most element in the isalist that matches our idea of a
1023 * system ABI.
1024 *
1025 * On machines with only one ABI, this is usually the same as uname -p.
1026 */
1027static int
1028natisa(char *buf, size_t bufsize)
1029{
1030	int bits;
1031	char *isa, *list;
1032	char *lasts;
1033
1034	if ((list = isalist()) == NULL)
1035		return (0);
1036
1037	for (isa = strtok_r(list, " ", &lasts);
1038	    isa; isa = strtok_r(0, " ", &lasts))
1039		if ((bits = bitness(isa)) != 0)
1040			break;	/* ignore "extension" architectures */
1041
1042	if (isa == 0 || bits == 0) {
1043		free(list);
1044		return (0);	/* can't figure it out :( */
1045	}
1046
1047	(void) strncpy(buf, isa, bufsize);
1048	free(list);
1049
1050	return (1);
1051}
1052
1053/*
1054 * Determine the platform (uname -i) to expand the $PLATFORM macro.
1055 */
1056static int
1057platform(char *buf, size_t bufsize)
1058{
1059	long ret;
1060
1061	ret = sysinfo(SI_PLATFORM, buf, bufsize);
1062	if (ret == -1L)
1063		return (0);
1064	else
1065		return (1);
1066}
1067
1068/*
1069 * Set environment variables
1070 */
1071void
1072put_automountd_env(void)
1073{
1074	char defval[PATH_MAX], *p, *a, *c;
1075	int ret = 0, bufsz = PATH_MAX;
1076
1077	ret = autofs_smf_get_prop("environment", defval, DEFAULT_INSTANCE,
1078	    SCF_TYPE_ASTRING, AUTOMOUNTD, &bufsz);
1079	if (ret == SA_OK) {
1080		a = c = defval;
1081		if (*a == '\0')
1082			return;
1083		/*
1084		 * Environment variables can have more than one value
1085		 * seperated by a comma and there can be multiple
1086		 * environment variables. * a=b\,c,d=e. For multiple
1087		 * valued environment variable, values are seperated
1088		 * with an escape character.
1089		 */
1090		while ((p = strchr(c, ',')) != NULL) {
1091			if (*(p - 1) == '\\') {
1092				c = p + 1;
1093				continue;
1094			}
1095			*p = '\0';
1096			if ((c = strchr(a, '=')) != NULL)
1097				putenv(strdup(a));
1098			a = c = p + 1;
1099		}
1100		if (*a != '\0') {
1101			if ((c = strchr(a, '=')) != NULL)
1102				putenv(strdup(a));
1103		}
1104	}
1105}
1106