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