/*****************************************************************************\ * slurm_opt.c - salloc/sbatch/srun option processing functions ***************************************************************************** * Copyright (C) 2019 SchedMD LLC. * Written by Tim Wickberg * * This file is part of Slurm, a resource management program. * For details, see . * 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" #include #include #include "src/common/cpu_frequency.h" #include "src/common/log.h" #include "src/common/optz.h" #include "src/common/parse_time.h" #include "src/common/plugstack.h" #include "src/common/proc_args.h" #include "src/common/slurm_acct_gather_profile.h" #include "src/common/slurm_resource_info.h" #include "src/common/tres_bind.h" #include "src/common/tres_frequency.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/common/slurm_opt.h" /* * This is ugly. But... less ugly than dozens of identical functions handling * variables that are just strings pushed and pulled out of the associated * structures. * * This takes one argument: the desired field in slurm_opt_t. * The function name will be automatically generated as arg_set_##field. */ #define ADD_DATA_ERROR(str, rc) \ do { \ data_t *err = data_set_dict( \ data_list_append(errors)); \ data_set_string( \ data_key_set(err, "error"), str); \ data_set_int( \ data_key_set(err, "errno"), rc); \ } while (0) #define COMMON_STRING_OPTION(field) \ COMMON_STRING_OPTION_SET(field) \ COMMON_STRING_OPTION_SET_DATA(field) \ COMMON_STRING_OPTION_GET(field) \ COMMON_STRING_OPTION_RESET(field) #define COMMON_STRING_OPTION_GET_AND_RESET(field) \ COMMON_STRING_OPTION_GET(field) \ COMMON_STRING_OPTION_RESET(field) #define COMMON_STRING_OPTION_SET(field) \ static int arg_set_##field(slurm_opt_t *opt, const char *arg) \ __attribute__((nonnull (1))); \ static int arg_set_##field(slurm_opt_t *opt, const char *arg) \ { \ xfree(opt->field); \ opt->field = xstrdup(arg); \ \ return SLURM_SUCCESS; \ } #define COMMON_STRING_OPTION_SET_DATA(field) \ static int arg_set_data_##field(slurm_opt_t *opt, \ const data_t *arg, \ data_t *errors) \ __attribute__((nonnull (1, 2))); \ static int arg_set_data_##field(slurm_opt_t *opt, \ const data_t *arg, \ data_t *errors) \ { \ xfree(opt->field); \ return data_get_string_converted(arg, &opt->field); \ } #define COMMON_STRING_OPTION_GET(field) \ static char *arg_get_##field(slurm_opt_t *opt) \ __attribute__((nonnull)); \ static char *arg_get_##field(slurm_opt_t *opt) \ { \ return xstrdup(opt->field); \ } #define COMMON_STRING_OPTION_RESET(field) \ static void arg_reset_##field(slurm_opt_t *opt) \ __attribute__((nonnull)); \ static void arg_reset_##field(slurm_opt_t *opt) \ { \ xfree(opt->field); \ } #define COMMON_SBATCH_STRING_OPTION(field) \ COMMON_SBATCH_STRING_OPTION_SET(field) \ COMMON_SBATCH_STRING_OPTION_SET_DATA(field) \ COMMON_SBATCH_STRING_OPTION_GET(field) \ COMMON_SBATCH_STRING_OPTION_RESET(field) #define COMMON_SBATCH_STRING_OPTION_SET(field) \ static int arg_set_##field(slurm_opt_t *opt, const char *arg) \ __attribute__((nonnull (1))); \ static int arg_set_##field(slurm_opt_t *opt, const char *arg) \ { \ if (!opt->sbatch_opt) \ return SLURM_ERROR; \ \ xfree(opt->sbatch_opt->field); \ opt->sbatch_opt->field = xstrdup(arg); \ \ return SLURM_SUCCESS; \ } #define COMMON_SBATCH_STRING_OPTION_SET_DATA(field) \ static int arg_set_data_##field(slurm_opt_t *opt, \ const data_t *arg, \ data_t *errors) \ __attribute__((nonnull (1, 2))); \ static int arg_set_data_##field(slurm_opt_t *opt, \ const data_t *arg, \ data_t *errors) \ { \ if (!opt->sbatch_opt) \ return SLURM_ERROR; \ \ xfree(opt->sbatch_opt->field); \ return data_get_string_converted(arg, \ &opt->sbatch_opt->field); \ } #define COMMON_SBATCH_STRING_OPTION_GET(field) \ static char *arg_get_##field(slurm_opt_t *opt) \ __attribute__((nonnull)); \ static char *arg_get_##field(slurm_opt_t *opt) \ { \ if (!opt->sbatch_opt) \ return xstrdup("invalid-context"); \ return xstrdup(opt->sbatch_opt->field); \ } #define COMMON_SBATCH_STRING_OPTION_RESET(field) \ static void arg_reset_##field(slurm_opt_t *opt) \ __attribute__((nonnull)); \ static void arg_reset_##field(slurm_opt_t *opt) \ { \ if (opt->sbatch_opt) \ xfree(opt->sbatch_opt->field); \ } #define COMMON_SRUN_STRING_OPTION(field) \ COMMON_SRUN_STRING_OPTION_SET(field) \ COMMON_SRUN_STRING_OPTION_GET(field) \ COMMON_SRUN_STRING_OPTION_RESET(field) #define COMMON_SRUN_STRING_OPTION_SET(field) \ static int arg_set_##field(slurm_opt_t *opt, const char *arg) \ __attribute__((nonnull (1))); \ static int arg_set_##field(slurm_opt_t *opt, const char *arg) \ { \ if (!opt->srun_opt) \ return SLURM_ERROR; \ \ xfree(opt->srun_opt->field); \ opt->srun_opt->field = xstrdup(arg); \ \ return SLURM_SUCCESS; \ } #define COMMON_SRUN_STRING_OPTION_GET(field) \ static char *arg_get_##field(slurm_opt_t *opt) \ __attribute__((nonnull)); \ static char *arg_get_##field(slurm_opt_t *opt) \ { \ if (!opt->srun_opt) \ return xstrdup("invalid-context"); \ return xstrdup(opt->srun_opt->field); \ } #define COMMON_SRUN_STRING_OPTION_RESET(field) \ static void arg_reset_##field(slurm_opt_t *opt) \ __attribute__((nonnull)); \ static void arg_reset_##field(slurm_opt_t *opt) \ { \ if (opt->srun_opt) \ xfree(opt->srun_opt->field); \ } #define COMMON_OPTION_RESET(field, value) \ static void arg_reset_##field(slurm_opt_t *opt) \ __attribute__((nonnull)); \ static void arg_reset_##field(slurm_opt_t *opt) \ { \ opt->field = value; \ } #define COMMON_BOOL_OPTION(field, option) \ static int arg_set_##field(slurm_opt_t *opt, const char *arg) \ __attribute__((nonnull (1))); \ static int arg_set_##field(slurm_opt_t *opt, const char *arg) \ { \ opt->field = true; \ \ return SLURM_SUCCESS; \ } \ static int arg_set_data_##field(slurm_opt_t *opt, \ const data_t *arg, \ data_t *errors) \ __attribute__((nonnull (1, 2))); \ static int arg_set_data_##field(slurm_opt_t *opt, \ const data_t *arg, \ data_t *errors) \ { \ return data_copy_bool_converted(arg, &opt->field); \ } \ static char *arg_get_##field(slurm_opt_t *opt) \ __attribute__((nonnull)); \ static char *arg_get_##field(slurm_opt_t *opt) \ { \ return xstrdup(opt->field ? "set" : "unset"); \ } \ COMMON_OPTION_RESET(field, false) #define COMMON_SRUN_BOOL_OPTION(field) \ static int arg_set_##field(slurm_opt_t *opt, const char *arg) \ __attribute__((nonnull (1))); \ static int arg_set_##field(slurm_opt_t *opt, const char *arg) \ { \ if (!opt->srun_opt) \ return SLURM_ERROR; \ \ opt->srun_opt->field = true; \ \ return SLURM_SUCCESS; \ } \ static char *arg_get_##field(slurm_opt_t *opt) \ __attribute__((nonnull)); \ static char *arg_get_##field(slurm_opt_t *opt) \ { \ if (!opt->srun_opt) \ return xstrdup("invalid-context"); \ \ return xstrdup(opt->srun_opt->field ? "set" : "unset"); \ } \ static void arg_reset_##field(slurm_opt_t *opt) \ __attribute__((nonnull)); \ static void arg_reset_##field(slurm_opt_t *opt) \ { \ if (opt->srun_opt) \ opt->srun_opt->field = false; \ } #define COMMON_INT_OPTION(field, option) \ COMMON_INT_OPTION_SET(field, option) \ COMMON_INT_OPTION_SET_DATA(field) \ COMMON_INT_OPTION_GET(field) \ COMMON_OPTION_RESET(field, 0) #define COMMON_INT_OPTION_GET_AND_RESET(field) \ COMMON_INT_OPTION_GET(field) \ COMMON_OPTION_RESET(field, 0) #define COMMON_INT_OPTION_SET(field, option) \ static int arg_set_##field(slurm_opt_t *opt, const char *arg) \ __attribute__((nonnull (1))); \ static int arg_set_##field(slurm_opt_t *opt, const char *arg) \ { \ opt->field = parse_int(option, arg, true); \ \ return SLURM_SUCCESS; \ } #define COMMON_INT_OPTION_SET_DATA(field) \ static int arg_set_data_##field(slurm_opt_t *opt, \ const data_t *arg, \ data_t *errors) \ __attribute__((nonnull (1, 2))); \ static int arg_set_data_##field(slurm_opt_t *opt, \ const data_t *arg, \ data_t *errors) \ { \ int64_t val; \ int rc = data_get_int_converted(arg, &val); \ if (rc) \ ADD_DATA_ERROR("Unable to read integer value", \ rc); \ else if (val >= INT_MAX) { \ rc = SLURM_ERROR; \ ADD_DATA_ERROR("Integer too large", rc); \ } else if (val <= INT_MIN) { \ rc = SLURM_ERROR; \ ADD_DATA_ERROR("Integer too small", rc); \ } else \ opt->field = (int) val; \ return rc; \ } #define COMMON_INT_OPTION_GET(field) \ static char *arg_get_##field(slurm_opt_t *opt) \ __attribute__((nonnull)); \ static char *arg_get_##field(slurm_opt_t *opt) \ { \ return xstrdup_printf("%d", opt->field); \ } #define COMMON_MBYTES_OPTION(field, option) \ COMMON_MBYTES_OPTION_SET(field, option) \ COMMON_MBYTES_OPTION_SET_DATA(field, option) \ COMMON_MBYTES_OPTION_GET(field) \ COMMON_OPTION_RESET(field, NO_VAL64) #define COMMON_MBYTES_OPTION_SET(field, option) \ static int arg_set_##field(slurm_opt_t *opt, const char *arg) \ { \ if ((opt->field = str_to_mbytes2(arg)) == NO_VAL64) { \ error("Invalid " #option " specification"); \ exit(-1); \ } \ \ return SLURM_SUCCESS; \ } #define COMMON_MBYTES_OPTION_SET_DATA(field, option) \ static int arg_set_data_##field(slurm_opt_t *opt, const data_t *arg, \ data_t *errors) \ { \ char *str = NULL; \ int rc; \ if ((rc = data_get_string_converted(arg, &str))) \ ADD_DATA_ERROR("Invalid " #option " specification string", \ rc); \ else if ((opt->field = str_to_mbytes2(str)) == NO_VAL64) \ ADD_DATA_ERROR("Invalid " #option " specification", \ (rc = SLURM_ERROR)); \ xfree(str); \ return rc; \ } #define COMMON_MBYTES_OPTION_GET_AND_RESET(field) \ COMMON_MBYTES_OPTION_GET(field) \ COMMON_OPTION_RESET(field, NO_VAL64) #define COMMON_MBYTES_OPTION_GET(field) \ static char *arg_get_##field(slurm_opt_t *opt) \ { \ return mbytes2_to_str(opt->field); \ } #define COMMON_TIME_DURATION_OPTION_GET_AND_RESET(field) \ static char *arg_get_##field(slurm_opt_t *opt) \ __attribute__((nonnull)); \ static char *arg_get_##field(slurm_opt_t *opt) \ { \ char time_str[32]; \ mins2time_str(opt->field, time_str, sizeof(time_str)); \ return xstrdup(time_str); \ } \ COMMON_OPTION_RESET(field, NO_VAL) typedef struct { /* * DO NOT ALTER THESE FIRST FOUR ARGUMENTS * They must match 'struct option', so that some * casting abuse is nice and trivial. */ const char *name; /* Long option name. */ int has_arg; /* no_argument, required_argument, * or optional_argument */ int *flag; /* Always NULL in our usage. */ int val; /* Single character, or LONG_OPT_* */ /* * Add new members below here: */ bool reset_each_pass; /* Reset on all HetJob passes or only first */ bool sbatch_early_pass; /* For sbatch - run in the early pass. */ /* For salloc/srun - this is ignored, and will * run alongside all other options. */ bool srun_early_pass; /* For srun - run in the early pass. */ /* * If set_func is set, it will be used, and the command * specific versions must not be set. * Otherwise, command specific versions will be used. */ int (*set_func)(slurm_opt_t *, const char *); int (*set_func_salloc)(slurm_opt_t *, const char *); int (*set_func_sbatch)(slurm_opt_t *, const char *); int (*set_func_srun)(slurm_opt_t *, const char *); /* * data_t handlers * IN opt - job component options * IN arg - job component entry * IN/OUT errors - appends new dictionary to list of error details */ int (*set_func_data)(slurm_opt_t *opt, const data_t *arg, data_t *errors); /* Return must be xfree()'d */ char *(*get_func)(slurm_opt_t *); void (*reset_func)(slurm_opt_t *); } slurm_cli_opt_t; /* * Function names should be directly correlated with the slurm_opt_t field * they manipulate. But the slurm_cli_opt_t name should always match that * of the long-form name for the argument itself. * * These should be alphabetized by the slurm_cli_opt_t name. */ static int arg_set__unknown_(slurm_opt_t *opt, const char *arg) { if (opt->salloc_opt) fprintf(stderr, "Try \"salloc --help\" for more information\n"); else if (opt->sbatch_opt) fprintf(stderr, "Try \"sbatch --help\" for more information\n"); else if (opt->srun_opt) fprintf(stderr, "Try \"srun --help\" for more information\n"); exit(-1); return SLURM_SUCCESS; } static char *arg_get__unknown_(slurm_opt_t *opt) { return NULL; /* no op */ } static void arg_reset__unknown_(slurm_opt_t *opt) { /* no op */ } static slurm_cli_opt_t slurm_opt__unknown_ = { .name = NULL, .has_arg = no_argument, .val = '?', .set_func = arg_set__unknown_, .get_func = arg_get__unknown_, .reset_func = arg_reset__unknown_, }; static int arg_set_accel_bind_type(slurm_opt_t *opt, const char *arg) { if (!opt->srun_opt) return SLURM_ERROR; if (strchr(arg, 'v')) opt->srun_opt->accel_bind_type |= ACCEL_BIND_VERBOSE; if (strchr(arg, 'g')) opt->srun_opt->accel_bind_type |= ACCEL_BIND_CLOSEST_GPU; if (strchr(arg, 'm')) opt->srun_opt->accel_bind_type |= ACCEL_BIND_CLOSEST_MIC; if (strchr(arg, 'n')) opt->srun_opt->accel_bind_type |= ACCEL_BIND_CLOSEST_NIC; if (!opt->srun_opt->accel_bind_type) { error("Invalid --accel-bind specification"); exit(-1); } return SLURM_SUCCESS; } static char *arg_get_accel_bind_type(slurm_opt_t *opt) { char *tmp = NULL; if (!opt->srun_opt) return xstrdup("invalid-context"); if (opt->srun_opt->accel_bind_type & ACCEL_BIND_VERBOSE) xstrcat(tmp, "v"); if (opt->srun_opt->accel_bind_type & ACCEL_BIND_CLOSEST_GPU) xstrcat(tmp, "g"); if (opt->srun_opt->accel_bind_type & ACCEL_BIND_CLOSEST_MIC) xstrcat(tmp, "m"); if (opt->srun_opt->accel_bind_type & ACCEL_BIND_CLOSEST_NIC) xstrcat(tmp, "n"); return tmp; } static void arg_reset_accel_bind_type(slurm_opt_t *opt) { if (opt->srun_opt) opt->srun_opt->accel_bind_type = 0; } static slurm_cli_opt_t slurm_opt_accel_bind = { .name = "accel-bind", .has_arg = required_argument, .val = LONG_OPT_ACCEL_BIND, .set_func_srun = arg_set_accel_bind_type, .get_func = arg_get_accel_bind_type, .reset_func = arg_reset_accel_bind_type, .reset_each_pass = true, }; COMMON_STRING_OPTION(account); static slurm_cli_opt_t slurm_opt_account = { .name = "account", .has_arg = required_argument, .val = 'A', .set_func = arg_set_account, .set_func_data = arg_set_data_account, .get_func = arg_get_account, .reset_func = arg_reset_account, }; static int arg_set_acctg_freq(slurm_opt_t *opt, const char *arg) { xfree(opt->acctg_freq); opt->acctg_freq = xstrdup(arg); if (validate_acctg_freq(opt->acctg_freq)) exit(-1); return SLURM_SUCCESS; } COMMON_STRING_OPTION_GET_AND_RESET(acctg_freq); COMMON_STRING_OPTION_SET_DATA(acctg_freq); static slurm_cli_opt_t slurm_opt_acctg_freq = { .name = "acctg-freq", .has_arg = required_argument, .val = LONG_OPT_ACCTG_FREQ, .set_func = arg_set_acctg_freq, .set_func_data = arg_set_data_acctg_freq, .get_func = arg_get_acctg_freq, .reset_func = arg_reset_acctg_freq, }; static int arg_set_alloc_nodelist(slurm_opt_t *opt, const char *arg) { if (!opt->srun_opt) return SLURM_ERROR; xfree(opt->srun_opt->alloc_nodelist); opt->srun_opt->alloc_nodelist = xstrdup(arg); return SLURM_SUCCESS; } static char *arg_get_alloc_nodelist(slurm_opt_t *opt) { if (!opt->srun_opt) return xstrdup("invalid-context"); return xstrdup(opt->srun_opt->alloc_nodelist); } static void arg_reset_alloc_nodelist(slurm_opt_t *opt) { if (opt->srun_opt) xfree(opt->srun_opt->alloc_nodelist); } static slurm_cli_opt_t slurm_opt_alloc_nodelist = { .name = NULL, /* envvar only */ .has_arg = required_argument, .val = LONG_OPT_ALLOC_NODELIST, .set_func = arg_set_alloc_nodelist, .get_func = arg_get_alloc_nodelist, .reset_func = arg_reset_alloc_nodelist, .reset_each_pass = true, }; COMMON_SBATCH_STRING_OPTION(array_inx); static slurm_cli_opt_t slurm_opt_array = { .name = "array", .has_arg = required_argument, .val = 'a', .set_func_sbatch = arg_set_array_inx, .set_func_data = arg_set_data_array_inx, .get_func = arg_get_array_inx, .reset_func = arg_reset_array_inx, }; COMMON_SBATCH_STRING_OPTION(batch_features); static slurm_cli_opt_t slurm_opt_batch = { .name = "batch", .has_arg = required_argument, .val = LONG_OPT_BATCH, .set_func_sbatch = arg_set_batch_features, .set_func_data = arg_set_data_batch_features, .get_func = arg_get_batch_features, .reset_func = arg_reset_batch_features, }; COMMON_STRING_OPTION(burst_buffer_file); static slurm_cli_opt_t slurm_opt_bbf = { .name = "bbf", .has_arg = required_argument, .val = LONG_OPT_BURST_BUFFER_FILE, .set_func = arg_set_burst_buffer_file, .set_func_data = arg_set_data_burst_buffer_file, .get_func = arg_get_burst_buffer_file, .reset_func = arg_reset_burst_buffer_file, }; static int arg_set_bcast(slurm_opt_t *opt, const char *arg) { if (!opt->srun_opt) return SLURM_ERROR; opt->srun_opt->bcast_flag = true; opt->srun_opt->bcast_file = xstrdup(arg); return SLURM_SUCCESS; } static char *arg_get_bcast(slurm_opt_t *opt) { if (!opt->srun_opt) return xstrdup("invalid-context"); if (opt->srun_opt->bcast_flag && !opt->srun_opt->bcast_file) return xstrdup("set"); else if (opt->srun_opt->bcast_flag) return xstrdup(opt->srun_opt->bcast_file); return NULL; } static void arg_reset_bcast(slurm_opt_t *opt) { if (opt->srun_opt) { opt->srun_opt->bcast_flag = false; xfree(opt->srun_opt->bcast_file); } } static slurm_cli_opt_t slurm_opt_bcast = { .name = "bcast", .has_arg = optional_argument, .val = LONG_OPT_BCAST, .set_func_srun = arg_set_bcast, .get_func = arg_get_bcast, .reset_func = arg_reset_bcast, .reset_each_pass = true, }; static int arg_set_begin(slurm_opt_t *opt, const char *arg) { if (!(opt->begin = parse_time(arg, 0))) { error("Invalid --begin specification"); exit(-1); } return SLURM_SUCCESS; } static int arg_set_data_begin(slurm_opt_t *opt, const data_t *arg, data_t *errors) { int rc; char *str = NULL; if ((rc = data_get_string_converted(arg, &str))) ADD_DATA_ERROR("Unable to read string", rc); else if (!(opt->begin = parse_time(str, 0))) { rc = ESLURM_INVALID_TIME_VALUE; ADD_DATA_ERROR("Unable to parse time", rc); } xfree(str); return rc; } static char *arg_get_begin(slurm_opt_t *opt) { char time_str[32]; slurm_make_time_str(&opt->begin, time_str, sizeof(time_str)); return xstrdup(time_str); } COMMON_OPTION_RESET(begin, 0); static slurm_cli_opt_t slurm_opt_begin = { .name = "begin", .has_arg = required_argument, .val = 'b', .set_func = arg_set_begin, .set_func_data = arg_set_data_begin, .get_func = arg_get_begin, .reset_func = arg_reset_begin, }; /* Also see --no-bell below */ static int arg_set_bell(slurm_opt_t *opt, const char *arg) { if (opt->salloc_opt) opt->salloc_opt->bell = BELL_ALWAYS; return SLURM_SUCCESS; } static char *arg_get_bell(slurm_opt_t *opt) { if (!opt->salloc_opt) return xstrdup("invalid-context"); if (opt->salloc_opt->bell == BELL_ALWAYS) return xstrdup("bell-always"); else if (opt->salloc_opt->bell == BELL_AFTER_DELAY) return xstrdup("bell-after-delay"); else if (opt->salloc_opt->bell == BELL_NEVER) return xstrdup("bell-never"); return NULL; } static void arg_reset_bell(slurm_opt_t *opt) { if (opt->salloc_opt) opt->salloc_opt->bell = BELL_AFTER_DELAY; } static slurm_cli_opt_t slurm_opt_bell = { .name = "bell", .has_arg = no_argument, .val = LONG_OPT_BELL, .set_func_salloc = arg_set_bell, .get_func = arg_get_bell, .reset_func = arg_reset_bell, }; COMMON_STRING_OPTION(burst_buffer); static slurm_cli_opt_t slurm_opt_bb = { .name = "bb", .has_arg = required_argument, .val = LONG_OPT_BURST_BUFFER_SPEC, .set_func = arg_set_burst_buffer, .set_func_data = arg_set_data_burst_buffer, .get_func = arg_get_burst_buffer, .reset_func = arg_reset_burst_buffer, .reset_each_pass = true, }; COMMON_STRING_OPTION(c_constraint); static slurm_cli_opt_t slurm_opt_c_constraint = { .name = "cluster-constraint", .has_arg = required_argument, .val = LONG_OPT_CLUSTER_CONSTRAINT, .set_func = arg_set_c_constraint, .set_func_data = arg_set_data_c_constraint, .get_func = arg_get_c_constraint, .reset_func = arg_reset_c_constraint, }; static int arg_set_chdir(slurm_opt_t *opt, const char *arg) { if (is_full_path(arg)) opt->chdir = xstrdup(arg); else opt->chdir = make_full_path(arg); return SLURM_SUCCESS; } static int arg_set_data_chdir(slurm_opt_t *opt, const data_t *arg, data_t *errors) { int rc; char *str = NULL; if ((rc = data_get_string_converted(arg, &str))) ADD_DATA_ERROR("Unable to read string", rc); else if (is_full_path(str)) { opt->chdir = str; str = NULL; } else opt->chdir = make_full_path(str); xfree(str); return SLURM_SUCCESS; } COMMON_STRING_OPTION_GET(chdir); static void arg_reset_chdir(slurm_opt_t *opt) { char buf[MAXPATHLEN + 1]; xfree(opt->chdir); if (opt->salloc_opt) return; if (!(getcwd(buf, MAXPATHLEN))) { error("getcwd failed: %m"); exit(-1); } opt->chdir = xstrdup(buf); } static slurm_cli_opt_t slurm_opt_chdir = { .name = "chdir", .has_arg = required_argument, .val = 'D', .set_func = arg_set_chdir, .set_func_data = arg_set_data_chdir, .get_func = arg_get_chdir, .reset_func = arg_reset_chdir, }; /* --clusters and --cluster are equivalent */ COMMON_STRING_OPTION(clusters); static slurm_cli_opt_t slurm_opt_clusters = { .name = "clusters", .has_arg = required_argument, .val = 'M', .set_func = arg_set_clusters, .set_func_data = arg_set_data_clusters, .get_func = arg_get_clusters, .reset_func = arg_reset_clusters, }; static slurm_cli_opt_t slurm_opt_cluster = { .name = "cluster", .has_arg = required_argument, .val = LONG_OPT_CLUSTER, .set_func = arg_set_clusters, .set_func_data = arg_set_data_clusters, .get_func = arg_get_clusters, .reset_func = arg_reset_clusters, }; COMMON_STRING_OPTION(comment); static slurm_cli_opt_t slurm_opt_comment = { .name = "comment", .has_arg = required_argument, .val = LONG_OPT_COMMENT, .set_func = arg_set_comment, .set_func_data = arg_set_data_comment, .get_func = arg_get_comment, .reset_func = arg_reset_comment, }; static int arg_set_compress(slurm_opt_t *opt, const char *arg) { if (!opt->srun_opt) return SLURM_ERROR; opt->srun_opt->compress = parse_compress_type(arg); return SLURM_SUCCESS; } static char *arg_get_compress(slurm_opt_t *opt) { if (!opt->srun_opt) return xstrdup("invalid-context"); if (opt->srun_opt->compress == COMPRESS_LZ4) return xstrdup("lz4"); if (opt->srun_opt->compress == COMPRESS_ZLIB) return xstrdup("zlib"); return xstrdup("none"); } static void arg_reset_compress(slurm_opt_t *opt) { if (opt->srun_opt) opt->srun_opt->compress = COMPRESS_OFF; } static slurm_cli_opt_t slurm_opt_compress = { .name = "compress", .has_arg = optional_argument, .val = LONG_OPT_COMPRESS, .set_func_srun = arg_set_compress, .get_func = arg_get_compress, .reset_func = arg_reset_compress, .reset_each_pass = true, }; COMMON_STRING_OPTION(constraint); static slurm_cli_opt_t slurm_opt_constraint = { .name = "constraint", .has_arg = required_argument, .val = 'C', .set_func = arg_set_constraint, .set_func_data = arg_set_data_constraint, .get_func = arg_get_constraint, .reset_func = arg_reset_constraint, .reset_each_pass = true, }; COMMON_BOOL_OPTION(contiguous, "contiguous"); static slurm_cli_opt_t slurm_opt_contiguous = { .name = "contiguous", .has_arg = no_argument, .val = LONG_OPT_CONTIGUOUS, .set_func = arg_set_contiguous, .set_func_data = arg_set_data_contiguous, .get_func = arg_get_contiguous, .reset_func = arg_reset_contiguous, .reset_each_pass = true, }; static int arg_set_core_spec(slurm_opt_t *opt, const char *arg) { if (opt->srun_opt) opt->srun_opt->core_spec_set = true; opt->core_spec = parse_int("--core-spec", arg, false); return SLURM_SUCCESS; } static int arg_set_data_core_spec(slurm_opt_t *opt, const data_t *arg, data_t *errors) { int rc; int64_t val; if ((rc = data_get_int_converted(arg, &val))) ADD_DATA_ERROR("Unable to read int", rc); else if (val < 0) ADD_DATA_ERROR("Invalid core specification", rc); else { if (opt->srun_opt) opt->srun_opt->core_spec_set = (val > 0); opt->core_spec = val; } return rc; } static char *arg_get_core_spec(slurm_opt_t *opt) { if ((opt->core_spec == NO_VAL16) || (opt->core_spec & CORE_SPEC_THREAD)) return xstrdup("unset"); return xstrdup_printf("%d", opt->core_spec); } static void arg_reset_core_spec(slurm_opt_t *opt) { if (opt->srun_opt) opt->srun_opt->core_spec_set = false; opt->core_spec = NO_VAL16; } static slurm_cli_opt_t slurm_opt_core_spec = { .name = "core-spec", .has_arg = required_argument, .val = 'S', .set_func = arg_set_core_spec, .set_func_data = arg_set_data_core_spec, .get_func = arg_get_core_spec, .reset_func = arg_reset_core_spec, .reset_each_pass = true, }; COMMON_INT_OPTION_SET(cores_per_socket, "--cores-per-socket"); COMMON_INT_OPTION_GET(cores_per_socket); COMMON_INT_OPTION_SET_DATA(cores_per_socket); COMMON_OPTION_RESET(cores_per_socket, NO_VAL); static slurm_cli_opt_t slurm_opt_cores_per_socket = { .name = "cores-per-socket", .has_arg = required_argument, .val = LONG_OPT_CORESPERSOCKET, .set_func = arg_set_cores_per_socket, .set_func_data = arg_set_data_cores_per_socket, .get_func = arg_get_cores_per_socket, .reset_func = arg_reset_cores_per_socket, .reset_each_pass = true, }; static int arg_set_cpu_bind(slurm_opt_t *opt, const char *arg) { if (!opt->srun_opt) return SLURM_ERROR; if (slurm_verify_cpu_bind(arg, &opt->srun_opt->cpu_bind, &opt->srun_opt->cpu_bind_type, 0)) exit(-1); return SLURM_SUCCESS; } static char *arg_get_cpu_bind(slurm_opt_t *opt) { char tmp[100]; if (!opt->srun_opt) return xstrdup("invalid-context"); slurm_sprint_cpu_bind_type(tmp, opt->srun_opt->cpu_bind_type); return xstrdup(tmp); } static void arg_reset_cpu_bind(slurm_opt_t *opt) { if (opt->srun_opt) { xfree(opt->srun_opt->cpu_bind); opt->srun_opt->cpu_bind_type = 0; } } static slurm_cli_opt_t slurm_opt_cpu_bind = { .name = "cpu-bind", .has_arg = required_argument, .val = LONG_OPT_CPU_BIND, .set_func_srun = arg_set_cpu_bind, .get_func = arg_get_cpu_bind, .reset_func = arg_reset_cpu_bind, .reset_each_pass = true, }; /* * OpenMPI hard-coded --cpu_bind as part of their mpirun/mpiexec launch * scripting for a long time, and thus we're stuck supporting this deprecated * version indefinitely. * * Keep this after the preferred --cpu-bind handling so cli_filter sees that * and not this form. */ static slurm_cli_opt_t slurm_opt_cpu_underscore_bind = { .name = "cpu_bind", .has_arg = required_argument, .val = LONG_OPT_CPU_BIND, .set_func_srun = arg_set_cpu_bind, .get_func = arg_get_cpu_bind, .reset_func = arg_reset_cpu_bind, .reset_each_pass = true, }; static int arg_set_cpu_freq(slurm_opt_t *opt, const char *arg) { if (cpu_freq_verify_cmdline(arg, &opt->cpu_freq_min, &opt->cpu_freq_max, &opt->cpu_freq_gov)) { error("Invalid --cpu-freq argument"); exit(-1); } return SLURM_SUCCESS; } static int arg_set_data_cpu_freq(slurm_opt_t *opt, const data_t *arg, data_t *errors) { int rc; char *str = NULL; if ((rc = data_get_string_converted(arg, &str))) ADD_DATA_ERROR("Unable to read string", rc); else if ((rc = cpu_freq_verify_cmdline(str, &opt->cpu_freq_min, &opt->cpu_freq_max, &opt->cpu_freq_gov))) ADD_DATA_ERROR("Unable to parse CPU frequency", rc); xfree(str); return rc; } static char *arg_get_cpu_freq(slurm_opt_t *opt) { return cpu_freq_to_cmdline(opt->cpu_freq_min, opt->cpu_freq_max, opt->cpu_freq_gov); } static void arg_reset_cpu_freq(slurm_opt_t *opt) { opt->cpu_freq_min = NO_VAL; opt->cpu_freq_max = NO_VAL; opt->cpu_freq_gov = NO_VAL; } static slurm_cli_opt_t slurm_opt_cpu_freq = { .name = "cpu-freq", .has_arg = required_argument, .val = LONG_OPT_CPU_FREQ, .set_func = arg_set_cpu_freq, .set_func_data = arg_set_data_cpu_freq, .get_func = arg_get_cpu_freq, .reset_func = arg_reset_cpu_freq, .reset_each_pass = true, }; COMMON_INT_OPTION(cpus_per_gpu, "--cpus-per-gpu"); static slurm_cli_opt_t slurm_opt_cpus_per_gpu = { .name = "cpus-per-gpu", .has_arg = required_argument, .val = LONG_OPT_CPUS_PER_GPU, .set_func = arg_set_cpus_per_gpu, .set_func_data = arg_set_data_cpus_per_gpu, .get_func = arg_get_cpus_per_gpu, .reset_func = arg_reset_cpus_per_gpu, .reset_each_pass = true, }; static int arg_set_cpus_per_task(slurm_opt_t *opt, const char *arg) { int old_cpus_per_task = opt->cpus_per_task; opt->cpus_per_task = parse_int("--cpus-per-task", arg, true); if (opt->cpus_set && opt->srun_opt && (old_cpus_per_task < opt->cpus_per_task)) info("Job step's --cpus-per-task value exceeds that of job (%d > %d). Job step may never run.", opt->cpus_per_task, old_cpus_per_task); opt->cpus_set = true; return SLURM_SUCCESS; } static int arg_set_data_cpus_per_task(slurm_opt_t *opt, const data_t *arg, data_t *errors) { int64_t val; int rc = data_get_int_converted(arg, &val); if (rc) ADD_DATA_ERROR("Unable to read integer value", rc); else if (val >= INT_MAX) { rc = SLURM_ERROR; ADD_DATA_ERROR("Integer too large", SLURM_ERROR); } else if (val < 1) { rc = SLURM_ERROR; ADD_DATA_ERROR("cpus per task much be greater than 0", SLURM_ERROR); } else { int old_cpus_per_task = opt->cpus_per_task; opt->cpus_per_task = (int) val; if (opt->cpus_set && opt->srun_opt && (old_cpus_per_task < opt->cpus_per_task)) { char str[1024]; snprintf(str, sizeof(str), "Job step's --cpus-per-task value exceeds that of job (%d > %d). Job step may never run.", opt->cpus_per_task, old_cpus_per_task); rc = SLURM_ERROR; ADD_DATA_ERROR(str, rc); } opt->cpus_set = true; } return rc; } COMMON_INT_OPTION_GET(cpus_per_task); static void arg_reset_cpus_per_task(slurm_opt_t *opt) { opt->cpus_per_task = 0; opt->cpus_set = false; } static slurm_cli_opt_t slurm_opt_cpus_per_task = { .name = "cpus-per-task", .has_arg = required_argument, .val = 'c', .set_func = arg_set_cpus_per_task, .set_func_data = arg_set_data_cpus_per_task, .get_func = arg_get_cpus_per_task, .reset_func = arg_reset_cpus_per_task, .reset_each_pass = true, }; static int arg_set_deadline(slurm_opt_t *opt, const char *arg) { if (!(opt->deadline = parse_time(arg, 0))) { error("Invalid --deadline specification"); exit(-1); } return SLURM_SUCCESS; } static int arg_set_data_deadline(slurm_opt_t *opt, const data_t *arg, data_t *errors) { int rc; char *str = NULL; if ((rc = data_get_string_converted(arg, &str))) ADD_DATA_ERROR("Unable to read string", rc); else if (!(opt->deadline = parse_time(str, 0))) { rc = SLURM_ERROR; ADD_DATA_ERROR("Invalid deadline time", rc); } xfree(str); return rc; } static char *arg_get_deadline(slurm_opt_t *opt) { char time_str[32]; slurm_make_time_str(&opt->deadline, time_str, sizeof(time_str)); return xstrdup(time_str); } COMMON_OPTION_RESET(deadline, 0); static slurm_cli_opt_t slurm_opt_deadline = { .name = "deadline", .has_arg = required_argument, .val = LONG_OPT_DEADLINE, .set_func = arg_set_deadline, .set_func_data = arg_set_data_deadline, .get_func = arg_get_deadline, .reset_func = arg_reset_deadline, }; static int arg_set_debugger_test(slurm_opt_t *opt, const char *arg) { if (!opt->srun_opt) return SLURM_ERROR; opt->srun_opt->debugger_test = true; return SLURM_SUCCESS; } static char *arg_get_debugger_test(slurm_opt_t *opt) { if (!opt->srun_opt) return NULL; return xstrdup(opt->srun_opt->debugger_test ? "set" : "unset"); } static void arg_reset_debugger_test(slurm_opt_t *opt) { if (opt->srun_opt) opt->srun_opt->debugger_test = false; } static slurm_cli_opt_t slurm_opt_debugger_test = { .name = "debugger-test", .has_arg = no_argument, .val = LONG_OPT_DEBUGGER_TEST, .set_func_srun = arg_set_debugger_test, .get_func = arg_get_debugger_test, .reset_func = arg_reset_debugger_test, }; static int arg_set_delay_boot(slurm_opt_t *opt, const char *arg) { if ((opt->delay_boot = time_str2secs(arg)) == NO_VAL) { error("Invalid --delay-boot specification"); exit(-1); } return SLURM_SUCCESS; } static int arg_set_data_delay_boot(slurm_opt_t *opt, const data_t *arg, data_t *errors) { int rc; char *str = NULL; if ((rc = data_get_string_converted(arg, &str))) ADD_DATA_ERROR("Unable to read string", rc); else if ((opt->delay_boot = time_str2secs(str)) == NO_VAL) { rc = SLURM_ERROR; ADD_DATA_ERROR("Invalid delay boot specification", rc); } xfree(str); return rc; } static char *arg_get_delay_boot(slurm_opt_t *opt) { char time_str[32]; secs2time_str(opt->delay_boot, time_str, sizeof(time_str)); return xstrdup(time_str); } COMMON_OPTION_RESET(delay_boot, NO_VAL); static slurm_cli_opt_t slurm_opt_delay_boot = { .name = "delay-boot", .has_arg = required_argument, .val = LONG_OPT_DELAY_BOOT, .set_func = arg_set_delay_boot, .set_func_data = arg_set_data_delay_boot, .get_func = arg_get_delay_boot, .reset_func = arg_reset_delay_boot, }; static data_for_each_cmd_t _parse_env(const char *key, const data_t *data, void *arg) { int rc = DATA_FOR_EACH_FAIL; char ***env = arg; char *ebuf = NULL; if (!data_get_string_converted(data, &ebuf)) { env_array_append(env, key, ebuf); rc = DATA_FOR_EACH_CONT; } xfree(ebuf); return rc; } static int arg_set_data_environment(slurm_opt_t *opt, const data_t *arg, data_t *errors) { if (data_get_type(arg) != DATA_TYPE_DICT) { ADD_DATA_ERROR("environment must be a dictionary", SLURM_ERROR); return SLURM_ERROR; } /* * always start with a fresh environment if client * provides one explicitly */ if (opt->environment) env_array_free(opt->environment); opt->environment = env_array_create(); if (data_dict_for_each_const(arg, _parse_env, &opt->environment) < 0) { ADD_DATA_ERROR("failure parsing environment", SLURM_ERROR); return SLURM_ERROR; } return SLURM_SUCCESS; } static void arg_reset_environment(slurm_opt_t *opt) { env_array_free(opt->environment); opt->environment = NULL; } static char *arg_get_environment(slurm_opt_t *opt) { return NULL; } static slurm_cli_opt_t slurm_opt_environment = { .name = "environment", .val = LONG_OPT_ENVIRONMENT, .has_arg = required_argument, .set_func_data = arg_set_data_environment, .get_func = arg_get_environment, .reset_func = arg_reset_environment, }; COMMON_STRING_OPTION(dependency); static slurm_cli_opt_t slurm_opt_dependency = { .name = "dependency", .has_arg = required_argument, .val = 'd', .set_func = arg_set_dependency, .set_func_data = arg_set_data_dependency, .get_func = arg_get_dependency, .reset_func = arg_reset_dependency, }; COMMON_SRUN_BOOL_OPTION(disable_status); static slurm_cli_opt_t slurm_opt_disable_status = { .name = "disable-status", .has_arg = no_argument, .val = 'X', .set_func_srun = arg_set_disable_status, .get_func = arg_get_disable_status, .reset_func = arg_reset_disable_status, }; static int arg_set_distribution(slurm_opt_t *opt, const char *arg) { opt->distribution = verify_dist_type(arg, &opt->plane_size); if (opt->distribution == SLURM_DIST_UNKNOWN) { error("Invalid --distribution specification"); exit(-1); } return SLURM_SUCCESS; } static int arg_set_data_distribution(slurm_opt_t *opt, const data_t *arg, data_t *errors) { int rc; char *str = NULL; if ((rc = data_get_string_converted(arg, &str))) ADD_DATA_ERROR("Unable to read string", rc); else { /* FIXME: ignore SLURM_DIST_PLANESIZE envvar for slurmrestd */ opt->distribution = verify_dist_type(str, &opt->plane_size); if (opt->distribution == SLURM_DIST_UNKNOWN) { rc = SLURM_ERROR; ADD_DATA_ERROR("Invalid distribution", rc); } } xfree(str); return rc; } static char *arg_get_distribution(slurm_opt_t *opt) { char *dist = xstrdup(format_task_dist_states(opt->distribution)); if (opt->distribution == SLURM_DIST_PLANE) xstrfmtcat(dist, "=%u", opt->plane_size); return dist; } static void arg_reset_distribution(slurm_opt_t *opt) { opt->distribution = SLURM_DIST_UNKNOWN; opt->plane_size = NO_VAL; } static slurm_cli_opt_t slurm_opt_distribution = { .name = "distribution", .has_arg = required_argument, .val = 'm', .set_func = arg_set_distribution, .set_func_data = arg_set_data_distribution, .get_func = arg_get_distribution, .reset_func = arg_reset_distribution, .reset_each_pass = true, }; COMMON_SRUN_STRING_OPTION(epilog); static slurm_cli_opt_t slurm_opt_epilog = { .name = "epilog", .has_arg = required_argument, .val = LONG_OPT_EPILOG, .set_func_srun = arg_set_epilog, .get_func = arg_get_epilog, .reset_func = arg_reset_epilog, }; static int arg_set_efname(slurm_opt_t *opt, const char *arg) { if (!opt->sbatch_opt && !opt->srun_opt) return SLURM_ERROR; xfree(opt->efname); if (!xstrcasecmp(arg, "none")) opt->efname = xstrdup("/dev/null"); else opt->efname = xstrdup(arg); return SLURM_SUCCESS; } static int arg_set_data_efname(slurm_opt_t *opt, const data_t *arg, data_t *errors) { int rc; char *str = NULL; if ((rc = data_get_string_converted(arg, &str))) ADD_DATA_ERROR("Unable to read string", rc); else { xfree(opt->efname); if (!xstrcasecmp(str, "none")) opt->efname = xstrdup("/dev/null"); else { opt->efname = str; str = NULL; } } xfree(str); return rc; } COMMON_STRING_OPTION_GET(efname); COMMON_STRING_OPTION_RESET(efname); static slurm_cli_opt_t slurm_opt_error = { .name = "error", .has_arg = required_argument, .val = 'e', .set_func_sbatch = arg_set_efname, .set_func_srun = arg_set_efname, .set_func_data = arg_set_data_efname, .get_func = arg_get_efname, .reset_func = arg_reset_efname, }; COMMON_STRING_OPTION(exclude); static slurm_cli_opt_t slurm_opt_exclude = { .name = "exclude", .has_arg = required_argument, .val = 'x', .set_func = arg_set_exclude, .set_func_data = arg_set_data_exclude, .get_func = arg_get_exclude, .reset_func = arg_reset_exclude, }; static int arg_set_exclusive(slurm_opt_t *opt, const char *arg) { if (!arg || !xstrcasecmp(arg, "exclusive")) { if (opt->srun_opt) opt->srun_opt->exclusive = true; opt->shared = JOB_SHARED_NONE; } else if (!xstrcasecmp(arg, "oversubscribe")) { opt->shared = JOB_SHARED_OK; } else if (!xstrcasecmp(arg, "user")) { opt->shared = JOB_SHARED_USER; } else if (!xstrcasecmp(arg, "mcs")) { opt->shared = JOB_SHARED_MCS; } else { error("Invalid --exclusive specification"); exit(-1); } return SLURM_SUCCESS; } static int arg_set_data_exclusive(slurm_opt_t *opt, const data_t *arg, data_t *errors) { int rc; char *str = NULL; if ((rc = data_get_string_converted(arg, &str))) ADD_DATA_ERROR("Unable to read string", rc); else { if (!str || !xstrcasecmp(str, "exclusive")) { if (opt->srun_opt) opt->srun_opt->exclusive = true; opt->shared = JOB_SHARED_NONE; } else if (!xstrcasecmp(str, "oversubscribe")) { opt->shared = JOB_SHARED_OK; } else if (!xstrcasecmp(str, "user")) { opt->shared = JOB_SHARED_USER; } else if (!xstrcasecmp(str, "mcs")) { opt->shared = JOB_SHARED_MCS; } else { rc = SLURM_ERROR; ADD_DATA_ERROR("Invalid exclusive specification", rc); } } xfree(str); return rc; } static char *arg_get_exclusive(slurm_opt_t *opt) { if (opt->shared == JOB_SHARED_NONE) return xstrdup("exclusive"); if (opt->shared == JOB_SHARED_OK) return xstrdup("oversubscribe"); if (opt->shared == JOB_SHARED_USER) return xstrdup("user"); if (opt->shared == JOB_SHARED_MCS) return xstrdup("mcs"); if (opt->shared == NO_VAL16) return xstrdup("unset"); return NULL; } /* warning: shared with --oversubscribe below */ static void arg_reset_shared(slurm_opt_t *opt) { if (opt->srun_opt) opt->srun_opt->exclusive = false; opt->shared = NO_VAL16; } static slurm_cli_opt_t slurm_opt_exclusive = { .name = "exclusive", .has_arg = optional_argument, .val = LONG_OPT_EXCLUSIVE, .set_func = arg_set_exclusive, .set_func_data = arg_set_data_exclusive, .get_func = arg_get_exclusive, .reset_func = arg_reset_shared, .reset_each_pass = true, }; static int arg_set_export(slurm_opt_t *opt, const char *arg) { if (!opt->sbatch_opt && !opt->srun_opt) return SLURM_ERROR; if (opt->sbatch_opt) opt->sbatch_opt->export_env = xstrdup(arg); if (opt->srun_opt) opt->srun_opt->export_env = xstrdup(arg); return SLURM_SUCCESS; } static char *arg_get_export(slurm_opt_t *opt) { if (!opt->sbatch_opt && !opt->srun_opt) return xstrdup("invalid-context"); if (opt->sbatch_opt) return xstrdup(opt->sbatch_opt->export_env); if (opt->srun_opt) return xstrdup(opt->srun_opt->export_env); return NULL; } static void arg_reset_export(slurm_opt_t *opt) { if (opt->sbatch_opt) xfree(opt->sbatch_opt->export_env); if (opt->srun_opt) xfree(opt->srun_opt->export_env); } static slurm_cli_opt_t slurm_opt_export = { .name = "export", .has_arg = required_argument, .val = LONG_OPT_EXPORT, .set_func_sbatch = arg_set_export, .set_func_srun = arg_set_export, .get_func = arg_get_export, .reset_func = arg_reset_export, }; COMMON_SBATCH_STRING_OPTION(export_file); static slurm_cli_opt_t slurm_opt_export_file = { .name = "export-file", .has_arg = required_argument, .val = LONG_OPT_EXPORT_FILE, .set_func_sbatch = arg_set_export_file, .set_func_data = arg_set_data_export_file, .get_func = arg_get_export_file, .reset_func = arg_reset_export_file, }; static int arg_set_extra_node_info(slurm_opt_t *opt, const char *arg) { cpu_bind_type_t *cpu_bind_type = NULL; if (opt->srun_opt) cpu_bind_type = &opt->srun_opt->cpu_bind_type; opt->extra_set = verify_socket_core_thread_count(arg, &opt->sockets_per_node, &opt->cores_per_socket, &opt->threads_per_core, cpu_bind_type); if (!opt->extra_set) { error("Invalid --extra-node-info specification"); exit(-1); } return SLURM_SUCCESS; } static char *arg_get_extra_node_info(slurm_opt_t *opt) { char *tmp = NULL; if (opt->sockets_per_node != NO_VAL) xstrfmtcat(tmp, "%d", opt->sockets_per_node); if (opt->cores_per_socket != NO_VAL) xstrfmtcat(tmp, ":%d", opt->cores_per_socket); if (opt->threads_per_core != NO_VAL) xstrfmtcat(tmp, ":%d", opt->threads_per_core); if (!tmp) return xstrdup("unset"); return tmp; } static void arg_reset_extra_node_info(slurm_opt_t *opt) { opt->extra_set = false; opt->sockets_per_node = NO_VAL; opt->cores_per_socket = NO_VAL; opt->threads_per_core = NO_VAL; } static slurm_cli_opt_t slurm_opt_extra_node_info = { .name = "extra-node-info", .has_arg = required_argument, .val = 'B', .set_func = arg_set_extra_node_info, .get_func = arg_get_extra_node_info, .reset_func = arg_reset_extra_node_info, .reset_each_pass = true, }; static int arg_set_get_user_env(slurm_opt_t *opt, const char *arg) { char *end_ptr; if (!arg) { opt->get_user_env_time = 0; return SLURM_SUCCESS; } opt->get_user_env_time = strtol(arg, &end_ptr, 10); if (!end_ptr || (end_ptr[0] == '\0')) return SLURM_SUCCESS; if ((end_ptr[0] == 's') || (end_ptr[0] == 'S')) opt->get_user_env_mode = 1; else if ((end_ptr[0] == 'l') || (end_ptr[0] == 'L')) opt->get_user_env_mode = 2; else { error("Invalid --get-user-env specification"); exit(-1); } return SLURM_SUCCESS; } static int arg_set_data_get_user_env(slurm_opt_t *opt, const data_t *arg, data_t *errors) { int rc = SLURM_SUCCESS; char *str = NULL; if ((data_get_type(arg) == DATA_TYPE_NULL)) opt->get_user_env_time = 0; else if ((rc = data_get_string_converted(arg, &str))) ADD_DATA_ERROR("Unable to read string", rc); else { char *end_ptr; opt->get_user_env_time = strtol(str, &end_ptr, 10); if (!end_ptr || (end_ptr[0] == '\0')) opt->get_user_env_mode = -1; /* not set */ else if ((end_ptr[0] == 's') || (end_ptr[0] == 'S')) opt->get_user_env_mode = 1; else if ((end_ptr[0] == 'l') || (end_ptr[0] == 'L')) opt->get_user_env_mode = 2; else { rc = SLURM_ERROR; ADD_DATA_ERROR("Invalid get user environment specification", rc); } } xfree(str); return rc; } static char *arg_get_get_user_env(slurm_opt_t *opt) { if (opt->get_user_env_mode == 1) return xstrdup_printf("%dS", opt->get_user_env_time); else if (opt->get_user_env_mode == 2) return xstrdup_printf("%dL", opt->get_user_env_time); else if (opt->get_user_env_time != -1) return xstrdup_printf("%d", opt->get_user_env_time); return NULL; } static void arg_reset_get_user_env(slurm_opt_t *opt) { opt->get_user_env_mode = -1; opt->get_user_env_time = -1; } static slurm_cli_opt_t slurm_opt_get_user_env = { .name = "get-user-env", .has_arg = optional_argument, .val = LONG_OPT_GET_USER_ENV, .set_func_salloc = arg_set_get_user_env, .set_func_sbatch = arg_set_get_user_env, .set_func_data = arg_set_data_get_user_env, .get_func = arg_get_get_user_env, .reset_func = arg_reset_get_user_env, }; static int arg_set_gid(slurm_opt_t *opt, const char *arg) { if (getuid() != 0) { error("--gid only permitted by root user"); exit(-1); } if (gid_from_string(arg, &opt->gid) < 0) { error("Invalid --gid specification"); exit(-1); } return SLURM_SUCCESS; } static int arg_set_data_gid(slurm_opt_t *opt, const data_t *arg, data_t *errors) { int rc; char *str = NULL; if ((rc = data_get_string_converted(arg, &str))) ADD_DATA_ERROR("Unable to read string", rc); else if (gid_from_string(str, &opt->gid) < 0) { rc = SLURM_ERROR; ADD_DATA_ERROR("Invalid or unknown gid", rc); } xfree(str); return rc; } COMMON_INT_OPTION_GET(gid); COMMON_OPTION_RESET(gid, getgid()); static slurm_cli_opt_t slurm_opt_gid = { .name = "gid", .has_arg = required_argument, .val = LONG_OPT_GID, .set_func = arg_set_gid, .set_func_data = arg_set_data_gid, .get_func = arg_get_gid, .reset_func = arg_reset_gid, }; static int arg_set_gpu_bind(slurm_opt_t *opt, const char *arg) { xfree(opt->gpu_bind); xfree(opt->tres_bind); opt->gpu_bind = xstrdup(arg); xstrfmtcat(opt->tres_bind, "gpu:%s", opt->gpu_bind); if (tres_bind_verify_cmdline(opt->tres_bind)) { error("Invalid --gpu-bind argument: %s", opt->tres_bind); exit(1); } return SLURM_SUCCESS; } static int arg_set_data_gpu_bind(slurm_opt_t *opt, const data_t *arg, data_t *errors) { int rc; char *str = NULL; if ((rc = data_get_string_converted(arg, &str))) ADD_DATA_ERROR("Unable to read string", rc); else { xfree(opt->gpu_bind); xfree(opt->tres_bind); opt->gpu_bind = xstrdup(str); xstrfmtcat(opt->tres_bind, "gpu:%s", opt->gpu_bind); if (tres_bind_verify_cmdline(opt->tres_bind)) { rc = SLURM_ERROR; ADD_DATA_ERROR("Invalid --gpu-bind argument", rc); xfree(opt->gpu_bind); xfree(opt->tres_bind); } } xfree(str); return rc; } static void arg_reset_gpu_bind(slurm_opt_t *opt) { xfree(opt->gpu_bind); xfree(opt->tres_bind); } COMMON_STRING_OPTION_GET(gpu_bind); static slurm_cli_opt_t slurm_opt_gpu_bind = { .name = "gpu-bind", .has_arg = required_argument, .val = LONG_OPT_GPU_BIND, .set_func = arg_set_gpu_bind, .set_func_data = arg_set_data_gpu_bind, .get_func = arg_get_gpu_bind, .reset_func = arg_reset_gpu_bind, .reset_each_pass = true, }; static int arg_set_gpu_freq(slurm_opt_t *opt, const char *arg) { xfree(opt->gpu_freq); xfree(opt->tres_freq); opt->gpu_freq = xstrdup(arg); xstrfmtcat(opt->tres_freq, "gpu:%s", opt->gpu_freq); if (tres_freq_verify_cmdline(opt->tres_freq)) { error("Invalid --gpu-freq argument: %s", opt->tres_freq); exit(1); } return SLURM_SUCCESS; } static int arg_set_data_gpu_freq(slurm_opt_t *opt, const data_t *arg, data_t *errors) { int rc; char *str = NULL; if ((rc = data_get_string_converted(arg, &str))) ADD_DATA_ERROR("Unable to read string", rc); else { xfree(opt->gpu_freq); xfree(opt->tres_freq); opt->gpu_freq = xstrdup(str); xstrfmtcat(opt->tres_freq, "gpu:%s", opt->gpu_freq); if (tres_freq_verify_cmdline(opt->tres_freq)) { rc = SLURM_ERROR; ADD_DATA_ERROR("Invalid --gpu-freq argument", rc); xfree(opt->gpu_freq); xfree(opt->tres_freq); } } xfree(str); return rc; } static void arg_reset_gpu_freq(slurm_opt_t *opt) { xfree(opt->gpu_freq); xfree(opt->tres_freq); } COMMON_STRING_OPTION_GET(gpu_freq); static slurm_cli_opt_t slurm_opt_gpu_freq = { .name = "gpu-freq", .has_arg = required_argument, .val = LONG_OPT_GPU_FREQ, .set_func = arg_set_gpu_freq, .set_func_data = arg_set_data_gpu_freq, .get_func = arg_get_gpu_freq, .reset_func = arg_reset_gpu_freq, .reset_each_pass = true, }; COMMON_STRING_OPTION(gpus); static slurm_cli_opt_t slurm_opt_gpus = { .name = "gpus", .has_arg = required_argument, .val = 'G', .set_func = arg_set_gpus, .set_func_data = arg_set_data_gpus, .get_func = arg_get_gpus, .reset_func = arg_reset_gpus, .reset_each_pass = true, }; COMMON_STRING_OPTION(gpus_per_node); static slurm_cli_opt_t slurm_opt_gpus_per_node = { .name = "gpus-per-node", .has_arg = required_argument, .val = LONG_OPT_GPUS_PER_NODE, .set_func = arg_set_gpus_per_node, .set_func_data = arg_set_data_gpus_per_node, .get_func = arg_get_gpus_per_node, .reset_func = arg_reset_gpus_per_node, .reset_each_pass = true, }; COMMON_STRING_OPTION(gpus_per_socket); static slurm_cli_opt_t slurm_opt_gpus_per_socket = { .name = "gpus-per-socket", .has_arg = required_argument, .val = LONG_OPT_GPUS_PER_SOCKET, .set_func = arg_set_gpus_per_socket, .set_func_data = arg_set_data_gpus_per_socket, .get_func = arg_get_gpus_per_socket, .reset_func = arg_reset_gpus_per_socket, .reset_each_pass = true, }; COMMON_STRING_OPTION(gpus_per_task); static slurm_cli_opt_t slurm_opt_gpus_per_task = { .name = "gpus-per-task", .has_arg = required_argument, .val = LONG_OPT_GPUS_PER_TASK, .set_func = arg_set_gpus_per_task, .set_func_data = arg_set_data_gpus_per_task, .get_func = arg_get_gpus_per_task, .reset_func = arg_reset_gpus_per_task, .reset_each_pass = true, }; static int arg_set_gres(slurm_opt_t *opt, const char *arg) { if (!xstrcasecmp(arg, "help") || !xstrcasecmp(arg, "list")) { print_gres_help(); exit(0); } xfree(opt->gres); opt->gres = xstrdup(arg); return SLURM_SUCCESS; } static int arg_set_data_gres(slurm_opt_t *opt, const data_t *arg, data_t *errors) { int rc; char *str = NULL; if ((rc = data_get_string_converted(arg, &str))) ADD_DATA_ERROR("Unable to read string", rc); else if (!xstrcasecmp(str, "help") || !xstrcasecmp(str, "list")) { rc = SLURM_ERROR; ADD_DATA_ERROR("GRES \"help\" not supported", rc); } else { xfree(opt->gres); opt->gres = str; str = NULL; } xfree(str); return rc; } COMMON_STRING_OPTION_GET_AND_RESET(gres); static slurm_cli_opt_t slurm_opt_gres = { .name = "gres", .has_arg = required_argument, .val = LONG_OPT_GRES, .set_func = arg_set_gres, .set_func_data = arg_set_data_gres, .get_func = arg_get_gres, .reset_func = arg_reset_gres, .reset_each_pass = true, }; static int arg_set_gres_flags(slurm_opt_t *opt, const char *arg) { /* clear both flag options first */ opt->job_flags &= ~(GRES_DISABLE_BIND|GRES_ENFORCE_BIND); if (!xstrcasecmp(arg, "disable-binding")) { opt->job_flags |= GRES_DISABLE_BIND; } else if (!xstrcasecmp(arg, "enforce-binding")) { opt->job_flags |= GRES_ENFORCE_BIND; } else { error("Invalid --gres-flags specification"); exit(-1); } return SLURM_SUCCESS; } static int arg_set_data_gres_flags(slurm_opt_t *opt, const data_t *arg, data_t *errors) { int rc; char *str = NULL; if ((rc = data_get_string_converted(arg, &str))) ADD_DATA_ERROR("Unable to read string", rc); else { /* clear both flag options first */ opt->job_flags &= ~(GRES_DISABLE_BIND|GRES_ENFORCE_BIND); if (!xstrcasecmp(str, "disable-binding")) { opt->job_flags |= GRES_DISABLE_BIND; } else if (!xstrcasecmp(str, "enforce-binding")) { opt->job_flags |= GRES_ENFORCE_BIND; } else { rc = SLURM_ERROR; ADD_DATA_ERROR("Invalid GRES flags", rc); } } xfree(str); return rc; } static char *arg_get_gres_flags(slurm_opt_t *opt) { if (opt->job_flags & GRES_DISABLE_BIND) return xstrdup("disable-binding"); else if (opt->job_flags & GRES_ENFORCE_BIND) return xstrdup("enforce-binding"); return xstrdup("unset"); } static void arg_reset_gres_flags(slurm_opt_t *opt) { opt->job_flags &= ~(GRES_DISABLE_BIND); opt->job_flags &= ~(GRES_ENFORCE_BIND); } static slurm_cli_opt_t slurm_opt_gres_flags = { .name = "gres-flags", .has_arg = required_argument, .val = LONG_OPT_GRES_FLAGS, .set_func = arg_set_gres_flags, .set_func_data = arg_set_data_gres_flags, .get_func = arg_get_gres_flags, .reset_func = arg_reset_gres_flags, .reset_each_pass = true, }; static int arg_set_help(slurm_opt_t *opt, const char *arg) { if (opt->help_func) (opt->help_func)(); else error("Could not find --help message"); exit(0); return SLURM_SUCCESS; } static char *arg_get_help(slurm_opt_t *opt) { return NULL; /* no op */ } static void arg_reset_help(slurm_opt_t *opt) { /* no op */ } static slurm_cli_opt_t slurm_opt_help = { .name = "help", .has_arg = no_argument, .val = 'h', .sbatch_early_pass = true, .set_func = arg_set_help, .get_func = arg_get_help, .reset_func = arg_reset_help, }; COMMON_STRING_OPTION(hint); static slurm_cli_opt_t slurm_opt_hint = { .name = "hint", .has_arg = required_argument, .val = LONG_OPT_HINT, .set_func = arg_set_hint, .set_func_data = arg_set_data_hint, .get_func = arg_get_hint, .reset_func = arg_reset_hint, .reset_each_pass = true, }; COMMON_BOOL_OPTION(hold, "hold"); static slurm_cli_opt_t slurm_opt_hold = { .name = "hold", .has_arg = no_argument, .val = 'H', .set_func = arg_set_hold, .set_func_data = arg_set_data_hold, .get_func = arg_get_hold, .reset_func = arg_reset_hold, }; static int arg_set_ignore_pbs(slurm_opt_t *opt, const char *arg) { if (!opt->sbatch_opt) return SLURM_ERROR; opt->sbatch_opt->ignore_pbs = true; return SLURM_SUCCESS; } static char *arg_get_ignore_pbs(slurm_opt_t *opt) { if (!opt->sbatch_opt) return xstrdup("invalid-context"); return xstrdup(opt->sbatch_opt->ignore_pbs ? "set" : "unset"); } static void arg_reset_ignore_pbs(slurm_opt_t *opt) { if (opt->sbatch_opt) opt->sbatch_opt->ignore_pbs = false; } static slurm_cli_opt_t slurm_opt_ignore_pbs = { .name = "ignore-pbs", .has_arg = no_argument, .val = LONG_OPT_IGNORE_PBS, .set_func_sbatch = arg_set_ignore_pbs, .get_func = arg_get_ignore_pbs, .reset_func = arg_reset_ignore_pbs, }; static int arg_set_immediate(slurm_opt_t *opt, const char *arg) { if (opt->sbatch_opt) return SLURM_ERROR; if (arg) opt->immediate = parse_int("immediate", arg, false); else opt->immediate = DEFAULT_IMMEDIATE; return SLURM_SUCCESS; } COMMON_INT_OPTION_GET_AND_RESET(immediate); static slurm_cli_opt_t slurm_opt_immediate = { .name = "immediate", .has_arg = optional_argument, .val = 'I', .set_func_salloc = arg_set_immediate, .set_func_srun = arg_set_immediate, .get_func = arg_get_immediate, .reset_func = arg_reset_immediate, }; static int arg_set_ifname(slurm_opt_t *opt, const char *arg) { if (!opt->sbatch_opt && !opt->srun_opt) return SLURM_ERROR; xfree(opt->ifname); if (!xstrcasecmp(arg, "none")) opt->ifname = xstrdup("/dev/null"); else opt->ifname = xstrdup(arg); return SLURM_SUCCESS; } static int arg_set_data_ifname(slurm_opt_t *opt, const data_t *arg, data_t *errors) { int rc; char *str = NULL; if (!opt->sbatch_opt && !opt->srun_opt) return SLURM_ERROR; if ((rc = data_get_string_converted(arg, &str))) ADD_DATA_ERROR("Unable to read string", rc); else { xfree(opt->ifname); if (!xstrcasecmp(str, "none")) opt->ifname = xstrdup("/dev/null"); else { opt->ifname = str; str = NULL; } } xfree(str); return rc; } COMMON_STRING_OPTION_GET(ifname); COMMON_STRING_OPTION_RESET(ifname); static slurm_cli_opt_t slurm_opt_input = { .name = "input", .has_arg = required_argument, .val = 'i', .set_func_sbatch = arg_set_ifname, .set_func_srun = arg_set_ifname, .set_func_data = arg_set_data_ifname, .get_func = arg_get_ifname, .reset_func = arg_reset_ifname, }; static int arg_set_jobid(slurm_opt_t *opt, const char *arg) { if (!opt->srun_opt) return SLURM_ERROR; opt->srun_opt->jobid = parse_int("--jobid", arg, true); return SLURM_SUCCESS; } static char *arg_get_jobid(slurm_opt_t *opt) { if (!opt->srun_opt) return NULL; if (opt->srun_opt->jobid == NO_VAL) return xstrdup("unset"); return xstrdup_printf("%d", opt->srun_opt->jobid); } static void arg_reset_jobid(slurm_opt_t *opt) { if (opt->srun_opt) opt->srun_opt->jobid = NO_VAL; } static slurm_cli_opt_t slurm_opt_jobid = { .name = "jobid", .has_arg = required_argument, .val = LONG_OPT_JOBID, .set_func_srun = arg_set_jobid, .get_func = arg_get_jobid, .reset_func = arg_reset_jobid, }; COMMON_STRING_OPTION(job_name); static slurm_cli_opt_t slurm_opt_job_name = { .name = "job-name", .has_arg = required_argument, .val = 'J', .set_func = arg_set_job_name, .set_func_data = arg_set_data_job_name, .get_func = arg_get_job_name, .reset_func = arg_reset_job_name, }; static int arg_set_kill_command(slurm_opt_t *opt, const char *arg) { if (!opt->salloc_opt) return SLURM_ERROR; /* Optional argument, enables default of SIGTERM if not given. */ if (!arg) { opt->salloc_opt->kill_command_signal = SIGTERM; return SLURM_SUCCESS; } if (!(opt->salloc_opt->kill_command_signal = sig_name2num(arg))) { error("Invalid --kill-command specification"); exit(-1); } return SLURM_SUCCESS; } static char *arg_get_kill_command(slurm_opt_t *opt) { if (!opt->salloc_opt) return NULL; return sig_num2name(opt->salloc_opt->kill_command_signal); } static void arg_reset_kill_command(slurm_opt_t *opt) { if (opt->salloc_opt) opt->salloc_opt->kill_command_signal = 0; } static slurm_cli_opt_t slurm_opt_kill_command = { .name = "kill-command", .has_arg = optional_argument, .val = 'K', .set_func_salloc = arg_set_kill_command, .get_func = arg_get_kill_command, .reset_func = arg_reset_kill_command, }; static int arg_set_kill_on_bad_exit(slurm_opt_t *opt, const char *arg) { if (!opt->srun_opt) return SLURM_ERROR; if (!arg) { opt->srun_opt->kill_bad_exit = 1; return SLURM_SUCCESS; } opt->srun_opt->kill_bad_exit = parse_int("--kill-on-bad-exit", arg, false); return SLURM_SUCCESS; } static char *arg_get_kill_on_bad_exit(slurm_opt_t *opt) { if (!opt->srun_opt) return NULL; return xstrdup_printf("%d", opt->srun_opt->kill_bad_exit); } static void arg_reset_kill_on_bad_exit(slurm_opt_t *opt) { if (opt->srun_opt) opt->srun_opt->kill_bad_exit = NO_VAL; } static slurm_cli_opt_t slurm_opt_kill_on_bad_exit = { .name = "kill-on-bad-exit", .has_arg = optional_argument, .val = 'K', .set_func_srun = arg_set_kill_on_bad_exit, .get_func = arg_get_kill_on_bad_exit, .reset_func = arg_reset_kill_on_bad_exit, }; static int arg_set_kill_on_invalid_dep(slurm_opt_t *opt, const char *arg) { if (!xstrcasecmp(arg, "yes")) opt->job_flags |= KILL_INV_DEP; else if (!xstrcasecmp(arg, "no")) opt->job_flags |= NO_KILL_INV_DEP; else { error("Invalid --kill-on-invalid-dep specification"); exit(-1); } return SLURM_SUCCESS; } static int arg_set_data_kill_on_invalid_dep(slurm_opt_t *opt, const data_t *arg, data_t *errors) { int rc; bool kill; if ((rc = data_copy_bool_converted(arg, &kill))) ADD_DATA_ERROR("Unable to read boolean", rc); else if (kill) opt->job_flags |= KILL_INV_DEP; else opt->job_flags |= NO_KILL_INV_DEP; return rc; } static char *arg_get_kill_on_invalid_dep(slurm_opt_t *opt) { if (opt->job_flags & KILL_INV_DEP) return xstrdup("yes"); else if (opt->job_flags & NO_KILL_INV_DEP) return xstrdup("no"); return xstrdup("unset"); } static void arg_reset_kill_on_invalid_dep(slurm_opt_t *opt) { opt->job_flags &= ~KILL_INV_DEP; opt->job_flags &= ~NO_KILL_INV_DEP; } static slurm_cli_opt_t slurm_opt_kill_on_invalid_dep = { .name = "kill-on-invalid-dep", .has_arg = required_argument, .val = LONG_OPT_KILL_INV_DEP, .set_func_sbatch = arg_set_kill_on_invalid_dep, .set_func_data = arg_set_data_kill_on_invalid_dep, .get_func = arg_get_kill_on_invalid_dep, .reset_func = arg_reset_kill_on_invalid_dep, }; COMMON_SRUN_BOOL_OPTION(labelio); static slurm_cli_opt_t slurm_opt_label = { .name = "label", .has_arg = no_argument, .val = 'l', .set_func_srun = arg_set_labelio, .get_func = arg_get_labelio, .reset_func = arg_reset_labelio, }; COMMON_STRING_OPTION(licenses); static slurm_cli_opt_t slurm_opt_licenses = { .name = "licenses", .has_arg = required_argument, .val = 'L', .set_func = arg_set_licenses, .set_func_data = arg_set_data_licenses, .get_func = arg_get_licenses, .reset_func = arg_reset_licenses, .reset_each_pass = true, }; static int arg_set_mail_type(slurm_opt_t *opt, const char *arg) { opt->mail_type |= parse_mail_type(arg); if (opt->mail_type == INFINITE16) { error("Invalid --mail-type specification"); exit(-1); } return SLURM_SUCCESS; } static int arg_set_data_mail_type(slurm_opt_t *opt, const data_t *arg, data_t *errors) { int rc; char *str = NULL; if ((rc = data_get_string_converted(arg, &str))) ADD_DATA_ERROR("Unable to read string", rc); else if ((opt->mail_type |= parse_mail_type(str)) == INFINITE16) { rc = SLURM_ERROR; ADD_DATA_ERROR("Invalid mail type specification", rc); } xfree(str); return rc; } static char *arg_get_mail_type(slurm_opt_t *opt) { return xstrdup(print_mail_type(opt->mail_type)); } COMMON_OPTION_RESET(mail_type, 0); static slurm_cli_opt_t slurm_opt_mail_type = { .name = "mail-type", .has_arg = required_argument, .val = LONG_OPT_MAIL_TYPE, .set_func = arg_set_mail_type, .set_func_data = arg_set_data_mail_type, .get_func = arg_get_mail_type, .reset_func = arg_reset_mail_type, .reset_each_pass = true, }; COMMON_STRING_OPTION(mail_user); static slurm_cli_opt_t slurm_opt_mail_user = { .name = "mail-user", .has_arg = required_argument, .val = LONG_OPT_MAIL_USER, .set_func = arg_set_mail_user, .set_func_data = arg_set_data_mail_user, .get_func = arg_get_mail_user, .reset_func = arg_reset_mail_user, .reset_each_pass = true, }; static int arg_set_max_threads(slurm_opt_t *opt, const char *arg) { if (!opt->srun_opt) return SLURM_ERROR; opt->srun_opt->max_threads = parse_int("--threads", arg, true); if (opt->srun_opt->max_threads > SRUN_MAX_THREADS) error("Thread value --threads=%d exceeds recommended limit of %d", opt->srun_opt->max_threads, SRUN_MAX_THREADS); return SLURM_SUCCESS; } static char *arg_get_max_threads(slurm_opt_t *opt) { if (!opt->srun_opt) return xstrdup("invalid-context"); return xstrdup_printf("%d", opt->srun_opt->max_threads); } static void arg_reset_max_threads(slurm_opt_t *opt) { if (opt->srun_opt) opt->srun_opt->max_threads = SRUN_MAX_THREADS; } static slurm_cli_opt_t slurm_opt_max_threads = { .name = "threads", .has_arg = required_argument, .val = 'T', .set_func_srun = arg_set_max_threads, .get_func = arg_get_max_threads, .reset_func = arg_reset_max_threads, }; COMMON_STRING_OPTION(mcs_label); static slurm_cli_opt_t slurm_opt_mcs_label = { .name = "mcs-label", .has_arg = required_argument, .val = LONG_OPT_MCS_LABEL, .set_func = arg_set_mcs_label, .set_func_data = arg_set_data_mcs_label, .get_func = arg_get_mcs_label, .reset_func = arg_reset_mcs_label, }; static int arg_set_mem(slurm_opt_t *opt, const char *arg) { if ((opt->pn_min_memory = str_to_mbytes2(arg)) == NO_VAL64) { error("Invalid --mem specification"); exit(-1); } /* * FIXME: the srun command silently stomps on any --mem-per-cpu * setting, as it was likely inherited from the env var. */ if (opt->srun_opt) opt->mem_per_cpu = NO_VAL64; return SLURM_SUCCESS; } static int arg_set_data_mem(slurm_opt_t *opt, const data_t *arg, data_t *errors) { int rc; char *str = NULL; if ((rc = data_get_string_converted(arg, &str))) ADD_DATA_ERROR("Unable to read string", rc); else if ((opt->pn_min_memory = str_to_mbytes2(str)) == NO_VAL64) { rc = SLURM_ERROR; ADD_DATA_ERROR("Invalid memory specification", rc); } xfree(str); return rc; } COMMON_MBYTES_OPTION_GET_AND_RESET(pn_min_memory); static slurm_cli_opt_t slurm_opt_mem = { .name = "mem", .has_arg = required_argument, .val = LONG_OPT_MEM, .set_func = arg_set_mem, .set_func_data = arg_set_data_mem, .get_func = arg_get_pn_min_memory, .reset_func = arg_reset_pn_min_memory, }; static int arg_set_mem_bind(slurm_opt_t *opt, const char *arg) { xfree(opt->mem_bind); if (slurm_verify_mem_bind(arg, &opt->mem_bind, &opt->mem_bind_type)) exit(-1); return SLURM_SUCCESS; } static int arg_set_data_mem_bind(slurm_opt_t *opt, const data_t *arg, data_t *errors) { int rc; char *str = NULL; xfree(opt->mem_bind); if ((rc = data_get_string_converted(arg, &str))) ADD_DATA_ERROR("Unable to read string", rc); else if (xstrcasestr(str, "help")) { rc = SLURM_ERROR; ADD_DATA_ERROR("memory binding help not supported", rc); } else if ((rc = slurm_verify_mem_bind(str, &opt->mem_bind, &opt->mem_bind_type))) ADD_DATA_ERROR("Invalid memory binding specification", rc); xfree(str); return rc; } static char *arg_get_mem_bind(slurm_opt_t *opt) { char *tmp; if (!opt->mem_bind_type) return xstrdup("unset"); tmp = slurm_xstr_mem_bind_type(opt->mem_bind_type); if (opt->mem_bind) xstrfmtcat(tmp, ":%s", opt->mem_bind); return tmp; } static void arg_reset_mem_bind(slurm_opt_t *opt) { xfree(opt->mem_bind); opt->mem_bind_type = 0; if (opt->srun_opt) { char *launch_params = slurm_get_launch_params(); if (xstrstr(launch_params, "mem_sort")) opt->mem_bind_type |= MEM_BIND_SORT; xfree(launch_params); } } static slurm_cli_opt_t slurm_opt_mem_bind = { .name = "mem-bind", .has_arg = required_argument, .val = LONG_OPT_MEM_BIND, .set_func = arg_set_mem_bind, .set_func_data = arg_set_data_mem_bind, .get_func = arg_get_mem_bind, .reset_func = arg_reset_mem_bind, .reset_each_pass = true, }; COMMON_MBYTES_OPTION(mem_per_cpu, --mem-per-cpu); static slurm_cli_opt_t slurm_opt_mem_per_cpu = { .name = "mem-per-cpu", .has_arg = required_argument, .val = LONG_OPT_MEM_PER_CPU, .set_func = arg_set_mem_per_cpu, .set_func_data = arg_set_data_mem_per_cpu, .get_func = arg_get_mem_per_cpu, .reset_func = arg_reset_mem_per_cpu, .reset_each_pass = true, }; COMMON_MBYTES_OPTION(mem_per_gpu, --mem-per-gpu); static slurm_cli_opt_t slurm_opt_mem_per_gpu = { .name = "mem-per-gpu", .has_arg = required_argument, .val = LONG_OPT_MEM_PER_GPU, .set_func = arg_set_mem_per_gpu, .set_func_data = arg_set_data_mem_per_gpu, .get_func = arg_get_mem_per_gpu, .reset_func = arg_reset_mem_per_gpu, .reset_each_pass = true, }; COMMON_INT_OPTION_SET(pn_min_cpus, "--mincpus"); COMMON_INT_OPTION_SET_DATA(pn_min_cpus); COMMON_INT_OPTION_GET(pn_min_cpus); COMMON_OPTION_RESET(pn_min_cpus, -1); static slurm_cli_opt_t slurm_opt_mincpus = { .name = "mincpus", .has_arg = required_argument, .val = LONG_OPT_MINCPUS, .set_func = arg_set_pn_min_cpus, .set_func_data = arg_set_data_pn_min_cpus, .get_func = arg_get_pn_min_cpus, .reset_func = arg_reset_pn_min_cpus, .reset_each_pass = true, }; COMMON_SRUN_STRING_OPTION(mpi_type); static slurm_cli_opt_t slurm_opt_mpi = { .name = "mpi", .has_arg = required_argument, .val = LONG_OPT_MPI, .set_func_srun = arg_set_mpi_type, .get_func = arg_get_mpi_type, .reset_func = arg_reset_mpi_type, }; static int arg_set_msg_timeout(slurm_opt_t *opt, const char *arg) { if (!opt->srun_opt) return SLURM_ERROR; opt->srun_opt->msg_timeout = parse_int("--msg-timeout", arg, true); return SLURM_SUCCESS; } static char *arg_get_msg_timeout(slurm_opt_t *opt) { if (!opt->srun_opt) return xstrdup("invalid-context"); return xstrdup_printf("%d", opt->srun_opt->msg_timeout); } static void arg_reset_msg_timeout(slurm_opt_t *opt) { if (opt->srun_opt) opt->srun_opt->msg_timeout = slurm_get_msg_timeout(); } static slurm_cli_opt_t slurm_opt_msg_timeout = { .name = "msg-timeout", .has_arg = required_argument, .val = LONG_OPT_MSG_TIMEOUT, .set_func_srun = arg_set_msg_timeout, .get_func = arg_get_msg_timeout, .reset_func = arg_reset_msg_timeout, }; COMMON_SRUN_BOOL_OPTION(multi_prog); static slurm_cli_opt_t slurm_opt_multi_prog = { .name = "multi-prog", .has_arg = no_argument, .val = LONG_OPT_MULTI, .set_func_srun = arg_set_multi_prog, .get_func = arg_get_multi_prog, .reset_func = arg_reset_multi_prog, }; COMMON_STRING_OPTION(network); static slurm_cli_opt_t slurm_opt_network = { .name = "network", .has_arg = required_argument, .val = LONG_OPT_NETWORK, .set_func = arg_set_network, .set_func_data = arg_set_data_network, .get_func = arg_get_network, .reset_func = arg_reset_network, .reset_each_pass = true, }; static int arg_set_nice(slurm_opt_t *opt, const char *arg) { long long tmp_nice; if (arg) tmp_nice = strtoll(arg, NULL, 10); else tmp_nice = 100; if (llabs(tmp_nice) > (NICE_OFFSET - 3)) { error("Invalid --nice value, out of range (+/- %u)", NICE_OFFSET - 3); exit(-1); } opt->nice = (int) tmp_nice; return SLURM_SUCCESS; } static int arg_set_data_nice(slurm_opt_t *opt, const data_t *arg, data_t *errors) { int64_t val; int rc = SLURM_SUCCESS; if (data_get_type(arg) == DATA_TYPE_NULL) opt->nice = 100; else if ((rc = data_get_int_converted(arg, &val))) ADD_DATA_ERROR("Unable to read integer value", rc); else if (llabs(val) >= (NICE_OFFSET - 3)) { rc = SLURM_ERROR; ADD_DATA_ERROR("Nice too large", rc); } else opt->nice = (int) val; return rc; } static char *arg_get_nice(slurm_opt_t *opt) { return xstrdup_printf("%d", opt->nice); } COMMON_OPTION_RESET(nice, NO_VAL); static slurm_cli_opt_t slurm_opt_nice = { .name = "nice", .has_arg = optional_argument, .val = LONG_OPT_NICE, .set_func = arg_set_nice, .set_func_data = arg_set_data_nice, .get_func = arg_get_nice, .reset_func = arg_reset_nice, }; COMMON_SRUN_BOOL_OPTION(no_alloc); static slurm_cli_opt_t slurm_opt_no_allocate = { .name = "no-allocate", .has_arg = no_argument, .val = 'Z', .set_func_srun = arg_set_no_alloc, .get_func = arg_get_no_alloc, .reset_func = arg_reset_no_alloc, }; /* See --bell above as well */ static int arg_set_no_bell(slurm_opt_t *opt, const char *arg) { if (opt->salloc_opt) opt->salloc_opt->bell = BELL_NEVER; return SLURM_SUCCESS; } static slurm_cli_opt_t slurm_opt_no_bell = { .name = "no-bell", .has_arg = no_argument, .val = LONG_OPT_NO_BELL, .set_func_salloc = arg_set_no_bell, .get_func = arg_get_bell, .reset_func = arg_reset_bell, }; static int arg_set_no_kill(slurm_opt_t *opt, const char *arg) { if (!arg || !xstrcasecmp(arg, "set")) opt->no_kill = true; else if (!xstrcasecmp(arg, "off") || !xstrcasecmp(arg, "no")) opt->no_kill = false; else { error("Invalid --no-kill specification"); exit(-1); } return SLURM_SUCCESS; } static int arg_set_data_no_kill(slurm_opt_t *opt, const data_t *arg, data_t *errors) { int rc = SLURM_SUCCESS; char *str = NULL; if (data_get_type(arg) == DATA_TYPE_NULL) opt->no_kill = true; else if ((rc = data_get_string_converted(arg, &str))) ADD_DATA_ERROR("Unable to read string", rc); else if (!xstrcasecmp(str, "set")) opt->no_kill = true; else if (!xstrcasecmp(str, "off") || !xstrcasecmp(str, "no")) opt->no_kill = false; else { rc = SLURM_ERROR; ADD_DATA_ERROR("Invalid no kill specification", rc); } xfree(str); return rc; } static char *arg_get_no_kill(slurm_opt_t *opt) { return xstrdup(opt->no_kill ? "set" : "unset"); } COMMON_OPTION_RESET(no_kill, false); static slurm_cli_opt_t slurm_opt_no_kill = { .name = "no-kill", .has_arg = optional_argument, .val = 'k', .set_func = arg_set_no_kill, .set_func_data = arg_set_data_no_kill, .get_func = arg_get_no_kill, .reset_func = arg_reset_no_kill, }; /* see --requeue below as well */ static int arg_set_no_requeue(slurm_opt_t *opt, const char *arg) { if (!opt->sbatch_opt) return SLURM_ERROR; opt->sbatch_opt->requeue = 0; return SLURM_SUCCESS; } static int arg_set_data_no_requeue(slurm_opt_t *opt, const data_t *arg, data_t *errors) { if (!opt->sbatch_opt) return SLURM_ERROR; opt->sbatch_opt->requeue = 0; return SLURM_SUCCESS; } static char *arg_get_requeue(slurm_opt_t *opt) { if (!opt->sbatch_opt) return xstrdup("invalid-context"); if (opt->sbatch_opt->requeue == NO_VAL) return xstrdup("unset"); else if (opt->sbatch_opt->requeue == 0) return xstrdup("no-requeue"); return xstrdup("requeue"); } static void arg_reset_requeue(slurm_opt_t *opt) { if (opt->sbatch_opt) opt->sbatch_opt->requeue = NO_VAL; } static slurm_cli_opt_t slurm_opt_no_requeue = { .name = "no-requeue", .has_arg = no_argument, .val = LONG_OPT_NO_REQUEUE, .set_func_sbatch = arg_set_no_requeue, .set_func_data = arg_set_data_no_requeue, .get_func = arg_get_requeue, .reset_func = arg_reset_requeue, }; static int arg_set_no_shell(slurm_opt_t *opt, const char *arg) { if (opt->salloc_opt) opt->salloc_opt->no_shell = true; return SLURM_SUCCESS; } static char *arg_get_no_shell(slurm_opt_t *opt) { if (!opt->salloc_opt) return xstrdup("invalid-context"); return xstrdup(opt->salloc_opt->no_shell ? "set" : "unset"); } static void arg_reset_no_shell(slurm_opt_t *opt) { if (opt->salloc_opt) opt->salloc_opt->no_shell = false; } static slurm_cli_opt_t slurm_opt_no_shell = { .name = "no-shell", .has_arg = no_argument, .val = LONG_OPT_NO_SHELL, .set_func_salloc = arg_set_no_shell, .get_func = arg_get_no_shell, .reset_func = arg_reset_no_shell, }; /* * FIXME: --nodefile and --nodelist options should be mutually exclusive. * Right now they'll overwrite one another; the last to run wins. */ static int arg_set_nodefile(slurm_opt_t *opt, const char *arg) { xfree(opt->nodefile); xfree(opt->nodelist); opt->nodefile = xstrdup(arg); return SLURM_SUCCESS; } COMMON_STRING_OPTION_GET_AND_RESET(nodefile); static slurm_cli_opt_t slurm_opt_nodefile = { .name = "nodefile", .has_arg = required_argument, .val = 'F', .set_func = arg_set_nodefile, .set_func_data = NULL, /* avoid security issues of reading user files */ .get_func = arg_get_nodefile, .reset_func = arg_reset_nodefile, .reset_each_pass = true, }; static int arg_set_nodelist(slurm_opt_t *opt, const char *arg) { xfree(opt->nodefile); xfree(opt->nodelist); opt->nodelist = xstrdup(arg); return SLURM_SUCCESS; } static int arg_set_data_nodelist(slurm_opt_t *opt, const data_t *arg, data_t *errors) { int rc; char *str = NULL; if ((rc = data_get_string_converted(arg, &str))) ADD_DATA_ERROR("Unable to read string", rc); else { xfree(opt->nodefile); xfree(opt->nodelist); opt->nodelist = str; str = NULL; } xfree(str); return rc; } COMMON_STRING_OPTION_GET_AND_RESET(nodelist); static slurm_cli_opt_t slurm_opt_nodelist = { .name = "nodelist", .has_arg = required_argument, .val = 'w', .set_func = arg_set_nodelist, .set_func_data = arg_set_data_nodelist, .get_func = arg_get_nodelist, .reset_func = arg_reset_nodelist, .reset_each_pass = true, }; static int arg_set_nodes(slurm_opt_t *opt, const char *arg) { if (!(opt->nodes_set = verify_node_count(arg, &opt->min_nodes, &opt->max_nodes))) exit(-1); return SLURM_SUCCESS; } typedef struct { int min; int max; data_t *errors; } node_cnt_t; static data_for_each_cmd_t _parse_nodes_counts(const data_t *data, void *arg) { node_cnt_t *nodes = arg; data_t *errors = nodes->errors; int64_t val; int rc; if ((rc = data_get_int_converted(data, &val))) { ADD_DATA_ERROR("Invalid node count", rc); return DATA_FOR_EACH_FAIL; } nodes->min = nodes->max; nodes->max = (int) val; return DATA_FOR_EACH_CONT; } static int arg_set_data_nodes(slurm_opt_t *opt, const data_t *arg, data_t *errors) { int rc = SLURM_SUCCESS; char *str = NULL; if (data_get_type(arg) == DATA_TYPE_LIST) { node_cnt_t counts = { .min = NO_VAL, .max = NO_VAL, .errors = errors }; if (data_get_list_length(arg) != 2) { rc = SLURM_ERROR; ADD_DATA_ERROR("Invalid node count list size", rc); } else if (data_list_for_each_const(arg, _parse_nodes_counts, &counts) < 0) { rc = SLURM_ERROR; ADD_DATA_ERROR("Invalid node count specification", rc); } else { opt->min_nodes = counts.min; opt->max_nodes = counts.max; } } else if ((rc = data_get_string_converted(arg, &str))) { ADD_DATA_ERROR("Unable to read string", rc); } else if (!(opt->nodes_set = verify_node_count(str, &opt->min_nodes, &opt->max_nodes))) { rc = SLURM_ERROR; ADD_DATA_ERROR("Invalid node count string", rc); } xfree(str); return rc; } static char *arg_get_nodes(slurm_opt_t *opt) { if (opt->min_nodes != opt->max_nodes) return xstrdup_printf("%d-%d", opt->min_nodes, opt->max_nodes); return xstrdup_printf("%d", opt->min_nodes); } static void arg_reset_nodes(slurm_opt_t *opt) { opt->min_nodes = 1; opt->max_nodes = 0; opt->nodes_set = false; } static slurm_cli_opt_t slurm_opt_nodes = { .name = "nodes", .has_arg = required_argument, .val = 'N', .set_func = arg_set_nodes, .set_func_data = arg_set_data_nodes, .get_func = arg_get_nodes, .reset_func = arg_reset_nodes, .reset_each_pass = true, }; static int arg_set_ntasks(slurm_opt_t *opt, const char *arg) { opt->ntasks = parse_int("--ntasks", arg, true); opt->ntasks_set = true; return SLURM_SUCCESS; } static int arg_set_data_ntasks(slurm_opt_t *opt, const data_t *arg, data_t *errors) { int64_t val; int rc = data_get_int_converted(arg, &val); if (rc) ADD_DATA_ERROR("Unable to read integer value", rc); else if (val >= INT_MAX) { rc = SLURM_ERROR; ADD_DATA_ERROR("ntasks too large", rc); } else if (val <= 0) { rc = SLURM_ERROR; ADD_DATA_ERROR("ntasks too small", rc); } else { opt->ntasks = (int) val; opt->ntasks_set = true; } return rc; } COMMON_INT_OPTION_GET(ntasks); static void arg_reset_ntasks(slurm_opt_t *opt) { opt->ntasks = 1; opt->ntasks_set = false; } static slurm_cli_opt_t slurm_opt_ntasks = { .name = "ntasks", .has_arg = required_argument, .val = 'n', .set_func = arg_set_ntasks, .set_func_data = arg_set_data_ntasks, .get_func = arg_get_ntasks, .reset_func = arg_reset_ntasks, .reset_each_pass = true, }; COMMON_INT_OPTION_SET(ntasks_per_core, "--ntasks-per-core"); COMMON_INT_OPTION_SET_DATA(ntasks_per_core); COMMON_INT_OPTION_GET(ntasks_per_core); COMMON_OPTION_RESET(ntasks_per_core, NO_VAL); static slurm_cli_opt_t slurm_opt_ntasks_per_core = { .name = "ntasks-per-core", .has_arg = required_argument, .val = LONG_OPT_NTASKSPERCORE, .set_func = arg_set_ntasks_per_core, .set_func_data = arg_set_data_ntasks_per_core, .get_func = arg_get_ntasks_per_core, .reset_func = arg_reset_ntasks_per_core, .reset_each_pass = true, }; COMMON_INT_OPTION_SET(ntasks_per_node, "--ntasks-per-node"); COMMON_INT_OPTION_SET_DATA(ntasks_per_node); COMMON_INT_OPTION_GET(ntasks_per_node); COMMON_OPTION_RESET(ntasks_per_node, NO_VAL); static slurm_cli_opt_t slurm_opt_ntasks_per_node = { .name = "ntasks-per-node", .has_arg = required_argument, .val = LONG_OPT_NTASKSPERNODE, .set_func = arg_set_ntasks_per_node, .set_func_data = arg_set_data_ntasks_per_node, .get_func = arg_get_ntasks_per_node, .reset_func = arg_reset_ntasks_per_node, .reset_each_pass = true, }; COMMON_INT_OPTION_SET(ntasks_per_socket, "--ntasks-per-socket"); COMMON_INT_OPTION_SET_DATA(ntasks_per_socket); COMMON_INT_OPTION_GET(ntasks_per_socket); COMMON_OPTION_RESET(ntasks_per_socket, NO_VAL); static slurm_cli_opt_t slurm_opt_ntasks_per_socket = { .name = "ntasks-per-socket", .has_arg = required_argument, .val = LONG_OPT_NTASKSPERSOCKET, .set_func = arg_set_ntasks_per_socket, .set_func_data = arg_set_data_ntasks_per_socket, .get_func = arg_get_ntasks_per_socket, .reset_func = arg_reset_ntasks_per_socket, .reset_each_pass = true, }; static int arg_set_open_mode(slurm_opt_t *opt, const char *arg) { uint8_t tmp = 0; if (!opt->sbatch_opt && !opt->srun_opt) return SLURM_ERROR; if (arg && (arg[0] == 'a' || arg[0] == 'A')) tmp = OPEN_MODE_APPEND; else if (arg && (arg[0] == 't' || arg[0] == 'T')) tmp = OPEN_MODE_TRUNCATE; else { error("Invalid --open-mode specification"); exit(-1); } if (opt->sbatch_opt) opt->sbatch_opt->open_mode = tmp; if (opt->srun_opt) opt->srun_opt->open_mode = tmp; return SLURM_SUCCESS; } static int arg_set_data_open_mode(slurm_opt_t *opt, const data_t *arg, data_t *errors) { int rc; char *str = NULL; if (!opt->sbatch_opt && !opt->srun_opt) return SLURM_ERROR; if ((rc = data_get_string_converted(arg, &str))) ADD_DATA_ERROR("Unable to read string", rc); else { uint8_t tmp = 0; if (str && (str[0] == 'a' || str[0] == 'A')) tmp = OPEN_MODE_APPEND; else if (str && (str[0] == 't' || str[0] == 'T')) tmp = OPEN_MODE_TRUNCATE; else { rc = SLURM_ERROR; ADD_DATA_ERROR("Invalid open mode specification", rc); } if (!rc) { if (opt->sbatch_opt) opt->sbatch_opt->open_mode = tmp; if (opt->srun_opt) opt->srun_opt->open_mode = tmp; } } xfree(str); return rc; } static char *arg_get_open_mode(slurm_opt_t *opt) { uint8_t tmp = 0; if (!opt->sbatch_opt && !opt->srun_opt) return xstrdup("invalid-context"); if (opt->sbatch_opt) tmp = opt->sbatch_opt->open_mode; if (opt->srun_opt) tmp = opt->srun_opt->open_mode; if (tmp == OPEN_MODE_APPEND) return xstrdup("a"); if (tmp == OPEN_MODE_TRUNCATE) return xstrdup("t"); return NULL; } static void arg_reset_open_mode(slurm_opt_t *opt) { if (opt->sbatch_opt) opt->sbatch_opt->open_mode = 0; if (opt->srun_opt) opt->srun_opt->open_mode = 0; } static slurm_cli_opt_t slurm_opt_open_mode = { .name = "open-mode", .has_arg = required_argument, .val = LONG_OPT_OPEN_MODE, .set_func_sbatch = arg_set_open_mode, .set_func_srun = arg_set_open_mode, .set_func_data = arg_set_data_open_mode, .get_func = arg_get_open_mode, .reset_func = arg_reset_open_mode, }; static int arg_set_ofname(slurm_opt_t *opt, const char *arg) { if (!opt->sbatch_opt && !opt->srun_opt) return SLURM_ERROR; xfree(opt->ofname); if (!xstrcasecmp(arg, "none")) opt->ofname = xstrdup("/dev/null"); else opt->ofname = xstrdup(arg); return SLURM_SUCCESS; } static int arg_set_data_ofname(slurm_opt_t *opt, const data_t *arg, data_t *errors) { int rc; char *str = NULL; if (!opt->sbatch_opt && !opt->srun_opt) return SLURM_ERROR; if ((rc = data_get_string_converted(arg, &str))) ADD_DATA_ERROR("Unable to read string", rc); else { xfree(opt->ofname); if (!xstrcasecmp(str, "none")) opt->ofname = xstrdup("/dev/null"); else { opt->ofname = str; str = NULL; } } xfree(str); return rc; } COMMON_STRING_OPTION_GET(ofname); COMMON_STRING_OPTION_RESET(ofname); static slurm_cli_opt_t slurm_opt_output = { .name = "output", .has_arg = required_argument, .val = 'o', .set_func_sbatch = arg_set_ofname, .set_func_srun = arg_set_ofname, .set_func_data = arg_set_data_ofname, .get_func = arg_get_ofname, .reset_func = arg_reset_ofname, }; COMMON_BOOL_OPTION(overcommit, "overcommit"); static slurm_cli_opt_t slurm_opt_overcommit = { .name = "overcommit", .has_arg = no_argument, .val = 'O', .set_func = arg_set_overcommit, .set_func_data = arg_set_data_overcommit, .get_func = arg_get_overcommit, .reset_func = arg_reset_overcommit, .reset_each_pass = true, }; /* * This option is directly tied to --exclusive. Both use the same output * function, and the string arguments are designed to mirror one another. */ static int arg_set_oversubscribe(slurm_opt_t *opt, const char *arg) { if (opt->srun_opt) opt->srun_opt->exclusive = false; opt->shared = JOB_SHARED_OK; return SLURM_SUCCESS; } static int arg_set_data_oversubscribe(slurm_opt_t *opt, const data_t *arg, data_t *errors) { if (opt->srun_opt) opt->srun_opt->exclusive = false; opt->shared = JOB_SHARED_OK; return SLURM_SUCCESS; } static slurm_cli_opt_t slurm_opt_oversubscribe = { .name = "oversubscribe", .has_arg = no_argument, .val = 's', .set_func = arg_set_oversubscribe, .set_func_data = arg_set_data_oversubscribe, .get_func = arg_get_exclusive, .reset_func = arg_reset_shared, .reset_each_pass = true, }; static int arg_set_het_group(slurm_opt_t *opt, const char *arg) { if (!opt->srun_opt) return SLURM_ERROR; xfree(opt->srun_opt->het_group); opt->srun_opt->het_group = xstrdup(arg); return SLURM_SUCCESS; } static char *arg_get_het_group(slurm_opt_t *opt) { if (!opt->srun_opt) return xstrdup("invalid-context"); return xstrdup(opt->srun_opt->het_group); } static void arg_reset_het_group(slurm_opt_t *opt) { if (opt->srun_opt) xfree(opt->srun_opt->het_group); } /* Continue support for pack-group */ static slurm_cli_opt_t slurm_opt_pack_group = { .name = "pack-group", .has_arg = required_argument, .val = LONG_OPT_HET_GROUP, .srun_early_pass = true, .set_func_srun = arg_set_het_group, .get_func = arg_get_het_group, .reset_func = arg_reset_het_group, }; static slurm_cli_opt_t slurm_opt_het_group = { .name = "het-group", .has_arg = required_argument, .val = LONG_OPT_HET_GROUP, .srun_early_pass = true, .set_func_srun = arg_set_het_group, .get_func = arg_get_het_group, .reset_func = arg_reset_het_group, }; static int arg_set_parsable(slurm_opt_t *opt, const char *arg) { if (!opt->sbatch_opt) return SLURM_ERROR; opt->sbatch_opt->parsable = true; return SLURM_SUCCESS; } static char *arg_get_parsable(slurm_opt_t *opt) { if (!opt->sbatch_opt) return xstrdup("invalid-context"); return xstrdup(opt->sbatch_opt->parsable ? "set" : "unset"); } static void arg_reset_parsable(slurm_opt_t *opt) { if (opt->sbatch_opt) opt->sbatch_opt->parsable = false; } static slurm_cli_opt_t slurm_opt_parsable = { .name = "parsable", .has_arg = no_argument, .val = LONG_OPT_PARSABLE, .set_func_sbatch = arg_set_parsable, .get_func = arg_get_parsable, .reset_func = arg_reset_parsable, }; COMMON_STRING_OPTION(partition); static slurm_cli_opt_t slurm_opt_partition = { .name = "partition", .has_arg = required_argument, .val = 'p', .set_func = arg_set_partition, .set_func_data = arg_set_data_partition, .get_func = arg_get_partition, .reset_func = arg_reset_partition, .reset_each_pass = true, }; static int arg_set_power(slurm_opt_t *opt, const char *arg) { opt->power = power_flags_id(arg); return SLURM_SUCCESS; } static int arg_set_data_power(slurm_opt_t *opt, const data_t *arg, data_t *errors) { int rc; char *str = NULL; if (!opt->sbatch_opt && !opt->srun_opt) return SLURM_ERROR; if ((rc = data_get_string_converted(arg, &str))) ADD_DATA_ERROR("Unable to read string", rc); else opt->power = power_flags_id(str); xfree(str); return rc; } static char *arg_get_power(slurm_opt_t *opt) { if (opt->power) return xstrdup(power_flags_str(opt->power)); return xstrdup("unset"); } COMMON_OPTION_RESET(power, 0); static slurm_cli_opt_t slurm_opt_power = { .name = "power", .has_arg = required_argument, .val = LONG_OPT_POWER, .set_func = arg_set_power, .set_func_data = arg_set_data_power, .get_func = arg_get_power, .reset_func = arg_reset_power, .reset_each_pass = true, }; COMMON_SRUN_BOOL_OPTION(preserve_env); static slurm_cli_opt_t slurm_opt_preserve_env = { .name = "preserve-env", .has_arg = no_argument, .val = 'E', .set_func_srun = arg_set_preserve_env, .get_func = arg_get_preserve_env, .reset_func = arg_reset_preserve_env, }; static int arg_set_priority(slurm_opt_t *opt, const char *arg) { if (!xstrcasecmp(arg, "TOP")) { opt->priority = NO_VAL - 1; } else { long long priority = strtoll(arg, NULL, 10); if (priority < 0) { error("Priority must be >= 0"); exit(-1); } if (priority >= NO_VAL) { error("Priority must be < %u", NO_VAL); exit(-1); } opt->priority = priority; } return SLURM_SUCCESS; } static int arg_set_data_priority(slurm_opt_t *opt, const data_t *arg, data_t *errors) { int rc; int64_t val; char *str = NULL; if ((rc = data_get_int_converted(arg, &val))) { if ((rc = data_get_string_converted(arg, &str))) ADD_DATA_ERROR("Unable to read string", rc); else if (!xstrcasecmp(str, "TOP")) opt->priority = NO_VAL - 1; else { rc = SLURM_ERROR; ADD_DATA_ERROR("Invalid priority", rc); } } else if (val >= NO_VAL) { rc = SLURM_ERROR; ADD_DATA_ERROR("Priority too large", rc); } else if (val <= 0) { rc = SLURM_ERROR; ADD_DATA_ERROR("Priority must be >0", rc); } else opt->priority = (int) val; xfree(str); return rc; } COMMON_INT_OPTION_GET_AND_RESET(priority); static slurm_cli_opt_t slurm_opt_priority = { .name = "priority", .has_arg = required_argument, .val = LONG_OPT_PRIORITY, .set_func = arg_set_priority, .set_func_data = arg_set_data_priority, .get_func = arg_get_priority, .reset_func = arg_reset_priority, }; static int arg_set_profile(slurm_opt_t *opt, const char *arg) { opt->profile = acct_gather_profile_from_string(arg); if (opt->profile == ACCT_GATHER_PROFILE_NOT_SET) { error("invalid --profile=%s option", arg); exit(-1); } return SLURM_SUCCESS; } static int arg_set_data_profile(slurm_opt_t *opt, const data_t *arg, data_t *errors) { int rc; char *str = NULL; if ((rc = data_get_string_converted(arg, &str))) ADD_DATA_ERROR("Unable to read string", rc); else opt->profile = acct_gather_profile_from_string(str); xfree(str); return rc; } static char *arg_get_profile(slurm_opt_t *opt) { return xstrdup(acct_gather_profile_to_string(opt->profile)); } COMMON_OPTION_RESET(profile, ACCT_GATHER_PROFILE_NOT_SET); static slurm_cli_opt_t slurm_opt_profile = { .name = "profile", .has_arg = required_argument, .val = LONG_OPT_PROFILE, .set_func = arg_set_profile, .set_func_data = arg_set_data_profile, .get_func = arg_get_profile, .reset_func = arg_reset_profile, }; COMMON_SRUN_STRING_OPTION(prolog); static slurm_cli_opt_t slurm_opt_prolog = { .name = "prolog", .has_arg = required_argument, .val = LONG_OPT_PROLOG, .set_func_srun = arg_set_prolog, .get_func = arg_get_prolog, .reset_func = arg_reset_prolog, }; static int arg_set_propagate(slurm_opt_t *opt, const char *arg) { const char *tmp = arg; if (!opt->sbatch_opt && !opt->srun_opt) return SLURM_ERROR; if (!tmp) tmp = "ALL"; if (opt->sbatch_opt) opt->sbatch_opt->propagate = xstrdup(tmp); if (opt->srun_opt) opt->srun_opt->propagate = xstrdup(tmp); return SLURM_SUCCESS; } static char *arg_get_propagate(slurm_opt_t *opt) { if (!opt->sbatch_opt && !opt->srun_opt) return xstrdup("invalid-context"); if (opt->sbatch_opt) return xstrdup(opt->sbatch_opt->propagate); if (opt->srun_opt) return xstrdup(opt->srun_opt->propagate); return NULL; } static void arg_reset_propagate(slurm_opt_t *opt) { if (opt->sbatch_opt) xfree(opt->sbatch_opt->propagate); if (opt->srun_opt) xfree(opt->srun_opt->propagate); } static slurm_cli_opt_t slurm_opt_propagate = { .name = "propagate", .has_arg = optional_argument, .val = LONG_OPT_PROPAGATE, .set_func_sbatch = arg_set_propagate, .set_func_srun = arg_set_propagate, .get_func = arg_get_propagate, .reset_func = arg_reset_propagate, }; COMMON_SRUN_BOOL_OPTION(pty); static slurm_cli_opt_t slurm_opt_pty = { .name = "pty", .has_arg = no_argument, .val = LONG_OPT_PTY, .set_func_srun = arg_set_pty, .get_func = arg_get_pty, .reset_func = arg_reset_pty, }; COMMON_STRING_OPTION(qos); static slurm_cli_opt_t slurm_opt_qos = { .name = "qos", .has_arg = required_argument, .val = 'q', .set_func = arg_set_qos, .set_func_data = arg_set_data_qos, .get_func = arg_get_qos, .reset_func = arg_reset_qos, }; static int arg_set_quiet(slurm_opt_t *opt, const char *arg) { opt->quiet++; return SLURM_SUCCESS; } COMMON_INT_OPTION_SET_DATA(quiet); COMMON_INT_OPTION_GET_AND_RESET(quiet); static slurm_cli_opt_t slurm_opt_quiet = { .name = "quiet", .has_arg = no_argument, .val = 'Q', .sbatch_early_pass = true, .set_func = arg_set_quiet, .set_func_data = arg_set_data_quiet, .get_func = arg_get_quiet, .reset_func = arg_reset_quiet, }; COMMON_SRUN_BOOL_OPTION(quit_on_intr); static slurm_cli_opt_t slurm_opt_quit_on_interrupt = { .name = "quit-on-interrupt", .has_arg = no_argument, .val = LONG_OPT_QUIT_ON_INTR, .set_func_srun = arg_set_quit_on_intr, .get_func = arg_get_quit_on_intr, .reset_func = arg_reset_quit_on_intr, }; COMMON_BOOL_OPTION(reboot, "reboot"); static slurm_cli_opt_t slurm_opt_reboot = { .name = "reboot", .has_arg = no_argument, .val = LONG_OPT_REBOOT, .set_func = arg_set_reboot, .set_func_data = arg_set_data_reboot, .get_func = arg_get_reboot, .reset_func = arg_reset_reboot, }; static int arg_set_relative(slurm_opt_t *opt, const char *arg) { if (!opt->srun_opt) return SLURM_ERROR; opt->srun_opt->relative = parse_int("--relative", arg, false); return SLURM_SUCCESS; } static char *arg_get_relative(slurm_opt_t *opt) { if (!opt->srun_opt) return xstrdup("invalid-context"); return xstrdup_printf("%d", opt->srun_opt->relative); } static void arg_reset_relative(slurm_opt_t *opt) { if (opt->srun_opt) opt->srun_opt->relative = NO_VAL; } static slurm_cli_opt_t slurm_opt_relative = { .name = "relative", .has_arg = required_argument, .val = 'r', .set_func_srun = arg_set_relative, .get_func = arg_get_relative, .reset_func = arg_reset_relative, .reset_each_pass = true, }; static int arg_set_requeue(slurm_opt_t *opt, const char *arg) { if (!opt->sbatch_opt) return SLURM_ERROR; opt->sbatch_opt->requeue = 1; return SLURM_SUCCESS; } static int arg_set_data_requeue(slurm_opt_t *opt, const data_t *arg, data_t *errors) { if (!opt->sbatch_opt) return SLURM_ERROR; opt->sbatch_opt->requeue = 1; return SLURM_SUCCESS; } /* arg_get_requeue and arg_reset_requeue defined before with --no-requeue */ static slurm_cli_opt_t slurm_opt_requeue = { .name = "requeue", .has_arg = no_argument, .val = LONG_OPT_REQUEUE, .set_func_sbatch = arg_set_requeue, .set_func_data = arg_set_data_requeue, .get_func = arg_get_requeue, .reset_func = arg_reset_requeue, }; COMMON_STRING_OPTION(reservation); static slurm_cli_opt_t slurm_opt_reservation = { .name = "reservation", .has_arg = required_argument, .val = LONG_OPT_RESERVATION, .set_func = arg_set_reservation, .set_func_data = arg_set_data_reservation, .get_func = arg_get_reservation, .reset_func = arg_reset_reservation, }; static int arg_set_resv_port_cnt(slurm_opt_t *opt, const char *arg) { if (!opt->srun_opt) return SLURM_ERROR; if (!arg) opt->srun_opt->resv_port_cnt = 0; else opt->srun_opt->resv_port_cnt = parse_int("--resv-port", arg, false); return SLURM_SUCCESS; } static char *arg_get_resv_port_cnt(slurm_opt_t *opt) { if (!opt->srun_opt) return xstrdup("invalid-context"); if (opt->srun_opt->resv_port_cnt == NO_VAL) return xstrdup("unset"); return xstrdup_printf("%d", opt->srun_opt->resv_port_cnt); } static void arg_reset_resv_port_cnt(slurm_opt_t *opt) { if (opt->srun_opt) opt->srun_opt->resv_port_cnt = NO_VAL; } static slurm_cli_opt_t slurm_opt_resv_ports = { .name = "resv-ports", .has_arg = optional_argument, .val = LONG_OPT_RESV_PORTS, .set_func_srun = arg_set_resv_port_cnt, .get_func = arg_get_resv_port_cnt, .reset_func = arg_reset_resv_port_cnt, .reset_each_pass = true, }; static int arg_set_signal(slurm_opt_t *opt, const char *arg) { if (get_signal_opts((char *) arg, &opt->warn_signal, &opt->warn_time, &opt->warn_flags)) { error("Invalid --signal specification"); exit(-1); } return SLURM_SUCCESS; } static int arg_set_data_signal(slurm_opt_t *opt, const data_t *arg, data_t *errors) { int rc; char *str = NULL; if ((rc = data_get_string_converted(arg, &str))) ADD_DATA_ERROR("Unable to read string", rc); else if (get_signal_opts(str, &opt->warn_signal, &opt->warn_time, &opt->warn_flags)) { rc = SLURM_ERROR; ADD_DATA_ERROR("Invalid SIGNAL specification", rc); } xfree(str); return rc; } static char *arg_get_signal(slurm_opt_t *opt) { return signal_opts_to_cmdline(opt->warn_signal, opt->warn_time, opt->warn_flags); } static void arg_reset_signal(slurm_opt_t *opt) { opt->warn_flags = 0; opt->warn_signal = 0; opt->warn_time = 0; } static slurm_cli_opt_t slurm_opt_signal = { .name = "signal", .has_arg = required_argument, .val = LONG_OPT_SIGNAL, .set_func = arg_set_signal, .set_func_data = arg_set_data_signal, .get_func = arg_get_signal, .reset_func = arg_reset_signal, }; static int arg_set_slurmd_debug(slurm_opt_t *opt, const char *arg) { if (!opt->srun_opt) return SLURM_ERROR; opt->srun_opt->slurmd_debug = log_string2num(arg); return SLURM_SUCCESS; } static char *arg_get_slurmd_debug(slurm_opt_t *opt) { if (!opt->srun_opt) return xstrdup("invalid-context"); return xstrdup(log_num2string(opt->srun_opt->slurmd_debug)); } static void arg_reset_slurmd_debug(slurm_opt_t *opt) { if (opt->srun_opt) opt->srun_opt->slurmd_debug = LOG_LEVEL_QUIET; } static slurm_cli_opt_t slurm_opt_slurmd_debug = { .name = "slurmd-debug", .has_arg = required_argument, .val = LONG_OPT_SLURMD_DEBUG, .set_func_srun = arg_set_slurmd_debug, .get_func = arg_get_slurmd_debug, .reset_func = arg_reset_slurmd_debug, }; COMMON_INT_OPTION_SET(sockets_per_node, "--sockets-per-node"); COMMON_INT_OPTION_SET_DATA(sockets_per_node); COMMON_INT_OPTION_GET(sockets_per_node); COMMON_OPTION_RESET(sockets_per_node, NO_VAL); static slurm_cli_opt_t slurm_opt_sockets_per_node = { .name = "sockets-per-node", .has_arg = required_argument, .val = LONG_OPT_SOCKETSPERNODE, .set_func = arg_set_sockets_per_node, .set_func_data = arg_set_data_sockets_per_node, .get_func = arg_get_sockets_per_node, .reset_func = arg_reset_sockets_per_node, .reset_each_pass = true, }; static int arg_set_spread_job(slurm_opt_t *opt, const char *arg) { opt->job_flags |= SPREAD_JOB; return SLURM_SUCCESS; } static int arg_set_data_spread_job(slurm_opt_t *opt, const data_t *arg, data_t *errors) { opt->job_flags |= SPREAD_JOB; return SLURM_SUCCESS; } static char *arg_get_spread_job(slurm_opt_t *opt) { if (opt->job_flags & SPREAD_JOB) return xstrdup("set"); return xstrdup("unset"); } static void arg_reset_spread_job(slurm_opt_t *opt) { opt->job_flags &= ~SPREAD_JOB; } static slurm_cli_opt_t slurm_opt_spread_job = { .name = "spread-job", .has_arg = no_argument, .val = LONG_OPT_SPREAD_JOB, .set_func = arg_set_spread_job, .set_func_data = arg_set_data_spread_job, .get_func = arg_get_spread_job, .reset_func = arg_reset_spread_job, .reset_each_pass = true, }; static int arg_set_switch_req(slurm_opt_t *opt, const char *arg) { opt->req_switch = parse_int("--switches", arg, true); return SLURM_SUCCESS; } static char *arg_get_switch_req(slurm_opt_t *opt) { if (opt->req_switch != -1) return xstrdup_printf("%d", opt->req_switch); return xstrdup("unset"); } static void arg_reset_switch_req(slurm_opt_t *opt) { opt->req_switch = -1; } COMMON_INT_OPTION_SET_DATA(req_switch); static slurm_cli_opt_t slurm_opt_switch_req = { .name = NULL, /* envvar only */ .has_arg = required_argument, .val = LONG_OPT_SWITCH_REQ, .set_func = arg_set_switch_req, .set_func_data = arg_set_data_req_switch, .get_func = arg_get_switch_req, .reset_func = arg_reset_switch_req, .reset_each_pass = true, }; static int arg_set_switch_wait(slurm_opt_t *opt, const char *arg) { opt->wait4switch = time_str2secs(arg); return SLURM_SUCCESS; } static int arg_set_data_switch_wait(slurm_opt_t *opt, const data_t *arg, data_t *errors) { int rc; char *str = NULL; if ((rc = data_get_string_converted(arg, &str))) ADD_DATA_ERROR("Unable to read string", rc); else opt->wait4switch = time_str2secs(str); xfree(str); return rc; } static char *arg_get_switch_wait(slurm_opt_t *opt) { char time_str[32]; secs2time_str(opt->wait4switch, time_str, sizeof(time_str)); return xstrdup_printf("%s", time_str); } static void arg_reset_switch_wait(slurm_opt_t *opt) { opt->req_switch = -1; opt->wait4switch = -1; } static slurm_cli_opt_t slurm_opt_switch_wait = { .name = NULL, /* envvar only */ .has_arg = required_argument, .val = LONG_OPT_SWITCH_WAIT, .set_func = arg_set_switch_wait, .set_func_data = arg_set_data_switch_wait, .get_func = arg_get_switch_wait, .reset_func = arg_reset_switch_wait, .reset_each_pass = true, }; static int arg_set_switches(slurm_opt_t *opt, const char *arg) { char *tmparg = xstrdup(arg); char *split = xstrchr(tmparg, '@'); if (split) { split[0] = '\0'; split++; opt->wait4switch = time_str2secs(split); } opt->req_switch = parse_int("--switches", tmparg, true); xfree(tmparg); return SLURM_SUCCESS; } static int _handle_data_switches_str(slurm_opt_t *opt, char *arg, data_t *errors) { int rc = SLURM_SUCCESS; char *split = xstrchr(arg, '@'); if (split) { split[0] = '\0'; split++; opt->wait4switch = time_str2secs(split); rc = _handle_data_switches_str(opt, arg, errors); } else opt->req_switch = atoi(arg); return rc; } static int _handle_data_switches_data(slurm_opt_t *opt, const data_t *arg, data_t *errors) { int rc; char *str = NULL; if ((rc = data_get_string_converted(arg, &str))) ADD_DATA_ERROR("Unable to read string", rc); else rc = _handle_data_switches_str(opt, str, errors); xfree(str); return rc; } typedef struct { slurm_opt_t *opt; data_t *errors; } data_foreach_switches_t; data_for_each_cmd_t _foreach_data_switches(const char *key, const data_t *data, void *arg) { data_foreach_switches_t *args = arg; data_t *errors = args->errors; if (!xstrcasecmp("count", key)) { int64_t val; if (data_get_int_converted(data, &val)) { ADD_DATA_ERROR("Invalid count specification", SLURM_ERROR); return DATA_FOR_EACH_FAIL; } args->opt->req_switch = (int) val; } else if (!xstrcasecmp("timeout", key)) { char *str = NULL; if (data_get_string_converted(data, &str)) { return DATA_FOR_EACH_FAIL; ADD_DATA_ERROR("Invalid timeout specification", SLURM_ERROR); } args->opt->wait4switch = time_str2secs(str); xfree(str); } else { ADD_DATA_ERROR("unknown key in switches specification", SLURM_ERROR); return DATA_FOR_EACH_FAIL; } return DATA_FOR_EACH_CONT; } static int arg_set_data_switches(slurm_opt_t *opt, const data_t *arg, data_t *errors) { int rc = SLURM_SUCCESS; int64_t val; if (data_get_type(arg) == DATA_TYPE_DICT) { data_foreach_switches_t args = { .opt = opt, .errors = errors, }; if (data_dict_for_each_const(arg, _foreach_data_switches, &args) < 0) { rc = SLURM_ERROR; ADD_DATA_ERROR("Invalid switch specification", rc); } } else if ((rc = data_get_int_converted(arg, &val))) return _handle_data_switches_data(opt, arg, errors); else if (val >= INT_MAX) { rc = SLURM_ERROR; ADD_DATA_ERROR("Integer too large", rc); } else if (val <= 0) { rc = SLURM_ERROR; ADD_DATA_ERROR("Must request at least 1 switch", rc); } else opt->req_switch = (int) val; return rc; } static char *arg_get_switches(slurm_opt_t *opt) { if (opt->wait4switch != -1) { char time_str[32]; secs2time_str(opt->wait4switch, time_str, sizeof(time_str)); return xstrdup_printf("%d@%s", opt->req_switch, time_str); } if (opt->req_switch != -1) return xstrdup_printf("%d", opt->req_switch); return xstrdup("unset"); } static void arg_reset_switches(slurm_opt_t *opt) { opt->req_switch = -1; opt->wait4switch = -1; } static slurm_cli_opt_t slurm_opt_switches = { .name = "switches", .has_arg = required_argument, .val = LONG_OPT_SWITCHES, .set_func = arg_set_switches, .set_func_data = arg_set_data_switches, .get_func = arg_get_switches, .reset_func = arg_reset_switches, .reset_each_pass = true, }; COMMON_SRUN_STRING_OPTION(task_epilog); static slurm_cli_opt_t slurm_opt_task_epilog = { .name = "task-epilog", .has_arg = required_argument, .val = LONG_OPT_TASK_EPILOG, .set_func_srun = arg_set_task_epilog, .get_func = arg_get_task_epilog, .reset_func = arg_reset_task_epilog, }; COMMON_SRUN_STRING_OPTION(task_prolog); static slurm_cli_opt_t slurm_opt_task_prolog = { .name = "task-prolog", .has_arg = required_argument, .val = LONG_OPT_TASK_PROLOG, .set_func_srun = arg_set_task_prolog, .get_func = arg_get_task_prolog, .reset_func = arg_reset_task_prolog, }; /* Deprecated form of --ntasks-per-node */ static slurm_cli_opt_t slurm_opt_tasks_per_node = { .name = "tasks-per-node", .has_arg = required_argument, .val = LONG_OPT_NTASKSPERNODE, .set_func = arg_set_ntasks_per_node, .get_func = arg_get_ntasks_per_node, .reset_func = arg_reset_ntasks_per_node, .reset_each_pass = true, }; static int arg_set_test_only(slurm_opt_t *opt, const char *arg) { if (!opt->sbatch_opt && !opt->srun_opt) return SLURM_ERROR; if (opt->sbatch_opt) opt->sbatch_opt->test_only = true; if (opt->srun_opt) opt->srun_opt->test_only = true; return SLURM_SUCCESS; } static int arg_set_data_test_only(slurm_opt_t *opt, const data_t *arg, data_t *errors) { if (!opt->sbatch_opt && !opt->srun_opt) return SLURM_ERROR; if (opt->sbatch_opt) opt->sbatch_opt->test_only = true; if (opt->srun_opt) opt->srun_opt->test_only = true; return SLURM_SUCCESS; } static char *arg_get_test_only(slurm_opt_t *opt) { bool tmp = false; if (!opt->sbatch_opt && !opt->srun_opt) return xstrdup("invalid-context"); if (opt->sbatch_opt) tmp = opt->sbatch_opt->test_only; if (opt->srun_opt) tmp = opt->srun_opt->test_only; return xstrdup(tmp ? "set" : "unset"); } static void arg_reset_test_only(slurm_opt_t *opt) { if (opt->sbatch_opt) opt->sbatch_opt->test_only = false; if (opt->srun_opt) opt->srun_opt->test_only = false; } static slurm_cli_opt_t slurm_opt_test_only = { .name = "test-only", .has_arg = no_argument, .val = LONG_OPT_TEST_ONLY, .set_func_sbatch = arg_set_test_only, .set_func_srun = arg_set_test_only, .set_func_data = arg_set_data_test_only, .get_func = arg_get_test_only, .reset_func = arg_reset_test_only, }; /* note this is mutually exclusive with --core-spec above */ static int arg_set_thread_spec(slurm_opt_t *opt, const char *arg) { opt->core_spec = parse_int("--thread-spec", arg, true); opt->core_spec |= CORE_SPEC_THREAD; return SLURM_SUCCESS; } static int arg_set_data_thread_spec(slurm_opt_t *opt, const data_t *arg, data_t *errors) { int rc; int64_t val; if ((rc = data_get_int_converted(arg, &val))) ADD_DATA_ERROR("Unable to read integer", rc); else if (val >= CORE_SPEC_THREAD) { rc = SLURM_ERROR; ADD_DATA_ERROR("core_spec is too large", rc); } else if (val <= 0) { rc = SLURM_ERROR; ADD_DATA_ERROR("core_spec must be >0", rc); } else { opt->core_spec = val; opt->core_spec |= CORE_SPEC_THREAD; } return rc; } static char *arg_get_thread_spec(slurm_opt_t *opt) { if ((opt->core_spec == NO_VAL16) || !(opt->core_spec & CORE_SPEC_THREAD)) return xstrdup("unset"); return xstrdup_printf("%d", (opt->core_spec & ~CORE_SPEC_THREAD)); } static slurm_cli_opt_t slurm_opt_thread_spec = { .name = "thread-spec", .has_arg = required_argument, .val = LONG_OPT_THREAD_SPEC, .set_func = arg_set_thread_spec, .set_func_data = arg_set_data_thread_spec, .get_func = arg_get_thread_spec, .reset_func = arg_reset_core_spec, .reset_each_pass = true, }; COMMON_INT_OPTION_SET(threads_per_core, "--threads-per-core"); COMMON_INT_OPTION_SET_DATA(threads_per_core); COMMON_INT_OPTION_GET(threads_per_core); COMMON_OPTION_RESET(threads_per_core, NO_VAL); static slurm_cli_opt_t slurm_opt_threads_per_core = { .name = "threads-per-core", .has_arg = required_argument, .val = LONG_OPT_THREADSPERCORE, .set_func = arg_set_threads_per_core, .set_func_data = arg_set_data_threads_per_core, .get_func = arg_get_threads_per_core, .reset_func = arg_reset_threads_per_core, .reset_each_pass = true, }; static int arg_set_time_limit(slurm_opt_t *opt, const char *arg) { int time_limit; time_limit = time_str2mins(arg); if (time_limit == NO_VAL) { error("Invalid --time specification"); exit(-1); } else if (time_limit == 0) { time_limit = INFINITE; } opt->time_limit = time_limit; return SLURM_SUCCESS; } static int arg_set_data_time_limit(slurm_opt_t *opt, const data_t *arg, data_t *errors) { int rc; char *str = NULL; if (!opt->sbatch_opt && !opt->srun_opt) return SLURM_ERROR; if ((rc = data_get_string_converted(arg, &str))) ADD_DATA_ERROR("Unable to read string", rc); else { int time_limit = time_str2mins(str); if (time_limit == NO_VAL) { rc = SLURM_ERROR; ADD_DATA_ERROR("Invalid time specification", rc); } else if (time_limit == 0) { opt->time_limit = INFINITE; } else opt->time_limit = time_limit; } xfree(str); return rc; } COMMON_TIME_DURATION_OPTION_GET_AND_RESET(time_limit); static slurm_cli_opt_t slurm_opt_time_limit = { .name = "time", .has_arg = required_argument, .val = 't', .set_func = arg_set_time_limit, .set_func_data = arg_set_data_time_limit, .get_func = arg_get_time_limit, .reset_func = arg_reset_time_limit, }; static int arg_set_time_min(slurm_opt_t *opt, const char *arg) { int time_min; time_min = time_str2mins(arg); if (time_min == NO_VAL) { error("Invalid --time-min specification"); exit(-1); } else if (time_min == 0) { time_min = INFINITE; } opt->time_min = time_min; return SLURM_SUCCESS; } static int arg_set_data_time_min(slurm_opt_t *opt, const data_t *arg, data_t *errors) { int rc; char *str = NULL; if (!opt->sbatch_opt && !opt->srun_opt) return SLURM_ERROR; if ((rc = data_get_string_converted(arg, &str))) ADD_DATA_ERROR("Unable to read string", rc); else { int time_limit = time_str2mins(str); if (time_limit == NO_VAL) { rc = SLURM_ERROR; ADD_DATA_ERROR("Invalid time specification", rc); } else if (time_limit == 0) { opt->time_min = INFINITE; } else opt->time_min = time_limit; } xfree(str); return rc; } COMMON_TIME_DURATION_OPTION_GET_AND_RESET(time_min); static slurm_cli_opt_t slurm_opt_time_min = { .name = "time-min", .has_arg = required_argument, .val = LONG_OPT_TIME_MIN, .set_func = arg_set_time_min, .set_func_data = arg_set_data_time_min, .get_func = arg_get_time_min, .reset_func = arg_reset_time_min, }; COMMON_MBYTES_OPTION(pn_min_tmp_disk, --tmp); static slurm_cli_opt_t slurm_opt_tmp = { .name = "tmp", .has_arg = required_argument, .val = LONG_OPT_TMP, .set_func = arg_set_pn_min_tmp_disk, .set_func_data = arg_set_data_pn_min_tmp_disk, .get_func = arg_get_pn_min_tmp_disk, .reset_func = arg_reset_pn_min_tmp_disk, .reset_each_pass = true, }; static int arg_set_uid(slurm_opt_t *opt, const char *arg) { if (getuid() != 0) { error("--uid only permitted by root user"); exit(-1); } if (uid_from_string(arg, &opt->uid) < 0) { error("Invalid --uid specification"); exit(-1); } return SLURM_SUCCESS; } static int arg_set_data_uid(slurm_opt_t *opt, const data_t *arg, data_t *errors) { int rc; char *str = NULL; if ((rc = data_get_string_converted(arg, &str))) ADD_DATA_ERROR("Unable to read string", rc); else if (uid_from_string(str, &opt->uid) < 0) { rc = SLURM_ERROR; ADD_DATA_ERROR("Invalid user id specification", rc); } xfree(str); return rc; } COMMON_INT_OPTION_GET(uid); COMMON_OPTION_RESET(uid, getuid()); static slurm_cli_opt_t slurm_opt_uid = { .name = "uid", .has_arg = required_argument, .val = LONG_OPT_UID, .set_func = arg_set_uid, .set_func_data = arg_set_data_uid, .get_func = arg_get_uid, .reset_func = arg_reset_uid, }; /* * This is not exposed as an argument in sbatch, but is used * in xlate.c to translate a PBS option. */ static int arg_set_umask(slurm_opt_t *opt, const char *arg) { if (!opt->sbatch_opt) return SLURM_ERROR; opt->sbatch_opt->umask = strtol(arg, NULL, 0); if ((opt->sbatch_opt->umask < 0) || (opt->sbatch_opt->umask > 0777)) { error("Invalid -W umask= specification"); exit(-1); } return SLURM_SUCCESS; } static int arg_set_data_umask(slurm_opt_t *opt, const data_t *arg, data_t *errors) { int rc; char *str = NULL; int32_t umask; if ((rc = data_get_string_converted(arg, &str))) ADD_DATA_ERROR("Unable to read string", rc); else if (sscanf(str, "%"SCNo32, &umask) != 1) { rc = SLURM_ERROR; ADD_DATA_ERROR("Invalid octal umask", rc); } else if (umask < 0) { rc = SLURM_ERROR; ADD_DATA_ERROR("umask too small", rc); } else if (umask < 0 || umask > 07777) { rc = SLURM_ERROR; ADD_DATA_ERROR("umask too large", rc); } else opt->sbatch_opt->umask = umask; xfree(str); return rc; } static char *arg_get_umask(slurm_opt_t *opt) { if (!opt->sbatch_opt) return xstrdup("invalid-context"); return xstrdup_printf("0%o", opt->sbatch_opt->umask); } static void arg_reset_umask(slurm_opt_t *opt) { if (opt->sbatch_opt) opt->sbatch_opt->umask = -1; } static slurm_cli_opt_t slurm_opt_umask = { .name = NULL, /* only for use through xlate.c */ .has_arg = no_argument, .val = LONG_OPT_UMASK, .set_func_sbatch = arg_set_umask, .set_func_data = arg_set_data_umask, .get_func = arg_get_umask, .reset_func = arg_reset_umask, .reset_each_pass = true, }; COMMON_SRUN_BOOL_OPTION(unbuffered); static slurm_cli_opt_t slurm_opt_unbuffered = { .name = "unbuffered", .has_arg = no_argument, .val = 'u', .set_func_srun = arg_set_unbuffered, .get_func = arg_get_unbuffered, .reset_func = arg_reset_unbuffered, }; static int arg_set_use_min_nodes(slurm_opt_t *opt, const char *arg) { opt->job_flags |= USE_MIN_NODES; return SLURM_SUCCESS; } static int arg_set_data_use_min_nodes(slurm_opt_t *opt, const data_t *arg, data_t *errors) { opt->job_flags |= USE_MIN_NODES; return SLURM_SUCCESS; } static char *arg_get_use_min_nodes(slurm_opt_t *opt) { if (opt->job_flags & USE_MIN_NODES) return xstrdup("set"); return xstrdup("unset"); } static void arg_reset_use_min_nodes(slurm_opt_t *opt) { opt->job_flags &= ~(USE_MIN_NODES); } static slurm_cli_opt_t slurm_opt_use_min_nodes = { .name = "use-min-nodes", .has_arg = no_argument, .val = LONG_OPT_USE_MIN_NODES, .set_func = arg_set_use_min_nodes, .set_func_data = arg_set_data_use_min_nodes, .get_func = arg_get_use_min_nodes, .reset_func = arg_reset_use_min_nodes, .reset_each_pass = true, }; static int arg_set_usage(slurm_opt_t *opt, const char *arg) { if (opt->usage_func) (opt->usage_func)(); else error("Could not find --usage message"); exit(0); return SLURM_SUCCESS; } static char *arg_get_usage(slurm_opt_t *opt) { return NULL; /* no op */ } static void arg_reset_usage(slurm_opt_t *opt) { /* no op */ } static slurm_cli_opt_t slurm_opt_usage = { .name = "usage", .has_arg = no_argument, .val = LONG_OPT_USAGE, .sbatch_early_pass = true, .set_func = arg_set_usage, .get_func = arg_get_usage, .reset_func = arg_reset_usage, }; static int arg_set_verbose(slurm_opt_t *opt, const char *arg) { /* * Note that verbose is handled a bit differently. As a cli argument, * it has no_argument set so repeated 'v' characters can be used. * As an environment variable though, it will have a numeric value. * The boolean treatment from slurm_process_option() will still pass * the string form along to us, which we can parse here into the * correct value. */ if (!arg) opt->verbose++; else opt->verbose = parse_int("--verbose", arg, false); return SLURM_SUCCESS; } COMMON_INT_OPTION_GET_AND_RESET(verbose); static slurm_cli_opt_t slurm_opt_verbose = { .name = "verbose", .has_arg = no_argument, /* sort of */ .val = 'v', .sbatch_early_pass = true, .set_func = arg_set_verbose, .get_func = arg_get_verbose, .reset_func = arg_reset_verbose, }; static int arg_set_version(slurm_opt_t *opt, const char *arg) { print_slurm_version(); exit(0); } static char *arg_get_version(slurm_opt_t *opt) { return NULL; /* no op */ } static void arg_reset_version(slurm_opt_t *opt) { /* no op */ } static slurm_cli_opt_t slurm_opt_version = { .name = "version", .has_arg = no_argument, .val = 'V', .sbatch_early_pass = true, .set_func = arg_set_version, .get_func = arg_get_version, .reset_func = arg_reset_version, }; static int arg_set_wait(slurm_opt_t *opt, const char *arg) { if (!opt->sbatch_opt) return SLURM_ERROR; opt->sbatch_opt->wait = true; return SLURM_SUCCESS; } static char *arg_get_wait(slurm_opt_t *opt) { if (!opt->sbatch_opt) return xstrdup("invalid-context"); return xstrdup(opt->sbatch_opt->wait ? "set" : "unset"); } static void arg_reset_wait(slurm_opt_t *opt) { if (opt->sbatch_opt) opt->sbatch_opt->wait = false; } static slurm_cli_opt_t slurm_opt_wait = { .name = "wait", .has_arg = no_argument, .val = 'W', .set_func_sbatch = arg_set_wait, .get_func = arg_get_wait, .reset_func = arg_reset_wait, }; static int arg_set_wait_srun(slurm_opt_t *opt, const char *arg) { if (!opt->srun_opt) return SLURM_ERROR; opt->srun_opt->max_wait = parse_int("--wait", arg, false); return SLURM_SUCCESS; } static char *arg_get_wait_srun(slurm_opt_t *opt) { if (!opt->srun_opt) return xstrdup("invalid-context"); return xstrdup_printf("%d", opt->srun_opt->max_wait); } static void arg_reset_wait_srun(slurm_opt_t *opt) { if (opt->srun_opt) opt->srun_opt->max_wait = slurm_get_wait_time(); } static slurm_cli_opt_t slurm_opt_wait_srun = { .name = "wait", .has_arg = required_argument, .val = 'W', .set_func_srun = arg_set_wait_srun, .get_func = arg_get_wait_srun, .reset_func = arg_reset_wait_srun, }; static int arg_set_wait_all_nodes(slurm_opt_t *opt, const char *arg) { uint16_t tmp; if (!opt->salloc_opt && !opt->sbatch_opt) return SLURM_ERROR; tmp = parse_int("--wait-all-nodes", arg, false); if (tmp > 1) { error("Invalid --wait-all-nodes specification"); exit(-1); } if (opt->salloc_opt) opt->salloc_opt->wait_all_nodes = tmp; if (opt->sbatch_opt) opt->sbatch_opt->wait_all_nodes = tmp; return SLURM_SUCCESS; } static int arg_set_data_wait_all_nodes(slurm_opt_t *opt, const data_t *arg, data_t *errors) { int64_t val; int rc = data_get_int_converted(arg, &val); if (rc) ADD_DATA_ERROR("Unable to read integer value", rc); else if (val > 1) { rc = SLURM_ERROR; ADD_DATA_ERROR("Wait all nodes too large", rc); } else if (val < 0) { rc = SLURM_ERROR; ADD_DATA_ERROR("Wait all nodes too small", rc); } else { if (opt->salloc_opt) opt->salloc_opt->wait_all_nodes = val; if (opt->sbatch_opt) opt->sbatch_opt->wait_all_nodes = val; } return rc; } static char *arg_get_wait_all_nodes(slurm_opt_t *opt) { uint16_t tmp = NO_VAL16; if (!opt->salloc_opt && !opt->sbatch_opt) return xstrdup("invalid-context"); if (opt->salloc_opt) tmp = opt->salloc_opt->wait_all_nodes; if (opt->sbatch_opt) tmp = opt->sbatch_opt->wait_all_nodes; return xstrdup_printf("%u", tmp); } static void arg_reset_wait_all_nodes(slurm_opt_t *opt) { if (opt->salloc_opt) opt->salloc_opt->wait_all_nodes = NO_VAL16; if (opt->sbatch_opt) opt->sbatch_opt->wait_all_nodes = NO_VAL16; } static slurm_cli_opt_t slurm_opt_wait_all_nodes = { .name = "wait-all-nodes", .has_arg = required_argument, .val = LONG_OPT_WAIT_ALL_NODES, .set_func_salloc = arg_set_wait_all_nodes, .set_func_sbatch = arg_set_wait_all_nodes, .set_func_data = arg_set_data_wait_all_nodes, .get_func = arg_get_wait_all_nodes, .reset_func = arg_reset_wait_all_nodes, }; COMMON_STRING_OPTION(wckey); static slurm_cli_opt_t slurm_opt_wckey = { .name = "wckey", .has_arg = required_argument, .val = LONG_OPT_WCKEY, .set_func = arg_set_wckey, .set_func_data = arg_set_data_wckey, .get_func = arg_get_wckey, .reset_func = arg_reset_wckey, }; COMMON_SBATCH_STRING_OPTION(wrap); static slurm_cli_opt_t slurm_opt_wrap = { .name = "wrap", .has_arg = required_argument, .val = LONG_OPT_WRAP, .sbatch_early_pass = true, .set_func_sbatch = arg_set_wrap, .set_func_data = arg_set_data_wrap, .get_func = arg_get_wrap, .reset_func = arg_reset_wrap, }; static int arg_set_x11(slurm_opt_t *opt, const char *arg) { if (arg) opt->x11 = x11_str2flags(arg); else opt->x11 = X11_FORWARD_ALL; return SLURM_SUCCESS; } static char *arg_get_x11(slurm_opt_t *opt) { return xstrdup(x11_flags2str(opt->x11)); } COMMON_OPTION_RESET(x11, 0); static slurm_cli_opt_t slurm_opt_x11 = { #ifdef WITH_SLURM_X11 .name = "x11", #else /* * Keep the code paths active, but disables the option name itself * so the SPANK plugin can claim it. */ .name = NULL, #endif .has_arg = optional_argument, .val = LONG_OPT_X11, .set_func_salloc = arg_set_x11, .set_func_srun = arg_set_x11, .get_func = arg_get_x11, .reset_func = arg_reset_x11, }; static const slurm_cli_opt_t *common_options[] = { &slurm_opt__unknown_, &slurm_opt_accel_bind, &slurm_opt_account, &slurm_opt_acctg_freq, &slurm_opt_alloc_nodelist, &slurm_opt_array, &slurm_opt_batch, &slurm_opt_bcast, &slurm_opt_begin, &slurm_opt_bell, &slurm_opt_bb, &slurm_opt_bbf, &slurm_opt_c_constraint, &slurm_opt_chdir, &slurm_opt_cluster, &slurm_opt_clusters, &slurm_opt_comment, &slurm_opt_compress, &slurm_opt_contiguous, &slurm_opt_constraint, &slurm_opt_core_spec, &slurm_opt_cores_per_socket, &slurm_opt_cpu_bind, &slurm_opt_cpu_underscore_bind, &slurm_opt_cpu_freq, &slurm_opt_cpus_per_gpu, &slurm_opt_cpus_per_task, &slurm_opt_deadline, &slurm_opt_debugger_test, &slurm_opt_delay_boot, &slurm_opt_environment, &slurm_opt_dependency, &slurm_opt_disable_status, &slurm_opt_distribution, &slurm_opt_epilog, &slurm_opt_error, &slurm_opt_exclude, &slurm_opt_exclusive, &slurm_opt_export, &slurm_opt_export_file, &slurm_opt_extra_node_info, &slurm_opt_get_user_env, &slurm_opt_gid, &slurm_opt_gpu_bind, &slurm_opt_gpu_freq, &slurm_opt_gpus, &slurm_opt_gpus_per_node, &slurm_opt_gpus_per_socket, &slurm_opt_gpus_per_task, &slurm_opt_gres, &slurm_opt_gres_flags, &slurm_opt_help, &slurm_opt_het_group, &slurm_opt_hint, &slurm_opt_hold, &slurm_opt_ignore_pbs, &slurm_opt_immediate, &slurm_opt_input, &slurm_opt_jobid, &slurm_opt_job_name, &slurm_opt_kill_command, &slurm_opt_kill_on_bad_exit, &slurm_opt_kill_on_invalid_dep, &slurm_opt_label, &slurm_opt_licenses, &slurm_opt_mail_type, &slurm_opt_mail_user, &slurm_opt_max_threads, &slurm_opt_mcs_label, &slurm_opt_mem, &slurm_opt_mem_bind, &slurm_opt_mem_per_cpu, &slurm_opt_mem_per_gpu, &slurm_opt_mincpus, &slurm_opt_mpi, &slurm_opt_msg_timeout, &slurm_opt_multi_prog, &slurm_opt_network, &slurm_opt_nice, &slurm_opt_no_allocate, &slurm_opt_no_bell, &slurm_opt_no_kill, &slurm_opt_no_shell, &slurm_opt_no_requeue, &slurm_opt_nodefile, &slurm_opt_nodelist, &slurm_opt_nodes, &slurm_opt_ntasks, &slurm_opt_ntasks_per_core, &slurm_opt_ntasks_per_node, &slurm_opt_ntasks_per_socket, &slurm_opt_open_mode, &slurm_opt_output, &slurm_opt_overcommit, &slurm_opt_oversubscribe, &slurm_opt_pack_group, &slurm_opt_parsable, &slurm_opt_partition, &slurm_opt_power, &slurm_opt_preserve_env, &slurm_opt_priority, &slurm_opt_profile, &slurm_opt_prolog, &slurm_opt_propagate, &slurm_opt_pty, &slurm_opt_qos, &slurm_opt_quiet, &slurm_opt_quit_on_interrupt, &slurm_opt_reboot, &slurm_opt_relative, &slurm_opt_requeue, &slurm_opt_reservation, &slurm_opt_resv_ports, &slurm_opt_signal, &slurm_opt_slurmd_debug, &slurm_opt_sockets_per_node, &slurm_opt_spread_job, &slurm_opt_switch_req, &slurm_opt_switch_wait, &slurm_opt_switches, &slurm_opt_task_epilog, &slurm_opt_task_prolog, &slurm_opt_tasks_per_node, &slurm_opt_test_only, &slurm_opt_thread_spec, &slurm_opt_threads_per_core, &slurm_opt_time_limit, &slurm_opt_time_min, &slurm_opt_tmp, &slurm_opt_uid, &slurm_opt_unbuffered, &slurm_opt_use_min_nodes, &slurm_opt_verbose, &slurm_opt_version, &slurm_opt_umask, &slurm_opt_usage, &slurm_opt_wait, &slurm_opt_wait_all_nodes, &slurm_opt_wait_srun, &slurm_opt_wckey, &slurm_opt_wrap, &slurm_opt_x11, NULL /* END */ }; struct option *slurm_option_table_create(slurm_opt_t *opt, char **opt_string) { struct option *optz = optz_create(), *spanked; *opt_string = xstrdup("+"); /* * Since the initial elements of slurm_cli_opt_t match * the layout of struct option, we can use this cast to * avoid needing to make a temporary structure. */ for (int i = 0; common_options[i]; i++) { bool set = true; /* * Runtime sanity checking for development builds, * as I cannot find a convenient way to instruct the * compiler to handle this. So if you make a mistake, * you'll hit an assertion failure in salloc/srun/sbatch. * * If set_func is set, the others must not be: */ xassert((common_options[i]->set_func && !common_options[i]->set_func_salloc && !common_options[i]->set_func_sbatch && !common_options[i]->set_func_srun) || !common_options[i]->set_func); /* * These two must always be set: */ xassert(common_options[i]->get_func); xassert(common_options[i]->reset_func); /* * A few options only exist as environment variables, and * should not be added to the table. They should be marked * with a NULL name field. */ if (!common_options[i]->name) continue; if (common_options[i]->set_func) optz_add(&optz, (struct option *) common_options[i]); else if (opt->salloc_opt && common_options[i]->set_func_salloc) optz_add(&optz, (struct option *) common_options[i]); else if (opt->sbatch_opt && common_options[i]->set_func_sbatch) optz_add(&optz, (struct option *) common_options[i]); else if (opt->srun_opt && common_options[i]->set_func_srun) optz_add(&optz, (struct option *) common_options[i]); else set = false; if (set && (common_options[i]->val < LONG_OPT_ENUM_START)) { xstrfmtcat(*opt_string, "%c", common_options[i]->val); if (common_options[i]->has_arg == required_argument) xstrcat(*opt_string, ":"); if (common_options[i]->has_arg == optional_argument) xstrcat(*opt_string, "::"); } } spanked = spank_option_table_create(optz); optz_destroy(optz); return spanked; } void slurm_option_table_destroy(struct option *optz) { optz_destroy(optz); } extern void slurm_free_options_members(slurm_opt_t *opt) { if (!opt) return; slurm_reset_all_options(opt, true); xfree(opt->chdir); xfree(opt->state); } static void _init_state(slurm_opt_t *opt) { if (opt->state) return; opt->state = xcalloc(sizeof(common_options), sizeof(slurm_opt_state_t)); } extern int slurm_process_option_data(slurm_opt_t *opt, int optval, const data_t *arg, data_t *errors) { int i; if (!opt) fatal("%s: missing slurm_opt_t struct", __func__); for (i = 0; common_options[i]; i++) { if (common_options[i]->val != optval) continue; /* Check that this is a valid match. */ if (!common_options[i]->set_func_data) continue; /* Match found */ break; } if (!common_options[i]) { char str[1024]; snprintf(str, sizeof(str), "Unknown option: %u", optval); ADD_DATA_ERROR(str, SLURM_ERROR); return SLURM_ERROR; } // TODO: implement data aware spank parsing _init_state(opt); if (!(common_options[i]->set_func_data)(opt, arg, errors)) { opt->state[i].set = true; opt->state[i].set_by_data = true; opt->state[i].set_by_env = false; return SLURM_SUCCESS; } return SLURM_ERROR; } int slurm_process_option(slurm_opt_t *opt, int optval, const char *arg, bool set_by_env, bool early_pass) { int i; const char *setarg = arg; bool set = true; if (!opt) fatal("%s: missing slurm_opt_t struct", __func__); for (i = 0; common_options[i]; i++) { if (common_options[i]->val != optval) continue; /* Check that this is a valid match. */ if (!common_options[i]->set_func && !(opt->salloc_opt && common_options[i]->set_func_salloc) && !(opt->sbatch_opt && common_options[i]->set_func_sbatch) && !(opt->srun_opt && common_options[i]->set_func_srun)) continue; /* Match found */ break; } /* * Not a Slurm internal option, so hopefully it's a SPANK option. * Skip this for early pass handling - SPANK options should only be * processed once during the main pass. */ if (!common_options[i] && !early_pass) { if (spank_process_option(optval, arg)) exit(-1); return SLURM_SUCCESS; } else if (!common_options[i]) { /* early pass, assume it is a SPANK option and skip */ return SLURM_SUCCESS; } /* * Special handling for the early pass in sbatch. * * Some options are handled in the early pass, but most are deferred * to a later pass, in which case those options are not re-evaluated. * Environment variables are always evaluated by this though - there * is no distinction for them of early vs normal passes. */ if (!set_by_env && opt->sbatch_opt) { if (!early_pass && common_options[i]->sbatch_early_pass) return SLURM_SUCCESS; if (early_pass && !common_options[i]->sbatch_early_pass) return SLURM_SUCCESS; } else if (!set_by_env && opt->srun_opt) { if (!early_pass && common_options[i]->srun_early_pass) return SLURM_SUCCESS; if (early_pass && !common_options[i]->srun_early_pass) return SLURM_SUCCESS; } if (arg) { if (common_options[i]->has_arg == no_argument) { char *end; /* * Treat these "flag" arguments specially. * For normal getopt_long() handling, arg is null. * But for envvars, arg may be set, and will be * processed by these rules: * arg == '\0', flag is set * arg == "yes", flag is set * arg is a non-zero number, flag is set * arg is anything else, call reset instead */ if (arg[0] == '\0') { set = true; } else if (!xstrcasecmp(arg, "yes")) { set = true; } else if (strtol(arg, &end, 10) && (*end == '\0')) { set = true; } else { set = false; } } else if (common_options[i]->has_arg == required_argument) { /* no special processing required */ } else if (common_options[i]->has_arg == optional_argument) { /* * If an empty string, convert to null, * as this will let the envvar processing * match the normal getopt_long() behavior. */ if (arg[0] == '\0') setarg = NULL; } } _init_state(opt); if (!set) { (common_options[i]->reset_func)(opt); opt->state[i].set = false; opt->state[i].set_by_data = false; opt->state[i].set_by_env = false; return SLURM_SUCCESS; } if (common_options[i]->set_func) { if (!(common_options[i]->set_func)(opt, setarg)) { opt->state[i].set = true; opt->state[i].set_by_data = false; opt->state[i].set_by_env = set_by_env; return SLURM_SUCCESS; } } else if (opt->salloc_opt && common_options[i]->set_func_salloc) { if (!(common_options[i]->set_func_salloc)(opt, setarg)) { opt->state[i].set = true; opt->state[i].set_by_data = false; opt->state[i].set_by_env = set_by_env; return SLURM_SUCCESS; } } else if (opt->sbatch_opt && common_options[i]->set_func_sbatch) { if (!(common_options[i]->set_func_sbatch)(opt, setarg)) { opt->state[i].set = true; opt->state[i].set_by_data = false; opt->state[i].set_by_env = set_by_env; return SLURM_SUCCESS; } } else if (opt->srun_opt && common_options[i]->set_func_srun) { if (!(common_options[i]->set_func_srun)(opt, setarg)) { opt->state[i].set = true; opt->state[i].set_by_data = false; opt->state[i].set_by_env = set_by_env; return SLURM_SUCCESS; } } /* * If we've made it here, then the set_func failed for some reason. * At which point we'll abandon ship. */ exit(-1); return SLURM_ERROR; } void slurm_print_set_options(slurm_opt_t *opt) { if (!opt) fatal("%s: missing slurm_opt_t struct", __func__); info("defined options"); info("-------------------- --------------------"); for (int i = 0; common_options[i]; i++) { char *val = NULL; if (!opt->state || !opt->state[i].set) continue; if (common_options[i]->get_func) val = (common_options[i]->get_func)(opt); info("%-20s: %s", common_options[i]->name, val); xfree(val); } info("-------------------- --------------------"); info("end of defined options"); } extern void slurm_reset_all_options(slurm_opt_t *opt, bool first_pass) { for (int i = 0; common_options[i]; i++) { if (!first_pass && !common_options[i]->reset_each_pass) continue; if (common_options[i]->reset_func) { (common_options[i]->reset_func)(opt); if (opt->state) opt->state[i].set = false; } } } /* * Was the option set by a cli argument? */ extern bool slurm_option_set_by_cli(slurm_opt_t *opt, int optval) { int i; if (!opt) { debug3("%s: opt=NULL optval=%u", __func__, optval); return false; } for (i = 0; common_options[i]; i++) { if (common_options[i]->val == optval) break; } /* This should not happen... */ if (!common_options[i]) return false; if (!opt->state) return false; /* * set is true if the option is set at all. If both set and set_by_env * are true, then the argument was set through the environment not the * cli, and we must return false. */ return (opt->state[i].set && !opt->state[i].set_by_env); } /* * Was the option set by an data_t value? */ extern bool slurm_option_set_by_data(slurm_opt_t *opt, int optval) { int i; if (!opt) { debug3("%s: opt=NULL optval=%u", __func__, optval); return false; } for (i = 0; common_options[i]; i++) { if (common_options[i]->val == optval) break; } /* This should not happen... */ if (!common_options[i]) return false; if (!opt->state) return false; return opt->state[i].set_by_data; } /* * Was the option set by an env var? */ extern bool slurm_option_set_by_env(slurm_opt_t *opt, int optval) { int i; if (!opt) { debug3("%s: opt=NULL optval=%u", __func__, optval); return false; } for (i = 0; common_options[i]; i++) { if (common_options[i]->val == optval) break; } /* This should not happen... */ if (!common_options[i]) return false; if (!opt->state) return false; return opt->state[i].set_by_env; } /* * Find the index into common_options for a given option name. */ static int _find_option_idx(const char *name) { for (int i = 0; common_options[i]; i++) if (!xstrcmp(name, common_options[i]->name)) return i; return -1; } /* * Get option value by common option name. */ extern char *slurm_option_get(slurm_opt_t *opt, const char *name) { int i = _find_option_idx(name); if (i < 0) return NULL; return common_options[i]->get_func(opt); } /* * Is option set? Discover by common option name. */ extern bool slurm_option_isset(slurm_opt_t *opt, const char *name) { int i = _find_option_idx(name); if (i < 0 || !opt->state) return false; return opt->state[i].set; } /* * Replace option value by common option name. */ extern int slurm_option_set(slurm_opt_t *opt, const char *name, const char *value, bool early) { int rc = SLURM_ERROR; int i = _find_option_idx(name); if (i < 0) return rc; /* Don't set early options if it is not early. */ if (opt->sbatch_opt && common_options[i]->sbatch_early_pass && !early) return SLURM_SUCCESS; if (opt->srun_opt && common_options[i]->srun_early_pass && !early) return SLURM_SUCCESS; /* Run the appropriate set function. */ if (common_options[i]->set_func) rc = common_options[i]->set_func(opt, value); else if (common_options[i]->set_func_salloc && opt->salloc_opt) rc = common_options[i]->set_func_salloc(opt, value); else if (common_options[i]->set_func_sbatch && opt->sbatch_opt) rc = common_options[i]->set_func_sbatch(opt, value); else if (common_options[i]->set_func_srun && opt->srun_opt) rc = common_options[i]->set_func_srun(opt, value); /* Ensure that the option shows up as "set". */ if (rc == SLURM_SUCCESS) { _init_state(opt); opt->state[i].set = true; } return rc; } /* * Reset option by common option name. */ extern bool slurm_option_reset(slurm_opt_t *opt, const char *name) { int i = _find_option_idx(name); if (i < 0) return false; common_options[i]->reset_func(opt); if (opt->state) opt->state[i].set = false; return true; } /* * Function for iterating through all the common option data structure * and returning (via parameter arguments) the name and value of each * set slurm option. * * IN opt - option data structure being interpreted * OUT name - xmalloc()'d string with the option name * OUT value - xmalloc()'d string with the option value * IN/OUT state - internal state, should be set to 0 for the first call * RETURNS - true if name/value set; false if no more options */ extern bool slurm_option_get_next_set(slurm_opt_t *opt, char **name, char **value, size_t *state) { size_t limit = sizeof(common_options) / sizeof(slurm_cli_opt_t *); if (*state >= limit) return false; while (common_options[*state] && (*state < limit) && (!(opt->state && opt->state[*state].set) || !common_options[*state]->name)) (*state)++; if (*state < limit && common_options[*state]) { *name = xstrdup(common_options[*state]->name); *value = common_options[*state]->get_func(opt); (*state)++; return true; } return false; } extern void validate_memory_options(slurm_opt_t *opt) { if ((slurm_option_set_by_cli(opt, LONG_OPT_MEM) + slurm_option_set_by_cli(opt, LONG_OPT_MEM_PER_CPU) + slurm_option_set_by_cli(opt, LONG_OPT_MEM_PER_GPU)) > 1) { fatal("--mem, --mem-per-cpu, and --mem-per-gpu are mutually exclusive."); } else if (slurm_option_set_by_cli(opt, LONG_OPT_MEM)) { slurm_option_reset(opt, "mem-per-cpu"); slurm_option_reset(opt, "mem-per-gpu"); } else if (slurm_option_set_by_cli(opt, LONG_OPT_MEM_PER_CPU)) { slurm_option_reset(opt, "mem"); slurm_option_reset(opt, "mem-per-gpu"); } else if (slurm_option_set_by_cli(opt, LONG_OPT_MEM_PER_GPU)) { slurm_option_reset(opt, "mem"); slurm_option_reset(opt, "mem-per-cpu"); } else if ((slurm_option_set_by_env(opt, LONG_OPT_MEM) + slurm_option_set_by_env(opt, LONG_OPT_MEM_PER_CPU) + slurm_option_set_by_env(opt, LONG_OPT_MEM_PER_GPU)) > 1) { fatal("SLURM_MEM_PER_CPU, SLURM_MEM_PER_GPU, and SLURM_MEM_PER_NODE are mutually exclusive."); } }