/* * Copyright (c) 2004-2010 The Trustees of Indiana University and Indiana * University Research and Technology * Corporation. All rights reserved. * Copyright (c) 2004-2007 The University of Tennessee and The University * of Tennessee Research Foundation. All rights * reserved. * Copyright (c) 2004-2006 High Performance Computing Center Stuttgart, * University of Stuttgart. All rights reserved. * Copyright (c) 2004-2005 The Regents of the University of California. * All rights reserved. * Copyright (c) 2006-2007 Cisco Systems, Inc. All rights reserved. * Copyright (c) 2007-2012 Los Alamos National Security, LLC. All rights * reserved. * Copyright (c) 2008-2009 Sun Microsystems, Inc. All rights reserved. * Copyright (c) 2011 IBM Corporation. All rights reserved. * Copyright (c) 2014-2015 Intel Corporation. All rights reserved. * Copyright (c) 2015 Research Organization for Information Science * and Technology (RIST). All rights reserved. * $COPYRIGHT$ * * Additional copyrights may follow * * $HEADER$ * * These symbols are in a file by themselves to provide nice linker * semantics. Since linkers generally pull in symbols by object * files, keeping these symbols as the only symbols in this file * prevents utility programs such as "ompi_info" from having to import * entire components just to query their version and parameters. */ #include "orte_config.h" #include "orte/constants.h" #include #ifdef HAVE_UNISTD_H #include #endif #include #include #ifdef HAVE_STRINGS_H #include #endif #ifdef HAVE_SYS_SELECT_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #include #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_SYS_WAIT_H #include #endif #include #include #ifdef HAVE_PWD_H #include #endif #include "opal/mca/installdirs/installdirs.h" #include "opal/util/output.h" #include "opal/mca/base/base.h" #include "opal/mca/event/event.h" #include "opal/util/argv.h" #include "opal/util/opal_environ.h" #include "opal/util/basename.h" #include "opal/util/path.h" #include "opal/class/opal_pointer_array.h" #include "orte/util/show_help.h" #include "orte/runtime/orte_wait.h" #include "orte/runtime/orte_globals.h" #include "orte/util/name_fns.h" #include "orte/util/nidmap.h" #include "orte/util/proc_info.h" #include "orte/mca/rml/rml.h" #include "orte/mca/rml/rml_types.h" #include "orte/mca/ess/ess.h" #include "orte/mca/ess/base/base.h" #include "orte/mca/errmgr/errmgr.h" #include "orte/mca/rmaps/rmaps.h" #include "orte/mca/routed/routed.h" #include "orte/mca/rml/base/rml_contact.h" #include "orte/mca/state/state.h" #include "orte/mca/plm/plm.h" #include "orte/mca/plm/base/base.h" #include "orte/mca/plm/base/plm_private.h" #include "orte/mca/plm/rsh/plm_rsh.h" static int rsh_init(void); static int rsh_launch(orte_job_t *jdata); static int remote_spawn(opal_buffer_t *launch); static int rsh_terminate_orteds(void); static int rsh_finalize(void); orte_plm_base_module_t orte_plm_rsh_module = { rsh_init, orte_plm_base_set_hnp_name, rsh_launch, remote_spawn, orte_plm_base_orted_terminate_job, rsh_terminate_orteds, orte_plm_base_orted_kill_local_procs, orte_plm_base_orted_signal_local_procs, rsh_finalize }; typedef struct { opal_list_item_t super; int argc; char **argv; orte_proc_t *daemon; } orte_plm_rsh_caddy_t; static void caddy_const(orte_plm_rsh_caddy_t *ptr) { ptr->argv = NULL; ptr->daemon = NULL; } static void caddy_dest(orte_plm_rsh_caddy_t *ptr) { if (NULL != ptr->argv) { opal_argv_free(ptr->argv); } if (NULL != ptr->daemon) { OBJ_RELEASE(ptr->daemon); } } OBJ_CLASS_INSTANCE(orte_plm_rsh_caddy_t, opal_list_item_t, caddy_const, caddy_dest); typedef enum { ORTE_PLM_RSH_SHELL_BASH = 0, ORTE_PLM_RSH_SHELL_ZSH, ORTE_PLM_RSH_SHELL_TCSH, ORTE_PLM_RSH_SHELL_CSH, ORTE_PLM_RSH_SHELL_KSH, ORTE_PLM_RSH_SHELL_SH, ORTE_PLM_RSH_SHELL_UNKNOWN } orte_plm_rsh_shell_t; /* These strings *must* follow the same order as the enum ORTE_PLM_RSH_SHELL_* */ static const char *orte_plm_rsh_shell_name[7] = { "bash", "zsh", "tcsh", /* tcsh has to be first otherwise strstr finds csh */ "csh", "ksh", "sh", "unknown" }; /* * Local functions */ static void set_handler_default(int sig); static orte_plm_rsh_shell_t find_shell(char *shell); static void ssh_child(int argc, char **argv) __opal_attribute_noreturn__; static int rsh_probe(char *nodename, orte_plm_rsh_shell_t *shell); static int setup_shell(orte_plm_rsh_shell_t *rshell, orte_plm_rsh_shell_t *lshell, char *nodename, int *argc, char ***argv); static void launch_daemons(int fd, short args, void *cbdata); static void process_launch_list(int fd, short args, void *cbdata); /* local global storage */ static int num_in_progress=0; static opal_list_t launch_list; static opal_event_t launch_event; /** * Init the module */ static int rsh_init(void) { int rc; /* point to our launch command */ if (ORTE_SUCCESS != (rc = orte_state.add_job_state(ORTE_JOB_STATE_LAUNCH_DAEMONS, launch_daemons, ORTE_SYS_PRI))) { ORTE_ERROR_LOG(rc); return rc; } /* setup the event for metering the launch */ OBJ_CONSTRUCT(&launch_list, opal_list_t); opal_event_set(orte_event_base, &launch_event, -1, 0, process_launch_list, NULL); opal_event_set_priority(&launch_event, ORTE_SYS_PRI); /* start the recvs */ if (ORTE_SUCCESS != (rc = orte_plm_base_comm_start())) { ORTE_ERROR_LOG(rc); } /* we assign daemon nodes at launch */ orte_plm_globals.daemon_nodes_assigned_at_launch = true; return rc; } /** * Callback on daemon exit. */ static void rsh_wait_daemon(orte_proc_t *daemon, void* cbdata) { orte_job_t *jdata; orte_plm_rsh_caddy_t *caddy=(orte_plm_rsh_caddy_t*)cbdata; if (orte_orteds_term_ordered || orte_abnormal_term_ordered) { /* ignore any such report - it will occur if we left the * session attached, e.g., while debugging */ OBJ_RELEASE(caddy); return; } if (! WIFEXITED(daemon->exit_code) || ! WEXITSTATUS(daemon->exit_code) == 0) { /* if abnormal exit */ /* if we are not the HNP, send a message to the HNP alerting it * to the failure */ if (!ORTE_PROC_IS_HNP) { opal_buffer_t *buf; OPAL_OUTPUT_VERBOSE((1, orte_plm_base_framework.framework_output, "%s daemon %d failed with status %d", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), (int)daemon->name.vpid, WEXITSTATUS(daemon->exit_code))); buf = OBJ_NEW(opal_buffer_t); opal_dss.pack(buf, &(daemon->name.vpid), 1, ORTE_VPID); opal_dss.pack(buf, &daemon->exit_code, 1, OPAL_INT); orte_rml.send_buffer_nb(ORTE_PROC_MY_HNP, buf, ORTE_RML_TAG_REPORT_REMOTE_LAUNCH, orte_rml_send_callback, NULL); /* note that this daemon failed */ daemon->state = ORTE_PROC_STATE_FAILED_TO_START; } else { jdata = orte_get_job_data_object(ORTE_PROC_MY_NAME->jobid); OPAL_OUTPUT_VERBOSE((1, orte_plm_base_framework.framework_output, "%s daemon %d failed with status %d", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), (int)daemon->name.vpid, WEXITSTATUS(daemon->exit_code))); /* set the exit status */ ORTE_UPDATE_EXIT_STATUS(WEXITSTATUS(daemon->exit_code)); /* note that this daemon failed */ daemon->state = ORTE_PROC_STATE_FAILED_TO_START; /* increment the #daemons terminated so we will exit properly */ jdata->num_terminated++; /* remove it from the routing table to ensure num_routes * returns the correct value */ orte_routed.route_lost(&daemon->name); /* report that the daemon has failed so we can exit */ ORTE_ACTIVATE_PROC_STATE(&daemon->name, ORTE_PROC_STATE_FAILED_TO_START); } } /* release any delay */ --num_in_progress; if (num_in_progress < mca_plm_rsh_component.num_concurrent) { /* trigger continuation of the launch */ opal_event_active(&launch_event, EV_WRITE, 1); } /* cleanup */ OBJ_RELEASE(caddy); } static int setup_launch(int *argcptr, char ***argvptr, char *nodename, int *node_name_index1, int *proc_vpid_index, char *prefix_dir) { int argc; char **argv; char *param, *value; orte_plm_rsh_shell_t remote_shell, local_shell; int orted_argc; char **orted_argv; char *orted_cmd, *orted_prefix, *final_cmd; int orted_index; int rc; int i, j; bool found; char *lib_base=NULL, *bin_base=NULL; char *opal_prefix = getenv("OPAL_PREFIX"); char* full_orted_cmd = NULL; /* Figure out the basenames for the libdir and bindir. This requires some explanation: - Use opal_install_dirs.libdir and opal_install_dirs.bindir. - After a discussion on the devel-core mailing list, the developers decided that we should use the local directory basenames as the basis for the prefix on the remote note. This does not handle a few notable cases (e.g., if the libdir/bindir is not simply a subdir under the prefix, if the libdir/bindir basename is not the same on the remote node as it is here on the local node, etc.), but we decided that --prefix was meant to handle "the common case". If you need something more complex than this, a) edit your shell startup files to set PATH/LD_LIBRARY_PATH properly on the remove node, or b) use some new/to-be-defined options that explicitly allow setting the bindir/libdir on the remote node. We decided to implement these options (e.g., --remote-bindir and --remote-libdir) to orterun when it actually becomes a problem for someone (vs. a hypothetical situation). Hence, for now, we simply take the basename of this install's libdir and bindir and use it to append this install's prefix and use that on the remote node. */ /* * Build argv array */ argv = opal_argv_copy(mca_plm_rsh_component.agent_argv); argc = opal_argv_count(mca_plm_rsh_component.agent_argv); /* if any ssh args were provided, now is the time to add them */ if (NULL != mca_plm_rsh_component.ssh_args) { char **ssh_argv; ssh_argv = opal_argv_split(mca_plm_rsh_component.ssh_args, ' '); for (i=0; NULL != ssh_argv[i]; i++) { opal_argv_append(&argc, &argv, ssh_argv[i]); } opal_argv_free(ssh_argv); } *node_name_index1 = argc; opal_argv_append(&argc, &argv, "