]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libc/gen/posix_spawn.c
MFV r337218: 7261 nvlist code should enforce name length limit
[FreeBSD/FreeBSD.git] / lib / libc / gen / posix_spawn.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include "namespace.h"
33 #include <sys/queue.h>
34 #include <sys/wait.h>
35
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <sched.h>
39 #include <spawn.h>
40 #include <signal.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44 #include "un-namespace.h"
45 #include "libc_private.h"
46
47 extern char **environ;
48
49 struct __posix_spawnattr {
50         short                   sa_flags;
51         pid_t                   sa_pgroup;
52         struct sched_param      sa_schedparam;
53         int                     sa_schedpolicy;
54         sigset_t                sa_sigdefault;
55         sigset_t                sa_sigmask;
56 };
57
58 struct __posix_spawn_file_actions {
59         STAILQ_HEAD(, __posix_spawn_file_actions_entry) fa_list;
60 };
61
62 typedef struct __posix_spawn_file_actions_entry {
63         STAILQ_ENTRY(__posix_spawn_file_actions_entry) fae_list;
64         enum { FAE_OPEN, FAE_DUP2, FAE_CLOSE } fae_action;
65
66         int fae_fildes;
67         union {
68                 struct {
69                         char *path;
70 #define fae_path        fae_data.open.path
71                         int oflag;
72 #define fae_oflag       fae_data.open.oflag
73                         mode_t mode;
74 #define fae_mode        fae_data.open.mode
75                 } open;
76                 struct {
77                         int newfildes;
78 #define fae_newfildes   fae_data.dup2.newfildes
79                 } dup2;
80         } fae_data;
81 } posix_spawn_file_actions_entry_t;
82
83 /*
84  * Spawn routines
85  */
86
87 static int
88 process_spawnattr(const posix_spawnattr_t sa)
89 {
90         struct sigaction sigact = { .sa_flags = 0, .sa_handler = SIG_DFL };
91         int i;
92
93         /*
94          * POSIX doesn't really describe in which order everything
95          * should be set. We'll just set them in the order in which they
96          * are mentioned.
97          */
98
99         /* Set process group */
100         if (sa->sa_flags & POSIX_SPAWN_SETPGROUP) {
101                 if (setpgid(0, sa->sa_pgroup) != 0)
102                         return (errno);
103         }
104
105         /* Set scheduler policy */
106         if (sa->sa_flags & POSIX_SPAWN_SETSCHEDULER) {
107                 if (sched_setscheduler(0, sa->sa_schedpolicy,
108                     &sa->sa_schedparam) != 0)
109                         return (errno);
110         } else if (sa->sa_flags & POSIX_SPAWN_SETSCHEDPARAM) {
111                 if (sched_setparam(0, &sa->sa_schedparam) != 0)
112                         return (errno);
113         }
114
115         /* Reset user ID's */
116         if (sa->sa_flags & POSIX_SPAWN_RESETIDS) {
117                 if (setegid(getgid()) != 0)
118                         return (errno);
119                 if (seteuid(getuid()) != 0)
120                         return (errno);
121         }
122
123         /*
124          * Set signal masks/defaults.
125          * Use unwrapped syscall, libthr is in undefined state after vfork().
126          */
127         if (sa->sa_flags & POSIX_SPAWN_SETSIGMASK) {
128                 __sys_sigprocmask(SIG_SETMASK, &sa->sa_sigmask, NULL);
129         }
130
131         if (sa->sa_flags & POSIX_SPAWN_SETSIGDEF) {
132                 for (i = 1; i <= _SIG_MAXSIG; i++) {
133                         if (sigismember(&sa->sa_sigdefault, i))
134                                 if (__sys_sigaction(i, &sigact, NULL) != 0)
135                                         return (errno);
136                 }
137         }
138
139         return (0);
140 }
141
142 static int
143 process_file_actions_entry(posix_spawn_file_actions_entry_t *fae)
144 {
145         int fd, saved_errno;
146
147         switch (fae->fae_action) {
148         case FAE_OPEN:
149                 /* Perform an open(), make it use the right fd */
150                 fd = _open(fae->fae_path, fae->fae_oflag, fae->fae_mode);
151                 if (fd < 0)
152                         return (errno);
153                 if (fd != fae->fae_fildes) {
154                         if (_dup2(fd, fae->fae_fildes) == -1) {
155                                 saved_errno = errno;
156                                 (void)_close(fd);
157                                 return (saved_errno);
158                         }
159                         if (_close(fd) != 0) {
160                                 if (errno == EBADF)
161                                         return (EBADF);
162                         }
163                 }
164                 if (_fcntl(fae->fae_fildes, F_SETFD, 0) == -1)
165                         return (errno);
166                 break;
167         case FAE_DUP2:
168                 /* Perform a dup2() */
169                 if (_dup2(fae->fae_fildes, fae->fae_newfildes) == -1)
170                         return (errno);
171                 if (_fcntl(fae->fae_newfildes, F_SETFD, 0) == -1)
172                         return (errno);
173                 break;
174         case FAE_CLOSE:
175                 /* Perform a close(), do not fail if already closed */
176                 (void)_close(fae->fae_fildes);
177                 break;
178         }
179         return (0);
180 }
181
182 static int
183 process_file_actions(const posix_spawn_file_actions_t fa)
184 {
185         posix_spawn_file_actions_entry_t *fae;
186         int error;
187
188         /* Replay all file descriptor modifications */
189         STAILQ_FOREACH(fae, &fa->fa_list, fae_list) {
190                 error = process_file_actions_entry(fae);
191                 if (error)
192                         return (error);
193         }
194         return (0);
195 }
196
197 static int
198 do_posix_spawn(pid_t *pid, const char *path,
199     const posix_spawn_file_actions_t *fa,
200     const posix_spawnattr_t *sa,
201     char * const argv[], char * const envp[], int use_env_path)
202 {
203         pid_t p;
204         volatile int error = 0;
205
206         p = vfork();
207         switch (p) {
208         case -1:
209                 return (errno);
210         case 0:
211                 if (sa != NULL) {
212                         error = process_spawnattr(*sa);
213                         if (error)
214                                 _exit(127);
215                 }
216                 if (fa != NULL) {
217                         error = process_file_actions(*fa);
218                         if (error)
219                                 _exit(127);
220                 }
221                 if (use_env_path)
222                         _execvpe(path, argv, envp != NULL ? envp : environ);
223                 else
224                         _execve(path, argv, envp != NULL ? envp : environ);
225                 error = errno;
226                 _exit(127);
227         default:
228                 if (error != 0)
229                         _waitpid(p, NULL, WNOHANG);
230                 else if (pid != NULL)
231                         *pid = p;
232                 return (error);
233         }
234 }
235
236 int
237 posix_spawn(pid_t *pid, const char *path,
238     const posix_spawn_file_actions_t *fa,
239     const posix_spawnattr_t *sa,
240     char * const argv[], char * const envp[])
241 {
242         return do_posix_spawn(pid, path, fa, sa, argv, envp, 0);
243 }
244
245 int
246 posix_spawnp(pid_t *pid, const char *path,
247     const posix_spawn_file_actions_t *fa,
248     const posix_spawnattr_t *sa,
249     char * const argv[], char * const envp[])
250 {
251         return do_posix_spawn(pid, path, fa, sa, argv, envp, 1);
252 }
253
254 /*
255  * File descriptor actions
256  */
257
258 int
259 posix_spawn_file_actions_init(posix_spawn_file_actions_t *ret)
260 {
261         posix_spawn_file_actions_t fa;
262
263         fa = malloc(sizeof(struct __posix_spawn_file_actions));
264         if (fa == NULL)
265                 return (-1);
266
267         STAILQ_INIT(&fa->fa_list);
268         *ret = fa;
269         return (0);
270 }
271
272 int
273 posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *fa)
274 {
275         posix_spawn_file_actions_entry_t *fae;
276
277         while ((fae = STAILQ_FIRST(&(*fa)->fa_list)) != NULL) {
278                 /* Remove file action entry from the queue */
279                 STAILQ_REMOVE_HEAD(&(*fa)->fa_list, fae_list);
280
281                 /* Deallocate file action entry */
282                 if (fae->fae_action == FAE_OPEN)
283                         free(fae->fae_path);
284                 free(fae);
285         }
286
287         free(*fa);
288         return (0);
289 }
290
291 int
292 posix_spawn_file_actions_addopen(posix_spawn_file_actions_t * __restrict fa,
293     int fildes, const char * __restrict path, int oflag, mode_t mode)
294 {
295         posix_spawn_file_actions_entry_t *fae;
296         int error;
297
298         if (fildes < 0)
299                 return (EBADF);
300
301         /* Allocate object */
302         fae = malloc(sizeof(posix_spawn_file_actions_entry_t));
303         if (fae == NULL)
304                 return (errno);
305
306         /* Set values and store in queue */
307         fae->fae_action = FAE_OPEN;
308         fae->fae_path = strdup(path);
309         if (fae->fae_path == NULL) {
310                 error = errno;
311                 free(fae);
312                 return (error);
313         }
314         fae->fae_fildes = fildes;
315         fae->fae_oflag = oflag;
316         fae->fae_mode = mode;
317
318         STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list);
319         return (0);
320 }
321
322 int
323 posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *fa,
324     int fildes, int newfildes)
325 {
326         posix_spawn_file_actions_entry_t *fae;
327
328         if (fildes < 0 || newfildes < 0)
329                 return (EBADF);
330
331         /* Allocate object */
332         fae = malloc(sizeof(posix_spawn_file_actions_entry_t));
333         if (fae == NULL)
334                 return (errno);
335
336         /* Set values and store in queue */
337         fae->fae_action = FAE_DUP2;
338         fae->fae_fildes = fildes;
339         fae->fae_newfildes = newfildes;
340
341         STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list);
342         return (0);
343 }
344
345 int
346 posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *fa,
347     int fildes)
348 {
349         posix_spawn_file_actions_entry_t *fae;
350
351         if (fildes < 0)
352                 return (EBADF);
353
354         /* Allocate object */
355         fae = malloc(sizeof(posix_spawn_file_actions_entry_t));
356         if (fae == NULL)
357                 return (errno);
358
359         /* Set values and store in queue */
360         fae->fae_action = FAE_CLOSE;
361         fae->fae_fildes = fildes;
362
363         STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list);
364         return (0);
365 }
366
367 /*
368  * Spawn attributes
369  */
370
371 int
372 posix_spawnattr_init(posix_spawnattr_t *ret)
373 {
374         posix_spawnattr_t sa;
375
376         sa = calloc(1, sizeof(struct __posix_spawnattr));
377         if (sa == NULL)
378                 return (errno);
379
380         /* Set defaults as specified by POSIX, cleared above */
381         *ret = sa;
382         return (0);
383 }
384
385 int
386 posix_spawnattr_destroy(posix_spawnattr_t *sa)
387 {
388         free(*sa);
389         return (0);
390 }
391
392 int
393 posix_spawnattr_getflags(const posix_spawnattr_t * __restrict sa,
394     short * __restrict flags)
395 {
396         *flags = (*sa)->sa_flags;
397         return (0);
398 }
399
400 int
401 posix_spawnattr_getpgroup(const posix_spawnattr_t * __restrict sa,
402     pid_t * __restrict pgroup)
403 {
404         *pgroup = (*sa)->sa_pgroup;
405         return (0);
406 }
407
408 int
409 posix_spawnattr_getschedparam(const posix_spawnattr_t * __restrict sa,
410     struct sched_param * __restrict schedparam)
411 {
412         *schedparam = (*sa)->sa_schedparam;
413         return (0);
414 }
415
416 int
417 posix_spawnattr_getschedpolicy(const posix_spawnattr_t * __restrict sa,
418     int * __restrict schedpolicy)
419 {
420         *schedpolicy = (*sa)->sa_schedpolicy;
421         return (0);
422 }
423
424 int
425 posix_spawnattr_getsigdefault(const posix_spawnattr_t * __restrict sa,
426     sigset_t * __restrict sigdefault)
427 {
428         *sigdefault = (*sa)->sa_sigdefault;
429         return (0);
430 }
431
432 int
433 posix_spawnattr_getsigmask(const posix_spawnattr_t * __restrict sa,
434     sigset_t * __restrict sigmask)
435 {
436         *sigmask = (*sa)->sa_sigmask;
437         return (0);
438 }
439
440 int
441 posix_spawnattr_setflags(posix_spawnattr_t *sa, short flags)
442 {
443         (*sa)->sa_flags = flags;
444         return (0);
445 }
446
447 int
448 posix_spawnattr_setpgroup(posix_spawnattr_t *sa, pid_t pgroup)
449 {
450         (*sa)->sa_pgroup = pgroup;
451         return (0);
452 }
453
454 int
455 posix_spawnattr_setschedparam(posix_spawnattr_t * __restrict sa,
456     const struct sched_param * __restrict schedparam)
457 {
458         (*sa)->sa_schedparam = *schedparam;
459         return (0);
460 }
461
462 int
463 posix_spawnattr_setschedpolicy(posix_spawnattr_t *sa, int schedpolicy)
464 {
465         (*sa)->sa_schedpolicy = schedpolicy;
466         return (0);
467 }
468
469 int
470 posix_spawnattr_setsigdefault(posix_spawnattr_t * __restrict sa,
471     const sigset_t * __restrict sigdefault)
472 {
473         (*sa)->sa_sigdefault = *sigdefault;
474         return (0);
475 }
476
477 int
478 posix_spawnattr_setsigmask(posix_spawnattr_t * __restrict sa,
479     const sigset_t * __restrict sigmask)
480 {
481         (*sa)->sa_sigmask = *sigmask;
482         return (0);
483 }