2020-01-17 21:05:03 +08:00
|
|
|
// Copyright 2019 Google LLC
|
2019-03-19 00:21:48 +08:00
|
|
|
//
|
|
|
|
// 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
|
|
|
|
//
|
|
|
|
// http://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 <memory>
|
|
|
|
|
|
|
|
#include <glog/logging.h>
|
2020-02-28 01:23:44 +08:00
|
|
|
#include "absl/status/status.h"
|
2019-03-19 00:21:48 +08:00
|
|
|
#include "absl/strings/str_cat.h"
|
|
|
|
#include "absl/time/time.h"
|
|
|
|
#include "sandboxed_api/sandbox.h"
|
|
|
|
#include "sandboxed_api/util/status_macros.h"
|
|
|
|
|
|
|
|
#define TRANSACTION_FAIL_IF_NOT(x, y) \
|
|
|
|
if (!(x)) { \
|
2020-02-28 01:23:44 +08:00
|
|
|
return absl::FailedPreconditionError(y); \
|
2019-03-19 00:21:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2020-02-18 21:27:08 +08:00
|
|
|
|
2019-03-19 00:21:48 +08:00
|
|
|
virtual ~TransactionBase();
|
|
|
|
|
2020-02-18 21:27:08 +08:00
|
|
|
// 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;
|
2019-03-19 00:21:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
|
2020-02-18 21:27:08 +08:00
|
|
|
bool IsInitialized() const { return initialized_; }
|
2019-03-19 00:21:48 +08:00
|
|
|
|
|
|
|
// Getter for the sandbox_.
|
2020-02-18 21:27:08 +08:00
|
|
|
Sandbox* sandbox() const { return sandbox_.get(); }
|
2019-03-19 00:21:48 +08:00
|
|
|
|
|
|
|
// Restarts the sandbox.
|
|
|
|
// WARNING: This will invalidate any references to the remote process, make
|
2020-02-18 21:27:08 +08:00
|
|
|
// sure you don't keep any vars or FDs to the remote process when
|
|
|
|
// calling this.
|
2020-02-28 01:23:44 +08:00
|
|
|
absl::Status Restart() {
|
2020-02-18 21:27:08 +08:00
|
|
|
if (initialized_) {
|
2019-03-19 00:21:48 +08:00
|
|
|
Finish().IgnoreError();
|
2020-02-18 21:27:08 +08:00
|
|
|
initialized_ = false;
|
2019-03-19 00:21:48 +08:00
|
|
|
}
|
|
|
|
return sandbox_->Restart(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
explicit TransactionBase(std::unique_ptr<Sandbox> sandbox)
|
2020-08-07 15:30:04 +08:00
|
|
|
: time_limit_(absl::ToTimeT(absl::UnixEpoch() + kDefaultTimeLimit)),
|
2019-03-19 00:21:48 +08:00
|
|
|
sandbox_(std::move(sandbox)) {}
|
|
|
|
|
|
|
|
// Runs the main (retrying) transaction loop.
|
2020-02-28 01:23:44 +08:00
|
|
|
absl::Status RunTransactionLoop(const std::function<absl::Status()>& f);
|
2019-03-19 00:21:48 +08:00
|
|
|
|
|
|
|
private:
|
|
|
|
// Number of default transaction execution re-tries, in case of failures.
|
2020-02-18 21:27:08 +08:00
|
|
|
static constexpr int kDefaultRetryCount = 1;
|
2019-03-19 00:21:48 +08:00
|
|
|
|
|
|
|
// 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.
|
2020-02-28 01:23:44 +08:00
|
|
|
absl::Status RunTransactionFunctionInSandbox(
|
|
|
|
const std::function<absl::Status()>& f);
|
2019-03-19 00:21:48 +08:00
|
|
|
|
2020-02-18 21:27:08 +08:00
|
|
|
// Initialization routine of the sandboxed process that will be called only
|
2019-03-19 00:21:48 +08:00
|
|
|
// once upon sandboxee startup.
|
2020-02-28 01:23:44 +08:00
|
|
|
virtual absl::Status Init() { return absl::OkStatus(); }
|
2019-03-19 00:21:48 +08:00
|
|
|
|
|
|
|
// End routine for the sandboxee that gets calls when the transaction is
|
|
|
|
// destroyed/restarted to clean up resources.
|
2020-02-28 01:23:44 +08:00
|
|
|
virtual absl::Status Finish() { return absl::OkStatus(); }
|
2019-03-19 00:21:48 +08:00
|
|
|
|
|
|
|
// Number of tries this transaction will be re-executed until it succeeds.
|
2020-08-07 15:30:04 +08:00
|
|
|
int retry_count_ = kDefaultRetryCount;
|
2019-03-19 00:21:48 +08:00
|
|
|
|
|
|
|
// 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?
|
2020-08-07 15:30:04 +08:00
|
|
|
bool initialized_ = false;
|
2019-03-19 00:21:48 +08:00
|
|
|
|
|
|
|
// 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.
|
2020-02-28 01:23:44 +08:00
|
|
|
absl::Status Run() {
|
2019-03-19 00:21:48 +08:00
|
|
|
return RunTransactionLoop([this] { return Main(); });
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
// The main sandboxee routine: Can be called multiple times.
|
2020-02-28 01:23:44 +08:00
|
|
|
virtual absl::Status Main() { return absl::OkStatus(); }
|
2019-03-19 00:21:48 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
// Callback style transactions:
|
|
|
|
class BasicTransaction final : public TransactionBase {
|
2019-09-13 17:28:09 +08:00
|
|
|
private:
|
2020-02-28 01:23:44 +08:00
|
|
|
using InitFunction = std::function<absl::Status(Sandbox*)>;
|
|
|
|
using FinishFunction = std::function<absl::Status(Sandbox*)>;
|
2019-03-19 00:21:48 +08:00
|
|
|
|
|
|
|
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)) {}
|
|
|
|
|
2020-02-18 21:27:08 +08:00
|
|
|
// Run any function as body of the transaction that matches our expectations
|
|
|
|
// (that is: Returning a Status and accepting a Sandbox object as first
|
2019-03-19 00:21:48 +08:00
|
|
|
// parameter).
|
|
|
|
template <typename T, typename... Args>
|
2020-02-28 01:23:44 +08:00
|
|
|
absl::Status Run(T func, Args&&... args) {
|
2019-03-19 00:21:48 +08:00
|
|
|
return RunTransactionLoop(
|
2020-02-18 21:27:08 +08:00
|
|
|
[&] { return func(sandbox(), std::forward<Args>(args)...); });
|
2019-03-19 00:21:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
InitFunction init_function_;
|
|
|
|
FinishFunction finish_function_;
|
|
|
|
|
2020-02-28 01:23:44 +08:00
|
|
|
absl::Status Init() final {
|
|
|
|
return init_function_ ? init_function_(sandbox()) : absl::OkStatus();
|
2019-03-19 00:21:48 +08:00
|
|
|
}
|
|
|
|
|
2020-02-28 01:23:44 +08:00
|
|
|
absl::Status Finish() final {
|
|
|
|
return finish_function_ ? finish_function_(sandbox()) : absl::OkStatus();
|
2019-03-19 00:21:48 +08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace sapi
|
|
|
|
|
|
|
|
#endif // SANDBOXED_API_TRANSACTION_H_
|