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