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 dirinit(char *mntpnt
, char *map
, char *opts
, int direct
, char **stack
,
81 if (strcmp(map
, "-null") == 0) {
82 if (strcmp(mntpnt
, "/-") == 0)
87 p
= mntpnt
+ (strlen(mntpnt
) - 1);
89 *p
= '\0'; /* trim trailing / */
91 pr_msg("dir %s must start with '/'", mntpnt
);
94 if (p
= check_hier(mntpnt
)) {
95 pr_msg("hierarchical mountpoint: %s and %s",
101 * If it's a direct map then call dirinit
102 * for every map entry.
104 if ((strcmp(mntpnt
, "/-") == 0) && !(nodirect_map
)) {
105 (void) loaddirect_map(map
, map
, opts
, stack
, stkptr
);
110 dir
= (struct autodir
*)malloc(sizeof (*dir
));
113 dir
->dir_name
= strdup(mntpnt
);
114 if (dir
->dir_name
== NULL
)
116 dir
->dir_map
= strdup(map
);
117 if (dir
->dir_map
== NULL
)
119 dir
->dir_opts
= strdup(opts
);
120 if (dir
->dir_opts
== NULL
)
122 dir
->dir_direct
= direct
;
123 dir
->dir_remount
= 0;
124 dir
->dir_next
= NULL
;
127 * Append to dir chain
129 if (dir_head
== NULL
)
132 dir_tail
->dir_next
= dir
;
134 dir
->dir_prev
= dir_tail
;
146 pr_msg("dirinit: memory allocation failed");
150 * Check whether the mount point is a
151 * subdirectory or a parent directory
152 * of any previously mounted automount
159 register struct autodir
*dir
;
160 register char *p
, *q
;
162 for (dir
= dir_head
; dir
; dir
= dir
->dir_next
) {
165 for (; *p
== *q
; p
++, q
++)
168 if (*p
== '/' && *q
== '\0')
169 return (dir
->dir_name
);
170 if (*p
== '\0' && *q
== '/')
171 return (dir
->dir_name
);
172 if (*p
== '\0' && *q
== '\0')
175 return (NULL
); /* it's not a subdir or parent */
179 * Gets the next token from the string "p" and copies it into "w". The "wq" is
180 * a quote vector for "w" and is derived from "pq", which is a quote vector for
181 * "p". Delim is the character to be used as a delimiter for the scan. A space
182 * means "whitespace". The call to getword must provide buffers w and wq of size
183 * at least wordsz. getword() will pass strings of maximum length (wordsz-1),
184 * since it needs to null terminate the string.
185 * Returns 0 on ok and -1 on error.
188 getword(char *w
, char *wq
, char **p
, char **pq
, char delim
, int wordsz
)
197 "getword: input word size %d must be > 0", wordsz
);
201 while ((delim
== ' ' ? isspace(**p
) : **p
== delim
) && **pq
== ' ')
205 !((delim
== ' ' ? isspace(**p
) : **p
== delim
) &&
211 "maximum word length (%d) exceeded", wordsz
);
224 * get_line attempts to get a line from the map, upto LINESZ. A line in
225 * the map is a concatenation of lines if the continuation symbol '\'
226 * is used at the end of the line. Returns line on success, a NULL on
227 * EOF, and an empty string on lines > linesz.
230 get_line(FILE *fp
, char *map
, char *line
, int linesz
)
232 register char *p
= line
;
239 if (fgets(p
, linesz
- (p
-line
), fp
) == NULL
) {
240 return (*line
? line
: NULL
); /* EOF */
251 * Is input line too long?
256 * Perhaps last char read was '\'. Reinsert it
257 * into the stream to ease the parsing when we
258 * read the rest of the line to discard.
260 (void) ungetc(*p
, fp
);
264 /* trim trailing white space */
265 while (p
>= line
&& isspace(*(uchar_t
*)p
))
267 if (p
< line
) { /* empty line */
272 if (*p
== '\\') { /* continuation */
278 * Ignore comments. Comments start with '#'
279 * which must be preceded by a whitespace, unless
280 * if '#' is the first character in the line.
283 while (p
= strchr(p
, '#')) {
284 if (p
== line
|| isspace(*(p
-1))) {
296 * discard rest of line and return an empty string.
297 * done to set the stream to the correct place when
298 * we are done with this line.
300 while ((c
= getc(fp
)) != EOF
) {
302 if (*p
== '\n') /* end of the long line */
304 else if (*p
== '\\') { /* continuation */
305 if (getc(fp
) == EOF
) /* ignore next char */
310 "map %s: line too long (max %d chars)",
319 * Gets the retry=n entry from opts.
320 * Returns 0 if retry=n is not present in option string,
321 * retry=n is invalid, or when option string is NULL.
324 get_retry(char *opts
)
327 char buf
[MAXOPTSLEN
];
328 char *p
, *pb
, *lasts
;
333 (void) strcpy(buf
, opts
);
335 while (p
= (char *)strtok_r(pb
, ",", &lasts
)) {
337 if (strncmp(p
, "retry=", 6) == 0)
340 return (retry
> 0 ? retry
: 0);
344 * Returns zero if "opt" is found in mnt->mnt_opts, setting
345 * *sval to whatever follows the equal sign after "opt".
346 * str_opt allocates a string long enough to store the value of
347 * "opt" plus a terminating null character and returns it as *sval.
348 * It is the responsability of the caller to deallocate *sval.
349 * *sval will be equal to NULL upon return if either "opt=" is not found,
350 * or "opt=" has no value associated with it.
352 * stropt will return -1 on error.
355 str_opt(struct mnttab
*mnt
, char *opt
, char **sval
)
360 * is "opt" in the options field?
362 if (str
= hasmntopt(mnt
, opt
)) {
365 (*str
== ',' || *str
== '\0')) {
366 syslog(LOG_ERR
, "Bad option field");
369 comma
= strchr(str
, ',');
384 * Performs text expansions in the string "pline".
385 * "plineq" is the quote vector for "pline".
386 * An identifier prefixed by "$" is replaced by the
387 * corresponding environment variable string. A "&"
388 * is replaced by the key string for the map entry.
390 * This routine will return an error (non-zero) if *size* would be
391 * exceeded after expansion, indicating that the macro_expand failed.
392 * This is to prevent writing past the end of pline and plineq.
393 * Both pline and plineq are left untouched in such error case.
396 macro_expand(key
, pline
, plineq
, size
)
397 char *key
, *pline
, *plineq
;
400 register char *p
, *q
;
401 register char *bp
, *bq
;
403 char buffp
[LINESZ
], buffq
[LINESZ
];
404 char namebuf
[64], *pn
;
407 char procbuf
[SYS_NMLN
];
410 p
= pline
; q
= plineq
;
411 bp
= buffp
; bq
= buffq
;
414 if (*p
== '&' && *q
== ' ') { /* insert key */
416 * make sure we don't overflow buffer
418 if ((int)((bp
- buffp
) + strlen(key
)) < size
) {
419 for (s
= key
; *s
; s
++) {
434 if (*p
== '$' && *q
== ' ') { /* insert env var */
439 while (*p
&& *p
!= '}') {
447 while (*p
&& (*p
== '_' || isalnum(*p
))) {
456 /* not found in env */
457 if (strcmp(namebuf
, "ARCH") == 0) {
458 if (arch(procbuf
, sizeof (procbuf
),
461 } else if (strcmp(namebuf
, "CPU") == 0) {
462 if (cpu(procbuf
, sizeof (procbuf
)))
464 } else if (strcmp(namebuf
, "HOST") == 0) {
467 } else if (strcmp(namebuf
, "KARCH") == 0) {
468 if (arch(procbuf
, sizeof (procbuf
),
471 } else if (strcmp(namebuf
, "OSREL") == 0) {
474 } else if (strcmp(namebuf
, "OSNAME") == 0) {
477 } else if (strcmp(namebuf
, "OSVERS") == 0) {
480 } else if (strcmp(namebuf
, "NATISA") == 0) {
481 if (natisa(isaname
, sizeof (isaname
)))
483 } else if (strcmp(namebuf
, "PLATFORM") == 0) {
484 if (platform(procbuf
, sizeof (procbuf
)))
490 if ((int)((bp
- buffp
) + strlen(s
)) < size
) {
506 * Since buffp needs to be null terminated, we need to
507 * check that there's still room in the buffer to
508 * place at least two more characters, *p and the
511 if (bp
- buffp
== size
- 1) {
513 * There was not enough room for at least two more
514 * characters, return with an error.
519 * The total number of characters so far better be less
520 * than the size of buffer passed in.
531 * We know buffp/buffq will fit in pline/plineq since we
532 * processed at most size characters.
534 (void) strcpy(pline
, buffp
);
535 (void) strcpy(plineq
, buffq
);
541 * Removes backslashes, quotes and brackets from the string "str"
542 * and returns the quoting information in "qbuf". Character is
543 * considered escaped when it is
545 * preceded with '\' e.g. \a
546 * within quotes e.g. "string"
547 * a ':' in brackets e.g. [an:ip:6::ad::d:re:s:s]
549 * original str: 'the "brown" f\ox said: [fe80::20a:e4ff:fe35:8b0d]'
550 * unquoted str: 'the brown fox said: [fe80::20a:e4ff:fe35:8b0d]'
551 * and the qbuf: ' ^^^^^ ^ ^^ ^ ^ ^ '
557 register int escaped
, inquote
, inbracket
, quoted
;
558 register char *ip
, *bp
, *qp
;
561 escaped
= inquote
= inbracket
= quoted
= 0;
563 for (ip
= str
, bp
= buf
, qp
= qbuf
; *ip
; ip
++) {
580 if (inbracket
> 0) inbracket
--;
585 *qp
++ = (inquote
|| escaped
) ? '^'
586 : ((inbracket
&& (*ip
== ':')) ? '^' : ' ');
594 (void) strcpy(str
, buf
);
598 * If str is enclosed in [brackets], trim them off.
604 char *b
= *s
+ strlen(*s
) - 1;
613 * Removes trailing spaces from string "s".
619 char *p
= &s
[strlen(s
) - 1];
621 while (p
>= s
&& isspace(*(uchar_t
*)p
))
626 * try to allocate memory using malloc, if malloc fails, then flush the
627 * rddir caches, and retry. If the second allocation after the readdir
628 * caches have been flushed fails too, then return NULL to indicate
629 * memory could not be allocated.
632 auto_rddir_malloc(unsigned nbytes
)
637 if ((p
= malloc(nbytes
)) == NULL
) {
639 * No memory, free rddir caches and try again
641 mutex_lock(&cleanup_lock
);
642 cond_signal(&cleanup_start_cv
);
643 if (cond_wait(&cleanup_done_cv
, &cleanup_lock
)) {
644 mutex_unlock(&cleanup_lock
);
645 syslog(LOG_ERR
, "auto_rddir_malloc interrupted\n");
647 mutex_unlock(&cleanup_lock
);
659 * try to strdup a string, if it fails, then flush the rddir caches,
660 * and retry. If the second strdup fails, return NULL to indicate failure.
663 auto_rddir_strdup(const char *s1
)
668 if ((s2
= strdup(s1
)) == NULL
) {
670 * No memory, free rddir caches and try again
672 mutex_lock(&cleanup_lock
);
673 cond_signal(&cleanup_start_cv
);
674 if (cond_wait(&cleanup_done_cv
, &cleanup_lock
)) {
675 mutex_unlock(&cleanup_lock
);
676 syslog(LOG_ERR
, "auto_rddir_strdup interrupted\n");
678 mutex_unlock(&cleanup_lock
);
690 * Returns a pointer to the entry corresponding to 'name' if found,
691 * otherwise it returns NULL.
694 btree_lookup(struct dir_entry
*head
, char *name
)
696 register struct dir_entry
*p
;
697 register int direction
;
699 for (p
= head
; p
!= NULL
; ) {
700 direction
= strcmp(name
, p
->name
);
711 * Add entry to binary tree
712 * Duplicate entries are not added
715 btree_enter(struct dir_entry
**head
, struct dir_entry
*ent
)
717 register struct dir_entry
*p
, *prev
= NULL
;
718 register int direction
;
720 ent
->right
= ent
->left
= NULL
;
726 for (p
= *head
; p
!= NULL
; ) {
728 direction
= strcmp(ent
->name
, p
->name
);
729 if (direction
== 0) {
731 * entry already in btree
739 assert(prev
!= NULL
);
742 else prev
->left
= ent
;
746 * If entry doesn't exist already, add it to the linear list
747 * after '*last' and to the binary tree list.
748 * If '*last == NULL' then the list is walked till the end.
749 * *last is always set to the new element after successful completion.
750 * if entry already exists '*last' is only updated if not previously
754 add_dir_entry(char *name
, struct dir_entry
**list
, struct dir_entry
**last
)
756 struct dir_entry
*e
, *l
;
758 if ((*list
!= NULL
) && (*last
== NULL
)) {
760 * walk the list to find last element
762 for (l
= *list
; l
!= NULL
; l
= l
->next
)
766 if (btree_lookup(*list
, name
) == NULL
) {
768 * not a duplicate, add it to list
770 /* LINTED pointer alignment */
771 e
= (struct dir_entry
*)
772 auto_rddir_malloc(sizeof (struct dir_entry
));
775 (void) memset((char *)e
, 0, sizeof (*e
));
776 e
->name
= auto_rddir_strdup(name
);
777 if (e
->name
== NULL
) {
789 * append to end of list
791 assert(*last
!= NULL
);
798 btree_enter(list
, e
);
804 * Print trace output.
805 * Like fprintf(stderr, fmt, ...) except that if "id" is nonzero, the output
806 * is preceeded by the ID of the calling thread.
808 #define FMT_BUFSIZ 1024
811 trace_prt(int id
, char *fmt
, ...)
815 char buf
[FMT_BUFSIZ
];
818 (void) sprintf(buf
, "t%u\t%s", thr_self(), fmt
);
822 (void) vfprintf(stderr
, fmt
, args
);
827 * Extract the isalist(5) for userland from the kernel.
833 size_t bufsize
= BUFSIZ
; /* wild guess */
836 buf
= malloc(bufsize
);
838 ret
= sysinfo(SI_ISALIST
, buf
, bufsize
);
843 buf
= realloc(buf
, bufsize
);
846 } while (buf
!= NULL
);
852 * Classify isa's as to bitness of the corresponding ABIs.
853 * isa's which have no "official" system ABI are returned
854 * unrecognised i.e. zero bits.
857 bitness(char *isaname
)
859 if (strcmp(isaname
, "sparc") == 0 ||
860 strcmp(isaname
, "i386") == 0)
863 if (strcmp(isaname
, "sparcv9") == 0 ||
864 strcmp(isaname
, "amd64") == 0)
871 * Determine the application architecture (derived from uname -m) to expand
872 * the $ARCH and $KARCH macros.
874 * We need to substitute "sun4" for "sun4u", "sun4v", ... for backward
875 * compatibility. When kflag is set (like arch -k), the unmodifed value is
879 arch(char *buf
, size_t bufsize
, bool_t karch
)
883 ret
= sysinfo(SI_MACHINE
, buf
, bufsize
);
886 if (!karch
&& strncmp(buf
, "sun4", 4) == 0)
887 (void) strlcpy(buf
, "sun4", bufsize
);
892 * Determine the basic ISA (uname -p) to expand the $CPU macro.
895 cpu(char *buf
, size_t bufsize
)
899 ret
= sysinfo(SI_ARCHITECTURE
, buf
, bufsize
);
907 * Find the left-most element in the isalist that matches our idea of a
910 * On machines with only one ABI, this is usually the same as uname -p.
913 natisa(char *buf
, size_t bufsize
)
919 if ((list
= isalist()) == NULL
)
922 for (isa
= strtok_r(list
, " ", &lasts
);
923 isa
; isa
= strtok_r(0, " ", &lasts
))
924 if ((bits
= bitness(isa
)) != 0)
925 break; /* ignore "extension" architectures */
927 if (isa
== 0 || bits
== 0) {
929 return (0); /* can't figure it out :( */
932 (void) strncpy(buf
, isa
, bufsize
);
939 * Determine the platform (uname -i) to expand the $PLATFORM macro.
942 platform(char *buf
, size_t bufsize
)
946 ret
= sysinfo(SI_PLATFORM
, buf
, bufsize
);
954 * Set environment variables
957 put_automountd_env(void)
959 char defval
[PATH_MAX
], *p
, *a
, *c
;
960 int ret
= 0, bufsz
= PATH_MAX
;
962 ret
= autofs_smf_get_prop("environment", defval
, DEFAULT_INSTANCE
,
963 SCF_TYPE_ASTRING
, AUTOMOUNTD
, &bufsz
);
969 * Environment variables can have more than one value
970 * seperated by a comma and there can be multiple
971 * environment variables. * a=b\,c,d=e. For multiple
972 * valued environment variable, values are seperated
973 * with an escape character.
975 while ((p
= strchr(c
, ',')) != NULL
) {
976 if (*(p
- 1) == '\\') {
981 if ((c
= strchr(a
, '=')) != NULL
)
986 if ((c
= strchr(a
, '=')) != NULL
)