/* * Copyright (c) 2004-2006 The Trustees of Indiana University and Indiana * University Research and Technology * Corporation. All rights reserved. * Copyright (c) 2004-2006 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-2006 The Regents of the University of California. * All rights reserved. * $COPYRIGHT$ * * Additional copyrights may follow * * $HEADER$ */ #include "opal_config.h" #include #include #include #ifdef HAVE_SYSLOG_H #include #endif #include #include #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_SYS_PARAM_H #include #endif #include "opal/util/opal_environ.h" #include "opal/util/output.h" #include "opal/threads/mutex.h" #include "opal/constants.h" /* * Private data */ static int verbose_stream = -1; static opal_output_stream_t verbose; static char *output_dir = NULL; static char *output_prefix = NULL; /* * Private functions */ static void construct(opal_object_t *stream); static int do_open(int output_id, opal_output_stream_t * lds); static int open_file(int i); static void free_descriptor(int output_id); static void output(int output_id, const char *format, va_list arglist); /* * Internal data structures and helpers for the generalized output * stream mechanism. */ struct output_desc_t { bool ldi_used; bool ldi_enabled; int ldi_verbose_level; bool ldi_syslog; int ldi_syslog_priority; #ifndef __WINDOWS__ char *ldi_syslog_ident; #else HANDLE ldi_syslog_ident; #endif char *ldi_prefix; int ldi_prefix_len; bool ldi_stdout; bool ldi_stderr; bool ldi_file; bool ldi_file_want_append; char *ldi_file_suffix; int ldi_fd; int ldi_file_num_lines_lost; }; typedef struct output_desc_t output_desc_t; #define OPAL_OUTPUT_MAX_STREAMS 32 /* * Local state */ static bool initialized = false; static output_desc_t info[OPAL_OUTPUT_MAX_STREAMS]; static char *temp_str = 0; static size_t temp_str_len = 0; static opal_mutex_t mutex; static bool syslog_opened = false; OBJ_CLASS_INSTANCE(opal_output_stream_t, opal_object_t, construct, NULL); /* * Setup the output stream infrastructure */ bool opal_output_init(void) { int i; char *str; char hostname[32]; if (initialized) { return true; } OBJ_CONSTRUCT(&verbose, opal_output_stream_t); #if defined(__WINDOWS__) { WSADATA wsaData; WSAStartup( MAKEWORD(2,2), &wsaData ); } #endif /* defined(__WINDOWS__) */ gethostname(hostname, sizeof(hostname)); verbose.lds_want_stderr = true; asprintf(&verbose.lds_prefix, "[%s:%05d] ", hostname, getpid()); for (i = 0; i < OPAL_OUTPUT_MAX_STREAMS; ++i) { info[i].ldi_used = false; info[i].ldi_enabled = false; info[i].ldi_syslog = false; info[i].ldi_file = false; info[i].ldi_file_suffix = NULL; info[i].ldi_file_want_append = false; info[i].ldi_fd = -1; info[i].ldi_file_num_lines_lost = 0; } /* Initialize the mutex that protects the output */ OBJ_CONSTRUCT(&mutex, opal_mutex_t); initialized = true; /* Set some defaults */ asprintf(&output_prefix, "output-pid%d-", getpid()); if (NULL != (str = getenv("TMPDIR"))) { output_dir = strdup(str); } else if (NULL != (str = getenv("TEMP"))) { output_dir = strdup(str); } else if (NULL != (str = getenv("TMP"))) { output_dir = strdup(str); } else if (NULL != (str = getenv("HOME"))) { output_dir = strdup(str); } else { output_dir = strdup("."); } /* Open the default verbose stream */ verbose_stream = opal_output_open(&verbose); return true; } /* * Open a stream */ int opal_output_open(opal_output_stream_t * lds) { return do_open(-1, lds); } /* * Reset the parameters on a stream */ int opal_output_reopen(int output_id, opal_output_stream_t * lds) { return do_open(output_id, lds); } /* * Enable and disable outptu streams */ bool opal_output_switch(int output_id, bool enable) { bool ret = false; /* Setup */ if (!initialized) { opal_output_init(); } if (output_id >= 0 && output_id < OPAL_OUTPUT_MAX_STREAMS) { ret = info[output_id].ldi_enabled; info[output_id].ldi_enabled = enable; } return ret; } /* * Reopen all the streams; used during checkpoint/restart. */ void opal_output_reopen_all(void) { int i; opal_output_stream_t lds; for (i = 0; i < OPAL_OUTPUT_MAX_STREAMS; ++i) { /* scan till we find ldi_used == 0, which is the end-marker */ if (!info[i].ldi_used) { break; } /* * set this to zero to ensure that opal_output_open will * return this same index as the output stream id */ info[i].ldi_used = false; lds.lds_want_syslog = info[i].ldi_syslog; lds.lds_syslog_priority = info[i].ldi_syslog_priority; lds.lds_syslog_ident = info[i].ldi_syslog_ident; lds.lds_prefix = info[i].ldi_prefix; lds.lds_want_stdout = info[i].ldi_stdout; lds.lds_want_stderr = info[i].ldi_stderr; lds.lds_want_file = (-1 == info[i].ldi_fd) ? false : true; /* open all streams in append mode */ lds.lds_want_file_append = true; lds.lds_file_suffix = info[i].ldi_file_suffix; /* * call opal_output_open to open the stream. The return value * is guaranteed to be i. So we can ignore it. */ opal_output_open(&lds); } } /* * Close a stream */ void opal_output_close(int output_id) { int i; /* Setup */ if (!initialized) { return; } /* If it's valid, used, enabled, and has an open file descriptor, * free the resources associated with the descriptor */ OPAL_THREAD_LOCK(&mutex); if (output_id >= 0 && output_id < OPAL_OUTPUT_MAX_STREAMS && info[output_id].ldi_used && info[output_id].ldi_enabled) { free_descriptor(output_id); /* If no one has the syslog open, we should close it */ for (i = 0; i < OPAL_OUTPUT_MAX_STREAMS; ++i) { if (info[i].ldi_used && info[i].ldi_syslog) { break; } } #ifndef __WINDOWS__ if (i >= OPAL_OUTPUT_MAX_STREAMS && syslog_opened) { closelog(); } #else DeregisterEventSource(info[output_id].ldi_syslog_ident); #endif } /* Somewhat of a hack to free up the temp_str */ if (NULL != temp_str) { free(temp_str); temp_str = NULL; temp_str_len = 0; } OPAL_THREAD_UNLOCK(&mutex); } /* * Main function to send output to a stream */ void opal_output(int output_id, const char *format, ...) { if (output_id >= 0 && output_id < OPAL_OUTPUT_MAX_STREAMS) { va_list arglist; va_start(arglist, format); output(output_id, format, arglist); va_end(arglist); } } /* * Send a message to a stream if the verbose level is high enough */ void opal_output_verbose(int level, int output_id, const char *format, ...) { if (output_id >= 0 && output_id < OPAL_OUTPUT_MAX_STREAMS && info[output_id].ldi_verbose_level >= level) { va_list arglist; va_start(arglist, format); output(output_id, format, arglist); va_end(arglist); } } /* * Set the verbosity level of a stream */ void opal_output_set_verbosity(int output_id, int level) { if (output_id >= 0 && output_id < OPAL_OUTPUT_MAX_STREAMS) { info[output_id].ldi_verbose_level = level; } } /* * Control where output flies will go */ void opal_output_set_output_file_info(const char *dir, const char *prefix, char **olddir, char **oldprefix) { if (NULL != olddir) { *olddir = strdup(output_dir); } if (NULL != oldprefix) { *oldprefix = strdup(output_prefix); } if (NULL != dir) { free(output_dir); output_dir = strdup(dir); } if (NULL != prefix) { free(output_prefix); output_prefix = strdup(prefix); } } /* * Shut down the output stream system */ void opal_output_finalize(void) { if (initialized) { if (verbose_stream != -1) { opal_output_close(verbose_stream); } verbose_stream = -1; free (output_prefix); free (output_dir); OBJ_DESTRUCT(&verbose); OBJ_DESTRUCT(&mutex); } #if defined(__WINDOWS__) WSACleanup(); #endif /* defined(__WINDOWS__) */ } /************************************************************************/ /* * Constructor */ static void construct(opal_object_t *obj) { opal_output_stream_t *stream = (opal_output_stream_t*) obj; stream->lds_verbose_level = 0; stream->lds_syslog_priority = 0; stream->lds_syslog_ident = NULL; stream->lds_prefix = NULL; stream->lds_is_debugging = false; stream->lds_want_syslog = false; stream->lds_want_stdout = false; stream->lds_want_stderr = false; stream->lds_want_file = false; stream->lds_want_file_append = false; stream->lds_file_suffix = NULL; } /* * Back-end of open() and reopen(). Necessary to have it as a * back-end function so that we can do the thread locking properly * (especially upon reopen). */ static int do_open(int output_id, opal_output_stream_t * lds) { int i; /* Setup */ if (!initialized) { opal_output_init(); } /* If output_id == -1, find an available stream, or return * OPAL_ERROR */ if (-1 == output_id) { OPAL_THREAD_LOCK(&mutex); for (i = 0; i < OPAL_OUTPUT_MAX_STREAMS; ++i) { if (!info[i].ldi_used) { break; } } if (i >= OPAL_OUTPUT_MAX_STREAMS) { OPAL_THREAD_UNLOCK(&mutex); return OPAL_ERR_OUT_OF_RESOURCE; } } /* Otherwise, we're reopening, so we need to free all previous * resources, close files, etc. */ else { free_descriptor(output_id); i = output_id; } /* Special case: if we got NULL for lds, then just use the default * verbose */ if (NULL == lds) { lds = &verbose; } /* Got a stream -- now initialize it and open relevant outputs */ info[i].ldi_used = true; if (-1 == output_id) { OPAL_THREAD_UNLOCK(&mutex); } info[i].ldi_enabled = lds->lds_is_debugging ? (bool) OMPI_ENABLE_DEBUG : true; info[i].ldi_verbose_level = lds->lds_verbose_level; info[i].ldi_syslog = lds->lds_want_syslog; if (lds->lds_want_syslog) { #ifndef __WINDOWS__ if (NULL != lds->lds_syslog_ident) { info[i].ldi_syslog_ident = strdup(lds->lds_syslog_ident); openlog(lds->lds_syslog_ident, LOG_PID, LOG_USER); } else { info[i].ldi_syslog_ident = NULL; openlog("opal", LOG_PID, LOG_USER); } #else if (NULL == (info[i].ldi_syslog_ident = RegisterEventSource(NULL, TEXT("opal: ")))) { /* handle the error */ return OPAL_ERROR; } #endif syslog_opened = true; info[i].ldi_syslog_priority = lds->lds_syslog_priority; } if (NULL != lds->lds_prefix) { info[i].ldi_prefix = strdup(lds->lds_prefix); info[i].ldi_prefix_len = (int)strlen(lds->lds_prefix); } else { info[i].ldi_prefix = NULL; info[i].ldi_prefix_len = 0; } info[i].ldi_stdout = lds->lds_want_stdout; info[i].ldi_stderr = lds->lds_want_stderr; info[i].ldi_fd = -1; info[i].ldi_file = lds->lds_want_file; info[i].ldi_file_suffix = (NULL == lds->lds_file_suffix) ? NULL : strdup(lds->lds_file_suffix); info[i].ldi_file_want_append = lds->lds_want_file_append; info[i].ldi_file_num_lines_lost = 0; /* Don't open a file in the session directory now -- do that lazily * so that if there's no output, we don't have an empty file */ return i; } static int open_file(int i) { int flags; char *filename; /* Setup the filename and open flags */ if (NULL != output_dir) { filename = (char *) malloc(MAXPATHLEN); if (NULL == filename) { return OPAL_ERR_OUT_OF_RESOURCE; } strcpy(filename, output_dir); strcat(filename, "/"); if (NULL != output_prefix) { strcat(filename, output_prefix); } if (info[i].ldi_file_suffix != NULL) { strcat(filename, info[i].ldi_file_suffix); } else { info[i].ldi_file_suffix = NULL; strcat(filename, "output.txt"); } flags = O_CREAT | O_RDWR; if (!info[i].ldi_file_want_append) { flags |= O_TRUNC; } /* Actually open the file */ info[i].ldi_fd = open(filename, flags, 0644); if (-1 == info[i].ldi_fd) { info[i].ldi_used = false; return OPAL_ERR_IN_ERRNO; } /* Make the file be close-on-exec to prevent child inheritance * problems */ #ifndef __WINDOWS__ /* TODO: Need to find out the equivalent in windows */ fcntl(info[i].ldi_fd, F_SETFD, 1); #endif free(filename); } /* Return successfully even if the session dir did not exist yet; * we'll try opening it later */ return OPAL_SUCCESS; } /* * Free all the resources associated with a descriptor. */ static void free_descriptor(int output_id) { output_desc_t *ldi; if (output_id >= 0 && output_id < OPAL_OUTPUT_MAX_STREAMS && info[output_id].ldi_used && info[output_id].ldi_enabled) { ldi = &info[output_id]; if (-1 != ldi->ldi_fd) { close(ldi->ldi_fd); } ldi->ldi_used = false; /* If we strduped a prefix, suffix, or syslog ident, free it */ if (NULL != ldi->ldi_prefix) { free(ldi->ldi_prefix); } ldi->ldi_prefix = NULL; if (NULL != ldi->ldi_file_suffix) { free(ldi->ldi_file_suffix); } ldi->ldi_file_suffix = NULL; #ifndef __WINDOWS__ if (NULL != ldi->ldi_syslog_ident) { free(ldi->ldi_syslog_ident); } ldi->ldi_syslog_ident = NULL; #endif } } /* * Do the actual output. Take a va_list so that we can be called from * multiple different places, even functions that took "..." as input * arguments. */ static void output(int output_id, const char *format, va_list arglist) { size_t len, total_len; bool want_newline = false; char *str; output_desc_t *ldi; /* Setup */ if (!initialized) { opal_output_init(); } /* If it's valid, used, and enabled, output */ if (output_id >= 0 && output_id < OPAL_OUTPUT_MAX_STREAMS && info[output_id].ldi_used && info[output_id].ldi_enabled) { ldi = &info[output_id]; /* Make the formatted string */ OPAL_THREAD_LOCK(&mutex); vasprintf(&str, format, arglist); total_len = len = strlen(str); if ('\n' != str[len - 1]) { want_newline = true; ++total_len; } if (NULL != ldi->ldi_prefix) { total_len += strlen(ldi->ldi_prefix); } if (temp_str_len < total_len + want_newline) { if (NULL != temp_str) { free(temp_str); } temp_str = (char *) malloc(total_len * 2); temp_str_len = total_len * 2; } if (NULL != ldi->ldi_prefix) { if (want_newline) { snprintf(temp_str, temp_str_len, "%s%s\n", ldi->ldi_prefix, str); } else { snprintf(temp_str, temp_str_len, "%s%s", ldi->ldi_prefix, str); } } else { if (want_newline) { snprintf(temp_str, temp_str_len, "%s\n", str); } else { snprintf(temp_str, temp_str_len, "%s", str); } } /* Syslog output */ if (ldi->ldi_syslog) { #ifndef __WINDOWS__ syslog(ldi->ldi_syslog_priority, str); #endif } /* stdout output */ if (ldi->ldi_stdout) { write(fileno(stdout), temp_str, (int)strlen(temp_str)); fflush(stdout); } /* stderr output */ if (ldi->ldi_stderr) { write(fileno(stderr),temp_str, (int)strlen(temp_str)); fflush(stderr); } /* File output -- first check to see if the file opening was * delayed. If so, try to open it. If we failed to open it, * then just discard (there are big warnings in the * opal_output.h docs about this!). */ if (ldi->ldi_file) { if (ldi->ldi_fd == -1) { if (OPAL_SUCCESS != open_file(output_id)) { ++ldi->ldi_file_num_lines_lost; } else if (ldi->ldi_file_num_lines_lost > 0) { char buffer[BUFSIZ]; memset(buffer, 0, BUFSIZ); snprintf(buffer, BUFSIZ - 1, "[WARNING: %d lines lost because the Open MPI process session directory did\n not exist when opal_output() was invoked]\n", ldi->ldi_file_num_lines_lost); write(ldi->ldi_fd, buffer, (int)strlen(buffer)); ldi->ldi_file_num_lines_lost = 0; } } if (ldi->ldi_fd != -1) { write(ldi->ldi_fd, temp_str, (int)total_len); } } OPAL_THREAD_UNLOCK(&mutex); free(str); } }