| /*****************************************************************************\ | |
| * opt.c - options processing for salloc | |
| ***************************************************************************** | |
| * Copyright (C) 2002-2007 The Regents of the University of California. | |
| * Copyright (C) 2008-2010 Lawrence Livermore National Security. | |
| * Copyright (C) SchedMD LLC. | |
| * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). | |
| * Written by Mark Grondona <grondona1@llnl.gov>, et. al. | |
| * CODE-OCEC-09-009. All rights reserved. | |
| * | |
| * This file is part of Slurm, a resource management program. | |
| * For details, see <https://slurm.schedmd.com/>. | |
| * Please also read the included file: DISCLAIMER. | |
| * | |
| * Slurm is free software; you can redistribute it and/or modify it under | |
| * the terms of the GNU General Public License as published by the Free | |
| * Software Foundation; either version 2 of the License, or (at your option) | |
| * any later version. | |
| * | |
| * In addition, as a special exception, the copyright holders give permission | |
| * to link the code of portions of this program with the OpenSSL library under | |
| * certain conditions as described in each individual source file, and | |
| * distribute linked combinations including the two. You must obey the GNU | |
| * General Public License in all respects for all of the code used other than | |
| * OpenSSL. If you modify file(s) with this exception, you may extend this | |
| * exception to your version of the file(s), but you are not obligated to do | |
| * so. If you do not wish to do so, delete this exception statement from your | |
| * version. If you delete this exception statement from all source files in | |
| * the program, then also delete it here. | |
| * | |
| * Slurm is distributed in the hope that it will be useful, but WITHOUT ANY | |
| * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | |
| * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more | |
| * details. | |
| * | |
| * You should have received a copy of the GNU General Public License along | |
| * with Slurm; if not, write to the Free Software Foundation, Inc., | |
| * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
| \*****************************************************************************/ | |
| #include "config.h" | |
| #define _GNU_SOURCE | |
| #include <ctype.h> /* isdigit */ | |
| #include <fcntl.h> | |
| #include <getopt.h> | |
| #include <limits.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> /* getenv, strtol, etc. */ | |
| #include <sys/stat.h> | |
| #include <sys/types.h> | |
| #include <sys/utsname.h> | |
| #include <unistd.h> | |
| #include "slurm/slurm.h" | |
| #include "src/common/cpu_frequency.h" | |
| #include "src/common/list.h" | |
| #include "src/common/log.h" | |
| #include "src/common/parse_time.h" | |
| #include "src/common/proc_args.h" | |
| #include "src/common/read_config.h" /* contains getnodename() */ | |
| #include "src/common/slurm_protocol_api.h" | |
| #include "src/common/slurm_resource_info.h" | |
| #include "src/common/slurm_rlimits_info.h" | |
| #include "src/common/spank.h" | |
| #include "src/common/uid.h" | |
| #include "src/common/util-net.h" | |
| #include "src/common/x11_util.h" | |
| #include "src/common/xmalloc.h" | |
| #include "src/common/xstring.h" | |
| #include "src/interfaces/acct_gather_profile.h" | |
| #include "src/interfaces/cli_filter.h" | |
| #include "src/interfaces/runtime.h" | |
| #include "src/salloc/opt.h" | |
| #include "src/salloc/salloc.h" | |
| static void _help(void); | |
| static void _usage(void); | |
| static void _autocomplete(const char *query); | |
| /*---- global variables, defined in opt.h ----*/ | |
| salloc_opt_t saopt; | |
| slurm_opt_t opt = { | |
| .salloc_opt = &saopt, | |
| .help_func = _help, | |
| .usage_func = _usage, | |
| .autocomplete_func = _autocomplete, | |
| }; | |
| int error_exit = 1; | |
| bool first_pass = true; | |
| int immediate_exit = 1; | |
| bool interactive_step = false; | |
| /*---- forward declarations of static functions ----*/ | |
| typedef struct env_vars env_vars_t; | |
| static void _opt_env(void); | |
| static void _opt_args(int argc, char **argv, int het_job_offset); | |
| static bool _opt_verify(void); | |
| static void _set_options(int argc, char **argv); | |
| /*---[ end forward declarations of static functions ]---------------------*/ | |
| /* process options: | |
| * 1. set defaults | |
| * 2. update options with env vars | |
| * 3. update options with commandline args | |
| * 4. perform some verification that options are reasonable | |
| * | |
| * argc IN - Count of elements in argv | |
| * argv IN - Array of elements to parse | |
| * argc_off OUT - Offset of first non-parsable element | |
| * het_job_inx IN - offset of hetjob | |
| */ | |
| extern int initialize_and_process_args(int argc, char **argv, int *argc_off, | |
| int het_job_inx) | |
| { | |
| /* | |
| * Resolve the runtime before the option table is built in | |
| * _set_options(), so that the options the plugin provides are in it. | |
| */ | |
| if (runtime_g_init(NULL, RUNTIME_CTXT_SALLOC, NULL)) | |
| fatal("failed to initialize runtime plugin"); | |
| /* | |
| * Record which component these options describe before the reset, | |
| * which drops what the runtime holds for it. | |
| */ | |
| opt.het_job_inx = het_job_inx; | |
| /* initialize option defaults */ | |
| slurm_reset_all_options(&opt, first_pass); | |
| /* cli_filter plugins can change the defaults */ | |
| if (first_pass) { | |
| if (cli_filter_g_setup_defaults(&opt, false)) { | |
| error("cli_filter plugin terminated with error"); | |
| exit(error_exit); | |
| } | |
| } | |
| /* initialize options with env vars */ | |
| _opt_env(); | |
| /* initialize options with argv */ | |
| _opt_args(argc, argv, het_job_inx); | |
| if (argc_off) | |
| *argc_off = optind; | |
| if (opt.verbose) | |
| slurm_print_set_options(&opt); | |
| first_pass = false; | |
| return 1; | |
| } | |
| /* | |
| * If the node list supplied is a file name, translate that into | |
| * a list of nodes, we orphan the data pointed to | |
| * RET true if the node list is a valid one | |
| */ | |
| static bool _valid_node_list(char **node_list_pptr) | |
| { | |
| int count = NO_VAL; | |
| /* If we are using Arbitrary and we specified the number of | |
| procs to use then we need exactly this many since we are | |
| saying, lay it out this way! Same for max and min nodes. | |
| Other than that just read in as many in the hostfile */ | |
| if (opt.ntasks_set) | |
| count = opt.ntasks; | |
| else if (opt.nodes_set) { | |
| if (opt.max_nodes) | |
| count = opt.max_nodes; | |
| else if (opt.min_nodes) | |
| count = opt.min_nodes; | |
| } | |
| return verify_node_list(node_list_pptr, opt.distribution, count); | |
| } | |
| /*---[ env var processing ]-----------------------------------------------*/ | |
| /* | |
| * try to use a similar scheme as popt. | |
| * | |
| * in order to add a new env var (to be processed like an option): | |
| * | |
| * define a new entry into env_vars[], and match to the option used in | |
| * slurm_opt.c for slurm_process_option() | |
| */ | |
| struct env_vars { | |
| const char *var; | |
| int type; | |
| }; | |
| env_vars_t env_vars[] = { | |
| { "SALLOC_ACCOUNT", 'A' }, | |
| { "SALLOC_ACCTG_FREQ", LONG_OPT_ACCTG_FREQ }, | |
| { "SALLOC_BELL", LONG_OPT_BELL }, | |
| { "SALLOC_BURST_BUFFER", LONG_OPT_BURST_BUFFER_SPEC }, | |
| { "SALLOC_CLUSTER_CONSTRAINT", LONG_OPT_CLUSTER_CONSTRAINT }, | |
| { "SALLOC_CLUSTERS", 'M' }, | |
| { "SLURM_CLUSTERS", 'M' }, | |
| { "SALLOC_CONSOLIDATE_SEGMENTS", LONG_OPT_CONSOLIDATE_SEGMENTS }, | |
| { "SALLOC_CONTAINER_ID", LONG_OPT_CONTAINER_ID }, | |
| { "SALLOC_RUNTIME", LONG_OPT_RUNTIME }, | |
| { "SALLOC_CONSTRAINT", 'C' }, | |
| { "SALLOC_CORE_SPEC", 'S' }, | |
| { "SALLOC_CPU_FREQ_REQ", LONG_OPT_CPU_FREQ }, | |
| { "SALLOC_CPUS_PER_GPU", LONG_OPT_CPUS_PER_GPU }, | |
| { "SALLOC_DEBUG", 'v' }, | |
| { "SALLOC_DELAY_BOOT", LONG_OPT_DELAY_BOOT }, | |
| { "SALLOC_EXCLUSIVE", LONG_OPT_EXCLUSIVE }, | |
| { "SALLOC_GPUS", 'G' }, | |
| { "SALLOC_GPU_BIND", LONG_OPT_GPU_BIND }, | |
| { "SALLOC_GPU_FREQ", LONG_OPT_GPU_FREQ }, | |
| { "SALLOC_GPUS_PER_NODE", LONG_OPT_GPUS_PER_NODE }, | |
| { "SALLOC_GPUS_PER_SOCKET", LONG_OPT_GPUS_PER_SOCKET }, | |
| { "SALLOC_GPUS_PER_TASK", LONG_OPT_GPUS_PER_TASK }, | |
| { "SALLOC_GRES", LONG_OPT_GRES }, | |
| { "SALLOC_GRES_FLAGS", LONG_OPT_GRES_FLAGS }, | |
| { "SALLOC_IMMEDIATE", 'I' }, | |
| { "SALLOC_HINT", LONG_OPT_HINT }, | |
| { "SLURM_HINT", LONG_OPT_HINT }, | |
| { "SALLOC_KILL_CMD", 'K' }, | |
| { "SALLOC_MEM_BIND", LONG_OPT_MEM_BIND }, | |
| { "SALLOC_MEM_PER_CPU", LONG_OPT_MEM_PER_CPU }, | |
| { "SALLOC_MEM_PER_GPU", LONG_OPT_MEM_PER_GPU }, | |
| { "SALLOC_MEM_PER_NODE", LONG_OPT_MEM }, | |
| { "SALLOC_NETWORK", LONG_OPT_NETWORK }, | |
| { "SALLOC_NO_BELL", LONG_OPT_NO_BELL }, | |
| { "SALLOC_NO_KILL", 'k' }, | |
| { "SALLOC_OVERCOMMIT", 'O' }, | |
| { "SALLOC_PARTITION", 'p' }, | |
| { "SALLOC_POWER", LONG_OPT_POWER }, | |
| { "SALLOC_PROFILE", LONG_OPT_PROFILE }, | |
| { "SALLOC_QOS", 'q' }, | |
| { "SALLOC_RELAY_IO", LONG_OPT_RELAY_IO }, | |
| { "SALLOC_REQ_SWITCH", LONG_OPT_SWITCH_REQ }, | |
| { "SALLOC_RESERVATION", LONG_OPT_RESERVATION }, | |
| { "SALLOC_SEGMENT_SIZE", LONG_OPT_SEGMENT_SIZE }, | |
| { "SALLOC_SIGNAL", LONG_OPT_SIGNAL }, | |
| { "SALLOC_SPREAD_JOB", LONG_OPT_SPREAD_JOB }, | |
| { "SALLOC_SPREAD_SEGMENTS", LONG_OPT_SPREAD_SEGMENTS }, | |
| { "SALLOC_THREAD_SPEC", LONG_OPT_THREAD_SPEC }, | |
| { "SALLOC_THREADS_PER_CORE", LONG_OPT_THREADSPERCORE }, | |
| { "SALLOC_TIMELIMIT", 't' }, | |
| { "SALLOC_TRES_BIND", LONG_OPT_TRES_BIND }, | |
| { "SALLOC_TRES_PER_TASK", LONG_OPT_TRES_PER_TASK }, | |
| { "SALLOC_USE_MIN_NODES", LONG_OPT_USE_MIN_NODES }, | |
| { "SALLOC_WAIT_ALL_NODES", LONG_OPT_WAIT_ALL_NODES }, | |
| { "SALLOC_WAIT4SWITCH", LONG_OPT_SWITCH_WAIT }, | |
| { "SALLOC_WCKEY", LONG_OPT_WCKEY }, | |
| { NULL } | |
| }; | |
| /* | |
| * _opt_env(): used by initialize_and_process_args to set options via | |
| * environment variables. See comments above for how to | |
| * extend srun to process different vars | |
| */ | |
| static void _opt_env(void) | |
| { | |
| char *val = NULL; | |
| env_vars_t *e = env_vars; | |
| while (e->var) { | |
| if ((val = getenv(e->var)) != NULL) | |
| slurm_process_option_or_exit(&opt, e->type, val, true, | |
| false); | |
| e++; | |
| } | |
| if (!runtime_g_cli_process_env(opt.runtime_idx, opt.het_job_inx, | |
| RUNTIME_CTXT_SALLOC, NULL, false)) | |
| exit(error_exit); | |
| /* Process spank env options */ | |
| if (spank_process_env_options()) | |
| exit(error_exit); | |
| } | |
| static void _set_options(int argc, char **argv) | |
| { | |
| char *opt_string = NULL; | |
| int opt_char, option_index = 0; | |
| struct option *optz = slurm_option_table_create(&opt, &opt_string); | |
| opt.submit_line = slurm_option_get_argv_str(argc, argv); | |
| optind = 0; | |
| while ((opt_char = getopt_long(argc, argv, opt_string, | |
| optz, &option_index)) != -1) { | |
| slurm_process_option_or_exit(&opt, opt_char, optarg, false, | |
| false); | |
| } | |
| slurm_option_table_destroy(optz); | |
| xfree(opt_string); | |
| } | |
| /* | |
| * _opt_args() : set options via commandline args and popt | |
| */ | |
| static void _opt_args(int argc, char **argv, int het_job_offset) | |
| { | |
| int i; | |
| char **rest = NULL; | |
| _set_options(argc, argv); | |
| if ((optind < argc) && !xstrcmp(argv[optind], ":")) { | |
| debug("hetjob component separator"); | |
| } else { | |
| opt.argc = 0; | |
| if (optind < argc) { | |
| rest = argv + optind; | |
| while (rest[opt.argc] != NULL) | |
| opt.argc++; | |
| } | |
| opt.argv = (char **) xmalloc((opt.argc + 1) * sizeof(char *)); | |
| for (i = 0; i < opt.argc; i++) { | |
| if ((i == 0) && (rest == NULL)) | |
| break; /* Fix for CLANG false positive */ | |
| opt.argv[i] = xstrdup(rest[i]); | |
| } | |
| opt.argv[i] = NULL; /* End of argv's (for possible execv) */ | |
| } | |
| if (cli_filter_g_pre_submit(&opt, het_job_offset)) { | |
| error("cli_filter plugin terminated with error"); | |
| exit(error_exit); | |
| } | |
| if (!_opt_verify()) | |
| exit(error_exit); | |
| } | |
| /* return a string containing the default shell for this user */ | |
| static char *_get_shell(void) | |
| { | |
| uid_t uid = opt.uid; | |
| char *shell; | |
| if (uid == SLURM_AUTH_NOBODY) | |
| uid = getuid(); | |
| if (!(shell = uid_to_shell(uid))) | |
| fatal("no user information for user %u", uid); | |
| return shell; | |
| } | |
| static void _salloc_default_command(int *argcp, char **argvp[]) | |
| { | |
| if (xstrstr(slurm_conf.launch_params, "use_interactive_step")) { | |
| /* | |
| * Use srun out of the same directory as this process. | |
| */ | |
| char *command, *pos; | |
| command = xstrdup(argvzero); | |
| if ((pos = xstrrchr(command, '/'))) | |
| *(++pos) = '\0'; | |
| else | |
| *command = '\0'; | |
| xstrcat(command, "srun "); | |
| xstrcat(command, slurm_conf.interactive_step_opts); | |
| interactive_step = true; | |
| *argcp = 3; | |
| *argvp = xcalloc(4, sizeof (char *)); | |
| (*argvp)[0] = "/bin/sh"; | |
| (*argvp)[1] = "-c"; | |
| (*argvp)[2] = command; | |
| (*argvp)[3] = NULL; | |
| } else { | |
| *argcp = 1; | |
| *argvp = xmalloc(sizeof (char *) * 2); | |
| (*argvp)[0] = _get_shell(); | |
| (*argvp)[1] = NULL; | |
| } | |
| } | |
| /* | |
| * _opt_verify : perform some post option processing verification | |
| * | |
| */ | |
| static bool _opt_verify(void) | |
| { | |
| bool verified = true; | |
| hostlist_t *hl = NULL; | |
| int hl_cnt = 0; | |
| validate_options_salloc_sbatch_srun(&opt); | |
| if (opt.quiet && opt.verbose) { | |
| error ("don't specify both --verbose (-v) and --quiet (-Q)"); | |
| verified = false; | |
| } | |
| if ((opt.resv_port_cnt != NO_VAL) && | |
| !(opt.job_flags & STEPMGR_ENABLED) && | |
| !xstrstr(slurm_conf.slurmctld_params, "enable_stepmgr")) { | |
| error("Slurmstepd step management must be enabled to use --resv-ports for job allocations"); | |
| verified = false; | |
| } | |
| if (opt.burst_buffer && opt.burst_buffer_file) { | |
| error("Cannot specify both --bb and --bbf"); | |
| exit(error_exit); | |
| } else if (opt.burst_buffer_file) { | |
| buf_t *buf = create_mmap_buf(opt.burst_buffer_file); | |
| if (!buf) { | |
| error("Invalid --bbf specification"); | |
| exit(error_exit); | |
| } | |
| opt.burst_buffer = xstrdup(get_buf_data(buf)); | |
| FREE_NULL_BUFFER(buf); | |
| xfree(opt.burst_buffer_file); | |
| } | |
| if (opt.container_id && !getenv("SLURM_CONTAINER_ID")) | |
| setenvf(NULL, "SLURM_CONTAINER_ID", "%s", opt.container_id); | |
| if (opt.runtime && !getenv("SLURM_RUNTIME")) | |
| setenvf(NULL, "SLURM_RUNTIME", "%s", opt.runtime); | |
| if (opt.hint && | |
| !validate_hint_option(&opt)) { | |
| bool help_printed; | |
| xassert(opt.ntasks_per_core == NO_VAL); | |
| xassert(opt.threads_per_core == NO_VAL); | |
| if (verify_hint(opt.hint, &opt.sockets_per_node, | |
| &opt.cores_per_socket, &opt.threads_per_core, | |
| &opt.ntasks_per_core, NULL, &help_printed)) | |
| exit(help_printed ? 0 : error_exit); | |
| } | |
| if (opt.exclude && !_valid_node_list(&opt.exclude)) | |
| exit(error_exit); | |
| if (opt.nodelist && !opt.nodes_set && !xstrchr(opt.nodelist, '{')) { | |
| hl = hostlist_create(opt.nodelist); | |
| if (!hl) | |
| fatal("Invalid node list specified"); | |
| hostlist_uniq(hl); | |
| hl_cnt = hostlist_count(hl); | |
| opt.min_nodes = hl_cnt; | |
| opt.nodes_set = true; | |
| } | |
| if (opt.cpus_set && (opt.pn_min_cpus < opt.cpus_per_task)) | |
| opt.pn_min_cpus = opt.cpus_per_task; | |
| /* Set the env var so that the spawned srun can set it */ | |
| if (opt.oom_kill_step != NO_VAL16 && !getenv("SLURM_OOM_KILL_STEP")) | |
| setenvf(NULL, "SLURM_OOM_KILL_STEP", "%u", opt.oom_kill_step); | |
| /* | |
| * Only the component that gives the command decides if it is the | |
| * step | |
| */ | |
| interactive_step = false; | |
| if ((saopt.no_shell == false) && (opt.argc == 0)) | |
| _salloc_default_command(&opt.argc, &opt.argv); | |
| /* check for realistic arguments */ | |
| if (opt.ntasks <= 0) { | |
| error("invalid number of tasks (-n %d)", | |
| opt.ntasks); | |
| verified = false; | |
| } | |
| if (opt.cpus_set && (opt.cpus_per_task <= 0)) { | |
| error("invalid number of cpus per task (-c %d)", | |
| opt.cpus_per_task); | |
| verified = false; | |
| } | |
| if ((opt.min_nodes < 0) || (opt.max_nodes < 0) || | |
| (opt.max_nodes && (opt.min_nodes > opt.max_nodes))) { | |
| error("invalid number of nodes (-N %d-%d)", | |
| opt.min_nodes, opt.max_nodes); | |
| verified = false; | |
| } | |
| /* Check to see if user has specified enough resources to | |
| * satisfy the plane distribution with the specified | |
| * plane_size. | |
| * if (n/plane_size < N) and ((N-1) * plane_size >= n) --> | |
| * problem Simple check will not catch all the problem/invalid | |
| * cases. | |
| * The limitations of the plane distribution in the cons_tres | |
| * environment are more extensive and are documented in the | |
| * Slurm reference guide. */ | |
| if ((opt.distribution & SLURM_DIST_STATE_BASE) == SLURM_DIST_PLANE && | |
| opt.plane_size) { | |
| if ((opt.ntasks/opt.plane_size) < opt.min_nodes) { | |
| if (((opt.min_nodes-1)*opt.plane_size) >= opt.ntasks) { | |
| #if (0) | |
| info("Too few processes ((n/plane_size) %d < N %d) " | |
| "and ((N-1)*(plane_size) %d >= n %d)) ", | |
| opt.ntasks/opt.plane_size, opt.min_nodes, | |
| (opt.min_nodes-1)*opt.plane_size, | |
| opt.ntasks); | |
| #endif | |
| error("Too few processes for the requested " | |
| "{plane,node} distribution"); | |
| exit(error_exit); | |
| } | |
| } | |
| } | |
| /* massage the numbers */ | |
| if ((opt.nodes_set || opt.extra_set) && | |
| ((opt.min_nodes == opt.max_nodes) || (opt.max_nodes == 0)) && | |
| (opt.ntasks_per_node == NO_VAL) && | |
| !opt.ntasks_set) { | |
| /* 1 proc / node default */ | |
| opt.ntasks = opt.min_nodes; | |
| /* 1 proc / min_[socket * core * thread] default */ | |
| if (opt.sockets_per_node != NO_VAL) { | |
| opt.ntasks *= opt.sockets_per_node; | |
| opt.ntasks_set = true; | |
| } | |
| if (opt.cores_per_socket != NO_VAL) { | |
| opt.ntasks *= opt.cores_per_socket; | |
| opt.ntasks_set = true; | |
| } | |
| if (opt.threads_per_core != NO_VAL) { | |
| opt.ntasks *= opt.threads_per_core; | |
| opt.ntasks_set = true; | |
| } | |
| if (opt.ntasks_set && opt.verbose) | |
| info("Number of tasks implicitly set to %d", | |
| opt.ntasks); | |
| } else if (opt.nodes_set && opt.ntasks_set) { | |
| /* | |
| * Make sure that the number of | |
| * max_nodes is <= number of tasks | |
| */ | |
| if (opt.ntasks < opt.max_nodes) | |
| opt.max_nodes = opt.ntasks; | |
| /* | |
| * make sure # of procs >= min_nodes | |
| */ | |
| if (opt.ntasks < opt.min_nodes) { | |
| warning("can't run %d processes on %d nodes, setting nnodes to %d", | |
| opt.ntasks, opt.min_nodes, opt.ntasks); | |
| opt.min_nodes = opt.max_nodes = opt.ntasks; | |
| } | |
| } /* else if (opt.ntasks_set && !opt.nodes_set) */ | |
| /* set up the proc and node counts based on the arbitrary list | |
| of nodes */ | |
| if (((opt.distribution & SLURM_DIST_STATE_BASE) == SLURM_DIST_ARBITRARY) | |
| && (!opt.nodes_set || !opt.ntasks_set) | |
| && !xstrchr(opt.nodelist, '{')) { | |
| FREE_NULL_HOSTLIST(hl); | |
| hl = hostlist_create(opt.nodelist); | |
| if (!hl) | |
| fatal("Invalid node list specified"); | |
| if (!opt.ntasks_set) { | |
| opt.ntasks_set = 1; | |
| opt.ntasks = hostlist_count(hl); | |
| } | |
| if (!opt.nodes_set) { | |
| opt.nodes_set = 1; | |
| hostlist_uniq(hl); | |
| opt.min_nodes = opt.max_nodes = hostlist_count(hl); | |
| } | |
| } | |
| FREE_NULL_HOSTLIST(hl); | |
| if ((opt.deadline) && (opt.begin) && (opt.deadline < opt.begin)) { | |
| error("Incompatible begin and deadline time specification"); | |
| exit(error_exit); | |
| } | |
| if (opt.mem_bind_type && (getenv("SLURM_MEM_BIND") == NULL)) { | |
| char *tmp = slurm_xstr_mem_bind_type(opt.mem_bind_type); | |
| if (opt.mem_bind) { | |
| setenvf(NULL, "SLURM_MEM_BIND", "%s:%s", | |
| tmp, opt.mem_bind); | |
| } else { | |
| setenvf(NULL, "SLURM_MEM_BIND", "%s", tmp); | |
| } | |
| xfree(tmp); | |
| } | |
| if (opt.mem_bind_type && (getenv("SLURM_MEM_BIND_VERBOSE") == NULL)) { | |
| if (opt.mem_bind_type & MEM_BIND_VERBOSE) { | |
| setenvf(NULL, "SLURM_MEM_BIND_VERBOSE", "verbose"); | |
| } else { | |
| setenvf(NULL, "SLURM_MEM_BIND_VERBOSE", "quiet"); | |
| } | |
| } | |
| if (opt.ntasks_per_core > 0) { | |
| if ((opt.threads_per_core != NO_VAL) && | |
| (opt.threads_per_core < opt.ntasks_per_core)) { | |
| error("--ntasks-per-core (%d) can not be bigger than --threads-per-core (%d)", | |
| opt.ntasks_per_core, opt.threads_per_core); | |
| verified = false; | |
| } | |
| if (verified && !getenv("SLURM_NTASKS_PER_CORE")) { | |
| setenvf(NULL, "SLURM_NTASKS_PER_CORE", "%d", | |
| opt.ntasks_per_core); | |
| } | |
| } | |
| if ((opt.ntasks_per_gpu != NO_VAL) && | |
| (getenv("SLURM_NTASKS_PER_GPU") == NULL)) { | |
| setenvf(NULL, "SLURM_NTASKS_PER_GPU", "%d", | |
| opt.ntasks_per_gpu); | |
| } | |
| if ((opt.ntasks_per_node > 0) && | |
| (getenv("SLURM_NTASKS_PER_NODE") == NULL)) { | |
| setenvf(NULL, "SLURM_NTASKS_PER_NODE", "%d", | |
| opt.ntasks_per_node); | |
| } | |
| if ((opt.ntasks_per_socket > 0) && | |
| (getenv("SLURM_NTASKS_PER_SOCKET") == NULL)) { | |
| setenvf(NULL, "SLURM_NTASKS_PER_SOCKET", "%d", | |
| opt.ntasks_per_socket); | |
| } | |
| if ((opt.ntasks_per_tres != NO_VAL) && | |
| (getenv("SLURM_NTASKS_PER_TRES") == NULL)) { | |
| setenvf(NULL, "SLURM_NTASKS_PER_TRES", "%d", | |
| opt.ntasks_per_tres); | |
| } | |
| if (opt.profile) | |
| setenvfs("SLURM_PROFILE=%s", | |
| acct_gather_profile_to_string(opt.profile)); | |
| cpu_freq_set_env("SLURM_CPU_FREQ_REQ", | |
| opt.cpu_freq_min, opt.cpu_freq_max, opt.cpu_freq_gov); | |
| if ((saopt.wait_all_nodes == NO_VAL16) && | |
| (xstrcasestr(slurm_conf.sched_params, "salloc_wait_nodes"))) | |
| saopt.wait_all_nodes = 1; | |
| if (opt.x11 && opt.clusters) | |
| warning("X11 forwarding may not work reliably in combination with --clusters."); | |
| if (opt.x11) { | |
| x11_get_display(&opt.x11_target_port, &opt.x11_target); | |
| opt.x11_magic_cookie = x11_get_xauth(); | |
| } | |
| if (saopt.no_shell && !opt.job_name) | |
| opt.job_name = xstrdup("no-shell"); | |
| if (!opt.job_name) | |
| opt.job_name = xstrdup("interactive"); | |
| return verified; | |
| } | |
| /* Functions used by SPANK plugins to read and write job environment | |
| * variables for use within job's Prolog and/or Epilog */ | |
| extern char *spank_get_job_env(const char *name) | |
| { | |
| int i, len; | |
| char *tmp_str = NULL; | |
| if ((name == NULL) || (name[0] == '\0') || | |
| (strchr(name, (int)'=') != NULL)) { | |
| errno = EINVAL; | |
| return NULL; | |
| } | |
| xstrcat(tmp_str, name); | |
| xstrcat(tmp_str, "="); | |
| len = strlen(tmp_str); | |
| for (i=0; i<opt.spank_job_env_size; i++) { | |
| if (xstrncmp(opt.spank_job_env[i], tmp_str, len)) | |
| continue; | |
| xfree(tmp_str); | |
| return (opt.spank_job_env[i] + len); | |
| } | |
| return NULL; | |
| } | |
| extern int spank_set_job_env(const char *name, const char *value, | |
| int overwrite) | |
| { | |
| int i, len; | |
| char *tmp_str = NULL; | |
| if ((name == NULL) || (name[0] == '\0') || | |
| (strchr(name, (int)'=') != NULL)) { | |
| errno = EINVAL; | |
| return -1; | |
| } | |
| xstrcat(tmp_str, name); | |
| xstrcat(tmp_str, "="); | |
| len = strlen(tmp_str); | |
| xstrcat(tmp_str, value); | |
| for (i=0; i<opt.spank_job_env_size; i++) { | |
| if (xstrncmp(opt.spank_job_env[i], tmp_str, len)) | |
| continue; | |
| if (overwrite) { | |
| xfree(opt.spank_job_env[i]); | |
| opt.spank_job_env[i] = tmp_str; | |
| } else | |
| xfree(tmp_str); | |
| return 0; | |
| } | |
| /* Need to add an entry */ | |
| opt.spank_job_env_size++; | |
| xrealloc(opt.spank_job_env, sizeof(char *) * opt.spank_job_env_size); | |
| opt.spank_job_env[i] = tmp_str; | |
| return 0; | |
| } | |
| extern int spank_unset_job_env(const char *name) | |
| { | |
| int i, j, len; | |
| char *tmp_str = NULL; | |
| if ((name == NULL) || (name[0] == '\0') || | |
| (strchr(name, (int)'=') != NULL)) { | |
| errno = EINVAL; | |
| return -1; | |
| } | |
| xstrcat(tmp_str, name); | |
| xstrcat(tmp_str, "="); | |
| len = strlen(tmp_str); | |
| for (i=0; i<opt.spank_job_env_size; i++) { | |
| if (xstrncmp(opt.spank_job_env[i], tmp_str, len)) | |
| continue; | |
| xfree(opt.spank_job_env[i]); | |
| for (j=(i+1); j<opt.spank_job_env_size; i++, j++) | |
| opt.spank_job_env[i] = opt.spank_job_env[j]; | |
| opt.spank_job_env_size--; | |
| if (opt.spank_job_env_size == 0) | |
| xfree(opt.spank_job_env); | |
| return 0; | |
| } | |
| return 0; /* not found */ | |
| } | |
| static void _autocomplete(const char *query) | |
| { | |
| char *opt_string = NULL; | |
| struct option *optz = slurm_option_table_create(&opt, &opt_string); | |
| suggest_completion(optz, query); | |
| xfree(opt_string); | |
| slurm_option_table_destroy(optz); | |
| } | |
| static void _usage(void) | |
| { | |
| printf( | |
| "Usage: salloc [-N numnodes|[min nodes]-[max nodes]] [-n num-processors]\n" | |
| " [-c cpus-per-node] [-r n] [-p partition] [--hold] [-t minutes]\n" | |
| " [--immediate[=secs]] [--no-kill] [--overcommit] [-D path]\n" | |
| " [--oversubscribe] [-J jobname] [--verbose] [--licenses=names]\n" | |
| " [--clusters=cluster_names] [--contiguous]\n" | |
| " [--container-id=id] [--runtime=name]\n" | |
| " [--mincpus=n] [--mem=MB] [--tmp=MB] [-C list] [--max-pids=n|max]\n" | |
| " [--account=name] [--dependency=type:jobid[+time]] [--comment=name]\n" | |
| " [--mail-type=type] [--mail-user=user] [--nice[=value]]\n" | |
| " [--bell] [--no-bell] [--kill-command[=signal]] [--spread-job]\n" | |
| " [--spread-segments]\n" | |
| " [--nodefile=file] [--nodelist=hosts] [--exclude=hosts]\n" | |
| " [--network=type] [--mem-per-cpu=MB] [--qos=qos]\n" | |
| " [--mem-bind=...] [--reservation=name] [--mcs-label=mcs]\n" | |
| " [--time-min=minutes] [--gres=list] [--gres-flags=opts]\n" | |
| " [--cpu-freq=min[-max[:gov]]] [--power=flags] [--profile=...]\n" | |
| " [--switches=max-switches[@max-time-to-wait]]\n" | |
| " [--core-spec=cores] [--thread-spec=threads] [--reboot]\n" | |
| " [--bb=burst_buffer_spec] [--bbf=burst_buffer_file]\n" | |
| " [--delay-boot=mins] [--use-min-nodes]\n" | |
| " [--cpus-per-gpu=n] [--gpus=n] [--gpu-bind=...] [--gpu-freq=...]\n" | |
| " [--gpus-per-node=n] [--gpus-per-socket=n] [--gpus-per-task=n]\n" | |
| " [--mem-per-gpu=MB] [--tres-bind=...] [--tres-per-task=list]\n" | |
| " [--oom-kill-step[=0|1]]\n"); | |
| runtime_g_cli_print_usage(stdout, 14); | |
| printf(" [command [args...]]\n"); | |
| } | |
| static void _help(void) | |
| { | |
| slurm_conf_t *conf; | |
| printf ( | |
| "Usage: salloc [OPTIONS(0)...] [ : [OPTIONS(N)]] [command(0) [args(0)...]]\n" | |
| "\n" | |
| "Parallel run options:\n" | |
| " -A, --account=name charge job to specified account\n" | |
| " -b, --begin=time defer job until HH:MM MM/DD/YY\n" | |
| " --bell ring the terminal bell when the job is allocated\n" | |
| " --bb=<spec> burst buffer specifications\n" | |
| " --bbf=<file_name> burst buffer specification file\n" | |
| " -c, --cpus-per-task=ncpus number of cpus required per task\n" | |
| " --comment=name arbitrary comment\n" | |
| " --cpu-freq=min[-max[:gov]] requested cpu frequency (and governor)\n" | |
| " --delay-boot=mins delay boot for desired node features\n" | |
| " -d, --dependency=type:jobid[:time] defer job until condition on jobid is satisfied\n" | |
| " --deadline=time remove the job if no ending possible before\n" | |
| " this deadline (start > (deadline - time[-min]))\n" | |
| " -D, --chdir=path change working directory\n" | |
| " --get-user-env used by Moab. See srun man page.\n" | |
| " --gres=list required generic resources per node\n" | |
| " --gres-flags=opts flags related to GRES management\n" | |
| " -H, --hold submit job in held state\n" | |
| " -I, --immediate[=secs] exit if resources not available in \"secs\"\n" | |
| " -J, --job-name=jobname name of job\n" | |
| " -k, --no-kill do not kill job on node failure\n" | |
| " -K, --kill-command[=signal] signal to send terminating job\n" | |
| " --kv=json job KV metadata, a JSON object\n" | |
| " --layout=key=value,... job layout, comma-separated key=value pairs\n" | |
| " -L, --licenses=names required license, comma separated\n" | |
| " -M, --clusters=names Comma separated list of clusters to issue\n" | |
| " commands to. Default is current cluster.\n" | |
| " Name of 'all' will submit to run on all clusters.\n" | |
| " NOTE: SlurmDBD must up.\n" | |
| " -m, --distribution=type distribution method for processes to nodes\n" | |
| " (type = block|cyclic|arbitrary)\n" | |
| " --mail-type=type notify on state change: BEGIN, END, FAIL or ALL\n" | |
| " --mail-user=user who to send email notification for job state\n" | |
| " changes\n" | |
| " --max-pids=n|max maximum number of PIDs that may exist\n" | |
| " simultaneously in each step on each node\n" | |
| " --mcs-label=mcs mcs label if mcs plugin mcs/group is used\n" | |
| " -n, --ntasks=N number of processors required\n" | |
| " --nice[=value] decrease scheduling priority by value\n" | |
| " --no-bell do NOT ring the terminal bell\n" | |
| " --ntasks-per-node=n number of tasks to invoke on each node\n" | |
| " -N, --nodes=N number of nodes on which to run (N = min[-max])\n" | |
| " --oom-kill-step[=0|1] set the OOMKillStep behaviour\n" | |
| " -O, --overcommit overcommit resources\n" | |
| " --power=flags power management options\n" | |
| " --priority=value set the priority of the job to value\n" | |
| " --profile=value enable acct_gather_profile for detailed data\n" | |
| " value is all or none or any combination of\n" | |
| " energy, lustre, network or task\n" | |
| " -p, --partition=partition partition requested\n" | |
| " -q, --qos=qos quality of service\n" | |
| " -Q, --quiet quiet mode (suppress informational messages)\n" | |
| " --reboot reboot compute nodes before starting job\n" | |
| " --relay-io=yes|no also relay job output to an aggregate location\n" | |
| " -s, --oversubscribe oversubscribe resources with other jobs\n" | |
| " --signal=[R:]num[@time] send signal when time limit within time seconds\n" | |
| " --spread-job spread job across as many nodes as possible\n" | |
| " --spread-segments spread job segments across separate base blocks\n" | |
| " --switches=max-switches{@max-time-to-wait}\n" | |
| " Optimum switches and max time to wait for optimum\n" | |
| " -S, --core-spec=cores count of reserved cores\n" | |
| " --thread-spec=threads count of reserved threads\n" | |
| " -t, --time=minutes time limit\n" | |
| " --time-min=minutes minimum time limit (if distinct)\n" | |
| " --tres-bind=... task to tres binding options\n" | |
| " --tres-per-task=list list of tres required per task\n" | |
| " --use-min-nodes if a range of node counts is given, prefer the\n" | |
| " smaller count\n" | |
| " -v, --verbose verbose mode (multiple -v's increase verbosity)\n" | |
| " --wckey=wckey wckey to run job under\n" | |
| "\n" | |
| "Constraint options:\n" | |
| " --cluster-constraint=list specify a list of cluster constraints\n" | |
| " --contiguous demand a contiguous range of nodes\n" | |
| " -C, --constraint=list specify a list of constraints\n" | |
| " -F, --nodefile=filename request a specific list of hosts\n" | |
| " --mem=MB minimum amount of real memory\n" | |
| " --mincpus=n minimum number of logical processors (threads)\n" | |
| " per node\n" | |
| " --reservation=name allocate resources from named reservation\n" | |
| " --tmp=MB minimum amount of temporary disk\n" | |
| " -w, --nodelist=hosts... request a specific list of hosts\n" | |
| " -x, --exclude=hosts... exclude a specific list of hosts\n" | |
| "\n" | |
| "Container options:\n" | |
| " --container-id=id Container identifier\n" | |
| " --runtime=name Runtime plugin type\n" | |
| "\n" | |
| "Consumable resources related options:\n" | |
| " --exclusive[=user] allocate nodes in exclusive mode when\n" | |
| " cpu consumable resource is enabled\n" | |
| " --exclusive[=mcs] allocate nodes in exclusive mode when\n" | |
| " cpu consumable resource is enabled\n" | |
| " and mcs plugin is enabled\n" | |
| " --mem-per-cpu=MB maximum amount of real memory per allocated\n" | |
| " cpu required by the job.\n" | |
| " --mem >= --mem-per-cpu if --mem is specified.\n" | |
| " --resv-ports reserve communication ports\n" | |
| "\n" | |
| "Affinity/Multi-core options: (when the task/affinity plugin is enabled)\n" | |
| " For the following 4 options, you are\n" | |
| " specifying the minimum resources available for\n" | |
| " the node(s) allocated to the job.\n" | |
| " --sockets-per-node=S number of sockets per node to allocate\n" | |
| " --cores-per-socket=C number of cores per socket to allocate\n" | |
| " --threads-per-core=T number of threads per core to allocate\n" | |
| " -B, --extra-node-info=S[:C[:T]] combine request of sockets per node,\n" | |
| " cores per socket and threads per core.\n" | |
| " Specify an asterisk (*) as a placeholder\n" | |
| " or a minimum value.\n" | |
| "\n" | |
| " --ntasks-per-core=n number of tasks to invoke on each core\n" | |
| " --ntasks-per-socket=n number of tasks to invoke on each socket\n"); | |
| conf = slurm_conf_lock(); | |
| if (xstrstr(conf->task_plugin, "affinity")) { | |
| printf( | |
| " --hint= Bind tasks according to application hints\n" | |
| " (see \"--hint=help\" for options)\n" | |
| " --mem-bind= Bind memory to locality domains (ldom)\n" | |
| " (see \"--mem-bind=help\" for options)\n"); | |
| } | |
| slurm_conf_unlock(); | |
| printf("\n" | |
| "GPU scheduling options:\n" | |
| " --cpus-per-gpu=n number of CPUs required per allocated GPU\n" | |
| " -G, --gpus=n count of GPUs required for the job\n" | |
| " --gpu-bind=... task to gpu binding options\n" | |
| " --gpu-freq=... frequency and voltage of GPUs\n" | |
| " --gpus-per-node=n number of GPUs required per allocated node\n" | |
| " --gpus-per-socket=n number of GPUs required per allocated socket\n" | |
| " --gpus-per-task=n number of GPUs required per spawned task\n" | |
| " --mem-per-gpu=n real memory required per allocated GPU\n" | |
| ); | |
| runtime_g_cli_print_help(stdout, 6, 30); | |
| spank_print_options(stdout, 6, 30); | |
| printf("\n" | |
| "\n" | |
| "Help options:\n" | |
| " -h, --help show this help message\n" | |
| " --usage display brief usage message\n" | |
| "\n" | |
| "Other options:\n" | |
| " -V, --version output version information and exit\n" | |
| "\n" | |
| ); | |
| } |