diff options
author | Sebastian | 2016-04-25 12:01:08 +0200 |
---|---|---|
committer | Sebastian | 2016-04-25 12:01:08 +0200 |
commit | 5acda3eaeabae9045609539303a8c12c4ce401f1 (patch) | |
tree | 7e71975f8570b05aafe2ea6ec0e242a8912387bb /core/includes | |
parent | initial commit (diff) | |
download | mltk-5acda3eaeabae9045609539303a8c12c4ce401f1.tar.gz mltk-5acda3eaeabae9045609539303a8c12c4ce401f1.tar.xz mltk-5acda3eaeabae9045609539303a8c12c4ce401f1.zip |
merge with latest dev version
Diffstat (limited to 'core/includes')
-rw-r--r-- | core/includes/binutil.inc | 222 | ||||
-rw-r--r-- | core/includes/chroot.inc | 248 | ||||
-rw-r--r-- | core/includes/clean_module_funcs.inc | 23 | ||||
-rw-r--r-- | core/includes/cleanup.inc | 34 | ||||
-rw-r--r-- | core/includes/distribution.inc | 53 | ||||
-rw-r--r-- | core/includes/downloader.inc | 80 | ||||
-rw-r--r-- | core/includes/helper/README.helper | 2 | ||||
-rw-r--r-- | core/includes/helper/distribution.inc | 4 | ||||
-rw-r--r-- | core/includes/helper/fileutil.inc | 39 | ||||
-rw-r--r-- | core/includes/helper/logging.inc | 53 | ||||
-rw-r--r-- | core/includes/helper/string.inc | 31 | ||||
-rw-r--r-- | core/includes/kernel.inc | 218 | ||||
-rw-r--r-- | core/includes/keyvalueutil.inc | 32 | ||||
-rw-r--r-- | core/includes/packagemanager.inc | 187 | ||||
-rw-r--r-- | core/includes/paths.inc | 31 | ||||
-rw-r--r-- | core/includes/qt.inc | 30 | ||||
-rw-r--r-- | core/includes/system.inc | 37 | ||||
-rw-r--r-- | core/includes/useradd.inc | 238 | ||||
-rw-r--r-- | core/includes/versioncompare.inc | 61 |
19 files changed, 1623 insertions, 0 deletions
diff --git a/core/includes/binutil.inc b/core/includes/binutil.inc new file mode 100644 index 00000000..b9867a90 --- /dev/null +++ b/core/includes/binutil.inc @@ -0,0 +1,222 @@ +# +# Common functions to copy binaries and their dependancies. +# +############################################################ +# This will parse the output of ldd on given binaries +# and echo the location of these libs to STDOUT +# The output of this function has to be used in some +# way, it only echos! +# +# About local search: +# It is required that we can search for the dynamic +# libraries in a specific directory, namely the one +# where we (potentially) built the binary. If a +# corresponding library is found, it should take +# precedence over ones found on the system. +# This can be done by using the '-l' switch, see below. +# +############################################################ +# We use a blacklist mechanism to exclude common libraries. +# This improves runtime quite a bit... +declare -rg BLACKLIST="ld-linux linux-gate linux-vdso libc.so" + + +# replace ' ' by '|' in the blacklist, so grep can use it directly. +CURRENT_BLACKLIST=$(echo ${BLACKLIST} | sed 's/ /\\|/g') + +# Initialise flag and path for local search +LOCALSEARCH=0 +LOCALSEARCHDIR="" + +DONEDONE=$(mktemp) +echo "-----------------------" > "$DONEDONE" + +############################################################ +# +# Usage: +# get_dynamic_dependencies [-l <searchdir>] <binary_list> +# * the list must be seperated by spaces +# * the search for lib needed by a binary can be done locally, +# using the -l <searchdir> option +# +# Ouput: +# Will simply echo list of required libraries + +get_dynamic_dependencies() { + # check if local search is activated by the '-l' switch + # if so the following argument is the path. + if [ "x$1" == "x-l" ]; then + local LOCALSEARCH=1 + shift + [ ! -d "$1" ] && perror "Directory '$1' does not exist, exiting." + local LOCALSEARCHDIR="$1" + shift + fi + + # main loop over the list of binaries + while [ $# != 0 ]; do + local BINARY="$1" + shift + [ -f "$BINARY" ] || continue + + # now run ldd on it and save the output in $LDD_OUT + local LDD_OUT="ldd_output" + if ldd "$BINARY" > "$LDD_OUT"; then + # Case 1: file is a dynamic executable + for LIB in $(grep -v "${CURRENT_BLACKLIST}${REQUIRED_LDD_BLACKLIST}" "$LDD_OUT" | awk '{print $1 $2 $3}'); do + # split the entry into an array, ex: + # libm.so.6 => /lib/libm.so.6 would be split into: + # LIBLINK[0] LIBLINK[1] + local LIBLINK=(${LIB//=>/ }) + # call helper function to find the correct lib + lib_search + done + #TODO: check if "statically linked" is output + else + # Case 2: not a dynamic, do nothing + pdebug "\t\t\t(Not a dynamic.)" + fi + rm -f -- "$LDD_OUT" + done + +} + +############################################################ +# +# Usage: +# lib_search +# +# Output: +# List of the path including any possible symbolic links +# of the found libraries. +# +# Note: This function takes no argument. It takes the library +# to look for from the local array LIBLINK. +# If the local was activated in get_dynamic_dependencies +# this will search for the library in LOCALSEARCHDIR first. +# If its not found, then it will look system-wide. +lib_search() { + + # if activated, start by searching the lib locally + if [ "x$LOCALSEARCH" == "x1" ]; then + cd "$LOCALSEARCHDIR" + local LOCAL_MATCHES=$(find . -name "${LIBLINK[0]}") # | awk -F '.' '{print $1}')".so\*) + cd - >/dev/null + if [ "x${LOCAL_MATCHES}" != "x" ]; then + for LOCALLIB in ${LOCAL_MATCHES}; do + grep -q "^${LOCALLIB}\$" "$DONEDONE" && continue + echo "${LOCALLIB}" >> "$DONEDONE" + get_link_chain "${LOCALSEARCHDIR}/${LOCALLIB}" "${LOCALSEARCHDIR}" + get_dynamic_dependencies -l "${LOCALSEARCHDIR}" "${LOCALLIB}" + done + # found the libs, we are done + return + fi + # mark local search as done + fi + + # search the lib on the system since it was not found earlier + if [ -n "${LIBLINK[1]}" ] && [ "x${LIBLINK[1]}" != "xnot" ]; then + grep -q "^${LIBLINK[1]}\$" "$DONEDONE" && return + echo "${LIBLINK[1]}" >> "$DONEDONE" + # get chain of symlink for that lib + get_link_chain "${LIBLINK[1]}" + else + pwarning "\t\tLib '${LIBLINK[0]}' from required dir '$ENTRY' neither found in build directory nor on this system." + pwarning "\t\tIf this lib is not supplied by another module, this module will probably fail in your final system" + fi +} +############################################################ +# +# Usage: +# get_link_chain <link> [prefix] +# * <link> must be in absolute form- +# * [prefix] is the prefix to strip from the ouput. +# +# Output: +# Lists the symlink chain until a hardlink is found. +# +get_link_chain() { + + # sanity checks + [[ "$1" == /* ]] || perror "get_link_chain() requires absolute paths, given: $1" + if [ ! -e $1 -a ! -L $1 ]; then + perror "'$1' is a link but its target '$LINK_TARGET' is not in '${LOCALSEARCHDIR}'" + fi + local PREFIX= + if [ $# == 2 ] ; then + [ -d "$2" ] || perror "get_link_chain: '$2' is not a directory. Local search can't work..." + # got a prefix + local PREFIX=$(dirname "$(canonicalize "$2/foo")") + [ -d "$PREFIX" ] || perror "Could not canonicalize $2" + [[ "$PREFIX" == */ ]] || PREFIX="$PREFIX/" + fi + + # canonalize + local LINK=$(canonicalize "$1") + + local CHAIN="$LINK" + + # write the first link in the chain + if [ "x$PREFIX" != "x" ]; then + if [ "x${LINK#$PREFIX}" == "x${LINK}" ]; then + # prefix was not in the link + echo "$LINK" + else + # prefix was in the link + echo ./"${LINK#$PREFIX}" + fi + else + # no prefix, copy like it is + echo "$LINK" + fi + + # now we check for symlinks + local TRY=0 + while [ -L "$LINK" ] && [ $TRY -lt 10 ]; do + let TRY=TRY+1 + + # save the directory prefix + CURRENTDIR=$(dirname "${LINK}") + # first follow the link + local NEWLINK=$(readlink "$LINK") + [ -z "$NEWLINK" -o "$NEWLINK" = "$LINK" ] && break + LINK=$NEWLINK + CHAIN+=" -> $LINK" + # $LINK can be absolute or relative, check cases + [[ "$LINK" == /* ]] || LINK=$(canonicalize "$CURRENTDIR"/"${LINK}") + # write the first link in the chain + if [ "x$PREFIX" != "x" ]; then + if [ "x${LINK#$PREFIX}" == "x${LINK}" ]; then + # prefix was not in the link + if [ ! -e "$LINK" ]; then + [ -e "$PREFIX/$LINK" ] && echo "./$LINK" + else + echo "$LINK" + fi + else + # prefix was in the link + echo ./"${LINK#$PREFIX}" + fi + else + # no prefix, copy like it is + echo "$LINK" + fi + done + pdebug "\t\t$CHAIN" +} +############################################################ +# +# Usage: +# list_basic_libs +# +# Output: +# list the path of following basic system libraries: +# - libc.so, ld-linux.so +# +list_basic_libs() { + for i in $(ldd ${SHELL}); do + [ $(echo $i | grep '^/' | grep -c ld) -eq 1 -o $(echo $i | grep '^/' | grep -c libc.so) -eq 1 ] && get_link_chain $i + done +} + diff --git a/core/includes/chroot.inc b/core/includes/chroot.inc new file mode 100644 index 00000000..b7f68f65 --- /dev/null +++ b/core/includes/chroot.inc @@ -0,0 +1,248 @@ +# ----------------------------------------------------------------------------- +# +# Copyright (c) 2014 - OpenSLX GmbH +# +# This program is free software distributed under the GPL version 2. +# See http://openslx.org/COPYING +# +# If you have any feedback please consult http://openslx.org/feedback and +# send your suggestions, praise, or complaints to feedback@openslx.org +# +# General information about OpenSLX can be found at http://openslx.org/ +# ----------------------------------------------------------------------------- +# +# Common functions for chrooting +# +# ----------------------------------------------------------------------------- + +declare -rg CHROOT_TEMPDIR="${ROOT_DIR}/tmp/chroot" +declare -rg CHROOT_MOUNTDIR="${CHROOT_TEMPDIR}/rootmount" +declare -rg CHROOT_BINDDIR="${CHROOT_TEMPDIR}/rootbind" +declare -rg CHROOT_LOWERDIR="/" +declare -rg CHROOT_BINDMOUNTS="/dev /proc /sys /run" + + +# Helper function to setup the directory structure +chroot_prepare_dirs() { + # first check if CHROOT_TEMPDIR exists + if [ -d "${CHROOT_TEMPDIR}" ]; then + # try to umount and rmdir CHROOT_MOUNTDIR + umount "${CHROOT_MOUNTDIR}" 2>/dev/null + if [ -d "${CHROOT_MOUNTDIR}" ]; then + rmdir "${CHROOT_MOUNTDIR}" || perror "Could not remove CHROOT_MOUNTDIR '${CHROOT_MOUNTDIR}', meaning it has stuff in it. Aborting..." + fi + + # try to umount and rmdir CHROOT_BINDDIR + umount "${CHROOT_BINDDIR}" 2>/dev/null + if [ -d "${CHROOT_BINDDIR}" ]; then + rmdir "${CHROOT_BINDDIR}" || perror "Could not remove CHROOT_BINDDIR '${CHROOT_BINDDIR}', meaning it has stuff in it. Aborting..." + fi + + # try to rmdir CHROOT_TEMPDIR + if [ -d "${CHROOT_TEMPDIR}" ]; then + rmdir "${CHROOT_TEMPDIR}" || perror "Could not remove CHROOT_TEMPDIR '${CHROOT_TEMPDIR}', meaning it has stuff in it. Aborting..." + fi + fi + + mkdir -p "${CHROOT_TEMPDIR}" || perror "Could not create base directory for mount directories $CHROOT_TEMPDIR." + for DIR in "${CHROOT_BINDDIR}" "${CHROOT_MOUNTDIR}"; do + mkdir -p "${DIR}" || perror "Could not create directory for mount directory $DIR." + done +} + +# Helper to mount the overlay structure: +# - bind mount system / to CHROOT_BINDDIR and make it read-only +# - make an overlay from CHROOT_LOWERDIR CHROOT_UPPERDIR +# - bind mount additional pseudo-fs (as given in CHROOT_BINDMOUNTS) +chroot_prepare_mounts() { + + # first mount / on CHROOT_BINDDIR and remount read-only + mount -o bind "${CHROOT_LOWERDIR}" "${CHROOT_BINDDIR}" \ + || perror "Could not bind-mount CHROOT_LOWERDIR '$CHROOT_LOWERDIR' to CHROOT_BINDDIR '$CHROOT_BINDDIR'." + mount -o remount,ro,bind "${CHROOT_BINDDIR}" || perror "Could not remount CHROOT_BINDDIR '$CHROOT_BINDDIR' read-only." + + # check that it really is read-only + [ "x$(mount | grep -E "^/ on ${CHROOT_BINDDIR}" | grep -v '\(.*ro.*\)')" != "x" ] \ + && perror "CHROOT_BINDDIR '${CHROOT_BINDDIR}' is not read-only! Aborting..." + + # Note: The overlay fs mount syntax seems to be changed between Ubuntu 14.04.2 and 14.04.3 (Kernel 3.13 and 3.19). Instead of + # checking overlay-modinfo (which may fail if overlayfs is not incorporated as module) or kernel versions, we simply try to + # mount 'old school' first and then, if that fails, the new way to mount with workdir. See differences in mount syntax below. + pinfo "Now mounting overlayfs. Trying old mount syntax (up to Kernel 3.13) ..." + mount -t overlayfs overlayfs -o lowerdir="${CHROOT_BINDDIR}",upperdir="${CHROOT_UPPERDIR}" "${CHROOT_MOUNTDIR}" 2>/dev/null + if [ $? -ne 0 ]; then + pinfo "Old mount syntax failed. Trying new mount syntax (Kernel 3.19+) ..." + # We have to use a overlayfs workdir which _must_ be in the same filesystem as CHROOT_UPPERDIR. So + # we traverse to the directory below CHROOT_UPPERDIR and mkdir/mktemp a workdir there. In the possible + # case that CHROOT_UPPERDIR is the root dir of a filesystem there's nothing we can do but exit. + CHROOT_WORKDIR="$(mktemp -d $(dirname ${CHROOT_UPPERDIR})/workdirXXX)" \ + || perror "Could not mkdir overlayfs workdir $CHROOT_WORKDIR for new overlayfs mount syntax." + # Now we try to mount the overlayfs in the new fashion: + mount -t overlayfs overlayfs -o lowerdir="$CHROOT_LOWERDIR",upperdir="${CHROOT_UPPERDIR}",workdir="${CHROOT_WORKDIR}" "${CHROOT_MOUNTDIR}" \ + || perror "Could not mount (overlayfs) $CHROOT_LOWERDIR, $CHROOT_UPPERDIR to $CHROOT_BINDDIR." + pinfo "New overlayfs mount syntax has worked, commencing." + else + pinfo "Old overlayfs mount syntax has worked, commencing." + fi + + # mount pseudo-filesystems + for DIR in $CHROOT_BINDMOUNTS; do + mount -o bind "${DIR}" "${CHROOT_MOUNTDIR}/${DIR}" \ + || perror "Could not bind mount '$DIR' into CHROOT_MOUNTDIR/DIR '$CHROOT_MOUNTDIR/$DIR'." + done +} + +# Helper to generate the mighty autoexec.bat +chroot_gen_autoexec() { + # create the script to be automatically executed. + cat >"${CHROOT_MOUNTDIR}/autoexec.bat"<<-EOF + #!/bin/bash + ####################################################### + # # + # Warning! # + # # + # This file is only meant to be executed within # + # the specially chrooted mltk building environment. # + # # + # Do NOT execute it if you are not sure what you do, # + # it may be very harmful if being run in a normal # + # system environment! # + # # + ####################################################### + echo "chroot started successfully." + EOF + + # dump the piped input to it + cat >> "${CHROOT_MOUNTDIR}/autoexec.bat" + + # make it executable + chmod +x "${CHROOT_MOUNTDIR}/autoexec.bat" || perror "Failed to make '${CHROOT_MOUNTDIR}/autoexec.bat' exeutable." +} + +chroot_handle_whiteouts() { + local WHITEOUT_LIST="${CHROOT_UPPERDIR}/overlay.whiteout.list" + rm -f -- "$WHITEOUT_LIST=" + #mkdir -p "$(dirname "$WHITEOUT_LIST")" || perror "Could not create $(dirname "$WHITEOUT_LIST")" + pdebug "Searching for overlayfs-whiteouts ..." + for WHITEOUT in $(find "$CHROOT_UPPERDIR" -lname "(overlay-whiteout)"); do + pdebug "Whiteout found: $WHITEOUT" + echo "/./${WHITEOUT#$CHROOT_UPPERDIR}" >> "$WHITEOUT_LIST" + rm -f -- "$WHITEOUT" || perror "Could not delete whiteout $WHITEOUT!" + done + pinfo "Whiteout list dumped to '${CHROOT_UPPERDIR}/overlay.whiteout.list'" +} + +############################################################################### +# +# MAIN FUNCTION +# +# Main function to be called from the outside +# Usage: +# chroot_run <build_dir> < <code_to_exec_in_chroot> +# +# Example: +# chroot_run /tmp/chroot_build <<-EOF +# echo "This will be executed inside the chroot" +# EOF +# +# It will run: +# - chroot_prepare +# - chroot $CHROOT_TEMPDIR/rootmount +# - executes $CHROOT_TEMPDIR/rootmount/autoexec.bat +# - chroot_cleanup +chroot_run() { + # check args + [ $# -eq 1 ] || perror "'chroot_run' requires exactly 1 parameter. Given $#. Use 'chroot_run <build_dir>'" + + local CHROOT_UPPERDIR="$1" + mkdir -p "$1" + + # first prepare the dir structure + chroot_prepare_dirs || perror "'chroot_prepare_dirs' failed with $?." + chroot_prepare_mounts || perror "'chroot_prepare_mounts' failed with $?." + + # generate the code to be executed when chroot'ing + chroot_gen_autoexec || perror "'chroot_gen_autoexec' failed with $?." + + # do the chroot + exec 0>&8 # This redirection is used for debugging within a chroot + chroot --userspec root:root "${CHROOT_MOUNTDIR}" /autoexec.bat + local RET=$? + if [ "$RET" -eq 0 ]; then + pinfo "chroot executed '${CHROOT_MOUNTDIR}/autoexec.bat' succeeded." + else + perror "Failed to run '$CHROOT_MOUNTDIR/autoexec.bat' inside the chroot to '$CHROOT_MOUNTDIR' with error code: $RET" + fi + + # handle whiteouts + chroot_handle_whiteouts || perror "'chroot_handle_whiteouts' failed with error code: $?" + + # finally cleanup all the mounting stuff we did previously + chroot_cleanup_mounts || perror "'chroot_cleanup' failed with $?." +} + +############################################################################### +# +# CLEANUP FUNCTIONS +# +# Helper to check if the given path is mounted +chroot_check_mount_point() { + [ "$#" -eq 1 ] || perror "'chroot_check_mount_point' called with $# arguements, only 1 accepted." + local MOUNT="$1" + if [ "x$(mount | grep "$(readlink -f $MOUNT)")" != "x" ]; then + # still mounted + pdebug "'$MOUNT' is mounted!" + return 1 + else + pdebug "'$MOUNT' is not mounted." + return 0 + fi +} + +# Helper to umount the given path +chroot_umount() { + [ "$#" -eq 1 ] || perror "'chroot_umount' called with $# arguments, only 1 accepted." + local MOUNT="$1" + + # check if MOUNT is mounted + if ! chroot_check_mount_point "${MOUNT}"; then + # still mounted + if umount -l "${MOUNT}"; then + pdebug "Successfully umounted '${MOUNT}'." + else + pwarning "Could not umount '${MOUNT}'! Trying again..." + # now it gets ugly + for i in `seq 1 5`; do + umount -l "${MOUNT}" && return 0 + done + perror "Could not umount '${MOUNT}' after 5 tries! This shouldn't happen. Check your scripts." + fi + else + pdebug "'${MOUNT}' is not mounted." + fi + + # better be safe than sorry + chroot_check_mount_point "$MOUNT" || perror "'$MOUNT' is still mounted, exiting before something bad happens..." +} + +# Helper to cleanup the temporary mounts +chroot_cleanup_mounts() { + if [[ "$(mount | grep -c $CHROOT_TEMPDIR)" -gt 0 ]]; then # No point in unmounting then... + for DIR in $CHROOT_BINDMOUNTS; do + chroot_umount "${CHROOT_MOUNTDIR}/${DIR}" + done + chroot_umount "${CHROOT_MOUNTDIR}" + chroot_umount "${CHROOT_BINDDIR}" + else + pinfo "Nothing chroot-related is mounted - exiting." + fi + + # In case of 'new' overlayfs mount - should perhaps be handled via flag... + if [ -d "${CHROOT_WORKDIR}" ]; then + # Too much of a coward to rm -rf somewhere. Both directories should be empty so we use rmdir. + rmdir "${CHROOT_WORKDIR}/work" && pinfo "rmdir-ed CHROOT_WORKDIR/work ${CHROOT_WORKDIR}/work needed for new overlayfs mount syntax." \ + || pinfo "Could not rmdir CHROOT_WORKDIR/work ${CHROOT_WORKDIR}/work - clean it by hand." + rmdir "${CHROOT_WORKDIR}" && pinfo "rmdir-ed CHROOT_WORKDIR ${CHROOT_WORKDIR} needed for new overlayfs mount syntax." \ + || pinfo "Could not rmdir CHROOT_WORKDIR ${CHROOT_WORKDIR} needed for new overlayfs mount syntax - clean by hand." + fi +} diff --git a/core/includes/clean_module_funcs.inc b/core/includes/clean_module_funcs.inc new file mode 100644 index 00000000..91f13942 --- /dev/null +++ b/core/includes/clean_module_funcs.inc @@ -0,0 +1,23 @@ +# Called when the sources for this module need to be fetched. +# Not called if fetched_source.flag exists +fetch_source() { + : +} + +# Called when the module should be built. +# Not called if build_complete.flag exists +build() { + : +} + +# Called after the relevant files from the module's build dir +# have been copied to the target dir. +post_copy() { + : +} + +# Called when this module is about to be handled, before fetching the source. +module_load() { + : +} + diff --git a/core/includes/cleanup.inc b/core/includes/cleanup.inc new file mode 100644 index 00000000..9716c841 --- /dev/null +++ b/core/includes/cleanup.inc @@ -0,0 +1,34 @@ +# ----------------------------------------------------------------------------- +# +# Copyright (c) 2014 - OpenSLX GmbH +# +# This program is free software distributed under the GPL version 2. +# See http://openslx.org/COPYING +# +# If you have any feedback please consult http://openslx.org/feedback and +# send your suggestions, praise, or complaints to feedback@openslx.org +# +# General information about OpenSLX can be found at http://openslx.org/ +# ----------------------------------------------------------------------------- +# +# Trapped cleanup functions +# +# ----------------------------------------------------------------------------- + +__init () { + # run 'cleanexit' when CTRL-c is pressed, an abrupt program termination or exit happens + trap cleanexit SIGINT SIGTERM +} + +# main cleaner function +cleanexit() { + trap '' SIGINT SIGTERM # from now on, ignore INT and TERM + unset_quiet # needed to get trap functioning correctly + pwarning "SIGINT/SIGTERM triggered - cleaning up ..." + # unmount and remove the temporary chroot stuff + pinfo "Calling chroot_cleanup_mounts ..." + chroot_cleanup_mounts + # TODO vmware etc/vmware/config stuff here, if it is still needed + exit 1 # perhaps a better exit code? +} + diff --git a/core/includes/distribution.inc b/core/includes/distribution.inc new file mode 100644 index 00000000..57bafdf2 --- /dev/null +++ b/core/includes/distribution.inc @@ -0,0 +1,53 @@ + +detect_distribution () { + # Set up distribution and package management + [ -z "$SYS_DISTRIBUTION" ] && perror "SYS_DISTRIBUTION not set (should be done by helper/distribution.inc)" + # Then determine packet manager + case "$SYS_DISTRIBUTION" in + ubuntu) + PACKET_MANAGER="apt" + PACKET_HANDLER="dpkg" + detect_ubuntu_lts + ;; + debian) + PACKET_MANAGER="apt" + PACKET_HANDLER="dpkg" + ;; + opensuse) + PACKET_MANAGER="zypper" + PACKET_HANDLER="rpm" + ;; + centos|scientific|fedora) + PACKET_MANAGER="yum" + PACKET_HANDLER="rpm" + ;; + *) + perror "Unknown Distribution: $SYS_DISTRIBUTION - Please specify its packet manager in core/bin/setup_target" + ;; + esac + # Get version - we mangle this quite a bit. first make sure it has no spaces, then split version at period (.), underscore (_) and dash (-) + SYS_VERSION=$(lsb_release -rs | tolower) + SYS_CODENAME=$(lsb_release -c|cut -f 2) # Codename: eg. Ubuntu raring, openSuse: Dartmouth etc. + local VERSION=$(echo $SYS_VERSION | sed -r 's/\s//g;s/[\._]/ /g;s/-//g') + local STRTMP="" + PRINT_SYS_VERSIONS="*.conf.$SYS_DISTRIBUTION" + SYS_VERSIONS="$SYS_DISTRIBUTION" + for PART in $VERSION; do + [ -z "$PART" ] && continue + STRTMP+=".$PART" + SYS_VERSIONS="${SYS_DISTRIBUTION}${STRTMP} $SYS_VERSIONS" + PRINT_SYS_VERSIONS="*.conf.${SYS_DISTRIBUTION}${STRTMP} $PRINT_SYS_VERSIONS" + done + pinfo "Config source order: *.conf first, then the first one of these (if found)" + pinfo "$PRINT_SYS_VERSIONS" +} + +detect_ubuntu_lts () { + local TMP=$(dpkg -S /usr/bin/Xorg) + [[ "$TMP" == xserver-xorg* ]] || perror "Could not detect xserver package version (returned: $TMP)" + TMP=${TMP%: *} + TMP=${TMP#xserver-xorg-core} + pinfo "Ubuntu LTS Xorg suffix: $TMP" + UBUNTU_XORG_PKG_SUFFIX="$TMP" +} + diff --git a/core/includes/downloader.inc b/core/includes/downloader.inc new file mode 100644 index 00000000..7ec8d872 --- /dev/null +++ b/core/includes/downloader.inc @@ -0,0 +1,80 @@ +# helper functions for downloading files or packages + +# download a file. usage: +# download FROM [TO] +# 1. download "http://example.com/something.tar.gz" +# 2. download "http://example.com/something.tar.gz" "somename.tar.gz" +download () { + [ $# -lt 1 -o $# -gt 2 ] && perror "download called with $# arguments, need 1 or 2" + [ -z "$1" ] && perror "download: URL empty." + local URL="$1" + # If a sourceforge mirror is set in ./config, try to use it + if [ -n "$sourceforge_mirror" ] && [[ "$URL" != *use_mirror* ]] && [[ "$URL" == *sourceforge.net* || "$URL" == *.sf.net* ]]; then + if [[ "$URL" == *\?* ]]; then + URL+="&use_mirror=$sourceforge_mirror" + else + URL+="?use_mirror=$sourceforge_mirror" + fi + fi + if [ $# -eq 2 ]; then + [ -z "$2" ] && perror "download: target file name given but empty" + pinfo "Downloading $2 from '$URL'...." + wget -O "$2" "$URL" + local RET=$? + else + pinfo "Downloading '$URL'...." + wget "$URL" + local RET=$? + fi + [ "x$RET" != "x0" ] && perror "downloading $URL failed, wget returned exit code $RET" +} + +# download a file and untar it. usage: +# download_untar FROM TO_DIR [TEMPFILE] +# 1. download_untar "http://example.com/something.tar.gz" "src/" +# 2. download_untar "http://example.com/something.tar.gz" "src/" "temporary_name.tar.gz" +download_untar () { + [ $# -lt 2 -o $# -gt 3 ] && perror "download_untar called with $# arguments, need 2 or 3" + local URL="$1" + local DEST="$2" + if [ $# -eq 2 ]; then + local TMPFILE=dltmp.$(basename "$URL") + else + local TMPFILE="$3" + fi + pdebug "$URL ---> $TMPFILE" + download "$URL" "$TMPFILE" + mkdir -p "$DEST" + pinfo "Unpacking to '$DEST'..." + tar xf "$TMPFILE" -C "${DEST}/" + local RET=$? + [ "x$RET" != "x0" ] && perror "could not untar $TMPFILE to $DEST (tar returned $RET)" + unlink "$TMPFILE" +} + +# Download first param URL to second param path, +# iff the local file does not exist or is empty. +# Return 1 if remote file does not exist, 0 otherwise +download_if_empty() { + [ $# -ne 2 ] && perror "download_if_empty: want 2 args, got $# ($@)" + local SRC="$1" + local DST="$2" + [ -s "$DST" ] && pdebug "Not downloading $DST: already there." && return 0 + pdebug "Downloading $DST" + [ -e "$DST" ] && unlink "$DST" + local DSTDIR="$(dirname "$DST")" + pdebug "wgetting $SRC" + local TMP=$(mktemp) + wget -O "$TMP" "$SRC" + local RET=$? + if [ "x$RET" = "x0" -a -s "$TMP" ]; then + mkdir -p "$DSTDIR" + mv "$TMP" "$DST" || perror "Could not mv '$TMP' '$DST'" + return 0 + fi + pdebug "WGET failed" + rmdir "$DSTDIR" + unlink "$TMP" + return 1 +} + diff --git a/core/includes/helper/README.helper b/core/includes/helper/README.helper new file mode 100644 index 00000000..ebe242a0 --- /dev/null +++ b/core/includes/helper/README.helper @@ -0,0 +1,2 @@ +Put your helper units here +Naming convention is <unitname>.inc diff --git a/core/includes/helper/distribution.inc b/core/includes/helper/distribution.inc new file mode 100644 index 00000000..f26ca46c --- /dev/null +++ b/core/includes/helper/distribution.inc @@ -0,0 +1,4 @@ +# Get simple distribution name +# This is also being used via ./openslx, not only via ./mltk, +# so it has not been moved to remote/includes. +SYS_DISTRIBUTION=$(lsb_release -is | tr '[A-Z]' '[a-z]' | sed -r 's/[^a-z0-9]//g;s/project$//g;s/scientificsl$/scientific/g') diff --git a/core/includes/helper/fileutil.inc b/core/includes/helper/fileutil.inc new file mode 100644 index 00000000..9dc2c07b --- /dev/null +++ b/core/includes/helper/fileutil.inc @@ -0,0 +1,39 @@ +# +# copy list of files using tar +tarcopy () { + if [ $# -gt 0 -a "x$1" == "x-i" ]; then + shift + local IGNORE_ERROR="--ignore-failed-read" + else + local IGNORE_ERROR= + fi + if [ "x$IGNORE_TAR_ERROR" != "x" ]; then + unset IGNORE_TAR_ERROR + IGNORE_ERROR="--ignore-failed-read" + fi + [ $# -ne 2 ] && perror "Sanity check failed: tarcopy needs exactly two params, but $# were given." + local FROM=$(trim "$1") + local TO=$(trim "$2") + if [ -z "$FROM" ]; then + pwarning "tarcopy called with empty input list (dest was '$TO')" + return + fi + local SHORT=$FROM + [ ${#SHORT} -gt 30 ] && SHORT=$(echo "$SHORT" | sed ':a;N;$!ba;s/\n/ /g' | cut -c-25)...$(echo "$SHORT" | cut -c$[${#SHORT} - 4]-) + [ -z "$TO" ] && perror "tarcopy called with empty destination." + [ ! -d "$TO" ] && { mkdir -p "$TO" || perror "could not create destination "$TO" for tar-copy."; } + # TODO count files copied? would remove the need to do it everywhere :) + tar $IGNORE_ERROR -cpP $FROM | tar -xp -C "$TO" \ + --transform 's,^/lib/udev/rules.d,/usr/lib/udev/rules.d,' \ + 2> /dev/null + local PS=(${PIPESTATUS[*]}) + [ "x$IGNORE_ERROR" == "x" -a "x${PS[0]}" != "x0" ] && perror "packing-part of tar-copy from '$SHORT' to '$TO' failed. (${PS[0]})" + [ "x${PS[1]}" != "x0" ] && perror "unpacking-part of tar-copy from '$SHORT' to '$TO' failed. (${PS[1]})" +} + +# usage: CANONICALIZED_PATH=$(canonalize <path>) +# usage with relative path requires you to be in the correct directory. +canonicalize() { + cd -P -- "$(dirname -- "$1")" && printf '%s\n' "$(pwd -P)/$(basename -- "$1")" +} + diff --git a/core/includes/helper/logging.inc b/core/includes/helper/logging.inc new file mode 100644 index 00000000..1e1f5996 --- /dev/null +++ b/core/includes/helper/logging.inc @@ -0,0 +1,53 @@ +[ -n "$MLTK_QUIET" ] && return 0 + +# This module is an exception, it doesn't use __init but runs commands directly... +# Otherwise we cannot ensure that other module's init functions won't run first +# and mess up the fds + +set_quiet () { + if [ "x$MLTK_QUIET" != "x1" ]; then + exec 6>&1 > "$LOG_DIR/stdout.log" + exec 7>&2 2> "$LOG_DIR/stderr.log" + MLTK_QUIET=1 + fi +} + +unset_quiet () { + if [ "x$MLTK_QUIET" == "x1" ]; then + exec 1>&6 6>&- + exec 2>&7 7>&- + exec 6>&2 + MLTK_QUIET=0 + fi +} + + +pinfo () { + echo -e "\033[38;5;10m[info]\033[0m $TOOL_STR $@" >&6 +} + +perror () { + echo -e "\033[38;5;9m[error]\033[0m $TOOL_STR $@" >&6 + qnd_exit +} + +pwarning () { + echo -e "\033[38;5;11m[warning]\033[0m $TOOL_STR $@" >&6 +} + +pdebug () { + if [ "x$MLTK_QUIET" = "x1" ]; then + echo -e "[DEBUG] $TOOL_STR $@" >&2 + else + echo -e "\033[38;5;6m[debug]\033[0m $TOOL_STR $@" >&6 + fi +} + +exec 6>&2 +exec 8>&0 # This redirection is used for chroot debugging + +MLTK_QUIET=0 +declare -rg LOG_DIR=${ROOT_DIR}/var/log +mkdir -p "$LOG_DIR" || perror "Could not create '$LOG_DIR'" +true + diff --git a/core/includes/helper/string.inc b/core/includes/helper/string.inc new file mode 100644 index 00000000..077f1719 --- /dev/null +++ b/core/includes/helper/string.inc @@ -0,0 +1,31 @@ + +# usage: VAR=$(trim " string ") +trim() { + local var=$1 + var="${var#"${var%%[![:space:]]*}"}" # remove leading whitespace characters + var="${var%"${var##*[![:space:]]}"}" # remove trailing whitespace characters + echo -n "$var" +} + +# Inline version of trim, use when piping +itrim () { + sed -r 's/^\s+//g;s/\s+$//g' +} + +escape_search() { + sed -e 's/[]\/()$*.^|[]/\\&/g' + +} + +escape_replace() { + sed -e 's/[\/&]/\\&/g' +} + +tolower () { + tr '[A-Z]' '[a-z]' +} + +toupper () { + tr '[a-z]' '[A-Z]' +} + diff --git a/core/includes/kernel.inc b/core/includes/kernel.inc new file mode 100644 index 00000000..671dca42 --- /dev/null +++ b/core/includes/kernel.inc @@ -0,0 +1,218 @@ +# +# Common functions to copy kernel related files +# +############################################################ +# +# +# copies kernel modules as given in the module config file +# * depends on 'depmod' +# * requires REQUIRED_KERNEL_MODULES to be set. +# (entries must be a relative path to /lib/modules/<SYSTEM_KERNEL_LONG>) +# +# ex: for /lib/modules/3.2.0/kernel/fs/nfs/nfs.ko +# must be given as kernel/fs/nfs/nfs.ko +# + +# set global KERNEL_BASE_DIR as in the directory containing lib/modules and lib/firmware +# for system kernel, that is "/" and for an openslx kernel KERNEL_BUILD_DIR +KERNEL_BASE_DIR="" + +get_kernel_version () { + pinfo "Running fallback get_kernel_version()" + # Running system kernel version + if [ -z "$SYSTEM_KERNEL_LONG" ]; then + declare -rg SYSTEM_KERNEL_LONG=$(uname -r) + declare -rg SYSTEM_KERNEL_SHORT=$(grep -o -E '^[0-9\.]+' <<<$SYSTEM_KERNEL_LONG) + fi +} + +check_kernel_base_dir () { + + # check if KERNEL_BASE_DIR was set, if not we don't know + # whether kernel-openslx or kernel-system has been built + # and therefore not were to look for kernel modules, + # firmware and the kernel itself + + if [ -z "${KERNEL_BASE_DIR}" ]; then + # this is bad, abort + perror "KERNEL_BASE_DIR is not set. The kernel module did not run properly" + else + pinfo "KERNEL_BASE_DIR is '$KERNEL_BASE_DIR'" + fi + +} + +copy_kernel_modules () { + pinfo "Copying kernel modules for kernel ${SYSTEM_KERNEL_LONG}..." + [ -z "${REQUIRED_KERNEL_MODULES}" ] && perror "REQUIRED_KERNEL_MODULES is empty. Check your config file." + [ -z "${KERNEL_HEADERS_DIR}" ] && perror "KERNEL_HEADERS_DIR is empty. Kernel headers appears to be missing." + + + check_kernel_base_dir + + local OLD_DIR="$(pwd)" + + # + # process modules list + # + # search for modules in KERNEL_BASE_DIR + cd "${KERNEL_BASE_DIR}" || perror "Could not cd to ${KERNEL_BASE_DIR}" + + local KERNEL_MODULES_DIR="lib/modules/${TARGET_KERNEL_LONG}" + local KERNEL_MODULES_LIST="" + local REQUIRED_KERNEL_MODULES_EXPANDED="" + local KERNEL_MODULE="" + local KERNEL_MODULE_PATH="" + local ELEM="" + + # Do some fancy stuff to allow wildcards etc. in required kernel modules. + cd "${KERNEL_MODULES_DIR}" + for KERNEL_MODULE in ${REQUIRED_KERNEL_MODULES}; do + for ELEM in $KERNEL_MODULE; do + echo $ELEM | grep '\*' && pwarning "Could not expand '$ELEM'." && continue + REQUIRED_KERNEL_MODULES_EXPANDED+=" $ELEM" + done + done + cd - 2>/dev/null + pinfo "Expanded the list of $(echo "$REQUIRED_KERNEL_MODULES" | wc -w) required kernel modules to $(echo "$REQUIRED_KERNEL_MODULES_EXPANDED" | wc -w)" + + # + # now loop over given modules and locate them + # + for KERNEL_MODULE in ${REQUIRED_KERNEL_MODULES_EXPANDED}; do + local KERNEL_MODULE_PATH="${KERNEL_MODULES_DIR}/${KERNEL_MODULE}" + if grep "^${KERNEL_MODULE}$" "${KERNEL_BASE_DIR}/${KERNEL_MODULES_DIR}/modules.builtin" >/dev/null; then + pdebug "Already built-in ${KERNEL_MODULE}." + elif [ -e "${KERNEL_MODULE_PATH}" ]; then + pdebug "Copying '${KERNEL_MODULE_PATH}'" + KERNEL_MODULES_LIST+=" ${KERNEL_MODULE_PATH}" + else + pwarning "Module ${KERNEL_MODULE} not found. Skipping. (might cause problems on certain clients!)" + continue + fi + + # check for dependencies + local DEPS=$(grep "${KERNEL_MODULE}:" "${KERNEL_BASE_DIR}/${KERNEL_MODULES_DIR}/modules.dep" | cut -d ":" -f2-) + if [ ! -z "$DEPS" ]; then + for DEP in $DEPS; do + pdebug "Adding dep: ${KERNEL_MODULES_DIR}/$DEP" + KERNEL_MODULES_LIST+=" ${KERNEL_MODULES_DIR}/$DEP" + done + else + pdebug "${KERNEL_MODULE} has no dependencies." + fi + done + + if [ ! -z "${KERNEL_MODULES_LIST}" ]; then + local COUNT=$(echo "${KERNEL_MODULES_LIST}" | wc -w) + pinfo "Copying $COUNT modules to target directory." + tarcopy "${KERNEL_MODULES_LIST}" "${TARGET_BUILD_DIR}" + fi + + # + # generate modules map files + # + # first strip modules.order of all the modules we don't use + cat "${KERNEL_MODULES_DIR}/modules.order" | grep -E "$(echo ${REQUIRED_KERNEL_MODULES} | tr '\ ' '|' | tr '_' '.' | tr '-' '.')" \ + >> "${TARGET_BUILD_DIR}/${KERNEL_MODULES_DIR}/modules.order" + # copy list of builtin kernel modules + cp "${KERNEL_MODULES_DIR}/modules.builtin" "${TARGET_BUILD_DIR}/${KERNEL_MODULES_DIR}" + # with modules.order and modules.builtin, we can run depmod for the rest of the files + depmod -b "${TARGET_BUILD_DIR}" -a "${TARGET_KERNEL_LONG}" + + # go back to wherever we were + cd "${OLD_DIR}" || perror "Could not cd back to ${OLD_DIR}." +} + +copy_firmware () { + + pinfo "Copying firmware for kernel ${SYSTEM_KERNEL_LONG}..." + [ -z "${REQUIRED_FIRMWARE}" ] && perror "REQUIRED_FIRMWARE is empty. Check your config file." + + check_kernel_base_dir + + local OLD_DIR=$(pwd) + # + # process firmware list + # + cd "${KERNEL_BASE_DIR}" || perror "Could not cd to ${KERNEL_BASE_DIR}" + local FIRMWARE_DIR="lib/firmware" + local FIRMWARE_SYSTEM_LIST="" + local FIRMWARE_BUILD_LIST="" + for FIRMWARE in ${REQUIRED_FIRMWARE}; do + local FOUND=0 + # check for firmware in the build directory of the kernel + for CANDIDATE in "${FIRMWARE_DIR}/${FIRMWARE}" "${FIRMWARE_DIR}/${TARGET_KERNEL_LONG}/${FIRMWARE}"; do + if [ -e "${CANDIDATE}" ]; then + pdebug "Copying from kernel base dir ('$KERNEL_BASE_DIR'): '${CANDIDATE}'" + FIRMWARE_BUILD_LIST+=" ${CANDIDATE}" + FOUND=1 + fi + done + + # dont look under / if KERNEL_BASE_DIR is already / + #if [ "x${KERNEL_BASE_DIR}" == "x/" ]; then + # [ $FOUND -ne 1 ] && pwarning "Neither '${FIRMWARE_DIR}/${FIRMWARE}' nor '${FIRMWARE_DIR}/${TARGET_KERNEL_LONG}/${FIRMWARE}' found on the system" + # continue + #fi + + # if we didn't found it in the kernel build directory, check for firmware in the system firmware directory + #if [ $FOUND -ne 1 ]; then + for CANDIDATE in "/${FIRMWARE_DIR}/${FIRMWARE}" "/${FIRMWARE_DIR}/${SYSTEM_KERNEL_LONG}/${FIRMWARE}"; do + if [ -e "${CANDIDATE}" ]; then + if [ $(echo "${CANDIDATE}" | grep -c "${SYSTEM_KERNEL_LONG}") -eq 0 ]; then + pdebug "Copying from system: '${CANDIDATE}'" + FIRMWARE_SYSTEM_LIST+=" ${CANDIDATE}" + else + pdebug "Copying from system: '${CANDIDATE}' to ${FIRMWARE_DIR}/${TARGET_KERNEL_LONG}/${FIRMWARE}" + FIRMWARE_SYSTEM_LIST+=" /${FIRMWARE_DIR}/${SYSTEM_KERNEL_LONG}/${FIRMWARE}" + fi + FOUND=1 + fi + done + #fi + + [ $FOUND -ne 1 ] && pwarning "Neither '${FIRMWARE_DIR}/${FIRMWARE}' nor '${FIRMWARE_DIR}/${TARGET_KERNEL_LONG}/${FIRMWARE}' "\ + " was found on the system. Skipping. (might cause problems on certain clients!)" + done + + for LIST in "${FIRMWARE_SYSTEM_LIST}" "${FIRMWARE_BUILD_LIST}"; do + [ -z "${LIST}" ] && continue + echo "${LIST}" >> "${MODULE_BUILD_DIR}/fwlist" + local COUNT=$(echo "${LIST}" | wc -w) + pinfo "Copying $COUNT firmware to target directory." + tarcopy "${LIST}" "${TARGET_BUILD_DIR}" + done + + # only for kernel-openslx + # post-process to fix the path of the firmwares found on the system unter /lib/firmware/$(uname -r) + # which have to be copied to /lib/firmware/${TARGET_KERNEL_LONG} + if [ "x${KERNEL_BASE_DIR}" != "x/" ]; then + if [ -d "${TARGET_BUILD_DIR}/lib/firmware/${SYSTEM_KERNEL_LONG}" ]; then + mkdir -p "${TARGET_BUILD_DIR}/lib/firmware/${TARGET_KERNEL_LONG}/" + cd "${TARGET_BUILD_DIR}/lib/firmware/${SYSTEM_KERNEL_LONG}" || perror "old kernel but no old kernel" + tarcopy "$(ls)" "${TARGET_BUILD_DIR}/lib/firmware/${TARGET_KERNEL_LONG}/" + cd - + rm -r "${TARGET_BUILD_DIR}/lib/firmware/${SYSTEM_KERNEL_LONG}" || perror "something went very wrong..." + else + pdebug "No ${TARGET_BUILD_DIR}/lib/firmware/${SYSTEM_KERNEL_LONG} directory, skipping the merge." + fi + fi + + cd "${OLD_DIR}" || perror "Could not cd back to ${OLD_DIR}." +} + +copy_kernel () { + check_kernel_base_dir + + local TOOL_STR="$TOOL_STR copy_kernel:" + local KERNEL_DIR="${ROOT_DIR}/var/builds/kernel" + + [ -d "${KERNEL_DIR}" ] || mkdir -p "${KERNEL_DIR}" + + cp "${ROOT_DIR}/tmp/work/kernel/build/kernel" "${KERNEL_DIR}" || perror "Could not copy '${ROOT_DIR}/tmp/work/kernel/build/kernel' to '${KERNEL_DIR}'" + + # kernel has 0600 perms since ubuntu 14.04, change that once we copied it + chmod +r "${KERNEL_DIR}/kernel" +} + diff --git a/core/includes/keyvalueutil.inc b/core/includes/keyvalueutil.inc new file mode 100644 index 00000000..a0a89db7 --- /dev/null +++ b/core/includes/keyvalueutil.inc @@ -0,0 +1,32 @@ +# Helper file for managing key-value containing files +# There are some specialized conveinience functions here first +# that mostly just pass a predefined filename to the genric function +# at the end + +# Add the given environment variable to /etc/environment +add_env () { + [ $# -ne 2 ] && perror "Usage: $0 'key' 'value'" + [ -z "$1" ] && perror "$0: Empty key!" + add_key_value "/etc/environment" "$1" "$2" +} + +# +# Adds the given key-value-pair to a given file +# The file will be relative to the current target build dir, +# even if it starts with a slash. +# Will perror if the key already exists with a different value +add_key_value () { + [ $# -ne 3 ] && perror "Usage: $0 'file' 'key' 'value'" + [ -z "$TARGET_BUILD_DIR" ] && perror "No TARGET_BUILD_DIR set. Aborting for safety." + local FILE="$TARGET_BUILD_DIR/$1" + local KEY="$2" + local VALUE="$(echo "$3" | sed "s/'/\\\\'/g")" # \\\\\\\\\\\\\\\\\\\\\\ßß + if [ -s "$FILE" ]; then + local CURRENT="$(grep -E "^\s*$KEY=.*$" "$FILE" | awk -F '=' '{$1=""; printf $0}' | itrim)" + [ -n "$CURRENT" -a "'$VALUE'" != "$CURRENT" ] && perror "Cannot set $KEY to '$VALUE' as it is already set to $CURRENT" + [ -n "$CURRENT" ] && return 0 + fi + mkdir -p "$(dirname "$FILE")" + echo "$KEY='$VALUE'" >> "$FILE" +} + diff --git a/core/includes/packagemanager.inc b/core/includes/packagemanager.inc new file mode 100644 index 00000000..9fcb4b87 --- /dev/null +++ b/core/includes/packagemanager.inc @@ -0,0 +1,187 @@ +#!/bin/bash +# +# get all files of required packages by a module +# +# Usage: +# list_content_packages +# - lists all files/directories in REQUIRED_CONTENT_PACKAGES +# list_content_packages --files +# - lists all files in REQUIRED_CONTENT_PACKAGES +# list_content_packages --dirs +# - lists all dirs in REQUIRED_CONTENT_PACKAGES +# +# NOTE: additional packages needed to be listed can be given +# through the environment variable EXTRA_PACKAGES + +list_content_packages() { + [ -z "$REQUIRED_CONTENT_PACKAGES" ] && pinfo "No required packages for $TOOL" && return 1 + [ $# -gt 2 ] && perror "'list_content_packages' accepts only 1 or no args. $# given." + local PACKAGE="" + for PACKAGE in $REQUIRED_CONTENT_PACKAGES $EXTRA_PACKAGES; do + list_content_package $1 $PACKAGE + done + +} +list_content_package(){ + #[ -z "$EXTRA_PACKAGES" ] || pinfo "Listing additional packages: $EXTRA_PACKAGES" + [ $# -gt 2 ] && perror "'list_content_package' accepts max 2 args. $# given." + local OP="-e" + case "$1" in + "--files") + OP="-f" + ;; + "--dirs") + OP="-d" + ;; + "") + OP="-e" + ;; + *) + perror "'list_content_packages' invalid argument: $1" + ;; + esac + local PACKAGE="$2" + local OPTIONAL="$(echo "$PACKAGE" | cut -c 1)" + [ "x$OPTIONAL" = "x@" ] && PACKAGE="$(echo "$PACKAGE" | cut -c 2-)" + local FILES="" + if [ "$PACKET_HANDLER" = "dpkg" ]; then + PACKAGECOMMAND="dpkg -L" + elif [ "$PACKET_HANDLER" = "rpm" ]; then + PACKAGECOMMAND="rpm -ql" + fi + + if [ -n "$REQUIRED_PACKET_FILES_BLACKLIST" ]; then + FILES="$($PACKAGECOMMAND "$PACKAGE" | grep "^/" | \ + grep -v "$REQUIRED_PACKET_FILES_BLACKLIST" | \ + grep -v -E 'share/(man|doc)|/var/run|/var/log|/etc/init\.d'; \ + echo ":###:${PIPESTATUS[0]}")" + else + FILES="$($PACKAGECOMMAND "$PACKAGE" | grep "^/" | grep -v -E 'share/(man|doc)|/var/run|/var/log|/etc/init\.d'; echo ":###:${PIPESTATUS[0]}")" + fi +# FILES="$(rpm -ql "$PACKAGE" | grep "^/" | grep -v -E 'share/(man|doc)|/var/run|/var/log'; echo ":###:${PIPESTATUS[0]}")" + + # ugly hack to get our return value + local LPRET=$(echo "$FILES" | awk -F ':###:' '{printf $2}') + FILES=$(echo "$FILES" | awk -F ':###:' '{print $1}') + if [ "x$LPRET" != "x0" -a "x$OPTIONAL" != "x@" ]; then + pdebug "FILES: '$FILES'" + perror "dpkg/rpm exited with code '$LPRET' for required package ${PACKAGE}." + fi + [ "x$LPRET" != "x0" ] && pwarning "dpkg/rpm exited with code '$LPRET' for optional package ${PACKAGE}." && continue + [ -z "$FILES" ] && pwarning "list_packet_files empty for packet ${PACKAGE}." && continue + pdebug "Packet $PACKAGE has $(echo $FILES | wc -w) files..." + for FILE in $FILES; do + [ "$OP" "$FILE" ] && echo "$FILE" + done +} +# +# Convenience function +# +list_packet_files() { + list_content_packages --files +} + +# +# Convenience function +# +# install all dependencies of a module +# goes through all package as given in the variable REQUIRED_INSTALLED_PACKAGES +install_dependencies() { + [ -z "$REQUIRED_INSTALLED_PACKAGES" ] && return + install_packages "$REQUIRED_INSTALLED_PACKAGES" +} + +# +# install given packet through system's packet manager +# uses PACKET_HANDLER as determined in helper/system.inc +# +install_packages() { + [ $# -eq 0 ] && perror "Sanity check failed: no argument given to install_package" + local PACKAGE_LIST="$@" + local INSTALLED_PACKAGES="" + local LRET="" + + for PKG in ${PACKAGE_LIST}; do + # check if installed: + # this is done mainly for performance reasons, even though + # reinstalling all packages is a valid possibility too... + if [ "x$PACKET_HANDLER" == "xdpkg" ]; then + dpkg -l ${PKG} > /dev/null 2>&1 + LRET=$? + elif [ "x$PACKET_HANDLER" == "xrpm" ]; then + rpm -ql "${PKG}" > /dev/null 2>&1 + LRET=$? + else + perror "No packet manager / handler determined, this should not happen!" + fi + + if [ "x$LRET" == "x0" ]; then + # check if it is completly installed, not just leftover configuration files + if [ "x$PACKET_HANDLER" == "xdpkg" ]; then + local INSTALL_STATUS=$(dpkg -l "${PKG}" | grep "${PKG}" | cut -c1-2) + if [[ $INSTALL_STATUS != "ii" ]]; then + pinfo "$PKG is either missing or a meta-package! Installing it to be sure..." + install_package "${PKG}" + else + # package installed + pdebug "${PKG} installed!" + fi + elif [ "x$PACKET_HANDLER" == "xrpm" ]; then + # TODO: blindly install again for now + rpm -q "${PKG}" >/dev/null 2>&1 || install_package "${PKG}" + fi + else + # package not installed + pdebug "$PKG not installed!" + install_package $PKG + fi + done + [ ! -z "$INSTALLED_PACKAGES" ] && pinfo "New packages installed: ${INSTALLED_PACKAGES}" +} + +# +# install individual package depending on package manager +# +install_package() { + if [ "$#" -ne 1 ]; then + perror "Only call install_package with one argument!" + fi + + if [ "x$PACKET_MANAGER" == "xapt" ]; then + apt-get install -y ${PKG} + local IRET=$? + if [ "x$IRET" == "x0" ]; then + # $PGK was installed successfully + INSTALLED_PACKAGES+="$PKG " + else + # PKG was not installed + # TODO error handling + perror "install_packages: apt-get failed with '$IRET' for package '$PKG'" + fi + elif [ "x$PACKET_MANAGER" == "xzypper" ]; then + zypper --no-refresh --non-interactive install --force-resolution ${PKG} + local IRET=$? + if [ "x$IRET" == "x0" ]; then + # $PGK was installed successfully + INSTALLED_PACKAGES+="$PKG " + else + # PKG was not installed + # TODO error handling + perror "install_packages: zypper failed with '$IRET' for package '$PKG'" + fi + elif [ "x$PACKET_MANAGER" == "xyum" ]; then + yum --assumeyes install ${PKG} + local IRET=$? + if [ "x$IRET" == "x0" ]; then + # $PGK was installed successfully + INSTALLED_PACKAGES+="$PKG " + else + # PKG was not installed + # TODO error handling + perror "install_packages: yum failed with '$IRET' for package '$PKG'" + fi + else + perror "No packet manager determined, this should not happen!" + fi +} + diff --git a/core/includes/paths.inc b/core/includes/paths.inc new file mode 100644 index 00000000..928ca964 --- /dev/null +++ b/core/includes/paths.inc @@ -0,0 +1,31 @@ +# +# This include tries to determine system paths needed by mltk-modules +# The idea here is to have a central place to determine paths required +# by different modules instead of each module determining them on its own. +# + +__init () { + # Location of PAM-modules + for CANDIDATE in $(strings "$(ldd "$(which login)" | grep libpam.so | head -n 1 | awk '{print $3}')" | grep /lib); do + [ -f "$CANDIDATE/pam_unix.so" ] && declare -rg SYS_PAM_MODULES_PATH="$CANDIDATE" && break + done + + [ -z "$SYS_PAM_MODULES_PATH" ] && perror "Failed to find pam_unix.so on this system." + + # + # Figure out if we have split usr + if [ -L "/bin" -a -L "/lib" ]; then + declare -rg USR_SPLIT="no" + else + declare -rg USR_SPLIT="yes" + fi + pdebug "/bin and /lib are split from /usr/bin and /usr/lib (they are not symlinks): $USR_SPLIT" + + SYS_LIB_PATHS="" + for DIR in /lib /lib32 /lib64 /usr/lib /usr/lib32 /usr/lib64; do + [ -d "$DIR" -a ! -L "$DIR" ] && SYS_LIB_PATHS+=" $DIR" + done + declare -rg SYS_LIB_PATHS=$SYS_LIB_PATHS + pdebug "System lib paths: $SYS_LIB_PATHS" +} + diff --git a/core/includes/qt.inc b/core/includes/qt.inc new file mode 100644 index 00000000..2b13c1f3 --- /dev/null +++ b/core/includes/qt.inc @@ -0,0 +1,30 @@ +activate_qt() { + [ $# -eq 1 ] || perror "activate_qt() requires the version as only argument, $# given." + local QT_VERSION="$1" + # Handle environment + export QT_SELECT="qt${QT_VERSION}" + QTDIR="/usr/share/qt${QT_VERSION}" + [ -d "$QTDIR" ] && export QTDIR + # Handle qt chooser + [ -d "/usr/share/qtchooser" ] || return 0 # No qtchooser found, hope for the best... + local QT_DEFAULT="$(readlink -f /usr/share/qtchooser/default.conf)" + if [ -e "$QT_DEFAULT" ]; then + [[ "$(basename "$QT_DEFAULT")" =~ ^qt$QT_VERSION.*\.conf$ ]] || perror "Something with QT4 is bad. Check '/usr/share/qtchooser'" + else + # no default active, try to set it + local QT_CANDIDATES="$(find "/usr/share/qtchooser" -name "qt$QT_VERSION*" | grep -E "$ARCHREGEX")" + [ -z "$QT_CANDIDATES" ] && QT_CANDIDATES="$(find "/usr/share/qtchooser" -name "qt$QT_VERSION*")" + local QTS="$(echo "$QT_CANDIDATES" | wc -w)" + if [ "$QTS" -eq 1 ]; then + # we found it, lets activate it and hope for the best + ln -sf "/usr/share/qtchooser/$QT_CANDIDATES" "/usr/share/qtchooser/default.conf" \ + || perror "Could not link 'default.conf' to '/usr/share/qtchooser/$QT_CANDIDATES'" + elif [ "$QTS" -eq 0 ]; then + perror "No qt ${QT_VERSION} config found in /usr/share/qtchooser" + else + # here we have the case with different qt4 files or none at all + # in both cases, there is a bigger problem we shouldn't try to guess which one is correct + perror "More than one qt ${QT_VERSION} version found in '/usr/share/qtchooser'. The configuration is messy. Fix it first." + fi + fi +} diff --git a/core/includes/system.inc b/core/includes/system.inc new file mode 100644 index 00000000..ea953d69 --- /dev/null +++ b/core/includes/system.inc @@ -0,0 +1,37 @@ +# Helper to determine various system information + +__init () { + # determine architecture triplet from the path of libc needed by the executing shell. + # please try not to use this to find/fetch libs in /lib or /usr/lib. + # Use ARCH_LIB_DIR, like "$ARCH_LIB_DIR/somefile.so" or "/usr$ARCH_LIB_DIR/somefile.so" + ARCH_TRIPLET=$(ldd $SHELL|grep "libc.so" | awk -F "/" '{print $3}') + [[ $ARCH_TRIPLET == *\(*\) ]] && ARCH_TRIPLET="" + + ARCH_LIB_DIR=$(ldd $SHELL | grep "libc.so" | sed -r 's#^.*(/lib.*)/libc.so.*$#\1#g') + [ -z "$ARCH_LIB_DIR" -o ! -d "$ARCH_LIB_DIR" ] && perror "Could not determine arch dependent lib dir (where libc.so resides)" + + # determine number of CPU cores + declare -rg CPU_CORES=$(cat /proc/cpuinfo | grep processor | wc -l) + export MAKEFLAGS="-j$CPU_CORES" + + # Determine if we have lib64 + if [ "$(uname -m)x" = "x86_64x" ]; then + # Setting LIB64, as openSuse differentiates but Ubuntu does not: + case $SYS_DISTRIBUTION in + ubuntu | debian) LIB64="lib" ;; + opensuse | fedora | centos) LIB64="lib64" ;; + *) perror "Cannot set LIB64, SYS_DISTRIBUTION: $SYS_DISTRIBUTION unknown!" ;; + esac + AMD64_I386=amd64 + X86_64_I586=x86_64 + AMD64_X86=amd64 + ARCHREGEX="(amd64|x86[_-]64)" + else + LIB64="lib" + AMD64_I386=i386 + X86_64_I586=i586 + AMD64_X86=x86 + ARCHREGEX="(i[3456]86|x86[_-]32)" + fi +} + diff --git a/core/includes/useradd.inc b/core/includes/useradd.inc new file mode 100644 index 00000000..2beaaeae --- /dev/null +++ b/core/includes/useradd.inc @@ -0,0 +1,238 @@ +# This helper is stupid because it reimplements what useradd etc. do. +# They could do the job just fine by using the -R option - maybe change this +# dome day. +# + +# Add a user to the system +# +# Usage: +# either do ID_OF_REQUESTED_USER=$(add_user herbert) +# or ID_OF_USER=$(USER=herbert GROUP=somegroup PASSWORD=secret123 add_user) +# Valid params are: +# USER, GROUP, USERID, GROUPID, PASSWORD, USERHOME, USERSHELL +# defaults are no password, home=/nonexistent, usershell=/bin/false +# IDs will be generated in the range of 5-999 if not explicitly given +# TODO: Make it possible to pass a range of IDs if you don't want one <1000 but don't care about the exact ID + +declare -rg NAME_REGEX='^[a-z][-a-z0-9]*$' + +# Generate a UID for a given USERNAME. Return existing UID if possible, generate new one otherwise +generate_uid() +{ + [ $# -ne 1 ] && perror "generate_uid fail. want 1 argument." + [ -z "${_PASSWD}" ] && perror "passwd file not set." + local _UID=$(grep -E "^$1:[^:]*:[0-9]+:" "${_PASSWD}" | head -1 | awk -F ':' '{print $3}') + if [ "x${_UID}" = "x" ] + then + local _TRIES=0 + while [ ${_TRIES} -lt 50 ] + do + _TRIES=$[ ${_TRIES} + 1 ] + _UID=$[ 5 + $RANDOM % 900 ] + local _TEST=$(grep -E "^[^:]+:[^:]*:${_UID}:" "${_PASSWD}") + [ "x${_TEST}" = "x" ] && break + done + [ ${_TRIES} -ge 50 ] && perror "Generating a UID failed." + fi + echo ${_UID} +} + +# Echo gid of given user if existent, nothing otherwise +get_gid_for_user() +{ + [ $# -ne 1 ] && perror "get_gid_for_user fail. want 1 argument." + [ -z "${_PASSWD}" ] && perror "passwd file not set." + local _GID=$(grep -E "^$1:[^:]*:[^:]*:[0-9]+:" "${_PASSWD}" | head -1 | awk -F ':' '{print $4}') + echo ${_GID} +} + +# Echo group name of given gid, nothing if non-existent +get_group_for_gid() +{ + [ $# -ne 1 ] && perror "get_group_for_gid fail. want 1 argument." + [ -z "${_GROUP}" ] && perror "group file not set." + local _NAME=$(grep -E "^[^:]*:[^:]*:$1:" "${_GROUP}" | head -1 | awk -F ':' '{print $1}') + echo ${_NAME} +} + +# Generate a GID for a given GROUPNAME. Return existing GID if possible, generate new one otherwise +generate_gid() +{ + [ $# -ne 2 ] && perror "generate_gid fail. want 2 arguments." + [ -z "${_GROUP}" ] && perror "group file not set." + local _GID=$(grep -E "^$1:[^:]*:[0-9]+:" "${_GROUP}" | head -1 | awk -F ':' '{print $3}') + if [ "x${_GID}" = "x" ] + then + # group does not exist, try to create + local _TRIES=0 + _GID=$2 # try to use uid as gid if not taken yet + while [ ${_TRIES} -lt 50 ] + do + _TRIES=$[ ${_TRIES} + 1 ] + local _TEST=$(grep -E "^[^:]+:[^:]*:${_GID}:" "${_GROUP}") + [ "x${_TEST}" = "x" ] && break + _GID=$[ 5 + $RANDOM % 900 ] # using uid as gid not possible, generate new one + done + [ ${_TRIES} -ge 50 ] && perror "Generating a GID failed." + fi + echo ${_GID} +} + +add_user() { + [ -z "${TARGET_BUILD_DIR}" ] && perror "add_user: TARGET_BUILD_DIR not set" + if [ -z "${USER}" -a $# -eq 0 ] + then + pwarning " ** add_user usage **" + pwarning "add_user <username>" + pwarning "OR" + pwarning "USER=<username> [GROUP=<groupname>] [USERID=<userid>] [GROUPID=<groupid>] [USERHOME=<homedir>] [USERSHELL=<shell>] [PASSWORD=<pass>] add_user" + perror "Aborting, please fix your script." + fi + local _PASSWD=${TARGET_BUILD_DIR}/etc/passwd + local _GROUP=${TARGET_BUILD_DIR}/etc/group + local _SHADOW=${TARGET_BUILD_DIR}/etc/shadow + init_users_and_groups + [ ! -f "${_PASSWD}" ] && perror "add_user: password file does not exist in target system. (build base first)" + [ ! -f "${_GROUP}" ] && perror "add_user: group file does not exist in target system. (build base first)" + [ ! -f "${_SHADOW}" ] && perror "add_user: shadow file does not exist in target system. (build base first)" + if [ "x$1" != "x" ] + then + local USER=$1 + local GROUP="" + local USERID="" + local GROUPID="" + local USERHOME="" + local USERSHELL="" + local PASSWORD="" + fi + USER=$(trim "$USER") + if ! [[ $USER =~ $NAME_REGEX ]]; then + perror "Invalid username: $USER" + fi + [ -z "$GROUPID" ] && local GROUPID=$(get_gid_for_user "${USER}") + [ -z "$GROUP" -a -n "$GROUPID" ] && local GROUP=$(get_group_for_gid "${GROUPID}") + [ -z "$GROUP" ] && local GROUP=$USER + GROUP=$(trim "$GROUP") + if ! [[ $GROUP =~ $NAME_REGEX ]]; then + perror "Invalid group: $GROUP" + fi + [ "x$USERID" = "x" ] && local USERID=$(generate_uid "${USER}") + USERID=$(trim "$USERID") + [ "$USERID" -lt "0" -o "$USERID" -gt "65535" ] && perror "Invalid userid: $USERID" + [ -z "$GROUPID" ] && local GROUPID=$(generate_gid "${GROUP}" "${USERID}") + GROUPID=$(trim "$GROUPID") + [ "$GROUPID" -lt "0" -o "$GROUPID" -gt "65535" ] && perror "Invalid groupid: $GROUPID" + # all required variables have been set + # does the desired username already exist? if so, check if UID matches, otherwise bail out + local _UID=$(grep -E "^${USER}:[^:]*:[0-9]+:" "${_PASSWD}" | head -1 | awk -F ':' '{print $3}') + [ -n "${_UID}" ] && [ "x${_UID}" != "x${USERID}" ] && perror "User ${USER}(${USERID}) already exists with UID ${_UID}" + # do the same for the group + local _GID=$(grep -E "^${GROUP}:[^:]*:[0-9]+:" "${_GROUP}" | head -1 | awk -F ':' '{print $3}') + [ -n "${_GID}" ] && [ "x${_GID}" != "x${GROUPID}" ] && perror "Group ${GROUP}(${GROUPID}) already exists with GID ${_GID}" + # if user already exists, check if he is in another group than the one requested. if so, bail out + # (TODO: don't bail out and add user to the new group) + if [ ! -z "${_UID}" ] + then + local _EXGID=$(grep -E "^${USER}:[^:]*:[0-9]+:" "${_PASSWD}" | head -1 | awk -F ':' '{print $4}') + [ "x${GROUPID}" != "x${_EXGID}" ] && perror "Requested GID $GROUPID differs from existing GID $_EXGID" + fi + # if user does not exist, try to add it + if [ -z "${_UID}" ] + then + local _TEST=$(grep -E "^[^:]+:[^:]*:${USERID}:" "${_PASSWD}") + [ -n "${_TEST}" ] && perror "Cannot add $USER - desired UID $USERID already in use." + fi + if [ -z "${_GID}" ] + then + local _TEST=$(grep -E "^[^:]+:[^:]*:${GROUPID}:" "${_GROUP}") + [ -n "${_TEST}" ] && perror "Cannot add $GROUP - desired GID $GROUPID already in use." + fi + [ -z "${USERHOME}" ] && local USERHOME=/nonexistent + [ -z "${USERSHELL}" ] && local USERSHELL=/bin/false + # create password + if [ -z "${PASSWORD}" ] + then + local PASSWORD='*' + else + pdebug "Hashing password '$PASSWORD' for '$USER'" + local PW=$(mkpasswd -m sha-512 "${PASSWORD}") + [ -z "${PW}" ] && PW=$(openssl passwd -1 "${PASSWORD}") + [ -z "${PW}" ] && perror "Error generating hashed password for $USER" + PASSWORD=$PW + fi + # add user, or replace password + if [ -z "${_UID}" ]; then + # create user + echo "${USER}:x:${USERID}:${GROUPID}:${USER}:${USERHOME}:${USERSHELL}" >> "${_PASSWD}" + echo "${USER}:${PASSWORD}:15555:0:99999:7:::" >> "${_SHADOW}" + pinfo "Created user $USER" + elif [ "$PASSWORD" != "*" ]; then + # update user's password + sed -i -r "s#^${USER}:[^:]*:(.*)\$"'#'"${USER}:${PASSWORD}:\1#g" "${_SHADOW}" + pinfo "Updated password of $USER" + fi + [ -z "${_GID}" ] && pinfo "Created group $GROUP" && echo "${GROUP}:x:${GROUPID}:" >> "${_GROUP}" + echo "${USERID}" +} + +add_group () { + [ $# -lt 1 ] && perror "add_group called without argument." + [ -z "${TARGET_BUILD_DIR}" ] && perror "add_group: TARGET_BUILD_DIR not set" + local _PASSWD=${TARGET_BUILD_DIR}/etc/passwd + local _GROUP=${TARGET_BUILD_DIR}/etc/group + local _SHADOW=${TARGET_BUILD_DIR}/etc/shadow + init_users_and_groups + [ ! -f "${_GROUP}" ] && perror "add_user: group file does not exist in target system. (build base first)" + local GROUP=$1 + local GROUPID="-" + if ! [[ $GROUP =~ $NAME_REGEX ]]; then + perror "Invalid group: $GROUP" + fi + [ $# -ge 2 ] && [ ! -z "$2" ] && GROUPID=$2 + local _GID=$(grep -E "^${GROUP}:[^:]*:[0-9]+:" "${_GROUP}" | head -1 | awk -F ':' '{print $3}') + [ "x${_GID}" != "x" ] && [ "x$GROUPID" = "x-" -o "x$GROUPID" = "x${_GID}" ] && echo "${_GID}" && return # nothing to do, already exists + [ "x${_GID}" != "x" ] && perror "Group $GROUP already exists with GID ${_GID}, but creation was requested with GID $GROUPID" + if [ "x$GROUPID" = "x-" ]; then + local _UID=$(grep -E "^${GROUP}:[^:]*:[0-9]+:" "${_PASSWD}" | head -1 | awk -F ':' '{print $3}') + [ -z "${_UID}" ] && _UID=100 + GROUPID=$(generate_gid "$GROUP" "${_UID}") + fi + if ! [[ $GROUPID =~ [0-9]+ ]]; then + perror "add_group: GROUPID not numeric (is '$GROUPID')" + fi + echo "${GROUP}:x:${GROUPID}:" >> "${_GROUP}" + pinfo "Created group $GROUP" + echo "${GROUPID}" +} + +init_users_and_groups() { + [ -z "$TARGET_BUILD_DIR" -o "$TARGET_BUILD_DIR" == "/" ] && perror "Almost wrecked your local passwd, group and shadow file. phew." + local PASSWD="$TARGET_BUILD_DIR/etc/passwd" + local GROUP="$TARGET_BUILD_DIR/etc/group" + local SHADOW="$TARGET_BUILD_DIR/etc/shadow" + [ -s "${PASSWD}" -a -s "${GROUP}" -a -s "${SHADOW}" ] && return + pinfo "Creating users and groups based on local system...." + cp -a "/etc/passwd" "$PASSWD" || perror "Could not copy /etc/passwd" + cp -a "/etc/group" "$GROUP" || perror "Could not copy /etc/group" + cp -a "/etc/shadow" "$SHADOW" || perror "Could not copy /etc/shadow" + # make sure shadow has group shadow (should be handled by cp -a but hey) + chgrp shadow "$SHADOW" + # remove local users from group file (TODO: currently assumes users have ids 1000-1999) + local LOCALUSERS=$(grep -E '^[^:]+:x?:1[0-9]{3}:' "${PASSWD}" | awk -F ':' '{print $1}') + for USER in $LOCALUSERS; do + sed -r -i "s/([:,])${USER}/\1/g" "${GROUP}" + done + # fix syntax: remove trailing ',' in group file + sed -r -i 's/,+$//g' "${GROUP}" + sed -r -i 's/,+/,/g' "${GROUP}" + sed -i 's/:,/:/g' "${GROUP}" + # remove all non-system groups (also assumes users have 1000-1999, so nogroup will be kept) + grep -v -E '^[^:]+:x?:1[0-9]{3}:' "${GROUP}" > "${GROUP}.tmp" + mv "${GROUP}.tmp" "${GROUP}" + # same for users... + grep -v -E '^[^:]+:x?:1[0-9]{3}:' "${PASSWD}" > "${PASSWD}.tmp" + mv "${PASSWD}.tmp" "${PASSWD}" + # generate fresh shadow file + awk -F ':' '{print $1":*:15555:0:99999:7:::"}' "${PASSWD}" > "${SHADOW}" +} + diff --git a/core/includes/versioncompare.inc b/core/includes/versioncompare.inc new file mode 100644 index 00000000..f49a404f --- /dev/null +++ b/core/includes/versioncompare.inc @@ -0,0 +1,61 @@ + +# From http://stackoverflow.com/a/4025065/2043481 +# Returns 0 when equal, 1 when $1 > $2, 2 otherwise +version_compare () { + if [[ "$1" == "$2" ]]; then + return 0 + fi + local IFS=".-_" + local i ver1=($1) ver2=($2) + # fill empty fields in ver1 with zeros + for ((i=${#ver1[@]}; i<${#ver2[@]}; i++)); do + ver1[i]=0 + done + for ((i=0; i<${#ver1[@]}; i++)); do + if [[ -z "${ver2[i]}" ]]; then + # fill empty fields in ver2 with zeros + ver2[i]=0 + fi + if ((10#${ver1[i]} > 10#${ver2[i]})); then + return 1 + fi + if ((10#${ver1[i]} < 10#${ver2[i]})); then + return 2 + fi + done + return 0 +} + +version_ge () { + version_compare "$1" "$2" + local RET=$? + [ "$RET" == "2" ] && return 1 + return 0 +} + +version_le () { + version_compare "$1" "$2" + local RET=$? + [ "$RET" == "1" ] && return 1 + return 0 +} + +version_gt () { + version_compare "$1" "$2" + local RET=$? + [ "$RET" == "1" ] && return 0 + return 1 +} + +version_lt () { + version_compare "$1" "$2" + local RET=$? + [ "$RET" == "2" ] && return 0 + return 1 +} + +version_eq () { + version_compare "$1" "$2" + return $? +} + |