#!/bin/bash #/** #@file oglivecli #@brief Command line tool to manage ogLive clients. #@usage oglivecli Command [Options ...] #@param Command: #@param help show this help #@param config [Parameter] show configuration parameters #@param disk_usage show disk usage information #@param list_installed_oglives list installed ogLive clients #@param check_services_status check status of critical services #@param download show a menu to download an ogLive ISO image from the OpenGnsys website #@param download Iso download a specific ogLive ISO image from the OpenGnsys website #@param install Iso install a new ogLive client from a downloaded ISO image #@param uninstall uuid uninstall an ogLive client using its UUID #@param get_default get index value for default ogLive client #@param set_default uuid set default ogLive client using its UUID #@param get_info uuid get JSON information about an installed ogLive client using its UUID #@warning This script needs "jq" command. #@version 3.0.0 - Updated with to adapt to Ogboot. #@author Ramón M. Gómez - ETSII Univ. Sevilla #@author Qindel formacion y servicios SL #@date 2024-08-06 #*/ ## # Global constants definition. PROG=$(basename "$(realpath "$0")") # Program name. PROGDIR=$(dirname "$(realpath "$0")") # Dir where program is OPENGNSYS=$(realpath $PROGDIR/../) # ogboot main directory. DOWNLOADDIR=$OPENGNSYS/lib # Directory to store ogLive images. #@DOWNLOADURL="https://ognproject.evlt.uma.es/trac/downloads" # Download URL. DOWNLOADURL="https://ognproject.evlt.uma.es/oglive/" 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 # 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\"." } function download() { local OGLIVEFILE TARGETFILE SOURCELENGTH # 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 download_url="$1" OGLIVEFILE=$(basename "$download_url") local oglive_name="${OGLIVEFILE%.iso}" # Nombre del ogLive sin la extensión .iso INSTALLED_PATH="$TFTPDIR/$oglive_name" # Ruta de instalación donde se verifica # Validar que la URL apunte a un archivo ISO. if [[ ! "$OGLIVEFILE" =~ \.iso$ ]]; then raiseError download "La URL no apunta a un archivo ISO." fi # Obtener el tamaño de descarga. SOURCELENGTH=$(curl -k --head --retry 5 --retry-delay 5 --max-time 30 "$download_url" | awk -F: '/Content-Length:/ {print $2}') [ -n "$SOURCELENGTH" ] || raiseError download "No se pudo obtener el tamaño del archivo desde \"$download_url\"." # Verificar si el ogLive ya está instalado if [ -d "$INSTALLED_PATH" ]; then raiseError download "El ogLive \"$oglive_name\" ya está instalado en \"$INSTALLED_PATH\"." fi # Descargar ogLive. TARGETFILE="$DOWNLOADDIR/$OGLIVEFILE" trap "rm -f $TARGETFILE" 1 2 3 6 9 15 curl -k --retry 5 --retry-delay 5 "$download_url" -o "$TARGETFILE" || raiseError download "No se pudo descargar \"$OGLIVEFILE\"." if [ -f "$TARGETFILE" ]; then local file_type=$(file -b "$TARGETFILE") if [[ "$file_type" =~ "ISO 9660" ]] && [[ "$file_type" =~ "ogClient" ]]; then install "$OGLIVEFILE" else raiseError download "El archivo descargado no es un ogLive ISO válido." fi else raiseError download "El archivo no fue descargado correctamente." fi 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 # Usamos grep para filtrar solo los enlaces que contienen archivos ISO, pero eliminamos etiquetas HTML con sed OGLIVE=( $(curl -k --silent "$DOWNLOADURL" | grep -oP '(?<=href=")[^"]*\.iso' | sed 's/^.*\///') ) NISOS=${#OGLIVE[@]} local downloads=() for i in $(seq 1 $NISOS); do installed=false compatible=false # Obtener el nombre completo del archivo ISO OGLIVEFILE="${OGLIVE[i-1]}" # Extraer la distribución, revisión y arquitectura 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) OGLIVEKRNL="${OGLIVEKRNL%-$OGLIVEARCH}" ;; *) OGLIVEARCH="i386" ;; esac OGLIVEDIR="$TFTPDIR/$DEFOGLIVE-${OGLIVEKRNL%%-*}-$OGLIVEARCH-$OGLIVEREV" OGLIVEDIR="${OGLIVEDIR/amd64-/}" # Verificar si el ogLive está instalado y no es un directorio .old if [ -d "$OGLIVEDIR" ] && [[ ! "$OGLIVEDIR" =~ \.old$ ]]; then installed=true else installed=false fi ISOREL=${OGLIVE[i-1]##*-r}; ISOREL=${ISOREL%%.*} [ "$ISOREL" -ge "$MINREL" ] && compatible=true url="$DOWNLOADURL/${OGLIVE[i-1]}" local DATA=$(jq -n \ --arg id "$i" \ --arg filename "$OGLIVEFILE" \ --arg url "$url" \ --argjson installed "$installed" \ --argjson compatible "$compatible" \ '{id: $id, filename: $filename, url: $url, installed: $installed, compatible: $compatible}') downloads+=("$DATA") done jq -n --argjson downloads "$(printf '%s\n' "${downloads[@]}" | jq -s .)" \ '{downloads: $downloads}' } # 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\"." } update_json() { local key=$1 local value=$2 result_json=$(jq --arg key "$key" --arg value "$value" '.[$key] = $value' <<< "$result_json") } add_message() { local message=$1 result_json=$(jq --arg message "$message" '.messages += [$message]' <<< "$result_json") } # 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 JSON_OUTPUT [ $# -ne 1 ] && { echo "{\"error\": \"USAGE_ERROR\", \"message\": \"Usage: install {iso_file}\"}"; exit 400; } OGLIVEFILE=$(realpath "$DOWNLOADDIR/$1") [ $(echo "$OGLIVEFILE" | wc -w) -gt 1 ] && { echo "{\"error\": \"USAGE_ERROR\", \"message\": \"Invalid ISO file: $1\"}"; exit 400; } [ ! -f "$OGLIVEFILE" ] && { echo "{\"error\": \"NOT_FOUND\", \"message\": \"ISO file $1 not found.\"}"; exit 404; } [ ! -r "$OGLIVEFILE" ] && { echo "{\"error\": \"ACCESS_DENIED\", \"message\": \"Cannot read ISO file $1.\"}"; exit 403; } [ ! -w "$(dirname "$INFOFILE")" ] && { echo "{\"error\": \"ACCESS_DENIED\", \"message\": \"Cannot write to configuration directory.\"}"; exit 403; } [ ! -w "$TFTPDIR" ] && { echo "{\"error\": \"ACCESS_DENIED\", \"message\": \"Cannot write to installation directory.\"}"; exit 403; } [ -z "$(file -b "$OGLIVEFILE" | grep "ISO.*ogClient")" ] && { echo "{\"error\": \"INVALID_FILE\", \"message\": \"File is not a valid ogLive ISO image.\"}"; exit 400; } 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) OGLIVEKRNL="${OGLIVEKRNL%-$OGLIVEARCH}" ;; *) OGLIVEARCH="i386" ;; esac #OGLIVEDIR="$TFTPDIR/$DEFOGLIVE-${OGLIVEKRNL%%-*}-$OGLIVEARCH-$OGLIVEREV" #OGLIVEDIR="${OGLIVEDIR/amd64-/}" OGLIVEDIR=$TFTPDIR$(basename "$OGLIVEFILE" .iso) OGINITRD="$OGLIVEDIR/oginitrd.img" [ ! -r "$OGINITRD" ] && OGINITRD="$TFTPDIR/$DEFOGLIVE/oginitrd.img" if [ -r "$OGINITRD" ]; then COMPRESS=$(file -b "$OGINITRD" | awk '{print tolower($1);}') if [[ z$COMPRESS == zascii ]]; then COMPRESS=cat else COMPRESS="$COMPRESS -dc" fi SAMBAPASS=$($COMPRESS "$OGINITRD" | \ cpio -i --to-stdout scripts/ogfunctions 2>/dev/null | \ sed -n '/^[ \t].*OPTIONS=/s/.*pass=\(\w*\).*/\1/p') fi rm -fr "${OGLIVEDIR}.old" 2>/dev/null mv -f "$OGLIVEDIR" "${OGLIVEDIR}.old" 2>/dev/null TMPDIR=$OPENGNSYS/mnt TMPDIR_SQUASHFS=/tmp/ogclient_mount mv -f "$OGLIVEFILE" "$DOWNLOADDIR/oglive.iso" 2>/dev/null || { echo "{\"error\": \"RENAME_FAILED\", \"message\": \"Cannot rename $OGLIVEFILE to oglive.iso.\"}"; exit 500; } OGLIVEFILE="$DOWNLOADDIR/oglive.iso" mkdir -p "$OGLIVEDIR" "$TMPDIR" "$TMPDIR_SQUASHFS" [ ! -d "$OGLIVEDIR" ] && { echo "{\"error\": \"DIR_CREATION_FAILED\", \"message\": \"Failed to create/access directory $OGLIVEDIR.\"}"; exit 500; } [ ! -d "$TMPDIR" ] && { echo "{\"error\": \"DIR_CREATION_FAILED\", \"message\": \"Failed to create/access directory $TMPDIR.\"}"; exit 500; } [ ! -d "$TMPDIR_SQUASHFS" ] && { echo "{\"error\": \"DIR_CREATION_FAILED\", \"message\": \"Failed to create/access directory $TMPDIR_SQUASHFS.\"}"; exit 500; } trap "umount $TMPDIR" 1 2 3 6 9 15 mount "$OGLIVEFILE" >/dev/null 2>&1 || { echo "{\"error\": \"MOUNT_FAILED\", \"message\": \"Failed to mount ISO file.\"}"; exit 500; } cp -va "$TMPDIR/ogclient/"* "$OGLIVEDIR" >/dev/null 2>&1 || { echo "{\"error\": \"COPY_FAILED\", \"message\": \"Cannot copy files to $OGLIVEDIR.\"}"; exit 500; } chmod -R u+w "$OGLIVEDIR" || { echo "{\"error\": \"PERMISSION_CHANGE_FAILED\", \"message\": \"Failed to change permissions for $OGLIVEDIR.\"}"; exit 500; } umount "$TMPDIR" >/dev/null 2>&1 if [ ! -f "$INFOFILE" ]; then rm -f "$TFTPDIR/$DEFOGLIVE" "$TFTPDIR/$OGCLIENT" ln -vfs "$(basename "$OGLIVEDIR")" "$TFTPDIR/$DEFOGLIVE" >/dev/null 2>&1 || { echo "{\"error\": \"LINK_FAILED\", \"message\": \"Linking to $TFTPDIR/$DEFOGLIVE failed.\"}"; exit 500; } ln -vfs "$DEFOGLIVE" "$TFTPDIR/$OGCLIENT" >/dev/null 2>&1 || { echo "{\"error\": \"LINK_FAILED\", \"message\": \"Linking to $TFTPDIR/$OGCLIENT failed.\"}"; exit 500; } fi if [ -n "$SAMBAPASS" ]; then echo -ne "$SAMBAPASS\n$SAMBAPASS\n" | $OPENGNSYS/bin/setsmbpass "$(basename "$OGLIVEDIR")" >/dev/null 2>&1 || { echo "{\"error\": \"SET_SMBPASS_FAILED\", \"message\": \"Failed to set Samba password with SAMBAPASS.\"}"; exit 500; } # else # $OPENGNSYS/bin/setsmbpass "$(basename "$OGLIVEDIR")" >/dev/null 2>&1 || { echo "{\"error\": \"SET_SMBPASS_FAILED\", \"message\": \"Failed to set Samba password without SAMBAPASS.\"}"; exit 500; } fi find -L "$OGLIVEDIR" -type d -exec chmod 755 {} \; >/dev/null 2>&1 || { echo "{\"error\": \"CHMOD_FAILED\", \"message\": \"Failed to change permissions for directories in $OGLIVEDIR.\"}"; exit 500; } find -L "$OGLIVEDIR" -type f -exec chmod 644 {} \; >/dev/null 2>&1 || { echo "{\"error\": \"CHMOD_FAILED\", \"message\": \"Failed to change permissions for files in $OGLIVEDIR.\"}"; exit 500; } OGSQFS="$OGLIVEDIR/ogclient.sqfs" trap "exit 0" SIGINT TMPDIR=/tmp/oglive if mountpoint -q "$TMPDIR"; then umount "$TMPDIR" >/dev/null 2>&1 || { echo "{\"error\": \"UMOUNT_FAILED\", \"message\": \"Failed to unmount $TMPDIR.\"}"; exit 500; } fi mount "$TMPDIR_SQUASHFS" || { echo "{\"error\": \"MOUNT_FAILED\", \"message\": \"Failed to mount $TMPDIR_SQUASHFS.\"}"; exit 500; } RSYNCSERV=$(rsync --version 2>/dev/null | awk '/protocol/ {print $6}') RSYNCCLNT=$($TMPDIR_SQUASHFS/usr/bin/rsync --version 2>/dev/null | awk '/protocol/ {print $6}') if [ -z "$RSYNCSERV" ] || [ "$RSYNCSERV" -gt "${RSYNCCLNT:-1}" ]; then [ -e "$OPENGNSYS/client/bin/rsync-$RSYNCSERV" ] && mv -f "$OPENGNSYS/client/bin/rsync-$RSYNCSERV" "$OPENGNSYS/client/bin/rsync" 2>/dev/null else [ -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 umount "$TMPDIR_SQUASHFS" || sudo rm -rf "$TMPDIR_SQUASHFS" CHECKSUM_FILE="$OGLIVEDIR/ogclient.sqfs.sum" [ -f "$CHECKSUM_FILE" ] && OGLIVEID=$(cat "$CHECKSUM_FILE") || OGLIVEID="N/A" jq -n \ --arg OGLIVEDIST "$OGLIVEDIST" \ --arg OGLIVEKRNL "$OGLIVEKRNL" \ --arg OGLIVEARCH "$OGLIVEARCH" \ --arg OGLIVEREV "$OGLIVEREV" \ --arg OGLIVEDIR "$OGLIVEDIR" \ '{ OGLIVEDIST: $OGLIVEDIST, OGLIVEKRNL: $OGLIVEKRNL, OGLIVEARCH: $OGLIVEARCH, OGLIVEREV: $OGLIVEREV, OGLIVEDIR: $OGLIVEDIR }' > "$OGLIVEDIR/oglive_info.json" [ ! -f "$OGLIVEDIR/oglive_info.json" ] && { echo "{\"error\": \"JSON_CREATION_FAILED\", \"message\": \"Failed to create oglive_info.json.\"}"; exit 500; } sudo chown -R :opengnsys "$OGLIVEDIR" >/dev/null 2>&1 || { echo "{\"error\": \"CHOWN_FAILED\", \"message\": \"Failed to change ownership for $OGLIVEDIR.\"}"; exit 500; } JSON_OUTPUT=$(jq -n \ --arg id "$OGLIVEID" \ --arg dist "$OGLIVEDIST" \ --arg krnl "$OGLIVEKRNL" \ --arg arch "$OGLIVEARCH" \ --arg rev "$OGLIVEREV" \ --arg dir "$OGLIVEDIR" \ '{status: "success", message: {id: $id, distribution: $dist, kernel: $krnl, architecture: $arch, revision: $rev, directory: $dir}}') echo "$JSON_OUTPUT" exit 0 } # Uninstall an ogLive client. function uninstall() { local CHECKSUM DIR DEFAULT_OGLIVE_DIR DEFAULT_CHECKSUM # Validar que se proporcionó exactamente un argumento (el checksum) [ $# -ne 1 ] && { echo "{\"error\": \"BAD_REQUEST\", \"message\": \"usage: uninstall {checksum}\"}"; exit 400; } CHECKSUM=$1 # Verificar acceso a los directorios necesarios [ ! -w $TFTPDIR ] && { echo "{\"error\": \"SERVER_ERROR\", \"message\": \"access installation directory.\"}"; exit 500; } # Verificar el enlace simbólico del ogLive por defecto if [ -L "$TFTPDIR/ogLive" ]; then DEFAULT_OGLIVE_DIR=$(readlink -f "$TFTPDIR/ogLive") DEFAULT_CHECKSUM_FILE="$DEFAULT_OGLIVE_DIR/ogclient.sqfs.sum" # Verificar si el archivo de checksum del ogLive por defecto existe if [ -f "$DEFAULT_CHECKSUM_FILE" ]; then DEFAULT_CHECKSUM=$(cat "$DEFAULT_CHECKSUM_FILE" | cut -d ' ' -f 1) # Comparar el checksum proporcionado con el del ogLive por defecto if [ "$CHECKSUM" == "$DEFAULT_CHECKSUM" ]; then echo "{\"error\": \"FORBIDDEN\", \"message\": \"Cannot uninstall the default ogLive client.\"}" exit 403 fi fi fi # Buscar el directorio correspondiente al checksum DIR=$(find $TFTPDIR -type f -name 'ogclient.sqfs.sum' -exec grep -l "$CHECKSUM" {} \; | xargs -I{} dirname {}) # Si no se encuentra el directorio, devolver error 404 if [ -z "$DIR" ]; then echo "{\"error\": \"NOT_FOUND\", \"message\": \"ogLive client with checksum $CHECKSUM not found.\"}" exit 404 fi # Eliminar archivos y directorio, redirigiendo la salida a /dev/null rm -vfr $DIR > /dev/null 2>&1 # Comprobar si la eliminación tuvo éxito, si no, devolver error 500 if [ -d "$DIR" ]; then echo "{\"error\": \"SERVER_ERROR\", \"message\": \"Failed to uninstall ogLive client in $DIR.\"}" exit 500 fi # Devolver mensaje de éxito echo "{\"message\": \"ogLive client uninstalled successfully.\", \"details\": \"Removed directory: $DIR\"}" exit 200 } # Get information about the default ogLive client. function get_default() { local DEFAULT_LINK="$TFTPDIR/$DEFOGLIVE" local DIR OGLIVEJSON OGLIVEDIST OGLIVEKRNL OGLIVEARCH OGLIVEREV OGLIVEDIR CHECKSUM_FILE CHECKSUM # Verificar que el enlace simbólico del ogLive por defecto existe. if [ ! -L "$DEFAULT_LINK" ]; then echo "{\"error\": \"Default ogLive client link not found.\"}" exit 1 fi # Obtener el directorio real al que apunta el enlace simbólico. DIR=$(readlink -f "$DEFAULT_LINK") # Si no se encuentra el directorio, devolver error. if [ -z "$DIR" ]; then echo "{\"error\": \"Default ogLive client directory not found.\"}" exit 1 fi # Verificar que el archivo oglive_info.json existe en el directorio. OGLIVEJSON="$DIR/oglive_info.json" if [ ! -f "$OGLIVEJSON" ]; then echo "{\"error\": \"oglive_info.json not found in $DIR.\"}" exit 1 fi # Leer los datos del archivo JSON. OGLIVEDIST=$(jq -r '.OGLIVEDIST' "$OGLIVEJSON") OGLIVEKRNL=$(jq -r '.OGLIVEKRNL' "$OGLIVEJSON") OGLIVEARCH=$(jq -r '.OGLIVEARCH' "$OGLIVEJSON") OGLIVEREV=$(jq -r '.OGLIVEREV' "$OGLIVEJSON") OGLIVEDIR=$(jq -r '.OGLIVEDIR' "$OGLIVEJSON") # Obtener el checksum del archivo ogclient.sqfs.sum CHECKSUM_FILE="$DIR/ogclient.sqfs.sum" if [ -f "$CHECKSUM_FILE" ]; then CHECKSUM=$(cat "$CHECKSUM_FILE" | cut -d ' ' -f 1) else CHECKSUM="" fi # Construir el JSON con la información. local INFO=$(cat << EOT { "id": "$CHECKSUM", "distribution": "$OGLIVEDIST", "kernel": "$OGLIVEKRNL", "architecture": "$OGLIVEARCH", "revision": "$OGLIVEREV", "directory": "$OGLIVEDIR" } EOT ) # Devolver la información en formato JSON. echo "$INFO" } # Set default ogLive client by checksum. function set_default() { local CHECKSUM=$1 local DIR # Validar que se proporcionó exactamente un argumento (el checksum) [ $# -ne 1 ] && { echo "{\"error\": \"usage: set-default {checksum}\"}"; exit 1; } # Verificar acceso a los directorios necesarios [ ! -w $TFTPDIR ] && { echo "{\"error\": \"access installation directory.\"}"; exit 1; } # Buscar el directorio correspondiente al checksum DIR=$(find $TFTPDIR -type f -name 'ogclient.sqfs.sum' -exec grep -l "$CHECKSUM" {} \; | xargs -I{} dirname {} | grep -v ".old") # Si no se encuentra el directorio, devolver error if [ -z "$DIR" ]; then echo "{\"error\": \"ogLive client with checksum $CHECKSUM not found.\"}" exit 1 fi # Eliminar el enlace simbólico existente y crear uno nuevo rm -f $TFTPDIR/$DEFOGLIVE > /dev/null 2>&1 ln -vfs $(basename $DIR) $TFTPDIR/$DEFOGLIVE > /dev/null 2>&1 # Comprobar si el enlace simbólico se creó correctamente if [ "$(readlink -f $TFTPDIR/$DEFOGLIVE)" == "$(readlink -f $DIR)" ]; then echo "{\"message\": \"ogLive client set as default successfully.\", \"details\": \"$DIR\"}" else echo "{\"error\": \"Failed to set default ogLive client.\"}" exit 1 fi } # Get disk usage information function disk_usage() { DISK_INFO=$(df / | awk 'NR==2{print "{\"total\":\""$2"\", \"used\":\""$3"\", \"available\":\""$4"\", \"percentage\":\""$5"\"}"}') echo $DISK_INFO } # Function to list installed ogLive clients and the default ogLive client function list_installed_oglives() { local INST NF DEF # Verificar si el directorio TFTPDIR es accesible if [ ! -d "$TFTPDIR" ]; then echo '{"error": "SERVER_ERROR", "message": "TFTP directory not found or not accessible."}' exit 500 fi # Buscar directorios de ogLive instalados INST=$(find "$TFTPDIR/" -type d -name "$DEFOGLIVE-*" -a ! -name "*.old" -printf "%f\n" 2>/dev/null | sort) if [ -z "$INST" ]; then echo '{"error": "NOT_FOUND", "message": "No installed ogLive clients found."}' exit 404 fi local installed_ogLives=() local oglive_count=0 for i in $INST; do local OGLIVEDIR="$TFTPDIR/$i" local OGLIVEDIST="" local OGLIVEKRNL="" local OGLIVEARCH="" local OGLIVEREV="" local CHECKSUM="" local OGLIVEJSON="$OGLIVEDIR/oglive_info.json" local CHECKSUM_FILE="$OGLIVEDIR/ogclient.sqfs.sum" # Intentar obtener el ID desde el archivo ogclient.sqfs.sum if [ -f "$CHECKSUM_FILE" ]; then CHECKSUM=$(cut -d ' ' -f 1 "$CHECKSUM_FILE") else local DATA=$(jq -n \ --arg id "unknown" \ --arg error "CHECKSUM_NOT_FOUND" \ --arg message "Checksum file not found or invalid in $OGLIVEDIR. Manual deletion is recommended." \ '{id: $id, error: $error, message: $message}') installed_ogLives+=("$DATA") continue fi # Verificar que el archivo oglive_info.json existe en el directorio if [ -f "$OGLIVEJSON" ]; then OGLIVEDIST=$(jq -r '.OGLIVEDIST' "$OGLIVEJSON") OGLIVEKRNL=$(jq -r '.OGLIVEKRNL' "$OGLIVEJSON") OGLIVEARCH=$(jq -r '.OGLIVEARCH' "$OGLIVEJSON") OGLIVEREV=$(jq -r '.OGLIVEREV' "$OGLIVEJSON") # Crear el JSON con los datos del ogLive local DATA=$(jq -n \ --arg id "$CHECKSUM" \ --arg dist "$OGLIVEDIST" \ --arg krnl "$OGLIVEKRNL" \ --arg arch "$OGLIVEARCH" \ --arg rev "$OGLIVEREV" \ --arg dir "$OGLIVEDIR" \ '{id: $id, distribution: $dist, kernel: $krnl, architecture: $arch, revision: $rev, directory: $dir}') installed_ogLives+=("$DATA") else # Informar que el archivo oglive_info.json no existe y que el ogLive puede estar corrupto local DATA=$(jq -n \ --arg id "$CHECKSUM" \ --arg error "CORRUPT_OG_LIVE" \ --arg message "oglive_info.json not found in $OGLIVEDIR. ogLive may be corrupted." \ '{id: $id, error: $error, message: $message}') installed_ogLives+=("$DATA") continue fi oglive_count=$((oglive_count + 1)) # Verificar si es el ogLive por defecto [ -n "$(stat -c "%N" "$TFTPDIR/$DEFOGLIVE" | awk '$3~/'$i'/ {print}')" ] && DEF="$i" done # Verificar si se encontraron ogLives if [ "$oglive_count" -eq 0 ]; then echo '{"error": "NOT_FOUND", "message": "No valid ogLive clients found."}' exit 404 fi local default_oglive=$(basename "$(readlink -f "$TFTPDIR/$DEFOGLIVE")") # Crear el JSON final con la lista de ogLives instalados y el ogLive por defecto jq -n \ --arg default_oglive "$default_oglive" \ --argjson installed_ogLives "$(printf '%s\n' "${installed_ogLives[@]}" | jq -s .)" \ '{ default_oglive: $default_oglive, installed_ogLives: $installed_ogLives }' exit 0 } # Get information about an installed ogLive client. function get_info() { local CHECKSUM="$1" local DIR OGLIVEDIST OGLIVEKRNL OGLIVEARCH OGLIVEREV OGLIVEDIR OGLIVEJSON # Verificar que se proporcionó un checksum. [ -z "$CHECKSUM" ] && { echo "{\"error\": \"usage: get_info {checksum}\"}"; exit 1; } # Verificar acceso al directorio de instalación. [ ! -w $TFTPDIR ] && { echo "{\"error\": \"access installation directory.\"}"; exit 1; } # Buscar el directorio correspondiente al checksum, excluyendo los que terminan en .old. DIR=$(find $TFTPDIR -type f -name 'ogclient.sqfs.sum' -exec grep -l "$CHECKSUM" {} \; | grep -v '.old' | xargs -I{} dirname {}) # Si no se encuentra el directorio, devolver error. if [ -z "$DIR" ]; then echo "{\"error\": \"ogLive client with checksum $CHECKSUM not found.\"}" exit 1 fi # Verificar que el archivo oglive_info.json existe en el directorio. OGLIVEJSON="$DIR/oglive_info.json" if [ ! -f "$OGLIVEJSON" ]; then echo "{\"error\": \"oglive_info.json not found in $DIR.\"}" exit 1 fi # Leer los datos del archivo JSON. OGLIVEDIST=$(jq -r '.OGLIVEDIST' "$OGLIVEJSON") OGLIVEKRNL=$(jq -r '.OGLIVEKRNL' "$OGLIVEJSON") OGLIVEARCH=$(jq -r '.OGLIVEARCH' "$OGLIVEJSON") OGLIVEREV=$(jq -r '.OGLIVEREV' "$OGLIVEJSON") OGLIVEDIR=$(jq -r '.OGLIVEDIR' "$OGLIVEJSON") # Construir el JSON con los datos del ogLive y el checksum (id). local INFO=$(cat << EOT { "id": "$CHECKSUM", "distribution": "$OGLIVEDIST", "kernel": "$OGLIVEKRNL", "architecture": "$OGLIVEARCH", "revision": "$OGLIVEREV", "directory": "$OGLIVEDIR" } EOT ) # Devolver la información en formato JSON. echo "$INFO" } # Function to check the status of services function check_services_status() { local SERVICES=("tftpd-hpa.service" "nginx.service") declare -A STATUS_MAP for service in "${SERVICES[@]}"; do if systemctl list-units --type=service --all | grep -q "$service"; then STATUS=$(systemctl is-active "$service") STATUS_MAP[$service]=$STATUS else STATUS_MAP[$service]="not installed" fi done local json_output=$(jq -n \ --arg tftpboot "${STATUS_MAP['tftpd-hpa.service']}" \ --arg nginx "${STATUS_MAP['nginx.service']}" \ '{ tftpboot: $tftpboot, nginx: $nginx }') echo "$json_output" } # Main program. # Access control. if [ "$USER" = "root" ] || [ "$USER" = "opengnsys" ] || groups $USER | grep -qw "opengnsys"; then ACCESS="root" else raiseError access "Need to be root, opengnsys or a member of the opengnsys group." fi # Check dependencies. which sponge &>/dev/null || raiseError notfound "Need to install \"moreutils\"." # Commands control. shopt -s extglob CMDS='+(help|disk_usage|list_installed_oglives|check_services_status|download|install|uninstall|get_default|set_default|get_info)' case "$1" in $CMDS) COMMAND="${1/-/}"; shift; $COMMAND "$@" ;; *) raiseError usage ;; esac exit $?