mirror of
				https://github.com/KevinMidboe/linguist.git
				synced 2025-10-29 17:50:22 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			463 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			463 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
 | |
|  *
 | |
|  * Permission is hereby granted, free of charge, to any person obtaining a copy
 | |
|  * of this software and associated documentation files (the "Software"), to
 | |
|  * deal in the Software without restriction, including without limitation the
 | |
|  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 | |
|  * sell copies of the Software, and to permit persons to whom the Software is
 | |
|  * furnished to do so, subject to the following conditions:
 | |
|  *
 | |
|  * The above copyright notice and this permission notice shall be included in
 | |
|  * all copies or substantial portions of the Software.
 | |
|  *
 | |
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | |
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | |
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | |
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | |
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 | |
|  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 | |
|  * IN THE SOFTWARE.
 | |
|  */
 | |
| 
 | |
| #include "uv.h"
 | |
| #include "internal.h"
 | |
| 
 | |
| #include <assert.h>
 | |
| #include <errno.h>
 | |
| #include <sys/wait.h>
 | |
| #include <poll.h>
 | |
| #include <unistd.h>
 | |
| #include <stdio.h>
 | |
| #include <fcntl.h>
 | |
| 
 | |
| #ifdef __APPLE__
 | |
| # include <TargetConditionals.h>
 | |
| #endif
 | |
| 
 | |
| #if defined(__APPLE__) && !TARGET_OS_IPHONE
 | |
| # include <crt_externs.h>
 | |
| # define environ (*_NSGetEnviron())
 | |
| #else
 | |
| extern char **environ;
 | |
| #endif
 | |
| 
 | |
| 
 | |
| static void uv__chld(EV_P_ ev_child* watcher, int revents) {
 | |
|   int status = watcher->rstatus;
 | |
|   int exit_status = 0;
 | |
|   int term_signal = 0;
 | |
|   uv_process_t *process = watcher->data;
 | |
| 
 | |
|   assert(&process->child_watcher == watcher);
 | |
|   assert(revents & EV_CHILD);
 | |
| 
 | |
|   ev_child_stop(EV_A_ &process->child_watcher);
 | |
| 
 | |
|   if (WIFEXITED(status)) {
 | |
|     exit_status = WEXITSTATUS(status);
 | |
|   }
 | |
| 
 | |
|   if (WIFSIGNALED(status)) {
 | |
|     term_signal = WTERMSIG(status);
 | |
|   }
 | |
| 
 | |
|   if (process->exit_cb) {
 | |
|     process->exit_cb(process, exit_status, term_signal);
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| int uv__make_socketpair(int fds[2], int flags) {
 | |
| #ifdef SOCK_NONBLOCK
 | |
|   int fl;
 | |
| 
 | |
|   fl = SOCK_CLOEXEC;
 | |
| 
 | |
|   if (flags & UV__F_NONBLOCK)
 | |
|     fl |= SOCK_NONBLOCK;
 | |
| 
 | |
|   if (socketpair(AF_UNIX, SOCK_STREAM|fl, 0, fds) == 0)
 | |
|     return 0;
 | |
| 
 | |
|   if (errno != EINVAL)
 | |
|     return -1;
 | |
| 
 | |
|   /* errno == EINVAL so maybe the kernel headers lied about
 | |
|    * the availability of SOCK_NONBLOCK. This can happen if people
 | |
|    * build libuv against newer kernel headers than the kernel
 | |
|    * they actually run the software on.
 | |
|    */
 | |
| #endif
 | |
| 
 | |
|   if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds))
 | |
|     return -1;
 | |
| 
 | |
|   uv__cloexec(fds[0], 1);
 | |
|   uv__cloexec(fds[1], 1);
 | |
| 
 | |
|   if (flags & UV__F_NONBLOCK) {
 | |
|     uv__nonblock(fds[0], 1);
 | |
|     uv__nonblock(fds[1], 1);
 | |
|   }
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| int uv__make_pipe(int fds[2], int flags) {
 | |
| #if __linux__
 | |
|   int fl;
 | |
| 
 | |
|   fl = UV__O_CLOEXEC;
 | |
| 
 | |
|   if (flags & UV__F_NONBLOCK)
 | |
|     fl |= UV__O_NONBLOCK;
 | |
| 
 | |
|   if (uv__pipe2(fds, fl) == 0)
 | |
|     return 0;
 | |
| 
 | |
|   if (errno != ENOSYS)
 | |
|     return -1;
 | |
| #endif
 | |
| 
 | |
|   if (pipe(fds))
 | |
|     return -1;
 | |
| 
 | |
|   uv__cloexec(fds[0], 1);
 | |
|   uv__cloexec(fds[1], 1);
 | |
| 
 | |
|   if (flags & UV__F_NONBLOCK) {
 | |
|     uv__nonblock(fds[0], 1);
 | |
|     uv__nonblock(fds[1], 1);
 | |
|   }
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * Used for initializing stdio streams like options.stdin_stream. Returns
 | |
|  * zero on success.
 | |
|  */
 | |
| static int uv__process_init_stdio(uv_stdio_container_t* container, int fds[2],
 | |
|                                   int writable) {
 | |
|   int fd = -1;
 | |
|   switch (container->flags & (UV_IGNORE | UV_CREATE_PIPE | UV_INHERIT_FD |
 | |
|                               UV_INHERIT_STREAM)) {
 | |
|     case UV_IGNORE:
 | |
|       return 0;
 | |
|     case UV_CREATE_PIPE:
 | |
|       assert(container->data.stream != NULL);
 | |
| 
 | |
|       if (container->data.stream->type != UV_NAMED_PIPE) {
 | |
|         errno = EINVAL;
 | |
|         return -1;
 | |
|       }
 | |
| 
 | |
|       return uv__make_socketpair(fds, 0);
 | |
|     case UV_INHERIT_FD:
 | |
|     case UV_INHERIT_STREAM:
 | |
|       if (container->flags & UV_INHERIT_FD) {
 | |
|         fd = container->data.fd;
 | |
|       } else {
 | |
|         fd = container->data.stream->fd;
 | |
|       }
 | |
| 
 | |
|       if (fd == -1) {
 | |
|         errno = EINVAL;
 | |
|         return -1;
 | |
|       }
 | |
| 
 | |
|       fds[writable ? 1 : 0] = fd;
 | |
| 
 | |
|       return 0;
 | |
|     default:
 | |
|       assert(0 && "Unexpected flags");
 | |
|       return -1;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| static int uv__process_stdio_flags(uv_stdio_container_t* container,
 | |
|                                    int writable) {
 | |
|   if (container->data.stream->type == UV_NAMED_PIPE &&
 | |
|       ((uv_pipe_t*)container->data.stream)->ipc) {
 | |
|     return UV_STREAM_READABLE | UV_STREAM_WRITABLE;
 | |
|   } else if (writable) {
 | |
|     return UV_STREAM_WRITABLE;
 | |
|   } else {
 | |
|     return UV_STREAM_READABLE;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| static int uv__process_open_stream(uv_stdio_container_t* container, int fds[2],
 | |
|                                    int writable) {
 | |
|   int fd = fds[writable ? 1 : 0];
 | |
|   int child_fd = fds[writable ? 0 : 1];
 | |
|   int flags;
 | |
| 
 | |
|   /* No need to create stream */
 | |
|   if (!(container->flags & UV_CREATE_PIPE) || fd < 0) {
 | |
|     return 0;
 | |
|   }
 | |
| 
 | |
|   assert(child_fd >= 0);
 | |
|   close(child_fd);
 | |
| 
 | |
|   uv__nonblock(fd, 1);
 | |
|   flags = uv__process_stdio_flags(container, writable);
 | |
| 
 | |
|   return uv__stream_open((uv_stream_t*)container->data.stream, fd, flags);
 | |
| }
 | |
| 
 | |
| 
 | |
| static void uv__process_close_stream(uv_stdio_container_t* container) {
 | |
|   if (!(container->flags & UV_CREATE_PIPE)) return;
 | |
| 
 | |
|   uv__stream_close((uv_stream_t*)container->data.stream);
 | |
| }
 | |
| 
 | |
| 
 | |
| static void uv__process_child_init(uv_process_options_t options,
 | |
|                                    int stdio_count,
 | |
|                                    int* pipes) {
 | |
|   int i;
 | |
| 
 | |
|   if (options.flags & UV_PROCESS_DETACHED) {
 | |
|     setsid();
 | |
|   }
 | |
| 
 | |
|   /* Dup fds */
 | |
|   for (i = 0; i < stdio_count; i++) {
 | |
|     /*
 | |
|      * stdin has swapped ends of pipe
 | |
|      * (it's the only one readable stream)
 | |
|      */
 | |
|     int close_fd = i == 0 ? pipes[i * 2 + 1] : pipes[i * 2];
 | |
|     int use_fd = i == 0 ? pipes[i * 2] : pipes[i * 2 + 1];
 | |
| 
 | |
|     if (use_fd >= 0) {
 | |
|       close(close_fd);
 | |
|     } else if (i < 3) {
 | |
|       /* `/dev/null` stdin, stdout, stderr even if they've flag UV_IGNORE */
 | |
|       use_fd = open("/dev/null", i == 0 ? O_RDONLY : O_RDWR);
 | |
| 
 | |
|       if (use_fd < 0) {
 | |
|         perror("failed to open stdio");
 | |
|         _exit(127);
 | |
|       }
 | |
|     } else {
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     if (i != use_fd) {
 | |
|       dup2(use_fd, i);
 | |
|       close(use_fd);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (options.cwd && chdir(options.cwd)) {
 | |
|     perror("chdir()");
 | |
|     _exit(127);
 | |
|   }
 | |
| 
 | |
|   if ((options.flags & UV_PROCESS_SETGID) && setgid(options.gid)) {
 | |
|     perror("setgid()");
 | |
|     _exit(127);
 | |
|   }
 | |
| 
 | |
|   if ((options.flags & UV_PROCESS_SETUID) && setuid(options.uid)) {
 | |
|     perror("setuid()");
 | |
|     _exit(127);
 | |
|   }
 | |
| 
 | |
|   environ = options.env;
 | |
| 
 | |
|   execvp(options.file, options.args);
 | |
|   perror("execvp()");
 | |
|   _exit(127);
 | |
| }
 | |
| 
 | |
| 
 | |
| #ifndef SPAWN_WAIT_EXEC
 | |
| # define SPAWN_WAIT_EXEC 1
 | |
| #endif
 | |
| 
 | |
| int uv_spawn(uv_loop_t* loop, uv_process_t* process,
 | |
|     uv_process_options_t options) {
 | |
|   /*
 | |
|    * Save environ in the case that we get it clobbered
 | |
|    * by the child process.
 | |
|    */
 | |
|   char** save_our_env = environ;
 | |
| 
 | |
|   int stdio_count = options.stdio_count < 3 ? 3 : options.stdio_count;
 | |
|   int* pipes = malloc(2 * stdio_count * sizeof(int));
 | |
| 
 | |
| #if SPAWN_WAIT_EXEC
 | |
|   int signal_pipe[2] = { -1, -1 };
 | |
|   struct pollfd pfd;
 | |
| #endif
 | |
|   int status;
 | |
|   pid_t pid;
 | |
|   int i;
 | |
| 
 | |
|   if (pipes == NULL) {
 | |
|     errno = ENOMEM;
 | |
|     goto error;
 | |
|   }
 | |
| 
 | |
|   assert(options.file != NULL);
 | |
|   assert(!(options.flags & ~(UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS |
 | |
|                              UV_PROCESS_DETACHED |
 | |
|                              UV_PROCESS_SETGID |
 | |
|                              UV_PROCESS_SETUID)));
 | |
| 
 | |
| 
 | |
|   uv__handle_init(loop, (uv_handle_t*)process, UV_PROCESS);
 | |
|   loop->counters.process_init++;
 | |
|   uv__handle_start(process);
 | |
| 
 | |
|   process->exit_cb = options.exit_cb;
 | |
| 
 | |
|   /* Init pipe pairs */
 | |
|   for (i = 0; i < stdio_count; i++) {
 | |
|     pipes[i * 2] = -1;
 | |
|     pipes[i * 2 + 1] = -1;
 | |
|   }
 | |
| 
 | |
|   /* Create socketpairs/pipes, or use raw fd */
 | |
|   for (i = 0; i < options.stdio_count; i++) {
 | |
|     if (uv__process_init_stdio(&options.stdio[i], pipes + i * 2, i != 0)) {
 | |
|       goto error;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /* This pipe is used by the parent to wait until
 | |
|    * the child has called `execve()`. We need this
 | |
|    * to avoid the following race condition:
 | |
|    *
 | |
|    *    if ((pid = fork()) > 0) {
 | |
|    *      kill(pid, SIGTERM);
 | |
|    *    }
 | |
|    *    else if (pid == 0) {
 | |
|    *      execve("/bin/cat", argp, envp);
 | |
|    *    }
 | |
|    *
 | |
|    * The parent sends a signal immediately after forking.
 | |
|    * Since the child may not have called `execve()` yet,
 | |
|    * there is no telling what process receives the signal,
 | |
|    * our fork or /bin/cat.
 | |
|    *
 | |
|    * To avoid ambiguity, we create a pipe with both ends
 | |
|    * marked close-on-exec. Then, after the call to `fork()`,
 | |
|    * the parent polls the read end until it sees POLLHUP.
 | |
|    */
 | |
| #if SPAWN_WAIT_EXEC
 | |
|   if (uv__make_pipe(signal_pipe, UV__F_NONBLOCK))
 | |
|     goto error;
 | |
| #endif
 | |
| 
 | |
|   pid = fork();
 | |
| 
 | |
|   if (pid == -1) {
 | |
| #if SPAWN_WAIT_EXEC
 | |
|     close(signal_pipe[0]);
 | |
|     close(signal_pipe[1]);
 | |
| #endif
 | |
|     environ = save_our_env;
 | |
|     goto error;
 | |
|   }
 | |
| 
 | |
|   if (pid == 0) {
 | |
|     /* Child */
 | |
|     uv__process_child_init(options, stdio_count, pipes);
 | |
| 
 | |
|     /* Execution never reaches here. */
 | |
|   }
 | |
| 
 | |
|   /* Parent. */
 | |
| 
 | |
|   /* Restore environment. */
 | |
|   environ = save_our_env;
 | |
| 
 | |
| #if SPAWN_WAIT_EXEC
 | |
|   /* POLLHUP signals child has exited or execve()'d. */
 | |
|   close(signal_pipe[1]);
 | |
|   do {
 | |
|     pfd.fd = signal_pipe[0];
 | |
|     pfd.events = POLLIN|POLLHUP;
 | |
|     pfd.revents = 0;
 | |
|     errno = 0, status = poll(&pfd, 1, -1);
 | |
|   }
 | |
|   while (status == -1 && (errno == EINTR || errno == ENOMEM));
 | |
| 
 | |
|   assert((status == 1) && "poll() on pipe read end failed");
 | |
|   close(signal_pipe[0]);
 | |
| #endif
 | |
| 
 | |
|   process->pid = pid;
 | |
| 
 | |
|   ev_child_init(&process->child_watcher, uv__chld, pid, 0);
 | |
|   ev_child_start(process->loop->ev, &process->child_watcher);
 | |
|   process->child_watcher.data = process;
 | |
| 
 | |
|   for (i = 0; i < options.stdio_count; i++) {
 | |
|     if (uv__process_open_stream(&options.stdio[i], pipes + i * 2, i == 0)) {
 | |
|       int j;
 | |
|       /* Close all opened streams */
 | |
|       for (j = 0; j < i; j++) {
 | |
|         uv__process_close_stream(&options.stdio[j]);
 | |
|       }
 | |
| 
 | |
|       goto error;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   free(pipes);
 | |
| 
 | |
|   return 0;
 | |
| 
 | |
| error:
 | |
|   uv__set_sys_error(process->loop, errno);
 | |
| 
 | |
|   for (i = 0; i < stdio_count; i++) {
 | |
|     close(pipes[i * 2]);
 | |
|     close(pipes[i * 2 + 1]);
 | |
|   }
 | |
| 
 | |
|   free(pipes);
 | |
| 
 | |
|   return -1;
 | |
| }
 | |
| 
 | |
| 
 | |
| int uv_process_kill(uv_process_t* process, int signum) {
 | |
|   int r = kill(process->pid, signum);
 | |
| 
 | |
|   if (r) {
 | |
|     uv__set_sys_error(process->loop, errno);
 | |
|     return -1;
 | |
|   } else {
 | |
|     return 0;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| uv_err_t uv_kill(int pid, int signum) {
 | |
|   int r = kill(pid, signum);
 | |
| 
 | |
|   if (r) {
 | |
|     return uv__new_sys_error(errno);
 | |
|   } else {
 | |
|     return uv_ok_;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| void uv__process_close(uv_process_t* handle) {
 | |
|   ev_child_stop(handle->loop->ev, &handle->child_watcher);
 | |
|   uv__handle_stop(handle);
 | |
| }
 |