diff --git a/bin/oglivecli b/bin/oglivecli new file mode 100755 index 0000000..7bd8246 --- /dev/null +++ b/bin/oglivecli @@ -0,0 +1,504 @@ +#!/bin/bash + +#/** +#@file oglivecli +#@brief Command line tool to manage ogLive clients. +#@usage oglivecli Command [Options ...] +#@param Command: +#@param help show this help +#@param version show script version +#@param config [Parameter] show configuration parameters +#@param check check system consistency +#@param convert convert old ogclient to new default ogLive client +#@param list list installed ogLive clients +#@param show all show JSON information about all installed ogLive clients +#@param show default show JSON information about ogLive client marked as default +#@param show Index|Dir show JSON information about an installed ogLive client +#@param search Index|Dir show corresponding index or directory +#@param download show a menu to download an ogLive ISO image from the OpenGnsys website +#@param download Iso download an specific ogLive ISO image from the OpenGnsys website +#@param install Iso install a new ogLive client from a downloaded ISO image +#@param uninstall Iso remove ISO image and uninstall its ogLive client +#@param uninstall Index|Dir uninstall an ogLive client +#@param get-default get index value for default ogLive client +#@param set-default Index set default ogLive client +#@param rebuild rebuild a lost configuration file +#@param assign Iso Index assign an ISO file to a JSON entry +#@param Options: +#@param Index a number, starting by 0 +#@param Dir directory (relative to installation directory) +#@param Iso ISO file name (relative to download URL or download directory) +#@warning This script needs "jq" command. +#@version 1.1.0 - Initial version. +#@author Ramón M. Gómez - ETSII Univ. Sevilla +#@date 2016-12-05 +#@version 1.1.1b - Use reduced directory names. +#@author Ramón M. Gómez - ETSII Univ. Sevilla +#@date 2020-01-17 +#*/ ## + + +# Global constants definition. +PROG=$(basename "$(realpath "$0")") # Program name. +OPENGNSYS=/home/qindel/ogboot # OpenGnsys main directory. +DOWNLOADDIR=$OPENGNSYS/lib # Directory to store ogLive images. +DOWNLOADURL="https://ognproject.evlt.uma.es/trac/downloads" # Download URL. +TFTPDIR=$OPENGNSYS/tftpboot # TFTP directory. +DEFOGLIVE="ogLive" # Default ogLive directory. +MINREL=20190601 # Mininum ogLive compatibility release. +INFOFILE=$OPENGNSYS/etc/ogliveinfo.json # Configuration file. + + +# Global and secondary functions. + +source $OPENGNSYS/lib/ogfunctions.sh || exit 1 + +# Create/edit JSON file about installed ogLive clients. +function addToJson() { + local i DATA OGLIVEDIST="$1" OGLIVEKRNL="$2" OGLIVEARCH="$3" OGLIVEREV="$4" + local OGLIVEDIR=$(basename $5 2>/dev/null) OGLIVEISO=$(basename $6 2>/dev/null) + # JSON data for installed ogLive. + DATA=$(cat << EOT | jq . +{"distribution":"$OGLIVEDIST","kernel":"$OGLIVEKRNL","architecture":"$OGLIVEARCH","revision":"$OGLIVEREV","directory":"$OGLIVEDIR","iso":"$OGLIVEISO"} +EOT + ) + # Check JSON file consistency. + if [ "$(jq -c keys $INFOFILE 2>/dev/null)" == '["default","oglive"]' ]; then + # Check if ogLive is defined into JSON file. + n=$(jq ".oglive | length" $INFOFILE) + for ((i=0; i/dev/null || raiseError access "Installation directory." + [ ! -f $OGCLIENT/ogvmlinuz ] && raiseError notfound "\"ogclient\"." + # Add entry to JSON file using ogclient kernel version. + OGLIVEKRNL=$(file -bkr $OGCLIENT/ogvmlinuz | awk '/Linux/ {for(i=1;i<=NF;i++) if($i~/version/) {v=$(i+1);sub(/-.*/,"",v);print v}}') + OGLIVEDIR=$DEFOGLIVE-$OGLIVEKRNL + [ -r $OLDINFOFILE ] && OGLIVEISO="$(head -1 $OLDINFOFILE)" + addToJson "$(echo $OGLIVEISO|cut -f2 -d-)" "$OGLIVEKRNL" "i386" "${OGLIVEISO##*-}" "$OGLIVEDIR" "$OGLIVEISO.iso" + # Rename directory, link to default and clean old files. + mv -v $OGCLIENT $OGLIVEDIR + ln -vfs $OGLIVEDIR $DEFOGLIVE + rm -f $OGCLIENT + ln -vfs $DEFOGLIVE $OGCLIENT + mv -v $OGCLIENT.old $OGLIVEDIR.old 2>/dev/null + rm -fv {ogvmlinuz,oginitrd.img}{,.sum} $OLDINFOFILE + popd >/dev/null + # Delete old config file. + rm -f $OLDINFOFILE +} + +# Show script configuration parameters. +function config() { + local DATA + DATA=$(cat << EOT +[ + { "param": "config-file", "description": "Configuration file", "value": "$INFOFILE" }, + { "param": "download-url", "description": "ogLive download URL", "value": "$DOWNLOADURL" }, + { "param": "download-dir", "description": "ogLive download directory", "value": "$DOWNLOADDIR" }, + { "param": "install-dir", "description": "ogLive installation directory", "value": "$TFTPDIR" }, + { "param": "default-name", "description": "Default ogLive name", "value": "$DEFOGLIVE" }, + { "param": "min-release", "description": "Mainimum compatibility release", "value": "r$MINREL" } +]" +EOT + ) + case $# in + 0) # Show all parameters. + echo $DATA | jq -r '.[] | .description + " (" + .param+ ")," + .value' | column -ts, + ;; + 1) # Show specified parameter. + DATA=$(echo $DATA | jq -r ".[] | select(.param==\"$1\").value") + [ "$DATA" ] || raiseError notfound "\"$1\"." + echo "$DATA" + ;; + *) # Usage error. + raiseError usage + ;; +esac +} + +# Check consistency, showing configuration problems. +function check() { + local ERR=0 AUX INST DEF + [ $# -ne 0 ] && raiseError usage + # Check for old system that needs conversion. + if [ -z "$(stat -c "%N" $TFTPDIR/ogclient | awk '$3~/'$DEFOGLIVE'/ {print}')" ]; then + echo "This server uses old ogclient, please run \"$PROG convert\" to update." + let ERR++ + [ ! -f $INFOFILE ] && return $ERR + fi + # Check for other problems. + [ ! -f $INFOFILE ] && echo "Configuration file does not exists: $INFOFILE" && let ERR++ + [ -f $INFOFILE -a "$(jq -c keys $INFOFILE 2>/dev/null)" != "[\"default\",\"oglive\"]" ] && echo "Format error in configuration file: $INFOFILE" && let ERR++ + [ ! -e $TFTPDIR ] && echo "TFTP directory does not exist: $TFTPDIR." && let ERR++ + # Check for installed ogLive clients. + INST=( $(find $TFTPDIR/ -type d -name "$DEFOGLIVE-*" -a ! -name "*.old" -printf "%f\n" | sort) ) + [[ ${#INST[@]} -eq 0 ]] && echo "No ogLive clients are installed." && let ERR++ + DEF=( $(jq -r .oglive[].directory $INFOFILE 2>/dev/null | sort) ) + # Compare installed and defined ogLive clients. + AUX=$(comm -23 <(printf "%s\n" ${INST[*]}) <(printf "%s\n" ${DEF[*]})) + [ -n "$AUX" ] && echo "Some ogLive are installed but not defined: ${AUX//$'\n'/, }" && let ERR++ + AUX=$(comm -13 <(printf "%s\n" ${INST[*]}) <(printf "%s\n" ${DEF[*]})) + [ -n "$AUX" ] && echo "Some ogLive are defined but not installed: ${AUX//$'\n'/, }" && let ERR++ + # Compare downloaded and defined ISO images. + INST=( $(find $DOWNLOADDIR/ -type f -name "$DEFOGLIVE-*.iso" -printf "%f\n" | sort) ) + DEF=( $(jq -r .oglive[].iso $INFOFILE 2>/dev/null | sort) ) + AUX=$(comm -23 <(printf "%s\n" ${INST[*]}) <(printf "%s\n" ${DEF[*]})) + [ -n "$AUX" ] && echo "Some ISOs are downloaded but not defined: ${AUX//$'\n'/, }" && let ERR++ + AUX=$(comm -13 <(printf "%s\n" ${INST[*]}) <(printf "%s\n" ${DEF[*]})) + [ -n "$AUX" ] && echo "Some ISOs are defined but not downloaded: ${AUX//$'\n'/, }" && let ERR++ + # Check for new ISO files downloaded after installation. + AUX=$(jq -r '.oglive[] as $og | $og.iso + ":" + $og.directory' $INFOFILE 2>/dev/null | \ + while IFS=":" read -r DEF INST; do + [ $DOWNLOADDIR/$DEF -nt $TFTPDIR/$INST ] && echo "$DEF" + done) + [ -n "$AUX" ] && echo "Some ISOs are downloaded after installation: ${AUX//$'\n'/, }" && let ERR++ + AUX=$(jq -r '.oglive[] as $og | if ($og.revision[1:9] | tonumber) < '$MINREL' then $og.directory else "" end' $INFOFILE 2>/dev/null) + [ -n "$AUX" ] && echo "Some installed ogLive aren't fully compatible: ${AUX//$'\n'/, }" && let ERR++ + DEF=$(jq -r ".oglive[$(getdefault)].directory" $INFOFILE 2>/dev/null) + INST=$(stat -c "%N" $TFTPDIR/$DEFOGLIVE | cut -f4 -d\') + [ "$DEF" != "$INST" ] && echo "Default ogLive is not linked to right directory: $DEF <> $INST" && let ERR++ + # Print result. + [ $ERR -eq 0 ] && echo "OK!" || echo "Problems detected: $ERR" + return $ERR +} + +# List installed ogLive clients. +function list() { + [ $# -ne 0 ] && raiseError usage + [ ! -r $INFOFILE ] && raiseError access "Configuration file." + # List all defined indexes, directories and check if missing. + jq -r .oglive[].directory $INFOFILE | nl -v 0 | \ + awk '{system("echo -n "$0"; test -d '$TFTPDIR'/"$2" || echo -n \" (missing)\"; echo")}' | column -t +} + +# Show information about an installed ogLive client. +function show() { + local INDEX + [ $# -ne 1 ] && raiseError usage + [ ! -r $INFOFILE ] && raiseError access "Configuration file." + # Show JSON entries. + case "$1" in + default) # Default index. + INDEX="[$(jq -r .default $INFOFILE)]" ;; + all) # All intries. + ;; + [0-9]*) # Index. + INDEX="[$1]" ;; + *) # Directory. + INDEX="[$(search "$1" 2>/dev/null)]" || raiseError notfound "Directory \"$1\"." + ;; + esac + jq ".oglive$INDEX" $INFOFILE || raiseError notfound "Index \"$1\"." +} + +# Show index or directory corresponding to searching parameter. +function search() { + [ $# -ne 1 ] && raiseError usage + [ ! -r $INFOFILE ] && raiseError access "Configuration file." + # Show corresponding index or directory. + list | awk -v d="$1" '{if ($2==d) print $1; if ($1==d) print $2}' | grep . || raiseError notfound "Index/Directory \"$1\"." +} + +# Show a menu to select and download an ogLive ISO image from the OpenGnsys website. +function download() { + local OGLIVE NISOS i HTTPCODE ISOREL + + # Verificar si el directorio de descarga existe y tiene permisos de escritura. + [ ! -d "$DOWNLOADDIR" ] && raiseError notfound "Directorio de descarga." + [ ! -w "$DOWNLOADDIR" ] && raiseError access "Directorio de descarga." + + # Si no se proporciona ningún parámetro, mostrar el menú de descargas. + if [ -z "$1" ]; then + downloadMenu + else + local selected_id="$1" + + # Obtener la opción correspondiente al ID seleccionado. + OGLIVE=( $(curl -k --silent $DOWNLOADURL | grep "$DEFOGLIVE.*iso") ) + OGLIVEFILE=${OGLIVE[selected_id - 1]} + [ -n "$OGLIVEFILE" ] || raiseError download "ID $selected_id inválido." + + # Obtener el tamaño de descarga. + local SOURCELENGTH=$(curl -k --head --retry 5 --retry-delay 5 --max-time 30 "$DOWNLOADURL/$OGLIVEFILE" | awk -F: '/Content-Length:/ {print $2}') + [ -n "$SOURCELENGTH" ] || raiseError download "$OGLIVEFILE" + + # Descargar ogLive. + local TARGETFILE="$DOWNLOADDIR/$OGLIVEFILE" + trap "rm -f $TARGETFILE" 1 2 3 6 9 15 + curl -k --retry 5 --retry-delay 5 "$DOWNLOADURL/$OGLIVEFILE" -o "$TARGETFILE" || raiseError download "\"$OGLIVEFILE\"." + install "$OGLIVEFILE" + fi +} +# Muestra un menú para seleccionar y descargar un archivo ogLive ISO del sitio web de OpenGnsys. +function downloadMenu() { + + local OGLIVE NISOS i HTTPCODE ISOREL + OGLIVE=( $(curl -k --silent $DOWNLOADURL | grep "$DEFOGLIVE.*iso") ) + NISOS=${#OGLIVE[@]} + echo "Descargas disponibles (+ = instalado, * = compatibilidad completa):" + for i in $(seq 1 $NISOS); do + [ -e $DOWNLOADDIR/${OGLIVE[i-1]} ] && OGLIVE[i-1]="(+) ${OGLIVE[i-1]}" + ISOREL=${OGLIVE[i-1]##*-r}; ISOREL=${ISOREL%%.*} + [ $ISOREL -ge $MINREL ] && OGLIVE[i-1]="(*) ${OGLIVE[i-1]}" + echo "$i) ${OGLIVE[i-1]}" + done +} + +# Show a menu to select and download an ogLive ISO image from the OpenGnsys website. +function download_old() { + local OGLIVE NISOS i HTTPCODE TARGETFILE + local ISOREL + [ $# -gt 1 ] && raiseError usage + [ ! -d $DOWNLOADDIR ] && raiseError notfound "Download directory." + [ ! -w $DOWNLOADDIR ] && raiseError access "Download directory." + # Check parameter. + if [ -n "$1" ]; then + # ogLive to download. + OGLIVEFILE="$1" + else + # Show download menu. + OGLIVE=( $(curl -k --silent $DOWNLOADURL | grep "$DEFOGLIVE.*iso") ) + NISOS=${#OGLIVE[@]} + echo "Available downloads (+ = installed, * = full compatibility):" + for i in $(seq 1 $NISOS); do + [ -e $DOWNLOADDIR/${OGLIVE[i-1]} ] && OGLIVE[i-1]="(+) ${OGLIVE[i-1]}" + ISOREL=${OGLIVE[i-1]##*-r}; ISOREL=${ISOREL%%.*} + [ $ISOREL -ge $MINREL ] && OGLIVE[i-1]="(*) ${OGLIVE[i-1]}" + done + select opt in "${OGLIVE[@]}"; do + [ -n "$opt" ] && OGLIVEFILE=${opt##* } && break + done + fi + # Get download size. + SOURCELENGTH=$(curl -k --head --retry 5 --retry-delay 5 --max-time 30 $DOWNLOADURL/$OGLIVEFILE | awk -F: '/Content-Length:/ {print $2}') + [ -n "$SOURCELENGTH" ] || raiseError download "$OGLIVEFILE" + # Download ogLive. + TARGETFILE=$DOWNLOADDIR/$OGLIVEFILE + trap "rm -f $TARGETFILE" 1 2 3 6 9 15 + curl -k --retry 5 --retry-delay 5 --max-time 30 $DOWNLOADURL/$OGLIVEFILE -o $TARGETFILE || raiseError download "\"$OGLIVEFILE\"." +} + +# Install an ogLive client from a previously downloaded ISO image. +function install() { + local OGLIVEFILE OGLIVEDIST OGLIVEREV OGLIVEKRNL OGLIVEDIR OGINITRD OGSQFS OGCLIENT=ogclient + local COMPRESS SAMBAPASS TMPDIR RSYNCSERV RSYNCCLNT + [ $# -ne 1 ] && raiseError usage + OGLIVEFILE=$(realpath $DOWNLOADDIR/$1) + # Only 1 file in pathname expansion. + [ $(echo $OGLIVEFILE | wc -w) -gt 1 ] && raiseError usage + [ ! -f $OGLIVEFILE ] && raiseError notfound "Downloaded file: \"$1\"." + [ ! -r $OGLIVEFILE ] && raiseError access "Downloaded file: \"$1\"." + [ ! -w $(dirname $INFOFILE) ] && raiseError access "Configuration directory." + [ ! -w $TFTPDIR ] && raiseError access "Installation directory." + [ -z "$(file -b $OGLIVEFILE | grep "ISO.*ogClient")" ] && raiseError access "File is not an ogLive ISO image." + # Working directory: + # 64-bit: ogLive-KernelVersion-rCodeRelease + # 32-bit: ogLive-KernelVersion-i386-rCodeRelease + OGLIVEDIST="$(echo $OGLIVEFILE|cut -f2 -d-)" + OGLIVEREV="${OGLIVEFILE##*-}"; OGLIVEREV="${OGLIVEREV%%.*}" + OGLIVEKRNL="$(echo $OGLIVEFILE|cut -f3- -d-)"; OGLIVEKRNL="${OGLIVEKRNL%-$OGLIVEREV.*}" + OGLIVEARCH="$(echo $OGLIVEFILE|awk -F- '{print $(NF-1)}')" + case "$OGLIVEARCH" in + i386|amd64) # Get architecture. + OGLIVEKRNL="${OGLIVEKRNL%-$OGLIVEARCH}" ;; + *) # 32-bit by default. + OGLIVEARCH="i386" ;; + esac + OGLIVEDIR="$TFTPDIR/$DEFOGLIVE-${OGLIVEKRNL%%-*}-$OGLIVEARCH-$OGLIVEREV" + OGLIVEDIR="${OGLIVEDIR/amd64-/}" + # Get current or default Samba key. + OGINITRD=$OGLIVEDIR/oginitrd.img + [ ! -r $OGINITRD ] && OGINITRD=$TFTPDIR/$DEFOGLIVE/oginitrd.img + if [ -r $OGINITRD ]; then + COMPRESS=$(file -b "$OGINITRD" | awk '{print tolower($1);}') + SAMBAPASS=$($COMPRESS -dc $OGINITRD | \ + cpio -i --to-stdout scripts/ogfunctions 2>&1 | \ + sed -n '/^[ ].*OPTIONS=/s/.*pass=\(\w*\).*/\1/p') + fi + # Make ogLive backup. + rm -fr ${OGLIVEDIR}.old + mv -fv $OGLIVEDIR ${OGLIVEDIR}.old 2>/dev/null + # Mount ogLive ISO image, update its files and unmount it. + TMPDIR=/tmp/${OGLIVEFILE%.iso} + mkdir -p $OGLIVEDIR $TMPDIR + trap "umount $TMPDIR; rm -fr $TMPDIR" 1 2 3 6 9 15 + mount -o loop,ro $OGLIVEFILE $TMPDIR + cp -va $TMPDIR/ogclient/* $OGLIVEDIR || raiseError access "Cannot copy files to ogLive directory." + umount $TMPDIR + # Link to default directory if it's the first ogLive. + if [ ! -f $INFOFILE ]; then + rm -f $TFTPDIR/$DEFOGLIVE $TFTPDIR/$OGCLIENT + ln -vfs $(basename $OGLIVEDIR) $TFTPDIR/$DEFOGLIVE + ln -vfs $DEFOGLIVE $TFTPDIR/$OGCLIENT + fi + # Recover or ask for a new Samba access key. + if [ -n "$SAMBAPASS" ]; then + echo -ne "$SAMBAPASS\n$SAMBAPASS\n" | $OPENGNSYS/bin/setsmbpass "$(basename $OGLIVEDIR)" + else + $OPENGNSYS/bin/setsmbpass "$(basename $OGLIVEDIR)" + fi + # Set permissions. + find -L $OGLIVEDIR -type d -exec chmod 755 {} \; + find -L $OGLIVEDIR -type f -exec chmod 644 {} \; + chown -R :opengnsys $OGLIVEDIR + # Mount SquashFS and check Rsync version. + OGSQFS=$OGLIVEDIR/ogclient.sqfs + mount -o loop,ro $OGSQFS $TMPDIR + # If Rsync server version > client version, link to compiled file. + RSYNCSERV=$(rsync --version 2>/dev/null | awk '/protocol/ {print $6}') + RSYNCCLNT=$(chroot $TMPDIR /usr/bin/rsync --version 2>/dev/null | awk '/protocol/ {print $6}') + if [ -z "$RSYNCSERV" -o ${RSYNCSERV:-0} -gt ${RSYNCCLNT:-1} ]; then + [ -e $OPENGNSYS/client/bin/rsync-$RSYNCSERV ] && mv -f $OPENGNSYS/client/bin/rsync-$RSYNCSERV $OPENGNSYS/client/bin/rsync + else + # Else, rename compiled file using Rsync protocol number. + [ -e $OPENGNSYS/client/bin/rsync ] && mv -f $OPENGNSYS/client/bin/rsync $OPENGNSYS/client/bin/rsync-$($OPENGNSYS/client/bin/rsync --version 2>/dev/null | awk '/protocol/ {print $6}') + fi + # Unmount SquashFS. + umount $TMPDIR + rmdir $TMPDIR + # Update JSON file. + addToJson "$OGLIVEDIST" "$OGLIVEKRNL" "$OGLIVEARCH" "$OGLIVEREV" "$OGLIVEDIR" "$OGLIVEFILE" +} + +# Uninstall an ogLive client. +function uninstall() { + local ISO DIR INDEX DEFINDEX + [ $# -ne 1 ] && raiseError usage + [ ! -r $INFOFILE ] && raiseError access "Configuration file." + [ ! -w $TFTPDIR ] && raiseError access "Installation directory." + # Get index and directory for the entry. + case "$1" in + */*) # Error (access to other directory). + raiseError access "Cannot access outside installation directory." + ;; + *.iso) # ISO file. + ISO="$1" + # Working directory (ogLive-Distribution-KernelVersion-CodeRevision). + DIR="$(echo $ISO|cut -f1,3 -d-)-${ISO##*-}"; DIR=${DIR%.*} + INDEX=$(search $DIR 2>/dev/null) + ;; + [0-9]*) # Index. + INDEX=$1; DIR=$(search $INDEX 2>/dev/null) + ;; + *) # Directory. + DIR="$1"; INDEX=$(search $DIR 2>/dev/null) + ;; + esac + DEFINDEX=$(getdefault) + [[ $INDEX = $DEFINDEX ]] && raiseError access "Cannot uninstall default ogLive." + # Remove files and delete index entry. + rm -vfr ${ISO:+$DOWNLOADDIR/$ISO} ${DIR:+$TFTPDIR/$DIR} ### Remove $TFTPDIR/$DIR.old ? + if [ -n "$INDEX" ]; then + jq "del(.oglive[$INDEX])" $INFOFILE | sponge $INFOFILE + # Decrement default index if needed (removed < default). + [[ $INDEX < $DEFINDEX ]] && jq ".default=$((DEFINDEX-1))" $INFOFILE | sponge $INFOFILE + fi +} + +# Get default ogLive index. +function getdefault() { + [ $# -ne 0 ] && raiseError usage + [ ! -r $INFOFILE ] && raiseError access "Configuration file." + # Read default parameter. + jq -r .default $INFOFILE || raiseError notfound "Undefined default index." +} + +# Set default ogLive index. +function setdefault() { + local INDEX OGLIVEDIR + [ $# -ne 1 ] && raiseError usage + [ ! -w $INFOFILE ] && raiseError access "Configuration file." + INDEX=$1 + # Check if index entry exists. + jq ".oglive[$INDEX]" $INFOFILE || raiseError notfound "Index \"$INDEX\"." + # Get ogLive directory. + OGLIVEDIR=$(jq -r ".oglive[$INDEX].directory" $INFOFILE) || raiseError notfound "Directory for index \"$INDEX\"." + # Update default parameter. + jq ".default=$INDEX" $INFOFILE | sponge $INFOFILE + # Link to default directory. + rm -f $TFTPDIR/$DEFOGLIVE + ln -vfs $(basename $OGLIVEDIR) $TFTPDIR/$DEFOGLIVE +} + +# Rebuild a lost configuration file. +function rebuild() { + local i INST NF DEF + [ $# -ne 0 ] && raiseError usage + [ -f $INFOFILE ] && raiseError access "Configuration file exists." + INST=$(find $TFTPDIR/ -type d -name "$DEFOGLIVE-*" -a ! -name "*.old" -printf "%f\n" | sort) + for i in $INST; do + NF=$(echo $i | awk -F- '{print NF-1}') + case $NF in + 1) addToJson "" "$(echo $i|cut -f2 -d-)" "i386" "" "$i" "" ;; + 2) eval addToJson $(echo $i | awk -F- '{printf "\"\" %s amd64 %s %s \"\"",$2,$3,$0}') ;; + 3) eval addToJson $(echo $i | awk -F- '{if ($3=="i386") printf "\"\" %s %s %s %s \"\"",$2,$3,$4,$0; else printf "%s %s i386 %s %s \"\"",$2,$3,$4,$0}') ;; + 4) eval addToJson $(echo $i | awk -F- '{printf "%s %s %s %s %s \"\"",$2,$3,$4,$5,$0}') ;; + esac + # Check for is default oglive. + [ -n "$(stat -c "%N" $TFTPDIR/$DEFOGLIVE | awk '$3~/'$i'/ {print}')" ] && DEF="$i" + done + # Set default ogLive. + [ -n "$DEF" ] && setdefault $(search $DEF) +} + +# Assign an ISO file to a JSON entry. +function assign() { +local ISOFILE DIR + [ $# -ne 2 ] && raiseError usage + [ ! -w $INFOFILE ] && raiseError access "Configuration file." + # Check if ISO file and index directory exist. + ISOFILE=$DOWNLOADFILE/$1 + [ ! -f $DOWNLOADDIR/$ISOFILE ] && raiseError notfound "ISO file \"$1\"." + DIR=$(search $2 2>/dev/null) + [ ! -d $TFTPDIR/$DIR ] && raiseError notfound "Directory for index \"$2\"." + # Assign ISO file to JSON entry. + jq ".oglive[$2].iso=\"$1\"" $INFOFILE | sponge $INFOFILE && jq ".oglive[$2]" $INFOFILE +} + + +# Main progrram. + +# Access control. +[ -r $OPENGNSYS/www/controlacceso.php ] && ACCESS="web" +[ "$USER" = "root" ] && ACCESS="root" +[ -z "$ACCESS" ] && raiseError access "Need to be root." +# Check dependencies. +which sponge &>/dev/null || raiseError notfound "Need to install \"moreutils\"." +# Commands control. +shopt -s extglob +case "$ACCESS" in + root) CMDS='+(help|version|convert|config|check|list|show|search|download|install|uninstall|get-default|set-default|rebuild|assign)' ;; + web) CMDS='+(list|show|search|get-default)' ;; +esac +case "$1" in + $CMDS) COMMAND="${1/-/}"; shift; $COMMAND "$@" ;; + *) raiseError usage ;; +esac + +exit $? + diff --git a/src/OgBootBundle/Controller/OgBootController.php b/src/OgBootBundle/Controller/OgBootController.php index 0daa213..79cfa0f 100644 --- a/src/OgBootBundle/Controller/OgBootController.php +++ b/src/OgBootBundle/Controller/OgBootController.php @@ -28,6 +28,539 @@ class OgBootController $this->logger = $logger; } +/*Tabla de Contenido +Obtener configuración de ogboot - GET /ogboot/config x +Obtener datos de rendimiento - GET /ogboot/status x +Mostrar información de todos los clientes ogLive instalados - GET /ogboot/oglives x +Mostrar información de un cliente ogLive instalado - GET /ogboot/oglives/{Index|Dir} x +Mostrar información del cliente ogLive predeterminado - GET /ogboot/oglives/default x +Cambiar ogLive predeterminado - POST /ogboot/oglives/default/{Index} x +Instalar nuevo cliente ogLive desde imagen descargada - POST /ogboot/oglive/{Index/iso} ---- +Desinstalar cliente ogLive y eliminar imagen - DELETE /ogboot/oglives/{Index/iso} ------- +Regenerar archivo de información de los ogLive - PUT /ogboot/oglives +Mostrar menú de descarga de imagen de ogLive - GET /ogboot/images/download X + +///////////////////////////////////////////// + +Obtener todos los archivos de arranque - GET /ogboot/pxes +Obtener archivo de arranque - GET /ogboot/clients/pxes/{mac} +Crear archivo de arranque - POST /ogboot/pxes +Eliminar archivo de arranque - DELETE /ogboot/clients/pxes +Obtener todas las plantillas - GET /ogboot/pxe-templates +Obtener contenido de la plantilla - GET /ogboot/pxe-templates/{nombre} +Crear plantilla - POST /ogboot/pxe-templates +Regenerar plantilla - PUT /ogboot/pxe-templates +*/ + + /** + * @Route("/ogboot/help", name="help", methods={"GET"}) + */ + public function help(): Response + { + + # $result = $this->curlRequestService->common_request(OG_REST_CMD_POWEROFF, POST, [OG_REST_PARAM_CLIENTS => $ips]); + $result = $this->curlRequestService->callOgLive("help"); + + if ($result) { + return new Response($result, Response::HTTP_OK); + } else { + return new Response('Failed', Response::HTTP_INTERNAL_SERVER_ERROR); + } + } + +/** + * @Route("/ogboot/config", name="config", methods={"GET"}) + * @OA\Get( + * path="/ogboot/config", + * summary="Get ogboot configuration", + * @OA\Response( + * response=200, + * description="Successful operation", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="config-file", + * type="string", + * description="Configuration file path" + * ), + * @OA\Property( + * property="download-url", + * type="string", + * description="ogLive download URL" + * ), + * @OA\Property( + * property="download-dir", + * type="string", + * description="ogLive download directory" + * ), + * @OA\Property( + * property="install-dir", + * type="string", + * description="ogLive installation directory" + * ), + * @OA\Property( + * property="default-name", + * type="string", + * description="Default ogLive name" + * ), + * @OA\Property( + * property="min-release", + * type="string", + * description="Minimum compatibility release" + * ) + * ) + * ), + * @OA\Response( + * response=500, + * description="Failed to retrieve configuration" + * ) + * ) + */ + public function config(): Response + { + + # $result = $this->curlRequestService->common_request(OG_REST_CMD_POWEROFF, POST, [OG_REST_PARAM_CLIENTS => $ips]); + $result = $this->curlRequestService->callOgLive("config"); + + if ($result) { + return new Response($result, Response::HTTP_OK); + } else { + return new Response('Failed', Response::HTTP_INTERNAL_SERVER_ERROR); + } + } + +/** + * @Route("/ogboot/status", name="status", methods={"GET"}) + * @OA\Get( + * path="/ogboot/status", + * summary="Get ogboot status", + * @OA\Response( + * response=200, + * description="Successful operation", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="message", + * type="string", + * description="Status message indicating any issues detected" + * ) + * ) + * ), + * @OA\Response( + * response=500, + * description="Failed to retrieve status" + * ) + * ) + */ + public function status(): Response + { + + # $result = $this->curlRequestService->common_request(OG_REST_CMD_POWEROFF, POST, [OG_REST_PARAM_CLIENTS => $ips]); + $result = $this->curlRequestService->callOgLive("check"); + + if ($result) { + return new Response($result, Response::HTTP_OK); + } else { + return new Response('Failed', Response::HTTP_INTERNAL_SERVER_ERROR); + } + } + +/** + * @Route("/ogboot/oglives", name="getOglives", methods={"GET"}) + * @OA\Get( + * path="/ogboot/oglives", + * summary="Get information of all installed ogLive clients", + * @OA\Response( + * response=200, + * description="Successful operation", + * @OA\JsonContent( + * type="array", + * @OA\Items( + * type="object", + * @OA\Property( + * property="distribution", + * type="string", + * description="Distribution name of the ogLive client" + * ), + * @OA\Property( + * property="kernel", + * type="string", + * description="Kernel version of the ogLive client" + * ), + * @OA\Property( + * property="architecture", + * type="string", + * description="Architecture of the ogLive client" + * ), + * @OA\Property( + * property="revision", + * type="string", + * description="Revision of the ogLive client" + * ), + * @OA\Property( + * property="directory", + * type="string", + * description="Directory name of the ogLive client" + * ), + * @OA\Property( + * property="iso", + * type="string", + * description="ISO file name of the ogLive client" + * ) + * ) + * ) + * ), + * @OA\Response( + * response=500, + * description="Failed to retrieve information of installed ogLive clients" + * ) + * ) + */ + public function getOglives(): Response + { + + # $result = $this->curlRequestService->common_request(OG_REST_CMD_POWEROFF, POST, [OG_REST_PARAM_CLIENTS => $ips]); + $result = $this->curlRequestService->callOgLive("show all"); + + if ($result) { + return new Response($result, Response::HTTP_OK); + } else { + return new Response('Failed', Response::HTTP_INTERNAL_SERVER_ERROR); + } + } + +/** + * @Route("/ogboot/oglives/default", name="getOgliveDefault", methods={"GET"}) + * @OA\Get( + * path="/ogboot/oglives/default", + * summary="Get information of the default ogLive client", + * @OA\Response( + * response=200, + * description="Successful operation", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="distribution", + * type="string", + * description="Distribution name of the default ogLive client" + * ), + * @OA\Property( + * property="kernel", + * type="string", + * description="Kernel version of the default ogLive client" + * ), + * @OA\Property( + * property="architecture", + * type="string", + * description="Architecture of the default ogLive client" + * ), + * @OA\Property( + * property="revision", + * type="string", + * description="Revision of the default ogLive client" + * ), + * @OA\Property( + * property="directory", + * type="string", + * description="Directory name of the default ogLive client" + * ), + * @OA\Property( + * property="iso", + * type="string", + * description="ISO file name of the default ogLive client" + * ) + * ) + * ), + * @OA\Response( + * response=500, + * description="Failed to retrieve information of the default ogLive client" + * ) + * ) + */ + + public function getOgliveDefault(Request $request): Response + { + # $result = $this->curlRequestService->common_request(OG_REST_CMD_POWEROFF, POST, [OG_REST_PARAM_CLIENTS => $ips]); + $result = $this->curlRequestService->callOgLive("show default"); + + if ($result) { + return new Response($result, Response::HTTP_OK); + } else { + return new Response('Failed', Response::HTTP_INTERNAL_SERVER_ERROR); + } + } + +/** + * @Route("/ogboot/oglives/{id}", name="getOglive", methods={"GET"}) + * @OA\Get( + * path="/ogboot/oglives/{id}", + * summary="Get information of an installed ogLive client", + * @OA\Parameter( + * name="id", + * in="path", + * description="Index or Directory of the installed ogLive client", + * required=true, + * @OA\Schema( + * type="string" + * ) + * ), + * @OA\Response( + * response=200, + * description="Successful operation", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="distribution", + * type="string", + * description="Distribution name of the installed ogLive client" + * ), + * @OA\Property( + * property="kernel", + * type="string", + * description="Kernel version of the installed ogLive client" + * ), + * @OA\Property( + * property="architecture", + * type="string", + * description="Architecture of the installed ogLive client" + * ), + * @OA\Property( + * property="revision", + * type="string", + * description="Revision of the installed ogLive client" + * ), + * @OA\Property( + * property="directory", + * type="string", + * description="Directory name of the installed ogLive client" + * ), + * @OA\Property( + * property="iso", + * type="string", + * description="ISO file name of the installed ogLive client" + * ) + * ) + * ), + * @OA\Response( + * response=404, + * description="ogLive client not found" + * ) + * ) + */ + + public function getOglive(Request $request): Response + { + $id = (int) $request->get('id'); + # $result = $this->curlRequestService->common_request(OG_REST_CMD_POWEROFF, POST, [OG_REST_PARAM_CLIENTS => $ips]); + $result = $this->curlRequestService->callOgLive("show $id"); + + if ($result) { + return new Response($result, Response::HTTP_OK); + } else { + return new Response('Failed', Response::HTTP_INTERNAL_SERVER_ERROR); + } + } + +/** + * @Route("/ogboot/oglives/default/{id}", name="setOgliveDefault", methods={"POST"}) + * @OA\Post( + * path="/ogboot/oglives/default/{id}", + * summary="Change default ogLive", + * @OA\Parameter( + * name="id", + * in="path", + * description="Index of the ogLive client to set as default", + * required=true, + * @OA\Schema( + * type="string" + * ) + * ), + * @OA\Response( + * response=200, + * description="Default ogLive changed successfully", + * @OA\JsonContent( + * type="object", + * @OA\Property(property="success", type="string") + * ) + * ), + * @OA\Response( + * response=404, + * description="ogLive client not found" + * ) + * ) + */ + + public function setOgliveDefault(Request $request): Response + { + $id = (int) $request->get('id'); + # $result = $this->curlRequestService->common_request(OG_REST_CMD_POWEROFF, POST, [OG_REST_PARAM_CLIENTS => $ips]); + $result = $this->curlRequestService->callOgLive("set-default $id"); + + if ($result) { + return new Response($result, Response::HTTP_OK); + } else { + return new Response('Failed', Response::HTTP_INTERNAL_SERVER_ERROR); + } + } + + + //Mostrar menú de descarga de imagen de ogLive - GET /ogboot/images/download + +/** + * @Route("/ogboot/images/download", name="getDownloadMenu", methods={"GET"}) + * @OA\Get( + * path="/ogboot/images/download", + * summary="Get available ISO images for download", + * @OA\Response( + * response=200, + * description="List of available ISO images", + * @OA\JsonContent( + * type="array", + * @OA\Items( + * type="object", + * @OA\Property(property="id", type="string"), + * @OA\Property(property="filename", type="string"), + * @OA\Property(property="installed", type="boolean"), + * @OA\Property(property="compatible", type="boolean") + * ) + * ) + * ) + * ) + */ + + public function getDownloadMenu(): Response + { + // Ejecutar el comando "oglivecli download" y obtener la salida + $downloadsOutput = $this->curlRequestService->callOgLive("download"); + + // Separar la salida por líneas + $downloads = explode("\n", trim($downloadsOutput)); + $result = []; + + foreach ($downloads as $download) { + // Define el patrón de la expresión regular + $pattern = '/^(\d+)\) (\(\*\))?\s?(\(\+\))?\s?(.+\.iso)$/'; + + // Realiza el matcheo de la línea con el patrón + preg_match($pattern, $download, $matches); + + // Si hay coincidencias, extrae la información + if (!empty($matches)) { + $id = $matches[1]; + $installed = !empty($matches[2]); + $compatible = !empty($matches[3]); + $filename = $matches[4]; + + // Agrega la información al resultado + $result[] = [ + 'id' => $id, + 'filename' => $filename, + 'installed' => $installed, + 'compatible' => $compatible + ]; + } + } + + // Convertir el array de downloads a JSON + $jsonDownloads = json_encode($result); + + // Crear una respuesta JSON y devolverla + return new Response($jsonDownloads, Response::HTTP_OK, [ + 'Content-Type' => 'application/json' + ]); + } + +/** + * @Route("/ogboot/oglives/{id}", name="installOglive", methods={"POST"}) + * @OA\Post( + * path="/ogboot/oglives/{id}", + * summary="Install an ogLive client", + * @OA\Parameter( + * name="id", + * in="path", + * description="ID of the ogLive client to install", + * required=true, + * @OA\Schema( + * type="integer" + * ) + * ), + * @OA\Response( + * response=200, + * description="ogLive client installed successfully", + * @OA\JsonContent( + * type="object", + * @OA\Property(property="message", type="string") + * ) + * ), + * @OA\Response( + * response=500, + * description="Failed to install ogLive client", + * @OA\JsonContent( + * type="object", + * @OA\Property(property="error", type="string") + * ) + * ) + * ) + */ + public function installOglive(Request $request): Response + { + $id = (int) $request->get('id'); + # $result = $this->curlRequestService->common_request(OG_REST_CMD_POWEROFF, POST, [OG_REST_PARAM_CLIENTS => $ips]); + $result = $this->curlRequestService->callOgLive("download $id"); + + if ($result) { + return new Response($result, Response::HTTP_OK); + } else { + return new Response('Failed', Response::HTTP_INTERNAL_SERVER_ERROR); + } + } + +/** + * @Route("/ogboot/oglives/{id}", name="uninstallOglive", methods={"DELETE"}) + * @OA\Delete( + * path="/ogboot/oglives/{id}", + * summary="Uninstall an ogLive client", + * @OA\Parameter( + * name="id", + * in="path", + * description="ID of the ogLive client to uninstall", + * required=true, + * @OA\Schema( + * type="integer" + * ) + * ), + * @OA\Response( + * response=200, + * description="ogLive client uninstalled successfully", + * @OA\JsonContent( + * type="object", + * @OA\Property(property="message", type="string") + * ) + * ), + * @OA\Response( + * response=500, + * description="Failed to uninstall ogLive client", + * @OA\JsonContent( + * type="object", + * @OA\Property(property="error", type="string") + * ) + * ) + * ) + */ + + public function uninstallOglive(Request $request): Response + { + $id = (int) $request->get('id'); + # $result = $this->curlRequestService->common_request(OG_REST_CMD_POWEROFF, POST, [OG_REST_PARAM_CLIENTS => $ips]); + $result = $this->curlRequestService->callOgLive("uninstall $id"); + + if ($result) { + return new Response($result, Response::HTTP_OK); + } else { + return new Response('Failed', Response::HTTP_INTERNAL_SERVER_ERROR); + } + } + /** * @OA\Post( * path="/poweroff", diff --git a/src/OgBootBundle/Service/CurlRequestService.php b/src/OgBootBundle/Service/CurlRequestService.php index 4c05a28..efbce14 100644 --- a/src/OgBootBundle/Service/CurlRequestService.php +++ b/src/OgBootBundle/Service/CurlRequestService.php @@ -64,5 +64,24 @@ class CurlRequestService return $bits; } + public function callOgLive($parameter) + { + #$command = sprintf('%s/bin/oglivecli %s', dirname(dirname(dirname(__DIR__))), escapeshellarg($parameter)); + // Ruta completa al script oglivecli + $ogLiveCliPath = sprintf("%s/bin/oglivecli", dirname(dirname(dirname(__DIR__)))); + // Construye el comando con sudo y la contraseña proporcionada + $sudoPassword = 'qindel'; + // Construye el comando con sudo y la contraseña proporcionada + $command = sprintf( + "echo %s | sudo -S %s %s", + escapeshellarg($sudoPassword), + $ogLiveCliPath, + $parameter // Sin comillas simples alrededor de $parameter + ); + syslog(LOG_INFO, 'command '.$command); + $output = shell_exec($command); + + return $output; + } }