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
loadzone_maps(char * mntpnt,char * map,char * opts,char ** stack,char *** stkptr)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
dirinit(char * mntpnt,char * map,char * opts,int direct,char ** stack,char *** stkptr)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 *
check_hier(mntpnt)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
getword(char * w,char * wq,char ** p,char ** pq,char delim,int wordsz)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 *
get_line(FILE * fp,char * map,char * line,int linesz)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
get_retry(char * opts)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
str_opt(struct mnttab * mnt,char * opt,char ** sval)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
macro_expand(key,pline,plineq,size)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
unquote(str,qbuf)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
unbracket(s)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
trim(s)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 *
auto_rddir_malloc(unsigned nbytes)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 *
auto_rddir_strdup(const char * s1)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 *
btree_lookup(struct dir_entry * head,char * name)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
btree_enter(struct dir_entry ** head,struct dir_entry * ent)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
add_dir_entry(char * name,struct dir_entry ** list,struct dir_entry ** last)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
trace_prt(int id,char * fmt,...)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 *
isalist(void)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
bitness(char * isaname)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
arch(char * buf,size_t bufsize,bool_t karch)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
cpu(char * buf,size_t bufsize)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
natisa(char * buf,size_t bufsize)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
platform(char * buf,size_t bufsize)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
put_automountd_env(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