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 /*
23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * Copyright (c) 2011 by Delphix. All rights reserved.
29 */
30
31 #include "lint.h"
32 #include "thr_uberdata.h"
33 #include <sys/libc_kernel.h>
34 #include <sys/procset.h>
35 #include <sys/fork.h>
36 #include <dirent.h>
37 #include <alloca.h>
38 #include <spawn.h>
39 #include <paths.h>
40
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_SETSID | \
49 POSIX_SPAWN_SETSIGIGN_NP | \
50 POSIX_SPAWN_NOSIGCHLD_NP | \
51 POSIX_SPAWN_WAITPID_NP | \
52 POSIX_SPAWN_NOEXECERR_NP)
53
54 typedef struct {
55 int sa_psflags; /* POSIX_SPAWN_* flags */
56 int sa_priority;
57 int sa_schedpolicy;
58 pid_t sa_pgroup;
59 sigset_t sa_sigdefault;
60 sigset_t sa_sigignore;
61 sigset_t sa_sigmask;
62 } spawn_attr_t;
63
64 typedef enum file_action {
65 FA_OPEN,
66 FA_CLOSE,
67 FA_DUP2,
68 FA_CLOSEFROM,
69 FA_CHDIR,
70 FA_FCHDIR
71 } file_action_t;
72
73 typedef struct file_attr {
74 struct file_attr *fa_next; /* circular list of file actions */
75 struct file_attr *fa_prev;
76 file_action_t fa_type; /* type of action */
77 int fa_need_dirbuf; /* only consulted in the head action */
78 char *fa_path; /* copied pathname for open() */
79 uint_t fa_pathsize; /* size of fa_path[] array */
80 int fa_oflag; /* oflag for open() */
81 mode_t fa_mode; /* mode for open() */
82 int fa_filedes; /* file descriptor for open()/close() */
83 int fa_newfiledes; /* new file descriptor for dup2() */
84 } file_attr_t;
85
86 #if defined(_LP64)
87 #define __open64 __open
88 #define getdents64 getdents
89 #define dirent64_t dirent_t
90 #else
91 extern int getdents64(int, dirent64_t *, size_t);
92 #endif
93
94 extern const char **_environ;
95
96 /*
97 * Support function:
98 * Close all open file descriptors greater than or equal to lowfd.
99 * This is executed in the child of vfork(), so we must not call
100 * opendir() / readdir() because that would alter the parent's
101 * address space. We use the low-level getdents64() system call.
102 * Return non-zero on error.
103 */
104 static int
spawn_closefrom(int lowfd,void * buf)105 spawn_closefrom(int lowfd, void *buf)
106 {
107 int procfd;
108 int fd;
109 int buflen;
110 dirent64_t *dp;
111 dirent64_t *dpend;
112
113 if (lowfd < 0)
114 lowfd = 0;
115
116 /*
117 * Close lowfd right away as a hedge against failing
118 * to open the /proc file descriptor directory due
119 * all file descriptors being currently used up.
120 */
121 (void) __close(lowfd++);
122
123 if ((procfd = __open64("/proc/self/fd", O_RDONLY, 0)) < 0) {
124 /*
125 * We could not open the /proc file descriptor directory.
126 * Just fail and be done with it.
127 */
128 return (-1);
129 }
130
131 for (;;) {
132 /*
133 * Collect a bunch of open file descriptors and close them.
134 * Repeat until the directory is exhausted.
135 */
136 dp = (dirent64_t *)buf;
137 if ((buflen = getdents64(procfd, dp, DIRBUF)) <= 0) {
138 (void) __close(procfd);
139 break;
140 }
141 dpend = (dirent64_t *)((uintptr_t)buf + buflen);
142 do {
143 /* skip '.', '..' and procfd */
144 if (dp->d_name[0] != '.' &&
145 (fd = atoi(dp->d_name)) != procfd &&
146 fd >= lowfd)
147 (void) __close(fd);
148 dp = (dirent64_t *)((uintptr_t)dp + dp->d_reclen);
149 } while (dp < dpend);
150 }
151
152 return (0);
153 }
154
155 static int
perform_flag_actions(spawn_attr_t * sap)156 perform_flag_actions(spawn_attr_t *sap)
157 {
158 int sig;
159 struct sigaction action;
160
161 if (sap->sa_psflags & POSIX_SPAWN_SETSIGMASK) {
162 (void) __lwp_sigmask(SIG_SETMASK, &sap->sa_sigmask);
163 }
164
165 if (sap->sa_psflags & POSIX_SPAWN_SETSIGIGN_NP) {
166 (void) memset(&action, 0, sizeof (action));
167 action.sa_handler = SIG_IGN;
168 for (sig = 1; sig < NSIG; sig++) {
169 if (sigismember(&sap->sa_sigignore, sig))
170 (void) __sigaction(sig, &action, NULL);
171 }
172 }
173
174 if (sap->sa_psflags & POSIX_SPAWN_SETSIGDEF) {
175 (void) memset(&action, 0, sizeof (action));
176 action.sa_handler = SIG_DFL;
177 for (sig = 1; sig < NSIG; sig++) {
178 if (sigismember(&sap->sa_sigdefault, sig))
179 (void) __sigaction(sig, &action, NULL);
180 }
181 }
182
183 if (sap->sa_psflags & POSIX_SPAWN_RESETIDS) {
184 if (setgid(getgid()) != 0 || setuid(getuid()) != 0)
185 return (errno);
186 }
187
188 if (sap->sa_psflags & POSIX_SPAWN_SETSID) {
189 if (setsid() == (pid_t)-1) {
190 return (errno);
191 }
192 }
193
194 if (sap->sa_psflags & POSIX_SPAWN_SETPGROUP) {
195 if (setpgid(0, sap->sa_pgroup) != 0)
196 return (errno);
197 }
198
199 if (sap->sa_psflags & POSIX_SPAWN_SETSCHEDULER) {
200 if (setparam(P_LWPID, P_MYID,
201 sap->sa_schedpolicy, sap->sa_priority) == -1)
202 return (errno);
203 } else if (sap->sa_psflags & POSIX_SPAWN_SETSCHEDPARAM) {
204 if (setprio(P_LWPID, P_MYID, sap->sa_priority, NULL) == -1)
205 return (errno);
206 }
207
208 return (0);
209 }
210
211 static int
perform_file_actions(file_attr_t * fap,void * dirbuf)212 perform_file_actions(file_attr_t *fap, void *dirbuf)
213 {
214 file_attr_t *froot = fap;
215 int fd;
216
217 do {
218 switch (fap->fa_type) {
219 case FA_OPEN:
220 fd = __open(fap->fa_path, fap->fa_oflag, fap->fa_mode);
221 if (fd < 0)
222 return (errno);
223 if (fd != fap->fa_filedes) {
224 if (__fcntl(fd, F_DUP2FD, fap->fa_filedes) < 0)
225 return (errno);
226 (void) __close(fd);
227 }
228 break;
229 case FA_CHDIR:
230 if (chdir(fap->fa_path) == -1)
231 return (errno);
232 break;
233 case FA_CLOSE:
234 if (__close(fap->fa_filedes) == -1 &&
235 errno != EBADF) /* already closed, no error */
236 return (errno);
237 break;
238 case FA_DUP2:
239 fd = __fcntl(fap->fa_filedes, F_DUP2FD,
240 fap->fa_newfiledes);
241 if (fd < 0)
242 return (errno);
243 break;
244 case FA_CLOSEFROM:
245 if (spawn_closefrom(fap->fa_filedes, dirbuf))
246 return (errno);
247 break;
248 case FA_FCHDIR:
249 if (fchdir(fap->fa_filedes) == -1)
250 return (errno);
251 break;
252 }
253 } while ((fap = fap->fa_next) != froot);
254
255 return (0);
256 }
257
258 static int
forkflags(spawn_attr_t * sap)259 forkflags(spawn_attr_t *sap)
260 {
261 int flags = 0;
262
263 if (sap != NULL) {
264 if (sap->sa_psflags & POSIX_SPAWN_NOSIGCHLD_NP)
265 flags |= FORK_NOSIGCHLD;
266 if (sap->sa_psflags & POSIX_SPAWN_WAITPID_NP)
267 flags |= FORK_WAITPID;
268 }
269
270 return (flags);
271 }
272
273 /*
274 * set_error() / get_error() are used to guarantee that the local variable
275 * 'error' is set correctly in memory on return from vfork() in the parent.
276 */
277
278 static int
set_error(int * errp,int err)279 set_error(int *errp, int err)
280 {
281 return (*errp = err);
282 }
283
284 static int
get_error(int * errp)285 get_error(int *errp)
286 {
287 return (*errp);
288 }
289
290 /*
291 * For MT safety, do not invoke the dynamic linker after calling vfork().
292 * If some other thread was in the dynamic linker when this thread's parent
293 * called vfork() then the dynamic linker's lock would still be held here
294 * (with a defunct owner) and we would deadlock ourself if we invoked it.
295 *
296 * Therefore, all of the functions we call here after returning from
297 * vforkx() in the child are not and must never be exported from libc
298 * as global symbols. To do so would risk invoking the dynamic linker.
299 */
300
301 int
posix_spawn(pid_t * pidp,const char * path,const posix_spawn_file_actions_t * file_actions,const posix_spawnattr_t * attrp,char * const * argv,char * const * envp)302 posix_spawn(
303 pid_t *pidp,
304 const char *path,
305 const posix_spawn_file_actions_t *file_actions,
306 const posix_spawnattr_t *attrp,
307 char *const *argv,
308 char *const *envp)
309 {
310 spawn_attr_t *sap = attrp? attrp->__spawn_attrp : NULL;
311 file_attr_t *fap = file_actions? file_actions->__file_attrp : NULL;
312 void *dirbuf = NULL;
313 int error; /* this will be set by the child */
314 pid_t pid;
315
316 if (attrp != NULL && sap == NULL)
317 return (EINVAL);
318
319 if (fap != NULL && fap->fa_need_dirbuf) {
320 /*
321 * Preallocate the buffer for the call to getdents64() in
322 * spawn_closefrom() since we can't do it in the vfork() child.
323 */
324 if ((dirbuf = lmalloc(DIRBUF)) == NULL)
325 return (ENOMEM);
326 }
327
328 switch (pid = vforkx(forkflags(sap))) {
329 case 0: /* child */
330 break;
331 case -1: /* parent, failure */
332 if (dirbuf)
333 lfree(dirbuf, DIRBUF);
334 return (errno);
335 default: /* parent, success */
336 /*
337 * We don't get here until the child exec()s or exit()s
338 */
339 if (pidp != NULL && get_error(&error) == 0)
340 *pidp = pid;
341 if (dirbuf)
342 lfree(dirbuf, DIRBUF);
343 return (get_error(&error));
344 }
345
346 if (sap != NULL)
347 if (set_error(&error, perform_flag_actions(sap)) != 0)
348 _exit(_EVAPORATE);
349
350 if (fap != NULL)
351 if (set_error(&error, perform_file_actions(fap, dirbuf)) != 0)
352 _exit(_EVAPORATE);
353
354 (void) set_error(&error, 0);
355 (void) execve(path, argv, envp);
356 if (sap != NULL && (sap->sa_psflags & POSIX_SPAWN_NOEXECERR_NP))
357 _exit(127);
358 (void) set_error(&error, errno);
359 _exit(_EVAPORATE);
360 return (0); /* not reached */
361 }
362
363 /*
364 * Much of posix_spawnp() blatently stolen from execvp() (port/gen/execvp.c).
365 */
366
367 extern int libc__xpg4;
368
369 static const char *
execat(const char * s1,const char * s2,char * si)370 execat(const char *s1, const char *s2, char *si)
371 {
372 int cnt = PATH_MAX + 1;
373 char *s;
374 char c;
375
376 for (s = si; (c = *s1) != '\0' && c != ':'; s1++) {
377 if (cnt > 0) {
378 *s++ = c;
379 cnt--;
380 }
381 }
382 if (si != s && cnt > 0) {
383 *s++ = '/';
384 cnt--;
385 }
386 for (; (c = *s2) != '\0' && cnt > 0; s2++) {
387 *s++ = c;
388 cnt--;
389 }
390 *s = '\0';
391 return (*s1? ++s1: NULL);
392 }
393
394 /* ARGSUSED */
395 int
posix_spawnp(pid_t * pidp,const char * file,const posix_spawn_file_actions_t * file_actions,const posix_spawnattr_t * attrp,char * const * argv,char * const * envp)396 posix_spawnp(
397 pid_t *pidp,
398 const char *file,
399 const posix_spawn_file_actions_t *file_actions,
400 const posix_spawnattr_t *attrp,
401 char *const *argv,
402 char *const *envp)
403 {
404 spawn_attr_t *sap = attrp? attrp->__spawn_attrp : NULL;
405 file_attr_t *fap = file_actions? file_actions->__file_attrp : NULL;
406 void *dirbuf = NULL;
407 const char *pathstr = (strchr(file, '/') == NULL)? getenv("PATH") : "";
408 int xpg4 = libc__xpg4;
409 int error = 0; /* this will be set by the child */
410 char path[PATH_MAX+4];
411 const char *cp;
412 pid_t pid;
413 char **newargs;
414 int argc;
415 int i;
416
417 if (attrp != NULL && sap == NULL)
418 return (EINVAL);
419
420 if (*file == '\0')
421 return (EACCES);
422
423 if (fap != NULL && fap->fa_need_dirbuf) {
424 /*
425 * Preallocate the buffer for the call to getdents64() in
426 * spawn_closefrom() since we can't do it in the vfork() child.
427 */
428 if ((dirbuf = lmalloc(DIRBUF)) == NULL)
429 return (ENOMEM);
430 }
431
432 /*
433 * We may need to invoke the shell with a slightly modified
434 * argv[] array. To do this we need to preallocate the array.
435 * We must call alloca() before calling vfork() because doing
436 * it after vfork() (in the child) would corrupt the parent.
437 */
438 for (argc = 0; argv[argc] != NULL; argc++)
439 continue;
440 newargs = alloca((argc + 2) * sizeof (char *));
441
442 switch (pid = vforkx(forkflags(sap))) {
443 case 0: /* child */
444 break;
445 case -1: /* parent, failure */
446 if (dirbuf)
447 lfree(dirbuf, DIRBUF);
448 return (errno);
449 default: /* parent, success */
450 /*
451 * We don't get here until the child exec()s or exit()s
452 */
453 if (pidp != NULL && get_error(&error) == 0)
454 *pidp = pid;
455 if (dirbuf)
456 lfree(dirbuf, DIRBUF);
457 return (get_error(&error));
458 }
459
460 if (sap != NULL)
461 if (set_error(&error, perform_flag_actions(sap)) != 0)
462 _exit(_EVAPORATE);
463
464 if (fap != NULL)
465 if (set_error(&error, perform_file_actions(fap, dirbuf)) != 0)
466 _exit(_EVAPORATE);
467
468 if (pathstr == NULL) {
469 /*
470 * XPG4: pathstr is equivalent to _CS_PATH, except that
471 * :/usr/sbin is appended when root, and pathstr must end
472 * with a colon when not root. Keep these paths in sync
473 * with _CS_PATH in confstr.c. Note that pathstr must end
474 * with a colon when not root so that when file doesn't
475 * contain '/', the last call to execat() will result in an
476 * attempt to execv file from the current directory.
477 */
478 if (geteuid() == 0 || getuid() == 0) {
479 if (!xpg4)
480 pathstr = "/usr/sbin:/usr/ccs/bin:/usr/bin";
481 else
482 pathstr = "/usr/xpg4/bin:/usr/ccs/bin:"
483 "/usr/bin:/opt/SUNWspro/bin:/usr/sbin";
484 } else {
485 if (!xpg4)
486 pathstr = "/usr/ccs/bin:/usr/bin:";
487 else
488 pathstr = "/usr/xpg4/bin:/usr/ccs/bin:"
489 "/usr/bin:/opt/SUNWspro/bin:";
490 }
491 }
492
493 cp = pathstr;
494 do {
495 cp = execat(cp, file, path);
496 /*
497 * 4025035 and 4038378
498 * if a filename begins with a "-" prepend "./" so that
499 * the shell can't interpret it as an option
500 */
501 if (*path == '-') {
502 char *s;
503
504 for (s = path; *s != '\0'; s++)
505 continue;
506 for (; s >= path; s--)
507 *(s + 2) = *s;
508 path[0] = '.';
509 path[1] = '/';
510 }
511 (void) set_error(&error, 0);
512 (void) execve(path, argv, envp);
513 if (set_error(&error, errno) == ENOEXEC) {
514 newargs[0] = "sh";
515 newargs[1] = path;
516 for (i = 1; i <= argc; i++)
517 newargs[i + 1] = argv[i];
518 (void) set_error(&error, 0);
519 (void) execve(_PATH_BSHELL, newargs, envp);
520 if (sap != NULL &&
521 (sap->sa_psflags & POSIX_SPAWN_NOEXECERR_NP))
522 _exit(127);
523 (void) set_error(&error, errno);
524 _exit(_EVAPORATE);
525 }
526 } while (cp);
527
528 if (sap != NULL &&
529 (sap->sa_psflags & POSIX_SPAWN_NOEXECERR_NP)) {
530 (void) set_error(&error, 0);
531 _exit(127);
532 }
533 _exit(_EVAPORATE);
534 return (0); /* not reached */
535 }
536
537 int
posix_spawn_file_actions_init(posix_spawn_file_actions_t * file_actions)538 posix_spawn_file_actions_init(
539 posix_spawn_file_actions_t *file_actions)
540 {
541 file_actions->__file_attrp = NULL;
542 return (0);
543 }
544
545 int
posix_spawn_file_actions_destroy(posix_spawn_file_actions_t * file_actions)546 posix_spawn_file_actions_destroy(
547 posix_spawn_file_actions_t *file_actions)
548 {
549 file_attr_t *froot = file_actions->__file_attrp;
550 file_attr_t *fap;
551 file_attr_t *next;
552
553 if ((fap = froot) != NULL) {
554 do {
555 next = fap->fa_next;
556 if (fap->fa_type == FA_OPEN || fap->fa_type == FA_CHDIR)
557 lfree(fap->fa_path, fap->fa_pathsize);
558 lfree(fap, sizeof (*fap));
559 } while ((fap = next) != froot);
560 }
561 file_actions->__file_attrp = NULL;
562 return (0);
563 }
564
565 static void
add_file_attr(posix_spawn_file_actions_t * file_actions,file_attr_t * fap)566 add_file_attr(posix_spawn_file_actions_t *file_actions, file_attr_t *fap)
567 {
568 file_attr_t *froot = file_actions->__file_attrp;
569
570 if (froot == NULL) {
571 fap->fa_next = fap->fa_prev = fap;
572 file_actions->__file_attrp = froot = fap;
573 } else {
574 fap->fa_next = froot;
575 fap->fa_prev = froot->fa_prev;
576 froot->fa_prev->fa_next = fap;
577 froot->fa_prev = fap;
578 }
579
580 /*
581 * Once set, __file_attrp no longer changes, so this assignment
582 * always goes into the first element in the list, as required.
583 */
584 if (fap->fa_type == FA_CLOSEFROM)
585 froot->fa_need_dirbuf = 1;
586 }
587
588 int
posix_spawn_file_actions_addopen(posix_spawn_file_actions_t * restrict file_actions,int filedes,const char * restrict path,int oflag,mode_t mode)589 posix_spawn_file_actions_addopen(
590 posix_spawn_file_actions_t *restrict file_actions,
591 int filedes,
592 const char *restrict path,
593 int oflag,
594 mode_t mode)
595 {
596 file_attr_t *fap;
597
598 if (filedes < 0)
599 return (EBADF);
600 if ((fap = lmalloc(sizeof (*fap))) == NULL)
601 return (ENOMEM);
602
603 fap->fa_pathsize = strlen(path) + 1;
604 if ((fap->fa_path = lmalloc(fap->fa_pathsize)) == NULL) {
605 lfree(fap, sizeof (*fap));
606 return (ENOMEM);
607 }
608 (void) memcpy(fap->fa_path, path, fap->fa_pathsize);
609
610 fap->fa_type = FA_OPEN;
611 fap->fa_oflag = oflag;
612 fap->fa_mode = mode;
613 fap->fa_filedes = filedes;
614 add_file_attr(file_actions, fap);
615
616 return (0);
617 }
618
619 int
posix_spawn_file_actions_addclose(posix_spawn_file_actions_t * restrict file_actions,int filedes)620 posix_spawn_file_actions_addclose(
621 posix_spawn_file_actions_t *restrict file_actions,
622 int filedes)
623 {
624 file_attr_t *fap;
625
626 if (filedes < 0)
627 return (EBADF);
628 if ((fap = lmalloc(sizeof (*fap))) == NULL)
629 return (ENOMEM);
630
631 fap->fa_type = FA_CLOSE;
632 fap->fa_filedes = filedes;
633 add_file_attr(file_actions, fap);
634
635 return (0);
636 }
637
638 int
posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t * restrict file_actions,int filedes,int newfiledes)639 posix_spawn_file_actions_adddup2(
640 posix_spawn_file_actions_t *restrict file_actions,
641 int filedes,
642 int newfiledes)
643 {
644 file_attr_t *fap;
645
646 if (filedes < 0 || newfiledes < 0)
647 return (EBADF);
648 if ((fap = lmalloc(sizeof (*fap))) == NULL)
649 return (ENOMEM);
650
651 fap->fa_type = FA_DUP2;
652 fap->fa_filedes = filedes;
653 fap->fa_newfiledes = newfiledes;
654 add_file_attr(file_actions, fap);
655
656 return (0);
657 }
658
659 int
posix_spawn_file_actions_addclosefrom_np(posix_spawn_file_actions_t * restrict file_actions,int lowfiledes)660 posix_spawn_file_actions_addclosefrom_np(
661 posix_spawn_file_actions_t *restrict file_actions,
662 int lowfiledes)
663 {
664 file_attr_t *fap;
665
666 if (lowfiledes < 0)
667 return (EBADF);
668 if ((fap = lmalloc(sizeof (*fap))) == NULL)
669 return (ENOMEM);
670 fap->fa_type = FA_CLOSEFROM;
671 fap->fa_filedes = lowfiledes;
672 add_file_attr(file_actions, fap);
673
674 return (0);
675 }
676
677 int
posix_spawn_file_actions_addchdir(posix_spawn_file_actions_t * restrict file_actions,const char * restrict path)678 posix_spawn_file_actions_addchdir(
679 posix_spawn_file_actions_t *restrict file_actions,
680 const char *restrict path)
681 {
682 file_attr_t *fap;
683
684 if ((fap = lmalloc(sizeof (*fap))) == NULL)
685 return (ENOMEM);
686
687 fap->fa_pathsize = strlen(path) + 1;
688 if ((fap->fa_path = lmalloc(fap->fa_pathsize)) == NULL) {
689 lfree(fap, sizeof (*fap));
690 return (ENOMEM);
691 }
692 (void) memcpy(fap->fa_path, path, fap->fa_pathsize);
693
694 fap->fa_type = FA_CHDIR;
695 add_file_attr(file_actions, fap);
696
697 return (0);
698 }
699
700 int
posix_spawn_file_actions_addchdir_np(posix_spawn_file_actions_t * restrict file_actions,const char * restrict path)701 posix_spawn_file_actions_addchdir_np(
702 posix_spawn_file_actions_t *restrict file_actions,
703 const char *restrict path)
704 {
705 return (posix_spawn_file_actions_addchdir(file_actions, path));
706 }
707
708 int
posix_spawn_file_actions_addfchdir(posix_spawn_file_actions_t * restrict file_actions,int fd)709 posix_spawn_file_actions_addfchdir(
710 posix_spawn_file_actions_t *restrict file_actions,
711 int fd)
712 {
713 file_attr_t *fap;
714
715 if (fd < 0)
716 return (EBADF);
717 if ((fap = lmalloc(sizeof (*fap))) == NULL)
718 return (ENOMEM);
719 fap->fa_type = FA_FCHDIR;
720 fap->fa_filedes = fd;
721 add_file_attr(file_actions, fap);
722 return (0);
723 }
724
725 int
posix_spawn_file_actions_addfchdir_np(posix_spawn_file_actions_t * restrict file_actions,int fd)726 posix_spawn_file_actions_addfchdir_np(
727 posix_spawn_file_actions_t *restrict file_actions,
728 int fd)
729 {
730 return (posix_spawn_file_actions_addfchdir(file_actions, fd));
731 }
732
733 int
posix_spawnattr_init(posix_spawnattr_t * attr)734 posix_spawnattr_init(
735 posix_spawnattr_t *attr)
736 {
737 if ((attr->__spawn_attrp = lmalloc(sizeof (posix_spawnattr_t))) == NULL)
738 return (ENOMEM);
739 /*
740 * Add default stuff here?
741 */
742 return (0);
743 }
744
745 int
posix_spawnattr_destroy(posix_spawnattr_t * attr)746 posix_spawnattr_destroy(
747 posix_spawnattr_t *attr)
748 {
749 spawn_attr_t *sap = attr->__spawn_attrp;
750
751 if (sap == NULL)
752 return (EINVAL);
753
754 /*
755 * deallocate stuff here?
756 */
757 lfree(sap, sizeof (*sap));
758 attr->__spawn_attrp = NULL;
759 return (0);
760 }
761
762 int
posix_spawnattr_setflags(posix_spawnattr_t * attr,short flags)763 posix_spawnattr_setflags(
764 posix_spawnattr_t *attr,
765 short flags)
766 {
767 spawn_attr_t *sap = attr->__spawn_attrp;
768
769 if (sap == NULL ||
770 (flags & ~ALL_POSIX_SPAWN_FLAGS))
771 return (EINVAL);
772
773 sap->sa_psflags = flags;
774 return (0);
775 }
776
777 int
posix_spawnattr_getflags(const posix_spawnattr_t * attr,short * flags)778 posix_spawnattr_getflags(
779 const posix_spawnattr_t *attr,
780 short *flags)
781 {
782 spawn_attr_t *sap = attr->__spawn_attrp;
783
784 if (sap == NULL)
785 return (EINVAL);
786
787 *flags = sap->sa_psflags;
788 return (0);
789 }
790
791 int
posix_spawnattr_setpgroup(posix_spawnattr_t * attr,pid_t pgroup)792 posix_spawnattr_setpgroup(
793 posix_spawnattr_t *attr,
794 pid_t pgroup)
795 {
796 spawn_attr_t *sap = attr->__spawn_attrp;
797
798 if (sap == NULL)
799 return (EINVAL);
800
801 sap->sa_pgroup = pgroup;
802 return (0);
803 }
804
805 int
posix_spawnattr_getpgroup(const posix_spawnattr_t * attr,pid_t * pgroup)806 posix_spawnattr_getpgroup(
807 const posix_spawnattr_t *attr,
808 pid_t *pgroup)
809 {
810 spawn_attr_t *sap = attr->__spawn_attrp;
811
812 if (sap == NULL)
813 return (EINVAL);
814
815 *pgroup = sap->sa_pgroup;
816 return (0);
817 }
818
819 int
posix_spawnattr_setschedparam(posix_spawnattr_t * attr,const struct sched_param * schedparam)820 posix_spawnattr_setschedparam(
821 posix_spawnattr_t *attr,
822 const struct sched_param *schedparam)
823 {
824 spawn_attr_t *sap = attr->__spawn_attrp;
825
826 if (sap == NULL)
827 return (EINVAL);
828
829 /*
830 * Check validity?
831 */
832 sap->sa_priority = schedparam->sched_priority;
833 return (0);
834 }
835
836 int
posix_spawnattr_getschedparam(const posix_spawnattr_t * attr,struct sched_param * schedparam)837 posix_spawnattr_getschedparam(
838 const posix_spawnattr_t *attr,
839 struct sched_param *schedparam)
840 {
841 spawn_attr_t *sap = attr->__spawn_attrp;
842
843 if (sap == NULL)
844 return (EINVAL);
845
846 schedparam->sched_priority = sap->sa_priority;
847 return (0);
848 }
849
850 int
posix_spawnattr_setschedpolicy(posix_spawnattr_t * attr,int schedpolicy)851 posix_spawnattr_setschedpolicy(
852 posix_spawnattr_t *attr,
853 int schedpolicy)
854 {
855 spawn_attr_t *sap = attr->__spawn_attrp;
856
857 if (sap == NULL || schedpolicy == SCHED_SYS)
858 return (EINVAL);
859
860 /*
861 * Cache the policy information for later use
862 * by the vfork() child of posix_spawn().
863 */
864 if (get_info_by_policy(schedpolicy) == NULL)
865 return (errno);
866
867 sap->sa_schedpolicy = schedpolicy;
868 return (0);
869 }
870
871 int
posix_spawnattr_getschedpolicy(const posix_spawnattr_t * attr,int * schedpolicy)872 posix_spawnattr_getschedpolicy(
873 const posix_spawnattr_t *attr,
874 int *schedpolicy)
875 {
876 spawn_attr_t *sap = attr->__spawn_attrp;
877
878 if (sap == NULL)
879 return (EINVAL);
880
881 *schedpolicy = sap->sa_schedpolicy;
882 return (0);
883 }
884
885 int
posix_spawnattr_setsigdefault(posix_spawnattr_t * attr,const sigset_t * sigdefault)886 posix_spawnattr_setsigdefault(
887 posix_spawnattr_t *attr,
888 const sigset_t *sigdefault)
889 {
890 spawn_attr_t *sap = attr->__spawn_attrp;
891
892 if (sap == NULL)
893 return (EINVAL);
894
895 sap->sa_sigdefault = *sigdefault;
896 return (0);
897 }
898
899 int
posix_spawnattr_getsigdefault(const posix_spawnattr_t * attr,sigset_t * sigdefault)900 posix_spawnattr_getsigdefault(
901 const posix_spawnattr_t *attr,
902 sigset_t *sigdefault)
903 {
904 spawn_attr_t *sap = attr->__spawn_attrp;
905
906 if (sap == NULL)
907 return (EINVAL);
908
909 *sigdefault = sap->sa_sigdefault;
910 return (0);
911 }
912
913 int
posix_spawnattr_setsigignore_np(posix_spawnattr_t * attr,const sigset_t * sigignore)914 posix_spawnattr_setsigignore_np(
915 posix_spawnattr_t *attr,
916 const sigset_t *sigignore)
917 {
918 spawn_attr_t *sap = attr->__spawn_attrp;
919
920 if (sap == NULL)
921 return (EINVAL);
922
923 sap->sa_sigignore = *sigignore;
924 return (0);
925 }
926
927 int
posix_spawnattr_getsigignore_np(const posix_spawnattr_t * attr,sigset_t * sigignore)928 posix_spawnattr_getsigignore_np(
929 const posix_spawnattr_t *attr,
930 sigset_t *sigignore)
931 {
932 spawn_attr_t *sap = attr->__spawn_attrp;
933
934 if (sap == NULL)
935 return (EINVAL);
936
937 *sigignore = sap->sa_sigignore;
938 return (0);
939 }
940
941 int
posix_spawnattr_setsigmask(posix_spawnattr_t * attr,const sigset_t * sigmask)942 posix_spawnattr_setsigmask(
943 posix_spawnattr_t *attr,
944 const sigset_t *sigmask)
945 {
946 spawn_attr_t *sap = attr->__spawn_attrp;
947
948 if (sap == NULL)
949 return (EINVAL);
950
951 sap->sa_sigmask = *sigmask;
952 return (0);
953 }
954
955 int
posix_spawnattr_getsigmask(const posix_spawnattr_t * attr,sigset_t * sigmask)956 posix_spawnattr_getsigmask(
957 const posix_spawnattr_t *attr,
958 sigset_t *sigmask)
959 {
960 spawn_attr_t *sap = attr->__spawn_attrp;
961
962 if (sap == NULL)
963 return (EINVAL);
964
965 *sigmask = sap->sa_sigmask;
966 return (0);
967 }
968
969 /*
970 * Spawn a process to run "sh -c <cmd>". Return the child's pid (in
971 * *pidp), and a file descriptor (in *fdp) for reading or writing to the
972 * child process, depending on the 'write' argument.
973 * Return 0 on success; otherwise return an error code.
974 */
975 int
posix_spawn_pipe_np(pid_t * pidp,int * fdp,const char * cmd,boolean_t write,posix_spawn_file_actions_t * fact,posix_spawnattr_t * attr)976 posix_spawn_pipe_np(pid_t *pidp, int *fdp,
977 const char *cmd, boolean_t write,
978 posix_spawn_file_actions_t *fact, posix_spawnattr_t *attr)
979 {
980 int p[2];
981 int myside, yourside, stdio;
982 const char *shpath = _PATH_BSHELL;
983 const char *argvec[4] = { "sh", "-c", cmd, NULL };
984 int error;
985
986 if (pipe(p) < 0)
987 return (errno);
988
989 if (access(shpath, X_OK)) /* XPG4 Requirement: */
990 shpath = ""; /* force child to fail immediately */
991
992 if (write) {
993 /*
994 * Data is read from p[0] and written to p[1].
995 * 'stdio' is the fd in the child process that should be
996 * connected to the pipe.
997 */
998 myside = p[1];
999 yourside = p[0];
1000 stdio = STDIN_FILENO;
1001 } else {
1002 myside = p[0];
1003 yourside = p[1];
1004 stdio = STDOUT_FILENO;
1005 }
1006
1007 error = posix_spawn_file_actions_addclose(fact, myside);
1008 if (yourside != stdio) {
1009 if (error == 0) {
1010 error = posix_spawn_file_actions_adddup2(fact,
1011 yourside, stdio);
1012 }
1013 if (error == 0) {
1014 error = posix_spawn_file_actions_addclose(fact,
1015 yourside);
1016 }
1017 }
1018
1019 if (error)
1020 return (error);
1021 error = posix_spawn(pidp, shpath, fact, attr,
1022 (char *const *)argvec, (char *const *)_environ);
1023 (void) close(yourside);
1024 if (error) {
1025 (void) close(myside);
1026 return (error);
1027 }
1028
1029 *fdp = myside;
1030 return (0);
1031 }
1032