/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <ctype.h>
#include "../modules/common/module-configs.h"
#include "docker-util.h"
#include "string-utils.h"
#include "util.h"
#include <grp.h>
#include <pwd.h>
#include <errno.h>

int entry_point = 0;

static int read_and_verify_command_file(const char *command_file, const char *docker_command,
                                        struct configuration *command_config) {
  int ret = 0;
  ret = read_config(command_file, command_config);
  if (ret != 0) {
    return INVALID_COMMAND_FILE;
  }
  char *command = get_configuration_value("docker-command", DOCKER_COMMAND_FILE_SECTION, command_config);
  if (command == NULL || (strcmp(command, docker_command) != 0)) {
    ret = INCORRECT_COMMAND;
  }
  free(command);
  return ret;
}

static int add_to_args(args *args, const char *string) {
  if (string == NULL) {
    return -1;
  }
  if (args->data == NULL || args->length >= DOCKER_ARG_MAX) {
    return -1;
  }
  char *clone = strdup(string);
  if (clone == NULL) {
    return -1;
  }
  if (args->data != NULL) {
    args->data[args->length] = clone;
    args->length++;
  }
  return 0;
}

void reset_args(args *args) {
  int i = 0;
  if (args == NULL) {
    return;
  }
  for (i = 0; i < args->length; i++) {
    free(args->data[i]);
  }
  args->length = 0;
}

char** extract_execv_args(args* args) {
  char** copy = (char**)malloc((args->length + 1) * sizeof(char*));
  for (int i = 0; i < args->length; i++) {
    copy[i] = args->data[i];
  }
  copy[args->length] = NULL;
  args->length = 0;
  return copy;
}

static int add_param_to_command(const struct configuration *command_config, const char *key, const char *param,
                                const int with_argument, args *args) {
  int ret = 0;
  char *tmp_buffer = NULL;
  char *value = get_configuration_value(key, DOCKER_COMMAND_FILE_SECTION, command_config);
  if (value != NULL) {
    if (with_argument) {
      tmp_buffer = make_string("%s%s", param, value);
      ret = add_to_args(args, tmp_buffer);
      free(tmp_buffer);
    } else if (strcmp(value, "true") == 0) {
      ret = add_to_args(args, param);
    }
    free(value);
    if (ret != 0) {
      ret = BUFFER_TOO_SMALL;
    }
  }
  return ret;
}

int check_trusted_image(const struct configuration *command_config, const struct configuration *conf) {
  int found = 0;
  int i = 0;
  int ret = 0;
  char *image_name = get_configuration_value("image", DOCKER_COMMAND_FILE_SECTION, command_config);
  char **privileged_registry = get_configuration_values_delimiter("docker.trusted.registries", CONTAINER_EXECUTOR_CFG_DOCKER_SECTION, conf, ",");
  char *registry_ptr = NULL;
  if (image_name == NULL) {
    ret = INVALID_DOCKER_IMAGE_NAME;
    goto free_and_exit;
  }
  if (privileged_registry != NULL) {
    for (i = 0; privileged_registry[i] != NULL; i++) {
      int len = strlen(privileged_registry[i]);
      if (privileged_registry[i][len - 1] != '/') {
        registry_ptr = (char *) alloc_and_clear_memory(len + 2, sizeof(char));
        strncpy(registry_ptr, privileged_registry[i], len);
        registry_ptr[len] = '/';
        registry_ptr[len + 1] = '\0';
      } else {
        registry_ptr = strdup(privileged_registry[i]);
      }
      if (strncmp(image_name, registry_ptr, strlen(registry_ptr))==0) {
        found=1;
        free(registry_ptr);
        break;
      }
      free(registry_ptr);
    }
  }
  if (found==0) {
    fprintf(ERRORFILE, "image: %s is not trusted.\n", image_name);
    ret = INVALID_DOCKER_IMAGE_TRUST;
  }

free_and_exit:
  free(image_name);
  free_values(privileged_registry);
  return ret;
}

static int is_regex(const char *str) {
  // regex should begin with prefix "regex:"
  return (strncmp(str, "regex:", 6) == 0);
}

static int is_valid_tmpfs_mount(const char *mount) {
  const char *regex_str = "^/[^:]+$";
  // execute_regex_match return 0 is matched success
  return execute_regex_match(regex_str, mount) == 0;
}

static int is_volume_name(const char *volume_name) {
  const char *regex_str = "^[a-zA-Z0-9]([a-zA-Z0-9_.-]*)$";
  // execute_regex_match return 0 is matched success
  return execute_regex_match(regex_str, volume_name) == 0;
}

static int is_volume_name_matched_by_regex(const char* requested, const char* pattern) {
  // execute_regex_match return 0 is matched success
  return is_volume_name(requested) && (execute_regex_match(pattern + sizeof("regex:"), requested) == 0);
}

static int add_param_to_command_if_allowed(const struct configuration *command_config,
                                           const struct configuration *executor_cfg,
                                           const char *key, const char *allowed_key, const char *param,
                                           const int multiple_values, const char prefix,
                                           args *args) {
  char *tmp_buffer = NULL;
  char *tmp_ptr = NULL;
  char **values = NULL;
  char **permitted_values = get_configuration_values_delimiter(allowed_key,
                                                               CONTAINER_EXECUTOR_CFG_DOCKER_SECTION, executor_cfg,
                                                               ",");
  int i = 0, j = 0, permitted = 0, ret = 0;
  if (multiple_values) {
    values = get_configuration_values_delimiter(key, DOCKER_COMMAND_FILE_SECTION, command_config, ",");
  } else {
    values = (char **) alloc_and_clear_memory(2, sizeof(char *));
    values[0] = get_configuration_value(key, DOCKER_COMMAND_FILE_SECTION, command_config);
    values[1] = NULL;
    if (values[0] == NULL) {
      ret = 0;
      goto free_and_exit;
    }
  }

  if (values != NULL) {
    // Disable capabilities, devices if image is not trusted.
    if (strcmp(key, "net") != 0) {
      if (check_trusted_image(command_config, executor_cfg) != 0) {
        fprintf(ERRORFILE, "Disable %s for untrusted image\n", key);
        ret = INVALID_DOCKER_IMAGE_TRUST;
        goto free_and_exit;
      }
    }

    if (permitted_values != NULL) {
      // Values are user requested.
      for (i = 0; values[i] != NULL; ++i) {
        permitted = 0;
        if(prefix != 0) {
          tmp_ptr = strchr(values[i], prefix);
          if (tmp_ptr == NULL) {
            fprintf(ERRORFILE, "Prefix char '%c' not found in '%s'\n",
                    prefix, values[i]);
            ret = -1;
            goto free_and_exit;
          }
        }
        // Iterate through each permitted value
        char* dst = NULL;
        char* pattern = NULL;

        for (j = 0; permitted_values[j] != NULL; ++j) {
          if (prefix == 0) {
            ret = strcmp(values[i], permitted_values[j]);
          } else {
            // If permitted-Values[j] is a REGEX, use REGEX to compare
            if (is_regex(permitted_values[j])) {
              dst = strndup(values[i], tmp_ptr - values[i]);
              pattern = strdup(permitted_values[j] + 6);
              ret = execute_regex_match(pattern, dst);
              free(dst);
              free(pattern);
            } else {
              ret = strncmp(values[i], permitted_values[j], tmp_ptr - values[i]);
            }
          }
          if (ret == 0) {
            permitted = 1;
            break;
          }
        }
        if (permitted == 1) {
          tmp_buffer = make_string("%s%s", param, values[i]);
          ret = add_to_args(args, tmp_buffer);
          free(tmp_buffer);
          if (ret != 0) {
            fprintf(ERRORFILE, "Output buffer too small\n");
            ret = BUFFER_TOO_SMALL;
            goto free_and_exit;
          }
        } else {
          fprintf(ERRORFILE, "Invalid param '%s' requested\n", values[i]);
          ret = -1;
          goto free_and_exit;
        }
      }
    } else {
      fprintf(ERRORFILE, "Invalid param '%s' requested, "
          "permitted values list is empty\n", values[0]);
      ret = -1;
      goto free_and_exit;
    }
  }

free_and_exit:
  free_values(values);
  free_values(permitted_values);
  return ret;
}

static int add_docker_config_param(const struct configuration *command_config, args *args) {
  return add_param_to_command(command_config, "docker-config", "--config=", 1, args);
}

static int validate_volume_name(const char *volume_name) {
  const char *regex_str = "^[a-zA-Z0-9]([a-zA-Z0-9_.-]*)$";
  return execute_regex_match(regex_str, volume_name);
}

static int validate_container_name(const char *container_name) {
  const char *CONTAINER_NAME_PREFIX = "container_";
  if (0 == strncmp(container_name, CONTAINER_NAME_PREFIX, strlen(CONTAINER_NAME_PREFIX))) {
    if (1 == validate_container_id(container_name)) {
      return 0;
    }
  }
  fprintf(ERRORFILE, "Specified container_id=%s is invalid\n", container_name);
  fflush(ERRORFILE);
  return INVALID_DOCKER_CONTAINER_NAME;
}

const char *get_docker_error_message(const int error_code) {

  switch (error_code) {
    case INVALID_COMMAND_FILE:
      return "Invalid command file passed";
    case INCORRECT_COMMAND:
      return "Incorrect command";
    case BUFFER_TOO_SMALL:
      return "Command buffer too small";
    case INVALID_DOCKER_CONTAINER_NAME:
      return "Invalid docker container name";
    case INVALID_DOCKER_IMAGE_NAME:
      return "Invalid docker image name";
    case INVALID_DOCKER_USER_NAME:
      return "Invalid docker user name";
    case INVALID_DOCKER_INSPECT_FORMAT:
      return "Invalid docker inspect format";
    case UNKNOWN_DOCKER_COMMAND:
      return "Unknown docker command";
    case INVALID_DOCKER_NETWORK:
      return "Invalid docker network";
    case INVALID_DOCKER_CAPABILITY:
      return "Invalid docker capability";
    case PRIVILEGED_CONTAINERS_DISABLED:
      return "Privileged containers are disabled";
    case INVALID_DOCKER_MOUNT:
      return "Invalid docker mount";
    case INVALID_DOCKER_RO_MOUNT:
      return "Invalid docker read-only mount";
    case INVALID_DOCKER_RW_MOUNT:
      return "Invalid docker read-write mount";
    case MOUNT_ACCESS_ERROR:
      return "Mount access error";
    case INVALID_DOCKER_DEVICE:
      return "Invalid docker device";
    case INVALID_DOCKER_VOLUME_DRIVER:
      return "Invalid docker volume-driver";
    case INVALID_DOCKER_VOLUME_NAME:
      return "Invalid docker volume name";
    case INVALID_DOCKER_VOLUME_COMMAND:
      return "Invalid docker volume command";
    case PID_HOST_DISABLED:
      return "Host pid namespace is disabled";
    case INVALID_PID_NAMESPACE:
      return "Invalid pid namespace";
    case INVALID_DOCKER_IMAGE_TRUST:
      return "Docker image is not trusted";
    case INVALID_DOCKER_TMPFS_MOUNT:
      return "Invalid docker tmpfs mount";
    default:
      return "Unknown error";
  }
}

int get_max_retries(const struct configuration *conf) {
  int retries = 10;
  char *max_retries = get_configuration_value(DOCKER_INSPECT_MAX_RETRIES_KEY,
      CONTAINER_EXECUTOR_CFG_DOCKER_SECTION, conf);
  if (max_retries != NULL) {
    retries = atoi(max_retries);
    free(max_retries);
  }
  return retries;
}

char *get_docker_binary(const struct configuration *conf) {
  char *docker_binary = NULL;
  docker_binary = get_configuration_value(DOCKER_BINARY_KEY, CONTAINER_EXECUTOR_CFG_DOCKER_SECTION, conf);
  if (docker_binary == NULL) {
    docker_binary = get_configuration_value(DOCKER_BINARY_KEY, "", conf);
    if (docker_binary == NULL) {
      docker_binary = strdup("/usr/bin/docker");
    }
  }
  return docker_binary;
}

int get_use_entry_point_flag() {
  return entry_point;
}

int docker_module_enabled(const struct configuration *conf) {
  struct section *section = get_configuration_section(CONTAINER_EXECUTOR_CFG_DOCKER_SECTION, conf);
  if (section != NULL) {
    return module_enabled(section, CONTAINER_EXECUTOR_CFG_DOCKER_SECTION);
  }
  return 0;
}

int get_docker_command(const char *command_file, const struct configuration *conf, args *args) {
  int ret = 0;
  struct configuration command_config = {0, NULL};

  ret = read_config(command_file, &command_config);
  if (ret != 0) {
    free_configuration(&command_config);
    return INVALID_COMMAND_FILE;
  }

  char *value = get_configuration_value("use-entry-point", DOCKER_COMMAND_FILE_SECTION, &command_config);
  if (value != NULL && strcasecmp(value, "true") == 0) {
    entry_point = 1;
  }
  free(value);

  char *docker = get_docker_binary(conf);
  ret = add_to_args(args, docker);
  free(docker);
  if (ret != 0) {
    free_configuration(&command_config);
    return BUFFER_TOO_SMALL;
  }

  ret = add_docker_config_param(&command_config, args);
  if (ret != 0) {
    free_configuration(&command_config);
    return BUFFER_TOO_SMALL;
  }

  char *command = get_configuration_value("docker-command", DOCKER_COMMAND_FILE_SECTION, &command_config);
  free_configuration(&command_config);
  if (strcmp(DOCKER_INSPECT_COMMAND, command) == 0) {
    ret = get_docker_inspect_command(command_file, conf, args);
  } else if (strcmp(DOCKER_KILL_COMMAND, command) == 0) {
    ret = get_docker_kill_command(command_file, conf, args);
  } else if (strcmp(DOCKER_LOAD_COMMAND, command) == 0) {
    ret = get_docker_load_command(command_file, conf, args);
  } else if (strcmp(DOCKER_PULL_COMMAND, command) == 0) {
    ret = get_docker_pull_command(command_file, conf, args);
  } else if (strcmp(DOCKER_RM_COMMAND, command) == 0) {
    ret = get_docker_rm_command(command_file, conf, args);
  } else if (strcmp(DOCKER_RUN_COMMAND, command) == 0) {
    ret = get_docker_run_command(command_file, conf, args);
  } else if (strcmp(DOCKER_STOP_COMMAND, command) == 0) {
    ret = get_docker_stop_command(command_file, conf, args);
  } else if (strcmp(DOCKER_VOLUME_COMMAND, command) == 0) {
    ret = get_docker_volume_command(command_file, conf, args);
  } else if (strcmp(DOCKER_START_COMMAND, command) == 0) {
    ret = get_docker_start_command(command_file, conf, args);
  } else {
    ret = UNKNOWN_DOCKER_COMMAND;
  }
  free(command);
  return ret;
}

// check if a key is permitted in the configuration
// return 1 if permitted
static int value_permitted(const struct configuration* executor_cfg,
                           const char* key, const char* value) {
  char **permitted_values = get_configuration_values_delimiter(key,
    CONTAINER_EXECUTOR_CFG_DOCKER_SECTION, executor_cfg, ",");
  if (!permitted_values) {
    return 0;
  }

  char** permitted = permitted_values;
  int found = 0;

  while (*permitted) {
    if (0 == strncmp(*permitted, value, 1024)) {
      found = 1;
      break;
    }
    permitted++;
  }

  free_values(permitted_values);

  return found;
}

int get_docker_volume_command(const char *command_file, const struct configuration *conf, args *args) {
  int ret = 0;
  char *driver = NULL, *volume_name = NULL, *sub_command = NULL, *format = NULL;
  struct configuration command_config = {0, NULL};
  ret = read_and_verify_command_file(command_file, DOCKER_VOLUME_COMMAND, &command_config);
  if (ret != 0) {
    goto cleanup;
  }
  sub_command = get_configuration_value("sub-command", DOCKER_COMMAND_FILE_SECTION, &command_config);

  if ((sub_command == NULL) || ((0 != strcmp(sub_command, "create")) &&
      (0 != strcmp(sub_command, "ls")))) {
    fprintf(ERRORFILE, "\"create/ls\" are the only acceptable sub-command of volume, input sub_command=\"%s\"\n",
       sub_command);
    ret = INVALID_DOCKER_VOLUME_COMMAND;
    goto cleanup;
  }

  ret = add_to_args(args, DOCKER_VOLUME_COMMAND);
  if (ret != 0) {
    goto cleanup;
  }

  if (0 == strcmp(sub_command, "create")) {
    volume_name = get_configuration_value("volume", DOCKER_COMMAND_FILE_SECTION, &command_config);
    if (volume_name == NULL || validate_volume_name(volume_name) != 0) {
      fprintf(ERRORFILE, "%s is not a valid volume name.\n", volume_name);
      ret = INVALID_DOCKER_VOLUME_NAME;
      goto cleanup;
    }

    driver = get_configuration_value("driver", DOCKER_COMMAND_FILE_SECTION, &command_config);
    if (driver == NULL) {
      ret = INVALID_DOCKER_VOLUME_DRIVER;
      goto cleanup;
    }

    ret = add_to_args(args, "create");
    if (ret != 0) {
      goto cleanup;
    }

    char *name_buffer = make_string("--name=%s", volume_name);
    ret = add_to_args(args, name_buffer);
    free(name_buffer);
    if (ret != 0) {
      ret = BUFFER_TOO_SMALL;
      goto cleanup;
    }

    if (!value_permitted(conf, "docker.allowed.volume-drivers", driver)) {
      fprintf(ERRORFILE, "%s is not permitted docker.allowed.volume-drivers\n",
        driver);
      ret = INVALID_DOCKER_VOLUME_DRIVER;
      goto cleanup;
    }

    char *driver_buffer = make_string("--driver=%s", driver);
    ret = add_to_args(args, driver_buffer);
    free(driver_buffer);
    if (ret != 0) {
      ret = BUFFER_TOO_SMALL;
      goto cleanup;
    }
  } else if (0 == strcmp(sub_command, "ls")) {
    format = get_configuration_value("format", DOCKER_COMMAND_FILE_SECTION, &command_config);

    ret = add_to_args(args, "ls");
    if (ret != 0) {
      goto cleanup;
    }
    if (format) {
      char *tmp_buffer = make_string("--format=%s", format);
      ret = add_to_args(args, tmp_buffer);
      free(tmp_buffer);
      if (ret != 0) {
        ret = BUFFER_TOO_SMALL;
        goto cleanup;
      }
    }
  }

cleanup:
  free_configuration(&command_config);
  free(driver);
  free(volume_name);
  free(sub_command);
  free(format);
  return ret;
}

int get_docker_inspect_command(const char *command_file, const struct configuration *conf, args *args) {
  const char *valid_format_strings[] = { "{{.State.Status}}",
                                "{{range(.NetworkSettings.Networks)}}{{.IPAddress}},{{end}}{{.Config.Hostname}}",
                                 "{{.State.Status}},{{.Config.StopSignal}}"};
  int ret = 0, i = 0, valid_format = 0;
  char *format = NULL, *container_name = NULL, *tmp_buffer = NULL;
  struct configuration command_config = {0, NULL};
  ret = read_and_verify_command_file(command_file, DOCKER_INSPECT_COMMAND, &command_config);
  if (ret != 0) {
    goto free_and_exit;
  }

  container_name = get_configuration_value("name", DOCKER_COMMAND_FILE_SECTION, &command_config);
  if (container_name == NULL || validate_container_name(container_name) != 0) {
    ret = INVALID_DOCKER_CONTAINER_NAME;
    goto free_and_exit;
  }

  format = get_configuration_value("format", DOCKER_COMMAND_FILE_SECTION, &command_config);
  if (format == NULL) {
    ret = INVALID_DOCKER_INSPECT_FORMAT;
    goto free_and_exit;
  }
  for (i = 0; i < 3; ++i) {
    if (strcmp(format, valid_format_strings[i]) == 0) {
      valid_format = 1;
      break;
    }
  }
  if (valid_format != 1) {
    fprintf(ERRORFILE, "Invalid format option '%s' not permitted\n", format);
    ret = INVALID_DOCKER_INSPECT_FORMAT;
    goto free_and_exit;
  }

  ret = add_to_args(args, DOCKER_INSPECT_COMMAND);
  if (ret != 0) {
    goto free_and_exit;
  }
  tmp_buffer = make_string("--format=%s", format);
  ret = add_to_args(args, tmp_buffer);
  free(tmp_buffer);
  if (ret != 0) {
    goto free_and_exit;
  }
  ret = add_to_args(args, container_name);
  if (ret != 0) {
    goto free_and_exit;
  }

free_and_exit:
  free_configuration(&command_config);
  free(format);
  free(container_name);
  return ret;
}

int get_docker_load_command(const char *command_file, const struct configuration *conf, args *args) {
  int ret = 0;
  char *image_name = NULL;
  struct configuration command_config = {0, NULL};
  ret = read_and_verify_command_file(command_file, DOCKER_LOAD_COMMAND, &command_config);
  if (ret != 0) {
    goto free_and_exit;
  }

  image_name = get_configuration_value("image", DOCKER_COMMAND_FILE_SECTION, &command_config);
  if (image_name == NULL) {
    ret = INVALID_DOCKER_IMAGE_NAME;
    goto free_and_exit;
  }

  ret = add_to_args(args, DOCKER_LOAD_COMMAND);
  if (ret == 0) {
    char *tmp_buffer = make_string("--i=%s", image_name);
    ret = add_to_args(args, tmp_buffer);
    free(tmp_buffer);
    if (ret != 0) {
      ret = BUFFER_TOO_SMALL;
    }
  }
free_and_exit:
  free(image_name);
  free_configuration(&command_config);
  return ret;
}

static int validate_docker_image_name(const char *image_name) {
  const char *regex_str = "^(([a-zA-Z0-9.-]+)(:[0-9]+)?/)?([a-z0-9_./-]+)(:[a-zA-Z0-9_.-]+)?$";
  return execute_regex_match(regex_str, image_name);
}

int get_docker_pull_command(const char *command_file, const struct configuration *conf, args *args) {
  int ret = 0;
  char *image_name = NULL;
  struct configuration command_config = {0, NULL};
  ret = read_and_verify_command_file(command_file, DOCKER_PULL_COMMAND, &command_config);
  if (ret != 0) {
    goto free_pull;
  }

  image_name = get_configuration_value("image", DOCKER_COMMAND_FILE_SECTION, &command_config);
  if (image_name == NULL || validate_docker_image_name(image_name) != 0) {
    ret = INVALID_DOCKER_IMAGE_NAME;
    goto free_pull;
  }

  ret = add_to_args(args, DOCKER_PULL_COMMAND);
  if (ret == 0) {
    ret = add_to_args(args, image_name);
  }
free_pull:
  free(image_name);
  free_configuration(&command_config);
  return ret;
}

int get_docker_rm_command(const char *command_file, const struct configuration *conf, args *args) {
  int ret = 0;
  char *container_name = NULL;
  struct configuration command_config = {0, NULL};
  ret = read_and_verify_command_file(command_file, DOCKER_RM_COMMAND, &command_config);
  if (ret != 0) {
    goto free_and_exit;
  }

  container_name = get_configuration_value("name", DOCKER_COMMAND_FILE_SECTION, &command_config);
  if (container_name == NULL || validate_container_name(container_name) != 0) {
    ret = INVALID_DOCKER_CONTAINER_NAME;
    goto free_and_exit;
  }

  ret = add_to_args(args, DOCKER_RM_COMMAND);
  if (ret == 0) {
    ret = add_to_args(args, container_name);
    if (ret != 0) {
      ret = BUFFER_TOO_SMALL;
    }
  }
free_and_exit:
  free(container_name);
  free_configuration(&command_config);
  return ret;
}

int get_docker_stop_command(const char *command_file, const struct configuration *conf,
                            args *args) {
  int ret = 0;
  size_t len = 0, i = 0;
  char *value = NULL;
  char *container_name = NULL;
  struct configuration command_config = {0, NULL};
  ret = read_and_verify_command_file(command_file, DOCKER_STOP_COMMAND, &command_config);
  if (ret != 0) {
    goto free_and_exit;
  }

  container_name = get_configuration_value("name", DOCKER_COMMAND_FILE_SECTION, &command_config);
  if (container_name == NULL || validate_container_name(container_name) != 0) {
    ret = INVALID_DOCKER_CONTAINER_NAME;
    goto free_and_exit;
  }

  ret = add_to_args(args, DOCKER_STOP_COMMAND);
  if (ret == 0) {
    value = get_configuration_value("time", DOCKER_COMMAND_FILE_SECTION, &command_config);
    if (value != NULL) {
      len = strlen(value);
      for (i = 0; i < len; ++i) {
        if (isdigit(value[i]) == 0) {
          fprintf(ERRORFILE, "Value for time is not a number '%s'\n", value);
          ret = INVALID_DOCKER_STOP_COMMAND;
          goto free_and_exit;
        }
      }
      char *time_buffer = make_string("--time=%s", value);
      ret = add_to_args(args, time_buffer);
      free(time_buffer);
      if (ret != 0) {
        goto free_and_exit;
      }
    }
    ret = add_to_args(args, container_name);
  }
free_and_exit:
  free(value);
  free(container_name);
  free_configuration(&command_config);
  return ret;
}

int get_docker_kill_command(const char *command_file, const struct configuration *conf,
                            args *args) {
  int ret = 0;
  size_t len = 0, i = 0;
  char *value = NULL;
  char *container_name = NULL;
  struct configuration command_config = {0, NULL};
  ret = read_and_verify_command_file(command_file, DOCKER_KILL_COMMAND, &command_config);
  if (ret != 0) {
    goto free_and_exit;
  }

  container_name = get_configuration_value("name", DOCKER_COMMAND_FILE_SECTION, &command_config);
  if (container_name == NULL || validate_container_name(container_name) != 0) {
    ret = INVALID_DOCKER_CONTAINER_NAME;
    goto free_and_exit;
  }

  ret = add_to_args(args, DOCKER_KILL_COMMAND);
  if (ret == 0) {
    value = get_configuration_value("signal", DOCKER_COMMAND_FILE_SECTION, &command_config);
    if (value != NULL) {
      len = strlen(value);
      for (i = 0; i < len; ++i) {
        if (isupper(value[i]) == 0) {
          fprintf(ERRORFILE, "Value for signal contains non-uppercase characters '%s'\n", value);
          ret = INVALID_DOCKER_KILL_COMMAND;
          goto free_and_exit;
        }
      }

      char *signal_buffer = make_string("--signal=%s", value);
      ret = add_to_args(args, signal_buffer);
      free(signal_buffer);
      if (ret != 0) {
        goto free_and_exit;
      }
    }
    ret = add_to_args(args, container_name);
  }
free_and_exit:
  free(value);
  free(container_name);
  free_configuration(&command_config);
  return ret;
}

int get_docker_start_command(const char *command_file, const struct configuration *conf, args *args) {
  int ret = 0;
  char *container_name = NULL;
  struct configuration command_config = {0, NULL};
  ret = read_and_verify_command_file(command_file, DOCKER_START_COMMAND, &command_config);
  if (ret != 0) {
    goto free_and_exit;
  }

  container_name = get_configuration_value("name", DOCKER_COMMAND_FILE_SECTION, &command_config);
  if (container_name == NULL || validate_container_name(container_name) != 0) {
    ret = INVALID_DOCKER_CONTAINER_NAME;
    goto free_and_exit;
  }

  ret = add_to_args(args, DOCKER_START_COMMAND);
  if (ret != 0) {
    goto free_and_exit;
  }
  ret = add_to_args(args, container_name);
free_and_exit:
  free(container_name);
  free_configuration(&command_config);
  return ret;
}

static int detach_container(const struct configuration *command_config, args *args) {
  return add_param_to_command(command_config, "detach", "-d", 0, args);
}

static int  rm_container_on_exit(const struct configuration *command_config, args *args) {
  return add_param_to_command(command_config, "rm", "--rm", 0, args);
}

static int set_container_workdir(const struct configuration *command_config, args *args) {
  return add_param_to_command(command_config, "workdir", "--workdir=", 1, args);
}

static int set_cgroup_parent(const struct configuration *command_config, args *args) {
  return add_param_to_command(command_config, "cgroup-parent", "--cgroup-parent=", 1, args);
}

static int set_hostname(const struct configuration *command_config, args *args) {
  return add_param_to_command(command_config, "hostname", "--hostname=", 1, args);
}

static int set_group_add(const struct configuration *command_config, args *args) {
  int i = 0, ret = 0;
  char **group_add = get_configuration_values_delimiter("group-add", DOCKER_COMMAND_FILE_SECTION, command_config, ",");
  char *privileged = NULL;

  privileged = get_configuration_value("privileged", DOCKER_COMMAND_FILE_SECTION, command_config);
  if (privileged != NULL && strcasecmp(privileged, "true") == 0 ) {
    goto free_and_exit;
  }

  if (group_add != NULL) {
    for (i = 0; group_add[i] != NULL; ++i) {
      ret = add_to_args(args, "--group-add");
      if (ret != 0) {
        goto free_and_exit;
      }
      ret = add_to_args(args, group_add[i]);
      if (ret != 0) {
        goto free_and_exit;
      }
    }
  }
free_and_exit:
  free_values(group_add);
  free(privileged);
  return ret;
}

static int set_network(const struct configuration *command_config,
                       const struct configuration *conf, args *args) {

  int ret = 0;
  ret = add_param_to_command_if_allowed(command_config, conf, "net",
                                        "docker.allowed.networks", "--net=",
                                        0, 0, args);
  if (ret != 0) {
    fprintf(ERRORFILE, "Could not find requested network in allowed networks\n");
    ret = INVALID_DOCKER_NETWORK;
  }

  return ret;
}

static int set_pid_namespace(const struct configuration *command_config,
                   const struct configuration *conf, args *args) {
  char *value = get_configuration_value("pid", DOCKER_COMMAND_FILE_SECTION,
      command_config);
  char *pid_host_enabled = get_configuration_value("docker.host-pid-namespace.enabled",
      CONTAINER_EXECUTOR_CFG_DOCKER_SECTION, conf);
  int ret = 0;

  if (value != NULL) {
    if (strcmp(value, "host") == 0) {
      if (pid_host_enabled != NULL) {
        if (strcmp(pid_host_enabled, "1") == 0 ||
            strcasecmp(pid_host_enabled, "True") == 0) {
          ret = add_to_args(args, "--pid=host");
          if (ret != 0) {
            ret = BUFFER_TOO_SMALL;
          }
        } else {
          fprintf(ERRORFILE, "Host pid namespace is disabled\n");
          ret = PID_HOST_DISABLED;
          goto free_and_exit;
        }
      } else {
        fprintf(ERRORFILE, "Host pid namespace is disabled\n");
        ret = PID_HOST_DISABLED;
        goto free_and_exit;
      }
    } else {
      fprintf(ERRORFILE, "Invalid pid namespace\n");
      ret = INVALID_PID_NAMESPACE;
    }
  }

free_and_exit:
  free(value);
  free(pid_host_enabled);
  return ret;
}

static int set_capabilities(const struct configuration *command_config,
                            const struct configuration *conf, args *args) {
  int ret = 0;

  ret = add_to_args(args, "--cap-drop=ALL");
  if (ret != 0) {
    return BUFFER_TOO_SMALL;
  }

  ret = add_param_to_command_if_allowed(command_config, conf, "cap-add",
                                        "docker.allowed.capabilities",
                                        "--cap-add=", 1, 0,
                                        args);
  switch (ret) {
    case 0:
      break;
    case INVALID_DOCKER_IMAGE_TRUST:
      fprintf(ERRORFILE, "Docker capability disabled for untrusted image\n");
      ret = 0;
      break;
    default:
      fprintf(ERRORFILE, "Invalid docker capability requested\n");
      ret = INVALID_DOCKER_CAPABILITY;
  }

  return ret;
}

static int set_devices(const struct configuration *command_config, const struct configuration *conf, args *args) {
  int ret = 0;
  ret = add_param_to_command_if_allowed(command_config, conf, "devices", "docker.allowed.devices", "--device=", 1, ':',
                                        args);
  if (ret != 0) {
    fprintf(ERRORFILE, "Invalid docker device requested\n");
    ret = INVALID_DOCKER_DEVICE;
  }

  return ret;
}

static int set_env(const struct configuration *command_config, struct args *args) {
  int ret = 0;
  // Use envfile method.
  char *envfile = get_configuration_value("environ", DOCKER_COMMAND_FILE_SECTION, command_config);
  if (envfile != NULL) {
    ret = add_to_args(args, "--env-file");
    if (ret != 0) {
      ret = BUFFER_TOO_SMALL;
    }
    ret = add_to_args(args, envfile);
    if (ret != 0) {
      ret = BUFFER_TOO_SMALL;
    }
    free(envfile);
  }
  return ret;
}

/**
 * Helper function to help normalize mounts for checking if mounts are
 * permitted. The function does the following -
 * 1. Find the canonical path for mount using realpath
 * 2. If the path is a directory, add a '/' at the end (if not present)
 * 3. Return a copy of the canonicalised path(to be freed by the caller)
 * @param mount path to be canonicalised
 * @param isRegexAllowed whether regex matching is allowed for normalize mount
 * @return pointer to canonicalised path, NULL on error
 */
static char* normalize_mount(const char* mount, int isRegexAllowed) {
  int ret = 0;
  struct stat buff;
  char *ret_ptr = NULL, *real_mount = NULL;
  if (mount == NULL) {
    return NULL;
  }
  real_mount = realpath(mount, NULL);
  if (real_mount == NULL) {
    // If mount is a valid named volume, just return it and let docker decide
    if (is_volume_name(mount)) {
      return strdup(mount);
    }
    // we only allow permitted mount to be REGEX, for permitted mount, we check
    // if it's a valid REGEX return; for user mount, we need to strictly check
    if (isRegexAllowed) {
      if (is_regex(mount)) {
        return strdup(mount);
      }
    }
    fprintf(ERRORFILE, "Could not determine real path of mount '%s'\n", mount);
    return NULL;
  }
  ret = stat(real_mount, &buff);
  if (ret == 0) {
    if (S_ISDIR(buff.st_mode)) {
      size_t len = strlen(real_mount);
      if (len <= 0) {
        free(real_mount);
        return NULL;
      }
      if (real_mount[len - 1] != '/') {
        ret_ptr = (char *) alloc_and_clear_memory(len + 2, sizeof(char));
        strncpy(ret_ptr, real_mount, len);
        ret_ptr[len] = '/';
        ret_ptr[len + 1] = '\0';
      } else {
        ret_ptr = strdup(real_mount);
      }
    } else {
      ret_ptr = strdup(real_mount);
    }
  } else {
    fprintf(ERRORFILE, "Could not stat path '%s'\n", real_mount);
    ret_ptr = NULL;
  }
  free(real_mount);
  return ret_ptr;
}

static int normalize_mounts(char **mounts, int isRegexAllowed) {
  int i = 0;
  char *tmp = NULL;
  if (mounts == NULL) {
    return 0;
  }
  for (i = 0; mounts[i] != NULL; ++i) {
    tmp = normalize_mount(mounts[i], isRegexAllowed);
    if (tmp == NULL) {
      return -1;
    }
    free(mounts[i]);
    mounts[i] = tmp;
  }
  return 0;
}

static int check_mount_permitted(const char **permitted_mounts, const char *requested) {
  int i = 0, ret = 0;
  size_t permitted_mount_len = 0;
  if (permitted_mounts == NULL) {
    return 0;
  }
  char *normalized_path = normalize_mount(requested, 0);
  if (normalized_path == NULL) {
    return -1;
  }
  for (i = 0; permitted_mounts[i] != NULL; ++i) {
    if (strcmp(normalized_path, permitted_mounts[i]) == 0) {
      ret = 1;
      break;
    }
    // if (permitted_mounts[i] is a REGEX): use REGEX to compare; return
    if (is_regex(permitted_mounts[i]) &&
    is_volume_name_matched_by_regex(normalized_path, permitted_mounts[i])) {
      ret = 1;
      break;
    }

    // directory check
    permitted_mount_len = strlen(permitted_mounts[i]);
    struct stat path_stat;
    stat(permitted_mounts[i], &path_stat);
    if(S_ISDIR(path_stat.st_mode)) {
      if (strncmp(normalized_path, permitted_mounts[i], permitted_mount_len) == 0) {
        ret = 1;
        break;
      }
    }
  }
  free(normalized_path);
  return ret;
}

static char* get_mount_source(const char *mount) {
  const char *tmp = strchr(mount, ':');
  if (tmp == NULL) {
    fprintf(ERRORFILE, "Invalid docker mount '%s'\n", mount);
    return NULL;
  }
  size_t len = tmp - mount;
  return strndup(mount, len);
}

static char* get_mount_type(const char *mount) {
  const char *tmp = strrchr(mount, ':');
  if (tmp == NULL) {
    fprintf(ERRORFILE, "Invalid docker mount '%s'\n", mount);
    return NULL;
  }
  if (strlen(tmp) < 2) {
    fprintf(ERRORFILE, "Invalid docker mount '%s'\n", mount);
    return NULL;
  }
  char *mount_type = strdup(&tmp[1]);
  if (strncmp("ro", mount_type, 2) != 0 &&
      strncmp("rw", mount_type, 2) != 0) {
    fprintf(ERRORFILE, "Invalid docker mount type '%s'\n", mount_type);
    free(mount_type);
    return NULL;
  }
  if (strlen(mount_type) > 2) {
    if (strlen(mount_type) < 8 ||
        (strcmp("shared", mount_type + 3) != 0 &&
        strcmp("rshared", mount_type + 3) != 0 &&
        strcmp("slave", mount_type + 3) != 0 &&
        strcmp("rslave", mount_type + 3) != 0 &&
        strcmp("private", mount_type + 3) != 0 &&
        strcmp("rprivate", mount_type + 3) != 0)) {
      fprintf(ERRORFILE, "Invalid docker mount type '%s'\n", mount_type);
      free(mount_type);
      return NULL;
    }
    mount_type[2] = ',';
  }
  return mount_type;
}

static int add_tmpfs_mounts(const struct configuration *command_config, args *args) {
  char **values = get_configuration_values_delimiter("tmpfs", DOCKER_COMMAND_FILE_SECTION, command_config, ",");
  int i = 0, ret = 0;
  if (values == NULL) {
    goto free_and_exit;
  }
  for (i = 0; values[i] != NULL; i++) {
    if (!is_valid_tmpfs_mount(values[i])) {
      fprintf(ERRORFILE, "Invalid docker tmpfs mount '%s'\n", values[i]);
      ret = INVALID_DOCKER_TMPFS_MOUNT;
      goto free_and_exit;
    }
    ret = add_to_args(args, "--tmpfs");
    if (ret != 0) {
      ret = BUFFER_TOO_SMALL;
      goto free_and_exit;
    }
    ret = add_to_args(args, values[i]);
    if (ret != 0) {
      ret = BUFFER_TOO_SMALL;
      goto free_and_exit;
    }
  }

free_and_exit:
  free_values(values);
  return ret;
}

static int add_mounts(const struct configuration *command_config, const struct configuration *conf, args *args) {
  const char *tmp_path_buffer[2] = {NULL, NULL};
  char *mount_src = NULL;
  char *mount_type = NULL;
  char **permitted_ro_mounts = get_configuration_values_delimiter("docker.allowed.ro-mounts",
                                                                  CONTAINER_EXECUTOR_CFG_DOCKER_SECTION, conf, ",");
  char **permitted_rw_mounts = get_configuration_values_delimiter("docker.allowed.rw-mounts",
                                                                  CONTAINER_EXECUTOR_CFG_DOCKER_SECTION, conf, ",");
  char **values = get_configuration_values_delimiter("mounts", DOCKER_COMMAND_FILE_SECTION, command_config, ",");
  char *config_path = get_config_path("");
  const char *container_executor_cfg_path = normalize_mount(config_path, 0);
  free(config_path);
  int i = 0, permitted_rw = 0, permitted_ro = 0, ret = 0;
  if (values == NULL) {
    goto free_and_exit;
  }
  // Disable mount volumes if image is not trusted.
  if (check_trusted_image(command_config, conf) != 0) {
    fprintf(ERRORFILE, "Disable mount volume for untrusted image\n");
    // YARN will implicitly bind node manager local directory to
    // docker image.  This can create file system security holes,
    // if docker container has binary to escalate privileges.
    // For untrusted image, we drop mounting without reporting
    // INVALID_DOCKER_MOUNT messages to allow running untrusted
    // image in a sandbox.
    ret = 0;
    goto free_and_exit;
  }
  ret = normalize_mounts(permitted_ro_mounts, 1);
  ret |= normalize_mounts(permitted_rw_mounts, 1);
  if (ret != 0) {
    fprintf(ERRORFILE, "Unable to find permitted docker mounts on disk\n");
    ret = MOUNT_ACCESS_ERROR;
    goto free_and_exit;
  }
  for (i = 0; values[i] != NULL; i++) {
    mount_src = get_mount_source(values[i]);
    if (mount_src == NULL) {
      fprintf(ERRORFILE, "Invalid docker mount '%s'\n", values[i]);
      ret = INVALID_DOCKER_MOUNT;
      goto free_and_exit;
    }
    mount_type = get_mount_type(values[i]);
    if (mount_type == NULL) {
      fprintf(ERRORFILE, "Invalid docker mount '%s'\n", values[i]);
      ret = INVALID_DOCKER_MOUNT;
      goto free_and_exit;
    }
    permitted_rw = check_mount_permitted((const char **) permitted_rw_mounts, mount_src);
    permitted_ro = check_mount_permitted((const char **) permitted_ro_mounts, mount_src);
    if (permitted_ro == -1 || permitted_rw == -1) {
      fprintf(ERRORFILE, "Invalid docker mount '%s', realpath=%s\n", values[i], mount_src);
      ret = INVALID_DOCKER_MOUNT;
      goto free_and_exit;
    }
    if (strncmp("rw", mount_type, 2) == 0) {
      // rw mount
      if (permitted_rw == 0) {
        fprintf(ERRORFILE, "Configuration does not allow docker mount '%s', realpath=%s\n", values[i], mount_src);
        ret = INVALID_DOCKER_RW_MOUNT;
        goto free_and_exit;
      } else {
        // determine if the user can modify the container-executor.cfg file
        tmp_path_buffer[0] = normalize_mount(mount_src, 0);
        // just re-use the function, flip the args to check if the container-executor path is in the requested
        // mount point
        ret = check_mount_permitted(tmp_path_buffer, container_executor_cfg_path);
        free((void *) tmp_path_buffer[0]);
        if (ret == 1) {
          fprintf(ERRORFILE, "Attempting to mount a parent directory '%s' of container-executor.cfg as read-write\n",
                  values[i]);
          ret = INVALID_DOCKER_RW_MOUNT;
          goto free_and_exit;
        }
      }
    } else {
      // ro mount
      if (permitted_ro == 0 && permitted_rw == 0) {
        fprintf(ERRORFILE, "Configuration does not allow docker mount '%s', realpath=%s\n", values[i], mount_src);
        ret = INVALID_DOCKER_RO_MOUNT;
        goto free_and_exit;
      }
    }

    if (strlen(mount_type) > 2) {
      // overwrite separator between read mode and propagation option with ','
      int mount_type_index = strlen(values[i]) - strlen(mount_type);
      values[i][mount_type_index + 2] = ',';
    }

    ret = add_to_args(args, "-v");
    if (ret != 0) {
      ret = BUFFER_TOO_SMALL;
      goto free_and_exit;
    }

    ret = add_to_args(args, values[i]);
    if (ret != 0) {
      ret = BUFFER_TOO_SMALL;
      goto free_and_exit;
    }
    free(mount_src);
    free(mount_type);
    mount_src = NULL;
    mount_type = NULL;
  }

free_and_exit:
  free(mount_src);
  free(mount_type);
  free_values(permitted_ro_mounts);
  free_values(permitted_rw_mounts);
  free_values(values);
  free((void *) container_executor_cfg_path);
  return ret;
}

static int check_privileges(const char *user) {
  int ngroups = 0;
  gid_t *groups = NULL;
  struct passwd *pw;
  struct group *gr;
  int ret = 0;
  int waitid = -1;
  int statval = 0;

  pw = getpwnam(user);
  if (pw == NULL) {
    fprintf(ERRORFILE, "User %s does not exist in host OS.\n", user);
    exit(INITIALIZE_USER_FAILED);
  }

  int rc = getgrouplist(user, pw->pw_gid, groups, &ngroups);
  if (rc < 0) {
    groups = (gid_t *) alloc_and_clear_memory(ngroups, sizeof(gid_t));
    if (groups == NULL) {
      fprintf(ERRORFILE, "Failed to allocate buffer for group lookup for user %s.\n", user);
      exit(OUT_OF_MEMORY);
    }
    if (getgrouplist(user, pw->pw_gid, groups, &ngroups) == -1) {
      fprintf(ERRORFILE, "Fail to lookup groups for user %s.\n", user);
      ret = 2;
    }
  }

  if (ret != 2) {
    for (int j = 0; j < ngroups; j++) {
      gr = getgrgid(groups[j]);
      if (gr != NULL) {
        if (strcmp(gr->gr_name, "root")==0 || strcmp(gr->gr_name, "docker")==0) {
          ret = 1;
          break;
        }
      }
    }
  }

  if (ret != 1) {
    int child_pid = fork();
    if (child_pid == 0) {
      execl("/usr/bin/sudo", "sudo", "-U", user, "-n", "-l", "docker", NULL);
      exit(INITIALIZE_USER_FAILED);
    } else {
      while ((waitid = waitpid(child_pid, &statval, 0)) != child_pid) {
        if (waitid == -1 && errno != EINTR) {
          fprintf(ERRORFILE, "waitpid failed: %s\n", strerror(errno));
          break;
        }
      }
      if (waitid == child_pid) {
        if (WIFEXITED(statval)) {
          if (WEXITSTATUS(statval) == 0) {
            ret = 1;
          }
        } else if (WIFSIGNALED(statval)) {
          fprintf(ERRORFILE, "sudo terminated by signal %d\n", WTERMSIG(statval));
        }
      }
    }
  }
  free(groups);
  if (ret == 1) {
    fprintf(ERRORFILE, "check privileges passed for user: %s\n", user);
  } else {
    fprintf(ERRORFILE, "check privileges failed for user: %s, error code: %d\n", user, ret);
    ret = 0;
  }
  return ret;
}

static int set_privileged(const struct configuration *command_config, const struct configuration *conf, args *args) {
  char *user = NULL;
  char *value = get_configuration_value("privileged", DOCKER_COMMAND_FILE_SECTION, command_config);
  char *privileged_container_enabled
      = get_configuration_value("docker.privileged-containers.enabled", CONTAINER_EXECUTOR_CFG_DOCKER_SECTION, conf);
  int ret = 0;
  int allowed = 1;

  user = get_configuration_value("user", DOCKER_COMMAND_FILE_SECTION, command_config);
  if (user == NULL) {
    ret = INVALID_DOCKER_USER_NAME;
    goto free_and_exit;
  }

  if (value != NULL && strcasecmp(value, "true") == 0 ) {
    if (privileged_container_enabled != NULL) {
      if (strcmp(privileged_container_enabled, "1") == 0 ||
          strcasecmp(privileged_container_enabled, "True") == 0) {
        // Disable set privileged if image is not trusted.
        if (check_trusted_image(command_config, conf) != 0) {
          fprintf(ERRORFILE, "Privileged containers are disabled from untrusted source\n");
          ret = PRIVILEGED_CONTAINERS_DISABLED;
          goto free_and_exit;
        }
        allowed = check_privileges(user);
        if (allowed) {
          ret = add_to_args(args, "--privileged");
          if (ret != 0) {
            ret = BUFFER_TOO_SMALL;
          }
        } else {
          fprintf(ERRORFILE, "Privileged containers are disabled for user: %s\n", user);
          ret = PRIVILEGED_CONTAINERS_DISABLED;
          goto free_and_exit;
        }
      } else {
        fprintf(ERRORFILE, "Privileged containers are disabled\n");
        ret = PRIVILEGED_CONTAINERS_DISABLED;
        goto free_and_exit;
      }
    } else {
      fprintf(ERRORFILE, "Privileged containers are disabled\n");
      ret = PRIVILEGED_CONTAINERS_DISABLED;
      goto free_and_exit;
    }
  }

free_and_exit:
  free(value);
  free(privileged_container_enabled);
  free(user);
  return ret;
}

int get_docker_run_command(const char *command_file, const struct configuration *conf, args *args) {
  int ret = 0, i = 0;
  char *container_name = NULL, *user = NULL, *image = NULL;
  char *tmp_buffer = NULL;
  char **launch_command = NULL;
  char *privileged = NULL;
  char *no_new_privileges_enabled = NULL;
  struct configuration command_config = {0, NULL};
  ret = read_and_verify_command_file(command_file, DOCKER_RUN_COMMAND, &command_config);
  if (ret != 0) {
    goto free_and_exit;
  }

  container_name = get_configuration_value("name", DOCKER_COMMAND_FILE_SECTION, &command_config);
  if (container_name == NULL || validate_container_name(container_name) != 0) {
    ret = INVALID_DOCKER_CONTAINER_NAME;
    goto free_and_exit;
  }
  user = get_configuration_value("user", DOCKER_COMMAND_FILE_SECTION, &command_config);
  if (user == NULL) {
    ret = INVALID_DOCKER_USER_NAME;
    goto free_and_exit;
  }
  image = get_configuration_value("image", DOCKER_COMMAND_FILE_SECTION, &command_config);
  if (image == NULL || validate_docker_image_name(image) != 0) {
    ret = INVALID_DOCKER_IMAGE_NAME;
    goto free_and_exit;
  }

  ret = add_to_args(args, DOCKER_RUN_COMMAND);
  if(ret != 0) {
    ret = BUFFER_TOO_SMALL;
    goto free_and_exit;
  }

  tmp_buffer = make_string("--name=%s", container_name);
  ret = add_to_args(args, tmp_buffer);
  free(tmp_buffer);
  if (ret != 0) {
    ret = BUFFER_TOO_SMALL;
    goto free_and_exit;
  }

  privileged = get_configuration_value("privileged", DOCKER_COMMAND_FILE_SECTION, &command_config);

  if (privileged == NULL || strcmp(privileged, "false") == 0) {
    char *user_buffer = make_string("--user=%s", user);
    ret = add_to_args(args, user_buffer);
    free(user_buffer);
    if (ret != 0) {
      ret = BUFFER_TOO_SMALL;
      goto free_and_exit;
    }
    no_new_privileges_enabled =
        get_configuration_value("docker.no-new-privileges.enabled",
        CONTAINER_EXECUTOR_CFG_DOCKER_SECTION, conf);
    if (no_new_privileges_enabled != NULL &&
        strcasecmp(no_new_privileges_enabled, "True") == 0) {
      ret = add_to_args(args, "--security-opt=no-new-privileges");
      if (ret != 0) {
        ret = BUFFER_TOO_SMALL;
        goto free_and_exit;
      }
    }
  }

  ret = detach_container(&command_config, args);
  if (ret != 0) {
    goto free_and_exit;
  }

  ret = rm_container_on_exit(&command_config, args);
  if (ret != 0) {
    goto free_and_exit;
  }

  ret = set_container_workdir(&command_config, args);
  if (ret != 0) {
    goto free_and_exit;
  }

  ret = set_network(&command_config, conf, args);
  if (ret != 0) {
    goto free_and_exit;
  }

  ret = set_pid_namespace(&command_config, conf, args);
  if (ret != 0) {
    goto free_and_exit;
  }

  ret = add_mounts(&command_config, conf, args);
  if (ret != 0) {
    goto free_and_exit;
  }

  ret = add_tmpfs_mounts(&command_config, args);
  if (ret != 0) {
    goto free_and_exit;
  }

  ret = set_cgroup_parent(&command_config, args);
  if (ret != 0) {
    goto free_and_exit;
  }

  ret = set_privileged(&command_config, conf, args);
  if (ret != 0) {
    goto free_and_exit;
  }

  ret = set_capabilities(&command_config, conf, args);
  if (ret != 0) {
    goto free_and_exit;
  }

  ret = set_hostname(&command_config, args);
  if (ret != 0) {
    goto free_and_exit;
  }

  ret = set_group_add(&command_config, args);
  if (ret != 0) {
    goto free_and_exit;
  }

  ret = set_devices(&command_config, conf, args);
  if (ret != 0) {
    goto free_and_exit;
  }

  ret = set_env(&command_config, args);
  if (ret != 0) {
    goto free_and_exit;
  }

  ret = add_to_args(args, image);
  if (ret != 0) {
    goto free_and_exit;
  }

  launch_command = get_configuration_values_delimiter("launch-command", DOCKER_COMMAND_FILE_SECTION, &command_config,
                                                      ",");
  if (launch_command != NULL) {
    for (i = 0; launch_command[i] != NULL; ++i) {
      ret = add_to_args(args, launch_command[i]);
      if (ret != 0) {
        ret = BUFFER_TOO_SMALL;
        goto free_and_exit;
      }
    }
  }
free_and_exit:
  if (ret != 0) {
    reset_args(args);
  }
  free(user);
  free(image);
  free(privileged);
  free(no_new_privileges_enabled);
  free(container_name);
  free_values(launch_command);
  free_configuration(&command_config);
  return ret;
}
