This source file includes following definitions.
- grow_inheritable_handles
- shrink_inheritable_handles
- close_inheritable_handles
- memiszero
- sigisempty
- open_handle
- do_open
- do_dup2
- do_close
- __spawni
- __spawni
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 #include <config.h>
19
20
21 #include <spawn.h>
22 #include "spawn_int.h"
23
24 #include <alloca.h>
25 #include <errno.h>
26
27 #include <fcntl.h>
28 #ifndef O_LARGEFILE
29 # define O_LARGEFILE 0
30 #endif
31
32 #if _LIBC || HAVE_PATHS_H
33 # include <paths.h>
34 #else
35 # define _PATH_BSHELL BOURNE_SHELL
36 #endif
37
38 #include <signal.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42
43 #if _LIBC
44 # include <not-cancel.h>
45 #else
46 # define close_not_cancel close
47 # define open_not_cancel open
48 #endif
49
50 #if _LIBC
51 # include <local-setxid.h>
52 #else
53 # if !HAVE_SETEUID
54 # define seteuid(id) setresuid (-1, id, -1)
55 # endif
56 # if !HAVE_SETEGID
57 # define setegid(id) setresgid (-1, id, -1)
58 # endif
59 # define local_seteuid(id) seteuid (id)
60 # define local_setegid(id) setegid (id)
61 #endif
62
63 #if _LIBC
64 # define alloca __alloca
65 # define execve __execve
66 # define dup2 __dup2
67 # define fork __fork
68 # define getgid __getgid
69 # define getuid __getuid
70 # define sched_setparam __sched_setparam
71 # define sched_setscheduler __sched_setscheduler
72 # define setpgid __setpgid
73 # define sigaction __sigaction
74 # define sigismember __sigismember
75 # define sigprocmask __sigprocmask
76 # define strchrnul __strchrnul
77 # define vfork __vfork
78 #endif
79
80
81
82
83
84
85
86 #define SPAWN_ERROR 127
87
88
89 #if defined _WIN32 && ! defined __CYGWIN__
90
91
92
93 # define WIN32_LEAN_AND_MEAN
94 # include <windows.h>
95
96 # include <stdbool.h>
97 # include <stdio.h>
98
99 # include "filename.h"
100 # include "concat-filename.h"
101 # include "findprog.h"
102 # include "malloca.h"
103 # include "windows-spawn.h"
104
105
106 # undef CreateFile
107 # define CreateFile CreateFileA
108 # undef STARTUPINFO
109 # define STARTUPINFO STARTUPINFOA
110 # undef CreateProcess
111 # define CreateProcess CreateProcessA
112
113
114
115
116 static int
117 grow_inheritable_handles (struct inheritable_handles *inh_handles, int newfd)
118 {
119 if (inh_handles->allocated <= newfd)
120 {
121 size_t new_allocated = 2 * inh_handles->allocated + 1;
122 if (new_allocated <= newfd)
123 new_allocated = newfd + 1;
124 HANDLE *new_handles_array =
125 (HANDLE *)
126 realloc (inh_handles->handles, new_allocated * sizeof (HANDLE));
127 if (new_handles_array == NULL)
128 {
129 errno = ENOMEM;
130 return -1;
131 }
132 unsigned char *new_flags_array =
133 (unsigned char *)
134 realloc (inh_handles->flags, new_allocated * sizeof (unsigned char));
135 if (new_flags_array == NULL)
136 {
137 free (new_handles_array);
138 errno = ENOMEM;
139 return -1;
140 }
141 inh_handles->allocated = new_allocated;
142 inh_handles->handles = new_handles_array;
143 inh_handles->flags = new_flags_array;
144 }
145
146 HANDLE *handles = inh_handles->handles;
147
148 for (; inh_handles->count <= newfd; inh_handles->count++)
149 handles[inh_handles->count] = INVALID_HANDLE_VALUE;
150
151 return 0;
152 }
153
154
155 static void
156 shrink_inheritable_handles (struct inheritable_handles *inh_handles)
157 {
158 HANDLE *handles = inh_handles->handles;
159
160 while (inh_handles->count > 3
161 && handles[inh_handles->count - 1] == INVALID_HANDLE_VALUE)
162 inh_handles->count--;
163 }
164
165
166 static void
167 close_inheritable_handles (struct inheritable_handles *inh_handles)
168 {
169 HANDLE *handles = inh_handles->handles;
170 size_t handles_count = inh_handles->count;
171 unsigned int fd;
172
173 for (fd = 0; fd < handles_count; fd++)
174 {
175 HANDLE handle = handles[fd];
176
177 if (handle != INVALID_HANDLE_VALUE)
178 CloseHandle (handle);
179 }
180 }
181
182
183
184 static bool
185 memiszero (const void *p, size_t n)
186 {
187 const char *cp = p;
188 for (; n > 0; cp++, n--)
189 if (*cp != 0)
190 return 0;
191 return 1;
192 }
193
194
195 static bool
196 sigisempty (const sigset_t *s)
197 {
198 return memiszero (s, sizeof (sigset_t));
199 }
200
201
202
203 static HANDLE
204 open_handle (const char *name, int flags, mode_t mode)
205 {
206
207 if (strcmp (name, "/dev/null") == 0)
208 name = "NUL";
209
210
211
212
213 if (ISSLASH (name[0]) && ISSLASH (name[1]) && ISSLASH (name[2]))
214 {
215 name += 2;
216 while (ISSLASH (name[1]))
217 name++;
218 }
219
220 size_t len = strlen (name);
221 size_t drive_prefix_len = (HAS_DEVICE (name) ? 2 : 0);
222
223
224
225 size_t rlen;
226 bool check_dir = false;
227
228 rlen = len;
229 while (rlen > drive_prefix_len && ISSLASH (name[rlen-1]))
230 {
231 check_dir = true;
232 if (rlen == drive_prefix_len + 1)
233 break;
234 rlen--;
235 }
236
237
238 if (!check_dir && rlen == drive_prefix_len)
239 {
240 errno = ENOENT;
241 return INVALID_HANDLE_VALUE;
242 }
243
244
245 if (rlen == 1 && ISSLASH (name[0]) && len >= 2)
246 {
247 errno = ENOENT;
248 return INVALID_HANDLE_VALUE;
249 }
250
251 const char *rname;
252 char *malloca_rname;
253 if (rlen == len)
254 {
255 rname = name;
256 malloca_rname = NULL;
257 }
258 else
259 {
260 malloca_rname = malloca (rlen + 1);
261 if (malloca_rname == NULL)
262 {
263 errno = ENOMEM;
264 return INVALID_HANDLE_VALUE;
265 }
266 memcpy (malloca_rname, name, rlen);
267 malloca_rname[rlen] = '\0';
268 rname = malloca_rname;
269 }
270
271
272
273
274
275
276
277 HANDLE handle =
278 CreateFile (rname,
279 ((flags & (O_WRONLY | O_RDWR)) != 0
280 ? GENERIC_READ | GENERIC_WRITE
281 : GENERIC_READ),
282 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
283 NULL,
284 ((flags & O_CREAT) != 0
285 ? ((flags & O_EXCL) != 0
286 ? CREATE_NEW
287 : ((flags & O_TRUNC) != 0 ? CREATE_ALWAYS : OPEN_ALWAYS))
288 : ((flags & O_TRUNC) != 0
289 ? TRUNCATE_EXISTING
290 : OPEN_EXISTING)),
291
292
293
294
295
296
297 FILE_ATTRIBUTE_NORMAL
298 | ((flags & O_TEMPORARY) != 0 ? FILE_FLAG_DELETE_ON_CLOSE : 0)
299 | ((flags & O_SEQUENTIAL ) != 0 ? FILE_FLAG_SEQUENTIAL_SCAN : 0)
300 | ((flags & O_RANDOM) != 0 ? FILE_FLAG_RANDOM_ACCESS : 0),
301 NULL);
302 if (handle == INVALID_HANDLE_VALUE)
303 switch (GetLastError ())
304 {
305
306
307 case ERROR_FILE_NOT_FOUND:
308 case ERROR_PATH_NOT_FOUND:
309 case ERROR_BAD_PATHNAME:
310 case ERROR_BAD_NETPATH:
311 case ERROR_BAD_NET_NAME:
312 case ERROR_INVALID_NAME:
313 case ERROR_DIRECTORY:
314 errno = ENOENT;
315 break;
316
317 case ERROR_ACCESS_DENIED:
318 case ERROR_SHARING_VIOLATION:
319
320 errno = EACCES;
321 break;
322
323 case ERROR_OUTOFMEMORY:
324 errno = ENOMEM;
325 break;
326
327 case ERROR_WRITE_PROTECT:
328 errno = EROFS;
329 break;
330
331 case ERROR_WRITE_FAULT:
332 case ERROR_READ_FAULT:
333 case ERROR_GEN_FAILURE:
334 errno = EIO;
335 break;
336
337 case ERROR_BUFFER_OVERFLOW:
338 case ERROR_FILENAME_EXCED_RANGE:
339 errno = ENAMETOOLONG;
340 break;
341
342 case ERROR_DELETE_PENDING:
343 errno = EPERM;
344 break;
345
346 default:
347 errno = EINVAL;
348 break;
349 }
350
351 if (malloca_rname != NULL)
352 {
353 int saved_errno = errno;
354 freea (malloca_rname);
355 errno = saved_errno;
356 }
357 return handle;
358 }
359
360
361
362
363 static int
364 do_open (struct inheritable_handles *inh_handles, int newfd,
365 const char *filename, const char *directory,
366 int flags, mode_t mode, HANDLE curr_process)
367 {
368 if (!(newfd >= 0 && newfd < _getmaxstdio ()))
369 {
370 errno = EBADF;
371 return -1;
372 }
373 if (grow_inheritable_handles (inh_handles, newfd) < 0)
374 return -1;
375 if (inh_handles->handles[newfd] != INVALID_HANDLE_VALUE
376 && !CloseHandle (inh_handles->handles[newfd]))
377 {
378 errno = EIO;
379 return -1;
380 }
381 if (filename == NULL)
382 {
383 errno = EINVAL;
384 return -1;
385 }
386 char *filename_to_free = NULL;
387 if (directory != NULL && IS_RELATIVE_FILE_NAME (filename))
388 {
389 char *real_filename = concatenated_filename (directory, filename, NULL);
390 if (real_filename == NULL)
391 {
392 errno = ENOMEM;
393 return -1;
394 }
395 filename = real_filename;
396 filename_to_free = real_filename;
397 }
398 HANDLE handle = open_handle (filename, flags, mode);
399 if (handle == INVALID_HANDLE_VALUE)
400 {
401 free (filename_to_free);
402 return -1;
403 }
404 free (filename_to_free);
405
406 if (!DuplicateHandle (curr_process, handle,
407 curr_process, &inh_handles->handles[newfd],
408 0, TRUE,
409 DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS))
410 {
411 errno = EBADF;
412 return -1;
413 }
414 inh_handles->flags[newfd] = ((flags & O_APPEND) != 0 ? 32 : 0);
415 return 0;
416 }
417
418
419
420
421 static int
422 do_dup2 (struct inheritable_handles *inh_handles, int oldfd, int newfd,
423 HANDLE curr_process)
424 {
425 if (!(oldfd >= 0 && oldfd < inh_handles->count
426 && inh_handles->handles[oldfd] != INVALID_HANDLE_VALUE))
427 {
428 errno = EBADF;
429 return -1;
430 }
431 if (!(newfd >= 0 && newfd < _getmaxstdio ()))
432 {
433 errno = EBADF;
434 return -1;
435 }
436 if (newfd != oldfd)
437 {
438 if (grow_inheritable_handles (inh_handles, newfd) < 0)
439 return -1;
440 if (inh_handles->handles[newfd] != INVALID_HANDLE_VALUE
441 && !CloseHandle (inh_handles->handles[newfd]))
442 {
443 errno = EIO;
444 return -1;
445 }
446
447
448 if (!DuplicateHandle (curr_process, inh_handles->handles[oldfd],
449 curr_process, &inh_handles->handles[newfd],
450 0, TRUE, DUPLICATE_SAME_ACCESS))
451 {
452 errno = EBADF;
453 return -1;
454 }
455 inh_handles->flags[newfd] = 0;
456 }
457 return 0;
458 }
459
460
461
462
463 static int
464 do_close (struct inheritable_handles *inh_handles, int fd)
465 {
466 if (!(fd >= 0 && fd < inh_handles->count
467 && inh_handles->handles[fd] != INVALID_HANDLE_VALUE))
468 {
469 errno = EBADF;
470 return -1;
471 }
472 if (!CloseHandle (inh_handles->handles[fd]))
473 {
474 errno = EIO;
475 return -1;
476 }
477 inh_handles->handles[fd] = INVALID_HANDLE_VALUE;
478 return 0;
479 }
480
481 int
482 __spawni (pid_t *pid, const char *prog_filename,
483 const posix_spawn_file_actions_t *file_actions,
484 const posix_spawnattr_t *attrp, const char *const prog_argv[],
485 const char *const envp[], int use_path)
486 {
487
488 if (prog_filename == NULL
489 || (attrp != NULL
490 && ((attrp->_flags & ~POSIX_SPAWN_SETPGROUP) != 0
491 || attrp->_pgrp != 0
492 || ! sigisempty (&attrp->_sd)
493 || ! sigisempty (&attrp->_ss)
494 || attrp->_sp.sched_priority != 0
495 || attrp->_policy != 0)))
496 return EINVAL;
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516 DWORD process_creation_flags =
517 (attrp != NULL && (attrp->_flags & POSIX_SPAWN_SETPGROUP) != 0 ? DETACHED_PROCESS : 0);
518
519 char *argv_mem_to_free;
520 const char **argv = prepare_spawn (prog_argv, &argv_mem_to_free);
521 if (argv == NULL)
522 return errno;
523 argv++;
524
525
526 char *command = compose_command (argv);
527 if (command == NULL)
528 {
529 free (argv_mem_to_free);
530 return ENOMEM;
531 }
532
533
534 char *envblock;
535 if (envp == NULL)
536 envblock = NULL;
537 else
538 {
539 envblock = compose_envblock (envp);
540 if (envblock == NULL)
541 {
542 free (command);
543 free (argv_mem_to_free);
544 return ENOMEM;
545 }
546 }
547
548
549
550
551
552
553 struct inheritable_handles inh_handles;
554 if (init_inheritable_handles (&inh_handles, true) < 0)
555 goto failed_1;
556
557
558 const char *directory = NULL;
559
560
561
562 if (file_actions != NULL)
563 {
564 HANDLE curr_process = GetCurrentProcess ();
565 int cnt;
566
567 for (cnt = 0; cnt < file_actions->_used; ++cnt)
568 {
569 struct __spawn_action *action = &file_actions->_actions[cnt];
570
571 switch (action->tag)
572 {
573 case spawn_do_close:
574 {
575 int fd = action->action.close_action.fd;
576 if (do_close (&inh_handles, fd) < 0)
577 goto failed_2;
578 }
579 break;
580
581 case spawn_do_open:
582 {
583 int newfd = action->action.open_action.fd;
584 const char *filename = action->action.open_action.path;
585 int flags = action->action.open_action.oflag;
586 mode_t mode = action->action.open_action.mode;
587 if (do_open (&inh_handles, newfd, filename, directory,
588 flags, mode, curr_process)
589 < 0)
590 goto failed_2;
591 }
592 break;
593
594 case spawn_do_dup2:
595 {
596 int oldfd = action->action.dup2_action.fd;
597 int newfd = action->action.dup2_action.newfd;
598 if (do_dup2 (&inh_handles, oldfd, newfd, curr_process) < 0)
599 goto failed_2;
600 }
601 break;
602
603 case spawn_do_chdir:
604 {
605 char *newdir = action->action.chdir_action.path;
606 if (directory != NULL && IS_RELATIVE_FILE_NAME (newdir))
607 {
608 newdir = concatenated_filename (directory, newdir, NULL);
609 if (newdir == NULL)
610 {
611 errno = ENOMEM;
612 goto failed_2;
613 }
614 }
615 directory = newdir;
616 }
617 break;
618
619 case spawn_do_fchdir:
620
621 errno = EINVAL;
622 goto failed_2;
623 }
624 }
625 }
626
627
628 shrink_inheritable_handles (&inh_handles);
629
630
631
632
633
634 STARTUPINFO sinfo;
635 sinfo.cb = sizeof (STARTUPINFO);
636 sinfo.lpReserved = NULL;
637 sinfo.lpDesktop = NULL;
638 sinfo.lpTitle = NULL;
639 if (compose_handles_block (&inh_handles, &sinfo) < 0)
640 goto failed_2;
641
642
643 char *resolved_prog_filename_to_free = NULL;
644 {
645 const char *resolved_prog_filename =
646 find_in_given_path (prog_filename, use_path ? getenv ("PATH") : "",
647 directory, false);
648 if (resolved_prog_filename == NULL)
649 goto failed_3;
650 if (resolved_prog_filename != prog_filename)
651 resolved_prog_filename_to_free = (char *) resolved_prog_filename;
652 prog_filename = resolved_prog_filename;
653 }
654
655 PROCESS_INFORMATION pinfo;
656 if (!CreateProcess (prog_filename, command, NULL, NULL, TRUE,
657 process_creation_flags, envblock, directory, &sinfo,
658 &pinfo))
659 {
660 DWORD error = GetLastError ();
661
662 free (resolved_prog_filename_to_free);
663 free (sinfo.lpReserved2);
664 close_inheritable_handles (&inh_handles);
665 free_inheritable_handles (&inh_handles);
666 free (envblock);
667 free (command);
668 free (argv_mem_to_free);
669
670 return convert_CreateProcess_error (error);
671 }
672
673 if (pinfo.hThread)
674 CloseHandle (pinfo.hThread);
675
676 free (resolved_prog_filename_to_free);
677 free (sinfo.lpReserved2);
678 close_inheritable_handles (&inh_handles);
679 free_inheritable_handles (&inh_handles);
680 free (envblock);
681 free (command);
682 free (argv_mem_to_free);
683
684 if (pid != NULL)
685 *pid = (intptr_t) pinfo.hProcess;
686 return 0;
687
688 failed_3:
689 {
690 int saved_errno = errno;
691 free (sinfo.lpReserved2);
692 close_inheritable_handles (&inh_handles);
693 free_inheritable_handles (&inh_handles);
694 free (envblock);
695 free (command);
696 free (argv_mem_to_free);
697 return saved_errno;
698 }
699
700 failed_2:
701 {
702 int saved_errno = errno;
703 close_inheritable_handles (&inh_handles);
704 free_inheritable_handles (&inh_handles);
705 free (envblock);
706 free (command);
707 free (argv_mem_to_free);
708 return saved_errno;
709 }
710
711 failed_1:
712 free (envblock);
713 free (command);
714 free (argv_mem_to_free);
715 return errno;
716 }
717
718 #else
719
720
721
722
723 int
724 __spawni (pid_t *pid, const char *file,
725 const posix_spawn_file_actions_t *file_actions,
726 const posix_spawnattr_t *attrp, const char *const argv[],
727 const char *const envp[], int use_path)
728 {
729 pid_t new_pid;
730 char *path, *p, *name;
731 size_t len;
732 size_t pathlen;
733
734
735 short int flags = attrp == NULL ? 0 : attrp->_flags;
736
737
738
739 (void) &flags;
740
741
742 #if HAVE_VFORK
743 if ((flags & POSIX_SPAWN_USEVFORK) != 0
744
745
746
747
748 || ((flags & (POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF
749 | POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER
750 | POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_RESETIDS)) == 0
751 && file_actions == NULL))
752 new_pid = vfork ();
753 else
754 #endif
755 new_pid = fork ();
756
757 if (new_pid != 0)
758 {
759 if (new_pid < 0)
760 return errno;
761
762
763 if (pid != NULL)
764 *pid = new_pid;
765
766 return 0;
767 }
768
769
770 if ((flags & POSIX_SPAWN_SETSIGMASK) != 0
771 && sigprocmask (SIG_SETMASK, &attrp->_ss, NULL) != 0)
772 _exit (SPAWN_ERROR);
773
774
775 if ((flags & POSIX_SPAWN_SETSIGDEF) != 0)
776 {
777
778
779
780
781 int sig;
782 struct sigaction sa;
783
784 memset (&sa, '\0', sizeof (sa));
785 sa.sa_handler = SIG_DFL;
786
787 for (sig = 1; sig <= NSIG; ++sig)
788 if (sigismember (&attrp->_sd, sig) != 0
789 && sigaction (sig, &sa, NULL) != 0)
790 _exit (SPAWN_ERROR);
791
792 }
793
794 #if (_LIBC ? defined _POSIX_PRIORITY_SCHEDULING : HAVE_SCHED_SETPARAM && HAVE_SCHED_SETSCHEDULER)
795
796 if ((flags & (POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER))
797 == POSIX_SPAWN_SETSCHEDPARAM)
798 {
799 if (sched_setparam (0, &attrp->_sp) == -1)
800 _exit (SPAWN_ERROR);
801 }
802 else if ((flags & POSIX_SPAWN_SETSCHEDULER) != 0)
803 {
804 if (sched_setscheduler (0, attrp->_policy,
805 (flags & POSIX_SPAWN_SETSCHEDPARAM) != 0
806 ? &attrp->_sp : NULL) == -1)
807 _exit (SPAWN_ERROR);
808 }
809 #endif
810
811
812 if ((flags & POSIX_SPAWN_SETPGROUP) != 0
813 && setpgid (0, attrp->_pgrp) != 0)
814 _exit (SPAWN_ERROR);
815
816
817 if ((flags & POSIX_SPAWN_RESETIDS) != 0
818 && (local_seteuid (getuid ()) != 0
819 || local_setegid (getgid ()) != 0))
820 _exit (SPAWN_ERROR);
821
822
823 if (file_actions != NULL)
824 {
825 int cnt;
826
827 for (cnt = 0; cnt < file_actions->_used; ++cnt)
828 {
829 struct __spawn_action *action = &file_actions->_actions[cnt];
830
831 switch (action->tag)
832 {
833 case spawn_do_close:
834 if (close_not_cancel (action->action.close_action.fd) != 0)
835
836 _exit (SPAWN_ERROR);
837 break;
838
839 case spawn_do_open:
840 {
841 int new_fd = open_not_cancel (action->action.open_action.path,
842 action->action.open_action.oflag
843 | O_LARGEFILE,
844 action->action.open_action.mode);
845
846 if (new_fd == -1)
847
848 _exit (SPAWN_ERROR);
849
850
851 if (new_fd != action->action.open_action.fd)
852 {
853 if (dup2 (new_fd, action->action.open_action.fd)
854 != action->action.open_action.fd)
855
856 _exit (SPAWN_ERROR);
857
858 if (close_not_cancel (new_fd) != 0)
859
860 _exit (SPAWN_ERROR);
861 }
862 }
863 break;
864
865 case spawn_do_dup2:
866 if (dup2 (action->action.dup2_action.fd,
867 action->action.dup2_action.newfd)
868 != action->action.dup2_action.newfd)
869
870 _exit (SPAWN_ERROR);
871 break;
872
873 case spawn_do_chdir:
874 if (chdir (action->action.chdir_action.path) < 0)
875
876 _exit (SPAWN_ERROR);
877 break;
878
879 case spawn_do_fchdir:
880 if (fchdir (action->action.fchdir_action.fd) < 0)
881
882 _exit (SPAWN_ERROR);
883 break;
884 }
885 }
886 }
887
888 if (! use_path || strchr (file, '/') != NULL)
889 {
890
891 execve (file, (char * const *) argv, (char * const *) envp);
892
893
894 _exit (SPAWN_ERROR);
895 }
896
897
898 path = getenv ("PATH");
899 if (path == NULL)
900 {
901 #if HAVE_CONFSTR
902
903
904
905 len = confstr (_CS_PATH, (char *) NULL, 0);
906 path = (char *) alloca (1 + len);
907 path[0] = ':';
908 (void) confstr (_CS_PATH, path + 1, len);
909 #else
910
911 path = "";
912 #endif
913 }
914
915 len = strlen (file) + 1;
916 pathlen = strlen (path);
917 name = alloca (pathlen + len + 1);
918
919 name = (char *) memcpy (name + pathlen + 1, file, len);
920
921 *--name = '/';
922
923 p = path;
924 do
925 {
926 char *startp;
927
928 path = p;
929 p = strchrnul (path, ':');
930
931 if (p == path)
932
933
934 startp = name + 1;
935 else
936 startp = (char *) memcpy (name - (p - path), path, p - path);
937
938
939 execve (startp, (char * const *) argv, (char * const *) envp);
940
941 switch (errno)
942 {
943 case EACCES:
944 case ENOENT:
945 case ESTALE:
946 case ENOTDIR:
947
948
949
950 break;
951
952 default:
953
954
955
956 _exit (SPAWN_ERROR);
957 }
958 }
959 while (*p++ != '\0');
960
961
962 _exit (SPAWN_ERROR);
963 }
964
965 #endif