Merge remote-tracking branch 'upstream/master' into libarchive

Merge fork with upstream for pull request.
This commit is contained in:
Andrei Medar 2020-10-07 10:10:08 +00:00
commit e87e583bca
88 changed files with 4169 additions and 591 deletions

4
.bazelignore Normal file
View File

@ -0,0 +1,4 @@
# Using CMake or own WORKSPACE
oss-internship-2020
# Uses its own WORKSPACE
sandboxed_api/examples/hello_sapi

12
.gitmodules vendored
View File

@ -1,12 +1,18 @@
[submodule "oss-internship-2020/sapi_lodepng/lodepng"]
path = oss-internship-2020/lodepng/lodepng
url = https://github.com/lvandeve/lodepng
[submodule "oss-internship-2020/jsonnet/jsonnet"]
path = oss-internship-2020/jsonnet/jsonnet
url = https://github.com/google/jsonnet.git
[submodule "oss-internship-2020/openjpeg/openjpeg"]
path = oss-internship-2020/openjpeg/openjpeg
url = https://github.com/uclouvain/openjpeg.git
[submodule "oss-internship-2020/pffft/master"]
path = oss-internship-2020/pffft/master
url = https://bitbucket.org/jpommier/pffft/src/master/
[submodule "oss-internship-2020/curl/curl_wrapper/curl"]
path = oss-internship-2020/curl/curl_wrapper/curl
url = https://github.com/curl/curl
[submodule "oss-internship-2020/gdal/gdal"]
path = oss-internship-2020/gdal/gdal
url = https://github.com/OSGeo/gdal/tree/master/gdal
[submodule "oss-internship-2020/curl/curl_wrapper/curl"]
path = oss-internship-2020/curl/curl_wrapper/curl
url = git@github.com:curl/curl.git

View File

@ -44,6 +44,9 @@ include(SapiBuildDefs)
# Allow the header generator to auto-configure include paths
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# Allow the header generator to auto-configure include paths
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
if (SAPI_FORCE_COLOR_OUTPUT)
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") # GCC
add_compile_options(-fdiagnostics-color=always)

View File

@ -132,44 +132,44 @@ function(add_sapi_library)
set(_sapi_embed_dir "${CMAKE_CURRENT_BINARY_DIR}")
set(_sapi_embed_name "${_sapi_NAME}")
endif()
set(_sapi_isystem "${_sapi_NAME}.isystem}")
list(APPEND _sapi_generator_args
"--sapi_name=${_sapi_LIBRARY_NAME}"
"--sapi_out=${_sapi_gen_header}"
"--sapi_embed_dir=${_sapi_embed_dir}"
"--sapi_embed_name=${_sapi_embed_name}"
"--sapi_functions=${_sapi_funcs}"
"--sapi_ns=${_sapi_NAMESPACE}"
"--sapi_isystem=${_sapi_isystem}"
)
list(JOIN _sapi_full_inputs "," _sapi_full_inputs)
if(SAPI_ENABLE_GENERATOR)
add_custom_command(
OUTPUT "${_sapi_gen_header}"
COMMAND sapi_generator_tool
"--sapi_name=${_sapi_LIBRARY_NAME}"
"--sapi_out=${_sapi_gen_header}"
"--sapi_embed_dir=${_sapi_embed_dir}"
"--sapi_embed_name=${_sapi_embed_name}"
"--sapi_functions=${_sapi_funcs}"
"--sapi_ns=${_sapi_NAMESPACE}"
${_sapi_full_inputs}
COMMENT "Generating interface"
DEPENDS ${_sapi_INPUTS}
VERBATIM
list(APPEND _sapi_generator_command
sapi_generator_tool
-p "${CMAKE_CURRENT_BINARY_DIR}"
${_sapi_generator_args}
${_sapi_full_inputs}
)
else()
set(_sapi_isystem "${_sapi_NAME}.isystem")
list(JOIN _sapi_full_inputs "," _sapi_full_inputs)
add_custom_command(
OUTPUT "${_sapi_gen_header}" "${_sapi_isystem}"
COMMAND sh -c
"${CMAKE_CXX_COMPILER} -E -x c++ -v /dev/null 2>&1 | \
awk '/> search starts here:/{f=1;next}/^End of search/{f=0}f{print $1}' \
> \"${_sapi_isystem}\""
COMMAND "${SAPI_PYTHON3_EXECUTABLE}" -B
"${SAPI_SOURCE_DIR}/sandboxed_api/tools/generator2/sapi_generator.py"
"--sapi_name=${_sapi_LIBRARY_NAME}"
"--sapi_out=${_sapi_gen_header}"
"--sapi_embed_dir=${_sapi_embed_dir}"
"--sapi_embed_name=${_sapi_embed_name}"
"--sapi_functions=${_sapi_funcs}"
"--sapi_ns=${_sapi_NAMESPACE}"
"--sapi_isystem=${_sapi_isystem}"
"--sapi_in=${_sapi_full_inputs}"
COMMENT "Generating interface"
VERBATIM
list(APPEND _sapi_generator_command
"${SAPI_PYTHON3_EXECUTABLE}" -B
"${SAPI_SOURCE_DIR}/sandboxed_api/tools/generator2/sapi_generator.py"
${_sapi_generator_args}
"--sapi_in=${_sapi_full_inputs}"
)
endif()
add_custom_command(
OUTPUT "${_sapi_gen_header}" "${_sapi_isystem}"
COMMAND sh -c
"${CMAKE_CXX_COMPILER} -E -x c++ -v /dev/null 2>&1 | \
awk '/> search starts here:/{f=1;next}/^End of search/{f=0}f{print $1}' \
> \"${_sapi_isystem}\""
COMMAND ${_sapi_generator_command}
COMMENT "Generating interface"
DEPENDS ${_sapi_INPUTS}
VERBATIM
)
# Library with the interface
if(NOT _sapi_SOURCES)

View File

@ -12,15 +12,15 @@
# See the License for the specific language governing permissions and
# limitations under the License.
cmake_minimum_required(VERSION 3.16)
cmake_minimum_required(VERSION 3.12)
project(libcurl_sandbox)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)
option(CURL_SAPI_ENABLE_EXAMPLES "" ON)
option(CURL_SAPI_ENABLE_TESTS "" ON)
option(SAPI_CURL_ENABLE_EXAMPLES "" ON)
option(SAPI_CURL_ENABLE_TESTS "" ON)
# Add callbacks used by examples and tests
if (CURL_SAPI_ENABLE_EXAMPLES OR CURL_SAPI_ENABLE_TESTS)
@ -35,8 +35,8 @@ add_subdirectory(curl_wrapper)
# Setup Sandboxed API
set(SAPI_ROOT "" CACHE PATH "Path to the Sandboxed API source tree")
set(SAPI_ENABLE_EXAMPLES ${CURL_SAPI_ENABLE_EXAMPLES} CACHE BOOL "" FORCE)
set(SAPI_ENABLE_TESTS ${CURL_SAPI_ENABLE_TESTS} CACHE BOOL "" FORCE)
set(SAPI_ENABLE_EXAMPLES ${SAPI_CURL_ENABLE_EXAMPLES} CACHE BOOL "" FORCE)
set(SAPI_ENABLE_TESTS ${SAPI_CURL_ENABLE_TESTS} CACHE BOOL "" FORCE)
add_subdirectory(
"${SAPI_ROOT}"
"${CMAKE_BINARY_DIR}/sandboxed-api-build"
@ -125,7 +125,7 @@ add_sapi_library(curl_sapi
LIBRARY_NAME Curl
NAMESPACE ""
NAMESPACE curl
)
# Include generated SAPI header
@ -134,11 +134,11 @@ target_include_directories(curl_sapi INTERFACE
)
# Add examples
if (CURL_SAPI_ENABLE_EXAMPLES)
if (SAPI_CURL_ENABLE_EXAMPLES)
add_subdirectory(examples)
endif()
# Add tests
if (CURL_SAPI_ENABLE_TESTS)
if (SAPI_CURL_ENABLE_TESTS)
add_subdirectory(tests)
endif()

View File

@ -38,12 +38,15 @@ Variadic methods are currently not supported by Sandboxed API. To solve this,
these methods are defined with an additional explicit parameter in
`custom_curl.h`.
The methods are: - `curl_easy_setopt`. Use `curl_easy_setopt_ptr`,
`curl_easy_setopt_long` or `curl_easy_setopt_curl_off_t` instead. -
`curl_easy_getinfo`. Use `curl_easy_getinfo_ptr` instead. - `curl_multi_setopt`.
Use `curl_multi_setopt_ptr`, `curl_multi_setopt_long` or
`curl_multi_setopt_curl_off_t` instead. - `curl_share_setopt`. Use
`curl_share_setopt_ptr` or `curl_share_setopt_long` instead
The methods are:
- `curl_easy_setopt`. Use `curl_easy_setopt_ptr`, `curl_easy_setopt_long` or
`curl_easy_setopt_curl_off_t` instead.
- `curl_easy_getinfo`. Use `curl_easy_getinfo_ptr` instead.
- `curl_multi_setopt`. Use `curl_multi_setopt_ptr`, `curl_multi_setopt_long`
or `curl_multi_setopt_curl_off_t` instead.
- `curl_share_setopt`. Use `curl_share_setopt_ptr` or `curl_share_setopt_long`
instead
#### Methods with incomplete array arguments
@ -51,8 +54,10 @@ Incomplete array arguments are currently not supported by Sandboxed API. To
solve this, methods taking an incomplete array argument have a wrapper in
`custom_curl.h`, and take a pointer as the argument.
The methods are: - `curl_multi_poll`. Use `curl_multi_poll_sapi` instead. -
`curl_multi_wait`. Use `curl_multi_wait_sapi` instead.
The methods are:
- `curl_multi_poll`. Use `curl_multi_poll_sapi` instead.
- `curl_multi_wait`. Use `curl_multi_wait_sapi` instead.
#### Methods with conflicts on the generated header
@ -60,11 +65,15 @@ Some methods create conflicts on the generated header because of redefined
`#define` directives from files included by the header. To solve this, the
conflicting types and methods are redefined in `custom_curl.h`.
The types are: - `time_t`. Use `time_t_sapi` instead. - `fd_set`. Use
`fd_set_sapi` instead.
The types are:
The methods are: - `curl_getdate`. Use `curl_getdate_sapi` instead. -
`curl_multi_fdset`. Use `curl_multi_fdset_sapi` instead.
- `time_t`. Use `time_t_sapi` instead.
- `fd_set`. Use `fd_set_sapi` instead.
The methods are:
- `curl_getdate`. Use `curl_getdate_sapi` instead.
- `curl_multi_fdset`. Use `curl_multi_fdset_sapi` instead.
#### Function pointers

View File

@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef TESTS_CALLBACKS_H
#define TESTS_CALLBACKS_H
#ifndef CALLBACKS_H_
#define CALLBACKS_H_
#include <curl/curl.h>
@ -21,4 +21,4 @@
extern "C" size_t WriteToMemory(char* contents, size_t size, size_t num_bytes,
void* userp);
#endif // TESTS_CALLBACKS_H
#endif // CALLBACKS_H_

View File

@ -14,8 +14,8 @@
// Wrapper for curl library
#ifndef CURL_WRAPPER_H
#define CURL_WRAPPER_H
#ifndef CURL_WRAPPER_H_
#define CURL_WRAPPER_H_
#include <curl/curl.h>
@ -77,6 +77,7 @@ CURLSHcode curl_share_setopt_ptr(CURLSH* handle, CURLSHoption option,
// The wrapper method is needed to make the variadic argument explicit
CURLSHcode curl_share_setopt_long(CURLSH* handle, CURLSHoption option,
long parameter);
}
#endif // CURL_WRAPPER_H
} // extern "C"
#endif // CURL_WRAPPER_H_

View File

@ -19,59 +19,67 @@
#include "../sandbox.h" // NOLINT(build/include)
namespace {
absl::Status Example1() {
// Initialize sandbox2 and sapi
curl::CurlSapiSandbox sandbox;
SAPI_RETURN_IF_ERROR(sandbox.Init());
curl::CurlApi api(&sandbox);
// Initialize the curl session
curl::CURL* curl_handle;
SAPI_ASSIGN_OR_RETURN(curl_handle, api.curl_easy_init());
sapi::v::RemotePtr curl(curl_handle);
if (!curl_handle) {
return absl::UnavailableError("curl_easy_init failed: curl is NULL");
}
int curl_code;
// Specify URL to get
sapi::v::ConstCStr url("http://example.com");
SAPI_ASSIGN_OR_RETURN(curl_code, api.curl_easy_setopt_ptr(&curl, curl::CURLOPT_URL,
url.PtrBefore()));
if (curl_code != 0) {
return absl::UnavailableError("curl_easy_setopt_ptr failed: " + curl_code);
}
// Set the library to follow a redirection
SAPI_ASSIGN_OR_RETURN(curl_code, api.curl_easy_setopt_long(
&curl, curl::CURLOPT_FOLLOWLOCATION, 1l));
if (curl_code != 0) {
return absl::UnavailableError("curl_easy_setopt_long failed: " + curl_code);
}
// Disable authentication of peer certificate
SAPI_ASSIGN_OR_RETURN(curl_code, api.curl_easy_setopt_long(
&curl, curl::CURLOPT_SSL_VERIFYPEER, 0l));
if (curl_code != 0) {
return absl::UnavailableError("curl_easy_setopt_long failed: " + curl_code);
}
// Perform the request
SAPI_ASSIGN_OR_RETURN(curl_code, api.curl_easy_perform(&curl));
if (curl_code != 0) {
return absl::UnavailableError("curl_easy_perform failed: " + curl_code);
}
// Cleanup curl
SAPI_RETURN_IF_ERROR(api.curl_easy_cleanup(&curl));
return absl::OkStatus();
}
} // namespace
int main(int argc, char* argv[]) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
google::InitGoogleLogging(argv[0]);
absl::Status status;
// Initialize sandbox2 and sapi
CurlSapiSandbox sandbox;
status = sandbox.Init();
if (!status.ok()) {
LOG(FATAL) << "Couldn't initialize Sandboxed API: " << status;
}
CurlApi api(&sandbox);
// Initialize the curl session
absl::StatusOr<CURL*> curl_handle = api.curl_easy_init();
if (!curl_handle.ok()) {
LOG(FATAL) << "curl_easy_init failed: " << curl_handle.status();
}
sapi::v::RemotePtr curl(curl_handle.value());
if (!curl.GetValue()) LOG(FATAL) << "curl_easy_init failed: curl is NULL";
absl::StatusOr<int> curl_code;
// Specify URL to get
sapi::v::ConstCStr url("http://example.com");
curl_code = api.curl_easy_setopt_ptr(&curl, CURLOPT_URL, url.PtrBefore());
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status();
}
// Set the library to follow a redirection
curl_code = api.curl_easy_setopt_long(&curl, CURLOPT_FOLLOWLOCATION, 1l);
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_long failed: " << curl_code.status();
}
// Disable authentication of peer certificate
curl_code = api.curl_easy_setopt_long(&curl, CURLOPT_SSL_VERIFYPEER, 0l);
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_long failed: " << curl_code.status();
}
// Perform the request
curl_code = api.curl_easy_perform(&curl);
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_perform failed: " << curl_code.status();
}
// Cleanup curl
status = api.curl_easy_cleanup(&curl);
if (!status.ok()) {
LOG(FATAL) << "curl_easy_cleanup failed: " << status;
if (absl::Status status = Example1(); !status.ok()) {
LOG(ERROR) << "Example1 failed: " << status.ToString();
return EXIT_FAILURE;
}
return EXIT_SUCCESS;

View File

@ -20,87 +20,89 @@
#include "../sandbox.h" // NOLINT(build/include)
int main(int argc, char* argv[]) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
google::InitGoogleLogging(argv[0]);
absl::Status status;
namespace {
absl::Status Example2() {
// Initialize sandbox2 and sapi
CurlSapiSandbox sandbox;
status = sandbox.Init();
if (!status.ok()) {
LOG(FATAL) << "Couldn't initialize Sandboxed API: " << status;
}
CurlApi api(&sandbox);
curl::CurlSapiSandbox sandbox;
SAPI_RETURN_IF_ERROR(sandbox.Init());
curl::CurlApi api(&sandbox);
// Generate pointer to WriteMemoryCallback function
void* function_ptr;
status = sandbox.rpc_channel()->Symbol("WriteToMemory", &function_ptr);
if (!status.ok()) {
LOG(FATAL) << "sapi::Sandbox::rpc_channel().Symbol failed: " << status;
}
sapi::v::RemotePtr remote_function_ptr(function_ptr);
SAPI_RETURN_IF_ERROR(
sandbox.rpc_channel()->Symbol("WriteToMemory", &function_ptr));
sapi::v::RemotePtr write_to_memory(function_ptr);
// Initialize the curl session
absl::StatusOr<CURL*> curl_handle = api.curl_easy_init();
if (!curl_handle.ok()) {
LOG(FATAL) << "curl_easy_init failed: " << curl_handle.status();
}
sapi::v::RemotePtr curl(curl_handle.value());
if (!curl.GetValue()) {
LOG(FATAL) << "curl_easy_init failed: curl is NULL";
curl::CURL* curl_handle;
SAPI_ASSIGN_OR_RETURN(curl_handle, api.curl_easy_init());
sapi::v::RemotePtr curl(curl_handle);
if (!curl_handle) {
return absl::UnavailableError("curl_easy_init failed: curl is NULL");
}
absl::StatusOr<int> curl_code;
int curl_code;
// Specify URL to get
sapi::v::ConstCStr url("http://example.com");
curl_code = api.curl_easy_setopt_ptr(&curl, CURLOPT_URL, url.PtrBefore());
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status();
SAPI_ASSIGN_OR_RETURN(curl_code, api.curl_easy_setopt_ptr(&curl, curl::CURLOPT_URL,
url.PtrBefore()));
if (curl_code != 0) {
return absl::UnavailableError("curl_easy_setopt_ptr failed: " + curl_code);
}
// Set WriteMemoryCallback as the write function
curl_code = api.curl_easy_setopt_ptr(&curl, CURLOPT_WRITEFUNCTION,
&remote_function_ptr);
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status();
SAPI_ASSIGN_OR_RETURN(curl_code,
api.curl_easy_setopt_ptr(&curl, curl::CURLOPT_WRITEFUNCTION,
&write_to_memory));
if (curl_code != 0) {
return absl::UnavailableError("curl_easy_setopt_ptr failed: " + curl_code);
}
// Pass 'chunk' struct to the callback function
sapi::v::LenVal chunk(0);
curl_code =
api.curl_easy_setopt_ptr(&curl, CURLOPT_WRITEDATA, chunk.PtrBoth());
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status();
SAPI_ASSIGN_OR_RETURN(curl_code,
api.curl_easy_setopt_ptr(&curl, curl::CURLOPT_WRITEDATA,
chunk.PtrBoth()));
if (curl_code != 0) {
return absl::UnavailableError("curl_easy_setopt_ptr failed: " + curl_code);
}
// Set a user agent
sapi::v::ConstCStr user_agent("libcurl-agent/1.0");
curl_code = api.curl_easy_setopt_ptr(&curl, CURLOPT_USERAGENT,
user_agent.PtrBefore());
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status();
SAPI_ASSIGN_OR_RETURN(curl_code,
api.curl_easy_setopt_ptr(&curl, curl::CURLOPT_USERAGENT,
user_agent.PtrBefore()));
if (curl_code != 0) {
return absl::UnavailableError("curl_easy_setopt_ptr failed: " + curl_code);
}
// Perform the request
curl_code = api.curl_easy_perform(&curl);
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_perform failed: " << curl_code.status();
SAPI_ASSIGN_OR_RETURN(curl_code, api.curl_easy_perform(&curl));
if (curl_code != 0) {
return absl::UnavailableError("curl_easy_perform failed: " + curl_code);
}
// Retrieve memory size
status = sandbox.TransferFromSandboxee(&chunk);
if (!status.ok()) {
LOG(FATAL) << "sandbox.TransferFromSandboxee failed: " << status;
}
SAPI_RETURN_IF_ERROR(sandbox.TransferFromSandboxee(&chunk));
std::cout << "memory size: " << chunk.GetDataSize() << " bytes" << std::endl;
// Cleanup curl
status = api.curl_easy_cleanup(&curl);
if (!status.ok()) {
LOG(FATAL) << "curl_easy_cleanup failed: " << status;
SAPI_RETURN_IF_ERROR(api.curl_easy_cleanup(&curl));
return absl::OkStatus();
}
} // namespace
int main(int argc, char* argv[]) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
google::InitGoogleLogging(argv[0]);
if (absl::Status status = Example2(); !status.ok()) {
LOG(ERROR) << "Example2 failed: " << status.ToString();
return EXIT_FAILURE;
}
return EXIT_SUCCESS;

View File

@ -19,7 +19,9 @@
#include "../sandbox.h" // NOLINT(build/include)
class CurlSapiSandboxEx3 : public CurlSapiSandbox {
namespace {
class CurlSapiSandboxEx3 : public curl::CurlSapiSandbox {
public:
CurlSapiSandboxEx3(std::string ssl_certificate, std::string ssl_key,
std::string ca_certificates)
@ -42,7 +44,7 @@ class CurlSapiSandboxEx3 : public CurlSapiSandbox {
.AddFile(ssl_key)
.AddFile(ca_certificates);
// Provide the new PolicyBuilder to ModifyPolicy in CurlSandbox
return CurlSapiSandbox::ModifyPolicy(policy_builder.get());
return curl::CurlSapiSandbox::ModifyPolicy(policy_builder.get());
}
std::string ssl_certificate;
@ -50,116 +52,121 @@ class CurlSapiSandboxEx3 : public CurlSapiSandbox {
std::string ca_certificates;
};
int main(int argc, char* argv[]) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
google::InitGoogleLogging(argv[0]);
absl::Status status;
// Get input parameters (should be absolute paths)
if (argc != 5) {
LOG(FATAL) << "wrong number of arguments (4 expected)";
}
std::string ssl_certificate = argv[1];
std::string ssl_key = argv[2];
std::string ssl_key_password = argv[3];
std::string ca_certificates = argv[4];
absl::Status Example3(std::string ssl_certificate, std::string ssl_key,
std::string ssl_key_password,
std::string ca_certificates) {
// Initialize sandbox2 and sapi
CurlSapiSandboxEx3 sandbox(ssl_certificate, ssl_key, ca_certificates);
status = sandbox.Init();
if (!status.ok()) {
LOG(FATAL) << "Couldn't initialize Sandboxed API: " << status;
}
CurlApi api(&sandbox);
SAPI_RETURN_IF_ERROR(sandbox.Init());
curl::CurlApi api(&sandbox);
absl::StatusOr<int> curl_code;
int curl_code;
// Initialize curl (CURL_GLOBAL_DEFAULT = 3)
curl_code = api.curl_global_init(3l);
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_global_init failed: " << curl_code.status();
SAPI_ASSIGN_OR_RETURN(curl_code, api.curl_global_init(3l));
if (curl_code != 0) {
return absl::UnavailableError("curl_global_init failed: " + curl_code);
}
// Initialize curl easy handle
absl::StatusOr<CURL*> curl_handle = api.curl_easy_init();
if (!curl_handle.ok()) {
LOG(FATAL) << "curl_easy_init failed: " << curl_handle.status();
}
sapi::v::RemotePtr curl(curl_handle.value());
if (!curl.GetValue()) {
LOG(FATAL) << "curl_easy_init failed: curl is NULL";
curl::CURL* curl_handle;
SAPI_ASSIGN_OR_RETURN(curl_handle, api.curl_easy_init());
sapi::v::RemotePtr curl(curl_handle);
if (!curl_handle) {
return absl::UnavailableError("curl_easy_init failed: curl is NULL");
}
// Specify URL to get (using HTTPS)
sapi::v::ConstCStr url("https://example.com");
curl_code = api.curl_easy_setopt_ptr(&curl, CURLOPT_URL, url.PtrBefore());
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status();
SAPI_ASSIGN_OR_RETURN(curl_code, api.curl_easy_setopt_ptr(&curl, curl::CURLOPT_URL,
url.PtrBefore()));
if (curl_code != 0) {
return absl::UnavailableError("curl_easy_setopt_ptr failed: " + curl_code);
}
// Set the SSL certificate type to "PEM"
sapi::v::ConstCStr ssl_cert_type("PEM");
curl_code = api.curl_easy_setopt_ptr(&curl, CURLOPT_SSLCERTTYPE,
ssl_cert_type.PtrBefore());
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status();
SAPI_ASSIGN_OR_RETURN(curl_code,
api.curl_easy_setopt_ptr(&curl, curl::CURLOPT_SSLCERTTYPE,
ssl_cert_type.PtrBefore()));
if (curl_code != 0) {
return absl::UnavailableError("curl_easy_setopt_ptr failed: " + curl_code);
}
// Set the certificate for client authentication
sapi::v::ConstCStr sapi_ssl_certificate(ssl_certificate.c_str());
curl_code = api.curl_easy_setopt_ptr(&curl, CURLOPT_SSLCERT,
sapi_ssl_certificate.PtrBefore());
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status();
SAPI_ASSIGN_OR_RETURN(curl_code,
api.curl_easy_setopt_ptr(&curl, curl::CURLOPT_SSLCERT,
sapi_ssl_certificate.PtrBefore()));
if (curl_code != 0) {
return absl::UnavailableError("curl_easy_setopt_ptr failed: " + curl_code);
}
// Set the private key for client authentication
sapi::v::ConstCStr sapi_ssl_key(ssl_key.c_str());
curl_code =
api.curl_easy_setopt_ptr(&curl, CURLOPT_SSLKEY, sapi_ssl_key.PtrBefore());
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status();
SAPI_ASSIGN_OR_RETURN(curl_code,
api.curl_easy_setopt_ptr(&curl, curl::CURLOPT_SSLKEY,
sapi_ssl_key.PtrBefore()));
if (curl_code != 0) {
return absl::UnavailableError("curl_easy_setopt_ptr failed: " + curl_code);
}
// Set the password used to protect the private key
sapi::v::ConstCStr sapi_ssl_key_password(ssl_key_password.c_str());
curl_code = api.curl_easy_setopt_ptr(&curl, CURLOPT_KEYPASSWD,
sapi_ssl_key_password.PtrBefore());
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status();
SAPI_ASSIGN_OR_RETURN(curl_code,
api.curl_easy_setopt_ptr(&curl, curl::CURLOPT_KEYPASSWD,
sapi_ssl_key_password.PtrBefore()));
if (curl_code != 0) {
return absl::UnavailableError("curl_easy_setopt_ptr failed: " + curl_code);
}
// Set the file with the certificates vaildating the server
sapi::v::ConstCStr sapi_ca_certificates(ca_certificates.c_str());
curl_code = api.curl_easy_setopt_ptr(&curl, CURLOPT_CAINFO,
sapi_ca_certificates.PtrBefore());
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status();
SAPI_ASSIGN_OR_RETURN(curl_code,
api.curl_easy_setopt_ptr(&curl, curl::CURLOPT_CAINFO,
sapi_ca_certificates.PtrBefore()));
if (curl_code != 0) {
return absl::UnavailableError("curl_easy_setopt_ptr failed: " + curl_code);
}
// Verify the authenticity of the server
curl_code = api.curl_easy_setopt_long(&curl, CURLOPT_SSL_VERIFYPEER, 1L);
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_long failed: " << curl_code.status();
SAPI_ASSIGN_OR_RETURN(curl_code, api.curl_easy_setopt_long(
&curl, curl::CURLOPT_SSL_VERIFYPEER, 1L));
if (curl_code != 0) {
return absl::UnavailableError("curl_easy_setopt_long failed: " + curl_code);
}
// Perform the request
curl_code = api.curl_easy_perform(&curl);
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_perform failed: " << curl_code.status();
SAPI_ASSIGN_OR_RETURN(curl_code, api.curl_easy_perform(&curl));
if (curl_code != 0) {
return absl::UnavailableError("curl_easy_perform failed: " + curl_code);
}
// Cleanup curl easy handle
status = api.curl_easy_cleanup(&curl);
if (!status.ok()) {
LOG(FATAL) << "curl_easy_cleanup failed: " << status;
}
SAPI_RETURN_IF_ERROR(api.curl_easy_cleanup(&curl));
// Cleanup curl
status = api.curl_global_cleanup();
if (!status.ok()) {
LOG(FATAL) << "curl_global_cleanup failed: " << status;
SAPI_RETURN_IF_ERROR(api.curl_global_cleanup());
return absl::OkStatus();
}
} // namespace
int main(int argc, char* argv[]) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
google::InitGoogleLogging(argv[0]);
// Get input parameters (should be absolute paths)
if (argc != 5) {
LOG(ERROR) << "wrong number of arguments (4 expected)";
return EXIT_FAILURE;
}
if (absl::Status status = Example3(argv[1], argv[2], argv[3], argv[4]);
!status.ok()) {
LOG(ERROR) << "Example3 failed: " << status.ToString();
return EXIT_FAILURE;
}
return EXIT_SUCCESS;

View File

@ -21,107 +21,113 @@
#include "curl_sapi.sapi.h" // NOLINT(build/include)
#include "sandboxed_api/util/flag.h"
int main(int argc, char* argv[]) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
google::InitGoogleLogging(argv[0]);
absl::Status status;
namespace {
absl::Status Example4() {
// Initialize sandbox2 and sapi
CurlSapiSandbox sandbox;
status = sandbox.Init();
if (!status.ok()) {
LOG(FATAL) << "Couldn't initialize Sandboxed API: " << status;
}
CurlApi api(&sandbox);
curl::CurlSapiSandbox sandbox;
SAPI_RETURN_IF_ERROR(sandbox.Init());
curl::CurlApi api(&sandbox);
// Number of running handles
sapi::v::Int still_running(1);
absl::StatusOr<int> curl_code;
int curl_code;
// Initialize curl (CURL_GLOBAL_DEFAULT = 3)
curl_code = api.curl_global_init(3l);
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_global_init failed: " << curl_code.status();
SAPI_ASSIGN_OR_RETURN(curl_code, api.curl_global_init(3l));
if (curl_code != 0) {
return absl::UnavailableError("curl_global_init failed: " + curl_code);
}
// Initialize http_handle
absl::StatusOr<CURL*> curl_handle = api.curl_easy_init();
if (!curl_handle.ok()) {
LOG(FATAL) << "curl_easy_init failed: " << curl_handle.status();
}
sapi::v::RemotePtr http_handle(curl_handle.value());
if (!http_handle.GetValue()) {
LOG(FATAL) << "curl_easy_init failed: http_handle is NULL";
curl::CURL* curl_handle;
SAPI_ASSIGN_OR_RETURN(curl_handle, api.curl_easy_init());
sapi::v::RemotePtr http_handle(curl_handle);
if (!curl_handle) {
return absl::UnavailableError("curl_easy_init failed: curl is NULL");
}
// Specify URL to get
sapi::v::ConstCStr url("http://example.com");
curl_code =
api.curl_easy_setopt_ptr(&http_handle, CURLOPT_URL, url.PtrBefore());
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status();
SAPI_ASSIGN_OR_RETURN(curl_code,
api.curl_easy_setopt_ptr(&http_handle, curl::CURLOPT_URL,
url.PtrBefore()));
if (curl_code != 0) {
return absl::UnavailableError("curl_easy_setopt_ptr failed: " + curl_code);
}
// Initialize multi_handle
absl::StatusOr<CURLM*> curlm_handle = api.curl_multi_init();
if (!curlm_handle.ok()) {
LOG(FATAL) << "curl_multi_init failed: " << curlm_handle.status();
}
sapi::v::RemotePtr multi_handle(curlm_handle.value());
if (!multi_handle.GetValue()) {
LOG(FATAL) << "curl_multi_init failed: multi_handle is NULL";
curl::CURLM* curlm_handle;
SAPI_ASSIGN_OR_RETURN(curlm_handle, api.curl_multi_init());
sapi::v::RemotePtr multi_handle(curlm_handle);
if (!curlm_handle) {
return absl::UnavailableError(
"curl_multi_init failed: multi_handle is NULL");
}
// Add http_handle to the multi stack
curl_code = api.curl_multi_add_handle(&multi_handle, &http_handle);
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_multi_add_handle failed: " << curl_code.status();
SAPI_ASSIGN_OR_RETURN(curl_code,
api.curl_multi_add_handle(&multi_handle, &http_handle));
if (curl_code != 0) {
return absl::UnavailableError("curl_multi_add_handle failed: " + curl_code);
}
while (still_running.GetValue()) {
sapi::v::Int numfds(0);
// Perform the request
curl_code = api.curl_multi_perform(&multi_handle, still_running.PtrBoth());
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_mutli_perform failed: " << curl_code.status();
SAPI_ASSIGN_OR_RETURN(curl_code, api.curl_multi_perform(
&multi_handle, still_running.PtrBoth()));
if (curl_code != 0) {
return absl::UnavailableError("curl_mutli_perform failed: " + curl_code);
}
if (still_running.GetValue()) {
// Wait for an event or timeout
sapi::v::NullPtr null_ptr;
curl_code = api.curl_multi_poll_sapi(&multi_handle, &null_ptr, 0, 1000,
numfds.PtrBoth());
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_multi_poll_sapi failed: " << curl_code.status();
SAPI_ASSIGN_OR_RETURN(
curl_code, api.curl_multi_poll_sapi(&multi_handle, &null_ptr, 0, 1000,
numfds.PtrBoth()));
if (curl_code != 0) {
return absl::UnavailableError("curl_multi_poll_sapi failed: " +
curl_code);
}
}
}
// Remove http_handle from the multi stack
curl_code = api.curl_multi_remove_handle(&multi_handle, &http_handle);
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_multi_remove_handle failed: " << curl_code.status();
SAPI_ASSIGN_OR_RETURN(curl_code,
api.curl_multi_remove_handle(&multi_handle, &http_handle));
if (curl_code != 0) {
return absl::UnavailableError("curl_multi_remove_handle failed: " +
curl_code);
}
// Cleanup http_handle
status = api.curl_easy_cleanup(&http_handle);
if (!status.ok()) {
LOG(FATAL) << "curl_easy_cleanup failed: " << status;
}
SAPI_RETURN_IF_ERROR(api.curl_easy_cleanup(&http_handle));
// Cleanup multi_handle
curl_code = api.curl_multi_cleanup(&multi_handle);
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_multi_cleanup failed: " << curl_code.status();
SAPI_ASSIGN_OR_RETURN(curl_code, api.curl_multi_cleanup(&multi_handle));
if (curl_code != 0) {
return absl::UnavailableError("curl_multi_cleanup failed: " + curl_code);
}
// Cleanup curl
status = api.curl_global_cleanup();
if (!status.ok()) {
LOG(FATAL) << "curl_global_cleanup failed: " << status;
SAPI_RETURN_IF_ERROR(api.curl_global_cleanup());
return absl::OkStatus();
}
} // namespace
int main(int argc, char* argv[]) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
google::InitGoogleLogging(argv[0]);
if (absl::Status status = Example4(); !status.ok()) {
LOG(ERROR) << "Example4 failed: " << status.ToString();
return EXIT_FAILURE;
}
return EXIT_SUCCESS;

View File

@ -15,87 +15,90 @@
// Sandboxed version of multithread.c
// Multithreaded HTTP GET requests
#include <pthread.h>
#include <cstdlib>
#include <future> // NOLINT(build/c++11)
#include <thread> // NOLINT(build/c++11)
#include "../sandbox.h" // NOLINT(build/include)
void pull_one_url(const std::string& url, CurlApi& api) {
namespace {
absl::Status pull_one_url(const std::string& url, curl::CurlApi& api) {
// Initialize the curl session
absl::StatusOr<CURL*> curl_handle = api.curl_easy_init();
if (!curl_handle.ok()) {
LOG(FATAL) << "curl_easy_init failed: " << curl_handle.status();
}
sapi::v::RemotePtr curl(curl_handle.value());
if (!curl.GetValue()) {
LOG(FATAL) << "curl_easy_init failed: curl is NULL";
curl::CURL* curl_handle;
SAPI_ASSIGN_OR_RETURN(curl_handle, api.curl_easy_init());
sapi::v::RemotePtr curl(curl_handle);
if (!curl_handle) {
return absl::UnavailableError("curl_easy_init failed: curl is NULL");
}
absl::StatusOr<int> curl_code;
int curl_code;
// Specify URL to get
sapi::v::ConstCStr sapi_url(url.c_str());
curl_code =
api.curl_easy_setopt_ptr(&curl, CURLOPT_URL, sapi_url.PtrBefore());
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status();
SAPI_ASSIGN_OR_RETURN(curl_code, api.curl_easy_setopt_ptr(&curl, curl::CURLOPT_URL,
sapi_url.PtrBefore()));
if (curl_code != 0) {
return absl::UnavailableError("curl_easy_setopt_ptr failed: " + curl_code);
}
// Perform the request
curl_code = api.curl_easy_perform(&curl);
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_perform failed: " << curl_code.status();
SAPI_ASSIGN_OR_RETURN(curl_code, api.curl_easy_perform(&curl));
if (curl_code != 0) {
return absl::UnavailableError("curl_easy_perform failed: " + curl_code);
}
// Cleanup curl
absl::Status status = api.curl_easy_cleanup(&curl);
if (!status.ok()) {
LOG(FATAL) << "curl_easy_cleanup failed: " << status;
}
// Cleanup curl easy handle
SAPI_RETURN_IF_ERROR(api.curl_easy_cleanup(&curl));
return absl::OkStatus();
}
const std::vector<std::string> urls = {
"http://example.com", "http://example.edu", "http://example.net",
"http://example.org"};
absl::Status Example5() {
// Initialize sandbox2 and sapi
curl::CurlSapiSandbox sandbox;
SAPI_RETURN_IF_ERROR(sandbox.Init());
curl::CurlApi api(&sandbox);
int curl_code;
// Initialize curl (CURL_GLOBAL_DEFAULT = 3)
SAPI_ASSIGN_OR_RETURN(curl_code, api.curl_global_init(3l));
if (curl_code != 0) {
return absl::UnavailableError("curl_global_init failed: " + curl_code);
}
// Create the threads (by using futures)
std::vector<std::future<absl::Status>> futures;
for (auto& url : urls) {
futures.emplace_back(
std::async(pull_one_url, std::ref(url), std::ref(api)));
}
// Join the threads and check for errors
for (auto& future : futures) {
SAPI_RETURN_IF_ERROR(future.get());
}
// Cleanup curl
SAPI_RETURN_IF_ERROR(api.curl_global_cleanup());
return absl::OkStatus();
}
} // namespace
int main(int argc, char* argv[]) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
google::InitGoogleLogging(argv[0]);
absl::Status status;
// Initialize sandbox2 and sapi
CurlSapiSandbox sandbox;
status = sandbox.Init();
if (!status.ok()) {
LOG(FATAL) << "Couldn't initialize Sandboxed API: " << status;
}
CurlApi api(&sandbox);
absl::StatusOr<int> curl_code;
// Initialize curl (CURL_GLOBAL_DEFAULT = 3)
curl_code = api.curl_global_init(3l);
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_global_init failed: " << curl_code.status();
}
// Create the threads
std::vector<std::thread> threads;
for (auto& url : urls) {
threads.emplace_back(pull_one_url, std::ref(url), std::ref(api));
}
// Join the threads
for (auto& thread : threads) {
thread.join();
}
// Cleanup curl
status = api.curl_global_cleanup();
if (!status.ok()) {
LOG(FATAL) << "curl_global_cleanup failed: " << status;
if (absl::Status status = Example5(); !status.ok()) {
LOG(ERROR) << "Example5 failed: " << status.ToString();
return EXIT_FAILURE;
}
return EXIT_SUCCESS;

View File

@ -20,6 +20,8 @@
#include "../sandbox.h" // NOLINT(build/include)
#include "sandboxed_api/transaction.h"
namespace {
class CurlTransaction : public sapi::Transaction {
public:
explicit CurlTransaction(std::unique_ptr<sapi::Sandbox> sandbox)
@ -36,7 +38,7 @@ class CurlTransaction : public sapi::Transaction {
};
absl::Status CurlTransaction::Main() {
CurlApi api(sandbox());
curl::CurlApi api(sandbox());
// Initialize the curl session
SAPI_ASSIGN_OR_RETURN(void* curl_remote, api.curl_easy_init());
@ -47,13 +49,13 @@ absl::Status CurlTransaction::Main() {
sapi::v::ConstCStr url("http://example.com");
SAPI_ASSIGN_OR_RETURN(
int setopt_url_code,
api.curl_easy_setopt_ptr(&curl, CURLOPT_URL, url.PtrBefore()));
TRANSACTION_FAIL_IF_NOT(setopt_url_code == CURLE_OK,
api.curl_easy_setopt_ptr(&curl, curl::CURLOPT_URL, url.PtrBefore()));
TRANSACTION_FAIL_IF_NOT(setopt_url_code == curl::CURLE_OK,
"curl_easy_setopt_ptr failed");
// Perform the request
SAPI_ASSIGN_OR_RETURN(int perform_code, api.curl_easy_perform(&curl));
TRANSACTION_FAIL_IF_NOT(setopt_url_code == CURLE_OK,
TRANSACTION_FAIL_IF_NOT(setopt_url_code == curl::CURLE_OK,
"curl_easy_perform failed");
// Cleanup curl
@ -63,8 +65,10 @@ absl::Status CurlTransaction::Main() {
return absl::OkStatus();
}
} // namespace
int main(int argc, char* argv[]) {
CurlTransaction curl{std::make_unique<CurlSapiSandbox>()};
CurlTransaction curl{std::make_unique<curl::CurlSapiSandbox>()};
absl::Status status = curl.Run();
CHECK(status.ok()) << "CurlTransaction failed";

View File

@ -24,7 +24,9 @@
#include "curl_sapi.sapi.h" // NOLINT(build/include)
#include "sandboxed_api/sandbox2/util/bpf_helper.h"
class CurlSapiSandbox : public CurlSandbox {
namespace curl {
class CurlSapiSandbox : public curl::CurlSandbox {
protected:
std::unique_ptr<sandbox2::Policy> ModifyPolicy(
sandbox2::PolicyBuilder* policy_builder) override {
@ -64,4 +66,6 @@ class CurlSapiSandbox : public CurlSandbox {
}
};
} // namespace curl
#endif // SANDBOX_H_

View File

@ -28,17 +28,17 @@
int CurlTestUtils::port_;
std::thread CurlTestUtils::server_thread_;
absl::Status CurlTestUtils::CurlTestSetUp() {
absl::Status curl::tests::CurlTestUtils::CurlTestSetUp() {
// Initialize sandbox2 and sapi
sandbox_ = std::make_unique<CurlSapiSandbox>();
sandbox_ = std::make_unique<curl::CurlSapiSandbox>();
absl::Status init = sandbox_->Init();
if (!init.ok()) {
return init;
}
api_ = std::make_unique<CurlApi>(sandbox_.get());
api_ = std::make_unique<curl::CurlApi>(sandbox_.get());
// Initialize curl
absl::StatusOr<CURL*> curl_handle = api_->curl_easy_init();
absl::StatusOr<curl::CURL*> curl_handle = api_->curl_easy_init();
if (!curl_handle.ok()) {
return curl_handle.status();
}
@ -51,23 +51,24 @@ absl::Status CurlTestUtils::CurlTestSetUp() {
// Specify request URL
sapi::v::ConstCStr sapi_url(kUrl.data());
curl_code = api_->curl_easy_setopt_ptr(curl_.get(), CURLOPT_URL,
curl_code = api_->curl_easy_setopt_ptr(curl_.get(), curl::CURLOPT_URL,
sapi_url.PtrBefore());
if (!curl_code.ok()) {
return curl_code.status();
}
if (curl_code.value() != CURLE_OK) {
if (curl_code.value() != curl::CURLE_OK) {
return absl::UnavailableError(
"curl_easy_setopt_ptr returned with the error code " +
curl_code.value());
}
// Set port
curl_code = api_->curl_easy_setopt_long(curl_.get(), CURLOPT_PORT, port_);
curl_code =
api_->curl_easy_setopt_long(curl_.get(), curl::CURLOPT_PORT, port_);
if (!curl_code.ok()) {
return curl_code.status();
}
if (curl_code.value() != CURLE_OK) {
if (curl_code.value() != curl::CURLE_OK) {
return absl::UnavailableError(
"curl_easy_setopt_long returned with the error code " +
curl_code.value());
@ -83,12 +84,12 @@ absl::Status CurlTestUtils::CurlTestSetUp() {
sapi::v::RemotePtr remote_function_ptr(function_ptr);
// Set WriteToMemory as the write function
curl_code = api_->curl_easy_setopt_ptr(curl_.get(), CURLOPT_WRITEFUNCTION,
&remote_function_ptr);
curl_code = api_->curl_easy_setopt_ptr(
curl_.get(), curl::CURLOPT_WRITEFUNCTION, &remote_function_ptr);
if (!curl_code.ok()) {
return curl_code.status();
}
if (curl_code.value() != CURLE_OK) {
if (curl_code.value() != curl::CURLE_OK) {
return absl::UnavailableError(
"curl_easy_setopt_ptr returned with the error code " +
curl_code.value());
@ -96,12 +97,12 @@ absl::Status CurlTestUtils::CurlTestSetUp() {
// Pass memory chunk object to the callback
chunk_ = std::make_unique<sapi::v::LenVal>(0);
curl_code = api_->curl_easy_setopt_ptr(curl_.get(), CURLOPT_WRITEDATA,
curl_code = api_->curl_easy_setopt_ptr(curl_.get(), curl::CURLOPT_WRITEDATA,
chunk_->PtrBoth());
if (!curl_code.ok()) {
return curl_code.status();
}
if (curl_code.value() != CURLE_OK) {
if (curl_code.value() != curl::CURLE_OK) {
return absl::UnavailableError(
"curl_easy_setopt_ptr returned with the error code " +
curl_code.value());
@ -110,18 +111,18 @@ absl::Status CurlTestUtils::CurlTestSetUp() {
return absl::OkStatus();
}
absl::Status CurlTestUtils::CurlTestTearDown() {
absl::Status curl::tests::CurlTestUtils::CurlTestTearDown() {
// Cleanup curl
return api_->curl_easy_cleanup(curl_.get());
}
absl::StatusOr<std::string> CurlTestUtils::PerformRequest() {
absl::StatusOr<std::string> curl::tests::CurlTestUtils::PerformRequest() {
// Perform the request
absl::StatusOr<int> curl_code = api_->curl_easy_perform(curl_.get());
if (!curl_code.ok()) {
return curl_code.status();
}
if (curl_code.value() != CURLE_OK) {
if (curl_code.value() != curl::CURLE_OK) {
return absl::UnavailableError(
"curl_easy_perform returned with the error code " + curl_code.value());
}
@ -267,7 +268,7 @@ void ServerLoop(int listening_socket, sockaddr_in socket_address) {
} // namespace
void CurlTestUtils::StartMockServer() {
void curl::tests::CurlTestUtils::StartMockServer() {
// Get the socket file descriptor
int listening_socket = socket(AF_INET, SOCK_STREAM, 0);

View File

@ -21,6 +21,8 @@
#include "sandboxed_api/util/flag.h"
#include "sandboxed_api/util/status_matchers.h"
namespace curl::tests {
// Helper class that can be used to test Curl Sandboxed
class CurlTestUtils {
protected:
@ -39,8 +41,8 @@ class CurlTestUtils {
// Responds with the POST request fields to a POST request
static void StartMockServer();
std::unique_ptr<CurlSapiSandbox> sandbox_;
std::unique_ptr<CurlApi> api_;
std::unique_ptr<curl::CurlSapiSandbox> sandbox_;
std::unique_ptr<curl::CurlApi> api_;
std::unique_ptr<sapi::v::RemotePtr> curl_;
static std::thread server_thread_;
@ -52,4 +54,6 @@ class CurlTestUtils {
std::unique_ptr<sapi::v::LenVal> chunk_;
};
} // namespace curl::tests
#endif // TESTS_H_

View File

@ -14,7 +14,9 @@
#include "test_utils.h" // NOLINT(build/include)
class CurlTest : public CurlTestUtils, public ::testing::Test {
namespace {
class CurlTest : public curl::tests::CurlTestUtils, public ::testing::Test {
protected:
void SetUp() override {
// Start mock server, get port number and check for any error
@ -38,9 +40,9 @@ TEST_F(CurlTest, EffectiveUrl) {
// Get effective URL
SAPI_ASSERT_OK_AND_ASSIGN(
int getinfo_code,
api_->curl_easy_getinfo_ptr(curl_.get(), CURLINFO_EFFECTIVE_URL,
api_->curl_easy_getinfo_ptr(curl_.get(), curl::CURLINFO_EFFECTIVE_URL,
effective_url_ptr.PtrBoth()));
ASSERT_EQ(getinfo_code, CURLE_OK);
ASSERT_EQ(getinfo_code, curl::CURLE_OK);
// Store effective URL in a string
SAPI_ASSERT_OK_AND_ASSIGN(std::string effective_url,
@ -74,9 +76,9 @@ TEST_F(CurlTest, ResponseCode) {
// Get response code
SAPI_ASSERT_OK_AND_ASSIGN(
int getinfo_code,
api_->curl_easy_getinfo_ptr(curl_.get(), CURLINFO_RESPONSE_CODE,
api_->curl_easy_getinfo_ptr(curl_.get(), curl::CURLINFO_RESPONSE_CODE,
response_code.PtrBoth()));
ASSERT_EQ(getinfo_code, CURLE_OK);
ASSERT_EQ(getinfo_code, curl::CURLE_OK);
// Check response code
ASSERT_EQ(response_code.GetValue(), 200);
@ -120,19 +122,21 @@ TEST_F(CurlTest, POSTResponse) {
// Set the size of the POST fields
SAPI_ASSERT_OK_AND_ASSIGN(
int setopt_post_fields_size,
api_->curl_easy_setopt_long(curl_.get(), CURLOPT_POSTFIELDSIZE,
api_->curl_easy_setopt_long(curl_.get(), curl::CURLOPT_POSTFIELDSIZE,
post_fields.GetSize()));
ASSERT_EQ(setopt_post_fields_size, CURLE_OK);
ASSERT_EQ(setopt_post_fields_size, curl::CURLE_OK);
// Set the POST fields
SAPI_ASSERT_OK_AND_ASSIGN(
int setopt_post_fields,
api_->curl_easy_setopt_ptr(curl_.get(), CURLOPT_POSTFIELDS,
api_->curl_easy_setopt_ptr(curl_.get(), curl::CURLOPT_POSTFIELDS,
post_fields.PtrBefore()));
ASSERT_EQ(setopt_post_fields, CURLE_OK);
ASSERT_EQ(setopt_post_fields, curl::CURLE_OK);
SAPI_ASSERT_OK_AND_ASSIGN(std::string response, PerformRequest());
// Compare response with expected response
ASSERT_EQ(std::string(post_fields.GetData()), response);
}
} // namespace

View File

@ -24,6 +24,7 @@ cc_library(
srcs = ["guetzli_entry_points.cc"],
hdrs = ["guetzli_entry_points.h"],
deps = [
"@com_google_absl//absl/status:statusor",
"@com_google_sandboxed_api//sandboxed_api:lenval_core",
"@com_google_sandboxed_api//sandboxed_api:vars",
"@guetzli//:guetzli_lib",
@ -53,9 +54,7 @@ sapi_library(
cc_binary(
name = "guetzli_sandboxed",
srcs = ["guetzli_sandboxed.cc"],
deps = [
":guetzli_sapi",
],
deps = [":guetzli_sapi"],
)
cc_test(

View File

@ -25,8 +25,8 @@
#include "guetzli/jpeg_data_reader.h"
#include "guetzli/quality.h"
#include "png.h" // NOLINT(build/include)
#include "absl/status/statusor.h"
#include "sandboxed_api/sandbox2/util/fileops.h"
#include "sandboxed_api/util/statusor.h"
namespace {
@ -51,7 +51,7 @@ sapi::LenValStruct CreateLenValFromData(const void* data, size_t size) {
return {size, new_data};
}
sapi::StatusOr<std::string> ReadFromFd(int fd) {
absl::StatusOr<std::string> ReadFromFd(int fd) {
struct stat file_data;
int status = fstat(fd, &file_data);
@ -70,9 +70,9 @@ sapi::StatusOr<std::string> ReadFromFd(int fd) {
return result;
}
sapi::StatusOr<GuetzliInitData> PrepareDataForProcessing(
absl::StatusOr<GuetzliInitData> PrepareDataForProcessing(
const ProcessingParams& processing_params) {
sapi::StatusOr<std::string> input = ReadFromFd(processing_params.remote_fd);
absl::StatusOr<std::string> input = ReadFromFd(processing_params.remote_fd);
if (!input.ok()) {
return input.status();
@ -96,7 +96,7 @@ inline uint8_t BlendOnBlack(const uint8_t val, const uint8_t alpha) {
}
// Modified version of ReadPNG from original guetzli.cc
sapi::StatusOr<ImageData> ReadPNG(const std::string& data) {
absl::StatusOr<ImageData> ReadPNG(const std::string& data) {
std::vector<uint8_t> rgb;
int xsize, ysize;
png_structp png_ptr =

View File

@ -17,7 +17,6 @@
#include "guetzli_transaction.h" // NOLINT(build/include)
#include "sandboxed_api/sandbox2/util/fileops.h"
#include "sandboxed_api/util/statusor.h"
namespace {

View File

@ -83,7 +83,7 @@ TEST_F(GuetzliSapiTest, ProcessRGB) {
*processing_params.mutable_data() = {
in_fd.GetRemoteFd(), 0, kDefaultQualityTarget, kDefaultMemlimitMb};
sapi::v::LenVal output(0);
sapi::StatusOr<bool> processing_result =
absl::StatusOr<bool> processing_result =
api_->ProcessRgb(processing_params.PtrBefore(), output.PtrBoth());
ASSERT_TRUE(processing_result.value_or(false)) << "Error processing rgb data";
std::string reference_data =
@ -108,7 +108,7 @@ TEST_F(GuetzliSapiTest, ProcessJpeg) {
*processing_params.mutable_data() = {
in_fd.GetRemoteFd(), 0, kDefaultQualityTarget, kDefaultMemlimitMb};
sapi::v::LenVal output(0);
sapi::StatusOr<bool> processing_result =
absl::StatusOr<bool> processing_result =
api_->ProcessJpeg(processing_params.PtrBefore(), output.PtrBoth());
ASSERT_TRUE(processing_result.value_or(false)) << "Error processing jpg data";
std::string reference_data =

View File

@ -99,7 +99,7 @@ absl::Status GuetzliTransaction::LinkOutFile(int out_fd) const {
return absl::OkStatus();
}
sapi::StatusOr<ImageType> GuetzliTransaction::GetImageTypeFromFd(int fd) const {
absl::StatusOr<ImageType> GuetzliTransaction::GetImageTypeFromFd(int fd) const {
static const unsigned char kPNGMagicBytes[] = {
0x89, 'P', 'N', 'G', '\r', '\n', 0x1a, '\n',
};

View File

@ -48,7 +48,7 @@ class GuetzliTransaction : public sapi::Transaction {
absl::Status Main() final;
absl::Status LinkOutFile(int out_fd) const;
sapi::StatusOr<ImageType> GetImageTypeFromFd(int fd) const;
absl::StatusOr<ImageType> GetImageTypeFromFd(int fd) const;
const TransactionParams params_;
ImageType image_type_ = ImageType::kJpeg;

View File

@ -0,0 +1,58 @@
# Copyright 2020 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
#
# 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.
cmake_minimum_required(VERSION 3.10)
project(jsonnet-sapi C CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)
add_subdirectory(jsonnet)
add_subdirectory(examples)
set(SAPI_ROOT "../.." CACHE PATH "Path to the Sandboxed API source tree")
set(SAPI_ENABLE_EXAMPLES OFF CACHE BOOL "")
set(SAPI_ENABLE_TESTS OFF CACHE BOOL "")
add_subdirectory("${SAPI_ROOT}"
"${CMAKE_BINARY_DIR}/sandboxed-api-build"
EXCLUDE_FROM_ALL)
add_sapi_library(jsonnet_sapi
FUNCTIONS c_jsonnet_evaluate_snippet
c_jsonnet_make
c_jsonnet_destroy
c_read_input
c_write_output_file
c_jsonnet_realloc
c_jsonnet_evaluate_snippet_multi
c_write_multi_output_files
c_write_output_stream
c_jsonnet_evaluate_snippet_stream
c_jsonnet_fmt_snippet
INPUTS jsonnet_helper.h
LIBRARY jsonnet_helper
LIBRARY_NAME Jsonnet
NAMESPACE ""
)
target_include_directories(jsonnet_sapi INTERFACE
"${PROJECT_BINARY_DIR}"
)
target_link_libraries(jsonnet_sapi PUBLIC jsonnet_helper)
add_subdirectory(tests)

View File

@ -0,0 +1,84 @@
# Jsonnet Sandboxed API
This library provides sandboxed version of the
[Jsonnet](https://github.com/google/jsonnet) library.
## Examples
The `examples/` directory contains code to produce three command-line tools --
`jsonnet_sandboxed`, `jsonnet_yaml_stream_sandboxed` and
`jsonnet_multiple_files_sandboxed` to evaluate jsonnet code. The first one
enables the user to evaluate jsonnet code held in one file and writing to one
output file. The second evaluates one jsonnet file into one file, which can be
interepreted as YAML stream. The third one is for evaluating one jsonnet file
into multiple output files. All three tools are based on what can be found
[here](https://github.com/google/jsonnet/blob/master/cmd/jsonnet.cpp).
Apart from these, there is also a file producing `jsonnet_formatter_sandboxed`
executable. It is based on a tool found from
[here](https://github.com/google/jsonnet/blob/master/cmd/jsonnetfmt.cpp). It is
a jsonnet code formatter -- it changes poorly written jsonnet files into their
canonical form.
## Build
To build these examples, after cloning the whole Sandbox API project, you also
need to run
```
git submodule update --init --recursive
```
anywhere in the project tree in order to clone the `jsonnet` submodule.
Then in the `sandboxed-api/oss-internship-2020/jsonnet` run
```
mkdir build && cd build
cmake -G Ninja
ninja
```
To run `jsonnet_sandboxed` (or `jsonnet_yaml_stream_sandboxed` or
`jsonnet_formatter_sandboxed` in a similar way):
```
cd examples
./jsonnet_sandboxed \
absolute/path/to/the/input_file.jsonnet \
absolute/path/to/the/output_file
```
To run `jsonnet_mutiple_files_sandboxed`:
```
cd examples
./jsonnet_mutiple_files_sandboxed \
absolute/path/to/the/input_file.jsonnet \
absolute/path/to/the/output_directory
```
All three tools support evaluating one input file (possibly relying on multiple
other files, e.x. by jsonnet `import` command; the files must be held in the
same directory as input file) into one or more output files. Example jsonnet
codes to evaluate in a one-in-one-out manner can be found
[here](https://github.com/google/jsonnet/tree/master/examples). Example code
producing multiple output files or YAML stream files can be found in the
`examples/jsonnet_codes` directory (along with some other examples copied with
minimal changes from the library files), in files called
`multiple_files_example.jsonnet` and `yaml_stream_example.jsonnet`,
respectively. In the `examples/jsonnet_codes_expected_output` directory one can
found outputs the mentioned above files' evaluation should produce.
The formatter reads one input file and produces one output file as a result.
Example code for this tool can also be found in `examples/jsonnet_codes`
directory, in a file called `formatter_example.jsonnet`.
## Testing
A few tests prepared with a use of
[Google Test](https://github.com/google/googletest) framework can be found in
the `tests/` directory. To run them type:
```
cd tests
./tests
```

View File

@ -0,0 +1,75 @@
# Copyright 2020 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
#
# 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.
file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/gen_files")
file(COPY "${PROJECT_SOURCE_DIR}/jsonnet/cmd/jsonnet.cpp" DESTINATION "${PROJECT_BINARY_DIR}/gen_files/")
file(COPY "${PROJECT_SOURCE_DIR}/jsonnet.patch" DESTINATION "${PROJECT_BINARY_DIR}/gen_files/")
add_custom_command(
OUTPUT ${PROJECT_BINARY_DIR}/gen_files/write_helper.cc
COMMAND cd ${PROJECT_BINARY_DIR}/gen_files && patch < ${PROJECT_SOURCE_DIR}/jsonnet.patch > /dev/null
COMMAND mv ${PROJECT_BINARY_DIR}/gen_files/jsonnet.cpp ${PROJECT_BINARY_DIR}/gen_files/write_helper.cc
)
list(APPEND JSONNET_SAPI_HEADERS
${PROJECT_SOURCE_DIR}
${PROJECT_SOURCE_DIR}/headers
${PROJECT_BINARY_DIR}/gen_files
)
add_library(jsonnet_helper STATIC
${PROJECT_SOURCE_DIR}/jsonnet_helper.cc
${PROJECT_SOURCE_DIR}/jsonnet_helper.h
${PROJECT_SOURCE_DIR}/jsonnet/cmd/utils.h
${PROJECT_SOURCE_DIR}/jsonnet/cmd/utils.cpp
${PROJECT_BINARY_DIR}/gen_files/write_helper.cc
)
target_include_directories(jsonnet_helper PUBLIC
${JSONNET_SAPI_HEADERS}
)
target_link_libraries(jsonnet_helper
libjsonnet_for_binaries
)
foreach(exe base multiple_files yaml_stream formatter)
add_executable(jsonnet_${exe}_sandboxed
jsonnet_${exe}_example.cc
)
target_link_libraries(jsonnet_${exe}_sandboxed PRIVATE
libjsonnet
jsonnet_helper
jsonnet_sapi
sandbox2::file_base
sandbox2::fileops
sapi::sapi
)
target_include_directories(jsonnet_${exe}_sandboxed PUBLIC
${JSONNET_SAPI_HEADERS}
)
endforeach()
add_executable(jsonnet_base_transacted
jsonnet_base_transaction.cc
)
target_link_libraries(jsonnet_base_transacted PRIVATE
libjsonnet
jsonnet_helper
jsonnet_sapi
sapi::sapi
)

View File

@ -0,0 +1,95 @@
// Copyright 2020 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
//
// 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.
#include <cstdlib>
#include <iostream>
#include "jsonnet_base_sandbox.h" // NOLINT(build/include)
#include "sandboxed_api/sandbox2/util/fileops.h"
#include "sandboxed_api/sandbox2/util/path.h"
absl::Status JsonnetMain(std::string in_file, std::string out_file) {
using sandbox2::file::JoinPath;
using sandbox2::file_util::fileops::Basename;
// Initialize sandbox.
JsonnetBaseSandbox sandbox(in_file, out_file);
SAPI_RETURN_IF_ERROR(sandbox.Init())
JsonnetApi api(&sandbox);
// Initialize library's main structure.
SAPI_ASSIGN_OR_RETURN(JsonnetVm * jsonnet_vm, api.c_jsonnet_make());
sapi::v::RemotePtr vm_pointer(jsonnet_vm);
// Read input file.
std::string in_file_in_sandboxee(JoinPath("/input", Basename(in_file)));
sapi::v::ConstCStr in_file_var(in_file_in_sandboxee.c_str());
SAPI_ASSIGN_OR_RETURN(char* input,
api.c_read_input(false, in_file_var.PtrBefore()));
// Process jsonnet data.
sapi::v::RemotePtr input_pointer(input);
sapi::v::Int error;
SAPI_ASSIGN_OR_RETURN(char* output, api.c_jsonnet_evaluate_snippet(
&vm_pointer, in_file_var.PtrBefore(),
&input_pointer, error.PtrAfter()));
CHECK(!error.GetValue()) << "Jsonnet code evaluation failed: "
<< error.GetValue() << "\n"
<< "Make sure all files used by your jsonnet file "
"are in the same directory as your file.";
// Write data to file.
std::string out_file_in_sandboxee(JoinPath("/output", Basename(out_file)));
sapi::v::ConstCStr out_file_var(out_file_in_sandboxee.c_str());
sapi::v::RemotePtr output_pointer(output);
SAPI_ASSIGN_OR_RETURN(
bool success,
api.c_write_output_file(&output_pointer, out_file_var.PtrBefore()));
CHECK(success) << "Writing to output file failed: " << success;
// Clean up.
SAPI_ASSIGN_OR_RETURN(char* result,
api.c_jsonnet_realloc(&vm_pointer, &output_pointer, 0));
SAPI_RETURN_IF_ERROR(api.c_jsonnet_destroy(&vm_pointer));
SAPI_RETURN_IF_ERROR(api.c_free_input(&input_pointer));
return absl::OkStatus();
}
int main(int argc, char* argv[]) {
using sandbox2::file_util::fileops::Basename;
google::InitGoogleLogging(argv[0]);
gflags::ParseCommandLineFlags(&argc, &argv, true);
if (!(argc == 3)) {
std::cerr << "Usage:\n"
<< Basename(argv[0])
<< " /absolute/path/to/INPUT.jsonnet /absolute/path/to/OUTPUT\n";
return EXIT_FAILURE;
}
std::string in_file(argv[1]);
std::string out_file(argv[2]);
absl::Status status = JsonnetMain(in_file, out_file);
if (!status.ok()) {
LOG(ERROR) << "Failed: " << status.ToString();
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,88 @@
// Copyright 2020 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
//
// 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.
#include "jsonnet_base_transaction.h" // NOLINT(build/include)
#include "sandboxed_api/sandbox2/util/fileops.h"
#include "sandboxed_api/sandbox2/util/path.h"
absl::Status JsonnetTransaction::Main() {
using sandbox2::file::JoinPath;
using sandbox2::file_util::fileops::Basename;
JsonnetApi api(sandbox());
// Initialize library's main structure.
SAPI_ASSIGN_OR_RETURN(JsonnetVm * jsonnet_vm, api.c_jsonnet_make());
sapi::v::RemotePtr vm_pointer(jsonnet_vm);
// Read input file.
std::string in_file_in_sandboxee(JoinPath("/input", Basename(in_file)));
sapi::v::ConstCStr in_file_var(in_file_in_sandboxee.c_str());
SAPI_ASSIGN_OR_RETURN(char* input,
api.c_read_input(false, in_file_var.PtrBefore()));
// Process jsonnet data.
sapi::v::RemotePtr input_pointer(input);
sapi::v::Int error;
SAPI_ASSIGN_OR_RETURN(char* output, api.c_jsonnet_evaluate_snippet(
&vm_pointer, in_file_var.PtrBefore(),
&input_pointer, error.PtrAfter()));
TRANSACTION_FAIL_IF_NOT(error.GetValue() == 0,
"Jsonnet code evaluation failed.");
// Write data to file.
std::string out_file_in_sandboxee(JoinPath("/output", Basename(out_file)));
sapi::v::ConstCStr out_file_var(out_file_in_sandboxee.c_str());
sapi::v::RemotePtr output_pointer(output);
SAPI_ASSIGN_OR_RETURN(
bool success,
api.c_write_output_file(&output_pointer, out_file_var.PtrBefore()));
TRANSACTION_FAIL_IF_NOT(success, "Writing to output file failed.");
// Clean up.
SAPI_ASSIGN_OR_RETURN(char* result,
api.c_jsonnet_realloc(&vm_pointer, &output_pointer, 0));
SAPI_RETURN_IF_ERROR(api.c_jsonnet_destroy(&vm_pointer));
SAPI_RETURN_IF_ERROR(api.c_free_input(&input_pointer));
return absl::OkStatus();
}
int main(int argc, char* argv[]) {
using sandbox2::file_util::fileops::Basename;
google::InitGoogleLogging(argv[0]);
gflags::ParseCommandLineFlags(&argc, &argv, true);
if (!(argc == 3)) {
std::cerr << "Usage:\n"
<< Basename(argv[0])
<< " /absolute/path/to/INPUT.jsonnet /absolute/path/to/OUTPUT\n";
return EXIT_FAILURE;
}
std::string in_file(argv[1]);
std::string out_file(argv[2]);
JsonnetTransaction jsonnet_transaction(in_file, out_file);
auto result = jsonnet_transaction.Run();
LOG(INFO) << "Transaction result: " << result.message();
CHECK(result.ok());
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,50 @@
/*
Copyright 2015 Google Inc. All rights reserved.
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.
*/
{
concat_array: [1, 2, 3] + [4],
concat_string: '123' + 4,
equality1: 1 == '1',
equality2: [{}, { x: 3 - 1 }]
== [{}, { x: 2 }],
ex1: 1 + 2 * 3 / (4 + 5),
// Bitwise operations first cast to int.
ex2: self.ex1 | 3,
// Modulo operator.
ex3: self.ex1 % 2,
// Boolean logic
ex4: (4 > 3) && (1 <= 3) || false,
// Mixing objects together
obj: { a: 1, b: 2 } + { b: 3, c: 4 },
// Test if a field is in an object
obj_member: 'foo' in { foo: 1 },
// String formatting
str1: 'The value of self.ex2 is '
+ self.ex2 + '.',
str2: 'The value of self.ex2 is %g.'
% self.ex2,
str3: 'ex1=%0.2f, ex2=%0.2f'
% [self.ex1, self.ex2],
// By passing self, we allow ex1 and ex2 to
// be extracted internally.
str4: 'ex1=%(ex1)0.2f, ex2=%(ex2)0.2f'
% self,
// Do textual templating of entire files:
str5: |||
ex1=%(ex1)0.2f
ex2=%(ex2)0.2f
||| % self,
}

View File

@ -0,0 +1,32 @@
// Copyright 2020 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
//
// 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.
// This is a poorly written jsonnet file. Given to the formatter executable will be changed into a canonical jsonnet file form.
local b = import "somefile.libsonnet"; # comment
local a = import "differentfile.libsonnet"; // another comment in different style
local SomeStuff = {bar: "foo"};
local funtion_to_do_addition(x,y)=x+y;
{
"this": ((3)) ,
"that that":
funtion_to_do_addition(4,2),
arrArr: [[
1, 2, 5
],
3, 10, 19
]
} + SomeStuff

View File

@ -0,0 +1,30 @@
/*
Copyright 2015 Google Inc. All rights reserved.
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.
*/
local martinis = import 'martinis.libsonnet';
{
'Vodka Martini': martinis['Vodka Martini'],
Manhattan: {
ingredients: [
{ kind: 'Rye', qty: 2.5 },
{ kind: 'Sweet Red Vermouth', qty: 1 },
{ kind: 'Angostura', qty: 'dash' },
],
garnish: importstr 'garnish.txt',
served: 'Straight Up',
},
}

View File

@ -0,0 +1,26 @@
// Copyright 2020 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
//
// 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.
// This is a jsonnet code which evaluates to mutliple output files.
{
"first_file.json": {
name: 'This is the first file created by the multiple-files example code.',
caption: 'The other one\'s name is -> ' + $["second_file.json"].name,
},
"second_file.json": {
name: 'And that is the other one.',
caption: 'If it was the first one, variable name would hold what\'s in <first_name> variable.',
first_name: $["first_file.json"].name,
},
}

View File

@ -0,0 +1,29 @@
/*
Copyright 2015 Google Inc. All rights reserved.
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.
*/
local utils = import 'utils.libsonnet';
{
Negroni: {
// Divide 3oz among the 3 ingredients.
ingredients: utils.equal_parts(3, [
'Farmers Gin',
'Sweet Red Vermouth',
'Campari',
]),
garnish: 'Orange Peel',
served: 'On The Rocks',
},
}

View File

@ -0,0 +1,26 @@
/*
Copyright 2015 Google Inc. All rights reserved.
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.
*/
{
equal_parts(size, ingredients)::
// Define a function-scoped variable.
local qty = size / std.length(ingredients);
// Return an array.
[
{ kind: i, qty: qty }
for i in ingredients
],
}

View File

@ -0,0 +1,26 @@
// Copyright 2020 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
//
// 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.
// This is a jsonnet code which evaluates to json file, which can be interpreted as YAML stream.
local
first_object = {
name: 'First object\'s name.',
age: 'Just created!',
},
second_object = {
name: 'Hi, my name is <second_object>.',
sibling: first_object.name
};
[first_object, second_object]

View File

@ -0,0 +1,26 @@
{
"concat_array": [
1,
2,
3,
4
],
"concat_string": "1234",
"equality1": false,
"equality2": true,
"ex1": 1.6666666666666665,
"ex2": 3,
"ex3": 1.6666666666666665,
"ex4": true,
"obj": {
"a": 1,
"b": 3,
"c": 4
},
"obj_member": true,
"str1": "The value of self.ex2 is 3.",
"str2": "The value of self.ex2 is 3.",
"str3": "ex1=1.67, ex2=3.00",
"str4": "ex1=1.67, ex2=3.00",
"str5": "ex1=1.67\nex2=3.00\n"
}

View File

@ -0,0 +1,4 @@
{
"caption": "The other one's name is -> And that is the other one.",
"name": "This is the first file created by the multiple-files example code."
}

View File

@ -0,0 +1,20 @@
{
"Negroni": {
"garnish": "Orange Peel",
"ingredients": [
{
"kind": "Farmers Gin",
"qty": 1
},
{
"kind": "Sweet Red Vermouth",
"qty": 1
},
{
"kind": "Campari",
"qty": 1
}
],
"served": "On The Rocks"
}
}

View File

@ -0,0 +1,5 @@
{
"caption": "If it was the first one, variable name would hold what's in <first_name> variable.",
"first_name": "This is the first file created by the multiple-files example code.",
"name": "And that is the other one."
}

View File

@ -0,0 +1,11 @@
---
{
"age": "Just created!",
"name": "First object's name."
}
---
{
"name": "Hi, my name is <second_object>.",
"sibling": "First object's name."
}
...

View File

@ -0,0 +1,128 @@
// Copyright 2020 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
//
// 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.
#include <libgen.h>
#include <syscall.h>
#include <cstdlib>
#include <iostream>
#include "jsonnet_sapi.sapi.h" // NOLINT(build/include)
#include "sandboxed_api/util/flag.h"
#include "sandboxed_api/sandbox2/util/fileops.h"
#include "sandboxed_api/sandbox2/util/path.h"
class JsonnetSapiSandbox : public JsonnetSandbox {
public:
explicit JsonnetSapiSandbox(std::string in_file, std::string out_file)
: in_file_(std::move(in_file)), out_file_(std::move(out_file)) {}
// We need only the input file here, not the whole input directory
std::unique_ptr<sandbox2::Policy> ModifyPolicy(
sandbox2::PolicyBuilder*) override {
return sandbox2::PolicyBuilder()
.AllowStaticStartup()
.AllowOpen()
.AllowRead()
.AllowWrite()
.AllowStat()
.AllowSystemMalloc()
.AllowExit()
.AllowSyscalls({
__NR_futex,
__NR_close,
})
.AddDirectoryAt(dirname(&out_file_[0]), "/output", /*is_ro=*/false)
.AddFile(in_file_, true)
.BuildOrDie();
}
private:
std::string in_file_;
std::string out_file_;
};
absl::Status JsonnetMain(std::string in_file, std::string out_file) {
using sandbox2::file::JoinPath;
using sandbox2::file_util::fileops::Basename;
// Initialize sandbox.
JsonnetSapiSandbox sandbox(in_file, out_file);
SAPI_RETURN_IF_ERROR(sandbox.Init())
JsonnetApi api(&sandbox);
// Initialize library's main structure.
SAPI_ASSIGN_OR_RETURN(JsonnetVm * jsonnet_vm, api.c_jsonnet_make());
sapi::v::RemotePtr vm_pointer(jsonnet_vm);
// Read input file.
std::string in_file_in_sandboxee(JoinPath("/input", Basename(in_file)));
sapi::v::ConstCStr in_file_var(in_file_in_sandboxee.c_str());
SAPI_ASSIGN_OR_RETURN(char* input,
api.c_read_input(false, in_file_var.PtrBefore()));
// Process jsonnet data.
sapi::v::RemotePtr input_pointer(input);
sapi::v::Int error;
SAPI_ASSIGN_OR_RETURN(char* output, api.c_jsonnet_fmt_snippet(
&vm_pointer, in_file_var.PtrBefore(),
&input_pointer, error.PtrAfter()));
CHECK(!error.GetValue()) << "Jsonnet code evaluation failed: "
<< error.GetValue() << "\n";
// Write data to file.
std::string out_file_in_sandboxee(JoinPath("/output", Basename(out_file)));
sapi::v::ConstCStr out_file_var(out_file_in_sandboxee.c_str());
sapi::v::RemotePtr output_pointer(output);
SAPI_ASSIGN_OR_RETURN(
bool success,
api.c_write_output_file(&output_pointer, out_file_var.PtrBefore()));
CHECK(success) << "Writing to output file failed: " << success;
// Clean up.
SAPI_ASSIGN_OR_RETURN(char* result,
api.c_jsonnet_realloc(&vm_pointer, &output_pointer, 0));
SAPI_RETURN_IF_ERROR(api.c_jsonnet_destroy(&vm_pointer));
SAPI_RETURN_IF_ERROR(api.c_free_input(&input_pointer));
return absl::OkStatus();
}
int main(int argc, char* argv[]) {
using sandbox2::file_util::fileops::Basename;
google::InitGoogleLogging(argv[0]);
gflags::ParseCommandLineFlags(&argc, &argv, true);
if (!(argc == 3)) {
std::cerr << "Usage:\n"
<< Basename(argv[0])
<< " /absolute/path/to/INPUT.jsonnet /absolute/path/to/OUTPUT\n";
return EXIT_FAILURE;
}
std::string in_file(argv[1]);
std::string out_file(argv[2]);
absl::Status status = JsonnetMain(in_file, out_file);
if (!status.ok()) {
LOG(ERROR) << "Failed: " << status.ToString();
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,132 @@
// Copyright 2020 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
//
// 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.
#include <libgen.h>
#include <syscall.h>
#include <cstdlib>
#include <iostream>
#include "jsonnet_sapi.sapi.h" // NOLINT(build/include)
#include "sandboxed_api/util/flag.h"
#include "sandboxed_api/sandbox2/util/fileops.h"
#include "sandboxed_api/sandbox2/util/path.h"
class JsonnetSapiSandbox : public JsonnetSandbox {
public:
explicit JsonnetSapiSandbox(std::string in_file, std::string out_directory)
: in_file_(std::move(in_file)),
out_directory_(std::move(out_directory)) {}
// We need a slightly different policy than the default one
std::unique_ptr<sandbox2::Policy> ModifyPolicy(
sandbox2::PolicyBuilder*) override {
return sandbox2::PolicyBuilder()
.AllowStaticStartup()
.AllowOpen()
.AllowRead()
.AllowWrite()
.AllowStat()
.AllowSystemMalloc()
.AllowExit()
.AllowSyscalls({
__NR_futex,
__NR_close,
})
.AddDirectoryAt(sandbox2::file::CleanPath(&out_directory_[0]),
"/output",
/*is_ro=*/false)
.AddDirectoryAt(dirname(&in_file_[0]), "/input", true)
.BuildOrDie();
}
private:
std::string in_file_;
std::string out_directory_;
};
absl::Status JsonnetMain(std::string in_file, std::string out_file) {
using sandbox2::file::JoinPath;
using sandbox2::file_util::fileops::Basename;
// Initialize sandbox.
JsonnetSapiSandbox sandbox(in_file, out_file);
SAPI_RETURN_IF_ERROR(sandbox.Init())
JsonnetApi api(&sandbox);
// Initialize library's main structure.
SAPI_ASSIGN_OR_RETURN(JsonnetVm * jsonnet_vm, api.c_jsonnet_make());
sapi::v::RemotePtr vm_pointer(jsonnet_vm);
// Read input file.
std::string in_file_in_sandboxee(JoinPath("/input", Basename(in_file)));
sapi::v::ConstCStr in_file_var(in_file_in_sandboxee.c_str());
SAPI_ASSIGN_OR_RETURN(char* input,
api.c_read_input(false, in_file_var.PtrBefore()));
// Process jsonnet data.
sapi::v::RemotePtr input_pointer(input);
sapi::v::Int error;
SAPI_ASSIGN_OR_RETURN(char* output, api.c_jsonnet_evaluate_snippet_multi(
&vm_pointer, in_file_var.PtrBefore(),
&input_pointer, error.PtrAfter()));
CHECK(!error.GetValue()) << "Jsonnet code evaluation failed: "
<< error.GetValue() << "\n"
<< "Make sure all files used by your jsonnet file "
"are in the same directory as your file.";
// Write data to file.
std::string out_file_in_sandboxee(JoinPath("/output", Basename(out_file)));
sapi::v::ConstCStr out_file_var(out_file_in_sandboxee.c_str());
sapi::v::RemotePtr output_pointer(output);
SAPI_ASSIGN_OR_RETURN(
bool success,
api.c_write_output_file(&output_pointer, out_file_var.PtrBefore()));
CHECK(success) << "Writing to output file failed: " << success;
// Clean up.
SAPI_ASSIGN_OR_RETURN(char* result,
api.c_jsonnet_realloc(&vm_pointer, &output_pointer, 0));
SAPI_RETURN_IF_ERROR(api.c_jsonnet_destroy(&vm_pointer));
SAPI_RETURN_IF_ERROR(api.c_free_input(&input_pointer));
return absl::OkStatus();
}
int main(int argc, char* argv[]) {
using sandbox2::file_util::fileops::Basename;
google::InitGoogleLogging(argv[0]);
gflags::ParseCommandLineFlags(&argc, &argv, true);
if (!(argc == 3)) {
std::cerr << "Usage:\n"
<< Basename(argv[0])
<< " /absolute/path/to/INPUT.jsonnet /absolute/path/to/OUTPUT\n";
return EXIT_FAILURE;
}
std::string in_file(argv[1]);
std::string out_file(argv[2]);
absl::Status status = JsonnetMain(in_file, out_file);
if (!status.ok()) {
LOG(ERROR) << "Failed: " << status.ToString();
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,95 @@
// Copyright 2020 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
//
// 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.
#include <cstdlib>
#include <iostream>
#include "jsonnet_base_sandbox.h" // NOLINT(build/include)
#include "sandboxed_api/sandbox2/util/fileops.h"
#include "sandboxed_api/sandbox2/util/path.h"
absl::Status JsonnetMain(std::string in_file, std::string out_file) {
using sandbox2::file::JoinPath;
using sandbox2::file_util::fileops::Basename;
// Initialize sandbox.
JsonnetBaseSandbox sandbox(in_file, out_file);
SAPI_RETURN_IF_ERROR(sandbox.Init())
JsonnetApi api(&sandbox);
// Initialize library's main structure.
SAPI_ASSIGN_OR_RETURN(JsonnetVm * jsonnet_vm, api.c_jsonnet_make());
sapi::v::RemotePtr vm_pointer(jsonnet_vm);
// Read input file.
std::string in_file_in_sandboxee(JoinPath("/input", Basename(in_file)));
sapi::v::ConstCStr in_file_var(in_file_in_sandboxee.c_str());
SAPI_ASSIGN_OR_RETURN(char* input,
api.c_read_input(false, in_file_var.PtrBefore()));
// Process jsonnet data.
sapi::v::RemotePtr input_pointer(input);
sapi::v::Int error;
SAPI_ASSIGN_OR_RETURN(char* output, api.c_jsonnet_evaluate_snippet_stream(
&vm_pointer, in_file_var.PtrBefore(),
&input_pointer, error.PtrAfter()));
CHECK(!error.GetValue())
<< "Jsonnet code evaluation failed: " << error.GetValue() << "\n"
<< "Make sure all files used by your jsonnet file are in the same "
"directory as your file.";
// Write data to file.
std::string out_file_in_sandboxee(JoinPath("/output", Basename(out_file)));
sapi::v::ConstCStr out_file_var(out_file_in_sandboxee.c_str());
sapi::v::RemotePtr output_pointer(output);
SAPI_ASSIGN_OR_RETURN(
bool success,
api.c_write_output_file(&output_pointer, out_file_var.PtrBefore()));
CHECK(success) << "Writing to output file failed: " << success;
// Clean up.
SAPI_ASSIGN_OR_RETURN(char* result,
api.c_jsonnet_realloc(&vm_pointer, &output_pointer, 0));
SAPI_RETURN_IF_ERROR(api.c_jsonnet_destroy(&vm_pointer));
SAPI_RETURN_IF_ERROR(api.c_free_input(&input_pointer));
return absl::OkStatus();
}
int main(int argc, char* argv[]) {
using sandbox2::file_util::fileops::Basename;
google::InitGoogleLogging(argv[0]);
gflags::ParseCommandLineFlags(&argc, &argv, true);
if (!(argc == 3)) {
std::cerr << "Usage:\n"
<< Basename(argv[0])
<< " /absolute/path/to/INPUT.jsonnet /absolute/path/to/OUTPUT\n";
return EXIT_FAILURE;
}
std::string in_file(argv[1]);
std::string out_file(argv[2]);
absl::Status status = JsonnetMain(in_file, out_file);
if (!status.ok()) {
LOG(ERROR) << "Failed: " << status.ToString();
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,55 @@
// Copyright 2020 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
//
// 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 JSONNET_BASE_SANDBOX_H_
#define JSONNET_BASE_SANDBOX_H_
#include <libgen.h>
#include <syscall.h>
#include "jsonnet_sapi.sapi.h" // NOLINT(build/include)
#include "sandboxed_api/util/flag.h"
#include "sandboxed_api/transaction.h"
#include "sandboxed_api/vars.h"
class JsonnetBaseSandbox : public JsonnetSandbox {
public:
explicit JsonnetBaseSandbox(std::string in_file, std::string out_file)
: in_file_(in_file), out_file_(out_file) {}
std::unique_ptr<sandbox2::Policy> ModifyPolicy(
sandbox2::PolicyBuilder *) override {
return sandbox2::PolicyBuilder()
.AllowStaticStartup()
.AllowOpen()
.AllowRead()
.AllowWrite()
.AllowStat()
.AllowSystemMalloc()
.AllowExit()
.AllowSyscalls({
__NR_futex,
__NR_close,
})
.AddDirectoryAt(dirname(&out_file_[0]), "/output", /*is_ro=*/false)
.AddDirectoryAt(dirname(&in_file_[0]), "/input", true)
.BuildOrDie();
}
private:
std::string in_file_;
std::string out_file_;
};
#endif // JSONNET_BASE_SANDBOX_H_

View File

@ -0,0 +1,38 @@
// Copyright 2020 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
//
// 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 JSNONNET_BASE_TRANSACTION_H_
#define JSNONNET_BASE_TRANSACTION_H_
#include "jsonnet_base_sandbox.h" // NOLINT(build/include)
class JsonnetTransaction : public sapi::Transaction {
public:
JsonnetTransaction(std::string in_file, std::string out_file)
: sapi::Transaction(
std::make_unique<JsonnetBaseSandbox>(in_file, out_file)),
in_file_(in_file),
out_file_(out_file) {
sapi::Transaction::set_retry_count(0); // Try once, no retries
sapi::Transaction::SetTimeLimit(0); // Infinite time limit
}
private:
std::string in_file_;
std::string out_file_;
absl::Status Main() override;
};
#endif // JSNONNET_BASE_TRANSACTION_H_

View File

@ -0,0 +1,57 @@
// Copyright 2020 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
//
// 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 JSONNET_TESTS_H_
#define JSONNET_TESTS_H_
#include <unistd.h>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <memory>
#include <streambuf>
#include <string>
#include "jsonnet_base_sandbox.h" // NOLINT(build/include)
#include "jsonnet_sapi.sapi.h" // NOLINT(build/include)
#include "gtest/gtest.h"
#include "sandboxed_api/util/flag.h"
#include "sandboxed_api/sandbox2/util/path.h"
#include "sandboxed_api/util/status_matchers.h"
class JsonnetTestHelper {
protected:
enum Evaluation { kBase, kMultipleFiles, kYamlStream };
void TestSetUp();
void TestTearDown();
void ReadInput(const char* filename);
void EvaluateJsonnetCode(Evaluation type, bool expected_correct);
void WriteOutput(const char* filename_or_directory, Evaluation type);
std::string ReadOutput(const char* filename);
std::unique_ptr<JsonnetBaseSandbox> sandbox_;
std::unique_ptr<JsonnetApi> api_;
std::unique_ptr<sapi::v::RemotePtr> input_;
std::unique_ptr<sapi::v::RemotePtr> output_;
std::unique_ptr<sapi::v::RemotePtr> vm_;
std::string input_filename_in_sandboxee_;
bool jsonnet_vm_was_used_;
bool input_was_read_;
};
#endif // JSONNET_TESTS_H_

@ -0,0 +1 @@
Subproject commit 3e25595d5c4acd32a1c3951a57471986b90d3bad

View File

@ -0,0 +1,664 @@
--- jsonnet.cpp 2020-09-09 12:15:33.687539042 +0000
+++ write_helper.cpp 2020-09-25 15:38:37.317147682 +0000
@@ -14,559 +14,125 @@
limitations under the License.
*/
-#include <cassert>
-#include <cstdlib>
-#include <cstring>
+// We need two functions defined in jsonnet.cpp file, used for writing output
+// (multiple files and yaml streams) -- with minor changes (e.x. return type).
-#include <exception>
#include <fstream>
#include <iostream>
-#include <list>
#include <map>
-#include <sstream>
-#include <string>
#include <vector>
-#include "utils.h"
+#include "jsonnet_helper.h" // NOLINT(build/include)
-extern "C" {
-#include <libjsonnet.h>
-}
-
-#ifdef _WIN32
-const char PATH_SEP = ';';
-#else
-const char PATH_SEP = ':';
-#endif
-
-void version(std::ostream &o)
-{
- o << "Jsonnet commandline interpreter " << jsonnet_version() << std::endl;
-}
-
-void usage(std::ostream &o)
-{
- version(o);
- o << "\n";
- o << "jsonnet {<option>} <filename>\n";
- o << "\n";
- o << "Available options:\n";
- o << " -h / --help This message\n";
- o << " -e / --exec Treat filename as code\n";
- o << " -J / --jpath <dir> Specify an additional library search dir (right-most wins)\n";
- o << " -o / --output-file <file> Write to the output file rather than stdout\n";
- o << " -m / --multi <dir> Write multiple files to the directory, list files on stdout\n";
- o << " -y / --yaml-stream Write output as a YAML stream of JSON documents\n";
- o << " -S / --string Expect a string, manifest as plain text\n";
- o << " -s / --max-stack <n> Number of allowed stack frames\n";
- o << " -t / --max-trace <n> Max length of stack trace before cropping\n";
- o << " --gc-min-objects <n> Do not run garbage collector until this many\n";
- o << " --gc-growth-trigger <n> Run garbage collector after this amount of object growth\n";
- o << " --version Print version\n";
- o << "Available options for specifying values of 'external' variables:\n";
- o << "Provide the value as a string:\n";
- o << " -V / --ext-str <var>[=<val>] If <val> is omitted, get from environment var <var>\n";
- o << " --ext-str-file <var>=<file> Read the string from the file\n";
- o << "Provide a value as Jsonnet code:\n";
- o << " --ext-code <var>[=<code>] If <code> is omitted, get from environment var <var>\n";
- o << " --ext-code-file <var>=<file> Read the code from the file\n";
- o << "Available options for specifying values of 'top-level arguments':\n";
- o << "Provide the value as a string:\n";
- o << " -A / --tla-str <var>[=<val>] If <val> is omitted, get from environment var <var>\n";
- o << " --tla-str-file <var>=<file> Read the string from the file\n";
- o << "Provide a value as Jsonnet code:\n";
- o << " --tla-code <var>[=<code>] If <code> is omitted, get from environment var <var>\n";
- o << " --tla-code-file <var>=<file> Read the code from the file\n";
- o << "Environment variables:\n";
- o << "JSONNET_PATH is a colon (semicolon on Windows) separated list of directories added\n";
- o << "in reverse order before the paths specified by --jpath (i.e. left-most wins)\n";
- o << "E.g. JSONNET_PATH=a:b jsonnet -J c -J d is equivalent to:\n";
- o << "JSONNET_PATH=d:c:a:b jsonnet\n";
- o << "jsonnet -J b -J a -J c -J d\n";
- o << "\n";
- o << "In all cases:\n";
- o << "<filename> can be - (stdin)\n";
- o << "Multichar options are expanded e.g. -abc becomes -a -b -c.\n";
- o << "The -- option suppresses option processing for subsequent arguments.\n";
- o << "Note that since filenames and jsonnet programs can begin with -, it is advised to\n";
- o << "use -- if the argument is unknown, e.g. jsonnet -- \"$FILENAME\".";
- o << std::endl;
-}
-
-/** Class for representing configuration read from command line flags. */
-struct JsonnetConfig {
- std::vector<std::string> inputFiles;
- std::string outputFile;
- bool filenameIsCode;
-
- // EVAL flags
- bool evalMulti;
- bool evalStream;
- std::string evalMultiOutputDir;
-
- JsonnetConfig()
- : filenameIsCode(false),
- evalMulti(false),
- evalStream(false)
- {
- }
-};
-
-bool get_var_val(const std::string &var_val, std::string &var, std::string &val)
-{
- size_t eq_pos = var_val.find_first_of('=', 0);
- if (eq_pos == std::string::npos) {
- var = var_val;
- const char *val_cstr = ::getenv(var.c_str());
- if (val_cstr == nullptr) {
- std::cerr << "ERROR: environment variable " << var << " was undefined." << std::endl;
- return false;
- }
- val = val_cstr;
- } else {
- var = var_val.substr(0, eq_pos);
- val = var_val.substr(eq_pos + 1, std::string::npos);
- }
- return true;
-}
-
-bool get_var_file(const std::string &var_file, const std::string &imp, std::string &var, std::string &val)
-{
- size_t eq_pos = var_file.find_first_of('=', 0);
- if (eq_pos == std::string::npos) {
- std::cerr << "ERROR: argument not in form <var>=<file> \"" << var_file << "\"."
- << std::endl;
- return false;
- }
- var = var_file.substr(0, eq_pos);
- const std::string path = var_file.substr(eq_pos + 1, std::string::npos);
-
- size_t b, e;
- val.erase().append(imp).append(" @'");
- // duplicate all the single quotes in @path to make a quoted string
- for (b = 0; (e = path.find("'", b)) != std::string::npos; b = e + 1) {
- val.append(path.substr(b, e - b + 1)).push_back('\'');
+/** Writes output files for multiple file output */
+bool write_multi_output_files(char *output, const std::string &output_dir, bool show_output_file_names) {
+ // If multiple file output is used, then iterate over each string from
+ // the sequence of strings returned by jsonnet_evaluate_snippet_multi,
+ // construct pairs of filename and content, and write each output file.
+ std::map<std::string, std::string> r;
+ for (const char *c = output; *c != '\0';) {
+ const char *filename = c;
+ const char *c2 = c;
+ while (*c2 != '\0') ++c2;
+ ++c2;
+ const char *json = c2;
+ while (*c2 != '\0') ++c2;
+ ++c2;
+ c = c2;
+ r[filename] = json;
+ }
+
+ std::ostream *o;
+ std::ofstream f;
+
+ o = &std::cout;
+
+ for (const auto &pair : r) {
+ const std::string &new_content = pair.second;
+ const std::string &filename = output_dir + pair.first;
+ if (show_output_file_names) {
+ (*o) << filename << std::endl;
}
- val.append(path.substr(b)).push_back('\'');
-
- return true;
-}
-
-enum ArgStatus {
- ARG_CONTINUE,
- ARG_SUCCESS,
- ARG_FAILURE,
-};
-
-/** Parse the command line arguments, configuring the Jsonnet VM context and
- * populating the JsonnetConfig.
- */
-static ArgStatus process_args(int argc, const char **argv, JsonnetConfig *config, JsonnetVm *vm)
-{
- auto args = simplify_args(argc, argv);
- std::vector<std::string> remaining_args;
-
- unsigned i = 0;
-
- for (; i < args.size(); ++i) {
- const std::string &arg = args[i];
- if (arg == "-h" || arg == "--help") {
- usage(std::cout);
- return ARG_SUCCESS;
- } else if (arg == "-v" || arg == "--version") {
- version(std::cout);
- return ARG_SUCCESS;
- } else if (arg == "-e" || arg == "--exec") {
- config->filenameIsCode = true;
- } else if (arg == "-o" || arg == "--output-file") {
- std::string output_file = next_arg(i, args);
- if (output_file.length() == 0) {
- std::cerr << "ERROR: -o argument was empty string" << std::endl;
- return ARG_FAILURE;
- }
- config->outputFile = output_file;
- } else if (arg == "--") {
- // All subsequent args are not options.
- while ((++i) < args.size())
- remaining_args.push_back(args[i]);
- break;
- } else if (arg == "-s" || arg == "--max-stack") {
- long l = strtol_check(next_arg(i, args));
- if (l < 1) {
- std::cerr << "ERROR: invalid --max-stack value: " << l << std::endl;
- return ARG_FAILURE;
- }
- jsonnet_max_stack(vm, l);
- } else if (arg == "-J" || arg == "--jpath") {
- std::string dir = next_arg(i, args);
- if (dir.length() == 0) {
- std::cerr << "ERROR: -J argument was empty string" << std::endl;
- return ARG_FAILURE;
- }
- if (dir[dir.length() - 1] != '/') {
- dir += '/';
- }
- jsonnet_jpath_add(vm, dir.c_str());
- } else if (arg == "-V" || arg == "--ext-str") {
- std::string var, val;
- if (!get_var_val(next_arg(i, args), var, val))
- return ARG_FAILURE;
- jsonnet_ext_var(vm, var.c_str(), val.c_str());
- } else if (arg == "-E" || arg == "--var" || arg == "--env") {
- // TODO(dcunnin): Delete this in a future release.
- std::cerr << "WARNING: jsonnet eval -E, --var and --env are deprecated,"
- << " please use -V or --ext-str." << std::endl;
- std::string var, val;
- if (!get_var_val(next_arg(i, args), var, val))
- return ARG_FAILURE;
- jsonnet_ext_var(vm, var.c_str(), val.c_str());
- } else if (arg == "--ext-str-file") {
- std::string var, val;
- if (!get_var_file(next_arg(i, args), "importstr", var, val))
- return ARG_FAILURE;
- jsonnet_ext_code(vm, var.c_str(), val.c_str());
- } else if (arg == "-F" || arg == "--file") {
- // TODO(dcunnin): Delete this in a future release.
- std::cerr << "WARNING: jsonnet eval -F and --file are deprecated,"
- << " please use --ext-str-file." << std::endl;
- std::string var, val;
- if (!get_var_file(next_arg(i, args), "importstr", var, val))
- return ARG_FAILURE;
- jsonnet_ext_code(vm, var.c_str(), val.c_str());
- } else if (arg == "--ext-code") {
- std::string var, val;
- if (!get_var_val(next_arg(i, args), var, val))
- return ARG_FAILURE;
- jsonnet_ext_code(vm, var.c_str(), val.c_str());
- } else if (arg == "--code-var" || arg == "--code-env") {
- // TODO(dcunnin): Delete this in a future release.
- std::cerr << "WARNING: jsonnet eval --code-var and --code-env are deprecated,"
- << " please use --ext-code." << std::endl;
- std::string var, val;
- if (!get_var_val(next_arg(i, args), var, val))
- return ARG_FAILURE;
- jsonnet_ext_code(vm, var.c_str(), val.c_str());
- } else if (arg == "--ext-code-file") {
- std::string var, val;
- if (!get_var_file(next_arg(i, args), "import", var, val))
- return ARG_FAILURE;
- jsonnet_ext_code(vm, var.c_str(), val.c_str());
- } else if (arg == "--code-file") {
- // TODO(dcunnin): Delete this in a future release.
- std::cerr << "WARNING: jsonnet eval --code-file is deprecated,"
- << " please use --ext-code-file." << std::endl;
- std::string var, val;
- if (!get_var_file(next_arg(i, args), "import", var, val))
- return ARG_FAILURE;
- jsonnet_ext_code(vm, var.c_str(), val.c_str());
- } else if (arg == "-A" || arg == "--tla-str") {
- std::string var, val;
- if (!get_var_val(next_arg(i, args), var, val))
- return ARG_FAILURE;
- jsonnet_tla_var(vm, var.c_str(), val.c_str());
- } else if (arg == "--tla-str-file") {
- std::string var, val;
- if (!get_var_file(next_arg(i, args), "importstr", var, val))
- return ARG_FAILURE;
- jsonnet_tla_code(vm, var.c_str(), val.c_str());
- } else if (arg == "--tla-code") {
- std::string var, val;
- if (!get_var_val(next_arg(i, args), var, val))
- return ARG_FAILURE;
- jsonnet_tla_code(vm, var.c_str(), val.c_str());
- } else if (arg == "--tla-code-file") {
- std::string var, val;
- if (!get_var_file(next_arg(i, args), "import", var, val))
- return ARG_FAILURE;
- jsonnet_tla_code(vm, var.c_str(), val.c_str());
-
- } else if (arg == "--gc-min-objects") {
- long l = strtol_check(next_arg(i, args));
- if (l < 0) {
- std::cerr << "ERROR: invalid --gc-min-objects value: " << l << std::endl;
- return ARG_FAILURE;
- }
- jsonnet_gc_min_objects(vm, l);
- } else if (arg == "-t" || arg == "--max-trace") {
- long l = strtol_check(next_arg(i, args));
- if (l < 0) {
- std::cerr << "ERROR: invalid --max-trace value: " << l << std::endl;
- return ARG_FAILURE;
- }
- jsonnet_max_trace(vm, l);
- } else if (arg == "--gc-growth-trigger") {
- std::string num = next_arg(i, args);
- char *ep;
- double v = std::strtod(num.c_str(), &ep);
- if (*ep != '\0' || num.length() == 0) {
- std::cerr << "ERROR: invalid number \"" << num << "\"" << std::endl;
- return ARG_FAILURE;
- }
- if (v < 0) {
- std::cerr << "ERROR: invalid --gc-growth-trigger \"" << num << "\""
- << std::endl;
- return ARG_FAILURE;
- }
- jsonnet_gc_growth_trigger(vm, v);
- } else if (arg == "-m" || arg == "--multi") {
- config->evalMulti = true;
- std::string output_dir = next_arg(i, args);
- if (output_dir.length() == 0) {
- std::cerr << "ERROR: -m argument was empty string" << std::endl;
- return ARG_FAILURE;
- }
- if (output_dir[output_dir.length() - 1] != '/') {
- output_dir += '/';
- }
- config->evalMultiOutputDir = output_dir;
- } else if (arg == "-y" || arg == "--yaml-stream") {
- config->evalStream = true;
- } else if (arg == "-S" || arg == "--string") {
- jsonnet_string_output(vm, 1);
- } else if (arg.length() > 1 && arg[0] == '-') {
- std::cerr << "ERROR: unrecognized argument: " << arg << std::endl;
- return ARG_FAILURE;
- } else {
- remaining_args.push_back(args[i]);
+ {
+ std::ifstream exists(filename.c_str());
+ if (exists.good()) {
+ std::string existing_content;
+ existing_content.assign(std::istreambuf_iterator<char>(exists),
+ std::istreambuf_iterator<char>());
+ if (existing_content == new_content) {
+ // Do not bump the timestamp on the file if its content is
+ // the same. This may trigger other tools (e.g. make) to do
+ // unnecessary work.
+ continue;
}
+ }
}
-
- const char *want = config->filenameIsCode ? "code" : "filename";
- if (remaining_args.size() == 0) {
- std::cerr << "ERROR: must give " << want << "\n" << std::endl;
- usage(std::cerr);
- return ARG_FAILURE;
- }
-
- if (remaining_args.size() > 1) {
- std::string filename = remaining_args[0];
- std::cerr << "ERROR: only one " << want << " is allowed\n" << std::endl;
- return ARG_FAILURE;
- }
- config->inputFiles = remaining_args;
- return ARG_CONTINUE;
-}
-
-/** Writes output files for multiple file output */
-static bool write_multi_output_files(JsonnetVm *vm, char *output, const std::string &output_dir,
- const std::string &output_file)
-{
- // If multiple file output is used, then iterate over each string from
- // the sequence of strings returned by jsonnet_evaluate_snippet_multi,
- // construct pairs of filename and content, and write each output file.
- std::map<std::string, std::string> r;
- for (const char *c = output; *c != '\0';) {
- const char *filename = c;
- const char *c2 = c;
- while (*c2 != '\0')
- ++c2;
- ++c2;
- const char *json = c2;
- while (*c2 != '\0')
- ++c2;
- ++c2;
- c = c2;
- r[filename] = json;
- }
- jsonnet_realloc(vm, output, 0);
-
- std::ostream *o;
std::ofstream f;
-
- if (output_file.empty()) {
- o = &std::cout;
- } else {
- f.open(output_file.c_str());
- if (!f.good()) {
- std::string msg = "Writing to output file: " + output_file;
- perror(msg.c_str());
- return false;
- }
- o = &f;
+ f.open(filename.c_str());
+ if (!f.good()) {
+ std::string msg = "Opening output file: " + filename;
+ perror(msg.c_str());
+ return false;
+ }
+ f << new_content;
+ f.close();
+ if (!f.good()) {
+ std::string msg = "Writing to output file: " + filename;
+ perror(msg.c_str());
+ return false;
}
+ }
- for (const auto &pair : r) {
- const std::string &new_content = pair.second;
- const std::string &filename = output_dir + pair.first;
- (*o) << filename << std::endl;
- {
- std::ifstream exists(filename.c_str());
- if (exists.good()) {
- std::string existing_content;
- existing_content.assign(std::istreambuf_iterator<char>(exists),
- std::istreambuf_iterator<char>());
- if (existing_content == new_content) {
- // Do not bump the timestamp on the file if its content is
- // the same. This may trigger other tools (e.g. make) to do
- // unnecessary work.
- continue;
- }
- }
- }
- std::ofstream f;
- f.open(filename.c_str());
- if (!f.good()) {
- std::string msg = "Opening output file: " + filename;
- perror(msg.c_str());
- return false;
- }
- f << new_content;
- f.close();
- if (!f.good()) {
- std::string msg = "Writing to output file: " + filename;
- perror(msg.c_str());
- return false;
- }
- }
+ std::cout.flush();
- if (output_file.empty()) {
- std::cout.flush();
- } else {
- f.close();
- if (!f.good()) {
- std::string msg = "Writing to output file: " + output_file;
- perror(msg.c_str());
- return false;
- }
- }
- return true;
+ return true;
}
/** Writes output files for YAML stream output */
-static bool write_output_stream(JsonnetVm *vm, char *output, const std::string &output_file)
-{
- std::ostream *o;
- std::ofstream f;
-
- if (output_file.empty()) {
- o = &std::cout;
- } else {
- f.open(output_file.c_str());
- if (!f.good()) {
- std::string msg = "Writing to output file: " + output_file;
- perror(msg.c_str());
- return false;
- }
- o = &f;
- }
-
- // If YAML stream output is used, then iterate over each string from
- // the sequence of strings returned by jsonnet_evaluate_snippet_stream,
- // and add the --- and ... as defined by the YAML spec.
- std::vector<std::string> r;
- for (const char *c = output; *c != '\0';) {
- const char *json = c;
- while (*c != '\0')
- ++c;
- ++c;
- r.emplace_back(json);
- }
- jsonnet_realloc(vm, output, 0);
- for (const auto &str : r) {
- (*o) << "---\n";
- (*o) << str;
- }
- if (r.size() > 0)
- (*o) << "...\n";
- o->flush();
-
- if (output_file.empty()) {
- std::cout.flush();
- } else {
- f.close();
- if (!f.good()) {
- std::string msg = "Writing to output file: " + output_file;
- perror(msg.c_str());
- return false;
- }
+bool write_output_stream(char *output, const std::string &output_file) {
+ std::ostream *o;
+ std::ofstream f;
+
+ if (output_file.empty()) {
+ o = &std::cout;
+ } else {
+ f.open(output_file.c_str());
+ if (!f.good()) {
+ std::string msg = "Writing to output file: " + output_file;
+ perror(msg.c_str());
+ return false;
+ }
+ o = &f;
+ }
+
+ // If YAML stream output is used, then iterate over each string from
+ // the sequence of strings returned by jsonnet_evaluate_snippet_stream,
+ // and add the --- and ... as defined by the YAML spec.
+ std::vector<std::string> r;
+ for (const char *c = output; *c != '\0';) {
+ const char *json = c;
+ while (*c != '\0') ++c;
+ ++c;
+ r.emplace_back(json);
+ }
+
+ for (const auto &str : r) {
+ (*o) << "---\n";
+ (*o) << str;
+ }
+ if (r.size() > 0) (*o) << "...\n";
+ o->flush();
+
+ if (output_file.empty()) {
+ std::cout.flush();
+ } else {
+ f.close();
+ if (!f.good()) {
+ std::string msg = "Writing to output file: " + output_file;
+ perror(msg.c_str());
+ return false;
}
+ }
- return true;
-}
-
-int main(int argc, const char **argv)
-{
- try {
- JsonnetVm *vm = jsonnet_make();
- JsonnetConfig config;
- if (const char *jsonnet_path_env = getenv("JSONNET_PATH")) {
- std::list<std::string> jpath;
- std::istringstream iss(jsonnet_path_env);
- std::string path;
- while (std::getline(iss, path, PATH_SEP)) {
- jpath.push_front(path);
- }
- for (const std::string &path : jpath) {
- jsonnet_jpath_add(vm, path.c_str());
- }
- }
- ArgStatus arg_status = process_args(argc, argv, &config, vm);
- if (arg_status != ARG_CONTINUE) {
- jsonnet_destroy(vm);
- return arg_status == ARG_SUCCESS ? EXIT_SUCCESS : EXIT_FAILURE;
- }
-
- // Evaluate input Jsonnet and handle any errors from Jsonnet VM.
- int error;
- char *output;
- assert(config.inputFiles.size() == 1);
-
- // Read input file.
- std::string input;
- if (!read_input(config.filenameIsCode, &config.inputFiles[0], &input)) {
- jsonnet_destroy(vm);
- return EXIT_FAILURE;
- }
-
- if (config.evalMulti) {
- output = jsonnet_evaluate_snippet_multi(
- vm, config.inputFiles[0].c_str(), input.c_str(), &error);
- } else if (config.evalStream) {
- output = jsonnet_evaluate_snippet_stream(
- vm, config.inputFiles[0].c_str(), input.c_str(), &error);
- } else {
- output = jsonnet_evaluate_snippet(
- vm, config.inputFiles[0].c_str(), input.c_str(), &error);
- }
-
- if (error) {
- std::cerr << output;
- jsonnet_realloc(vm, output, 0);
- jsonnet_destroy(vm);
- return EXIT_FAILURE;
- }
-
- // Write output JSON.
- if (config.evalMulti) {
- if (!write_multi_output_files(
- vm, output, config.evalMultiOutputDir, config.outputFile)) {
- jsonnet_destroy(vm);
- return EXIT_FAILURE;
- }
- } else if (config.evalStream) {
- if (!write_output_stream(vm, output, config.outputFile)) {
- jsonnet_destroy(vm);
- return EXIT_FAILURE;
- }
- } else {
- bool successful = write_output_file(output, config.outputFile);
- jsonnet_realloc(vm, output, 0);
- if (!successful) {
- jsonnet_destroy(vm);
- return EXIT_FAILURE;
- }
- }
-
- jsonnet_destroy(vm);
- return EXIT_SUCCESS;
-
- } catch (const std::bad_alloc &) {
- // Avoid further allocation attempts
- fputs("Internal out-of-memory error (please report this)\n", stderr);
- } catch (const std::exception &e) {
- std::cerr << "Internal error (please report this): " << e.what() << std::endl;
- } catch (...) {
- std::cerr << "An unknown exception occurred (please report this)." << std::endl;
- }
- return EXIT_FAILURE;
+ return true;
}

View File

@ -0,0 +1,76 @@
// Copyright 2020 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
//
// 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.
#include "jsonnet_helper.h" // NOLINT(build/include)
#include <cstring>
struct JsonnetVm* c_jsonnet_make(void) {
return jsonnet_make();
}
void c_jsonnet_destroy(struct JsonnetVm* vm) { return jsonnet_destroy(vm); }
char* c_jsonnet_evaluate_snippet(struct JsonnetVm* vm, const char* filename,
char* snippet, int* error) {
return jsonnet_evaluate_snippet(vm, filename, snippet, error);
}
char* c_jsonnet_evaluate_snippet_multi(struct JsonnetVm* vm,
const char* filename,
const char* snippet, int* error) {
return jsonnet_evaluate_snippet_multi(vm, filename, snippet, error);
}
char* c_jsonnet_evaluate_snippet_stream(struct JsonnetVm* vm,
const char* filename,
const char* snippet, int* error) {
return jsonnet_evaluate_snippet_stream(vm, filename, snippet, error);
}
char* c_read_input(bool filename_is_code, const char* filename) {
std::string s_filename(filename);
std::string s_input;
bool check = read_input(filename_is_code, &s_filename, &s_input);
char* c_input = strdup(s_input.c_str());
if (check) return c_input;
return nullptr;
}
void c_free_input(char* input) { free(input); }
bool c_write_output_file(const char* output, const char* output_file) {
std::string s_output_file(output_file);
return write_output_file(output, s_output_file);
}
bool c_write_multi_output_files(char* output, char* output_dir,
bool show_output_file_names) {
std::string s_output_dir(output_dir);
return write_multi_output_files(output, s_output_dir, show_output_file_names);
}
bool c_write_output_stream(char* output, char* output_file) {
std::string s_output_file(output_file);
return write_output_stream(output, s_output_file);
}
char* c_jsonnet_realloc(struct JsonnetVm* vm, char* str, size_t sz) {
return jsonnet_realloc(vm, str, sz);
}
char* c_jsonnet_fmt_snippet(struct JsonnetVm* vm, const char* filename,
const char* snippet, int* error) {
return jsonnet_fmt_snippet(vm, filename, snippet, error);
}

View File

@ -0,0 +1,66 @@
// Copyright 2020 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
//
// 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 JSONNET_HELPER_H_
#define JSONNET_HELPER_H_
extern "C" {
#include <libjsonnet.h> // NOLINT(build/include)
#include <libjsonnet_fmt.h> // NOLINT(build/include)
}
#include "jsonnet/cmd/utils.h" // NOLINT(build/include)
extern "C" struct JsonnetVm* c_jsonnet_make(void);
extern "C" void c_jsonnet_destroy(struct JsonnetVm* vm);
extern "C" char* c_jsonnet_evaluate_snippet(struct JsonnetVm* vm,
const char* filename, char* snippet,
int* error);
extern "C" char* c_jsonnet_evaluate_snippet_multi(struct JsonnetVm* vm,
const char* filename,
const char* snippet,
int* error);
extern "C" char* c_jsonnet_evaluate_snippet_stream(struct JsonnetVm* vm,
const char* filename,
const char* snippet,
int* error);
extern "C" char* c_read_input(bool filename_is_code, const char* filename);
extern "C" void c_free_input(char* input);
extern "C" bool c_write_output_file(const char* output,
const char* output_file);
extern "C" char* c_jsonnet_realloc(struct JsonnetVm* vm, char* str, size_t sz);
extern "C" bool c_write_multi_output_files(char* output, char* output_dir,
bool show_output_file_names);
bool write_multi_output_files(char* output, const std::string& output_dir,
bool show_output_file_names);
extern "C" bool c_write_output_stream(char* output, char* output_file);
bool write_output_stream(char* output, const std::string& output_file);
extern "C" char* c_jsonnet_fmt_snippet(struct JsonnetVm* vm,
const char* filename,
const char* snippet, int* error);
#endif // JSONNET_HELPER_H_

View File

@ -0,0 +1,42 @@
# Copyright 2020 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
#
# 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.
include(GoogleTest)
# We need to prepare convenient directories so the tests will be able to access them
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/tests/tests_input)
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/tests/tests_output)
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/tests/tests_expected_output)
add_custom_target(test_preparation ALL
COMMAND cp ${PROJECT_SOURCE_DIR}/examples/jsonnet_codes/* ${PROJECT_BINARY_DIR}/tests/tests_input
COMMAND cp ${PROJECT_SOURCE_DIR}/examples/jsonnet_codes_expected_output/* ${PROJECT_BINARY_DIR}/tests/tests_expected_output
)
add_executable(tests
${PROJECT_SOURCE_DIR}/headers/jsonnet_tests.h
jsonnet_tests.cc
jsonnet_tests_utils.cc
)
target_include_directories(tests PUBLIC
${PROJECT_SOURCE_DIR}/headers
)
target_link_libraries(tests
jsonnet_sapi sapi::sapi
gtest gmock gtest_main
)
gtest_discover_tests(tests)

View File

@ -0,0 +1,113 @@
// Copyright 2020 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
//
// 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.
#include "jsonnet_tests.h" // NOLINT(build/include)
namespace {
class JsonnetTest : public JsonnetTestHelper, public testing::Test {
protected:
void SetUp() override { TestSetUp(); }
void TearDown() override { TestTearDown(); }
};
// Basic test
TEST_F(JsonnetTest, SetUp_TearDown) {
ASSERT_FALSE(jsonnet_vm_was_used_);
ASSERT_FALSE(input_was_read_);
}
// One file evaluation to one file
TEST_F(JsonnetTest, One_file_no_dependencies) {
constexpr char kInputFile[] = "arith.jsonnet";
constexpr char kOutputFile[] = "arith_output";
constexpr char kOutputToRead[] = "tests_output/arith_output";
constexpr char kOutputToExpect[] = "tests_expected_output/arith.golden";
Read_input(kInputFile);
EvaluateJsonnetCode(kBase, true);
WriteOutput(kOutputFile, kBase);
std::string produced_output = ReadOutput(kOutputToRead);
std::string expected_output = ReadOutput(kOutputToExpect);
ASSERT_STREQ(produced_output.c_str(), expected_output.c_str());
}
// One file evaluating to one file, dependent on some other files
TEST_F(JsonnetTest, One_file_some_dependencies) {
constexpr char kInputFile[] = "negroni.jsonnet";
constexpr char kOutputFile[] = "negroni_output";
constexpr char kOutputToRead[] = "tests_output/negroni_output";
constexpr char kOutputToExpect[] = "tests_expected_output/negroni.golden";
ReadInput(kInputFile);
EvaluateJsonnetCode(kBase, true);
WriteOutput(kOutputFile, kBase);
const std::string produced_output = Read_output(kOutputToRead);
const std::string expected_output = Read_output(kOutputToExpect);
ASSERT_STREQ(produced_output.c_str(), expected_output.c_str());
}
// One file evaluating to two files
TEST_F(JsonnetTest, Multiple_files) {
constexpr char kInputFile[] = "multiple_files_example.jsonnet";
constexpr char kOutputFile[] = "";
constexpr char kOutputToRead1[] = "tests_output/first_file.json";
constexpr char kOutputToRead2[] = "tests_output/second_file.json";
constexpr char kOutputToExpect1[] = "tests_expected_output/first_file.json";
constexpr char kOutputToExpect2[] = "tests_expected_output/second_file.json";
ReadInput(kInputFile);
EvaluateJsonnetCode(kMultipleFiles, true);
WriteOutput(kOutputFile, kMultipleFiles);
const std::string produced_output_1 = ReadOutput(kOutputToRead1);
const std::string produced_output_2 = ReadOutput(kOutputToRead2);
const std::string expected_output_1 = ReadOutput(kOutputToExpect1);
const std::string expected_output_2 = ReadOutput(kOutputToExpect2);
ASSERT_STREQ(produced_output_1.c_str(), expected_output_1.c_str());
ASSERT_STREQ(produced_output_2.c_str(), expected_output_2.c_str());
}
// One file evaluating to yaml stream format
TEST_F(JsonnetTest, Yaml_stream) {
constexpr char kInputFile[] = "yaml_stream_example.jsonnet";
constexpr char kOutputFile[] = "yaml_stream_example.yaml";
constexpr char kOutputToRead[] = "tests_output/yaml_stream_example.yaml";
constexpr char kOutputToExpect[] =
"tests_expected_output/yaml_stream_example.yaml";
ReadInput(kInputFile);
EvaluateJsonnetCode(kYamlStream, true);
WriteOutput(kOutputFile, kYamlStream);
const std::string produced_output = ReadOutput(kOutputToRead);
const std::string expected_output = ReadOutput(kOutputToExpect);
ASSERT_STREQ(produced_output.c_str(), expected_output.c_str());
}
// One file depended on some other files not accessible by the sandbox
TEST_F(JsonnetTest, Bad_evaluation) {
constexpr char kInputFile[] = "imports.jsonnet";
ReadInput(kInputFile);
EvaluateJsonnetCode(kBase, false);
}
} // namespace

View File

@ -0,0 +1,160 @@
// Copyright 2020 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
//
// 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.
#include "jsonnet_tests.h" // NOLINT(build/include)
// Prepares what is needed to perform a test.
void JsonnetTestHelper::TestSetUp() {
// Get paths to where input and output is stored.
char buffer[256];
int error = readlink("/proc/self/exe", buffer, 256);
ASSERT_GE(error, 0);
std::pair<absl::string_view, absl::string_view> parts_of_path =
sandbox2::file::SplitPath(buffer);
absl::string_view binary_path = parts_of_path.first;
std::string input_path =
sandbox2::file::JoinPath(binary_path, "tests_input", "dummy_input");
std::string output_path =
sandbox2::file::JoinPath(binary_path, "tests_output", "dummy_input");
// Set up sandbox and api.
sandbox_ = absl::make_unique<JsonnetBaseSandbox>(input_path, output_path);
ASSERT_THAT(sandbox_->Init(), sapi::IsOk());
api_ = absl::make_unique<JsonnetApi>(sandbox_.get());
// Initialize library's main structure.
SAPI_ASSERT_OK_AND_ASSIGN(JsonnetVm * vm_ptr, api_->c_jsonnet_make());
vm_ = absl::make_unique<sapi::v::RemotePtr>(vm_ptr);
jsonnet_vm_was_used_ = false;
input_was_read_ = false;
}
// Cleans up after a test.
void JsonnetTestHelper::TestTearDown() {
if (jsonnet_vm_was_used_) {
SAPI_ASSERT_OK_AND_ASSIGN(char* result,
api_->c_jsonnet_realloc(vm_.get(), output_.get(), 0));
}
ASSERT_THAT(api_->c_jsonnet_destroy(vm_.get()), sapi::IsOk());
if (input_was_read_) {
ASSERT_THAT(api_->c_free_input(input_.get()), sapi::IsOk());
}
}
// Reads input from file.
void JsonnetTestHelper::ReadInput(const char* filename) {
std::string in_file_in_sandboxee(std::string("/input/") +
basename(const_cast<char*>(&filename[0])));
input_filename_in_sandboxee_ = std::move(in_file_in_sandboxee);
sapi::v::ConstCStr in_file_var(input_filename_in_sandboxee_.c_str());
SAPI_ASSERT_OK_AND_ASSIGN(char* input_ptr,
api_->c_read_input(0, in_file_var.PtrBefore()));
input_ = absl::make_unique<sapi::v::RemotePtr>(input_ptr);
input_was_read_ = true;
}
// Evaluates jsonnet code.
void JsonnetTestHelper::Evaluate_jsonnet_code(Evaluation type,
bool expected_correct) {
sapi::v::ConstCStr in_file_var(input_filename_in_sandboxee_.c_str());
sapi::v::Int error;
char* output_ptr;
switch (type) {
case kBase: {
SAPI_ASSERT_OK_AND_ASSIGN(output_ptr, api_->c_jsonnet_evaluate_snippet(
vm_.get(), in_file_var.PtrBefore(),
input_.get(), error.PtrAfter()));
break;
}
case kMultipleFiles: {
SAPI_ASSERT_OK_AND_ASSIGN(output_ptr, api_->c_jsonnet_evaluate_snippet_multi(
vm_.get(), in_file_var.PtrBefore(),
input_.get(), error.PtrAfter()));
break;
}
case kYamlStream: {
SAPI_ASSERT_OK_AND_ASSIGN(output_ptr, api_->c_jsonnet_evaluate_snippet_stream(
vm_.get(), in_file_var.PtrBefore(),
input_.get(), error.PtrAfter()));
break;
}
}
if (expected_correct) {
ASSERT_THAT(error.GetValue(), testing::Eq(0));
} else {
ASSERT_THAT(error.GetValue(), testing::Eq(1));
}
output_ = absl::make_unique<sapi::v::RemotePtr>(output_ptr);
jsonnet_vm_was_used_ = true;
}
// Writes output to file.
void JsonnetTestHelper::WriteOutput(const char* filename_or_directory,
Evaluation type) {
bool success;
switch (type) {
case kBase: {
std::string out_file_in_sandboxee(
std::string("/output/") +
basename(const_cast<char*>(&filename_or_directory[0])));
sapi::v::ConstCStr out_file_var(out_file_in_sandboxee.c_str());
SAPI_ASSERT_OK_AND_ASSIGN(
success,
api_->c_write_output_file(output_.get(), out_file_var.PtrBefore()));
break;
}
case kMultipleFiles: {
std::string out_file_in_sandboxee(std::string("/output/"));
sapi::v::ConstCStr out_file_var(out_file_in_sandboxee.c_str());
SAPI_ASSERT_OK_AND_ASSIGN(success,
api_->c_write_multi_output_files(
output_.get(), out_file_var.PtrBefore(), false));
break;
}
case kYamlStream: {
std::string out_file_in_sandboxee(
std::string("/output/") +
basename(const_cast<char*>(&filename_or_directory[0])));
sapi::v::ConstCStr out_file_var(out_file_in_sandboxee.c_str());
SAPI_ASSERT_OK_AND_ASSIGN(
success,
api_->c_write_output_stream(output_.get(), out_file_var.PtrBefore()));
break;
}
}
ASSERT_THAT(success, testing::Eq(true));
}
// Reads the output written to a file by library function / expected output
std::string JsonnetTestHelper::ReadOutput(const char* filename) {
std::ifstream input_stream(filename);
std::string contents((std::istreambuf_iterator<char>(input_stream)),
std::istreambuf_iterator<char>());
return contents;
}

View File

@ -0,0 +1,3 @@
build/
.clang-format
.cache

View File

@ -0,0 +1,80 @@
# Copyright 2020 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
#
# 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.
cmake_minimum_required(VERSION 3.16)
project(lodepng_sapi CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED 17)
# Apply the patches to the header file.
add_custom_command(
OUTPUT "${PROJECT_BINARY_DIR}/lodepng/lodepng.h" "${PROJECT_BINARY_DIR}/lodepng/lodepng.cpp"
COMMENT "Applying patch to header file."
COMMAND cp -r "${PROJECT_SOURCE_DIR}/lodepng" "${PROJECT_BINARY_DIR}/"
COMMAND cp "${PROJECT_SOURCE_DIR}/patches/header.patch" "${PROJECT_BINARY_DIR}/lodepng/"
COMMAND cd "${PROJECT_BINARY_DIR}/lodepng" && patch < header.patch
)
# Build static library.
add_library(lodepng STATIC
"${PROJECT_BINARY_DIR}/lodepng/lodepng.cpp"
"${PROJECT_BINARY_DIR}/lodepng/lodepng.h"
)
target_include_directories(lodepng PUBLIC "${PROJECT_BINARY_DIR}/lodepng")
# Build SAPI library
set(SAPI_ROOT "" CACHE PATH "Path to the Sandboxed API source tree")
add_subdirectory("${SAPI_ROOT}"
"${CMAKE_BINARY_DIR}/sandboxed-api-build"
EXCLUDE_FROM_ALL
)
add_sapi_library(
lodepng_sapi
FUNCTIONS
lodepng_decode_memory
lodepng_decode32
lodepng_decode24
lodepng_decode_file
lodepng_decode32_file
lodepng_decode24_file
lodepng_encode_memory
lodepng_encode32
lodepng_encode24
lodepng_encode_file
lodepng_encode32_file
lodepng_encode24_file
lodepng_save_file
lodepng_load_file
INPUTS "${PROJECT_BINARY_DIR}/lodepng/lodepng.h"
LIBRARY lodepng
LIBRARY_NAME Lodepng
NAMESPACE ""
)
target_include_directories(lodepng_sapi INTERFACE
"${PROJECT_BINARY_DIR}" # To find the generated SAPI header
)
add_subdirectory(examples)

View File

@ -0,0 +1,42 @@
# LodePNG Sandboxed API
Sandboxed version of the [LodePNG](https://github.com/lvandeve/lodepng) library, using [Sandboxed API](https://github.com/google/sandboxed-api)
## Details
With Sandboxed API, many of the library's functions can be sandboxed. However, they need the `extern "C"` keyword defined so that name mangling does not happen, which is why a patch that adds it is used. The only differences are found in the header file. An alternative to this is to define another library that wraps every needed function, specifying the required keyword.
Even if many of the functions from the library can be sandboxed, there are some that are not supported (those which have `std::vector` parameters, overloaded functions etc.). If you really need these functions, a solution is to implement a custom library that wraps around these functions in order to make them compatible.
## Patches
In the **patches** folder there is a patch file that adds `extern "C"` to the required functions in the header file in order to sandbox them. This patch is applied automatically during the build phase.
## Build
First, run `git submodule update --init --recursive` to update submodules.
After this, run the following commands:
`mkdir -p build && cd build`
`cmake .. -G Ninja`
`cmake --build .`
The example binary files can be found in `build/examples`.
## Examples
The code found in the **examples** folder features a basic use case of the library. An image is generated, encoded into a file and then decoded to check that the values are the same. The encoding part was based on [this example](https://github.com/lvandeve/lodepng/blob/master/examples/example_encode.c) while decoding was based on [this](https://github.com/lvandeve/lodepng/blob/master/examples/example_decode.c).
This example code is structured as:
- `main_unsandboxed.cc` - unsandboxed example
- `main_sandboxed.cc` - sandboxed version of the example
- `main_unit_test.cc` - tests(using [Google Test](https://github.com/google/googletest)).
On top of those files, there are other files used by all three of the examples:
- `sandbox.h` - custom sandbox policy
- `helpers.h` and `helpers.cc` - constants and functions used in the main files.
The executables generated from these files will create a temporary directory in the current working path. Inside that directory the two generated **png** files will be created. At the end, the directory is deleted. If those programs do not stop midway or return a failure code, then everything works fine.

View File

@ -0,0 +1,70 @@
# Copyright 2020 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
#
# 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.
# Build the unsandboxed main
add_executable(lodepng_unsandboxed
main_unsandboxed.cc
helpers.cc
)
target_link_libraries(lodepng_unsandboxed PRIVATE
lodepng
sapi::sapi
sandbox2::temp_file
sandbox2::file_base
sandbox2::fileops
glog::glog
)
# Build the sandboxed main
add_executable(lodepng_sandboxed
main_sandboxed.cc
sandbox.h
helpers.cc
)
target_link_libraries(lodepng_sandboxed PRIVATE
lodepng_sapi
sapi::sapi
sandbox2::temp_file
sandbox2::fileops
sapi::vars
sapi::status
glog::glog
)
# Build the unit tests
include(GoogleTest)
enable_testing()
add_executable(main_unit_test
main_unit_test.cc
helpers.cc
)
target_link_libraries(main_unit_test PRIVATE
lodepng_sapi
absl::memory
absl::strings
absl::time
glog::glog
sapi::flags
sapi::sapi
sandbox2::temp_file
sandbox2::fileops
sapi::status
sapi::test_main
sapi::vars
)
gtest_discover_tests(main_unit_test)

View File

@ -0,0 +1,43 @@
// Copyright 2020 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
//
// 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.
#include "helpers.h" // NOLINT(build/include)
#include "sandboxed_api/sandbox2/util/temp_file.h"
std::vector<uint8_t> GenerateValues() {
std::vector<uint8_t> image;
image.reserve(kImgLen);
for (int y = 0; y < kHeight; ++y) {
for (int x = 0; x < kWidth; ++x) {
image.push_back(255 * !(x & y));
image.push_back(x ^ y);
image.push_back(x | y);
image.push_back(255);
}
}
return image;
}
std::string CreateTempDirAtCWD() {
std::string cwd = sandbox2::file_util::fileops::GetCWD();
CHECK(!cwd.empty()) << "Could not get current working directory";
cwd.append("/");
absl::StatusOr<std::string> result = sandbox2::CreateTempDir(cwd);
CHECK(result.ok()) << "Could not create temporary directory";
return result.value();
}

View File

@ -0,0 +1,39 @@
// Copyright 2020 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
//
// 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 LODEPNG_EXAMPLES_HELPERS_H_
#define LODEPNG_EXAMPLES_HELPERS_H_
#include <cstddef>
#include <cstdint>
#include <glog/logging.h>
#include "sandboxed_api/sandbox2/util/fileops.h"
#include "sandboxed_api/sandbox2/util/temp_file.h"
inline constexpr size_t kWidth = 512;
inline constexpr size_t kHeight = 512;
inline constexpr size_t kImgLen = kWidth * kHeight * 4;
// Returns a vector that contains values used for testing.
// This part of code is taken from
// https://github.com/lvandeve/lodepng/blob/master/examples/example_encode.c#L96-L104.
// The generated image contains square fractals.
std::vector<uint8_t> GenerateValues();
// Creates a temporary directory in the current working directory and returns
// the path.
std::string CreateTempDirAtCWD();
#endif // LODEPNG_EXAMPLES_HELPERS_H_

View File

@ -0,0 +1,193 @@
// Copyright 2020 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
//
// 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.
#include <iostream>
#include <glog/logging.h>
#include "helpers.h" // NOLINT(build/include)
#include "sandbox.h" // NOLINT(build/include)
void EncodeDecodeOneStep(SapiLodepngSandbox& sandbox, LodepngApi& api) {
// Generate the values.
std::vector<uint8_t> image = GenerateValues();
// Encode the image.
sapi::v::Array<uint8_t> sapi_image(kImgLen);
CHECK(std::copy(image.begin(), image.end(), sapi_image.GetData()))
<< "Could not copy values";
sapi::v::ConstCStr sapi_filename("/output/out_generated1.png");
absl::StatusOr<unsigned int> result = api.lodepng_encode32_file(
sapi_filename.PtrBefore(), sapi_image.PtrBefore(), kWidth, kHeight);
CHECK(result.ok()) << "encode32_file call failed";
CHECK(!result.value()) << "Unexpected result from encode32_file call";
// After the image has been encoded, decode it to check that the
// pixel values are the same.
sapi::v::UInt sapi_width, sapi_height;
sapi::v::IntBase<uint8_t*> sapi_image_ptr(0);
result = api.lodepng_decode32_file(
sapi_image_ptr.PtrBoth(), sapi_width.PtrBoth(), sapi_height.PtrBoth(),
sapi_filename.PtrBefore());
CHECK(result.ok()) << "decode32_file call failes";
CHECK(!result.value()) << "Unexpected result from decode32_file call";
CHECK(sapi_width.GetValue() == kWidth) << "Widths differ";
CHECK(sapi_height.GetValue() == kHeight) << "Heights differ";
// The pixels have been allocated inside the sandboxed process
// memory, so we need to transfer them to this process.
// Transferring the memory has the following steps:
// 1) define an array with the required length.
// 2) set the remote pointer for the array to specify where the memory
// that will be transferred is located.
// 3) transfer the memory to this process (this step is why we need
// the pointer and the length).
sapi::v::Array<uint8_t> sapi_pixels(kImgLen);
sapi_pixels.SetRemote(sapi_image_ptr.GetValue());
CHECK(sandbox.TransferFromSandboxee(&sapi_pixels).ok())
<< "Error during transfer from sandboxee";
// Now, we can compare the values.
CHECK(absl::equal(image.begin(), image.end(), sapi_pixels.GetData(),
sapi_pixels.GetData() + kImgLen))
<< "Values differ";
// Free the memory allocated inside the sandbox.
CHECK(sandbox.GetRpcChannel()->Free(sapi_image_ptr.GetValue()).ok())
<< "Could not free memory inside sandboxed process";
}
void EncodeDecodeTwoSteps(SapiLodepngSandbox& sandbox, LodepngApi& api) {
// Generate the values.
std::vector<uint8_t> image = GenerateValues();
// Encode the image into memory first.
sapi::v::Array<uint8_t> sapi_image(kImgLen);
CHECK(std::copy(image.begin(), image.end(), sapi_image.GetData()))
<< "Could not copy values";
sapi::v::ConstCStr sapi_filename("/output/out_generated2.png");
sapi::v::ULLong sapi_pngsize;
sapi::v::IntBase<uint8_t*> sapi_png_ptr(0);
// Encode it into memory.
absl::StatusOr<unsigned int> result =
api.lodepng_encode32(sapi_png_ptr.PtrBoth(), sapi_pngsize.PtrBoth(),
sapi_image.PtrBefore(), kWidth, kHeight);
CHECK(result.ok()) << "encode32 call failed";
CHECK(!result.value()) << "Unexpected result from encode32 call";
// The new array (pointed to by sapi_png_ptr) is allocated
// inside the sandboxed process so we need to transfer it to this
// process.
sapi::v::Array<uint8_t> sapi_png_array(sapi_pngsize.GetValue());
sapi_png_array.SetRemote(sapi_png_ptr.GetValue());
CHECK(sandbox.TransferFromSandboxee(&sapi_png_array).ok())
<< "Error during transfer from sandboxee";
// Write the image into the file (from memory).
result =
api.lodepng_save_file(sapi_png_array.PtrBefore(), sapi_pngsize.GetValue(),
sapi_filename.PtrBefore());
CHECK(result.ok()) << "save_file call failed";
CHECK(!result.value()) << "Unexpected result from save_file call";
// Now, decode the image using the 2 steps in order to compare the values.
sapi::v::UInt sapi_width, sapi_height;
sapi::v::IntBase<uint8_t*> sapi_png_ptr2(0);
sapi::v::ULLong sapi_pngsize2;
// Load the file in memory.
result =
api.lodepng_load_file(sapi_png_ptr2.PtrBoth(), sapi_pngsize2.PtrBoth(),
sapi_filename.PtrBefore());
CHECK(result.ok()) << "load_file call failed";
CHECK(!result.value()) << "Unexpected result from load_file call";
CHECK(sapi_pngsize.GetValue() == sapi_pngsize2.GetValue())
<< "Png sizes differ";
// Transfer the png array.
sapi::v::Array<uint8_t> sapi_png_array2(sapi_pngsize2.GetValue());
sapi_png_array2.SetRemote(sapi_png_ptr2.GetValue());
CHECK(sandbox.TransferFromSandboxee(&sapi_png_array2).ok())
<< "Error during transfer from sandboxee";
// After the file is loaded, decode it so we have access to the values
// directly.
sapi::v::IntBase<uint8_t*> sapi_png_ptr3(0);
result = api.lodepng_decode32(
sapi_png_ptr3.PtrBoth(), sapi_width.PtrBoth(), sapi_height.PtrBoth(),
sapi_png_array2.PtrBefore(), sapi_pngsize2.GetValue());
CHECK(result.ok()) << "decode32 call failed";
CHECK(!result.value()) << "Unexpected result from decode32 call";
CHECK(sapi_width.GetValue() == kWidth) << "Widths differ";
CHECK(sapi_height.GetValue() == kHeight) << "Heights differ";
// Transfer the pixels so they can be used here.
sapi::v::Array<uint8_t> sapi_pixels(kImgLen);
sapi_pixels.SetRemote(sapi_png_ptr3.GetValue());
CHECK(sandbox.TransferFromSandboxee(&sapi_pixels).ok())
<< "Error during transfer from sandboxee";
// Compare the values.
CHECK(absl::equal(image.begin(), image.end(), sapi_pixels.GetData(),
sapi_pixels.GetData() + kImgLen))
<< "Values differ";
// Free the memory allocated inside the sandbox.
CHECK(sandbox.GetRpcChannel()->Free(sapi_png_ptr.GetValue()).ok())
<< "Could not free memory inside sandboxed process";
CHECK(sandbox.GetRpcChannel()->Free(sapi_png_ptr2.GetValue()).ok())
<< "Could not free memory inside sandboxed process";
CHECK(sandbox.GetRpcChannel()->Free(sapi_png_ptr3.GetValue()).ok())
<< "Could not free memory inside sandboxed process";
}
int main(int argc, char* argv[]) {
google::InitGoogleLogging(argv[0]);
const std::string images_path = CreateTempDirAtCWD();
CHECK(sandbox2::file_util::fileops::Exists(images_path, false))
<< "Temporary directory does not exist";
SapiLodepngSandbox sandbox(images_path);
CHECK(sandbox.Init().ok()) << "Error during sandbox initialization";
LodepngApi api(&sandbox);
EncodeDecodeOneStep(sandbox, api);
EncodeDecodeTwoSteps(sandbox, api);
if (sandbox2::file_util::fileops::DeleteRecursively(images_path)) {
LOG(WARNING) << "Temporary folder could not be deleted";
}
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,213 @@
// Copyright 2020 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
//
// 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.
#include "helpers.h" // NOLINT(build/include)
#include "sandbox.h" // NOLINT(build/include)
#include "gtest/gtest.h"
#include "sandboxed_api/util/status_matchers.h"
using ::sapi::IsOk;
using ::testing::Eq;
using ::testing::IsTrue;
using ::testing::NotNull;
namespace {
TEST(HelpersTest, CreateTempDirAtCWD) {
const std::string images_path = CreateTempDirAtCWD();
ASSERT_THAT(sandbox2::file_util::fileops::Exists(images_path, false),
IsTrue())
<< "Temporary directory does not exist";
EXPECT_THAT(sandbox2::file_util::fileops::DeleteRecursively(images_path),
IsTrue())
<< "Temporary directory could not be deleted";
}
TEST(HelpersTest, GenerateValues) {
EXPECT_THAT(GenerateValues().size(), Eq(kImgLen));
}
TEST(LodePngTest, Init) {
const std::string images_path = CreateTempDirAtCWD();
ASSERT_THAT(sandbox2::file_util::fileops::Exists(images_path, false),
IsTrue())
<< "Temporary directory does not exist";
SapiLodepngSandbox sandbox(images_path);
ASSERT_THAT(sandbox.Init(), IsOk()) << "Error during sandbox init";
EXPECT_THAT(sandbox2::file_util::fileops::DeleteRecursively(images_path),
IsTrue())
<< "Temporary directory could not be deleted";
}
// Generate an image, encode it, decode it and compare the pixels with the
// initial values.
TEST(LodePngTest, EncodeDecodeOneStep) {
const std::string images_path = CreateTempDirAtCWD();
ASSERT_THAT(sandbox2::file_util::fileops::Exists(images_path, false),
IsTrue())
<< "Temporary directory does not exist";
SapiLodepngSandbox sandbox(images_path);
ASSERT_THAT(sandbox.Init(), IsOk()) << "Error during sandbox initialization";
LodepngApi api(&sandbox);
std::vector<uint8_t> image = GenerateValues();
sapi::v::Array<uint8_t> sapi_image(kImgLen);
EXPECT_THAT(std::copy(image.begin(), image.end(), sapi_image.GetData()),
IsTrue())
<< "Could not copy values";
sapi::v::ConstCStr sapi_filename("/output/out_generated1.png");
SAPI_ASSERT_OK_AND_ASSIGN(
unsigned int result,
api.lodepng_encode32_file(sapi_filename.PtrBefore(),
sapi_image.PtrBefore(), kWidth, kHeight));
ASSERT_THAT(result, Eq(0)) << "Unexpected result from encode32_file call";
sapi::v::UInt sapi_width, sapi_height;
sapi::v::IntBase<uint8_t*> sapi_image_ptr(0);
SAPI_ASSERT_OK_AND_ASSIGN(result,
api.lodepng_decode32_file(
sapi_image_ptr.PtrBoth(), sapi_width.PtrBoth(),
sapi_height.PtrBoth(), sapi_filename.PtrBefore()));
ASSERT_THAT(result, Eq(0)) << "Unexpected result from decode32_file call";
EXPECT_THAT(sapi_width.GetValue(), Eq(kWidth)) << "Widths differ";
EXPECT_THAT(sapi_height.GetValue(), Eq(kHeight)) << "Heights differ";
sapi::v::Array<uint8_t> sapi_pixels(kImgLen);
sapi_pixels.SetRemote(sapi_image_ptr.GetValue());
ASSERT_THAT(sandbox.TransferFromSandboxee(&sapi_pixels), IsOk())
<< "Error during transfer from sandboxee";
EXPECT_THAT(absl::equal(image.begin(), image.end(), sapi_pixels.GetData(),
sapi_pixels.GetData() + kImgLen),
IsTrue())
<< "Values differ";
EXPECT_THAT(sandbox.GetRpcChannel()->Free(sapi_image_ptr.GetValue()), IsOk())
<< "Could not free memory inside sandboxed process";
EXPECT_THAT(sandbox2::file_util::fileops::DeleteRecursively(images_path),
IsTrue())
<< "Temporary directory could not be deleted";
}
// Similar to the previous test, only that we use encoding by saving the data in
// memory and then writing it to the file and decoding by first decoding in
// memory and then getting the actual pixel values.
TEST(LodePngTest, EncodeDecodeTwoSteps) {
const std::string images_path = CreateTempDirAtCWD();
ASSERT_THAT(sandbox2::file_util::fileops::Exists(images_path, false),
IsTrue())
<< "Temporary directory does not exist";
SapiLodepngSandbox sandbox(images_path);
ASSERT_THAT(sandbox.Init(), IsOk()) << "Error during sandbox init";
LodepngApi api(&sandbox);
std::vector<uint8_t> image = GenerateValues();
sapi::v::Array<uint8_t> sapi_image(kImgLen);
EXPECT_THAT(std::copy(image.begin(), image.end(), sapi_image.GetData()),
IsTrue())
<< "Could not copy values";
sapi::v::ConstCStr sapi_filename("/output/out_generated2.png");
sapi::v::ULLong sapi_pngsize;
sapi::v::IntBase<uint8_t*> sapi_png_ptr(0);
SAPI_ASSERT_OK_AND_ASSIGN(
unsigned int result,
api.lodepng_encode32(sapi_png_ptr.PtrBoth(), sapi_pngsize.PtrBoth(),
sapi_image.PtrBefore(), kWidth, kHeight));
ASSERT_THAT(result, Eq(0)) << "Unexpected result from encode32 call";
sapi::v::Array<uint8_t> sapi_png_array(sapi_pngsize.GetValue());
sapi_png_array.SetRemote(sapi_png_ptr.GetValue());
ASSERT_THAT(sandbox.TransferFromSandboxee(&sapi_png_array), IsOk())
<< "Error during transfer from sandboxee";
SAPI_ASSERT_OK_AND_ASSIGN(
result,
api.lodepng_save_file(sapi_png_array.PtrBefore(), sapi_pngsize.GetValue(),
sapi_filename.PtrBefore()));
ASSERT_THAT(result, Eq(0)) << "Unexpected result from save_file call";
sapi::v::UInt sapi_width, sapi_height;
sapi::v::IntBase<uint8_t*> sapi_png_ptr2(0);
sapi::v::ULLong sapi_pngsize2;
SAPI_ASSERT_OK_AND_ASSIGN(
result,
api.lodepng_load_file(sapi_png_ptr2.PtrBoth(), sapi_pngsize2.PtrBoth(),
sapi_filename.PtrBefore()));
ASSERT_THAT(result, Eq(0)) << "Unexpected result from load_file call";
EXPECT_THAT(sapi_pngsize.GetValue(), Eq(sapi_pngsize2.GetValue()))
<< "Png sizes differ";
sapi::v::Array<uint8_t> sapi_png_array2(sapi_pngsize2.GetValue());
sapi_png_array2.SetRemote(sapi_png_ptr2.GetValue());
ASSERT_THAT(sandbox.TransferFromSandboxee(&sapi_png_array2), IsOk())
<< "Error during transfer from sandboxee";
sapi::v::IntBase<uint8_t*> sapi_png_ptr3(0);
SAPI_ASSERT_OK_AND_ASSIGN(
result,
api.lodepng_decode32(sapi_png_ptr3.PtrBoth(), sapi_width.PtrBoth(),
sapi_height.PtrBoth(), sapi_png_array2.PtrBefore(),
sapi_pngsize2.GetValue()));
ASSERT_THAT(result, Eq(0)) << "Unexpected result from decode32 call";
EXPECT_THAT(sapi_width.GetValue(), Eq(kWidth)) << "Widths differ";
EXPECT_THAT(sapi_height.GetValue(), Eq(kHeight)) << "Heights differ";
sapi::v::Array<uint8_t> sapi_pixels(kImgLen);
sapi_pixels.SetRemote(sapi_png_ptr3.GetValue());
ASSERT_THAT(sandbox.TransferFromSandboxee(&sapi_pixels), IsOk())
<< "Error during transfer from sandboxee";
EXPECT_THAT(absl::equal(image.begin(), image.end(), sapi_pixels.GetData(),
sapi_pixels.GetData() + kImgLen),
IsTrue())
<< "Values differ";
EXPECT_THAT(sandbox.GetRpcChannel()->Free(sapi_png_ptr.GetValue()), IsOk());
EXPECT_THAT(sandbox.GetRpcChannel()->Free(sapi_png_ptr2.GetValue()), IsOk());
EXPECT_THAT(sandbox.GetRpcChannel()->Free(sapi_png_ptr3.GetValue()), IsOk());
EXPECT_THAT(sandbox2::file_util::fileops::DeleteRecursively(images_path),
IsTrue())
<< "Temporary directory could not be deleted";
}
} // namespace

View File

@ -0,0 +1,116 @@
// Copyright 2020 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
//
// 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.
#include <iostream>
#include <glog/logging.h>
#include "helpers.h" // NOLINT(build/include)
#include "lodepng.h" // NOLINT(build/include)
#include "sandboxed_api/sandbox2/util/fileops.h"
#include "sandboxed_api/sandbox2/util/path.h"
void EncodeDecodeOneStep(const std::string& images_path) {
// Generate the values.
std::vector<uint8_t> image = GenerateValues();
// Encode the image.
const std::string filename =
sandbox2::file::JoinPath(images_path, "/out_generated1.png");
unsigned int result =
lodepng_encode32_file(filename.c_str(), image.data(), kWidth, kHeight);
CHECK(!result) << "Unexpected result from encode32_file call";
// After the image has been encoded, decode it to check that the
// pixel values are the same.
unsigned int width, height;
uint8_t* image2 = 0;
result = lodepng_decode32_file(&image2, &width, &height, filename.c_str());
CHECK(!result) << "Unexpected result from decode32_file call";
CHECK(width == kWidth) << "Widths differ";
CHECK(height == kHeight) << "Heights differ";
// Now, we can compare the values.
CHECK(absl::equal(image.begin(), image.end(), image2, image2 + kImgLen))
<< "Values differ";
free(image2);
}
void EncodeDecodeTwoSteps(const std::string& images_path) {
// Generate the values.
std::vector<uint8_t> image = GenerateValues();
// Encode the image into memory first.
const std::string filename =
sandbox2::file::JoinPath(images_path, "/out_generated2.png");
uint8_t* png;
size_t pngsize;
unsigned int result =
lodepng_encode32(&png, &pngsize, image.data(), kWidth, kHeight);
CHECK(!result) << "Unexpected result from encode32 call";
// Write the image into the file (from memory).
result = lodepng_save_file(png, pngsize, filename.c_str());
CHECK(!result) << "Unexpected result from save_file call";
// Now, decode the image using the 2 steps in order to compare the values.
unsigned int width, height;
uint8_t* png2;
size_t pngsize2;
// Load the file in memory.
result = lodepng_load_file(&png2, &pngsize2, filename.c_str());
CHECK(!result) << "Unexpected result from load_file call";
CHECK(pngsize == pngsize2) << "Png sizes differ";
uint8_t* image2;
result = lodepng_decode32(&image2, &width, &height, png2, pngsize2);
CHECK(!result) << "Unexpected result from decode32 call";
CHECK(width == kWidth) << "Widths differ";
CHECK(height == kHeight) << "Heights differ";
// Compare the values.
CHECK(absl::equal(image.begin(), image.end(), image2, image2 + kImgLen))
<< "Values differ";
free(png);
free(png2);
free(image2);
}
int main(int argc, char* argv[]) {
google::InitGoogleLogging(argv[0]);
const std::string images_path = CreateTempDirAtCWD();
CHECK(sandbox2::file_util::fileops::Exists(images_path, false))
<< "Temporary directory does not exist";
EncodeDecodeOneStep(images_path);
EncodeDecodeTwoSteps(images_path);
if (sandbox2::file_util::fileops::DeleteRecursively(images_path)) {
LOG(WARNING) << "Temporary folder could not be deleted";
}
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,49 @@
// Copyright 2020 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
//
// 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 LODEPNG_EXAMPLES_SANDBOX_H_
#define LODEPNG_EXAMPLES_SANDBOX_H_
#include <syscall.h>
#include "lodepng_sapi.sapi.h" // NOLINT(build/include)
class SapiLodepngSandbox : public LodepngSandbox {
public:
explicit SapiLodepngSandbox(const std::string& images_path)
: images_path_(images_path) {}
private:
std::unique_ptr<sandbox2::Policy> ModifyPolicy(
sandbox2::PolicyBuilder*) override {
return sandbox2::PolicyBuilder()
.AllowRead()
.AllowWrite()
.AllowOpen()
.AllowSystemMalloc()
.AllowExit()
.AllowStat()
.AddDirectoryAt(images_path_, "/output/", /*is_ro=*/false)
.AllowSyscalls({
__NR_futex,
__NR_lseek,
__NR_close,
})
.BuildOrDie();
}
const std::string images_path_;
};
#endif // LODEPNG_EXAMPLES_SANDBOX__

View File

@ -0,0 +1,86 @@
--- lodepng.h 2020-09-11 08:41:22.280259945 +0000
+++ lodepng2.h 2020-09-11 08:45:17.134266042 +0000
@@ -89,6 +89,8 @@
#endif
#endif
+
+
#ifdef LODEPNG_COMPILE_CPP
#include <vector>
#include <string>
@@ -126,6 +128,9 @@
bitdepth: the desired bit depth for the raw output image. See explanation on PNG color types.
Return value: LodePNG error code (0 means no error).
*/
+
+extern "C" {
+
unsigned lodepng_decode_memory(unsigned char** out, unsigned* w, unsigned* h,
const unsigned char* in, size_t insize,
LodePNGColorType colortype, unsigned bitdepth);
@@ -154,10 +159,12 @@
/*Same as lodepng_decode_file, but always decodes to 24-bit RGB raw image.*/
unsigned lodepng_decode24_file(unsigned char** out, unsigned* w, unsigned* h,
const char* filename);
+
#endif /*LODEPNG_COMPILE_DISK*/
#endif /*LODEPNG_COMPILE_DECODER*/
+
#ifdef LODEPNG_COMPILE_ENCODER
/*
Converts raw pixel data into a PNG image in memory. The colortype and bitdepth
@@ -204,6 +211,9 @@
/*Same as lodepng_encode_file, but always encodes from 24-bit RGB raw image.*/
unsigned lodepng_encode24_file(const char* filename,
const unsigned char* image, unsigned w, unsigned h);
+
+}
+
#endif /*LODEPNG_COMPILE_DISK*/
#endif /*LODEPNG_COMPILE_ENCODER*/
@@ -219,6 +229,8 @@
unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h,
const std::vector<unsigned char>& in,
LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8);
+
+
#ifdef LODEPNG_COMPILE_DISK
/*
Converts PNG file from disk to raw pixel data in memory.
@@ -251,6 +263,7 @@
unsigned encode(const std::string& filename,
const std::vector<unsigned char>& in, unsigned w, unsigned h,
LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8);
+
#endif /* LODEPNG_COMPILE_DISK */
#endif /* LODEPNG_COMPILE_ENCODER */
} /* namespace lodepng */
@@ -318,6 +331,7 @@
extern const LodePNGCompressSettings lodepng_default_compress_settings;
void lodepng_compress_settings_init(LodePNGCompressSettings* settings);
+
#endif /*LODEPNG_COMPILE_ENCODER*/
#ifdef LODEPNG_COMPILE_PNG
@@ -943,6 +957,8 @@
#endif /*LODEPNG_COMPILE_ZLIB*/
#ifdef LODEPNG_COMPILE_DISK
+
+extern "C" {
/*
Load a file from disk into buffer. The function allocates the out buffer, and
after usage you should free it.
@@ -962,6 +978,7 @@
return value: error code (0 means ok)
*/
unsigned lodepng_save_file(const unsigned char* buffer, size_t buffersize, const char* filename);
+}
#endif /*LODEPNG_COMPILE_DISK*/
#ifdef LODEPNG_COMPILE_CPP

View File

@ -73,12 +73,12 @@ int main(int argc, char* argv[]) {
sapi::v::ConstCStr in_file_v(in_file.c_str());
// Initialize library's main data-holders.
sapi::StatusOr<opj_stream_t*> stream =
absl::StatusOr<opj_stream_t*> stream =
api.opj_stream_create_default_file_stream(in_file_v.PtrBefore(), 1);
CHECK(stream.ok()) << "Stream initialization failed: " << stream.status();
sapi::v::RemotePtr stream_pointer(stream.value());
sapi::StatusOr<opj_codec_t*> codec = api.opj_create_decompress(OPJ_CODEC_JP2);
absl::StatusOr<opj_codec_t*> codec = api.opj_create_decompress(OPJ_CODEC_JP2);
CHECK(codec.ok()) << "Codec initialization failed: " << stream.status();
sapi::v::RemotePtr codec_pointer(codec.value());
@ -86,7 +86,7 @@ int main(int argc, char* argv[]) {
status = api.opj_set_default_decoder_parameters(parameters.PtrBoth());
CHECK(status.ok()) << "Parameters initialization failed " << status;
sapi::StatusOr<OPJ_BOOL> bool_status =
absl::StatusOr<OPJ_BOOL> bool_status =
api.opj_setup_decoder(&codec_pointer, parameters.PtrBefore());
CHECK(bool_status.ok() && bool_status.value()) << "Decoder setup failed";

View File

@ -40,7 +40,6 @@ sapi_library(
cc_binary(
name = "hello",
srcs = ["hello_main.cc"],
includes = ["."], # To find the generated header
deps = [":hello_sapi"],
)
@ -50,7 +49,6 @@ cc_binary(
cc_binary(
name = "hello_transacted",
srcs = ["hello_transacted.cc"],
includes = ["."], # To find the generated header
deps = [
":hello_sapi",
"@com_google_absl//absl/memory",

View File

@ -201,7 +201,7 @@ absl::Status Sandbox::Init() {
ModifyExecutor(executor.get());
s2_ = absl::make_unique<sandbox2::Sandbox2>(std::move(executor),
std::move(s2p));
std::move(s2p), CreateNotifier());
auto res = s2_->RunAsync();
comms_ = s2_->comms();

View File

@ -141,6 +141,9 @@ class Sandbox {
// Modifies the Executor object if needed.
virtual void ModifyExecutor(sandbox2::Executor* executor) {}
// Provides a custom notifier for sandboxee events. May return nullptr.
virtual std::unique_ptr<sandbox2::Notify> CreateNotifier() { return nullptr; }
// Exits the sandboxee.
void Exit() const;

View File

@ -54,7 +54,7 @@ absl::StatusOr<std::unique_ptr<Buffer>> Buffer::CreateFromFd(int fd) {
// Creates a new Buffer of the specified size, backed by a temporary file that
// will be immediately deleted.
absl::StatusOr<std::unique_ptr<Buffer>> Buffer::CreateWithSize(int64_t size) {
absl::StatusOr<std::unique_ptr<Buffer>> Buffer::CreateWithSize(size_t size) {
int fd;
if (!util::CreateMemFd(&fd)) {
return absl::InternalError("Could not create buffer temp file");

View File

@ -41,7 +41,7 @@ class Buffer final {
// Creates a new Buffer of the specified size, backed by a temporary file that
// will be immediately deleted.
static absl::StatusOr<std::unique_ptr<Buffer>> CreateWithSize(int64_t size);
static absl::StatusOr<std::unique_ptr<Buffer>> CreateWithSize(size_t size);
// Returns a pointer to the buffer, which is read/write.
uint8_t* data() const { return buf_; }

View File

@ -20,8 +20,6 @@
namespace sandbox2 {
const char kForkServerDisableEnv[] = "SANDBOX2_NOFORKSERVER";
pid_t ForkClient::SendRequest(const ForkRequest& request, int exec_fd,
int comms_fd, int user_ns_fd, pid_t* init_pid) {
// Acquire the channel ownership for this request (transaction).

View File

@ -17,34 +17,35 @@
#include <sys/types.h>
#include "absl/base/attributes.h"
#include "absl/base/thread_annotations.h"
#include "absl/synchronization/mutex.h"
namespace sandbox2 {
// Envvar indicating that this process should not start the fork-server.
ABSL_CONST_INIT extern const char kForkServerDisableEnv[];
constexpr inline char kForkServerDisableEnv[] = "SANDBOX2_NOFORKSERVER";
class Comms;
class ForkRequest;
class ForkClient {
public:
explicit ForkClient(Comms* comms) : comms_(comms) {}
ForkClient(const ForkClient&) = delete;
ForkClient& operator=(const ForkClient&) = delete;
explicit ForkClient(Comms* comms) : comms_(comms) {}
// Sends the fork request over the supplied Comms channel.
pid_t SendRequest(const ForkRequest& request, int exec_fd, int comms_fd,
int user_ns_fd = -1, pid_t* init_pid = nullptr);
private:
// Comms channel connecting with the ForkServer. Not owned by the object.
Comms* comms_;
Comms* comms_ ABSL_GUARDED_BY(comms_mutex_);
// Mutex locking transactions (requests) over the Comms channel.
absl::Mutex comms_mutex_;
};
} // namespace sandbox2
#endif // SANDBOXED_API_SANDBOX2_FORK_CLIENT_H_

View File

@ -45,6 +45,7 @@ target_link_libraries(sapi_generator PUBLIC
absl::memory
absl::random_random
absl::status
absl::statusor
absl::strings
clangFormat
clangFrontendTool
@ -59,12 +60,15 @@ add_executable(sapi_generator_tool
)
target_link_libraries(sapi_generator_tool PRIVATE
sapi::base
sandbox2::file_helpers
sandbox2::fileops
sapi::generator
)
if(SAPI_ENABLE_TESTS)
add_executable(sapi_generator_test
frontend_action_test_util.cc
frontend_action_test_util.h
emitter_test.cc
)
target_link_libraries(sapi_generator_test PRIVATE

View File

@ -23,9 +23,13 @@
#include "absl/strings/str_format.h"
#include "absl/strings/str_join.h"
#include "absl/strings/str_replace.h"
#include "absl/strings/string_view.h"
#include "absl/strings/strip.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/Type.h"
#include "clang/Format/Format.h"
#include "sandboxed_api/tools/clang_generator/diagnostics.h"
#include "sandboxed_api/tools/clang_generator/generator.h"
#include "sandboxed_api/util/status_macros.h"
namespace sapi {
@ -106,6 +110,30 @@ constexpr absl::string_view kClassFooterTemplate = R"(
};
)";
namespace internal {
absl::StatusOr<std::string> ReformatGoogleStyle(const std::string& filename,
const std::string& code) {
// Configure code style based on Google style, but enforce pointer alignment
clang::format::FormatStyle style =
clang::format::getGoogleStyle(clang::format::FormatStyle::LK_Cpp);
style.DerivePointerAlignment = false;
style.PointerAlignment = clang::format::FormatStyle::PAS_Left;
clang::tooling::Replacements replacements = clang::format::reformat(
style, code, llvm::makeArrayRef(clang::tooling::Range(0, code.size())),
filename);
llvm::Expected<std::string> formatted_header =
clang::tooling::applyAllReplacements(code, replacements);
if (!formatted_header) {
return absl::InternalError(llvm::toString(formatted_header.takeError()));
}
return *formatted_header;
}
} // namespace internal
std::string GetIncludeGuard(absl::string_view filename) {
if (filename.empty()) {
static auto* bit_gen = new absl::BitGen();
@ -250,7 +278,8 @@ absl::StatusOr<std::string> EmitFunction(const clang::FunctionDecl* decl) {
}
absl::StatusOr<std::string> EmitHeader(
std::vector<clang::FunctionDecl*> functions, const QualTypeSet& types,
const std::vector<std::string>& functions,
const Emitter::RenderedTypesMap& rendered_types,
const GeneratorOptions& options) {
std::string out;
const std::string include_guard = GetIncludeGuard(options.out_file);
@ -276,45 +305,19 @@ absl::StatusOr<std::string> EmitHeader(
}
// Emit type dependencies
// TODO(cblichmann): Coalesce namespaces
std::string out_types = "// Types this API depends on\n";
bool added_types = false;
for (const clang::QualType& qual : types) {
clang::TypeDecl* decl = nullptr;
if (const auto* typedef_type = qual->getAs<clang::TypedefType>()) {
decl = typedef_type->getDecl();
} else if (const auto* enum_type = qual->getAs<clang::EnumType>()) {
decl = enum_type->getDecl();
} else {
decl = qual->getAsRecordDecl();
}
if (!decl) {
continue;
}
const std::vector<std::string> ns_path = GetNamespacePath(decl);
std::string nested_ns_name;
if (!ns_path.empty()) {
if (const auto& ns_root = ns_path.front();
ns_root == "std" || ns_root == "sapi" || ns_root == "__gnu_cxx") {
// Filter out any and all declarations from the C++ standard library,
// from SAPI itself and from other well-known namespaces. This avoids
// re-declaring things like standard integer types, for example.
continue;
if (!rendered_types.empty()) {
absl::StrAppend(&out, "// Types this API depends on\n");
for (const auto& [ns_name, types] : rendered_types) {
if (!ns_name.empty()) {
absl::StrAppend(&out, "namespace ", ns_name, " {\n");
}
for (const auto& type : types) {
absl::StrAppend(&out, type, ";\n");
}
if (!ns_name.empty()) {
absl::StrAppend(&out, "} // namespace ", ns_name, "\n\n");
}
nested_ns_name = absl::StrCat(ns_path[0].empty() ? "" : " ",
absl::StrJoin(ns_path, "::"));
absl::StrAppend(&out_types, "namespace", nested_ns_name, " {\n");
}
absl::StrAppend(&out_types, PrintAstDecl(decl), ";");
if (!ns_path.empty()) {
absl::StrAppend(&out_types, "\n} // namespace", nested_ns_name);
}
absl::StrAppend(&out_types, "\n");
added_types = true;
}
if (added_types) {
absl::StrAppend(&out, out_types);
}
// Optionally emit a default sandbox that instantiates an embedded sandboxee
@ -329,11 +332,7 @@ absl::StatusOr<std::string> EmitHeader(
// TODO(cblichmann): Make the "Api" suffix configurable or at least optional.
absl::StrAppendFormat(&out, kClassHeaderTemplate,
absl::StrCat(options.name, "Api"));
std::string out_func;
for (const clang::FunctionDecl* decl : functions) {
SAPI_ASSIGN_OR_RETURN(out_func, EmitFunction(decl));
absl::StrAppend(&out, out_func);
}
absl::StrAppend(&out, absl::StrJoin(functions, "\n"));
absl::StrAppend(&out, kClassFooterTemplate);
// Close out the header: close namespace (if needed) and end include guard
@ -344,4 +343,45 @@ absl::StatusOr<std::string> EmitHeader(
return out;
}
void Emitter::CollectType(clang::QualType qual) {
clang::TypeDecl* decl = nullptr;
if (const auto* typedef_type = qual->getAs<clang::TypedefType>()) {
decl = typedef_type->getDecl();
} else if (const auto* enum_type = qual->getAs<clang::EnumType>()) {
decl = enum_type->getDecl();
} else {
decl = qual->getAsRecordDecl();
}
if (!decl) {
return;
}
const std::vector<std::string> ns_path = GetNamespacePath(decl);
std::string ns_name;
if (!ns_path.empty()) {
if (const auto& ns_root = ns_path.front();
ns_root == "std" || ns_root == "sapi" || ns_root == "__gnu_cxx") {
// Filter out any and all declarations from the C++ standard library,
// from SAPI itself and from other well-known namespaces. This avoids
// re-declaring things like standard integer types, for example.
return;
}
ns_name = absl::StrCat(ns_path[0].empty() ? "" : " ",
absl::StrJoin(ns_path, "::"));
}
rendered_types_[ns_name].push_back(PrintAstDecl(decl));
}
void Emitter::CollectFunction(clang::FunctionDecl* decl) {
functions_.push_back(*EmitFunction(decl)); // Cannot currently fail
}
absl::StatusOr<std::string> Emitter::EmitHeader(
const GeneratorOptions& options) {
SAPI_ASSIGN_OR_RETURN(const std::string header,
::sapi::EmitHeader(functions_, rendered_types_, options));
return internal::ReformatGoogleStyle(options.out_file, header);
}
} // namespace sapi

View File

@ -16,16 +16,44 @@
#define SANDBOXED_API_TOOLS_CLANG_GENERATOR_EMITTER_H_
#include <string>
#include <vector>
#include "absl/container/flat_hash_map.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include "clang/AST/Decl.h"
#include "clang/AST/Type.h"
#include "sandboxed_api/tools/clang_generator/generator.h"
#include "sandboxed_api/tools/clang_generator/types.h"
namespace sapi {
namespace internal {
absl::StatusOr<std::string> ReformatGoogleStyle(const std::string& filename,
const std::string& code);
} // namespace internal
class GeneratorOptions;
class Emitter {
public:
using RenderedTypesMap =
absl::flat_hash_map<std::string, std::vector<std::string>>;
void CollectType(clang::QualType qual);
void CollectFunction(clang::FunctionDecl* decl);
// Outputs a formatted header for a list of functions and their related types.
absl::StatusOr<std::string> EmitHeader(const GeneratorOptions& options);
protected:
// Maps namespace to a list of spellings for types
RenderedTypesMap rendered_types_;
// Functions for sandboxed API, including their bodies
std::vector<std::string> functions_;
};
// Constructs an include guard name for the given filename. The name is of the
// same form as the include guards in this project.
@ -35,11 +63,6 @@ namespace sapi {
// SANDBOXED_API_EXAMPLES_ZLIB_ZLIB_SAPI_SAPI_H_
std::string GetIncludeGuard(absl::string_view filename);
// Outputs a formatted header for a list of functions and their related types.
absl::StatusOr<std::string> EmitHeader(
std::vector<clang::FunctionDecl*> functions, const QualTypeSet& types,
const GeneratorOptions& options);
} // namespace sapi
#endif // SANDBOXED_API_TOOLS_CLANG_GENERATOR_EMITTER_H_

View File

@ -14,18 +14,48 @@
#include "sandboxed_api/tools/clang_generator/emitter.h"
#include <initializer_list>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/memory/memory.h"
#include "absl/strings/string_view.h"
#include "sandboxed_api/sandbox2/testing.h"
#include "sandboxed_api/tools/clang_generator/frontend_action_test_util.h"
#include "sandboxed_api/tools/clang_generator/generator.h"
#include "sandboxed_api/util/status_matchers.h"
namespace sapi {
namespace {
using ::testing::MatchesRegex;
using ::testing::SizeIs;
using ::testing::StrEq;
using ::testing::StrNe;
class EmitterForTesting : public Emitter {
public:
using Emitter::functions_;
};
class EmitterTest : public FrontendActionTest {};
TEST_F(EmitterTest, BasicFunctionality) {
GeneratorOptions options;
options.out_file = "input.h";
options.set_function_names<std::initializer_list<std::string>>(
{"ExposedFunction"});
EmitterForTesting emitter;
RunFrontendAction(R"(extern "C" void ExposedFunction() {})",
absl::make_unique<GeneratorAction>(emitter, options));
EXPECT_THAT(emitter.functions_, SizeIs(1));
absl::StatusOr<std::string> header = emitter.EmitHeader(options);
EXPECT_THAT(header, IsOk());
}
TEST(IncludeGuard, CreatesRandomizedGuardForEmptyFilename) {
// Copybara will transform the string. This is intentional.
constexpr absl::string_view kGeneratedHeaderPrefix =

View File

@ -0,0 +1,70 @@
// Copyright 2020 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
//
// 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.
#include "sandboxed_api/tools/clang_generator/frontend_action_test_util.h"
#include <vector>
#include "absl/status/status.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/FileSystemOptions.h"
#include "clang/Frontend/FrontendAction.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/VirtualFileSystem.h"
namespace sapi {
namespace internal {
absl::Status RunClangTool(
const std::vector<std::string> command_line,
const absl::flat_hash_map<std::string, std::string> file_contents,
std::unique_ptr<clang::FrontendAction> action) {
// Setup an in-memory virtual filesystem
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> fs(
new llvm::vfs::InMemoryFileSystem());
llvm::IntrusiveRefCntPtr<clang::FileManager> files =
new clang::FileManager(clang::FileSystemOptions(), fs);
for (const auto& [filename, content] : file_contents) {
if (!fs->addFile(filename, /*ModificationTime=*/0,
llvm::MemoryBuffer::getMemBuffer(content))) {
return absl::UnknownError(
absl::StrCat("Couldn't add file to in-memory VFS: ", filename));
}
}
#if LLVM_VERSION_MAJOR >= 10
clang::tooling::ToolInvocation invocation(command_line, std::move(action),
files.get());
#else
clang::tooling::ToolInvocation invocation(command_line, action.get(),
files.get());
#endif
if (!invocation.run()) {
return absl::UnknownError("Tool invocation failed");
}
return absl::OkStatus();
}
} // namespace internal
std::vector<std::string> FrontendActionTest::GetCommandLineFlagsForTesting(
absl::string_view input_file) {
return {"tool", "-fsyntax-only", "--std=c++17",
"-I.", "-Wno-error", std::string(input_file)};
}
} // namespace sapi

View File

@ -0,0 +1,83 @@
// Copyright 2020 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
//
// 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_TOOLS_CLANG_GENERATOR_FRONTEND_ACTION_TEST_UTIL_H_
#define SANDBOXED_API_TOOLS_CLANG_GENERATOR_FRONTEND_ACTION_TEST_UTIL_H_
#include <memory>
#include <string>
#include <vector>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/container/flat_hash_map.h"
#include "absl/memory/memory.h"
#include "absl/status/status.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "clang/Frontend/FrontendAction.h"
#include "sandboxed_api/util/status_matchers.h"
namespace sapi {
namespace internal {
absl::Status RunClangTool(
const std::vector<std::string> command_line,
const absl::flat_hash_map<std::string, std::string> file_contents,
std::unique_ptr<clang::FrontendAction> action);
} // namespace internal
class FrontendActionTest : public ::testing::Test {
protected:
// Adds code to a virtual filesystem with the given filename.
void AddCode(const std::string& filename, absl::string_view code) {
absl::StrAppend(&file_contents_[filename], code);
}
// Changes the name of the virtual input file. Useful for special cases where
// the filenames of compiled sources matter.
void set_input_file(absl::string_view value) {
input_file_ = std::string(value);
}
virtual std::vector<std::string> GetCommandLineFlagsForTesting(
absl::string_view input_file);
// Runs the specified frontend action on in-memory source code.
void RunFrontendAction(absl::string_view code,
std::unique_ptr<clang::FrontendAction> action) {
std::vector<std::string> command_line =
GetCommandLineFlagsForTesting(input_file_);
AddCode(input_file_, code);
ASSERT_THAT(
internal::RunClangTool(command_line, file_contents_, std::move(action)),
IsOk());
}
// Runs the specified frontend action. Provided for compatibility with LLVM <
// 10. Takes ownership.
void RunFrontendAction(absl::string_view code,
clang::FrontendAction* action) {
RunFrontendAction(code, absl::WrapUnique(action));
}
private:
std::string input_file_ = "input.cc";
absl::flat_hash_map<std::string, std::string> file_contents_;
};
} // namespace sapi
#endif // SANDBOXED_API_TOOLS_CLANG_GENERATOR_FRONTEND_ACTION_TEST_UTIL_H_

View File

@ -19,6 +19,7 @@
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "clang/AST/Type.h"
#include "clang/Format/Format.h"
#include "sandboxed_api/sandbox2/util/fileops.h"
#include "sandboxed_api/tools/clang_generator/diagnostics.h"
@ -39,88 +40,46 @@ std::string ReplaceFileExtension(absl::string_view path,
return absl::StrCat(path.substr(0, pos), new_extension);
}
std::string GetOutputFilename(absl::string_view source_file) {
return ReplaceFileExtension(source_file, ".sapi.h");
}
inline absl::string_view ToStringView(llvm::StringRef ref) {
return absl::string_view(ref.data(), ref.size());
}
} // namespace
std::string GetOutputFilename(absl::string_view source_file) {
return ReplaceFileExtension(source_file, ".sapi.h");
}
bool GeneratorASTVisitor::VisitFunctionDecl(clang::FunctionDecl* decl) {
if (!decl->isCXXClassMember() && // Skip classes
decl->isExternC() && // Skip non external functions
!decl->isTemplated() && // Skip function templates
// Process either all function or just the requested ones
(options_->function_names.empty() ||
options_->function_names.count(ToStringView(decl->getName())) > 0)) {
(options_.function_names.empty() ||
options_.function_names.count(ToStringView(decl->getName())) > 0)) {
functions_.push_back(decl);
GatherRelatedTypes(decl->getDeclaredReturnType(), &types_);
collector_.CollectRelatedTypes(decl->getDeclaredReturnType());
for (const clang::ParmVarDecl* param : decl->parameters()) {
GatherRelatedTypes(param->getType(), &types_);
collector_.CollectRelatedTypes(param->getType());
}
}
return true;
}
namespace internal {
absl::StatusOr<std::string> ReformatGoogleStyle(const std::string& filename,
const std::string& code) {
// Configure code style based on Google style, but enforce pointer alignment
clang::format::FormatStyle style =
clang::format::getGoogleStyle(clang::format::FormatStyle::LK_Cpp);
style.DerivePointerAlignment = false;
style.PointerAlignment = clang::format::FormatStyle::PAS_Left;
clang::tooling::Replacements replacements = clang::format::reformat(
style, code, llvm::makeArrayRef(clang::tooling::Range(0, code.size())),
filename);
llvm::Expected<std::string> formatted_header =
clang::tooling::applyAllReplacements(code, replacements);
if (!formatted_header) {
return absl::InternalError(llvm::toString(formatted_header.takeError()));
}
return *formatted_header;
}
} // namespace internal
absl::Status GeneratorASTConsumer::GenerateAndSaveHeader() {
const std::string out_file =
options_->out_file.empty() ? GetOutputFilename(in_file_)
: sandbox2::file_util::fileops::MakeAbsolute(
options_->out_file, options_->work_dir);
SAPI_ASSIGN_OR_RETURN(const std::string header,
EmitHeader(visitor_.functions_, visitor_.types_, *options_));
SAPI_ASSIGN_OR_RETURN(const std::string formatted_header,
internal::ReformatGoogleStyle(in_file_, header));
std::ofstream os(out_file, std::ios::out | std::ios::trunc);
os << formatted_header;
if (!os) {
return absl::UnknownError("I/O error");
}
return absl::OkStatus();
}
void GeneratorASTConsumer::HandleTranslationUnit(clang::ASTContext& context) {
absl::Status status;
std::cout << "Processing " << in_file_ << "\n";
if (!visitor_.TraverseDecl(context.getTranslationUnitDecl())) {
status = absl::InternalError("AST traversal exited early");
} else {
status = GenerateAndSaveHeader();
ReportFatalError(context.getDiagnostics(),
context.getTranslationUnitDecl()->getBeginLoc(),
"AST traversal exited early");
}
if (!status.ok()) {
ReportFatalError(context.getDiagnostics(),
GetDiagnosticLocationFromStatus(status).value_or(
context.getTranslationUnitDecl()->getBeginLoc()),
status.message());
for (clang::QualType qual : visitor_.collector_.collected()) {
emitter_.CollectType(qual);
}
for (clang::FunctionDecl* func : visitor_.functions_) {
emitter_.CollectFunction(func);
}
}

View File

@ -26,6 +26,7 @@
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendAction.h"
#include "clang/Tooling/Tooling.h"
#include "sandboxed_api/tools/clang_generator/emitter.h"
#include "sandboxed_api/tools/clang_generator/types.h"
namespace sapi {
@ -46,85 +47,87 @@ struct GeneratorOptions {
std::string work_dir;
std::string name; // Name of the Sandboxed API
std::string namespace_name; // Namespace to wrap the SAPI in
std::string out_file; // Output path of the generated header
std::string embed_dir; // Directory with embedded includes
std::string embed_name; // Identifier of the embed object
// Output path of the generated header. Used to build the header include
// guard.
std::string out_file;
std::string embed_dir; // Directory with embedded includes
std::string embed_name; // Identifier of the embed object
};
class GeneratorASTVisitor
: public clang::RecursiveASTVisitor<GeneratorASTVisitor> {
public:
explicit GeneratorASTVisitor(const GeneratorOptions& options)
: options_(options) {}
bool VisitFunctionDecl(clang::FunctionDecl* decl);
private:
friend class GeneratorASTConsumer;
const GeneratorOptions* options_ = nullptr;
TypeCollector collector_;
std::vector<clang::FunctionDecl*> functions_;
QualTypeSet types_;
const GeneratorOptions& options_;
};
namespace internal {
absl::StatusOr<std::string> ReformatGoogleStyle(const std::string& filename,
const std::string& code);
} // namespace internal
class GeneratorASTConsumer : public clang::ASTConsumer {
public:
GeneratorASTConsumer(std::string in_file, const GeneratorOptions* options)
: in_file_(std::move(in_file)), options_(options) {
visitor_.options_ = options_;
}
GeneratorASTConsumer(std::string in_file, Emitter& emitter,
const GeneratorOptions& options)
: in_file_(std::move(in_file)), visitor_(options), emitter_(emitter) {}
private:
void HandleTranslationUnit(clang::ASTContext& context) override;
absl::Status GenerateAndSaveHeader();
std::string in_file_;
const GeneratorOptions* options_;
GeneratorASTVisitor visitor_;
Emitter& emitter_;
};
class GeneratorAction : public clang::ASTFrontendAction {
public:
explicit GeneratorAction(const GeneratorOptions* options)
: options_(options) {}
GeneratorAction(Emitter& emitter, const GeneratorOptions& options)
: emitter_(emitter), options_(options) {}
private:
std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(
clang::CompilerInstance&, llvm::StringRef in_file) override {
return absl::make_unique<GeneratorASTConsumer>(std::string(in_file),
options_);
emitter_, options_);
}
bool hasCodeCompletionSupport() const override { return false; }
const GeneratorOptions* options_;
Emitter& emitter_;
const GeneratorOptions& options_;
};
class GeneratorFactory : public clang::tooling::FrontendActionFactory {
public:
explicit GeneratorFactory(GeneratorOptions options = {})
: options_(std::move(options)) {}
// Does not take ownership
GeneratorFactory(Emitter& emitter, const GeneratorOptions& options)
: emitter_(emitter), options_(options) {}
private:
#if LLVM_VERSION_MAJOR >= 10
std::unique_ptr<clang::FrontendAction> create() override {
return absl::make_unique<GeneratorAction>(&options_);
return absl::make_unique<GeneratorAction>(emitter_, options_);
}
#else
clang::FrontendAction* create() override {
return new GeneratorAction(&options_);
return new GeneratorAction(emitter_, options_);
}
#endif
GeneratorOptions options_;
Emitter& emitter_;
const GeneratorOptions& options_;
};
std::string GetOutputFilename(absl::string_view source_file);
} // namespace sapi
#endif // SANDBOXED_API_TOOLS_CLANG_GENERATOR_GENERATOR_H_

View File

@ -12,16 +12,23 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <cstdlib>
#include <string>
#include <vector>
#include "absl/memory/memory.h"
#include "absl/status/status.h"
#include "absl/strings/str_format.h"
#include "absl/strings/str_split.h"
#include "clang/AST/ASTContext.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Tooling/ArgumentsAdjusters.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "llvm/Support/CommandLine.h"
#include "sandboxed_api/sandbox2/util/file_helpers.h"
#include "sandboxed_api/sandbox2/util/fileops.h"
#include "sandboxed_api/tools/clang_generator/generator.h"
#include "sandboxed_api/util/status_macros.h"
namespace sapi {
namespace {
@ -49,11 +56,11 @@ static auto* g_sapi_functions = new llvm::cl::list<std::string>(
llvm::cl::cat(*g_tool_category));
static auto* g_sapi_in = new llvm::cl::list<std::string>(
"sapi_in", llvm::cl::CommaSeparated,
llvm::cl::desc("List of input files to analyze. Deprecated, use positional "
"arguments instead."),
llvm::cl::desc("List of input files to analyze."),
llvm::cl::cat(*g_tool_category));
static auto* g_sapi_isystem = new llvm::cl::opt<std::string>(
"sapi_isystem", llvm::cl::desc("Extra system include paths"),
"sapi_isystem",
llvm::cl::desc("Parameter file with extra system include paths"),
llvm::cl::cat(*g_tool_category));
static auto* g_sapi_limit_scan_depth = new llvm::cl::opt<bool>(
"sapi_limit_scan_depth",
@ -69,28 +76,28 @@ static auto* g_sapi_ns = new llvm::cl::opt<std::string>(
static auto* g_sapi_out = new llvm::cl::opt<std::string>(
"sapi_out",
llvm::cl::desc(
"Ouput path of the generated header. If empty, simply appends .sapi.h "
"Output path of the generated header. If empty, simply appends .sapi.h "
"to the basename of the first source file specified."),
llvm::cl::cat(*g_tool_category));
} // namespace
GeneratorOptions GeneratorOptionsFromFlags() {
GeneratorOptions GeneratorOptionsFromFlags(
const std::vector<std::string>& sources) {
GeneratorOptions options;
options.function_names.insert(g_sapi_functions->begin(),
g_sapi_functions->end());
options.work_dir = sandbox2::file_util::fileops::GetCWD();
options.name = *g_sapi_name;
options.namespace_name = *g_sapi_ns;
options.out_file = *g_sapi_out;
options.out_file =
!g_sapi_out->empty() ? *g_sapi_out : GetOutputFilename(sources.front());
options.embed_dir = *g_sapi_embed_dir;
options.embed_name = *g_sapi_embed_name;
return options;
}
} // namespace sapi
int main(int argc, const char** argv) {
absl::Status GeneratorMain(int argc, const char** argv) {
clang::tooling::CommonOptionsParser opt_parser(
argc, argv, *sapi::g_tool_category, llvm::cl::ZeroOrMore,
"Generates a Sandboxed API header for C/C++ translation units.");
@ -98,9 +105,45 @@ int main(int argc, const char** argv) {
for (const auto& sapi_in : *sapi::g_sapi_in) {
sources.push_back(sapi_in);
}
if (sources.empty()) {
return absl::InvalidArgumentError("error: no input files");
}
auto options = sapi::GeneratorOptionsFromFlags(sources);
sapi::Emitter emitter;
clang::tooling::ClangTool tool(opt_parser.getCompilations(), sources);
return tool.run(absl::make_unique<sapi::GeneratorFactory>(
sapi::GeneratorOptionsFromFlags())
.get());
if (!sapi::g_sapi_isystem->empty()) {
std::string isystem_lines;
SAPI_RETURN_IF_ERROR(sandbox2::file::GetContents(
*sapi::g_sapi_isystem, &isystem_lines, sandbox2::file::Defaults()));
std::vector<std::string> isystem =
absl::StrSplit(isystem_lines, '\n', absl::SkipWhitespace());
for (std::string& line : isystem) {
line.insert(0, "-isystem");
}
tool.appendArgumentsAdjuster(clang::tooling::getInsertArgumentAdjuster(
isystem, clang::tooling::ArgumentInsertPosition::BEGIN));
}
if (int result = tool.run(
absl::make_unique<sapi::GeneratorFactory>(emitter, options).get());
result != 0) {
return absl::UnknownError("header generation failed");
}
SAPI_ASSIGN_OR_RETURN(std::string header, emitter.EmitHeader(options));
SAPI_RETURN_IF_ERROR(sandbox2::file::SetContents(options.out_file, header,
sandbox2::file::Defaults()));
return absl::OkStatus();
}
} // namespace sapi
int main(int argc, const char** argv) {
if (absl::Status status = sapi::GeneratorMain(argc, argv); !status.ok()) {
absl::FPrintF(stderr, "error: %s\n", status.message());
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

View File

@ -30,10 +30,15 @@ bool IsFunctionReferenceType(clang::QualType qual) {
} // namespace
void GatherRelatedTypes(clang::QualType qual, QualTypeSet* types) {
void TypeCollector::CollectRelatedTypes(clang::QualType qual) {
if (seen_.contains(qual)) {
return;
}
seen_.insert(qual);
if (const auto* typedef_type = qual->getAs<clang::TypedefType>()) {
GatherRelatedTypes(typedef_type->getDecl()->getUnderlyingType(), types);
types->insert(qual);
CollectRelatedTypes(typedef_type->getDecl()->getUnderlyingType());
collected_.insert(qual);
return;
}
@ -43,26 +48,26 @@ void GatherRelatedTypes(clang::QualType qual, QualTypeSet* types) {
->getAs<clang::FunctionProtoType>()) {
// Note: Do not add the function type itself, as this will always be a
// pointer argument. We only need to collect all its related types.
GatherRelatedTypes(function_type->getReturnType(), types);
CollectRelatedTypes(function_type->getReturnType());
for (const clang::QualType& param : function_type->getParamTypes()) {
GatherRelatedTypes(param, types);
CollectRelatedTypes(param);
}
return;
}
}
if (IsPointerOrReference(qual)) {
clang::QualType pointee = qual->getPointeeType();
while (IsPointerOrReference(pointee)) {
clang::QualType pointee = qual;
do {
pointee = pointee->getPointeeType();
}
GatherRelatedTypes(pointee, types);
} while (IsPointerOrReference(pointee));
CollectRelatedTypes(pointee);
return;
}
// C array with specified constant size (i.e. int a[42])?
if (const clang::ArrayType* array_type = qual->getAsArrayTypeUnsafe()) {
GatherRelatedTypes(array_type->getElementType(), types);
CollectRelatedTypes(array_type->getElementType());
return;
}
@ -71,19 +76,19 @@ void GatherRelatedTypes(clang::QualType qual, QualTypeSet* types) {
// Collect the underlying integer type of enum classes as well, as it may
// be a typedef.
if (const clang::EnumDecl* decl = enum_type->getDecl(); decl->isFixed()) {
GatherRelatedTypes(decl->getIntegerType(), types);
CollectRelatedTypes(decl->getIntegerType());
}
}
types->insert(qual);
collected_.insert(qual);
return;
}
if (const auto* record_type = qual->getAs<clang::RecordType>()) {
const clang::RecordDecl* decl = record_type->getDecl();
for (const clang::FieldDecl* field : decl->fields()) {
GatherRelatedTypes(field->getType(), types);
CollectRelatedTypes(field->getType());
}
types->insert(qual);
collected_.insert(qual);
return;
}
}

View File

@ -41,20 +41,29 @@ inline bool IsPointerOrReference(clang::QualType qual) {
qual->isLValueReferenceType() || qual->isRValueReferenceType();
}
// Computes the transitive closure of all types that a type depends on. Those
// are types that need to be declared before a declaration of the type denoted
// by the qual parameter is valid. For example, given
// struct SubStruct { bool truth_value; };
// struct AggregateStruct {
// int int_member;
// SubStruct struct_member;
// };
//
// calling this function on the type "AggregateStruct" yields these types:
// int
// SubStruct
// bool
void GatherRelatedTypes(clang::QualType qual, QualTypeSet* types);
class TypeCollector {
public:
// Computes the transitive closure of all types that a type depends on. Those
// are types that need to be declared before a declaration of the type denoted
// by the qual parameter is valid. For example, given
// struct SubStruct { bool truth_value; };
// struct AggregateStruct {
// int int_member;
// SubStruct struct_member;
// };
//
// calling this function on the type "AggregateStruct" yields these types:
// int
// SubStruct
// bool
void CollectRelatedTypes(clang::QualType qual);
QualTypeSet& collected() { return collected_; }
private:
QualTypeSet collected_;
QualTypeSet seen_;
};
// Maps a qualified type to a fully qualified SAPI-compatible type name. This
// is used for the generated code that invokes the actual function call RPC.

View File

@ -492,7 +492,6 @@ class Function(object):
self._tu = tu
self.cursor = cursor # type: cindex.Index
self.name = cursor.spelling # type: Text
self.mangled_name = cursor.mangled_name # type: Text
self.result = ReturnType(self, cursor.result_type)
self.original_definition = '{} {}'.format(
cursor.result_type.spelling, self.cursor.displayname) # type: Text
@ -542,7 +541,7 @@ class Function(object):
def is_mangled(self):
# type: () -> bool
return self.name != self.mangled_name
return self.cursor.mangled_name != self.cursor.spelling
def __hash__(self):
# type: () -> int
@ -550,7 +549,7 @@ class Function(object):
def __eq__(self, other):
# type: (Function) -> bool
return self.mangled_name == other.mangled_name
return self.cursor.mangled_name == other.cursor.mangled_name
class _TranslationUnit(object):
@ -580,12 +579,11 @@ class _TranslationUnit(object):
for i, cursor in enumerate(self._walk_preorder()):
# Workaround for issue#32
# ignore all the cursors with kinds not implemented in python bindings
try:
cursor.kind
except ValueError:
if cursor._kind_id >= 440: # pylint: disable=protected-access
continue
raise
continue
# naive way to order types: they should be ordered when walking the tree
if cursor.kind.is_declaration():
self.order[cursor.hash] = i
@ -911,7 +909,7 @@ class Generator(object):
result.append('#include "sandboxed_api/sandbox.h"')
result.append('#include "sandboxed_api/vars.h"')
if (embed_dir is not None) and (embed_name is not None):
if embed_dir and embed_name:
result.append(
Generator.EMBED_INCLUDE.format(
os.path.join(embed_dir, embed_name) + '_embed.h'))
@ -928,7 +926,7 @@ class Generator(object):
result.append('')
if (embed_dir is not None) and (embed_name is not None):
if embed_dir and embed_name:
result.append(
Generator.EMBED_CLASS.format(name, embed_name.replace('-', '_')))

View File

@ -832,7 +832,7 @@ class CodeAnalysisTest(parameterized.TestCase):
functions = tu.get_functions()
self.assertLen(functions, 2)
mangled_names = [f.mangled_name for f in functions]
mangled_names = [f.cursor.mangled_name for f in functions]
self.assertSameElements(mangled_names, ['sum', '_Z3sumif'])

View File

@ -46,6 +46,7 @@ cc_library(
name = "statusor",
hdrs = ["statusor.h"],
deprecation = "Migrate to `absl::StatusOr<T>`",
visibility = ["//visibility:public"],
deps = [
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/status:statusor",