blob: 24e989be698a7cdb8628963e02d612666504b067 [file]
/*****************************************************************************\
* multi_prog.c - Find the argv array for each task when multi-prog is enabled.
*
* NOTE: This code could be moved into the API if desired. That would mean the
* logic would be executed once per step instead of once per task. This would
* require substantial modifications to the srun, slurmd, slurmstepd, and
* communications logic; so we'll stick with the simple solution for now.
*****************************************************************************
* Produced at National University of Defense Technology (China)
* Written by Hongjia Cao <hjcao@nudt.edu.cn>
* and
* Lawrence Livermore National Laboratory (cf, DISCLAIMER).
* Written by Morris Jette <jette1@llnl.gov>,
* CODE-OCEC-09-009. All rights reserved.
*
* This file is part of Slurm, a resource management program.
* For details, see <https://slurm.schedmd.com/>.
* Please also read the included file: DISCLAIMER.
*
* Slurm is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* In addition, as a special exception, the copyright holders give permission
* to link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two. You must obey the GNU
* General Public License in all respects for all of the code used other than
* OpenSSL. If you modify file(s) with this exception, you may extend this
* exception to your version of the file(s), but you are not obligated to do
* so. If you do not wish to do so, delete this exception statement from your
* version. If you delete this exception statement from all source files in
* the program, then also delete it here.
*
* Slurm is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along
* with Slurm; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\*****************************************************************************/
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "src/common/log.h"
#include "src/common/xassert.h"
#include "src/common/xmalloc.h"
#include "src/common/xstring.h"
#include "multi_prog.h"
#define MAX_ARGC 128
#define _DEBUG 0
/*
* Test if the specified rank is included in the supplied task range
* IN rank - this task's rank
* IN spec - a line from the configuration file
* OUT offset - the task's offset within rank range of the configuration file
* RET 1 if within range, 0 otherwise
*/
static int
_in_range(int rank, char* spec, int *offset)
{
char* range;
char* p;
char* upper;
int high_num, low_num, passed = 0;
xassert(offset);
if (spec[0] == '*' && spec[1] == '\0') {
*offset = rank;
return 1;
}
for (range = strtok (spec, ","); range != NULL;
range = strtok (NULL, ",")) {
p = range;
while (*p != '\0' && isdigit (*p))
p ++;
if (*p == '\0') { /* single rank */
if (rank == atoi (range)) {
*offset = passed;
return 1;
}
passed ++;
} else if (*p == '-') { /* lower-upper */
upper = ++ p;
while (isdigit (*p))
p ++;
if (*p != '\0') {
error ("Invalid task range specification (%s) "
"ignored.", range);
continue;
};
low_num = atoi (range);
high_num = atoi (upper);
if ((rank >= low_num) && (rank <= high_num)) {
*offset = passed + (rank - low_num);
return 1;
} else
passed += (1 + high_num - low_num);
} else {
error ("Invalid task range specification (%s) ignored.",
range);
}
}
return 0;
}
/*
* FIXME - It would be nice to parse the multi-prog array just once
* to retrieve the argv arrays for each task on this node, rather
* than calling multi_prog_get_argv once for each task.
*/
extern int multi_prog_get_argv(char *config_data, char **prog_env,
int task_rank, uint32_t *argc, char ***argv,
int global_argc, char **global_argv)
{
char *line = NULL;
int i, line_num = 0;
int task_offset;
char *p = NULL, *ptrptr = NULL;
char *rank_spec = NULL, *args_spec = NULL;
int prog_argc = 0;
char **prog_argv = NULL;
char *local_data = NULL;
char *arg_buf = NULL;
bool last_line_break = false, line_break = false;
int line_len;
prog_argv = xcalloc(MAX_ARGC, sizeof(char *));
if (task_rank < 0) {
error("Invalid task rank %d", task_rank);
*argc = 1;
*argv = prog_argv;
return -1;
}
local_data = xstrdup(config_data);
line = strtok_r(local_data, "\n", &ptrptr);
while (line) {
if (line_num > 0)
line = strtok_r(NULL, "\n", &ptrptr);
if (line == NULL) {
error("No executable program specified for this task");
goto fail;
}
line_num++;
line_len = strlen(line);
if ((line_len > 0) && (line[line_len - 1] == '\\'))
line_break = true;
else
line_break = false;
if (last_line_break) {
last_line_break = line_break;
continue;
}
last_line_break = line_break;
p = line;
while ((*p != '\0') && isspace (*p)) /* remove leading spaces */
p++;
if (*p == '#') /* only whole-line comments handled */
continue;
if (*p == '\0') /* blank line ignored */
continue;
rank_spec = p;
while ((*p != '\0') && !isspace (*p))
p++;
if (*p == '\0') {
error("Invalid MPMD configuration line %d", line_num);
goto fail;
}
*p++ = '\0';
if (!_in_range(task_rank, rank_spec, &task_offset))
continue;
/* skip all whitespace after the range spec */
while ((*p != '\0') && isspace (*p))
p++;
args_spec = p;
while (*args_spec != '\0') {
/* Only simple quote and escape supported */
if (arg_buf) {
prog_argv[prog_argc++] = arg_buf;
arg_buf=NULL;
}
if ((prog_argc + 1) >= MAX_ARGC) {
info("Exceeded multi-prog argc limit");
break;
}
CONT: p = args_spec;
while ((*args_spec != '\0') && (*args_spec != '\\') &&
(*args_spec != '%') && (*args_spec != '\'') &&
!isspace(*args_spec)) {
args_spec++;
}
xstrncat(arg_buf, p, (args_spec - p));
if (*args_spec == '\0') {
/* the last argument */
break;
} else if (*args_spec == '%') {
args_spec++;
if (*args_spec == 't') {
/* task rank */
xstrfmtcat(arg_buf, "%d", task_rank);
} else if (*args_spec == 'o') {
/* task offset */
xstrfmtcat(arg_buf, "%d", task_offset);
}
args_spec++;
goto CONT;
} else if (*args_spec == '\\') {
/* escape, just remove the backslash */
args_spec++;
if (*args_spec != '\0') {
xstrcatchar(arg_buf, *args_spec);
args_spec++;
} else {
line = strtok_r(NULL, "\n", &ptrptr);
if (!line)
break;
line_num++;
args_spec = line;
}
goto CONT;
} else if (*args_spec == '\'') {
/* single quote,
* preserve all characters quoted. */
p = ++args_spec;
LINE_BREAK: while ((*args_spec != '\0') &&
(*args_spec != '\'')) {
args_spec++;
}
if (*args_spec == '\0') {
/* closing quote not found */
if (*(args_spec - 1) == '\\') {
line = strtok_r(NULL, "\n",
&ptrptr);
if (line) {
line_num++;
args_spec = line;
goto LINE_BREAK;
}
}
error("Program arguments specification format invalid: %s.",
prog_argv[prog_argc - 1]);
goto fail;
}
xstrncat(arg_buf, p, (args_spec - p));
args_spec++;
goto CONT;
} else {
/* space */
while ((*args_spec != '\0') &&
isspace(*args_spec)) {
args_spec++;
}
}
}
if (arg_buf) {
prog_argv[prog_argc++] = arg_buf;
arg_buf = NULL;
}
for (i = 2; i < global_argc; i++) {
if ((prog_argc + 1) >= MAX_ARGC) {
info("Exceeded multi-prog argc limit");
break;
}
prog_argv[prog_argc++] = xstrdup(global_argv[i]);
}
prog_argv[prog_argc] = NULL;
*argc = prog_argc;
*argv = prog_argv;
xfree(local_data);
return 0;
}
error("Program for task rank %d not specified.", task_rank);
fail:
xfree(local_data);
*argc = 1;
prog_argv[0] = NULL;
*argv = prog_argv;
return -1;
}