From ee063f40a30fd0564ab0aaed0767a40cc7f2e8ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Thu, 10 Jan 2013 11:52:53 +0100 Subject: [PATCH 1/2] Add a module to manage symlinks like /bin/sh, /usr/bin/pinentry... --- libs/Makefile.am | 2 + libs/bin-symlink.bash.in | 218 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 220 insertions(+) create mode 100644 libs/bin-symlink.bash.in diff --git a/libs/Makefile.am b/libs/Makefile.am index bab373e..eefbeff 100644 --- a/libs/Makefile.am +++ b/libs/Makefile.am @@ -1,6 +1,7 @@ eselectlibsdir = $(datadir)/$(PACKAGE_NAME)/libs/ eselectlibs_DATA = \ + bin-symlink.bash \ config.bash \ core.bash \ default.eselect \ @@ -14,6 +15,7 @@ eselectlibs_DATA = \ tests.bash EXTRA_DIST = \ + bin-symlink.bash.in \ config.bash.in \ core.bash.in \ default.eselect.in \ diff --git a/libs/bin-symlink.bash.in b/libs/bin-symlink.bash.in new file mode 100644 index 0000000..9332ba8 --- /dev/null +++ b/libs/bin-symlink.bash.in @@ -0,0 +1,218 @@ +# -*-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. + +# 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 + fi + + set_symlinks "${TARGETS[0]}" +} + +# vim:ts=4:sts=4:sw=4 -- 1.8.1