#include "pmix_config.h"
#include <stdlib.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <errno.h>
#include <sys/types.h>
#ifdef HAVE_SYS_WAIT_H
# include <sys/wait.h>
#endif
#include <signal.h>
#ifdef HAVE_UTIL_H
# include <util.h>
#endif
#ifdef HAVE_PTY_H
# include <pty.h>
#endif
#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif
#ifdef HAVE_TERMIOS_H
# include <termios.h>
# ifdef HAVE_TERMIO_H
# include <termio.h>
# endif
#endif
#ifdef HAVE_LIBUTIL_H
# include <libutil.h>
#endif
#ifdef HAVE_DIRENT_H
# include <dirent.h>
#endif
#include "include/pmix.h"
#include "pmix_common.h"
#include "include/pmix_server.h"
#include "src/include/pmix_globals.h"
#include "src/include/pmix_stdint.h"
#include "src/mca/gds/base/base.h"
#include "src/mca/ptl/base/base.h"
#include "src/threads/pmix_threads.h"
#include "src/util/pmix_argv.h"
#include "src/util/pmix_context_fns.h"
#include "src/util/pmix_error.h"
#include "src/util/pmix_name_fns.h"
#include "src/util/pmix_os_dirpath.h"
#include "src/util/pmix_os_path.h"
#include "src/util/pmix_path.h"
#include "src/util/pmix_environ.h"
#include "src/util/pmix_pty.h"
#include "src/util/pmix_printf.h"
#include "src/util/pmix_show_help.h"
#include "src/client/pmix_client_ops.h"
#include "src/common/pmix_pfexec.h"
#include "src/server/pmix_server_ops.h"
#ifndef MAXPATHLEN
#define MAXPATHLEN 1024
#endif
static pmix_status_t setup_prefork(pmix_pfexec_child_t *child);
static pmix_status_t register_nspace(char *nspace, pmix_setup_caddy_t *fcd);
static void wait_signal_callback(int fd, short event, void *arg);
static int fork_proc(pmix_app_t *app, pmix_pfexec_child_t *child, char **env);
pmix_pfexec_globals_t pmix_pfexec_globals = {
.handler = NULL,
.active = false,
.children = PMIX_LIST_STATIC_INIT,
.timeout_before_sigkill = 0,
.nextid = 0,
.selected = false
};
int pmix_pfexec_base_close(void)
{
if (pmix_pfexec_globals.active) {
pmix_event_del(pmix_pfexec_globals.handler);
pmix_pfexec_globals.active = false;
}
PMIX_LIST_DESTRUCT(&pmix_pfexec_globals.children);
free(pmix_pfexec_globals.handler);
pmix_pfexec_globals.selected = false;
return PMIX_SUCCESS;
}
void pmix_pfexec_check_complete(int sd, short args, void *cbdata)
{
(void) sd;
(void) args;
pmix_pfexec_cmpl_caddy_t *cd = (pmix_pfexec_cmpl_caddy_t *) cbdata;
pmix_info_t info[2];
pmix_status_t rc;
pmix_pfexec_child_t *child;
bool stillalive = false;
pmix_proc_t wildcard;
pmix_list_remove_item(&pmix_pfexec_globals.children, &cd->child->super);
PMIX_LIST_FOREACH (child, &pmix_pfexec_globals.children, pmix_pfexec_child_t) {
if (PMIX_CHECK_NSPACE(child->proc.nspace, cd->child->proc.nspace)) {
stillalive = true;
}
}
if (!stillalive) {
PMIX_INFO_LOAD(&info[0], PMIX_EVENT_NON_DEFAULT, NULL, PMIX_BOOL);
PMIX_LOAD_NSPACE(wildcard.nspace, cd->child->proc.nspace);
PMIX_INFO_LOAD(&info[1], PMIX_EVENT_AFFECTED_PROC, &wildcard, PMIX_PROC);
rc = PMIx_Notify_event(PMIX_ERR_JOB_TERMINATED, &pmix_globals.myid, PMIX_RANGE_PROC_LOCAL,
info, 2, NULL, NULL);
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
}
}
PMIX_RELEASE(cd->child);
PMIX_RELEASE(cd);
}
int pmix_pfexec_register(void)
{
pmix_pfexec_globals.timeout_before_sigkill = 1;
pmix_mca_base_var_register("pmix", "pfexec", "base", "sigkill_timeout",
"Time to wait for a process to die after issuing a kill signal to it",
PMIX_MCA_BASE_VAR_TYPE_INT,
&pmix_pfexec_globals.timeout_before_sigkill);
return PMIX_SUCCESS;
}
int pmix_pfexec_base_open(void)
{
memset(&pmix_pfexec_globals, 0, sizeof(pmix_pfexec_globals_t));
PMIX_CONSTRUCT(&pmix_pfexec_globals.children, pmix_list_t);
pmix_pfexec_globals.nextid = 1;
return PMIX_SUCCESS;
}
static pmix_status_t setup_path(pmix_app_t *app)
{
pmix_status_t rc;
char dir[MAXPATHLEN];
if (NULL != app->cwd) {
if (PMIX_SUCCESS != (rc = pmix_util_check_context_cwd(&app->cwd, true, true))) {
return rc;
}
if (NULL == getcwd(dir, sizeof(dir))) {
return PMIX_ERR_OUT_OF_RESOURCE;
}
PMIx_Setenv("PWD", dir, true, &app->env);
}
rc = pmix_util_check_context_app(&app->cmd, app->cwd, app->env);
return rc;
}
pmix_status_t pmix_pfexec_base_spawn_job(pmix_setup_caddy_t *fcd)
{
sigset_t unblock;
pmix_output_verbose(5, pmix_client_globals.spawn_output,
"%s pfexec:linux spawning child job",
PMIX_NAME_PRINT(&pmix_globals.myid));
if (NULL == pmix_pfexec_globals.handler) {
if (0 != sigemptyset(&unblock)) {
return PMIX_ERROR;
}
if (0 != sigaddset(&unblock, SIGCHLD)) {
return PMIX_ERROR;
}
if (0 != sigprocmask(SIG_UNBLOCK, &unblock, NULL)) {
return PMIX_ERR_NOT_SUPPORTED;
}
pmix_pfexec_globals.handler = (pmix_event_t *) malloc(sizeof(pmix_event_t));
pmix_event_set(pmix_globals.evauxbase, pmix_pfexec_globals.handler, SIGCHLD,
PMIX_EV_SIGNAL | PMIX_EV_PERSIST, wait_signal_callback,
pmix_pfexec_globals.handler);
pmix_pfexec_globals.active = true;
pmix_event_add(pmix_pfexec_globals.handler, NULL);
}
PMIX_PFEXEC_SPAWN(fcd);
return PMIX_SUCCESS;
}
void pmix_pfexec_base_spawn_proc(int sd, short args, void *cbdata)
{
pmix_setup_caddy_t *fcd = (pmix_setup_caddy_t *) cbdata;
pmix_app_t *app;
int i, n;
size_t m, k;
pmix_status_t rc;
char **argv = NULL, **env = NULL;
pmix_nspace_t nspace;
char basedir[MAXPATHLEN], sock[10];
pmix_pfexec_child_t *child;
pmix_rank_info_t *info;
pmix_namespace_t *nptr;
pmix_rank_t rank = 0;
char tmp[2048];
bool nohup = false;
char *security_mode;
PMIX_HIDE_UNUSED_PARAMS(sd, args);
pmix_output_verbose(5, pmix_client_globals.spawn_output,
"%s pfexec:base spawn proc",
PMIX_NAME_PRINT(&pmix_globals.myid));
if (NULL == getcwd(basedir, sizeof(basedir))) {
rc = PMIX_ERROR;
goto complete;
}
memset(tmp, 0, 2048);
(void) pmix_snprintf(tmp, 2047, "%s:%lu", pmix_globals.myid.nspace,
(unsigned long) pmix_pfexec_globals.nextid);
PMIX_LOAD_NSPACE(nspace, tmp);
++pmix_pfexec_globals.nextid;
nptr = PMIX_NEW(pmix_namespace_t);
nptr->nspace = strdup(nspace);
pmix_list_append(&pmix_globals.nspaces, &nptr->super);
rc = register_nspace(nspace, fcd);
if (PMIX_SUCCESS != rc) {
pmix_list_remove_item(&pmix_globals.nspaces, &nptr->super);
PMIX_RELEASE(nptr);
goto complete;
}
for (k = 0; k < fcd->ninfo; k++) {
if (PMIX_CHECK_KEY(&fcd->info[k], PMIX_SET_ENVAR)) {
} else if (PMIX_CHECK_KEY(&fcd->info[k], PMIX_ADD_ENVAR)) {
} else if (PMIX_CHECK_KEY(&fcd->info[k], PMIX_UNSET_ENVAR)) {
} else if (PMIX_CHECK_KEY(&fcd->info[k], PMIX_PREPEND_ENVAR)) {
} else if (PMIX_CHECK_KEY(&fcd->info[k], PMIX_APPEND_ENVAR)) {
} else if (PMIX_CHECK_KEY(&fcd->info[k], PMIX_NOHUP)) {
nohup = PMIX_INFO_TRUE(&fcd->info[k]);
}
}
for (m = 0; m < fcd->napps; m++) {
app = (pmix_app_t *) &fcd->apps[m];
rc = pmix_environ_merge_inplace(&app->env, environ);
if (PMIX_SUCCESS != rc) {
goto complete;
}
if (NULL != app->info) {
for (k = 0; k < app->ninfo; k++) {
if (PMIX_CHECK_KEY(&app->info[k], PMIX_FORKEXEC_AGENT)) {
argv = PMIx_Argv_split(app->info[k].value.data.string, ' ');
for (i = 0; NULL != argv[i]; i++) {
PMIx_Argv_prepend_nosize(&app->argv, argv[i]);
}
if (NULL != app->cmd) {
free(app->cmd);
}
app->cmd = pmix_path_findv(argv[0], X_OK, app->env, NULL);
if (NULL == app->cmd) {
pmix_show_help("help-pfexec-base.txt", "fork-agent-not-found", true,
pmix_globals.hostname, argv[0]);
rc = PMIX_ERR_NOT_FOUND;
PMIx_Argv_free(argv);
goto complete;
}
PMIx_Argv_free(argv);
}
}
}
if (PMIX_SUCCESS != (rc = setup_path(app))) {
goto complete;
}
for (n = 0; n < app->maxprocs; n++) {
child = PMIX_NEW(pmix_pfexec_child_t);
PMIX_LOAD_PROCID(&child->proc, nspace, rank);
++rank;
pmix_list_append(&pmix_pfexec_globals.children, &child->super);
child->opts.usepty = PMIX_ENABLE_PTY_SUPPORT;
if (PMIX_SUCCESS != (rc = setup_prefork(child))) {
PMIX_ERROR_LOG(rc);
pmix_list_remove_item(&pmix_pfexec_globals.children, &child->super);
PMIX_RELEASE(child);
goto complete;
}
info = PMIX_NEW(pmix_rank_info_t);
if (NULL == info) {
rc = PMIX_ERR_NOMEM;
pmix_list_remove_item(&pmix_pfexec_globals.children, &child->super);
PMIX_RELEASE(child);
goto complete;
}
info->pname.nspace = strdup(child->proc.nspace);
info->pname.rank = child->proc.rank;
info->uid = pmix_globals.uid;
info->gid = pmix_globals.gid;
pmix_list_append(&nptr->ranks, &info->super);
env = PMIx_Argv_copy(app->env);
PMIx_Setenv("PMIX_NAMESPACE", child->proc.nspace, true, &env);
PMIx_Setenv("PMIX_SERVER_NSPACE", child->proc.nspace, true, &env);
memset(tmp, 0, 2048);
(void) pmix_snprintf(tmp, 2047, "%u", child->proc.rank);
PMIx_Setenv("PMIX_RANK", tmp, true, &env);
PMIx_Setenv("PMIX_SERVER_RANK", tmp, true, &env);
security_mode = pmix_psec_base_get_available_modules();
PMIx_Setenv("PMIX_SECURITY_MODE", security_mode, true, &env);
free(security_mode);
if (PMIX_BFROP_BUFFER_FULLY_DESC == pmix_globals.mypeer->nptr->compat.type) {
PMIx_Setenv("PMIX_BFROP_BUFFER_TYPE", "PMIX_BFROP_BUFFER_FULLY_DESC", true, &env);
} else {
PMIx_Setenv("PMIX_BFROP_BUFFER_TYPE", "PMIX_BFROP_BUFFER_NON_DESC", true, &env);
}
if (PMIX_SUCCESS != (rc = pmix_ptl.setup_fork(&child->proc, &env))) {
PMIX_ERROR_LOG(rc);
pmix_list_remove_item(&pmix_pfexec_globals.children, &child->super);
PMIX_RELEASE(child);
goto complete;
}
PMIx_Setenv("PMIX_HOSTNAME", pmix_globals.hostname, true, &env);
PMIx_Setenv("PMIX_VERSION", PMIX_VERSION, true, &env);
if (!nohup) {
rc = pipe(child->keepalive);
if (0 != rc) {
PMIX_ERROR_LOG(PMIX_ERR_SYS_OTHER);
pmix_list_remove_item(&pmix_pfexec_globals.children, &child->super);
PMIX_RELEASE(child);
goto complete;
}
pmix_snprintf(sock, 10, "%d", child->keepalive[1]);
PMIx_Setenv("PMIX_KEEPALIVE_PIPE", sock, true, &env);
}
pmix_output_verbose(5, pmix_client_globals.spawn_output,
"%s pfexec:base spawning child %s",
PMIX_NAME_PRINT(&pmix_globals.myid), app->cmd);
rc = fork_proc(app, child, env);
PMIx_Argv_free(env);
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
pmix_list_remove_item(&pmix_pfexec_globals.children, &child->super);
PMIX_RELEASE(child);
goto complete;
}
PMIX_IOF_READ_ACTIVATE(child->stdoutev);
PMIX_IOF_READ_ACTIVATE(child->stderrev);
}
}
rc = PMIX_SUCCESS;
complete:
if (0 != chdir(basedir)) {
PMIX_ERROR_LOG(PMIX_ERROR);
}
fcd->spcbfunc(rc, nspace, fcd->cbdata);
PMIX_RELEASE(fcd);
return;
}
static pmix_status_t sigproc(pid_t pd, int signum)
{
pid_t pgrp;
pid_t pid;
pid = pd;
#if HAVE_SETPGID
pgrp = getpgid(pd);
if (-1 != pgrp) {
pid = -pgrp;
}
#endif
if (0 != kill(pid, signum)) {
if (ESRCH != errno) {
pmix_output_verbose(2, pmix_client_globals.spawn_output,
"%s pfexec:linux:SENT SIGNAL %d TO PID %d GOT ERRNO %d",
PMIX_NAME_PRINT(&pmix_globals.myid), signum, (int) pid, errno);
return errno;
}
}
pmix_output_verbose(2, pmix_client_globals.spawn_output,
"%s pfexec:linux:SENT SIGNAL %d TO PID %d SUCCESS",
PMIX_NAME_PRINT(&pmix_globals.myid), signum, (int) pid);
return 0;
}
void pmix_pfexec_base_kill_proc(int sd, short args, void *cbdata)
{
pmix_pfexec_signal_caddy_t *scd = (pmix_pfexec_signal_caddy_t *) cbdata;
pmix_pfexec_child_t *child, *cd;
PMIX_HIDE_UNUSED_PARAMS(sd, args);
child = NULL;
PMIX_LIST_FOREACH (cd, &pmix_pfexec_globals.children, pmix_pfexec_child_t) {
if (PMIX_CHECK_PROCID(scd->proc, &cd->proc)) {
child = cd;
break;
}
}
if (NULL == child) {
scd->lock->status = PMIX_SUCCESS;
PMIX_WAKEUP_THREAD(scd->lock);
PMIX_RELEASE(scd);
return;
}
pmix_list_remove_item(&pmix_pfexec_globals.children, &child->super);
pmix_output_verbose(5, pmix_client_globals.spawn_output, "%s SENDING SIGCONT",
PMIX_NAME_PRINT(&pmix_globals.myid));
sigproc(child->pid, SIGCONT);
sleep(pmix_pfexec_globals.timeout_before_sigkill);
pmix_output_verbose(5, pmix_client_globals.spawn_output, "%s SENDING SIGTERM",
PMIX_NAME_PRINT(&pmix_globals.myid));
scd->lock->status = sigproc(child->pid, SIGTERM);
if (0 != scd->lock->status) {
sleep(pmix_pfexec_globals.timeout_before_sigkill);
pmix_output_verbose(5, pmix_client_globals.spawn_output, "%s SENDING SIGKILL",
PMIX_NAME_PRINT(&pmix_globals.myid));
scd->lock->status = sigproc(child->pid, SIGKILL);
}
PMIX_RELEASE(child);
PMIX_WAKEUP_THREAD(scd->lock);
PMIX_RELEASE(scd);
return;
}
void pmix_pfexec_base_signal_proc(int sd, short args, void *cbdata)
{
pmix_pfexec_signal_caddy_t *scd = (pmix_pfexec_signal_caddy_t *) cbdata;
pmix_pfexec_child_t *child, *cd;
PMIX_HIDE_UNUSED_PARAMS(sd, args);
child = NULL;
PMIX_LIST_FOREACH (cd, &pmix_pfexec_globals.children, pmix_pfexec_child_t) {
if (PMIX_CHECK_PROCID(scd->proc, &cd->proc)) {
child = cd;
break;
}
}
if (NULL == child) {
scd->lock->status = PMIX_SUCCESS;
PMIX_WAKEUP_THREAD(scd->lock);
return;
}
pmix_output_verbose(5, pmix_client_globals.spawn_output, "%s SIGNALING %d",
PMIX_NAME_PRINT(&pmix_globals.myid), scd->signal);
scd->lock->status = sigproc(child->pid, scd->signal);
PMIX_WAKEUP_THREAD(scd->lock);
}
static pmix_status_t setup_prefork(pmix_pfexec_child_t *child)
{
int ret = -1;
pmix_pfexec_base_io_conf_t *opts = &child->opts;
fflush(stdout);
#if PMIX_ENABLE_PTY_SUPPORT
if (opts->usepty) {
ret = pmix_openpty(&(opts->p_stdout[0]), &(opts->p_stdout[1]), (char *) NULL,
(struct termios *) NULL, (struct winsize *) NULL);
}
#else
opts->usepty = 0;
#endif
if (ret < 0) {
opts->usepty = 0;
if (pipe(opts->p_stdout) < 0) {
PMIX_ERROR_LOG(PMIX_ERR_SYS_OTHER);
return PMIX_ERR_SYS_OTHER;
}
}
if (pipe(opts->p_stdin) < 0) {
PMIX_ERROR_LOG(PMIX_ERR_SYS_OTHER);
return PMIX_ERR_SYS_OTHER;
}
if (pipe(opts->p_stderr) < 0) {
PMIX_ERROR_LOG(PMIX_ERR_SYS_OTHER);
return PMIX_ERR_SYS_OTHER;
}
PMIX_IOF_SINK_DEFINE(&child->stdinsink, &child->proc, opts->p_stdin[1],
PMIX_FWD_STDIN_CHANNEL, pmix_iof_write_handler);
PMIX_IOF_READ_EVENT_LOCAL(&child->stdoutev, opts->p_stdout[0],
pmix_iof_read_local_handler, false);
PMIX_LOAD_PROCID(&child->stdoutev->name, child->proc.nspace, child->proc.rank);
child->stdoutev->childproc = (void *) child;
child->stdoutev->channel = PMIX_FWD_STDOUT_CHANNEL;
PMIX_IOF_READ_EVENT_LOCAL(&child->stderrev, opts->p_stderr[0],
pmix_iof_read_local_handler, false);
PMIX_LOAD_PROCID(&child->stderrev->name, child->proc.nspace, child->proc.rank);
child->stderrev->childproc = (void *) child;
child->stderrev->channel = PMIX_FWD_STDERR_CHANNEL;
return PMIX_SUCCESS;
}
pmix_status_t pmix_pfexec_base_setup_child(pmix_pfexec_child_t *child)
{
int ret;
pmix_pfexec_base_io_conf_t *opts = &child->opts;
if (0 <= opts->p_stdin[1]) {
close(opts->p_stdin[1]);
opts->p_stdin[1] = -1;
}
if (0 <= opts->p_stdout[0]) {
close(opts->p_stdout[0]);
opts->p_stdout[0] = -1;
}
if (0 <= opts->p_stderr[0]) {
close(opts->p_stderr[0]);
opts->p_stderr[0] = -1;
}
if (opts->usepty) {
struct termios term_attrs;
if (tcgetattr(opts->p_stdout[1], &term_attrs) < 0) {
return PMIX_ERR_SYS_OTHER;
}
term_attrs.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | ECHONL);
term_attrs.c_iflag &= ~(ICRNL | INLCR | ISTRIP | INPCK | IXON);
term_attrs.c_oflag &= ~(OCRNL | ONLCR);
if (tcsetattr(opts->p_stdout[1], TCSANOW, &term_attrs) == -1) {
return PMIX_ERR_SYS_OTHER;
}
ret = dup2(opts->p_stdout[1], fileno(stdout));
if (ret < 0) {
return PMIX_ERR_SYS_OTHER;
}
if (0 <= opts->p_stdout[1]) {
close(opts->p_stdout[1]);
opts->p_stdout[1] = -1;
}
} else {
if (opts->p_stdout[1] != fileno(stdout)) {
ret = dup2(opts->p_stdout[1], fileno(stdout));
if (ret < 0) {
return PMIX_ERR_SYS_OTHER;
}
if (0 <= opts->p_stdout[1]) {
close(opts->p_stdout[1]);
opts->p_stdout[1] = -1;
}
}
}
if (opts->p_stdin[0] != fileno(stdin)) {
ret = dup2(opts->p_stdin[0], fileno(stdin));
if (ret < 0) {
return PMIX_ERR_SYS_OTHER;
}
if (0 <= opts->p_stdin[0]) {
close(opts->p_stdin[0]);
opts->p_stdin[0] = -1;
}
}
if (opts->p_stderr[1] != fileno(stderr)) {
ret = dup2(opts->p_stderr[1], fileno(stderr));
if (ret < 0) {
return PMIX_ERR_SYS_OTHER;
}
if (0 <= opts->p_stderr[1]) {
close(opts->p_stderr[1]);
opts->p_stderr[1] = -1;
}
}
return PMIX_SUCCESS;
}
static pmix_status_t register_nspace(char *nspace, pmix_setup_caddy_t *fcd)
{
pmix_status_t rc;
size_t n, ninfo;
int m;
uint32_t nprocs, u32;
uint16_t u16;
pmix_proc_t proc;
pmix_rank_t zero = 0, rk;
pmix_info_t *info = NULL;
pmix_namespace_t *nptr, *tmp;
void *jinfo, *tmpinfo, *pinfo;
pmix_data_array_t darray;
char *str;
nprocs = 0;
for (n = 0; n < fcd->napps; n++) {
nprocs += fcd->apps[n].maxprocs;
}
if (0 == nprocs) {
return PMIX_ERR_BAD_PARAM;
}
nptr = NULL;
PMIX_LIST_FOREACH (tmp, &pmix_globals.nspaces, pmix_namespace_t) {
if (0 == strcmp(tmp->nspace, nspace)) {
nptr = tmp;
break;
}
}
if (NULL == nptr) {
nptr = PMIX_NEW(pmix_namespace_t);
if (NULL == nptr) {
return PMIX_ERR_NOMEM;
}
nptr->nspace = strdup(nspace);
pmix_list_append(&pmix_globals.nspaces, &nptr->super);
}
nptr->nlocalprocs = nprocs;
PMIX_INFO_LIST_START(jinfo);
PMIX_LOAD_PROCID(&proc, nspace, PMIX_RANK_UNDEF);
PMIX_INFO_LIST_ADD(rc, jinfo, PMIX_JOBID, &proc, PMIX_PROC);
if (PMIX_SUCCESS != rc) {
PMIX_INFO_LIST_RELEASE(jinfo);
return rc;
}
PMIX_INFO_LIST_ADD(rc, jinfo, PMIX_HOSTNAME, pmix_globals.hostname, PMIX_STRING);
if (PMIX_SUCCESS != rc) {
PMIX_INFO_LIST_RELEASE(jinfo);
return rc;
}
PMIX_INFO_LIST_ADD(rc, jinfo, PMIX_PARENT_ID, &pmix_globals.myid, PMIX_PROC);
if (PMIX_SUCCESS != rc) {
PMIX_INFO_LIST_RELEASE(jinfo);
return rc;
}
PMIX_INFO_LIST_ADD(rc, jinfo, PMIX_NODE_SIZE, &nprocs, PMIX_UINT32);
if (PMIX_SUCCESS != rc) {
PMIX_INFO_LIST_RELEASE(jinfo);
return rc;
}
PMIX_INFO_LIST_ADD(rc, jinfo, PMIX_LOCAL_SIZE, &nprocs, PMIX_UINT32);
if (PMIX_SUCCESS != rc) {
PMIX_INFO_LIST_RELEASE(jinfo);
return rc;
}
PMIX_INFO_LIST_ADD(rc, jinfo, PMIX_LOCALLDR, &zero, PMIX_PROC_RANK);
if (PMIX_SUCCESS != rc) {
PMIX_INFO_LIST_RELEASE(jinfo);
return rc;
}
for (n = 0; n < fcd->ninfo; n++) {
if (PMIX_ENVAR == fcd->info[n].value.type) {
continue; }
PMIX_INFO_LIST_XFER(rc, jinfo, &fcd->info[n]);
}
proc.rank = 0;
rk = 0;
for (n = 0; n < fcd->napps; n++) {
PMIX_INFO_LIST_START(tmpinfo);
u32 = n;
PMIX_INFO_LIST_ADD(rc, tmpinfo, PMIX_APPNUM, &u32, PMIX_UINT32);
u32 = fcd->apps[n].maxprocs;
PMIX_INFO_LIST_ADD(rc, tmpinfo, PMIX_APP_SIZE, &u32, PMIX_UINT32);
PMIX_INFO_LIST_ADD(rc, tmpinfo, PMIX_APPLDR, &proc.rank, PMIX_PROC_RANK);
proc.rank += fcd->apps[n].maxprocs;
if (NULL != fcd->apps[n].cwd) {
PMIX_INFO_LIST_ADD(rc, tmpinfo, PMIX_WDIR, fcd->apps[n].cwd, PMIX_STRING);
}
str = PMIx_Argv_join(fcd->apps[n].argv, ' ');
PMIX_INFO_LIST_ADD(rc, tmpinfo, PMIX_APP_ARGV, str, PMIX_STRING);
PMIX_INFO_LIST_CONVERT(rc, tmpinfo, &darray);
PMIX_INFO_LIST_RELEASE(tmpinfo);
PMIX_INFO_LIST_ADD(rc, jinfo, PMIX_APP_INFO_ARRAY, &darray, PMIX_DATA_ARRAY);
PMIX_DATA_ARRAY_DESTRUCT(&darray);
for (m = 0; m < fcd->apps[n].maxprocs; m++) {
PMIX_INFO_LIST_START(pinfo);
PMIX_INFO_LIST_ADD(rc, pinfo, PMIX_RANK, &rk, PMIX_PROC_RANK);
++rk;
u32 = n;
PMIX_INFO_LIST_ADD(rc, pinfo, PMIX_APPNUM, &u32, PMIX_UINT32);
u16 = rk;
PMIX_INFO_LIST_ADD(rc, pinfo, PMIX_LOCAL_RANK, &u16, PMIX_UINT16);
PMIX_INFO_LIST_CONVERT(rc, pinfo, &darray);
PMIX_INFO_LIST_RELEASE(pinfo);
PMIX_INFO_LIST_ADD(rc, jinfo, PMIX_PROC_DATA, &darray, PMIX_DATA_ARRAY);
PMIX_DATA_ARRAY_DESTRUCT(&darray);
}
}
PMIX_INFO_LIST_CONVERT(rc, jinfo, &darray);
PMIX_INFO_LIST_RELEASE(jinfo);
info = (pmix_info_t *) darray.array;
ninfo = darray.size;
PMIX_GDS_ADD_NSPACE(rc, nptr->nspace, nprocs, info, ninfo);
if (PMIX_SUCCESS == rc) {
PMIX_GDS_CACHE_JOB_INFO(rc, pmix_globals.mypeer, nptr, info, ninfo);
}
PMIX_DATA_ARRAY_DESTRUCT(&darray);
return rc;
}
static void set_handler_linux(int sig)
{
struct sigaction act;
act.sa_handler = SIG_DFL;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
sigaction(sig, &act, (struct sigaction *) 0);
}
static int write_help_msg(int fd, pmix_pfexec_pipe_err_msg_t *msg, const char *file,
const char *topic, va_list ap)
{
int ret;
char *str;
if (NULL == file || NULL == topic) {
return PMIX_ERR_BAD_PARAM;
}
str = pmix_show_help_vstring(file, topic, true, ap);
msg->file_str_len = (int) strlen(file);
if (msg->file_str_len > PMIX_PFEXEC_MAX_FILE_LEN) {
PMIX_ERROR_LOG(PMIX_ERR_BAD_PARAM);
return PMIX_ERR_BAD_PARAM;
}
msg->topic_str_len = (int) strlen(topic);
if (msg->topic_str_len > PMIX_PFEXEC_MAX_TOPIC_LEN) {
PMIX_ERROR_LOG(PMIX_ERR_BAD_PARAM);
return PMIX_ERR_BAD_PARAM;
}
msg->msg_str_len = (int) strlen(str);
if (PMIX_SUCCESS != (ret = pmix_fd_write(fd, sizeof(*msg), msg))) {
goto out;
}
if (msg->file_str_len > 0
&& PMIX_SUCCESS != (ret = pmix_fd_write(fd, msg->file_str_len, file))) {
goto out;
}
if (msg->topic_str_len > 0
&& PMIX_SUCCESS != (ret = pmix_fd_write(fd, msg->topic_str_len, topic))) {
goto out;
}
if (msg->msg_str_len > 0 && PMIX_SUCCESS != (ret = pmix_fd_write(fd, msg->msg_str_len, str))) {
goto out;
}
out:
free(str);
return ret;
}
static void send_error_show_help(int fd, int exit_status, const char *file, const char *topic, ...)
{
va_list ap;
pmix_pfexec_pipe_err_msg_t msg;
msg.fatal = true;
msg.exit_status = exit_status;
va_start(ap, topic);
write_help_msg(fd, &msg, file, topic, ap);
va_end(ap);
exit(exit_status);
}
static int close_open_file_descriptors(int write_fd, int keepalive)
{
#if defined(__OSX__)
DIR *dir = opendir("/dev/fd");
#else
DIR *dir = opendir("/proc/self/fd");
#endif
if (NULL == dir) {
return PMIX_ERR_FILE_OPEN_FAILURE;
}
struct dirent *files;
int dir_scan_fd = dirfd(dir);
if (dir_scan_fd < 0) {
return PMIX_ERR_FILE_OPEN_FAILURE;
}
while (NULL != (files = readdir(dir))) {
if (!isdigit(files->d_name[0])) {
continue;
}
int fd = strtol(files->d_name, NULL, 10);
if (errno == EINVAL || errno == ERANGE) {
closedir(dir);
return PMIX_ERR_TYPE_MISMATCH;
}
if (fd >= 3 && fd != write_fd && fd != dir_scan_fd && fd != keepalive) {
close(fd);
}
}
closedir(dir);
return PMIX_SUCCESS;
}
static void do_child(pmix_app_t *app, char **env, pmix_pfexec_child_t *child, int write_fd)
{
int i, errval;
sigset_t sigs;
long fd, fdmax = sysconf(_SC_OPEN_MAX);
char dir[MAXPATHLEN];
#if HAVE_SETPGID
setpgid(0, 0);
#endif
pmix_fd_set_cloexec(write_fd);
if (PMIX_SUCCESS != (i = pmix_pfexec_base_setup_child(child))) {
PMIX_ERROR_LOG(i);
send_error_show_help(write_fd, 1, "help-pfexec-linux.txt", "iof setup failed",
pmix_globals.hostname, app->cmd);
}
if (PMIX_SUCCESS != close_open_file_descriptors(write_fd, child->keepalive[1])) {
for (fd = 3; fd < fdmax; fd++) {
if (fd != write_fd && fd != child->keepalive[1]) {
close(fd);
}
}
}
set_handler_linux(SIGTERM);
set_handler_linux(SIGINT);
set_handler_linux(SIGHUP);
set_handler_linux(SIGPIPE);
set_handler_linux(SIGCHLD);
sigprocmask(0, 0, &sigs);
sigprocmask(SIG_UNBLOCK, &sigs, 0);
if (NULL != app->cwd) {
if (0 != chdir(app->cwd)) {
send_error_show_help(write_fd, 1, "help-pfexec-linux.txt", "wdir-not-found", "pmixd",
app->cwd, pmix_globals.hostname);
}
}
execve(app->cmd, app->argv, env);
errval = errno;
if (0 != getcwd(dir, sizeof(dir))) {
pmix_strncpy(dir, "GETCWD-FAILED", sizeof(dir));
}
send_error_show_help(write_fd, 1, "help-pfexec-linux.txt", "execve error",
pmix_globals.hostname, dir, app->cmd, strerror(errval));
}
static pmix_status_t do_parent(pmix_app_t *app, pmix_pfexec_child_t *child, int read_fd)
{
pmix_status_t rc;
pmix_pfexec_pipe_err_msg_t msg;
char file[PMIX_PFEXEC_MAX_FILE_LEN + 1], topic[PMIX_PFEXEC_MAX_TOPIC_LEN + 1], *str = NULL;
if (child->opts.connect_stdin && 0 <= child->opts.p_stdin[0]) {
close(child->opts.p_stdin[0]);
}
if (0 <= child->opts.p_stdout[1]) {
close(child->opts.p_stdout[1]);
}
if (0 <= child->opts.p_stderr[1])
close(child->opts.p_stderr[1]);
if (0 <= child->keepalive[1]) {
close(child->keepalive[1]);
}
while (1) {
rc = pmix_fd_read(read_fd, sizeof(msg), &msg);
if (PMIX_ERR_TIMEOUT == rc) {
break;
}
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
close(read_fd);
return rc;
}
if (msg.file_str_len > 0) {
rc = pmix_fd_read(read_fd, msg.file_str_len, file);
if (PMIX_SUCCESS != rc) {
pmix_show_help("help-pfexec-linux.txt", "syscall fail", true, pmix_globals.hostname,
app->cmd, "pmix_fd_read", __FILE__, __LINE__);
return rc;
}
file[msg.file_str_len] = '\0';
}
if (msg.topic_str_len > 0) {
rc = pmix_fd_read(read_fd, msg.topic_str_len, topic);
if (PMIX_SUCCESS != rc) {
pmix_show_help("help-pfexec-linux.txt", "syscall fail", true, pmix_globals.hostname,
app->cmd, "pmix_fd_read", __FILE__, __LINE__);
return rc;
}
topic[msg.topic_str_len] = '\0';
}
if (msg.msg_str_len > 0) {
str = calloc(1, msg.msg_str_len + 1);
if (NULL == str) {
pmix_show_help("help-pfexec-linux.txt", "syscall fail", true, pmix_globals.hostname,
app->cmd, "calloc", __FILE__, __LINE__);
return PMIX_ERR_NOMEM;
}
rc = pmix_fd_read(read_fd, msg.msg_str_len, str);
if (PMIX_SUCCESS != rc) {
pmix_show_help("help-pfexec-linux.txt", "syscall fail", true, pmix_globals.hostname,
app->cmd, "pmix_fd_read", __FILE__, __LINE__);
free(str);
return rc;
}
str[msg.msg_str_len] = '\0'; }
if (msg.msg_str_len > 0) {
fprintf(stderr, "%s\n", str);
free(str);
str = NULL;
}
if (msg.fatal) {
close(read_fd);
if (NULL != str) {
free(str);
}
return PMIX_ERR_SYS_OTHER;
}
if (NULL != str) {
free(str);
str = NULL;
}
}
close(read_fd);
return PMIX_SUCCESS;
}
static int fork_proc(pmix_app_t *app, pmix_pfexec_child_t *child, char **env)
{
int p[2];
if (pipe(p) < 0) {
PMIX_ERROR_LOG(PMIX_ERR_SYS_OTHER);
return PMIX_ERR_SYS_OTHER;
}
child->pid = fork();
if (child->pid < 0) {
PMIX_ERROR_LOG(PMIX_ERR_SYS_OTHER);
return PMIX_ERR_SYS_OTHER;
}
if (child->pid == 0) {
if (0 <= p[0]) {
close(p[0]);
}
if (0 <= child->keepalive[0]) {
close(child->keepalive[0]);
child->keepalive[0] = -1;
}
do_child(app, env, child, p[1]);
}
close(p[1]);
return do_parent(app, child, p[0]);
}
static void wait_signal_callback(int fd, short event, void *arg)
{
(void) fd;
(void) event;
pmix_event_t *signal = (pmix_event_t *) arg;
int status;
pid_t pid;
pmix_pfexec_child_t *child;
PMIX_ACQUIRE_OBJECT(signal);
if (SIGCHLD != PMIX_EVENT_SIGNAL(signal)) {
return;
}
if (0 == pmix_list_get_size(&pmix_pfexec_globals.children)) {
return;
}
while (1) {
pid = waitpid(-1, &status, WNOHANG);
if (-1 == pid && EINTR == errno) {
continue;
}
if (pid <= 0) {
return;
}
PMIX_LIST_FOREACH (child, &pmix_pfexec_globals.children, pmix_pfexec_child_t) {
if (pid == child->pid) {
if (WIFEXITED(status)) {
child->exitcode = WEXITSTATUS(status);
} else {
if (WIFSIGNALED(status)) {
child->exitcode = WTERMSIG(status) + 128;
}
}
child->completed = true;
if ((NULL == child->stdoutev || !child->stdoutev->active)
&& (NULL == child->stderrev || !child->stderrev->active)) {
PMIX_PFEXEC_CHK_COMPLETE(child);
}
break;
}
}
}
}
static void chcon(pmix_pfexec_child_t *p)
{
memset(&p->ev, 0, sizeof(pmix_event_t));
PMIX_LOAD_PROCID(&p->proc, NULL, PMIX_RANK_UNDEF);
p->pid = 0;
p->completed = false;
p->keepalive[0] = -1;
p->keepalive[1] = -1;
memset(&p->opts, 0, sizeof(pmix_pfexec_base_io_conf_t));
p->opts.p_stdin[0] = -1;
p->opts.p_stdin[1] = -1;
p->opts.p_stdout[0] = -1;
p->opts.p_stdout[1] = -1;
p->opts.p_stderr[0] = -1;
p->opts.p_stderr[1] = -1;
PMIX_CONSTRUCT(&p->stdinsink, pmix_iof_sink_t);
p->stdoutev = NULL;
p->stderrev = NULL;
}
static void chdes(pmix_pfexec_child_t *p)
{
PMIX_DESTRUCT(&p->stdinsink);
if (NULL != p->stdoutev) {
PMIX_RELEASE(p->stdoutev);
}
if (NULL != p->stderrev) {
PMIX_RELEASE(p->stderrev);
}
if (0 <= p->keepalive[0]) {
close(p->keepalive[0]);
}
if (0 <= p->keepalive[1]) {
close(p->keepalive[1]);
}
}
PMIX_CLASS_INSTANCE(pmix_pfexec_child_t,
pmix_list_item_t,
chcon, chdes);
PMIX_CLASS_INSTANCE(pmix_pfexec_signal_caddy_t,
pmix_object_t, NULL, NULL);
PMIX_CLASS_INSTANCE(pmix_pfexec_cmpl_caddy_t,
pmix_object_t, NULL, NULL);