/*****************************************************************************\ * proc_args.c - helper functions for command argument processing ***************************************************************************** * Copyright (C) 2007 Hewlett-Packard Development Company, L.P. * Portions Copyright (C) 2010-2015 SchedMD LLC . * Written by Christopher Holmes , who borrowed heavily * from existing Slurm source code, particularly src/srun/opt.c * * 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" #define _GNU_SOURCE #include /* isdigit */ #include #include #include /* getpwuid */ #include /* va_start */ #include #include /* getenv, strtoll */ #include /* strcpy */ #include /* MAXPATHLEN */ #include #include #include #include #include "src/common/gres.h" #include "src/common/list.h" #include "src/common/log.h" #include "src/common/proc_args.h" #include "src/common/parse_time.h" #include "src/common/slurm_protocol_api.h" #include "src/common/slurm_acct_gather_profile.h" #include "src/common/xmalloc.h" #include "src/common/xstring.h" /* print this version of Slurm */ void print_slurm_version(void) { printf("%s %s\n", PACKAGE_NAME, SLURM_VERSION_STRING); } /* print the available gres options */ void print_gres_help(void) { char *msg = gres_plugin_help_msg(); printf("%s", msg); xfree(msg); } void set_distribution(task_dist_states_t distribution, char **dist, char **lllp_dist) { if (((int)distribution >= 0) && ((distribution & SLURM_DIST_STATE_BASE) != SLURM_DIST_UNKNOWN)) { switch (distribution & SLURM_DIST_STATE_BASE) { case SLURM_DIST_CYCLIC: *dist = "cyclic"; break; case SLURM_DIST_BLOCK: *dist = "block"; break; case SLURM_DIST_PLANE: *dist = "plane"; *lllp_dist = "plane"; break; case SLURM_DIST_ARBITRARY: *dist = "arbitrary"; break; case SLURM_DIST_CYCLIC_CYCLIC: *dist = "cyclic:cyclic"; *lllp_dist = "cyclic"; break; case SLURM_DIST_CYCLIC_BLOCK: *dist = "cyclic:block"; *lllp_dist = "block"; break; case SLURM_DIST_BLOCK_CYCLIC: *dist = "block:cyclic"; *lllp_dist = "cyclic"; break; case SLURM_DIST_BLOCK_BLOCK: *dist = "block:block"; *lllp_dist = "block"; break; case SLURM_DIST_CYCLIC_CFULL: *dist = "cyclic:fcyclic"; *lllp_dist = "fcyclic"; break; case SLURM_DIST_BLOCK_CFULL: *dist = "block:fcyclic"; *lllp_dist = "cyclic"; break; case SLURM_DIST_CYCLIC_CYCLIC_CYCLIC: *dist = "cyclic:cyclic:cyclic"; *lllp_dist = "cyclic:cyclic"; break; case SLURM_DIST_CYCLIC_CYCLIC_BLOCK: *dist = "cyclic:cyclic:block"; *lllp_dist = "cyclic:block"; break; case SLURM_DIST_CYCLIC_CYCLIC_CFULL: *dist = "cyclic:cyclic:fcyclic"; *lllp_dist = "cyclic:fcyclic"; break; case SLURM_DIST_CYCLIC_BLOCK_CYCLIC: *dist = "cyclic:block:cyclic"; *lllp_dist = "block:cyclic"; break; case SLURM_DIST_CYCLIC_BLOCK_BLOCK: *dist = "cyclic:block:block"; *lllp_dist = "block:block"; break; case SLURM_DIST_CYCLIC_BLOCK_CFULL: *dist = "cyclic:cylic:cyclic"; *lllp_dist = "cyclic:cyclic"; break; case SLURM_DIST_CYCLIC_CFULL_CYCLIC: *dist = "cyclic:cylic:cyclic"; *lllp_dist = "cyclic:cyclic"; break; case SLURM_DIST_CYCLIC_CFULL_BLOCK: *dist = "cyclic:fcyclic:block"; *lllp_dist = "fcyclic:block"; break; case SLURM_DIST_CYCLIC_CFULL_CFULL: *dist = "cyclic:fcyclic:fcyclic"; *lllp_dist = "fcyclic:fcyclic"; break; case SLURM_DIST_BLOCK_CYCLIC_CYCLIC: *dist = "block:cyclic:cyclic"; *lllp_dist = "cyclic:cyclic"; break; case SLURM_DIST_BLOCK_CYCLIC_BLOCK: *dist = "block:cyclic:block"; *lllp_dist = "cyclic:block"; break; case SLURM_DIST_BLOCK_CYCLIC_CFULL: *dist = "block:cyclic:fcyclic"; *lllp_dist = "cyclic:fcyclic"; break; case SLURM_DIST_BLOCK_BLOCK_CYCLIC: *dist = "block:block:cyclic"; *lllp_dist = "block:cyclic"; break; case SLURM_DIST_BLOCK_BLOCK_BLOCK: *dist = "block:block:block"; *lllp_dist = "block:block"; break; case SLURM_DIST_BLOCK_BLOCK_CFULL: *dist = "block:block:fcyclic"; *lllp_dist = "block:fcyclic"; break; case SLURM_DIST_BLOCK_CFULL_CYCLIC: *dist = "block:fcyclic:cyclic"; *lllp_dist = "fcyclic:cyclic"; break; case SLURM_DIST_BLOCK_CFULL_BLOCK: *dist = "block:fcyclic:block"; *lllp_dist = "fcyclic:block"; break; case SLURM_DIST_BLOCK_CFULL_CFULL: *dist = "block:fcyclic:fcyclic"; *lllp_dist = "fcyclic:fcyclic"; break; default: error("unknown dist, type 0x%X", distribution); break; } } } /* * Get the size of the plane distribution and put it in plane_size. * * An invalid plane size is zero, negative, or larger than INT_MAX. * * Return SLURM_DIST_PLANE for a valid plane size, SLURM_DIST_UNKNOWN otherwise. */ static task_dist_states_t _parse_plane_dist(const char *tok, uint32_t *plane_size) { long tmp_long; char *endptr, *plane_size_str; /* * Check for plane size given after '=' sign or in SLURM_DIST_PLANESIZE * environment variable. */ if ((plane_size_str = strchr(tok, '='))) plane_size_str++; else if (!(plane_size_str = getenv("SLURM_DIST_PLANESIZE"))) return SLURM_DIST_UNKNOWN; /* No plane size given */ else if (*plane_size_str == '\0') return SLURM_DIST_UNKNOWN; /* No plane size given */ tmp_long = strtol(plane_size_str, &endptr, 10); if ((plane_size_str == endptr) || (*endptr != '\0')) { /* No valid digits or there are characters after plane_size */ return SLURM_DIST_UNKNOWN; } else if ((tmp_long > INT_MAX) || (tmp_long <= 0) || ((errno == ERANGE) && (tmp_long == LONG_MAX))) return SLURM_DIST_UNKNOWN; /* Number is too high/low */ *plane_size = (uint32_t)tmp_long; return SLURM_DIST_PLANE; } /* * verify that a distribution type in arg is of a known form * returns the task_dist_states, or -1 if state is unknown */ task_dist_states_t verify_dist_type(const char *arg, uint32_t *plane_size) { int len; char *dist_str = NULL; task_dist_states_t result = SLURM_DIST_UNKNOWN; bool pack_nodes = false, no_pack_nodes = false; char *tok, *tmp, *save_ptr = NULL; int i, j; char *cur_ptr; char buf[3][25]; buf[0][0] = '\0'; buf[1][0] = '\0'; buf[2][0] = '\0'; char outstr[100]; outstr[0]='\0'; if (!arg) return result; if (!xstrncasecmp(arg, "plane", 5)) { /* * plane distribution can't be with any other type, * so just parse plane distribution and then break */ return _parse_plane_dist(arg, plane_size); } tmp = xstrdup(arg); tok = strtok_r(tmp, ",", &save_ptr); while (tok) { bool lllp_dist = false; len = strlen(tok); dist_str = strchr(tok, ':'); if (dist_str != NULL) { /* -m cyclic|block:cyclic|block */ lllp_dist = true; } cur_ptr = tok; for (j = 0; j < 3; j++) { for (i = 0; i < 24; i++) { if (*cur_ptr == '\0' || *cur_ptr ==':') break; buf[j][i] = *cur_ptr++; } buf[j][i] = '\0'; if (*cur_ptr == '\0') break; buf[j][i] = '\0'; cur_ptr++; } if (xstrcmp(buf[0], "*") == 0) /* default node distribution is block */ strcpy(buf[0], "block"); strcat(outstr, buf[0]); if (xstrcmp(buf[1], "\0") != 0) { strcat(outstr, ":"); if (!xstrcmp(buf[1], "*") || !xstrcmp(buf[1], "\0")) { /* default socket distribution is cyclic */ strcpy(buf[1], "cyclic"); } strcat(outstr, buf[1]); } if (xstrcmp(buf[2], "\0") != 0) { strcat(outstr, ":"); if (!xstrcmp(buf[2], "*") || !xstrcmp(buf[2], "\0")) { /* default core dist is inherited socket dist */ strcpy(buf[2], buf[1]); } strcat(outstr, buf[2]); } if (lllp_dist) { if (xstrcasecmp(outstr, "cyclic:cyclic") == 0) { result = SLURM_DIST_CYCLIC_CYCLIC; } else if (xstrcasecmp(outstr, "cyclic:block") == 0) { result = SLURM_DIST_CYCLIC_BLOCK; } else if (xstrcasecmp(outstr, "block:block") == 0) { result = SLURM_DIST_BLOCK_BLOCK; } else if (xstrcasecmp(outstr, "block:cyclic") == 0) { result = SLURM_DIST_BLOCK_CYCLIC; } else if (xstrcasecmp(outstr, "block:fcyclic") == 0) { result = SLURM_DIST_BLOCK_CFULL; } else if (xstrcasecmp(outstr, "cyclic:fcyclic") == 0) { result = SLURM_DIST_CYCLIC_CFULL; } else if (xstrcasecmp(outstr, "cyclic:cyclic:cyclic") == 0) { result = SLURM_DIST_CYCLIC_CYCLIC_CYCLIC; } else if (xstrcasecmp(outstr, "cyclic:cyclic:block") == 0) { result = SLURM_DIST_CYCLIC_CYCLIC_BLOCK; } else if (xstrcasecmp(outstr, "cyclic:cyclic:fcyclic") == 0) { result = SLURM_DIST_CYCLIC_CYCLIC_CFULL; } else if (xstrcasecmp(outstr, "cyclic:block:cyclic") == 0) { result = SLURM_DIST_CYCLIC_BLOCK_CYCLIC; } else if (xstrcasecmp(outstr, "cyclic:block:block") == 0) { result = SLURM_DIST_CYCLIC_BLOCK_BLOCK; } else if (xstrcasecmp(outstr, "cyclic:block:fcyclic") == 0) { result = SLURM_DIST_CYCLIC_BLOCK_CFULL; } else if (xstrcasecmp(outstr, "cyclic:fcyclic:cyclic") == 0) { result = SLURM_DIST_CYCLIC_CFULL_CYCLIC; } else if (xstrcasecmp(outstr, "cyclic:fcyclic:block") == 0) { result = SLURM_DIST_CYCLIC_CFULL_BLOCK; } else if (xstrcasecmp(outstr, "cyclic:fcyclic:fcyclic") == 0) { result = SLURM_DIST_CYCLIC_CFULL_CFULL; } else if (xstrcasecmp(outstr, "block:cyclic:cyclic") == 0) { result = SLURM_DIST_BLOCK_CYCLIC_CYCLIC; } else if (xstrcasecmp(outstr, "block:cyclic:block") == 0) { result = SLURM_DIST_BLOCK_CYCLIC_BLOCK; } else if (xstrcasecmp(outstr, "block:cyclic:fcyclic") == 0) { result = SLURM_DIST_BLOCK_CYCLIC_CFULL; } else if (xstrcasecmp(outstr, "block:block:cyclic") == 0) { result = SLURM_DIST_BLOCK_BLOCK_CYCLIC; } else if (xstrcasecmp(outstr, "block:block:block") == 0) { result = SLURM_DIST_BLOCK_BLOCK_BLOCK; } else if (xstrcasecmp(outstr, "block:block:fcyclic") == 0) { result = SLURM_DIST_BLOCK_BLOCK_CFULL; } else if (xstrcasecmp(outstr, "block:fcyclic:cyclic") == 0) { result = SLURM_DIST_BLOCK_CFULL_CYCLIC; } else if (xstrcasecmp(outstr, "block:fcyclic:block") == 0) { result = SLURM_DIST_BLOCK_CFULL_BLOCK; } else if (xstrcasecmp(outstr, "block:fcyclic:fcyclic") == 0) { result = SLURM_DIST_BLOCK_CFULL_CFULL; } } else { if (xstrncasecmp(tok, "cyclic", len) == 0) { result = SLURM_DIST_CYCLIC; } else if ((xstrncasecmp(tok, "block", len) == 0) || (xstrncasecmp(tok, "*", len) == 0)) { /* * We can get here with syntax like this: * -m *,pack * '*' means get default (block for node dist). */ result = SLURM_DIST_BLOCK; } else if ((xstrncasecmp(tok, "arbitrary", len) == 0) || (xstrncasecmp(tok, "hostfile", len) == 0)) { result = SLURM_DIST_ARBITRARY; } else if (xstrncasecmp(tok, "nopack", len) == 0) { no_pack_nodes = true; } else if (xstrncasecmp(tok, "pack", len) == 0) { pack_nodes = true; } } tok = strtok_r(NULL, ",", &save_ptr); } xfree(tmp); if (pack_nodes) result |= SLURM_DIST_PACK_NODES; else if (no_pack_nodes) result |= SLURM_DIST_NO_PACK_NODES; return result; } extern char *format_task_dist_states(task_dist_states_t t) { switch (t & SLURM_DIST_STATE_BASE) { case SLURM_DIST_BLOCK: return "block"; case SLURM_DIST_CYCLIC: return "cyclic"; case SLURM_DIST_PLANE: return "plane"; case SLURM_DIST_ARBITRARY: return "arbitrary"; case SLURM_DIST_CYCLIC_CYCLIC: return "cyclic:cyclic"; case SLURM_DIST_CYCLIC_BLOCK: return "cyclic:block"; case SLURM_DIST_CYCLIC_CFULL: return "cyclic:fcyclic"; case SLURM_DIST_BLOCK_CYCLIC: return "block:cyclic"; case SLURM_DIST_BLOCK_BLOCK: return "block:block"; case SLURM_DIST_BLOCK_CFULL: return "block:fcyclic"; case SLURM_DIST_CYCLIC_CYCLIC_CYCLIC: return "cyclic:cyclic:cyclic"; case SLURM_DIST_CYCLIC_CYCLIC_BLOCK: return "cyclic:cyclic:block"; case SLURM_DIST_CYCLIC_CYCLIC_CFULL: return "cyclic:cyclic:fcyclic"; case SLURM_DIST_CYCLIC_BLOCK_CYCLIC: return "cyclic:block:cyclic"; case SLURM_DIST_CYCLIC_BLOCK_BLOCK: return "cyclic:block:block"; case SLURM_DIST_CYCLIC_BLOCK_CFULL: return "cyclic:block:fcyclic"; case SLURM_DIST_CYCLIC_CFULL_CYCLIC: return "cyclic:fcyclic:cyclic" ; case SLURM_DIST_CYCLIC_CFULL_BLOCK: return "cyclic:fcyclic:block"; case SLURM_DIST_CYCLIC_CFULL_CFULL: return "cyclic:fcyclic:fcyclic"; case SLURM_DIST_BLOCK_CYCLIC_CYCLIC: return "block:cyclic:cyclic"; case SLURM_DIST_BLOCK_CYCLIC_BLOCK: return "block:cyclic:block"; case SLURM_DIST_BLOCK_CYCLIC_CFULL: return "block:cyclic:fcyclic"; case SLURM_DIST_BLOCK_BLOCK_CYCLIC: return "block:block:cyclic"; case SLURM_DIST_BLOCK_BLOCK_BLOCK: return "block:block:block"; case SLURM_DIST_BLOCK_BLOCK_CFULL: return "block:block:fcyclic"; case SLURM_DIST_BLOCK_CFULL_CYCLIC: return "block:fcyclic:cyclic"; case SLURM_DIST_BLOCK_CFULL_BLOCK: return "block:fcyclic:block"; case SLURM_DIST_BLOCK_CFULL_CFULL: return "block:fcyclic:fcyclic"; default: return "unknown"; } } /* return command name from its full path name */ char *base_name(const char *command) { const char *char_ptr; if (command == NULL) return NULL; char_ptr = strrchr(command, (int)'/'); if (char_ptr == NULL) char_ptr = command; else char_ptr++; return xstrdup(char_ptr); } static uint64_t _str_to_mbytes(const char *arg, int use_gbytes) { long long result; char *endptr; errno = 0; result = strtoll(arg, &endptr, 10); if ((errno != 0) && ((result == LLONG_MIN) || (result == LLONG_MAX))) return NO_VAL64; if (result < 0) return NO_VAL64; else if ((endptr[0] == '\0') && (use_gbytes == 1)) /* GB default */ result *= 1024; else if (endptr[0] == '\0') /* MB default */ ; else if ((endptr[0] == 'k') || (endptr[0] == 'K')) result = (result + 1023) / 1024; /* round up */ else if ((endptr[0] == 'm') || (endptr[0] == 'M')) ; else if ((endptr[0] == 'g') || (endptr[0] == 'G')) result *= 1024; else if ((endptr[0] == 't') || (endptr[0] == 'T')) result *= (1024 * 1024); else return NO_VAL64; return (uint64_t) result; } /* * str_to_mbytes(): verify that arg is numeric with optional "K", "M", "G" * or "T" at end and return the number in mega-bytes. Default units are MB. */ uint64_t str_to_mbytes(const char *arg) { return _str_to_mbytes(arg, 0); } /* * str_to_mbytes2(): verify that arg is numeric with optional "K", "M", "G" * or "T" at end and return the number in mega-bytes. Default units are GB * if "SchedulerParameters=default_gbytes" is configured, otherwise MB. */ uint64_t str_to_mbytes2(const char *arg) { static int use_gbytes = -1; if (use_gbytes == -1) { char *sched_params = slurm_get_sched_params(); if (xstrcasestr(sched_params, "default_gbytes")) use_gbytes = 1; else use_gbytes = 0; xfree(sched_params); } return _str_to_mbytes(arg, use_gbytes); } extern char *mbytes2_to_str(uint64_t mbytes) { int i = 0; char *unit = "MGTP?"; static int use_gbytes = -1; if (mbytes == NO_VAL64) return NULL; if (use_gbytes == -1) { char *sched_params = slurm_get_sched_params(); if (xstrcasestr(sched_params, "default_gbytes")) use_gbytes = 1; else use_gbytes = 0; xfree(sched_params); } for (i = 0; unit[i] != '?'; i++) { if (mbytes && (mbytes % 1024)) break; mbytes /= 1024; } /* no need to display the default unit */ if ((unit[i] == 'G' && use_gbytes) || (unit[i] == 'M' && !use_gbytes)) return xstrdup_printf("%"PRIu64, mbytes); return xstrdup_printf("%"PRIu64"%c", mbytes, unit[i]); } /* Convert a string into a node count */ static int _str_to_nodes(const char *num_str, char **leftover) { long int num; char *endptr; num = strtol(num_str, &endptr, 10); if (endptr == num_str) { /* no valid digits */ *leftover = (char *)num_str; return -1; } if (*endptr != '\0' && (*endptr == 'k' || *endptr == 'K')) { num *= 1024; endptr++; } if (*endptr != '\0' && (*endptr == 'm' || *endptr == 'M')) { num *= (1024 * 1024); endptr++; } *leftover = endptr; return (int)num; } /* * verify that a node count in arg is of a known form (count or min-max) * OUT min, max specified minimum and maximum node counts * RET true if valid */ bool verify_node_count(const char *arg, int *min_nodes, int *max_nodes) { char *ptr, *min_str, *max_str; char *leftover; /* * Does the string contain a "-" character? If so, treat as a range. * otherwise treat as an absolute node count. */ if ((ptr = xstrchr(arg, '-')) != NULL) { min_str = xstrndup(arg, ptr-arg); *min_nodes = _str_to_nodes(min_str, &leftover); if (!xstring_is_whitespace(leftover)) { error("\"%s\" is not a valid node count", min_str); xfree(min_str); return false; } xfree(min_str); if (*min_nodes < 0) *min_nodes = 1; max_str = xstrndup(ptr+1, strlen(arg)-((ptr+1)-arg)); *max_nodes = _str_to_nodes(max_str, &leftover); if (!xstring_is_whitespace(leftover)) { error("\"%s\" is not a valid node count", max_str); xfree(max_str); return false; } xfree(max_str); } else { *min_nodes = *max_nodes = _str_to_nodes(arg, &leftover); if (!xstring_is_whitespace(leftover)) { error("\"%s\" is not a valid node count", arg); return false; } if (*min_nodes < 0) { error("\"%s\" is not a valid node count", arg); return false; } } if ((*max_nodes != 0) && (*max_nodes < *min_nodes)) { error("Maximum node count %d is less than minimum node count %d", *max_nodes, *min_nodes); return false; } return true; } /* * If the node list supplied is a file name, translate that into * a list of nodes, we orphan the data pointed to * RET true if the node list is a valid one */ bool verify_node_list(char **node_list_pptr, enum task_dist_states dist, int task_count) { char *nodelist = NULL; xassert (node_list_pptr); xassert (*node_list_pptr); if (strchr(*node_list_pptr, '/') == NULL) return true; /* not a file name */ /* If we are using Arbitrary grab count out of the hostfile using them exactly the way we read it in since we are saying, lay it out this way! */ if ((dist & SLURM_DIST_STATE_BASE) == SLURM_DIST_ARBITRARY) nodelist = slurm_read_hostfile(*node_list_pptr, task_count); else nodelist = slurm_read_hostfile(*node_list_pptr, NO_VAL); if (!nodelist) return false; xfree(*node_list_pptr); *node_list_pptr = xstrdup(nodelist); free(nodelist); return true; } /* * get either 1 or 2 integers for a resource count in the form of either * (count, min-max, or '*') * A partial error message is passed in via the 'what' param. * IN arg - argument * IN what - variable name (for errors) * OUT min - first number * OUT max - maximum value if specified, NULL if don't care * IN isFatal - if set, exit on error * RET true if valid */ bool get_resource_arg_range(const char *arg, const char *what, int* min, int *max, bool isFatal) { char *p; long int result; /* wildcard meaning every possible value in range */ if ((*arg == '\0') || (*arg == '*' )) { *min = 1; if (max) *max = INT_MAX; return true; } result = strtol(arg, &p, 10); if (*p == 'k' || *p == 'K') { result *= 1024; p++; } else if (*p == 'm' || *p == 'M') { result *= 1048576; p++; } if (((*p != '\0') && (*p != '-')) || (result < 0L)) { error ("Invalid numeric value \"%s\" for %s.", arg, what); if (isFatal) exit(1); return false; } else if (result > INT_MAX) { error ("Numeric argument (%ld) to big for %s.", result, what); if (isFatal) exit(1); return false; } *min = (int) result; if (*p == '\0') return true; if (*p == '-') p++; result = strtol(p, &p, 10); if ((*p == 'k') || (*p == 'K')) { result *= 1024; p++; } else if (*p == 'm' || *p == 'M') { result *= 1048576; p++; } if (((*p != '\0') && (*p != '-')) || (result <= 0L)) { error ("Invalid numeric value \"%s\" for %s.", arg, what); if (isFatal) exit(1); return false; } else if (result > INT_MAX) { error ("Numeric argument (%ld) to big for %s.", result, what); if (isFatal) exit(1); return false; } if (max) *max = (int) result; return true; } /* * verify that a resource counts in arg are of a known form X, X:X, X:X:X, or * X:X:X:X, where X is defined as either (count, min-max, or '*') * RET true if valid */ bool verify_socket_core_thread_count(const char *arg, int *min_sockets, int *min_cores, int *min_threads, cpu_bind_type_t *cpu_bind_type) { bool tmp_val, ret_val; int i, j; int max_sockets = 0, max_cores = 0, max_threads = 0; const char *cur_ptr = arg; char buf[3][48]; /* each can hold INT64_MAX - INT64_MAX */ if (!arg) { error("%s: argument is NULL", __func__); return false; } memset(buf, 0, sizeof(buf)); for (j = 0; j < 3; j++) { for (i = 0; i < 47; i++) { if (*cur_ptr == '\0' || *cur_ptr ==':') break; buf[j][i] = *cur_ptr++; } if (*cur_ptr == '\0') break; xassert(*cur_ptr == ':'); cur_ptr++; } /* if cpu_bind_type doesn't already have a auto preference, choose * the level based on the level of the -E specification */ if (cpu_bind_type && !(*cpu_bind_type & (CPU_BIND_TO_SOCKETS | CPU_BIND_TO_CORES | CPU_BIND_TO_THREADS))) { if (j == 0) { *cpu_bind_type |= CPU_BIND_TO_SOCKETS; } else if (j == 1) { *cpu_bind_type |= CPU_BIND_TO_CORES; } else if (j == 2) { *cpu_bind_type |= CPU_BIND_TO_THREADS; } } ret_val = true; tmp_val = get_resource_arg_range(&buf[0][0], "first arg of -B", min_sockets, &max_sockets, true); if ((*min_sockets == 1) && (max_sockets == INT_MAX)) *min_sockets = NO_VAL; /* Use full range of values */ ret_val = ret_val && tmp_val; tmp_val = get_resource_arg_range(&buf[1][0], "second arg of -B", min_cores, &max_cores, true); if ((*min_cores == 1) && (max_cores == INT_MAX)) *min_cores = NO_VAL; /* Use full range of values */ ret_val = ret_val && tmp_val; tmp_val = get_resource_arg_range(&buf[2][0], "third arg of -B", min_threads, &max_threads, true); if ((*min_threads == 1) && (max_threads == INT_MAX)) *min_threads = NO_VAL; /* Use full range of values */ ret_val = ret_val && tmp_val; return ret_val; } /* * verify that a hint is valid and convert it into the implied settings * RET true if valid */ bool verify_hint(const char *arg, int *min_sockets, int *min_cores, int *min_threads, int *ntasks_per_core, cpu_bind_type_t *cpu_bind_type) { char *buf, *p, *tok; if (!arg) return true; buf = xstrdup(arg); p = buf; /* change all ',' delimiters not followed by a digit to ';' */ /* simplifies parsing tokens while keeping map/mask together */ while (p[0] != '\0') { if ((p[0] == ',') && (!isdigit((int)p[1]))) p[0] = ';'; p++; } p = buf; while ((tok = strsep(&p, ";"))) { if (xstrcasecmp(tok, "help") == 0) { printf( "Application hint options:\n" " --hint= Bind tasks according to application hints\n" " compute_bound use all cores in each socket\n" " memory_bound use only one core in each socket\n" " [no]multithread [don't] use extra threads with in-core multi-threading\n" " help show this help message\n"); xfree(buf); return 1; } else if (xstrcasecmp(tok, "compute_bound") == 0) { *min_sockets = NO_VAL; *min_cores = NO_VAL; *min_threads = 1; if (cpu_bind_type) *cpu_bind_type |= CPU_BIND_TO_CORES; } else if (xstrcasecmp(tok, "memory_bound") == 0) { *min_cores = 1; *min_threads = 1; if (cpu_bind_type) *cpu_bind_type |= CPU_BIND_TO_CORES; } else if (xstrcasecmp(tok, "multithread") == 0) { *min_threads = NO_VAL; if (cpu_bind_type) { *cpu_bind_type |= CPU_BIND_TO_THREADS; *cpu_bind_type &= (~CPU_BIND_ONE_THREAD_PER_CORE); } if (*ntasks_per_core == NO_VAL) *ntasks_per_core = INFINITE16; } else if (xstrcasecmp(tok, "nomultithread") == 0) { *min_threads = 1; if (cpu_bind_type) { *cpu_bind_type |= CPU_BIND_TO_THREADS; *cpu_bind_type |= CPU_BIND_ONE_THREAD_PER_CORE; } } else { error("unrecognized --hint argument \"%s\", " "see --hint=help", tok); xfree(buf); return 1; } } if (!cpu_bind_type) setenvf(NULL, "SLURM_HINT", "%s", arg); xfree(buf); return 0; } uint16_t parse_mail_type(const char *arg) { char *buf, *tok, *save_ptr = NULL; uint16_t rc = 0; bool none_set = false; if (!arg) return INFINITE16; buf = xstrdup(arg); tok = strtok_r(buf, ",", &save_ptr); while (tok) { if (xstrcasecmp(tok, "NONE") == 0) { rc = 0; none_set = true; break; } else if (xstrcasecmp(tok, "ARRAY_TASKS") == 0) rc |= MAIL_ARRAY_TASKS; else if (xstrcasecmp(tok, "BEGIN") == 0) rc |= MAIL_JOB_BEGIN; else if (xstrcasecmp(tok, "END") == 0) rc |= MAIL_JOB_END; else if (xstrcasecmp(tok, "FAIL") == 0) rc |= MAIL_JOB_FAIL; else if (xstrcasecmp(tok, "REQUEUE") == 0) rc |= MAIL_JOB_REQUEUE; else if (xstrcasecmp(tok, "ALL") == 0) rc |= MAIL_JOB_BEGIN | MAIL_JOB_END | MAIL_JOB_FAIL | MAIL_JOB_REQUEUE | MAIL_JOB_STAGE_OUT; else if (!xstrcasecmp(tok, "STAGE_OUT")) rc |= MAIL_JOB_STAGE_OUT; else if (xstrcasecmp(tok, "TIME_LIMIT") == 0) rc |= MAIL_JOB_TIME100; else if (xstrcasecmp(tok, "TIME_LIMIT_90") == 0) rc |= MAIL_JOB_TIME90; else if (xstrcasecmp(tok, "TIME_LIMIT_80") == 0) rc |= MAIL_JOB_TIME80; else if (xstrcasecmp(tok, "TIME_LIMIT_50") == 0) rc |= MAIL_JOB_TIME50; tok = strtok_r(NULL, ",", &save_ptr); } xfree(buf); if (!rc && !none_set) rc = INFINITE16; return rc; } char *print_mail_type(const uint16_t type) { static char buf[256]; buf[0] = '\0'; if (type == 0) return "NONE"; if (type & MAIL_ARRAY_TASKS) { if (buf[0]) strcat(buf, ","); strcat(buf, "ARRAY_TASKS"); } if (type & MAIL_JOB_BEGIN) { if (buf[0]) strcat(buf, ","); strcat(buf, "BEGIN"); } if (type & MAIL_JOB_END) { if (buf[0]) strcat(buf, ","); strcat(buf, "END"); } if (type & MAIL_JOB_FAIL) { if (buf[0]) strcat(buf, ","); strcat(buf, "FAIL"); } if (type & MAIL_JOB_REQUEUE) { if (buf[0]) strcat(buf, ","); strcat(buf, "REQUEUE"); } if (type & MAIL_JOB_STAGE_OUT) { if (buf[0]) strcat(buf, ","); strcat(buf, "STAGE_OUT"); } if (type & MAIL_JOB_TIME50) { if (buf[0]) strcat(buf, ","); strcat(buf, "TIME_LIMIT_50"); } if (type & MAIL_JOB_TIME80) { if (buf[0]) strcat(buf, ","); strcat(buf, "TIME_LIMIT_80"); } if (type & MAIL_JOB_TIME90) { if (buf[0]) strcat(buf, ","); strcat(buf, "TIME_LIMIT_90"); } if (type & MAIL_JOB_TIME100) { if (buf[0]) strcat(buf, ","); strcat(buf, "TIME_LIMIT"); } return buf; } static List _create_path_list(void) { List l = list_create(xfree_ptr); char *path; char *c, *lc; c = getenv("PATH"); if (!c) { error("No PATH environment variable"); return l; } path = xstrdup(c); c = lc = path; while (*c != '\0') { if (*c == ':') { /* nullify and push token onto list */ *c = '\0'; if (lc != NULL && strlen(lc) > 0) list_append(l, xstrdup(lc)); lc = ++c; } else c++; } if (strlen(lc) > 0) list_append(l, xstrdup(lc)); xfree(path); return l; } /* * Check a specific path to see if the executable exists and is not a directory * IN path - path of executable to check * RET true if path exists and is not a directory; false otherwise */ static bool _exists(const char *path) { struct stat st; if (stat(path, &st)) { debug2("_check_exec: failed to stat path %s", path); return false; } if (S_ISDIR(st.st_mode)) { debug2("_check_exec: path %s is a directory", path); return false; } return true; } /* * Check a specific path to see if the executable is accessible * IN path - path of executable to check * IN access_mode - determine if executable is accessible to caller with * specified mode * RET true if path is accessible according to access mode, false otherwise */ static bool _accessible(const char *path, int access_mode) { if (access(path, access_mode)) { debug2("_check_exec: path %s is not accessible", path); return false; } return true; } /* * search PATH to confirm the location and access mode of the given command * IN cwd - current working directory * IN cmd - command to execute * IN check_cwd_last - if true, search cwd after PATH is checked * - if false, search cwd for the command first * IN access_mode - required access rights of cmd * IN test_exec - if false, do not confirm access mode of cmd if full path * RET full path of cmd or NULL if not found */ char *search_path(char *cwd, char *cmd, bool check_cwd_last, int access_mode, bool test_exec) { List l = NULL; ListIterator i = NULL; char *path, *fullpath = NULL; /* Relative path */ if (cmd[0] == '.') { if (test_exec) { char *cmd1 = xstrdup_printf("%s/%s", cwd, cmd); if (_exists(cmd1) && _accessible(cmd1, access_mode)) { fullpath = xstrdup(cmd1); debug5("%s: relative path found %s -> %s", __func__, cmd, cmd1); } else { debug5("%s: relative path not found %s -> %s", __func__, cmd, cmd1); } xfree(cmd1); } return fullpath; } /* Absolute path */ if (cmd[0] == '/') { if (test_exec && _exists(cmd) && _accessible(cmd, access_mode)) { fullpath = xstrdup(cmd); debug5("%s: absolute path found %s", __func__, cmd); } else { debug5("%s: absolute path not found %s", __func__, cmd); } return fullpath; } /* Otherwise search in PATH */ l = _create_path_list(); if (l == NULL) { debug5("%s: empty PATH environment", __func__); return NULL; } if (check_cwd_last) list_append(l, xstrdup(cwd)); else list_prepend(l, xstrdup(cwd)); i = list_iterator_create(l); while ((path = list_next(i))) { if (path[0] == '.') xstrfmtcat(fullpath, "%s/%s/%s", cwd, path, cmd); else xstrfmtcat(fullpath, "%s/%s", path, cmd); /* Use first executable found in PATH */ if (_exists(fullpath)) { if (!test_exec) { debug5("%s: env PATH found: %s", __func__, fullpath); break; } if (_accessible(path, access_mode)) { debug5("%s: env PATH found: %s", __func__, fullpath); break; } } debug5("%s: env PATH not found: %s", __func__, fullpath); xfree(fullpath); } list_iterator_destroy(i); FREE_NULL_LIST(l); return fullpath; } char *print_commandline(const int script_argc, char **script_argv) { int i; char *out_buf = NULL, *prefix = ""; for (i = 0; i < script_argc; i++) { xstrfmtcat(out_buf, "%s%s", prefix, script_argv[i]); prefix = " "; } return out_buf; } /* Translate a signal option string "--signal=[@