blob: df3d9f6d99f3e6db7f4de25c594e02b668f07ccb [file] [log] [blame]
Jorge E. Moreira6a9d6292018-06-11 11:52:57 -07001/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16#pragma once
17
18#include <sys/types.h>
19
Jorge E. Moreirac4339ad2019-09-05 13:36:06 -070020#include <functional>
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -070021#include <map>
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -070022#include <sstream>
Jorge E. Moreira189d8232019-08-30 11:43:05 -070023#include <string>
Jorge E. Moreira6a9d6292018-06-11 11:52:57 -070024#include <vector>
25
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -070026#include <common/libs/fs/shared_fd.h>
27
Jorge E. Moreira6a9d6292018-06-11 11:52:57 -070028namespace cvd {
Jorge E. Moreirac4339ad2019-09-05 13:36:06 -070029class Subprocess;
30using SubprocessStopper = std::function<bool(Subprocess*)>;
31// Kills a process by sending it the SIGKILL signal.
32bool KillSubprocess(Subprocess* subprocess);
33
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -070034// Keeps track of a running (sub)process. Allows to wait for its completion.
35// It's an error to wait twice for the same subprocess.
36class Subprocess {
37 public:
Jorge E. Moreiraa4dac8b2019-01-25 18:14:51 -080038 enum class StdIOChannel {
39 kStdIn = 0,
40 kStdOut = 1,
41 kStdErr = 2,
42 };
43
Jorge E. Moreirac4339ad2019-09-05 13:36:06 -070044 Subprocess(pid_t pid, SharedFD control, SubprocessStopper stopper = KillSubprocess)
45 : pid_(pid),
46 started_(pid > 0),
47 control_socket_(control),
48 stopper_(stopper) {}
Jorge E. Moreira8e9793e2018-11-05 21:57:26 -080049 // The default implementation won't do because we need to reset the pid of the
50 // moved object.
51 Subprocess(Subprocess&&);
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -070052 ~Subprocess() = default;
Jorge E. Moreira8e9793e2018-11-05 21:57:26 -080053 Subprocess& operator=(Subprocess&&);
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -070054 // Waits for the subprocess to complete. Returns zero if completed
55 // successfully, non-zero otherwise.
56 int Wait();
57 // Same as waitpid(2)
58 pid_t Wait(int* wstatus, int options);
59 // Whether the command started successfully. It only says whether the call to
60 // fork() succeeded or not, it says nothing about exec or successful
61 // completion of the command, that's what Wait is for.
Jorge E. Moreira189d8232019-08-30 11:43:05 -070062 bool Started() const { return started_; }
63 SharedFD control_socket() { return control_socket_; }
64 pid_t pid() const { return pid_; }
Jorge E. Moreirac4339ad2019-09-05 13:36:06 -070065 bool Stop() { return stopper_(this); }
Jorge E. Moreira6a9d6292018-06-11 11:52:57 -070066
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -070067 private:
68 // Copy is disabled to avoid waiting twice for the same pid (the first wait
69 // frees the pid, which allows the kernel to reuse it so we may end up waiting
70 // for the wrong process)
71 Subprocess(const Subprocess&) = delete;
72 Subprocess& operator=(const Subprocess&) = delete;
73 pid_t pid_ = -1;
Jorge E. Moreira1a62e762018-11-05 22:05:57 -080074 bool started_ = false;
75 SharedFD control_socket_;
Jorge E. Moreirac4339ad2019-09-05 13:36:06 -070076 SubprocessStopper stopper_;
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -070077};
Jorge E. Moreira6a9d6292018-06-11 11:52:57 -070078
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -070079// An executable command. Multiple subprocesses can be started from the same
80// command object. This class owns any file descriptors that the subprocess
81// should inherit.
82class Command {
83 private:
Jorge E. Moreira189d8232019-08-30 11:43:05 -070084 template <typename T>
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -070085 // For every type other than SharedFD (for which there is a specialisation)
86 bool BuildParameter(std::stringstream* stream, T t) {
87 *stream << t;
88 return true;
89 }
90 // Special treatment for SharedFD
91 bool BuildParameter(std::stringstream* stream, SharedFD shared_fd);
Jorge E. Moreira189d8232019-08-30 11:43:05 -070092 template <typename T, typename... Args>
93 bool BuildParameter(std::stringstream* stream, T t, Args... args) {
94 return BuildParameter(stream, t) && BuildParameter(stream, args...);
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -070095 }
Jorge E. Moreira189d8232019-08-30 11:43:05 -070096
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -070097 public:
Jorge E. Moreirab945eb12019-05-06 17:20:38 -070098 class ParameterBuilder {
Jorge E. Moreira189d8232019-08-30 11:43:05 -070099 public:
100 ParameterBuilder(Command* cmd) : cmd_(cmd){};
Jorge E. Moreirab945eb12019-05-06 17:20:38 -0700101 ParameterBuilder(ParameterBuilder&& builder) = default;
102 ~ParameterBuilder();
103
Jorge E. Moreira189d8232019-08-30 11:43:05 -0700104 template <typename T>
Jorge E. Moreirab945eb12019-05-06 17:20:38 -0700105 ParameterBuilder& operator<<(T t) {
106 cmd_->BuildParameter(&stream_, t);
107 return *this;
108 }
109
110 void Build();
Jorge E. Moreira189d8232019-08-30 11:43:05 -0700111
112 private:
Jorge E. Moreirab945eb12019-05-06 17:20:38 -0700113 cvd::Command* cmd_;
114 std::stringstream stream_;
115 };
116
Jorge E. Moreirac4339ad2019-09-05 13:36:06 -0700117 // Constructs a command object from the path to an executable binary and an
118 // optional subprocess stopper. When not provided, stopper defaults to sending
119 // SIGKILL to the subprocess.
120 Command(const std::string& executable,
Cody Schuffelen1cf4f132019-12-09 18:54:51 -0800121 SubprocessStopper stopper = KillSubprocess,
122 bool exit_with_parent = true)
123 : subprocess_stopper_(stopper), verbose_(true),
124 exit_with_parent_(exit_with_parent) {
Jorge E. Moreirac4339ad2019-09-05 13:36:06 -0700125 command_.push_back(executable);
126 }
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -0700127 Command(Command&&) = default;
128 // The default copy constructor is unsafe because it would mean multiple
129 // closing of the inherited file descriptors. If needed it can be implemented
130 // using dup(2)
131 Command(const Command&) = delete;
132 Command& operator=(const Command&) = delete;
133 ~Command();
134
135 // Specify the environment for the subprocesses to be started. By default
136 // subprocesses inherit the parent's environment.
137 void SetEnvironment(const std::vector<std::string>& env) {
138 use_parent_env_ = false;
139 env_ = env;
140 }
141 // Adds a single parameter to the command. All arguments are concatenated into
142 // a single string to form a parameter. If one of those arguments is a
143 // SharedFD a duplicate of it will be used and won't be closed until the
144 // object is destroyed. To add multiple parameters to the command the function
145 // must be called multiple times, one per parameter.
Jorge E. Moreira189d8232019-08-30 11:43:05 -0700146 template <typename... Args>
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -0700147 bool AddParameter(Args... args) {
148 std::stringstream ss;
149 if (BuildParameter(&ss, args...)) {
150 command_.push_back(ss.str());
151 return true;
152 }
153 return false;
154 }
Jorge E. Moreiraa4dac8b2019-01-25 18:14:51 -0800155
Jorge E. Moreira189d8232019-08-30 11:43:05 -0700156 ParameterBuilder GetParameterBuilder() { return ParameterBuilder(this); }
Jorge E. Moreirab945eb12019-05-06 17:20:38 -0700157
Jorge E. Moreiraa4dac8b2019-01-25 18:14:51 -0800158 // Redirects the standard IO of the command.
159 bool RedirectStdIO(Subprocess::StdIOChannel channel, cvd::SharedFD shared_fd);
Cody Schuffelenc374c8c2019-08-21 18:47:42 -0700160 bool RedirectStdIO(Subprocess::StdIOChannel subprocess_channel,
161 Subprocess::StdIOChannel parent_channel);
Jorge E. Moreiraa4dac8b2019-01-25 18:14:51 -0800162
Cody Schuffelenc79185d2019-09-27 12:37:00 -0700163 void SetVerbose(bool verbose);
Cody Schuffelen1cf4f132019-12-09 18:54:51 -0800164 void SetExitWithParent(bool exit_with_parent);
Cody Schuffelenc79185d2019-09-27 12:37:00 -0700165
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -0700166 // Starts execution of the command. This method can be called multiple times,
Jorge E. Moreira1a62e762018-11-05 22:05:57 -0800167 // effectively staring multiple (possibly concurrent) instances. If
168 // with_control_socket is true the returned Subprocess instance will have a
169 // sharedFD that enables communication with the child process.
170 Subprocess Start(bool with_control_socket = false) const;
Jorge E. Moreira189d8232019-08-30 11:43:05 -0700171 // Same as Start(bool), but the subprocess runs as head of its own process
172 // group.
173 Subprocess StartInGroup(bool with_control_socket = false) const;
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -0700174
Jorge E. Moreira1a62e762018-11-05 22:05:57 -0800175 std::string GetShortName() const {
176 // This is safe because the constructor guarantees the name of the binary to
177 // be at index 0 on the vector
178 return command_[0];
179 }
Jorge E. Moreira189d8232019-08-30 11:43:05 -0700180
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -0700181 private:
Jorge E. Moreira189d8232019-08-30 11:43:05 -0700182 Subprocess StartHelper(bool with_control_socket, bool in_group) const;
183
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -0700184 std::vector<std::string> command_;
185 std::map<cvd::SharedFD, int> inherited_fds_{};
Jorge E. Moreiraa4dac8b2019-01-25 18:14:51 -0800186 std::map<Subprocess::StdIOChannel, int> redirects_{};
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -0700187 bool use_parent_env_ = true;
188 std::vector<std::string> env_{};
Jorge E. Moreirac4339ad2019-09-05 13:36:06 -0700189 SubprocessStopper subprocess_stopper_;
Cody Schuffelenc79185d2019-09-27 12:37:00 -0700190 bool verbose_;
Cody Schuffelen1cf4f132019-12-09 18:54:51 -0800191 bool exit_with_parent_;
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -0700192};
193
Cody Schuffelen7e35fb32019-10-03 16:28:35 -0700194/*
195 * Consumes a cvd::Command and runs it, optionally managing the stdio channels.
196 *
197 * If `stdin` is set, the subprocess stdin will be pipe providing its contents.
198 * If `stdout` is set, the subprocess stdout will be captured and saved to it.
199 * If `stderr` is set, the subprocess stderr will be captured and saved to it.
200 *
201 * If `command` exits normally, the lower 8 bits of the return code will be
202 * returned in a value between 0 and 255.
203 * If some setup fails, `command` fails to start, or `command` exits due to a
204 * signal, the return value will be negative.
205 */
206int RunWithManagedStdio(cvd::Command&& command, const std::string* stdin,
207 std::string* stdout, std::string* stderr);
208
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -0700209// Convenience wrapper around Command and Subprocess class, allows to easily
210// execute a command and wait for it to complete. The version without the env
211// parameter starts the command with the same environment as the parent. Returns
212// zero if the command completed successfully, non zero otherwise.
Jorge E. Moreira6a9d6292018-06-11 11:52:57 -0700213int execute(const std::vector<std::string>& command,
214 const std::vector<std::string>& env);
215int execute(const std::vector<std::string>& command);
216
Jorge E. Moreira6a9d6292018-06-11 11:52:57 -0700217} // namespace cvd