#!/bin/bash # Copyright 1999-2014 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 # # Miscellaneous shell functions that make use of the ebuild env but don't need # to be included directly in ebuild.sh. # # We're sourcing ebuild.sh here so that we inherit all of it's goodness, # including bashrc trickery. This approach allows us to do our miscellaneous # shell work within the same env that ebuild.sh has, but without polluting # ebuild.sh itself with unneeded logic and shell code. # # XXX hack: clear the args so ebuild.sh doesn't see them setvar MISC_FUNCTIONS_ARGS = "@ARGV" shift $Argc source "${PORTAGE_BIN_PATH}/ebuild.sh" || exit 1 proc install_symlink_html_docs { if ! ___eapi_has_prefix_variables { local ED=${D} } cd ${ED} || die "cd failed" #symlink the html documentation (if DOC_SYMLINKS_DIR is set in make.conf) if test -n ${DOC_SYMLINKS_DIR} { local mydocdir docdir for docdir in "${HTMLDOC_DIR:-does/not/exist}" "${PF}/html" "${PF}/HTML" "${P}/html" "${P}/HTML" { if test -d "usr/share/doc/${docdir}" { setvar mydocdir = ""/usr/share/doc/${docdir}"" } } if test -n ${mydocdir} { local mysympath if test -z ${SLOT} -o ${SLOT%/*} = "0" { setvar mysympath = ""${DOC_SYMLINKS_DIR}/${CATEGORY}/${PN}"" } else { setvar mysympath = ""${DOC_SYMLINKS_DIR}/${CATEGORY}/${PN}-${SLOT%/*}"" } einfo "Symlinking ${mysympath} to the HTML documentation" dodir "${DOC_SYMLINKS_DIR}/${CATEGORY}" dosym ${mydocdir} ${mysympath} } } } # replacement for "readlink -f" or "realpath" setvar READLINK_F_WORKS = """" proc canonicalize { if [[ -z ${READLINK_F_WORKS} ]] { if [[ $(readlink -f -- /../ 2>/dev/null) == "/" ]] { setvar READLINK_F_WORKS = 'true' } else { setvar READLINK_F_WORKS = 'false' } } if ${READLINK_F_WORKS} { readlink -f -- @ARGV return } local f=$1 b n=10 wd=$(pwd) { { setvar f = ${f%/} } setvar b = ${f##*/} cd ${f%"${b}"} 2>/dev/null || break if [[ ! -L ${b} ]] { setvar f = $(pwd -P) echo "${f%/}/${b}" cd ${wd} return 0 } setvar f = $(readlink "${b}") } cd ${wd} return 1 } proc prepcompress { local -a include exclude incl_d incl_f local f g i real_f real_d if ! ___eapi_has_prefix_variables { local ED=${D} } # Canonicalize path names and check for their existence. setvar real_d = $(canonicalize "${ED}") for (( i = 0; i < ${#PORTAGE_DOCOMPRESS[@]}; i++ )); do real_f=$(canonicalize "${ED}${PORTAGE_DOCOMPRESS[i]}") f=${real_f#"${real_d}"} if [[ ${real_f} != "${f}" ]] && [[ -d ${real_f} || -f ${real_f} ]] then include[${#include[@]}]=${f:-/} elif [[ ${i} -ge 3 ]]; then ewarn "prepcompress:" \ "ignoring nonexistent path '${PORTAGE_DOCOMPRESS[i]}'" fi done for (( i = 0; i < ${#PORTAGE_DOCOMPRESS_SKIP[@]}; i++ )); do real_f=$(canonicalize "${ED}${PORTAGE_DOCOMPRESS_SKIP[i]}") f=${real_f#"${real_d}"} if [[ ${real_f} != "${f}" ]] && [[ -d ${real_f} || -f ${real_f} ]] then exclude[${#exclude[@]}]=${f:-/} elif [[ ${i} -ge 1 ]]; then ewarn "prepcompress:" \ "ignoring nonexistent path '${PORTAGE_DOCOMPRESS_SKIP[i]}'" fi done # Remove redundant entries from lists. # For the include list, remove any entries that are: # a) contained in a directory in the include or exclude lists, or # b) identical with an entry in the exclude list. for (( i = ${#include[@]} - 1; i >= 0; i-- )); do f=${include[i]} for g in "${include[@]}"; do if [[ ${f} == "${g%/}"/* ]]; then unset include[i] continue 2 fi done for g in "${exclude[@]}"; do if [[ ${f} = "${g}" || ${f} == "${g%/}"/* ]]; then unset include[i] continue 2 fi done done # For the exclude list, remove any entries that are: # a) contained in a directory in the exclude list, or # b) _not_ contained in a directory in the include list. for (( i = ${#exclude[@]} - 1; i >= 0; i-- )); do f=${exclude[i]} for g in "${exclude[@]}"; do if [[ ${f} == "${g%/}"/* ]]; then unset exclude[i] continue 2 fi done for g in "${include[@]}"; do [[ ${f} == "${g%/}"/* ]] && continue 2 done unset exclude[i] done # Split the include list into directories and files for f in "${include[@]}" { if [[ -d ${ED}${f} ]] { setvar incl_d[${#incl_d[@]}]=${f} } else { setvar incl_f[${#incl_f[@]}]=${f} } } # Queue up for compression. # ecompress{,dir} doesn't like to be called with empty argument lists. [[ ${#incl_d[@]} -gt 0 ]] && ecompressdir --limit ${PORTAGE_DOCOMPRESS_SIZE_LIMIT:-0} --queue ${incl_d[@]} [[ ${#incl_f[@]} -gt 0 ]] && ecompress --queue ${incl_f[@]/#/${ED}} [[ ${#exclude[@]} -gt 0 ]] && ecompressdir --ignore ${exclude[@]} return 0 } proc install_qa_check { local d f i qa_var x paths qa_checks=() checks_run=() if ! ___eapi_has_prefix_variables { local EPREFIX= ED=${D} } cd ${ED} || die "cd failed" # Collect the paths for QA checks, highest prio first. setvar paths = ''( # sysadmin overrides "${PORTAGE_OVERRIDE_EPREFIX}"/usr/local/lib/install-qa-check.d # system-wide package installs "${PORTAGE_OVERRIDE_EPREFIX}"/usr/lib/install-qa-check.d ) # Now repo-specific checks. # (yes, PORTAGE_ECLASS_LOCATIONS contains repo paths...) for d in "${PORTAGE_ECLASS_LOCATIONS[@]}" { setvar paths = ''( "${d}"/metadata/install-qa-check.d ) } setvar paths = ''( # Portage built-in checks "${PORTAGE_OVERRIDE_EPREFIX}"/usr/lib/portage/install-qa-check.d "${PORTAGE_BIN_PATH}"/install-qa-check.d ) # Collect file names of QA checks. We need them early to support # overrides properly. for d in "${paths[@]}" { for f in "${d}"/* { [[ -f ${f} ]] && setvar qa_checks = ''( "${f##*/}" ) } } # Now we need to sort the filenames lexically, and process # them in order. while read -r -d '' f { # Find highest priority file matching the basename. for d in "${paths[@]}" { [[ -f ${d}/${f} ]] && break } # Run in a subshell to treat it like external script, # but use 'source' to pass all variables through. shell { # Allow inheriting eclasses. # XXX: we want this only in repository-wide checks. setvar _IN_INSTALL_QA_CHECK = '1' source "${d}/${f}" || eerror "Post-install QA check ${f} failed to run" } } < <(printf "%s\0" "${qa_checks[@]}" | LC_ALL=C sort -u -z) export STRIP_MASK prepall ___eapi_has_docompress && prepcompress ecompressdir --dequeue ecompress --dequeue # Create NEEDED.ELF.2 regardless of RESTRICT=binchecks, since this info is # too useful not to have (it's required for things like preserve-libs), and # it's tempting for ebuild authors to set RESTRICT=binchecks for packages # containing pre-built binaries. if type -P scanelf > /dev/null { # Save NEEDED information after removing self-contained providers rm -f "$PORTAGE_BUILDDIR"/build-info/NEEDED{,.ELF.2} scanelf -qyRF '%a;%p;%S;%r;%n' ${D} | do {'' while IFS= read -r l { setvar arch = ${l%%;*}; setvar l = ${l#*;} setvar obj = ""/${l%%;*}""; setvar l = ${l#*;} setvar soname = ${l%%;*}; setvar l = ${l#*;} setvar rpath = ${l%%;*}; setvar l = ${l#*;}; test ${rpath} = " - " && setvar rpath = """" setvar needed = ${l%%;*}; setvar l = ${l#*;} echo "${obj} ${needed}" >> "${PORTAGE_BUILDDIR}"/build-info/NEEDED echo "${arch:3};${obj};${soname};${rpath};${needed}" >> "${PORTAGE_BUILDDIR}"/build-info/NEEDED.ELF.2 } } test -n ${QA_SONAME_NO_SYMLINK} && \ echo ${QA_SONAME_NO_SYMLINK} > \ "${PORTAGE_BUILDDIR}"/build-info/QA_SONAME_NO_SYMLINK if has binchecks ${RESTRICT} && \ test -s "${PORTAGE_BUILDDIR}/build-info/NEEDED.ELF.2" { eqawarn "QA Notice: RESTRICT=binchecks prevented checks on these ELF files:" eqawarn $(while read -r x; do x=${x#*;} ; x=${x%%;*} ; echo "${x#${EPREFIX}}" ; done < "${PORTAGE_BUILDDIR}"/build-info/NEEDED.ELF.2) } } # Portage regenerates this on the installed system. rm -f "${ED}"/usr/share/info/dir{,.gz,.bz2} || die "rm failed!" } proc postinst_qa_check { local d f paths qa_checks=() if ! ___eapi_has_prefix_variables { local EPREFIX= EROOT=${ROOT} } cd ${EROOT} || die "cd failed" # Collect the paths for QA checks, highest prio first. setvar paths = ''( # sysadmin overrides "${PORTAGE_OVERRIDE_EPREFIX}"/usr/local/lib/postinst-qa-check.d # system-wide package installs "${PORTAGE_OVERRIDE_EPREFIX}"/usr/lib/postinst-qa-check.d ) # Now repo-specific checks. # (yes, PORTAGE_ECLASS_LOCATIONS contains repo paths...) for d in "${PORTAGE_ECLASS_LOCATIONS[@]}" { setvar paths = ''( "${d}"/metadata/postinst-qa-check.d ) } setvar paths = ''( # Portage built-in checks "${PORTAGE_OVERRIDE_EPREFIX}"/usr/lib/portage/postinst-qa-check.d "${PORTAGE_BIN_PATH}"/postinst-qa-check.d ) # Collect file names of QA checks. We need them early to support # overrides properly. for d in "${paths[@]}" { for f in "${d}"/* { [[ -f ${f} ]] && setvar qa_checks = ''( "${f##*/}" ) } } # Now we need to sort the filenames lexically, and process # them in order. while read -r -d '' f { # Find highest priority file matching the basename. for d in "${paths[@]}" { [[ -f ${d}/${f} ]] && break } # Run in a subshell to treat it like external script, # but use 'source' to pass all variables through. shell { # Allow inheriting eclasses. # XXX: we want this only in repository-wide checks. setvar _IN_INSTALL_QA_CHECK = '1' source "${d}/${f}" || eerror "Post-postinst QA check ${f} failed to run" } } < <(printf "%s\0" "${qa_checks[@]}" | LC_ALL=C sort -u -z) } proc install_mask { local root="$1" shift local install_mask="$[join(ARGV)]" # We think of $install_mask as a space-separated list of # globs. We don't want globbing in the "for" loop; that is, we # want to keep the asterisks in the indivual entries. local shopts=$- set -o noglob local no_inst for no_inst in ${install_mask} { # Here, $no_inst is a single "entry" potentially # containing a glob. From now on, we *do* want to # expand it. set +o noglob # The standard case where $no_inst is something that # the shell could expand on its own. if [[ -e "${root}"/${no_inst} || -L "${root}"/${no_inst} || "${root}"/${no_inst} != $(echo "${root}"/${no_inst}) ]] { __quiet_mode || einfo "Removing ${no_inst}" rm -Rf "${root}"/${no_inst} >&/dev/null } # We also want to allow the user to specify a "bare # glob." For example, $no_inst="*.a" should prevent # ALL files ending in ".a" from being installed, # regardless of their location/depth. We achieve this # by passing the pattern to `find`. find ${root} '(' -path ${no_inst} -or -name ${no_inst} ')' \ -print0 '2> /dev/null \ | LC_ALL=C' sort -z \ | while read -r -d { __quiet_mode || einfo "Removing /${REPLY#${root}}" rm -Rf ${REPLY} >&/dev/null } } # set everything back the way we found it set +o noglob set -${shopts} } proc preinst_mask { if test -z ${D} { eerror "${FUNCNAME}: D is unset" return 1 } if ! ___eapi_has_prefix_variables { local ED=${D} } # Make sure $PWD is not ${D} so that we don't leave gmon.out files # in there in case any tools were built with -pg in CFLAGS. cd ${T} # remove man pages, info pages, docs if requested local f for f in man info doc { if has no${f} $FEATURES { setvar INSTALL_MASK = ""${INSTALL_MASK} /usr/share/${f}"" } } install_mask ${ED} ${INSTALL_MASK} # remove share dir if unnessesary if has nodoc $FEATURES || has noman $FEATURES || has noinfo $FEATURES { rmdir "${ED}usr/share" &> /dev/null } } proc preinst_sfperms { if test -z ${D} { eerror "${FUNCNAME}: D is unset" return 1 } if ! ___eapi_has_prefix_variables { local ED=${D} } # Smart FileSystem Permissions if has sfperms $FEATURES { local i find ${ED} -type f -perm -4000 -print0 | \ while read -r -d $'\0' i { if test -n $(find "$i" -perm -2000) { ebegin ">>> SetUID and SetGID: [chmod o-r] /${i#${ED}}" chmod o-r $i eend $? } else { ebegin ">>> SetUID: [chmod go-r] /${i#${ED}}" chmod go-r $i eend $? } } find ${ED} -type f -perm -2000 -print0 | \ while read -r -d $'\0' i { if test -n $(find "$i" -perm -4000) { # This case is already handled # by the SetUID check above. true } else { ebegin ">>> SetGID: [chmod o-r] /${i#${ED}}" chmod o-r $i eend $? } } } } proc preinst_suid_scan { if test -z ${D} { eerror "${FUNCNAME}: D is unset" return 1 } if ! ___eapi_has_prefix_variables { local ED=${D} } # total suid control. if has suidctl $FEATURES { local i sfconf x setvar sfconf = "${PORTAGE_CONFIGROOT}etc/portage/suidctl.conf" # sandbox prevents us from writing directly # to files outside of the sandbox, but this # can easly be bypassed using the addwrite() function addwrite ${sfconf} __vecho ">>> Performing suid scan in ${ED}" for i in $(find "${ED}" -type f \( -perm -4000 -o -perm -2000 \) ) { if test -s ${sfconf} { setvar install_path = "/${i#${ED}}" if grep -q "^${install_path}\$" ${sfconf} { __vecho "- ${install_path} is an approved suid file" } else { __vecho ">>> Removing sbit on non registered ${install_path}'" LC_ALL=C' sleep 1.5 setvar ls_ret = $(ls -ldh "${i}") chmod ugo-s ${i} grep "^#${install_path}$" ${sfconf} > /dev/null || do { __vecho ">>> Appending commented out entry to ${sfconf} for ${PF}" echo "## ${ls_ret%${ED}*}${install_path}" >> "${sfconf}" echo "#${install_path}" >> "${sfconf}" # no delwrite() eh? # delwrite ${sconf} } } } else { __vecho "suidctl feature set but you are lacking a ${sfconf}" } } } } proc preinst_selinux_labels { if test -z ${D} { eerror "${FUNCNAME}: D is unset" return 1 } if has selinux ${FEATURES} { # SELinux file labeling (needs to execute after preinst) # only attempt to label if setfiles is executable # and 'context' is available on selinuxfs. if test -f /selinux/context -o -f /sys/fs/selinux/context && \ test -x /usr/sbin/setfiles -a -x /usr/sbin/selinuxconfig { __vecho ">>> Setting SELinux security labels" shell { eval $(/usr/sbin/selinuxconfig) || \ die "Failed to determine SELinux policy paths."; addwrite /selinux/context addwrite /sys/fs/selinux/context /usr/sbin/setfiles -F ${file_contexts_path} -r ${D} ${D} } || die "Failed to set SELinux security labels." } else { # nonfatal, since merging can happen outside a SE kernel # like during a recovery situation __vecho "!!! Unable to set SELinux security labels" } } } proc __dyn_package { local PROOT if ! ___eapi_has_prefix_variables { local EPREFIX= ED=${D} } # Make sure $PWD is not ${D} so that we don't leave gmon.out files # in there in case any tools were built with -pg in CFLAGS. cd ${T} || die if [[ -n ${PKG_INSTALL_MASK} ]] { setvar PROOT = "${T}/packaging/" # make a temporary copy of ${D} so that any modifications we do that # are binpkg specific, do not influence the actual installed image. rm -rf ${PROOT} || die "failed removing stale package tree" cp -pPR $(cp --help | grep -qs -e-l && echo -l) \ ${D} ${PROOT} \ || die "failed creating packaging tree" install_mask "${PROOT%/}${EPREFIX}/" ${PKG_INSTALL_MASK} } else { setvar PROOT = ${D} } local tar_options="" [[ $PORTAGE_VERBOSE = 1 ]] && setvar tar_options = "" -v"" has xattr ${FEATURES} && [[ $(tar --help 2> /dev/null) == *--xattrs* ]] && setvar tar_options = "" --xattrs"" # Sandbox is disabled in case the user wants to use a symlink # for $PKGDIR and/or $PKGDIR/All. export SANDBOX_ON="0" test -z ${PORTAGE_BINPKG_TMPFILE} && \ die "PORTAGE_BINPKG_TMPFILE is unset" mkdir -p ${PORTAGE_BINPKG_TMPFILE%/*} || die "mkdir failed" test -z ${PORTAGE_COMPRESSION_COMMAND} && \ die "PORTAGE_COMPRESSION_COMMAND is unset" tar $tar_options -cf - $PORTAGE_BINPKG_TAR_OPTS -C ${PROOT} . | \ $PORTAGE_COMPRESSION_COMMAND -c > "$PORTAGE_BINPKG_TMPFILE" assert "failed to pack binary package: '$PORTAGE_BINPKG_TMPFILE'" PYTHONPATH=${PORTAGE_PYTHONPATH:-${PORTAGE_PYM_PATH}} \ ${PORTAGE_PYTHON:-/usr/bin/python} "$PORTAGE_BIN_PATH"/xpak-helper.py recompose \ $PORTAGE_BINPKG_TMPFILE "$PORTAGE_BUILDDIR/build-info" if test $? -ne 0 { rm -f ${PORTAGE_BINPKG_TMPFILE} die "Failed to append metadata to the tbz2 file" } local md5_hash="" if type md5sum &>/dev/null { setvar md5_hash = $(md5sum "${PORTAGE_BINPKG_TMPFILE}") setvar md5_hash = ${md5_hash%% *} } elif type md5 &>/dev/null { setvar md5_hash = $(md5 "${PORTAGE_BINPKG_TMPFILE}") setvar md5_hash = ${md5_hash##* } } test -n ${md5_hash} && \ echo ${md5_hash} > "${PORTAGE_BUILDDIR}"/build-info/BINPKGMD5 __vecho ">>> Done." # cleanup our temp tree [[ -n ${PKG_INSTALL_MASK} ]] && rm -rf ${PROOT} cd ${PORTAGE_BUILDDIR} >> "$PORTAGE_BUILDDIR/.packaged" || \ die "Failed to create $PORTAGE_BUILDDIR/.packaged" } proc __dyn_spec { local sources_dir=${T}/rpmbuild/SOURCES mkdir -p ${sources_dir} declare -a tar_args=("${EBUILD}") [[ -d ${FILESDIR} ]] && setvar tar_args = ''("${EBUILD}" "${FILESDIR}") tar czf "${sources_dir}/${PF}.tar.gz" \ ${tar_args[@]} || \ die "Failed to create base rpm tarball." cat <<< """ > ${PF}.spec Summary: ${DESCRIPTION} Name: ${PN} Version: ${PV} Release: ${PR} License: GPL Group: portage/${CATEGORY} Source: ${PF}.tar.gz %description ${DESCRIPTION} ${HOMEPAGE} %prep %setup -c %build %install %clean %files / """ > ${PF}.spec Summary: ${DESCRIPTION} Name: ${PN} Version: ${PV} Release: ${PR} License: GPL Group: portage/${CATEGORY} Source: ${PF}.tar.gz %description ${DESCRIPTION} ${HOMEPAGE} %prep %setup -c %build %install %clean %files / __END1__ } proc __dyn_rpm { if ! ___eapi_has_prefix_variables { local EPREFIX= } cd ${T} || die "cd failed" local machine_name=${CHOST%%-*} local dest_dir=${T}/rpmbuild/RPMS/${machine_name} addwrite ${RPMDIR} __dyn_spec HOME=${T} \ rpmbuild -bb --clean --nodeps --rmsource "${PF}.spec" --buildroot ${D} --target ${CHOST} || die "Failed to integrate rpm spec file" install -D "${dest_dir}/${PN}-${PV}-${PR}.${machine_name}.rpm" \ "${RPMDIR}/${CATEGORY}/${PN}-${PV}-${PR}.rpm" || \ die "Failed to move rpm" } proc die_hooks { [[ -f $PORTAGE_BUILDDIR/.die_hooks ]] && return local x for x in $EBUILD_DEATH_HOOKS { $x >&2 } > "$PORTAGE_BUILDDIR/.die_hooks" } proc success_hooks { local x for x in $EBUILD_SUCCESS_HOOKS { $x } } proc install_hooks { local hooks_dir="${PORTAGE_CONFIGROOT}etc/portage/hooks/install" local fp local ret=0 shopt -s nullglob for fp in "${hooks_dir}"/* { if test -x $fp { $fp setvar ret = $(( $ret | $? )) } } shopt -u nullglob return $ret } proc eqatag { __eqatag ${@} } if test -n ${MISC_FUNCTIONS_ARGS} { __source_all_bashrcs test $PORTAGE_DEBUG == "1" && set -x for x in ${MISC_FUNCTIONS_ARGS} { ${x} } unset x [[ -n $PORTAGE_EBUILD_EXIT_FILE ]] && > "$PORTAGE_EBUILD_EXIT_FILE" if [[ -n $PORTAGE_IPC_DAEMON ]] { [[ ! -s $SANDBOX_LOG ]] "$PORTAGE_BIN_PATH"/ebuild-ipc exit $? } } :