| /*****************************************************************************\ | |
| * opt.c - options processing for sattach | |
| ***************************************************************************** | |
| * Copyright (C) 2002-2006 The Regents of the University of California. | |
| * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). | |
| * Written by Mark Grondona <grondona1@llnl.gov>, et. al. | |
| * CODE-OCEC-09-009. All rights reserved. | |
| * | |
| * This file is part of Slurm, a resource management program. | |
| * For details, see <https://slurm.schedmd.com/>. | |
| * Please also read the included file: DISCLAIMER. | |
| * | |
| * Slurm is free software; you can redistribute it and/or modify it under | |
| * the terms of the GNU General Public License as published by the Free | |
| * Software Foundation; either version 2 of the License, or (at your option) | |
| * any later version. | |
| * | |
| * Slurm is distributed in the hope that it will be useful, but WITHOUT ANY | |
| * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | |
| * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more | |
| * details. | |
| * | |
| * You should have received a copy of the GNU General Public License along | |
| * with Slurm; if not, write to the Free Software Foundation, Inc., | |
| * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
| \*****************************************************************************/ | |
| #include "config.h" | |
| #define _GNU_SOURCE | |
| #include <ctype.h> /* isdigit */ | |
| #include <fcntl.h> | |
| #include <getopt.h> | |
| #include <limits.h> | |
| #include <stdarg.h> /* va_start */ | |
| #include <stdio.h> | |
| #include <stdlib.h> /* getenv */ | |
| #include <sys/stat.h> | |
| #include <sys/types.h> | |
| #include <sys/utsname.h> | |
| #include <unistd.h> | |
| #include "src/common/list.h" | |
| #include "src/common/log.h" | |
| #include "src/common/parse_time.h" | |
| #include "src/common/proc_args.h" | |
| #include "src/common/read_config.h" /* contains getnodename() */ | |
| #include "src/interfaces/mpi.h" | |
| #include "src/common/sluid.h" | |
| #include "src/common/slurm_protocol_api.h" | |
| #include "src/common/slurm_rlimits_info.h" | |
| #include "src/common/uid.h" | |
| #include "src/common/xmalloc.h" | |
| #include "src/common/xstring.h" | |
| #include "src/sattach/opt.h" | |
| /* generic getopt_long flags, integers and *not* valid characters */ | |
| #define LONG_OPT_LAYOUT_ONLY 0x100 | |
| #define LONG_OPT_DEBUGGER_TEST 0x101 | |
| #define LONG_OPT_IN_FILTER 0x102 | |
| #define LONG_OPT_OUT_FILTER 0x103 | |
| #define LONG_OPT_ERR_FILTER 0x104 | |
| #define LONG_OPT_PTY 0x105 | |
| #define OPT_LONG_AUTOCOMP 0x106 | |
| /*---- global variables, defined in opt.h ----*/ | |
| opt_t opt; | |
| int error_exit = 1; | |
| /*---- forward declarations of static functions ----*/ | |
| typedef struct env_vars env_vars_t; | |
| static void _help(void); | |
| /* fill in default options */ | |
| static void _opt_default(void); | |
| /* set options based upon env vars */ | |
| static void _opt_env(void); | |
| static void _opt_args(int argc, char **argv); | |
| /* list known options and their settings */ | |
| static void _opt_list(void); | |
| /* verify options sanity */ | |
| static bool _opt_verify(void); | |
| static void _process_env_var(env_vars_t *e, const char *val); | |
| /* Get a POSITIVE decimal integer from arg */ | |
| static int _get_pos_int(const char *arg, const char *what); | |
| static void _usage(void); | |
| /*---[ end forward declarations of static functions ]---------------------*/ | |
| int initialize_and_process_args(int argc, char **argv) | |
| { | |
| /* initialize option defaults */ | |
| _opt_default(); | |
| /* initialize options with env vars */ | |
| _opt_env(); | |
| /* initialize options with argv */ | |
| _opt_args(argc, argv); | |
| if (opt.verbose > 1) | |
| _opt_list(); | |
| return 1; | |
| } | |
| /* | |
| * Get a POSITIVE decimal integer from arg. | |
| * | |
| * Returns the integer on success, exits program on failure. | |
| * | |
| */ | |
| static int | |
| _get_pos_int(const char *arg, const char *what) | |
| { | |
| char *p; | |
| long int result = strtol(arg, &p, 10); | |
| if (p == arg || !xstring_is_whitespace(p) || (result < 0L)) { | |
| error ("Invalid numeric value \"%s\" for %s.", arg, what); | |
| exit(error_exit); | |
| } | |
| if (result > INT_MAX) { | |
| error ("Numeric argument %ld to big for %s.", result, what); | |
| exit(error_exit); | |
| } | |
| return (int) result; | |
| } | |
| /* | |
| * _opt_default(): used by initialize_and_process_args to set defaults | |
| */ | |
| static void _opt_default(void) | |
| { | |
| static slurm_step_io_fds_t fds = SLURM_STEP_IO_FDS_INITIALIZER; | |
| uid_t uid = getuid(); | |
| if (!(opt.user = uid_to_string_or_null(uid))) | |
| fatal("Invalid user id: %u", uid); | |
| opt.uid = uid; | |
| opt.gid = getgid(); | |
| opt.progname = NULL; | |
| opt.quiet = 0; | |
| opt.verbose = 0; | |
| opt.euid = SLURM_AUTH_NOBODY; | |
| opt.egid = SLURM_AUTH_NOBODY; | |
| opt.labelio = false; | |
| opt.ctrl_comm_ifhn = xshort_hostname(); | |
| memcpy(&opt.fds, &fds, sizeof(fds)); | |
| opt.layout_only = false; | |
| opt.debugger_test = false; | |
| opt.input_filter = (uint32_t)-1; | |
| opt.input_filter_set = false; | |
| opt.output_filter = (uint32_t)-1; | |
| opt.output_filter_set = false; | |
| opt.error_filter = (uint32_t)-1; | |
| opt.error_filter_set = false; | |
| opt.pty = false; | |
| } | |
| /*---[ env var processing ]-----------------------------------------------*/ | |
| /* | |
| * try to use a similar scheme as popt. | |
| * | |
| * in order to add a new env var (to be processed like an option): | |
| * | |
| * define a new entry into env_vars[], if the option is a simple int | |
| * or string you may be able to get away with adding a pointer to the | |
| * option to set. Otherwise, process var based on "type" in _opt_env. | |
| */ | |
| struct env_vars { | |
| const char *var; | |
| int type; | |
| void *arg; | |
| void *set_flag; | |
| }; | |
| env_vars_t env_vars[] = { | |
| {NULL, 0, NULL, NULL} | |
| }; | |
| /* | |
| * _opt_env(): used by initialize_and_process_args to set options via | |
| * environment variables. See comments above for how to | |
| * extend sattach to process different vars | |
| */ | |
| static void _opt_env(void) | |
| { | |
| char *val = NULL; | |
| env_vars_t *e = env_vars; | |
| while (e->var) { | |
| if ((val = getenv(e->var)) != NULL) | |
| _process_env_var(e, val); | |
| e++; | |
| } | |
| } | |
| static void | |
| _process_env_var(env_vars_t *e, const char *val) | |
| { | |
| debug2("now processing env var %s=%s", e->var, val); | |
| if (e->set_flag) { | |
| *((bool *) e->set_flag) = true; | |
| } | |
| switch (e->type) { | |
| default: | |
| /* do nothing */ | |
| break; | |
| } | |
| } | |
| void set_options(const int argc, char **argv) | |
| { | |
| int opt_char, option_index = 0; | |
| static struct option long_options[] = { | |
| {"autocomplete", required_argument, 0, OPT_LONG_AUTOCOMP}, | |
| {"help", no_argument, 0, 'h'}, | |
| {"label", no_argument, 0, 'l'}, | |
| {"quiet", no_argument, 0, 'Q'}, | |
| {"usage", no_argument, 0, 'u'}, | |
| {"verbose", no_argument, 0, 'v'}, | |
| {"version", no_argument, 0, 'V'}, | |
| {"layout", no_argument, 0, LONG_OPT_LAYOUT_ONLY}, | |
| {"debugger-test",no_argument, 0, LONG_OPT_DEBUGGER_TEST}, | |
| {"input-filter", required_argument,0, LONG_OPT_IN_FILTER}, | |
| {"output-filter",required_argument,0, LONG_OPT_OUT_FILTER}, | |
| {"error-filter", required_argument,0, LONG_OPT_ERR_FILTER}, | |
| {"pty", no_argument, 0, LONG_OPT_PTY}, | |
| {NULL} | |
| }; | |
| char *opt_string = "+hlQuvV"; | |
| opt.progname = xbasename(argv[0]); | |
| optind = 0; | |
| while((opt_char = getopt_long(argc, argv, opt_string, | |
| long_options, &option_index)) != -1) { | |
| switch (opt_char) { | |
| case '?': | |
| fprintf(stderr, "Try \"sattach --help\" for more " | |
| "information\n"); | |
| exit(error_exit); | |
| break; | |
| case 'h': | |
| _help(); | |
| exit(0); | |
| case 'l': | |
| opt.labelio = true; | |
| break; | |
| case 'Q': | |
| opt.quiet++; | |
| break; | |
| case 'u': | |
| _usage(); | |
| exit(0); | |
| case 'v': | |
| opt.verbose++; | |
| break; | |
| case 'V': | |
| print_slurm_version(); | |
| exit(0); | |
| break; | |
| case LONG_OPT_IN_FILTER: | |
| if (xstrcmp(optarg, "-") != 0) { | |
| opt.input_filter = (uint32_t) | |
| _get_pos_int(optarg, "input-filter"); | |
| } | |
| opt.input_filter_set = true; | |
| break; | |
| case LONG_OPT_OUT_FILTER: | |
| if (xstrcmp(optarg, "-") != 0) { | |
| opt.output_filter = (uint32_t) | |
| _get_pos_int(optarg, "output-filter"); | |
| } | |
| opt.output_filter_set = true; | |
| break; | |
| case LONG_OPT_ERR_FILTER: | |
| if (xstrcmp(optarg, "-") != 0) { | |
| opt.error_filter = (uint32_t) | |
| _get_pos_int(optarg, "error-filter"); | |
| } | |
| opt.error_filter_set = true; | |
| break; | |
| case LONG_OPT_LAYOUT_ONLY: | |
| opt.layout_only = true; | |
| break; | |
| case LONG_OPT_DEBUGGER_TEST: | |
| opt.debugger_test = true; | |
| break; | |
| case LONG_OPT_PTY: | |
| #ifdef HAVE_PTY_H | |
| opt.pty = true; | |
| #else | |
| error("--pty not currently supported on this system " | |
| "type"); | |
| #endif | |
| break; | |
| case OPT_LONG_AUTOCOMP: | |
| suggest_completion(long_options, optarg); | |
| exit(0); | |
| break; | |
| default: | |
| error("Unrecognized command line parameter %c", | |
| opt_char); | |
| exit(error_exit); | |
| } | |
| } | |
| } | |
| /* | |
| * _opt_args() : set options via commandline args and popt | |
| */ | |
| static void _opt_args(int argc, char **argv) | |
| { | |
| char **rest = NULL; | |
| int leftover; | |
| set_options(argc, argv); | |
| leftover = 0; | |
| if (optind < argc) { | |
| rest = argv + optind; | |
| while (rest[leftover] != NULL) | |
| leftover++; | |
| } | |
| if (leftover != 1) { | |
| error("too many parameters"); | |
| _usage(); | |
| exit(error_exit); | |
| } | |
| opt.selected_step = slurm_parse_step_str(*(argv + optind)); | |
| if (opt.selected_step->step_id.step_id == NO_VAL) { | |
| error("Failed to parse stepid from command line options"); | |
| _usage(); | |
| exit(error_exit); | |
| } | |
| if (!_opt_verify()) | |
| exit(error_exit); | |
| } | |
| /* | |
| * _opt_verify : perform some post option processing verification | |
| * | |
| */ | |
| static bool _opt_verify(void) | |
| { | |
| bool verified = true; | |
| if (opt.quiet && opt.verbose) { | |
| error ("don't specify both --verbose (-v) and --quiet (-Q)"); | |
| verified = false; | |
| } | |
| if ((opt.selected_step->step_id.step_id == SLURM_EXTERN_CONT) || | |
| (opt.selected_step->step_id.step_id == SLURM_BATCH_SCRIPT)) { | |
| error("Cannot be used with extern or batch steps"); | |
| verified = false; | |
| } | |
| /* | |
| * set up standard IO filters | |
| */ | |
| if ((opt.input_filter_set || opt.output_filter_set || | |
| opt.error_filter_set) && opt.pty) { | |
| error("don't specify both --pty and I/O filtering"); | |
| verified = false; | |
| } | |
| if (opt.input_filter_set) | |
| opt.fds.input.taskid = opt.input_filter; | |
| if (opt.output_filter_set) | |
| opt.fds.out.taskid = opt.output_filter; | |
| if (opt.error_filter_set) { | |
| opt.fds.err.taskid = opt.error_filter; | |
| } else if (opt.output_filter_set) { | |
| opt.fds.err.taskid = opt.output_filter; | |
| } | |
| return verified; | |
| } | |
| #define tf_(b) (b == true) ? "true" : "false" | |
| static void _opt_list(void) | |
| { | |
| char tmp[SLUID_STR_BYTES]; | |
| print_sluid(opt.selected_step->step_id.sluid, tmp, sizeof(tmp)); | |
| info("defined options for program `%s'", opt.progname); | |
| info("--------------- ---------------------"); | |
| info("job ID : %u", opt.selected_step->step_id.job_id); | |
| info("sluid : %s", tmp); | |
| info("step ID : %u", opt.selected_step->step_id.step_id); | |
| info("user : `%s'", opt.user); | |
| info("uid : %u", opt.uid); | |
| info("gid : %u", opt.gid); | |
| info("verbose : %d", opt.verbose); | |
| } | |
| static void _usage(void) | |
| { | |
| printf("Usage: sattach [options] <jobid.stepid>\n"); | |
| } | |
| static void _help(void) | |
| { | |
| printf("Usage: sattach [options] <jobid.stepid>\n"); | |
| printf( | |
| " --input-filter=taskid send stdin to only the specified task\n" | |
| " --output-filter=taskid only print stdout from the specified task\n" | |
| " --error-filter=taskid only print stderr from the specified task\n" | |
| " -l, --label prepend task number to lines of stdout & stderr\n" | |
| " --layout print task layout info and exit (does not attach to tasks)\n" | |
| " -Q, --quiet quiet mode (suppress informational messages)\n" | |
| " -v, --verbose verbose mode (multiple -v's increase verbosity)\n" | |
| " -V, --version print the Slurm version and exit\n\n" | |
| "Help options:\n" | |
| " -h, --help print this help message\n" | |
| " -u, --usage print a brief usage message\n" | |
| ); | |
| } |