# -*-eselect-*- vim: ft=eselect # Copyright (c) 2005-2013 Gentoo Foundation # # This file is part of the 'eselect' tools framework. # # eselect is free software: you can redistribute it and/or modify it under the # terms of the GNU General Public License as published by the Free Software # Foundation, either version 2 of the License, or (at your option) any later # version. # # eselect is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR # A PARTICULAR PURPOSE. See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along with # eselect. If not, see . # This library is for managing a common binary symlink like /bin/sh # or /usr/bin/pinentry. To use it, you need to set the following # variables: # # SYMLINK_PATH is the name of symlink being created, e.g. "/bin/sh". # The path should be absolute, without EPREFIX (it will be added). # # SYMLINK_TARGETS is the list (bash array) of targets being available. # The paths should be relative to ${SYMLINK_PATH}. Only basenames # of the targets will be displayed to user. Best (preferred) targets # should go earlier on the list. # # SYMLINK_DESCRIPTION is the human-friendly name of the symlink, e.g. # "POSIX shell". It will be used in the context of "implementation" # or "symlink". Defaults to basename of ${SYMLINK_PATH}. # # SYMLINK_CRUCIAL should be set to a non-null value if existence of no # targets should trigger a fatal error rather than silent symlink # removal. # Version: 0.1.1 # Maintainer: mgorny@gentoo.org # Based on the work of: # - Erik Hahn; bug #214817 # - Samuli Suominen ; eselect-pinentry ## Global variables ## if [[ ! ${SYMLINK_PATH} ]]; then die "SYMLINK_PATH needs to be set by eselect module." fi if [[ ! ${SYMLINK_TARGETS[@]} ]]; then die "SYMLINK_TARGETS need to be set by eselect module." fi : ${SYMLINK_DESCRIPTION:-${SYMLINK_PATH##*/}} ## Functions ## # find a list of symlink targets, best first find_targets() { local basedir=${SYMLINK_PATH%/*} TARGETS=() local t for t in "${SYMLINK_TARGETS[@]}"; do if [[ -x ${EROOT}${basedir}/${t} ]]; then TARGETS+=( ${t} ) fi done } # set the pinentry symlink set_symlinks() { local target="${1}" targets local basedir=${SYMLINK_PATH%/*} [[ ! -L ${EROOT}${SYMLINK_PATH} && -e ${EROOT}${SYMLINK_PATH} ]] && \ die -q "${EROOT}${SYMLINK_PATH} is not a symlink!" if is_number "${target}" && [[ ${target} -ge 1 ]]; then local TARGETS find_targets target=${TARGETS[target-1]} elif [[ ! -x ${EROOT}${basedir}/${target} ]]; then # try basename matching local TARGETS t find_targets for t in "${TARGETS[@]}"; do if [[ ${t##*/} == ${target} ]]; then target=${t} break fi done fi if [[ -x ${EROOT}${basedir}/${target} ]]; then local tmpf=${EROOT}${SYMLINK_PATH}.new # we could use 'ln -f' to directly replace the symlink # but 'mv' is an atomic operation so it should be more fault-proof ln -s "${target}" "${tmpf}" || \ die -q "Unable to create temporary symlink" if ! mv "${tmpf}" "${EROOT}${SYMLINK_PATH}"; then rm -f "${tmpf}" # cleanup die -q "Unable to replace ${EROOT}${SYMLINK_PATH} symlink with ${target}" fi else die -q "Target '${target}' doesn't appear to be valid!" fi } ### show action ### describe_show() { echo "Show the current ${SYMLINK_DESCRIPTION} implementation" } do_show() { [[ ${@} ]] && die -q "Too many parameters" write_list_start "Current ${SYMLINK_DESCRIPTION} implementation:" if [[ -L ${EROOT}${SYMLINK_PATH} ]]; then local t=$(readlink "${EROOT}${SYMLINK_PATH}") write_kv_list_entry "${t##*/}" "" elif [[ -e ${EROOT}${SYMLINK_PATH} ]]; then write_kv_list_entry "(not a symlink)" "" else write_kv_list_entry "(unset)" "" fi } ### list action ### describe_list() { echo "List available ${SYMLINK_DESCRIPTION} implementations" } do_list() { [[ ${@} ]] && die -q "Too many parameters" local i TARGETS find_targets write_list_start "Available ${SYMLINK_DESCRIPTION} implementations:" if [[ ${TARGETS[@]} ]]; then local curr=$(readlink "${EROOT}${SYMLINK_PATH}") for (( i = 0; i < ${#TARGETS[@]}; i++ )) ; do [[ ${TARGETS[${i}]##*/} == ${curr##*/} ]] && \ TARGETS[${i}]+=" $(highlight '*')" done write_numbered_list "${TARGETS[@]##*/}" else write_kv_list_entry "(none found)" "" fi } ### set action ### describe_set() { echo "Set a new ${SYMLINK_DESCRIPTION} implementation" } describe_set_options() { echo "target : Target name, number (from 'list' action) or path" } describe_set_parameters() { echo "" } do_set() { if [[ ${#} != 1 ]]; then die -q "set takes exactly one parameter" else set_symlinks "${1}" fi } ### update action ### describe_update() { echo "Automatically update the ${SYMLINK_DESCRIPTION} implementation" } describe_update_options() { echo "--if-unset : do not override existing implementation" } do_update() { [[ ${1} == ifunset ]] && set -- --if-unset "${@:2}" if [[ ( ${1} && ${1} != '--if-unset' ) || ${2} ]] then die -q "usage error" fi if [[ ${1} == '--if-unset' && -L ${EROOT}${SYMLINK_PATH} \ && -x ${EROOT}${SYMLINK_PATH} ]] then return fi local TARGETS find_targets if [[ ! ${TARGETS[@]} ]]; then if [[ ${SYMLINK_CRUCIAL} ]]; then die "No targets for ${EROOT}${SYMLINK_PATH}, system likely broken." else rm -f "${EROOT}${SYMLINK_PATH}" fi else set_symlinks "${TARGETS[0]}" fi } # vim:ts=4:sts=4:sw=4