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
This commit is contained in:
LI Qing 2019-08-23 20:48:43 +08:00 committed by Tate, Hongliang Tian
parent f37eb34038
commit 8ef52c7c2d
11 changed files with 214 additions and 41 deletions

@ -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<sgx_aes_gcm_128bit_tag_t, Error> {
pub struct Config {
pub vm: ConfigVM,
pub process: ConfigProcess,
pub env: Vec<CString>,
pub mount: Vec<ConfigMount>,
}
@ -137,6 +139,13 @@ impl Config {
fn from_input(input: &InputConfig) -> Result<Config, Error> {
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<String>,
#[serde(default)]
pub mount: Vec<InputConfigMount>,
}

@ -76,11 +76,10 @@ fn do_boot(path_str: &str, argv: &Vec<CString>) -> 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(())
}

@ -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<std::io::Error> for Error {
}
}
impl convert::From<std::ffi::NulError> 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

@ -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

@ -7,6 +7,10 @@
"default_heap_size": "16MB",
"default_mmap_size": "32MB"
},
"env": [
"OCCLUM=yes",
"TEST=true"
],
"mount": [
{
"target": "/",

8
test/env/Makefile vendored

@ -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)"

200
test/env/main.c vendored

@ -2,56 +2,200 @@
#include <stdio.h>
#include <elf.h>
#include <errno.h>
#include <spawn.h>
#include <sys/auxv.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#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));
}
}

@ -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);

@ -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) {

@ -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 \

@ -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`