# Copyright 1999-2013 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 # $Id: $ inherit config multilib DESCRIPTION="Manage active PostgreSQL client applications and libraries" MAINTAINER="pgsql-bugs@gentoo.org" VERSION="1.3" # Global Data B_PATH="${EROOT%/}/usr" E_PATH="${EROOT%/}/etc/eselect/postgresql" ENV_FILE="${EROOT%/}/etc/env.d/50postgresql" # This list of files/directories are the symbolic link targets that need to be # created when a slot is set. # # If you change this list, remember to change include_sources in do_set. And, # they must be listed in the same order. INCLUDE_TARGETS=( "${B_PATH}"/include/postgresql "${B_PATH}"/include/libpq-fe.h "${B_PATH}"/include/pg_config_ext.h "${B_PATH}"/include/pg_config_manual.h "${B_PATH}"/include/libpq "${B_PATH}"/include/postgres_ext.h ) active_slot() { # ${B_PATH}/share/postgresql is a symlink. See if it's there, then # find out where it links to if [[ -h "${B_PATH}/share/postgresql" ]] ; then canonicalise "${B_PATH}/share/postgresql" | \ sed 's|.*postgresql-\([1-9][0-9.]*\)|\1|' else echo "(none)" fi } lib_dir() { local lib_list=$(list_libdirs) if [[ ${lib_list} =~ .*lib64.* && \ -n $(ls -d ${B_PATH}/lib64/postgresql-*/lib64 2> /dev/null) ]] ; then echo "lib64" elif [[ ${lib_list} =~ .*lib32.* && \ -n $(ls -d ${B_PATH}/lib32/postgresql-*/lib32 2> /dev/null) ]] ; then echo "lib32" elif [[ ${lib_list} =~ .*libx32.* && \ -n $(ls -d ${B_PATH}/libx32/postgresql-*/libx32 2> /dev/null) ]] ; then echo "libx32" else echo "lib" fi } ### TODO: Refactor to move find into a separate function ### Linker Function ### # Takes four arguments: # - Full source path (e.g. /usr/lib/postgresql-9.0/lib) # - Pattern to search for # - Full target directory path (e.g. /usr/bin) # - Suffix (Optional) (e.g 84 to make /usr/bin/psql84) linker() { local source_dir=$1 local pattern=$2 local target_dir=$3 local suffix=$4 local link_source local findings local rel_source # Prevent passed patterns from being globbed # If this module is run in /usr, '-name lib*' ends up globbing 'lib*', # passing to 'find' the pattern '-name lib lib32 lib64' and find interprets # those as path arguments causing failure. set -f findings=$(find -L "${source_dir}" -maxdepth 1 -mindepth 1 ${pattern}) set +f for link_source in ${findings} ; do local link_target="${target_dir%/}/$(basename ${link_source})${suffix}" # For good measure, remove target before creating the symlink [[ -h ${link_target} ]] && rm -f "${link_target}" [[ -e ${link_target} ]] && \ die -q "The target '${link_target}' still exists and could not be removed!" # Create relative links so that they work both here and inside the new # root if $ROOT is not "/". rel_source=$(relative_name "${link_source}" "${target_dir}") ln -s "${rel_source}" "${link_target}" || die -q "Unable to create link!" done } ### TODO: Refactor so that it searches source directories for removal targets, ### and move the old stuff into do_update ### Unlinker Function ### # Takes one argument: # - Full path to active links file (e.g. /etc/eselect/postgresql/active.links) unlinker() { local active_link_file=$1 if [[ -r ${active_link_file} ]] ; then local active_links=($(<"${active_link_file}")) for (( i=0; $i < ${#active_links[@]}; i++ )) ; do [[ -h "${ROOT%/}/${active_links[$i]}" ]] && \ rm -f "${ROOT%/}/${active_links[$i]}" [[ -e "${ROOT%/}/${active_links[$i]}" ]] && \ die -q "The target '${active_links[$i]}' still exists and could not be removed!" done rm -f "${active_link_file}" fi } ### Get Slots Function ### # Find all available slots in the preferred lib_dir() and return them. get_slots() { local slot local found_slots for slot in $(find "${B_PATH}/$(lib_dir)/" -maxdepth 1 -type d \ -regex '.*postgresql-[0-9][0-9]*\.[0-9][0-9]*' | \ sed -re 's#.*([0-9]+\.[0-9]+)$#\1#' | sort -n) do # Check that pg_config exists for this slot, otherwise we have # a false positive. [[ -x "${B_PATH}/$(lib_dir)/postgresql-${slot}/bin/pg_config" ]] && \ found_slots+=( ${slot} ) done echo ${found_slots[@]} } ### List Action ### describe_list() { echo "List available PostgreSQL slots." } do_list() { if $(is_output_mode brief) ; then echo $(get_slots) else write_list_start "Available PostgreSQL Slots" local provider local slot local bindir for slot in $(get_slots) ; do bindir="${B_PATH}/$(lib_dir)/postgresql-${slot}/bin" # The output of `pg_config --version` also includes "PostgreSQL" in # the string, which is a bit redundant. provider=$("${bindir}"/pg_config --version | \ sed 's/[^0-9]*\(.*\)/\1/') # Unless a file exists that's controlled by the 'server' use flag, # report that it's client only. [[ -e "${bindir}/postmaster" ]] || provider+=' (Clients Only)' case "${slot}" in "$(active_slot)" ) write_kv_list_entry \ "$(highlight_marker ${slot})" "${provider}";; * ) write_kv_list_entry "${slot}" "${provider}";; esac done [[ -z "$(get_slots)" ]] && write_warning_msg "No slots available." fi } ### Show Action ### describe_show() { echo "Show which slot is currently active." } do_show() { echo $(active_slot) } ### Show Service Action ### # Here for backwards compatibility with ebuilds describe_show-service() { echo "Deprecated. For ebuild use; returns no useful information." } do_show-service() { echo 1 } ### Set Action ### describe_set() { echo "Create symbolic links for PostgreSQL libraries and applications." } do_set() { local SLOT=$1 if [[ ! -d ${B_PATH}/$(lib_dir)/postgresql-${SLOT} ]] ; then die -q "Not a valid slot." fi # If there's an active slot, unset that one first if [[ "$(active_slot)" == "${SLOT}" ]] ; then echo $(highlight "No work to do.") echo "If you think the links need to be reset, use the reset action." return 0 elif [[ "$(active_slot)" != "(none)" ]] ; then echo -ne "\tRemoving old links..." do_unset $(active_slot) echo "done." fi echo "Setting ${SLOT} as the default installation..." echo -ne "\tGenerating new links..." # Sources for header files # Targets are listed in the global variable INCLUDE_TARGETS. # # If you change this list, you must change the INCLUDE_TARGETS list, # too. And, they must be listed in the same order. local include_sources=( "${B_PATH}"/include/postgresql-${SLOT} "${B_PATH}"/include/postgresql-${SLOT}/libpq-fe.h "${B_PATH}"/include/postgresql-${SLOT}/pg_config_ext.h "${B_PATH}"/include/postgresql-${SLOT}/pg_config_manual.h "${B_PATH}"/include/postgresql-${SLOT}/libpq "${B_PATH}"/include/postgresql-${SLOT}/postgres_ext.h ) # The linker function cannot accomadate this special purpose. local rel_source local i for (( i=0; $i < ${#include_sources[@]}; i++ )) ; do # Some headers are present only in specific versions of PostgreSQL [[ -e ${include_sources[$i]} ]] || continue # Create relative links so that they work both here and inside the new # root if $ROOT is not "/" rel_source=$(relative_name "${include_sources[$i]}" "$(dirname "${INCLUDE_TARGETS[$i]}")") ln -s "$rel_source" "${INCLUDE_TARGETS[$i]}" || \ die -q "Unable to create link!" done # Link modules to /usr/lib{,32,64}/ local x for x in $(list_libdirs) ; do if [[ -d ${B_PATH}/${x}/postgresql-${SLOT}/${x} ]] ; then # 'linker' function doesn't work for linking directories. # Default lib path - create a relative link ln -s "postgresql-${SLOT}/${x}" "${B_PATH}/${x}/postgresql" # Linker works for files linker "${B_PATH}/${x}/postgresql-${SLOT}/${x}/" \ "-name lib*" "${B_PATH}/${x}" fi done # Link binaries to /usr/bin/ linker "${B_PATH}/$(lib_dir)/postgresql-${SLOT}/bin/" \ "-type f" "${B_PATH}/bin" # Default share path - use a relative link here by just specifying the # base name ln -s "postgresql-${SLOT}" "${B_PATH}/share/postgresql" echo "done." echo "Setting ${SLOT} as default was successful!" } ### Unset Action ### describe_unset() { echo "Remove symbolic links." } # Undo everything done by do_set(). do_unset() { local SLOT=$1 if [[ ${SLOT} = $(active_slot) ]] ; then echo -n "Unsetting ${SLOT} as the default installation..." echo "done." echo "Setting a new slot as the default." do_update else echo "Inactive slot selected. No work to do." fi local l for l in ${INCLUDE_TARGETS[@]} "${B_PATH}/share/postgresql" ; do # Remove target before creating the symlink [[ -h ${l} ]] && rm -f "${l}" # Check if include target still exists [[ -e ${l} ]] && \ die -q "The target '${l}' exists and could not be removed!" done for x in $(list_libdirs) ; do if [[ -d ${B_PATH}/${x}/postgresql-${SLOT}/${x} ]] ; then if [[ -h "${B_PATH}/${x}/postgresql" ]] ; then rm -f "${B_PATH}/${x}/postgresql" [[ -e "${B_PATH}/${x}/postgresql" ]] && \ die -q "Unable to remove '${B_PATH}/${x}/postgresql'" fi unlinker "${B_PATH}/${x}/postgresql-${SLOT}/${x}/" \ "-name lib*" "${B_PATH}/${x}" fi done } ### Reset Action ### describe_reset() { echo "Recreate symbolic links for currently active slot." } do_reset() { local SLOT=$(active_slot) [[ ${SLOT} = "(none)" ]] && die -q "No active slot to reset." do_unset ${SLOT} do_set ${SLOT} } ### Update Action ### describe_update() { echo "Refreshes all symbolic links managed by this module" } do_update() { local slot=$(active_slot) # Remove some files outright as they're entirely useless now. # ${E_PATH}/active: Contents was the active slot (e.g., 9.5) # ${E_PATH}/service: Told the initscript which slot to start local f for f in "${E_PATH}/active" "${E_PATH}/service" ; do [[ -e "${f}" ]] && rm -f "${f}" done local slots=($(get_slots)) local index=${#slots[@]} # In case all slots have been unmerged if [[ ${index} -eq 0 ]] ; then write_warning_msg "No slots found!" write_warning_msg "Removing links (Control-C to abort) in..." local i=6 while [[ $[i--] -gt 0 ]] ; do echo -n " $i" sleep 1 done local sym_links for sym_links in "${E_PATH}"/active.links* ; do unlinker "${sym_links}" done rm -f "${ENV_FILE}" do_action env update &> /dev/null echo "Done!" return 0 fi # Reset, otherwise set the highest slot available. if [[ ${slots[@]} =~ ${slot} ]] ; then do_set ${slot} else # best_version doesn't work here as pkg_postrm runs before the world # file is updated, thereby returning a false positive. do_set ${slots[$index-1]} fi echo -en "\nCleaning out old links before refreshing..." local sym_links for sym_links in "${E_PATH}"/active.links?* ; do unlinker "${sym_links}" done echo "done." # Update paths to libs and docs local ldpath local x for x in $(list_libdirs) ; do [[ -h ${B_PATH}/${x}/postgresql ]] && \ ldpath+="${B_PATH}/${x}/postgresql:" done ldpath="${ldpath%:}" local manpath="${B_PATH}/share/postgresql/man/" while [[ $[--index] -gt -1 ]] ; do local curslot="${slots[$index]}" echo -n "Refreshing symbolic links for ${curslot} applications (like " echo -n "/usr/bin/psql${curslot//.})..." for x in $(list_libdirs) ; do local lib_path="${B_PATH}/${x}/postgresql-${curslot}/${x}/" [[ -d ${lib_path} ]] && ldpath+=":${lib_path}" done local tmp_manpath="${B_PATH}/share/postgresql-${curslot}/man/" [[ -d ${tmp_manpath} ]] && manpath+=":${tmp_manpath}" linker "${B_PATH}/$(lib_dir)/postgresql-${curslot}/bin/" \ "-type f" "${B_PATH}/bin" "${curslot//.}" echo "done." done # Remove environment files that have been generated by the ebuilds rm -f "${ENV_FILE}"-* store_config "${ENV_FILE}" LDPATH "${ldpath}" store_config "${ENV_FILE}" MANPATH "${manpath}" do_action env update &> /dev/null }