• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
20 #include <map>
21 #include <string>
22 #include <sstream>
23 #include <vector>
24 
25 #include <common/libs/fs/shared_fd.h>
26 
27 namespace cvd {
28 // Keeps track of a running (sub)process. Allows to wait for its completion.
29 // It's an error to wait twice for the same subprocess.
30 class Subprocess {
31  public:
32   enum class StdIOChannel {
33     kStdIn = 0,
34     kStdOut = 1,
35     kStdErr = 2,
36   };
37 
Subprocess(pid_t pid,SharedFD control)38   Subprocess(pid_t pid, SharedFD control)
39       : pid_(pid), started_(pid > 0), control_socket_(control) {}
40   // The default implementation won't do because we need to reset the pid of the
41   // moved object.
42   Subprocess(Subprocess&&);
43   ~Subprocess() = default;
44   Subprocess& operator=(Subprocess&&);
45   // Waits for the subprocess to complete. Returns zero if completed
46   // successfully, non-zero otherwise.
47   int Wait();
48   // Same as waitpid(2)
49   pid_t Wait(int* wstatus, int options);
50   // Whether the command started successfully. It only says whether the call to
51   // fork() succeeded or not, it says nothing about exec or successful
52   // completion of the command, that's what Wait is for.
Started()53   bool Started() const {return started_;}
control_socket()54   SharedFD control_socket() {
55     return control_socket_;
56   }
57 
58  private:
59   // Copy is disabled to avoid waiting twice for the same pid (the first wait
60   // frees the pid, which allows the kernel to reuse it so we may end up waiting
61   // for the wrong process)
62   Subprocess(const Subprocess&) = delete;
63   Subprocess& operator=(const Subprocess&) = delete;
64   pid_t pid_ = -1;
65   bool started_ = false;
66   SharedFD control_socket_;
67 };
68 
69 // An executable command. Multiple subprocesses can be started from the same
70 // command object. This class owns any file descriptors that the subprocess
71 // should inherit.
72 class Command {
73  private:
74   template<typename T>
75   // For every type other than SharedFD (for which there is a specialisation)
BuildParameter(std::stringstream * stream,T t)76   bool BuildParameter(std::stringstream* stream, T t) {
77     *stream << t;
78     return true;
79   }
80   // Special treatment for SharedFD
81   bool BuildParameter(std::stringstream* stream, SharedFD shared_fd);
82   template<typename T, typename...Args>
BuildParameter(std::stringstream * stream,T t,Args...args)83   bool BuildParameter(std::stringstream* stream, T t, Args...args) {
84     return BuildParameter(stream, t) &&
85            BuildParameter(stream, args...);
86   }
87  public:
Command(const std::string & executable)88   Command(const std::string& executable) {
89     command_.push_back(executable);
90   }
91   Command(Command&&) = default;
92   // The default copy constructor is unsafe because it would mean multiple
93   // closing of the inherited file descriptors. If needed it can be implemented
94   // using dup(2)
95   Command(const Command&) = delete;
96   Command& operator=(const Command&) = delete;
97   ~Command();
98 
99   // Specify the environment for the subprocesses to be started. By default
100   // subprocesses inherit the parent's environment.
SetEnvironment(const std::vector<std::string> & env)101   void SetEnvironment(const std::vector<std::string>& env) {
102     use_parent_env_ = false;
103     env_ = env;
104   }
105   // Adds a single parameter to the command. All arguments are concatenated into
106   // a single string to form a parameter. If one of those arguments is a
107   // SharedFD a duplicate of it will be used and won't be closed until the
108   // object is destroyed. To add multiple parameters to the command the function
109   // must be called multiple times, one per parameter.
110   template<typename... Args>
AddParameter(Args...args)111   bool AddParameter(Args... args) {
112     std::stringstream ss;
113     if (BuildParameter(&ss, args...)) {
114       command_.push_back(ss.str());
115       return true;
116     }
117     return false;
118   }
119 
120   // Redirects the standard IO of the command.
121   bool RedirectStdIO(Subprocess::StdIOChannel channel, cvd::SharedFD shared_fd);
122 
123   // Starts execution of the command. This method can be called multiple times,
124   // effectively staring multiple (possibly concurrent) instances. If
125   // with_control_socket is true the returned Subprocess instance will have a
126   // sharedFD that enables communication with the child process.
127   Subprocess Start(bool with_control_socket = false) const;
128 
GetShortName()129   std::string GetShortName() const {
130     // This is safe because the constructor guarantees the name of the binary to
131     // be at index 0 on the vector
132     return command_[0];
133   }
134  private:
135   std::vector<std::string> command_;
136   std::map<cvd::SharedFD, int> inherited_fds_{};
137   std::map<Subprocess::StdIOChannel, int> redirects_{};
138   bool use_parent_env_ = true;
139   std::vector<std::string> env_{};
140 };
141 
142 // Convenience wrapper around Command and Subprocess class, allows to easily
143 // execute a command and wait for it to complete. The version without the env
144 // parameter starts the command with the same environment as the parent. Returns
145 // zero if the command completed successfully, non zero otherwise.
146 int execute(const std::vector<std::string>& command,
147             const std::vector<std::string>& env);
148 int execute(const std::vector<std::string>& command);
149 
150 }  // namespace cvd
151