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