blob: 44976f907c74ca83f98cc46334f837d726664df4 [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,
121 SubprocessStopper stopper = KillSubprocess)
Cody Schuffelenc79185d2019-09-27 12:37:00 -0700122 : subprocess_stopper_(stopper), verbose_(true) {
Jorge E. Moreirac4339ad2019-09-05 13:36:06 -0700123 command_.push_back(executable);
124 }
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -0700125 Command(Command&&) = default;
126 // The default copy constructor is unsafe because it would mean multiple
127 // closing of the inherited file descriptors. If needed it can be implemented
128 // using dup(2)
129 Command(const Command&) = delete;
130 Command& operator=(const Command&) = delete;
131 ~Command();
132
133 // Specify the environment for the subprocesses to be started. By default
134 // subprocesses inherit the parent's environment.
135 void SetEnvironment(const std::vector<std::string>& env) {
136 use_parent_env_ = false;
137 env_ = env;
138 }
139 // Adds a single parameter to the command. All arguments are concatenated into
140 // a single string to form a parameter. If one of those arguments is a
141 // SharedFD a duplicate of it will be used and won't be closed until the
142 // object is destroyed. To add multiple parameters to the command the function
143 // must be called multiple times, one per parameter.
Jorge E. Moreira189d8232019-08-30 11:43:05 -0700144 template <typename... Args>
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -0700145 bool AddParameter(Args... args) {
146 std::stringstream ss;
147 if (BuildParameter(&ss, args...)) {
148 command_.push_back(ss.str());
149 return true;
150 }
151 return false;
152 }
Jorge E. Moreiraa4dac8b2019-01-25 18:14:51 -0800153
Jorge E. Moreira189d8232019-08-30 11:43:05 -0700154 ParameterBuilder GetParameterBuilder() { return ParameterBuilder(this); }
Jorge E. Moreirab945eb12019-05-06 17:20:38 -0700155
Jorge E. Moreiraa4dac8b2019-01-25 18:14:51 -0800156 // Redirects the standard IO of the command.
157 bool RedirectStdIO(Subprocess::StdIOChannel channel, cvd::SharedFD shared_fd);
Cody Schuffelenc374c8c2019-08-21 18:47:42 -0700158 bool RedirectStdIO(Subprocess::StdIOChannel subprocess_channel,
159 Subprocess::StdIOChannel parent_channel);
Jorge E. Moreiraa4dac8b2019-01-25 18:14:51 -0800160
Cody Schuffelenc79185d2019-09-27 12:37:00 -0700161 void SetVerbose(bool verbose);
162
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -0700163 // Starts execution of the command. This method can be called multiple times,
Jorge E. Moreira1a62e762018-11-05 22:05:57 -0800164 // effectively staring multiple (possibly concurrent) instances. If
165 // with_control_socket is true the returned Subprocess instance will have a
166 // sharedFD that enables communication with the child process.
167 Subprocess Start(bool with_control_socket = false) const;
Jorge E. Moreira189d8232019-08-30 11:43:05 -0700168 // Same as Start(bool), but the subprocess runs as head of its own process
169 // group.
170 Subprocess StartInGroup(bool with_control_socket = false) const;
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -0700171
Jorge E. Moreira1a62e762018-11-05 22:05:57 -0800172 std::string GetShortName() const {
173 // This is safe because the constructor guarantees the name of the binary to
174 // be at index 0 on the vector
175 return command_[0];
176 }
Jorge E. Moreira189d8232019-08-30 11:43:05 -0700177
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -0700178 private:
Jorge E. Moreira189d8232019-08-30 11:43:05 -0700179 Subprocess StartHelper(bool with_control_socket, bool in_group) const;
180
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -0700181 std::vector<std::string> command_;
182 std::map<cvd::SharedFD, int> inherited_fds_{};
Jorge E. Moreiraa4dac8b2019-01-25 18:14:51 -0800183 std::map<Subprocess::StdIOChannel, int> redirects_{};
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -0700184 bool use_parent_env_ = true;
185 std::vector<std::string> env_{};
Jorge E. Moreirac4339ad2019-09-05 13:36:06 -0700186 SubprocessStopper subprocess_stopper_;
Cody Schuffelenc79185d2019-09-27 12:37:00 -0700187 bool verbose_;
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -0700188};
189
Cody Schuffelen7e35fb32019-10-03 16:28:35 -0700190/*
191 * Consumes a cvd::Command and runs it, optionally managing the stdio channels.
192 *
193 * If `stdin` is set, the subprocess stdin will be pipe providing its contents.
194 * If `stdout` is set, the subprocess stdout will be captured and saved to it.
195 * If `stderr` is set, the subprocess stderr will be captured and saved to it.
196 *
197 * If `command` exits normally, the lower 8 bits of the return code will be
198 * returned in a value between 0 and 255.
199 * If some setup fails, `command` fails to start, or `command` exits due to a
200 * signal, the return value will be negative.
201 */
202int RunWithManagedStdio(cvd::Command&& command, const std::string* stdin,
203 std::string* stdout, std::string* stderr);
204
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -0700205// Convenience wrapper around Command and Subprocess class, allows to easily
206// execute a command and wait for it to complete. The version without the env
207// parameter starts the command with the same environment as the parent. Returns
208// zero if the command completed successfully, non zero otherwise.
Jorge E. Moreira6a9d6292018-06-11 11:52:57 -0700209int execute(const std::vector<std::string>& command,
210 const std::vector<std::string>& env);
211int execute(const std::vector<std::string>& command);
212
Jorge E. Moreira6a9d6292018-06-11 11:52:57 -0700213} // namespace cvd