From 2250be582dbf33b60aaa055afd92b292949a9c6b Mon Sep 17 00:00:00 2001 From: Iain Bason Date: Tue, 21 Jul 2009 20:19:38 +0000 Subject: [PATCH] Added autodetect installdirs component. Currently supports Solaris and Linux. * Installation directories will be inferred from the actual location of the shared library that contains the component. * OPAL_PREFIX and other environment variables allow users to override the inferred directories. They should no longer be necessary in most cases, though. * Any directories that cannot be inferred will fall back to whatever is provided by the config installdirs component. This commit was SVN r21723. --- AUTHORS | 1 + NEWS | 7 +- opal/mca/installdirs/autodetect/Makefile.am | 26 ++ opal/mca/installdirs/autodetect/configure.m4 | 65 ++++ .../installdirs/autodetect/configure.params | 15 + .../opal_installdirs_autodetect_component.c | 181 +++++++++++ .../autodetect/opal_installdirs_backtrace.c | 23 ++ .../autodetect/opal_installdirs_linux.c | 188 +++++++++++ .../autodetect/opal_installdirs_solaris.c | 91 ++++++ .../autodetect/opal_installdirs_walkcontext.c | 34 ++ .../base/installdirs_base_components.c | 112 ++++--- .../base/installdirs_base_expand.c | 306 ++++++++++++++---- 12 files changed, 951 insertions(+), 98 deletions(-) create mode 100644 opal/mca/installdirs/autodetect/Makefile.am create mode 100644 opal/mca/installdirs/autodetect/configure.m4 create mode 100644 opal/mca/installdirs/autodetect/configure.params create mode 100644 opal/mca/installdirs/autodetect/opal_installdirs_autodetect_component.c create mode 100644 opal/mca/installdirs/autodetect/opal_installdirs_backtrace.c create mode 100644 opal/mca/installdirs/autodetect/opal_installdirs_linux.c create mode 100644 opal/mca/installdirs/autodetect/opal_installdirs_solaris.c create mode 100644 opal/mca/installdirs/autodetect/opal_installdirs_walkcontext.c diff --git a/AUTHORS b/AUTHORS index 3604b30029..5321a6e98b 100644 --- a/AUTHORS +++ b/AUTHORS @@ -32,6 +32,7 @@ gwatson Greg Watson LANL herault Thomas Herault INRIA hpcstork Sven Stork HLRS htor Torsten Hoefler IU, TUC +igb Iain Bason Sun jdmason Jon Mason Chelsio jjhursey Josh Hursey IU, ORNL, LANL, LBNL jnysal Nysal Jan IBM diff --git a/NEWS b/NEWS index 43c4d6fd1a..fc85c291f1 100644 --- a/NEWS +++ b/NEWS @@ -10,7 +10,7 @@ Copyright (c) 2004-2006 The Regents of the University of California. All rights reserved. Copyright (c) 2006-2009 Cisco Systems, Inc. All rights reserved. Copyright (c) 2006 Voltaire, Inc. All rights reserved. -Copyright (c) 2006 Sun Microsystems, Inc. All rights reserved. +Copyright (c) 2006-2009 Sun Microsystems, Inc. All rights reserved. Use is subject to license terms. Copyright (c) 2006-2007 Los Alamos National Security, LLC. All rights reserved. @@ -32,6 +32,11 @@ Trunk (not on release branches yet) - Added Cray Compute Node Linux (CNL) and ALPS Support. --> Expected: ??? +- Added automatic detection of installation directories for + Solaris and Linux. The OPAL_PREFIX and OPAL_DESTDIR + environment variables no longer need to be set when binaries + are installed in non-default locations. + 1.5 --- diff --git a/opal/mca/installdirs/autodetect/Makefile.am b/opal/mca/installdirs/autodetect/Makefile.am new file mode 100644 index 0000000000..1f0df228fa --- /dev/null +++ b/opal/mca/installdirs/autodetect/Makefile.am @@ -0,0 +1,26 @@ +# +# Copyright (c) 2009 Sun Microsystems, Inc. All rights reserved. +# $COPYRIGHT$ +# +# Additional copyrights may follow +# +# $HEADER$ +# + +noinst_LTLIBRARIES = libmca_installdirs_autodetect.la + +libmca_installdirs_autodetect_la_SOURCES = opal_installdirs_autodetect_component.c + +EXTRA_libmca_installdirs_autodetect_la_SOURCES = \ + opal_installdirs_solaris.c \ + opal_installdirs_linux.c \ + opal_installdirs_backtrace.c \ + opal_installdirs_walkcontext.c + +libmca_installdirs_autodetect_la_LIBADD = \ + $(OMPI_INSTALLDIRS_AUTODETECT_PATH) \ + $(OMPI_INSTALLDIRS_AUTODETECT_PC) + +libmca_installdirs_autodetect_la_DEPENDENCIES = \ + $(OMPI_INSTALLDIRS_AUTODETECT_PATH) \ + $(OMPI_INSTALLDIRS_AUTODETECT_PC) diff --git a/opal/mca/installdirs/autodetect/configure.m4 b/opal/mca/installdirs/autodetect/configure.m4 new file mode 100644 index 0000000000..2ee7991dfe --- /dev/null +++ b/opal/mca/installdirs/autodetect/configure.m4 @@ -0,0 +1,65 @@ +# -*- shell-script -*- +# +# Copyright (c) 2009 Sun Microsystems, Inc. All rights reserved. +# $COPYRIGHT$ +# +# Additional copyrights may follow +# +# $HEADER$ +# + +AC_DEFUN([MCA_installdirs_autodetect_COMPILE_MODE], [ + AC_MSG_CHECKING([for MCA component $2:$3 compile mode]) + $4="static" + AC_MSG_RESULT([$$4]) +]) + + +# MCA_installdirs_autodetect_CONFIG(action-if-can-compile, +# [action-if-cant-compile]) +# ------------------------------------------------ +# +# Solaris uses a binary-format /proc/$$/map file that contains a +# sequence of prmap_t structures. Those can be used to look up +# associated files in /proc/$$/path, which are symbolic links to +# the actual files. +# +# AIX has /proc/$$/map and /proc/$$/object, but no /proc/$$/path. +# I don't know how to achieve autodetect functionality on AIX. +# +# Linux has /proc/self/maps that contains text with both virtual +# addresses and paths. (Under Linux 2.0 there are no paths. I +# don't know how to achieve autodetect functionality on such +# systems.) + +AC_DEFUN([MCA_installdirs_autodetect_CONFIG],[ + AC_CHECK_HEADERS(procfs.h, + [AC_CHECK_FILE(/proc/$$/path, + [procfs_path_happy="yes" + OMPI_INSTALLDIRS_AUTODETECT_PATH=opal_installdirs_solaris.lo], + [procfs_path_happy="no"])], + [AC_CHECK_FILE(/proc/self/maps, + [procfs_path_happy="yes" + OMPI_INSTALLDIRS_AUTODETECT_PATH=opal_installdirs_linux.lo], + [procfs_path_happy="no"])]) + + AS_IF([test "$procfs_path_happy" = "yes"],[ + # The following check is from opal/mca/backtrace/execinfo/configure.m4 + AC_CHECK_HEADERS([execinfo.h],[ + # FreeBSD has backtrace in -lexecinfo, usually in libc + OMPI_CHECK_FUNC_LIB([backtrace], [execinfo], + [findpc_happy="yes" + OMPI_INSTALLDIRS_AUTODETECT_PC=opal_installdirs_backtrace.lo], + [findpc_happy="no"])]) + + AS_IF([test "$backtrace_execinfo_happy" = "no"],[ + AC_CHECK_HEADERS([ucontext.h],[ + OMPI_CHECK_FUNC_LIB([walkcontext],, + [findpc_happy="yes" + OMPI_INSTALLDIRS_AUTODETECT_PC=opal_installdirs_walkcontext.lo], + [findpc_happy="no"])])])]) + + AS_IF([test "$procfs_path_happy" = "yes" -a "$findpc_happy" = "yes"],[ + AC_SUBST([OMPI_INSTALLDIRS_AUTODETECT_PATH]) + AC_SUBST([OMPI_INSTALLDIRS_AUTODETECT_PC]) + $1],[$2])]) diff --git a/opal/mca/installdirs/autodetect/configure.params b/opal/mca/installdirs/autodetect/configure.params new file mode 100644 index 0000000000..ea6d1d4b88 --- /dev/null +++ b/opal/mca/installdirs/autodetect/configure.params @@ -0,0 +1,15 @@ +# -*- shell-script -*- +# +# Copyright (c) 2006 Los Alamos National Security, LLC. All rights +# reserved. +# $COPYRIGHT$ +# +# Additional copyrights may follow +# +# $HEADER$ +# + +# Specific to this module + +PARAM_CONFIG_PRIORITY=5 +PARAM_CONFIG_FILES="Makefile" diff --git a/opal/mca/installdirs/autodetect/opal_installdirs_autodetect_component.c b/opal/mca/installdirs/autodetect/opal_installdirs_autodetect_component.c new file mode 100644 index 0000000000..a3ff5c241b --- /dev/null +++ b/opal/mca/installdirs/autodetect/opal_installdirs_autodetect_component.c @@ -0,0 +1,181 @@ +/* -*- Mode: C; c-basic-offset:4 ; -*- */ +/* + * Copyright (c) 2009 Sun Microsystems, Inc. All rights reserved. + * + * $COPYRIGHT$ + * + * Additional copyrights may follow + * + * $HEADER$ + * + * This module automatically detects the path to itself, and + * instructs the base install_dirs module to infer the prefix + * from that. + */ + +#include "opal_config.h" + +#include "opal/constants.h" +#include "opal/mca/mca.h" +#include "opal/mca/installdirs/installdirs.h" +#include "opal/util/basename.h" + +#include +#include +#include +#include +#include + +int opal_installdirs_autodetect_open(void); + +opal_installdirs_base_component_t mca_installdirs_autodetect_component = { + /* First, the mca_component_t struct containing meta information + about the component itself */ + { + OPAL_INSTALLDIRS_BASE_VERSION_2_0_0, + + /* Component name and version */ + "autodetect", + OPAL_MAJOR_VERSION, + OPAL_MINOR_VERSION, + OPAL_RELEASE_VERSION, + + /* Component open and close functions */ + opal_installdirs_autodetect_open, + NULL + }, + { + /* This component is Checkpointable */ + MCA_BASE_METADATA_PARAM_CHECKPOINT + } +}; + +/* + * Determine whether the load object is an executable or a shared library. + * The code only works for ELF files. The values and offsets are cribbed + * from the "file" command's magic number file. +*/ + +typedef enum { executable, shared_object, unknown_object } load_obj_t; + +static load_obj_t +whatis(const char *path) +{ + int fd; + char buf[18]; + uint16_t *kind; + + fd = open(path, O_RDONLY); + if (fd < 0) { + return unknown_object; + } + + if (read(fd, buf, sizeof(buf)) < sizeof(buf)) { + close(fd); + return unknown_object; + } + close(fd); + + if (strncmp(buf, "\177ELF", 4) != 0) { + return unknown_object; + } + + kind = (uint16_t*)&buf[16]; + if (2 == *kind) { + return executable; + } else if (3 == *kind) { + return shared_object; + } else { + return unknown_object; + } +} + +/* + * OS-dependent function to get the address of some instruction in + * this module. We cannot in general just do &func, because if the + * module is in a shared library that can return some other address. + * (On Solaris, for example, it will return the address of an entry in + * the PLT of the program, rather than the shared library.) + */ + +uintptr_t opal_installdirs_autodetect_pc(void); + +/* + * OS-dependent function to find the path to the executable or shared + * library that contains the given address in the process' address + * space. + */ + +const char *opal_installdirs_autodetect_path(uintptr_t); + +static int +opal_installdirs_autodetect_open(void) +{ + uintptr_t my_addr; + const char *path; + const char *my_dir; + const char *infer_from; + + if (getenv("OPAL_DESTDIR") != NULL) { + /* + * OPAL_DESTDIR does not play well with autodetect. We + * certainly don't want it to be used as a prefix for the actual + * installed path. We could try to inhibit it only for those + * paths that cannot be inferred from the actual installed + * path, but it is simpler just to assume the user wants no + * auto detection if OPAL_DESTDIR is set. + */ + return OPAL_ERR_NOT_FOUND; + } + + my_addr = opal_installdirs_autodetect_pc(); + if (0 == my_addr) { + return OPAL_ERR_NOT_FOUND; + } + path = opal_installdirs_autodetect_path(my_addr); + if (NULL == path) { + return OPAL_ERR_NOT_FOUND; + } + my_dir = opal_dirname(path); + if (NULL == my_dir) { + free(path); + return OPAL_ERR_NOT_FOUND; + } + switch(whatis(path)) { + case executable: + mca_installdirs_autodetect_component.install_dirs_data.bindir = my_dir; + infer_from = "${infer-bindir}"; + mca_installdirs_autodetect_component.install_dirs_data.libdir = infer_from; + break; + case shared_object: + mca_installdirs_autodetect_component.install_dirs_data.libdir = my_dir; + infer_from = "${infer-libdir}"; + mca_installdirs_autodetect_component.install_dirs_data.bindir = infer_from; + break; + default: + free(my_dir); + free(path); + return OPAL_ERR_NOT_FOUND; + break; + } + free(path); + + mca_installdirs_autodetect_component.install_dirs_data.prefix = infer_from; + mca_installdirs_autodetect_component.install_dirs_data.exec_prefix = infer_from; + mca_installdirs_autodetect_component.install_dirs_data.sbindir = infer_from; + mca_installdirs_autodetect_component.install_dirs_data.libexecdir = infer_from; + mca_installdirs_autodetect_component.install_dirs_data.datarootdir = infer_from; + mca_installdirs_autodetect_component.install_dirs_data.datadir = infer_from; + mca_installdirs_autodetect_component.install_dirs_data.sysconfdir = infer_from; + mca_installdirs_autodetect_component.install_dirs_data.sharedstatedir = infer_from; + mca_installdirs_autodetect_component.install_dirs_data.localstatedir = infer_from; + mca_installdirs_autodetect_component.install_dirs_data.includedir = infer_from; + mca_installdirs_autodetect_component.install_dirs_data.infodir = infer_from; + mca_installdirs_autodetect_component.install_dirs_data.mandir = infer_from; + mca_installdirs_autodetect_component.install_dirs_data.pkgdatadir = infer_from; + mca_installdirs_autodetect_component.install_dirs_data.pkglibdir = infer_from; + mca_installdirs_autodetect_component.install_dirs_data.pkgincludedir = infer_from; + + return OPAL_SUCCESS; +} + diff --git a/opal/mca/installdirs/autodetect/opal_installdirs_backtrace.c b/opal/mca/installdirs/autodetect/opal_installdirs_backtrace.c new file mode 100644 index 0000000000..8720194f06 --- /dev/null +++ b/opal/mca/installdirs/autodetect/opal_installdirs_backtrace.c @@ -0,0 +1,23 @@ +/* -*- Mode: C; c-basic-offset:4 ; -*- */ +/* + * Copyright (c) 2009 Sun Microsystems, Inc. All rights reserved. + * + * $COPYRIGHT$ + * + * Additional copyrights may follow + * + * $HEADER$ + */ + +#include "opal_config.h" + +#include + +uintptr_t +opal_installdirs_autodetect_pc() +{ + void *pc = 0; + + backtrace(&pc, 1); + return pc; +} diff --git a/opal/mca/installdirs/autodetect/opal_installdirs_linux.c b/opal/mca/installdirs/autodetect/opal_installdirs_linux.c new file mode 100644 index 0000000000..719f33c0fb --- /dev/null +++ b/opal/mca/installdirs/autodetect/opal_installdirs_linux.c @@ -0,0 +1,188 @@ +/* -*- Mode: C; c-basic-offset:4 ; -*- */ +/* + * Copyright (c) 2009 Sun Microsystems, Inc. All rights reserved. + * + * $COPYRIGHT$ + * + * Additional copyrights may follow + * + * $HEADER$ + * + * Read /proc/self/maps to find the path to the file containing + * an address. + */ + +#include "opal_config.h" +#include "opal_stdint.h" + +#include "opal/constants.h" + +#include +#include +#include + +uintptr_t read_addr(FILE *); +int skip_fields(FILE *, int); +int read_path(FILE *); +int skip_line(FILE *); + +const char * +opal_installdirs_autodetect_path(uintptr_t my_addr) +{ + FILE *f; + uintptr_t lo_start, lo_end; + + f = fopen("/proc/self/maps", "r"); + if (NULL == f) { + return NULL; + } + + for (;;) { + lo_start = read_addr(f); + if (0 == lo_start) { + fclose(f); + return NULL; + } + lo_end = read_addr(f); + if (0 == lo_end) { + fclose(f); + return NULL; + } + if (lo_start <= my_addr && lo_end > my_addr) { + const char *path, *my_dir; + int e = skip_fields(f, 4); + if (OPAL_SUCCESS != e) { + fclose(f); + return e; + } + path = read_path(f); + fclose(f); + return path; + } else { + int e = skip_line(f); + if (OPAL_SUCCESS != e) { + fclose(f); + return NULL; + } + } + } +} + +/* + * The following routines don't use isspace et al, because we don't + * want the locale setting to influence them. + */ + +/* + * Read a hex address from a file. We don't know how many + * digits there will be. We only know that the result will + * fit into a uintptr_t, and will not be zero. + * + * Return zero on error. + */ + +static uintptr_t +read_addr(FILE *f) +{ + int c; + uintptr_t n = 0; + + for (;;) { + c = getc(f); + if (EOF == c) { + return 0; + } + if (' ' == c || '-' == c) { + if (n > 0) { + return n; + } else { + continue; + } + } + if (c >= '0' && c <= '9') { + n <<= 4; + n += c - '0'; + } else if (c >= 'a' && c <= 'f') { + n <<= 4; + n += c - 'a' + 10; + } else if (c >= 'A' && c <= 'F') { + n <<= 4; + n += c - 'A' + 10; + } else { + return n; + } + } +} + +/* + * Skip n fields of input, where fields are separated by white space. + * + * Return OPAL_SUCCESS on success. + */ + +static int +skip_fields(FILE *f, int n) +{ + int c = getc(f); + while (n-- > 0) { + for (; c == ' ' || c == '\t'; c = getc(f)); + if (c == EOF || c == '\n') { + return OPAL_ERR_NOT_AVAILABLE; + } + for(; c != ' ' && c != '\t' && c != '\n'; c = getc(f)); + } + return OPAL_SUCCESS; +} + +/* + * Read a path, and return it. Return NULL on error. + */ + +static int +read_path(FILE *f) +{ + char *path = malloc(100); + int n = 100; + int i = 0; + int c = getc(f); + + for (; (c == ' ' || c == '\t') && c != '\n' && c != EOF; c = getc(f)); + for (; c != '\n' && EOF != c; c = getc(f)) { + if (i >= n) { + n += 100; + path = realloc(path, n); + } + if (NULL == path) { + return NULL; + } + path[i++] = c; + } + if (i >= n) { + n++; + path = realloc(path, n); + } else { + path = realloc(path, i + 1); + } + if (NULL == path) { + return NULL; + } + path[i] = '\0'; + return path; +} + +/* + * Skip to the next line of input. Return OPAL_SUCCESS on success. + */ + +static int +skip_line(FILE *f) +{ + int c = 0; + while (c != '\n') { + c = getc(f); + if (EOF == c) { + return OPAL_ERR_NOT_AVAILABLE; + } + } + return OPAL_SUCCESS; +} diff --git a/opal/mca/installdirs/autodetect/opal_installdirs_solaris.c b/opal/mca/installdirs/autodetect/opal_installdirs_solaris.c new file mode 100644 index 0000000000..8da33732ed --- /dev/null +++ b/opal/mca/installdirs/autodetect/opal_installdirs_solaris.c @@ -0,0 +1,91 @@ +/* -*- Mode: C; c-basic-offset:4 ; -*- */ +/* + * Copyright (c) 2009 Sun Microsystems, Inc. All rights reserved. + * + * $COPYRIGHT$ + * + * Additional copyrights may follow + * + * $HEADER$ + * + * Read /proc//map and /proc//path to find the file mapped + * into the process over the given address. + */ + +#include "opal_config.h" +#include "opal_stdint.h" + +#include "opal/constants.h" + +#include +#include +#include +#include +#include +#include + +const char * +opal_installdirs_autodetect_path(uintptr_t my_addr) +{ + pid_t my_pid; + char *map_path; + prmap_t map; + int map_fd; + char *obj_name; + ssize_t path_size, ls; + char *path; + + my_pid = getpid(); + asprintf(&map_path, "/proc/%d/map", my_pid); + if (NULL == map_path) { + return NULL; + } + map_fd = open(map_path, O_RDONLY); + free(map_path); + if (map_fd < 0) { + return NULL; + } + + for (;;) { + if (read(map_fd, &map, sizeof(map)) < sizeof(map)) { + return OPAL_ERR_NOT_FOUND; + } + if (map.pr_vaddr <= my_addr && map.pr_vaddr + map.pr_size > my_addr) { + break; + } + } + close(map_fd); + asprintf(&obj_name, "/proc/%d/path/%s", my_pid, map.pr_mapname); + if (NULL == obj_name) { + return NULL; + } + /* + We don't know how long the path to the load object might be. + Try allocating a reasonable length. If the readlink system + call doesn't use the entire buffer passed to it then we know + the path is complete. + */ + for (path_size = 100; ; path_size += 100) { + path = malloc(path_size); + if (NULL == path) { + free(obj_name); + return OPAL_ERR_OUT_OF_RESOURCE; + } + ls = readlink(obj_name, path, path_size); + if (ls < 0) { + free(obj_name); + free(path); + return OPAL_ERR_NOT_FOUND; + } + if (ls < path_size) { + path[ls] = '\0'; + break; + } + free(path); + } + + free(obj_name); + + return path; +} + diff --git a/opal/mca/installdirs/autodetect/opal_installdirs_walkcontext.c b/opal/mca/installdirs/autodetect/opal_installdirs_walkcontext.c new file mode 100644 index 0000000000..52fa3904cd --- /dev/null +++ b/opal/mca/installdirs/autodetect/opal_installdirs_walkcontext.c @@ -0,0 +1,34 @@ +/* -*- Mode: C; c-basic-offset:4 ; -*- */ +/* + * Copyright (c) 2009 Sun Microsystems, Inc. All rights reserved. + * + * $COPYRIGHT$ + * + * Additional copyrights may follow + * + * $HEADER$ + */ + +#include "opal_config.h" + +#include + +static int +savepc(uintptr_t pc, int sig, void *storage) +{ + *(uintptr_t*)storage = pc; + return 1; +} + +uintptr_t +opal_installdirs_autodetect_pc() +{ + ucontext_t ctx; + uintptr_t value = 0; + + if (getcontext(&ctx) == -1) { + return 0; + } + walkcontext(&ctx, savepc, &value); + return value; +} diff --git a/opal/mca/installdirs/base/installdirs_base_components.c b/opal/mca/installdirs/base/installdirs_base_components.c index 3849c77b75..2e28b8f213 100644 --- a/opal/mca/installdirs/base/installdirs_base_components.c +++ b/opal/mca/installdirs/base/installdirs_base_components.c @@ -1,11 +1,12 @@ /* * Copyright (c) 2006-2007 Los Alamos National Security, LLC. All rights - * reserved. + * reserved. * Copyright (c) 2007 Cisco Systems, Inc. All rights reserved. + * Copyright (c) 2009 Sun Microsystem, Inc. All rights reserved. * $COPYRIGHT$ - * + * * Additional copyrights may follow - * + * * $HEADER$ * */ @@ -22,29 +23,59 @@ int opal_installdirs_base_output; opal_install_dirs_t opal_install_dirs; opal_list_t opal_installdirs_components; -#define CONDITIONAL_COPY(target, origin, field) \ - do { \ - if (origin.field != NULL && target.field == NULL) { \ - target.field = origin.field; \ - } \ - } while (0) +char * +opal_install_dirs_infer(const char *inferred_field, + const char *infer_from_field, + size_t infer_from_field_len, + opal_install_dirs_t *component_installdirs); + +/* + * There is a memory leak when inferring a field. The function + * opal_install_dirs_infer returns allocated memory. We will + * later call opal_install_dirs_expand for the field, which + * also allocates memory. We don't record which fields were + * inferred, so we don't know whether to call free after calling + * opal_install_dirs_expand. + */ + +#define CONDITIONAL_COPY(target, origin, field) \ + do { \ + if (origin.field != NULL) { \ + if (NULL != target.field && \ + strncmp(target.field, "${infer-", 8) == 0) { \ + const char *fe = strchr(target.field, '}'); \ + if (NULL != fe) { \ + const char *f = target.field + 8; \ + target.field = \ + opal_install_dirs_infer(#field, f, fe - f, &origin); \ + } else { \ + target.field = NULL; \ + } \ + } \ + if (NULL == target.field) { \ + target.field = origin.field; \ + } \ + } \ + \ + } while (0) \ int opal_installdirs_base_open(void) { int i, ret; mca_base_component_list_item_t *cli; + opal_install_dirs_t expanded_dirs; OBJ_CONSTRUCT(&opal_installdirs_components, opal_list_t); for (i = 0 ; mca_installdirs_base_static_components[i] != NULL ; ++i) { - opal_installdirs_base_component_t *component = - (opal_installdirs_base_component_t*) + opal_installdirs_base_component_t *component = + (opal_installdirs_base_component_t*) mca_installdirs_base_static_components[i]; /* Save it in a global list for ompi_info */ cli = OBJ_NEW(mca_base_component_list_item_t); cli->cli_component = mca_installdirs_base_static_components[i]; - opal_list_append(&opal_installdirs_components, + opal_list_append(&opal_installdirs_components, &cli->super); if (NULL != component->component.mca_open_component) { @@ -53,7 +84,7 @@ opal_installdirs_base_open(void) } /* copy over the data, if something isn't already there */ - CONDITIONAL_COPY(opal_install_dirs, component->install_dirs_data, + CONDITIONAL_COPY(opal_install_dirs, component->install_dirs_data, prefix); CONDITIONAL_COPY(opal_install_dirs, component->install_dirs_data, exec_prefix); @@ -67,63 +98,64 @@ opal_installdirs_base_open(void) datarootdir); CONDITIONAL_COPY(opal_install_dirs, component->install_dirs_data, datadir); - CONDITIONAL_COPY(opal_install_dirs, component->install_dirs_data, + CONDITIONAL_COPY(opal_install_dirs, component->install_dirs_data, sysconfdir); - CONDITIONAL_COPY(opal_install_dirs, component->install_dirs_data, + CONDITIONAL_COPY(opal_install_dirs, component->install_dirs_data, sharedstatedir); - CONDITIONAL_COPY(opal_install_dirs, component->install_dirs_data, + CONDITIONAL_COPY(opal_install_dirs, component->install_dirs_data, localstatedir); - CONDITIONAL_COPY(opal_install_dirs, component->install_dirs_data, + CONDITIONAL_COPY(opal_install_dirs, component->install_dirs_data, libdir); - CONDITIONAL_COPY(opal_install_dirs, component->install_dirs_data, + CONDITIONAL_COPY(opal_install_dirs, component->install_dirs_data, includedir); - CONDITIONAL_COPY(opal_install_dirs, component->install_dirs_data, + CONDITIONAL_COPY(opal_install_dirs, component->install_dirs_data, infodir); - CONDITIONAL_COPY(opal_install_dirs, component->install_dirs_data, + CONDITIONAL_COPY(opal_install_dirs, component->install_dirs_data, mandir); CONDITIONAL_COPY(opal_install_dirs, component->install_dirs_data, pkgdatadir); - CONDITIONAL_COPY(opal_install_dirs, component->install_dirs_data, + CONDITIONAL_COPY(opal_install_dirs, component->install_dirs_data, pkglibdir); - CONDITIONAL_COPY(opal_install_dirs, component->install_dirs_data, + CONDITIONAL_COPY(opal_install_dirs, component->install_dirs_data, pkgincludedir); } /* expand out all the fields */ - opal_install_dirs.prefix = + expanded_dirs.prefix = opal_install_dirs_expand(opal_install_dirs.prefix); - opal_install_dirs.exec_prefix = + expanded_dirs.exec_prefix = opal_install_dirs_expand(opal_install_dirs.exec_prefix); - opal_install_dirs.bindir = + expanded_dirs.bindir = opal_install_dirs_expand(opal_install_dirs.bindir); - opal_install_dirs.sbindir = + expanded_dirs.sbindir = opal_install_dirs_expand(opal_install_dirs.sbindir); - opal_install_dirs.libexecdir = + expanded_dirs.libexecdir = opal_install_dirs_expand(opal_install_dirs.libexecdir); - opal_install_dirs.datarootdir = + expanded_dirs.datarootdir = opal_install_dirs_expand(opal_install_dirs.datarootdir); - opal_install_dirs.datadir = + expanded_dirs.datadir = opal_install_dirs_expand(opal_install_dirs.datadir); - opal_install_dirs.sysconfdir = + expanded_dirs.sysconfdir = opal_install_dirs_expand(opal_install_dirs.sysconfdir); - opal_install_dirs.sharedstatedir = + expanded_dirs.sharedstatedir = opal_install_dirs_expand(opal_install_dirs.sharedstatedir); - opal_install_dirs.localstatedir = + expanded_dirs.localstatedir = opal_install_dirs_expand(opal_install_dirs.localstatedir); - opal_install_dirs.libdir = + expanded_dirs.libdir = opal_install_dirs_expand(opal_install_dirs.libdir); - opal_install_dirs.includedir = + expanded_dirs.includedir = opal_install_dirs_expand(opal_install_dirs.includedir); - opal_install_dirs.infodir = + expanded_dirs.infodir = opal_install_dirs_expand(opal_install_dirs.infodir); - opal_install_dirs.mandir = + expanded_dirs.mandir = opal_install_dirs_expand(opal_install_dirs.mandir); - opal_install_dirs.pkgdatadir = + expanded_dirs.pkgdatadir = opal_install_dirs_expand(opal_install_dirs.pkgdatadir); - opal_install_dirs.pkglibdir = + expanded_dirs.pkglibdir = opal_install_dirs_expand(opal_install_dirs.pkglibdir); - opal_install_dirs.pkgincludedir = + expanded_dirs.pkgincludedir = opal_install_dirs_expand(opal_install_dirs.pkgincludedir); + opal_install_dirs = expanded_dirs; #if 0 fprintf(stderr, "prefix: %s\n", opal_install_dirs.prefix); @@ -179,7 +211,7 @@ opal_installdirs_base_close(void) free(opal_install_dirs.pkgincludedir); for (item = opal_list_remove_first(&opal_installdirs_components); - NULL != item; + NULL != item; item = opal_list_remove_first(&opal_installdirs_components)) { OBJ_RELEASE(item); } diff --git a/opal/mca/installdirs/base/installdirs_base_expand.c b/opal/mca/installdirs/base/installdirs_base_expand.c index 0d6abc9bcb..1d3271694d 100644 --- a/opal/mca/installdirs/base/installdirs_base_expand.c +++ b/opal/mca/installdirs/base/installdirs_base_expand.c @@ -1,12 +1,12 @@ /* * Copyright (c) 2006-2007 Los Alamos National Security, LLC. All rights - * reserved. + * reserved. * Copyright (c) 2007 Cisco Systems, Inc. All rights reserved. - * Copyright (c) 2007 Sun Microsystem, Inc. All rights reserved. + * Copyright (c) 2007-2009 Sun Microsystem, Inc. All rights reserved. * $COPYRIGHT$ - * + * * Additional copyrights may follow - * + * * $HEADER$ * */ @@ -19,77 +19,269 @@ #include "opal/mca/installdirs/base/base.h" #include "opal/mca/installdirs/installdirs.h" -#define EXPAND_STRING(field) \ - do { \ - if (NULL != (start_pos = strstr(retval, "${" #field "}"))) { \ - tmp = retval; \ - *start_pos = '\0'; \ - end_pos = start_pos + strlen("${" #field "}"); \ - asprintf(&retval, "%s%s%s", tmp, \ - opal_install_dirs.field + destdir_offset, \ - end_pos); \ - free(tmp); \ - changed = true; \ - } \ - } while (0) +/* + * Read a field from an opal_install_dirs_t structure. + * + * The field name is passed as a string + a length, and need not be + * null terminated. + * + * The implementation is reasonaly efficient, but perhaps less + * readable than a bunch of strncmp calls. + */ +#define CHKTRAIL(s,n) if (strncmp(field, #s, field_length - n) != 0) return NULL +static const char * +field_lookup(const char *field, + int field_length, + opal_install_dirs_t *install_dirs) +{ + switch (*field++) { + case 'b': + CHKTRAIL(indir, 1); + return install_dirs->bindir; + case 'd': + CHKTRAIL(atarootdir, 1); + return install_dirs->datarootdir; + case 'e': + CHKTRAIL(xec_prefix, 1); + return install_dirs->exec_prefix; + case 'i': + if ('n' != *field++) return NULL; + switch (*field++) { + case 'c': + CHKTRAIL(ludedir, 3); + return install_dirs->includedir; + case 'f': + CHKTRAIL(odir, 3); + return install_dirs->infodir; + } + return NULL; + case 'l': + switch (*field++) { + case 'o': + CHKTRAIL(calstatedir, 2); + return install_dirs->localstatedir; + case 'i': + switch (*field++) { + case 'b': + switch (*field++) { + case 'd': + CHKTRAIL(ir, 4); + return install_dirs->libdir; + case 'e': + CHKTRAIL(xecdir, 4); + return install_dirs->libexecdir; + } + } + } + return NULL; + + case 'm': + CHKTRAIL(andir, 1); + return install_dirs->mandir; + + case 'p': + switch (*field++) { + case 'r': + CHKTRAIL(efix, 2); + return install_dirs->prefix; + case 'k': + if ('g' == *field++) { + switch (*field++) { + case 'd': + CHKTRAIL(atadir, 4); + return install_dirs->pkgdatadir; + case 'l': + CHKTRAIL(ibdir, 4); + return install_dirs->pkglibdir; + case 'i': + CHKTRAIL(ncludedir, 4); + return install_dirs->pkgincludedir; + } + } + } + return NULL; + case 's': + switch (*field++) { + case 'b': + CHKTRAIL(indir, 2); + return install_dirs->sbindir; + case 'h': + CHKTRAIL(aredstatedir, 2); + return install_dirs->sharedstatedir; + case 'y': + CHKTRAIL(sconfdir, 2); + return install_dirs->sysconfdir; + } + } + return NULL; +} + +/* + * Sets *output to the input string with any "${foo}" references expanded. + * The expansion values come from the fields of install_dirs. + * + * If dont_expand is non-NULL, references to the field it names are + * not expanded. (E.g., "${libdir}" will be copied as is to *output if + * dont_expand is "libdir".) + */ + +static void +install_dirs_expand(const char *input, + char **output, + size_t *output_len, + size_t *j, + opal_install_dirs_t *install_dirs, + const char *dont_expand) +{ + size_t len, i, m, n; + const char *expansion; + + if (0 == *output_len) { + *output_len = 100; + *output = malloc(*output_len); + if (NULL == *output) { + return; + } + } + len = strlen(input); + for (i = 0 ; i < len ; ++i) { + expansion = NULL; + if ('$' == input[i] && '{' == input[i+1]) { + for (n = 0; i + n + 2 < len; n++) { + if ('}' == input[i+n+2]) { + break; + } + } + if (NULL == dont_expand || + strncmp(&input[i+2], dont_expand, n) != 0 ) { + expansion = field_lookup(&input[i+2], n, install_dirs); + } else { + expansion = NULL; + } + } + if (NULL != expansion) { + install_dirs_expand(expansion, output, output_len, j, install_dirs, + dont_expand); + i += n + 2; + } else { + if (*j + 1 >= *output_len) { + *output_len += 100; + *output = realloc(*output, *output_len); + if (NULL == *output) { + return; + } + } + (*output)[(*j)++] = input[i]; + } + } + + (*output)[*j] = '\0'; +} char * opal_install_dirs_expand(const char* input) { - size_t len, i; - bool needs_expand = false; char *retval = NULL; char *destdir = getenv("OPAL_DESTDIR"); - size_t destdir_offset = 0; + size_t j = 0, retval_len = 0; - if (NULL != destdir && strlen(destdir) > 0) { - destdir_offset = strlen(destdir); + if (NULL != destdir) { + j = strlen(destdir) + sizeof(OPAL_PATH_SEP) - 1; + retval_len = j + 100; + retval = malloc(retval_len); + if (NULL == retval) { + return NULL; + } + sprintf(retval, "%s%s", destdir, OPAL_PATH_SEP); } - len = strlen(input); - for (i = 0 ; i < len ; ++i) { - if (input[i] == '$') { - needs_expand = true; + install_dirs_expand(input, &retval, &retval_len, &j, &opal_install_dirs, NULL); + if (NULL != retval) { + retval = realloc(retval, j + 1); + } + return retval; +} + +char * +opal_install_dirs_infer(const char *inferred_field, + const char *infer_from_field, + size_t infer_from_field_len, + opal_install_dirs_t *component_installdirs) +{ + const char *infer_from_path; + char *component_field = NULL; + size_t component_field_len = 0; + const char *installed_field = NULL; + char *p, *q; + size_t j = 0, inferred_field_len = strlen(inferred_field); + size_t leading, trailing, installed_field_len, retval_len; + char *retval; + + infer_from_path = field_lookup(infer_from_field, infer_from_field_len, + component_installdirs); + install_dirs_expand(infer_from_path, + &component_field, + &component_field_len, + &j, + component_installdirs, + inferred_field); + + installed_field = field_lookup(infer_from_field, infer_from_field_len, + &opal_install_dirs); + + /* + * Let's say component_field is, "/path/${prefix}/bin", and + * infer_from_field is "bindir". Then we want to: + * + * 1. Make sure that opal_install_dirs.bindir starts with "/path/" + * + * 2. Make sure that opal_install_dirs.bindir ends with "/bin" + * + * 3. Return the string that appears between those + */ + + for (p = component_field; *p; p++) { + if (*p == '$' && p[1] == '{' && + strncmp(p+2, inferred_field, inferred_field_len) == 0) { break; } } + if (*p == '\0') { + free(component_field); + return NULL; + } + leading = p - component_field; + trailing = component_field + strlen(component_field) - p - + inferred_field_len - 3; - retval = strdup(input); - if (NULL == retval) return NULL; - - if (needs_expand) { - bool changed = false; - char *start_pos, *end_pos, *tmp; - - do { - changed = false; - EXPAND_STRING(prefix); - EXPAND_STRING(exec_prefix); - EXPAND_STRING(bindir); - EXPAND_STRING(sbindir); - EXPAND_STRING(libexecdir); - EXPAND_STRING(datarootdir); - EXPAND_STRING(datadir); - EXPAND_STRING(sysconfdir); - EXPAND_STRING(sharedstatedir); - EXPAND_STRING(localstatedir); - EXPAND_STRING(libdir); - EXPAND_STRING(includedir); - EXPAND_STRING(infodir); - EXPAND_STRING(mandir); - EXPAND_STRING(pkgdatadir); - EXPAND_STRING(pkglibdir); - EXPAND_STRING(pkgincludedir); - } while (changed); + installed_field_len = strlen(installed_field); + if (installed_field_len < trailing + leading) { + free(component_field); + return NULL; } - if (NULL != destdir) { - char *tmp = retval; - retval = opal_os_path(false, destdir, tmp, NULL); - free(tmp); + if (strncmp(component_field, installed_field, leading) != 0) { + free(component_field); + return NULL; } + if (strcmp(p + inferred_field_len + 3, + installed_field + installed_field_len - trailing) != 0) { + free(component_field); + return NULL; + } + + free(component_field); + retval_len = installed_field_len - leading - trailing; + retval = malloc(retval_len + 1); + if (NULL == retval) { + return NULL; + } + memcpy(retval, installed_field + leading, retval_len); + retval[retval_len] = '\0'; return retval; } + +