blob: a51d5ee5dd9c5a6fa4ecab371ce3ece1cf8749c9 [file] [log] [blame]
/*****************************************************************************\
** client.c - PMI2 client wire protocol message handling
*****************************************************************************
* Copyright (C) 2011-2012 National University of Defense Technology.
* Written by Hongjia Cao <hjcao@nudt.edu.cn>.
* 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 <stdlib.h>
#include "src/common/slurm_xlator.h"
#include "src/common/xmalloc.h"
#include "src/common/xstring.h"
#include "client.h"
#include "setup.h"
#include "pmi.h"
#define KEY_INDEX(i) (i * 2)
#define VAL_INDEX(i) (i * 2 + 1)
#define MP_KEY(msg, index) (msg->pairs[KEY_INDEX(index)])
#define MP_VAL(req, index) (req->pairs[VAL_INDEX(index)])
#define REQ_PAIR_SIZE_INC 32
static int pmi_version = 0;
static int pmi_subversion = 0;
extern int
is_pmi11(void)
{
return (pmi_version == PMI11_VERSION &&
pmi_subversion == PMI11_SUBVERSION);
}
extern int
is_pmi20(void)
{
return (pmi_version == PMI20_VERSION &&
pmi_subversion == PMI20_SUBVERSION);
}
extern int
get_pmi_version(int *version, int *subversion)
{
if (pmi_version) {
*version = pmi_version;
*subversion = pmi_subversion;
return SLURM_SUCCESS;
} else
return SLURM_ERROR;
}
extern int
set_pmi_version(int version, int subversion)
{
if ( (version == PMI11_VERSION && subversion == PMI11_SUBVERSION) ||
(version == PMI20_VERSION && subversion == PMI20_SUBVERSION) ) {
if (pmi_version && (pmi_version != version ||
pmi_subversion != subversion)) {
error("mpi/pmi2: inconsistent client PMI version: "
"%d.%d(req) <> %d.%d(orig)", version, subversion,
pmi_version, pmi_subversion);
return SLURM_ERROR;
} else if (! pmi_version) {
verbose("mpi/pmi2: got client PMI1 init, version=%d.%d",
version, subversion);
pmi_version = version;
pmi_subversion = subversion;
}
} else {
error("mpi/pmi2: unsupported PMI version: %d.%d", version,
subversion);
return SLURM_ERROR;
}
return SLURM_SUCCESS;
}
static int
_parse_cmd(client_req_t *req)
{
int i = 0, len = 0;
len = strlen (MCMD_KEY"=");
if (! xstrncmp(req->buf, MCMD_KEY"=", len)) {
req->cmd = MCMD_KEY; /* XXX: mcmd=spawn */
req->sep = '\n';
req->term = '\n';
return SLURM_SUCCESS;
}
len = strlen (CMD_KEY"=");
if (xstrncmp(req->buf, CMD_KEY"=", len)) {
error("mpi/pmi2: request not begin with '" CMD_KEY "='");
error("mpi/pmi2: full request is: %s", req->buf);
return SLURM_ERROR;
}
req->cmd = &req->buf[len];
i = len;
if (is_pmi11()) {
req->sep = ' ';
req->term = '\n';
while (req->buf[i] != req->sep &&
req->buf[i] != req->term &&
i < req->buf_len) {
i ++;
}
} else if (is_pmi20()) {
req->sep = ';';
req->term = ';';
while (req->buf[i] != req->sep &&
req->buf[i] != req->term &&
i < req->buf_len) {
i ++;
}
}
if (i >= req->buf_len) {
error ("mpi/pmi2: cmd not properly terminated in client request");
return SLURM_ERROR;
}
req->buf[i] = '\0'; /* make it nul terminated */
req->parse_idx = i + 1;
/* TODO: concat processing */
return SLURM_SUCCESS;
}
extern client_req_t *
client_req_init(uint32_t len, char *buf)
{
client_req_t *req = NULL;
/* buf always '\0' terminated */
req = xmalloc(sizeof(client_req_t));
req->buf = buf;
req->buf_len = len;
req->parse_idx = 0;
if (_parse_cmd(req) != SLURM_SUCCESS) {
xfree(req);
}
return req;
}
extern void
client_req_free(client_req_t *req)
{
if (req) {
xfree(req->buf);
xfree(req->pairs);
xfree(req);
}
}
/*
* No escape of ';' supported for now, hence no ';' in value.
* TODO: concat command processing
*/
extern int
client_req_parse_body(client_req_t *req)
{
int i = 0, rc = SLURM_SUCCESS;
char *key, *val;
/* skip cmd */
i = req->parse_idx;
while (i < req->buf_len) {
/* search for key */
key = &req->buf[i];
while (req->buf[i] != '=' && i < req->buf_len) {
i ++;
}
if (i >= req->buf_len) {
error("mpi/pmi2: no value for key %s in req", key);
rc = SLURM_ERROR;
break;
}
req->buf[i] = '\0'; /* make it nul terminated */
i ++;
debug3("mpi/pmi2: client req key %s", key);
/* search for val */
val = &req->buf[i];
while (req->buf[i] != req->sep &&
req->buf[i] != req->term &&
i < req->buf_len) {
i ++;
}
if (i >= req->buf_len) {
error("mpi/pmi2: value not properly terminated in "
"client request");
rc = SLURM_ERROR;
break;
}
req->buf[i] = '\0'; /* make it nul terminated */
i ++;
debug3("mpi/pmi2: client req val %s", val);
/*
* append pair.
* there may be duplicate keys in the pairs, such as in the
* spawn cmd. Hence the order of the pairs is of significance.
*/
if (2 * (req->pairs_cnt + 2) > req->pairs_size) {
req->pairs_size += REQ_PAIR_SIZE_INC;
xrealloc(req->pairs, req->pairs_size * sizeof(char *));
}
req->pairs[KEY_INDEX(req->pairs_cnt)] = key;
req->pairs[VAL_INDEX(req->pairs_cnt)] = val;
req->pairs_cnt ++;
}
/* add a pair of NULL at the end, without increasing req->pairs_cnt */
req->pairs[KEY_INDEX(req->pairs_cnt)] = NULL;
req->pairs[VAL_INDEX(req->pairs_cnt)] = NULL;
return rc;
}
extern spawn_req_t *
client_req_parse_spawn_req(client_req_t *req)
{
spawn_req_t *spawn_req = NULL;
spawn_subcmd_t *subcmd = NULL;
int i = 0, j = 0, pi = 0;
/* req body already parsed */
pi = 0;
if (req->pairs_cnt - pi < 5) {
/* NCMDS, PREPUTCOUNT, SUBCMD, MAXPROCS, ARGC */
error("mpi/pmi2: wrong number of key-val pairs in spawn cmd");
return NULL;
}
spawn_req = spawn_req_new();
/* ncmds */
if (xstrcmp(MP_KEY(req, pi), NCMDS_KEY)) {
error("mpi/pmi2: '" NCMDS_KEY "' expected in spawn cmd");
goto req_err;
}
spawn_req->subcmd_cnt = atoi(MP_VAL(req, pi));
spawn_req->subcmds = xcalloc(spawn_req->subcmd_cnt,
sizeof(spawn_subcmd_t *));
pi ++;
/* preputcount */
if (xstrcmp(MP_KEY(req, pi), PREPUTCOUNT_KEY)) {
error("mpi/pmi2: '" PREPUTCOUNT_KEY "' expected in spawn cmd");
goto req_err;
}
spawn_req->preput_cnt = atoi(MP_VAL(req, pi));
pi ++;
if (req->pairs_cnt - pi <
( (2 * spawn_req->preput_cnt) + (3 * spawn_req->subcmd_cnt) )) {
/* <PPKEY, PPVAL>, <SUBCMD, MAXPROCS, ARGC> */
error("mpi/pmi2: wrong number of key-val pairs in spawn cmd");
goto req_err;
}
spawn_req->pp_keys = xcalloc(spawn_req->preput_cnt, sizeof(char *));
spawn_req->pp_vals = xcalloc(spawn_req->preput_cnt, sizeof(char *));
/* ppkey,ppval */
for (i = 0; i < spawn_req->preput_cnt; i ++) {
/* ppkey */
if (xstrncmp(MP_KEY(req, pi), PPKEY_KEY, strlen(PPKEY_KEY)) ||
atoi((MP_KEY(req, pi) + strlen(PPKEY_KEY))) != i) {
error("mpi/pmi2: '" PPKEY_KEY
"%d' expected in spawn cmd", i);
goto req_err;
}
spawn_req->pp_keys[i] = xstrdup(MP_VAL(req, pi));
pi ++;
/* ppval */
if (xstrncmp(MP_KEY(req, pi), PPVAL_KEY, strlen(PPVAL_KEY)) ||
atoi((MP_KEY(req, pi) + strlen(PPVAL_KEY))) != i) {
error("mpi/pmi2: '" PPVAL_KEY
"%d' expected in spawn cmd", i);
goto req_err;
}
spawn_req->pp_vals[i] = xstrdup(MP_VAL(req, pi));
pi ++;
}
/* subcmd */
for (i = 0; i < spawn_req->subcmd_cnt; i ++) {
spawn_req->subcmds[i] = spawn_subcmd_new();
subcmd = spawn_req->subcmds[i];
/* subcmd */
if (xstrcmp(MP_KEY(req, pi), SUBCMD_KEY)) {
error("mpi/pmi2: '" SUBCMD_KEY
"' expected in spawn cmd");
goto req_err;
}
subcmd->cmd = xstrdup(MP_VAL(req, pi));
pi ++;
/* maxprocs */
if (xstrcmp(MP_KEY(req, pi), MAXPROCS_KEY)) {
error("mpi/pmi2: '" MAXPROCS_KEY
"' expected in spawn cmd");
goto req_err;
}
subcmd->max_procs = atoi(MP_VAL(req, pi));
pi ++;
/* argc */
if (xstrcmp(MP_KEY(req, pi), ARGC_KEY)) {
error("mpi/pmi2: '" ARGC_KEY
"' expected in spawn cmd");
goto req_err;
}
subcmd->argc = atoi(MP_VAL(req, pi));
pi ++;
if (req->pairs_cnt - pi <
( subcmd->argc + (3 * (spawn_req->subcmd_cnt - i - 1))) ) {
/* <ARGV>, <SUBCMD, MAXPROCS, ARGC> */
error("mpi/pmi2: wrong number of key-val pairs"
" in spawn cmd");
goto req_err;
}
debug("mpi/pmi2: argc = %d", subcmd->argc);
if (subcmd->argc > 0) {
subcmd->argv = xcalloc(subcmd->argc, sizeof(char *));
}
/* argv */
for (j = 0; j < subcmd->argc; j ++) {
if (xstrncmp(MP_KEY(req, pi), ARGV_KEY,
strlen(ARGV_KEY)) ||
atoi((MP_KEY(req, pi) + strlen(ARGV_KEY))) != j) {
error("mpi/pmi2: '" ARGV_KEY
"%d' expected in spawn cmd", j);
goto req_err;
}
subcmd->argv[j] = xstrdup(MP_VAL(req, pi));
pi ++;
}
debug("mpi/pmi2: got argv");
/* infokeycount, optional */
if (pi == req->pairs_cnt) {
if (i != spawn_req->subcmd_cnt - 1) {
error("mpi/pmi2: wrong number of key-val pairs"
"in spawn cmd");
goto req_err;
}
break;
} else if (xstrcmp(MP_KEY(req, pi), INFOKEYCOUNT_KEY)) {
subcmd->info_cnt = 0;
continue;
}
subcmd->info_cnt = atoi(MP_VAL(req, pi));
pi ++;
if (req->pairs_cnt - pi <
( (2 * subcmd->info_cnt) +
(3 * (spawn_req->subcmd_cnt - i - 1)) )) {
/* <INFOKEY, INFOVAL>, <SUBCMD, MAXPROCS, ARGC> */
error("mpi/pmi2: wrong number of key-val pairs"
" in spawn cmd");
goto req_err;
}
if (subcmd->info_cnt > 0) {
subcmd->info_keys = xcalloc(subcmd->info_cnt,
sizeof(char *));
subcmd->info_vals = xcalloc(subcmd->info_cnt,
sizeof(char *));
}
/* infokey,infoval */
for (j = 0; j < subcmd->info_cnt; j ++) {
/* infokey */
if (xstrncmp(MP_KEY(req, pi), INFOKEY_KEY,
strlen(INFOKEY_KEY)) ||
atoi((MP_KEY(req, pi) +
strlen(INFOKEY_KEY))) != j) {
error("mpi/pmi2: '" INFOKEY_KEY
"%d' expected in spawn cmd", j);
goto req_err;
}
subcmd->info_keys[j] = xstrdup(MP_VAL(req, pi));
pi ++;
/* infoval */
if (xstrncmp(MP_KEY(req, pi), INFOVAL_KEY,
strlen(INFOVAL_KEY)) ||
atoi((MP_KEY(req, pi) +
strlen(INFOVAL_KEY))) != j) {
error("mpi/pmi2: '" INFOVAL_KEY
"%d' expected in spawn cmd", j);
goto req_err;
}
subcmd->info_vals[j] = xstrdup(MP_VAL(req, pi));
pi ++;
}
}
debug("mpi/pmi2: out client_req_parse_spawn");
return spawn_req;
req_err:
spawn_req_free(spawn_req);
return NULL;
}
extern spawn_subcmd_t *
client_req_parse_spawn_subcmd(client_req_t *req)
{
spawn_subcmd_t *subcmd = NULL;
char buf[PMI2_MAX_KEYLEN];
int i = 0;
subcmd = xmalloc(sizeof(spawn_subcmd_t));
client_req_get_str(req, EXECNAME_KEY, &subcmd->cmd);
client_req_get_int(req, NPROCS_KEY, (int *)&subcmd->max_procs);
client_req_get_int(req, ARGCNT_KEY, (int *)&subcmd->argc);
subcmd->argv = xcalloc(subcmd->argc, sizeof(char *));
for (i = 0; i < subcmd->argc; i ++) {
snprintf(buf, PMI2_MAX_KEYLEN, "arg%d", i + 1);
client_req_get_str(req, buf, &(subcmd->argv[i]));
}
client_req_get_int(req, INFONUM_KEY, (int *)&subcmd->info_cnt);
subcmd->info_keys = xcalloc(subcmd->info_cnt, sizeof(char *));
subcmd->info_vals = xcalloc(subcmd->info_cnt, sizeof(char *));
for (i = 0; i < subcmd->info_cnt; i ++) {
snprintf(buf, PMI2_MAX_KEYLEN, "info_key_%d", i);
client_req_get_str(req, buf, &(subcmd->info_keys[i]));
snprintf(buf, PMI2_MAX_KEYLEN, "info_val_%d", i);
client_req_get_str(req, buf, &(subcmd->info_vals[i]));
}
return subcmd;
}
/************************************************************************/
/* returned value not dup-ed */
static char *
_client_req_get_val(client_req_t *req, const char *key)
{
int i;
for (i = 0; i < req->pairs_cnt; i ++) {
if (! xstrcmp(key, req->pairs[KEY_INDEX(i)]))
return req->pairs[VAL_INDEX(i)];
}
return NULL;
}
/* return true if found */
extern bool
client_req_get_str(client_req_t *req, const char *key, char **pval)
{
char *val;
val = _client_req_get_val(req, key);
if (val == NULL)
return false;
*pval = xstrdup(val);
return true;
}
extern bool
client_req_get_int(client_req_t *req, const char *key, int *pval)
{
char *val;
val = _client_req_get_val(req, key);
if (val == NULL)
return false;
*pval = atoi(val);
return true;
}
extern bool
client_req_get_bool(client_req_t *req, const char *key, bool *pval)
{
char *val;
val = _client_req_get_val(req, key);
if (val == NULL)
return false;
if (!xstrcasecmp(val, TRUE_VAL))
*pval = true;
else
*pval = false;
return true;
}
/* ************************************************************ */
extern client_resp_t *client_resp_new(void)
{
return xmalloc(sizeof(client_resp_t));
}
extern int
client_resp_send(client_resp_t *resp, int fd)
{
char len_buf[7];
int len;
len = strlen(resp->buf);
if ( is_pmi20() ) {
/* catch truncation if response is too long */
if (snprintf(len_buf, 7, "%-6d", len) > 6)
goto rwfail;
debug2("mpi/pmi2: client_resp_send: %s%s", len_buf, resp->buf);
safe_write(fd, len_buf, 6);
} else if ( is_pmi11() ) {
debug2("mpi/pmi2: client_resp_send: %s", resp->buf);
}
safe_write(fd, resp->buf, len);
return SLURM_SUCCESS;
rwfail:
return SLURM_ERROR;
}
extern void
client_resp_free(client_resp_t *resp)
{
if (resp) {
xfree(resp->buf);
xfree(resp);
}
}
/* caller must free the result */
static char *
_str_replace(char *str, char src, char dst)
{
char *res, *ptr;
res = xstrdup(str);
ptr = res;
while (*ptr) {
if (*ptr == src)
*ptr = dst;
ptr ++;
}
return res;
}
/* send fence_resp/barrier_out to tasks */
extern int
send_kvs_fence_resp_to_clients(int rc, char *errmsg)
{
int i = 0;
client_resp_t *resp;
char *msg;
resp = client_resp_new();
if ( is_pmi11() ) {
if (rc != 0 && errmsg != NULL) {
// XXX: pmi1.1 does not check the rc
msg = _str_replace(errmsg, ' ', '_');
client_resp_append(resp, CMD_KEY"="BARRIEROUT_CMD" "
RC_KEY"=%d "MSG_KEY"=%s\n",
rc, msg);
xfree(msg);
} else {
client_resp_append(resp, CMD_KEY"="BARRIEROUT_CMD" "
RC_KEY"=%d\n", rc);
}
} else if (is_pmi20()) {
if (rc != 0 && errmsg != NULL) {
// TODO: pmi2.0 accept escaped ';' (";;")
msg = _str_replace(errmsg, ';', '_');
client_resp_append(resp, CMD_KEY"="KVSFENCERESP_CMD";"
RC_KEY"=%d;"ERRMSG_KEY"=%s;",
rc, msg);
xfree(msg);
} else {
client_resp_append(resp, CMD_KEY"="KVSFENCERESP_CMD";"
RC_KEY"=%d;", rc);
}
}
for (i = 0; i < job_info.ltasks; i ++) {
rc = client_resp_send(resp, STEPD_PMI_SOCK(i));
}
client_resp_free(resp);
return rc;
}