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.
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.
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]
22 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
38 #include <sys/param.h>
41 #include <sys/types.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>
52 #include <rpcsvc/nfs_prot.h>
53 #include <rpcsvc/daemon_utils.h>
55 #include "automount.h"
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);
70 struct mntlist
*current_mounts
;
72 static bool_t nodirect_map
= FALSE
;
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.
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:
87 * * -fstype=lofs :/zone/public/export/home/&
90 loadzone_maps(char *mntpnt
, char *map
, char *opts
, char **stack
, char ***stkptr
)
92 zoneid_t
*zids
= NULL
;
98 if (!priv_ineffect(PRIV_SYS_MOUNT
))
101 if (zone_list(NULL
, &nzents
) != 0) {
104 my_zoneid
= getzoneid();
109 zids
= malloc(nzents
* sizeof (zoneid_t
));
110 nzents_saved
= nzents
;
112 if (zone_list(zids
, &nzents
) != 0) {
116 if (nzents
!= nzents_saved
) {
117 /* list changed, try again */
122 for (i
= 0; i
< nzents
; i
++) {
123 char zonename
[ZONENAME_MAX
];
124 char zoneroot
[MAXPATHLEN
];
126 if (getzonenamebyid(zids
[i
], zonename
, ZONENAME_MAX
) != -1) {
127 char appended_map
[MAXPATHLEN
];
128 char prepended_mntpnt
[MAXPATHLEN
];
129 char map_path
[MAXPATHLEN
];
132 (void) snprintf(appended_map
, sizeof (appended_map
),
133 "%s_%s", map
, zonename
);
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)
144 (void) strcpy(prepended_mntpnt
, mntpnt
);
148 dirinit(prepended_mntpnt
, appended_map
, opts
, 0, stack
,
151 * Next create auto_home_<zone> maps for each zone
154 (void) snprintf(map_path
, sizeof (map_path
),
155 "/etc/%s", appended_map
);
157 * If the map file doesn't exist create a template
159 if ((fd
= open(map_path
, O_RDWR
| O_CREAT
| O_EXCL
,
160 S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
)) != -1) {
162 char map_rec
[MAXPATHLEN
];
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
);
177 dirinit(char *mntpnt
, char *map
, char *opts
, int direct
, char **stack
,
183 if (strcmp(map
, "-null") == 0) {
184 if (strcmp(mntpnt
, "/-") == 0)
189 p
= mntpnt
+ (strlen(mntpnt
) - 1);
191 *p
= '\0'; /* trim trailing / */
192 if (*mntpnt
!= '/') {
193 pr_msg("dir %s must start with '/'", mntpnt
);
196 if (p
= check_hier(mntpnt
)) {
197 pr_msg("hierarchical mountpoint: %s and %s",
203 * If it's a direct map then call dirinit
204 * for every map entry.
206 if ((strcmp(mntpnt
, "/-") == 0) && !(nodirect_map
)) {
207 (void) loaddirect_map(map
, map
, opts
, stack
, stkptr
);
212 * Home directories are polyinstantiated on
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
);
222 dir
= (struct autodir
*)malloc(sizeof (*dir
));
225 dir
->dir_name
= strdup(mntpnt
);
226 if (dir
->dir_name
== NULL
)
228 dir
->dir_map
= strdup(map
);
229 if (dir
->dir_map
== NULL
)
231 dir
->dir_opts
= strdup(opts
);
232 if (dir
->dir_opts
== NULL
)
234 dir
->dir_direct
= direct
;
235 dir
->dir_remount
= 0;
236 dir
->dir_next
= NULL
;
239 * Append to dir chain
241 if (dir_head
== NULL
)
244 dir_tail
->dir_next
= dir
;
246 dir
->dir_prev
= dir_tail
;
261 pr_msg("dirinit: memory allocation failed");
265 * Check whether the mount point is a
266 * subdirectory or a parent directory
267 * of any previously mounted automount
274 register struct autodir
*dir
;
275 register char *p
, *q
;
277 for (dir
= dir_head
; dir
; dir
= dir
->dir_next
) {
280 for (; *p
== *q
; p
++, q
++)
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')
290 return (NULL
); /* it's not a subdir or parent */
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.
303 getword(char *w
, char *wq
, char **p
, char **pq
, char delim
, int wordsz
)
312 "getword: input word size %d must be > 0", wordsz
);
316 while ((delim
== ' ' ? isspace(**p
) : **p
== delim
) && **pq
== ' ')
320 !((delim
== ' ' ? isspace(**p
) : **p
== delim
) &&
326 "maximum word length (%d) exceeded", wordsz
);
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.
345 get_line(FILE *fp
, char *map
, char *line
, int linesz
)
347 register char *p
= line
;
354 if (fgets(p
, linesz
- (p
-line
), fp
) == NULL
) {
355 return (*line
? line
: NULL
); /* EOF */
366 * Is input line too long?
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.
375 (void) ungetc(*p
, fp
);
379 /* trim trailing white space */
380 while (p
>= line
&& isspace(*(uchar_t
*)p
))
382 if (p
< line
) { /* empty line */
387 if (*p
== '\\') { /* continuation */
393 * Ignore comments. Comments start with '#'
394 * which must be preceded by a whitespace, unless
395 * if '#' is the first character in the line.
398 while (p
= strchr(p
, '#')) {
399 if (p
== line
|| isspace(*(p
-1))) {
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.
415 while ((c
= getc(fp
)) != EOF
) {
417 if (*p
== '\n') /* end of the long line */
419 else if (*p
== '\\') { /* continuation */
420 if (getc(fp
) == EOF
) /* ignore next char */
425 "map %s: line too long (max %d chars)",
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.
439 get_retry(char *opts
)
442 char buf
[MAXOPTSLEN
];
443 char *p
, *pb
, *lasts
;
448 (void) strcpy(buf
, opts
);
450 while (p
= (char *)strtok_r(pb
, ",", &lasts
)) {
452 if (strncmp(p
, "retry=", 6) == 0)
455 return (retry
> 0 ? retry
: 0);
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.
467 * stropt will return -1 on error.
470 str_opt(struct mnttab
*mnt
, char *opt
, char **sval
)
475 * is "opt" in the options field?
477 if (str
= hasmntopt(mnt
, opt
)) {
480 (*str
== ',' || *str
== '\0')) {
481 syslog(LOG_ERR
, "Bad option field");
484 comma
= strchr(str
, ',');
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.
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.
511 macro_expand(key
, pline
, plineq
, size
)
512 char *key
, *pline
, *plineq
;
515 register char *p
, *q
;
516 register char *bp
, *bq
;
518 char buffp
[LINESZ
], buffq
[LINESZ
];
519 char namebuf
[64], *pn
;
522 char procbuf
[SYS_NMLN
];
525 p
= pline
; q
= plineq
;
526 bp
= buffp
; bq
= buffq
;
529 if (*p
== '&' && *q
== ' ') { /* insert key */
531 * make sure we don't overflow buffer
533 if ((int)((bp
- buffp
) + strlen(key
)) < size
) {
534 for (s
= key
; *s
; s
++) {
549 if (*p
== '$' && *q
== ' ') { /* insert env var */
554 while (*p
&& *p
!= '}') {
562 while (*p
&& (*p
== '_' || isalnum(*p
))) {
571 /* not found in env */
572 if (strcmp(namebuf
, "ARCH") == 0) {
573 if (arch(procbuf
, sizeof (procbuf
),
576 } else if (strcmp(namebuf
, "CPU") == 0) {
577 if (cpu(procbuf
, sizeof (procbuf
)))
579 } else if (strcmp(namebuf
, "HOST") == 0) {
582 } else if (strcmp(namebuf
, "KARCH") == 0) {
583 if (arch(procbuf
, sizeof (procbuf
),
586 } else if (strcmp(namebuf
, "OSREL") == 0) {
589 } else if (strcmp(namebuf
, "OSNAME") == 0) {
592 } else if (strcmp(namebuf
, "OSVERS") == 0) {
595 } else if (strcmp(namebuf
, "NATISA") == 0) {
596 if (natisa(isaname
, sizeof (isaname
)))
598 } else if (strcmp(namebuf
, "PLATFORM") == 0) {
599 if (platform(procbuf
, sizeof (procbuf
)))
605 if ((int)((bp
- buffp
) + strlen(s
)) < size
) {
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
626 if (bp
- buffp
== size
- 1) {
628 * There was not enough room for at least two more
629 * characters, return with an error.
634 * The total number of characters so far better be less
635 * than the size of buffer passed in.
646 * We know buffp/buffq will fit in pline/plineq since we
647 * processed at most size characters.
649 (void) strcpy(pline
, buffp
);
650 (void) strcpy(plineq
, buffq
);
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
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]
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: ' ^^^^^ ^ ^^ ^ ^ ^ '
672 register int escaped
, inquote
, inbracket
, quoted
;
673 register char *ip
, *bp
, *qp
;
676 escaped
= inquote
= inbracket
= quoted
= 0;
678 for (ip
= str
, bp
= buf
, qp
= qbuf
; *ip
; ip
++) {
695 if (inbracket
> 0) inbracket
--;
700 *qp
++ = (inquote
|| escaped
) ? '^'
701 : ((inbracket
&& (*ip
== ':')) ? '^' : ' ');
709 (void) strcpy(str
, buf
);
713 * If str is enclosed in [brackets], trim them off.
719 char *b
= *s
+ strlen(*s
) - 1;
728 * Removes trailing spaces from string "s".
734 char *p
= &s
[strlen(s
) - 1];
736 while (p
>= s
&& isspace(*(uchar_t
*)p
))
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.
747 auto_rddir_malloc(unsigned nbytes
)
752 if ((p
= malloc(nbytes
)) == NULL
) {
754 * No memory, free rddir caches and try again
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");
762 mutex_unlock(&cleanup_lock
);
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.
778 auto_rddir_strdup(const char *s1
)
783 if ((s2
= strdup(s1
)) == NULL
) {
785 * No memory, free rddir caches and try again
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");
793 mutex_unlock(&cleanup_lock
);
805 * Returns a pointer to the entry corresponding to 'name' if found,
806 * otherwise it returns NULL.
809 btree_lookup(struct dir_entry
*head
, char *name
)
811 register struct dir_entry
*p
;
812 register int direction
;
814 for (p
= head
; p
!= NULL
; ) {
815 direction
= strcmp(name
, p
->name
);
826 * Add entry to binary tree
827 * Duplicate entries are not added
830 btree_enter(struct dir_entry
**head
, struct dir_entry
*ent
)
832 register struct dir_entry
*p
, *prev
= NULL
;
833 register int direction
;
835 ent
->right
= ent
->left
= NULL
;
841 for (p
= *head
; p
!= NULL
; ) {
843 direction
= strcmp(ent
->name
, p
->name
);
844 if (direction
== 0) {
846 * entry already in btree
854 assert(prev
!= NULL
);
857 else prev
->left
= ent
;
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
869 add_dir_entry(char *name
, struct dir_entry
**list
, struct dir_entry
**last
)
871 struct dir_entry
*e
, *l
;
873 if ((*list
!= NULL
) && (*last
== NULL
)) {
875 * walk the list to find last element
877 for (l
= *list
; l
!= NULL
; l
= l
->next
)
881 if (btree_lookup(*list
, name
) == NULL
) {
883 * not a duplicate, add it to list
885 /* LINTED pointer alignment */
886 e
= (struct dir_entry
*)
887 auto_rddir_malloc(sizeof (struct dir_entry
));
890 (void) memset((char *)e
, 0, sizeof (*e
));
891 e
->name
= auto_rddir_strdup(name
);
892 if (e
->name
== NULL
) {
904 * append to end of list
906 assert(*last
!= NULL
);
913 btree_enter(list
, e
);
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.
923 #define FMT_BUFSIZ 1024
926 trace_prt(int id
, char *fmt
, ...)
930 char buf
[FMT_BUFSIZ
];
933 (void) sprintf(buf
, "t%u\t%s", thr_self(), fmt
);
937 (void) vfprintf(stderr
, fmt
, args
);
942 * Extract the isalist(5) for userland from the kernel.
948 size_t bufsize
= BUFSIZ
; /* wild guess */
951 buf
= malloc(bufsize
);
953 ret
= sysinfo(SI_ISALIST
, buf
, bufsize
);
958 buf
= realloc(buf
, bufsize
);
961 } while (buf
!= NULL
);
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.
972 bitness(char *isaname
)
974 if (strcmp(isaname
, "sparc") == 0 ||
975 strcmp(isaname
, "i386") == 0)
978 if (strcmp(isaname
, "sparcv9") == 0 ||
979 strcmp(isaname
, "amd64") == 0)
986 * Determine the application architecture (derived from uname -m) to expand
987 * the $ARCH and $KARCH macros.
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.
994 arch(char *buf
, size_t bufsize
, bool_t karch
)
998 ret
= sysinfo(SI_MACHINE
, buf
, bufsize
);
1001 if (!karch
&& strncmp(buf
, "sun4", 4) == 0)
1002 (void) strlcpy(buf
, "sun4", bufsize
);
1007 * Determine the basic ISA (uname -p) to expand the $CPU macro.
1010 cpu(char *buf
, size_t bufsize
)
1014 ret
= sysinfo(SI_ARCHITECTURE
, buf
, bufsize
);
1022 * Find the left-most element in the isalist that matches our idea of a
1025 * On machines with only one ABI, this is usually the same as uname -p.
1028 natisa(char *buf
, size_t bufsize
)
1034 if ((list
= isalist()) == NULL
)
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 */
1042 if (isa
== 0 || bits
== 0) {
1044 return (0); /* can't figure it out :( */
1047 (void) strncpy(buf
, isa
, bufsize
);
1054 * Determine the platform (uname -i) to expand the $PLATFORM macro.
1057 platform(char *buf
, size_t bufsize
)
1061 ret
= sysinfo(SI_PLATFORM
, buf
, bufsize
);
1069 * Set environment variables
1072 put_automountd_env(void)
1074 char defval
[PATH_MAX
], *p
, *a
, *c
;
1075 int ret
= 0, bufsz
= PATH_MAX
;
1077 ret
= autofs_smf_get_prop("environment", defval
, DEFAULT_INSTANCE
,
1078 SCF_TYPE_ASTRING
, AUTOMOUNTD
, &bufsz
);
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.
1090 while ((p
= strchr(c
, ',')) != NULL
) {
1091 if (*(p
- 1) == '\\') {
1096 if ((c
= strchr(a
, '=')) != NULL
)
1101 if ((c
= strchr(a
, '=')) != NULL
)