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