blob: 0a516b963a0b6ae42d2e0dd67fbdde515966be94 [file] [log] [blame]
/*****************************************************************************\
* proc_args.c - helper functions for command argument processing
*****************************************************************************
* Copyright (C) 2007 Hewlett-Packard Development Company, L.P.
* Copyright (C) SchedMD LLC.
* Written by Christopher Holmes <cholmes@hp.com>, 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 <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 <ctype.h> /* isdigit */
#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#include <stdarg.h> /* va_start */
#include <stdio.h>
#include <stdlib.h> /* getenv, strtoll */
#include <string.h> /* strcpy */
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <unistd.h>
#include "src/interfaces/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/interfaces/acct_gather_profile.h"
#include "src/common/xmalloc.h"
#include "src/common/xstring.h"
enum {
RESV_NEW, /* It is a new reservation */
RESV_ADD, /* It is a reservation update with += */
RESV_REM, /* It is an reservation update with -= */
};
/* 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_help_msg();
printf("%s", msg);
xfree(msg);
}
void set_distribution(task_dist_states_t distribution, char **dist)
{
task_dist_states_t dist_flag = 0;
if (((int)distribution <= 0) || (distribution == SLURM_DIST_UNKNOWN))
return; /* distribution not set */
if ((distribution & SLURM_DIST_STATE_BASE) != SLURM_DIST_UNKNOWN)
*dist = xstrdup(format_task_dist_states(distribution));
if ((dist_flag = (distribution & SLURM_DIST_STATE_FLAGS))) {
if (dist_flag == SLURM_DIST_PACK_NODES)
xstrfmtcat(*dist, "%spack", *dist ? "," : "");
else if (dist_flag == SLURM_DIST_NO_PACK_NODES)
xstrfmtcat(*dist, "%snopack", *dist ? "," : "");
else
error("%s: Unknown distribution flag value: 0x%x",
__func__, dist_flag);
}
}
/*
* 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_ERROR otherwise.
*/
static task_dist_states_t _parse_plane_dist(const char *tok,
uint32_t *plane_size)
{
task_dist_states_t rc = SLURM_ERROR;
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")))
goto fini; /* No plane size given */
else if (*plane_size_str == '\0')
goto fini; /* 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 */
goto fini;
} else if ((tmp_long > INT_MAX) || (tmp_long <= 0) ||
((errno == ERANGE) && (tmp_long == LONG_MAX)))
goto fini; /* Number is too high/low */
*plane_size = (uint32_t)tmp_long;
rc = SLURM_DIST_PLANE;
fini:
if (rc == SLURM_ERROR)
error("Invalid plane size or size not specified");
return rc;
}
static void _parse_dist_flag(char *flag_str, task_dist_states_t *result)
{
xassert(result);
if (!result)
return;
if (!*result) {
*result = SLURM_ERROR;
return;
}
if (xstrcasecmp(flag_str, "nopack") == 0)
*result |= SLURM_DIST_NO_PACK_NODES;
else if (xstrcasecmp(flag_str, "pack") == 0)
*result |= SLURM_DIST_PACK_NODES;
else
*result = SLURM_ERROR;
}
static task_dist_states_t _parse_dist_base(const char *str)
{
task_dist_states_t result = SLURM_DIST_UNKNOWN;
int i;
char *tmp, *dist_base, *flag_str = NULL, *outstr = NULL;
char *token, *save_ptr = NULL;
char *sock_dist = NULL;
if (!str || !*str || (*str == ',')) {
return SLURM_ERROR;
}
tmp = xstrdup(str);
if (!(dist_base = strtok_r(tmp, ",", &flag_str))) {
xfree(tmp);
return SLURM_ERROR;
}
i = 0;
token = strtok_r(dist_base, ":", &save_ptr);
while (token) {
if (i > 2)
return SLURM_ERROR;
if (i > 0)
xstrcat(outstr, ":");
if (!xstrcmp(token, "*")) {
switch (i) {
case 0:
/* default node distribution is block */
xstrcat(outstr, "block");
break;
case 1:
/* default socket distribution is cyclic */
sock_dist = "cyclic";
xstrcat(outstr, sock_dist);
break;
case 2:
/* default core dist is inherited socket dist */
xstrcat(outstr, sock_dist);
break;
}
} else {
xstrcat(outstr, token);
if (i == 1)
sock_dist = token;
}
token = strtok_r(NULL, ":", &save_ptr);
i++;
}
if (xstrcasecmp(outstr, "cyclic") == 0) {
result = SLURM_DIST_CYCLIC;
} else if (xstrcasecmp(outstr, "block") == 0) {
result = SLURM_DIST_BLOCK;
} else if ((xstrcasecmp(outstr, "arbitrary") == 0) ||
(xstrcasecmp(outstr, "hostfile") == 0)) {
result = SLURM_DIST_ARBITRARY;
} else 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 {
_parse_dist_flag(outstr, &result);
}
if ((result != SLURM_ERROR) && flag_str && *flag_str)
_parse_dist_flag(flag_str, &result);
xfree(tmp);
xfree(outstr);
return result;
}
/*
* verify that a distribution type in arg is of a known form
*
* It is valid to use pack or nopack alone. For example:
* srun --distribution=pack
* In this case, we return SLURM_DIST_UNKNOWN bitwise OR'd with
* SLURM_DIST_PACK_NODES or SLURM_DIST_NO_PACK_NODES. slurmctld treats
* SLURM_DIST_UNKNOWN as the default distribution methods and also handles
* SLURM_DIST_PACK_NODES or SLURM_DIST_NO_PACK_NODES appropriately.
*
* Returns the distribution type for a valid argument, SLURM_DIST_UNKNOWN if
* arg is NULL, or SLURM_ERROR (-1) if arg is invalid.
*/
task_dist_states_t verify_dist_type(const char *arg, uint32_t *plane_size)
{
task_dist_states_t result = SLURM_DIST_UNKNOWN;
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);
}
return _parse_dist_base(arg);
}
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 bool _end_on_byte(const char * endptr) {
if ((endptr[1] == '\0') ||
(((endptr[1] == 'B') || (endptr[1] == 'b')) && endptr[2] == '\0'))
return true;
return false;
}
/*
* 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)
{
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 (arg == endptr)
return NO_VAL64;
if (result < 0)
return NO_VAL64;
else if (endptr[0] == '\0') /* MB default */
;
else if (((endptr[0] == 'k') || (endptr[0] == 'K')) &&
_end_on_byte(endptr))
result = ROUNDUP(result, 1024); /* round up */
else if (((endptr[0] == 'm') || (endptr[0] == 'M')) &&
_end_on_byte(endptr))
;
else if (((endptr[0] == 'g') || (endptr[0] == 'G')) &&
_end_on_byte(endptr))
result *= 1024;
else if (((endptr[0] == 't') || (endptr[0] == 'T')) &&
_end_on_byte(endptr))
result *= (1024 * 1024);
else
return NO_VAL64;
return (uint64_t) result;
}
extern char *mbytes_to_str(uint64_t mbytes)
{
int i = 0;
char *unit = "MGTP?";
if (mbytes == NO_VAL64)
return NULL;
for (i = 0; unit[i] != '?'; i++) {
if (mbytes && (mbytes % 1024))
break;
mbytes /= 1024;
}
/* no need to display the default unit */
if (unit[i] == 'M')
return xstrdup_printf("%"PRIu64, mbytes);
return xstrdup_printf("%"PRIu64"%c", mbytes, unit[i]);
}
/* Convert a string into a node count */
extern 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;
if ((num < 0) || (num > INT_MAX))
return -1;
return (int)num;
}
/*
* verify that a node count in arg is of a known form (count or min-max or list)
* OUT min, max specified minimum and maximum node counts
* OUT job_size_str
* RET true if valid
*/
bool verify_node_count(const char *arg, int *min_nodes, int *max_nodes,
char **job_size_str)
{
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 (job_size_str)
xfree(*job_size_str);
if ((ptr = xstrchr(arg, ',')) || (ptr = xstrchr(arg, ':'))) {
bitstr_t *job_size_bitmap;
char *tok, *tmp_str, *save_ptr = NULL;
long int max = 0;
tmp_str = xstrdup(arg);
tok = strtok_r(tmp_str, ",-:", &save_ptr);
while (tok) {
char *endptr;
long int num = strtol(tok, &endptr, 10);
if ((endptr == tok) ||
((*endptr != '\0') && (*endptr != ',') &&
(*endptr != '-') && (*endptr != ':')) ||
(num >= MAX_JOB_SIZE_BITMAP)) {
error("\"%s\" is not a valid node count", tok);
xfree(tmp_str);
return false;
}
if (num > max)
max = num;
tok = strtok_r(NULL, ",-:", &save_ptr);
}
xfree(tmp_str);
tmp_str = xstrdup(arg);
job_size_bitmap = bit_alloc(max + 1);
if (bit_unfmt(job_size_bitmap, tmp_str)) {
error("\"%s\" is not a valid node count", arg);
FREE_NULL_BITMAP(job_size_bitmap);
xfree(tmp_str);
return false;
}
*min_nodes = bit_ffs(job_size_bitmap);
*max_nodes = bit_fls(job_size_bitmap);
if (job_size_str)
*job_size_str = bit_fmt_full(job_size_bitmap);
FREE_NULL_BITMAP(job_size_bitmap);
xfree(tmp_str);
} else 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) too large 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) too large 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);
}
*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, "INVALID_DEPEND") == 0)
rc |= MAIL_INVALID_DEPEND;
else if (xstrcasecmp(tok, "REQUEUE") == 0)
rc |= MAIL_JOB_REQUEUE;
else if (xstrcasecmp(tok, "ALL") == 0)
rc |= MAIL_INVALID_DEPEND | 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_INVALID_DEPEND) {
if (buf[0])
strcat(buf, ",");
strcat(buf, "INVALID_DEPEND");
}
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_t *_create_path_list(void)
{
list_t *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_t *l = NULL;
list_itr_t *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=<int>[@<time>]" into
* it's warn_signal and warn_time components.
* RET 0 on success, -1 on failure */
int get_signal_opts(char *optarg, uint16_t *warn_signal, uint16_t *warn_time,
uint16_t *warn_flags)
{
char *endptr;
long num;
if (optarg == NULL)
return -1;
if (!xstrncasecmp(optarg, "R", 1)) {
*warn_flags |= KILL_JOB_RESV;
optarg++;
}
if (!xstrncasecmp(optarg, "B", 1)) {
*warn_flags |= KILL_JOB_BATCH;
optarg++;
}
/* easiest way to handle BR and RB */
if (!xstrncasecmp(optarg, "R", 1)) {
*warn_flags |= KILL_JOB_RESV;
optarg++;
}
if (*optarg == ':')
optarg++;
endptr = strchr(optarg, '@');
if (endptr)
endptr[0] = '\0';
num = (uint16_t) sig_name2num(optarg);
if (endptr)
endptr[0] = '@';
if ((num < 1) || (num > 0x0ffff))
return -1;
*warn_signal = (uint16_t) num;
if (!endptr) {
*warn_time = 60;
return 0;
}
num = strtol(endptr+1, &endptr, 10);
if ((num < 0) || (num > 0x0ffff))
return -1;
*warn_time = (uint16_t) num;
if (endptr[0] == '\0')
return 0;
return -1;
}
extern char *signal_opts_to_cmdline(uint16_t warn_signal, uint16_t warn_time,
uint16_t warn_flags)
{
char *cmdline = NULL, *sig_name;
if (warn_flags & KILL_JOB_RESV)
xstrcat(cmdline, "R");
if (warn_flags & KILL_JOB_BATCH)
xstrcat(cmdline, "B");
if ((warn_flags & KILL_JOB_RESV) || (warn_flags & KILL_JOB_BATCH))
xstrcat(cmdline, ":");
sig_name = sig_num2name(warn_signal);
xstrcat(cmdline, sig_name);
xfree(sig_name);
if (warn_time != 60) /* default value above, don't print */
xstrfmtcat(cmdline, "@%u", warn_time);
return cmdline;
}
static struct {
char *name;
uint16_t val;
} signals_mapping[] = {
{ "HUP", SIGHUP },
{ "INT", SIGINT },
{ "QUIT", SIGQUIT },
{ "ABRT", SIGABRT },
{ "KILL", SIGKILL },
{ "ALRM", SIGALRM },
{ "TERM", SIGTERM },
{ "CHLD", SIGCHLD },
{ "USR1", SIGUSR1 },
{ "USR2", SIGUSR2 },
{ "PIPE", SIGPIPE },
{ "URG", SIGURG },
{ "CONT", SIGCONT },
{ "STOP", SIGSTOP },
{ "TSTP", SIGTSTP },
{ "TTIN", SIGTTIN },
{ "TTOU", SIGTTOU },
{ "XCPU", SIGXCPU },
{ NULL, 0 } /* terminate array */
};
/* Convert a signal name to it's numeric equivalent.
* Return 0 on failure */
int sig_name2num(const char *signal_name)
{
char *ptr;
long tmp;
int i;
tmp = strtol(signal_name, &ptr, 10);
if (ptr != signal_name) { /* found a number */
if (xstring_is_whitespace(ptr))
return (int)tmp;
else
return 0;
}
/* search the array */
ptr = (char *) signal_name;
while (isspace((int)*ptr))
ptr++;
if (xstrncasecmp(ptr, "SIG", 3) == 0)
ptr += 3;
for (i = 0; ; i++) {
int siglen;
if (signals_mapping[i].name == NULL)
return 0;
siglen = strlen(signals_mapping[i].name);
if ((!xstrncasecmp(ptr, signals_mapping[i].name, siglen)
&& xstring_is_whitespace(ptr + siglen))) {
/* found the signal name */
return signals_mapping[i].val;
}
}
return 0; /* not found */
}
extern char *sig_num2name(int signal)
{
for (int i = 0; signals_mapping[i].name; i++) {
if (signal == signals_mapping[i].val)
return xstrdup(signals_mapping[i].name);
}
return xstrdup_printf("%d", signal);
}
/*
* parse_uint16 - Convert ascii string to a 16 bit unsigned int.
* IN aval - ascii string.
* IN/OUT ival - 16 bit pointer.
* RET 0 if no error, 1 otherwise.
*/
extern int parse_uint16(char *aval, uint16_t *ival)
{
/*
* First, convert the ascii value it to a
* long long int. If the result is greater then
* or equal to 0 and less than NO_VAL16
* set the value and return. Otherwise
* return an error.
*/
uint16_t max16uint = NO_VAL16;
long long tval;
char *p;
/*
* Return error for invalid value.
*/
tval = strtoll(aval, &p, 10);
if (p[0] || (tval == LLONG_MIN) || (tval == LLONG_MAX) ||
(tval < 0) || (tval >= max16uint))
return 1;
*ival = (uint16_t) tval;
return 0;
}
/*
* parse_uint32 - Convert ascii string to a 32 bit unsigned int.
* IN aval - ascii string.
* IN/OUT ival - 32 bit pointer.
* RET 0 if no error, 1 otherwise.
*/
extern int parse_uint32(char *aval, uint32_t *ival)
{
/*
* First, convert the ascii value it to a
* long long int. If the result is greater
* than or equal to 0 and less than NO_VAL
* set the value and return. Otherwise return
* an error.
*/
uint32_t max32uint = NO_VAL;
long long tval;
char *p;
/*
* Return error for invalid value.
*/
tval = strtoll(aval, &p, 10);
if (p[0] || (tval == LLONG_MIN) || (tval == LLONG_MAX) ||
(tval < 0) || (tval >= max32uint))
return 1;
*ival = (uint32_t) tval;
return 0;
}
/*
* parse_uint64 - Convert ascii string to a 64 bit unsigned int.
* IN aval - ascii string.
* IN/OUT ival - 64 bit pointer.
* RET 0 if no error, 1 otherwise.
*/
extern int parse_uint64(char *aval, uint64_t *ival)
{
/*
* First, convert the ascii value it to an
* unsigned long long. If the result is greater
* than or equal to 0 and less than NO_VAL
* set the value and return. Otherwise return
* an error.
*/
uint64_t max64uint = NO_VAL64;
long long tval;
char *p;
/*
* Return error for invalid value.
*/
tval = strtoll(aval, &p, 10);
if (p[0] || (tval == LLONG_MIN) || (tval == LLONG_MAX) ||
(tval < 0) || (tval >= max64uint))
return 1;
*ival = (uint64_t) tval;
return 0;
}
/*
* Get a decimal integer from arg.
*
* Returns the integer on success, exits program on failure.
*/
extern int parse_int(const char *name, const char *val, bool positive)
{
char *p = NULL;
long int result = 0;
if (val)
result = strtol(val, &p, 10);
if ((p == NULL) || (p[0] != '\0') || (result < 0L) ||
(positive && (result <= 0L))) {
error ("Invalid numeric value \"%s\" for %s.", val, name);
exit(1);
} else if (result >= INT_MAX) {
error("Numeric argument (%ld) too large for %s.", result, name);
exit(1);
} else if (result <= INT_MIN) {
error ("Numeric argument (%ld) to small for %s.", result, name);
exit(1);
}
return (int) result;
}
/* print_db_notok() - Print an error message about slurmdbd
* is unreachable or wrong cluster name.
* IN cname - char * cluster name
* IN isenv - bool cluster name from env or from command line option.
*/
void print_db_notok(const char *cname, bool isenv)
{
if (errno)
error("There is a problem talking to the database: %m. "
"Only local cluster communication is available, remove "
"%s or contact your admin to resolve the problem.",
isenv ? "SLURM_CLUSTERS from your environment" :
"--cluster from your command line");
else if (!xstrcasecmp("all", cname))
error("No clusters can be reached now. "
"Contact your admin to resolve the problem.");
else
error("'%s' can't be reached now, "
"or it is an invalid entry for %s. "
"Use 'sacctmgr list clusters' to see available clusters.",
cname, isenv ? "SLURM_CLUSTERS" : "--cluster");
}
/*
* parse_resv_flags() used to parse the Flags= option. It handles
* daily, weekly, static_alloc, part_nodes, and maint, optionally
* preceded by + or -, separated by a comma but no spaces.
*
* flagstr IN - reservation flag string
* msg IN - string to append to error message (e.g. function name)
* resv_msg_ptr IN/OUT - sets flags and times in ptr.
* RET equivalent reservation flag bits
*/
extern uint64_t parse_resv_flags(const char *flagstr, const char *msg,
resv_desc_msg_t *resv_msg_ptr)
{
int op = RESV_NEW;
uint64_t outflags = 0;
char *curr = xstrdup(flagstr), *start = curr;
int taglen = 0;
while (*curr != '\0') {
if (*curr == '+') {
op = RESV_ADD;
curr++;
} else if (*curr == '-') {
op = RESV_REM;
curr++;
}
taglen = 0;
while (curr[taglen] != ',' && curr[taglen] != '\0'
&& curr[taglen] != '=')
taglen++;
if (xstrncasecmp(curr, "Maintenance", MAX(taglen,3)) == 0) {
curr += taglen;
if (op == RESV_REM)
outflags |= RESERVE_FLAG_NO_MAINT;
else
outflags |= RESERVE_FLAG_MAINT;
} else if ((xstrncasecmp(curr, "Overlap", MAX(taglen,1))
== 0) && (op != RESV_REM)) {
curr += taglen;
outflags |= RESERVE_FLAG_OVERLAP;
/*
* "-OVERLAP" is not supported since that's the
* default behavior and the option only applies
* for reservation creation, not updates
*/
} else if (xstrncasecmp(curr, "Flex", MAX(taglen,1)) == 0) {
curr += taglen;
if (op == RESV_REM)
outflags |= RESERVE_FLAG_NO_FLEX;
else
outflags |= RESERVE_FLAG_FLEX;
} else if (xstrncasecmp(curr, "Ignore_Jobs", MAX(taglen,1))
== 0) {
curr += taglen;
if (op == RESV_REM)
outflags |= RESERVE_FLAG_NO_IGN_JOB;
else
outflags |= RESERVE_FLAG_IGN_JOBS;
} else if (xstrncasecmp(curr, "Hourly", MAX(taglen, 1)) == 0) {
curr += taglen;
if (op == RESV_REM)
outflags |= RESERVE_FLAG_NO_HOURLY;
else
outflags |= RESERVE_FLAG_HOURLY;
} else if (xstrncasecmp(curr, "Daily", MAX(taglen,1)) == 0) {
curr += taglen;
if (op == RESV_REM)
outflags |= RESERVE_FLAG_NO_DAILY;
else
outflags |= RESERVE_FLAG_DAILY;
} else if (xstrncasecmp(curr, "Weekday", MAX(taglen,1)) == 0) {
curr += taglen;
if (op == RESV_REM)
outflags |= RESERVE_FLAG_NO_WEEKDAY;
else
outflags |= RESERVE_FLAG_WEEKDAY;
} else if (xstrncasecmp(curr, "Weekend", MAX(taglen,1)) == 0) {
curr += taglen;
if (op == RESV_REM)
outflags |= RESERVE_FLAG_NO_WEEKEND;
else
outflags |= RESERVE_FLAG_WEEKEND;
} else if (xstrncasecmp(curr, "Weekly", MAX(taglen,1)) == 0) {
curr += taglen;
if (op == RESV_REM)
outflags |= RESERVE_FLAG_NO_WEEKLY;
else
outflags |= RESERVE_FLAG_WEEKLY;
} else if (!xstrncasecmp(curr, "Any_Nodes", MAX(taglen,1)) ||
!xstrncasecmp(curr, "License_Only", MAX(taglen,1))) {
curr += taglen;
if (op == RESV_REM)
outflags |= RESERVE_FLAG_NO_ANY_NODES;
else
outflags |= RESERVE_FLAG_ANY_NODES;
} else if (xstrncasecmp(curr, "Static_Alloc", MAX(taglen,1))
== 0) {
curr += taglen;
if (op == RESV_REM)
outflags |= RESERVE_FLAG_NO_STATIC;
else
outflags |= RESERVE_FLAG_STATIC;
} else if (xstrncasecmp(curr, "Part_Nodes", MAX(taglen, 2))
== 0) {
curr += taglen;
if (op == RESV_REM)
outflags |= RESERVE_FLAG_NO_PART_NODES;
else
outflags |= RESERVE_FLAG_PART_NODES;
} else if (!xstrncasecmp(curr, "magnetic", MAX(taglen, 3)) ||
!xstrncasecmp(curr, "promiscuous", MAX(taglen, 2))) {
curr += taglen;
if (op == RESV_REM)
outflags |= RESERVE_FLAG_NO_MAGNETIC;
else
outflags |= RESERVE_FLAG_MAGNETIC;
} else if (!xstrncasecmp(curr, "PURGE_COMP", MAX(taglen, 2))) {
if (curr[taglen] == '=') {
int num_end;
taglen++;
num_end = taglen;
while (curr[num_end] != ',' &&
curr[num_end] != '\0')
num_end++;
if (curr[num_end] == ',') {
curr[num_end] = '\0';
num_end++;
}
if (resv_msg_ptr)
resv_msg_ptr->purge_comp_time =
time_str2secs(curr+taglen);
taglen = num_end;
}
curr += taglen;
if (op == RESV_REM)
outflags |= RESERVE_FLAG_NO_PURGE_COMP;
else
outflags |= RESERVE_FLAG_PURGE_COMP;
} else if (!xstrncasecmp(curr, "Time_Float", MAX(taglen,1)) &&
op == RESV_NEW) {
curr += taglen;
outflags |= RESERVE_FLAG_TIME_FLOAT;
} else if (!xstrncasecmp(curr, "Replace", MAX(taglen, 1)) &&
op != RESV_REM) {
curr += taglen;
outflags |= RESERVE_FLAG_REPLACE;
} else if (!xstrncasecmp(curr, "Replace_Down", MAX(taglen, 8))
&& op != RESV_REM) {
curr += taglen;
outflags |= RESERVE_FLAG_REPLACE_DOWN;
} else if (!xstrncasecmp(curr, "NO_HOLD_JOBS_AFTER_END",
MAX(taglen, 1)) && op != RESV_REM) {
curr += taglen;
outflags |= RESERVE_FLAG_NO_HOLD_JOBS;
} else if (!xstrncasecmp(curr, "User_Delete", MAX(taglen, 1))) {
curr += taglen;
if (op == RESV_REM)
outflags |= RESERVE_FLAG_NO_USER_DEL;
else
outflags |= RESERVE_FLAG_USER_DEL;
} else if (!xstrncasecmp(curr, "Force_Start", MAX(taglen, 1)) &&
op == RESV_NEW) {
curr += taglen;
outflags |= RESERVE_FLAG_FORCE_START;
} else {
error("Error parsing flags %s. %s", flagstr, msg);
return INFINITE64;
}
if (*curr == ',') {
curr++;
}
}
if (resv_msg_ptr && (outflags != INFINITE64)) {
if (resv_msg_ptr->flags == NO_VAL64)
resv_msg_ptr->flags = outflags;
else
resv_msg_ptr->flags |= outflags;
}
xfree(start);
return outflags;
}
/* parse --compress for a compression type, set to default type if not found */
uint16_t parse_compress_type(const char *arg)
{
/* if called with null string return default compression type */
if (!arg) {
#if HAVE_LZ4
return COMPRESS_LZ4;
#else
error("No compression library available, compression disabled.");
return COMPRESS_OFF;
#endif
}
if (!strcasecmp(arg, "lz4"))
return COMPRESS_LZ4;
else if (!strcasecmp(arg, "none"))
return COMPRESS_OFF;
error("Compression type '%s' unknown, disabling compression support.",
arg);
return COMPRESS_OFF;
}
/*
* IN: option argument value to interpret.
* RET: 1 if enabled, 0 if disabled, -1 if error
*/
int parse_send_libs(const char *arg)
{
if (!arg || !xstrcasecmp(arg, "yes") || !xstrcasecmp(arg, "y"))
return 1;
if (!xstrcasecmp(arg, "no") || !xstrcasecmp(arg, "n"))
return 0;
return -1;
}
/*
* IN: char pointer to path1
* IN: char pointer to path2
*
* RET: true if path2 is a subpath of path1; false otherwise
*
* Examples:
*
* path1 path2 ret
* ---------------------------------
* NULL NULL true
* NULL /foo false
* /foo NULL true
* /foo/bar /foo true
* /foo/bar /bar false
* /foo/bar /foo/b false
* /foo /foo/bar false
* /foo /foo/ true
*/
extern bool subpath(char *path1, char *path2)
{
bool ret = true;
char *p1 = NULL, *p2 = NULL;
char *tok1 = NULL, *tok2 = NULL;
char *save_ptr1 = NULL, *save_ptr2 = NULL;
if (!path2)
return true;
else if (!path1)
return false;
/* Both non-NULL. */
p1 = xstrdup(path1);
p2 = xstrdup(path2);
tok1 = strtok_r(p1, "/", &save_ptr1);
tok2 = strtok_r(p2, "/", &save_ptr2);
while (tok1 && tok2) {
if (xstrcmp(tok1, tok2)) {
ret = false;
break;
}
tok1 = strtok_r(NULL, "/", &save_ptr1);
tok2 = strtok_r(NULL, "/", &save_ptr2);
}
if (tok2 && !tok1)
ret = false;
xfree(p1);
xfree(p2);
return ret;
}
extern int validate_acctg_freq(char *acctg_freq)
{
int i;
char *save_ptr = NULL, *tok, *tmp;
bool valid;
int rc = SLURM_SUCCESS;
if (!acctg_freq)
return rc;
tmp = xstrdup(acctg_freq);
tok = strtok_r(tmp, ",", &save_ptr);
while (tok) {
valid = false;
for (i = 0; i < PROFILE_CNT; i++)
if (acct_gather_parse_freq(i, tok) != -1) {
valid = true;
break;
}
if (!valid) {
error("Invalid --acctg-freq specification: %s", tok);
rc = SLURM_ERROR;
}
tok = strtok_r(NULL, ",", &save_ptr);
}
xfree(tmp);
return rc;
}
/*
* Format a tres_per_* argument
* dest OUT - resulting string
* prefix IN - TRES type (e.g. "gres/gpu")
* src IN - user input, can include multiple comma-separated specifications
*/
extern void xfmt_tres(char **dest, char *prefix, char *src)
{
char *result = NULL, *save_ptr = NULL, *sep = "", *tmp, *tok;
if (!src || (src[0] == '\0'))
return;
if (*dest) {
result = xstrdup(*dest);
sep = ",";
}
tmp = xstrdup(src);
tok = strtok_r(tmp, ",", &save_ptr);
while (tok) {
xstrfmtcat(result, "%s%s:%s", sep, prefix, tok);
sep = ",";
tok = strtok_r(NULL, ",", &save_ptr);
}
xfree(tmp);
*dest = result;
}
/*
* Format a tres_freq argument
* dest OUT - resulting string
* prefix IN - TRES type (e.g. "gres/gpu")
* src IN - user input
*/
extern void xfmt_tres_freq(char **dest, char *prefix, char *src)
{
char *result = NULL, *sep = "";
if (!src || (src[0] == '\0'))
return;
if (*dest) {
result = xstrdup(*dest);
sep = ";";
}
xstrfmtcat(result, "%s%s:%s", sep, prefix, src);
*dest = result;
}
extern bool valid_runtime_directory(char *runtime_dir)
{
/*
* systemd >= v240 prepends "/run" to generate RUNTIME_DIRECTORY
* environment variable off of RuntimeDirectory unit option.
*
* Example:
* RuntimeDirectory=foo/bar results in RUNTIME_DIRECTORY=/run/foo/bar.
*/
if (xstrncmp(runtime_dir, "/run/", 5) || (strlen(runtime_dir) < 6))
return false;
return true;
}