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]
23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
28 * Copyright (c) 2011 by Delphix. All rights reserved.
32 #include "thr_uberdata.h"
33 #include <sys/libc_kernel.h>
34 #include <sys/procset.h>
41 #define ALL_POSIX_SPAWN_FLAGS \
42 (POSIX_SPAWN_RESETIDS | \
43 POSIX_SPAWN_SETPGROUP | \
44 POSIX_SPAWN_SETSIGDEF | \
45 POSIX_SPAWN_SETSIGMASK | \
46 POSIX_SPAWN_SETSCHEDPARAM | \
47 POSIX_SPAWN_SETSCHEDULER | \
48 POSIX_SPAWN_SETSIGIGN_NP | \
49 POSIX_SPAWN_NOSIGCHLD_NP | \
50 POSIX_SPAWN_WAITPID_NP | \
51 POSIX_SPAWN_NOEXECERR_NP)
54 int sa_psflags
; /* POSIX_SPAWN_* flags */
58 sigset_t sa_sigdefault
;
59 sigset_t sa_sigignore
;
63 typedef struct file_attr
{
64 struct file_attr
*fa_next
; /* circular list of file actions */
65 struct file_attr
*fa_prev
;
66 enum {FA_OPEN
, FA_CLOSE
, FA_DUP2
, FA_CLOSEFROM
} fa_type
;
67 int fa_need_dirbuf
; /* only consulted in the head action */
68 char *fa_path
; /* copied pathname for open() */
69 uint_t fa_pathsize
; /* size of fa_path[] array */
70 int fa_oflag
; /* oflag for open() */
71 mode_t fa_mode
; /* mode for open() */
72 int fa_filedes
; /* file descriptor for open()/close() */
73 int fa_newfiledes
; /* new file descriptor for dup2() */
77 #define __open64 __open
78 #define getdents64 getdents
79 #define dirent64_t dirent_t
81 extern int getdents64(int, dirent64_t
*, size_t);
84 extern const char **_environ
;
88 * Close all open file descriptors greater than or equal to lowfd.
89 * This is executed in the child of vfork(), so we must not call
90 * opendir() / readdir() because that would alter the parent's
91 * address space. We use the low-level getdents64() system call.
92 * Return non-zero on error.
95 spawn_closefrom(int lowfd
, void *buf
)
107 * Close lowfd right away as a hedge against failing
108 * to open the /proc file descriptor directory due
109 * all file descriptors being currently used up.
111 (void) __close(lowfd
++);
113 if ((procfd
= __open64("/proc/self/fd", O_RDONLY
, 0)) < 0) {
115 * We could not open the /proc file descriptor directory.
116 * Just fail and be done with it.
123 * Collect a bunch of open file descriptors and close them.
124 * Repeat until the directory is exhausted.
126 dp
= (dirent64_t
*)buf
;
127 if ((buflen
= getdents64(procfd
, dp
, DIRBUF
)) <= 0) {
128 (void) __close(procfd
);
131 dpend
= (dirent64_t
*)((uintptr_t)buf
+ buflen
);
133 /* skip '.', '..' and procfd */
134 if (dp
->d_name
[0] != '.' &&
135 (fd
= atoi(dp
->d_name
)) != procfd
&&
138 dp
= (dirent64_t
*)((uintptr_t)dp
+ dp
->d_reclen
);
139 } while (dp
< dpend
);
146 perform_flag_actions(spawn_attr_t
*sap
)
149 struct sigaction action
;
151 if (sap
->sa_psflags
& POSIX_SPAWN_SETSIGMASK
) {
152 (void) __lwp_sigmask(SIG_SETMASK
, &sap
->sa_sigmask
);
155 if (sap
->sa_psflags
& POSIX_SPAWN_SETSIGIGN_NP
) {
156 (void) memset(&action
, 0, sizeof (action
));
157 action
.sa_handler
= SIG_IGN
;
158 for (sig
= 1; sig
< NSIG
; sig
++) {
159 if (sigismember(&sap
->sa_sigignore
, sig
))
160 (void) __sigaction(sig
, &action
, NULL
);
164 if (sap
->sa_psflags
& POSIX_SPAWN_SETSIGDEF
) {
165 (void) memset(&action
, 0, sizeof (action
));
166 action
.sa_handler
= SIG_DFL
;
167 for (sig
= 1; sig
< NSIG
; sig
++) {
168 if (sigismember(&sap
->sa_sigdefault
, sig
))
169 (void) __sigaction(sig
, &action
, NULL
);
173 if (sap
->sa_psflags
& POSIX_SPAWN_RESETIDS
) {
174 if (setgid(getgid()) != 0 || setuid(getuid()) != 0)
178 if (sap
->sa_psflags
& POSIX_SPAWN_SETPGROUP
) {
179 if (setpgid(0, sap
->sa_pgroup
) != 0)
183 if (sap
->sa_psflags
& POSIX_SPAWN_SETSCHEDULER
) {
184 if (setparam(P_LWPID
, P_MYID
,
185 sap
->sa_schedpolicy
, sap
->sa_priority
) == -1)
187 } else if (sap
->sa_psflags
& POSIX_SPAWN_SETSCHEDPARAM
) {
188 if (setprio(P_LWPID
, P_MYID
, sap
->sa_priority
, NULL
) == -1)
196 perform_file_actions(file_attr_t
*fap
, void *dirbuf
)
198 file_attr_t
*froot
= fap
;
202 switch (fap
->fa_type
) {
204 fd
= __open(fap
->fa_path
, fap
->fa_oflag
, fap
->fa_mode
);
207 if (fd
!= fap
->fa_filedes
) {
208 if (__fcntl(fd
, F_DUP2FD
, fap
->fa_filedes
) < 0)
214 if (__close(fap
->fa_filedes
) == -1 &&
215 errno
!= EBADF
) /* already closed, no error */
219 fd
= __fcntl(fap
->fa_filedes
, F_DUP2FD
,
225 if (spawn_closefrom(fap
->fa_filedes
, dirbuf
))
229 } while ((fap
= fap
->fa_next
) != froot
);
235 forkflags(spawn_attr_t
*sap
)
240 if (sap
->sa_psflags
& POSIX_SPAWN_NOSIGCHLD_NP
)
241 flags
|= FORK_NOSIGCHLD
;
242 if (sap
->sa_psflags
& POSIX_SPAWN_WAITPID_NP
)
243 flags
|= FORK_WAITPID
;
250 * set_error() / get_error() are used to guarantee that the local variable
251 * 'error' is set correctly in memory on return from vfork() in the parent.
255 set_error(int *errp
, int err
)
257 return (*errp
= err
);
267 * For MT safety, do not invoke the dynamic linker after calling vfork().
268 * If some other thread was in the dynamic linker when this thread's parent
269 * called vfork() then the dynamic linker's lock would still be held here
270 * (with a defunct owner) and we would deadlock ourself if we invoked it.
272 * Therefore, all of the functions we call here after returning from
273 * vforkx() in the child are not and must never be exported from libc
274 * as global symbols. To do so would risk invoking the dynamic linker.
281 const posix_spawn_file_actions_t
*file_actions
,
282 const posix_spawnattr_t
*attrp
,
286 spawn_attr_t
*sap
= attrp
? attrp
->__spawn_attrp
: NULL
;
287 file_attr_t
*fap
= file_actions
? file_actions
->__file_attrp
: NULL
;
289 int error
; /* this will be set by the child */
292 if (attrp
!= NULL
&& sap
== NULL
)
295 if (fap
!= NULL
&& fap
->fa_need_dirbuf
) {
297 * Preallocate the buffer for the call to getdents64() in
298 * spawn_closefrom() since we can't do it in the vfork() child.
300 if ((dirbuf
= lmalloc(DIRBUF
)) == NULL
)
304 switch (pid
= vforkx(forkflags(sap
))) {
307 case -1: /* parent, failure */
309 lfree(dirbuf
, DIRBUF
);
311 default: /* parent, success */
313 * We don't get here until the child exec()s or exit()s
315 if (pidp
!= NULL
&& get_error(&error
) == 0)
318 lfree(dirbuf
, DIRBUF
);
319 return (get_error(&error
));
323 if (set_error(&error
, perform_flag_actions(sap
)) != 0)
327 if (set_error(&error
, perform_file_actions(fap
, dirbuf
)) != 0)
330 (void) set_error(&error
, 0);
331 (void) execve(path
, argv
, envp
);
332 if (sap
!= NULL
&& (sap
->sa_psflags
& POSIX_SPAWN_NOEXECERR_NP
))
334 (void) set_error(&error
, errno
);
336 return (0); /* not reached */
340 * Much of posix_spawnp() blatently stolen from execvp() (port/gen/execvp.c).
343 extern int libc__xpg4
;
346 execat(const char *s1
, const char *s2
, char *si
)
348 int cnt
= PATH_MAX
+ 1;
352 for (s
= si
; (c
= *s1
) != '\0' && c
!= ':'; s1
++) {
358 if (si
!= s
&& cnt
> 0) {
362 for (; (c
= *s2
) != '\0' && cnt
> 0; s2
++) {
367 return (*s1
? ++s1
: NULL
);
375 const posix_spawn_file_actions_t
*file_actions
,
376 const posix_spawnattr_t
*attrp
,
380 spawn_attr_t
*sap
= attrp
? attrp
->__spawn_attrp
: NULL
;
381 file_attr_t
*fap
= file_actions
? file_actions
->__file_attrp
: NULL
;
383 const char *pathstr
= (strchr(file
, '/') == NULL
)? getenv("PATH") : "";
384 int xpg4
= libc__xpg4
;
385 int error
= 0; /* this will be set by the child */
386 char path
[PATH_MAX
+4];
393 if (attrp
!= NULL
&& sap
== NULL
)
399 if (fap
!= NULL
&& fap
->fa_need_dirbuf
) {
401 * Preallocate the buffer for the call to getdents64() in
402 * spawn_closefrom() since we can't do it in the vfork() child.
404 if ((dirbuf
= lmalloc(DIRBUF
)) == NULL
)
409 * We may need to invoke the shell with a slightly modified
410 * argv[] array. To do this we need to preallocate the array.
411 * We must call alloca() before calling vfork() because doing
412 * it after vfork() (in the child) would corrupt the parent.
414 for (argc
= 0; argv
[argc
] != NULL
; argc
++)
416 newargs
= alloca((argc
+ 2) * sizeof (char *));
418 switch (pid
= vforkx(forkflags(sap
))) {
421 case -1: /* parent, failure */
423 lfree(dirbuf
, DIRBUF
);
425 default: /* parent, success */
427 * We don't get here until the child exec()s or exit()s
429 if (pidp
!= NULL
&& get_error(&error
) == 0)
432 lfree(dirbuf
, DIRBUF
);
433 return (get_error(&error
));
437 if (set_error(&error
, perform_flag_actions(sap
)) != 0)
441 if (set_error(&error
, perform_file_actions(fap
, dirbuf
)) != 0)
444 if (pathstr
== NULL
) {
446 * XPG4: pathstr is equivalent to _CS_PATH, except that
447 * :/usr/sbin is appended when root, and pathstr must end
448 * with a colon when not root. Keep these paths in sync
449 * with _CS_PATH in confstr.c. Note that pathstr must end
450 * with a colon when not root so that when file doesn't
451 * contain '/', the last call to execat() will result in an
452 * attempt to execv file from the current directory.
454 if (geteuid() == 0 || getuid() == 0) {
456 pathstr
= "/usr/sbin:/usr/ccs/bin:/usr/bin";
458 pathstr
= "/usr/xpg4/bin:/usr/ccs/bin:"
459 "/usr/bin:/opt/SUNWspro/bin:/usr/sbin";
462 pathstr
= "/usr/ccs/bin:/usr/bin:";
464 pathstr
= "/usr/xpg4/bin:/usr/ccs/bin:"
465 "/usr/bin:/opt/SUNWspro/bin:";
471 cp
= execat(cp
, file
, path
);
473 * 4025035 and 4038378
474 * if a filename begins with a "-" prepend "./" so that
475 * the shell can't interpret it as an option
480 for (s
= path
; *s
!= '\0'; s
++)
482 for (; s
>= path
; s
--)
487 (void) set_error(&error
, 0);
488 (void) execve(path
, argv
, envp
);
489 if (set_error(&error
, errno
) == ENOEXEC
) {
492 for (i
= 1; i
<= argc
; i
++)
493 newargs
[i
+ 1] = argv
[i
];
494 (void) set_error(&error
, 0);
495 (void) execve(_PATH_BSHELL
, newargs
, envp
);
497 (sap
->sa_psflags
& POSIX_SPAWN_NOEXECERR_NP
))
499 (void) set_error(&error
, errno
);
505 (sap
->sa_psflags
& POSIX_SPAWN_NOEXECERR_NP
)) {
506 (void) set_error(&error
, 0);
510 return (0); /* not reached */
514 posix_spawn_file_actions_init(
515 posix_spawn_file_actions_t
*file_actions
)
517 file_actions
->__file_attrp
= NULL
;
522 posix_spawn_file_actions_destroy(
523 posix_spawn_file_actions_t
*file_actions
)
525 file_attr_t
*froot
= file_actions
->__file_attrp
;
529 if ((fap
= froot
) != NULL
) {
532 if (fap
->fa_type
== FA_OPEN
)
533 lfree(fap
->fa_path
, fap
->fa_pathsize
);
534 lfree(fap
, sizeof (*fap
));
535 } while ((fap
= next
) != froot
);
537 file_actions
->__file_attrp
= NULL
;
542 add_file_attr(posix_spawn_file_actions_t
*file_actions
, file_attr_t
*fap
)
544 file_attr_t
*froot
= file_actions
->__file_attrp
;
547 fap
->fa_next
= fap
->fa_prev
= fap
;
548 file_actions
->__file_attrp
= froot
= fap
;
550 fap
->fa_next
= froot
;
551 fap
->fa_prev
= froot
->fa_prev
;
552 froot
->fa_prev
->fa_next
= fap
;
553 froot
->fa_prev
= fap
;
557 * Once set, __file_attrp no longer changes, so this assignment
558 * always goes into the first element in the list, as required.
560 if (fap
->fa_type
== FA_CLOSEFROM
)
561 froot
->fa_need_dirbuf
= 1;
565 posix_spawn_file_actions_addopen(
566 posix_spawn_file_actions_t
*file_actions
,
576 if ((fap
= lmalloc(sizeof (*fap
))) == NULL
)
579 fap
->fa_pathsize
= strlen(path
) + 1;
580 if ((fap
->fa_path
= lmalloc(fap
->fa_pathsize
)) == NULL
) {
581 lfree(fap
, sizeof (*fap
));
584 (void) strcpy(fap
->fa_path
, path
);
586 fap
->fa_type
= FA_OPEN
;
587 fap
->fa_oflag
= oflag
;
589 fap
->fa_filedes
= filedes
;
590 add_file_attr(file_actions
, fap
);
596 posix_spawn_file_actions_addclose(
597 posix_spawn_file_actions_t
*file_actions
,
604 if ((fap
= lmalloc(sizeof (*fap
))) == NULL
)
607 fap
->fa_type
= FA_CLOSE
;
608 fap
->fa_filedes
= filedes
;
609 add_file_attr(file_actions
, fap
);
615 posix_spawn_file_actions_adddup2(
616 posix_spawn_file_actions_t
*file_actions
,
622 if (filedes
< 0 || newfiledes
< 0)
624 if ((fap
= lmalloc(sizeof (*fap
))) == NULL
)
627 fap
->fa_type
= FA_DUP2
;
628 fap
->fa_filedes
= filedes
;
629 fap
->fa_newfiledes
= newfiledes
;
630 add_file_attr(file_actions
, fap
);
636 posix_spawn_file_actions_addclosefrom_np(
637 posix_spawn_file_actions_t
*file_actions
,
644 if ((fap
= lmalloc(sizeof (*fap
))) == NULL
)
646 fap
->fa_type
= FA_CLOSEFROM
;
647 fap
->fa_filedes
= lowfiledes
;
648 add_file_attr(file_actions
, fap
);
654 posix_spawnattr_init(
655 posix_spawnattr_t
*attr
)
657 if ((attr
->__spawn_attrp
= lmalloc(sizeof (posix_spawnattr_t
))) == NULL
)
660 * Add default stuff here?
666 posix_spawnattr_destroy(
667 posix_spawnattr_t
*attr
)
669 spawn_attr_t
*sap
= attr
->__spawn_attrp
;
675 * deallocate stuff here?
677 lfree(sap
, sizeof (*sap
));
678 attr
->__spawn_attrp
= NULL
;
683 posix_spawnattr_setflags(
684 posix_spawnattr_t
*attr
,
687 spawn_attr_t
*sap
= attr
->__spawn_attrp
;
690 (flags
& ~ALL_POSIX_SPAWN_FLAGS
))
693 sap
->sa_psflags
= flags
;
698 posix_spawnattr_getflags(
699 const posix_spawnattr_t
*attr
,
702 spawn_attr_t
*sap
= attr
->__spawn_attrp
;
707 *flags
= sap
->sa_psflags
;
712 posix_spawnattr_setpgroup(
713 posix_spawnattr_t
*attr
,
716 spawn_attr_t
*sap
= attr
->__spawn_attrp
;
721 sap
->sa_pgroup
= pgroup
;
726 posix_spawnattr_getpgroup(
727 const posix_spawnattr_t
*attr
,
730 spawn_attr_t
*sap
= attr
->__spawn_attrp
;
735 *pgroup
= sap
->sa_pgroup
;
740 posix_spawnattr_setschedparam(
741 posix_spawnattr_t
*attr
,
742 const struct sched_param
*schedparam
)
744 spawn_attr_t
*sap
= attr
->__spawn_attrp
;
752 sap
->sa_priority
= schedparam
->sched_priority
;
757 posix_spawnattr_getschedparam(
758 const posix_spawnattr_t
*attr
,
759 struct sched_param
*schedparam
)
761 spawn_attr_t
*sap
= attr
->__spawn_attrp
;
766 schedparam
->sched_priority
= sap
->sa_priority
;
771 posix_spawnattr_setschedpolicy(
772 posix_spawnattr_t
*attr
,
775 spawn_attr_t
*sap
= attr
->__spawn_attrp
;
777 if (sap
== NULL
|| schedpolicy
== SCHED_SYS
)
781 * Cache the policy information for later use
782 * by the vfork() child of posix_spawn().
784 if (get_info_by_policy(schedpolicy
) == NULL
)
787 sap
->sa_schedpolicy
= schedpolicy
;
792 posix_spawnattr_getschedpolicy(
793 const posix_spawnattr_t
*attr
,
796 spawn_attr_t
*sap
= attr
->__spawn_attrp
;
801 *schedpolicy
= sap
->sa_schedpolicy
;
806 posix_spawnattr_setsigdefault(
807 posix_spawnattr_t
*attr
,
808 const sigset_t
*sigdefault
)
810 spawn_attr_t
*sap
= attr
->__spawn_attrp
;
815 sap
->sa_sigdefault
= *sigdefault
;
820 posix_spawnattr_getsigdefault(
821 const posix_spawnattr_t
*attr
,
822 sigset_t
*sigdefault
)
824 spawn_attr_t
*sap
= attr
->__spawn_attrp
;
829 *sigdefault
= sap
->sa_sigdefault
;
834 posix_spawnattr_setsigignore_np(
835 posix_spawnattr_t
*attr
,
836 const sigset_t
*sigignore
)
838 spawn_attr_t
*sap
= attr
->__spawn_attrp
;
843 sap
->sa_sigignore
= *sigignore
;
848 posix_spawnattr_getsigignore_np(
849 const posix_spawnattr_t
*attr
,
852 spawn_attr_t
*sap
= attr
->__spawn_attrp
;
857 *sigignore
= sap
->sa_sigignore
;
862 posix_spawnattr_setsigmask(
863 posix_spawnattr_t
*attr
,
864 const sigset_t
*sigmask
)
866 spawn_attr_t
*sap
= attr
->__spawn_attrp
;
871 sap
->sa_sigmask
= *sigmask
;
876 posix_spawnattr_getsigmask(
877 const posix_spawnattr_t
*attr
,
880 spawn_attr_t
*sap
= attr
->__spawn_attrp
;
885 *sigmask
= sap
->sa_sigmask
;
890 * Spawn a process to run "sh -c <cmd>". Return the child's pid (in
891 * *pidp), and a file descriptor (in *fdp) for reading or writing to the
892 * child process, depending on the 'write' argument.
893 * Return 0 on success; otherwise return an error code.
896 posix_spawn_pipe_np(pid_t
*pidp
, int *fdp
,
897 const char *cmd
, boolean_t write
,
898 posix_spawn_file_actions_t
*fact
, posix_spawnattr_t
*attr
)
901 int myside
, yourside
, stdio
;
902 const char *shpath
= _PATH_BSHELL
;
903 const char *argvec
[4] = { "sh", "-c", cmd
, NULL
};
909 if (access(shpath
, X_OK
)) /* XPG4 Requirement: */
910 shpath
= ""; /* force child to fail immediately */
914 * Data is read from p[0] and written to p[1].
915 * 'stdio' is the fd in the child process that should be
916 * connected to the pipe.
920 stdio
= STDIN_FILENO
;
924 stdio
= STDOUT_FILENO
;
927 error
= posix_spawn_file_actions_addclose(fact
, myside
);
928 if (yourside
!= stdio
) {
930 error
= posix_spawn_file_actions_adddup2(fact
,
934 error
= posix_spawn_file_actions_addclose(fact
,
941 error
= posix_spawn(pidp
, shpath
, fact
, attr
,
942 (char *const *)argvec
, (char *const *)_environ
);
943 (void) close(yourside
);
945 (void) close(myside
);