xref: /illumos-gate/usr/src/cmd/fs.d/autofs/auto_subr.c (revision 08855964b9970604433f7b19dcd71cf5af5e5f14)
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 
64 static char *check_hier(char *);
65 static int arch(char *, size_t, bool_t);
66 static int cpu(char *, size_t);
67 static int natisa(char *, size_t);
68 static int platform(char *, size_t);
69 
70 struct mntlist *current_mounts;
71 
72 static 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  */
89 static void
90 loadzone_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();
105 again:
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 
176 void
177 dirinit(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 	}
221 enter:
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 
251 alloc_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  */
270 static char *
271 check_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  */
302 int
303 getword(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  */
344 char *
345 get_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 		}
378 trim:
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  */
438 int
439 get_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  */
469 int
470 str_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  */
510 int
511 macro_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  */
668 void
669 unquote(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  */
715 void
716 unbracket(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  */
730 void
731 trim(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  */
746 char *
747 auto_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  */
777 char *
778 auto_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  */
808 struct dir_entry *
809 btree_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  */
829 void
830 btree_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  */
868 int
869 add_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 
925 void
926 trace_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(7) for userland from the kernel.
943  */
944 static char *
945 isalist(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  */
971 static int
972 bitness(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  */
993 static int
994 arch(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  */
1009 static int
1010 cpu(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  */
1027 static int
1028 natisa(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  */
1056 static int
1057 platform(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  */
1071 void
1072 put_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