/* * Copyright (c) 2004-2005 The Trustees of Indiana University and Indiana * University Research and Technology * Corporation. All rights reserved. * Copyright (c) 2004-2009 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 Cisco Systems, Inc. All rights reserved. * Copyright (c) 2007 Los Alamos National Security, LLC. 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_SYS_SELECT_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #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 #include #include #include #pragma comment(lib, "wbemuuid.lib") #pragma comment(lib, "comsuppw.lib") #pragma comment(lib, "Credui.lib") #include "opal/mca/installdirs/installdirs.h" #include "opal/mca/base/mca_base_param.h" #include "opal/util/output.h" #include "opal/util/os_path.h" #include "opal/util/path.h" #include "opal/event/event.h" #include "opal/util/argv.h" #include "opal/util/opal_environ.h" #include "orte/util/show_help.h" #include "opal/util/trace.h" #include "opal/util/basename.h" #include "opal/util/opal_environ.h" #include "orte/util/name_fns.h" #include "orte/runtime/orte_wait.h" #include "orte/runtime/orte_globals.h" #include "orte/mca/errmgr/errmgr.h" #include "orte/mca/ras/ras_types.h" #include "orte/mca/rmaps/rmaps.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/process/plm_process.h" #define rindex(a,b) strrchr((a),(b)) #if OPAL_HAVE_POSIX_THREADS && OPAL_THREADS_HAVE_DIFFERENT_PIDS && OPAL_ENABLE_PROGRESS_THREADS static int orte_plm_process_launch_threaded(orte_jobid_t jobid); #endif /* * Interface */ static int orte_plm_process_init(void); static int orte_plm_process_launch(orte_job_t*); static int orte_plm_process_terminate_orteds(void); static int orte_plm_process_signal_job(orte_jobid_t, int32_t); orte_plm_base_module_t orte_plm_process_module = { orte_plm_process_init, orte_plm_base_set_hnp_name, #if OPAL_HAVE_POSIX_THREADS && OPAL_THREADS_HAVE_DIFFERENT_PIDS && OPAL_ENABLE_PROGRESS_THREADS orte_plm_process_launch_threaded, #else orte_plm_process_launch, #endif NULL, orte_plm_base_orted_terminate_job, orte_plm_process_terminate_orteds, orte_plm_base_orted_kill_local_procs, orte_plm_process_signal_job, orte_plm_process_finalize }; static void set_handler_default(int sig); 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 }; typedef int orte_plm_process_shell; static const char * orte_plm_process_shell_name[] = { "bash", "zsh", "tcsh", /* tcsh has to be first otherwise strstr finds csh */ "csh", "ksh", "sh", "unknown" }; /* * local functions */ static char *generate_commandline(char *prefix, int argc, char **argv); static int wmi_launch_child(char *prefix, char *remote_node, int argc, char **argv); static int get_credential(char *node_name); static char *read_remote_registry(uint32_t root, char *sub_key, char *key, char *remote_node, char *ntlm_auth); /* local global storage of timing variables */ static struct timeval joblaunchstart, joblaunchstop; /* local global storage of user credential */ static char user_name[CREDUI_MAX_USERNAME_LENGTH+1]; static char user_password[CREDUI_MAX_PASSWORD_LENGTH+1]; /* WMI service locator */ IWbemLocator *pLoc = NULL; /* namespace for \hostname\root\default */ IWbemServices *pSvc_registry = NULL; /* namespace for \hostname\root\cimv2 */ IWbemServices *pSvc_cimv2 = NULL; /* Security levels on a WMI connection */ SEC_WINNT_AUTH_IDENTITY cID; /* global storage of active jobid being launched */ static orte_jobid_t active_job = ORTE_JOBID_INVALID; /** * Init the module */ int orte_plm_process_init(void) { int rc; HRESULT hres; if (ORTE_SUCCESS != (rc = orte_plm_base_comm_start())) { ORTE_ERROR_LOG(rc); } /* Initialize COM for WMI */ hres = CoInitializeEx(0, COINIT_APARTMENTTHREADED); if (FAILED(hres)) { opal_output(0, "Failed to initialize COM library. Error code = %d \n", hres); return ORTE_ERROR; } /* Set general COM security levels. */ hres = CoInitializeSecurity(NULL, -1, /* COM authentication */ NULL, /* Authentication services */ NULL, /* Reserved */ RPC_C_AUTHN_LEVEL_DEFAULT, /* Default authentication */ RPC_C_IMP_LEVEL_IMPERSONATE, /* Default Impersonation */ NULL, /* Authentication info */ EOAC_NONE, /* Additional capabilities */ NULL /* Reserved */ ); if (FAILED(hres)) { opal_output(0, "Failed to initialize security. Error code = %d \n",hres); CoUninitialize(); return ORTE_ERROR; } /* Obtain the initial locator to WMI. */ hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *) &pLoc); if (FAILED(hres)) { opal_output(0,"Failed to create IWbemLocator object. Err code = %d \n", hres); CoUninitialize(); return ORTE_ERROR; } SecureZeroMemory(user_name, sizeof(user_name)); SecureZeroMemory(user_password, sizeof(user_password)); /* we don't need a barrier to exit */ orte_orted_exit_with_barrier = false; return rc; } static char *generate_commandline(char *prefix, int argc, char **argv) { int i, len = 0; char *commandline; /* Generate remote launch command line. */ for( i = 0; i < argc; i++ ) { if(argv[i] != NULL) { len += strlen(argv[i]) + 1; } } commandline = (char*)malloc( len + strlen(prefix) + 13); memset(commandline, '\0', strlen(commandline)); strcat(commandline, "\""); strcat(commandline, prefix); strcat(commandline, "\\bin\\orted\" "); for(i=1;iConnectServer(_com_util::ConvertStringToBSTR(namespace_default), /* namespace */ _com_util::ConvertStringToBSTR(user_name), /* User name */ _com_util::ConvertStringToBSTR(user_password), /* User password */ (L"MS_409"), /* Locale */ NULL, /* Security flags */ _com_util::ConvertStringToBSTR(ntlm_auth), /* Authority */ 0, /* Context object */ &pSvc_registry /* IWbemServices proxy */ ); if (FAILED(hres)) { opal_output(0,"Could not connect to namespace DEFAULT on node %s. Error code = %d \n", remote_node, hres); goto cleanup; } OPAL_OUTPUT_VERBOSE((1, orte_plm_globals.output, "%s plm:process: Connected to \\\\%s\\\\ROOT\\\\DEFAULT", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), remote_node)); /* default namespace */ hres = CoSetProxyBlanket(pSvc_registry, /* Indicates the proxy to set */ RPC_C_AUTHN_WINNT, /* RPC_C_AUTHN_xxx */ RPC_C_AUTHZ_NONE, /* RPC_C_AUTHZ_xxx */ NULL, /* Server principal name */ RPC_C_AUTHN_LEVEL_CALL, /* RPC_C_AUTHN_LEVEL_xxx */ RPC_C_IMP_LEVEL_IMPERSONATE, /* RPC_C_IMP_LEVEL_xxx */ &cID, /* client identity */ EOAC_NONE /* proxy capabilities */ ); if (FAILED(hres)) { opal_output(0,"Could not set proxy blanket. Error code = %d \n", hres); goto cleanup; } } hres = pSvc_registry->GetObject(ClassName_registry, 0, NULL, &pClass_registry, NULL); if (FAILED(hres)) { opal_output(0,"Could not get Wbem class object. Error code = %d \n", hres); goto cleanup; } hres = pClass_registry->GetMethod(MethodName_registry, 0, &pInParamsDefinition_registry, NULL); hres = pInParamsDefinition_registry->SpawnInstance(0, &pClassInstance_registry); VARIANT hkey_root; hkey_root.vt = VT_I4; hkey_root.intVal = root; hres = pClassInstance_registry->Put(L"hDefKey", 0, &hkey_root, 0); VARIANT varSubKeyName; varSubKeyName.vt = VT_BSTR; varSubKeyName.bstrVal = _com_util::ConvertStringToBSTR(sub_key); hres = pClassInstance_registry->Put(L"sSubKeyName", 0, &varSubKeyName, 0); if(FAILED(hres)) { opal_output(0,"Could not Store the value for the in parameters. Error code = %d \n",hres); goto cleanup; } VARIANT varsValueName; varsValueName.vt = VT_BSTR; varsValueName.bstrVal = _com_util::ConvertStringToBSTR(key); hres = pClassInstance_registry->Put(L"sValueName", 0, &varsValueName, 0); if(FAILED(hres)) { opal_output(0,"Could not Store the value for the in parameters. Error code = %d \n",hres); goto cleanup; } /* Execute Method to read OPAL_PREFIX in the registry */ hres = pSvc_registry->ExecMethod(ClassName_registry, MethodName_registry, 0, NULL, pClassInstance_registry, &pOutParams_registry, NULL); if (FAILED(hres)) { opal_output(0,"Could not execute method. Error code = %d \n",hres); goto cleanup; } /* To see what the method returned */ /* The return value will be in &varReturnValue */ VARIANT varReturnValue_registry; hres = pOutParams_registry->Get(L"ReturnValue", 0, &varReturnValue_registry, NULL, 0); hres = pOutParams_registry->Get(L"sValue", 0, &varsValue_registry, NULL, 0); cleanup: if(NULL!=pClass_registry){ pClass_registry->Release(); } if(NULL!=pOutParams_registry){ pOutParams_registry->Release(); } if(NULL!=pInParamsDefinition_registry){ pInParamsDefinition_registry->Release(); } if(NULL!=pClassInstance_registry) { pClassInstance_registry->Release(); } if(NULL!=ClassName_registry){ SysFreeString(ClassName_registry); } if(NULL!=MethodName_registry){ SysFreeString(MethodName_registry); } if( VT_NULL != varsValue_registry.vt && VT_EMPTY != varsValue_registry.vt) { char *value = strdup(_com_util::ConvertBSTRToString(varsValue_registry.bstrVal)); return value; } else { return NULL; } } /** * Remote spawn process using WMI. */ static int wmi_launch_child(char *prefix, char *remote_node, int argc, char **argv) { char *command_line = NULL; int len = 0, pid = -1; HRESULT hres; IWbemClassObject* pClass_cimv2 = NULL; IWbemClassObject* pInParamsDefinition_cimv2 = NULL; IWbemClassObject* pClassInstance_cimv2 = NULL; IWbemClassObject* pOutParams_cimv2 = NULL; VARIANT varCommand; VARIANT varProcessId; VariantInit(&varCommand); VariantInit(&varProcessId); BSTR MethodName_cimv2 = SysAllocString(L"Create"); BSTR ClassName_cimv2 = SysAllocString(L"Win32_Process"); /*Connect to WMI through the IWbemLocator::ConnectServer method*/ char namespace_cimv2[100]; char *ntlm_auth = (char *) malloc(sizeof(char)*(strlen("ntlmdomain:")+strlen(remote_node)+1)); memset(ntlm_auth, 0, strlen(ntlm_auth)); strcat(ntlm_auth, "ntlmdomain:"); strcat(ntlm_auth, remote_node); if( 0 != get_credential(remote_node)) { goto cleanup; } /* set up remote namespace path */ char *rt_namespace_cimv2 = "\\root\\cimv2"; strcpy(namespace_cimv2, "\\\\"); strcat(namespace_cimv2, remote_node ); strcat(namespace_cimv2, rt_namespace_cimv2); /* connect to cimv2 namespace */ hres = pLoc->ConnectServer(_com_util::ConvertStringToBSTR(namespace_cimv2), /* namespace */ _com_util::ConvertStringToBSTR(user_name), /* User name */ _com_util::ConvertStringToBSTR(user_password), /* User password */ (L"MS_409"), /* Locale */ NULL, /* Security flags */ _com_util::ConvertStringToBSTR(ntlm_auth), /* Authority */ 0, /* Context object */ &pSvc_cimv2 /* IWbemServices proxy */ ); if (FAILED(hres)) { opal_output(0,"Could not connect to namespace cimv2 on node %s. Error code =%d \n", remote_node, hres); goto cleanup; } OPAL_OUTPUT_VERBOSE((1, orte_plm_globals.output, "%s plm:process: Connected to \\\\%s\\\\ROOT\\\\CIMV2", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), remote_node)); /* Set security levels on a WMI connection */ cID.User = (unsigned char *) user_name; cID.UserLength = lstrlen(user_name); cID.Password = (unsigned char *) user_password; cID.PasswordLength = lstrlen(user_password); cID.Domain = (unsigned char *) remote_node; cID.DomainLength = lstrlen(remote_node); cID.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI; /* cimv2 namespace */ hres = CoSetProxyBlanket(pSvc_cimv2, /* Indicates the proxy to set */ RPC_C_AUTHN_WINNT, /* RPC_C_AUTHN_xxx */ RPC_C_AUTHZ_NONE, /* RPC_C_AUTHZ_xxx */ NULL, /* Server principal name */ RPC_C_AUTHN_LEVEL_CALL, /* RPC_C_AUTHN_LEVEL_xxx */ RPC_C_IMP_LEVEL_IMPERSONATE, /* RPC_C_IMP_LEVEL_xxx */ &cID, /* client identity */ EOAC_NONE /* proxy capabilities */ ); if (FAILED(hres)) { opal_output(0,"Could not set proxy blanket. Error code = %d \n", hres ); goto cleanup; } /* if there isn't a prefix ( e.g., '--noprefix' specfied, or Open MPI was configured without ORTE_WANT_ORTERUN_PREFIX_BY_DEFAULT), let's first check the OPENMPI_HOME in user environment variables, and then the OPAL_PREFIX registry value. */ if( NULL == prefix ) { if( mca_plm_process_component.remote_env_prefix ) { char *path = "Environment"; char *key = "OPENMPI_HOME"; /* read registry at HKEY_CURRENT_USER please note: this MUST be the same user as for WMI authorization. */ char *reg_prefix = read_remote_registry(0x80000001, path, key, remote_node, ntlm_auth); if( NULL != reg_prefix) { command_line = generate_commandline(reg_prefix, argc, argv); } } if( NULL == command_line && mca_plm_process_component.remote_reg_prefix ) { char *path = "Software\\Open MPI\\"; char *key = "OPAL_PREFIX"; char *reg_prefix = read_remote_registry(0x80000002, path, key, remote_node, ntlm_auth); if( NULL != reg_prefix) { command_line = generate_commandline(reg_prefix, argc, argv); } } } else { /* use user specified/default prefix */ command_line = generate_commandline(prefix, argc, argv); } if ( NULL == command_line ) { /* we couldn't find the execute path, abort. */ opal_output(0, "couldn't find executable path for orted on node %s. aborted.", remote_node); goto cleanup; } /* Use the IWbemServices pointer to make requests of WMI */ /* set up to call the Win32_Process::Create method */ hres = pSvc_cimv2->GetObject(ClassName_cimv2, 0, NULL, &pClass_cimv2, NULL); if (FAILED(hres)) { opal_output(0,"Could not get Wbem class object. Error code = %d \n", hres); goto cleanup; } hres = pClass_cimv2->GetMethod(MethodName_cimv2, 0, &pInParamsDefinition_cimv2, NULL); hres = pInParamsDefinition_cimv2->SpawnInstance(0, &pClassInstance_cimv2); /* Create the values for the in parameters */ varCommand.vt = VT_BSTR; varCommand.bstrVal = _com_util::ConvertStringToBSTR(command_line); /* Store the value for the in parameters */ hres = pClassInstance_cimv2->Put(L"CommandLine", 0, &varCommand, 0); /* Execute Method to launch orted on remote node*/ hres = pSvc_cimv2->ExecMethod(ClassName_cimv2, MethodName_cimv2, 0, NULL, pClassInstance_cimv2, &pOutParams_cimv2, NULL); if (FAILED(hres)) { opal_output(0,"Could not execute method. Error code = %d \n",hres); goto cleanup; } /* get remote process ID */ hres = pOutParams_cimv2->Get((L"ProcessId"), 0, &varProcessId, NULL, 0); pid = varProcessId.intVal; cleanup: if(NULL!=pClass_cimv2) { pClass_cimv2->Release(); } if(NULL!=pInParamsDefinition_cimv2) { pInParamsDefinition_cimv2->Release(); } if(NULL!=pOutParams_cimv2){ pOutParams_cimv2->Release(); } if(NULL!=ClassName_cimv2){ SysFreeString(ClassName_cimv2); } if(NULL!=MethodName_cimv2){ SysFreeString(MethodName_cimv2); } if(VT_NULL!=varCommand.vt) { VariantClear(&varCommand); } if(VT_NULL!=varProcessId.vt){ VariantClear(&varProcessId); } return pid; } /** * Check the Shell variable on the specified node */ static int orte_plm_process_probe(orte_node_t * node, orte_plm_process_shell * shell) { char ** argv; int rc, nfds; int fd[2]; pid_t pid; /* HANDLE myPipeFd[2]; SECURITY_ATTRIBUTES securityAttr; STARTUPINFO startupInfo; PROCESS_INFORMATION processInfo; */ fd_set readset; fd_set errset; char outbuf[4096]; OPAL_OUTPUT_VERBOSE((1, orte_plm_globals.output, "%s plm:process: going to check SHELL variable on node %s", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), node->name)); *shell = ORTE_PLM_RSH_SHELL_UNKNOWN; /* * Build argv array */ pid = _spawnve( _P_DETACH, argv[0], argv, NULL); #if 0 securityAttr.nLength = sizeof(SECURITY_ATTRIBUTES); // Size of struct securityAttr.lpSecurityDescriptor = NULL; // Default descriptor securityAttr.bInheritHandle = TRUE; // Inheritable // Create the pipe if (CreatePipe(&myPipeFd[0], &myPipeFd[1], &securityAttr, 0)) { // Create duplicate of write end so that original if (!DuplicateHandle( GetCurrentProcess(), myPipeFd[0], // Original handle GetCurrentProcess(), NULL, // don't create new handle 0, FALSE, // Not inheritable DUPLICATE_SAME_ACCESS) ) { CloseHandle(myPipeFd[0]); CloseHandle(myPipeFd[1]); opal_output(0, "plm:process: DuplicateHandle failed with errno=%d\n", errno); return ORTE_ERR_IN_ERRNO; } ZeroMemory( &startupInfo, sizeof(startupInfo) ); startupInfo.cb = sizeof(startupInfo); ZeroMemory( &processInfo, sizeof(processInfo) ); // Now populate startup info for CreateProcess GetStartupInfo(&startupInfo); startupInfo.dwFlags = STARTF_USESTDHANDLES; startupInfo.hStdInput = myPipeFd[0]; startupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); startupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE); // Start the child process. if( !CreateProcess( argv[0], //module name NULL, NULL, //(LPSTR)(const char *) argv, NULL, // Process handle not inheritable NULL, // Thread handle not inheritable TRUE, // Set handle inheritance to TRUE; // each inheritable handle in the calling process is inherited by the new process 0, // No creation flags NULL, // Use parent's environment block NULL, // Use parent's starting directory &startupInfo, // Pointer to STARTUPINFO structure &processInfo ) // Pointer to PROCESS_INFORMATION structure ) { CloseHandle(myPipeFd[1]); opal_output(0, "plm:process: CreateProcess failed with errno=%d\n", errno); //, GetLastError() ); return ORTE_ERR_IN_ERRNO; } } #endif /* if ((pid = fork()) < 0) { opal_output(0, "plm:process: fork failed with errno=%d\n", errno); return ORTE_ERR_IN_ERRNO; } else if (pid == 0) { // child //processInfo.hProcess if (dup2(fd[1], 1) < 0) { opal_output(0, "plm:process: dup2 failed with errno=%d\n", errno); return ORTE_ERR_IN_ERRNO; } execvp(argv[0], argv); exit(errno); } if (close(fd[1])) { opal_output(0, "plm:process: close failed with errno=%d\n", errno); return ORTE_ERR_IN_ERRNO; } */ /* Monitor stdout */ FD_ZERO(&readset); nfds = fd[0]+1; memset (outbuf, 0, sizeof (outbuf)); rc = ORTE_SUCCESS;; while (ORTE_SUCCESS == rc) { int err; FD_SET (fd[0], &readset); errset = readset; err = select(nfds, &readset, NULL, &errset, NULL); if (err == -1) { if (errno == EINTR) continue; else { rc = ORTE_ERR_IN_ERRNO; break; } } if (FD_ISSET(fd[0], &errset) != 0) rc = ORTE_ERR_FATAL; /* In case we have something valid to read on stdin */ if (FD_ISSET(fd[0], &readset) != 0) { ssize_t ret = 1; char temp[4096]; char * ptr = outbuf; ssize_t outbufsize = sizeof(outbuf); memset (temp, 0, sizeof(temp)); while (ret != 0) { ret = read (fd[0], temp, 256); if (ret < 0) { if (errno == EINTR) continue; else { rc = ORTE_ERR_IN_ERRNO; break; } } else { if (outbufsize > 0) { memcpy (ptr, temp, (ret > outbufsize) ? outbufsize : ret); outbufsize -= ret; ptr += ret; if (outbufsize > 0) *ptr = '\0'; } } } /* After reading complete string (aka read returns 0), we just break */ break; } } /* Search for the substring of known shell-names */ /* for (i = 0; i < (int)(sizeof (orte_plm_process_shell_name)/ sizeof(orte_plm_process_shell_name[0])); i++) { char *sh_name = NULL; sh_name = rindex(outbuf, '/'); if ( sh_name != NULL ) { sh_name++; /* skip '/' */ /* We cannot use "echo -n $SHELL" because -n is not portable. Therefore * we have to remove the "\n" */ /* if ( sh_name[strlen(sh_name)-1] == '\n' ) { sh_name[strlen(sh_name)-1] = '\0'; } if ( 0 == strcmp(sh_name, orte_plm_process_shell_name[i]) ) { *shell = i; break; } } } */ OPAL_OUTPUT_VERBOSE((1, orte_plm_globals.output, "%s plm:process: node:%s has SHELL: %s", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), node->name, orte_plm_process_shell_name[*shell])); return rc; } /** * Fill the exec_path variable with the directory to the orted */ static int orte_plm_process_fill_exec_path( char ** exec_path ) { struct stat buf; asprintf(exec_path, "%s/orted", opal_install_dirs.bindir); if (0 != stat(*exec_path, &buf)) { char *path = getenv("PATH"); if (NULL == path) { path = "PATH is empty!"; } orte_show_help("help-plm-process.txt", "no-local-orted", true, path, opal_install_dirs.bindir); return ORTE_ERR_NOT_FOUND; } return ORTE_SUCCESS; } /** * Callback on daemon exit. */ static void orte_plm_process_wait_daemon(pid_t pid, int status, void* cbdata) { unsigned long deltat; if (! WIFEXITED(status) || ! WEXITSTATUS(status) == 0) { /* tell the user something went wrong */ opal_output(0, "ERROR: A daemon failed to start as expected."); opal_output(0, "ERROR: There may be more information available from"); opal_output(0, "ERROR: the remote shell (see above)."); if (WIFEXITED(status)) { opal_output(0, "ERROR: The daemon exited unexpectedly with status %d.", WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) { #ifdef WCOREDUMP if (WCOREDUMP(status)) { opal_output(0, "The daemon received a signal %d (with core).", WTERMSIG(status)); } else { opal_output(0, "The daemon received a signal %d.", WTERMSIG(status)); } #else opal_output(0, "The daemon received a signal %d.", WTERMSIG(status)); #endif /* WCOREDUMP */ } else { opal_output(0, "No extra status information is available: %d.", status); } /* report that the daemon has failed so we break out of the daemon * callback receive and can exit */ orte_plm_base_launch_failed(active_job, pid, status, ORTE_JOB_STATE_FAILED_TO_START); } /* if abnormal exit */ /* release any waiting threads */ OPAL_THREAD_LOCK(&mca_plm_process_component.lock); if (mca_plm_process_component.num_children-- >= mca_plm_process_component.num_concurrent || mca_plm_process_component.num_children == 0) { opal_condition_signal(&mca_plm_process_component.cond); } if (mca_plm_process_component.timing && mca_plm_process_component.num_children == 0) { if (0 != gettimeofday(&joblaunchstop, NULL)) { opal_output(0, "plm_process: could not obtain job launch stop time"); } else { deltat = (joblaunchstop.tv_sec - joblaunchstart.tv_sec)*1000000 + (joblaunchstop.tv_usec - joblaunchstart.tv_usec); opal_output(0, "plm_process: total time to launch job is %lu usec", deltat); } } OPAL_THREAD_UNLOCK(&mca_plm_process_component.lock); } /** * Launch a daemon (bootproxy) on each node. The daemon will be responsible * for launching the application. */ /* When working in this function, ALWAYS jump to "cleanup" if * you encounter an error so that orterun will be woken up and * the job can cleanly terminate */ int orte_plm_process_launch(orte_job_t *jdata) { orte_job_map_t *map = NULL; int proc_vpid_index; int local_exec_index; char *vpid_string = NULL; char *param; char **argv = NULL; char *prefix_dir; int argc = 0; int rc; char *lib_base = NULL, *bin_base = NULL; bool failed_launch = true; orte_app_context_t **apps; orte_node_t **nodes; orte_std_cntr_t nnode; orte_job_state_t job_state = ORTE_JOB_NEVER_LAUNCHED; if (orte_timing) { if (0 != gettimeofday(&joblaunchstart, NULL)) { opal_output(0, "plm_process: could not obtain start time"); joblaunchstart.tv_sec = 0; joblaunchstart.tv_usec = 0; } } /* setup the job */ if (ORTE_SUCCESS != (rc = orte_plm_base_setup_job(jdata))) { ORTE_ERROR_LOG(rc); goto cleanup; } OPAL_OUTPUT_VERBOSE((1, orte_plm_globals.output, "%s plm:process: launching job %s", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), ORTE_JOBID_PRINT(jdata->jobid))); /* set the active jobid */ active_job = jdata->jobid; /* Get the map for this job */ if (NULL == (map = orte_rmaps.get_job_map(active_job))) { ORTE_ERROR_LOG(ORTE_ERR_NOT_FOUND); rc = ORTE_ERR_NOT_FOUND; goto cleanup; } apps = (orte_app_context_t**)jdata->apps->addr; nodes = (orte_node_t**)map->nodes->addr; if (0 == map->num_new_daemons) { /* have all the daemons we need - launch app */ OPAL_OUTPUT_VERBOSE((1, orte_plm_globals.output, "%s plm:process: no new daemons to launch", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME))); goto launch_apps; } if (orte_debug_daemons_flag && mca_plm_process_component.num_concurrent < map->num_new_daemons) { /** * If we are in '--debug-daemons' we keep the ssh connection * alive for the span of the run. If we use this option * AND we launch on more than "num_concurrent" machines * then we will deadlock. No connections are terminated * until the job is complete, no job is started * since all the orteds are waiting for all the others * to come online, and the others ore not launched because * we are waiting on those that have started to terminate * their ssh tunnels. :( * As we cannot run in this situation, pretty print the error * and return an error code. */ orte_show_help("help-plm-process.txt", "deadlock-params", true, mca_plm_process_component.num_concurrent, map->num_new_daemons); rc = ORTE_ERR_FATAL; goto cleanup; } /* * After a discussion between Ralph & Jeff, we concluded that we * really are handling the prefix dir option incorrectly. It currently * is associated with an app_context, yet it really refers to the * location where OpenRTE/Open MPI is installed on a NODE. Fixing * this right now would involve significant change to orterun as well * as elsewhere, so we will intentionally leave this incorrect at this * point. The error, however, is identical to that seen in all prior * releases of OpenRTE/Open MPI, so our behavior is no worse than before. * * A note to fix this, along with ideas on how to do so, has been filed * on the project's Trac system under "feature enhancement". * * For now, default to the prefix_dir provided in the first app_context. * Since there always MUST be at least one app_context, we are safe in * doing this. */ prefix_dir = apps[0]->prefix_dir; /* * Build argv array */ opal_argv_append(&argc, &argv, "