1
1
openmpi/orte/tools/orte-clean/orte-clean.c

450 строки
14 KiB
C
Исходник Обычный вид История

/*
* Copyright (c) 2004-2010 The Trustees of Indiana University and Indiana
* University Research and Technology
* Corporation. All rights reserved.
* Copyright (c) 2004-2005 The University of Tennessee and The University
* of Tennessee Research Foundation. All rights
* reserved.
* Copyright (c) 2004-2005 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) 2007-2008 Sun Microsystems, Inc. All rights reserved.
* Copyright (c) 2007-2013 Los Alamos National Security, LLC. All rights
* reserved.
* Copyright (c) 2011-2013 Cisco Systems, Inc. All rights reserved.
* Copyright (c) 2015 Research Organization for Information Science
* and Technology (RIST). All rights reserved.
* $COPYRIGHT$
*
* Additional copyrights may follow
*
* $HEADER$
*/
#include "orte_config.h"
#include "orte/constants.h"
#include <stdio.h>
#include <errno.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif /* HAVE_UNISTD_H */
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif /* HAVE_STDLIB_H */
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif /* HAVE_SYS_STAT_H */
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif /* HAVE_SYS_TYPES_H */
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif /* HAVE_SYS_WAIT_H */
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif /* HAVE_SYS_PARAM_H */
#ifdef HAVE_STRING_H
#include <string.h>
#endif /* HAVE_STRING_H */
#ifdef HAVE_DIRENT_H
#include <dirent.h>
#endif /* HAVE_DIRENT_H */
#include <signal.h>
#ifdef HAVE_PWD_H
#include <pwd.h>
#endif /* HAVE_PWD_H */
#include "opal/util/cmd_line.h"
#include "opal/util/opal_environ.h"
#include "opal/util/os_dirpath.h"
#include "opal/util/basename.h"
#include "opal/util/error.h"
#include "opal/mca/base/base.h"
#include "opal/util/show_help.h"
#include "orte/util/proc_info.h"
#include "orte/util/show_help.h"
#include "opal/runtime/opal.h"
#if OPAL_ENABLE_FT_CR == 1
#include "opal/runtime/opal_cr.h"
#endif
#include "orte/runtime/runtime.h"
/******************
* Local Functions
******************/
static int parse_args(int argc, char *argv[]);
static void kill_procs(void);
/*****************************************
* Global Vars for Command line Arguments
*****************************************/
typedef struct {
bool help;
bool verbose;
bool debug;
} orte_clean_globals_t;
orte_clean_globals_t orte_clean_globals;
opal_cmd_line_init_t cmd_line_opts[] = {
{ NULL,
'h', NULL, "help",
0,
&orte_clean_globals.help, OPAL_CMD_LINE_TYPE_BOOL,
"This help message" },
{ NULL,
'v', NULL, "verbose",
0,
&orte_clean_globals.verbose, OPAL_CMD_LINE_TYPE_BOOL,
"Generate verbose output" },
{ NULL,
'd', NULL, "debug",
0,
&orte_clean_globals.debug, OPAL_CMD_LINE_TYPE_BOOL,
"Extra debug output for developers to ensure that orte-clean is working" },
/* End of list */
{ NULL,
'\0', NULL, NULL,
0,
NULL, OPAL_CMD_LINE_TYPE_NULL,
NULL }
};
/*
* This utility will do a brute force clean of a node. It will
* attempt to clean up any files in the user's session directory.
* It will also look for any orted and orterun processes that are
* not part of this job, and kill them off.
*/
int
main(int argc, char *argv[])
{
int ret = ORTE_SUCCESS;
#if OPAL_ENABLE_FT_CR == 1
char *tmp_env_var;
#endif
/* This is needed so we can print the help message */
if (ORTE_SUCCESS != (ret = opal_init_util(&argc, &argv))) {
return ret;
}
if (ORTE_SUCCESS != (ret = parse_args(argc, argv))) {
return ret;
}
#if OPAL_ENABLE_FT_CR == 1
/* Disable the checkpoint notification routine for this
* tool. As we will never need to checkpoint this tool.
* Note: This must happen before opal_init().
*/
opal_cr_set_enabled(false);
/* Select the none component, since we don't actually use a checkpointer */
MCA/base: Add new MCA variable system Features: - Support for an override parameter file (openmpi-mca-param-override.conf). Variable values in this file can not be overridden by any file or environment value. - Support for boolean, unsigned, and unsigned long long variables. - Support for true/false values. - Support for enumerations on integer variables. - Support for MPIT scope, verbosity, and binding. - Support for command line source. - Support for setting variable source via the environment using OMPI_MCA_SOURCE_<var name>=source (either command or file:filename) - Cleaner API. - Support for variable groups (equivalent to MPIT categories). Notes: - Variables must be created with a backing store (char **, int *, or bool *) that must live at least as long as the variable. - Creating a variable with the MCA_BASE_VAR_FLAG_SETTABLE enables the use of mca_base_var_set_value() to change the value. - String values are duplicated when the variable is registered. It is up to the caller to free the original value if necessary. The new value will be freed by the mca_base_var system and must not be freed by the user. - Variables with constant scope may not be settable. - Variable groups (and all associated variables) are deregistered when the component is closed or the component repository item is freed. This prevents a segmentation fault from accessing a variable after its component is unloaded. - After some discussion we decided we should remove the automatic registration of component priority variables. Few component actually made use of this feature. - The enumerator interface was updated to be general enough to handle future uses of the interface. - The code to generate ompi_info output has been moved into the MCA variable system. See mca_base_var_dump(). opal: update core and components to mca_base_var system orte: update core and components to mca_base_var system ompi: update core and components to mca_base_var system This commit also modifies the rmaps framework. The following variables were moved from ppr and lama: rmaps_base_pernode, rmaps_base_n_pernode, rmaps_base_n_persocket. Both lama and ppr create synonyms for these variables. This commit was SVN r28236.
2013-03-28 01:09:41 +04:00
(void) mca_base_var_env_name("crs", &tmp_env_var);
opal_setenv(tmp_env_var,
"none",
true, &environ);
free(tmp_env_var);
tmp_env_var = NULL;
MCA/base: Add new MCA variable system Features: - Support for an override parameter file (openmpi-mca-param-override.conf). Variable values in this file can not be overridden by any file or environment value. - Support for boolean, unsigned, and unsigned long long variables. - Support for true/false values. - Support for enumerations on integer variables. - Support for MPIT scope, verbosity, and binding. - Support for command line source. - Support for setting variable source via the environment using OMPI_MCA_SOURCE_<var name>=source (either command or file:filename) - Cleaner API. - Support for variable groups (equivalent to MPIT categories). Notes: - Variables must be created with a backing store (char **, int *, or bool *) that must live at least as long as the variable. - Creating a variable with the MCA_BASE_VAR_FLAG_SETTABLE enables the use of mca_base_var_set_value() to change the value. - String values are duplicated when the variable is registered. It is up to the caller to free the original value if necessary. The new value will be freed by the mca_base_var system and must not be freed by the user. - Variables with constant scope may not be settable. - Variable groups (and all associated variables) are deregistered when the component is closed or the component repository item is freed. This prevents a segmentation fault from accessing a variable after its component is unloaded. - After some discussion we decided we should remove the automatic registration of component priority variables. Few component actually made use of this feature. - The enumerator interface was updated to be general enough to handle future uses of the interface. - The code to generate ompi_info output has been moved into the MCA variable system. See mca_base_var_dump(). opal: update core and components to mca_base_var system orte: update core and components to mca_base_var system ompi: update core and components to mca_base_var system This commit also modifies the rmaps framework. The following variables were moved from ppr and lama: rmaps_base_pernode, rmaps_base_n_pernode, rmaps_base_n_persocket. Both lama and ppr create synonyms for these variables. This commit was SVN r28236.
2013-03-28 01:09:41 +04:00
(void) mca_base_var_env_name("opal_cr_is_tool", &tmp_env_var);
opal_setenv(tmp_env_var,
"1", true, NULL);
free(tmp_env_var);
#endif
if (ORTE_SUCCESS != (ret = orte_init(&argc, &argv, ORTE_PROC_TOOL))) {
return ret;
}
/*
* Clean out all session directories - we don't have to protect
* our own session directory because (since we are a tool) we
* didn't create one!
*/
if (orte_clean_globals.verbose) {
fprintf(stderr, "orte-clean: cleaning session dir tree %s\n",
orte_process_info.top_session_dir);
}
opal_os_dirpath_destroy(orte_process_info.top_session_dir, true, NULL);
/* now kill any lingering procs, if we can */
kill_procs();
orte_finalize();
return ORTE_SUCCESS;
}
/*
* Parse the command line arguments using the functions command
* line utility functions.
*/
static int parse_args(int argc, char *argv[]) {
int ret;
opal_cmd_line_t cmd_line;
orte_clean_globals_t tmp = { false, false, false };
/* NOTE: There is a bug in the PGI 6.2 series that causes the
compiler to choke when copying structs containing bool members
by value. So do a memcpy here instead. */
memcpy(&orte_clean_globals, &tmp, sizeof(tmp));
/*
* Initialize list of available command line options.
*/
opal_cmd_line_create(&cmd_line, cmd_line_opts);
ret = opal_cmd_line_parse(&cmd_line, false, argc, argv);
if (OPAL_SUCCESS != ret) {
if (OPAL_ERR_SILENT != ret) {
fprintf(stderr, "%s: command line error (%s)\n", argv[0],
opal_strerror(ret));
}
return ret;
}
/**
* Now start parsing our specific arguments
*/
if (orte_clean_globals.help) {
char *str, *args = NULL;
args = opal_cmd_line_get_usage_msg(&cmd_line);
str = opal_show_help_string("help-orte-clean.txt", "usage", true,
args);
if (NULL != str) {
printf("%s", str);
free(str);
}
free(args);
/* If we show the help message, that should be all we do */
exit(0);
}
OBJ_DESTRUCT(&cmd_line);
return ORTE_SUCCESS;
}
static char *orte_getline(FILE *fp)
{
char *ret, *buff;
char input[1024];
int i;
ret = fgets(input, 1024, fp);
if (NULL != ret) {
/* remove trailing spaces */
for (i=strlen(input)-2; i > 0; i--) {
if (input[i] != ' ') {
input[i+1] = '\0';
break;
}
}
buff = strdup(input);
return buff;
}
return NULL;
}
/*
* This function makes a call to "ps" to find out the processes that
* are running on this node. It then attempts to kill off any orteds
* and orteruns that are not related to this job.
*/
static
void kill_procs(void) {
int ortedpid;
char *fullprocname;
char *procname;
char *pidstr;
char *user;
int procpid;
FILE *psfile;
char *inputline;
char *this_user;
int uid;
char *separator = " \t"; /* output can be delimited by space or tab */
/*
* This is the command that is used to get the information about
* all the processes that are running. The output looks like the
* following:
* COMMAND PID USER
* tcsh 12556 rolfv
* ps 14424 rolfv
* etc.
* Currently, we do not make use of the USER field, but we may later
* on so we grab it also.
*/
/*
* The configure determines if there is a valid ps command for us to
* use. If it is set to unknown, then we skip this section.
*/
char command[] = ORTE_CLEAN_PS_CMD;
if (0 == strcmp("unknown", command)) {
return;
}
if (orte_clean_globals.verbose) {
fprintf(stderr, "orte-clean: killing any lingering procs\n");
}
/*
* Get our parent pid which is the pid of the orted.
*/
ortedpid = getppid();
/* get the name of the user */
uid = getuid();
#if OPAL_ENABLE_GETPWUID
{
struct passwd *pwdent;
#ifdef HAVE_GETPWUID
pwdent = getpwuid(uid);
if (NULL == pwdent) {
/* this indicates a problem with the passwd system,
* so pretty-print a message just for info
*/
orte_show_help("help-orte-runtime.txt",
"orte:session:dir:nopwname", true);
}
#else
pwdent = NULL;
#endif
if (NULL != pwdent) {
this_user = strdup(pwdent->pw_name);
} else {
asprintf(&this_user, "%d", uid);
}
}
#else
asprintf(&this_user, "%d", uid);
#endif
/*
* There is a race condition here. The problem is that we are looking
* for any processes named orted. However, one may erroneously find more
* orteds then there really are because the orted is doing a series of
* fork/execs. If we run with more than one orte-clean on a node, then
* one of the orte-cleans may catch the other one while it has forked,
* but not exec'ed. It will therefore kill an orte-clean. Now one
* can argue it is silly to run more than one orte-clean on a node, and
* this is true. We will have to figure out how to prevent this. For
* now, we use a big hammer and just sleep a second to decrease the
* probability.
*/
sleep(1);
psfile = popen(command, "r");
/*
* Read the first line of the output. We just throw it away
* as it is the header consisting of the words COMMAND, PID and
* USER.
*/
if (NULL == (inputline = orte_getline(psfile))) {
free(this_user);
fclose(psfile);
return;
}
free(inputline); /* dump the header line */
while (NULL != (inputline = orte_getline(psfile))) {
/* The three fields are typically seperated by spaces */
fullprocname = strtok(inputline, separator);
pidstr = strtok(NULL, separator);
user = strtok(NULL, separator);
if (orte_clean_globals.debug) {
fprintf(stdout, "\norte-clean: user(pid)=%s, me=%s\n",
user, this_user);
}
/* If the user is not us, and the user is not root, then skip
* further checking. If the user is root, then continue on as
* we want root to kill off everybody. */
if ((0 != strcmp(user, this_user)) && (0 != strcmp("root", this_user))) {
/* not us */
free(inputline);
continue;
}
procpid = atoi(pidstr);
procname = opal_basename(fullprocname);
if (orte_clean_globals.debug) {
fprintf(stdout, "orte-clean: fullname=%s, basename=%s, pid=%d\n",
fullprocname, procname, procpid);
}
/*
* Look for any orteds that are not our parent and attempt to
* kill them. We currently do not worry whether we are the
* owner or not. If we are not, we will just fail to send
* the signal and that is OK. This also allows a root process
* to kill all orteds.
*
* NOTE: need to also look for "(orted)" as a non-active
* proc is sometimes reported that way
*/
if (0 == strncmp("orted", procname, strlen("orted")) ||
0 == strncmp("(orted)", procname, strlen("(orted)"))) {
if (procpid != ortedpid) {
if (orte_clean_globals.verbose) {
fprintf(stderr, "orte-clean: found potential rogue orted process"
" (pid=%d,user=%s), sending SIGKILL...\n",
procpid, user);
}
/*
* We ignore the return code here as we do not really
* care whether this worked or not.
*/
(void)kill(procpid, SIGKILL);
}
}
/*
* Now check for any orteruns.
*/
if (0 == strncmp("orterun", procname, strlen("orterun")) ||
0 == strncmp("mpirun", procname, strlen("mpirun"))) {
/* if we are on the same node as the HNP, then the ortedpid
* is the same as that of our orterun, so don't kill it
*/
if (procpid != ortedpid) {
if (orte_clean_globals.verbose) {
fprintf(stderr, "orte-clean: found potential rogue orterun process"
" (pid=%d,user=%s), sending SIGKILL...\n",
procpid, user);
}
/* if we are a singleton, check the hnp_pid as well */
if (ORTE_PROC_IS_SINGLETON) {
if (procpid != orte_process_info.hnp_pid) {
(void)kill(procpid, SIGKILL);
}
} else {
/* We ignore the return code here as we do not really
* care whether this worked or not.
*/
(void)kill(procpid, SIGKILL);
}
}
}
free(inputline);
free(procname);
}
free(this_user);
fclose(psfile);
return;
}