int exec_container()

in hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/container-executor.c [1533:1752]


int exec_container(const char *command_file) {
  int exit_code = -1;
  struct configuration command_config = {0, NULL};
  char **args = NULL;
  char **env = NULL;
  char *workdir = NULL;
  char *docker_binary = get_docker_binary(&CFG);
  char *binary = NULL;
  int fdm, fds, rc;
  char input[4000];
  int docker = 0;
  char *user = NULL;

  int ret = read_config(command_file, &command_config);
  if (ret != 0) {
    free_configuration(&command_config);
    free(docker_binary);
    return INVALID_DOCKER_COMMAND_FILE;
  }

  char *value = get_configuration_value("docker-command", DOCKER_COMMAND_FILE_SECTION, &command_config);
  if (value != NULL && strcasecmp(value, "exec") == 0) {
    args = construct_docker_command(command_file);
    binary = strdup(docker_binary);
    docker = 1;
  } else {
    value = get_configuration_value("command", COMMAND_FILE_SECTION, &command_config);
    if (value != NULL && strcasecmp(value, "exec") == 0) {
      args = get_configuration_values_delimiter("launch-command",
                                                COMMAND_FILE_SECTION, &command_config, ",");
      if (args == NULL) {
        goto cleanup;
      }
      binary = strdup(args[0]);
      workdir = get_configuration_value("workdir", COMMAND_FILE_SECTION, &command_config);
      if (workdir == NULL) {
        goto cleanup;
      }
      env = (char **) alloc_and_clear_memory(3, sizeof(char *));
      env[0] = make_string("PWD=%s", workdir);
      env[1] = make_string("TERM=%s", "xterm-256color");
      env[2] = NULL;
      user = get_configuration_value("user", COMMAND_FILE_SECTION, &command_config);
      if (user == NULL) {
        goto cleanup;
      }
    } else {
      goto cleanup;
    }
  }

  fdm = posix_openpt(O_RDWR);
  if (fdm < 0) {
    fprintf(stderr, "Error %d on posix_openpt()\n", errno);
    exit_code = DOCKER_EXEC_FAILED;
    goto cleanup;
  }

  rc = grantpt(fdm);
  if (rc != 0) {
    fprintf(stderr, "Error %d on grantpt()\n", errno);
    exit_code = DOCKER_EXEC_FAILED;
    goto cleanup;
  }

  rc = unlockpt(fdm);
  if (rc != 0) {
    fprintf(stderr, "Error %d on unlockpt()\n", errno);
    exit_code = DOCKER_EXEC_FAILED;
    goto cleanup;
  }

  // Open the slave PTY
  fds = open(ptsname(fdm), O_RDWR);

  // Creation of a child process
  if (fork()) {
    fd_set fd_in;
    // Parent

    // Close the slave side of the PTY
    close(fds);
    while (1) {
      // Wait for data from standard input and master side of PTY
      FD_ZERO(&fd_in);
      FD_SET(0, &fd_in);
      FD_SET(fdm, &fd_in);
      rc = select(fdm + 1, &fd_in, NULL, NULL, NULL);
      switch(rc) {
        case -1 : fprintf(stderr, "Error %d on select()\n", errno);
                  exit(1);
        default :
            {
              // If data on standard input
              if (FD_ISSET(0, &fd_in)) {
                rc = read(0, input, sizeof(input));
                if (rc > 0) {
                  // Send data on the master side of PTY
                  ssize_t written = write(fdm, input, rc);
                  if (written == -1) {
                    fprintf(stderr, "Error %d writing to container.\n", errno);
                    exit(DOCKER_EXEC_FAILED);
                  }
                } else {
                  if (rc < 0) {
                    fprintf(stderr, "Error %d on read standard input\n", errno);
                    exit(DOCKER_EXEC_FAILED);
                  }
                }
              }

              // If data on master side of PTY
              if (FD_ISSET(fdm, &fd_in)) {
                rc = read(fdm, input, sizeof(input));
                if (rc > 0) {
                  // Send data on standard output
                  ssize_t written = write(1, input, rc);
                  if (written == -1) {
                    fprintf(stderr, "Error %d writing to terminal.\n", errno);
                    exit(DOCKER_EXEC_FAILED);
                  }
                } else {
                  if (rc < 0) {
                    if (errno == EIO) {
                      fprintf(stderr, "Remote Connection Closed.\n");
                      exit(0);
                    } else {
                      fprintf(stderr, "Error %d on read master PTY\n", errno);
                      exit(DOCKER_EXEC_FAILED);
                    }
                  }
                }
              }
            }
      } // End switch
    } // End while
  } else {
    struct termios slave_orig_term_settings; // Saved terminal settings
    struct termios new_term_settings; // Current terminal settings

    // Child

    // Close the master side of the PTY
    close(fdm);

    // Save the default parameters of the slave side of the PTY
    rc = tcgetattr(fds, &slave_orig_term_settings);

    // Set raw mode on the slave side of the PTY
    new_term_settings = slave_orig_term_settings;
    cfmakeraw (&new_term_settings);
    if (!docker) {
      new_term_settings.c_lflag |= ECHO;
    }
    tcsetattr (fds, TCSANOW, &new_term_settings);

    // The slave side of the PTY becomes the standard input and outputs of the child process
    close(STDIN_FILENO); // Close standard input (current terminal)
    close(STDOUT_FILENO); // Close standard output (current terminal)
    close(STDERR_FILENO); // Close standard error (current terminal)

    if (dup(fds) == -1) {
      // PTY becomes standard input (0)
      _exit(DOCKER_EXEC_FAILED);
    }
    if (dup(fds) == -1) {
      // PTY becomes standard output (1)
      _exit(DOCKER_EXEC_FAILED);
    }
    if (dup(fds) == -1) {
      // PTY becomes standard error (2)
      _exit(DOCKER_EXEC_FAILED);
    }

    // Now the original file descriptor is useless
    close(fds);

    // Make the current process a new session leader
    if (docker) {
      setsid();
    } else {
      exit_code = set_user(user);
      if (exit_code != 0) {
        _exit(exit_code);
      }
    }

    // As the child is a session leader, set the controlling terminal to be the slave side of the PTY
    // (Mandatory for programs like the shell to make them manage correctly their outputs)
    ioctl(0, TIOCSCTTY, 1);
    if (docker) {
      ret = execvp(binary, args);
      fprintf(ERRORFILE, "exec failed - %s\n", strerror(errno));
      _exit(DOCKER_EXEC_FAILED);
    } else {
      if (change_user(user_detail->pw_uid, user_detail->pw_gid) != 0) {
        _exit(DOCKER_EXEC_FAILED);
      }
      ret = chdir(workdir);
      if (ret != 0) {
        fprintf(ERRORFILE, "chdir failed - %s", strerror(errno));
        _exit(DOCKER_EXEC_FAILED);
      }
      execve(binary, args, env);
      fprintf(ERRORFILE, "exec failed - %s\n", strerror(errno));
      _exit(DOCKER_EXEC_FAILED);
    }
  }

cleanup:
  free(docker_binary);
  free(binary);
  free(user);
  free(workdir);
  free_values(args);
  free_values(env);
  free_configuration(&command_config);

  return exit_code; // we reach this point only if an error occurs
}