From 8ef52c7c2d376b3e5f084b90384baa0571233979 Mon Sep 17 00:00:00 2001 From: LI Qing Date: Fri, 23 Aug 2019 20:48:43 +0800 Subject: [PATCH] Support environmental variables in Occlum.json 1. Now we support set App's env in Occlum.json, for example: "env": [ "OCCLUM=yes", "TEST=true" ] 2. Rewrite env test cases 3. Update Dockerfile to install "jq" tool --- src/libos/src/config.rs | 18 +++- src/libos/src/entry.rs | 5 +- src/libos/src/errno.rs | 8 +- test/Makefile | 4 +- test/Occlum.json | 4 + test/env/Makefile | 8 +- test/env/main.c | 200 +++++++++++++++++++++++++++++++++------ test/server/main.c | 2 +- test/server_epoll/main.c | 2 +- tools/docker/Dockerfile | 1 + tools/occlum | 3 + 11 files changed, 214 insertions(+), 41 deletions(-) diff --git a/src/libos/src/config.rs b/src/libos/src/config.rs index ffaf40a8..2ce98b0e 100644 --- a/src/libos/src/config.rs +++ b/src/libos/src/config.rs @@ -1,5 +1,6 @@ use super::*; use serde::{Deserialize, Serialize}; +use std::ffi::CString; use std::path::{Path, PathBuf}; use std::sgxfs::SgxFile; @@ -96,6 +97,7 @@ fn parse_mac(mac_str: &str) -> Result { pub struct Config { pub vm: ConfigVM, pub process: ConfigProcess, + pub env: Vec, pub mount: Vec, } @@ -137,6 +139,13 @@ impl Config { fn from_input(input: &InputConfig) -> Result { let vm = ConfigVM::from_input(&input.vm)?; let process = ConfigProcess::from_input(&input.process)?; + let env = { + let mut env = Vec::new(); + for input_env in &input.env { + env.push(CString::new(input_env.clone())?); + } + env + }; let mount = { let mut mount = Vec::new(); for input_mount in &input.mount { @@ -144,7 +153,12 @@ impl Config { } mount }; - Ok(Config { vm, process, mount }) + Ok(Config { + vm, + process, + env, + mount, + }) } } @@ -248,6 +262,8 @@ struct InputConfig { #[serde(default)] pub process: InputConfigProcess, #[serde(default)] + pub env: Vec, + #[serde(default)] pub mount: Vec, } diff --git a/src/libos/src/entry.rs b/src/libos/src/entry.rs index ebde908a..58b2d3a9 100644 --- a/src/libos/src/entry.rs +++ b/src/libos/src/entry.rs @@ -76,11 +76,10 @@ fn do_boot(path_str: &str, argv: &Vec) -> Result<(), Error> { // info!("boot: path: {:?}, argv: {:?}", path_str, argv); util::mpx_util::mpx_enable()?; - // The default environment variables - let envp = vec![CString::new("OCCLUM=yes").unwrap()]; + let envp = &config::LIBOS_CONFIG.env; let file_actions = Vec::new(); let parent = &process::IDLE_PROCESS; - process::do_spawn(&path_str, argv, &envp, &file_actions, parent)?; + process::do_spawn(&path_str, argv, envp, &file_actions, parent)?; Ok(()) } diff --git a/src/libos/src/errno.rs b/src/libos/src/errno.rs index c00f39e3..cdf8717d 100644 --- a/src/libos/src/errno.rs +++ b/src/libos/src/errno.rs @@ -1,5 +1,5 @@ use prelude::*; -use std::{convert, error, fmt}; +use std::{convert, error, ffi, fmt}; // TODO: remove errno.h @@ -31,6 +31,12 @@ impl convert::From for Error { } } +impl convert::From for Error { + fn from(info: std::ffi::NulError) -> Error { + Error::new(Errno::EINVAL, "std::ffi::NulError") + } +} + impl error::Error for Error { fn description(&self) -> &str { self.desc diff --git a/test/Makefile b/test/Makefile index c3bc8d24..fd754288 100644 --- a/test/Makefile +++ b/test/Makefile @@ -36,7 +36,7 @@ all: build build: prebuild $(BUILD_TARGETS) postbuild prebuild: - @$(RM) -rf $(BUILD_DIR)/test/* + @$(RM) -rf $(BUILD_DIR)/test @mkdir -p $(BUILD_DIR)/test @cd $(BUILD_DIR)/test && \ $(PROJECT_DIR)/build/bin/occlum init @@ -86,4 +86,4 @@ $(BENCH_TARGETS): bench-%: % ############################################################################# clean: - @$(RM) -rf $(BUILD_DIR)/test/* + @$(RM) -rf $(BUILD_DIR)/test diff --git a/test/Occlum.json b/test/Occlum.json index 93930e04..5e2da2bd 100644 --- a/test/Occlum.json +++ b/test/Occlum.json @@ -7,6 +7,10 @@ "default_heap_size": "16MB", "default_mmap_size": "32MB" }, + "env": [ + "OCCLUM=yes", + "TEST=true" + ], "mount": [ { "target": "/", diff --git a/test/env/Makefile b/test/env/Makefile index 90664f55..221a1765 100644 --- a/test/env/Makefile +++ b/test/env/Makefile @@ -6,9 +6,9 @@ ARG2 := arg2 ARG3 := this is a string with spaces EXTRA_C_FLAGS := \ - -DEXPECTED_ARGC=$(ARGC) \ - -DEXPECTED_ARG1="\"$(ARG1)\"" \ - -DEXPECTED_ARG2="\"$(ARG2)\"" \ - -DEXPECTED_ARG3="\"$(ARG3)\"" + -DEXPECT_ARGC=$(ARGC) \ + -DEXPECT_ARG1="\"$(ARG1)\"" \ + -DEXPECT_ARG2="\"$(ARG2)\"" \ + -DEXPECT_ARG3="\"$(ARG3)\"" EXTRA_LINK_FLAGS := BIN_ARGS := "$(ARG1)" "$(ARG2)" "$(ARG3)" diff --git a/test/env/main.c b/test/env/main.c index c20b693d..61b0b4ad 100644 --- a/test/env/main.c +++ b/test/env/main.c @@ -2,56 +2,200 @@ #include #include #include +#include #include #include +#include +#include +#include "test.h" + +// ============================================================================ +// Helper structs & variables & functions +// ============================================================================ + +const char** g_argv; +int g_argc; // Expected arguments are given by Makefile throught macro ARGC, ARG1, ARG2 and // ARG3 -const char* expected_argv[EXPECTED_ARGC] = { +const char* expect_argv[EXPECT_ARGC] = { "env", - EXPECTED_ARG1, - EXPECTED_ARG2, - EXPECTED_ARG3, + EXPECT_ARG1, + EXPECT_ARG2, + EXPECT_ARG3, }; -int main(int argc, const char* argv[]) { - // Test argc - if (argc != EXPECTED_ARGC) { - printf("ERROR: expect %d arguments, but %d are given\n", EXPECTED_ARGC, argc); - return -1; - } +// Expected child arguments +const int child_argc = 2; +const char* child_argv[child_argc + 1] = { + "env", + "child", + NULL +}; - // Test argv - for (int arg_i = 0; arg_i < argc; arg_i++) { - printf("arg[%d] = %s\n", arg_i, argv[arg_i]); - const char* actual_arg = argv[arg_i]; - const char* expected_arg = expected_argv[arg_i]; - if (strcmp(actual_arg, expected_arg) != 0) { +// Expected child environment variables +const char* child_envp[] = { + "ENV_CHILD=ok", + NULL +}; + +static int test_argv_val(const char** expect_argv) { + for (int arg_i = 0; arg_i < g_argc; arg_i++) { + const char* actual_arg = *(g_argv + arg_i); + const char* expect_arg = *(expect_argv + arg_i); + if (strcmp(actual_arg, expect_arg) != 0) { printf("ERROR: expect argument %d is %s, but given %s\n", - arg_i, expected_arg, actual_arg); + arg_i, expect_arg, actual_arg); return -1; } } + return 0; +} - // Test envp - // Occlum LibOS by default passes an environment OCCLUM=yes - const char* env_val = getenv("OCCLUM"); - if (env_val == 0) { - printf("ERROR: cannot find environment variable OCCLUM\n"); +static int test_env_val(const char* expect_env_key, const char* expect_env_val) { + const char* actual_env_val = getenv(expect_env_key); + if (actual_env_val == NULL) { + printf("ERROR: cannot find %s\n", expect_env_key); return -1; } - else if (strcmp(env_val, "yes") != 0) { - printf("ERROR: environment variable OCCLUM=yes expected, but given %s\n", - env_val); + if (strcmp(actual_env_val, expect_env_val) != 0) { + printf("ERROR: environment variable %s=%s expected, but given %s\n", + expect_env_key, expect_env_val, actual_env_val); return -1; } + return 0; +} - // Test aux +// ============================================================================ +// Test cases for argv +// ============================================================================ + +static int test_env_getargv() { + // Test argc + if (g_argc != EXPECT_ARGC) { + printf("ERROR: expect %d arguments, but %d are given\n", EXPECT_ARGC, g_argc); + throw_error("arguments count is not expected"); + } + // Test argv + if (test_argv_val(expect_argv) < 0) { + throw_error("argument variables are not expected"); + } + return 0; +} + +// ============================================================================ +// Test cases for aux +// ============================================================================ + +static int test_env_getauxval() { unsigned long page_size = getauxval(AT_PAGESZ); if (errno != 0 || page_size != 4096) { - printf("ERROR: auxilary vector does not pass correct the value\n"); - return -1; + throw_error("auxilary vector does not pass correct the value"); } return 0; } + +// ============================================================================ +// Test cases for env +// ============================================================================ + +// The environment variables are specified in Occlum.json +static int test_env_getenv() { + if (test_env_val("OCCLUM", "yes") < 0) { + throw_error("get environment variable failed"); + } + + // Here we call getenv() again to make sure that + // LibOS can handle several environment variables in Occlum.json correctly + if (test_env_val("TEST", "true") < 0) { + throw_error("get environment variable failed"); + } + return 0; +} + +static int test_env_set_child_env_and_argv() { + int status, child_pid; + int ret = posix_spawn(&child_pid, + "/bin/env", NULL, NULL, + (char *const *)child_argv, + (char *const *)child_envp); + if (ret < 0) { + throw_error("spawn process error"); + } + printf("Spawn a child process with pid=%d\n", child_pid); + ret = wait4(-1, &status, 0, NULL); + if (ret < 0) { + throw_error("failed to wait4 the child process"); + } + if (!WIFEXITED(status)) { + throw_error("test cases in child faild"); + } + return 0; +} + +// ============================================================================ +// Child Test cases for argv +// ============================================================================ + +static int test_env_child_getargv() { + // Test argc + if (g_argc != child_argc) { + printf("ERROR: expect %d arguments, but %d are given\n", child_argc, g_argc); + throw_error("arguments count is not expected"); + } + // Test argv + if (test_argv_val(child_argv) < 0) { + throw_error("argument variables are not expected"); + } + return 0; +} + +// ============================================================================ +// Child Test cases for env +// ============================================================================ + +#define ENV_SIZE (256) +static int test_env_child_getenv() { + char env_key[ENV_SIZE]; + char env_val[ENV_SIZE]; + for (int i = 0; child_envp[i] != NULL; ++i) { + int num = sscanf(child_envp[i], "%[^=]=%s", env_key, env_val); + if (num != 2) { + throw_error("parse environment variable failed"); + } + if (test_env_val(env_key, env_val) < 0) { + throw_error("get environment variable failed"); + } + } + return 0; +} + +// ============================================================================ +// Test suite main +// ============================================================================ + +static test_case_t test_cases[] = { + TEST_CASE(test_env_getargv), + TEST_CASE(test_env_getauxval), + TEST_CASE(test_env_getenv), + TEST_CASE(test_env_set_child_env_and_argv), +}; + +static test_case_t child_test_cases[] = { + TEST_CASE(test_env_getauxval), + TEST_CASE(test_env_child_getargv), + TEST_CASE(test_env_child_getenv), +}; + +int main(int argc, const char* argv[]) { + // Save argument for test cases + g_argc = argc; + g_argv = argv; + // Test argc + if (getpid() > 1) { + return test_suite_run(child_test_cases, ARRAY_SIZE(child_test_cases)); + } else { + return test_suite_run(test_cases, ARRAY_SIZE(test_cases)); + } +} diff --git a/test/server/main.c b/test/server/main.c index 4665c81c..a8962926 100644 --- a/test/server/main.c +++ b/test/server/main.c @@ -37,7 +37,7 @@ int main(int argc, const char *argv[]) { } int client_pid; - char* client_argv[] = {"client", "127.0.0.1"}; + char* client_argv[] = {"client", "127.0.0.1", NULL}; ret = posix_spawn(&client_pid, "/bin/client", NULL, NULL, client_argv, NULL); if (ret < 0) { printf("spawn client process error: %s(errno: %d)\n", strerror(errno), errno); diff --git a/test/server_epoll/main.c b/test/server_epoll/main.c index 049e154c..dad1ffca 100644 --- a/test/server_epoll/main.c +++ b/test/server_epoll/main.c @@ -65,7 +65,7 @@ main(int argc, char *argv[]) { // spawn clients int client_pid; - char* client_argv[] = {"client", "127.0.0.1"}; + char* client_argv[] = {"client", "127.0.0.1", NULL}; for(int i=0; i<3; ++i) { int ret = posix_spawn(&client_pid, "/bin/client", NULL, NULL, client_argv, NULL); if (ret < 0) { diff --git a/tools/docker/Dockerfile b/tools/docker/Dockerfile index 63b4ae81..7ba7f269 100644 --- a/tools/docker/Dockerfile +++ b/tools/docker/Dockerfile @@ -14,6 +14,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ expect \ gdb \ git-core \ + jq \ kmod \ libboost-system-dev \ libboost-thread-dev \ diff --git a/tools/occlum b/tools/occlum index f59523ad..880c7041 100755 --- a/tools/occlum +++ b/tools/occlum @@ -103,7 +103,10 @@ cmd_build() { export OCCLUM_CONF_DEFAULT_MMAP_SIZE=`get_conf_default_mmap_size` cd "$context_dir/build" "$occlum_dir/build/bin/occlum-gen-default-occlum-json"\ + > "Occlum_new.json" + jq -s '.[0] + .[1]' "../../Occlum.json" "Occlum_new.json"\ > "Occlum.json" + rm -f "Occlum_new.json" "$occlum_dir/build/bin/occlum-protect-integrity" protect Occlum.json export OCCLUM_BUILTIN_CONF_FILE_MAC=`get_occlum_conf_file_mac`