#include "src/include/pmix_config.h"
#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#else
# ifdef HAVE_SYS_FCNTL_H
# include <sys/fcntl.h>
# endif
#endif
#include <ctype.h>
#include "src/include/pmix_socket_errno.h"
#include "src/include/pmix_stdint.h"
#include "include/pmix.h"
#include "pmix_common.h"
#include "include/pmix_server.h"
#include "src/mca/bfrops/bfrops.h"
#include "src/common/pmix_pfexec.h"
#include "src/mca/ptl/ptl.h"
#include "src/threads/pmix_threads.h"
#include "src/util/pmix_argv.h"
#include "src/util/pmix_basename.h"
#include "src/util/pmix_error.h"
#include "src/util/pmix_name_fns.h"
#include "src/util/pmix_output.h"
#include "src/util/pmix_os_dirpath.h"
#include "src/util/pmix_printf.h"
#include "src/client/pmix_client_ops.h"
#include "src/include/pmix_globals.h"
#include "src/server/pmix_server_ops.h"
static void msgcbfunc(struct pmix_peer_t *peer, pmix_ptl_hdr_t *hdr,
pmix_buffer_t *buf, void *cbdata)
{
pmix_shift_caddy_t *cd = (pmix_shift_caddy_t *) cbdata;
int32_t m;
pmix_status_t rc, status;
size_t refid = SIZE_MAX;
size_t localid = SIZE_MAX;
PMIX_HIDE_UNUSED_PARAMS(hdr);
PMIX_ACQUIRE_OBJECT(cd);
m = 1;
PMIX_BFROPS_UNPACK(rc, peer, buf, &status, &m, PMIX_STATUS);
if (PMIX_SUCCESS != rc) {
if (PMIX_ERR_UNPACK_READ_PAST_END_OF_BUFFER == rc) {
status = PMIX_SUCCESS;
}
else {
status = rc;
}
}
if (NULL != cd->iofreq) {
pmix_output_verbose(2, pmix_client_globals.iof_output,
"pmix:iof_register returned status %s", PMIx_Error_string(status));
if (PMIX_SUCCESS == status) {
m = 1;
PMIX_BFROPS_UNPACK(rc, peer, buf, &refid, &m, PMIX_SIZE);
if (PMIX_SUCCESS != rc) {
status = rc;
} else {
cd->iofreq->remote_id = refid;
localid = cd->iofreq->local_id;
}
}
if (NULL == cd->cbfunc.hdlrregcbfn) {
cd->status = status;
cd->iofreq->remote_id = refid;
PMIX_WAKEUP_THREAD(&cd->lock);
} else {
cd->cbfunc.hdlrregcbfn(status, localid, cd->cbdata);
}
return;
}
pmix_output_verbose(2, pmix_client_globals.iof_output, "pmix:iof_deregister returned status %s",
PMIx_Error_string(status));
if (NULL == cd->cbfunc.opcbfn) {
cd->status = status;
PMIX_WAKEUP_THREAD(&cd->lock);
} else {
cd->cbfunc.opcbfn(status, cd->cbdata);
}
PMIX_RELEASE(cd);
}
static void mycbfn(pmix_status_t status, size_t refid, void *cbdata)
{
pmix_shift_caddy_t *cd = (pmix_shift_caddy_t *) cbdata;
PMIX_ACQUIRE_OBJECT(cd);
if (PMIX_SUCCESS == status) {
cd->status = refid;
} else {
cd->status = status;
}
PMIX_WAKEUP_THREAD(&cd->lock);
}
static void process_cache(int sd, short args, void *cbdata)
{
pmix_iof_req_t *req = (pmix_iof_req_t *) cbdata;
pmix_iof_cache_t *iof, *ionext;
bool found;
size_t n;
pmix_status_t rc;
pmix_buffer_t *msg;
PMIX_HIDE_UNUSED_PARAMS(sd, args);
PMIX_LIST_FOREACH_SAFE (iof, ionext, &pmix_server_globals.iof, pmix_iof_cache_t) {
if (!(iof->channel & req->channels)) {
continue;
}
if (PMIX_CHECK_NAMES(&iof->source, &req->requestor->info->pname)) {
continue;
}
if (PMIX_CHECK_NAMES(&req->requestor->info->pname, &pmix_globals.myid)) {
continue;
}
found = false;
for (n = 0; n < req->nprocs; n++) {
if (PMIX_CHECK_PROCID(&iof->source, &req->procs[n])) {
found = true;
break;
}
}
if (found) {
if (NULL == (msg = PMIX_NEW(pmix_buffer_t))) {
PMIX_ERROR_LOG(PMIX_ERR_OUT_OF_RESOURCE);
return;
}
PMIX_BFROPS_PACK(rc, req->requestor, msg, &iof->source, 1, PMIX_PROC);
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
PMIX_RELEASE(msg);
return;
}
PMIX_BFROPS_PACK(rc, req->requestor, msg, &iof->channel, 1, PMIX_IOF_CHANNEL);
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
PMIX_RELEASE(msg);
return;
}
PMIX_BFROPS_PACK(rc, req->requestor, msg, &req->local_id, 1, PMIX_SIZE);
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
PMIX_RELEASE(msg);
return;
}
PMIX_BFROPS_PACK(rc, req->requestor, msg, &iof->ninfo, 1, PMIX_SIZE);
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
PMIX_RELEASE(msg);
return;
}
if (0 < iof->ninfo) {
PMIX_BFROPS_PACK(rc, req->requestor, msg, iof->info, iof->ninfo, PMIX_INFO);
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
PMIX_RELEASE(msg);
return;
}
}
PMIX_BFROPS_PACK(rc, req->requestor, msg, iof->bo, 1, PMIX_BYTE_OBJECT);
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
PMIX_RELEASE(msg);
return;
}
PMIX_PTL_SEND_ONEWAY(rc, req->requestor, msg, PMIX_PTL_TAG_IOF);
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
PMIX_RELEASE(msg);
}
}
}
}
static void myreg(int sd, short args, void *cbdata)
{
pmix_iof_req_t *req = (pmix_iof_req_t *) cbdata;
PMIX_HIDE_UNUSED_PARAMS(sd, args);
if (NULL != req->regcbfunc) {
req->regcbfunc(PMIX_SUCCESS, req->local_id, req->cbdata);
}
process_cache(0, 0, req);
}
PMIX_EXPORT pmix_status_t PMIx_IOF_pull(const pmix_proc_t procs[], size_t nprocs,
const pmix_info_t directives[], size_t ndirs,
pmix_iof_channel_t channel, pmix_iof_cbfunc_t cbfunc,
pmix_hdlr_reg_cbfunc_t regcbfunc, void *regcbdata)
{
pmix_shift_caddy_t *cd;
pmix_cmd_t cmd = PMIX_IOF_PULL_CMD;
pmix_buffer_t *msg = NULL;
pmix_status_t rc;
pmix_iof_req_t *req;
PMIX_ACQUIRE_THREAD(&pmix_global_lock);
pmix_output_verbose(2, pmix_client_globals.iof_output, "pmix:iof:PULL");
if (pmix_globals.init_cntr <= 0) {
PMIX_RELEASE_THREAD(&pmix_global_lock);
return PMIX_ERR_INIT;
}
if (PMIX_FWD_STDIN_CHANNEL & channel) {
PMIX_RELEASE_THREAD(&pmix_global_lock);
return PMIX_ERR_NOT_SUPPORTED;
}
if (PMIX_PEER_IS_SERVER(pmix_globals.mypeer) &&
pmix_client_globals.myserver == pmix_globals.mypeer) {
PMIX_RELEASE_THREAD(&pmix_global_lock);
req = PMIX_NEW(pmix_iof_req_t);
if (NULL == req) {
return PMIX_ERR_NOMEM;
}
PMIX_RETAIN(pmix_globals.mypeer);
req->requestor = pmix_globals.mypeer;
req->nprocs = nprocs;
PMIX_PROC_CREATE(req->procs, req->nprocs);
memcpy(req->procs, procs, nprocs * sizeof(pmix_proc_t));
req->channels = channel;
req->local_id = pmix_pointer_array_add(&pmix_globals.iof_requests, req);
if (NULL != regcbfunc) {
req->regcbfunc = regcbfunc;
req->cbdata = regcbdata;
PMIX_THREADSHIFT(req, myreg);
return PMIX_SUCCESS;
}
PMIX_THREADSHIFT(req, process_cache);
return PMIX_OPERATION_SUCCEEDED;
}
if (!pmix_globals.connected) {
PMIX_RELEASE_THREAD(&pmix_global_lock);
PMIX_ERROR_LOG(PMIX_ERR_UNREACH);
return PMIX_ERR_UNREACH;
}
PMIX_RELEASE_THREAD(&pmix_global_lock);
cd = PMIX_NEW(pmix_shift_caddy_t);
if (NULL == cd) {
return PMIX_ERR_NOMEM;
}
if (NULL == regcbfunc) {
cd->cbfunc.hdlrregcbfn = mycbfn;
PMIX_RETAIN(cd);
cd->cbdata = cd;
} else {
cd->cbfunc.hdlrregcbfn = regcbfunc;
cd->cbdata = regcbdata;
}
req = PMIX_NEW(pmix_iof_req_t);
if (NULL == req) {
rc = PMIX_ERR_NOMEM;
goto cleanup;
}
req->channels = channel;
req->cbfunc = cbfunc;
req->local_id = pmix_pointer_array_add(&pmix_globals.iof_requests, req);
cd->iofreq = req;
msg = PMIX_NEW(pmix_buffer_t);
if (NULL == msg) {
PMIX_RELEASE(req);
PMIX_RELEASE(cd);
return PMIX_ERR_NOMEM;
}
PMIX_BFROPS_PACK(rc, pmix_client_globals.myserver, msg, &cmd, 1, PMIX_COMMAND);
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
goto cleanup;
}
PMIX_BFROPS_PACK(rc, pmix_client_globals.myserver, msg, &nprocs, 1, PMIX_SIZE);
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
goto cleanup;
}
PMIX_BFROPS_PACK(rc, pmix_client_globals.myserver, msg, procs, nprocs, PMIX_PROC);
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
goto cleanup;
}
PMIX_BFROPS_PACK(rc, pmix_client_globals.myserver, msg, &ndirs, 1, PMIX_SIZE);
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
goto cleanup;
}
if (0 < ndirs) {
PMIX_BFROPS_PACK(rc, pmix_client_globals.myserver, msg, directives, ndirs, PMIX_INFO);
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
goto cleanup;
}
}
PMIX_BFROPS_PACK(rc, pmix_client_globals.myserver, msg, &channel, 1, PMIX_IOF_CHANNEL);
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
goto cleanup;
}
PMIX_BFROPS_PACK(rc, pmix_client_globals.myserver, msg, &req->local_id, 1, PMIX_SIZE);
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
goto cleanup;
}
pmix_output_verbose(2, pmix_client_globals.iof_output,
"pmix:iof:PULL sending request to server");
PMIX_PTL_SEND_RECV(rc, pmix_client_globals.myserver, msg, msgcbfunc, (void *) cd);
cleanup:
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
if (NULL != msg) {
PMIX_RELEASE(msg);
}
PMIX_RELEASE(req);
PMIX_RELEASE(cd);
} else if (NULL == regcbfunc) {
PMIX_WAIT_THREAD(&cd->lock);
rc = cd->status;
if (0 > rc) {
pmix_pointer_array_set_item(&pmix_globals.iof_requests, req->local_id, NULL);
PMIX_RELEASE(req);
}
PMIX_RELEASE(cd);
}
return rc;
}
PMIX_EXPORT pmix_status_t PMIx_IOF_deregister(size_t iofhdlr, const pmix_info_t directives[],
size_t ndirs, pmix_op_cbfunc_t cbfunc, void *cbdata)
{
pmix_shift_caddy_t *cd;
pmix_cmd_t cmd = PMIX_IOF_DEREG_CMD;
pmix_buffer_t *msg;
pmix_status_t rc;
pmix_iof_req_t *req;
size_t remote_id;
PMIX_ACQUIRE_THREAD(&pmix_global_lock);
pmix_output_verbose(2, pmix_client_globals.iof_output, "pmix:iof_deregister");
if (pmix_globals.init_cntr <= 0) {
PMIX_RELEASE_THREAD(&pmix_global_lock);
return PMIX_ERR_INIT;
}
if (PMIX_PEER_IS_SERVER(pmix_globals.mypeer) && !PMIX_PEER_IS_LAUNCHER(pmix_globals.mypeer)) {
PMIX_RELEASE_THREAD(&pmix_global_lock);
return PMIX_ERR_NOT_SUPPORTED;
}
if (!pmix_globals.connected) {
PMIX_RELEASE_THREAD(&pmix_global_lock);
return PMIX_ERR_UNREACH;
}
PMIX_RELEASE_THREAD(&pmix_global_lock);
req = (pmix_iof_req_t *) pmix_pointer_array_get_item(&pmix_globals.iof_requests, iofhdlr);
if (NULL == req) {
return PMIX_ERR_BAD_PARAM;
}
remote_id = req->remote_id;
pmix_pointer_array_set_item(&pmix_globals.iof_requests, iofhdlr, NULL);
PMIX_RELEASE(req);
cd = PMIX_NEW(pmix_shift_caddy_t);
if (NULL == cd) {
return PMIX_ERR_NOMEM;
}
cd->cbfunc.opcbfn = cbfunc;
cd->cbdata = cbdata;
msg = PMIX_NEW(pmix_buffer_t);
if (NULL == msg) {
PMIX_RELEASE(cd->iofreq);
PMIX_RELEASE(cd);
return PMIX_ERR_NOMEM;
}
PMIX_BFROPS_PACK(rc, pmix_client_globals.myserver, msg, &cmd, 1, PMIX_COMMAND);
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
goto cleanup;
}
PMIX_BFROPS_PACK(rc, pmix_client_globals.myserver, msg, &ndirs, 1, PMIX_SIZE);
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
goto cleanup;
}
if (0 < ndirs) {
PMIX_BFROPS_PACK(rc, pmix_client_globals.myserver, msg, directives, ndirs, PMIX_INFO);
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
goto cleanup;
}
}
PMIX_BFROPS_PACK(rc, pmix_client_globals.myserver, msg, &remote_id, 1, PMIX_SIZE);
pmix_output_verbose(2, pmix_client_globals.iof_output, "pmix:iof_dereg sending to server");
PMIX_PTL_SEND_RECV(rc, pmix_client_globals.myserver, msg, msgcbfunc, (void *) cd);
cleanup:
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
PMIX_RELEASE(msg);
PMIX_RELEASE(cd);
} else if (NULL == cbfunc) {
PMIX_WAIT_THREAD(&cd->lock);
rc = cd->status;
PMIX_RELEASE(cd);
}
return rc;
}
typedef struct {
pmix_object_t super;
pmix_event_t ev;
pmix_lock_t lock;
pmix_status_t status;
pmix_op_cbfunc_t cbfunc;
void *cbdata;
} pmix_ltcaddy_t;
static void ltcon(pmix_ltcaddy_t *p)
{
PMIX_CONSTRUCT_LOCK(&p->lock);
}
static void ltdes(pmix_ltcaddy_t *p)
{
PMIX_DESTRUCT_LOCK(&p->lock);
}
static PMIX_CLASS_INSTANCE(pmix_ltcaddy_t, pmix_object_t, ltcon, ltdes);
static pmix_event_t stdinsig_ev;
static pmix_iof_read_event_t *stdinev_global = NULL;
static void stdincbfunc(struct pmix_peer_t *peer, pmix_ptl_hdr_t *hdr,
pmix_buffer_t *buf, void *cbdata)
{
pmix_ltcaddy_t *cd = (pmix_ltcaddy_t *) cbdata;
int cnt;
pmix_status_t rc, status;
PMIX_HIDE_UNUSED_PARAMS(hdr);
if (PMIX_BUFFER_IS_EMPTY(buf)) {
if (NULL != cd->cbfunc) {
cd->cbfunc(PMIX_ERR_COMM_FAILURE, cd->cbdata);
}
free(cd);
return;
}
cnt = 1;
PMIX_BFROPS_UNPACK(rc, peer, buf, &status, &cnt, PMIX_STATUS);
if (PMIX_SUCCESS != rc) {
status = rc;
}
if (NULL != cd->cbfunc) {
cd->cbfunc(status, cd->cbdata);
}
free(cd);
}
static void myopcb(pmix_status_t status, void *cbdata)
{
pmix_ltcaddy_t *cd = (pmix_ltcaddy_t *) cbdata;
cd->status = status;
PMIX_WAKEUP_THREAD(&cd->lock);
}
pmix_status_t PMIx_IOF_push(const pmix_proc_t targets[], size_t ntargets, pmix_byte_object_t *bo,
const pmix_info_t directives[], size_t ndirs, pmix_op_cbfunc_t cbfunc,
void *cbdata)
{
pmix_buffer_t *msg;
pmix_cmd_t cmd = PMIX_IOF_PUSH_CMD;
pmix_status_t rc = PMIX_SUCCESS;
pmix_ltcaddy_t *cd;
size_t n;
bool begincollecting, stopcollecting;
int flags, fd = fileno(stdin);
PMIX_ACQUIRE_THREAD(&pmix_global_lock);
if (pmix_globals.init_cntr <= 0) {
PMIX_RELEASE_THREAD(&pmix_global_lock);
return PMIX_ERR_INIT;
}
PMIX_RELEASE_THREAD(&pmix_global_lock);
if (NULL == bo) {
for (n = 0; n < ndirs; n++) {
if (PMIX_CHECK_KEY(&directives[n], PMIX_IOF_PUSH_STDIN)) {
begincollecting = PMIX_INFO_TRUE(&directives[n]);
if (begincollecting) {
if (!pmix_globals.pushstdin) {
pmix_globals.pushstdin = true;
if (0 != fd) {
if ((flags = fcntl(fd, F_GETFL, 0)) < 0) {
pmix_output(pmix_client_globals.iof_output,
"[%s:%d]: fcntl(F_GETFL) failed with errno=%d\n",
__FILE__, __LINE__, errno);
} else {
flags |= O_NONBLOCK;
if (0 != fcntl(fd, F_SETFL, flags)) {
pmix_output(pmix_client_globals.iof_output,
"[%s:%d]: fcntl(F_SETFL) failed with errno=%d\n",
__FILE__, __LINE__, errno);
}
}
}
if (isatty(fd)) {
pmix_event_signal_set(pmix_globals.evauxbase, &stdinsig_ev, SIGCONT,
pmix_iof_stdin_cb, NULL);
PMIX_IOF_READ_EVENT(&stdinev_global, targets, ntargets, directives,
ndirs, fd, pmix_iof_read_local_handler, false);
if (pmix_iof_stdin_check(fd)) {
PMIX_IOF_READ_ACTIVATE(stdinev_global);
}
} else {
PMIX_IOF_READ_EVENT(&stdinev_global, targets, ntargets, directives,
ndirs, fd, pmix_iof_read_local_handler, true);
}
}
} else {
if (pmix_globals.pushstdin) {
}
}
} else if (PMIX_CHECK_KEY(&directives[n], PMIX_IOF_COMPLETE)) {
stopcollecting = PMIX_INFO_TRUE(&directives[n]);
if (stopcollecting) {
if (pmix_globals.pushstdin) {
}
}
}
}
return PMIX_OPERATION_SUCCEEDED;
}
if (!PMIX_PEER_IS_SERVER(pmix_globals.mypeer) ||
PMIX_PEER_IS_LAUNCHER(pmix_globals.mypeer)) {
msg = PMIX_NEW(pmix_buffer_t);
if (NULL == msg) {
return PMIX_ERR_NOMEM;
}
PMIX_BFROPS_PACK(rc, pmix_client_globals.myserver, msg, &cmd, 1, PMIX_COMMAND);
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
PMIX_RELEASE(msg);
return rc;
}
PMIX_BFROPS_PACK(rc, pmix_client_globals.myserver, msg, &ntargets, 1, PMIX_SIZE);
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
PMIX_RELEASE(msg);
return rc;
}
if (0 < ntargets) {
PMIX_BFROPS_PACK(rc, pmix_client_globals.myserver, msg, targets, ntargets, PMIX_PROC);
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
PMIX_RELEASE(msg);
return rc;
}
}
PMIX_BFROPS_PACK(rc, pmix_client_globals.myserver, msg, &ndirs, 1, PMIX_SIZE);
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
PMIX_RELEASE(msg);
return rc;
}
if (0 < ndirs) {
PMIX_BFROPS_PACK(rc, pmix_client_globals.myserver, msg, directives, ndirs, PMIX_INFO);
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
PMIX_RELEASE(msg);
return rc;
}
}
if (NULL != bo) {
PMIX_BFROPS_PACK(rc, pmix_client_globals.myserver, msg, bo, 1, PMIX_BYTE_OBJECT);
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
PMIX_RELEASE(msg);
return rc;
}
}
cd = PMIX_NEW(pmix_ltcaddy_t);
if (NULL == cd) {
PMIX_RELEASE(msg);
rc = PMIX_ERR_NOMEM;
return rc;
}
if (NULL == cbfunc) {
cd->cbfunc = myopcb;
PMIX_RETAIN(cd);
cd->cbdata = cd;
} else {
cd->cbfunc = cbfunc;
cd->cbdata = cbdata;
}
PMIX_PTL_SEND_RECV(rc, pmix_client_globals.myserver, msg, stdincbfunc, cd);
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
PMIX_RELEASE(msg);
free(cd);
} else if (NULL == cbfunc) {
PMIX_WAIT_THREAD(&cd->lock);
rc = cd->status;
PMIX_RELEASE(cd);
}
return rc;
}
if (NULL == pmix_host_server.push_stdin) {
return PMIX_ERR_NOT_SUPPORTED;
}
rc = pmix_host_server.push_stdin(&pmix_globals.myid, targets, ntargets, directives, ndirs, bo,
cbfunc, cbdata);
return rc;
}
static pmix_iof_write_event_t* pmix_iof_setup(pmix_namespace_t *nptr,
pmix_rank_t rank,
pmix_iof_channel_t stream)
{
int rc;
char *outdir, *outfile;
int np, numdigs, fdout;
pmix_iof_sink_t *snk;
pmix_proc_t src;
pmix_output_verbose(5, pmix_server_globals.iof_output,
"IOF SETUP %s %u",
nptr->nspace, rank);
PMIX_LOAD_PROCID(&src, nptr->nspace, rank);
np = nptr->nprocs / 10;
numdigs = 1;
while (np > 0) {
numdigs++;
np = np / 10;
}
if (NULL != nptr->iof_flags.directory) {
pmix_asprintf(&outdir, "%s/%s/rank.%0*u", nptr->iof_flags.directory,
nptr->nspace, numdigs, rank);
rc = pmix_os_dirpath_create(outdir, S_IRWXU | S_IRGRP | S_IXGRP);
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
free(outdir);
return NULL;
}
if (PMIX_FWD_STDOUT_CHANNEL & stream ||
nptr->iof_flags.merge) {
pmix_asprintf(&outfile, "%s/stdout", outdir);
fdout = open(outfile, O_CREAT | O_RDWR | O_TRUNC, 0644);
free(outfile);
if (fdout < 0) {
PMIX_ERROR_LOG(PMIX_ERR_FILE_OPEN_FAILURE);
free(outdir);
return NULL;
}
snk = PMIX_NEW(pmix_iof_sink_t);
if (nptr->iof_flags.merge) {
PMIX_IOF_SINK_DEFINE(snk, &src, fdout,
PMIX_FWD_ALL_CHANNELS, pmix_iof_write_handler);
} else {
PMIX_IOF_SINK_DEFINE(snk, &src, fdout,
PMIX_FWD_STDOUT_CHANNEL, pmix_iof_write_handler);
}
pmix_list_append(&nptr->sinks, &snk->super);
free(outdir);
return &snk->wev;
} else {
pmix_asprintf(&outfile, "%s/stderr", outdir);
fdout = open(outfile, O_CREAT | O_RDWR | O_TRUNC, 0644);
free(outfile);
if (fdout < 0) {
PMIX_ERROR_LOG(PMIX_ERR_FILE_OPEN_FAILURE);
free(outdir);
return NULL;
}
snk = PMIX_NEW(pmix_iof_sink_t);
PMIX_IOF_SINK_DEFINE(snk, &src, fdout,
PMIX_FWD_STDERR_CHANNEL, pmix_iof_write_handler);
pmix_list_append(&nptr->sinks, &snk->super);
free(outdir);
return &snk->wev;
}
}
if (NULL != nptr->iof_flags.file) {
outdir = pmix_dirname(nptr->iof_flags.file);
rc = pmix_os_dirpath_create(outdir, S_IRWXU | S_IRGRP | S_IXGRP);
free(outdir);
if (PMIX_SUCCESS != rc && PMIX_ERR_EXISTS != rc) {
PMIX_ERROR_LOG(rc);
return NULL;
}
if (PMIX_FWD_STDOUT_CHANNEL & stream ||
nptr->iof_flags.merge) {
if (nptr->iof_flags.pattern) {
if (NULL == strchr(nptr->iof_flags.file, '%')) {
pmix_asprintf(&outfile, "%s.out", nptr->iof_flags.file);
} else {
pmix_asprintf(&outfile, "%s.pattern.out", nptr->iof_flags.file);
}
} else {
pmix_asprintf(&outfile, "%s.%s.%0*u.out", nptr->iof_flags.file,
nptr->nspace, numdigs, rank);
}
fdout = open(outfile, O_CREAT | O_RDWR | O_TRUNC, 0644);
free(outfile);
if (fdout < 0) {
PMIX_ERROR_LOG(PMIX_ERR_FILE_OPEN_FAILURE);
return NULL;
}
snk = PMIX_NEW(pmix_iof_sink_t);
if (nptr->iof_flags.merge) {
PMIX_IOF_SINK_DEFINE(snk, &src, fdout,
PMIX_FWD_ALL_CHANNELS, pmix_iof_write_handler);
} else {
PMIX_IOF_SINK_DEFINE(snk, &src, fdout,
PMIX_FWD_STDOUT_CHANNEL, pmix_iof_write_handler);
}
pmix_list_append(&nptr->sinks, &snk->super);
return &snk->wev;
} else {
if (nptr->iof_flags.pattern) {
if (NULL == strchr(nptr->iof_flags.file, '%')) {
pmix_asprintf(&outfile, "%s.err", nptr->iof_flags.file);
} else {
pmix_asprintf(&outfile, "%s.pattern.err", nptr->iof_flags.file);
}
} else {
pmix_asprintf(&outfile, "%s.%s.%0*u.err", nptr->iof_flags.file,
nptr->nspace, numdigs, rank);
}
fdout = open(outfile, O_CREAT | O_RDWR | O_TRUNC, 0644);
free(outfile);
if (fdout < 0) {
PMIX_ERROR_LOG(PMIX_ERR_FILE_OPEN_FAILURE);
return NULL;
}
snk = PMIX_NEW(pmix_iof_sink_t);
PMIX_IOF_SINK_DEFINE(snk, &src, fdout,
PMIX_FWD_STDERR_CHANNEL, pmix_iof_write_handler);
pmix_list_append(&nptr->sinks, &snk->super);
return &snk->wev;
}
}
return NULL;
}
void pmix_iof_check_flags(pmix_info_t *info, pmix_iof_flags_t *flags)
{
if (PMIX_CHECK_KEY(info, PMIX_IOF_TAG_OUTPUT) ||
PMIX_CHECK_KEY(info, PMIX_TAG_OUTPUT)) {
flags->tag = PMIX_INFO_TRUE(info);
flags->set = true;
} else if (PMIX_CHECK_KEY(info, PMIX_IOF_TAG_DETAILED_OUTPUT)) {
flags->tag_detailed = PMIX_INFO_TRUE(info);
flags->set = true;
} else if (PMIX_CHECK_KEY(info, PMIX_IOF_TAG_FULLNAME_OUTPUT)) {
flags->tag_fullname = PMIX_INFO_TRUE(info);
flags->set = true;
} else if (PMIX_CHECK_KEY(info, PMIX_IOF_RANK_OUTPUT)) {
flags->rank = PMIX_INFO_TRUE(info);
flags->set = true;
} else if (PMIX_CHECK_KEY(info, PMIX_IOF_TIMESTAMP_OUTPUT) ||
PMIX_CHECK_KEY(info, PMIX_TIMESTAMP_OUTPUT)) {
flags->timestamp = PMIX_INFO_TRUE(info);
flags->set = true;
} else if (PMIX_CHECK_KEY(info, PMIX_IOF_XML_OUTPUT)) {
flags->xml = PMIX_INFO_TRUE(info);
flags->set = true;
} else if (PMIX_CHECK_KEY(info, PMIX_IOF_OUTPUT_TO_FILE) ||
PMIX_CHECK_KEY(info, PMIX_OUTPUT_TO_FILE)) {
flags->file = strdup(info->value.data.string);
flags->set = true;
flags->local_output = true;
flags->local_output_given = true;
} else if (PMIX_CHECK_KEY(info, PMIX_IOF_OUTPUT_TO_DIRECTORY) ||
PMIX_CHECK_KEY(info, PMIX_OUTPUT_TO_DIRECTORY)) {
flags->directory = strdup(info->value.data.string);
flags->set = true;
flags->local_output = true;
flags->local_output_given = true;
} else if (PMIX_CHECK_KEY(info, PMIX_IOF_FILE_ONLY) ||
PMIX_CHECK_KEY(info, PMIX_OUTPUT_NOCOPY)) {
flags->nocopy = PMIX_INFO_TRUE(info);
flags->set = true;
} else if (PMIX_CHECK_KEY(info, PMIX_IOF_MERGE_STDERR_STDOUT) ||
PMIX_CHECK_KEY(info, PMIX_MERGE_STDERR_STDOUT)) {
flags->merge = PMIX_INFO_TRUE(info);
flags->set = true;
} else if (PMIX_CHECK_KEY(info, PMIX_IOF_LOCAL_OUTPUT)) {
flags->local_output = PMIX_INFO_TRUE(info);
flags->set = true;
flags->local_output_given = true;
} else if (PMIX_CHECK_KEY(info, PMIX_IOF_OUTPUT_RAW)) {
flags->raw = PMIX_INFO_TRUE(info);
flags->set = true;
} else if (PMIX_CHECK_KEY(info, PMIX_IOF_FILE_PATTERN)) {
flags->pattern = PMIX_INFO_TRUE(info);
}
}
pmix_status_t pmix_iof_process_iof(pmix_iof_channel_t channels, const pmix_proc_t *source,
const pmix_byte_object_t *bo, const pmix_info_t *info,
size_t ninfo, pmix_iof_req_t *req)
{
bool match;
size_t m;
pmix_buffer_t *msg;
pmix_status_t rc;
pmix_byte_object_t *wbo;
pmix_proc_t proc;
if (!(channels & req->channels)) {
return PMIX_SUCCESS;
}
match = false;
for (m = 0; m < req->nprocs; m++) {
if (PMIX_CHECK_PROCID(source, &req->procs[m])) {
match = true;
break;
}
}
if (!match) {
return PMIX_SUCCESS;
}
if (NULL == req->requestor->info || req->requestor->finalized) {
return PMIX_SUCCESS;
}
if (PMIX_CHECK_NAMES(source, &req->requestor->info->pname)) {
return PMIX_SUCCESS;
}
if (PMIX_CHECK_NAMES(&req->requestor->info->pname, &pmix_globals.myid)) {
return PMIX_SUCCESS;
}
if (NULL == (msg = PMIX_NEW(pmix_buffer_t))) {
PMIX_ERROR_LOG(PMIX_ERR_OUT_OF_RESOURCE);
return PMIX_ERR_OUT_OF_RESOURCE;
}
PMIX_BFROPS_PACK(rc, req->requestor, msg, source, 1, PMIX_PROC);
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
PMIX_RELEASE(msg);
return rc;
}
PMIX_BFROPS_PACK(rc, req->requestor, msg, &channels, 1, PMIX_IOF_CHANNEL);
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
PMIX_RELEASE(msg);
return rc;
}
PMIX_BFROPS_PACK(rc, req->requestor, msg, &req->remote_id, 1, PMIX_SIZE);
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
PMIX_RELEASE(msg);
return rc;
}
PMIX_BFROPS_PACK(rc, req->requestor, msg, &ninfo, 1, PMIX_SIZE);
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
PMIX_RELEASE(msg);
return rc;
}
if (0 < ninfo) {
PMIX_BFROPS_PACK(rc, req->requestor, msg, info, ninfo, PMIX_INFO);
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
PMIX_RELEASE(msg);
return rc;
}
}
PMIX_LOAD_PROCID(&proc, req->requestor->info->pname.nspace, req->requestor->info->pname.rank);
wbo = pmix_iof_prep_output(&proc, &req->flags, req->channels, bo);
if (NULL == wbo) {
PMIX_ERROR_LOG(PMIX_ERR_NOT_SUPPORTED);
PMIX_RELEASE(msg);
return PMIX_ERR_NOT_SUPPORTED;
}
PMIX_BFROPS_PACK(rc, req->requestor, msg, wbo, 1, PMIX_BYTE_OBJECT);
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
PMIx_Byte_object_free(wbo, 1);
PMIX_RELEASE(msg);
return rc;
}
PMIx_Byte_object_free(wbo, 1);
PMIX_PTL_SEND_ONEWAY(rc, req->requestor, msg, PMIX_PTL_TAG_IOF);
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
PMIX_RELEASE(msg);
}
return PMIX_OPERATION_SUCCEEDED;
}
pmix_byte_object_t* pmix_iof_prep_output(const pmix_proc_t *name,
pmix_iof_flags_t *myflags,
pmix_iof_channel_t stream,
const pmix_byte_object_t *bo)
{
char starttag[PMIX_IOF_BASE_TAG_MAX], endtag[PMIX_IOF_BASE_TAG_MAX], *suffix;
char timestamp[PMIX_IOF_BASE_TAG_MAX], outtag[PMIX_IOF_BASE_TAG_MAX];
char begintag[PMIX_IOF_BASE_TAG_MAX];
char **segments = NULL;
pmix_byte_object_t *output;
size_t offset, j, n, m, bufsize;
char *buffer, qprint[15], *cptr;
const char *usestring;
bool bufcopy;
pmix_cb_t cb2;
pmix_info_t optional;
pmix_kval_t *kv;
pid_t pid;
char *pidstring;
pmix_status_t rc;
output = PMIx_Byte_object_create(1);
if (0 == bo->size) {
return output;
}
memset(begintag, 0, PMIX_IOF_BASE_TAG_MAX);
memset(starttag, 0, PMIX_IOF_BASE_TAG_MAX);
memset(endtag, 0, PMIX_IOF_BASE_TAG_MAX);
memset(timestamp, 0, PMIX_IOF_BASE_TAG_MAX);
memset(outtag, 0, PMIX_IOF_BASE_TAG_MAX);
PMIX_INFO_LOAD(&optional, PMIX_OPTIONAL, NULL, PMIX_BOOL);
if (PMIX_FWD_STDOUT_CHANNEL & stream) {
suffix = "stdout";
} else if (PMIX_FWD_STDERR_CHANNEL & stream) {
suffix = "stderr";
} else if (PMIX_FWD_STDDIAG_CHANNEL & stream) {
suffix = "stddiag";
} else {
PMIX_ERROR_LOG(PMIX_ERR_VALUE_OUT_OF_BOUNDS);
pmix_output_verbose(1, pmix_client_globals.iof_output, "%s stream %0x",
PMIX_NAME_PRINT(&pmix_globals.myid), stream);
PMIx_Byte_object_free(output, 1);
return NULL;
}
if (!myflags->set) {
output->bytes = (char*)malloc(bo->size);
memcpy(output->bytes, bo->bytes, bo->size);
output->size = bo->size;
return output;
}
if (myflags->xml) {
if (myflags->tag) {
cptr = strrchr(name->nspace, '@');
if (NULL == cptr) {
usestring = name->nspace; } else {
++cptr;
usestring = cptr; }
pmix_snprintf(begintag, PMIX_IOF_BASE_TAG_MAX,
"<%s %s=\"%s\" rank=\"%s\"", suffix,
(usestring == name->nspace) ? "nspace" : "jobid",
usestring, PMIX_RANK_PRINT(name->rank));
} else if (myflags->tag_fullname) {
pmix_snprintf(begintag, PMIX_IOF_BASE_TAG_MAX,
"<%s nspace=\"%s\" rank=\"%s\"", suffix,
name->nspace, PMIX_RANK_PRINT(name->rank));
} else if (myflags->tag_detailed) {
PMIX_CONSTRUCT(&cb2, pmix_cb_t);
cb2.proc = (pmix_proc_t*)name;
cb2.key = PMIX_HOSTNAME;
cb2.info = &optional;
cb2.ninfo = 1;
PMIX_GDS_FETCH_KV(rc, pmix_globals.mypeer, &cb2);
if (PMIX_SUCCESS == rc || PMIX_OPERATION_SUCCEEDED == rc) {
kv = (pmix_kval_t*)pmix_list_remove_first(&cb2.kvs);
if (NULL != kv) { cptr = strdup(kv->value->data.string);
PMIX_RELEASE(kv);
} else {
cptr = strdup("unknown");
}
} else {
cptr = strdup("unknown");
}
PMIX_DESTRUCT(&cb2);
PMIX_CONSTRUCT(&cb2, pmix_cb_t);
cb2.proc = (pmix_proc_t*)name;
cb2.key = PMIX_PROC_PID;
cb2.info = &optional;
cb2.ninfo = 1;
PMIX_GDS_FETCH_KV(rc, pmix_globals.mypeer, &cb2);
if (PMIX_SUCCESS == rc || PMIX_OPERATION_SUCCEEDED == rc) {
kv = (pmix_kval_t*)pmix_list_remove_first(&cb2.kvs);
if (NULL != kv) { PMIX_VALUE_GET_NUMBER(rc, kv->value, pid, pid_t);
PMIX_RELEASE(kv);
if (PMIX_SUCCESS != rc) {
pidstring = strdup("unknown");
} else {
pmix_asprintf(&pidstring, "%u", pid);
}
} else {
pidstring = strdup("unknown");
}
} else {
pidstring = strdup("unknown");
}
PMIX_DESTRUCT(&cb2);
pmix_snprintf(begintag, PMIX_IOF_BASE_TAG_MAX,
"<%s nspace=\"%s\" rank=\"%s\"[\"%s\":\"%s\"",
suffix, name->nspace,
PMIX_RANK_PRINT(name->rank),
cptr, pidstring);
free(cptr);
free(pidstring);
} else if (myflags->rank) {
pmix_snprintf(begintag, PMIX_IOF_BASE_TAG_MAX,
"<%s rank=\"%s\"", suffix,
PMIX_RANK_PRINT(name->rank));
} else if (myflags->timestamp) {
pmix_snprintf(begintag, PMIX_IOF_BASE_TAG_MAX,
"<%s rank=\"%s\"", suffix,
PMIX_RANK_PRINT(name->rank));
} else {
pmix_snprintf(begintag, PMIX_IOF_BASE_TAG_MAX,
"<%s rank=\"%s\"", suffix,
PMIX_RANK_PRINT(name->rank));
}
pmix_snprintf(endtag, PMIX_IOF_BASE_TAG_MAX,
"</%s>", suffix);
} else {
if (myflags->tag) {
cptr = strrchr(name->nspace, '@');
if (NULL == cptr) {
usestring = name->nspace; } else {
++cptr;
usestring = cptr; }
pmix_snprintf(outtag, PMIX_IOF_BASE_TAG_MAX,
"[%s,%s]<%s>: ",
usestring,
PMIX_RANK_PRINT(name->rank),
suffix);
} else if (myflags->tag_detailed) {
if (myflags->tag_fullname) {
usestring = name->nspace;
} else {
cptr = strrchr(name->nspace, '@');
if (NULL == cptr) {
usestring = name->nspace; } else {
++cptr;
usestring = cptr; }
}
PMIX_CONSTRUCT(&cb2, pmix_cb_t);
cb2.proc = (pmix_proc_t*)name;
cb2.key = PMIX_HOSTNAME;
cb2.info = &optional;
cb2.ninfo = 1;
PMIX_GDS_FETCH_KV(rc, pmix_globals.mypeer, &cb2);
if (PMIX_SUCCESS == rc || PMIX_OPERATION_SUCCEEDED == rc) {
kv = (pmix_kval_t*)pmix_list_remove_first(&cb2.kvs);
cptr = strdup(kv->value->data.string);
PMIX_RELEASE(kv);
} else {
cptr = strdup("unknown");
}
PMIX_DESTRUCT(&cb2);
PMIX_CONSTRUCT(&cb2, pmix_cb_t);
cb2.proc = (pmix_proc_t*)name;
cb2.key = PMIX_PROC_PID;
cb2.info = &optional;
cb2.ninfo = 1;
PMIX_GDS_FETCH_KV(rc, pmix_globals.mypeer, &cb2);
if (PMIX_SUCCESS == rc || PMIX_OPERATION_SUCCEEDED == rc) {
kv = (pmix_kval_t*)pmix_list_remove_first(&cb2.kvs);
PMIX_VALUE_GET_NUMBER(rc, kv->value, pid, pid_t);
PMIX_RELEASE(kv);
if (PMIX_SUCCESS != rc) {
pidstring = strdup("unknown");
} else {
pmix_asprintf(&pidstring, "%u", pid);
}
} else {
pidstring = strdup("unknown");
}
PMIX_DESTRUCT(&cb2);
pmix_snprintf(outtag, PMIX_IOF_BASE_TAG_MAX,
"[%s,%s][%s:%s]<%s>: ",
usestring,
PMIX_RANK_PRINT(name->rank),
cptr, pidstring,
suffix);
free(cptr);
free(pidstring);
} else if (myflags->tag_fullname) {
pmix_snprintf(outtag, PMIX_IOF_BASE_TAG_MAX,
"[%s,%s]<%s>: ",
name->nspace,
PMIX_RANK_PRINT(name->rank),
suffix);
} else if (myflags->rank) {
pmix_snprintf(outtag, PMIX_IOF_BASE_TAG_MAX,
"[%s]<%s>: ",
PMIX_RANK_PRINT(name->rank), suffix);
}
}
if (myflags->timestamp) {
time_t mytime;
time(&mytime);
cptr = ctime(&mytime);
cptr[strlen(cptr) - 1] = '\0';
if (myflags->xml && !myflags->tag && !myflags->rank) {
pmix_snprintf(timestamp, PMIX_IOF_BASE_TAG_MAX,
" timestamp=\"%s\"", cptr);
} else if (myflags->xml && (myflags->tag || myflags->rank)) {
pmix_snprintf(timestamp, PMIX_IOF_BASE_TAG_MAX,
" timestamp=\"%s\"", cptr);
} else if (myflags->tag || myflags->rank) {
pmix_snprintf(timestamp, PMIX_IOF_BASE_TAG_MAX, "[%s]", cptr);
} else {
pmix_snprintf(timestamp, PMIX_IOF_BASE_TAG_MAX, "[%s]<%s>: ", cptr, suffix);
}
}
if (0 < strlen(begintag)) {
PMIx_Argv_append_nosize(&segments, begintag);
}
if (0 < strlen(timestamp)) {
PMIx_Argv_append_nosize(&segments, timestamp);
}
if (0 < strlen(outtag)) {
PMIx_Argv_append_nosize(&segments, outtag);
}
if (myflags->xml) {
PMIx_Argv_append_nosize(&segments, ">");
}
if (myflags->xml) {
bufsize = bo->size;
for (n = 0; n < bo->size; n++) {
if ('&' == bo->bytes[n]) {
bufsize += 5;
} else if ('<' == bo->bytes[n] || '>' == bo->bytes[n]) {
bufsize += 4;
} else if (!isprint(bo->bytes[n])) {
pmix_snprintf(qprint, 10, "&#%03d;", (int) bo->bytes[n]);
bufsize += strlen(qprint);
}
}
if (bo->size < bufsize) {
buffer = malloc(bufsize);
memset(buffer, 0, bufsize);
bufcopy = true;
m = 0;
for (n = 0; n < bo->size; n++) {
if ('&' == bo->bytes[n]) {
buffer[m++] = '&';
buffer[m++] = 'a';
buffer[m++] = 'p';
buffer[m++] = ';';
} else if ('<' == bo->bytes[n]) {
buffer[m++] = '&';
buffer[m++] = 'l';
buffer[m++] = 't';
buffer[m++] = ';';
} else if ('>' == bo->bytes[n]) {
buffer[m++] = '&';
buffer[m++] = 'g';
buffer[m++] = 't';
buffer[m++] = ';';
} else if (!isprint(bo->bytes[n])) {
pmix_snprintf(qprint, 10, "&#%03d;", (int) bo->bytes[n]);
for (j = 0; j < strlen(qprint); j++) {
buffer[m++] = qprint[j];
}
} else {
buffer[m++] = bo->bytes[n];
}
}
} else {
buffer = bo->bytes;
bufsize = bo->size;
bufcopy = false;
}
} else {
buffer = bo->bytes;
bufsize = bo->size;
bufcopy = false;
}
if (NULL != segments) {
for (n=0; NULL != segments[n]; n++) {
output->size += strlen(segments[n]);
}
}
output->size += bufsize;
output->size += strlen(endtag);
if (myflags->xml) {
output->size++;
}
output->bytes = (char*)malloc(output->size);
offset = 0;
if (NULL != segments) {
for (n=0; NULL != segments[n]; n++) {
memcpy(&output->bytes[offset], segments[n], strlen(segments[n]));
offset += strlen(segments[n]);
}
}
memcpy(&output->bytes[offset], buffer, bufsize);
offset += bufsize;
if (0 < strlen(endtag)) {
memcpy(&output->bytes[offset], endtag, strlen(endtag));
}
if (myflags->xml) {
output->bytes[output->size-1] = '\n';
}
if (bufcopy) {
free(buffer);
}
return output;
}
static pmix_status_t write_output_line(const pmix_proc_t *name,
pmix_iof_write_event_t *channel,
pmix_iof_flags_t *myflags,
pmix_iof_channel_t stream,
bool copystdout, bool copystderr,
const pmix_byte_object_t *bo)
{
pmix_byte_object_t *wbo;
pmix_iof_write_output_t *copy;
pmix_iof_write_output_t *output;
if (PMIX_FWD_STDIN_CHANNEL & stream) {
output = PMIX_NEW(pmix_iof_write_output_t);
if (0 < bo->size) {
output->data = (char*)malloc(bo->size);
memcpy(output->data, bo->bytes, bo->size);
}
output->numbytes = bo->size;
goto process;
}
wbo = pmix_iof_prep_output(name, myflags, stream, bo);
if (NULL == wbo) {
return PMIX_ERR_NOT_SUPPORTED;
}
output = PMIX_NEW(pmix_iof_write_output_t);
output->data = wbo->bytes;
output->numbytes = wbo->size;
wbo->bytes = NULL;
wbo->size = 0;
PMIx_Byte_object_free(wbo, 1);
process:
pmix_list_append(&channel->outputs, &output->super);
if (copystdout){
copy = PMIX_NEW(pmix_iof_write_output_t);
copy->data = (char *) malloc(output->numbytes);
memcpy(copy->data, output->data, output->numbytes);
copy->numbytes = output->numbytes;
pmix_list_append(&pmix_client_globals.iof_stdout.wev.outputs, ©->super);
if (!pmix_client_globals.iof_stdout.wev.pending) {
PMIX_IOF_SINK_ACTIVATE(&pmix_client_globals.iof_stdout.wev);
}
}
if (copystderr){
copy = PMIX_NEW(pmix_iof_write_output_t);
copy->data = (char *) malloc(output->numbytes);
memcpy(copy->data, output->data, output->numbytes);
copy->numbytes = output->numbytes;
pmix_list_append(&pmix_client_globals.iof_stderr.wev.outputs, ©->super);
if (!pmix_client_globals.iof_stderr.wev.pending) {
PMIX_IOF_SINK_ACTIVATE(&pmix_client_globals.iof_stderr.wev);
}
}
if (!channel->pending) {
pmix_output_verbose(1, pmix_client_globals.iof_output,
"%s write:output adding write event",
PMIX_NAME_PRINT(&pmix_globals.myid));
PMIX_IOF_SINK_ACTIVATE(channel);
}
return PMIX_SUCCESS;
}
pmix_status_t pmix_iof_write_output(const pmix_proc_t *name,
pmix_iof_channel_t stream,
const pmix_byte_object_t *bo)
{
pmix_status_t rc;
size_t n, start;
pmix_byte_object_t bopass;
pmix_iof_write_event_t *channel;
pmix_iof_flags_t myflags;
pmix_namespace_t *nptr, *ns;
bool outputio;
bool copystdout = false;
bool copystderr = false;
pmix_iof_sink_t *sink;
pmix_iof_residual_t *res;
char *inputdata;
size_t inputsize;
bool copied;
if (PMIX_FWD_STDIN_CHANNEL & stream) {
return PMIX_ERR_BAD_PARAM;
}
nptr = NULL;
PMIX_LIST_FOREACH (ns, &pmix_globals.nspaces, pmix_namespace_t)
{
if (0 == strcmp(ns->nspace, name->nspace)) {
nptr = ns;
break;
}
}
channel = NULL;
outputio = pmix_globals.iof_flags.local_output;
if (NULL != nptr) {
if (nptr->iof_flags.set) {
if (nptr->iof_flags.local_output_given) {
outputio = nptr->iof_flags.local_output;
}
if (!outputio) {
return PMIX_SUCCESS;
}
if (NULL != nptr->iof_flags.directory) {
PMIX_LIST_FOREACH(sink, &nptr->sinks, pmix_iof_sink_t) {
if (sink->name.rank == name->rank &&
((stream & sink->tag) || nptr->iof_flags.merge)) {
channel = &sink->wev;
break;
}
}
if (NULL == channel) {
channel = pmix_iof_setup(nptr, name->rank, stream);
if (NULL == channel) {
return PMIX_ERR_IOF_FAILURE;
}
}
if (!nptr->iof_flags.nocopy && pmix_globals.iof_flags.local_output) {
if (PMIX_FWD_STDOUT_CHANNEL & stream) {
copystdout = true;
} else {
copystderr = true;
}
}
} else if (NULL != nptr->iof_flags.file) {
PMIX_LIST_FOREACH(sink, &nptr->sinks, pmix_iof_sink_t) {
if (sink->name.rank == name->rank &&
((stream & sink->tag) || nptr->iof_flags.merge)) {
channel = &sink->wev;
break;
}
}
if (NULL == channel) {
channel = pmix_iof_setup(nptr, name->rank, stream);
if (NULL == channel) {
return PMIX_ERR_IOF_FAILURE;
}
}
if (!nptr->iof_flags.nocopy && pmix_globals.iof_flags.local_output) {
if (PMIX_FWD_STDOUT_CHANNEL & stream) {
copystdout = true;
} else {
copystderr = true;
}
}
}
myflags = nptr->iof_flags;
} else {
myflags = pmix_globals.iof_flags;
}
} else {
myflags = pmix_globals.iof_flags;
}
if (!outputio) {
return PMIX_SUCCESS;
}
if (NULL == channel) {
if (PMIX_FWD_STDOUT_CHANNEL & stream) {
channel = &pmix_client_globals.iof_stdout.wev;
} else {
if(!myflags.merge) {
channel = &pmix_client_globals.iof_stderr.wev;
}
else {
channel = &pmix_client_globals.iof_stdout.wev;
}
}
}
pmix_output_verbose(1, pmix_client_globals.iof_output,
"%s write:output setting up to write %lu bytes to %s for %s on fd %d",
PMIX_NAME_PRINT(&pmix_globals.myid), (unsigned long) bo->size,
PMIx_IOF_channel_string(stream), PMIX_NAME_PRINT(name),
(NULL == channel) ? -1 : channel->fd);
if (0 == bo->size) {
rc = write_output_line(name, channel, &myflags, stream,
false, false, bo);
return rc;
}
inputdata = bo->bytes;
inputsize = bo->size;
copied = false;
PMIX_LIST_FOREACH(res, &pmix_server_globals.iof_residuals, pmix_iof_residual_t) {
if (PMIX_CHECK_PROCID(name, &res->name) || (stream & res->stream)) {
inputdata = (char*)malloc(inputsize + res->bo.size);
memcpy(inputdata, res->bo.bytes, res->bo.size);
memcpy(&inputdata[res->bo.size], bo->bytes, bo->size);
inputsize += res->bo.size;
copied = true;
pmix_list_remove_item(&pmix_server_globals.iof_residuals, &res->super);
PMIX_RELEASE(res);
break;
}
}
start = 0;
for (n=0; n < inputsize; n++) {
if ('\n' == inputdata[n]) {
bopass.bytes = &inputdata[start];
bopass.size = n - start + 1;
rc = write_output_line(name, channel, &myflags, stream,
copystdout, copystderr, &bopass);
if (PMIX_SUCCESS != rc) {
if (copied) {
free(inputdata);
}
return rc;
}
start = n + 1;
}
}
if (start < inputsize) {
if (myflags.raw) {
bopass.bytes = &inputdata[start];
bopass.size = inputsize - start;
rc = write_output_line(name, channel, &myflags, stream,
copystdout, copystderr, &bopass);
if (PMIX_SUCCESS != rc) {
if (copied) {
free(inputdata);
}
return rc;
}
} else {
res = PMIX_NEW(pmix_iof_residual_t);
PMIX_XFER_PROCID(&res->name, name);
res->channel = channel;
memcpy(&res->flags, &myflags, sizeof(pmix_iof_flags_t));
res->stream = stream;
res->copystdout = copystdout;
res->copystderr = copystderr;
res->bo.bytes = (char*)malloc(inputsize - start);
memcpy(res->bo.bytes, &inputdata[start], inputsize - start);
res->bo.size = inputsize - start;
pmix_list_append(&pmix_server_globals.iof_residuals, &res->super);
}
}
if (copied) {
free(inputdata);
}
return PMIX_SUCCESS;
}
void pmix_iof_flush_residuals(void)
{
pmix_status_t rc;
pmix_iof_residual_t *res;
PMIX_LIST_FOREACH(res, &pmix_server_globals.iof_residuals, pmix_iof_residual_t) {
rc = write_output_line(&res->name, res->channel, &res->flags,
res->stream, res->copystdout, res->copystderr, &res->bo);
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
return;
}
}
}
void pmix_iof_static_dump_output(pmix_iof_sink_t *sink)
{
bool dump;
int num_written;
pmix_iof_write_event_t *wev = &sink->wev;
pmix_iof_write_output_t *output;
if (!pmix_list_is_empty(&wev->outputs)) {
dump = false;
while (NULL != (output = (pmix_iof_write_output_t *) pmix_list_remove_first(&wev->outputs))) {
if (!dump && 0 < output->numbytes) {
num_written = write(wev->fd, output->data, output->numbytes);
if (num_written < output->numbytes) {
dump = true;
}
}
PMIX_RELEASE(output);
}
}
}
void pmix_iof_write_handler(int sd, short args, void *cbdata)
{
pmix_iof_sink_t *sink = (pmix_iof_sink_t *) cbdata;
pmix_iof_write_event_t *wev = &sink->wev;
pmix_list_item_t *item;
pmix_iof_write_output_t *output;
int num_written, total_written = 0;
PMIX_HIDE_UNUSED_PARAMS(sd, args);
PMIX_ACQUIRE_OBJECT(sink);
pmix_output_verbose(1, pmix_client_globals.iof_output,
"%s write:handler writing data to %d",
PMIX_NAME_PRINT(&pmix_globals.myid), wev->fd);
while (NULL != (item = pmix_list_remove_first(&wev->outputs))) {
output = (pmix_iof_write_output_t *) item;
if (0 == output->numbytes) {
PMIX_RELEASE(output);
if (2 < wev->fd) { close(wev->fd);
wev->fd = -1;
}
return;
}
num_written = write(wev->fd, output->data, output->numbytes);
if (num_written < 0) {
if (EAGAIN == errno || EINTR == errno) {
pmix_list_prepend(&wev->outputs, item);
if (pmix_globals.output_limit < pmix_list_get_size(&wev->outputs)) {
pmix_output(0, "IO Forwarding is running too far behind - something is "
"blocking us from writing");
goto ABORT;
}
wev->numtries++;
if (PMIX_IOF_MAX_RETRIES < wev->numtries) {
pmix_output(0, "IO Forwarding is unable to output - something is "
"blocking us from writing");
goto ABORT;
}
goto NEXT_CALL;
}
PMIX_RELEASE(output);
goto ABORT;
} else if (num_written < output->numbytes) {
memmove(output->data, &output->data[num_written], output->numbytes - num_written);
output->numbytes -= num_written;
pmix_list_prepend(&wev->outputs, item);
if (pmix_globals.output_limit < pmix_list_get_size(&wev->outputs)) {
pmix_output(0, "IO Forwarding is running too far behind - something is blocking us "
"from writing");
goto ABORT;
}
wev->numtries = 0;
goto NEXT_CALL;
}
PMIX_RELEASE(output);
wev->numtries = 0;
total_written += num_written;
if (wev->always_writable && (PMIX_IOF_SINK_BLOCKSIZE <= total_written)) {
goto NEXT_CALL;
}
}
ABORT:
wev->pending = false;
PMIX_POST_OBJECT(wev);
return;
NEXT_CALL:
PMIX_IOF_SINK_ACTIVATE(wev);
}
bool pmix_iof_stdin_check(int fd)
{
#if defined(HAVE_TCGETPGRP)
if (isatty(fd) && (getpgrp() != tcgetpgrp(fd))) {
return false;
}
#endif
return true;
}
void pmix_iof_stdin_cb(int sd, short args, void *cbdata)
{
bool should_process;
pmix_iof_read_event_t *stdinev = (pmix_iof_read_event_t *) cbdata;
PMIX_HIDE_UNUSED_PARAMS(sd, args);
PMIX_ACQUIRE_OBJECT(stdinev);
should_process = pmix_iof_stdin_check(0);
if (should_process) {
PMIX_IOF_READ_ACTIVATE(stdinev);
} else {
pmix_event_del(&stdinev->ev);
stdinev->active = false;
PMIX_POST_OBJECT(stdinev);
}
}
static void iof_stdin_cbfunc(struct pmix_peer_t *peer, pmix_ptl_hdr_t *hdr,
pmix_buffer_t *buf, void *cbdata)
{
pmix_iof_read_event_t *stdinev = (pmix_iof_read_event_t *) cbdata;
int cnt;
pmix_status_t rc, ret;
PMIX_HIDE_UNUSED_PARAMS(hdr);
PMIX_ACQUIRE_OBJECT(stdinev);
cnt = 1;
PMIX_BFROPS_UNPACK(rc, peer, buf, &ret, &cnt, PMIX_STATUS);
if (PMIX_SUCCESS != rc) {
pmix_event_del(&stdinev->ev);
stdinev->active = false;
PMIX_POST_OBJECT(stdinev);
return;
}
if (PMIX_SUCCESS != ret) {
pmix_event_del(&stdinev->ev);
stdinev->active = false;
PMIX_POST_OBJECT(stdinev);
if (PMIX_ERR_IOF_COMPLETE != ret) {
PMIx_Notify_event(PMIX_ERR_IOF_FAILURE, &pmix_globals.myid, PMIX_RANGE_PROC_LOCAL, NULL,
0, NULL, NULL);
}
return;
}
pmix_iof_stdin_cb(0, 0, stdinev);
}
static void opcbfn(pmix_status_t status, void *cbdata)
{
pmix_byte_object_t *boptr = (pmix_byte_object_t *) cbdata;
PMIX_HIDE_UNUSED_PARAMS(status);
PMIX_ACQUIRE_OBJECT(boptr);
PMIX_BYTE_OBJECT_FREE(boptr, 1);
}
void pmix_iof_read_local_handler(int sd, short args, void *cbdata)
{
pmix_iof_read_event_t *rev = (pmix_iof_read_event_t *) cbdata;
unsigned char data[PMIX_IOF_BASE_MSG_MAX];
int32_t numbytes;
pmix_status_t rc;
pmix_buffer_t *msg;
pmix_cmd_t cmd = PMIX_IOF_PUSH_CMD;
pmix_byte_object_t bo, *boptr;
int fd;
pmix_pfexec_child_t *child = (pmix_pfexec_child_t *) rev->childproc;
PMIX_HIDE_UNUSED_PARAMS(sd, args);
PMIX_ACQUIRE_OBJECT(rev);
if (0 > rev->fd) {
fd = fileno(stdin);
} else {
fd = rev->fd;
}
memset(data, 0, PMIX_IOF_BASE_MSG_MAX);
numbytes = read(fd, data, sizeof(data));
rev->active = false;
if (numbytes < 0) {
if (EAGAIN == errno || EINTR == errno) {
PMIX_IOF_READ_ACTIVATE(rev);
return;
}
pmix_output_verbose(1, pmix_client_globals.iof_output,
"%s iof:read handler Error on %s",
PMIX_NAME_PRINT(&pmix_globals.myid),
PMIx_IOF_channel_string(rev->channel));
bo.bytes = NULL;
bo.size = 0;
numbytes = 0;
} else {
bo.bytes = (char *) data;
bo.size = numbytes;
}
if (NULL != child &&
(PMIX_FWD_STDOUT_CHANNEL == rev->channel ||
PMIX_FWD_STDERR_CHANNEL == rev->channel)) {
if (PMIX_FWD_STDOUT_CHANNEL == rev->channel) {
rc = pmix_iof_write_output(&child->stdoutev->name, PMIX_FWD_STDOUT_CHANNEL, &bo);
} else if (PMIX_FWD_STDERR_CHANNEL == rev->channel) {
rc = pmix_iof_write_output(&child->stderrev->name, PMIX_FWD_STDERR_CHANNEL, &bo);
} else {
rc = PMIX_ERR_BAD_PARAM;
}
if (0 > rc) {
PMIX_ERROR_LOG(rc);
}
if (0 == numbytes) {
if (NULL != child && child->completed &&
(NULL == child->stdoutev || !child->stdoutev->active) &&
(NULL == child->stderrev || !child->stderrev->active)) {
PMIX_PFEXEC_CHK_COMPLETE(child);
}
return;
}
goto reactivate;
}
if (PMIX_PEER_IS_LAUNCHER(pmix_globals.mypeer)) {
if (rev == stdinev_global && NULL != rev->targets) {
PMIX_LIST_FOREACH(child, &pmix_pfexec_globals.children, pmix_pfexec_child_t) {
if (PMIX_CHECK_PROCID(&child->proc, &rev->targets[0])) {
rc = write_output_line(&child->proc, &child->stdinsink.wev, NULL,
PMIX_FWD_STDIN_CHANNEL, false, false, &bo);
goto reactivate;
}
}
}
}
if ((PMIX_PEER_IS_LAUNCHER(pmix_globals.mypeer) ||
PMIX_PEER_IS_TOOL(pmix_globals.mypeer)) &&
pmix_globals.connected) {
goto forward;
}
if (PMIX_PEER_IS_SERVER(pmix_globals.mypeer)) {
if (NULL == pmix_host_server.push_stdin) {
return;
}
PMIX_BYTE_OBJECT_CREATE(boptr, 1);
if (0 < bo.size) {
boptr->bytes = (char*)malloc(bo.size);
memcpy(boptr->bytes, bo.bytes, bo.size);
boptr->size = bo.size;
}
rc = pmix_host_server.push_stdin(&pmix_globals.myid, rev->targets, rev->ntargets,
rev->directives, rev->ndirs, boptr, opcbfn, (void*)boptr);
goto reactivate;
}
forward:
msg = PMIX_NEW(pmix_buffer_t);
if (NULL == msg) {
return;
}
PMIX_BFROPS_PACK(rc, pmix_client_globals.myserver, msg, &cmd, 1, PMIX_COMMAND);
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
PMIX_RELEASE(msg);
return;
}
PMIX_BFROPS_PACK(rc, pmix_client_globals.myserver, msg, &rev->ntargets, 1, PMIX_SIZE);
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
PMIX_RELEASE(msg);
return;
}
if (0 < rev->ntargets) {
PMIX_BFROPS_PACK(rc, pmix_client_globals.myserver, msg, rev->targets, rev->ntargets,
PMIX_PROC);
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
PMIX_RELEASE(msg);
return;
}
}
PMIX_BFROPS_PACK(rc, pmix_client_globals.myserver, msg, &rev->ndirs, 1, PMIX_SIZE);
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
PMIX_RELEASE(msg);
return;
}
if (0 < rev->ndirs) {
PMIX_BFROPS_PACK(rc, pmix_client_globals.myserver, msg, rev->directives, rev->ndirs,
PMIX_INFO);
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
PMIX_RELEASE(msg);
return;
}
}
PMIX_BFROPS_PACK(rc, pmix_client_globals.myserver, msg, &bo, 1, PMIX_BYTE_OBJECT);
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
PMIX_RELEASE(msg);
return;
}
PMIX_PTL_SEND_RECV(rc, pmix_client_globals.myserver, msg, iof_stdin_cbfunc, rev);
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
PMIX_RELEASE(msg);
}
reactivate:
if (0 < numbytes) {
PMIX_IOF_READ_ACTIVATE(rev);
}
return;
}
static void iof_sink_construct(pmix_iof_sink_t *ptr)
{
PMIX_CONSTRUCT(&ptr->wev, pmix_iof_write_event_t);
ptr->xoff = false;
ptr->exclusive = false;
ptr->closed = false;
}
static void iof_sink_destruct(pmix_iof_sink_t *ptr)
{
if (0 <= ptr->wev.fd) {
pmix_output_verbose
(20, pmix_client_globals.iof_output, "%s iof: closing sink for process %s on fd %d",
PMIX_NAME_PRINT(&pmix_globals.myid), PMIX_NAME_PRINT(&ptr->name), ptr->wev.fd);
PMIX_DESTRUCT(&ptr->wev);
}
}
PMIX_CLASS_INSTANCE(pmix_iof_sink_t, pmix_list_item_t, iof_sink_construct, iof_sink_destruct);
static void iof_read_event_construct(pmix_iof_read_event_t *rev)
{
rev->tv.tv_sec = 0;
rev->tv.tv_usec = 0;
rev->fd = -1;
rev->channel = PMIX_FWD_NO_CHANNELS;
rev->active = false;
rev->childproc = NULL;
rev->always_readable = false;
rev->targets = NULL;
rev->ntargets = 0;
rev->directives = NULL;
rev->ndirs = 0;
}
static void iof_read_event_destruct(pmix_iof_read_event_t *rev)
{
if (rev->active) {
pmix_event_del(&rev->ev);
}
if (0 <= rev->fd) {
pmix_output_verbose(20, pmix_client_globals.iof_output, "%s iof: closing fd %d",
PMIX_NAME_PRINT(&pmix_globals.myid), rev->fd);
close(rev->fd);
rev->fd = -1;
}
if (NULL != rev->targets) {
PMIX_PROC_FREE(rev->targets, rev->ntargets);
}
if (NULL != rev->directives) {
PMIX_INFO_FREE(rev->directives, rev->ndirs);
}
}
PMIX_CLASS_INSTANCE(pmix_iof_read_event_t, pmix_object_t, iof_read_event_construct,
iof_read_event_destruct);
static void iof_write_event_construct(pmix_iof_write_event_t *wev)
{
wev->pending = false;
wev->always_writable = false;
wev->numtries = 0;
wev->ev = (pmix_event_t*)malloc(sizeof(pmix_event_t));
wev->fd = -1;
PMIX_CONSTRUCT(&wev->outputs, pmix_list_t);
wev->tv.tv_sec = 0;
wev->tv.tv_usec = 0;
}
static void iof_write_event_destruct(pmix_iof_write_event_t *wev)
{
if (wev->pending) {
pmix_event_del(wev->ev);
}
free(wev->ev);
if (2 < wev->fd) {
pmix_output_verbose(20, pmix_client_globals.iof_output,
"%s iof: closing fd %d for write event",
PMIX_NAME_PRINT(&pmix_globals.myid), wev->fd);
close(wev->fd);
}
PMIX_LIST_DESTRUCT(&wev->outputs);
}
PMIX_CLASS_INSTANCE(pmix_iof_write_event_t, pmix_list_item_t, iof_write_event_construct,
iof_write_event_destruct);
static void wocon(pmix_iof_write_output_t *p)
{
p->data = NULL;
p->numbytes = 0;
}
static void wodes(pmix_iof_write_output_t *p)
{
if (NULL != p->data) {
free(p->data);
}
}
PMIX_CLASS_INSTANCE(pmix_iof_write_output_t,
pmix_list_item_t,
wocon, wodes);
static void iofrescon(pmix_iof_residual_t *p)
{
PMIX_BYTE_OBJECT_CONSTRUCT(&p->bo);
}
static void iofresdes(pmix_iof_residual_t *p)
{
if (NULL != p->bo.bytes) {
free(p->bo.bytes);
}
}
PMIX_CLASS_INSTANCE(pmix_iof_residual_t,
pmix_list_item_t,
iofrescon, iofresdes);