| /****************************************************************************\ | |
| * opts.c - strigger command line option processing functions | |
| ***************************************************************************** | |
| * Copyright (C) 2006-2007 The Regents of the University of California. | |
| * Copyright (C) 2008-2010 Lawrence Livermore National Security. | |
| * Copyright (C) SchedMD LLC. | |
| * 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. | |
| \*****************************************************************************/ | |
| #define _GNU_SOURCE | |
| #include <ctype.h> | |
| #include <getopt.h> | |
| #include <limits.h> | |
| #include <pwd.h> | |
| #include <stdlib.h> | |
| #include <sys/stat.h> | |
| #include <sys/types.h> | |
| #include <unistd.h> | |
| #include "src/common/xmalloc.h" | |
| #include "src/common/xstring.h" | |
| #include "src/common/proc_args.h" | |
| #include "src/common/uid.h" | |
| #include "src/strigger/strigger.h" | |
| #define OPT_LONG_HELP 0x100 | |
| #define OPT_LONG_USAGE 0x101 | |
| #define OPT_LONG_SET 0x102 | |
| #define OPT_LONG_GET 0x103 | |
| #define OPT_LONG_CLEAR 0x104 | |
| #define OPT_LONG_USER 0x105 | |
| #define OPT_LONG_FLAGS 0x108 | |
| #define OPT_LONG_BURST_BUFFER 0x109 | |
| #define OPT_LONG_DRAINING 0x10a | |
| #define OPT_LONG_AUTOCOMP 0x10b | |
| /* getopt_long options, integers but not characters */ | |
| /* FUNCTIONS */ | |
| static void _help( void ); | |
| static void _init_options( void ); | |
| static void _print_options( void ); | |
| static void _usage( void ); | |
| static void _validate_options( void ); | |
| struct strigger_parameters params; | |
| /* | |
| * parse_command_line, fill in params data structure with data | |
| */ | |
| extern void parse_command_line(int argc, char **argv) | |
| { | |
| int opt_char; | |
| int option_index; | |
| uid_t some_uid; | |
| long tmp_l; | |
| static struct option long_options[] = { | |
| {"autocomplete", required_argument, 0, OPT_LONG_AUTOCOMP}, | |
| {"primary_slurmctld_failure", no_argument, 0, 'a'}, | |
| {"primary_slurmctld_resumed_operation", no_argument, 0, 'A'}, | |
| {"primary_slurmctld_resumed_control", no_argument, 0, 'b'}, | |
| {"backup_slurmctld_failure", no_argument, 0, 'B'}, | |
| {"backup_slurmctld_resumed_operation", no_argument, 0, 'c'}, | |
| {"backup_slurmctld_assumed_control", no_argument, 0, 'C'}, | |
| {"down", no_argument, 0, 'd'}, | |
| {"drained", no_argument, 0, 'D'}, | |
| {"primary_slurmctld_acct_buffer_full", no_argument, 0, 'e'}, | |
| {"fini", no_argument, 0, 'f'}, | |
| {"fail", no_argument, 0, 'F'}, | |
| {"primary_slurmdbd_failure", no_argument, 0, 'g'}, | |
| {"primary_slurmdbd_resumed_operation", no_argument, 0, 'G'}, | |
| {"primary_database_failure", no_argument, 0, 'h'}, | |
| {"primary_database_resumed_operation", no_argument, 0, 'H'}, | |
| {"id", required_argument, 0, 'i'}, | |
| {"idle", no_argument, 0, 'I'}, | |
| {"jobid", required_argument, 0, 'j'}, | |
| {"cluster", required_argument, 0, 'M'}, | |
| {"clusters", required_argument, 0, 'M'}, | |
| {"node", optional_argument, 0, 'n'}, | |
| {"noheader", no_argument, 0, 'N'}, | |
| {"offset", required_argument, 0, 'o'}, | |
| {"program", required_argument, 0, 'p'}, | |
| {"quiet", no_argument, 0, 'Q'}, | |
| {"reconfig", no_argument, 0, 'r'}, | |
| {"time", no_argument, 0, 't'}, | |
| {"up", no_argument, 0, 'u'}, | |
| {"verbose", no_argument, 0, 'v'}, | |
| {"version", no_argument, 0, 'V'}, | |
| {"burst_buffer", no_argument, 0, OPT_LONG_BURST_BUFFER}, | |
| {"clear", no_argument, 0, OPT_LONG_CLEAR}, | |
| {"flags", required_argument, 0, OPT_LONG_FLAGS}, | |
| {"get", no_argument, 0, OPT_LONG_GET}, | |
| {"help", no_argument, 0, OPT_LONG_HELP}, | |
| {"set", no_argument, 0, OPT_LONG_SET}, | |
| {"usage", no_argument, 0, OPT_LONG_USAGE}, | |
| {"user", required_argument, 0, OPT_LONG_USER}, | |
| {"draining", no_argument, 0, OPT_LONG_DRAINING}, | |
| {"resume", no_argument, 0, 'R'}, | |
| {NULL, 0, 0, 0} | |
| }; | |
| _init_options(); | |
| optind = 0; | |
| while ((opt_char = getopt_long(argc, argv, | |
| "aAbBcCdDeFfgGhHi:Ij:M:n::No:p:QrRtuvV", | |
| long_options, &option_index)) != -1) { | |
| switch (opt_char) { | |
| case (int)'?': | |
| fprintf(stderr, "Try \"strigger --help\" for " | |
| "more information\n"); | |
| exit(1); | |
| break; | |
| case (int)'a': | |
| params.pri_ctld_fail = true; | |
| break; | |
| case (int)'A': | |
| params.pri_ctld_res_op = true; | |
| break; | |
| case (int)'b': | |
| params.pri_ctld_res_ctrl = true; | |
| break; | |
| case (int)'B': | |
| params.bu_ctld_fail = true; | |
| break; | |
| case (int)'c': | |
| params.bu_ctld_res_op = true; | |
| break; | |
| case (int)'C': | |
| params.bu_ctld_as_ctrl = true; | |
| break; | |
| case (int)'d': | |
| params.node_down = true; | |
| break; | |
| case (int)'D': | |
| params.node_drained = true; | |
| break; | |
| case (int)'e': | |
| params.pri_ctld_acct_buffer_full = true; | |
| break; | |
| case (int)'f': | |
| params.job_fini = true; | |
| break; | |
| case (int)'F': | |
| params.node_fail = true; | |
| break; | |
| case (int)'g': | |
| params.pri_dbd_fail = true; | |
| break; | |
| case (int)'G': | |
| params.pri_dbd_res_op = true; | |
| break; | |
| case (int)'h': | |
| params.pri_db_fail = true; | |
| break; | |
| case (int)'H': | |
| params.pri_db_res_op = true; | |
| break; | |
| case (int)'i': | |
| if (!optarg) /* CLANG Fix */ | |
| break; | |
| params.trigger_id = atoi(optarg); | |
| break; | |
| case (int)'I': | |
| params.node_idle = true; | |
| break; | |
| case (int)'j': | |
| if (!optarg) /* CLANG Fix */ | |
| break; | |
| tmp_l = atol(optarg); | |
| if (tmp_l <= 0) { | |
| error("Invalid jobid %s", optarg); | |
| exit(1); | |
| } | |
| params.job_id = tmp_l; | |
| break; | |
| case (int) 'M': | |
| FREE_NULL_LIST(params.clusters); | |
| if (slurm_get_cluster_info(&(params.clusters), | |
| optarg, 0)) { | |
| print_db_notok(optarg, 0); | |
| fatal("Could not get cluster information"); | |
| } | |
| working_cluster_rec = list_peek(params.clusters); | |
| break; | |
| case (int)'n': | |
| xfree(params.node_id); | |
| if (optarg) | |
| params.node_id = xstrdup(optarg); | |
| else | |
| params.node_id = xstrdup("*"); | |
| break; | |
| case (int)'N': | |
| params.no_header = true; | |
| break; | |
| case (int)'o': | |
| if (!optarg) /* CLANG Fix */ | |
| break; | |
| params.offset = atoi(optarg); | |
| break; | |
| case (int)'p': | |
| xfree(params.program); | |
| params.program = xstrdup(optarg); | |
| break; | |
| case (int)'Q': | |
| params.quiet = true; | |
| break; | |
| case (int)'r': | |
| params.reconfig = true; | |
| break; | |
| case (int)'R': | |
| params.node_resume = true; | |
| break; | |
| case (int)'t': | |
| params.time_limit = true; | |
| break; | |
| case (int)'u': | |
| params.node_up = true; | |
| break; | |
| case (int) 'v': | |
| params.verbose++; | |
| break; | |
| case (int) 'V': | |
| print_slurm_version(); | |
| exit(0); | |
| case (int) OPT_LONG_BURST_BUFFER: | |
| params.burst_buffer = true; | |
| break; | |
| case (int) OPT_LONG_HELP: | |
| _help(); | |
| exit(0); | |
| case (int) OPT_LONG_USAGE: | |
| _usage(); | |
| exit(0); | |
| case (int) OPT_LONG_CLEAR: | |
| params.mode_clear = true; | |
| break; | |
| case (int) OPT_LONG_FLAGS: | |
| if (!optarg) /* CLANG Fix */ | |
| break; | |
| if (!xstrncasecmp(optarg, "perm", 4)) | |
| params.flags = TRIGGER_FLAG_PERM; | |
| else { | |
| error("Invalid flags %s", optarg); | |
| exit(1); | |
| } | |
| break; | |
| case (int) OPT_LONG_GET: | |
| params.mode_get = true; | |
| break; | |
| case (int) OPT_LONG_SET: | |
| params.mode_set = true; | |
| break; | |
| case (int) OPT_LONG_USER: | |
| if (uid_from_string(optarg, &some_uid) != | |
| SLURM_SUCCESS) { | |
| error("Invalid user %s", optarg); | |
| exit(1); | |
| } | |
| params.user_id = (uint32_t) some_uid; | |
| break; | |
| case (int) OPT_LONG_DRAINING: | |
| params.node_draining = true; | |
| break; | |
| case OPT_LONG_AUTOCOMP: | |
| suggest_completion(long_options, optarg); | |
| exit(0); | |
| break; | |
| } | |
| } | |
| if (params.verbose) | |
| _print_options(); | |
| _validate_options(); | |
| } | |
| /* initialize the parameters */ | |
| static void _init_options( void ) | |
| { | |
| params.mode_set = false; | |
| params.mode_get = false; | |
| params.mode_clear = false; | |
| params.burst_buffer = false; | |
| params.pri_ctld_fail = false; | |
| params.pri_ctld_res_op = false; | |
| params.pri_ctld_res_ctrl = false; | |
| params.pri_ctld_acct_buffer_full = false; | |
| params.bu_ctld_fail = false; | |
| params.bu_ctld_res_op = false; | |
| params.bu_ctld_as_ctrl = false; | |
| params.flags = 0; | |
| params.no_header = false; | |
| params.node_down = false; | |
| params.node_drained = false; | |
| params.node_draining = false; | |
| params.node_fail = false; | |
| params.node_idle = false; | |
| params.trigger_id = 0; | |
| params.job_fini = false; | |
| params.pri_dbd_fail = false; | |
| params.pri_dbd_res_op = false; | |
| params.pri_db_fail = false; | |
| params.pri_db_res_op = false; | |
| params.job_id = 0; | |
| params.node_id = NULL; | |
| params.node_resume = false; | |
| params.offset = 0; | |
| params.program = NULL; | |
| params.quiet = false; | |
| params.reconfig = false; | |
| params.time_limit = false; | |
| params.node_up = false; | |
| params.user_id = NO_VAL; | |
| params.verbose = 0; | |
| } | |
| /* print the parameters specified */ | |
| static void _print_options( void ) | |
| { | |
| verbose("-----------------------------"); | |
| verbose("set = %s", params.mode_set ? "true" : "false"); | |
| verbose("get = %s", params.mode_get ? "true" : "false"); | |
| verbose("clear = %s", params.mode_clear ? "true" : "false"); | |
| verbose("burst_buffer = %s", params.burst_buffer ? "true" : "false"); | |
| verbose("flags = %u", params.flags); | |
| verbose("job_id = %u", params.job_id); | |
| verbose("job_fini = %s", params.job_fini ? "true" : "false"); | |
| verbose("no_header = %s", params.no_header ? "true" : "false"); | |
| verbose("node_down = %s", params.node_down ? "true" : "false"); | |
| verbose("node_drained = %s", params.node_drained ? "true" : "false"); | |
| verbose("node_draining = %s", params.node_draining ? "true" : "false"); | |
| verbose("node_fail = %s", params.node_fail ? "true" : "false"); | |
| verbose("node_idle = %s", params.node_idle ? "true" : "false"); | |
| verbose("node_up = %s", params.node_up ? "true" : "false"); | |
| verbose("node = %s", params.node_id); | |
| verbose("offset = %d secs", params.offset); | |
| verbose("program = %s", params.program); | |
| verbose("quiet = %s", params.quiet ? "true" : "false"); | |
| verbose("reconfig = %s", params.reconfig ? "true" : "false"); | |
| verbose("resume = %s", params.node_resume ? "true" : "false"); | |
| verbose("time_limit = %s", params.time_limit ? "true" : "false"); | |
| verbose("trigger_id = %u", params.trigger_id); | |
| if (params.user_id == NO_VAL) | |
| verbose("user_id = N/A"); | |
| else | |
| verbose("user_id = %u", params.user_id); | |
| verbose("verbose = %d", params.verbose); | |
| verbose("primary_slurmctld_failure = %s", | |
| params.pri_ctld_fail ? "true" : "false"); | |
| verbose("primary_slurmctld_resumed_operation = %s", | |
| params.pri_ctld_res_op ? "true" : "false"); | |
| verbose("primary_slurmctld_resumed_control = %s", | |
| params.pri_ctld_res_ctrl ? "true" : "false"); | |
| verbose("primary_slurmctld_acct_buffer_full = %s", | |
| params.pri_ctld_acct_buffer_full ? "true" : "false"); | |
| verbose("backup_slurmctld_failure = %s", | |
| params.bu_ctld_fail ? "true" : "false"); | |
| verbose("backup_slurmctld_resumed_operation = %s", | |
| params.bu_ctld_res_op ? "true" : "false"); | |
| verbose("backup_slurmctld_as_ctrl = %s", | |
| params.bu_ctld_as_ctrl ? "true" : "false"); | |
| verbose("primary_slurmdbd_failure = %s", | |
| params.pri_dbd_fail ? "true" : "false"); | |
| verbose("primary_slurmdbd_resumed_operation = %s", | |
| params.pri_dbd_res_op ? "true" : "false"); | |
| verbose("primary_database_failure = %s", | |
| params.pri_db_fail ? "true" : "false"); | |
| verbose("primary_database_resumed_operation = %s", | |
| params.pri_db_res_op ? "true" : "false"); | |
| verbose("-----------------------------"); | |
| } | |
| static void _validate_options( void ) | |
| { | |
| if ((params.mode_set + params.mode_get + params.mode_clear) != 1) { | |
| error("You must use exactly one of the following options: " | |
| "--set, --get or --clear"); | |
| exit(1); | |
| } | |
| if (params.mode_clear && (params.user_id == NO_VAL) && | |
| (params.trigger_id == 0) && (params.job_id == 0)) { | |
| error("You must specify a --id, --jobid, or --user to clear"); | |
| exit(1); | |
| } | |
| if (params.mode_set && | |
| ((params.node_down + params.node_drained + params.node_fail + | |
| params.node_idle + params.node_up + params.reconfig + | |
| params.job_fini + params.time_limit + | |
| params.node_draining + params.node_resume + params.burst_buffer + | |
| params.pri_ctld_fail + params.pri_ctld_res_op + | |
| params.pri_ctld_res_ctrl + params.pri_ctld_acct_buffer_full + | |
| params.bu_ctld_fail + params.bu_ctld_res_op + | |
| params.bu_ctld_as_ctrl + params.pri_dbd_fail + | |
| params.pri_dbd_res_op + params.pri_db_fail + | |
| params.pri_db_res_op) == 0)) { | |
| error("You must specify a trigger"); | |
| exit(1); | |
| } | |
| if (params.mode_set && (params.program == NULL)) { | |
| error("You must specify a --program value"); | |
| exit(1); | |
| } | |
| if (((params.job_fini + params.time_limit) != 0) | |
| && (params.job_id == 0)) { | |
| error("You must specify a --jobid value"); | |
| exit(1); | |
| } | |
| if (params.program && (params.program[0] != '/')) { | |
| error("The --program value must start with \"/\""); | |
| exit(1); | |
| } | |
| if (params.program) { | |
| int i; | |
| struct stat buf; | |
| char *program = xstrdup(params.program); | |
| for (i = 0; program[i]; i++) { | |
| if (isspace(program[i])) { | |
| program[i] = '\0'; | |
| break; | |
| } | |
| } | |
| if (stat(program, &buf)) { | |
| error("Invalid --program value, file not found"); | |
| exit(1); | |
| } | |
| if (!S_ISREG(buf.st_mode)) { | |
| error("Invalid --program value, not regular file"); | |
| exit(1); | |
| } | |
| xfree(program); | |
| } | |
| if ((params.offset < -32000) || (params.offset > 32000)) { | |
| error("The --offset parameter must be between +/-32000"); | |
| exit(1); | |
| } | |
| } | |
| static void _usage( void ) | |
| { | |
| printf("Usage: strigger [--set | --get | --clear | --version] " | |
| "[-aAbBcCdDefFgGhHiIjnNopQrtuv]\n"); | |
| } | |
| static void _help( void ) | |
| { | |
| printf ("\ | |
| Usage: strigger [--set | --get | --clear] [OPTIONS]\n\ | |
| --set create a trigger\n\ | |
| --get get trigger information\n\ | |
| --clear delete a trigger\n\n\ | |
| --burst_buffer trigger event on burst buffer error\n\ | |
| -a, --primary_slurmctld_failure\n\ | |
| trigger event when primary slurmctld fails\n\ | |
| -A, --primary_slurmctld_resumed_operation\n\ | |
| trigger event on primary slurmctld resumed operation\n\ | |
| after failure\n\ | |
| -b, --primary_slurmctld_resumed_control\n\ | |
| trigger event on primary slurmctld resumed control\n\ | |
| -B, --backup_slurmctld_failure\n\ | |
| trigger event when backup slurmctld fails\n\ | |
| -c, --backup_slurmctld_resumed_operation\n\ | |
| trigger event when backup slurmctld resumed operation\n\ | |
| after failure\n\ | |
| -C, --backup_slurmctld_assumed_control\n\ | |
| trigger event when backup slurmctld assumed control\n\ | |
| -d, --down trigger event when node goes DOWN\n\ | |
| -D, --drained trigger event when node becomes DRAINED\n\ | |
| --draining trigger event when node is DRAINING but not already\n\ | |
| DRAINED\n\ | |
| -e, --primary_slurmctld_acct_buffer_full\n\ | |
| trigger event when primary slurmctld acct buffer full\n\ | |
| -F, --fail trigger event when node is expected to FAIL\n\ | |
| -f, --fini trigger event when job finishes\n\ | |
| --flags=perm trigger event flag (perm = permanent)\n\n\ | |
| -g, --primary_slurmdbd_failure\n\ | |
| trigger when primary slurmdbd fails\n\ | |
| -G, --primary_slurmdbd_resumed_operation\n\ | |
| trigger when primary slurmdbd resumed operation after\n\ | |
| failure\n\ | |
| -h, --primary_database_failure\n\ | |
| trigger when primary database fails\n\ | |
| -H, --primary_database_resumed_operation\n\ | |
| trigger when primary database resumed operation after\n\ | |
| failure\n\ | |
| -i, --id=# a trigger's ID number\n\ | |
| -I, --idle trigger event when node remains IDLE\n\ | |
| -j, --jobid=# trigger related to specific jobid\n\ | |
| -M, --cluster=name cluster to issue commands to. Default is\n\ | |
| current cluster. cluster with no name will\n\ | |
| reset to default.\n\ | |
| NOTE: SlurmDBD must up.\n\ | |
| -n, --node[=host] trigger related to specific node, all nodes by default\n\ | |
| -N, --noheader Do not print the message header\n\ | |
| -o, --offset=# trigger's offset time from event, negative to precede\n\ | |
| -p, --program=path pathname of program to execute when triggered\n\ | |
| -Q, --quiet quiet mode (suppress informational messages)\n\ | |
| -r, --reconfig trigger event on configuration changes\n\ | |
| -R, --resume trigger event when node is set to RESUME state\n\ | |
| -t, --time trigger event on job's time limit\n\ | |
| -u, --up trigger event when node returned to service from DOWN \n\ | |
| state\n\ | |
| --user a user name or ID to filter triggers by\n\ | |
| -v, --verbose print 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"); | |
| } |