Index: projects/portbuild/scripts/build =================================================================== --- projects/portbuild/scripts/build (revision 243880) +++ projects/portbuild/scripts/build (revision 243881) @@ -1,679 +1,686 @@ #!/bin/sh # $FreeBSD: ports/Tools/portbuild/scripts/build,v 1.15 2011/03/24 06:24:04 linimon Exp $ # server-side script to handle various commands common to builds # configurable variables pbc=${PORTBUILD_CHECKOUT:-/var/portbuild} pbd=${PORTBUILD_DATA:-/var/portbuild} # subdirectories to process. yes, this is a hack, but it saves code # duplication. quoted_subdirs="'/src' '/ports' ''" # XXX unused get_latest_snap() { snap=$1 zfs list -rHt snapshot ${snap} | tail -1 | awk '{print $1}' } now() { date +%Y%m%d%H%M%S } do_list() { arch=$1 branch=$2 buildpar=${pbd}/${arch}/${branch}/builds if [ -d ${buildpar} ]; then snaps=$(cd ${buildpar}; ls -1d 2* 2> /dev/null) echo "The following builds are active:" echo ${snaps} if [ -L ${buildpar}/latest -a -d ${buildpar}/latest/ ]; then link=$(readlink ${buildpar}/latest) link=${link%/} link=${link##*/} echo "Latest build is: ${link}" fi else echo "No such build environment ${arch}/${branch}" exit 1 fi } do_create() { arch=$1 branch=$2 buildid=$3 shift 3 # exit if arch-specific directory does not already exist or is - # not manageable by ports-:portmgr. + # not manageable by uid:gid. archdir=${pbd}/${arch} if [ ! -d ${archdir} ]; then - echo "${archdir} must exist and be owned by ports-${arch}:portmgr mode 77x." + echo "${archdir} must exist and be owned by ${uid}:${gid} mode 77x." exit 1 fi - chown -R ports-${arch}:portmgr ${archdir} || exit 1 + chown -R ${uid}:${gid} ${archdir} || exit 1 chmod 775 ${archdir} || exit 1 # create zfs instance for arch if it does not already exist archfs=${ZFS_VOLUME}/portbuild/${arch} if ! test_fs "${archfs}"; then zfs create -o mountpoint=${archdir} ${archfs} || exit 1 - chown -R ports-${arch}:portmgr ${archdir} + chown -R ${uid}:${gid} ${archdir} chmod -R g+w ${archdir} fi # create .ssh/ directory if it does not already exist sshdir=${pbd}/${arch}/.ssh if [ ! -d ${sshdir} ]; then mkdir -p ${sshdir} || exit 1 - chown -R ports-${arch}:portmgr ${sshdir} + chown -R ${uid}:${gid} ${sshdir} chmod 700 ${sshdir} fi # create overall archive directory if it does not already exist archivedir=${pbd}/${arch}/archive if [ ! -d ${archivedir} ]; then mkdir -p ${archivedir} || exit 1 - chown -R ports-${arch}:portmgr ${archivedir} + chown -R ${uid}:${gid} ${archivedir} chmod -R g+w ${archivedir} fi # create archive directory for all build logs if it does not already exist buildlogsdir=${archivedir}/buildlogs if [ ! -d ${buildlogsdir} ]; then mkdir -p ${buildlogsdir} || exit 1 - chown -R ports-${arch}:portmgr ${buildlogsdir} + chown -R ${uid}:${gid} ${buildlogsdir} chmod -R g+w ${buildlogsdir} fi # create archive directory for all errorlogs if it does not already exist errorlogsdir=${archivedir}/errorlogs if [ ! -d ${errorlogsdir} ]; then mkdir -p ${errorlogsdir} || exit 1 - chown -R ports-${arch}:portmgr ${errorlogsdir} + chown -R ${uid}:${gid} ${errorlogsdir} chmod -R g+w ${errorlogsdir} fi # create directory for all builds for buildenv if it does not already exist buildsdir=${pbab}/builds if [ ! -d ${buildsdir} ]; then mkdir -p ${buildsdir} || exit 1 - chown -R ports-${arch}:portmgr ${pbab} + chown -R ${uid}:${gid} ${pbab} chmod -R g+w ${pbab} fi # create directory for latest build for buildenv if it does not already exist builddir=$(realpath ${buildsdir})/${buildid} if [ -d ${builddir} ]; then echo "Can't create ${builddir}, it already exists" exit 1 fi # create zfs instance for latest build on buildenv mountpoint=${builddir} newfs=${ZFS_VOLUME}/portbuild/${arch}/${buildid} zfs create -o mountpoint=${mountpoint} ${newfs} || exit 1 - chown -R ports-${arch}:portmgr ${mountpoint} + chown -R ${uid}:${gid} ${mountpoint} chmod -R g+w ${mountpoint} # populate ports for latest build on buildenv do_portsupdate_inner ${arch} ${branch} ${buildid} ${builddir} $@ # populate src for latest build on buildenv do_srcupdate_inner ${arch} ${branch} ${buildid} ${builddir} $@ # create the link for building packages for latest build ln -sf ${builddir} ${pbab}/builds/latest # create clients/ directory if it does not already exist clientsdir=${pbd}/${arch}/clients if [ ! -d ${clientsdir} ]; then mkdir -p ${clientsdir} || exit 1 - chown -R ports-${arch}:portmgr ${clientsdir} + chown -R ${uid}:${gid} ${clientsdir} chmod -R g+w ${clientsdir} fi # create loads/ directory if it does not already exist loadsdir=${pbd}/${arch}/loads if [ ! -d ${loadsdir} ]; then mkdir -p ${loadsdir} || exit 1 - chown -R ports-${arch}:portmgr ${loadsdir} + chown -R ${uid}:${gid} ${loadsdir} chmod -R g+w ${loadsdir} fi # create empty mlist file if it does not already exist mlist=${pbd}/${arch}/mlist if [ ! -d ${mlist} ]; then touch ${mlist} || exit 1 - chown -R ports-${arch}:portmgr ${mlist} + chown -R ${uid}:${gid} ${mlist} chmod -R g+w ${mlist} fi # create the links for the webserver under ../errorlogs/ errorlogs=${pbd}/errorlogs ln -s ${buildsdir}/latest/bak/errors ${errorlogs}/${arch}-${branch}-previous ln -s ${buildsdir}/latest/bak/logs ${errorlogs}/${arch}-${branch}-previous-logs ln -s ${buildsdir}/latest/errors ${errorlogs}/${arch}-${branch}-latest ln -s ${buildsdir}/latest/logs ${errorlogs}/${arch}-${branch}-latest-logs ln -s ${buildsdir}/latest/bak/packages ${errorlogs}/${arch}-${branch}-packages-previous ln -s ${buildsdir}/latest/packages ${errorlogs}/${arch}-${branch}-packages-latest buildlogs_archive_link=${errorlogs}/${arch}-buildlogs if [ ! -e ${buildlogs_archive_link} ]; then ln -s ${archivedir}/buildlogs ${buildlogs_archive_link} fi errorlogs_archive_link=${errorlogs}/${arch}-errorlogs if [ ! -e ${errorlogs_archive_link} ]; then ln -s ${archivedir}/errorlogs ${errorlogs_archive_link} fi echo "New build ID is ${buildid}" } do_clone() { arch=$1 branch=$2 buildid=$3 builddir=$4 shift 4 if [ "$#" -gt 0 ]; then newid=$1 shift else newid=$(now) fi tmp=$(realpath ${builddir}) tmp=${tmp%/} newbuilddir="${tmp%/*}/${newid}" oldfs=${ZFS_VOLUME}/portbuild/${arch}/${buildid} newfs=${ZFS_VOLUME}/portbuild/${arch}/${newid} zfs snapshot ${oldfs}@${newid} zfs clone ${oldfs}@${newid} ${newfs} zfs set mountpoint=${newbuilddir} ${newfs} zfs promote ${newfs} if zfs list -H -t filesystem ${oldfs}/ports 2> /dev/null; then portsnap=${oldfs}/ports@${newid} zfs snapshot ${portsnap} zfs clone ${portsnap} ${newfs}/ports zfs promote ${newfs}/ports fi if zfs list -H -t filesystem ${oldfs}/src 2> /dev/null; then srcsnap=${oldfs}/src@${newid} zfs snapshot ${srcsnap} zfs clone ${srcsnap} ${newfs}/src zfs promote ${newfs}/src fi if [ -d ${newbuilddir} ]; then if [ ! -f ${pbab}/builds/previous/.keep ]; then ${pbc}/scripts/build destroy ${arch} ${branch} previous fi rm -f ${pbab}/builds/previous mv ${pbab}/builds/latest ${pbab}/builds/previous ln -sf ${newbuilddir} ${pbab}/builds/latest fi echo "New build ID is ${newid}" } do_portsupdate() { arch=$1 branch=$2 buildid=$3 builddir=$4 shift 4 if [ $# -gt 0 ]; then arg=$1 shift fi destroy_fs ${ZFS_VOLUME}/portbuild/${arch} ${buildid} /ports || exit 1 if [ "${arg}" = "-umount" ]; then return fi echo do_portsupdate_inner ${arch} ${branch} ${buildid} ${builddir} $@ } do_portsupdate_inner() { arch=$1 branch=$2 buildid=$3 builddir=$4 shift 4 echo "================================================" echo "Reimaging ZFS ports tree on ${builddir}/ports" echo "================================================" portsfs=${ZFS_VOLUME}/portbuild/${arch}/${buildid}/ports now=$(now) zfs snapshot ${ZFS_VOLUME}/${SNAP_PORTS_DIRECTORY}/ports@${now} zfs clone ${ZFS_VOLUME}/${SNAP_PORTS_DIRECTORY}/ports@${now} ${portsfs} zfs set mountpoint=${builddir}/ports ${portsfs} } do_srcupdate() { arch=$1 branch=$2 buildid=$3 builddir=$4 shift 4 if [ $# -gt 0 ]; then arg=$1 shift fi destroy_fs ${ZFS_VOLUME}/portbuild/${arch} ${buildid} /src || exit 1 if [ "${arg}" = "-umount" ]; then return fi echo do_srcupdate_inner ${arch} ${branch} ${buildid} ${builddir} $@ } do_srcupdate_inner() { arch=$1 branch=$2 buildid=$3 builddir=$4 shift 4 echo "================================================" echo "Reimaging ZFS src tree on ${builddir}/src" echo "================================================" strippedbranch=${branch%%[-\.]*} srcfs=${ZFS_VOLUME}/portbuild/${arch}/${buildid}/src now=$(now) zfs snapshot ${ZFS_VOLUME}/${SNAP_SRC_DIRECTORY_PREFIX}${strippedbranch}/src@${now} zfs clone ${ZFS_VOLUME}/${SNAP_SRC_DIRECTORY_PREFIX}${strippedbranch}/src@${now} ${srcfs} zfs set mountpoint=${builddir}/src ${srcfs} } cleanup_client() { arch=$1 branch=$2 buildid=$3 mach=$4 arg=$5 # XXX use same exclusion protocol as claim-chroot echo "Started cleaning up ${arch}/${branch} build ID ${buildid} on ${mach}" test -f ${pbd}/${arch}/portbuild.${mach} && . ${pbd}/${arch}/portbuild.${mach} # Kill off builds and clean up chroot ${pbc}/scripts/dosetupnode ${arch} ${branch} ${buildid} ${mach} -nocopy -queue -full echo "Finished cleaning up ${arch}/${branch} build ID ${buildid} on ${mach}" } do_cleanup() { arch=$1 branch=$2 buildid=$3 builddir=$4 arg=$5 shift 5 for i in `cat ${pbd}/${arch}/mlist`; do cleanup_client ${arch} ${branch} ${buildid} ${i} ${arg} & done wait } do_upload() { arch=$1 branch=$2 buildid=$3 builddir=$4 shift 4 echo "Not implemented yet" exit 1 } test_fs() { local fs=$1 zfs list -Ht filesystem ${fs} > /dev/null 2>&1 } get_latest_child() { local fs=$1 # Return the child of this filesystem with lexicographically # highest name # # XXX if a filesystem is cloned into a different prefix # (e.g. different arch) then we may not get the most recent one # but that should not happen. zfs get -H -o name,value origin | grep ${fs} | sort | \ (while read zfs origin; do if [ "${origin%@*}" = "${fs}" ]; then child=${zfs} fi done; echo ${child}) } get_parent() { local fs=$1 # Check whether this filesystem has a parent zfs get -H -o value origin ${fs} | \ (read snap; case "${snap}" in -|${ZFS_VOLUME}/${SNAP_DIRECTORY}/*) ;; *) parent=${snap} ;; esac; echo ${parent}) } destroy_fs() { fs=$1 buildid=$2 subfs=$3 fullfs=${fs}/${buildid}${subfs} if test_fs "${fullfs}"; then # We can destroy a leaf filesystem (having no dependent # clones) with no further effort. However if we are # destroying the root of the clone tree then we have to # promote a child to be the new root. # # XXX In principle we might have to iterate until we end up as # a leaf but I don't know if this can happen. echo "Filesystem ${fullfs}" child=$(get_latest_child ${fullfs}) parent=$(get_parent ${fullfs}) echo "Filesystem has parent ${parent}" if [ -z "${child}" ]; then echo "Filesystem is a leaf" echo else echo "Filesystem has latest child ${child}" # Check whether filesystem is root if [ -z "${parent}" ]; then echo "Filesystem is root; promoting ${child}" zfs promote ${child} parent=$(get_parent ${fullfs}) echo "New parent is ${parent}" echo else echo "Filesystem has parent ${parent} and cannot be destroyed" echo return 1 fi fi # We might have snapshots on the target filesystem, e.g. if it # is both the head and tail of its clone tree. They should be # unreferenced. # We have to grep because zfs list -H returns an error instead of # a null list if no snapshots exist if ! (zfs list -r -H -o name -t snapshot ${fullfs} | grep "^${fullfs}@" | xargs -n 1 zfs destroy); then return 1 fi # The target filesystem should now be unreferenced if ! zfs destroy -f "${fullfs}"; then return 1 fi # Destroy the origin snapshot, which should be unreferenced if [ ! -z "${parent}" ]; then if ! zfs destroy -f ${parent}; then return 1 fi fi fi } do_destroy() { arch=$1 branch=$2 buildid=$3 builddir=$4 shift 4 echo buildid=$(resolve ${pbd} ${arch} ${branch} ${buildid}) if [ -z "${buildid}" ]; then echo "Invalid build ID ${buildid}" exit 1 fi # perform sanity check to prevent e.g. "latest" being destroyed # before "previous" for quoted_subdir in $quoted_subdirs; do fullfs=${ZFS_VOLUME}/portbuild/${arch}/${buildid}`echo $quoted_subdir | sed -e "s/'//g"` if test_fs "${fullfs}"; then child=$(get_latest_child ${fullfs}) parent=$(get_parent ${fullfs}) if [ ! -z "${child}" -a ! -z "${parent}" ]; then echo "Filesystem ${fullfs} has parent ${parent} and child ${child}" echo "and thus cannot be destroyed. Run 'build destroy' on the child, first." fi fi done # now ok to do the actual destroy. for quoted_subdir in $quoted_subdirs; do destroy_fs ${ZFS_VOLUME}/portbuild/${arch} ${buildid} `echo $quoted_subdir | sed -e "s/'//g"` || exit 1 done rmdir ${builddir} } # Run a command as root if running as user # Authentication and command validation is taken care of by buildproxy proxy_root() { cmd=$1 arch=$2 branch=$3 buildid=$4 builddir=$5 shift 5 args=$@ id=$(id -u) if [ ${id} != "0" ]; then ${pbc}/scripts/buildproxy-client "build ${cmd} ${arch} ${branch} ${buildid} ${args}" error=$? if [ ${error} -eq 254 ]; then echo "Proxy error" fi else eval "do_${cmd} ${arch} ${branch} ${buildid} ${builddir} ${args}" error=$? fi exit ${error} } -# Run a command as the ports-${arch} user if root +# Run a command as the ${uid} user if root proxy_user() { cmd=$1 arch=$2 branch=$3 buildid=$4 builddir=$5 shift 5 args=$@ id=$(id -u) if [ ${id} != "0" ]; then eval "do_${cmd} ${arch} ${branch} ${buildid} \"${builddir}\" ${args}" error=$? else - su ports-${arch} -c "${pbc}/scripts/build ${cmd} ${arch} ${branch} ${buildid} \"${builddir}\" ${args}" + su ${uid} -c "${pbc}/scripts/build ${cmd} ${arch} ${branch} ${buildid} \"${builddir}\" ${args}" error=$? fi exit ${error} } usage () { echo "usage: build [] [ ...]" exit 1 } ################## if [ $# -lt 3 ]; then usage fi cmd=$1 arch=$2 branch=$3 shift 3 . ${pbc}/conf/server.conf . ${pbd}/${arch}/portbuild.conf . ${pbc}/scripts/buildenv pbab=${pbd}/${arch}/${branch} validate_env ${arch} ${branch} || exit 1 + +uid=ports-${arch} +if [ ! -z "${PORTBUILD_GROUP}" ]; then + gid=${PORTBUILD_GROUP} +else + gid=${uid} +fi # Not every command requires a buildid as arg if [ $# -ge 1 ]; then buildid=$1 shift 1 # Most commands require a buildid that is valid on the server. The # exception is "cleanup" which is cleaning up a client build that may # already be destroyed on the server. case "$cmd" in cleanup) # Resolve symlinks but don't bail if the build doesn't exist. newbuildid=$(resolve ${pbd} ${arch} ${branch} ${buildid}) if [ ! -z "${newbuildid}" -a "${newbuildid}" != "${buildid}" ]; then echo "Resolved ${buildid} to ${newbuildid}" buildid=${newbuildid} builddir=$(realpath ${pbab}/builds/${buildid}/) # We can't rely on buildenv for this code path fi ;; create) # XXX some way to avoid the latest/previous dance? if [ "${buildid}" = "latest" ]; then buildid=$(now)"."`hostname -s` elif [ "${buildid}" = "previous" ]; then echo "Use build clone latest instead" exit 1 else buildid=${buildid%/} fi # We can't rely on buildenv for this code path ;; *) newbuildid=$(resolve ${pbd} ${arch} ${branch} ${buildid}) if [ -z "${newbuildid}" ]; then echo "Build ID ${buildid} does not exist" exit 1 fi if [ ${newbuildid} != ${buildid} ]; then echo "Resolved ${buildid} to ${newbuildid}" buildid=${newbuildid} fi builddir=$(realpath ${pbab}/builds/${buildid}/) buildenv ${pbd} ${arch} ${branch} ${builddir} ;; esac fi if [ -n "${buildid}" ]; then if [ -f ${pbd}/${arch}/${branch}/builds/${buildid}/portbuild.conf ]; then . ${pbd}/${arch}/${branch}/builds/${buildid}/portbuild.conf fi fi # Unprivileged commands case "$cmd" in list) do_list ${arch} ${branch} $@ || exit 1 ;; create) if [ -z "${buildid}" ]; then echo "buildid is empty; forcing to 'latest'" buildid=$(now)"."`hostname -s` fi proxy_root create ${arch} ${branch} ${buildid} $@ || exit 1 ;; clone) if [ -z "${buildid}" ]; then usage fi proxy_root clone ${arch} ${branch} ${buildid} ${builddir} $@ || exit 1 ;; portsupdate) if [ -z "${buildid}" ]; then usage fi proxy_root portsupdate ${arch} ${branch} ${buildid} ${builddir} $@ || exit 1 ;; srcupdate) if [ -z "${buildid}" ]; then usage fi proxy_root srcupdate ${arch} ${branch} ${buildid} ${builddir} $@ || exit 1 ;; cleanup) if [ -z "${buildid}" ]; then usage fi # builddir may be null if cleaning up a destroyed build proxy_user cleanup ${arch} ${branch} ${buildid} "${builddir}" $@ || exit 1 ;; upload) if [ -z "${buildid}" ]; then usage fi proxy_user upload ${arch} ${branch} ${buildid} ${builddir} $@ || exit 1 ;; destroy) if [ -z "${buildid}" ]; then usage fi proxy_root destroy ${arch} ${branch} ${buildid} ${builddir} $@ || exit 1 ;; *) echo "build: invalid command: $cmd" exit 1 ;; esac