1 // Copyright 2019 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #ifndef SANDBOXED_API_TRANSACTION_H_ 16 #define SANDBOXED_API_TRANSACTION_H_ 17 18 #include <ctime> 19 #include <functional> 20 #include <memory> 21 #include <utility> 22 23 #include "absl/base/attributes.h" 24 #include "absl/log/check.h" 25 #include "absl/log/log.h" 26 #include "absl/status/status.h" 27 #include "absl/strings/str_cat.h" 28 #include "absl/time/time.h" 29 #include "sandboxed_api/sandbox.h" 30 31 #define TRANSACTION_FAIL_IF_NOT(x, y) \ 32 if (!(x)) { \ 33 return absl::FailedPreconditionError(y); \ 34 } 35 36 namespace sapi { 37 38 // The Transaction class allows to perform operations in the sandboxee, 39 // repeating them if necessary (if the sandboxing, or IPC failed). 40 // 41 // We provide two different implementations of transactions: 42 // 1) Single function transactions - They consist out of a single function 43 // Main() that will be invoked as body of the transaction. For this, 44 // inherit from the Transaction class and implement Main(). 45 // 2) Function pointer based transactions - The BasicTransaction class accepts 46 // functions that take a sandbox object (along with arbitrary other 47 // parameters) and return a status. This way no custom implementation of a 48 // Transaction class is required. 49 // 50 // Additionally both methods support Init() and Finish() functions. 51 // Init() will be called after the sandbox has been set up. 52 // Finish() will be called when the transaction object goes out of scope. 53 class TransactionBase { 54 public: 55 TransactionBase(const TransactionBase&) = delete; 56 TransactionBase& operator=(const TransactionBase&) = delete; 57 58 virtual ~TransactionBase(); 59 60 // Getter/Setter for retry_count_. retry_count()61 int retry_count() const { return retry_count_; } set_retry_count(int value)62 void set_retry_count(int value) { 63 CHECK_GE(value, 0); 64 retry_count_ = value; 65 } 66 67 // Getter/Setter for time_limit_. GetTimeLimit()68 time_t GetTimeLimit() const { 69 return absl::ToTimeT(absl::UnixEpoch() + time_limit_); 70 } SetTimeLimit(time_t time_limit)71 void SetTimeLimit(time_t time_limit) { 72 time_limit_ = absl::Seconds(time_limit); 73 } SetTimeLimit(absl::Duration time_limit)74 void SetTimeLimit(absl::Duration time_limit) { time_limit_ = time_limit; } 75 IsInitialized()76 bool IsInitialized() const { return initialized_; } 77 78 // Getter for the sandbox_. sandbox()79 Sandbox* sandbox() const { return sandbox_.get(); } 80 81 // Restarts the sandbox. 82 // WARNING: This will invalidate any references to the remote process, make 83 // sure you don't keep any vars or FDs to the remote process when 84 // calling this. Restart()85 absl::Status Restart() { 86 if (initialized_) { 87 Finish().IgnoreError(); 88 initialized_ = false; 89 } 90 return sandbox_->Restart(true); 91 } 92 93 protected: TransactionBase(std::unique_ptr<Sandbox> sandbox)94 explicit TransactionBase(std::unique_ptr<Sandbox> sandbox) 95 : time_limit_(kDefaultTimeLimit), sandbox_(std::move(sandbox)) {} 96 97 // Runs the main (retrying) transaction loop. 98 absl::Status RunTransactionLoop(const std::function<absl::Status()>& f); 99 100 private: 101 // Number of default transaction execution re-tries, in case of failures. 102 static constexpr int kDefaultRetryCount = 1; 103 104 // Wall-time limit for a single transaction execution (60 s.). 105 static constexpr absl::Duration kDefaultTimeLimit = absl::Seconds(60); 106 107 // Executes a single function in the sandbox, used in the main transaction 108 // loop. Asserts that the sandbox has been set up and Init() was called. 109 absl::Status RunTransactionFunctionInSandbox( 110 const std::function<absl::Status()>& f); 111 112 // Initialization routine of the sandboxed process that will be called only 113 // once upon sandboxee startup. Init()114 virtual absl::Status Init() { return absl::OkStatus(); } 115 116 // End routine for the sandboxee that gets calls when the transaction is 117 // destroyed/restarted to clean up resources. Finish()118 virtual absl::Status Finish() { return absl::OkStatus(); } 119 120 // Number of tries this transaction will be re-executed until it succeeds. 121 int retry_count_ = kDefaultRetryCount; 122 123 // Time (wall-time) limit for a single Run() call (in seconds). 0 means: no 124 // wall-time limit. 125 absl::Duration time_limit_; 126 127 // Has Init() finished with success? 128 bool initialized_ = false; 129 130 // The main sapi::Sandbox object. 131 std::unique_ptr<Sandbox> sandbox_; 132 }; 133 134 // Regular style transactions, based on inheriting. 135 class Transaction : public TransactionBase { 136 public: 137 Transaction(const Transaction&) = delete; 138 Transaction& operator=(const Transaction&) = delete; 139 using TransactionBase::TransactionBase; 140 141 // Run the transaction. Run()142 absl::Status Run() { 143 return RunTransactionLoop([this] { return Main(); }); 144 } 145 146 protected: 147 // The main sandboxee routine: Can be called multiple times. Main()148 virtual absl::Status Main() { return absl::OkStatus(); } 149 }; 150 151 // Callback style transactions: 152 class BasicTransaction final : public TransactionBase { 153 private: 154 using InitFunction = std::function<absl::Status(Sandbox*)>; 155 using FinishFunction = std::function<absl::Status(Sandbox*)>; 156 157 public: BasicTransaction(std::unique_ptr<Sandbox> sandbox)158 explicit BasicTransaction(std::unique_ptr<Sandbox> sandbox) 159 : TransactionBase(std::move(sandbox)), 160 init_function_(nullptr), 161 finish_function_(nullptr) {} 162 163 template <typename F> BasicTransaction(std::unique_ptr<Sandbox> sandbox,F init_function)164 BasicTransaction(std::unique_ptr<Sandbox> sandbox, F init_function) 165 : TransactionBase(std::move(sandbox)), 166 init_function_(static_cast<InitFunction>(init_function)), 167 finish_function_(nullptr) {} 168 169 template <typename F, typename G> BasicTransaction(std::unique_ptr<Sandbox> sandbox,F init_function,G fini_function)170 BasicTransaction(std::unique_ptr<Sandbox> sandbox, F init_function, 171 G fini_function) 172 : TransactionBase(std::move(sandbox)), 173 init_function_(static_cast<InitFunction>(init_function)), 174 finish_function_(static_cast<FinishFunction>(fini_function)) {} 175 176 // Run any function as body of the transaction that matches our expectations 177 // (that is: Returning a Status and accepting a Sandbox object as first 178 // parameter). 179 template <typename T, typename... Args> Run(T func,Args &&...args)180 absl::Status Run(T func, Args&&... args) { 181 return RunTransactionLoop( 182 [&] { return func(sandbox(), std::forward<Args>(args)...); }); 183 } 184 185 private: 186 InitFunction init_function_; 187 FinishFunction finish_function_; 188 Init()189 absl::Status Init() final { 190 return init_function_ ? init_function_(sandbox()) : absl::OkStatus(); 191 } 192 Finish()193 absl::Status Finish() final { 194 return finish_function_ ? finish_function_(sandbox()) : absl::OkStatus(); 195 } 196 }; 197 198 } // namespace sapi 199 200 #endif // SANDBOXED_API_TRANSACTION_H_ 201