github.com/singularityware/singularity@v3.1.1+incompatible/docs/2.x-slurm/singularity.c (about)

     1  /* 
     2   * Copyright (c) 2017-2018, SyLabs, Inc. All rights reserved.
     3   * Copyright (c) 2017, SingularityWare, LLC. All rights reserved.
     4   *
     5   * Copyright (c) 2015-2017, Gregory M. Kurtzer. All rights reserved.
     6   * 
     7   * Copyright (c) 2016-2017, The Regents of the University of California,
     8   * through Lawrence Berkeley National Laboratory (subject to receipt of any
     9   * required approvals from the U.S. Dept. of Energy).  All rights reserved.
    10   * 
    11   * This software is licensed under a customized 3-clause BSD license.  Please
    12   * consult LICENSE file distributed with the sources of this project regarding
    13   * your rights to use or distribute this software.
    14   * 
    15   * NOTICE.  This Software was developed under funding from the U.S. Department of
    16   * Energy and the U.S. Government consequently retains certain rights. As such,
    17   * the U.S. Government has been granted for itself and others acting on its
    18   * behalf a paid-up, nonexclusive, irrevocable, worldwide license in the Software
    19   * to reproduce, distribute copies to the public, prepare derivative works, and
    20   * perform publicly and display publicly, and to permit other to do so. 
    21   * 
    22   */
    23  
    24  #define _GNU_SOURCE 1
    25  
    26  #include <stdio.h>
    27  #include <stdlib.h>
    28  #include <unistd.h>
    29  #include <sys/types.h>
    30  #include <errno.h>
    31  #include <string.h>
    32  #include <fcntl.h>
    33  
    34  #include "util/util.h"
    35  #include "util/file.h"
    36  #include "util/registry.h"
    37  #include "lib/image/image.h"
    38  #include "util/suid.h"
    39  #include "util/sessiondir.h"
    40  #include "util/cleanupd.h"
    41  #include "lib/runtime/runtime.h"
    42  #include "util/config_parser.h"
    43  #include "util/privilege.h"
    44  
    45  #include "action-lib/include.h"
    46  
    47  #include "slurm/spank.h"
    48  
    49  SPANK_PLUGIN(singularity, 1);
    50  
    51  #ifndef SYSCONFDIR
    52  #define SYSCONFDIR "/etc"
    53  #endif
    54  
    55  #define INT_MAX_STRING_SIZE 30
    56  
    57  // These should only be set post-fork and before exec;
    58  // this means reading / writing to these should be thread-safe.
    59  static char job_uid_str[INT_MAX_STRING_SIZE]; //Flawfinder: ignore
    60  static char job_gid_str[INT_MAX_STRING_SIZE]; //Flawfinder: ignore
    61  static char *job_image = NULL;
    62  static char *job_bindpath = NULL;
    63  
    64  static int setup_container_environment(spank_t spank)
    65  {
    66  
    67      uid_t job_uid = -1;
    68      gid_t job_gid = -1;
    69      char *job_cwd = NULL;
    70      char *bindpath = NULL;
    71      int argc;
    72      char **argv = NULL;
    73      int i;
    74  
    75       setenv("SINGULARITY_MESSAGELEVEL", "1", 0); //Don't overwrite if exists
    76  
    77      if (ESPANK_SUCCESS != spank_get_item(spank, S_JOB_UID, &job_uid)) {
    78          slurm_error("spank/%s: Failed to get job's target UID", plugin_name);
    79          return -1;
    80      }
    81      if (INT_MAX_STRING_SIZE <=
    82          snprintf(job_uid_str, INT_MAX_STRING_SIZE, "%u", job_uid)) { // Flawfinder: ignore
    83          slurm_error("spank/%s: Failed to serialize job's UID to string",
    84                      plugin_name);
    85          return -1;
    86      }
    87      if (setenv("SINGULARITY_TARGET_UID", job_uid_str, 1) < 0) {
    88          slurm_error("spank/%s: Failed to setenv(\"SINGULARITY_TARGET_UID\")",
    89                      plugin_name);
    90          return -1;
    91      }
    92  
    93      if (ESPANK_SUCCESS != spank_get_item(spank, S_JOB_GID, &job_gid)) {
    94          slurm_error("spank/%s: Failed to get job's target GID", plugin_name);
    95          return -1;
    96      }
    97      if (INT_MAX_STRING_SIZE <=
    98          snprintf(job_gid_str, INT_MAX_STRING_SIZE, "%u", job_gid)) { // Flawfinder: ignore
    99          slurm_error("spank/%s: Failed to serialize job's GID to string",
   100                      plugin_name);
   101          return -1;
   102      }
   103      if (setenv("SINGULARITY_TARGET_GID", job_gid_str, 1) < 0) {
   104          slurm_error("spank/%s: Failed to setenv(\"SINGULARITY_TARGET_GID\")",
   105                      plugin_name);
   106          return -1;
   107      }
   108  
   109      job_cwd = get_current_dir_name();
   110      if (!job_cwd) {
   111          slurm_error("spank/%s: Failed to determine job's correct PWD: %s",
   112                      plugin_name, strerror(errno));
   113          return -1;
   114      }
   115      if (setenv("SINGULARITY_TARGET_PWD", job_cwd, 1) < 0) {
   116          slurm_error("spank/%s: Failed to setenv(\"SINGULARITY_TARGET_PWD\")",
   117                      plugin_name);
   118          return -1;
   119      }
   120      /* setenv() makes a copy */
   121      free(job_cwd);
   122  
   123      if (!job_image) {
   124          slurm_error("spank/%s: Unable to determine job's image file.",
   125                      plugin_name);
   126          return -1;
   127      }
   128      if (setenv("SINGULARITY_IMAGE", job_image, 1) < 0) {
   129          slurm_error("spank/%s: Failed to setenv(\"SINGULARITY_IMAGE\")",
   130                      plugin_name);
   131          return -1;
   132      }
   133  
   134      if ((job_bindpath) &&
   135          (setenv("SINGULARITY_BINDPATH", job_bindpath, 1) < 0)) {
   136          slurm_error("spank/%s: Failed to setenv(\"SINGULARITY_BINDPATH\")",
   137                      plugin_name);
   138          return -1;
   139      }
   140  
   141      return 0;
   142  }
   143  
   144  static int setup_container_cwd() {
   145      singularity_message(DEBUG, "Trying to change directory to where we started\n");
   146      char *target_pwd = singularity_registry_get("TARGET_PWD");
   147  
   148      if (!target_pwd || (chdir(target_pwd) < 0)) {
   149          singularity_message(ERROR, "Failed to change into correct directory "
   150                              "(%s) inside container.",
   151                              target_pwd ? target_pwd : "UNKNOWN");
   152          return -1;
   153      }
   154      free(target_pwd);
   155      return 0;
   156  }
   157  
   158  static int setup_container(spank_t spank)
   159  {
   160      int rc;
   161      struct image_object image;
   162      char *command = NULL;
   163  
   164      if ((rc = setup_container_environment(spank)) != 0) { return rc; }
   165  
   166      /*
   167       * Ugg, singularity_* calls tend to call ABORT(255), which translates to
   168       * exit(255), all over the place.  The slurm SPANK hook API may not
   169       * expect such sudden death of the pending slurm task.  I've left
   170       * a bunch of following "return rc;" commented out, as the failure
   171       * conditions from singularity_* calls isn't clear to me.
   172       */
   173  
   174      // Before we do anything, check privileges and drop permission
   175      singularity_priv_init();
   176      singularity_priv_drop();
   177  
   178      singularity_message(VERBOSE, "Running Slurm/Singularity integration "
   179                          "plugin\n");
   180  
   181      if ((rc = singularity_config_init()) != 0) {
   182           return rc;
   183      }
   184  
   185  
   186      singularity_priv_init();
   187  //TODO    singularity_suid_init(argv);
   188  
   189      singularity_registry_init();
   190      singularity_priv_userns();
   191      singularity_priv_drop();
   192  
   193      singularity_cleanupd();
   194  
   195      singularity_runtime_ns(SR_NS_ALL);
   196  
   197      singularity_sessiondir();
   198  
   199      image = singularity_image_init(singularity_registry_get("IMAGE")); 
   200  
   201      if ( singularity_registry_get("WRITABLE") == NULL ) {
   202          singularity_image_open(&image, O_RDONLY);
   203      } else {
   204          singularity_image_open(&image, O_RDWR);
   205      }  
   206  
   207      singularity_image_check(&image);
   208      singularity_image_bind(&image);
   209      singularity_image_mount(&image, singularity_runtime_rootfs(NULL));
   210  
   211      action_ready(singularity_runtime_rootfs(NULL));
   212  
   213      singularity_runtime_overlayfs();
   214      singularity_runtime_mounts();
   215      singularity_runtime_files();
   216      singularity_runtime_enter();
   217  
   218      singularity_runtime_environment();
   219  
   220      singularity_priv_drop_perm();
   221  
   222   
   223      if ((rc = setup_container_cwd()) < 0) { 
   224         singularity_message(ERROR, "Could not obtain current directory.\n");
   225         return rc; 
   226      }
   227  
   228      envar_set("SINGULARITY_CONTAINER", singularity_image_name(&image), 1); // Legacy PS1 support
   229      envar_set("SINGULARITY_NAME", singularity_image_name(&image), 1);
   230      envar_set("SINGULARITY_SHELL", singularity_registry_get("SHELL"), 1);
   231  
   232      command = singularity_registry_get("COMMAND");
   233      singularity_message(LOG, "USER=%s, IMAGE='%s', COMMAND='%s'\n", singularity_priv_getuser(), singularity_image_name(&image), singularity_registry_get("COMMAND"));
   234  
   235      // At this point, the current process is in the runtime container environment.
   236      // Return control flow back to Slurm: when execv is invoked, it'll be done from
   237      // within the container.
   238  
   239      return 0;
   240  }
   241  
   242  // TODO: When run on the submit host, we should evaluate the URL and create/cache docker images as necessary.
   243  static int determine_image(int val, const char *optarg, int remote)
   244  {
   245      if (val) {}  // Suppresses unused error...
   246      // TODO: could do some basic path validation here in order to prevent an ABORT() later.
   247      job_image = strdup(optarg);
   248  
   249      return job_image == NULL;
   250  }
   251  
   252  static int determine_bind(int val, const char *optarg, int remote)
   253  {
   254      if (!job_bindpath) {
   255          job_bindpath = strdup(optarg);
   256      }
   257      return job_bindpath == NULL;
   258  }
   259  
   260  /// SPANK plugin functions.
   261  
   262  int slurm_spank_init(spank_t spank, int ac, char **av)
   263  {
   264      int i;
   265      struct spank_option image_opt,
   266                          bind_opt;
   267  
   268      memset(&image_opt, '\0', sizeof(image_opt));
   269      image_opt.name = "singularity-image";
   270      image_opt.arginfo = "[path]";
   271      image_opt.usage = "Specify a path to a Singularity image, directory tree, "
   272                        "or Docker image";
   273      image_opt.has_arg = 1;
   274      image_opt.val = 0;
   275      image_opt.cb = determine_image;
   276      if (ESPANK_SUCCESS != spank_option_register(spank, &image_opt)) {
   277          slurm_error("spank/%s: Unable to register a new option.",
   278                      plugin_name);
   279          return -1;
   280      }
   281  
   282      memset(&bind_opt, '\0', sizeof(bind_opt));
   283      bind_opt.name = "singularity-bind";
   284      bind_opt.arginfo = "[path || src:dest],...";
   285      bind_opt.usage = "Specify a user-bind path specification.  Can either be "
   286                       "a path or a src:dest pair, specifying the bind mount to "
   287                       "perform";
   288      bind_opt.has_arg = 1;
   289      bind_opt.val = 0;
   290      bind_opt.cb = determine_bind;
   291      if (ESPANK_SUCCESS != spank_option_register(spank, &bind_opt)) {
   292          slurm_error("spank/%s: Unable to register a new option.",
   293                      plugin_name);
   294          return -1;
   295      }
   296  
   297      // Make this a no-op except when starting the task.
   298      if (spank_context() == S_CTX_ALLOCATOR || (spank_remote(spank) != 1)) {
   299          return 0;
   300      }
   301  
   302      for (i = 0; i < ac; i++) {
   303          if (strncmp ("default_image=", av[i], 14) == 0) {
   304              const char *optarg = av[i] + 14;
   305              job_image = strdup(optarg);
   306          } else {
   307              slurm_error ("spank/%s: Invalid option: %s", av[i], plugin_name);
   308          }
   309      }
   310  
   311      return 0;
   312  }
   313  
   314  int slurm_spank_task_init_privileged(spank_t spank, int ac, char *argv[])
   315  {
   316      if (job_image) {
   317          return setup_container(spank);
   318      }
   319      return 0;
   320  }