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]
24 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
35 #include <sys/param.h>
37 #include <rpcsvc/nfs_prot.h>
44 #include <sys/types.h>
47 #include "automount.h"
49 static int read_execout(char *key
, char **lp
, char *fname
, char *line
,
51 static int call_read_execout(char *key
, char **lp
, char *fname
, char *line
,
53 static FILE *file_open(char *, char *, char **, char ***);
56 * Initialize the stack
59 init_files(char **stack
, char ***stkptr
)
62 * The call is bogus for automountd since the stack is
63 * is more appropriately initialized in the thread-private
66 if (stack
== NULL
&& stkptr
== NULL
)
68 (void) stack_op(INIT
, NULL
, stack
, stkptr
);
72 getmapent_files(key
, mapname
, ml
, stack
, stkptr
, iswildcard
, isrestricted
)
76 char **stack
, ***stkptr
;
82 char word
[MAXPATHLEN
+1], wordq
[MAXPATHLEN
+1];
83 char linebuf
[LINESZ
], lineqbuf
[LINESZ
];
86 char fname
[MAXFILENAMELEN
]; /* /etc prepended to mapname if reqd */
91 if ((fp
= file_open(mapname
, fname
, stack
, stkptr
)) == NULL
) {
92 nserr
= __NSW_UNAVAIL
;
96 if (stat(fname
, &stbuf
) < 0) {
97 nserr
= __NSW_UNAVAIL
;
102 * If the file has its execute bit on then
103 * assume it's an executable map.
104 * Execute it and pass the key as an argument.
105 * Expect to get a map entry on the stdout.
106 * Ignore the "x" bit on restricted maps.
108 if (!isrestricted
&& (stbuf
.st_mode
& S_IXUSR
)) {
113 "\tExecutable map: map=%s key=%s\n",
117 rc
= call_read_execout(key
, &lp
, fname
, ml
->linebuf
, LINESZ
);
120 nserr
= __NSW_UNAVAIL
;
124 if (strlen(ml
->linebuf
) == 0) {
125 nserr
= __NSW_NOTFOUND
;
129 unquote(ml
->linebuf
, ml
->lineqbuf
);
130 nserr
= __NSW_SUCCESS
;
136 * It's just a normal map file.
137 * Search for the entry with the required key.
140 lp
= get_line(fp
, fname
, linebuf
, sizeof (linebuf
));
142 nserr
= __NSW_NOTFOUND
;
145 if (verbose
&& syntaxok
&& isspace(*(uchar_t
*)lp
)) {
148 "leading space in map entry \"%s\" in %s",
153 if ((getword(word
, wordq
, &lp
, &lq
, ' ', sizeof (word
))
154 == -1) || (word
[0] == '\0'))
156 if (strcmp(word
, key
) == 0)
158 if (word
[0] == '*' && word
[1] == '\0') {
163 if (word
[0] == '+') {
164 nserr
= getmapent(key
, word
+1, ml
, stack
, stkptr
,
165 iswildcard
, isrestricted
);
166 if (nserr
== __NSW_SUCCESS
)
172 * sanity check each map entry key against
173 * the lookup key as the map is searched.
175 if (verbose
&& syntaxok
) { /* sanity check entry */
180 "bad key \"%s\" in direct map %s\n",
184 if (strchr(word
, '/')) {
187 "bad key \"%s\" in indirect map %s\n",
194 (void) strcpy(ml
->linebuf
, lp
);
195 (void) strcpy(ml
->lineqbuf
, lq
);
196 nserr
= __NSW_SUCCESS
;
199 (void) stack_op(POP
, NULL
, stack
, stkptr
);
208 getmapkeys_files(mapname
, list
, error
, cache_time
, stack
, stkptr
)
210 struct dir_entry
**list
;
213 char **stack
, ***stkptr
;
216 char word
[MAXPATHLEN
+1], wordq
[MAXPATHLEN
+1];
217 char linebuf
[LINESZ
], lineqbuf
[LINESZ
];
220 char fname
[MAXFILENAMELEN
]; /* /etc prepended to mapname if reqd */
223 struct dir_entry
*last
= NULL
;
226 trace_prt(1, "getmapkeys_files %s\n", mapname
);
228 *cache_time
= RDDIR_CACHE_TIME
;
229 if ((fp
= file_open(mapname
, fname
, stack
, stkptr
)) == NULL
) {
231 nserr
= __NSW_UNAVAIL
;
234 if (fseek(fp
, 0L, SEEK_SET
) == -1) {
236 nserr
= __NSW_UNAVAIL
;
240 if (stat(fname
, &stbuf
) < 0) {
242 nserr
= __NSW_UNAVAIL
;
247 * If the file has its execute bit on then
248 * assume it's an executable map.
249 * I don't know how to list executable maps, return
252 if (stbuf
.st_mode
& S_IXUSR
) {
254 nserr
= __NSW_SUCCESS
;
258 * It's just a normal map file.
259 * List entries one line at a time.
262 lp
= get_line(fp
, fname
, linebuf
, sizeof (linebuf
));
264 nserr
= __NSW_SUCCESS
;
267 if (syntaxok
&& isspace(*(uchar_t
*)lp
)) {
270 "leading space in map entry \"%s\" in %s",
275 if ((getword(word
, wordq
, &lp
, &lq
, ' ', MAXFILENAMELEN
)
276 == -1) || (word
[0] == '\0'))
279 * Wildcard entries should be ignored and this should be
280 * the last entry read to corroborate the search through
281 * files, i.e., search for key until a wildcard is reached.
283 if (word
[0] == '*' && word
[1] == '\0')
285 if (word
[0] == '+') {
289 getmapkeys(word
+1, list
, error
, cache_time
,
292 * the list may have been updated, therefore
293 * our 'last' may no longer be valid
299 if (add_dir_entry(word
, list
, &last
) != 0) {
303 assert(last
!= NULL
);
306 nserr
= __NSW_SUCCESS
;
309 (void) stack_op(POP
, NULL
, stack
, stkptr
);
315 * list of entries found
323 loadmaster_files(mastermap
, defopts
, stack
, stkptr
)
326 char **stack
, ***stkptr
;
330 char *line
, *dir
, *map
, *opts
;
331 char linebuf
[LINESZ
];
333 char fname
[MAXFILENAMELEN
]; /* /etc prepended to mapname if reqd */
336 if ((fp
= file_open(mastermap
, fname
, stack
, stkptr
)) == NULL
)
337 return (__NSW_UNAVAIL
);
339 while ((line
= get_line(fp
, fname
, linebuf
,
340 sizeof (linebuf
))) != NULL
) {
341 unquote(line
, lineq
);
342 if (macro_expand("", line
, lineq
, LINESZ
)) {
344 "map %s: line too long (max %d chars)",
345 mastermap
, LINESZ
- 1);
349 while (*dir
&& isspace(*dir
))
355 while (*map
&& !isspace(*map
)) map
++;
361 while (*opts
&& isspace(*opts
))
368 * Check for no embedded blanks.
370 if (strcspn(opts
, " ") == strlen(opts
)) {
372 (void) loadmaster_map(dir
, opts
, stack
, stkptr
);
374 pr_msg("Warning: invalid entry for %s in %s ignored.\n", dir
, fname
);
379 while (*map
&& isspace(*map
))
384 while (*opts
&& !isspace(*opts
))
388 while (*opts
&& isspace(*opts
))
396 * Check for no embedded blanks.
398 if (strcspn(opts
, " ") == strlen(opts
)) {
399 dirinit(dir
, map
, opts
, 0, stack
, stkptr
);
401 pr_msg("Warning: invalid entry for %s in %s ignored.\n", dir
, fname
);
408 (void) stack_op(POP
, NULL
, stack
, stkptr
);
411 return (done
? __NSW_SUCCESS
: __NSW_NOTFOUND
);
415 loaddirect_files(map
, local_map
, opts
, stack
, stkptr
)
416 char *map
, *local_map
, *opts
;
417 char **stack
, ***stkptr
;
421 char *line
, *p1
, *p2
;
422 char linebuf
[LINESZ
];
423 char fname
[MAXFILENAMELEN
]; /* /etc prepended to mapname if reqd */
425 if ((fp
= file_open(map
, fname
, stack
, stkptr
)) == NULL
)
426 return (__NSW_UNAVAIL
);
428 while ((line
= get_line(fp
, fname
, linebuf
,
429 sizeof (linebuf
))) != NULL
) {
431 while (*p1
&& isspace(*p1
))
436 while (*p2
&& !isspace(*p2
))
441 (void) loaddirect_map(p1
, local_map
, opts
, stack
,
444 dirinit(p1
, local_map
, opts
, 1, stack
, stkptr
);
449 (void) stack_op(POP
, NULL
, stack
, stkptr
);
452 return (done
? __NSW_SUCCESS
: __NSW_NOTFOUND
);
456 * This procedure opens the file and pushes it onto the
457 * the stack. Only if a file is opened successfully, is
458 * it pushed onto the stack
461 file_open(map
, fname
, stack
, stkptr
)
463 char **stack
, ***stkptr
;
468 /* prepend an "/etc" */
469 (void) strcpy(fname
, "/etc/");
470 (void) strcat(fname
, map
);
472 (void) strcpy(fname
, map
);
474 fp
= fopen(fname
, "r");
477 if (!stack_op(PUSH
, fname
, stack
, stkptr
)) {
486 * reimplemnted to be MT-HOT.
489 stack_op(op
, name
, stack
, stkptr
)
492 char **stack
, ***stkptr
;
495 char **stk_top
= &stack
[STACKSIZ
- 1];
498 * the stackptr points to the next empty slot
499 * for PUSH: put the element and increment stkptr
500 * for POP: decrement stkptr and free
505 for (ptr
= stack
; ptr
!= stk_top
; ptr
++)
510 for (ptr
= stack
; ptr
!= stk_top
; ptr
++)
513 trace_prt(1, " ERASE %s\n", *ptr
);
520 if (*stkptr
== stk_top
)
522 for (ptr
= stack
; ptr
!= *stkptr
; ptr
++)
523 if (*ptr
&& (strcmp(*ptr
, name
) == 0)) {
527 trace_prt(1, " PUSH %s\n", name
);
528 if ((**stkptr
= strdup(name
)) == NULL
) {
529 syslog(LOG_ERR
, "stack_op: Memory alloc failed : %m");
535 if (*stkptr
!= stack
)
538 syslog(LOG_ERR
, "Attempt to pop empty stack\n");
540 if (*stkptr
&& **stkptr
) {
542 trace_prt(1, " POP %s\n", **stkptr
);
552 #define READ_EXECOUT_ARGS 3
555 * read_execout(char *key, char **lp, char *fname, char *line, int linesz)
556 * A simpler, multithreaded implementation of popen(). Used due to
557 * non multithreaded implementation of popen() (it calls vfork()) and a
558 * significant bug in execl().
559 * Returns 0 on OK or -1 on error.
562 read_execout(char *key
, char **lp
, char *fname
, char *line
, int linesz
)
567 char *args
[READ_EXECOUT_ARGS
];
571 syslog(LOG_ERR
, "read_execout: Cannot create pipe");
575 /* setup args for execv */
576 if (((args
[0] = strdup(fname
)) == NULL
) ||
577 ((args
[1] = strdup(key
)) == NULL
)) {
579 syslog(LOG_ERR
, "read_execout: Memory allocation failed");
585 trace_prt(1, "\tread_execout: forking .....\n");
587 switch ((child_pid
= fork1())) {
589 syslog(LOG_ERR
, "read_execout: Cannot fork");
597 if (fcntl(p
[1], F_DUPFD
, 1) != 1) {
599 "read_execout: dup of stdout failed");
603 execv(fname
, &args
[0]);
612 * wait for child to complete. Note we read after the
613 * child exits to guarantee a full pipe.
615 while (waitpid(child_pid
, &status
, 0) < 0) {
616 /* if waitpid fails with EINTR, restart */
617 if (errno
!= EINTR
) {
623 if ((fp0
= fdopen(p
[0], "r")) != NULL
) {
624 *lp
= get_line(fp0
, fname
, line
, linesz
);
639 trace_prt(1, "\tread_execout: map=%s key=%s line=%s\n",
647 automountd_do_exec_map(void *cookie
, char *argp
, size_t arg_size
,
648 door_desc_t
*dfd
, uint_t n_desc
)
655 command
= (command_t
*)argp
;
657 if (sizeof (*command
) != arg_size
) {
659 syslog(LOG_ERR
, "read_execout: invalid door arguments");
660 door_return((char *)&rc
, sizeof (rc
), NULL
, 0);
663 rc
= read_execout(command
->key
, &lp
, command
->file
, line
, LINESZ
);
667 * read_execout returned an error, return 0 to the door_client
668 * to indicate failure
671 door_return((char *)&rc
, sizeof (rc
), NULL
, 0);
673 door_return((char *)line
, LINESZ
, NULL
, 0);
675 trace_prt(1, "automountd_do_exec_map, door return failed %s, %s\n",
676 command
->file
, strerror(errno
));
677 door_return(NULL
, 0, NULL
, 0);
681 call_read_execout(char *key
, char **lp
, char *fname
, char *line
,
688 bzero(&command
, sizeof (command
));
689 (void) strlcpy(command
.file
, fname
, MAXPATHLEN
);
690 (void) strlcpy(command
.key
, key
, MAXOPTSLEN
);
693 trace_prt(1, "call_read_execout %s %s\n", fname
, key
);
694 darg
.data_ptr
= (char *)&command
;
695 darg
.data_size
= sizeof (command
);
696 darg
.desc_ptr
= NULL
;
701 ret
= door_call(did_exec_map
, &darg
);