blob: c4dbab8cb272a761299d066baa60523eee62ab3c [file] [log] [blame]
/****************************************************************************\
* opts.c - sbcast command line option processing functions
*****************************************************************************
* Copyright (C) 2006-2007 The Regents of the University of California.
* Copyright (C) 2008 Lawrence Livermore National Security.
* Copyright (C) SchedMD LLC.
* Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
* Written by Morris Jette <jette1@llnl.gov>
* CODE-OCEC-09-009. All rights reserved.
*
* This file is part of Slurm, a resource management program.
* For details, see <https://slurm.schedmd.com/>.
* Please also read the included file: DISCLAIMER.
*
* Slurm is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* In addition, as a special exception, the copyright holders give permission
* to link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two. You must obey the GNU
* General Public License in all respects for all of the code used other than
* OpenSSL. If you modify file(s) with this exception, you may extend this
* exception to your version of the file(s), but you are not obligated to do
* so. If you do not wish to do so, delete this exception statement from your
* version. If you delete this exception statement from all source files in
* the program, then also delete it here.
*
* Slurm is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along
* with Slurm; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\*****************************************************************************/
#include "config.h"
#define _GNU_SOURCE
#include <getopt.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "src/common/proc_args.h"
#include "src/common/read_config.h"
#include "src/common/xmalloc.h"
#include "src/common/xstring.h"
#include "src/sbcast/sbcast.h"
#define OPT_LONG_EXCLUDE 0x100
#define OPT_LONG_HELP 0x101
#define OPT_LONG_USAGE 0x102
#define OPT_LONG_SEND_LIBS 0x103
#define OPT_LONG_AUTOCOMP 0x104
#define OPT_LONG_TREE_WIDTH 0x105
/* getopt_long options, integers but not characters */
/* FUNCTIONS */
static void _fill_in_selected_step_from_controller();
static void _fill_in_selected_step_from_env(void);
static void _fill_in_selected_steps_from_env(char *het_size_str);
static void _help( void );
static bool _need_hetjob_components(job_info_msg_t **job_info_msg);
static uint32_t _map_size( char *buf );
static void _print_options( void );
static void _usage( void );
static bool _need_hetjob_components(job_info_msg_t **job_info_msg)
{
slurm_job_info_t *jobs = NULL;
int rc;
/* If <jobid>+<offset> specified we target single component. */
if (params.selected_step->het_job_offset != NO_VAL)
return false;
if (!job_info_msg) {
error("job_info_msg pointer is NULL before calling slurm_load_job().");
exit(1);
}
if ((rc = slurm_load_job(job_info_msg,
params.selected_step->step_id.job_id,
SHOW_ALL)) != SLURM_SUCCESS) {
error("Failed to load JobId=%u: '%s'",
params.selected_step->step_id.job_id,
slurm_strerror(rc));
exit(1);
} else if (!*job_info_msg || (((*job_info_msg)->record_count) <= 0)) {
error("Failed to load JobId=%u: No jobs returned.",
params.selected_step->step_id.job_id);
exit(1);
}
jobs = (*job_info_msg)->job_array;
if (jobs->job_id != jobs->het_job_id)
return false;
if ((*job_info_msg)->record_count < 2)
fatal("slurm_load_job(%u) returned less than 2 records",
params.selected_step->step_id.job_id);
/* <jobid> without +<offset> and its HetJob leader */
return true;
}
static void _fill_in_selected_step_from_controller()
{
job_info_msg_t *job_info_msg = NULL;
slurm_job_info_t *job = NULL;
char *job_id_str = NULL;
if (!_need_hetjob_components(&job_info_msg))
return;
job = job_info_msg->job_array;
params.selected_steps = list_create(slurm_destroy_selected_step);
for (int i = 0; i < job_info_msg->record_count; i++, job++) {
job_id_str = xstrdup_printf("%u+%u", job->het_job_id,
job->het_job_offset);
list_append(params.selected_steps,
slurm_parse_step_str(job_id_str));
xfree(job_id_str);
}
xassert(list_count(params.selected_steps) >= 2);
}
static void _fill_in_selected_steps_from_env(char *het_size_str)
{
char *name = NULL, *job_id_str = NULL;
uint32_t het_size;
if (parse_uint32(het_size_str, &het_size) || (het_size < 2))
fatal("Invalid environment SLURM_HET_SIZE value: %s",
het_size_str);
params.selected_steps = list_create(slurm_destroy_selected_step);
for (int i = 0; i < het_size; i++) {
name = xstrdup_printf("SLURM_JOB_ID_HET_GROUP_%d", i);
if (!(job_id_str = getenv(name)))
fatal("getenv(%s) returned NULL", name);
xfree(name);
list_append(params.selected_steps,
slurm_parse_step_str(job_id_str));
}
}
static void _fill_in_selected_step_from_env(void)
{
char *env_val = NULL;
if ((env_val = getenv("SLURM_HET_SIZE"))) {
_fill_in_selected_steps_from_env(env_val);
return;
}
if (!(env_val = getenv("SLURM_JOB_ID"))) {
error("Need a job id to run this command. "
"Run from within a Slurm job or use the "
"--jobid option.");
exit(1);
}
slurm_destroy_selected_step(params.selected_step);
params.selected_step = slurm_parse_step_str(env_val);
}
/*
* parse_command_line, fill in params data structure with data
*/
extern void parse_command_line(int argc, char **argv)
{
char *env_val = NULL, *tmp;
int opt_char, ret;
int option_index;
static struct option long_options[] = {
{"autocomplete", required_argument, 0, OPT_LONG_AUTOCOMP},
{"compress", optional_argument, 0, 'C'},
{"exclude", required_argument, 0, OPT_LONG_EXCLUDE},
{"fanout", required_argument, 0, 'F'},
{"treewidth", required_argument, 0, OPT_LONG_TREE_WIDTH},
{"force", no_argument, 0, 'f'},
{"jobid", required_argument, 0, 'j'},
{"no-allocation", optional_argument, 0, 'Z'},
{"nodelist", optional_argument, 0, 'w'},
{"send-libs", optional_argument, 0, OPT_LONG_SEND_LIBS},
{"preserve", no_argument, 0, 'p'},
{"size", required_argument, 0, 's'},
{"timeout", required_argument, 0, 't'},
{"verbose", no_argument, 0, 'v'},
{"version", no_argument, 0, 'V'},
{"help", no_argument, 0, OPT_LONG_HELP},
{"usage", no_argument, 0, OPT_LONG_USAGE},
{NULL, 0, 0, 0}
};
if ((tmp = conf_get_opt_str(slurm_conf.bcast_parameters,
"Compression="))) {
params.compress = parse_compress_type(tmp);
xfree(tmp);
}
if (slurm_conf.bcast_exclude)
params.exclude = xstrdup(slurm_conf.bcast_exclude);
if ((env_val = getenv("SBCAST_COMPRESS")))
params.compress = parse_compress_type(env_val);
if ((env_val = getenv("SBCAST_EXCLUDE"))) {
xfree(params.exclude);
params.exclude = xstrdup(env_val);
}
if ((env_val = getenv("SBCAST_TREE_WIDTH")) ||
(env_val = getenv("SBCAST_FANOUT"))) {
if (!xstrcasecmp(env_val, "off"))
params.tree_width = 0xfffd;
else
params.tree_width = atoi(env_val);
}
if (getenv("SBCAST_FORCE"))
params.flags |= BCAST_FLAG_FORCE;
if (getenv("SBCAST_PRESERVE"))
params.flags |= BCAST_FLAG_PRESERVE;
if (xstrcasestr(slurm_conf.bcast_parameters, "send_libs"))
params.flags |= BCAST_FLAG_SEND_LIBS;
if ((env_val = getenv("SBCAST_SEND_LIBS"))) {
ret = parse_send_libs(env_val);
if (ret == -1)
error("Ignoring unrecognized SBCAST_SEND_LIBS value '%s'",
env_val);
else if (ret)
params.flags |= BCAST_FLAG_SEND_LIBS;
else
params.flags &= ~BCAST_FLAG_SEND_LIBS;
}
if ( ( env_val = getenv("SBCAST_SIZE") ) )
params.block_size = _map_size(env_val);
else
params.block_size = 8 * 1024 * 1024;
if ( ( env_val = getenv("SBCAST_TIMEOUT") ) )
params.timeout = (atoi(env_val) * 1000);
optind = 0;
while ((opt_char = getopt_long(argc, argv, "C::fF:j:ps:t:vVw:Z",
long_options, &option_index)) != -1) {
switch (opt_char) {
case (int)'?':
fprintf(stderr,
"Try \"sbcast --help\" for more information\n");
exit(1);
break;
case (int)'C':
params.compress = parse_compress_type(optarg);
break;
case (int) OPT_LONG_EXCLUDE:
xfree(params.exclude);
params.exclude = xstrdup(optarg);
break;
case (int)'f':
params.flags |= BCAST_FLAG_FORCE;
break;
case OPT_LONG_TREE_WIDTH:
case (int)'F':
if (!xstrcasecmp(optarg, "off"))
params.tree_width = 0xfffd;
else
params.tree_width = atoi(optarg);
break;
case (int)'j':
params.selected_step = slurm_parse_step_str(optarg);
break;
case (int)'p':
params.flags |= BCAST_FLAG_PRESERVE;
break;
case (int) OPT_LONG_SEND_LIBS:
ret = parse_send_libs(optarg);
if (ret == -1)
error("Ignoring unrecognized --send-libs value '%s'",
optarg);
else if (ret)
params.flags |= BCAST_FLAG_SEND_LIBS;
else if (params.flags & BCAST_FLAG_SEND_LIBS)
params.flags &= ~BCAST_FLAG_SEND_LIBS;
break;
case (int) 's':
params.block_size = _map_size(optarg);
break;
case (int)'t':
params.timeout = (atoi(optarg) * 1000);
break;
case (int) 'v':
params.verbose++;
break;
case (int) 'V':
print_slurm_version();
exit(0);
case (int) 'w':
xfree(params.node_list);
params.node_list = xstrdup(optarg);
break;
case (int) 'Z':
params.flags |= BCAST_FLAG_NO_JOB;
break;
case (int) OPT_LONG_HELP:
_help();
exit(0);
case (int) OPT_LONG_USAGE:
_usage();
exit(0);
case OPT_LONG_AUTOCOMP:
suggest_completion(long_options, optarg);
exit(0);
break;
}
}
if ((argc - optind) != 2) {
fprintf(stderr, "Need two file names, have %d names\n",
(argc - optind));
fprintf(stderr, "Try \"sbcast --help\" for more information\n");
exit(1);
}
if ((!params.selected_step ||
(params.selected_step->step_id.job_id == NO_VAL)) &&
!(params.flags & BCAST_FLAG_NO_JOB)) {
_fill_in_selected_step_from_env();
} else if ((params.selected_step &&
params.selected_step->step_id.job_id != NO_VAL) &&
!(params.flags & BCAST_FLAG_NO_JOB)) {
_fill_in_selected_step_from_controller();
}
params.src_fname = xstrdup(argv[optind]);
if (argv[optind+1][0] == '/') {
params.dst_fname = xstrdup(argv[optind+1]);
} else if ((params.dst_fname =
conf_get_opt_str(slurm_conf.bcast_parameters,
"DestDir="))) {
xstrfmtcat(params.dst_fname, "/%s", argv[optind+1]);
} else {
#ifdef HAVE_GET_CURRENT_DIR_NAME
tmp = get_current_dir_name();
#else
tmp = malloc(PATH_MAX);
tmp = getcwd(tmp, PATH_MAX);
#endif
xstrfmtcat(params.dst_fname, "%s/%s", tmp, argv[optind+1]);
free(tmp);
}
if (params.dst_fname[strlen(params.dst_fname) - 1] == '/') {
error("Target filename cannot be a directory.");
exit(1);
}
if ((params.flags & BCAST_FLAG_NO_JOB) && (params.selected_step)) {
error("--no-allocation/-Z and --jobid/-j options are mutually exclusive");
exit(1);
}
if ((params.flags & BCAST_FLAG_NO_JOB) && (!params.node_list)) {
error("--nodelist/-w is required with --no-allocation/-Z.");
exit(1);
}
if (params.verbose)
_print_options();
}
/* map size in string to number, interpret suffix of "k" or "m" */
static uint32_t _map_size( char *buf )
{
long b_size;
char *end_ptr;
b_size = strtol(buf, &end_ptr, 10);
if ((b_size == LONG_MIN) || (b_size == LONG_MAX) || (b_size < 0)) {
fprintf(stderr, "size specification is invalid, ignored\n");
b_size = 0;
} else if (end_ptr[0] == '\0')
;
else if ((end_ptr[0] == 'k') || (end_ptr[0] == 'K'))
b_size *= 1024;
else if ((end_ptr[0] == 'm') || (end_ptr[0] == 'M'))
b_size *= (1024 * 1024);
else {
fprintf(stderr, "size specification is invalid, ignored\n");
b_size = 0;
}
return (uint32_t) b_size;
}
/* print the parameters specified */
static void _print_options( void )
{
info("-----------------------------");
info("block_size = %u", params.block_size);
info("compress = %u", params.compress);
info("exclude = %s", params.exclude);
info("force = %s",
(params.flags & BCAST_FLAG_FORCE) ? "true" : "false");
info("treewidth = %d", params.tree_width);
info("preserve = %s",
(params.flags & BCAST_FLAG_PRESERVE) ? "true" : "false");
info("send_libs = %s",
(params.flags & BCAST_FLAG_SEND_LIBS) ? "true" : "false");
info("timeout = %d", params.timeout);
info("verbose = %d", params.verbose);
info("source = %s", params.src_fname);
info("dest = %s", params.dst_fname);
info("-----------------------------");
}
static void _usage( void )
{
printf("Usage: sbcast [--exclude] [-CfFjpvV] [--send-libs] SOURCE DEST\n");
}
static void _help( void )
{
printf ("\
Usage: sbcast [OPTIONS] SOURCE DEST\n\
-C, --compress[=lib] compress the file being transmitted\n\
--exclude=<path_list> shared object paths to be excluded\n\
-f, --force replace destination file as required\n\
--treewidth=num specify message treewidth\n\
-j, --jobid=#[+#][.#] specify job ID with optional hetjob offset and/or step ID\n\
-p, --preserve preserve modes and times of source file\n\
--send-libs[=yes|no] autodetect and broadcast executable's shared objects\n\
-s, --size=num block size in bytes (rounded off)\n\
-t, --timeout=secs specify message timeout (seconds)\n\
-v, --verbose provide detailed event logging\n\
-V, --version print version information and exit\n\
\nHelp options:\n\
--help show this help message\n\
--usage display brief usage message\n");
}