1
1
openmpi/opal/util/info.c
Jeff Squyres 9b88e60fc8 info_get: ensure to copy all requested characters
When querying an info value, copy out exactly as many characters as
the caller asked for -- do not artificially truncate the target just
to ensure that it is \0-terminated.

Specifically: do not use opal_string_copy() to copy info values,
because opal_string_copy() will guarantee to \0-terminate the target,
even if it means truncating the target.  E.g., if the caller calls
opal_info_get_nolock() with valuelen=5, opal_string_copy() will return
"1234\0" -- which is wrong.  This commit fixes the behavior to return
"12345".

Signed-off-by: Jeff Squyres <jsquyres@cisco.com>
2018-12-19 13:01:45 -08:00

597 строки
17 KiB
C

/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil -*- */
/*
* Copyright (c) 2004-2005 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-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-2018 Cisco Systems, Inc. All rights reserved
* Copyright (c) 2009 Sun Microsystems, Inc. All rights reserved.
* Copyright (c) 2012-2017 Los Alamos National Security, LLC. All rights
* reserved.
* Copyright (c) 2015-2017 Research Organization for Information Science
* and Technology (RIST). All rights reserved.
* Copyright (c) 2016-2018 IBM Corporation. All rights reserved.
* Copyright (c) 2017 Intel, Inc. All rights reserved.
* $COPYRIGHT$
*
* Additional copyrights may follow
*
* $HEADER$
*/
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <limits.h>
#include <ctype.h>
#ifdef HAVE_SYS_UTSNAME_H
#include <sys/utsname.h>
#endif
#include <assert.h>
#include "opal/util/argv.h"
#include "opal/util/opal_getcwd.h"
#include "opal/util/output.h"
#include "opal/util/string_copy.h"
#include "opal/util/info.h"
/*
* Local functions
*/
static void info_constructor(opal_info_t *info);
static void info_destructor(opal_info_t *info);
static void info_entry_constructor(opal_info_entry_t *entry);
static void info_entry_destructor(opal_info_entry_t *entry);
static opal_info_entry_t *info_find_key (opal_info_t *info, const char *key);
/*
* opal_info_t classes
*/
OBJ_CLASS_INSTANCE(opal_info_t,
opal_list_t,
info_constructor,
info_destructor);
/*
* opal_info_entry_t classes
*/
OBJ_CLASS_INSTANCE(opal_info_entry_t,
opal_list_item_t,
info_entry_constructor,
info_entry_destructor);
/*
* Duplicate an info
*/
int opal_info_dup (opal_info_t *info, opal_info_t **newinfo)
{
int err;
opal_info_entry_t *iterator;
OPAL_THREAD_LOCK(info->i_lock);
OPAL_LIST_FOREACH(iterator, &info->super, opal_info_entry_t) {
err = opal_info_set(*newinfo, iterator->ie_key, iterator->ie_value);
if (OPAL_SUCCESS != err) {
OPAL_THREAD_UNLOCK(info->i_lock);
return err;
}
}
OPAL_THREAD_UNLOCK(info->i_lock);
return OPAL_SUCCESS;
}
static void opal_info_get_nolock (opal_info_t *info, const char *key, int valuelen,
char *value, int *flag)
{
opal_info_entry_t *search;
search = info_find_key (info, key);
if (NULL == search){
*flag = 0;
} else if (value && valuelen) {
/*
* We have found the element, so we can return the value
* Set the flag and value
*/
*flag = 1;
// Note: we copy exactly (valuelen) characters, because that's
// what the caller asked for. Don't use opal_string_copy()
// here, because that will guarantee to \0-terminate what is
// copied (i.e., potentially copy (valuelen-1) chars and then
// an additional \0). Instead: copy over exactly (valuelen)
// characters, and if that's not \0-terminated, then so be it.
memcpy(value, search->ie_value, valuelen);
}
}
static int opal_info_set_nolock (opal_info_t *info, const char *key, const char *value)
{
char *new_value;
opal_info_entry_t *new_info;
opal_info_entry_t *old_info;
new_value = strdup(value);
if (NULL == new_value) {
return OPAL_ERR_OUT_OF_RESOURCE;
}
old_info = info_find_key (info, key);
if (NULL != old_info) {
/*
* key already exists. remove the value associated with it
*/
free(old_info->ie_value);
old_info->ie_value = new_value;
} else {
new_info = OBJ_NEW(opal_info_entry_t);
if (NULL == new_info) {
free(new_value);
OPAL_THREAD_UNLOCK(info->i_lock);
return OPAL_ERR_OUT_OF_RESOURCE;
}
opal_string_copy (new_info->ie_key, key, OPAL_MAX_INFO_KEY);
new_info->ie_value = new_value;
opal_list_append (&(info->super), (opal_list_item_t *) new_info);
}
return OPAL_SUCCESS;
}
/*
* An object's info can be set, but those settings can be modified by
* system callbacks. When those callbacks happen, we save a "__IN_<key>"/"val"
* copy of changed or erased values.
*
* extra options for how to dup:
* include_system_extras (default 1)
* omit_ignored (default 1)
* show_modifications (default 0)
*/
static
int opal_info_dup_mode (opal_info_t *info, opal_info_t **newinfo,
int include_system_extras, // (k/v with no corresponding __IN_k)
int omit_ignored, // (__IN_k with no k/v)
int show_modifications) // (pick v from k/v or __IN_k/v)
{
int err, flag;
opal_info_entry_t *iterator;
char savedkey[OPAL_MAX_INFO_KEY + 1]; // iterator->ie_key has this as its size
char savedval[OPAL_MAX_INFO_VAL];
char *valptr, *pkey;
int is_IN_key;
int exists_IN_key, exists_reg_key;
OPAL_THREAD_LOCK(info->i_lock);
OPAL_LIST_FOREACH(iterator, &info->super, opal_info_entry_t) {
// If we see an __IN_<key> key but no <key>, decide what to do based on mode.
// If we see an __IN_<key> and a <key>, skip since it'll be handled when
// we process <key>.
is_IN_key = 0;
exists_IN_key = 0;
exists_reg_key = 0;
pkey = iterator->ie_key;
if (0 == strncmp(iterator->ie_key, OPAL_INFO_SAVE_PREFIX,
strlen(OPAL_INFO_SAVE_PREFIX)))
{
pkey += strlen(OPAL_INFO_SAVE_PREFIX);
is_IN_key = 1;
exists_IN_key = 1;
opal_info_get_nolock (info, pkey, 0, NULL, &flag);
if (flag) {
exists_reg_key = 1;
}
} else {
is_IN_key = 0;
exists_reg_key = 1;
// see if there is an __IN_<key> for the current <key>
if (strlen(OPAL_INFO_SAVE_PREFIX) + strlen(pkey) < OPAL_MAX_INFO_KEY) {
snprintf(savedkey, OPAL_MAX_INFO_KEY+1,
OPAL_INFO_SAVE_PREFIX "%s", pkey);
// (the prefix macro is a string, so the unreadable part above is a string concatenation)
opal_info_get_nolock (info, savedkey, OPAL_MAX_INFO_VAL,
savedval, &flag);
} else {
flag = 0;
}
if (flag) {
exists_IN_key = 1;
}
}
if (is_IN_key) {
if (exists_reg_key) {
// we're processing __IN_<key> and there exists a <key> so we'll handle it then
continue;
} else {
// we're processing __IN_<key> and no <key> exists
// this would mean <key> was set by the user but ignored by the system
// so base our behavior on the omit_ignored
if (!omit_ignored) {
err = opal_info_set_nolock(*newinfo, pkey, iterator->ie_value);
if (OPAL_SUCCESS != err) {
OPAL_THREAD_UNLOCK(info->i_lock);
return err;
}
}
}
} else {
valptr = 0;
if (!exists_IN_key) {
// we're processing <key> and no __IN_<key> <key> exists
// this would mean it's a system setting, not something that came from the user
if (include_system_extras) {
valptr = iterator->ie_value;
}
} else {
// we're processing <key> and __IN_<key> also exists
// pick which value to use
if (!show_modifications) {
valptr = savedval;
} else {
valptr = iterator->ie_value;
}
}
if (valptr) {
err = opal_info_set_nolock(*newinfo, pkey, valptr);
if (OPAL_SUCCESS != err) {
OPAL_THREAD_UNLOCK(info->i_lock);
return err;
}
}
}
}
OPAL_THREAD_UNLOCK(info->i_lock);
return OPAL_SUCCESS;
}
/*
* Implement opal_info_dup_mpistandard by using whatever mode
* settings represent our interpretation of the standard
*/
int opal_info_dup_mpistandard (opal_info_t *info, opal_info_t **newinfo)
{
return opal_info_dup_mode (info, newinfo, 1, 1, 0);
}
/*
* Set a value on the info
*/
int opal_info_set (opal_info_t *info, const char *key, const char *value)
{
int ret;
OPAL_THREAD_LOCK(info->i_lock);
ret = opal_info_set_nolock(info, key, value);
OPAL_THREAD_UNLOCK(info->i_lock);
return ret;
}
int opal_info_set_value_enum (opal_info_t *info, const char *key, int value,
mca_base_var_enum_t *var_enum)
{
char *string_value;
int ret;
ret = var_enum->string_from_value (var_enum, value, &string_value);
if (OPAL_SUCCESS != ret) {
return ret;
}
return opal_info_set (info, key, string_value);
}
/*
* Get a value from an info
*/
int opal_info_get (opal_info_t *info, const char *key, int valuelen,
char *value, int *flag)
{
OPAL_THREAD_LOCK(info->i_lock);
opal_info_get_nolock(info, key, valuelen, value, flag);
OPAL_THREAD_UNLOCK(info->i_lock);
return OPAL_SUCCESS;
}
int opal_info_get_value_enum (opal_info_t *info, const char *key, int *value,
int default_value, mca_base_var_enum_t *var_enum,
int *flag)
{
opal_info_entry_t *search;
int ret;
*value = default_value;
OPAL_THREAD_LOCK(info->i_lock);
search = info_find_key (info, key);
if (NULL == search){
OPAL_THREAD_UNLOCK(info->i_lock);
*flag = 0;
return OPAL_SUCCESS;
}
/* we found a mathing key. pass the string value to the enumerator and
* return */
*flag = 1;
ret = var_enum->value_from_string (var_enum, search->ie_value, value);
OPAL_THREAD_UNLOCK(info->i_lock);
return ret;
}
/*
* Similar to opal_info_get(), but cast the result into a boolean
* using some well-defined rules.
*/
int opal_info_get_bool(opal_info_t *info, char *key, bool *value, int *flag)
{
char str[256];
str[sizeof(str) - 1] = '\0';
opal_info_get(info, key, sizeof(str) - 1, str, flag);
if (*flag) {
*value = opal_str_to_bool(str);
}
return OPAL_SUCCESS;
}
bool
opal_str_to_bool(char *str)
{
bool result = false;
char *ptr;
/* Trim whitespace */
ptr = str + sizeof(str) - 1;
while (ptr >= str && isspace(*ptr)) {
*ptr = '\0';
--ptr;
}
ptr = str;
while (ptr < str + sizeof(str) - 1 && *ptr != '\0' &&
isspace(*ptr)) {
++ptr;
}
if ('\0' != *ptr) {
if (isdigit(*ptr)) {
result = (bool) atoi(ptr);
} else if (0 == strcasecmp(ptr, "yes") ||
0 == strcasecmp(ptr, "true")) {
result = true;
} else if (0 != strcasecmp(ptr, "no") &&
0 != strcasecmp(ptr, "false")) {
/* RHC unrecognized value -- print a warning? */
}
}
return result;
}
/*
* Delete a key from an info
*/
int opal_info_delete(opal_info_t *info, const char *key)
{
opal_info_entry_t *search;
OPAL_THREAD_LOCK(info->i_lock);
search = info_find_key (info, key);
if (NULL == search){
OPAL_THREAD_UNLOCK(info->i_lock);
return OPAL_ERR_NOT_FOUND;
} else {
/*
* An entry with this key value was found. Remove the item
* and free the memory allocated to it.
* As this key *must* be available, we do not check for errors.
*/
opal_list_remove_item (&(info->super),
(opal_list_item_t *)search);
OBJ_RELEASE(search);
}
OPAL_THREAD_UNLOCK(info->i_lock);
return OPAL_SUCCESS;
}
/*
* Return the length of a value
*/
int opal_info_get_valuelen (opal_info_t *info, const char *key, int *valuelen,
int *flag)
{
opal_info_entry_t *search;
OPAL_THREAD_LOCK(info->i_lock);
search = info_find_key (info, key);
if (NULL == search){
*flag = 0;
} else {
/*
* We have found the element, so we can return the value
* Set the flag, value_length and value
*/
*flag = 1;
*valuelen = strlen(search->ie_value);
}
OPAL_THREAD_UNLOCK(info->i_lock);
return OPAL_SUCCESS;
}
/*
* Get the nth key
*/
int opal_info_get_nthkey (opal_info_t *info, int n, char *key)
{
opal_info_entry_t *iterator;
/*
* Iterate over and over till we get to the nth key
*/
OPAL_THREAD_LOCK(info->i_lock);
for (iterator = (opal_info_entry_t *)opal_list_get_first(&(info->super));
n > 0;
--n) {
iterator = (opal_info_entry_t *)opal_list_get_next(iterator);
if (opal_list_get_end(&(info->super)) ==
(opal_list_item_t *) iterator) {
OPAL_THREAD_UNLOCK(info->i_lock);
return OPAL_ERR_BAD_PARAM;
}
}
/*
* iterator is of the type opal_list_item_t. We have to
* cast it to opal_info_entry_t before we can use it to
* access the value
*/
opal_string_copy(key, iterator->ie_key, OPAL_MAX_INFO_KEY);
OPAL_THREAD_UNLOCK(info->i_lock);
return OPAL_SUCCESS;
}
/*
* This function is invoked when OBJ_NEW() is called. Here, we add this
* info pointer to the table and then store its index as the handle
*/
static void info_constructor(opal_info_t *info)
{
info->i_lock = OBJ_NEW(opal_mutex_t);
}
/*
* This function is called during OBJ_DESTRUCT of "info". When this
* done, we need to remove the entry from the opal fortran to C
* translation table
*/
static void info_destructor(opal_info_t *info)
{
opal_list_item_t *item;
opal_info_entry_t *iterator;
/* Remove every key in the list */
for (item = opal_list_remove_first(&(info->super));
NULL != item;
item = opal_list_remove_first(&(info->super))) {
iterator = (opal_info_entry_t *) item;
OBJ_RELEASE(iterator);
}
/* Release the lock */
OBJ_RELEASE(info->i_lock);
}
/*
* opal_info_entry_t interface functions
*/
static void info_entry_constructor(opal_info_entry_t *entry)
{
memset(entry->ie_key, 0, sizeof(entry->ie_key));
entry->ie_key[OPAL_MAX_INFO_KEY] = 0;
}
static void info_entry_destructor(opal_info_entry_t *entry)
{
if (NULL != entry->ie_value) {
free(entry->ie_value);
}
}
/*
* Find a key
*
* Do NOT thread lock in here -- the calling function is responsible
* for that.
*/
static opal_info_entry_t *info_find_key (opal_info_t *info, const char *key)
{
opal_info_entry_t *iterator;
/* No thread locking in here! */
/* Iterate over all the entries. If the key is found, then
* return immediately. Else, the loop will fall of the edge
* and NULL is returned
*/
OPAL_LIST_FOREACH(iterator, &info->super, opal_info_entry_t) {
if (0 == strcmp(key, iterator->ie_key)) {
return iterator;
}
}
return NULL;
}
int
opal_info_value_to_int(char *value, int *interp)
{
long tmp;
char *endp;
if (NULL == value || '\0' == value[0]) return OPAL_ERR_BAD_PARAM;
errno = 0;
tmp = strtol(value, &endp, 10);
/* we found something not a number */
if (*endp != '\0') return OPAL_ERR_BAD_PARAM;
/* underflow */
if (tmp == 0 && errno == EINVAL) return OPAL_ERR_BAD_PARAM;
*interp = (int) tmp;
return OPAL_SUCCESS;
}
int
opal_info_value_to_bool(char *value, bool *interp)
{
int tmp;
/* idiot case */
if (NULL == value || NULL == interp) return OPAL_ERR_BAD_PARAM;
/* is it true / false? */
if (0 == strcmp(value, "true")) {
*interp = true;
return OPAL_SUCCESS;
} else if (0 == strcmp(value, "false")) {
*interp = false;
return OPAL_SUCCESS;
/* is it a number? */
} else if (OPAL_SUCCESS == opal_info_value_to_int(value, &tmp)) {
if (tmp == 0) {
*interp = false;
} else {
*interp = true;
}
return OPAL_SUCCESS;
}
return OPAL_ERR_BAD_PARAM;
}