xref: /titanic_41/usr/src/cmd/fs.d/autofs/auto_subr.c (revision 22337b4b3c8a2b9db615e524e0e9e1fbd3dc71bf)
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 2007 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 it into "w".  The "wq" is
292  * a quote vector for "w" and is derived from "pq", which is a quote vector for
293  * "p". Delim is the character to be used as a delimiter for the scan.  A space
294  * means "whitespace". The call to getword must provide buffers w and wq of size
295  * at least wordsz. getword() will pass strings of maximum length (wordsz-1),
296  * since it needs to null terminate the string.
297  * Returns 0 on ok and -1 on error.
298  */
299 int
300 getword(char *w, char *wq, char **p, char **pq, char delim, int wordsz)
301 {
302 	char *tmp = w;
303 	char *tmpq = wq;
304 	int count = wordsz;
305 
306 	if (wordsz <= 0) {
307 		if (verbose)
308 			syslog(LOG_ERR,
309 			"getword: input word size %d must be > 0", wordsz);
310 		return (-1);
311 	}
312 
313 	while ((delim == ' ' ? isspace(**p) : **p == delim) && **pq == ' ')
314 		(*p)++, (*pq)++;
315 
316 	while (**p &&
317 		!((delim == ' ' ? isspace(**p) : **p == delim) &&
318 			**pq == ' ')) {
319 		if (--count <= 0) {
320 			*tmp = '\0';
321 			*tmpq = '\0';
322 			syslog(LOG_ERR,
323 			"maximum word length (%d) exceeded", wordsz);
324 			return (-1);
325 		}
326 		*w++  = *(*p)++;
327 		*wq++ = *(*pq)++;
328 	}
329 	*w  = '\0';
330 	*wq = '\0';
331 
332 	return (0);
333 }
334 
335 /*
336  * get_line attempts to get a line from the map, upto LINESZ. A line in
337  * the map is a concatenation of lines if the continuation symbol '\'
338  * is used at the end of the line. Returns line on success, a NULL on
339  * EOF, and an empty string on lines > linesz.
340  */
341 char *
342 get_line(FILE *fp, char *map, char *line, int linesz)
343 {
344 	register char *p = line;
345 	register int len;
346 	int excess = 0;
347 
348 	*p = '\0';
349 
350 	for (;;) {
351 		if (fgets(p, linesz - (p-line), fp) == NULL) {
352 			return (*line ? line : NULL);	/* EOF */
353 		}
354 
355 		len = strlen(line);
356 		if (len <= 0) {
357 			p = line;
358 			continue;
359 		}
360 		p = &line[len - 1];
361 
362 		/*
363 		 * Is input line too long?
364 		 */
365 		if (*p != '\n') {
366 			excess = 1;
367 			/*
368 			 * Perhaps last char read was '\'. Reinsert it
369 			 * into the stream to ease the parsing when we
370 			 * read the rest of the line to discard.
371 			 */
372 			(void) ungetc(*p, fp);
373 			break;
374 		}
375 trim:
376 		/* trim trailing white space */
377 		while (p >= line && isspace(*(uchar_t *)p))
378 			*p-- = '\0';
379 		if (p < line) {			/* empty line */
380 			p = line;
381 			continue;
382 		}
383 
384 		if (*p == '\\') {		/* continuation */
385 			*p = '\0';
386 			continue;
387 		}
388 
389 		/*
390 		 * Ignore comments. Comments start with '#'
391 		 * which must be preceded by a whitespace, unless
392 		 * if '#' is the first character in the line.
393 		 */
394 		p = line;
395 		while (p = strchr(p, '#')) {
396 			if (p == line || isspace(*(p-1))) {
397 				*p-- = '\0';
398 				goto trim;
399 			}
400 			p++;
401 		}
402 		break;
403 	}
404 	if (excess) {
405 		int c;
406 
407 		/*
408 		 * discard rest of line and return an empty string.
409 		 * done to set the stream to the correct place when
410 		 * we are done with this line.
411 		 */
412 		while ((c = getc(fp)) != EOF) {
413 			*p = c;
414 			if (*p == '\n')		/* end of the long line */
415 				break;
416 			else if (*p == '\\') {		/* continuation */
417 				if (getc(fp) == EOF)	/* ignore next char */
418 					break;
419 			}
420 		}
421 		syslog(LOG_ERR,
422 			"map %s: line too long (max %d chars)",
423 			map, linesz-1);
424 		*line = '\0';
425 	}
426 
427 	return (line);
428 }
429 
430 /*
431  * Gets the retry=n entry from opts.
432  * Returns 0 if retry=n is not present in option string,
433  * retry=n is invalid, or when option string is NULL.
434  */
435 int
436 get_retry(char *opts)
437 {
438 	int retry = 0;
439 	char buf[MAXOPTSLEN];
440 	char *p, *pb, *lasts;
441 
442 	if (opts == NULL)
443 		return (retry);
444 
445 	(void) strcpy(buf, opts);
446 	pb = buf;
447 	while (p = (char *)strtok_r(pb, ",", &lasts)) {
448 		pb = NULL;
449 		if (strncmp(p, "retry=", 6) == 0)
450 			retry = atoi(p+6);
451 	}
452 	return (retry > 0 ? retry : 0);
453 }
454 
455 /*
456  * Returns zero if "opt" is found in mnt->mnt_opts, setting
457  * *sval to whatever follows the equal sign after "opt".
458  * str_opt allocates a string long enough to store the value of
459  * "opt" plus a terminating null character and returns it as *sval.
460  * It is the responsability of the caller to deallocate *sval.
461  * *sval will be equal to NULL upon return if either "opt=" is not found,
462  * or "opt=" has no value associated with it.
463  *
464  * stropt will return -1 on error.
465  */
466 int
467 str_opt(struct mnttab *mnt, char *opt, char **sval)
468 {
469 	char *str, *comma;
470 
471 	/*
472 	 * is "opt" in the options field?
473 	 */
474 	if (str = hasmntopt(mnt, opt)) {
475 		str += strlen(opt);
476 		if (*str++ != '=' ||
477 		    (*str == ',' || *str == '\0')) {
478 			syslog(LOG_ERR, "Bad option field");
479 			return (-1);
480 		}
481 		comma = strchr(str, ',');
482 		if (comma != NULL)
483 			*comma = '\0';
484 		*sval = strdup(str);
485 		if (comma != NULL)
486 			*comma = ',';
487 		if (*sval == NULL)
488 			return (-1);
489 	} else
490 		*sval = NULL;
491 
492 	return (0);
493 }
494 
495 /*
496  * Performs text expansions in the string "pline".
497  * "plineq" is the quote vector for "pline".
498  * An identifier prefixed by "$" is replaced by the
499  * corresponding environment variable string.  A "&"
500  * is replaced by the key string for the map entry.
501  *
502  * This routine will return an error (non-zero) if *size* would be
503  * exceeded after expansion, indicating that the macro_expand failed.
504  * This is to prevent writing past the end of pline and plineq.
505  * Both pline and plineq are left untouched in such error case.
506  */
507 int
508 macro_expand(key, pline, plineq, size)
509 	char *key, *pline, *plineq;
510 	int size;
511 {
512 	register char *p,  *q;
513 	register char *bp, *bq;
514 	register char *s;
515 	char buffp[LINESZ], buffq[LINESZ];
516 	char namebuf[64], *pn;
517 	int expand = 0;
518 	struct utsname name;
519 	char procbuf[SYS_NMLN];
520 	char isaname[64];
521 
522 	p = pline;  q = plineq;
523 	bp = buffp; bq = buffq;
524 
525 	while (*p) {
526 		if (*p == '&' && *q == ' ') {	/* insert key */
527 			/*
528 			 * make sure we don't overflow buffer
529 			 */
530 			if ((int)((bp - buffp) + strlen(key)) < size) {
531 				for (s = key; *s; s++) {
532 					*bp++ = *s;
533 					*bq++ = ' ';
534 				}
535 				expand++;
536 				p++; q++;
537 				continue;
538 			} else {
539 				/*
540 				 * line too long...
541 				 */
542 				return (1);
543 			}
544 		}
545 
546 		if (*p == '$' && *q == ' ') {	/* insert env var */
547 			p++; q++;
548 			pn = namebuf;
549 			if (*p == '{') {
550 				p++; q++;
551 				while (*p && *p != '}') {
552 					*pn++ = *p++;
553 					q++;
554 				}
555 				if (*p) {
556 					p++; q++;
557 				}
558 			} else {
559 				while (*p && (*p == '_' || isalnum(*p))) {
560 					*pn++ = *p++;
561 					q++;
562 				}
563 			}
564 			*pn = '\0';
565 
566 			s = getenv(namebuf);
567 			if (!s) {
568 				/* not found in env */
569 				if (strcmp(namebuf, "ARCH") == 0) {
570 					if (arch(procbuf, sizeof (procbuf),
571 					    FALSE))
572 						s = procbuf;
573 				} else if (strcmp(namebuf, "CPU") == 0) {
574 					if (cpu(procbuf, sizeof (procbuf)))
575 						s = procbuf;
576 				} else if (strcmp(namebuf, "HOST") == 0) {
577 					(void) uname(&name);
578 					s = name.nodename;
579 				} else if (strcmp(namebuf, "KARCH") == 0) {
580 					if (arch(procbuf, sizeof (procbuf),
581 					    TRUE))
582 						s = procbuf;
583 				} else if (strcmp(namebuf, "OSREL") == 0) {
584 					(void) uname(&name);
585 					s = name.release;
586 				} else if (strcmp(namebuf, "OSNAME") == 0) {
587 					(void) uname(&name);
588 					s = name.sysname;
589 				} else if (strcmp(namebuf, "OSVERS") == 0) {
590 					(void) uname(&name);
591 					s = name.version;
592 				} else if (strcmp(namebuf, "NATISA") == 0) {
593 					if (natisa(isaname, sizeof (isaname)))
594 						s = isaname;
595 				} else if (strcmp(namebuf, "PLATFORM") == 0) {
596 					if (platform(procbuf, sizeof (procbuf)))
597 						s = procbuf;
598 				}
599 			}
600 
601 			if (s) {
602 				if ((int)((bp - buffp) + strlen(s)) < size) {
603 					while (*s) {
604 						*bp++ = *s++;
605 						*bq++ = ' ';
606 					}
607 				} else {
608 					/*
609 					 * line too long...
610 					 */
611 					return (1);
612 				}
613 			}
614 			expand++;
615 			continue;
616 		}
617 		/*
618 		 * Since buffp needs to be null terminated, we need to
619 		 * check that there's still room in the buffer to
620 		 * place at least two more characters, *p and the
621 		 * terminating null.
622 		 */
623 		if (bp - buffp == size - 1) {
624 			/*
625 			 * There was not enough room for at least two more
626 			 * characters, return with an error.
627 			 */
628 			return (1);
629 		}
630 		/*
631 		 * The total number of characters so far better be less
632 		 * than the size of buffer passed in.
633 		 */
634 		*bp++ = *p++;
635 		*bq++ = *q++;
636 
637 	}
638 	if (!expand)
639 		return (0);
640 	*bp = '\0';
641 	*bq = '\0';
642 	/*
643 	 * We know buffp/buffq will fit in pline/plineq since we
644 	 * processed at most size characters.
645 	 */
646 	(void) strcpy(pline, buffp);
647 	(void) strcpy(plineq, buffq);
648 
649 	return (0);
650 }
651 
652 /*
653  * Removes backslashes, quotes and brackets from the string "str"
654  * and returns the quoting information in "qbuf". Character is
655  * considered escaped when it is
656  *
657  * 	preceded with '\'	e.g. \a
658  * 	within quotes		e.g. "string"
659  * 	a ':' in brackets 	e.g. [an:ip:6::ad::d:re:s:s]
660  *
661  * original str: 'the "brown" f\ox said: [fe80::20a:e4ff:fe35:8b0d]'
662  * unquoted str: 'the brown fox said: [fe80::20a:e4ff:fe35:8b0d]'
663  * and the qbuf: '    ^^^^^  ^             ^^   ^    ^    ^     '
664  */
665 void
666 unquote(str, qbuf)
667 	char *str, *qbuf;
668 {
669 	register int escaped, inquote, inbracket, quoted;
670 	register char *ip, *bp, *qp;
671 	char buf[LINESZ];
672 
673 	escaped = inquote = inbracket = quoted = 0;
674 
675 	for (ip = str, bp = buf, qp = qbuf; *ip; ip++) {
676 		if (!escaped) {
677 			if (*ip == '\\') {
678 				escaped = 1;
679 				quoted++;
680 				continue;
681 			} else
682 			if (*ip == '"') {
683 				inquote = !inquote;
684 				quoted++;
685 				continue;
686 			} else
687 			if (*ip == '[') {
688 				inbracket++;
689 				quoted++;
690 			} else
691 			if (*ip == ']') {
692 				if (inbracket > 0) inbracket--;
693 			}
694 		}
695 
696 		*bp++ = *ip;
697 		*qp++ = (inquote || escaped) ? '^'
698 				: ((inbracket && (*ip == ':')) ? '^' : ' ');
699 		escaped = 0;
700 	}
701 
702 	*bp = '\0';
703 	*qp = '\0';
704 
705 	if (quoted)
706 		(void) strcpy(str, buf);
707 }
708 
709 /*
710  * If str is enclosed in [brackets], trim them off.
711  */
712 void
713 unbracket(s)
714 	char **s;
715 {
716 	char *b = *s + strlen(*s) - 1;
717 
718 	if (*b == ']')
719 		*b = '\0';
720 	if (**s == '[')
721 		(*s)++;
722 }
723 
724 /*
725  * Removes trailing spaces from string "s".
726  */
727 void
728 trim(s)
729 	char *s;
730 {
731 	char *p = &s[strlen(s) - 1];
732 
733 	while (p >= s && isspace(*(uchar_t *)p))
734 		*p-- = '\0';
735 }
736 
737 /*
738  * try to allocate memory using malloc, if malloc fails, then flush the
739  * rddir caches, and retry. If the second allocation after the readdir
740  * caches have been flushed fails too, then return NULL to indicate
741  * memory could not be allocated.
742  */
743 char *
744 auto_rddir_malloc(unsigned nbytes)
745 {
746 	char *p;
747 	int again = 0;
748 
749 	if ((p = malloc(nbytes)) == NULL) {
750 		/*
751 		 * No memory, free rddir caches and try again
752 		 */
753 		mutex_lock(&cleanup_lock);
754 		cond_signal(&cleanup_start_cv);
755 		if (cond_wait(&cleanup_done_cv, &cleanup_lock)) {
756 			mutex_unlock(&cleanup_lock);
757 			syslog(LOG_ERR, "auto_rddir_malloc interrupted\n");
758 		} else {
759 			mutex_unlock(&cleanup_lock);
760 			again = 1;
761 		}
762 	}
763 
764 	if (again)
765 		p = malloc(nbytes);
766 
767 	return (p);
768 }
769 
770 /*
771  * try to strdup a string, if it fails, then flush the rddir caches,
772  * and retry. If the second strdup fails, return NULL to indicate failure.
773  */
774 char *
775 auto_rddir_strdup(const char *s1)
776 {
777 	char *s2;
778 	int again = 0;
779 
780 	if ((s2 = strdup(s1)) == NULL) {
781 		/*
782 		 * No memory, free rddir caches and try again
783 		 */
784 		mutex_lock(&cleanup_lock);
785 		cond_signal(&cleanup_start_cv);
786 		if (cond_wait(&cleanup_done_cv, &cleanup_lock)) {
787 			mutex_unlock(&cleanup_lock);
788 			syslog(LOG_ERR, "auto_rddir_strdup interrupted\n");
789 		} else {
790 			mutex_unlock(&cleanup_lock);
791 			again = 1;
792 		}
793 	}
794 
795 	if (again)
796 		s2 = strdup(s1);
797 
798 	return (s2);
799 }
800 
801 /*
802  * Returns a pointer to the entry corresponding to 'name' if found,
803  * otherwise it returns NULL.
804  */
805 struct dir_entry *
806 btree_lookup(struct dir_entry *head, char *name)
807 {
808 	register struct dir_entry *p;
809 	register int direction;
810 
811 	for (p = head; p != NULL; ) {
812 		direction = strcmp(name, p->name);
813 		if (direction == 0)
814 			return (p);
815 		if (direction > 0)
816 			p = p->right;
817 		else p = p->left;
818 	}
819 	return (NULL);
820 }
821 
822 /*
823  * Add entry to binary tree
824  * Duplicate entries are not added
825  */
826 void
827 btree_enter(struct dir_entry **head, struct dir_entry *ent)
828 {
829 	register struct dir_entry *p, *prev = NULL;
830 	register int direction;
831 
832 	ent->right = ent->left = NULL;
833 	if (*head == NULL) {
834 		*head = ent;
835 		return;
836 	}
837 
838 	for (p = *head; p != NULL; ) {
839 		prev = p;
840 		direction = strcmp(ent->name, p->name);
841 		if (direction == 0) {
842 			/*
843 			 * entry already in btree
844 			 */
845 			return;
846 		}
847 		if (direction > 0)
848 			p = p->right;
849 		else p = p->left;
850 	}
851 	assert(prev != NULL);
852 	if (direction > 0)
853 		prev->right = ent;
854 	else prev->left = ent;
855 }
856 
857 /*
858  * If entry doesn't exist already, add it to the linear list
859  * after '*last' and to the binary tree list.
860  * If '*last == NULL' then the list is walked till the end.
861  * *last is always set to the new element after successful completion.
862  * if entry already exists '*last' is only updated if not previously
863  * provided.
864  */
865 int
866 add_dir_entry(char *name, struct dir_entry **list, struct dir_entry **last)
867 {
868 	struct dir_entry *e, *l;
869 
870 	if ((*list != NULL) && (*last == NULL)) {
871 		/*
872 		 * walk the list to find last element
873 		 */
874 		for (l = *list; l != NULL; l = l->next)
875 			*last = l;
876 	}
877 
878 	if (btree_lookup(*list, name) == NULL) {
879 		/*
880 		 * not a duplicate, add it to list
881 		 */
882 		/* LINTED pointer alignment */
883 		e = (struct dir_entry *)
884 			auto_rddir_malloc(sizeof (struct dir_entry));
885 		if (e == NULL)
886 			return (ENOMEM);
887 		(void) memset((char *)e, 0, sizeof (*e));
888 		e->name = auto_rddir_strdup(name);
889 		if (e->name == NULL) {
890 			free(e);
891 			return (ENOMEM);
892 		}
893 		e->next = NULL;
894 		if (*list == NULL) {
895 			/*
896 			 * list is empty
897 			 */
898 			*list = *last = e;
899 		} else {
900 			/*
901 			 * append to end of list
902 			 */
903 			assert(*last != NULL);
904 			(*last)->next = e;
905 			*last = e;
906 		}
907 		/*
908 		 * add to binary tree
909 		 */
910 		btree_enter(list, e);
911 	}
912 	return (0);
913 }
914 
915 /*
916  * Print trace output.
917  * Like fprintf(stderr, fmt, ...) except that if "id" is nonzero, the output
918  * is preceeded by the ID of the calling thread.
919  */
920 #define	FMT_BUFSIZ 1024
921 
922 void
923 trace_prt(int id, char *fmt, ...)
924 {
925 	va_list args;
926 
927 	char buf[FMT_BUFSIZ];
928 
929 	if (id) {
930 		(void) sprintf(buf, "t%u\t%s", thr_self(), fmt);
931 		fmt = buf;
932 	}
933 	va_start(args, fmt);
934 	(void) vfprintf(stderr, fmt, args);
935 	va_end(args);
936 }
937 
938 /*
939  * Extract the isalist(5) for userland from the kernel.
940  */
941 static char *
942 isalist(void)
943 {
944 	char *buf;
945 	size_t bufsize = BUFSIZ;	/* wild guess */
946 	long ret;
947 
948 	buf = malloc(bufsize);
949 	do {
950 		ret = sysinfo(SI_ISALIST, buf, bufsize);
951 		if (ret == -1l)
952 			return (NULL);
953 		if (ret > bufsize) {
954 			bufsize = ret;
955 			buf = realloc(buf, bufsize);
956 		} else
957 			break;
958 	} while (buf != NULL);
959 
960 	return (buf);
961 }
962 
963 /*
964  * Classify isa's as to bitness of the corresponding ABIs.
965  * isa's which have no "official" system ABI are returned
966  * unrecognised i.e. zero bits.
967  */
968 static int
969 bitness(char *isaname)
970 {
971 	if (strcmp(isaname, "sparc") == 0 ||
972 	    strcmp(isaname, "i386") == 0)
973 		return (32);
974 
975 	if (strcmp(isaname, "sparcv9") == 0 ||
976 	    strcmp(isaname, "amd64") == 0)
977 		return (64);
978 
979 	return (0);
980 }
981 
982 /*
983  * Determine the application architecture (derived from uname -m) to expand
984  * the $ARCH and $KARCH macros.
985  *
986  * Like arch(1), we need to substitute "sun4" for "sun4u", "sun4v", ... for
987  * backward compatibility. When kflag is set (like arch -k), the unmodifed
988  * value is returned instead.
989  */
990 static int
991 arch(char *buf, size_t bufsize, bool_t karch)
992 {
993 	long ret;
994 
995 	ret = sysinfo(SI_MACHINE, buf, bufsize);
996 	if (ret == -1L)
997 		return (0);
998 	if (!karch && strncmp(buf, "sun4", 4) == 0)
999 		(void) strlcpy(buf, "sun4", bufsize);
1000 	return (1);
1001 }
1002 
1003 /*
1004  * Determine the basic ISA (uname -p) to expand the $CPU macro.
1005  */
1006 static int
1007 cpu(char *buf, size_t bufsize)
1008 {
1009 	long ret;
1010 
1011 	ret = sysinfo(SI_ARCHITECTURE, buf, bufsize);
1012 	if (ret == -1L)
1013 		return (0);
1014 	else
1015 		return (1);
1016 }
1017 
1018 /*
1019  * Find the left-most element in the isalist that matches our idea of a
1020  * system ABI.
1021  *
1022  * On machines with only one ABI, this is usually the same as uname -p.
1023  */
1024 static int
1025 natisa(char *buf, size_t bufsize)
1026 {
1027 	int bits;
1028 	char *isa, *list;
1029 	char *lasts;
1030 
1031 	if ((list = isalist()) == NULL)
1032 		return (0);
1033 
1034 	for (isa = strtok_r(list, " ", &lasts);
1035 	    isa; isa = strtok_r(0, " ", &lasts))
1036 		if ((bits = bitness(isa)) != 0)
1037 			break;	/* ignore "extension" architectures */
1038 
1039 	if (isa == 0 || bits == 0) {
1040 		free(list);
1041 		return (0);	/* can't figure it out :( */
1042 	}
1043 
1044 	(void) strncpy(buf, isa, bufsize);
1045 	free(list);
1046 
1047 	return (1);
1048 }
1049 
1050 /*
1051  * Determine the platform (uname -i) to expand the $PLATFORM macro.
1052  */
1053 static int
1054 platform(char *buf, size_t bufsize)
1055 {
1056 	long ret;
1057 
1058 	ret = sysinfo(SI_PLATFORM, buf, bufsize);
1059 	if (ret == -1L)
1060 		return (0);
1061 	else
1062 		return (1);
1063 }
1064 
1065 /*
1066  * Set environment variables specified in /etc/default/autofs.
1067  */
1068 void
1069 put_automountd_env(void)
1070 {
1071 	char *defval;
1072 	int defflags;
1073 
1074 	if ((defval = defread("AUTOMOUNTD_ENV=")) != NULL) {
1075 		(void) putenv(strdup(defval));
1076 		defflags = defcntl(DC_GETFLAGS, 0);
1077 		TURNON(defflags, DC_NOREWIND);
1078 		defflags = defcntl(DC_SETFLAGS, defflags);
1079 		while ((defval = defread("AUTOMOUNTD_ENV=")) != NULL)
1080 			(void) putenv(strdup(defval));
1081 		(void) defcntl(DC_SETFLAGS, defflags);
1082 	}
1083 }
1084