• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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