1 /* Copyright 2017-present Facebook, Inc. 2 * Licensed under the Apache License, Version 2.0 */ 3 #pragma once 4 #include "watchman_system.h" 5 #include <spawn.h> 6 #include <mutex> 7 #include <string> 8 #include <unordered_map> 9 #include <vector> 10 #include "Future.h" 11 #include "Pipe.h" 12 #include "thirdparty/jansson/jansson.h" 13 #include "watchman_string.h" 14 15 namespace watchman { 16 17 class ChildProcess { 18 public: 19 struct Deleter { operatorDeleter20 void operator()(char** vec) const { 21 free((void*)vec); 22 } 23 }; 24 25 class Environment { 26 public: 27 // Constructs an environment from the current process environment 28 Environment(); 29 Environment(const Environment&) = default; 30 /* implicit */ Environment( 31 const std::unordered_map<w_string, w_string>& map); 32 33 Environment& operator=(const Environment&) = default; 34 35 // Returns the environment as an environ compatible array 36 std::unique_ptr<char*, Deleter> asEnviron(size_t* env_size = nullptr) const; 37 38 // Set a value in the environment 39 void set(const w_string& key, const w_string& value); 40 void set( 41 std::initializer_list<std::pair<w_string_piece, w_string_piece>> pairs); 42 void set(const w_string& key, bool bval); 43 44 // Remove a value from the environment 45 void unset(const w_string& key); 46 47 private: 48 std::unordered_map<w_string, w_string> map_; 49 }; 50 51 class Options { 52 public: 53 Options(); 54 // Not copyable 55 Options(const Options&) = delete; 56 Options(Options&&) = default; 57 Options& operator=(const Options&) = delete; 58 Options& operator=(Options&&) = default; 59 60 #ifdef POSIX_SPAWN_SETSIGMASK 61 void setSigMask(const sigset_t& mask); 62 #endif 63 // Adds flags to the set of flags maintainted in the spawn attributes. 64 // This is logically equivalent to calling setflags(getflags()|flags) 65 void setFlags(short flags); 66 67 Environment& environment(); 68 69 // Arranges to duplicate an fd from the parent as targetFd in 70 // the child process. 71 void dup2(int sourceFd, int targetFd); 72 void dup2(const FileDescriptor& fd, int targetFd); 73 74 // Arranges to create a pipe for communicating between the 75 // parent and child process and setting it as targetFd in 76 // the child. 77 void pipe(int targetFd, bool childRead); 78 79 // Set up stdin with a pipe 80 void pipeStdin(); 81 82 // Set up stdout with a pipe 83 void pipeStdout(); 84 85 // Set up stderr with a pipe 86 void pipeStderr(); 87 88 // Set up stdin with a null device 89 void nullStdin(); 90 91 // Arrange to open(2) a file for the child process and make 92 // it available as targetFd 93 void open(int targetFd, const char* path, int flags, int mode); 94 95 // Arrange to set the cwd for the child process 96 void chdir(w_string_piece path); 97 98 private: 99 struct Inner { 100 // There is no defined way to copy or move either of 101 // these things, so we separate them out into a container 102 // that we can point to and move the pointer. 103 posix_spawn_file_actions_t actions; 104 posix_spawnattr_t attr; 105 106 Inner(); 107 ~Inner(); 108 }; 109 std::unique_ptr<Inner> inner_; 110 Environment env_; 111 std::unordered_map<int, std::unique_ptr<Pipe>> pipes_; 112 std::string cwd_; 113 114 friend class ChildProcess; 115 }; 116 117 ChildProcess(std::vector<w_string_piece> args, Options&& options); 118 ChildProcess(const json_ref& args, Options&& options); 119 ~ChildProcess(); 120 121 // Check to see if the process has terminated. 122 // Does not block. Returns true if the process has 123 // terminated, false otherwise. 124 bool terminated(); 125 126 // Wait for the process to terminate and return its 127 // exit status. If the process has already terminated, 128 // immediately returns its exit status. 129 int wait(); 130 131 // Disassociate from the running process. 132 // We will no longer be able to wait for it to complete. 133 // This causes minor leakage of resources. 134 void disown(); 135 136 // This mutex is present to avoid fighting over the cwd when multiple 137 // process might need to chdir concurrently 138 static std::unique_lock<std::mutex> lockCwdMutex(); 139 140 // Terminates the process 141 void kill( 142 #ifndef _WIN32 143 int signo = SIGTERM 144 #endif 145 ); 146 147 // The pipeWriteCallback is called by communicate when it is safe to write 148 // data to the pipe. The callback should then attempt to write to it. 149 // The callback must return true when it has nothing more 150 // to write to the input of the child. This will cause the 151 // pipe to be closed. 152 // Note that the pipe may be non-blocking, and you must not loop attempting 153 // to write data to the pipe - the caller will arrange to call you again 154 // if you return false (e.g. after a partial write). 155 using pipeWriteCallback = std::function<bool(FileDescriptor&)>; 156 157 /** ChildProcess::communicate() performs a read/write operation. 158 * The provided pipeWriteCallback allows sending data to the input stream. 159 * communicate() will return with the pair of output and error streams once 160 * they have been completely consumed. */ 161 std::pair<w_string, w_string> communicate( 162 pipeWriteCallback writeCallback = [](FileDescriptor&) { 163 // If not provided by the caller, we're just going to close the input 164 // stream 165 return true; 166 }); 167 168 // these are public for the sake of testing. You should use the 169 // communicate() method instead of calling these directly. 170 std::pair<w_string, w_string> pollingCommunicate(pipeWriteCallback writable); 171 std::pair<w_string, w_string> threadedCommunicate(pipeWriteCallback writable); 172 173 private: 174 pid_t pid_; 175 bool waited_{false}; 176 int status_; 177 std::unordered_map<int, std::unique_ptr<Pipe>> pipes_; 178 179 Future<w_string> readPipe(int fd); 180 }; 181 } 182