From fe61288f83d90ddff402cc63494b54d5f89dc1b1 Mon Sep 17 00:00:00 2001 From: Natalia Serrano Date: Tue, 25 Feb 2025 10:11:26 +0100 Subject: [PATCH 01/18] refs #1591 remove "imagenes sincronizadas" --- client/engine/Rsync.lib | 900 ------------------ .../shared/scripts/ImagenesSincronizadas.lib | 368 ------- client/shared/scripts/createBaseImage | 166 ---- client/shared/scripts/createDiffImage | 201 ---- client/shared/scripts/ogCrearImagenBasica | 167 ---- client/shared/scripts/ogCrearSoftIncremental | 185 ---- client/shared/scripts/ogRestaurarImagenBasica | 206 ---- .../shared/scripts/ogRestaurarSoftIncremental | 209 ---- client/shared/scripts/restoreBaseImage | 175 ---- client/shared/scripts/restoreDiffImage | 159 ---- sources/interface/CrearImagenBasica | 98 -- sources/interface/CrearSoftIncremental | 98 -- sources/interface/RestaurarImagenBasica | 89 -- sources/interface/RestaurarSoftIncremental | 82 -- 14 files changed, 3103 deletions(-) delete mode 100755 client/engine/Rsync.lib delete mode 100755 client/shared/scripts/ImagenesSincronizadas.lib delete mode 100755 client/shared/scripts/createBaseImage delete mode 100755 client/shared/scripts/createDiffImage delete mode 100755 client/shared/scripts/ogCrearImagenBasica delete mode 100755 client/shared/scripts/ogCrearSoftIncremental delete mode 100755 client/shared/scripts/ogRestaurarImagenBasica delete mode 100755 client/shared/scripts/ogRestaurarSoftIncremental delete mode 100755 client/shared/scripts/restoreBaseImage delete mode 100755 client/shared/scripts/restoreDiffImage delete mode 100755 sources/interface/CrearImagenBasica delete mode 100755 sources/interface/CrearSoftIncremental delete mode 100755 sources/interface/RestaurarImagenBasica delete mode 100755 sources/interface/RestaurarSoftIncremental diff --git a/client/engine/Rsync.lib b/client/engine/Rsync.lib deleted file mode 100755 index 2478956..0000000 --- a/client/engine/Rsync.lib +++ /dev/null @@ -1,900 +0,0 @@ -#!/bin/bash - -#/** -# rsync -#@brief Función para utilizar la versión de rsync situada en $OPENGNSYS/bin en vez de la del sistema operativo. -#@param los mismos que el comando rsync del sistema operativo. -#@warning Solo en clientes ogLive de 32 bits. -#@return instrucción para ser ejecutada. -#*/ -function rsync () -{ -local RSYNC -[ "$(arch)" == "i686" -a -x $OPENGNSYS/bin/rsync ] && RSYNC=$OPENGNSYS/bin/rsync -RSYNC=${RSYNC:-$(which rsync)} - -$RSYNC "$@" -} - - -#/** -# ogCreateFileImage [ REPO | CACHE ] image_name extension size -#@brief Crear el archivo -#@param 1 Repositorio [ REPO | CACHE ] -#@param 2 Nombre Imagen -#@param 3 Tipo imagen [ img |diff ] -#@param 4 Tamaño de la imagen -#@return instrucción para ser ejecutada. -#*/ - -function ogCreateFileImage () { -local SIZEREQUIRED IMGDIR IMGFILE DIRMOUNT LOOPDEVICE IMGSIZE IMGEXT KERNELVERSION - -if [ "$*" == "help" ]; then - ogHelp "$FUNCNAME" \ - "$FUNCNAME [ REPO|CACHE ] image_name extension size(K)" \ - "$FUNCNAME REPO Ubuntu12 img 300000" \ - "$FUNCNAME CACHE Windows7 diff 20000000" - return -fi - - -if [ $# -lt 4 ]; then - ogRaiseError $OG_ERR_FORMAT "$MSG_FORMAT: $FUNCNAME [ REPO|CACHE ] image_name extension size(k)" - return $? -fi - -SIZEREQUIRED=$4 -[ $SIZEREQUIRED -lt 300000 ] && SIZEREQUIRED=300000 -KERNELVERSION=$(uname -r| awk '{printf("%d",$1);sub(/[0-9]*\./,"",$1);printf(".%02d",$1)}') - -if [ "$1" == "CACHE" -o "$1" == "cache" ]; then - IMGDIR="$(ogGetParentPath "$1" "/$2")" - [ "$3" == "img" ] && IMGEXT="img" || IMGEXT="img.diff" - IMGFILE="${IMGDIR}/$(basename "/$2").$IMGEXT" - ## Si no existe, crear subdirectorio de la imagen. - if [ $? != 0 ]; then - ogEcho log session " $MSG_HELP_ogMakeDir \"$1 $(dirname "$2")." - ogMakeDir "$1" "$(dirname "/$2")" || return $(ogRaiseError $OG_ERR_NOTWRITE "$3 /$4"; echo $?) - IMGDIR="$(ogGetParentPath "$1" "/$2")" || return $(ogRaiseError $OG_ERR_NOTWRITE "$3 /$4"; echo $?) - fi - DIRMOUNT="/tmp/$(ogGetMountImageDir "$2" "$3")" - mkdir -p "$DIRMOUNT" - LOOPDEVICE=$(losetup -f) - # Si existe el fichero de la imagen se hace copia de seguridad, si no existe se crea. - if [ -f "$IMGFILE" ]; then - # Si la imagen esta montada la desmonto - if [ -r "$DIRMOUNT/ogimg.info" ]; then - umount "$DIRMOUNT" - [ $? -ne 0 ] && return $(ogRaiseError $OG_ERR_DONTUNMOUNT_IMAGE "$1 $2.$IMGEXT"; echo $?) - fi - - if [ "$BACKUP" == "true" -o "$BACKUP" == "TRUE" ]; then - # Copia seguridad - ogEcho log session " $MSG_SCRIPTS_FILE_RENAME \"$IMGFILE\" -> \"$IMGFILE.ant\"." - cp -f "$IMGFILE" "$IMGFILE.ant" - mv -f "$IMGFILE.torrent" "$IMGFILE.torrent.ant" 2>/dev/null - rm -f "$IMGFILE.sum" - fi - - IMGSIZE=$(ls -l --block-size=1024 "$IMGFILE" | awk '{print $5}') - if [ $IMGSIZE -lt $SIZEREQUIRED ];then - ogEcho log session " $MSG_SYNC_RESIZE" - echo " truncate --size=>$SIZEREQUIRED k $IMGFILE" - truncate --size=">$SIZEREQUIRED"k "$IMGFILE" &> $OGLOGCOMMAND - # FS de la imagen segun el contenido del archivo .img - if file "$IMGFILE" |grep -i -e " ext4 filesystem " 2>&1 > /dev/null ; then - losetup $LOOPDEVICE "$IMGFILE" - echo " resize2fs -f $LOOPDEVICE" - resize2fs -f $LOOPDEVICE &> $OGLOGCOMMAND - - else - echo " ogMountImage $1 "$2" $3" - ogMountImage $1 "$2" $3 - echo " btrfs filesystem resize max $DIRMOUNT" - btrfs filesystem resize max "$DIRMOUNT" &> $OGLOGCOMMAND - fi - fi - else - touch "$IMGFILE" - echo " truncate --size=>$SIZEREQUIRED k $IMGFILE" - truncate --size=">$SIZEREQUIRED"k "$IMGFILE" &> $OGLOGCOMMAND - #Formateamos imagen - losetup $LOOPDEVICE $IMGFILE - # FS de la imagen segun la configuracion y la version del kernel: < 3.7 ext4, si >= btrfs - [ $KERNELVERSION \< 3.07 ] && IMGFS="EXT4" || IMGFS=${IMGFS:-"BTRFS"} - - if [ "$IMGFS" == "EXT4" ]; then - echo " mkfs.ext4 -i 4096 -b 4096 -L "${2##*\/}" $LOOPDEVICE" - mkfs.ext4 -i 4096 -b 4096 -L "${2##*\/}" $LOOPDEVICE 2>&1 |tee -a $OGLOGCOMMAND - else - echo " mkfs.btrfs -L ${2##*\/} $LOOPDEVICE " - mkfs.btrfs -L "${2##*\/}" $LOOPDEVICE 2>&1 | tee -a $OGLOGCOMMAND - fi - fi - # Monto la imagen - ogMountImage $1 "$2" $3 &>/dev/null - [ $? -eq 0 ] || return $( ogRaiseError $OG_ERR_IMAGE "$3 $4"; echo $?) - echo "mounted"> $IMGFILE.lock - - # Si existe dispositivo de loop lo borro. - [ $LOOPDEVICE ] && losetup -d $LOOPDEVICE 2>&1 &>/dev/null - -else - [ -z $REPOIP ] && REPOIP=$(ogGetRepoIp) - echo " hose $REPOIP 2009 --out sh -c \"echo -ne CREATE_IMAGE $2 $3 $SIZEREQUIRED \"" - hose $REPOIP 2009 --out sh -c "echo -ne CREATE_IMAGE \"$2\" $3 $SIZEREQUIRED" -fi - -} - - -#/** -# ogCreateInfoImage -#@brief Crear listados con la informacion de la imagen, los situa en /tmp. -#@param 1 num_disk -#@param 2 num_part -#@param 3 Repositorio [ REPO | CACHE ] (opcional en las completas) -#@param 4 Nombre Imagen Basica (opcional en las completas) -#@param 5 Tipo imagen [ img | diff ] -#@version 1.0.6 rsync opcion W (whole) para que sea más rápido -#*/ -function ogCreateInfoImage () { -local IMGTYPE IMGDIRAUX DIRMOUNT DESTRSYNC PASSWORD USERRSYNC ORIG FSTYPE PART DIREMPTY IMGLIST IMGINFO IMGACL KERNELVERSION -# Ayuda o menos de 5 parametros y la imagen no es basica -if [ "$*" == "help" -o $# -lt 5 -a "$3" != "img" ]; then - ogHelp "$FUNCNAME" \ - "$FUNCNAME num_disk num_part [ REPO|CACHE ] [ base_image_name ] extension " \ - "base image -> $FUNCNAME 1 2 img" \ - "diff image -> $FUNCNAME 1 1 CACHE Windows7 diff " - return -fi - -if [ $# -lt 3 ]; then - ogRaiseError $OG_ERR_FORMAT "$MSG_FORMAT: $FUNCNAME num_disk num_part [ REPO|CACHE ] [ base_image_name] extension " - return $? -fi - -# Comprobar errores. -PART=$(ogDiskToDev "$1" "$2") || return $? -ORIG=$(ogMount $1 $2) || return $? - -if [ $3 == "img" ]; then - IMGTYPE="img" -else - # Comprobamos que las extension sea valida - ogCheckStringInGroup $5 "img diff" || return $( ogRaiseError $OG_ERR_FORMAT "$MSG_SYNC_EXTENSION"; echo $?) - IMGTYPE=$5 - if [ "$IMGTYPE" == "diff" ]; then - # Imagen completa con la que comparo la particion. - IMGDIRAUX="$(ogGetMountImageDir "$4" "img")" - if [ "$3" == "CACHE" -o "$3" == "cache" ]; then - DIRMOUNT="/tmp/$IMGDIRAUX" - DESTRSYNC="$DIRMOUNT" - else - [ -z $REPOIP ] && REPOIP=$(ogGetRepoIp) - DIRMOUNT="$OGIMG/$IMGDIRAUX" - USERRSYNC="opengnsys" - PASSWORD="--password-file=/scripts/passrsync" - DESTRSYNC="$USERRSYNC@$REPOIP::ogimages/$IMGDIRAUX" - fi - fi -fi - - -FSTYPE=$(ogGetFsType $1 $2) - -# Creamos la lista del contenido y lo situamos en la particion a copiar. -DIREMPTY="/tmp/empty$$" -IMGLIST="/tmp/ogimg.list" -IMGINFO="/tmp/ogimg.info" -IMGACL="/tmp/ogimg.acl" - -# Borramos archivos antiguos. -rm -f /tmp/ogimg.* 2>/dev/null -rm -f $ORIG/ogimg.* 2>/dev/null - -# En las diferenciales no sabemos el tamaño -> ponemos una constante. -SIZEDATA=${SIZEDATA:-"SIZEDATA"} - -# Incluimos información de la imagen. Segun el kernel sera ext4 o btrfs. -KERNELVERSION=$(uname -r| awk '{printf("%d",$1);sub(/[0-9]*\./,"",$1);printf(".%02d",$1)}') -[ $KERNELVERSION \< 3.07 ] && IMGFS="EXT4" || IMGFS=${IMGFS:-"BTRFS"} -echo "#$IMGFS:NO:$FSTYPE:$SIZEDATA" > $IMGINFO - -if [ "$IMGTYPE" == "img" ]; then - # Imagen Basica - echo " rsync -aHAXWvn --delete $ORIG/ $DIREMPTY >> $IMGINFO" - rsync -aHAXWvn --delete $ORIG/ $DIREMPTY>> $IMGINFO - sed -i -e s/"^sent.*.bytes\/sec"//g -e s/^total.*.speedup.*.$//g -e s/"sending.*.list"//g $IMGINFO - sed -i '/^\.\//d' $IMGINFO - -else - # Imagen Diferencial - echo " rsync -aHAXWvn --delete $ORIG/ $DESTRSYNC a $IMGLIST" - rsync -aHAXWvn $PASSWORD --delete "$ORIG/" "$DESTRSYNC" >> $IMGLIST - sed -i -e s/"^sent.*.bytes\/sec"//g -e s/^total.*.speedup.*.$//g -e s/"sending.*.list"//g $IMGLIST - sed -i '/^\.\//d' $IMGLIST - - # Creamos informacion de la imagen - grep -e '\->' -e '\=>' $IMGLIST > /tmp/ogimg.ln - grep -e ^deleting $IMGLIST | sed s/^deleting\ //g | grep -v ^ogimg > /tmp/ogimg.rm - #grep -v -e '\->' -e '\=>' -e ^deleting $IMGLIST >> $IMGINFO - grep -v -e '\->' -e '\=>' -e ^deleting -e ^created $IMGLIST >> $IMGINFO - - rm -f $IMGLIST - - # Comprobamos que los ficheros de diferencias no esten vacios o salimos con error. - if [ $(grep -v -e "^$" -e "^#" $IMGINFO /tmp/ogimg.ln /tmp/ogimg.rm |wc -l) -eq 0 ]; then - ogRaiseError $OG_ERR_NOTDIFFERENT "$1 $2 $3 $4 $5" - return $? - fi - -fi - -# Guardamos el contenido de las acl (Solo win) Necesario particion desmontada (esta asi) -ogUnmount $1 $2 -if [ $FSTYPE == "NTFS" ]; then - echo " ntfs-3g.secaudit -b $PART /" - ntfs-3g.secaudit -b $PART / > $IMGACL -fi - -} - - -#/** -# ogAclFilter -#@brief Del fichero de acl de la partición extraemos las acl de los ficheros de la diferencial (falla: no se usa) -#@param No. -#@return (nada) -#*/ -function ogAclFilter () { -local IMGACL IMGINFO FILES ACLTMP - -# Ayuda -if [ "$*" == "help" ]; then - ogHelp "$FUNCNAME" "$FUNCNAME" - return -fi - -IMGACL="/tmp/ogimg.acl" -IMGINFO="/tmp/ogimg.info" -FILES="/tmp/files$$" -ACLTMP="/tmp/acl$$.tmp" -ACLFILES="/tmp/aclfiles$$" - -# comprobamos que existan los archivos de origen. Si no salimos sin error. -[ -f $IMGACL -a -f $IMGINFO ] || return 0 - -echo "" > $ACLTMP -grep -n -e "File" -e "Directory" $IMGACL > $ACLFILES - -# Al listado de ficheros le quitamos las líneas sobrantes: comentarios y lineas vacias. -sed -e s/"^#.*$"//g -e '/^$/d' $IMGINFO > $FILES - - -# Recorremos el listado y extraemos la acl correspondiente al fichero o directorio. -while read LINE; do - read END INI <<< "$(grep -A 1 "$LINE" $ACLFILES | awk -F : '!(NR%2){print $1" "p}{p=$1}' )" - let NUM=$END-$INI-1 - # Si algún archivo no se encuentra, el error lo mandamos a /dev/null - sed -n -e $INI,+"$NUM"p $IMGACL 2>/dev/null >> $ACLTMP - echo "aclfilter: $LINE" >> $OGLOGCOMMAND -done < $FILES - -cp $ACLTMP $IMGACL -rm -f $FILES $ACLTMP $ACLFILES -} - - -#/** -# ogRestoreInfoImage -#@brief Crear o modificar enlaces y restaurar las ACL. La informacion esta ya copiada a la particion. -#@param 1 num_disk -#@param 2 num_part -#*/ -function ogRestoreInfoImage () { -local DEST PART IMGACL IMGLN OPTLN LINEA DESTLN ORIGLN TYPELN - -# Ayuda o menos de 5 parametros y la imagen no es basica -if [ "$*" == "help" ]; then - ogHelp "$FUNCNAME" \ - "$FUNCNAME num_disk num_part" \ - "base image -> $FUNCNAME 1 2 " \ - "diff image -> $FUNCNAME 1 1 " - return -fi - -if [ $# -lt 2 ]; then - ogRaiseError $OG_ERR_FORMAT "$MSG_FORMAT: $FUNCNAME num_disk num_part " - return $? -fi - -# Control de errores. -PART=$(ogDiskToDev "$1" "$2") || return $? -DEST=$(ogMount $1 $2) || return $? - -IMGACL="ogimg.acl" -IMGLN="ogimg.ln" -IMGINFO="ogimg.info" - -# Copiamos informacion de la imagen a /tmp (para basicas) -[ -r $DEST/$IMGINFO ] && cp $DEST/ogimg.* /tmp - -#Creamos o modificamos los enlaces. -# La imagen diferencial tiene ogimg.ln -# para la completa lo generamos con los enlaces que contengan /mnt/ -[ -r "/tmp/$IMGLN" ] || grep -e "->" -e "=>" "/tmp/$IMGINFO"|grep "/mnt/" > "/tmp/$IMGLN" -if [ $(wc -l "/tmp/$IMGLN"|cut -f1 -d" ") -ne 0 ]; then - while read LINEA - do - ORIGLN="${LINEA#*> }" - # Si origen hace referencia a la particion lo modificamos - echo $ORIGLN|grep "/mnt/"> /dev/null && ORIGLN="$DEST/${ORIGLN#/mnt/*/}" - # rsync marca - los enlaces simbolicos y = enlaces "duros" - LINEA="${LINEA%>*}" - TYPELN="${LINEA##* }" - DESTLN="${LINEA% *}" - - if [ "$TYPELN" == "-" ] - then - OPTLN='-s' - else - OPTLN='' - fi - cd "$DEST/$(dirname "$DESTLN")" - rm -f "$(basename "$DESTLN")" - ln $OPTLN "$ORIGLN" "$(basename "$DESTLN")" - echo -n "." - done < "/tmp/$IMGLN" 2>/dev/null - echo "" - -fi -cd / -} - - -#/** -# ogRestoreAclImage -#@brief Restaurar las ACL. La informacion esta ya copiada al directorio /tmp -#@param 1 num_disk -#@param 2 num_part -#*/ -function ogRestoreAclImage () { -local PART IMGACL - -if [ "$*" == "help" ]; then - ogHelp "$FUNCNAME" \ - "$FUNCNAME num_disk num_part" \ - "$FUNCNAME 1 1" - return -fi - -PART=$(ogDiskToDev "$1" "$2") || return $? -IMGACL="ogimg.acl" - -# Restauramos acl -if [ "$(ogGetFsType $1 $2)" == "NTFS" -a -f "/tmp/$IMGACL" ] ; then - cd / - ogUnmount "$1" "$2" - echo "ntfs-3g.secaudit -se $PART /tmp/$IMGACL" - ntfs-3g.secaudit -se $PART /tmp/$IMGACL - # Para evitar que de falso error - echo "" -fi -} - - -#/** -# ogSyncCreate -#@brief sincroniza los datos de la partición a la imagen para crearla. La imagen esta montada en un directorio. -#@param 1 num_disk -#@param 2 num_part -#@param 3 Repositorio [ REPO | CACHE ] -#@param 4 Nombre Imagen -#@param 5 Tipo imagen [ img | diff ] -#*/ -function ogSyncCreate () { -local ORIG DIRAUX DIRMOUNT DESTRSYNC USERRSYNC PASSWORD OPTRSYNC RETVAL - -# Limpiamos los archivo de log -echo "" >$OGLOGCOMMAND; - -if [ "$*" == "help" ]; then - ogHelp "$FUNCNAME" \ - "$FUNCNAME num_disk num_part [ REPO|CACHE ] image_name extension " \ - "$FUNCNAME 1 2 REPO Ubuntu12 img" \ - "$FUNCNAME 1 1 CACHE Windows7 diff " - return -fi - - -if [ $# -lt 4 ]; then - ogRaiseError $OG_ERR_FORMAT "$MSG_FORMAT: $FUNCNAME num_disk num_part [ REPO|CACHE ] image_name extension " - return $? -fi - -ORIG=$(ogMount $1 $2) || return $? - -DIRMOUNT="$(ogGetMountImageDir "$4" $5)" -# Si la imagen es diferencial la lista de ficheros a transferir esta dentro de la imagen. -if [ "$5" == "diff" ]; then - FILESFROM=" --files-from=/tmp/ogimg.info" - # Borramos los directorios - sed -i '/\/$/d' /tmp/ogimg.info -else - FILESFROM="" -fi - -if [ "$3" == "CACHE" -o "$3" == "cache" ]; then - DESTRSYNC="/tmp/$DIRMOUNT" -else - [ -z $REPOIP ] && REPOIP=$(ogGetRepoIp) - PASSWORD="--password-file=/scripts/passrsync" - [ "$ogrsyncz" == "true" ] && OPTRSYNC="z " - [ "$ogrsyncw" == "true" ] && OPTRSYNC="W$OPTRSYNC" - USERRSYNC="opengnsys" - DESTRSYNC="$USERRSYNC@$REPOIP::ogimages/$DIRMOUNT" -fi -# Sincronizamos los datos de la partición a la imagen -echo " rsync -aHAX$OPTRSYNC --progress --inplace --delete $FILESFROM $ORIG/ $DESTRSYNC" -rsync -aHAX$OPTRSYNC $PASSWORD --progress --inplace --delete $FILESFROM "$ORIG/" "$DESTRSYNC" 2>$OGLOGCOMMAND | egrep "^deleting|^sent|^sending|^total|%" |tee -a $OGLOGCOMMAND -RETVAL=${PIPESTATUS[0]} -echo " rsync -aHAX$OPTRSYNC --inplace /tmp/ogimg* $DESTRSYNC" -rsync -aHAX$OPTRSYNC $PASSWORD --inplace /tmp/ogimg* "$DESTRSYNC" - -return $RETVAL -} - - -#/** -# ogSyncRestore -#@brief sincroniza los datos de la imagen a la partición para restaurarla. -#@param 1 Repositorio [ REPO | CACHE ] -#@param 2 Nombre Imagen -#@param 3 Tipo imagen [ img | diff ] -#@param 4 num_disk -#@param 5 num_part -#*/ -function ogSyncRestore () { -local DIRMOUNT ORIG DESTRSYNC PASSWORD OPTRSYNC USERRSYNC IMGINFO FILESFROM - -# Limpiamos los archivo de log -echo "" >$OGLOGCOMMAND; - -if [ "$*" == "help" ]; then - ogHelp "$FUNCNAME" \ - "$FUNCNAME [ REPO|CACHE ] image_name extension num_disk num_part " \ - "$FUNCNAME REPO Ubuntu12 img 1 2" \ - "$FUNCNAME CACHE Windows7 diff 1 1" - return -fi - - -if [ $# -lt 5 ]; then - ogRaiseError $OG_ERR_FORMAT "$MSG_FORMAT: $FUNCNAME [ REPO|CACHE ] image_name extension num_disk num_part " - return $? -fi - - -DIRMOUNT="$(ogGetMountImageDir "$2" "$3")" -DESTRSYNC=$(ogGetMountPoint $4 $5) - -# Borramos ficheros de informacion de restauraciones antiguas -rm -rf $DESTRSYNC/ogimg.* -rm -rf /tmp/ogimg.* - -# Origen y destino de la sincronizacion y en REPO opciones rsync -if [ "$1" == "CACHE" -o "$1" == "cache" ]; then - ORIG="/tmp/$DIRMOUNT" -else - [ -z $REPOIP ] && REPOIP=$(ogGetRepoIp) - PASSWORD="--password-file=/scripts/passrsync" - [ "$ogrsyncz" == "true" ] && OPTRSYNC="z " - [ "$ogrsyncw" == "true" ] && OPTRSYNC="W$OPTRSYNC" - USERRSYNC="opengnsys" - ORIG="$USERRSYNC@$REPOIP::ogimages/$DIRMOUNT" -fi - -# Opciones rsync en cache y repo -# Para la imagen basica, opcion de borrar archivos de la particion que no existen en la imagen -[ "$3" == "img" ] && [ "$ogrsyncdel" != "false" ] && OPTRSYNC="$OPTRSYNC --delete" - -# Nos traemos listado ficheros y bajamos la imagen - -ogEcho log session " $MSG_SYNC_RESTORE" - -# Si la imagen es diferencial nos traemos los archivos de informacion de la imagen. -if [ "$3" == "diff" ]; then - # Lista de archivos a copiar: - IMGINFO="ogimg.info" - FILESFROM=" --files-from=/tmp/$IMGINFO" - - echo " rsync -aHAX$OPTRSYNC --progress $ORIG/ogimg* /tmp" - rsync -aHAX$OPTRSYNC $PASSWORD --progress "$ORIG"/ogimg* /tmp - # Borramos linea de información de la imagen, sino busca un fichero con ese nombre - sed -i '/^\#/d' /tmp/$IMGINFO - - cd $DESTRSYNC - # Diferencial: Borramos archivos sobrantes. - ogEcho log session " $MSG_SYNC_DELETE" - sed -e s/^/\"/g -e s/$/\"/g "/tmp/ogimg.rm" 2>/dev/null | xargs rm -rf - -fi - -echo " rsync -aHAX$OPTRSYNC --progress $FILESFROM $ORIG/ $DESTRSYNC" -rsync -aHAX$OPTRSYNC $PASSWORD --progress $FILESFROM "$ORIG/" "$DESTRSYNC" 2>$OGLOGCOMMAND | egrep "^deleting|^sent|^sending|^total|%" |tee -a $OGLOGCOMMAND -RETVAL=${PIPESTATUS[0]} -cd / -#*/ " Comentario Doxygen -} - - -#/** -# ogMountImage -#@brief Monta la imagen para sincronizar. -#@param 1 Repositorio [ REPO | CACHE ] -#@param 2 Nombre Imagen -#@param 3 Tipo imagen [ img |diff ] -#@return punto de montaje -#*/ -function ogMountImage () { -local IMGEXT IMGFILE DIRMOUNT KERNELVERSION - -if [ "$*" == "help" ]; then - ogHelp "$FUNCNAME" \ - "$FUNCNAME [ REPO|CACHE ] image_name [ extension ]" \ - "$FUNCNAME REPO Ubuntu12" \ - "$FUNCNAME CACHE Windows7 diff" - return -fi - - -if [ $# -lt 2 ]; then - ogRaiseError $OG_ERR_FORMAT "$MSG_FORMAT: $FUNCNAME [ REPO|CACHE ] image_name [ extension ]" - return $? -fi - -[ "$3" == "" -o "$3" == "img" ] && IMGEXT="img" || IMGEXT="img.diff" - -DIRMOUNT="$(ogGetMountImageDir "$2" ${IMGEXT#*\.})" - -if [ "$1" == "REPO" -o "$1" == "repo" ]; then - [ -z $REPOIP ] && REPOIP=$(ogGetRepoIp) - hose $REPOIP 2009 --out sh -c "echo -ne MOUNT_IMAGE \"$2\" ${IMGEXT#*\.}" - echo "$OGIMG/$DIRMOUNT" -else - # Si está montado nada que hacer. - df | grep "$DIRMOUNT$" 2>&1 >/dev/null && echo "/tmp/$DIRMOUNT" && return 0 - - IMGFILE="$(ogGetPath "$1" /"$2.$IMGEXT")" \ - || return $(ogRaiseError $OG_ERR_NOTFOUND "$1 $2.$IMGEXT"; echo $?) - mkdir -p "/tmp/$DIRMOUNT" - - - # FS de la imagen segun el contenido del archivo .img - if file "$IMGFILE" |grep -i -e " ext4 filesystem " 2>&1 > /dev/null ; then - mount -t ext4 -o loop "$IMGFILE" "/tmp/$DIRMOUNT" 1>/dev/null - else - mount -o compress=lzo "$IMGFILE" "/tmp/$DIRMOUNT" 1>/dev/null - fi - - # Comprobamos que se ha montado bien - [ $? -eq 0 ] || return $(ogRaiseError $OG_ERR_DONTMOUNT_IMAGE "$1 $2 $3"; echo $?) - echo "/tmp/$DIRMOUNT" -fi - -} - - -#/** -# ogUnmountImage [ REPO | CACHE ] Image_name [ extension ] -#@brief Desmonta la imagen para sincronizar. -#@param 1 Repositorio [ REPO | CACHE ] -#@param 2 Nombre Imagen -#@param 3 Tipo imagen [ img |diff ] -#*/ -function ogUnmountImage () { -local IMGTYPE DIRMOUNT - -if [ "$*" == "help" ]; then - ogHelp "$FUNCNAME" \ - "$FUNCNAME [ REPO|CACHE ] image_name [ extension ]" \ - "$FUNCNAME REPO Ubuntu12" \ - "$FUNCNAME CACHE Windows7 diff" - return -fi - -if [ $# -lt 2 ]; then - ogRaiseError $OG_ERR_FORMAT "$MSG_FORMAT: $FUNCNAME [ REPO|CACHE ] image_name [ extension ]" - return $? -fi - -[ "$3" == "" ] && IMGTYPE="img" || IMGTYPE="$3" - -if [ "$1" == "CACHE" -o "$1" == "cache" ]; then - DIRMOUNT="/tmp/$(ogGetMountImageDir "$2" $IMGTYPE)" - umount "$DIRMOUNT" - rmdir "$DIRMOUNT" - [ -f $IMGFILE.lock ] && sed -i s/"mounted"//g $IMGFILE.lock -else - [ -z $REPOIP ] && REPOIP=$(ogGetRepoIp) - echo " hose $REPOIP 2009 --out sh -c echo -ne UMOUNT_IMAGE \"$2\" $IMGTYPE" - hose $REPOIP 2009 --out sh -c "echo -ne UMOUNT_IMAGE \"$2\" $IMGTYPE" -fi -} - - -#/** -# ogGetMountImageDir -#@brief Devuelve el directorio de montaje de la imagen. -#@param 1 Nombre Imagen -#@param 2 Tipo imagen [ img |diff ] -#*/ -function ogGetMountImageDir () { -local DIRMOUNT -if [ "$*" == "help" ]; then - ogHelp "$FUNCNAME" \ - "$FUNCNAME image_name [ extension ]" \ - "$FUNCNAME Ubuntu12" \ - "$FUNCNAME Windows7 diff" - return -fi - - -if [ $# -lt 1 ]; then - ogRaiseError $OG_ERR_FORMAT "$MSG_FORMAT: $FUNCNAME image_name [ extension ]" - return $? -fi - - -DIRMOUNT="mount/$1" -[ "$2" == "diff" ] && DIRMOUNT="$DIRMOUNT.diff" -echo "$DIRMOUNT" - -} - - -#/** -# ogWaitSyncImage image_name extension stado imagen_size -#@brief Se espera un tiempo a que se monte la imagen en el servidor. -#@brief Cuando se esta creando la imagen hay que dar el tamaño, para que espere el tiempo de creación. -#@param 1 Respositorio [ REPO | CACHE ] -#@param 2 Nombre Imagen -#@param 3 Tipo imagen [ img | diff ] -#@param 4 Estado [ mounted | reduced ] -#@param 5 Tamaño imagen (opcional) -#*/ -function ogWaitSyncImage () { -local SIZE TIME DIRMOUNT TIMEOUT TIMEAUX LOCKFILE IMGDIR IMGEXT STATE - -TIME=$SECONDS - -# Ayuda o menos de 5 parametros y la imagen no es basica -if [ "$*" == "help" ]; then - ogHelp "$FUNCNAME" \ - "$FUNCNAME [ REPO | CACHE ] image_name extension state [ image_size ] " \ - "$FUNCNAME REPO Ubuntu12 img 30000000" \ - "$FUNCNAME CACHE Windows7 diff " - return -fi - -if [ $# -lt 4 ]; then - ogRaiseError $OG_ERR_FORMAT "$MSG_FORMAT: $FUNCNAME [ REPO | CACHE ] image_name extension state [ image_size ] " - return $? -fi - -SIZE=${5:-"300000"} -STATE="$4" -ogCheckStringInGroup "$STATE" "mounted reduced" || \ - return $(ogRaiseError command $OG_ERR_FORMAT "STATE = [ mounted | reduced ]" ) - -IMGDIR="$(ogGetParentPath "$1" "/$2")" -[ "$3" == "img" ] && IMGEXT="img" || IMGEXT="img.diff" -LOCKFILE="${IMGDIR}/$(basename "/$2").$IMGEXT.lock" - -if [ "$1" == "CACHE" -o "$1" == "cache" ]; then - DIRMOUNT="/tmp/$(ogGetMountImageDir "$2" $3)" -else - DIRMOUNT="$OGIMG/$(ogGetMountImageDir "$2" $3)" -fi - -echo -n -e " $MSG_SYNC_SLEEP: $DIRMOUNT\n #" | tee -a $OGLOGSESSION $OGLOGFILE - -# Comprobamos: mounted -> que exista $DIRMOUNT/ogimg.info o que el fichero de lock contenga mounted -# reduced -> que el fichero de lock contenga reduced. - -# time-out segun el tamaño de la imagen. por defecto: 100000k -> 3s -let TIMEOUT=$SIZE/$CREATESPEED -[ $TIMEOUT -lt 60 ] && TIMEOUT=60 -until $(grep -i $STATE $LOCKFILE &>/dev/null) ; do - [ $STATE = "mounted" -a -f "$DIRMOUNT/ogimg.info" ] && ogEcho log session "" && return 0 - TIMEAUX=$[SECONDS-TIME] - [ "$TIMEAUX" -lt "$TIMEOUT" ] || return $(ogRaiseError $OG_ERR_DONTMOUNT_IMAGE "$3 $4 $IMGEXT: time_out."; echo $?) - echo -n "#" | tee -a $OGLOGSESSION $OGLOGFILE - sleep 5 -done -echo "" | tee -a $OGLOGSESSION $OGLOGFILE - -} - - -#/** -# ogReduceImage -#@brief Reduce el archivo de la imagen a tamaño datos + 500M -#@param 1 Repositorio [ REPO | CACHE ] -#@param 2 Nombre Imagen -#@param 3 Tipo Imagen [ img |diff ] -#@return -#@exception OG_ERR_FORMAT # 1 formato incorrecto. -#@exception OG_ERR_NOTFOUND # 2 Fichero o dispositivo no encontrado. -#*/ -function ogReduceImage () { -local IMGEXT DIRMOUNT AVAILABLE USED IMGDIR IMGFILE ENDSIZE LOOPDEVICE -if [ "$*" == "help" ]; then - ogHelp "$FUNCNAME" \ - "$FUNCNAME [ REPO|CACHE ] image_name [ extension ]" \ - "$FUNCNAME REPO Ubuntu12" \ - "$FUNCNAME CACHE Windows7 diff" - return -fi - -if [ $# -lt 2 ]; then - ogRaiseError $OG_ERR_FORMAT "$MSG_FORMAT: $FUNCNAME [ REPO|CACHE ] image_name [ extension ]" - return $? -fi - - -[ "$3" == "" -o "$3" == "img" ] && IMGEXT="img" || IMGEXT="img.diff" -IMGDIR="$(ogGetParentPath "$1" "/$2")" -IMGFILE="${IMGDIR}/$(basename "/$2").$IMGEXT" - - -if [ "$1" == "CACHE" -o "$1" == "cache" ]; then - # Para imagenes EXT4 reduzco, para BTRFS solo desmonto. - if file "$IMGFILE" | grep -i " ext4 filesystem " 2>&1 > /dev/null; then - # Al montar se comprueba la existencia de la imagen - DIRMOUNT="$(ogMountImage $1 "$2" ${IMGEXT#*\.})" - AVAILABLE=$(df -k|grep "$DIRMOUNT$"|awk '{print $4}') - # Si el espacio libre menor que 500Mb nos salimos - if [ $AVAILABLE -lt 200000 ]; then - ogUnmountImage $1 "$2" ${IMGEXT#*\.} - echo "reduced" > "$IMGFILE.lock" - return 0 - fi - - # Calculamos la diferencia entre el tamaño interno y externo - EXTSIZE=$(ls -l --block-size=1024 "$IMGFILE" | cut -f5 -d" ") - INTSIZE=$(df -k|grep "$DIRMOUNT"|awk '{print $2}') - let EDGESIZE=$EXTSIZE-$INTSIZE - ogUnmountImage $1 "$2" ${IMGEXT#*\.} - LOOPDEVICE=$(losetup -f) - losetup $LOOPDEVICE "$IMGFILE" - - # Redimensiono sistema de ficheros - echo " resize2fs -fpM $LOOPDEVICE" - resize2fs -fpM $LOOPDEVICE |tee -a $OGLOGCOMMAND - ogMountImage $1 "$2" ${IMGEXT#*\.} >/dev/null - - # Calculamos el tamaño final del archivo - INTSIZE=$(df -k|grep "$DIRMOUNT"|awk '{print $2}') - let EXTSIZE=$INTSIZE+$EDGESIZE - umount "$DIRMOUNT" - - # Si existe dispositivo de loop lo borro. - [ $LOOPDEVICE ] && losetup -d $LOOPDEVICE - - # Corto el archivo al tamaño del sistema de ficheros. - echo " truncate --size=\"$EXTSIZE\"k $IMGFILE " - truncate --size="$EXTSIZE"k "$IMGFILE" - else - # Desmonto la imagen - umount "$DIRMOUNT" - fi - - echo "reduced" > "$IMGFILE.lock" - rmdir "$DIRMOUNT" - -else - [ -z $REPOIP ] && REPOIP=$(ogGetRepoIp) - echo " hose $REPOIP 2009 --out sh -c echo -ne REDUCE_IMAGE \"$2\" ${IMGEXT#*\.}" - hose $REPOIP 2009 --out sh -c "echo -ne REDUCE_IMAGE \"$2\" ${IMGEXT#*\.}" -fi - -} - - - -#/** -# ogIsSyncImage -#@brief Comprueba si la imagen es sincronizable -#@param 1 Repositorio [ REPO | CACHE ] -#@param 2 Nombre Imagen -#@param 3 Tipo Imagen [ img |diff ] -#@return -#@exception OG_ERR_FORMAT # 1 formato incorrecto. -#@exception OG_ERR_NOTFOUND # 2 Fichero o dispositivo no encontrado. -#*/ -function ogIsSyncImage () { -local IMGEXT IMGDIR IMGFILE - -if [ "$*" == "help" ]; then - ogHelp "$FUNCNAME" \ - "$FUNCNAME [ REPO|CACHE ] image_name [ extension ]" \ - "$FUNCNAME REPO Ubuntu12" \ - "$FUNCNAME CACHE Windows7 diff" - return -fi - -if [ $# -lt 2 ]; then - ogRaiseError $OG_ERR_FORMAT "$MSG_FORMAT: $FUNCNAME [ REPO|CACHE ] image_name [ extension ]" - return $? -fi - -[ "$3" == "" -o "$3" == "img" ] && IMGEXT="img" || IMGEXT="img.diff" -IMGDIR="$(ogGetParentPath "$1" "/$2")" -IMGFILE="${IMGDIR}"/$(basename "/$2").$IMGEXT - -file "$IMGFILE" | grep -i -e " BTRFS Filesystem " -e " ext4 filesystem " >/dev/null -[ $? -eq 0 ] && return 0 || return $OG_ERR_DONTSYNC_IMAGE - -} - - -#/** -# ogCheckSyncImage -#@brief Muestra el contenido de la imagen para comprobarla. -#@param 1 Repositorio [ REPO | CACHE ] -#@param 2 Nombre Imagen -#@param 3 Tipo Imagen [ img |diff ] -#@return -#@exception OG_ERR_FORMAT # 1 formato incorrecto. -#@exception OG_ERR_NOTFOUND # 2 Fichero o dispositivo no encontrado. -#*/ -function ogCheckSyncImage () { -local IMGEXT IMGDIR IMGFILE DIRMOUNT ISMOUNT RETVAL KERNELVERSION - -if [ "$*" == "help" ]; then - ogHelp "$FUNCNAME" \ - "$FUNCNAME [ REPO|CACHE ] image_name [ extension ]" \ - "$FUNCNAME REPO Ubuntu12" \ - "$FUNCNAME CACHE Windows7 diff" - return -fi - -if [ $# -lt 2 ]; then - ogRaiseError $OG_ERR_FORMAT "$MSG_FORMAT: $FUNCNAME [ REPO|CACHE ] image_name [ extension ]" - return $? -fi - -[ "$3" == "" -o "$3" == "img" ] && IMGEXT="img" || IMGEXT="img.diff" -IMGDIR="$(ogGetParentPath "$1" "/$2")" -IMGFILE="${IMGDIR}/$(basename "/$2").$IMGEXT" - -ogIsSyncImage $1 "$2" "${IMGEXT#*\.}" || return $(ogRaiseError $OG_ERR_DONTSYNC_IMAGE "$3 $4"; echo $?) - -# Comprobamos que no esté montada (daria falso error) -if [ "$1" == "CACHE" -o "$1" == "cache" ]; then - $(df | grep "/tmp/mount/$2${IMGEXT#img}$" &>/dev/null) && ISMOUNT=TRUE -else - [ -f "$OGIMG/mount/$2${IMGEXT#img}/ogimg.info" ] && ISMOUNT=TRUE -fi -[ "$ISMOUNT" == TRUE ] && ogEcho log session warning "$MSG_SYNC_NOCHECK" && return 0 - -DIRMOUNT="/tmp/ogCheckImage$$" -mkdir "$DIRMOUNT" -# FS de la imagen segun el contenido del archivo .img -if file "$IMGFILE" |grep -i -e " ext4 filesystem " 2>&1 > /dev/null ; then - mount -t ext4 -o loop "$IMGFILE" "$DIRMOUNT" 2>&1 | tee -a $OGLOGCOMMAND - RETVAL=${PIPESTATUS[0]} -else - mount -o compress=lzo "$IMGFILE" "$DIRMOUNT" 2>&1 | tee -a $OGLOGCOMMAND - RETVAL=${PIPESTATUS[0]} -fi -ls -C "$DIRMOUNT" | tee -a $OGLOGCOMMAND -umount "$DIRMOUNT" - -rmdir "$DIRMOUNT" -return $RETVAL -} - diff --git a/client/shared/scripts/ImagenesSincronizadas.lib b/client/shared/scripts/ImagenesSincronizadas.lib deleted file mode 100755 index 717d052..0000000 --- a/client/shared/scripts/ImagenesSincronizadas.lib +++ /dev/null @@ -1,368 +0,0 @@ -#!/bin/bash -#___________________________________________________________________ -# -#@file ImagenesSincronizadas.lib -#@brief Librería o clase ImagenesSincronizadas -#@class ImagenesSincronizadas -#@brief Funciones para la creación y restauración de imagenes por sincronización. -#@version 1.0.4 -#@warning License: GNU GPLv3+ -#___________________________________________________________________ - - #Load engine configurator from engine.cfg file. - #Carga el configurador del engine desde el fichero engine.cfg - [ -z $OGENGINECONFIGURATE ] && source /opt/opengnsys/etc/engine.cfg - - # Clear temporary file used as log track by httpdlog - # Limpia los ficheros temporales usados como log de seguimieincludento para httpdlog - echo " " > $OGLOGSESSION; echo " " > $OGLOGCOMMAND; echo " " > ${OGLOGCOMMAND}.tmp - - # Registro de inicio de ejecución - #echo "[START Interface ] Run this command: $0 $*" | tee -a $OGLOGSESSION $OGLOGFILE - echo "$MSG_INTERFACE_START $0 $*" | tee -a $OGLOGSESSION $OGLOGFILE - - # Solo ejecutable por OpenGnsys Client. - PATH=$PATH:$(dirname $0) - PROG=$(basename $0) - - #___________________________________________________________________ - # - # Variables globales - #___________________________________________________________________ - - TIPOPARTICION="$(ogGetPartitionId $DISCO $NPART)"; # Tipo de particion - if [ -z $TIPOPARTICION ]; then - echo "No existe la partición $NPART, debe crearla" | tee -a $OGLOGSESSION $OGLOGFILE - exit $OG_ERR_PARTITION - fi - - PARTICION=$(ogMount $DISCO $NPART); # Monta partición - if [ -z $PARTICION ]; then - echo "No existe la unidad /dev/sda$NPART o no está formateada" | tee -a $OGLOGSESSION $OGLOGFILE - exit $OG_ERR_PARTITION - fi - - SISTEMAFICHERO="$(ogGetFsType $DISCO $NPART)" # Sistema de ficheros - if [ -z $SISTEMAFICHERO ]; then - echo "No se ha definido un sistema de fichero en la unidad /dev/sda$NPART. Debe formatearla previamente." | tee -a $OGLOGSESSION $OGLOGFILE - exit $OG_ERR_PARTITION - fi - - REPOSITORIO="opengnsys@$IPREPOSITORIO::ogimages" # Ruta de las imagenes en el repositorio - - FILE_ACL="ogwinimage.acl" - - - # Envío de archivo completo - OP_WHOLE="--whole" - if [ $WHLFILE -eq 0 ]; then - OP_WHOLE="" - fi - - # Borrar archivos en destino - OP_DELETE="--delete" - if [ $NOBORRACHIVOS -eq 1 ]; then - OP_DELETE="" - fi - if [ $RMVFILE -eq 0 ]; then - OP_DELETE="" - fi - - # Opción de compresión - OP_COMPRESS="-z" - if [ $CMPFILE -eq 0 ]; then - OP_COMPRESS="" - fi - - #___________________________________________________________________ - # - # Fichero con la clave remota del rsync para el parámetro - # "--password-file" para que no se pida ésta en el proceso. - #___________________________________________________________________ - # - FILE_PASSWD="--password-file=/scripts/passrsync" - - - #___________________________________________________________________ - # - # Función: montaCache - # - # Descripción: - # - # Monta la cache y devuelve la ruta hacía ella - # - # Parámetros: - # - # Ninguno - #___________________________________________________________________ - # - function montaCache() - { - # Error si no existe caché - if ! $(ogFindCache >/dev/null); then - echo "" - return - fi - cache=$(ogMountCache) - echo $cache - } - #___________________________________________________________________ - # - # Función: editarLista - # - # Descripción: - # - # Edita lista de archivos a transferir para depurar lineas - # - # Parámetros: - # - # $1 Lista de entrada - # $2 Lista de salida - #___________________________________________________________________ - # - function editarLista() - { - # Edición: - # a) Quitarle lineas que contengan './' - # b) La primera linea (reporter del rsync) - # c) Las dos últimas lineas del final (reporter del rsync) - - echo "Editando lista de archivos Entrada:$1 Salida:$2" | tee -a $OGLOGSESSION $OGLOGFILE - cat $1 | sed '/\.\//d' | sed '1d' | sed -n -e :a -e '1,2!{P;N;D;};N;ba'>$2 - - # Otra forma - #sed -i -e s/"^sent.*.bytes\/sec"//g -e s/^total.*.speedup.*.$//g -e s/"sending.*.list"//g $IMGINFO - #sed -i '/^\.\//d' $IMGINFO - - } - - #___________________________________________________________________ - # - # Función: crearImagen - # - # Descripción: - # - # Sincroniza archivos entre origen y destino. Al final del - # proceso el contenido de destino será igual al de origen. - # La creación de imagen siempre tiene lugar entre una partición - # y un repositorio como origen y destino respectivamente. - # - # Parámetros: - # - # $1: Origen - # $2: Destino - # $3: Sistema de ficheros de la partición - # $4: Indica si la sincronización es local o remota - # 1: El origen o el destino es remoto - # 2: Tanto el origen como el destino son locales - # $5: Vale - # 1=Para crear la lista de archivos a transferir - # 2= Cuando se quiere sincronizar usando la lista - # $6: Path a la lista de archivos - #___________________________________________________________________ - # - function crearImagen() - { - case "$3" in - NTFS) - OP_ARCHIVO="-aH" - rm "$1pagefile.sys" - #OP_EXCLUDE="--exclude 'pagefile.sys'" - ;; - EXT2 | EXT3 | EXT4 ) - OP_ARCHIVO="-alH" - OP_EXCLUDE="--exclude 'tmp' --exclude 'proc' --exclude 'sys'" - ;; - esac - - - case "$4" in - 1) - OP_PASSWD=$FILE_PASSWD - ;; - 2) - OP_PASSWD="" - ;; - esac - - FREG=$OGLOGCOMMAND # Por defecto se redirecciona al archivo de log de comandos - case "$5" in - 1) - OP_ARCHIVO=$OP_ARCHIVO"nv" # Simulación para crear lista - FREG=$6 - ;; - 2) - OP_FILELIST="--files-from=$6" - OP_ARCHIVO="$OP_ARCHIVO $OP_FILELIST" - ;; - esac - - echo "rsync $OP_ARCHIVO $OP_COMPRESS $OP_DELETE $OP_EXCLUDE $OP_PASSWD $1 $2 " | tee -a $OGLOGSESSION $OGLOGFILE - rsync $OP_ARCHIVO $OP_COMPRESS $OP_DELETE $OP_EXCLUDE $OP_PASSWD $1 $2>$FREG; - } - - #___________________________________________________________________ - # - # Función: restaurarImagen - # - # Descripción: - # - # Sincroniza archivos entre origen y destino. Al final del - # proceso el contenido de destino será igual al de origen. - # La restauración de imagen siempre tiene lugar entre la caché - # o un repositorio y una partición o carpeta como origen y destino - # respectivamente. - # - # Parámetros: - # - # $1: Origen - # $2: Destino - # $3: Sistema de ficheros de la partición - # $4: Indica si la sincronización es local o remota - # 1: El origen o el destino es remoto - # 2: Tanto el origen como el destino son locales - #___________________________________________________________________ - # - function restaurarImagen() - { - case "$3" in - NTFS) - OP_ARCHIVO="-aH" - ;; - EXT2 | EXT3 | EXT4) - OP_ARCHIVO="-alH" - ;; - esac - - case "$4" in - 1) - OP_PASSWD=$FILE_PASSWD - ;; - 2) - OP_PASSWD="" - ;; - esac - - echo "rsync $OP_ARCHIVO $OP_COMPRESS $OP_DELETE $OP_PASSWD $1 $2" | tee -a $OGLOGSESSION $OGLOGFILE - rsync $OP_ARCHIVO $OP_COMPRESS $OP_DELETE $OP_PASSWD $1 $2>$OGLOGCOMMAND; - } - - #___________________________________________________________________ - # - # Función: crearListaAcl - # - # Descripción: - # - # Crea la lista de control de archivos para el sistema operativo - # que ocupa la partición que se quiere clonar - # - # Parámetros: - # - # $1: Origen - # $2: Destino - # $3: Sistema de ficheros de la partición - # $4: disco - # $5: partición - #___________________________________________________________________ - # - function crearListaAcl() - { - case "$3" in - NTFS) - echo "Creando lista de control de acceso a ficheros para el sistema windows de la particion $5" | tee -a $OGLOGSESSION $OGLOGFILE - echo "Desmontando la particion $5" | tee -a $OGLOGSESSION $OGLOGFILE - ogUnmount $4 $5 | tee -a $OGLOGSESSION $OGLOGFILE - echo "Ejecutando comando ntfs-3g.secaudit -b /dev/sda$5 /" | tee -a $OGLOGSESSION $OGLOGFILE - ntfs-3g.secaudit -b /dev/sda$5 / > /tmp/$FILE_ACL - echo "Montando de nuevo la particion $5" | tee -a $OGLOGSESSION $OGLOGFILE - ogMount $4 $5 - echo "Copiando archivo de listas de control (ACL) desde /tmp/$FILE_ACL a $1." | tee -a $OGLOGSESSION $OGLOGFILE - cp /tmp/$FILE_ACL $1. - ;; - esac - } - #___________________________________________________________________ - # - # Función: restauraListaAcl - # - # Descripción: - # - # Restaura la lista de control de archivos para el sistema operativo - # que ocupa la partición que se quiere restaurar - # - # Parámetros: - # - # $1: Origen - # $2: Destino - # $3: Sistema de ficheros de la partición - # $4: disco - # $5: partición - #___________________________________________________________________ - # - function restauraListaAcl() - { - case "$3" in - NTFS) - echo "Restaurando lista de control de acceso a ficheros para el sistema windows de la particion $5" | tee -a $OGLOGSESSION $OGLOGFILE - echo "Copiando archivo de listas de control (ACL) desde $2$FILE_ACL a /tmp/." | tee -a $OGLOGSESSION $OGLOGFILE - cp $2$FILE_ACL /tmp/. - echo "Desmontando la particion $5" | tee -a $OGLOGSESSION $OGLOGFILE - ogUnmount $4 $5 | tee -a $OGLOGSESSION $OGLOGFILE - echo "Ejecutando comando ntfs-3g.secaudit -se /dev/sda$5 /" | tee -a $OGLOGSESSION $OGLOGFILE - ntfs-3g.secaudit -se /dev/sda$5 /tmp/$FILE_ACL - echo "Montando de nuevo la particion $5" | tee -a $OGLOGSESSION $OGLOGFILE - ogMount $4 $5 - echo "Borrando archivo de listas de control (ACL) de $2$FILE_ACL" | tee -a $OGLOGSESSION $OGLOGFILE - rm $2$FILE_ACL - ;; - esac - } - #___________________________________________________________________ - # - # Función: eliminaListaAcl - # - # Descripción: - # - # Elimina la lista de control de archivos creada temporalmente - # para el proceso de creación e imagen - # - # Parámetros: - # - # $1: Origen - # $2: Sistema de ficheros de la partición - #___________________________________________________________________ - # - function eliminaListaAcl() - { - case "$2" in - NTFS) - echo "Borrando archivo de listas de control (ACL) de $1$FILE_ACL" | tee -a $OGLOGSESSION $OGLOGFILE - rm $1$FILE_ACL - ;; - esac - } - #___________________________________________________________________ - # - # Función: comprimeImagen - # - # Descripción: - # - # Comprimer la carpeta donde está se ha almacenado la imagen - # recientemente creada - # - # Parámetros: - # - # $1: Nombre de la imagen (Es el mismo que la carpeta que la alberga) - #___________________________________________________________________ - # - function comprimeImagen() - { - TIME1=$SECONDS - RUTAIMG="/opt/opengnsys/images" - CARPETAIMAGEN=$RUTAIMG/$1 - echo "Comprimiendo la imagen $1. Ruta de la imagen: $RUTAIMG" | tee -a $OGLOGSESSION $OGLOGFILE - echo "find $CARPETAIMAGEN | cpio -H newc -oa | gzip -9c en $CARPETAIMAGEN.IMG" | tee -a $OGLOGSESSION $OGLOGFILE - find $CARPETAIMAGEN | cpio -H newc -oa | gzip -9c > $CARPETAIMAGEN.IMG - TIME=$[SECONDS-TIME1] - echo "[100] $MSG_SCRIPTS_TIME_TOTAL $[TIME/60]m $[TIME%60]s" | tee -a $OGLOGSESSION $OGLOGFILE - } diff --git a/client/shared/scripts/createBaseImage b/client/shared/scripts/createBaseImage deleted file mode 100755 index f7ef2a5..0000000 --- a/client/shared/scripts/createBaseImage +++ /dev/null @@ -1,166 +0,0 @@ -#!/bin/bash - -#/** -# createBaseImage -#@brief Script de ejemplo para crear una imagen de un sistema de archivos. -#@brief Se usa como base para el programa de creación de imágenes de OpenGnsys Admin). -#@param 1 disco -#@param 2 particion -#@param 3 REPO|CACHE|IPREPO -#@param 4 imagen -#@return -#@exception OG_ERR_FORMAT # 1 formato incorrecto. -#@exception OG_ERR_PARTITION # 3 Error en partición de disco o en su sistema de archivos -#@exception OG_ERR_LOCKED # 4 Imagen o particion bloqueada -#@exception OG_ERR_IMAGE # 5 Error en funcion ogCreateImage o ogRestoreImage. -#@exception OG_ERR_NOTWRITE # 14 error de escritura -#@exception OG_ERR_NOTCACHE # 15 si cache no existe 15 -#@exception OG_ERR_CACHESIZE # 16 si espacio de la cache local o remota no tiene espacio 16 -#@exception OG_ERR_DONTMOUNT_IMAGE # 70 Error al montar una imagen sincronizada -#@note No necesario permiso se escritura por samba en repo. -#@todo: que hacer, si el tamaño de la cache es sufciente, pero no tiene espacio libre -#@version 1.0 - creación imagen con btrfs -#@author -#@date 2012-12-04 -#@version 1.1.0 - Se muestra el espacio necesario para alojar la imagen y el disponible (ticket #771) -#@author Irina Gomez - ETSII Universidad de Sevilla -#@date 2017-03-28 -#@version 1.1.1 - Varios repositorios para un mismo cliente (ticket #679). -#@author Irina Gomez - ETSII Universidad de Sevilla -#@date 2018/11/06 -#*/ ## - -trap "onexit $1 $2 $IMGFILE" 1 2 3 6 9 14 15 EXIT - -# Si salimos con error demontamos la imagen y desbloqueamos la imagen y la particion -function onexit() { - local exit_status=$? - if [ $exit_status -ne 4 ]; then - ogUnlockImage "$IMGFILE" - ogUnlock $1 $2 - fi - exit $exit_status -} - -TIME1=$SECONDS -#Carga el configurador del engine desde el fichero engine.cfg -[ -z $OGENGINECONFIGURATE ] && source /opt/opengnsys/etc/engine.cfg - -PROG="$(basename $0)" -# Si se solicita, mostrar ayuda. -if [ "$*" == "help" ]; then - ogHelp "$PROG: $MSG_HELP_createBaseImage" \ - "$PROG ndisco nparticion REPO|CACHE base_image" \ - "$PROG 1 1 REPO Windows7" - exit 0 -fi - -[ $# -ne 4 ] && exit $(ogRaiseError session $OG_ERR_FORMAT "$MSG_FORMAT: $PROG ndisco nparticion REPO|CACHE imagen" ; echo $?) - -# Limpiamos fichero de log -echo " " > $OGLOGCOMMAND -[ "$(ogGetCaller)" == "CrearImagenBasica" ] || echo -n ""> $OGLOGSESSION - -ogEcho log session "[1] $MSG_SCRIPTS_START $0 $*" - -# Valores por defecto en etc/engine.cfg -IMGEXT=${IMGEXT:-"img"} -REPO="${3^^}" -# No permite directorios diferentes para OU -OGUNIT="" - -# Si es una ip y es igual a la del equipo restaura desde cache -[ "$REPO" == "$(ogGetIpAddress)" ] && REPO="CACHE" -# Si es una ip y es distinta a la del recurso samba cambiamos de REPO. -ogCheckIpAddress $REPO -if [ $? == 0 -o $REPO == "REPO" ] ; then - # Si falla el cambio -> salimos con error repositorio no valido - ogChangeRepo $REPO $OGUNIT || exit $(ogRaiseError $OG_ERR_NOTFOUND '$REPO'; echo $?) - REPO="REPO" -fi - -# Comprobamos si la imagen o la particion estan bloqueada: -ogIsImageLocked "$REPO" "$4.$IMGEXT" && exit $(ogRaiseError session $OG_ERR_LOCKED "$REPO $4.$IMGEXT"; echo $?) -ogIsLocked "$1" "$2" && exit $(ogRaiseError session $OG_ERR_LOCKED "$1 $2"; echo $?) - -# Si el repositorio es CACHE comprobamos que exista -if [ "$REPO" == "CACHE" -o "$REPO" == "cache" ]; then - ! ogFindCache >/dev/null && exit $(ogRaiseError session $OG_ERR_NOTCACHE "CACHE "; echo $?) -fi - -# Obtener información de los parámetros de entrada. -PART=$(ogDiskToDev "$1" "$2" 2>/dev/null) || exit $(ogRaiseError session $OG_ERR_PARTITION "$1 $2" ; echo $?) -# Comprobar consistencia del sistema de archivos. -echo " " > $OGLOGCOMMAND -SIZEFS=$(ogGetFsSize $1 $2) -ogEcho log session "[20] $MSG_HELP_ogCheckFs $PART $SIZEFS (KB) " -ogUnmount $1 $2 -ogCheckFs $1 $2 &> $OGLOGCOMMAND || exit $(ogRaiseError session $OG_ERR_PARTITION "ogCheckFs $1 $2"; echo $?) - -# Comprobamos que la particion se puede montar -ORIG=$(ogMount $1 $2) || exit $(ogRaiseError session $OG_ERR_PARTITION "$1 $2" ; echo $?) - -# Borramos ficheros de paginacion y configuracion -ogCleanOs $1 $2 - -#Comprobar espacio que requerira la imagen para ser almacenada -read SIZEDATA SIZEREQUIRED SIZEFREE ISENOUGHSPACE <<< $(ogGetSizeParameters $1 $2 "$REPO" "$4" SYNC) - -ogEcho log session "[16] $PROG: $MSG_SCRIPTS_CREATE_SIZE $SIZEREQUIRED $SIZEFREE" -[ "$ISENOUGHSPACE" == "TRUE" ] || exit $(ogRaiseError session $OG_ERR_CACHESIZE "$REPO"; echo $?) - -IMGDIR="$(ogGetParentPath "$REPO" "/$4")" -IMGFILE=${IMGDIR}/$(basename "/$4").$IMGEXT - -# Crear la imagen. -echo " " > $OGLOGCOMMAND -TIME2=$SECONDS - -ogEcho log session "[40] $MSG_HELP_ogCreateImage $1 $2 $REPO $4 " - -# Si existe el fichero de la imagen se hace copia de seguridad y se redimensiona, si no existe se crea. -# Bloqueo la imagen. Si esta en modo lectura dara error y nos salimos -ogEcho log session "[50] $MSG_HELP_ogCreateFileImage." -ogLockImage "$REPO" "/$4.$IMGEXT" || exit $? -ogCreateFileImage $REPO "$4" $IMGEXT $SIZEREQUIRED - -# Creamos la lista del contenido y lo situamos en la particion a copiar. -ogEcho log session "[60] $MSG_HELP_ogCreateInfoImage" -ogCreateInfoImage $1 $2 $IMGEXT - -TIMEAUX3=$[SECONDS-TIME2] -ogEcho log session " $MSG_SCRIPTS_TASK_END, $MSG_SCRIPTS_TIME_PARTIAL : $[TIMEAUX3/60]m $[TIMEAUX3%60]s" - -# Esperamos que el servidor termine de crear y montar la imagen -ogWaitSyncImage "$REPO" "$4" $IMGEXT "mounted" $SIZEREQUIRED || exit $(ogRaiseError session $OG_ERR_DONTMOUNT_IMAGE "$REPO $4 $IMGEXT: time_out."; echo $?) - -# Sincronizamos los datos de la particion con la imagen. -ogEcho log session "[70] $MSG_HELP_ogSyncCreate." -ogSyncCreate $1 $2 $REPO "$4" $IMGEXT -RETVAL=$? -[ $RETVAL == 0 ] || ogEcho session warning "$MSG_ERR_SYNCHRONIZING" - -TIMEAUX5=$[SECONDS-TIMEAUX3] -ogEcho log session " $MSG_SCRIPTS_TASK_END, $MSG_SCRIPTS_TIME_PARTIAL: $[TIMEAUX5/60]m $[TIMEAUX5%60]s" - -# Reducimos la imagen: solo para kernel <= 3.7, imagenes con FS ext4. (Desmonta y desbloquea la imagen) -ogEcho log session "[80] $MSG_HELP_ogReduceImage: $REPO /$4.$IMGEXT" -ogReduceImage $REPO "$4" $IMGEXT -# Esperamos que el servidor termine de reducir la imagen -ogWaitSyncImage "$REPO" "$4" $IMGEXT "reduced" $SIZEREQUIRED || exit $(ogRaiseError session $OG_ERR_DONTMOUNT_IMAGE "$REPO $4 $IMGEXT: time_out."; echo $?) - -echo " " > $OGLOGCOMMAND -sleep 2 -# Comprobamos que la imagen esta bien detectacdo que es un sistema de ficheros. -ogEcho log session "[95] $MSG_HELP_ogCheckSyncImage" -ogCheckSyncImage $REPO "$4" "img" || exit $(ogRaiseError session $OG_ERR_IMAGE "$REPO $4 img" ; echo $?) - -#resumen de la operacion -IMGSIZE=$(ls -l --block-size=1024 "$IMGFILE" | cut -f5 -d" ") - -TIME=$[SECONDS-TIME1] -ogEcho log session "[100] $MSG_SCRIPTS_TIME_TOTAL $[TIME/60]m $[TIME%60]s" -ogEcho log session " FileSystem $PART with $SIZEDATA KB data created onto file-image as $4 and used $IMGSIZE KB acros DFS rsync " - -# Si ha habido problema al sincronizar nos salimos con error -[ $RETVAL == 0 ] || exit $OG_ERR_SYNCHRONIZING diff --git a/client/shared/scripts/createDiffImage b/client/shared/scripts/createDiffImage deleted file mode 100755 index ddee5c7..0000000 --- a/client/shared/scripts/createDiffImage +++ /dev/null @@ -1,201 +0,0 @@ -#!/bin/bash - -#/** -# createDiffImage -#@brief Script de ejemplo para crear una imagen diferencial de un sistema de archivos. -#@brief Se usa como base para el programa de creación de imágenes de OpenGnsys Admin). -#@param 1 disco -#@param 2 particion -#@param 3 REPO|CACHE -#@param 4 imagen_completa -#@param 5 imagen_diferencial -#@return -#@exception OG_ERR_FORMAT # 1 formato incorrecto. -#@exception OG_ERR_PARTITION # 3 Error en partición de disco o en su sistema de archivos -#@exception OG_ERR_NOTFOUND # Fichero de registro no encontrado. -#@exception OG_ERR_IMAGE # 5 Error en funcion ogCreateImage o ogRestoreImage. -#@exception OG_ERR_NOTWRITE # 14 error de escritura -#@exception OG_ERR_NOTCACHE # 15 si cache no existe 15 -#@exception OG_ERR_CACHESIZE # 16 si espacio de la cache local o remota no tiene espacio 16 -#@exception OG_ERR_LOCKED # 4 Partición o fichero bloqueado. -#@exception OG_ERR_DONTMOUNT_IMAGE # 70 Error al montar una imagen sincronizada. -#@exception OG_ERR_DONTSYNC_IMAGE # 71 Imagen no sincronizable (es monolitica) -#@note Se toma como punto de partida el script createImage. -#@todo: que hacer, si el tamaño de la cache es sufciente, pero no tiene espacio libre -#@version 1.0 - creacion de la diferencial con rsync y btrfs -#@author -#@date 2012-12-04 -#@version 1.1.0 - Se muestra el espacio necesario para alojar la imagen y el disponible (ticket #771) -#@author Irina Gomez - ETSII Universidad de Sevilla -#@date 2017-03-28 -#@version 1.1.1 - Varios repositorios para un mismo cliente (ticket #679). -#@author Irina Gomez - ETSII Universidad de Sevilla -#@date 2018/11/06 -#*/ ## -trap "onexit $1 $2 \"$4\" \"$5\"" 1 2 3 6 9 14 15 EXIT - -function onexit() { - local exit_status=$? - ogUnmountImage $REPO "$5" $DIFFTYPE &>/dev/null - ogUnmountImage $REPO "$3" $IMGEXT &>/dev/null - if [ $exit_status -ne 4 ]; then - ogUnlockImage "$IMGFILE" &>/dev/null - ogUnlockImage "$DIFFFILE" &>/dev/null - ogUnlock $1 $2 &>/dev/null - fi - exit $exit_status -} - -#Carga el configurador del engine desde el fichero engine.cfg -[ -z $OGENGINECONFIGURATE ] && source /opt/opengnsys/etc/engine.cfg - -TIME1=$SECONDS - -PROG="$(basename $0)" -# Si se solicita, mostrar ayuda. -if [ "$*" == "help" ]; then - ogHelp "$PROG: $MSG_HELP_createDiffImage" \ - "$PROG ndisco nparticion REPO|CACHE base_image diff_image" \ - "$PROG 1 1 REPO Windows7 Win7aula23" - exit 0 -fi - -[ $# -lt 5 ] && exit $(ogRaiseError session $OG_ERR_FORMAT "$MSG_FORMAT: $PROG ndisco nparticion REPO|CACHE base_image diff_image"; echo $?) - -# Limpiamos los archivo de log -echo -n "" >$OGLOGCOMMAND -[ "$(ogGetCaller)" == "RestaurarSoftIncremental" ] || echo -n "" > $OGLOGSESSION - -ogEcho log session "[1] $MSG_SCRIPTS_START $0 $*" - -# Valores por defecto en etc/engine.cfg -IMGEXT="img" -DIFFEXT="img.diff" -DIFFTYPE="diff" -REPO="${3^^}" -# No permite directorios diferentes para OU -OGUNIT="" - -# Si es una ip y es igual a la del equipo restaura desde cache -[ "$REPO" == "$(ogGetIpAddress)" ] && REPO="CACHE" -# Si es una ip y es distinta a la del recurso samba cambiamos de REPO. -ogCheckIpAddress $REPO -if [ $? == 0 -o $REPO == "REPO" ] ; then - # Si falla el cambio -> salimos con error repositorio no valido - ogChangeRepo $REPO $OGUNIT || exit $(ogRaiseError $OG_ERR_NOTFOUND '$REPO'; echo $?) - REPO="REPO" -fi - -# Comprobamos si la imagen diferencial o la particion estan bloqueada: -ogIsImageLocked "$REPO" "$5.$DIFFEXT" && exit $(ogRaiseError session $OG_ERR_LOCKED "$REPO $5.$DIFFEXT"; echo $?) -ogIsLocked "$1" "$2" && exit $(ogRaiseError session $OG_ERR_LOCKED "$1 $2"; echo $?) - -# Comprobar si la imagen completa existe. -IMGFILE="$(ogGetPath "$REPO" "$4.$IMGEXT")" -[ -n "$IMGFILE" ] || exit $(ogRaiseError session $OG_ERR_NOTFOUND "$REPO $4.$IMGEXT"; echo $?) - -# Comprobar que la imagen completa es sincronizable -ogIsSyncImage "$REPO" "$4" "img" || exit $(ogRaiseError session $OG_ERR_DONTSYNC_IMAGE "$REPO $4"; echo $?) -# Comprobar que no está bloqueada -ogIsImageLocked "$REPO" "$4.$IMGEXT" && exit $(ogRaiseError session $OG_ERR_LOCKED "$REPO $4.$IMGEXT"; echo $?) - -# Si el repositorio es CACHE comprobamos que exista -if [ "$REPO" == "CACHE" -o "$REPO" == "cache" ]; then - ! ogFindCache >/dev/null && exit $(ogRaiseError session $OG_ERR_NOTCACHE "CACHE "; echo $?) -fi - -echo " " > $OGLOGCOMMAND - -# Obtener información de los parámetros de entrada. -PART=$(ogDiskToDev "$1" "$2" 2>/dev/null) || exit $(ogRaiseError session $OG_ERR_PARTITION "$1 $2"; echo $?) -#Comprobamos que la partición se puede montar. -ORIG=$(ogMount $1 $2) || exit $(ogRaiseError session $OG_ERR_PARTITION "$1 $2"; echo $?) - -DIFFDIR="$(ogGetParentPath "$REPO" "/$5")" -DIFFFILE="$DIFFDIR/$5.$DIFFEXT" - -# Bloqueo las imagenes -> Si no hay acceso de escritura dara error y nos saldremos. -ogLockImage "$REPO" "/$4.$IMGEXT" || exit $? -ogLockImage "$REPO" "/$5.$DIFFEXT" || exit $? - -# Comprobar consistencia del sistema de archivos. -echo " " > $OGLOGCOMMAND -SIZEFS=$(ogGetFsSize $1 $2) -ogEcho log session "[20] $MSG_HELP_ogCheckFs $1 $2 $SIZEFS (KB) " -ogUnmount $1 $2 -ogCheckFs $1 $2 >$OGLOGCOMMAND || exit $(ogRaiseError session $OG_ERR_PARTITION "ogCheckFs $1 $2" ; echo $?) - -# Borramos ficheros de paginacion y configuracion -ogCleanOs $1 $2 - -# Crear la imagen. -echo " " > $OGLOGCOMMAND -TIME2=$SECONDS -ogEcho log session "[40] $MSG_HELP_createDiffImage: $1 $2 $REPO $4" -# Creamos la lista del contenido y lo situamos en la particion a copiar. - -# Montamos imagen completa (con la que vamos a comparar) -ogMountImage $REPO "$4" $IMGEXT >/dev/null - -# Comprobar que la imagen completa se ha montado -ogWaitSyncImage $REPO "$4" $IMGEXT "mounted" || exit $(ogRaiseError session $OG_ERR_DONTMOUNT_IMAGE "$REPO $4 $IMGEXT: time_out."; echo $?) - -# Creamos la informacion de la imagen. -ogEcho log session "[45] $MSG_HELP_ogCreateInfoImage " -ogCreateInfoImage $1 $2 $REPO "$4" "${DIFFTYPE}" -[ $? -eq $OG_ERR_NOTDIFFERENT ] && exit $(ogRaiseError session $OG_ERR_NOTDIFFERENT; echo $?) - -#Comprobar espacio que requerira la imagen para ser almacenada -read SIZEDATA SIZEREQUIRED SIZEFREE ISENOUGHSPACE <<< $(ogGetSizeParameters $1 $2 "$REPO" "$5" DIFF) - -ogEcho log session "[50] $PROG: $MSG_SCRIPTS_CREATE_SIZE $SIZEREQUIRED $SIZEFREE" -[ "$ISENOUGHSPACE" == "TRUE" ] || exit $(ogRaiseError session $OG_ERR_CACHESIZE "$REPO"; echo $?) - -#Calculamos el tamaño de la imagen: -ogMount $1 $2 >/dev/null -ogLock $1 $2 - -TIMEAUX3=$[SECONDS-TIME2] -ogEcho log session " $MSG_SCRIPTS_TASK_END $MSG_SCRIPTS_TIME_PARTIAL: $[TIMEAUX3/60]m $[TIMEAUX3%60]s" - -TIMEAUX5=$[SECONDS-TIMEAUX3] - -# Configuro la informacion del tamaño de los datos en /tmp/ogimg.info -sed -i s/SIZEDATA/"$SIZEDATA"/g /tmp/ogimg.info - -# Creamos o redimensionamos la imagen -ogEcho log session "[60] $MSG_HELP_ogCreateFileImage." -ogCreateFileImage $REPO "$5" $DIFFTYPE $SIZEREQUIRED - -# Esperamos que se monte la imagen despues de crarla en el servidor -ogWaitSyncImage "$REPO" "$5" "$DIFFTYPE" "mounted" $SIZEREQUIRED || exit $(ogRaiseError session $OG_ERR_DONTMOUNT_IMAGE "$REPO $5 $DIFFTYPE: time_out."; echo $?) - -ogEcho log session "[70] $MSG_HELP_ogSyncCreate" -# Copio los datos a la imagen diferecial : -ogSyncCreate $1 $2 $REPO "$5" $DIFFTYPE -RETVAL=$? -[ $RETVAL == 0 ] || ogEcho session warning "$MSG_ERR_SYNCHRONIZING" -TIMEAUX6=$[SECONDS-TIMEAUX5] -ogEcho log session " $MSG_SCRIPTS_TASK_END, $MSG_SCRIPTS_TIME_PARTIAL: $[TIMEAUX6/60]m $[TIMEAUX6%60]s" - -# Reducimos la imagen diferencial -> solo para kernel <= 3.7, imagenes con FS ext4 -ogEcho log session "[80] $MSG_HELP_ogReduceImage: $REPO /$4.$IMGEXT" -ogReduceImage $REPO "$5" $DIFFTYPE -# Esperamos que el servidor termine de reducir la imagen -ogWaitSyncImage "$REPO" "$5" $DIFFTYPE "reduced" $SIZEREQUIRED || exit $(ogRaiseError session $OG_ERR_DONTMOUNT_IMAGE "$REPO $4 $IMGEXT: time_out."; echo $?) -# TODO: necesaria funcion que espere a que se reduzca la imagen para continuar el script - -echo " " > $OGLOGCOMMAND -sleep 2 -# Comprobamos que la imagen esta bien -ogEcho log session "[95] $MSG_HELP_ogCheckSyncImage" -ogCheckSyncImage $REPO "$5" diff || exit $(ogRaiseError session $OG_ERR_IMAGE "$REPO $5 diff"; echo $?) - -#resumen de la operacion -IMGSIZE=$(ls -l --block-size=1024 "${DIFFFILE}"| cut -f5 -d" ") -TIME=$[SECONDS-TIME1] -ogEcho log session "[100] $MSG_SCRIPTS_TIME_TOTAL $[TIME/60]m $[TIME%60]s" -ogEcho log session " FileSystem $PART with $SIZEDATA KB data created onto file-image as $5 and used $IMGSIZE KB acros DFS rsync " - -# Si ha habido problema al sincronizar nos salimos con error -[ $RETVAL == 0 ] || exit $OG_ERR_SYNCHRONIZING diff --git a/client/shared/scripts/ogCrearImagenBasica b/client/shared/scripts/ogCrearImagenBasica deleted file mode 100755 index c4f622c..0000000 --- a/client/shared/scripts/ogCrearImagenBasica +++ /dev/null @@ -1,167 +0,0 @@ -#!/bin/bash -#___________________________________________________________________ -# -# CREAR IMAGEN BÁSICA -#___________________________________________________________________ -# -# Parámetros recibidos desde el cliente: -# -# $1 Número de disco -# $2 Número de particion -# $3 Nombre canónico de la imagen básica (sin extensión) -# $4 Dirección del repositorio -# $5 Es una cadena "nnn" tipo flags que codifica varios parametros. -# Tiene el formato "nnn" donde "n" vale 0 ó 1. -# 1XX: Gestionar fichero completo en lugar de diferencas -# X1X: Eliminar archivos de la imagen que no estén en la partición -# XX1: Comprimir archivos para enviarlos por la red -# El valor X indica que no importa el valor que tenga el dato -# $6 Es una cadena "nnnn" tipo flags que codifica varios parametros. -# Tiene el formato "nnnn" donde "n" vale 0 ó 1. -# 1XXX: Borrar la imagen del repositorio antes de crearla -# X1XX: Copiar Imagen básica también a la cache -# XX1X: Borrar previamente la imagen de la cache antes de copiarla -# XXX1: No borrar archivos en destino -# El valor X indica que no importa el valor que tenga el dato -# $7 Método de syncronización 1=Sincronización1 2=Sincronizacion2 -# $8 Ruta de origen de la Imagen (Carpeta) - - -#___________________________________________________________________ -# -# Control parámetros -#___________________________________________________________________ - - PROG="$(basename $0)" - if [ $# -lt 7 ]; then - usage=" ndisco nparticion nombre_imagen_basica ip_repositorio" - usage="$usage copiar_a_caché Borrar_cache_previamente Ruta_origen" - ogRaiseError $OG_ERR_FORMAT "$MSG_FORMAT: $PROG $usage" - exit $? - fi - - DISCO=$1 - NPART=$2 - NOMBREIMG=$3 - IPREPOSITORIO=$4 - - flag=$5 - echo "flah:$flag">/tmp/log - WHLFILE=${flag:0:1} - RMVFILE=${flag:1:1} - CMPFILE=${flag:2:1} - - flag=$6 - echo "flah:$flag">/tmp/log - BORRARIMG=${flag:0:1} - COPIACACHE=${flag:1:1} - BORRACACHE=${flag:2:1} - NOBORRACHIVOS=${flag:3:1} - - RUTAORIGEN=$8 -#___________________________________________________________________ -# -# Variables y configuración logs -#___________________________________________________________________ - - source /opt/opengnsys/scripts/ImagenesSincronizadas.lib - -#___________________________________________________________________ -# -# Activa navegador para ver progreso -#___________________________________________________________________ - - coproc /opt/opengnsys/bin/browser -qws http://localhost/cgi-bin/httpd-log.sh -#___________________________________________________________________ -# -# Proceso -#___________________________________________________________________ - - echo "Creacion de imagen basica..." | tee -a $OGLOGSESSION $OGLOGFILE - - ORIGEN=$PARTICION$RUTAORIGEN/ - DESTINO="$REPOSITORIO/$NOMBREIMG/" - - # Borrado previo de imagen en repositorio - if [ $BORRARIMG -eq 1 ]; then - echo "Borrando previamente imagen del repositorio" | tee -a $OGLOGSESSION $OGLOGFILE - fi - - ## Marca de localizacion Repositorio - FILE_IMG=".marcimg" - MARCA=$ORIGEN$FILE_IMG - ## Marca de localizacion Repositorio en Imagen - if [[ ! -f $MARCA ]]; then - ##echo "Creando Marca Repositorio en Imagen $MARCA" | tee -a $OGLOGSESSION $OGLOGFILE - touch $MARCA - fi - - # Creación de la imagen básica - echo "Sincronizando imagen entre $ORIGEN y $DESTINO" | tee -a $OGLOGSESSION $OGLOGFILE - crearListaAcl $ORIGEN $DESTINO $SISTEMAFICHERO $DISCO $NPART - crearImagen $ORIGEN $DESTINO $SISTEMAFICHERO 1 - RETVAL=$? - if [ $RETVAL -ne 0 ]; then - kill $COPROC_PID - exit $OG_ERR_IMAGE - fi - - echo "!!! Finaliza correctamente !!! la creacion de imagen básica $NOMBREIMG entre $ORIGEN y $DESTINO" | tee -a $OGLOGSESSION $OGLOGFILE - - # Copia opcional a la caché - if [ $COPIACACHE -eq 1 ]; then - echo "Copiando imagen a cache" | tee -a $OGLOGSESSION $OGLOGFILE - CACHE=$(montaCache) - if [ -z $CACHE ]; then - echo "No se ha podido copiar la imagen a la cache" | tee -a $OGLOGSESSION $OGLOGFILE - kill $COPROC_PID - exit $OG_ERR_NOTCACHE - fi - - # Borrar imagen de la caché - if [ $BORRACACHE -eq 1 ]; then - echo "Borrando imagen $NOMBREIMG de la cache" | tee -a $OGLOGSESSION $OGLOGFILE - rm -R $CACHE$OGIMG/$NOMBREIMG - fi - - # Redefinir destino - DESTINO="$CACHE$OGIMG/$NOMBREIMG/" - - ## Marca de localizacion Repositorio - FILE_IMG=".marcimg" - MARCA=$ORIGEN$FILE_IMG - ## Marca de localizacion Repositorio en Imagen - if [[ ! -f $MARCA ]]; then - ##echo "Creando Marca Repositorio en Imagen $MARCA" | tee -a $OGLOGSESSION $OGLOGFILE - touch $MARCA - fi - - echo "Sincronizando imagen entre $ORIGEN y $DESTINO" | tee -a $OGLOGSESSION $OGLOGFILE - - crearImagen $ORIGEN $DESTINO $SISTEMAFICHERO 2 - - RETVAL=$? - if [ $RETVAL -ne 0 ]; then - kill $COPROC_PID - exit $OG_ERR_IMAGE - fi - echo "!!! Finaliza correctamente !!! la copia de imagen basica $NOMBREIMG a la cache" | tee -a $OGLOGSESSION $OGLOGFILE - fi - - eliminaListaAcl $ORIGEN $SISTEMAFICHERO - ## Eliminar Marca Repositorio - rm $MARCA - - #comprimeImagen $NOMBREIMG - RETVAL=$? - if [ $RETVAL -ne 0 ]; then - kill $COPROC_PID - exit $OG_ERR_IMAGE - fi -#___________________________________________________________________ -# -# Retorno -#___________________________________________________________________ - - kill $COPROC_PID - exit 0 diff --git a/client/shared/scripts/ogCrearSoftIncremental b/client/shared/scripts/ogCrearSoftIncremental deleted file mode 100755 index de91670..0000000 --- a/client/shared/scripts/ogCrearSoftIncremental +++ /dev/null @@ -1,185 +0,0 @@ -#!/bin/bash -#___________________________________________________________________ -# -# CREAR IMAGEN INCREMENTAL -#___________________________________________________________________ -# -# Parámetros recibidos desde el cliente: -# -# $1 Número de disco -# $2 Número de particion -# $3 Nombre canónico de la imagen básica (sin extensión) -# $4 Dirección del repositorio -# $5 Nombre canónico de la imagen incremental (sin extensión) -# $6 Es una cadena "nnn" tipo flags que codifica varios parametros. -# Tiene el formato "nnn" donde "n" vale 0 ó 1. -# 1XX: Gestionar fichero completo en lugar de diferencas -# X1X: Eliminar archivos de la imagen que no estén en la partición -# XX1: Comprimir archivos para enviarlos por la red -# El valor X indica que no importa el valor que tenga el dato -# $7 Es una cadena "nnnn" tipo flags que codifica varios parametros. -# Tiene el formato "nnnn" donde "n" vale 0 ó 1. -# 1XXX: Borrar la imagen incremental del repositorio antes de crearla -# X1XX: Copiar imagen incremental también a la cache -# XX1X: Borrar previamente la imagen incremental de la cache antes de copiarla -# XXX1: No borrar archivos en destino -# El valor X indica que no importa el valor que tenga el dato -# $8 Método de syncronización 1=Sincronización1 2=Sincronizacion2 -# $9 Ruta de origen de la Imagen (Carpeta) - -#___________________________________________________________________ -# -# Control parámetros -#___________________________________________________________________ - - PROG="$(basename $0)" - if [ $# -lt 7 ]; then - usage=" ndisco nparticion nombre_imagen_basica ip_repositorio nombre_imagen_incremental" - usage="$usage copiar_a_caché Borrar_cache_previamente Ruta_origen" - ogRaiseError $OG_ERR_FORMAT "$MSG_FORMAT: $PROG $usage" - exit $? - fi - - DISCO=$1 - NPART=$2 - NOMBREIMG=$3 - IPREPOSITORIO=$4 - NOMBREIMGINC=$5 - - flag=$6 - echo "flah:$flag">/tmp/log - WHLFILE=${flag:0:1} - RMVFILE=${flag:1:1} - CMPFILE=${flag:2:1} - - flag=$7 - echo "flah:$flag">/tmp/log - BORRARIMG=${flag:0:1} - COPIACACHE=${flag:1:1} - BORRACACHE=${flag:2:1} - NOBORRACHIVOS=${flag:3:1} - - RUTAORIGEN=$9 -#___________________________________________________________________ -# -# Variables y configuración logs -#___________________________________________________________________ - - source /opt/opengnsys/scripts/ImagenesSincronizadas.lib -#___________________________________________________________________ -# -# Activa navegador para ver progreso -#___________________________________________________________________ - - coproc /opt/opengnsys/bin/browser -qws http://localhost/cgi-bin/httpd-log.sh -#___________________________________________________________________ -# -# Lista de archivos a sincronizar -#___________________________________________________________________ - - TMPFILELIST="/tmp/_listatmp_" - FILELIST="/tmp/_lista_" -#___________________________________________________________________ -# -# Proceso -#___________________________________________________________________ - - echo "Creacion de imagen incremental..." | tee -a $OGLOGSESSION $OGLOGFILE - - ORIGEN=$PARTICION$RUTAORIGEN/ - DESTINO="$REPOSITORIO/$NOMBREIMG/" - - # Borrado previo de imagen en repositorio - if [ $BORRARIMG -eq 1 ]; then - echo "Borrando previamente imagen del $NOMBREIMGINC repositorio" | tee -a $OGLOGSESSION $OGLOGFILE - fi - - ## Marca de localizacion Repositorio - FILE_IMG=".marcimg" - MARCAORIGEN=$ORIGEN$FILE_IMG - ## Marca de localizacion Repositorio en Imagen - if [ ! -f $MARCAORIGEN ]; then - ##echo "Creando Marca Repositorio en Particion $MARCAORIGEN" | tee -a $OGLOGSESSION $OGLOGFILE - touch $MARCAORIGEN - fi - - # Creación de la lista de archivos entre partición e imagen básica del repositorio - echo "Creacion de la lista de archivos a transferir entre $ORIGEN y $DESTINO" | tee -a $OGLOGSESSION $OGLOGFILE - crearListaAcl $ORIGEN $DESTINO $SISTEMAFICHERO $DISCO $NPART - crearImagen $ORIGEN $DESTINO $SISTEMAFICHERO 1 1 $TMPFILELIST - RETVAL=$? - if [ $RETVAL -ne 0 ]; then - kill $COPROC_PID - exit $OG_ERR_IMAGE - fi - - # Editar la lista y depurarla - editarLista $TMPFILELIST $FILELIST - - # Creación de la imagen incremental en el repositorio - DESTINO="$REPOSITORIO/$NOMBREIMGINC/" - echo "Sincronizacion para crear imagen incremental entre $ORIGEN y $DESTINO" | tee -a $OGLOGSESSION $OGLOGFILE - crearImagen $ORIGEN $DESTINO $SISTEMAFICHERO 1 2 $FILELIST - RETVAL=$? - if [ $RETVAL -ne 0 && [$RETVAL -ne 23]; then - kill $COPROC_PID - exit $OG_ERR_IMAGE - fi - ## Marca de localizacion Repositorio - FILE_IMG=".marcimg" - MARCA=$DESTINO$FILE_IMG - ## Marca de localizacion Repositorio en Imagen - if [ ! -f $MARCA ]; then - ##echo "Creando Marca Repositorio en Imagen $MARCA" | tee -a $OGLOGSESSION $OGLOGFILE - touch $MARCA - fi - - echo "!!! Finaliza correctamente !!! la creacion de imagen incremental $NOMBREIMGINC entre $ORIGEN y $DESTINO" | tee -a $OGLOGSESSION $OGLOGFILE - - # Copia opcional a la caché - if [ $COPIACACHE -eq 1 ]; then - echo "Copiando imagen a cache" | tee -a $OGLOGSESSION $OGLOGFILE - CACHE=$(montaCache) - if [ -z $CACHE ]; then - echo "No se ha podido copiar la imagen a la cache" | tee -a $OGLOGSESSION $OGLOGFILE - kill $COPROC_PID - exit $OG_ERR_NOTCACHE - fi - - # Borrar imagen de la caché - if [ $BORRACACHE -eq 1 ]; then - echo "Borrando imagen $NOMBREIMGINC de la cache" | tee -a $OGLOGSESSION $OGLOGFILE - rm -R $CACHE$OGIMG/$NOMBREIMGINC - fi - - DESTINO="$CACHE$OGIMG/$NOMBREIMGINC/" - echo "Sincronizando imagen entre $ORIGEN y $DESTINO" | tee -a $OGLOGSESSION $OGLOGFILE - crearImagen $ORIGEN $DESTINO $SISTEMAFICHERO 2 2 $FILELIST - RETVAL=$? - if [ $RETVAL -ne 0 ]; then - kill $COPROC_PID - exit $OG_ERR_IMAGE - fi - echo "!!! Finaliza correctamente !!! la copia de imagen incremental $NOMBREIMGINC a la cache" | tee -a $OGLOGSESSION $OGLOGFILE - fi - - ## Marca de localizacion Repositorio - FILE_IMG=".marcimg" - MARCA=$DESTINO$FILE_IMG - ## Marca de localizacion Repositorio en CACHE - if [ ! -f $MARCA ]; then - ##echo "Creando Marca Repositorio en CACHE $MARCA" | tee -a $OGLOGSESSION $OGLOGFILE - touch $MARCA - fi - - ## Eliminando Marca Origen Repositorio - rm $MARCAORIGEN - eliminaListaAcl $ORIGEN $SISTEMAFICHERO -#___________________________________________________________________ -# -# Retorno -#___________________________________________________________________ - - kill $COPROC_PID - exit 0 - diff --git a/client/shared/scripts/ogRestaurarImagenBasica b/client/shared/scripts/ogRestaurarImagenBasica deleted file mode 100755 index d045f83..0000000 --- a/client/shared/scripts/ogRestaurarImagenBasica +++ /dev/null @@ -1,206 +0,0 @@ -#!/bin/bash -#___________________________________________________________________ -# -# RESTAURAR IMAGEN BÁSICA -#___________________________________________________________________ -# -# Parámetros recibidos desde el cliente: -# -# $1 Número de disco -# $2 Número de particion -# $3 Nombre canónico de la imagen básica (sin extensión) -# $4 Dirección del repositorio -# $5 Tipo de transmisión 0=Unicast 1=Multicast -# $6 Es una cadena "nnn" tipo flags que codifica varios parametros. -# Tiene el formato "nnn" donde "n" vale 0 ó 1. -# 1XX: Gestionar fichero completo en lugar de diferencas -# X1X: Eliminar archivos de la partición que no estén en la imagen -# XX1: Comprimir archivos para enviarlos por la red -# El valor X indica que no importa el valor que tenga el dato -# $7 Es una cadena "nnnn" tipo flags que codifica varios parametros. -# Tiene el formato "nnnn" donde "n" vale 0 ó 1. -# 1XXX: Borrar la particion de destino antes de restaurar la imagen basica -# X1XX: Copiar Imagen básica también a la cache -# XX1X: Borrar previamente la imagen basica de la cache antes de copiarla -# XXX1: No borrar archivos en destino -# El valor X indica que no importa el valor que tenga el dato -# $8 Método de clonación 0=Desde caché 1=Desde repositorio -# $9 Metodo de sincronizacion 1=Sincronización1 2=Sincronizacion2 -# $10 Ruta de origen de la Imagen (Carpeta) -#___________________________________________________________________ -# -# Control parámetros -#___________________________________________________________________ - - PROG="$(basename $0)" - if [ $# -lt 8 ]; then - usage=" ndisco nparticion nombre_imagen_basica ip_repositorio copiar_a_caché " - usage="$usage Borrar_cache_previamente metodo_clonación Ruta_origen" - ogRaiseError $OG_ERR_FORMAT "$MSG_FORMAT: $PROG $usage" - exit $? - fi - - DISCO=$1 - NPART=$2 - NOMBREIMG=$3 - IPREPOSITORIO=$4 - - flag=$6 - echo "flah:$flag">/tmp/log - WHLFILE=${flag:0:1} - RMVFILE=${flag:1:1} - CMPFILE=${flag:2:1} - - flag=$7 - echo "flah:$flag">/tmp/log - BORRARIMG=${flag:0:1} - COPIACACHE=${flag:1:1} - BORRACACHE=${flag:2:1} - NOBORRACHIVOS=${flag:3:1} - - METCLONA=$8 - RUTAORIGEN=${10} -#___________________________________________________________________ -# -# Variables y configuración logs -#___________________________________________________________________ - - source /opt/opengnsys/scripts/ImagenesSincronizadas.lib -#___________________________________________________________________ -# -# Activa navegador para ver progreso -#___________________________________________________________________ - - coproc /opt/opengnsys/bin/browser -qws http://localhost/cgi-bin/httpd-log.sh - -#___________________________________________________________________ -# -# Proceso -#___________________________________________________________________ - - # Llamada a la función de sincronización. - echo "Restauracion de imagen basica..." | tee -a $OGLOGSESSION $OGLOGFILE - - # Borrado previo de la partición - if [ $BORRARIMG -eq 1 ]; then - if [ -n $RUTAORIGEN ]; then - echo "Borrando carpeta $PARTICION$RUTAORIGEN" | tee -a $OGLOGSESSION $OGLOGFILE - rm -R $PARTICION$RUTAORIGEN - else - echo "Formateando la particion" | tee -a $OGLOGSESSION $OGLOGFILE - ogFormat $DISCO $PARTICION $TIPOPARTICION - RETVAL=$? - if [ $RETVAL -ne 0 ]; then - kill $COPROC_PID - exit $OG_ERR_PARTITION - fi - fi - fi - - # Sincronización desde repositorio - if [ $METCLONA = 1 ]; then - if [ $COPIACACHE = 0 ]; then - echo "Restaurando imagen basica desde repositorio $IPREPOSITORIO" | tee -a $OGLOGSESSION $OGLOGFILE - ORIGEN="$REPOSITORIO/$NOMBREIMG/" - DESTINO=$PARTICION$RUTAORIGEN/ - # Restauración de la imagen básica - echo "Sincronizando imagen entre $ORIGEN y $DESTINO" | tee -a $OGLOGSESSION $OGLOGFILE - restaurarImagen $ORIGEN $DESTINO $SISTEMAFICHERO 1 - - RETVAL=$? - if [ $RETVAL -ne 0 ]; then - echo "***Error:$RETVAL. restaurando imagen basica desde repositorio $IPREPOSITORIO" | tee -a $OGLOGSESSION $OGLOGFILE - kill $COPROC_PID - exit $OG_ERR_IMAGE - fi - restauraListaAcl $ORIGEN $DESTINO $SISTEMAFICHERO $DISCO $NPART - - RETVAL=$? - if [ $RETVAL -ne 0 ]; then - echo "***Error:$RETVAL. restaurando listas ACL desde repositorio $IPREPOSITORIO" | tee -a $OGLOGSESSION $OGLOGFILE - kill $COPROC_PID - exit $OG_ERR_IMAGE - fi - echo "!!! Finaliza correctamente !!! la restauracion de imagen basica $NOMBREIMG desde repositorio $IPREPOSITORIO" | tee -a $OGLOGSESSION $OGLOGFILE - kill $COPROC_PID - exit 0 - fi - fi - - if [ $METCLONA = 0 ]; then - # Restauración desde la caché - echo "Restaurando desde la cache" | tee -a $OGLOGSESSION $OGLOGFILE - fi - # Comprobar si existe caché - CACHE=$(montaCache) - if [ -z $CACHE ]; then - echo "No se puede restaurar la imagen desde la cache ya que no existe" | tee -a $OGLOGSESSION $OGLOGFILE - kill $COPROC_PID - ogRaiseError $OG_ERR_NOTCACHE "CACHE" - exit $OG_ERR_NOTCACHE - fi - - # Borrar imagen de la caché - if [ $BORRACACHE -eq 1 ]; then - echo "Borrando imagen $NOMBREIMG de la cache" | tee -a $OGLOGSESSION $OGLOGFILE - rm -R $CACHE$OGIMG/$NOMBREIMG - fi - - # Actualización de la caché - echo "Actualizando cache local desde repositorio $IPREPOSITORIO" | tee -a $OGLOGSESSION $OGLOGFILE - ORIGEN="$REPOSITORIO/$NOMBREIMG/" - DESTINO="$CACHE$OGIMG/$NOMBREIMG/" - echo "Sincronizando imagen entre $ORIGEN y $DESTINO" | tee -a $OGLOGSESSION $OGLOGFILE - restaurarImagen $ORIGEN $DESTINO $SISTEMAFICHERO 1 - - RETVAL=$? - if [ $RETVAL -ne 0 ]; then - echo "***Error:$RETVAL. Actualizando cache local desde repositorio $IPREPOSITORIO" | tee -a $OGLOGSESSION $OGLOGFILE - kill $COPROC_PID - exit $OG_ERR_IMAGE - fi - - echo "!!! Finaliza correctamente !!! copia de imagen basica a cache local" | tee -a $OGLOGSESSION $OGLOGFILE - - # Restauración desde caché a la partición - echo "Copiando imagen a la particion desde cache local" | tee -a $OGLOGSESSION $OGLOGFILE - - ORIGEN="$CACHE$OGIMG/$NOMBREIMG/" - DESTINO="$PARTICION$RUTAORIGEN/" - echo "Sincronizando imagen entre $ORIGEN y $DESTINO" | tee -a $OGLOGSESSION $OGLOGFILE - restaurarImagen $ORIGEN $DESTINO $SISTEMAFICHERO 2 - - RETVAL=$? - if [ $RETVAL -ne 0 ]; then - echo "***Error:$RETVAL. restaurando imagen basica desde cache local" | tee -a $OGLOGSESSION $OGLOGFILE - kill $COPROC_PID - exit $OG_ERR_IMAGE - fi - - restauraListaAcl $ORIGEN $DESTINO $SISTEMAFICHERO $DISCO $NPART - RETVAL=$? - if [ $RETVAL -ne 0 ]; then - echo "***Error:$RETVAL. restaurando listas ACL desde cache local" | tee -a $OGLOGSESSION $OGLOGFILE - kill $COPROC_PID - exit $OG_ERR_IMAGE - fi - - echo "Ejecutando configure OS" | tee -a $OGLOGSESSION $OGLOGFILE - if which configureOsCustom &>/dev/null; then - ogEcho log session "[90] configureOsCustom" - configureOsCustom "$DISCO" "$NPART" "$IPREPOSITORIO" "$NOMBREIMG" - else - ogEcho log session "[90] $MSG_SCRIPTS_OS_CONFIGURE " - configureOs "$DISCO" "$NPART" - fi - - - echo "!!! Finaliza correctamente !!! restauracion imagen basica $NOMBREIMG desde cache local" | tee -a $OGLOGSESSION $OGLOGFILE -#___________________________________________________________________ -# -# Retorno -#___________________________________________________________________ - - kill $COPROC_PID - - exit 0 diff --git a/client/shared/scripts/ogRestaurarSoftIncremental b/client/shared/scripts/ogRestaurarSoftIncremental deleted file mode 100755 index a4f73aa..0000000 --- a/client/shared/scripts/ogRestaurarSoftIncremental +++ /dev/null @@ -1,209 +0,0 @@ -#!/bin/bash -#___________________________________________________________________ -# -# RESTAURAR IMAGEN INCREMENTAL -#___________________________________________________________________ -# -# Parámetros recibidos desde el cliente: -# -# $1 Número de disco -# $2 Número de particion -# $3 Nombre canónico de la imagen básica (sin extensión) -# $4 Dirección del repositorio -# $5 Nombre canónico del software incremental (sin extensión) -# $6 Tipo de transmisión 0=Unicast 1=Multicast -# $7 Es una cadena "nnn" tipo flags que codifica varios parametros. -# Tiene el formato "nnn" donde "n" vale 0 ó 1. -# 1XX: Gestionar fichero completo en lugar de diferencas -# X1X: Eliminar archivos de la partición que no estén en la imagen -# XX1: Comprimir archivos para enviarlos por la red -# El valor X indica que no importa el valor que tenga el dato -# $8 Es una cadena "nnnn" tipo flags que codifica varios parametros. -# Tiene el formato "nnnn" donde "n" vale 0 ó 1. -# 1XXX: Borrar la particion de destino antes de restaurar la imagen basica -# X1XX: Copiar Imagen básica también a la cache -# XX1X: Borrar previamente la imagen basica de la cache antes de copiarla -# XXX1: No borrar archivos en destino -# El valor X indica que no importa el valor que tenga el dato -# $9 Método de clonación 0=Desde caché 1=Desde repositorio -# $10 Metodo de sincronizacion 1=Sincronización1 2=Sincronizacion2 -# $11 Ruta de origen de la Imagen (Carpeta) -#___________________________________________________________________ -# -# Control parámetros -#___________________________________________________________________ - - PROG="$(basename $0)" - if [ $# -lt 6 ]; then - usage=" ndisco nparticion nombre_imagen_basica ip_repositorio copiar_a_caché " - usage="$usage Borrar_cache_previamente metodo_clonación Ruta_origen" - ogRaiseError $OG_ERR_FORMAT "$MSG_FORMAT: $PROG $usage" - exit $? - fi - - DISCO=$1 - NPART=$2 - NOMBREIMG=$3 - IPREPOSITORIO=$4 - NOMBREIMGINC=$5 - - flag=$7 - echo "flah:$flag">/tmp/log - WHLFILE=${flag:0:1} - RMVFILE=${flag:1:1} - CMPFILE=${flag:2:1} - - flag=$8 - echo "flah:$flag">/tmp/log - BORRARIMG=${flag:0:1} - COPIACACHE=${flag:1:1} - BORRACACHE=${flag:2:1} - NOBORRACHIVOS=${flag:3:1} - - METCLONA=$9 - RUTAORIGEN=${11} -#___________________________________________________________________ -# -# Variables y configuración logs -#___________________________________________________________________ - - source /opt/opengnsys/scripts/ImagenesSincronizadas.lib - -#___________________________________________________________________ -# -# Activa navegador para ver progreso -#___________________________________________________________________ - - coproc /opt/opengnsys/bin/browser -qws http://localhost/cgi-bin/httpd-log.sh -#___________________________________________________________________ -# -# Proceso -#___________________________________________________________________ - - echo "Restauracion de imagen incremental..." | tee -a $OGLOGSESSION $OGLOGFILE - - # Borrado previo de la partición - if [ $BORRARIMG -eq 1 ]; then - if [ -n $RUTAORIGEN ]; then - echo "Borrando carpeta $PARTICION$RUTAORIGEN" | tee -a $OGLOGSESSION $OGLOGFILE - rm -R $PARTICION$RUTAORIGEN - else - echo "Formateando la particion" | tee -a $OGLOGSESSION $OGLOGFILE - ogFormat $DISCO $PARTICION $TIPOPARTICION - RETVAL=$? - if [ $RETVAL -ne 0 ]; then - kill $COPROC_PID - exit $OG_ERR_PARTITION - fi - fi - fi - - # Sincronización desde repositorio - if [ $METCLONA = 1 ]; then - if [ $COPIACACHE = 0 ]; then - echo "Restaurando imagen basica desde repositorio $IPREPOSITORIO" | tee -a $OGLOGSESSION $OGLOGFILE - ORIGEN="$REPOSITORIO/$NOMBREIMG/" - DESTINO=$PARTICION$RUTAORIGEN/ - # Restauración de la imagen básica - echo "Sincronizando imagen basica entre $ORIGEN y $DESTINO" | tee -a $OGLOGSESSION $OGLOGFILE - restaurarImagen $ORIGEN $DESTINO $SISTEMAFICHERO 1 - RETVAL=$? - if [ $RETVAL -ne 0 ]; then - kill $COPROC_PID - exit $OG_ERR_IMAGE - fi - - # Restauración de la imagen incremental - echo "Restaurando imagen incremental desde repositorio $IPREPOSITORIO" | tee -a $OGLOGSESSION $OGLOGFILE - ORIGEN="$REPOSITORIO/$NOMBREIMGINC/" - DESTINO=$PARTICION$RUTAORIGEN/ - OP_DELETE="" - # Restauración de la imagen incremental - echo "Sincronizando imagen entre $ORIGEN y $DESTINO" | tee -a $OGLOGSESSION $OGLOGFILE - restaurarImagen $ORIGEN $DESTINO $SISTEMAFICHERO 1 - restauraListaAcl $ORIGEN $DESTINO $SISTEMAFICHERO $DISCO $NPART - RETVAL=$? - kill $COPROC_PID - echo "!!! Finaliza correctamente !!! la restauracion de imagen incremental $NOMBREIMGINC desde repositorio $IPREPOSITORIO" | tee -a $OGLOGSESSION $OGLOGFILE - exit $RETVAL - fi - fi - - # Restauración desde la caché - echo "Restaurando desde la cache" | tee -a $OGLOGSESSION $OGLOGFILE - - # Comprobar si existe caché - CACHE=$(montaCache) - if [ -z $CACHE ]; then - echo "No se ha podido restaurar la imagen desde la cache" | tee -a $OGLOGSESSION $OGLOGFILE - ogRaiseError $OG_ERR_NOTCACHE "CACHE" - kill $COPROC_PID - exit $? - fi - - # Borrar imagen de la caché - if [ $BORRACACHE -eq 1 ]; then - echo "Borrando imagen basica $NOMBREIMG de la cache" | tee -a $OGLOGSESSION $OGLOGFILE - rm -R $CACHE$OGIMG/$NOMBREIMG - echo "Borrando imagen incremental $NOMBREIMGINC de la cache" | tee -a $OGLOGSESSION $OGLOGFILE - rm -R $CACHE$OGIMG/$NOMBREIMGINC - fi - - # Actualización de la caché - echo "Actualizando cache local desde repositorio $IPREPOSITORIO" | tee -a $OGLOGSESSION $OGLOGFILE - - ORIGEN="$REPOSITORIO/$NOMBREIMG/" - DESTINO="$CACHE$OGIMG/$NOMBREIMG/" - echo "Sincronizando imagen basica entre $ORIGEN y $DESTINO" | tee -a $OGLOGSESSION $OGLOGFILE - restaurarImagen $ORIGEN $DESTINO $SISTEMAFICHERO 1 - RETVAL=$? - if [ $RETVAL -ne 0 ]; then - kill $COPROC_PID - exit $OG_ERR_IMAGE - fi - ORIGEN="$REPOSITORIO/$NOMBREIMGINC/" - DESTINO="$CACHE$OGIMG/$NOMBREIMGINC/" - echo "Sincronizando imagen incremental entre $ORIGEN y $DESTINO" | tee -a $OGLOGSESSION $OGLOGFILE - restaurarImagen $ORIGEN $DESTINO $SISTEMAFICHERO 1 - RETVAL=$? - if [ $RETVAL -ne 0 ]; then - kill $COPROC_PID - exit $OG_ERR_IMAGE - fi - - echo "!!! Finaliza correctamente !!! copia de imagen basica e incremental a cache local" | tee -a $OGLOGSESSION $OGLOGFILE - - # Restauración desde caché a la partición - - echo "Copiando imagen basica a la partición desde cache local" | tee -a $OGLOGSESSION $OGLOGFILE - ORIGEN="$CACHE$OGIMG/$NOMBREIMG/" - DESTINO=$PARTICION$RUTAORIGEN/ - echo "Sincronizando imagen entre $ORIGEN y $DESTINO" | tee -a $OGLOGSESSION $OGLOGFILE - restaurarImagen $ORIGEN $DESTINO $SISTEMAFICHERO 2 - RETVAL=$? - if [ $RETVAL -ne 0 ]; then - kill $COPROC_PID - exit $OG_ERR_IMAGE - fi - - echo "Copiando imagen incremental a la partición desde cache local" | tee -a $OGLOGSESSION $OGLOGFILE - ORIGEN="$CACHE$OGIMG/$NOMBREIMGINC/" - DESTINO=$PARTICION$RUTAORIGEN/ - OP_DELETE="" - echo "Sincronizando imagen entre $ORIGEN y $DESTINO" | tee -a $OGLOGSESSION $OGLOGFILE - restaurarImagen $ORIGEN $DESTINO $SISTEMAFICHERO 2 - restauraListaAcl $ORIGEN $DESTINO $SISTEMAFICHERO $DISCO $NPART - RETVAL=$? - if [ $RETVAL -ne 0 ]; then - kill $COPROC_PID - exit $OG_ERR_IMAGE - fi - echo "!!! Finaliza correctamente !!! restauracion imagen incremental $NOMBREIMGINC desde cache local" | tee -a $OGLOGSESSION $OGLOGFILE -#___________________________________________________________________ -# -# Retorno -#___________________________________________________________________ - - kill $COPROC_PID - - exit 0 diff --git a/client/shared/scripts/restoreBaseImage b/client/shared/scripts/restoreBaseImage deleted file mode 100755 index 046c465..0000000 --- a/client/shared/scripts/restoreBaseImage +++ /dev/null @@ -1,175 +0,0 @@ -#!/bin/bash -#/** -# restoreBaseImage -#@brief Script de ejemplo para restaurar una imagen -#@brief Se usa como base para el programa de restauración de imágenes de OpenGnsys Admin). -#@param 1 REPO|CACHE|IPREPO -#@param 2 imagen -#@param 3 disco -#@param 4 particion -#@param 5 protocolo -#@param 6 opciones protocolo -#@return -#@exception OG_ERR_FORMAT # 1 formato incorrecto. -#@exception updateCache # 15 No hay cache -#@exception updateCache # 16 no hay espacio sufiente -#@exception OG_ERR_LOCKED # 4 Partición o fichero bloqueado. -#@exception OG_ERR_NOTFOUND # 2 Fichero o dispositivo no encontrado. -#@exception OG_ERR_FILESYS # 20 Sistema de archivos desconocido o no se puede montar -#@exception OG_ERR_DONTSYNC_IMAGE # 71 Imagen no sincronizable (es monolitica) -#@exception OG_ERR_DONTMOUNT_IMAGE # 70 Error al montar una imagen sincronizada. -#@note se toma como punto de partida el script restoreImage -#@version 1.0 - restauración de imagen con sincronizacion. -#@author -#@date 2012-12-04 -#@version 1.1.1 - Varios repositorios para un mismo cliente (ticket #679). -#@author Irina Gomez - ETSII Universidad de Sevilla -#@date 2018/11/06 -#*/ ## - -trap "onexit $REPO \"$2\" $3 $4" 1 2 3 6 9 14 15 EXIT - -# Si salimos con error demontamos la imagen y desbloqueamos la imagen y la particion -function onexit() { - local exit_status=$? - ogUnmountImage "$REPO" "$2" "$IMGEXT" &>/dev/null - [ $exit_status -ne 4 ] && ogUnlock $3 $4 &>/dev/null - exit $exit_status -} - -# Limpiamos los archivo de log -echo -n "" >$OGLOGCOMMAND; -[ "$(ogGetCaller)" == "RestaurarImagenBasica" -o "$(ogGetCaller)" == "restoreDiffImage" ] || echo -n "" > $OGLOGSESSION - -# Mensaje de inicio de script salvo si es llamado desde restoreDiffImage. -[ "$(ogGetCaller)" == "restoreDiffImage" ] || ogEcho log session "[1] $MSG_SCRIPTS_START $0 $*" - -TIME1=$SECONDS -PROG="$(basename $0)" -# Si se solicita, mostrar ayuda. -if [ "$*" == "help" ]; then - ogHelp "$PROG: $MSG_HELP_restoreBaseImage" \ - "$PROG REPO|CACHE imagen ndisco nparticion [ UNICAST|MULTICAST|TORRENT ] [opciones protocolo]" \ - "$PROG REPO Windows7 1 1 " \ - "$PROG CACHE Ubuntu12 1 6 MULTICAST 9000:full-duplex:239.194.14.00:150M:50:60" - exit 0 -fi - -[ $# -lt 4 ] && exit $(ogRaiseError session $OG_ERR_FORMAT "$MSG_FORMAT: $PROG REPO|CACHE imagen ndisco nparticion [ UNICAST|MULTICAST|TORRENT ] [opciones protocolo]"; echo $?) - -# Procesar parámetros de entrada -REPO="${1^^}" -# No permite directorios diferentes para OU -OGUNIT="" - -# Si es una ip y es igual a la del equipo restaura desde cache -[ "$REPO" == "$(ogGetIpAddress)" ] && REPO="CACHE" -# Si es una ip y es distinta a la del recurso samba cambiamos de REPO. -ogCheckIpAddress $REPO -if [ $? == 0 -o $REPO == "REPO" ] ; then - # Si falla el cambio -> salimos con error repositorio no valido - ogChangeRepo $REPO ${OGUNIT%/} || exit $(ogRaiseError $OG_ERR_NOTFOUND '$REPO $OGUNIT'; echo $?) - REPO="REPO" -fi - -# Carga del configurador del engine -[ -z $OGENGINECONFIGURATE ] && source /opt/opengnsys/etc/engine.cfg - -# Procesar parámetros de entrada -REPOFILE="$(ogGetPath "REPO" "$2.$IMGEXT")" -[ -n "$REPOFILE" ] || exit $(ogRaiseError session $OG_ERR_NOTFOUND "REPO, $2.$IMGEXT"; echo $?) - -# Comprobar que es sincronizable (con REPO) -ogIsSyncImage REPO "$2" $IMGEXT || exit $(ogRaiseError session $OG_ERR_DONTSYNC_IMAGE "$REPO $2"; echo $?) - -IMGEXT="img" -# Comprobamos si la imagen o la particion estan bloqueada: -ogIsImageLocked "REPO" "$2.$IMGEXT" && exit $(ogRaiseError session $OG_ERR_LOCKED "$REPO $2.$IMGEXT"; echo $?) -ogIsLocked "$3" "$4" && exit $(ogRaiseError session $OG_ERR_LOCKED "$3 $4"; echo $?) - -# Detectamos el sistema de ficheros de la imagen -# TODO ogGetImageInfo -DIRMOUNT=$(ogMountImage "REPO" "$2") -ogWaitSyncImage "REPO" "$2" "$IMGEXT" "mounted" || exit $(ogRaiseError session $OG_ERR_DONTMOUNT_IMAGE "$REPO $2 $IMGEXT: time_out."; echo $?) -IMGFSTYPE=$(head -1 $DIRMOUNT/ogimg.info |cut -d: -f3) - -# Comprobamos si el sistema de ficheros se puede montar -ogMount "$3" "$4" &>/dev/null || RETVAL=$? -# Si da error formateamos el sistema de ficheros. -if [ "$RETVAL" ] ; then - ogEcho log session " $MSG_HELP_ogFormat: $3 $4 $IMGFSTYPE" - ogFormat "$3" "$4" "$IMGFSTYPE" || exit $(ogRaiseError session $OG_ERR_FILESYS "$3 $4"; echo $?) - ogMount "$3" "$4" || exit $(ogRaiseError session $OG_ERR_FILESYS "$3 $4"; echo $?) -fi - -# Warning si el sistema de ficheros de la imagen es distinto del de la particion destino -[ "$IMGFSTYPE" == "$(ogGetFsType $3 $4)" ] || ogEcho session warning "$MSG_SYNC_DIFFERENTFS" - - -PROTO=${5:-"UNICAST"} -if [ "$REPO" == "CACHE" -o "$REPO" == "cache" ]; then - # Las imagenes sincronizables aunque sean iguales no tienen el mismo sum. - # Sincronizamos si existe el fichero y si no usamos updateCache. - ogEcho log session "[10] $MSG_SCRIPTS_TASK_START updateCache REPO $2.$IMGEXT $PROTO $6" - ogEcho log session " updateCache REPO" "/$2.$IMGEXT" "$PROTO" "$6" - updateCache REPO "/$2.$IMGEXT" "$PROTO" "$6" - RETVAL=$? - if [ "$RETVAL" != "0" ] - then - ogEcho log session "[39] $MSG_SCRIPTS_TASK_END updateCache REPO $2.$IMGEXT $5 $6 con error $RETVAL" - # RC=15 No hay cache - # RC=16 no hay espacio sufiente - exit $RETVAL - fi - TIMEAUX1=$[SECONDS-TIME] - ogEcho log session " $MSG_SCRIPTS_TASK_END, $MSG_SCRIPTS_TIME_PARTIAL : $[TIMEAUX1/60]m $[TIMEAUX1%60]s" - - # Montamos la imagen de cache - ogMountImage "$REPO" "$2" >/dev/null - ogWaitSyncImage "$REPO" "$2" "$IMGEXT" "mounted" || exit $(ogRaiseError session $OG_ERR_DONTMOUNT_IMAGE "$REPO $2 $IMGEXT: time_out."; echo $?) -fi - -TIME2=$SECONDS - -# Restaurar la imagen. -ogEcho log session "[40] $MSG_HELP_ogRestoreImage: $REPO $2 $3 $4" - -ogLock $3 $4 - -# Sincronizamos la partición con la imagen. -ogEcho log session "[60] $MSG_HELP_ogSyncRestore: $REPO $2 $IMGEXT $3 $4" -ogSyncRestore "$REPO" "$2" "$IMGEXT" $3 $4 -RETVAL=$? -[ $RETVAL == 0 ] || ogEcho session warning "$MSG_ERR_SYNCHRONIZING" - -TIMEAUX2=$[SECONDS-TIME2] -ogEcho log session " $MSG_SCRIPTS_TASK_END, $MSG_SCRIPTS_TIME_PARTIAL : $[TIMEAUX2/60]m $[TIMEAUX2%60]s" - -# Desmontamos imagen basica. -ogUnmountImage "$REPO" "$2" "$IMGEXT" - -# Restableciendo acl -ogUnlock $3 $4 -ogEcho log session "[70] $MSG_HELP_ogRestoreInfoImage" -ogExecAndLog command ogRestoreInfoImage $3 $4 - -# Llamar al script de post-configuración del sistema operativo. -# Si a este script lo ha llamado el script restoreDiffImage no post-configuramos -if [ "$(ogGetCaller)" != "restoreDiffImage" ];then - ogExecAndLog command ogRestoreAclImage $3 $4 - if which configureOsCustom &>/dev/null; then - ogEcho log session "[90] configureOsCustom" - configureOsCustom "$3" "$4" "$REPO" "$2" - else - ogEcho log session "[90] $MSG_HELP_configureOs" - configureOs $3 $4 - fi -fi -TIMEAUX3=$[SECONDS-TIMEAUX2] -ogEcho log session " $MSG_SCRIPTS_TASK_END, $MSG_SCRIPTS_TIME_PARTIAL : $[TIMEAUX3/60]m $[TIMEAUX3%60]s" - -TIME=$[SECONDS-TIME1] -ogEcho log session "[100] $MSG_SCRIPTS_END $MSG_SCRIPTS_TIME_TOTAL: $[TIME/60]m $[TIME%60]s" - -# Si ha habido problema al sincronizar nos salimos con error -[ $RETVAL == 0 ] || exit $OG_ERR_SYNCHRONIZING diff --git a/client/shared/scripts/restoreDiffImage b/client/shared/scripts/restoreDiffImage deleted file mode 100755 index 4de5f31..0000000 --- a/client/shared/scripts/restoreDiffImage +++ /dev/null @@ -1,159 +0,0 @@ -#!/bin/bash -#/** -# restoreDiffImage -#@brief Script de ejemplo para restaurar una imagen diferencial. -#@brief Se usa como base para el programa de restauración de imágenes de OpenGnsys Admin). -#@param 1 REPO|CACHE -#@param 2 imagen basica -#@param 3 imagen diferencial -#@param 4 disco -#@param 5 particion -#@param 6 protocolo -#@param 7 opciones protocolo -#@return -#@exception OG_ERR_FORMAT # 1 formato incorrecto. -#@exception OG_ERR_NOTFOUND # 2 Fichero o dispositivo no encontrado -#@exception OG_ERR_PARTITION# 3 Error en partición de disco. -#@exception OG_ERR_LOCKED # 4 Partición o fichero bloqueado. -#@exception updateCache # 15 No hay cache -#@exception updateCache # 16 no hay espacio sufiente -#@exception OG_ERR_DONTSYNC_IMAGE # 71 Imagen no sincronizable (es monolitica) -#@exception OG_ERR_DONTMOUNT_IMAGE # 70 Error al montar una imagen sincronizada. -#@todo: Se puede dar la opcion de que mantenga los ficheros nuevos. -#@todo: Se puede dar la opcion que la trasferencia se manden los datos comprimidos. -#@version 1.0 - restauración de imagen con sincronizacion. -#@author -#@date 2012-12-04 -#@version 1.1.1 - Varios repositorios para un mismo cliente (ticket #679). -#@author Irina Gomez - ETSII Universidad de Sevilla -#@date 2018/11/06 -#*/ ## - -trap "onexit $REPO \"$2\" \"$3\" $4 $5" 1 2 3 6 9 14 15 EXIT - -# Si salimos con error demontamos la imagen y desbloqueamos la imagen y la particion -function onexit() { - local exit_status=$? - ogUnmountImage "$REPO" "$3" "$DIFFTYPE" &>/dev/null - [ $exit_status -ne 4 ] && ogUnlock $4 $5 &>/dev/null - exit $exit_status -} - - -# Limpiamos los archivo de log -echo -n ""> $OGLOGSESSION -echo "" >$OGLOGCOMMAND - -TIME1=$SECONDS -PROG="$(basename $0)" -# Si se solicita, mostrar ayuda. -if [ "$*" == "help" ]; then - ogHelp "$PROG: $MSG_HELP_restoreDiffImage" \ - "$PROG REPO|CACHE base_image diff_image ndisco nparticion [ UNICAST|MULTICAST|TORRENT ] [opciones protocolo]" \ - "$PROG REPO Windows7 Windows7_pc123 1 1 " \ - "$PROG CACHE Ubuntu12 Ubuntu12_aula21 1 6 MULTICAST 9000:full-duplex:239.194.14.00:150M:50:60" - exit 0 -fi - -[ $# -lt 5 ] && exit $(ogRaiseError session $OG_ERR_FORMAT "$MSG_FORMAT: $PROG REPO|CACHE base_image diff_imagen ndisco nparticion [ UNICAST|MULTICAST|TORRENT ] [opciones protocolo]"; echo $?) - -ogEcho log session "[1] $MSG_SCRIPTS_START $0 $*" - -# Procesar parámetros de entrada -DIFFTYPE="diff" -DIFFEXT="img.diff" -REPO="${1^^}" -# No permite directorios diferentes para OU -OGUNIT="" - -# Si es una ip y es igual a la del equipo restaura desde cache -[ "$REPO" == "$(ogGetIpAddress)" ] && REPO="CACHE" -# Si es una ip y es distinta a la del recurso samba cambiamos de REPO. -ogCheckIpAddress $REPO -if [ $? == 0 -o $REPO == "REPO" ] ; then - # Si falla el cambio -> salimos con error repositorio no valido - ogChangeRepo $REPO ${OGUNIT%/} || exit $(ogRaiseError $OG_ERR_NOTFOUND '$REPO $OGUNIT'; echo $?) - REPO="REPO" -fi - -REPODIFFFILE="$(ogGetPath "REPO" "$3.$DIFFEXT")" -[ -n "$REPODIFFFILE" ] || exit $(ogRaiseError session $OG_ERR_NOTFOUND "REPO $3.$DIFFEXT";echo $?) - -# Comprobamos si la imagen o la particion estan bloqueada: -ogIsImageLocked "$REPO" "$3.$DIFFEXT" && exit $(ogRaiseError session $OG_ERR_LOCKED "$REPO $3.$DIFFEXT";echo $?) -ogIsLocked "$4" "$5" && exit $(ogRaiseError session $OG_ERR_LOCKED "$4 $5";echo $?) - -PART=$(ogDiskToDev "$4" "$5" 2>/dev/null ) || exit $(ogRaiseError session $OG_ERR_PARTITION "$REPO $3";echo $?) - -# Carga del configurador del engine -[ -z $OGENGINECONFIGURATE ] && source /opt/opengnsys/etc/engine.cfg - -# Comprobar que es sincronizable -ogIsSyncImage REPO "$3" diff || exit $(ogRaiseError session $OG_ERR_DONTSYNC_IMAGE "$REPO $3";echo $?) - -# Restauramos la imagen Basica. -restoreBaseImage $REPO "$2" "$4" "$5" "$6" "$7" - -PROTO=${6:-"UNICAST"} - -if [ "$REPO" == "CACHE" -o "$REPO" == "cache" ]; then - ogEcho log session "[10] $MSG_HELP_updateCache." - ogEcho log session " updateCache REPO" "/$3.$DIFFEXT" "$PROTO" "$7" - updateCache REPO "/$3.$DIFFEXT" "$PROTO" "$7" - RETVAL=$? - if [ "$RETVAL" != "0" ] - then - ogEcho log session "[39] $MSG_SCRIPTS_TASK_END updateCache REPO $3.$DIFFEXT $6 $7 con error $RETVAL" - # RC=15 No hay cache - # RC=16 no hay espacio sufiente - exit $RETVAL - fi - - TIMEAUX1=$[SECONDS-TIME] - ogEcho log session " $MSG_SCRIPTS_TASK_END, $MSG_SCRIPTS_TIME_PARTIAL : $[TIMEAUX1/60]m $[TIMEAUX1%60]s" -fi -TIME2=$SECONDS - -# Restaurar la imagen. -ogEcho log session "[40] $MSG_RESTORE $PART" - -# Montamos la diferencial -ogMountImage "$REPO" "$3" "$DIFFTYPE" &>/dev/null -# Comprobar que la imagen diferencial se ha montado -ogWaitSyncImage "$REPO" "$3" "$DIFFTYPE" "mounted" || exit $(ogRaiseError session $OG_ERR_DONTMOUNT_IMAGE "$REPO $3 $DIFFTYPE: time_out $TIMEAUX seg.";echo $?) - -ogMount $4 $5 >/dev/null -ogLock $4 $5 -# Sincronizamos la partición con la imagen. -ogEcho log session "[60] $MSG_HELP_ogSyncRestore: $REPO $3 $DIFFTYPE $4 $5" -ogSyncRestore "$REPO" "$3" "$DIFFTYPE" $4 $5 -RETVAL=$? -[ $RETVAL == 0 ] || ogEcho session warning "$MSG_ERR_SYNCHRONIZING" - -# Desmontamos la imagen -ogUnmountImage "$REPO" "$3" "$DIFFTYPE" -ogUnlock $4 $5 - -TIMEAUX2=$[SECONDS-TIME2] -ogEcho log session " $MSG_SCRIPTS_TASK_END, $MSG_SCRIPTS_TIME_PARTIAL : $[TIMEAUX2/60]m $[TIMEAUX2%60]s" - -# creando enlaces y restableciendo ACL (en windows) -ogEcho log session "[70] $MSG_HELP_ogRestoreInfoImage" -ogExecAndLog command ogRestoreInfoImage "$4" "$5" -ogExecAndLog command ogRestoreAclImage "$4" "$5" -TIMEAUX3=$[SECONDS-TIMEAUX2] -ogEcho log session " $MSG_SCRIPTS_TASK_END, $MSG_SCRIPTS_TIME_PARTIAL : $[TIMEAUX3/60]m $[TIMEAUX3%60]s" - -# Llamar al script de post-configuración del sistema operativo. -if which configureOsCustom &>/dev/null; then - ogEcho log session "[90] configureOsCustom" - configureOsCustom "$4" "$5" "$REPO" "$2" -else - ogEcho log session "[90] $MSG_HELP_configureOs." - configureOs $4 $5 -fi -TIME=$[SECONDS-TIME1] -ogEcho log session "[100] $MSG_SCRIPTS_END $MSG_SCRIPTS_TIME_TOTAL: $[TIME/60]m $[TIME%60]s" - -# Si ha habido problema al sincronizar nos salimos con error -[ $RETVAL == 0 ] || exit $OG_ERR_SYNCHRONIZING diff --git a/sources/interface/CrearImagenBasica b/sources/interface/CrearImagenBasica deleted file mode 100755 index 8aaf5aa..0000000 --- a/sources/interface/CrearImagenBasica +++ /dev/null @@ -1,98 +0,0 @@ -#!/bin/bash -#___________________________________________________________________ -# -# CREAR IMAGEN BÁSICA -#___________________________________________________________________ -# -# Parámetros recibidos desde el cliente: -# -# $1 Número de disco -# $2 Número de particion -# $3 Nombre canónico de la imagen básica (sin extensión) -# $4 Dirección del repositorio -# $5 Es una cadena "nnn" tipo flags que codifica varios parametros. -# Tiene el formato "nnn" donde "n" vale 0 ó 1. -# 1XX: Gestionar fichero completo en lugar de diferencas -# X1X: Eliminar archivos de la imagen que no estén en la partición -# XX1: Comprimir archivos para enviarlos por la red -# El valor X indica que no importa el valor que tenga el dato -# $6 Es una cadena "nnnn" tipo flags que codifica varios parametros. -# Tiene el formato "nnnn" donde "n" vale 0 ó 1. -# 1XXX: Borrar la imagen del repositorio antes de crearla -# X1XX: Copiar Imagen básica también a la cache -# XX1X: Borrar previamente la imagen de la cache antes de copiarla -# XXX1: No borrar archivos en destino -# El valor X indica que no importa el valor que tenga el dato -# $7 Método de syncronización 1=Sincronización1 2=Sincronizacion2 -# $8 Ruta de origen de la Imagen (Carpeta) - -#___________________________________________________________________ -# -# Control parámetros -#___________________________________________________________________ - - - PROG="$(basename $0)" - if [ $# -lt 7 ]; then - usage=" ndisco nparticion nombre_imagen_basica ip_repositorio" - usage="$usage copiar_a_caché Borrar_cache_previamente Ruta_origen" - ogRaiseError $OG_ERR_FORMAT "$MSG_FORMAT: $PROG $usage" - exit $? - fi - - #Load engine configurator from engine.cfg file. - #Carga el configurador del engine desde el fichero engine.cfg - [ -z $OGENGINECONFIGURATE ] && source /opt/opengnsys/etc/engine.cfg - - # Clear temporary file used as log track by httpdlog - # Limpia los ficheros temporales usados como log de seguimiento para httpdlog - echo -n "" > $OGLOGSESSION; echo -n "" > $OGLOGCOMMAND - - # Registro de inicio de ejecución - ogEcho log session "$MSG_INTERFACE_START $0 $*" - - # Si el destino es REPO y el cliente no está en modo "admin"; activar repositorio para escritura, - PATH=$PATH:$(dirname $0) - CLIENTIP=$(ogGetIpAddress) - [ "$4" == "$CLIENTIP" ] && DEST=CACHE || DEST=REPO - if [ "$DEST" == "REPO" -a "$boot" != "admin" ]; then - CambiarAcceso admin &>> $OGLOGFILE - RETVAL=$? - [ $RETVAL -gt 0 ] && exit $RETVAL - fi - - if [ $7 -eq 1 ]; then - #sincronizacion1 - ogCrearImagenBasica "$@" - RETVAL=$? - fi - - if [ $7 -eq 2 ]; then - # Sincronizacion2 - flag=$5 - WHOLE=${flag:0:1} - DELFILE=${flag:1:1} - COMPRESS=${flag:2:1} - - # Nota los valores se pasan como variables de entorno, - # la idea es que mas adelante vayan en el fichero PXE. - [ $WHOLE -eq 1 ] && export ogrsyncw=true || export ogrsyncw=false - [ $DELFILE -eq 1 ] && export ogrsyncdel=true || export ogrsyncdel=false - [ $COMPRESS -eq 1 ] && export ogrsyncz=true || export ogrsyncz=false - - # [ "$4" == "0.0.0.0" ] && DEST="CACHE" || DEST="REPO" - - createBaseImage $1 $2 $DEST "$3" - RETVAL=$? - - fi - - # Si estamos en modo user montamos el repositorio en modo lectura. - [ "$DEST" == "REPO" -a "$boot" != "admin" ] && CambiarAcceso user - # Registro de fin de ejecución - ogEcho log session "$MSG_INTERFACE_END $RETVAL" - - - - exit $RETVAL - diff --git a/sources/interface/CrearSoftIncremental b/sources/interface/CrearSoftIncremental deleted file mode 100755 index fc29451..0000000 --- a/sources/interface/CrearSoftIncremental +++ /dev/null @@ -1,98 +0,0 @@ -#!/bin/bash -#___________________________________________________________________ -# -# CREAR IMAGEN INCREMENTAL -#___________________________________________________________________ -# -# Parámetros recibidos desde el cliente: -# -# $1 Número de disco -# $2 Número de particion -# $3 Nombre canónico de la imagen básica (sin extensión) -# $4 Dirección del repositorio -# $5 Nombre canónico de la imagen incremental (sin extensión) -# $6 Es una cadena "nnn" tipo flags que codifica varios parametros. -# Tiene el formato "nnn" donde "n" vale 0 ó 1. -# 1XX: Gestionar fichero completo en lugar de diferencas -# X1X: Eliminar archivos de la imagen que no estén en la partición -# XX1: Comprimir archivos para enviarlos por la red -# El valor X indica que no importa el valor que tenga el dato -# $7 Es una cadena "nnnn" tipo flags que codifica varios parametros. -# Tiene el formato "nnnn" donde "n" vale 0 ó 1. -# 1XXX: Borrar la imagen incremental del repositorio antes de crearla -# X1XX: Copiar imagen incremental también a la cache -# XX1X: Borrar previamente la imagen incremental de la cache antes de copiarla -# XXX1: No borrar archivos en destino -# El valor X indica que no importa el valor que tenga el dato -# $8 Método de syncronización 1=Sincronización1 2=Sincronizacion2 -# $9 Ruta de origen de la Imagen (Carpeta) - -#___________________________________________________________________ -# -# Control parámetros -#___________________________________________________________________ - PROG="$(basename $0)" - if [ $# -lt 8 ]; then - usage=" ndisco nparticion nombre_imagen_basica ip_repositorio nombre_imagen_incremental" - usage="$usage copiar_a_caché Borrar_cache_previamente Ruta_origen" - ogRaiseError $OG_ERR_FORMAT "$MSG_FORMAT: $PROG $usage" - exit $? - fi - - #Load engine configurator from engine.cfg file. - #Carga el configurador del engine desde el fichero engine.cfg - [ -z $OGENGINECONFIGURATE ] && source /opt/opengnsys/etc/engine.cfg - - # Clear temporary file used as log track by httpdlog - # Limpia los ficheros temporales usados como log de seguimiento para httpdlog - echo -n "" > $OGLOGSESSION; echo -n "" > $OGLOGCOMMAND - - # Registro de inicio de ejecución - ogEcho log session "$MSG_INTERFACE_START $0 $*" - - - # Si el destino es REPO y el cliente no está en modo "admin"; activar repositorio para escritura, - PATH=$PATH:$(dirname $0) - CLIENTIP=$(ogGetIpAddress) - [ "$4" == "$CLIENTIP" ] && DEST=CACHE || DEST=REPO - if [ "$DEST" == "REPO" -a "$boot" != "admin" ]; then - CambiarAcceso admin &>> $OGLOGFILE - RETVAL=$? - [ $RETVAL -gt 0 ] && exit $RETVAL - fi - - if [ $8 -eq 1 ]; then - #sincronizacion1 - ogCrearSoftIncremental "$@" - RETVAL=$? - fi - - - if [ $8 -eq 2 ]; then - #sincronizacion 2 - flag=$6 - WHOLE=${flag:0:1} - DELFILE=${flag:1:1} - COMPRESS=${flag:2:1} - - # Nota los valores se pasan como variables de entorno, - # la idea es que mas adelante vayan en el fichero PXE. - [ $WHOLE -eq 1 ] && export ogrsyncw=true || export ogrsyncw=false - [ $DELFILE -eq 1 ] && export ogrsyncdel=true || export ogrsyncdel=false - [ $COMPRESS -eq 1 ] && export ogrsyncz=true || export ogrsyncz=false - - # REPOSITORIO: Si la ip es la del propio equipo CACHE, si no REPO - #CLIENTIP=$(ogGetIpAddress) - #[ "$4" == "$CLIENTIP" ] && DEST=CACHE || DEST=REPO - createDiffImage $1 $2 $DEST "$3" "$5" - RETVAL=$? - - fi - - # Si estamos en modo user montamos el repositorio en modo lectura. - [ "$DEST" == "REPO" -a "$boot" != "admin" ] && CambiarAcceso user - - # Registro de fin de ejecución - ogEcho log session "$MSG_INTERFACE_END $RETVAL" - - exit $RETVAL diff --git a/sources/interface/RestaurarImagenBasica b/sources/interface/RestaurarImagenBasica deleted file mode 100755 index c87b318..0000000 --- a/sources/interface/RestaurarImagenBasica +++ /dev/null @@ -1,89 +0,0 @@ -#!/bin/bash -#___________________________________________________________________ -# -# RESTAURAR IMAGEN BÁSICA -#___________________________________________________________________ -# -# Parámetros recibidos desde el cliente: -# -# $1 Número de disco -# $2 Número de particion -# $3 Nombre canónico de la imagen básica (sin extensión) -# $4 Dirección del repositorio -# $5 Tipo de transmisión 0=Unicast 1=Multicast -# $6 Es una cadena "nnn" tipo flags que codifica varios parametros. -# Tiene el formato "nnn" donde "n" vale 0 ó 1. -# 1XX: Gestionar fichero completo en lugar de diferencas -# X1X: Eliminar archivos de la partición que no estén en la imagen -# XX1: Comprimir archivos para enviarlos por la red -# El valor X indica que no importa el valor que tenga el dato -# $7 Es una cadena "nnnn" tipo flags que codifica varios parametros. -# Tiene el formato "nnnn" donde "n" vale 0 ó 1. -# 1XXX: Borrar la particion de destino antes de restaurar la imagen basica -# X1XX: Copiar Imagen básica también a la cache -# XX1X: Borrar previamente la imagen basica de la cache antes de copiarla -# XXX1: No borrar archivos en destino -# El valor X indica que no importa el valor que tenga el dato -# $8 Método de clonación 0=Desde caché 1=Desde repositorio -# $9 Metodo de sincronizacion 1=Sincronización1 2=Sincronizacion2 -# $10 Ruta de origen de la Imagen (Carpeta) - -#___________________________________________________________________ -# -# Control parámetros -#___________________________________________________________________ - - PROG="$(basename $0)" - if [ $# -lt 9 ]; then - usage=" ndisco nparticion nombre_imagen_basica ip_repositorio copiar_a_caché " - usage="$usage Borrar_cache_previamente metodo_clonación Ruta_origen" - ogRaiseError $OG_ERR_FORMAT "$MSG_FORMAT: $PROG $usage" - exit $? - fi - - #Load engine configurator from engine.cfg file. - #Carga el configurador del engine desde el fichero engine.cfg - [ -z $OGENGINECONFIGURATE ] && source /opt/opengnsys/etc/engine.cfg - - # Clear temporary file used as log track by httpdlog - # Limpia los ficheros temporales usados como log de seguimiento para httpdlog - echo -n "" > $OGLOGSESSION; echo " " > $OGLOGCOMMAND - - # Registro de inicio de ejecución - ogEcho log session "$MSG_INTERFACE_START $0 $*" - - if [ $9 -eq 1 ]; then - #sincronizacion1 - ogRestaurarImagenBasica "$@" - RETVAL=$? - fi - - if [ $9 -eq 2 ]; then - #sincronizacion2 - # Opciones de clonacion - flag=$6 - WHOLE=${flag:0:1} - DELFILE=${flag:1:1} - COMPRESS=${flag:2:1} - - # Nota los valores se pasan como variables de entorno, - # la idea es que mas adelante vayan en el fichero PXE. - [ $WHOLE -eq 1 ] && export ogrsyncw=true || export ogrsyncw=false - [ $DELFILE -eq 1 ] && export ogrsyncdel=true || export ogrsyncdel=false - [ $COMPRESS -eq 1 ] && export ogrsyncz=true || export ogrsyncz=false - - # Origen de la imagen. - [ $8 -eq 0 ] && DEST="CACHE" || DEST="REPO" - - # Protocolo de clonacion y opciones - PROTO=${5%_*} - OPT=${5#*_} - - restoreBaseImage $DEST "$3" $1 $2 $PROTO $OPT - RETVAL=$? - fi - - # Registro de fin de ejecución - ogEcho log session "$MSG_INTERFACE_END $RETVAL" - - exit $RETVAL diff --git a/sources/interface/RestaurarSoftIncremental b/sources/interface/RestaurarSoftIncremental deleted file mode 100755 index ee895d9..0000000 --- a/sources/interface/RestaurarSoftIncremental +++ /dev/null @@ -1,82 +0,0 @@ -#!/bin/bash -#___________________________________________________________________ -# -# RESTAURAR IMAGEN INCREMENTAL -#___________________________________________________________________ -# -# Parámetros recibidos desde el cliente: -# -# $1 Número de disco -# $2 Número de particion -# $3 Nombre canónico de la imagen básica (sin extensión) -# $4 Dirección del repositorio -# $5 Nombre canónico del software incremental (sin extensión) -# $6 Tipo de transmisión 0=Unicast 1=Multicast -# $7 Es una cadena "nnn" tipo flags que codifica varios parametros. -# Tiene el formato "nnn" donde "n" vale 0 ó 1. -# 1XX: Gestionar fichero completo en lugar de diferencas -# X1X: Eliminar archivos de la partición que no estén en la imagen -# XX1: Comprimir archivos para enviarlos por la red -# El valor X indica que no importa el valor que tenga el dato -# $8 Es una cadena "nnnn" tipo flags que codifica varios parametros. -# Tiene el formato "nnnn" donde "n" vale 0 ó 1. -# 1XXX: Borrar la particion de destino antes de restaurar la imagen basica -# X1XX: Copiar Imagen básica también a la cache -# XX1X: Borrar previamente la imagen basica de la cache antes de copiarla -# XXX1: No borrar archivos en destino -# El valor X indica que no importa el valor que tenga el dato -# $9 Método de clonación 0=Desde caché 1=Desde repositorio -# $10 Metodo de sincronizacion 1=Sincronización1 2=Sincronizacion2 -# $11 Ruta de origen de la Imagen (Carpeta) - -#___________________________________________________________________ -# -# Control parámetros -#___________________________________________________________________ - #Load engine configurator from engine.cfg file. - #Carga el configurador del engine desde el fichero engine.cfg - [ -z $OGENGINECONFIGURATE ] && source /opt/opengnsys/etc/engine.cfg - - # Clear temporary file used as log track by httpdlog - # Limpia los ficheros temporales usados como log de seguimiento para httpdlog - echo " " > $OGLOGSESSION; echo " " > $OGLOGCOMMAND - - # Registro de inicio de ejecución - ogEcho log session "$MSG_INTERFACE_START $0 $*" - - - if [ ${10} -eq 1 ]; then - #sincronizacion1 - ogRestaurarSoftIncremental "$@" - RETVAL=$? - fi - - if [ ${10} -eq 2 ]; then - #sincronizacion2 - # Opciones de clonacion - flag=$7 - WHOLE=${flag:0:1} - DELFILE=${flag:1:1} - COMPRESS=${flag:2:1} - - # Nota los valores se pasan como variables de entorno, - # la idea es que mas adelante vayan en el fichero PXE. - [ $WHOLE -eq 1 ] && export ogrsyncw=true || export ogrsyncw=false - [ $DELFILE -eq 1 ] && export ogrsyncdel=true || export ogrsyncdel=false - [ $COMPRESS -eq 1 ] && export ogrsyncz=true || export ogrsyncz=false - - # Origen de la imagen. - [ $9 -eq 0 ] && DEST="CACHE" || DEST="REPO" - - # Protocolo de clonacion y opciones - PROTO=${6%_*} - OPT=${6#*_} - - restoreDiffImage $DEST "$3" "$5" $1 $2 $PROTO $OPT - RETVAL=$? - fi - - # Registro de fin de ejecución - ogEcho log session "$MSG_INTERFACE_END $RETVAL" - - exit $RETVAL From 2e512ecc419dc84aed0678640f91185d2b0af7ce Mon Sep 17 00:00:00 2001 From: Natalia Serrano Date: Tue, 25 Feb 2025 10:21:58 +0100 Subject: [PATCH 02/18] refs #1592 move interfaceAdm files --- {sources/interface => client/interfaceAdm}/Apagar | 0 {sources/interface => client/interfaceAdm}/CambiarAcceso | 0 {sources/interface => client/interfaceAdm}/Configurar | 0 {sources/interface => client/interfaceAdm}/ConsolaRemota | 0 {sources/interface => client/interfaceAdm}/CrearImagen | 0 {sources/interface => client/interfaceAdm}/EjecutarScript | 0 {sources/interface => client/interfaceAdm}/IniciarSesion | 0 {sources/interface => client/interfaceAdm}/InventarioHardware | 0 {sources/interface => client/interfaceAdm}/InventarioSoftware | 0 {sources/interface => client/interfaceAdm}/Reiniciar | 0 {sources/interface => client/interfaceAdm}/RestaurarImagen | 0 {sources/interface => client/interfaceAdm}/getConfiguration | 0 {sources/interface => client/interfaceAdm}/getIpAddress | 0 {sources/interface => client/interfaceAdm}/procesaCache | 0 installer/ogboot_installer.py | 2 +- 15 files changed, 1 insertion(+), 1 deletion(-) rename {sources/interface => client/interfaceAdm}/Apagar (100%) rename {sources/interface => client/interfaceAdm}/CambiarAcceso (100%) rename {sources/interface => client/interfaceAdm}/Configurar (100%) rename {sources/interface => client/interfaceAdm}/ConsolaRemota (100%) rename {sources/interface => client/interfaceAdm}/CrearImagen (100%) rename {sources/interface => client/interfaceAdm}/EjecutarScript (100%) rename {sources/interface => client/interfaceAdm}/IniciarSesion (100%) rename {sources/interface => client/interfaceAdm}/InventarioHardware (100%) rename {sources/interface => client/interfaceAdm}/InventarioSoftware (100%) rename {sources/interface => client/interfaceAdm}/Reiniciar (100%) rename {sources/interface => client/interfaceAdm}/RestaurarImagen (100%) rename {sources/interface => client/interfaceAdm}/getConfiguration (100%) rename {sources/interface => client/interfaceAdm}/getIpAddress (100%) rename {sources/interface => client/interfaceAdm}/procesaCache (100%) diff --git a/sources/interface/Apagar b/client/interfaceAdm/Apagar similarity index 100% rename from sources/interface/Apagar rename to client/interfaceAdm/Apagar diff --git a/sources/interface/CambiarAcceso b/client/interfaceAdm/CambiarAcceso similarity index 100% rename from sources/interface/CambiarAcceso rename to client/interfaceAdm/CambiarAcceso diff --git a/sources/interface/Configurar b/client/interfaceAdm/Configurar similarity index 100% rename from sources/interface/Configurar rename to client/interfaceAdm/Configurar diff --git a/sources/interface/ConsolaRemota b/client/interfaceAdm/ConsolaRemota similarity index 100% rename from sources/interface/ConsolaRemota rename to client/interfaceAdm/ConsolaRemota diff --git a/sources/interface/CrearImagen b/client/interfaceAdm/CrearImagen similarity index 100% rename from sources/interface/CrearImagen rename to client/interfaceAdm/CrearImagen diff --git a/sources/interface/EjecutarScript b/client/interfaceAdm/EjecutarScript similarity index 100% rename from sources/interface/EjecutarScript rename to client/interfaceAdm/EjecutarScript diff --git a/sources/interface/IniciarSesion b/client/interfaceAdm/IniciarSesion similarity index 100% rename from sources/interface/IniciarSesion rename to client/interfaceAdm/IniciarSesion diff --git a/sources/interface/InventarioHardware b/client/interfaceAdm/InventarioHardware similarity index 100% rename from sources/interface/InventarioHardware rename to client/interfaceAdm/InventarioHardware diff --git a/sources/interface/InventarioSoftware b/client/interfaceAdm/InventarioSoftware similarity index 100% rename from sources/interface/InventarioSoftware rename to client/interfaceAdm/InventarioSoftware diff --git a/sources/interface/Reiniciar b/client/interfaceAdm/Reiniciar similarity index 100% rename from sources/interface/Reiniciar rename to client/interfaceAdm/Reiniciar diff --git a/sources/interface/RestaurarImagen b/client/interfaceAdm/RestaurarImagen similarity index 100% rename from sources/interface/RestaurarImagen rename to client/interfaceAdm/RestaurarImagen diff --git a/sources/interface/getConfiguration b/client/interfaceAdm/getConfiguration similarity index 100% rename from sources/interface/getConfiguration rename to client/interfaceAdm/getConfiguration diff --git a/sources/interface/getIpAddress b/client/interfaceAdm/getIpAddress similarity index 100% rename from sources/interface/getIpAddress rename to client/interfaceAdm/getIpAddress diff --git a/sources/interface/procesaCache b/client/interfaceAdm/procesaCache similarity index 100% rename from sources/interface/procesaCache rename to client/interfaceAdm/procesaCache diff --git a/installer/ogboot_installer.py b/installer/ogboot_installer.py index 8688d9e..9e78e7c 100755 --- a/installer/ogboot_installer.py +++ b/installer/ogboot_installer.py @@ -463,7 +463,7 @@ TFTP_OPTIONS="--secure -v" def copyInterfaceAdm(): hayErrores = 0 - cp_process = subprocess.run(["cp", "-ar", f"{REPO_DIR}/sources/interface", f"{INSTALL_OGBOOT_TARGET}/client/interfaceAdm"]) + cp_process = subprocess.run(["cp", "-ar", f"{REPO_DIR}/client/interfaceAdm", f"{INSTALL_OGBOOT_TARGET}/client/interfaceAdm"]) if cp_process.returncode != 0: logger.error(f"Error while copying Administration Interface Folder") hayErrores = 1 From 29f497aefd24251736a00c96734df4dd6899601d Mon Sep 17 00:00:00 2001 From: Natalia Serrano Date: Tue, 25 Feb 2025 10:24:10 +0100 Subject: [PATCH 03/18] refs #1592 move lib/engine/bin files --- client/{engine => lib/engine/bin}/Boot.lib | 0 client/{engine => lib/engine/bin}/Cache.lib | 0 client/{engine => lib/engine/bin}/Disk.lib | 0 client/{engine => lib/engine/bin}/File.lib | 0 client/{engine => lib/engine/bin}/FileSystem.lib | 0 client/{engine => lib/engine/bin}/Image.lib | 0 client/{engine => lib/engine/bin}/Inventory.lib | 0 client/{engine => lib/engine/bin}/Net.lib | 0 client/{engine => lib/engine/bin}/PostConf.lib | 0 client/{engine => lib/engine/bin}/PostConfEAC.lib | 0 client/{engine => lib/engine/bin}/Protocol.lib | 0 client/{engine => lib/engine/bin}/README.es.txt | 0 client/{engine => lib/engine/bin}/Registry.lib | 0 client/{engine => lib/engine/bin}/String.lib | 0 client/{engine => lib/engine/bin}/System.lib | 0 client/{engine => lib/engine/bin}/ToolsGNU.c | 0 client/{engine => lib/engine/bin}/UEFI.lib | 0 installer/ogboot_installer.py | 8 +------- 18 files changed, 1 insertion(+), 7 deletions(-) rename client/{engine => lib/engine/bin}/Boot.lib (100%) rename client/{engine => lib/engine/bin}/Cache.lib (100%) rename client/{engine => lib/engine/bin}/Disk.lib (100%) rename client/{engine => lib/engine/bin}/File.lib (100%) rename client/{engine => lib/engine/bin}/FileSystem.lib (100%) rename client/{engine => lib/engine/bin}/Image.lib (100%) rename client/{engine => lib/engine/bin}/Inventory.lib (100%) rename client/{engine => lib/engine/bin}/Net.lib (100%) rename client/{engine => lib/engine/bin}/PostConf.lib (100%) rename client/{engine => lib/engine/bin}/PostConfEAC.lib (100%) rename client/{engine => lib/engine/bin}/Protocol.lib (100%) rename client/{engine => lib/engine/bin}/README.es.txt (100%) rename client/{engine => lib/engine/bin}/Registry.lib (100%) rename client/{engine => lib/engine/bin}/String.lib (100%) rename client/{engine => lib/engine/bin}/System.lib (100%) rename client/{engine => lib/engine/bin}/ToolsGNU.c (100%) rename client/{engine => lib/engine/bin}/UEFI.lib (100%) diff --git a/client/engine/Boot.lib b/client/lib/engine/bin/Boot.lib similarity index 100% rename from client/engine/Boot.lib rename to client/lib/engine/bin/Boot.lib diff --git a/client/engine/Cache.lib b/client/lib/engine/bin/Cache.lib similarity index 100% rename from client/engine/Cache.lib rename to client/lib/engine/bin/Cache.lib diff --git a/client/engine/Disk.lib b/client/lib/engine/bin/Disk.lib similarity index 100% rename from client/engine/Disk.lib rename to client/lib/engine/bin/Disk.lib diff --git a/client/engine/File.lib b/client/lib/engine/bin/File.lib similarity index 100% rename from client/engine/File.lib rename to client/lib/engine/bin/File.lib diff --git a/client/engine/FileSystem.lib b/client/lib/engine/bin/FileSystem.lib similarity index 100% rename from client/engine/FileSystem.lib rename to client/lib/engine/bin/FileSystem.lib diff --git a/client/engine/Image.lib b/client/lib/engine/bin/Image.lib similarity index 100% rename from client/engine/Image.lib rename to client/lib/engine/bin/Image.lib diff --git a/client/engine/Inventory.lib b/client/lib/engine/bin/Inventory.lib similarity index 100% rename from client/engine/Inventory.lib rename to client/lib/engine/bin/Inventory.lib diff --git a/client/engine/Net.lib b/client/lib/engine/bin/Net.lib similarity index 100% rename from client/engine/Net.lib rename to client/lib/engine/bin/Net.lib diff --git a/client/engine/PostConf.lib b/client/lib/engine/bin/PostConf.lib similarity index 100% rename from client/engine/PostConf.lib rename to client/lib/engine/bin/PostConf.lib diff --git a/client/engine/PostConfEAC.lib b/client/lib/engine/bin/PostConfEAC.lib similarity index 100% rename from client/engine/PostConfEAC.lib rename to client/lib/engine/bin/PostConfEAC.lib diff --git a/client/engine/Protocol.lib b/client/lib/engine/bin/Protocol.lib similarity index 100% rename from client/engine/Protocol.lib rename to client/lib/engine/bin/Protocol.lib diff --git a/client/engine/README.es.txt b/client/lib/engine/bin/README.es.txt similarity index 100% rename from client/engine/README.es.txt rename to client/lib/engine/bin/README.es.txt diff --git a/client/engine/Registry.lib b/client/lib/engine/bin/Registry.lib similarity index 100% rename from client/engine/Registry.lib rename to client/lib/engine/bin/Registry.lib diff --git a/client/engine/String.lib b/client/lib/engine/bin/String.lib similarity index 100% rename from client/engine/String.lib rename to client/lib/engine/bin/String.lib diff --git a/client/engine/System.lib b/client/lib/engine/bin/System.lib similarity index 100% rename from client/engine/System.lib rename to client/lib/engine/bin/System.lib diff --git a/client/engine/ToolsGNU.c b/client/lib/engine/bin/ToolsGNU.c similarity index 100% rename from client/engine/ToolsGNU.c rename to client/lib/engine/bin/ToolsGNU.c diff --git a/client/engine/UEFI.lib b/client/lib/engine/bin/UEFI.lib similarity index 100% rename from client/engine/UEFI.lib rename to client/lib/engine/bin/UEFI.lib diff --git a/installer/ogboot_installer.py b/installer/ogboot_installer.py index 9e78e7c..68fdca5 100755 --- a/installer/ogboot_installer.py +++ b/installer/ogboot_installer.py @@ -478,13 +478,7 @@ def copyClientFiles(): logger.info(f"Copying OpenGnsys Cloning Engine files in client library.") os.makedirs(f"{INSTALL_OGBOOT_TARGET}/client/lib/engine/bin", mode=0o775, exist_ok=True) - if 0 != subprocess.run (['rsync', '-aH', f'{REPO_DIR}/client/engine/', f'{INSTALL_OGBOOT_TARGET}/client/lib/engine/bin/']).returncode: - logger.error(f"Error while copying engine files") - errstatus = 1 - - logger.info(f"Copying OpenGnsys Cloning Engine files in client engine.") - os.makedirs(f"{INSTALL_OGBOOT_TARGET}/client/engine", mode=0o775, exist_ok=True) - if 0 != subprocess.run (['rsync', '-aH', f'{REPO_DIR}/client/engine/', f'{INSTALL_OGBOOT_TARGET}/client/engine/']).returncode: + if 0 != subprocess.run (['rsync', '-aH', f'{REPO_DIR}/client/lib/engine/bin/', f'{INSTALL_OGBOOT_TARGET}/client/lib/engine/bin/']).returncode: logger.error(f"Error while copying engine files") errstatus = 1 From 19bc89f36c86bb9170766328fbd02879c3ebcd30 Mon Sep 17 00:00:00 2001 From: Natalia Serrano Date: Tue, 25 Feb 2025 11:12:43 +0100 Subject: [PATCH 04/18] refs #1592 move client/shared files --- .gitignore | 1 + client/README.es.txt | 29 ++++++++++++------ client/{shared => }/bin/EACInterfaces | Bin client/{shared => }/bin/browser | Bin client/{shared => }/bin/grub-probe1.99_i686 | Bin client/{shared => }/bin/grub-probe1.99_x86_64 | Bin client/{shared => }/bin/poweroffconf | 0 client/{shared => }/bin/rsync-31 | Bin client/{shared => }/bin/runtest | 0 client/{shared => }/etc/engine.cfg | 0 client/{shared => }/etc/engine.json | 0 client/{shared => }/etc/es.qmap | Bin client/{shared => }/etc/init/default.sh | 0 client/{shared => }/etc/lang.ca_ES.UTF-8.conf | 0 client/{shared => }/etc/lang.ca_ES.conf | 0 client/{shared => }/etc/lang.en_GB.UTF-8.conf | 0 client/{shared => }/etc/lang.en_GB.conf | 0 client/{shared => }/etc/lang.es_ES.UTF-8.conf | 0 client/{shared => }/etc/lang.es_ES.conf | 0 client/{shared => }/etc/preinit/default.sh | 0 client/{shared => }/etc/preinit/fileslinks.sh | 0 .../{shared => }/etc/preinit/loadenviron.sh | 0 .../{shared => }/etc/preinit/loadmodules.sh | 0 client/{shared => }/etc/preinit/metadevs.sh | 0 client/{shared => }/etc/preinit/mountrepo.sh | 0 .../{shared => }/etc/preinit/otherservices.sh | 0 client/{shared => }/etc/preinit/poweroff.sh | 0 .../themes/OpenGnsys/background-original.png | Bin .../lib/burg/themes/OpenGnsys/background.png | Bin .../lib/burg/themes/OpenGnsys/extended | 0 .../themes/OpenGnsys/icons/hover_debian.png | Bin .../OpenGnsys/icons/hover_elementary.png | Bin .../themes/OpenGnsys/icons/hover_freebsd.png | Bin .../themes/OpenGnsys/icons/hover_haiku.png | Bin .../themes/OpenGnsys/icons/hover_linux.png | Bin .../OpenGnsys/icons/hover_opengnsys.png | Bin .../themes/OpenGnsys/icons/hover_opensuse.png | Bin .../burg/themes/OpenGnsys/icons/hover_os.png | Bin .../burg/themes/OpenGnsys/icons/hover_osx.png | Bin .../themes/OpenGnsys/icons/hover_recovery.png | Bin .../themes/OpenGnsys/icons/hover_restart.png | Bin .../themes/OpenGnsys/icons/hover_shutdown.png | Bin .../themes/OpenGnsys/icons/hover_ubuntu.png | Bin .../themes/OpenGnsys/icons/hover_windows.png | Bin .../OpenGnsys/icons/hover_windows10.png | Bin .../themes/OpenGnsys/icons/hover_windows7.png | Bin .../OpenGnsys/icons/hover_windows_metro.png | Bin .../lib/burg/themes/OpenGnsys/icons/icons | 0 .../themes/OpenGnsys/icons/normal_debian.png | Bin .../OpenGnsys/icons/normal_elementary.png | Bin .../themes/OpenGnsys/icons/normal_freebsd.png | Bin .../themes/OpenGnsys/icons/normal_haiku.png | Bin .../themes/OpenGnsys/icons/normal_linux.png | Bin .../OpenGnsys/icons/normal_opengnsys.png | Bin .../OpenGnsys/icons/normal_opensuse.png | Bin .../burg/themes/OpenGnsys/icons/normal_os.png | Bin .../themes/OpenGnsys/icons/normal_osx.png | Bin .../OpenGnsys/icons/normal_recovery.png | Bin .../themes/OpenGnsys/icons/normal_restart.png | Bin .../OpenGnsys/icons/normal_shutdown.png | Bin .../themes/OpenGnsys/icons/normal_ubuntu.png | Bin .../themes/OpenGnsys/icons/normal_windows.png | Bin .../OpenGnsys/icons/normal_windows10.png | Bin .../OpenGnsys/icons/normal_windows7.png | Bin .../OpenGnsys/icons/normal_windows_metro.png | Bin .../themes/OpenGnsys/images/000-70opaque.png | Bin .../themes/OpenGnsys/images/button-bg.png | Bin .../OpenGnsys/images/button-hover-bg.png | Bin .../OpenGnsys/images/button-hover-l.png | Bin .../OpenGnsys/images/button-hover-r.png | Bin .../burg/themes/OpenGnsys/images/button-l.png | Bin .../burg/themes/OpenGnsys/images/button-r.png | Bin .../OpenGnsys/images/button-tools-hover.png | Bin .../themes/OpenGnsys/images/button-tools.png | Bin .../themes/OpenGnsys/images/container-b.png | Bin .../themes/OpenGnsys/images/container-bg.png | Bin .../themes/OpenGnsys/images/container-bl.png | Bin .../themes/OpenGnsys/images/container-br.png | Bin .../themes/OpenGnsys/images/container-l.png | Bin .../themes/OpenGnsys/images/container-r.png | Bin .../themes/OpenGnsys/images/container-t.png | Bin .../OpenGnsys/images/container-title-bg.png | Bin .../OpenGnsys/images/container-title-l.png | Bin .../OpenGnsys/images/container-title-r.png | Bin .../OpenGnsys/images/container-title-t.png | Bin .../OpenGnsys/images/container-title-tl.png | Bin .../OpenGnsys/images/container-title-tr.png | Bin .../themes/OpenGnsys/images/container-tl.png | Bin .../themes/OpenGnsys/images/container-tr.png | Bin .../burg/themes/OpenGnsys/images/dialog-b.png | Bin .../themes/OpenGnsys/images/dialog-bg.png | Bin .../themes/OpenGnsys/images/dialog-bl.png | Bin .../themes/OpenGnsys/images/dialog-bl.xcf | Bin .../themes/OpenGnsys/images/dialog-br.png | Bin .../themes/OpenGnsys/images/dialog-lr.png | Bin .../themes/OpenGnsys/images/dialog-spacer.png | Bin .../burg/themes/OpenGnsys/images/dialog-t.png | Bin .../themes/OpenGnsys/images/dialog-tl.png | Bin .../themes/OpenGnsys/images/dialog-tr.png | Bin .../OpenGnsys/images/progressbar-bg-b.png | Bin .../OpenGnsys/images/progressbar-bg-bl.png | Bin .../OpenGnsys/images/progressbar-bg-br.png | Bin .../OpenGnsys/images/progressbar-bg-l.png | Bin .../OpenGnsys/images/progressbar-bg-r.png | Bin .../OpenGnsys/images/progressbar-bg-t.png | Bin .../OpenGnsys/images/progressbar-bg-tl.png | Bin .../OpenGnsys/images/progressbar-bg-tr.png | Bin .../OpenGnsys/images/progressbar-bg.png | Bin .../themes/OpenGnsys/images/text-line-l.png | Bin .../themes/OpenGnsys/images/text-line-r.png | Bin .../lib/burg/themes/OpenGnsys/images/tick.png | Bin .../themes/OpenGnsys/images/txt-about.png | Bin .../burg/themes/OpenGnsys/images/txt-help.png | Bin .../themes/OpenGnsys/images/txt-select.png | Bin .../themes/OpenGnsys/images/txt-tools.png | Bin .../OpenGnsys/images/ubuntu-glow-96.png | Bin .../lib/burg/themes/OpenGnsys/menus | 0 .../lib/burg/themes/OpenGnsys/style | 0 .../lib/burg/themes/OpenGnsys/theme | 0 .../lib/engine/tests/Modify/Cache.shtest | 0 .../lib/engine/tests/NoModify/File1.shtest | 0 .../lib/engine/tests/NoModify/Lock1.shtest | 0 .../lib/engine/tests/NoModify/Net1.shtest | 0 client/{shared => }/lib/engine/tests/README | 0 .../lib/engine/tests/crearTestDisk1 | 0 .../lib/engine/tests/crearTestLock2 | 0 .../lib/fonts/DejaVuSans-Bold.ttf | Bin .../lib/fonts/DejaVuSans-BoldOblique.ttf | Bin .../lib/fonts/DejaVuSans-Oblique.ttf | Bin client/{shared => }/lib/fonts/DejaVuSans.ttf | Bin .../lib/fonts/DejaVuSansMono-Bold.ttf | Bin .../lib/fonts/DejaVuSansMono-BoldOblique.ttf | Bin .../lib/fonts/DejaVuSansMono-Oblique.ttf | Bin .../{shared => }/lib/fonts/DejaVuSansMono.ttf | Bin .../lib/fonts/DejaVuSerif-Bold.ttf | Bin .../lib/fonts/DejaVuSerif-BoldOblique.ttf | Bin .../lib/fonts/DejaVuSerif-Oblique.ttf | Bin client/{shared => }/lib/fonts/DejaVuSerif.ttf | Bin client/{shared => }/lib/fonts/README | 0 client/{shared => }/lib/fonts/UTBI____.pfa | 0 client/{shared => }/lib/fonts/UTB_____.pfa | 0 client/{shared => }/lib/fonts/UTI_____.pfa | 0 client/{shared => }/lib/fonts/UTRG____.pfa | 0 client/{shared => }/lib/fonts/Vera.ttf | Bin client/{shared => }/lib/fonts/VeraBI.ttf | Bin client/{shared => }/lib/fonts/VeraBd.ttf | Bin client/{shared => }/lib/fonts/VeraIt.ttf | Bin client/{shared => }/lib/fonts/VeraMoBI.ttf | Bin client/{shared => }/lib/fonts/VeraMoBd.ttf | Bin client/{shared => }/lib/fonts/VeraMoIt.ttf | Bin client/{shared => }/lib/fonts/VeraMono.ttf | Bin client/{shared => }/lib/fonts/VeraSe.ttf | Bin client/{shared => }/lib/fonts/VeraSeBd.ttf | Bin client/{shared => }/lib/fonts/c0419bt_.pfb | Bin client/{shared => }/lib/fonts/c0582bt_.pfb | Bin client/{shared => }/lib/fonts/c0583bt_.pfb | Bin client/{shared => }/lib/fonts/c0611bt_.pfb | Bin client/{shared => }/lib/fonts/c0632bt_.pfb | Bin client/{shared => }/lib/fonts/c0633bt_.pfb | Bin client/{shared => }/lib/fonts/c0648bt_.pfb | Bin client/{shared => }/lib/fonts/c0649bt_.pfb | Bin client/{shared => }/lib/fonts/cour.pfa | 0 client/{shared => }/lib/fonts/courb.pfa | 0 client/{shared => }/lib/fonts/courbi.pfa | 0 client/{shared => }/lib/fonts/couri.pfa | 0 client/{shared => }/lib/fonts/cursor.pfa | 0 .../{shared => }/lib/fonts/fixed_120_50.qpf | Bin client/{shared => }/lib/fonts/fixed_70_50.qpf | Bin .../lib/fonts/helvetica_100_50.qpf | Bin .../lib/fonts/helvetica_100_50i.qpf | Bin .../lib/fonts/helvetica_100_75.qpf | Bin .../lib/fonts/helvetica_100_75i.qpf | Bin .../lib/fonts/helvetica_120_50.qpf | Bin .../lib/fonts/helvetica_120_50i.qpf | Bin .../lib/fonts/helvetica_120_75.qpf | Bin .../lib/fonts/helvetica_120_75i.qpf | Bin .../lib/fonts/helvetica_140_50.qpf | Bin .../lib/fonts/helvetica_140_50i.qpf | Bin .../lib/fonts/helvetica_140_75.qpf | Bin .../lib/fonts/helvetica_140_75i.qpf | Bin .../lib/fonts/helvetica_180_50.qpf | Bin .../lib/fonts/helvetica_180_50i.qpf | Bin .../lib/fonts/helvetica_180_75.qpf | Bin .../lib/fonts/helvetica_180_75i.qpf | Bin .../lib/fonts/helvetica_240_50.qpf | Bin .../lib/fonts/helvetica_240_50i.qpf | Bin .../lib/fonts/helvetica_240_75.qpf | Bin .../lib/fonts/helvetica_240_75i.qpf | Bin .../lib/fonts/helvetica_80_50.qpf | Bin .../lib/fonts/helvetica_80_50i.qpf | Bin .../lib/fonts/helvetica_80_75.qpf | Bin .../lib/fonts/helvetica_80_75i.qpf | Bin .../lib/fonts/japanese_230_50.qpf | Bin client/{shared => }/lib/fonts/l047013t.pfa | 0 client/{shared => }/lib/fonts/l047016t.pfa | 0 client/{shared => }/lib/fonts/l047033t.pfa | 0 client/{shared => }/lib/fonts/l047036t.pfa | 0 client/{shared => }/lib/fonts/l048013t.pfa | 0 client/{shared => }/lib/fonts/l048016t.pfa | 0 client/{shared => }/lib/fonts/l048033t.pfa | 0 client/{shared => }/lib/fonts/l048036t.pfa | 0 client/{shared => }/lib/fonts/l049013t.pfa | 0 client/{shared => }/lib/fonts/l049016t.pfa | 0 client/{shared => }/lib/fonts/l049033t.pfa | 0 client/{shared => }/lib/fonts/l049036t.pfa | 0 client/{shared => }/lib/fonts/micro_40_50.qpf | Bin .../{shared => }/lib/fonts/unifont_160_50.qpf | Bin client/{shared => }/lib/grub4dos/COPYING | 0 .../lib/grub4dos/ChangeLog_GRUB4DOS.txt | 0 .../lib/grub4dos/Get_Source_of_This_Build.txt | 0 .../lib/grub4dos/README_GRUB4DOS.txt | 0 client/{shared => }/lib/grub4dos/badgrub.exe | Bin client/{shared => }/lib/grub4dos/bootlace.com | Bin client/{shared => }/lib/grub4dos/config.sys | 0 client/{shared => }/lib/grub4dos/default | 0 .../lib/grub4dos/example.menu.lst | 0 client/{shared => }/lib/grub4dos/grldr | Bin client/{shared => }/lib/grub4dos/grldr.mbr | Bin client/{shared => }/lib/grub4dos/grub.exe | Bin client/{shared => }/lib/grub4dos/grub.pif | Bin .../lib/grub4dos/grub4dos-0.4.5b/COPYING | 0 .../grub4dos-0.4.5b/ChangeLog_GRUB4DOS.txt | 0 .../grub4dos-0.4.5b/ChangeLog_chenall.txt | 0 .../grub4dos-0.4.5b/README_GRUB4DOS.txt | 0 .../grub4dos-0.4.5b/README_GRUB4DOS_CN.txt | 0 .../lib/grub4dos/grub4dos-0.4.5b/badgrub.exe | Bin .../lib/grub4dos/grub4dos-0.4.5b/bootlace.com | Bin .../lib/grub4dos/grub4dos-0.4.5b/config.sys | 0 .../lib/grub4dos/grub4dos-0.4.5b/default | 0 .../lib/grub4dos/grub4dos-0.4.5b/grldr | Bin .../lib/grub4dos/grub4dos-0.4.5b/grldr.mbr | Bin .../lib/grub4dos/grub4dos-0.4.5b/grub.exe | Bin .../lib/grub4dos/grub4dos-0.4.5b/grub.pif | Bin .../lib/grub4dos/grub4dos-0.4.5b/hmload.com | Bin .../lib/grub4dos/grub4dos-0.4.5b/menu.lst | 0 .../lib/grub4dos/grub4dos-0.4.6a/COPYING | 0 .../Get_Source_of_This_Build.txt | 0 .../lib/grub4dos/grub4dos-0.4.6a/badgrub.exe | Bin .../lib/grub4dos/grub4dos-0.4.6a/bootlace.com | Bin .../grub4dos/grub4dos-0.4.6a/bootlace64.com | Bin .../lib/grub4dos/grub4dos-0.4.6a/eltorito.sys | Bin .../lib/grub4dos/grub4dos-0.4.6a/grldr | Bin .../lib/grub4dos/grub4dos-0.4.6a/grldr.mbr | Bin .../lib/grub4dos/grub4dos-0.4.6a/grldr.pbr | Bin .../lib/grub4dos/grub4dos-0.4.6a/grldr_cd.bin | Bin .../lib/grub4dos/grub4dos-0.4.6a/grub.exe | Bin .../lib/grub4dos/grub4dos-0.4.6a/grub.pif | Bin .../lib/grub4dos/grub4dos-0.4.6a/hmload.com | Bin .../lib/grub4dos/grub4dos-0.4.6a/ipxegrldr | Bin .../grub4dos-0.4.6a/sample/config.sys | 0 .../grub4dos/grub4dos-0.4.6a/sample/default | 0 .../grub4dos/grub4dos-0.4.6a/sample/menu.lst | 0 client/{shared => }/lib/grub4dos/hmload.com | Bin client/{shared => }/lib/httpd/10-cgi.conf | 0 client/{shared => }/lib/httpd/LogCommand.sh | 0 client/{shared => }/lib/httpd/LogSession.sh | 0 client/{shared => }/lib/httpd/bandwidth.sh | 0 client/{shared => }/lib/httpd/cache.sh | 0 client/{shared => }/lib/httpd/httpd-log.sh | 0 client/{shared => }/lib/httpd/httpd-menu.sh | 0 .../{shared => }/lib/httpd/httpd-runengine.sh | 0 client/{shared => }/lib/httpd/lighttpd.conf | 0 client/{shared => }/lib/httpd/oglive.css | 0 .../lib/locale/ca/LC_MESSAGES/browser.mo | Bin .../lib/locale/en/LC_MESSAGES/browser.mo | Bin client/{shared => }/lib/modules/psmouse.ko | Bin client/{shared => }/lib/os-probes/10zvol-test | 0 .../lib/os-probes/50mounted-tests | 0 .../lib/os-probes/init/10filesystems | 0 .../{shared => }/lib/os-probes/mounted/05efi | 0 .../lib/os-probes/mounted/10freedos | 0 .../{shared => }/lib/os-probes/mounted/10qnx | 0 .../lib/os-probes/mounted/20macosx | 0 .../lib/os-probes/mounted/20microsoft | 0 .../lib/os-probes/mounted/30utility | 0 .../{shared => }/lib/os-probes/mounted/40lsb | 0 .../{shared => }/lib/os-probes/mounted/70hurd | 0 .../lib/os-probes/mounted/80minix | 0 .../lib/os-probes/mounted/83haiku | 0 .../lib/os-probes/mounted/90linux-distro | 0 .../lib/os-probes/mounted/90solaris | 0 .../lib/os-probes/mounted/efi/10elilo | 0 .../lib/os-probes/mounted/efi/20microsoft | 0 .../lib/os-probes/mounted/efi/31part-x-y | 0 client/{shared => }/lib/pci.ids | 0 client/{shared => }/lib/pictures/oglogo.png | Bin client/{shared => }/lib/qtlib/libQtCore.so.4 | Bin client/{shared => }/lib/qtlib/libQtGui.so.4 | Bin .../{shared => }/lib/qtlib/libQtNetwork.so.4 | Bin .../{shared => }/lib/qtlib/libQtWebKit.so.4 | Bin .../{shared => }/lib/qtlib/libcrypto.so.1.0.0 | Bin client/{shared => }/lib/qtlib/libssl.so.1.0.0 | Bin .../lib/qtplugins/imageformats/libqjpeg.so | Bin client/{shared => }/scripts/README.es.txt | 0 client/{shared => }/scripts/bootLinux | 0 client/{shared => }/scripts/bootOs | 0 .../scripts/bootOsCustom.template | 0 client/{shared => }/scripts/bootWindows | 0 client/{shared => }/scripts/buildToOrder | 0 .../scripts/cloneRemoteFromMaster | 0 client/{shared => }/scripts/configureOs | 0 .../scripts/configureOsCustom.template | 0 client/{shared => }/scripts/createImage | 0 .../scripts/createImageCustom.template | 0 .../scripts/createLogicalPartitions | 0 .../scripts/createPrimaryPartitions | 0 client/{shared => }/scripts/deployImage | 0 client/{shared => }/scripts/formatFs | 0 .../{shared => }/scripts/generateMenuDefault | 0 client/{shared => }/scripts/getFsType | 0 client/{shared => }/scripts/getIpAddress | 0 client/{shared => }/scripts/getOsVersion | 0 client/{shared => }/scripts/grubSyntax | 0 client/{shared => }/scripts/initCache | 0 .../{shared => }/scripts/installOfflineMode | 0 .../scripts/launchOgagentInstaller | 0 client/{shared => }/scripts/listHardwareInfo | 0 client/{shared => }/scripts/listPartitions | 0 .../scripts/listPrimaryPartitions | 0 client/{shared => }/scripts/listSoftwareInfo | 0 client/{shared => }/scripts/menuBrowser | 0 client/{shared => }/scripts/poweroff | 0 client/{shared => }/scripts/reboot | 0 client/{shared => }/scripts/remoteConsole | 0 client/{shared => }/scripts/restoreImage | 0 .../scripts/restoreImageCustom.template | 0 client/{shared => }/scripts/runAplicationX.sh | 0 client/{shared => }/scripts/runhttplog.sh | 0 .../scripts/samples/configureGroup | 0 .../scripts/samples/firstRunOnceWindows | 0 .../scripts/samples/smartPartition | 0 client/{shared => }/scripts/sendFileMcast | 0 client/{shared => }/scripts/setBootMode | 0 client/{shared => }/scripts/updateBootCache | 0 client/{shared => }/scripts/updateCache | 0 client/shared/README.es.txt | 29 ------------------ installer/ogboot_installer.py | 25 ++------------- 337 files changed, 23 insertions(+), 61 deletions(-) rename client/{shared => }/bin/EACInterfaces (100%) rename client/{shared => }/bin/browser (100%) rename client/{shared => }/bin/grub-probe1.99_i686 (100%) rename client/{shared => }/bin/grub-probe1.99_x86_64 (100%) rename client/{shared => }/bin/poweroffconf (100%) rename client/{shared => }/bin/rsync-31 (100%) rename client/{shared => }/bin/runtest (100%) rename client/{shared => }/etc/engine.cfg (100%) rename client/{shared => }/etc/engine.json (100%) rename client/{shared => }/etc/es.qmap (100%) rename client/{shared => }/etc/init/default.sh (100%) rename client/{shared => }/etc/lang.ca_ES.UTF-8.conf (100%) rename client/{shared => }/etc/lang.ca_ES.conf (100%) rename client/{shared => }/etc/lang.en_GB.UTF-8.conf (100%) rename client/{shared => }/etc/lang.en_GB.conf (100%) rename client/{shared => }/etc/lang.es_ES.UTF-8.conf (100%) rename client/{shared => }/etc/lang.es_ES.conf (100%) rename client/{shared => }/etc/preinit/default.sh (100%) rename client/{shared => }/etc/preinit/fileslinks.sh (100%) rename client/{shared => }/etc/preinit/loadenviron.sh (100%) rename client/{shared => }/etc/preinit/loadmodules.sh (100%) rename client/{shared => }/etc/preinit/metadevs.sh (100%) rename client/{shared => }/etc/preinit/mountrepo.sh (100%) rename client/{shared => }/etc/preinit/otherservices.sh (100%) rename client/{shared => }/etc/preinit/poweroff.sh (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/background-original.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/background.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/extended (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/icons/hover_debian.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/icons/hover_elementary.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/icons/hover_freebsd.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/icons/hover_haiku.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/icons/hover_linux.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/icons/hover_opengnsys.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/icons/hover_opensuse.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/icons/hover_os.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/icons/hover_osx.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/icons/hover_recovery.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/icons/hover_restart.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/icons/hover_shutdown.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/icons/hover_ubuntu.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/icons/hover_windows.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/icons/hover_windows10.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/icons/hover_windows7.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/icons/hover_windows_metro.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/icons/icons (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/icons/normal_debian.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/icons/normal_elementary.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/icons/normal_freebsd.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/icons/normal_haiku.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/icons/normal_linux.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/icons/normal_opengnsys.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/icons/normal_opensuse.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/icons/normal_os.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/icons/normal_osx.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/icons/normal_recovery.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/icons/normal_restart.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/icons/normal_shutdown.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/icons/normal_ubuntu.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/icons/normal_windows.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/icons/normal_windows10.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/icons/normal_windows7.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/icons/normal_windows_metro.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/000-70opaque.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/button-bg.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/button-hover-bg.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/button-hover-l.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/button-hover-r.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/button-l.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/button-r.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/button-tools-hover.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/button-tools.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/container-b.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/container-bg.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/container-bl.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/container-br.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/container-l.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/container-r.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/container-t.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/container-title-bg.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/container-title-l.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/container-title-r.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/container-title-t.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/container-title-tl.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/container-title-tr.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/container-tl.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/container-tr.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/dialog-b.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/dialog-bg.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/dialog-bl.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/dialog-bl.xcf (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/dialog-br.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/dialog-lr.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/dialog-spacer.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/dialog-t.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/dialog-tl.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/dialog-tr.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/progressbar-bg-b.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/progressbar-bg-bl.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/progressbar-bg-br.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/progressbar-bg-l.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/progressbar-bg-r.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/progressbar-bg-t.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/progressbar-bg-tl.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/progressbar-bg-tr.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/progressbar-bg.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/text-line-l.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/text-line-r.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/tick.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/txt-about.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/txt-help.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/txt-select.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/txt-tools.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/images/ubuntu-glow-96.png (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/menus (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/style (100%) rename client/{shared => }/lib/burg/themes/OpenGnsys/theme (100%) rename client/{shared => }/lib/engine/tests/Modify/Cache.shtest (100%) rename client/{shared => }/lib/engine/tests/NoModify/File1.shtest (100%) rename client/{shared => }/lib/engine/tests/NoModify/Lock1.shtest (100%) rename client/{shared => }/lib/engine/tests/NoModify/Net1.shtest (100%) rename client/{shared => }/lib/engine/tests/README (100%) rename client/{shared => }/lib/engine/tests/crearTestDisk1 (100%) rename client/{shared => }/lib/engine/tests/crearTestLock2 (100%) rename client/{shared => }/lib/fonts/DejaVuSans-Bold.ttf (100%) rename client/{shared => }/lib/fonts/DejaVuSans-BoldOblique.ttf (100%) rename client/{shared => }/lib/fonts/DejaVuSans-Oblique.ttf (100%) rename client/{shared => }/lib/fonts/DejaVuSans.ttf (100%) rename client/{shared => }/lib/fonts/DejaVuSansMono-Bold.ttf (100%) rename client/{shared => }/lib/fonts/DejaVuSansMono-BoldOblique.ttf (100%) rename client/{shared => }/lib/fonts/DejaVuSansMono-Oblique.ttf (100%) rename client/{shared => }/lib/fonts/DejaVuSansMono.ttf (100%) rename client/{shared => }/lib/fonts/DejaVuSerif-Bold.ttf (100%) rename client/{shared => }/lib/fonts/DejaVuSerif-BoldOblique.ttf (100%) rename client/{shared => }/lib/fonts/DejaVuSerif-Oblique.ttf (100%) rename client/{shared => }/lib/fonts/DejaVuSerif.ttf (100%) rename client/{shared => }/lib/fonts/README (100%) rename client/{shared => }/lib/fonts/UTBI____.pfa (100%) rename client/{shared => }/lib/fonts/UTB_____.pfa (100%) rename client/{shared => }/lib/fonts/UTI_____.pfa (100%) rename client/{shared => }/lib/fonts/UTRG____.pfa (100%) rename client/{shared => }/lib/fonts/Vera.ttf (100%) rename client/{shared => }/lib/fonts/VeraBI.ttf (100%) rename client/{shared => }/lib/fonts/VeraBd.ttf (100%) rename client/{shared => }/lib/fonts/VeraIt.ttf (100%) rename client/{shared => }/lib/fonts/VeraMoBI.ttf (100%) rename client/{shared => }/lib/fonts/VeraMoBd.ttf (100%) rename client/{shared => }/lib/fonts/VeraMoIt.ttf (100%) rename client/{shared => }/lib/fonts/VeraMono.ttf (100%) rename client/{shared => }/lib/fonts/VeraSe.ttf (100%) rename client/{shared => }/lib/fonts/VeraSeBd.ttf (100%) rename client/{shared => }/lib/fonts/c0419bt_.pfb (100%) rename client/{shared => }/lib/fonts/c0582bt_.pfb (100%) rename client/{shared => }/lib/fonts/c0583bt_.pfb (100%) rename client/{shared => }/lib/fonts/c0611bt_.pfb (100%) rename client/{shared => }/lib/fonts/c0632bt_.pfb (100%) rename client/{shared => }/lib/fonts/c0633bt_.pfb (100%) rename client/{shared => }/lib/fonts/c0648bt_.pfb (100%) rename client/{shared => }/lib/fonts/c0649bt_.pfb (100%) rename client/{shared => }/lib/fonts/cour.pfa (100%) rename client/{shared => }/lib/fonts/courb.pfa (100%) rename client/{shared => }/lib/fonts/courbi.pfa (100%) rename client/{shared => }/lib/fonts/couri.pfa (100%) rename client/{shared => }/lib/fonts/cursor.pfa (100%) rename client/{shared => }/lib/fonts/fixed_120_50.qpf (100%) rename client/{shared => }/lib/fonts/fixed_70_50.qpf (100%) rename client/{shared => }/lib/fonts/helvetica_100_50.qpf (100%) rename client/{shared => }/lib/fonts/helvetica_100_50i.qpf (100%) rename client/{shared => }/lib/fonts/helvetica_100_75.qpf (100%) rename client/{shared => }/lib/fonts/helvetica_100_75i.qpf (100%) rename client/{shared => }/lib/fonts/helvetica_120_50.qpf (100%) rename client/{shared => }/lib/fonts/helvetica_120_50i.qpf (100%) rename client/{shared => }/lib/fonts/helvetica_120_75.qpf (100%) rename client/{shared => }/lib/fonts/helvetica_120_75i.qpf (100%) rename client/{shared => }/lib/fonts/helvetica_140_50.qpf (100%) rename client/{shared => }/lib/fonts/helvetica_140_50i.qpf (100%) rename client/{shared => }/lib/fonts/helvetica_140_75.qpf (100%) rename client/{shared => }/lib/fonts/helvetica_140_75i.qpf (100%) rename client/{shared => }/lib/fonts/helvetica_180_50.qpf (100%) rename client/{shared => }/lib/fonts/helvetica_180_50i.qpf (100%) rename client/{shared => }/lib/fonts/helvetica_180_75.qpf (100%) rename client/{shared => }/lib/fonts/helvetica_180_75i.qpf (100%) rename client/{shared => }/lib/fonts/helvetica_240_50.qpf (100%) rename client/{shared => }/lib/fonts/helvetica_240_50i.qpf (100%) rename client/{shared => }/lib/fonts/helvetica_240_75.qpf (100%) rename client/{shared => }/lib/fonts/helvetica_240_75i.qpf (100%) rename client/{shared => }/lib/fonts/helvetica_80_50.qpf (100%) rename client/{shared => }/lib/fonts/helvetica_80_50i.qpf (100%) rename client/{shared => }/lib/fonts/helvetica_80_75.qpf (100%) rename client/{shared => }/lib/fonts/helvetica_80_75i.qpf (100%) rename client/{shared => }/lib/fonts/japanese_230_50.qpf (100%) rename client/{shared => }/lib/fonts/l047013t.pfa (100%) rename client/{shared => }/lib/fonts/l047016t.pfa (100%) rename client/{shared => }/lib/fonts/l047033t.pfa (100%) rename client/{shared => }/lib/fonts/l047036t.pfa (100%) rename client/{shared => }/lib/fonts/l048013t.pfa (100%) rename client/{shared => }/lib/fonts/l048016t.pfa (100%) rename client/{shared => }/lib/fonts/l048033t.pfa (100%) rename client/{shared => }/lib/fonts/l048036t.pfa (100%) rename client/{shared => }/lib/fonts/l049013t.pfa (100%) rename client/{shared => }/lib/fonts/l049016t.pfa (100%) rename client/{shared => }/lib/fonts/l049033t.pfa (100%) rename client/{shared => }/lib/fonts/l049036t.pfa (100%) rename client/{shared => }/lib/fonts/micro_40_50.qpf (100%) rename client/{shared => }/lib/fonts/unifont_160_50.qpf (100%) rename client/{shared => }/lib/grub4dos/COPYING (100%) rename client/{shared => }/lib/grub4dos/ChangeLog_GRUB4DOS.txt (100%) rename client/{shared => }/lib/grub4dos/Get_Source_of_This_Build.txt (100%) rename client/{shared => }/lib/grub4dos/README_GRUB4DOS.txt (100%) rename client/{shared => }/lib/grub4dos/badgrub.exe (100%) rename client/{shared => }/lib/grub4dos/bootlace.com (100%) rename client/{shared => }/lib/grub4dos/config.sys (100%) rename client/{shared => }/lib/grub4dos/default (100%) rename client/{shared => }/lib/grub4dos/example.menu.lst (100%) rename client/{shared => }/lib/grub4dos/grldr (100%) rename client/{shared => }/lib/grub4dos/grldr.mbr (100%) rename client/{shared => }/lib/grub4dos/grub.exe (100%) rename client/{shared => }/lib/grub4dos/grub.pif (100%) rename client/{shared => }/lib/grub4dos/grub4dos-0.4.5b/COPYING (100%) rename client/{shared => }/lib/grub4dos/grub4dos-0.4.5b/ChangeLog_GRUB4DOS.txt (100%) rename client/{shared => }/lib/grub4dos/grub4dos-0.4.5b/ChangeLog_chenall.txt (100%) rename client/{shared => }/lib/grub4dos/grub4dos-0.4.5b/README_GRUB4DOS.txt (100%) rename client/{shared => }/lib/grub4dos/grub4dos-0.4.5b/README_GRUB4DOS_CN.txt (100%) rename client/{shared => }/lib/grub4dos/grub4dos-0.4.5b/badgrub.exe (100%) rename client/{shared => }/lib/grub4dos/grub4dos-0.4.5b/bootlace.com (100%) rename client/{shared => }/lib/grub4dos/grub4dos-0.4.5b/config.sys (100%) rename client/{shared => }/lib/grub4dos/grub4dos-0.4.5b/default (100%) rename client/{shared => }/lib/grub4dos/grub4dos-0.4.5b/grldr (100%) rename client/{shared => }/lib/grub4dos/grub4dos-0.4.5b/grldr.mbr (100%) rename client/{shared => }/lib/grub4dos/grub4dos-0.4.5b/grub.exe (100%) rename client/{shared => }/lib/grub4dos/grub4dos-0.4.5b/grub.pif (100%) rename client/{shared => }/lib/grub4dos/grub4dos-0.4.5b/hmload.com (100%) rename client/{shared => }/lib/grub4dos/grub4dos-0.4.5b/menu.lst (100%) rename client/{shared => }/lib/grub4dos/grub4dos-0.4.6a/COPYING (100%) rename client/{shared => }/lib/grub4dos/grub4dos-0.4.6a/Get_Source_of_This_Build.txt (100%) rename client/{shared => }/lib/grub4dos/grub4dos-0.4.6a/badgrub.exe (100%) rename client/{shared => }/lib/grub4dos/grub4dos-0.4.6a/bootlace.com (100%) rename client/{shared => }/lib/grub4dos/grub4dos-0.4.6a/bootlace64.com (100%) rename client/{shared => }/lib/grub4dos/grub4dos-0.4.6a/eltorito.sys (100%) rename client/{shared => }/lib/grub4dos/grub4dos-0.4.6a/grldr (100%) rename client/{shared => }/lib/grub4dos/grub4dos-0.4.6a/grldr.mbr (100%) rename client/{shared => }/lib/grub4dos/grub4dos-0.4.6a/grldr.pbr (100%) rename client/{shared => }/lib/grub4dos/grub4dos-0.4.6a/grldr_cd.bin (100%) rename client/{shared => }/lib/grub4dos/grub4dos-0.4.6a/grub.exe (100%) rename client/{shared => }/lib/grub4dos/grub4dos-0.4.6a/grub.pif (100%) rename client/{shared => }/lib/grub4dos/grub4dos-0.4.6a/hmload.com (100%) rename client/{shared => }/lib/grub4dos/grub4dos-0.4.6a/ipxegrldr (100%) rename client/{shared => }/lib/grub4dos/grub4dos-0.4.6a/sample/config.sys (100%) rename client/{shared => }/lib/grub4dos/grub4dos-0.4.6a/sample/default (100%) rename client/{shared => }/lib/grub4dos/grub4dos-0.4.6a/sample/menu.lst (100%) rename client/{shared => }/lib/grub4dos/hmload.com (100%) rename client/{shared => }/lib/httpd/10-cgi.conf (100%) rename client/{shared => }/lib/httpd/LogCommand.sh (100%) rename client/{shared => }/lib/httpd/LogSession.sh (100%) rename client/{shared => }/lib/httpd/bandwidth.sh (100%) rename client/{shared => }/lib/httpd/cache.sh (100%) rename client/{shared => }/lib/httpd/httpd-log.sh (100%) rename client/{shared => }/lib/httpd/httpd-menu.sh (100%) rename client/{shared => }/lib/httpd/httpd-runengine.sh (100%) rename client/{shared => }/lib/httpd/lighttpd.conf (100%) rename client/{shared => }/lib/httpd/oglive.css (100%) rename client/{shared => }/lib/locale/ca/LC_MESSAGES/browser.mo (100%) rename client/{shared => }/lib/locale/en/LC_MESSAGES/browser.mo (100%) rename client/{shared => }/lib/modules/psmouse.ko (100%) rename client/{shared => }/lib/os-probes/10zvol-test (100%) rename client/{shared => }/lib/os-probes/50mounted-tests (100%) rename client/{shared => }/lib/os-probes/init/10filesystems (100%) rename client/{shared => }/lib/os-probes/mounted/05efi (100%) rename client/{shared => }/lib/os-probes/mounted/10freedos (100%) rename client/{shared => }/lib/os-probes/mounted/10qnx (100%) rename client/{shared => }/lib/os-probes/mounted/20macosx (100%) rename client/{shared => }/lib/os-probes/mounted/20microsoft (100%) rename client/{shared => }/lib/os-probes/mounted/30utility (100%) rename client/{shared => }/lib/os-probes/mounted/40lsb (100%) rename client/{shared => }/lib/os-probes/mounted/70hurd (100%) rename client/{shared => }/lib/os-probes/mounted/80minix (100%) rename client/{shared => }/lib/os-probes/mounted/83haiku (100%) rename client/{shared => }/lib/os-probes/mounted/90linux-distro (100%) rename client/{shared => }/lib/os-probes/mounted/90solaris (100%) rename client/{shared => }/lib/os-probes/mounted/efi/10elilo (100%) rename client/{shared => }/lib/os-probes/mounted/efi/20microsoft (100%) rename client/{shared => }/lib/os-probes/mounted/efi/31part-x-y (100%) rename client/{shared => }/lib/pci.ids (100%) rename client/{shared => }/lib/pictures/oglogo.png (100%) rename client/{shared => }/lib/qtlib/libQtCore.so.4 (100%) rename client/{shared => }/lib/qtlib/libQtGui.so.4 (100%) rename client/{shared => }/lib/qtlib/libQtNetwork.so.4 (100%) rename client/{shared => }/lib/qtlib/libQtWebKit.so.4 (100%) rename client/{shared => }/lib/qtlib/libcrypto.so.1.0.0 (100%) rename client/{shared => }/lib/qtlib/libssl.so.1.0.0 (100%) rename client/{shared => }/lib/qtplugins/imageformats/libqjpeg.so (100%) rename client/{shared => }/scripts/README.es.txt (100%) rename client/{shared => }/scripts/bootLinux (100%) rename client/{shared => }/scripts/bootOs (100%) rename client/{shared => }/scripts/bootOsCustom.template (100%) rename client/{shared => }/scripts/bootWindows (100%) rename client/{shared => }/scripts/buildToOrder (100%) rename client/{shared => }/scripts/cloneRemoteFromMaster (100%) rename client/{shared => }/scripts/configureOs (100%) rename client/{shared => }/scripts/configureOsCustom.template (100%) rename client/{shared => }/scripts/createImage (100%) rename client/{shared => }/scripts/createImageCustom.template (100%) rename client/{shared => }/scripts/createLogicalPartitions (100%) rename client/{shared => }/scripts/createPrimaryPartitions (100%) rename client/{shared => }/scripts/deployImage (100%) rename client/{shared => }/scripts/formatFs (100%) rename client/{shared => }/scripts/generateMenuDefault (100%) rename client/{shared => }/scripts/getFsType (100%) rename client/{shared => }/scripts/getIpAddress (100%) rename client/{shared => }/scripts/getOsVersion (100%) rename client/{shared => }/scripts/grubSyntax (100%) rename client/{shared => }/scripts/initCache (100%) rename client/{shared => }/scripts/installOfflineMode (100%) rename client/{shared => }/scripts/launchOgagentInstaller (100%) rename client/{shared => }/scripts/listHardwareInfo (100%) rename client/{shared => }/scripts/listPartitions (100%) rename client/{shared => }/scripts/listPrimaryPartitions (100%) rename client/{shared => }/scripts/listSoftwareInfo (100%) rename client/{shared => }/scripts/menuBrowser (100%) rename client/{shared => }/scripts/poweroff (100%) rename client/{shared => }/scripts/reboot (100%) rename client/{shared => }/scripts/remoteConsole (100%) rename client/{shared => }/scripts/restoreImage (100%) rename client/{shared => }/scripts/restoreImageCustom.template (100%) rename client/{shared => }/scripts/runAplicationX.sh (100%) rename client/{shared => }/scripts/runhttplog.sh (100%) rename client/{shared => }/scripts/samples/configureGroup (100%) rename client/{shared => }/scripts/samples/firstRunOnceWindows (100%) rename client/{shared => }/scripts/samples/smartPartition (100%) rename client/{shared => }/scripts/sendFileMcast (100%) rename client/{shared => }/scripts/setBootMode (100%) rename client/{shared => }/scripts/updateBootCache (100%) rename client/{shared => }/scripts/updateCache (100%) delete mode 100644 client/shared/README.es.txt diff --git a/.gitignore b/.gitignore index de562d7..f86f647 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +**/*.swp ###> symfony/framework-bundle ### /.env.local diff --git a/client/README.es.txt b/client/README.es.txt index 7d2c03b..374bf3c 100644 --- a/client/README.es.txt +++ b/client/README.es.txt @@ -1,12 +1,23 @@ -OpenGnsys Client README -========================= +OpenGnsys Client README +======================== +Este directorio contiene la estructura principal de datos que será +importada por los clientes OpenGnsys mediante Samba. -Este directorio contiene la estructura de datos del cliente OpenGnsys. +Los subdirectorios se copian íntegramente al servidor bajo +/opt/opengnsys/client y serán importados por los clientes en +/opt/opengnsys. -- boot-tools herramientas para generar la distribución de OpenGnsys Client. -- browser código fuente del cliente gráfico OpenGnsys Browser. -- engine funciones del motor (se instalará en el servidor en - /opt/opengneys/client/lib/engine). -- shared estructura principal del cliente exportado por NFS o Samba - (se instalará en el servidor en /opt/opengneys/client). +La estructura de datos es la siguiente: + +- bin scripts o binarios ejecutables por el cliente (compilados estáticamente). +- etc ficheros de configuración del cliente. +- interfaceAdm interface con el agente +- lib librerías de funciones. + - engine/bin ficheros con las funciones del motor de clonación. + - httpd ficheros de configuración del servicio lighttpd. + - modules módulos extra para el Kernel del cliente. + - qtlib librerías Qt complementarias del Browser. + - qtplugins plugins Qt para el Browser. +- scripts funciones de alto nivel ejecutables por OpenGnsys Browser + y OpenGnsys Admin. diff --git a/client/shared/bin/EACInterfaces b/client/bin/EACInterfaces similarity index 100% rename from client/shared/bin/EACInterfaces rename to client/bin/EACInterfaces diff --git a/client/shared/bin/browser b/client/bin/browser similarity index 100% rename from client/shared/bin/browser rename to client/bin/browser diff --git a/client/shared/bin/grub-probe1.99_i686 b/client/bin/grub-probe1.99_i686 similarity index 100% rename from client/shared/bin/grub-probe1.99_i686 rename to client/bin/grub-probe1.99_i686 diff --git a/client/shared/bin/grub-probe1.99_x86_64 b/client/bin/grub-probe1.99_x86_64 similarity index 100% rename from client/shared/bin/grub-probe1.99_x86_64 rename to client/bin/grub-probe1.99_x86_64 diff --git a/client/shared/bin/poweroffconf b/client/bin/poweroffconf similarity index 100% rename from client/shared/bin/poweroffconf rename to client/bin/poweroffconf diff --git a/client/shared/bin/rsync-31 b/client/bin/rsync-31 similarity index 100% rename from client/shared/bin/rsync-31 rename to client/bin/rsync-31 diff --git a/client/shared/bin/runtest b/client/bin/runtest similarity index 100% rename from client/shared/bin/runtest rename to client/bin/runtest diff --git a/client/shared/etc/engine.cfg b/client/etc/engine.cfg similarity index 100% rename from client/shared/etc/engine.cfg rename to client/etc/engine.cfg diff --git a/client/shared/etc/engine.json b/client/etc/engine.json similarity index 100% rename from client/shared/etc/engine.json rename to client/etc/engine.json diff --git a/client/shared/etc/es.qmap b/client/etc/es.qmap similarity index 100% rename from client/shared/etc/es.qmap rename to client/etc/es.qmap diff --git a/client/shared/etc/init/default.sh b/client/etc/init/default.sh similarity index 100% rename from client/shared/etc/init/default.sh rename to client/etc/init/default.sh diff --git a/client/shared/etc/lang.ca_ES.UTF-8.conf b/client/etc/lang.ca_ES.UTF-8.conf similarity index 100% rename from client/shared/etc/lang.ca_ES.UTF-8.conf rename to client/etc/lang.ca_ES.UTF-8.conf diff --git a/client/shared/etc/lang.ca_ES.conf b/client/etc/lang.ca_ES.conf similarity index 100% rename from client/shared/etc/lang.ca_ES.conf rename to client/etc/lang.ca_ES.conf diff --git a/client/shared/etc/lang.en_GB.UTF-8.conf b/client/etc/lang.en_GB.UTF-8.conf similarity index 100% rename from client/shared/etc/lang.en_GB.UTF-8.conf rename to client/etc/lang.en_GB.UTF-8.conf diff --git a/client/shared/etc/lang.en_GB.conf b/client/etc/lang.en_GB.conf similarity index 100% rename from client/shared/etc/lang.en_GB.conf rename to client/etc/lang.en_GB.conf diff --git a/client/shared/etc/lang.es_ES.UTF-8.conf b/client/etc/lang.es_ES.UTF-8.conf similarity index 100% rename from client/shared/etc/lang.es_ES.UTF-8.conf rename to client/etc/lang.es_ES.UTF-8.conf diff --git a/client/shared/etc/lang.es_ES.conf b/client/etc/lang.es_ES.conf similarity index 100% rename from client/shared/etc/lang.es_ES.conf rename to client/etc/lang.es_ES.conf diff --git a/client/shared/etc/preinit/default.sh b/client/etc/preinit/default.sh similarity index 100% rename from client/shared/etc/preinit/default.sh rename to client/etc/preinit/default.sh diff --git a/client/shared/etc/preinit/fileslinks.sh b/client/etc/preinit/fileslinks.sh similarity index 100% rename from client/shared/etc/preinit/fileslinks.sh rename to client/etc/preinit/fileslinks.sh diff --git a/client/shared/etc/preinit/loadenviron.sh b/client/etc/preinit/loadenviron.sh similarity index 100% rename from client/shared/etc/preinit/loadenviron.sh rename to client/etc/preinit/loadenviron.sh diff --git a/client/shared/etc/preinit/loadmodules.sh b/client/etc/preinit/loadmodules.sh similarity index 100% rename from client/shared/etc/preinit/loadmodules.sh rename to client/etc/preinit/loadmodules.sh diff --git a/client/shared/etc/preinit/metadevs.sh b/client/etc/preinit/metadevs.sh similarity index 100% rename from client/shared/etc/preinit/metadevs.sh rename to client/etc/preinit/metadevs.sh diff --git a/client/shared/etc/preinit/mountrepo.sh b/client/etc/preinit/mountrepo.sh similarity index 100% rename from client/shared/etc/preinit/mountrepo.sh rename to client/etc/preinit/mountrepo.sh diff --git a/client/shared/etc/preinit/otherservices.sh b/client/etc/preinit/otherservices.sh similarity index 100% rename from client/shared/etc/preinit/otherservices.sh rename to client/etc/preinit/otherservices.sh diff --git a/client/shared/etc/preinit/poweroff.sh b/client/etc/preinit/poweroff.sh similarity index 100% rename from client/shared/etc/preinit/poweroff.sh rename to client/etc/preinit/poweroff.sh diff --git a/client/shared/lib/burg/themes/OpenGnsys/background-original.png b/client/lib/burg/themes/OpenGnsys/background-original.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/background-original.png rename to client/lib/burg/themes/OpenGnsys/background-original.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/background.png b/client/lib/burg/themes/OpenGnsys/background.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/background.png rename to client/lib/burg/themes/OpenGnsys/background.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/extended b/client/lib/burg/themes/OpenGnsys/extended similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/extended rename to client/lib/burg/themes/OpenGnsys/extended diff --git a/client/shared/lib/burg/themes/OpenGnsys/icons/hover_debian.png b/client/lib/burg/themes/OpenGnsys/icons/hover_debian.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/icons/hover_debian.png rename to client/lib/burg/themes/OpenGnsys/icons/hover_debian.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/icons/hover_elementary.png b/client/lib/burg/themes/OpenGnsys/icons/hover_elementary.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/icons/hover_elementary.png rename to client/lib/burg/themes/OpenGnsys/icons/hover_elementary.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/icons/hover_freebsd.png b/client/lib/burg/themes/OpenGnsys/icons/hover_freebsd.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/icons/hover_freebsd.png rename to client/lib/burg/themes/OpenGnsys/icons/hover_freebsd.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/icons/hover_haiku.png b/client/lib/burg/themes/OpenGnsys/icons/hover_haiku.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/icons/hover_haiku.png rename to client/lib/burg/themes/OpenGnsys/icons/hover_haiku.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/icons/hover_linux.png b/client/lib/burg/themes/OpenGnsys/icons/hover_linux.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/icons/hover_linux.png rename to client/lib/burg/themes/OpenGnsys/icons/hover_linux.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/icons/hover_opengnsys.png b/client/lib/burg/themes/OpenGnsys/icons/hover_opengnsys.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/icons/hover_opengnsys.png rename to client/lib/burg/themes/OpenGnsys/icons/hover_opengnsys.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/icons/hover_opensuse.png b/client/lib/burg/themes/OpenGnsys/icons/hover_opensuse.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/icons/hover_opensuse.png rename to client/lib/burg/themes/OpenGnsys/icons/hover_opensuse.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/icons/hover_os.png b/client/lib/burg/themes/OpenGnsys/icons/hover_os.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/icons/hover_os.png rename to client/lib/burg/themes/OpenGnsys/icons/hover_os.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/icons/hover_osx.png b/client/lib/burg/themes/OpenGnsys/icons/hover_osx.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/icons/hover_osx.png rename to client/lib/burg/themes/OpenGnsys/icons/hover_osx.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/icons/hover_recovery.png b/client/lib/burg/themes/OpenGnsys/icons/hover_recovery.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/icons/hover_recovery.png rename to client/lib/burg/themes/OpenGnsys/icons/hover_recovery.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/icons/hover_restart.png b/client/lib/burg/themes/OpenGnsys/icons/hover_restart.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/icons/hover_restart.png rename to client/lib/burg/themes/OpenGnsys/icons/hover_restart.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/icons/hover_shutdown.png b/client/lib/burg/themes/OpenGnsys/icons/hover_shutdown.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/icons/hover_shutdown.png rename to client/lib/burg/themes/OpenGnsys/icons/hover_shutdown.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/icons/hover_ubuntu.png b/client/lib/burg/themes/OpenGnsys/icons/hover_ubuntu.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/icons/hover_ubuntu.png rename to client/lib/burg/themes/OpenGnsys/icons/hover_ubuntu.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/icons/hover_windows.png b/client/lib/burg/themes/OpenGnsys/icons/hover_windows.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/icons/hover_windows.png rename to client/lib/burg/themes/OpenGnsys/icons/hover_windows.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/icons/hover_windows10.png b/client/lib/burg/themes/OpenGnsys/icons/hover_windows10.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/icons/hover_windows10.png rename to client/lib/burg/themes/OpenGnsys/icons/hover_windows10.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/icons/hover_windows7.png b/client/lib/burg/themes/OpenGnsys/icons/hover_windows7.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/icons/hover_windows7.png rename to client/lib/burg/themes/OpenGnsys/icons/hover_windows7.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/icons/hover_windows_metro.png b/client/lib/burg/themes/OpenGnsys/icons/hover_windows_metro.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/icons/hover_windows_metro.png rename to client/lib/burg/themes/OpenGnsys/icons/hover_windows_metro.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/icons/icons b/client/lib/burg/themes/OpenGnsys/icons/icons similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/icons/icons rename to client/lib/burg/themes/OpenGnsys/icons/icons diff --git a/client/shared/lib/burg/themes/OpenGnsys/icons/normal_debian.png b/client/lib/burg/themes/OpenGnsys/icons/normal_debian.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/icons/normal_debian.png rename to client/lib/burg/themes/OpenGnsys/icons/normal_debian.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/icons/normal_elementary.png b/client/lib/burg/themes/OpenGnsys/icons/normal_elementary.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/icons/normal_elementary.png rename to client/lib/burg/themes/OpenGnsys/icons/normal_elementary.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/icons/normal_freebsd.png b/client/lib/burg/themes/OpenGnsys/icons/normal_freebsd.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/icons/normal_freebsd.png rename to client/lib/burg/themes/OpenGnsys/icons/normal_freebsd.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/icons/normal_haiku.png b/client/lib/burg/themes/OpenGnsys/icons/normal_haiku.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/icons/normal_haiku.png rename to client/lib/burg/themes/OpenGnsys/icons/normal_haiku.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/icons/normal_linux.png b/client/lib/burg/themes/OpenGnsys/icons/normal_linux.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/icons/normal_linux.png rename to client/lib/burg/themes/OpenGnsys/icons/normal_linux.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/icons/normal_opengnsys.png b/client/lib/burg/themes/OpenGnsys/icons/normal_opengnsys.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/icons/normal_opengnsys.png rename to client/lib/burg/themes/OpenGnsys/icons/normal_opengnsys.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/icons/normal_opensuse.png b/client/lib/burg/themes/OpenGnsys/icons/normal_opensuse.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/icons/normal_opensuse.png rename to client/lib/burg/themes/OpenGnsys/icons/normal_opensuse.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/icons/normal_os.png b/client/lib/burg/themes/OpenGnsys/icons/normal_os.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/icons/normal_os.png rename to client/lib/burg/themes/OpenGnsys/icons/normal_os.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/icons/normal_osx.png b/client/lib/burg/themes/OpenGnsys/icons/normal_osx.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/icons/normal_osx.png rename to client/lib/burg/themes/OpenGnsys/icons/normal_osx.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/icons/normal_recovery.png b/client/lib/burg/themes/OpenGnsys/icons/normal_recovery.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/icons/normal_recovery.png rename to client/lib/burg/themes/OpenGnsys/icons/normal_recovery.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/icons/normal_restart.png b/client/lib/burg/themes/OpenGnsys/icons/normal_restart.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/icons/normal_restart.png rename to client/lib/burg/themes/OpenGnsys/icons/normal_restart.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/icons/normal_shutdown.png b/client/lib/burg/themes/OpenGnsys/icons/normal_shutdown.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/icons/normal_shutdown.png rename to client/lib/burg/themes/OpenGnsys/icons/normal_shutdown.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/icons/normal_ubuntu.png b/client/lib/burg/themes/OpenGnsys/icons/normal_ubuntu.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/icons/normal_ubuntu.png rename to client/lib/burg/themes/OpenGnsys/icons/normal_ubuntu.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/icons/normal_windows.png b/client/lib/burg/themes/OpenGnsys/icons/normal_windows.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/icons/normal_windows.png rename to client/lib/burg/themes/OpenGnsys/icons/normal_windows.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/icons/normal_windows10.png b/client/lib/burg/themes/OpenGnsys/icons/normal_windows10.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/icons/normal_windows10.png rename to client/lib/burg/themes/OpenGnsys/icons/normal_windows10.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/icons/normal_windows7.png b/client/lib/burg/themes/OpenGnsys/icons/normal_windows7.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/icons/normal_windows7.png rename to client/lib/burg/themes/OpenGnsys/icons/normal_windows7.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/icons/normal_windows_metro.png b/client/lib/burg/themes/OpenGnsys/icons/normal_windows_metro.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/icons/normal_windows_metro.png rename to client/lib/burg/themes/OpenGnsys/icons/normal_windows_metro.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/000-70opaque.png b/client/lib/burg/themes/OpenGnsys/images/000-70opaque.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/000-70opaque.png rename to client/lib/burg/themes/OpenGnsys/images/000-70opaque.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/button-bg.png b/client/lib/burg/themes/OpenGnsys/images/button-bg.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/button-bg.png rename to client/lib/burg/themes/OpenGnsys/images/button-bg.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/button-hover-bg.png b/client/lib/burg/themes/OpenGnsys/images/button-hover-bg.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/button-hover-bg.png rename to client/lib/burg/themes/OpenGnsys/images/button-hover-bg.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/button-hover-l.png b/client/lib/burg/themes/OpenGnsys/images/button-hover-l.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/button-hover-l.png rename to client/lib/burg/themes/OpenGnsys/images/button-hover-l.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/button-hover-r.png b/client/lib/burg/themes/OpenGnsys/images/button-hover-r.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/button-hover-r.png rename to client/lib/burg/themes/OpenGnsys/images/button-hover-r.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/button-l.png b/client/lib/burg/themes/OpenGnsys/images/button-l.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/button-l.png rename to client/lib/burg/themes/OpenGnsys/images/button-l.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/button-r.png b/client/lib/burg/themes/OpenGnsys/images/button-r.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/button-r.png rename to client/lib/burg/themes/OpenGnsys/images/button-r.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/button-tools-hover.png b/client/lib/burg/themes/OpenGnsys/images/button-tools-hover.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/button-tools-hover.png rename to client/lib/burg/themes/OpenGnsys/images/button-tools-hover.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/button-tools.png b/client/lib/burg/themes/OpenGnsys/images/button-tools.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/button-tools.png rename to client/lib/burg/themes/OpenGnsys/images/button-tools.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/container-b.png b/client/lib/burg/themes/OpenGnsys/images/container-b.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/container-b.png rename to client/lib/burg/themes/OpenGnsys/images/container-b.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/container-bg.png b/client/lib/burg/themes/OpenGnsys/images/container-bg.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/container-bg.png rename to client/lib/burg/themes/OpenGnsys/images/container-bg.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/container-bl.png b/client/lib/burg/themes/OpenGnsys/images/container-bl.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/container-bl.png rename to client/lib/burg/themes/OpenGnsys/images/container-bl.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/container-br.png b/client/lib/burg/themes/OpenGnsys/images/container-br.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/container-br.png rename to client/lib/burg/themes/OpenGnsys/images/container-br.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/container-l.png b/client/lib/burg/themes/OpenGnsys/images/container-l.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/container-l.png rename to client/lib/burg/themes/OpenGnsys/images/container-l.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/container-r.png b/client/lib/burg/themes/OpenGnsys/images/container-r.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/container-r.png rename to client/lib/burg/themes/OpenGnsys/images/container-r.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/container-t.png b/client/lib/burg/themes/OpenGnsys/images/container-t.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/container-t.png rename to client/lib/burg/themes/OpenGnsys/images/container-t.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/container-title-bg.png b/client/lib/burg/themes/OpenGnsys/images/container-title-bg.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/container-title-bg.png rename to client/lib/burg/themes/OpenGnsys/images/container-title-bg.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/container-title-l.png b/client/lib/burg/themes/OpenGnsys/images/container-title-l.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/container-title-l.png rename to client/lib/burg/themes/OpenGnsys/images/container-title-l.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/container-title-r.png b/client/lib/burg/themes/OpenGnsys/images/container-title-r.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/container-title-r.png rename to client/lib/burg/themes/OpenGnsys/images/container-title-r.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/container-title-t.png b/client/lib/burg/themes/OpenGnsys/images/container-title-t.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/container-title-t.png rename to client/lib/burg/themes/OpenGnsys/images/container-title-t.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/container-title-tl.png b/client/lib/burg/themes/OpenGnsys/images/container-title-tl.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/container-title-tl.png rename to client/lib/burg/themes/OpenGnsys/images/container-title-tl.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/container-title-tr.png b/client/lib/burg/themes/OpenGnsys/images/container-title-tr.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/container-title-tr.png rename to client/lib/burg/themes/OpenGnsys/images/container-title-tr.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/container-tl.png b/client/lib/burg/themes/OpenGnsys/images/container-tl.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/container-tl.png rename to client/lib/burg/themes/OpenGnsys/images/container-tl.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/container-tr.png b/client/lib/burg/themes/OpenGnsys/images/container-tr.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/container-tr.png rename to client/lib/burg/themes/OpenGnsys/images/container-tr.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/dialog-b.png b/client/lib/burg/themes/OpenGnsys/images/dialog-b.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/dialog-b.png rename to client/lib/burg/themes/OpenGnsys/images/dialog-b.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/dialog-bg.png b/client/lib/burg/themes/OpenGnsys/images/dialog-bg.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/dialog-bg.png rename to client/lib/burg/themes/OpenGnsys/images/dialog-bg.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/dialog-bl.png b/client/lib/burg/themes/OpenGnsys/images/dialog-bl.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/dialog-bl.png rename to client/lib/burg/themes/OpenGnsys/images/dialog-bl.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/dialog-bl.xcf b/client/lib/burg/themes/OpenGnsys/images/dialog-bl.xcf similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/dialog-bl.xcf rename to client/lib/burg/themes/OpenGnsys/images/dialog-bl.xcf diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/dialog-br.png b/client/lib/burg/themes/OpenGnsys/images/dialog-br.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/dialog-br.png rename to client/lib/burg/themes/OpenGnsys/images/dialog-br.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/dialog-lr.png b/client/lib/burg/themes/OpenGnsys/images/dialog-lr.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/dialog-lr.png rename to client/lib/burg/themes/OpenGnsys/images/dialog-lr.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/dialog-spacer.png b/client/lib/burg/themes/OpenGnsys/images/dialog-spacer.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/dialog-spacer.png rename to client/lib/burg/themes/OpenGnsys/images/dialog-spacer.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/dialog-t.png b/client/lib/burg/themes/OpenGnsys/images/dialog-t.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/dialog-t.png rename to client/lib/burg/themes/OpenGnsys/images/dialog-t.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/dialog-tl.png b/client/lib/burg/themes/OpenGnsys/images/dialog-tl.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/dialog-tl.png rename to client/lib/burg/themes/OpenGnsys/images/dialog-tl.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/dialog-tr.png b/client/lib/burg/themes/OpenGnsys/images/dialog-tr.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/dialog-tr.png rename to client/lib/burg/themes/OpenGnsys/images/dialog-tr.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/progressbar-bg-b.png b/client/lib/burg/themes/OpenGnsys/images/progressbar-bg-b.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/progressbar-bg-b.png rename to client/lib/burg/themes/OpenGnsys/images/progressbar-bg-b.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/progressbar-bg-bl.png b/client/lib/burg/themes/OpenGnsys/images/progressbar-bg-bl.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/progressbar-bg-bl.png rename to client/lib/burg/themes/OpenGnsys/images/progressbar-bg-bl.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/progressbar-bg-br.png b/client/lib/burg/themes/OpenGnsys/images/progressbar-bg-br.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/progressbar-bg-br.png rename to client/lib/burg/themes/OpenGnsys/images/progressbar-bg-br.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/progressbar-bg-l.png b/client/lib/burg/themes/OpenGnsys/images/progressbar-bg-l.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/progressbar-bg-l.png rename to client/lib/burg/themes/OpenGnsys/images/progressbar-bg-l.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/progressbar-bg-r.png b/client/lib/burg/themes/OpenGnsys/images/progressbar-bg-r.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/progressbar-bg-r.png rename to client/lib/burg/themes/OpenGnsys/images/progressbar-bg-r.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/progressbar-bg-t.png b/client/lib/burg/themes/OpenGnsys/images/progressbar-bg-t.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/progressbar-bg-t.png rename to client/lib/burg/themes/OpenGnsys/images/progressbar-bg-t.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/progressbar-bg-tl.png b/client/lib/burg/themes/OpenGnsys/images/progressbar-bg-tl.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/progressbar-bg-tl.png rename to client/lib/burg/themes/OpenGnsys/images/progressbar-bg-tl.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/progressbar-bg-tr.png b/client/lib/burg/themes/OpenGnsys/images/progressbar-bg-tr.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/progressbar-bg-tr.png rename to client/lib/burg/themes/OpenGnsys/images/progressbar-bg-tr.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/progressbar-bg.png b/client/lib/burg/themes/OpenGnsys/images/progressbar-bg.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/progressbar-bg.png rename to client/lib/burg/themes/OpenGnsys/images/progressbar-bg.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/text-line-l.png b/client/lib/burg/themes/OpenGnsys/images/text-line-l.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/text-line-l.png rename to client/lib/burg/themes/OpenGnsys/images/text-line-l.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/text-line-r.png b/client/lib/burg/themes/OpenGnsys/images/text-line-r.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/text-line-r.png rename to client/lib/burg/themes/OpenGnsys/images/text-line-r.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/tick.png b/client/lib/burg/themes/OpenGnsys/images/tick.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/tick.png rename to client/lib/burg/themes/OpenGnsys/images/tick.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/txt-about.png b/client/lib/burg/themes/OpenGnsys/images/txt-about.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/txt-about.png rename to client/lib/burg/themes/OpenGnsys/images/txt-about.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/txt-help.png b/client/lib/burg/themes/OpenGnsys/images/txt-help.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/txt-help.png rename to client/lib/burg/themes/OpenGnsys/images/txt-help.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/txt-select.png b/client/lib/burg/themes/OpenGnsys/images/txt-select.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/txt-select.png rename to client/lib/burg/themes/OpenGnsys/images/txt-select.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/txt-tools.png b/client/lib/burg/themes/OpenGnsys/images/txt-tools.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/txt-tools.png rename to client/lib/burg/themes/OpenGnsys/images/txt-tools.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/images/ubuntu-glow-96.png b/client/lib/burg/themes/OpenGnsys/images/ubuntu-glow-96.png similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/images/ubuntu-glow-96.png rename to client/lib/burg/themes/OpenGnsys/images/ubuntu-glow-96.png diff --git a/client/shared/lib/burg/themes/OpenGnsys/menus b/client/lib/burg/themes/OpenGnsys/menus similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/menus rename to client/lib/burg/themes/OpenGnsys/menus diff --git a/client/shared/lib/burg/themes/OpenGnsys/style b/client/lib/burg/themes/OpenGnsys/style similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/style rename to client/lib/burg/themes/OpenGnsys/style diff --git a/client/shared/lib/burg/themes/OpenGnsys/theme b/client/lib/burg/themes/OpenGnsys/theme similarity index 100% rename from client/shared/lib/burg/themes/OpenGnsys/theme rename to client/lib/burg/themes/OpenGnsys/theme diff --git a/client/shared/lib/engine/tests/Modify/Cache.shtest b/client/lib/engine/tests/Modify/Cache.shtest similarity index 100% rename from client/shared/lib/engine/tests/Modify/Cache.shtest rename to client/lib/engine/tests/Modify/Cache.shtest diff --git a/client/shared/lib/engine/tests/NoModify/File1.shtest b/client/lib/engine/tests/NoModify/File1.shtest similarity index 100% rename from client/shared/lib/engine/tests/NoModify/File1.shtest rename to client/lib/engine/tests/NoModify/File1.shtest diff --git a/client/shared/lib/engine/tests/NoModify/Lock1.shtest b/client/lib/engine/tests/NoModify/Lock1.shtest similarity index 100% rename from client/shared/lib/engine/tests/NoModify/Lock1.shtest rename to client/lib/engine/tests/NoModify/Lock1.shtest diff --git a/client/shared/lib/engine/tests/NoModify/Net1.shtest b/client/lib/engine/tests/NoModify/Net1.shtest similarity index 100% rename from client/shared/lib/engine/tests/NoModify/Net1.shtest rename to client/lib/engine/tests/NoModify/Net1.shtest diff --git a/client/shared/lib/engine/tests/README b/client/lib/engine/tests/README similarity index 100% rename from client/shared/lib/engine/tests/README rename to client/lib/engine/tests/README diff --git a/client/shared/lib/engine/tests/crearTestDisk1 b/client/lib/engine/tests/crearTestDisk1 similarity index 100% rename from client/shared/lib/engine/tests/crearTestDisk1 rename to client/lib/engine/tests/crearTestDisk1 diff --git a/client/shared/lib/engine/tests/crearTestLock2 b/client/lib/engine/tests/crearTestLock2 similarity index 100% rename from client/shared/lib/engine/tests/crearTestLock2 rename to client/lib/engine/tests/crearTestLock2 diff --git a/client/shared/lib/fonts/DejaVuSans-Bold.ttf b/client/lib/fonts/DejaVuSans-Bold.ttf similarity index 100% rename from client/shared/lib/fonts/DejaVuSans-Bold.ttf rename to client/lib/fonts/DejaVuSans-Bold.ttf diff --git a/client/shared/lib/fonts/DejaVuSans-BoldOblique.ttf b/client/lib/fonts/DejaVuSans-BoldOblique.ttf similarity index 100% rename from client/shared/lib/fonts/DejaVuSans-BoldOblique.ttf rename to client/lib/fonts/DejaVuSans-BoldOblique.ttf diff --git a/client/shared/lib/fonts/DejaVuSans-Oblique.ttf b/client/lib/fonts/DejaVuSans-Oblique.ttf similarity index 100% rename from client/shared/lib/fonts/DejaVuSans-Oblique.ttf rename to client/lib/fonts/DejaVuSans-Oblique.ttf diff --git a/client/shared/lib/fonts/DejaVuSans.ttf b/client/lib/fonts/DejaVuSans.ttf similarity index 100% rename from client/shared/lib/fonts/DejaVuSans.ttf rename to client/lib/fonts/DejaVuSans.ttf diff --git a/client/shared/lib/fonts/DejaVuSansMono-Bold.ttf b/client/lib/fonts/DejaVuSansMono-Bold.ttf similarity index 100% rename from client/shared/lib/fonts/DejaVuSansMono-Bold.ttf rename to client/lib/fonts/DejaVuSansMono-Bold.ttf diff --git a/client/shared/lib/fonts/DejaVuSansMono-BoldOblique.ttf b/client/lib/fonts/DejaVuSansMono-BoldOblique.ttf similarity index 100% rename from client/shared/lib/fonts/DejaVuSansMono-BoldOblique.ttf rename to client/lib/fonts/DejaVuSansMono-BoldOblique.ttf diff --git a/client/shared/lib/fonts/DejaVuSansMono-Oblique.ttf b/client/lib/fonts/DejaVuSansMono-Oblique.ttf similarity index 100% rename from client/shared/lib/fonts/DejaVuSansMono-Oblique.ttf rename to client/lib/fonts/DejaVuSansMono-Oblique.ttf diff --git a/client/shared/lib/fonts/DejaVuSansMono.ttf b/client/lib/fonts/DejaVuSansMono.ttf similarity index 100% rename from client/shared/lib/fonts/DejaVuSansMono.ttf rename to client/lib/fonts/DejaVuSansMono.ttf diff --git a/client/shared/lib/fonts/DejaVuSerif-Bold.ttf b/client/lib/fonts/DejaVuSerif-Bold.ttf similarity index 100% rename from client/shared/lib/fonts/DejaVuSerif-Bold.ttf rename to client/lib/fonts/DejaVuSerif-Bold.ttf diff --git a/client/shared/lib/fonts/DejaVuSerif-BoldOblique.ttf b/client/lib/fonts/DejaVuSerif-BoldOblique.ttf similarity index 100% rename from client/shared/lib/fonts/DejaVuSerif-BoldOblique.ttf rename to client/lib/fonts/DejaVuSerif-BoldOblique.ttf diff --git a/client/shared/lib/fonts/DejaVuSerif-Oblique.ttf b/client/lib/fonts/DejaVuSerif-Oblique.ttf similarity index 100% rename from client/shared/lib/fonts/DejaVuSerif-Oblique.ttf rename to client/lib/fonts/DejaVuSerif-Oblique.ttf diff --git a/client/shared/lib/fonts/DejaVuSerif.ttf b/client/lib/fonts/DejaVuSerif.ttf similarity index 100% rename from client/shared/lib/fonts/DejaVuSerif.ttf rename to client/lib/fonts/DejaVuSerif.ttf diff --git a/client/shared/lib/fonts/README b/client/lib/fonts/README similarity index 100% rename from client/shared/lib/fonts/README rename to client/lib/fonts/README diff --git a/client/shared/lib/fonts/UTBI____.pfa b/client/lib/fonts/UTBI____.pfa similarity index 100% rename from client/shared/lib/fonts/UTBI____.pfa rename to client/lib/fonts/UTBI____.pfa diff --git a/client/shared/lib/fonts/UTB_____.pfa b/client/lib/fonts/UTB_____.pfa similarity index 100% rename from client/shared/lib/fonts/UTB_____.pfa rename to client/lib/fonts/UTB_____.pfa diff --git a/client/shared/lib/fonts/UTI_____.pfa b/client/lib/fonts/UTI_____.pfa similarity index 100% rename from client/shared/lib/fonts/UTI_____.pfa rename to client/lib/fonts/UTI_____.pfa diff --git a/client/shared/lib/fonts/UTRG____.pfa b/client/lib/fonts/UTRG____.pfa similarity index 100% rename from client/shared/lib/fonts/UTRG____.pfa rename to client/lib/fonts/UTRG____.pfa diff --git a/client/shared/lib/fonts/Vera.ttf b/client/lib/fonts/Vera.ttf similarity index 100% rename from client/shared/lib/fonts/Vera.ttf rename to client/lib/fonts/Vera.ttf diff --git a/client/shared/lib/fonts/VeraBI.ttf b/client/lib/fonts/VeraBI.ttf similarity index 100% rename from client/shared/lib/fonts/VeraBI.ttf rename to client/lib/fonts/VeraBI.ttf diff --git a/client/shared/lib/fonts/VeraBd.ttf b/client/lib/fonts/VeraBd.ttf similarity index 100% rename from client/shared/lib/fonts/VeraBd.ttf rename to client/lib/fonts/VeraBd.ttf diff --git a/client/shared/lib/fonts/VeraIt.ttf b/client/lib/fonts/VeraIt.ttf similarity index 100% rename from client/shared/lib/fonts/VeraIt.ttf rename to client/lib/fonts/VeraIt.ttf diff --git a/client/shared/lib/fonts/VeraMoBI.ttf b/client/lib/fonts/VeraMoBI.ttf similarity index 100% rename from client/shared/lib/fonts/VeraMoBI.ttf rename to client/lib/fonts/VeraMoBI.ttf diff --git a/client/shared/lib/fonts/VeraMoBd.ttf b/client/lib/fonts/VeraMoBd.ttf similarity index 100% rename from client/shared/lib/fonts/VeraMoBd.ttf rename to client/lib/fonts/VeraMoBd.ttf diff --git a/client/shared/lib/fonts/VeraMoIt.ttf b/client/lib/fonts/VeraMoIt.ttf similarity index 100% rename from client/shared/lib/fonts/VeraMoIt.ttf rename to client/lib/fonts/VeraMoIt.ttf diff --git a/client/shared/lib/fonts/VeraMono.ttf b/client/lib/fonts/VeraMono.ttf similarity index 100% rename from client/shared/lib/fonts/VeraMono.ttf rename to client/lib/fonts/VeraMono.ttf diff --git a/client/shared/lib/fonts/VeraSe.ttf b/client/lib/fonts/VeraSe.ttf similarity index 100% rename from client/shared/lib/fonts/VeraSe.ttf rename to client/lib/fonts/VeraSe.ttf diff --git a/client/shared/lib/fonts/VeraSeBd.ttf b/client/lib/fonts/VeraSeBd.ttf similarity index 100% rename from client/shared/lib/fonts/VeraSeBd.ttf rename to client/lib/fonts/VeraSeBd.ttf diff --git a/client/shared/lib/fonts/c0419bt_.pfb b/client/lib/fonts/c0419bt_.pfb similarity index 100% rename from client/shared/lib/fonts/c0419bt_.pfb rename to client/lib/fonts/c0419bt_.pfb diff --git a/client/shared/lib/fonts/c0582bt_.pfb b/client/lib/fonts/c0582bt_.pfb similarity index 100% rename from client/shared/lib/fonts/c0582bt_.pfb rename to client/lib/fonts/c0582bt_.pfb diff --git a/client/shared/lib/fonts/c0583bt_.pfb b/client/lib/fonts/c0583bt_.pfb similarity index 100% rename from client/shared/lib/fonts/c0583bt_.pfb rename to client/lib/fonts/c0583bt_.pfb diff --git a/client/shared/lib/fonts/c0611bt_.pfb b/client/lib/fonts/c0611bt_.pfb similarity index 100% rename from client/shared/lib/fonts/c0611bt_.pfb rename to client/lib/fonts/c0611bt_.pfb diff --git a/client/shared/lib/fonts/c0632bt_.pfb b/client/lib/fonts/c0632bt_.pfb similarity index 100% rename from client/shared/lib/fonts/c0632bt_.pfb rename to client/lib/fonts/c0632bt_.pfb diff --git a/client/shared/lib/fonts/c0633bt_.pfb b/client/lib/fonts/c0633bt_.pfb similarity index 100% rename from client/shared/lib/fonts/c0633bt_.pfb rename to client/lib/fonts/c0633bt_.pfb diff --git a/client/shared/lib/fonts/c0648bt_.pfb b/client/lib/fonts/c0648bt_.pfb similarity index 100% rename from client/shared/lib/fonts/c0648bt_.pfb rename to client/lib/fonts/c0648bt_.pfb diff --git a/client/shared/lib/fonts/c0649bt_.pfb b/client/lib/fonts/c0649bt_.pfb similarity index 100% rename from client/shared/lib/fonts/c0649bt_.pfb rename to client/lib/fonts/c0649bt_.pfb diff --git a/client/shared/lib/fonts/cour.pfa b/client/lib/fonts/cour.pfa similarity index 100% rename from client/shared/lib/fonts/cour.pfa rename to client/lib/fonts/cour.pfa diff --git a/client/shared/lib/fonts/courb.pfa b/client/lib/fonts/courb.pfa similarity index 100% rename from client/shared/lib/fonts/courb.pfa rename to client/lib/fonts/courb.pfa diff --git a/client/shared/lib/fonts/courbi.pfa b/client/lib/fonts/courbi.pfa similarity index 100% rename from client/shared/lib/fonts/courbi.pfa rename to client/lib/fonts/courbi.pfa diff --git a/client/shared/lib/fonts/couri.pfa b/client/lib/fonts/couri.pfa similarity index 100% rename from client/shared/lib/fonts/couri.pfa rename to client/lib/fonts/couri.pfa diff --git a/client/shared/lib/fonts/cursor.pfa b/client/lib/fonts/cursor.pfa similarity index 100% rename from client/shared/lib/fonts/cursor.pfa rename to client/lib/fonts/cursor.pfa diff --git a/client/shared/lib/fonts/fixed_120_50.qpf b/client/lib/fonts/fixed_120_50.qpf similarity index 100% rename from client/shared/lib/fonts/fixed_120_50.qpf rename to client/lib/fonts/fixed_120_50.qpf diff --git a/client/shared/lib/fonts/fixed_70_50.qpf b/client/lib/fonts/fixed_70_50.qpf similarity index 100% rename from client/shared/lib/fonts/fixed_70_50.qpf rename to client/lib/fonts/fixed_70_50.qpf diff --git a/client/shared/lib/fonts/helvetica_100_50.qpf b/client/lib/fonts/helvetica_100_50.qpf similarity index 100% rename from client/shared/lib/fonts/helvetica_100_50.qpf rename to client/lib/fonts/helvetica_100_50.qpf diff --git a/client/shared/lib/fonts/helvetica_100_50i.qpf b/client/lib/fonts/helvetica_100_50i.qpf similarity index 100% rename from client/shared/lib/fonts/helvetica_100_50i.qpf rename to client/lib/fonts/helvetica_100_50i.qpf diff --git a/client/shared/lib/fonts/helvetica_100_75.qpf b/client/lib/fonts/helvetica_100_75.qpf similarity index 100% rename from client/shared/lib/fonts/helvetica_100_75.qpf rename to client/lib/fonts/helvetica_100_75.qpf diff --git a/client/shared/lib/fonts/helvetica_100_75i.qpf b/client/lib/fonts/helvetica_100_75i.qpf similarity index 100% rename from client/shared/lib/fonts/helvetica_100_75i.qpf rename to client/lib/fonts/helvetica_100_75i.qpf diff --git a/client/shared/lib/fonts/helvetica_120_50.qpf b/client/lib/fonts/helvetica_120_50.qpf similarity index 100% rename from client/shared/lib/fonts/helvetica_120_50.qpf rename to client/lib/fonts/helvetica_120_50.qpf diff --git a/client/shared/lib/fonts/helvetica_120_50i.qpf b/client/lib/fonts/helvetica_120_50i.qpf similarity index 100% rename from client/shared/lib/fonts/helvetica_120_50i.qpf rename to client/lib/fonts/helvetica_120_50i.qpf diff --git a/client/shared/lib/fonts/helvetica_120_75.qpf b/client/lib/fonts/helvetica_120_75.qpf similarity index 100% rename from client/shared/lib/fonts/helvetica_120_75.qpf rename to client/lib/fonts/helvetica_120_75.qpf diff --git a/client/shared/lib/fonts/helvetica_120_75i.qpf b/client/lib/fonts/helvetica_120_75i.qpf similarity index 100% rename from client/shared/lib/fonts/helvetica_120_75i.qpf rename to client/lib/fonts/helvetica_120_75i.qpf diff --git a/client/shared/lib/fonts/helvetica_140_50.qpf b/client/lib/fonts/helvetica_140_50.qpf similarity index 100% rename from client/shared/lib/fonts/helvetica_140_50.qpf rename to client/lib/fonts/helvetica_140_50.qpf diff --git a/client/shared/lib/fonts/helvetica_140_50i.qpf b/client/lib/fonts/helvetica_140_50i.qpf similarity index 100% rename from client/shared/lib/fonts/helvetica_140_50i.qpf rename to client/lib/fonts/helvetica_140_50i.qpf diff --git a/client/shared/lib/fonts/helvetica_140_75.qpf b/client/lib/fonts/helvetica_140_75.qpf similarity index 100% rename from client/shared/lib/fonts/helvetica_140_75.qpf rename to client/lib/fonts/helvetica_140_75.qpf diff --git a/client/shared/lib/fonts/helvetica_140_75i.qpf b/client/lib/fonts/helvetica_140_75i.qpf similarity index 100% rename from client/shared/lib/fonts/helvetica_140_75i.qpf rename to client/lib/fonts/helvetica_140_75i.qpf diff --git a/client/shared/lib/fonts/helvetica_180_50.qpf b/client/lib/fonts/helvetica_180_50.qpf similarity index 100% rename from client/shared/lib/fonts/helvetica_180_50.qpf rename to client/lib/fonts/helvetica_180_50.qpf diff --git a/client/shared/lib/fonts/helvetica_180_50i.qpf b/client/lib/fonts/helvetica_180_50i.qpf similarity index 100% rename from client/shared/lib/fonts/helvetica_180_50i.qpf rename to client/lib/fonts/helvetica_180_50i.qpf diff --git a/client/shared/lib/fonts/helvetica_180_75.qpf b/client/lib/fonts/helvetica_180_75.qpf similarity index 100% rename from client/shared/lib/fonts/helvetica_180_75.qpf rename to client/lib/fonts/helvetica_180_75.qpf diff --git a/client/shared/lib/fonts/helvetica_180_75i.qpf b/client/lib/fonts/helvetica_180_75i.qpf similarity index 100% rename from client/shared/lib/fonts/helvetica_180_75i.qpf rename to client/lib/fonts/helvetica_180_75i.qpf diff --git a/client/shared/lib/fonts/helvetica_240_50.qpf b/client/lib/fonts/helvetica_240_50.qpf similarity index 100% rename from client/shared/lib/fonts/helvetica_240_50.qpf rename to client/lib/fonts/helvetica_240_50.qpf diff --git a/client/shared/lib/fonts/helvetica_240_50i.qpf b/client/lib/fonts/helvetica_240_50i.qpf similarity index 100% rename from client/shared/lib/fonts/helvetica_240_50i.qpf rename to client/lib/fonts/helvetica_240_50i.qpf diff --git a/client/shared/lib/fonts/helvetica_240_75.qpf b/client/lib/fonts/helvetica_240_75.qpf similarity index 100% rename from client/shared/lib/fonts/helvetica_240_75.qpf rename to client/lib/fonts/helvetica_240_75.qpf diff --git a/client/shared/lib/fonts/helvetica_240_75i.qpf b/client/lib/fonts/helvetica_240_75i.qpf similarity index 100% rename from client/shared/lib/fonts/helvetica_240_75i.qpf rename to client/lib/fonts/helvetica_240_75i.qpf diff --git a/client/shared/lib/fonts/helvetica_80_50.qpf b/client/lib/fonts/helvetica_80_50.qpf similarity index 100% rename from client/shared/lib/fonts/helvetica_80_50.qpf rename to client/lib/fonts/helvetica_80_50.qpf diff --git a/client/shared/lib/fonts/helvetica_80_50i.qpf b/client/lib/fonts/helvetica_80_50i.qpf similarity index 100% rename from client/shared/lib/fonts/helvetica_80_50i.qpf rename to client/lib/fonts/helvetica_80_50i.qpf diff --git a/client/shared/lib/fonts/helvetica_80_75.qpf b/client/lib/fonts/helvetica_80_75.qpf similarity index 100% rename from client/shared/lib/fonts/helvetica_80_75.qpf rename to client/lib/fonts/helvetica_80_75.qpf diff --git a/client/shared/lib/fonts/helvetica_80_75i.qpf b/client/lib/fonts/helvetica_80_75i.qpf similarity index 100% rename from client/shared/lib/fonts/helvetica_80_75i.qpf rename to client/lib/fonts/helvetica_80_75i.qpf diff --git a/client/shared/lib/fonts/japanese_230_50.qpf b/client/lib/fonts/japanese_230_50.qpf similarity index 100% rename from client/shared/lib/fonts/japanese_230_50.qpf rename to client/lib/fonts/japanese_230_50.qpf diff --git a/client/shared/lib/fonts/l047013t.pfa b/client/lib/fonts/l047013t.pfa similarity index 100% rename from client/shared/lib/fonts/l047013t.pfa rename to client/lib/fonts/l047013t.pfa diff --git a/client/shared/lib/fonts/l047016t.pfa b/client/lib/fonts/l047016t.pfa similarity index 100% rename from client/shared/lib/fonts/l047016t.pfa rename to client/lib/fonts/l047016t.pfa diff --git a/client/shared/lib/fonts/l047033t.pfa b/client/lib/fonts/l047033t.pfa similarity index 100% rename from client/shared/lib/fonts/l047033t.pfa rename to client/lib/fonts/l047033t.pfa diff --git a/client/shared/lib/fonts/l047036t.pfa b/client/lib/fonts/l047036t.pfa similarity index 100% rename from client/shared/lib/fonts/l047036t.pfa rename to client/lib/fonts/l047036t.pfa diff --git a/client/shared/lib/fonts/l048013t.pfa b/client/lib/fonts/l048013t.pfa similarity index 100% rename from client/shared/lib/fonts/l048013t.pfa rename to client/lib/fonts/l048013t.pfa diff --git a/client/shared/lib/fonts/l048016t.pfa b/client/lib/fonts/l048016t.pfa similarity index 100% rename from client/shared/lib/fonts/l048016t.pfa rename to client/lib/fonts/l048016t.pfa diff --git a/client/shared/lib/fonts/l048033t.pfa b/client/lib/fonts/l048033t.pfa similarity index 100% rename from client/shared/lib/fonts/l048033t.pfa rename to client/lib/fonts/l048033t.pfa diff --git a/client/shared/lib/fonts/l048036t.pfa b/client/lib/fonts/l048036t.pfa similarity index 100% rename from client/shared/lib/fonts/l048036t.pfa rename to client/lib/fonts/l048036t.pfa diff --git a/client/shared/lib/fonts/l049013t.pfa b/client/lib/fonts/l049013t.pfa similarity index 100% rename from client/shared/lib/fonts/l049013t.pfa rename to client/lib/fonts/l049013t.pfa diff --git a/client/shared/lib/fonts/l049016t.pfa b/client/lib/fonts/l049016t.pfa similarity index 100% rename from client/shared/lib/fonts/l049016t.pfa rename to client/lib/fonts/l049016t.pfa diff --git a/client/shared/lib/fonts/l049033t.pfa b/client/lib/fonts/l049033t.pfa similarity index 100% rename from client/shared/lib/fonts/l049033t.pfa rename to client/lib/fonts/l049033t.pfa diff --git a/client/shared/lib/fonts/l049036t.pfa b/client/lib/fonts/l049036t.pfa similarity index 100% rename from client/shared/lib/fonts/l049036t.pfa rename to client/lib/fonts/l049036t.pfa diff --git a/client/shared/lib/fonts/micro_40_50.qpf b/client/lib/fonts/micro_40_50.qpf similarity index 100% rename from client/shared/lib/fonts/micro_40_50.qpf rename to client/lib/fonts/micro_40_50.qpf diff --git a/client/shared/lib/fonts/unifont_160_50.qpf b/client/lib/fonts/unifont_160_50.qpf similarity index 100% rename from client/shared/lib/fonts/unifont_160_50.qpf rename to client/lib/fonts/unifont_160_50.qpf diff --git a/client/shared/lib/grub4dos/COPYING b/client/lib/grub4dos/COPYING similarity index 100% rename from client/shared/lib/grub4dos/COPYING rename to client/lib/grub4dos/COPYING diff --git a/client/shared/lib/grub4dos/ChangeLog_GRUB4DOS.txt b/client/lib/grub4dos/ChangeLog_GRUB4DOS.txt similarity index 100% rename from client/shared/lib/grub4dos/ChangeLog_GRUB4DOS.txt rename to client/lib/grub4dos/ChangeLog_GRUB4DOS.txt diff --git a/client/shared/lib/grub4dos/Get_Source_of_This_Build.txt b/client/lib/grub4dos/Get_Source_of_This_Build.txt similarity index 100% rename from client/shared/lib/grub4dos/Get_Source_of_This_Build.txt rename to client/lib/grub4dos/Get_Source_of_This_Build.txt diff --git a/client/shared/lib/grub4dos/README_GRUB4DOS.txt b/client/lib/grub4dos/README_GRUB4DOS.txt similarity index 100% rename from client/shared/lib/grub4dos/README_GRUB4DOS.txt rename to client/lib/grub4dos/README_GRUB4DOS.txt diff --git a/client/shared/lib/grub4dos/badgrub.exe b/client/lib/grub4dos/badgrub.exe similarity index 100% rename from client/shared/lib/grub4dos/badgrub.exe rename to client/lib/grub4dos/badgrub.exe diff --git a/client/shared/lib/grub4dos/bootlace.com b/client/lib/grub4dos/bootlace.com similarity index 100% rename from client/shared/lib/grub4dos/bootlace.com rename to client/lib/grub4dos/bootlace.com diff --git a/client/shared/lib/grub4dos/config.sys b/client/lib/grub4dos/config.sys similarity index 100% rename from client/shared/lib/grub4dos/config.sys rename to client/lib/grub4dos/config.sys diff --git a/client/shared/lib/grub4dos/default b/client/lib/grub4dos/default similarity index 100% rename from client/shared/lib/grub4dos/default rename to client/lib/grub4dos/default diff --git a/client/shared/lib/grub4dos/example.menu.lst b/client/lib/grub4dos/example.menu.lst similarity index 100% rename from client/shared/lib/grub4dos/example.menu.lst rename to client/lib/grub4dos/example.menu.lst diff --git a/client/shared/lib/grub4dos/grldr b/client/lib/grub4dos/grldr similarity index 100% rename from client/shared/lib/grub4dos/grldr rename to client/lib/grub4dos/grldr diff --git a/client/shared/lib/grub4dos/grldr.mbr b/client/lib/grub4dos/grldr.mbr similarity index 100% rename from client/shared/lib/grub4dos/grldr.mbr rename to client/lib/grub4dos/grldr.mbr diff --git a/client/shared/lib/grub4dos/grub.exe b/client/lib/grub4dos/grub.exe similarity index 100% rename from client/shared/lib/grub4dos/grub.exe rename to client/lib/grub4dos/grub.exe diff --git a/client/shared/lib/grub4dos/grub.pif b/client/lib/grub4dos/grub.pif similarity index 100% rename from client/shared/lib/grub4dos/grub.pif rename to client/lib/grub4dos/grub.pif diff --git a/client/shared/lib/grub4dos/grub4dos-0.4.5b/COPYING b/client/lib/grub4dos/grub4dos-0.4.5b/COPYING similarity index 100% rename from client/shared/lib/grub4dos/grub4dos-0.4.5b/COPYING rename to client/lib/grub4dos/grub4dos-0.4.5b/COPYING diff --git a/client/shared/lib/grub4dos/grub4dos-0.4.5b/ChangeLog_GRUB4DOS.txt b/client/lib/grub4dos/grub4dos-0.4.5b/ChangeLog_GRUB4DOS.txt similarity index 100% rename from client/shared/lib/grub4dos/grub4dos-0.4.5b/ChangeLog_GRUB4DOS.txt rename to client/lib/grub4dos/grub4dos-0.4.5b/ChangeLog_GRUB4DOS.txt diff --git a/client/shared/lib/grub4dos/grub4dos-0.4.5b/ChangeLog_chenall.txt b/client/lib/grub4dos/grub4dos-0.4.5b/ChangeLog_chenall.txt similarity index 100% rename from client/shared/lib/grub4dos/grub4dos-0.4.5b/ChangeLog_chenall.txt rename to client/lib/grub4dos/grub4dos-0.4.5b/ChangeLog_chenall.txt diff --git a/client/shared/lib/grub4dos/grub4dos-0.4.5b/README_GRUB4DOS.txt b/client/lib/grub4dos/grub4dos-0.4.5b/README_GRUB4DOS.txt similarity index 100% rename from client/shared/lib/grub4dos/grub4dos-0.4.5b/README_GRUB4DOS.txt rename to client/lib/grub4dos/grub4dos-0.4.5b/README_GRUB4DOS.txt diff --git a/client/shared/lib/grub4dos/grub4dos-0.4.5b/README_GRUB4DOS_CN.txt b/client/lib/grub4dos/grub4dos-0.4.5b/README_GRUB4DOS_CN.txt similarity index 100% rename from client/shared/lib/grub4dos/grub4dos-0.4.5b/README_GRUB4DOS_CN.txt rename to client/lib/grub4dos/grub4dos-0.4.5b/README_GRUB4DOS_CN.txt diff --git a/client/shared/lib/grub4dos/grub4dos-0.4.5b/badgrub.exe b/client/lib/grub4dos/grub4dos-0.4.5b/badgrub.exe similarity index 100% rename from client/shared/lib/grub4dos/grub4dos-0.4.5b/badgrub.exe rename to client/lib/grub4dos/grub4dos-0.4.5b/badgrub.exe diff --git a/client/shared/lib/grub4dos/grub4dos-0.4.5b/bootlace.com b/client/lib/grub4dos/grub4dos-0.4.5b/bootlace.com similarity index 100% rename from client/shared/lib/grub4dos/grub4dos-0.4.5b/bootlace.com rename to client/lib/grub4dos/grub4dos-0.4.5b/bootlace.com diff --git a/client/shared/lib/grub4dos/grub4dos-0.4.5b/config.sys b/client/lib/grub4dos/grub4dos-0.4.5b/config.sys similarity index 100% rename from client/shared/lib/grub4dos/grub4dos-0.4.5b/config.sys rename to client/lib/grub4dos/grub4dos-0.4.5b/config.sys diff --git a/client/shared/lib/grub4dos/grub4dos-0.4.5b/default b/client/lib/grub4dos/grub4dos-0.4.5b/default similarity index 100% rename from client/shared/lib/grub4dos/grub4dos-0.4.5b/default rename to client/lib/grub4dos/grub4dos-0.4.5b/default diff --git a/client/shared/lib/grub4dos/grub4dos-0.4.5b/grldr b/client/lib/grub4dos/grub4dos-0.4.5b/grldr similarity index 100% rename from client/shared/lib/grub4dos/grub4dos-0.4.5b/grldr rename to client/lib/grub4dos/grub4dos-0.4.5b/grldr diff --git a/client/shared/lib/grub4dos/grub4dos-0.4.5b/grldr.mbr b/client/lib/grub4dos/grub4dos-0.4.5b/grldr.mbr similarity index 100% rename from client/shared/lib/grub4dos/grub4dos-0.4.5b/grldr.mbr rename to client/lib/grub4dos/grub4dos-0.4.5b/grldr.mbr diff --git a/client/shared/lib/grub4dos/grub4dos-0.4.5b/grub.exe b/client/lib/grub4dos/grub4dos-0.4.5b/grub.exe similarity index 100% rename from client/shared/lib/grub4dos/grub4dos-0.4.5b/grub.exe rename to client/lib/grub4dos/grub4dos-0.4.5b/grub.exe diff --git a/client/shared/lib/grub4dos/grub4dos-0.4.5b/grub.pif b/client/lib/grub4dos/grub4dos-0.4.5b/grub.pif similarity index 100% rename from client/shared/lib/grub4dos/grub4dos-0.4.5b/grub.pif rename to client/lib/grub4dos/grub4dos-0.4.5b/grub.pif diff --git a/client/shared/lib/grub4dos/grub4dos-0.4.5b/hmload.com b/client/lib/grub4dos/grub4dos-0.4.5b/hmload.com similarity index 100% rename from client/shared/lib/grub4dos/grub4dos-0.4.5b/hmload.com rename to client/lib/grub4dos/grub4dos-0.4.5b/hmload.com diff --git a/client/shared/lib/grub4dos/grub4dos-0.4.5b/menu.lst b/client/lib/grub4dos/grub4dos-0.4.5b/menu.lst similarity index 100% rename from client/shared/lib/grub4dos/grub4dos-0.4.5b/menu.lst rename to client/lib/grub4dos/grub4dos-0.4.5b/menu.lst diff --git a/client/shared/lib/grub4dos/grub4dos-0.4.6a/COPYING b/client/lib/grub4dos/grub4dos-0.4.6a/COPYING similarity index 100% rename from client/shared/lib/grub4dos/grub4dos-0.4.6a/COPYING rename to client/lib/grub4dos/grub4dos-0.4.6a/COPYING diff --git a/client/shared/lib/grub4dos/grub4dos-0.4.6a/Get_Source_of_This_Build.txt b/client/lib/grub4dos/grub4dos-0.4.6a/Get_Source_of_This_Build.txt similarity index 100% rename from client/shared/lib/grub4dos/grub4dos-0.4.6a/Get_Source_of_This_Build.txt rename to client/lib/grub4dos/grub4dos-0.4.6a/Get_Source_of_This_Build.txt diff --git a/client/shared/lib/grub4dos/grub4dos-0.4.6a/badgrub.exe b/client/lib/grub4dos/grub4dos-0.4.6a/badgrub.exe similarity index 100% rename from client/shared/lib/grub4dos/grub4dos-0.4.6a/badgrub.exe rename to client/lib/grub4dos/grub4dos-0.4.6a/badgrub.exe diff --git a/client/shared/lib/grub4dos/grub4dos-0.4.6a/bootlace.com b/client/lib/grub4dos/grub4dos-0.4.6a/bootlace.com similarity index 100% rename from client/shared/lib/grub4dos/grub4dos-0.4.6a/bootlace.com rename to client/lib/grub4dos/grub4dos-0.4.6a/bootlace.com diff --git a/client/shared/lib/grub4dos/grub4dos-0.4.6a/bootlace64.com b/client/lib/grub4dos/grub4dos-0.4.6a/bootlace64.com similarity index 100% rename from client/shared/lib/grub4dos/grub4dos-0.4.6a/bootlace64.com rename to client/lib/grub4dos/grub4dos-0.4.6a/bootlace64.com diff --git a/client/shared/lib/grub4dos/grub4dos-0.4.6a/eltorito.sys b/client/lib/grub4dos/grub4dos-0.4.6a/eltorito.sys similarity index 100% rename from client/shared/lib/grub4dos/grub4dos-0.4.6a/eltorito.sys rename to client/lib/grub4dos/grub4dos-0.4.6a/eltorito.sys diff --git a/client/shared/lib/grub4dos/grub4dos-0.4.6a/grldr b/client/lib/grub4dos/grub4dos-0.4.6a/grldr similarity index 100% rename from client/shared/lib/grub4dos/grub4dos-0.4.6a/grldr rename to client/lib/grub4dos/grub4dos-0.4.6a/grldr diff --git a/client/shared/lib/grub4dos/grub4dos-0.4.6a/grldr.mbr b/client/lib/grub4dos/grub4dos-0.4.6a/grldr.mbr similarity index 100% rename from client/shared/lib/grub4dos/grub4dos-0.4.6a/grldr.mbr rename to client/lib/grub4dos/grub4dos-0.4.6a/grldr.mbr diff --git a/client/shared/lib/grub4dos/grub4dos-0.4.6a/grldr.pbr b/client/lib/grub4dos/grub4dos-0.4.6a/grldr.pbr similarity index 100% rename from client/shared/lib/grub4dos/grub4dos-0.4.6a/grldr.pbr rename to client/lib/grub4dos/grub4dos-0.4.6a/grldr.pbr diff --git a/client/shared/lib/grub4dos/grub4dos-0.4.6a/grldr_cd.bin b/client/lib/grub4dos/grub4dos-0.4.6a/grldr_cd.bin similarity index 100% rename from client/shared/lib/grub4dos/grub4dos-0.4.6a/grldr_cd.bin rename to client/lib/grub4dos/grub4dos-0.4.6a/grldr_cd.bin diff --git a/client/shared/lib/grub4dos/grub4dos-0.4.6a/grub.exe b/client/lib/grub4dos/grub4dos-0.4.6a/grub.exe similarity index 100% rename from client/shared/lib/grub4dos/grub4dos-0.4.6a/grub.exe rename to client/lib/grub4dos/grub4dos-0.4.6a/grub.exe diff --git a/client/shared/lib/grub4dos/grub4dos-0.4.6a/grub.pif b/client/lib/grub4dos/grub4dos-0.4.6a/grub.pif similarity index 100% rename from client/shared/lib/grub4dos/grub4dos-0.4.6a/grub.pif rename to client/lib/grub4dos/grub4dos-0.4.6a/grub.pif diff --git a/client/shared/lib/grub4dos/grub4dos-0.4.6a/hmload.com b/client/lib/grub4dos/grub4dos-0.4.6a/hmload.com similarity index 100% rename from client/shared/lib/grub4dos/grub4dos-0.4.6a/hmload.com rename to client/lib/grub4dos/grub4dos-0.4.6a/hmload.com diff --git a/client/shared/lib/grub4dos/grub4dos-0.4.6a/ipxegrldr b/client/lib/grub4dos/grub4dos-0.4.6a/ipxegrldr similarity index 100% rename from client/shared/lib/grub4dos/grub4dos-0.4.6a/ipxegrldr rename to client/lib/grub4dos/grub4dos-0.4.6a/ipxegrldr diff --git a/client/shared/lib/grub4dos/grub4dos-0.4.6a/sample/config.sys b/client/lib/grub4dos/grub4dos-0.4.6a/sample/config.sys similarity index 100% rename from client/shared/lib/grub4dos/grub4dos-0.4.6a/sample/config.sys rename to client/lib/grub4dos/grub4dos-0.4.6a/sample/config.sys diff --git a/client/shared/lib/grub4dos/grub4dos-0.4.6a/sample/default b/client/lib/grub4dos/grub4dos-0.4.6a/sample/default similarity index 100% rename from client/shared/lib/grub4dos/grub4dos-0.4.6a/sample/default rename to client/lib/grub4dos/grub4dos-0.4.6a/sample/default diff --git a/client/shared/lib/grub4dos/grub4dos-0.4.6a/sample/menu.lst b/client/lib/grub4dos/grub4dos-0.4.6a/sample/menu.lst similarity index 100% rename from client/shared/lib/grub4dos/grub4dos-0.4.6a/sample/menu.lst rename to client/lib/grub4dos/grub4dos-0.4.6a/sample/menu.lst diff --git a/client/shared/lib/grub4dos/hmload.com b/client/lib/grub4dos/hmload.com similarity index 100% rename from client/shared/lib/grub4dos/hmload.com rename to client/lib/grub4dos/hmload.com diff --git a/client/shared/lib/httpd/10-cgi.conf b/client/lib/httpd/10-cgi.conf similarity index 100% rename from client/shared/lib/httpd/10-cgi.conf rename to client/lib/httpd/10-cgi.conf diff --git a/client/shared/lib/httpd/LogCommand.sh b/client/lib/httpd/LogCommand.sh similarity index 100% rename from client/shared/lib/httpd/LogCommand.sh rename to client/lib/httpd/LogCommand.sh diff --git a/client/shared/lib/httpd/LogSession.sh b/client/lib/httpd/LogSession.sh similarity index 100% rename from client/shared/lib/httpd/LogSession.sh rename to client/lib/httpd/LogSession.sh diff --git a/client/shared/lib/httpd/bandwidth.sh b/client/lib/httpd/bandwidth.sh similarity index 100% rename from client/shared/lib/httpd/bandwidth.sh rename to client/lib/httpd/bandwidth.sh diff --git a/client/shared/lib/httpd/cache.sh b/client/lib/httpd/cache.sh similarity index 100% rename from client/shared/lib/httpd/cache.sh rename to client/lib/httpd/cache.sh diff --git a/client/shared/lib/httpd/httpd-log.sh b/client/lib/httpd/httpd-log.sh similarity index 100% rename from client/shared/lib/httpd/httpd-log.sh rename to client/lib/httpd/httpd-log.sh diff --git a/client/shared/lib/httpd/httpd-menu.sh b/client/lib/httpd/httpd-menu.sh similarity index 100% rename from client/shared/lib/httpd/httpd-menu.sh rename to client/lib/httpd/httpd-menu.sh diff --git a/client/shared/lib/httpd/httpd-runengine.sh b/client/lib/httpd/httpd-runengine.sh similarity index 100% rename from client/shared/lib/httpd/httpd-runengine.sh rename to client/lib/httpd/httpd-runengine.sh diff --git a/client/shared/lib/httpd/lighttpd.conf b/client/lib/httpd/lighttpd.conf similarity index 100% rename from client/shared/lib/httpd/lighttpd.conf rename to client/lib/httpd/lighttpd.conf diff --git a/client/shared/lib/httpd/oglive.css b/client/lib/httpd/oglive.css similarity index 100% rename from client/shared/lib/httpd/oglive.css rename to client/lib/httpd/oglive.css diff --git a/client/shared/lib/locale/ca/LC_MESSAGES/browser.mo b/client/lib/locale/ca/LC_MESSAGES/browser.mo similarity index 100% rename from client/shared/lib/locale/ca/LC_MESSAGES/browser.mo rename to client/lib/locale/ca/LC_MESSAGES/browser.mo diff --git a/client/shared/lib/locale/en/LC_MESSAGES/browser.mo b/client/lib/locale/en/LC_MESSAGES/browser.mo similarity index 100% rename from client/shared/lib/locale/en/LC_MESSAGES/browser.mo rename to client/lib/locale/en/LC_MESSAGES/browser.mo diff --git a/client/shared/lib/modules/psmouse.ko b/client/lib/modules/psmouse.ko similarity index 100% rename from client/shared/lib/modules/psmouse.ko rename to client/lib/modules/psmouse.ko diff --git a/client/shared/lib/os-probes/10zvol-test b/client/lib/os-probes/10zvol-test similarity index 100% rename from client/shared/lib/os-probes/10zvol-test rename to client/lib/os-probes/10zvol-test diff --git a/client/shared/lib/os-probes/50mounted-tests b/client/lib/os-probes/50mounted-tests similarity index 100% rename from client/shared/lib/os-probes/50mounted-tests rename to client/lib/os-probes/50mounted-tests diff --git a/client/shared/lib/os-probes/init/10filesystems b/client/lib/os-probes/init/10filesystems similarity index 100% rename from client/shared/lib/os-probes/init/10filesystems rename to client/lib/os-probes/init/10filesystems diff --git a/client/shared/lib/os-probes/mounted/05efi b/client/lib/os-probes/mounted/05efi similarity index 100% rename from client/shared/lib/os-probes/mounted/05efi rename to client/lib/os-probes/mounted/05efi diff --git a/client/shared/lib/os-probes/mounted/10freedos b/client/lib/os-probes/mounted/10freedos similarity index 100% rename from client/shared/lib/os-probes/mounted/10freedos rename to client/lib/os-probes/mounted/10freedos diff --git a/client/shared/lib/os-probes/mounted/10qnx b/client/lib/os-probes/mounted/10qnx similarity index 100% rename from client/shared/lib/os-probes/mounted/10qnx rename to client/lib/os-probes/mounted/10qnx diff --git a/client/shared/lib/os-probes/mounted/20macosx b/client/lib/os-probes/mounted/20macosx similarity index 100% rename from client/shared/lib/os-probes/mounted/20macosx rename to client/lib/os-probes/mounted/20macosx diff --git a/client/shared/lib/os-probes/mounted/20microsoft b/client/lib/os-probes/mounted/20microsoft similarity index 100% rename from client/shared/lib/os-probes/mounted/20microsoft rename to client/lib/os-probes/mounted/20microsoft diff --git a/client/shared/lib/os-probes/mounted/30utility b/client/lib/os-probes/mounted/30utility similarity index 100% rename from client/shared/lib/os-probes/mounted/30utility rename to client/lib/os-probes/mounted/30utility diff --git a/client/shared/lib/os-probes/mounted/40lsb b/client/lib/os-probes/mounted/40lsb similarity index 100% rename from client/shared/lib/os-probes/mounted/40lsb rename to client/lib/os-probes/mounted/40lsb diff --git a/client/shared/lib/os-probes/mounted/70hurd b/client/lib/os-probes/mounted/70hurd similarity index 100% rename from client/shared/lib/os-probes/mounted/70hurd rename to client/lib/os-probes/mounted/70hurd diff --git a/client/shared/lib/os-probes/mounted/80minix b/client/lib/os-probes/mounted/80minix similarity index 100% rename from client/shared/lib/os-probes/mounted/80minix rename to client/lib/os-probes/mounted/80minix diff --git a/client/shared/lib/os-probes/mounted/83haiku b/client/lib/os-probes/mounted/83haiku similarity index 100% rename from client/shared/lib/os-probes/mounted/83haiku rename to client/lib/os-probes/mounted/83haiku diff --git a/client/shared/lib/os-probes/mounted/90linux-distro b/client/lib/os-probes/mounted/90linux-distro similarity index 100% rename from client/shared/lib/os-probes/mounted/90linux-distro rename to client/lib/os-probes/mounted/90linux-distro diff --git a/client/shared/lib/os-probes/mounted/90solaris b/client/lib/os-probes/mounted/90solaris similarity index 100% rename from client/shared/lib/os-probes/mounted/90solaris rename to client/lib/os-probes/mounted/90solaris diff --git a/client/shared/lib/os-probes/mounted/efi/10elilo b/client/lib/os-probes/mounted/efi/10elilo similarity index 100% rename from client/shared/lib/os-probes/mounted/efi/10elilo rename to client/lib/os-probes/mounted/efi/10elilo diff --git a/client/shared/lib/os-probes/mounted/efi/20microsoft b/client/lib/os-probes/mounted/efi/20microsoft similarity index 100% rename from client/shared/lib/os-probes/mounted/efi/20microsoft rename to client/lib/os-probes/mounted/efi/20microsoft diff --git a/client/shared/lib/os-probes/mounted/efi/31part-x-y b/client/lib/os-probes/mounted/efi/31part-x-y similarity index 100% rename from client/shared/lib/os-probes/mounted/efi/31part-x-y rename to client/lib/os-probes/mounted/efi/31part-x-y diff --git a/client/shared/lib/pci.ids b/client/lib/pci.ids similarity index 100% rename from client/shared/lib/pci.ids rename to client/lib/pci.ids diff --git a/client/shared/lib/pictures/oglogo.png b/client/lib/pictures/oglogo.png similarity index 100% rename from client/shared/lib/pictures/oglogo.png rename to client/lib/pictures/oglogo.png diff --git a/client/shared/lib/qtlib/libQtCore.so.4 b/client/lib/qtlib/libQtCore.so.4 similarity index 100% rename from client/shared/lib/qtlib/libQtCore.so.4 rename to client/lib/qtlib/libQtCore.so.4 diff --git a/client/shared/lib/qtlib/libQtGui.so.4 b/client/lib/qtlib/libQtGui.so.4 similarity index 100% rename from client/shared/lib/qtlib/libQtGui.so.4 rename to client/lib/qtlib/libQtGui.so.4 diff --git a/client/shared/lib/qtlib/libQtNetwork.so.4 b/client/lib/qtlib/libQtNetwork.so.4 similarity index 100% rename from client/shared/lib/qtlib/libQtNetwork.so.4 rename to client/lib/qtlib/libQtNetwork.so.4 diff --git a/client/shared/lib/qtlib/libQtWebKit.so.4 b/client/lib/qtlib/libQtWebKit.so.4 similarity index 100% rename from client/shared/lib/qtlib/libQtWebKit.so.4 rename to client/lib/qtlib/libQtWebKit.so.4 diff --git a/client/shared/lib/qtlib/libcrypto.so.1.0.0 b/client/lib/qtlib/libcrypto.so.1.0.0 similarity index 100% rename from client/shared/lib/qtlib/libcrypto.so.1.0.0 rename to client/lib/qtlib/libcrypto.so.1.0.0 diff --git a/client/shared/lib/qtlib/libssl.so.1.0.0 b/client/lib/qtlib/libssl.so.1.0.0 similarity index 100% rename from client/shared/lib/qtlib/libssl.so.1.0.0 rename to client/lib/qtlib/libssl.so.1.0.0 diff --git a/client/shared/lib/qtplugins/imageformats/libqjpeg.so b/client/lib/qtplugins/imageformats/libqjpeg.so similarity index 100% rename from client/shared/lib/qtplugins/imageformats/libqjpeg.so rename to client/lib/qtplugins/imageformats/libqjpeg.so diff --git a/client/shared/scripts/README.es.txt b/client/scripts/README.es.txt similarity index 100% rename from client/shared/scripts/README.es.txt rename to client/scripts/README.es.txt diff --git a/client/shared/scripts/bootLinux b/client/scripts/bootLinux similarity index 100% rename from client/shared/scripts/bootLinux rename to client/scripts/bootLinux diff --git a/client/shared/scripts/bootOs b/client/scripts/bootOs similarity index 100% rename from client/shared/scripts/bootOs rename to client/scripts/bootOs diff --git a/client/shared/scripts/bootOsCustom.template b/client/scripts/bootOsCustom.template similarity index 100% rename from client/shared/scripts/bootOsCustom.template rename to client/scripts/bootOsCustom.template diff --git a/client/shared/scripts/bootWindows b/client/scripts/bootWindows similarity index 100% rename from client/shared/scripts/bootWindows rename to client/scripts/bootWindows diff --git a/client/shared/scripts/buildToOrder b/client/scripts/buildToOrder similarity index 100% rename from client/shared/scripts/buildToOrder rename to client/scripts/buildToOrder diff --git a/client/shared/scripts/cloneRemoteFromMaster b/client/scripts/cloneRemoteFromMaster similarity index 100% rename from client/shared/scripts/cloneRemoteFromMaster rename to client/scripts/cloneRemoteFromMaster diff --git a/client/shared/scripts/configureOs b/client/scripts/configureOs similarity index 100% rename from client/shared/scripts/configureOs rename to client/scripts/configureOs diff --git a/client/shared/scripts/configureOsCustom.template b/client/scripts/configureOsCustom.template similarity index 100% rename from client/shared/scripts/configureOsCustom.template rename to client/scripts/configureOsCustom.template diff --git a/client/shared/scripts/createImage b/client/scripts/createImage similarity index 100% rename from client/shared/scripts/createImage rename to client/scripts/createImage diff --git a/client/shared/scripts/createImageCustom.template b/client/scripts/createImageCustom.template similarity index 100% rename from client/shared/scripts/createImageCustom.template rename to client/scripts/createImageCustom.template diff --git a/client/shared/scripts/createLogicalPartitions b/client/scripts/createLogicalPartitions similarity index 100% rename from client/shared/scripts/createLogicalPartitions rename to client/scripts/createLogicalPartitions diff --git a/client/shared/scripts/createPrimaryPartitions b/client/scripts/createPrimaryPartitions similarity index 100% rename from client/shared/scripts/createPrimaryPartitions rename to client/scripts/createPrimaryPartitions diff --git a/client/shared/scripts/deployImage b/client/scripts/deployImage similarity index 100% rename from client/shared/scripts/deployImage rename to client/scripts/deployImage diff --git a/client/shared/scripts/formatFs b/client/scripts/formatFs similarity index 100% rename from client/shared/scripts/formatFs rename to client/scripts/formatFs diff --git a/client/shared/scripts/generateMenuDefault b/client/scripts/generateMenuDefault similarity index 100% rename from client/shared/scripts/generateMenuDefault rename to client/scripts/generateMenuDefault diff --git a/client/shared/scripts/getFsType b/client/scripts/getFsType similarity index 100% rename from client/shared/scripts/getFsType rename to client/scripts/getFsType diff --git a/client/shared/scripts/getIpAddress b/client/scripts/getIpAddress similarity index 100% rename from client/shared/scripts/getIpAddress rename to client/scripts/getIpAddress diff --git a/client/shared/scripts/getOsVersion b/client/scripts/getOsVersion similarity index 100% rename from client/shared/scripts/getOsVersion rename to client/scripts/getOsVersion diff --git a/client/shared/scripts/grubSyntax b/client/scripts/grubSyntax similarity index 100% rename from client/shared/scripts/grubSyntax rename to client/scripts/grubSyntax diff --git a/client/shared/scripts/initCache b/client/scripts/initCache similarity index 100% rename from client/shared/scripts/initCache rename to client/scripts/initCache diff --git a/client/shared/scripts/installOfflineMode b/client/scripts/installOfflineMode similarity index 100% rename from client/shared/scripts/installOfflineMode rename to client/scripts/installOfflineMode diff --git a/client/shared/scripts/launchOgagentInstaller b/client/scripts/launchOgagentInstaller similarity index 100% rename from client/shared/scripts/launchOgagentInstaller rename to client/scripts/launchOgagentInstaller diff --git a/client/shared/scripts/listHardwareInfo b/client/scripts/listHardwareInfo similarity index 100% rename from client/shared/scripts/listHardwareInfo rename to client/scripts/listHardwareInfo diff --git a/client/shared/scripts/listPartitions b/client/scripts/listPartitions similarity index 100% rename from client/shared/scripts/listPartitions rename to client/scripts/listPartitions diff --git a/client/shared/scripts/listPrimaryPartitions b/client/scripts/listPrimaryPartitions similarity index 100% rename from client/shared/scripts/listPrimaryPartitions rename to client/scripts/listPrimaryPartitions diff --git a/client/shared/scripts/listSoftwareInfo b/client/scripts/listSoftwareInfo similarity index 100% rename from client/shared/scripts/listSoftwareInfo rename to client/scripts/listSoftwareInfo diff --git a/client/shared/scripts/menuBrowser b/client/scripts/menuBrowser similarity index 100% rename from client/shared/scripts/menuBrowser rename to client/scripts/menuBrowser diff --git a/client/shared/scripts/poweroff b/client/scripts/poweroff similarity index 100% rename from client/shared/scripts/poweroff rename to client/scripts/poweroff diff --git a/client/shared/scripts/reboot b/client/scripts/reboot similarity index 100% rename from client/shared/scripts/reboot rename to client/scripts/reboot diff --git a/client/shared/scripts/remoteConsole b/client/scripts/remoteConsole similarity index 100% rename from client/shared/scripts/remoteConsole rename to client/scripts/remoteConsole diff --git a/client/shared/scripts/restoreImage b/client/scripts/restoreImage similarity index 100% rename from client/shared/scripts/restoreImage rename to client/scripts/restoreImage diff --git a/client/shared/scripts/restoreImageCustom.template b/client/scripts/restoreImageCustom.template similarity index 100% rename from client/shared/scripts/restoreImageCustom.template rename to client/scripts/restoreImageCustom.template diff --git a/client/shared/scripts/runAplicationX.sh b/client/scripts/runAplicationX.sh similarity index 100% rename from client/shared/scripts/runAplicationX.sh rename to client/scripts/runAplicationX.sh diff --git a/client/shared/scripts/runhttplog.sh b/client/scripts/runhttplog.sh similarity index 100% rename from client/shared/scripts/runhttplog.sh rename to client/scripts/runhttplog.sh diff --git a/client/shared/scripts/samples/configureGroup b/client/scripts/samples/configureGroup similarity index 100% rename from client/shared/scripts/samples/configureGroup rename to client/scripts/samples/configureGroup diff --git a/client/shared/scripts/samples/firstRunOnceWindows b/client/scripts/samples/firstRunOnceWindows similarity index 100% rename from client/shared/scripts/samples/firstRunOnceWindows rename to client/scripts/samples/firstRunOnceWindows diff --git a/client/shared/scripts/samples/smartPartition b/client/scripts/samples/smartPartition similarity index 100% rename from client/shared/scripts/samples/smartPartition rename to client/scripts/samples/smartPartition diff --git a/client/shared/scripts/sendFileMcast b/client/scripts/sendFileMcast similarity index 100% rename from client/shared/scripts/sendFileMcast rename to client/scripts/sendFileMcast diff --git a/client/shared/scripts/setBootMode b/client/scripts/setBootMode similarity index 100% rename from client/shared/scripts/setBootMode rename to client/scripts/setBootMode diff --git a/client/shared/scripts/updateBootCache b/client/scripts/updateBootCache similarity index 100% rename from client/shared/scripts/updateBootCache rename to client/scripts/updateBootCache diff --git a/client/shared/scripts/updateCache b/client/scripts/updateCache similarity index 100% rename from client/shared/scripts/updateCache rename to client/scripts/updateCache diff --git a/client/shared/README.es.txt b/client/shared/README.es.txt deleted file mode 100644 index fbc03c4..0000000 --- a/client/shared/README.es.txt +++ /dev/null @@ -1,29 +0,0 @@ -OpenGsSys Client README -========================== - - -Este directorio contiene la estructura principal de datos que será -importada por los cleintes OpenGnsys mediante Samba (o NFS en las -primeras versiones del Proyecto). - -Los subdirectorios se copian íntegramente al servidor bajo -/opt/opengnsys/client y serán importados por los clientes en -/opt/opengnsys. - -La estructura de datos es la siguiente: - -- bin scripts o binarios ejecutables por el cliente (compilados - estáticamente). -- cache directorio donde se montará la caché local del cliente. -- etc ficheros de configuración del cliente. -- lib librerías de funciones. - - engine/bin ficheros con las funciones del motor de clonación. - - httpd ficheros de configuración del servicio lighttpd. - - modules módulos extra para el Kernel del cliente. - - qtlib librerías Qt complementarias del Browser. - - qtplugins plugins Qt para el Browser. -- images repositorio de imágenes de sistemas operativos. -- log registro de incidencias de los clientes. -- scripts funciones de alto nivel ejecutables por OpenGnsys Browser - y OpenGnsys Admin. - diff --git a/installer/ogboot_installer.py b/installer/ogboot_installer.py index 68fdca5..ed0b79d 100755 --- a/installer/ogboot_installer.py +++ b/installer/ogboot_installer.py @@ -461,28 +461,14 @@ TFTP_OPTIONS="--secure -v" logger.error(f"Subprocess failed: {e}") logger.error("Continuing with the installation...") -def copyInterfaceAdm(): - hayErrores = 0 - cp_process = subprocess.run(["cp", "-ar", f"{REPO_DIR}/client/interfaceAdm", f"{INSTALL_OGBOOT_TARGET}/client/interfaceAdm"]) - if cp_process.returncode != 0: - logger.error(f"Error while copying Administration Interface Folder") - hayErrores = 1 - return hayErrores - def copyClientFiles(): errstatus = 0 + logger.info(f"Copying OpenGnsys Client files.") - if 0 != subprocess.run (['rsync', '-aH', f'{REPO_DIR}/client/shared/', f'{INSTALL_OGBOOT_TARGET}/client/']).returncode: + if 0 != subprocess.run (['rsync', '-aH', f'{REPO_DIR}/client/', f'{INSTALL_OGBOOT_TARGET}/client/']).returncode: logger.error(f"Error while copying client structure") errstatus = 1 - logger.info(f"Copying OpenGnsys Cloning Engine files in client library.") - os.makedirs(f"{INSTALL_OGBOOT_TARGET}/client/lib/engine/bin", mode=0o775, exist_ok=True) - if 0 != subprocess.run (['rsync', '-aH', f'{REPO_DIR}/client/lib/engine/bin/', f'{INSTALL_OGBOOT_TARGET}/client/lib/engine/bin/']).returncode: - logger.error(f"Error while copying engine files") - errstatus = 1 - - # Creación del archivo ogAdmClient.cfg try: template_path = os.path.join(REPO_DIR, "etc/ogAdmClient.cfg.tmpl") @@ -931,13 +917,6 @@ except Exception as e: logger.error(f"Error installing iPXE: {e}") exit(1) -try: - logger.info("Copy folder Interface between administration and cloning engine") - copyInterfaceAdm() -except Exception as e: - logger.error(f"Error copying Administration Interface: {e}") - exit(1) - try: logger.info("Create the structure of the accesses to the server from the client (shared)") if not copyClientFiles(): From 7387b12cb9055f6ea348ebfe817c1274a099ddf0 Mon Sep 17 00:00:00 2001 From: Natalia Serrano Date: Tue, 25 Feb 2025 11:53:21 +0100 Subject: [PATCH 05/18] refs #1593 add python libs and executables --- client/functions/ogAddCmd | 25 + client/functions/ogAddRegistryKey | 24 + client/functions/ogAddRegistryValue | 25 + client/functions/ogCalculateChecksum | 32 + client/functions/ogCalculateFullChecksum | 32 + client/functions/ogChangeRepo | 23 + client/functions/ogCheckFs | 23 + client/functions/ogCheckIpAddress | 22 + client/functions/ogCleanLinuxDevices | 23 + client/functions/ogCleanOs | 23 + client/functions/ogConfigureFstab | 23 + client/functions/ogConfigureOgagent | 23 + client/functions/ogCopyEfiBootLoader | 23 + client/functions/ogCopyFile | 63 + client/functions/ogCreateCache | 36 + client/functions/ogCreateGptPartitions | 23 + client/functions/ogCreateImage | 37 + client/functions/ogCreateImageSyntax | 25 + client/functions/ogCreatePartitionTable | 30 + client/functions/ogCreatePartitions | 23 + client/functions/ogCreateTorrent | 31 + client/functions/ogDeleteCache | 13 + client/functions/ogDeleteFile | 32 + client/functions/ogDeletePartitionTable | 22 + client/functions/ogDeleteRegistryKey | 24 + client/functions/ogDeleteRegistryValue | 24 + client/functions/ogDeleteTree | 32 + client/functions/ogDevToDisk | 22 + client/functions/ogDiskToDev | 23 + client/functions/ogEcho | 24 + client/functions/ogExecAndLog | 53 + client/functions/ogExtendFs | 23 + client/functions/ogFindCache | 22 + client/functions/ogFixBootSector | 23 + client/functions/ogFormat | 28 + client/functions/ogFormatCache | 22 + client/functions/ogFormatFs | 25 + client/functions/ogGetArch | 22 + client/functions/ogGetCacheSize | 22 + client/functions/ogGetCacheSpace | 13 + client/functions/ogGetCaller | 22 + client/functions/ogGetDiskSize | 22 + client/functions/ogGetDiskType | 22 + client/functions/ogGetEsp | 22 + client/functions/ogGetFreeSize | 24 + client/functions/ogGetFsSize | 24 + client/functions/ogGetFsType | 23 + client/functions/ogGetHivePath | 23 + client/functions/ogGetHostname | 22 + client/functions/ogGetImageCompressor | 23 + client/functions/ogGetImageInfo | 22 + client/functions/ogGetImageProgram | 23 + client/functions/ogGetImageSize | 23 + client/functions/ogGetImageType | 23 + client/functions/ogGetIpAddress | 22 + client/functions/ogGetLastSector | 30 + client/functions/ogGetMacAddress | 22 + client/functions/ogGetMountPoint | 23 + client/functions/ogGetOsType | 23 + client/functions/ogGetOsUuid | 23 + client/functions/ogGetOsVersion | 23 + client/functions/ogGetParentPath | 32 + client/functions/ogGetPartitionActive | 22 + client/functions/ogGetPartitionId | 23 + client/functions/ogGetPartitionSize | 23 + client/functions/ogGetPartitionTableType | 22 + client/functions/ogGetPartitionType | 23 + client/functions/ogGetPartitionsNumber | 22 + client/functions/ogGetPath | 36 + client/functions/ogGetRegistryValue | 24 + client/functions/ogGetRepoIp | 22 + client/functions/ogGetSerialNumber | 17 + client/functions/ogGetServerIp | 22 + client/functions/ogGetSizeParameters | 21 + client/functions/ogGrubInstallMbr | 25 + client/functions/ogGrubInstallPartition | 25 + client/functions/ogHelp | 24 + client/functions/ogHidePartition | 23 + client/functions/ogIdToType | 22 + client/functions/ogInstallFirstBoot | 24 + client/functions/ogInstallMiniSetup | 35 + client/functions/ogInstallRunonce | 29 + client/functions/ogIsDiskLocked | 22 + client/functions/ogIsEfiActive | 22 + client/functions/ogIsFormated | 23 + client/functions/ogIsImageLocked | 23 + client/functions/ogIsLocked | 23 + client/functions/ogIsMounted | 23 + client/functions/ogIsPartitionLocked | 23 + client/functions/ogIsReadonly | 23 + client/functions/ogIsRepoLocked | 17 + client/functions/ogIsVirtualMachine | 22 + client/functions/ogIsWritable | 23 + client/functions/ogListHardwareInfo | 17 + client/functions/ogListLogicalPartitions | 22 + client/functions/ogListPartitions | 22 + client/functions/ogListPrimaryPartitions | 22 + client/functions/ogListRegistryKeys | 25 + client/functions/ogListRegistryValues | 25 + client/functions/ogListSoftware | 24 + client/functions/ogLock | 23 + client/functions/ogLockDisk | 22 + client/functions/ogLockImage | 30 + client/functions/ogLockPartition | 23 + client/functions/ogMakeDir | 32 + client/functions/ogMcastReceiverFile | 39 + client/functions/ogMcastReceiverPartition | 26 + client/functions/ogMcastSendFile | 46 + client/functions/ogMcastSendPartition | 26 + client/functions/ogMcastSyntax | 34 + client/functions/ogMount | 35 + client/functions/ogMountCache | 22 + client/functions/ogMountCdrom | 22 + client/functions/ogMountFirstFs | 22 + client/functions/ogMountFs | 23 + client/functions/ogNvramActiveEntry | 22 + client/functions/ogNvramAddEntry | 24 + client/functions/ogNvramDeleteEntry | 22 + client/functions/ogNvramGetCurrent | 22 + client/functions/ogNvramGetNext | 22 + client/functions/ogNvramGetOrder | 22 + client/functions/ogNvramGetTimeout | 22 + client/functions/ogNvramInactiveEntry | 22 + client/functions/ogNvramList | 22 + client/functions/ogNvramPxeFirstEntry | 22 + client/functions/ogNvramSetNext | 22 + client/functions/ogNvramSetOrder | 16 + client/functions/ogNvramSetTimeout | 22 + client/functions/ogRaiseError | 34 + client/functions/ogReduceFs | 23 + client/functions/ogRestoreEfiBootLoader | 23 + client/functions/ogRestoreImage | 25 + client/functions/ogRestoreImageSyntax | 25 + client/functions/ogSetPartitionActive | 23 + client/functions/ogSetPartitionId | 24 + client/functions/ogSetPartitionSize | 24 + client/functions/ogSetPartitionType | 24 + client/functions/ogSetRegistryValue | 25 + client/functions/ogSetWindowsName | 24 + client/functions/ogTorrentStart | 39 + client/functions/ogTypeToId | 23 + client/functions/ogUcastReceiverPartition | 26 + client/functions/ogUcastSendFile | 39 + client/functions/ogUcastSendPartition | 26 + client/functions/ogUcastSyntax | 34 + client/functions/ogUnhidePartition | 23 + client/functions/ogUninstallLinuxClient | 23 + client/functions/ogUninstallWindowsClient | 24 + client/functions/ogUnlock | 23 + client/functions/ogUnlockDisk | 22 + client/functions/ogUnlockImage | 30 + client/functions/ogUnlockPartition | 23 + client/functions/ogUnmount | 23 + client/functions/ogUnmountAll | 22 + client/functions/ogUnmountCache | 22 + client/functions/ogUnmountFs | 23 + client/functions/ogUnsetDirtyBit | 23 + client/functions/ogUpdateCacheIsNecesary | 24 + client/functions/ogUpdatePartitionTable | 13 + client/functions/ogWindowsBootParameters | 23 + client/functions/ogWindowsRegisterPartition | 26 + client/lib/python3/BootLib.py | 1018 +++++++++++++ client/lib/python3/CacheLib.py | 369 +++++ client/lib/python3/DiskLib.py | 1424 +++++++++++++++++++ client/lib/python3/FileLib.py | 517 +++++++ client/lib/python3/FileSystemLib.py | 906 ++++++++++++ client/lib/python3/ImageLib.py | 839 +++++++++++ client/lib/python3/InventoryLib.py | 531 +++++++ client/lib/python3/NetLib.py | 315 ++++ client/lib/python3/PostConfLib.py | 320 +++++ client/lib/python3/ProtocolLib.py | 973 +++++++++++++ client/lib/python3/RegistryLib.py | 382 +++++ client/lib/python3/StringLib.py | 31 + client/lib/python3/SystemLib.py | 310 ++++ client/lib/python3/ogGlobals.py | 145 ++ 175 files changed, 12074 insertions(+) create mode 100755 client/functions/ogAddCmd create mode 100755 client/functions/ogAddRegistryKey create mode 100755 client/functions/ogAddRegistryValue create mode 100755 client/functions/ogCalculateChecksum create mode 100755 client/functions/ogCalculateFullChecksum create mode 100755 client/functions/ogChangeRepo create mode 100755 client/functions/ogCheckFs create mode 100755 client/functions/ogCheckIpAddress create mode 100755 client/functions/ogCleanLinuxDevices create mode 100755 client/functions/ogCleanOs create mode 100755 client/functions/ogConfigureFstab create mode 100755 client/functions/ogConfigureOgagent create mode 100755 client/functions/ogCopyEfiBootLoader create mode 100755 client/functions/ogCopyFile create mode 100755 client/functions/ogCreateCache create mode 100755 client/functions/ogCreateGptPartitions create mode 100755 client/functions/ogCreateImage create mode 100755 client/functions/ogCreateImageSyntax create mode 100755 client/functions/ogCreatePartitionTable create mode 100755 client/functions/ogCreatePartitions create mode 100755 client/functions/ogCreateTorrent create mode 100755 client/functions/ogDeleteCache create mode 100755 client/functions/ogDeleteFile create mode 100755 client/functions/ogDeletePartitionTable create mode 100755 client/functions/ogDeleteRegistryKey create mode 100755 client/functions/ogDeleteRegistryValue create mode 100755 client/functions/ogDeleteTree create mode 100755 client/functions/ogDevToDisk create mode 100755 client/functions/ogDiskToDev create mode 100755 client/functions/ogEcho create mode 100755 client/functions/ogExecAndLog create mode 100755 client/functions/ogExtendFs create mode 100755 client/functions/ogFindCache create mode 100755 client/functions/ogFixBootSector create mode 100755 client/functions/ogFormat create mode 100755 client/functions/ogFormatCache create mode 100755 client/functions/ogFormatFs create mode 100755 client/functions/ogGetArch create mode 100755 client/functions/ogGetCacheSize create mode 100755 client/functions/ogGetCacheSpace create mode 100755 client/functions/ogGetCaller create mode 100755 client/functions/ogGetDiskSize create mode 100755 client/functions/ogGetDiskType create mode 100755 client/functions/ogGetEsp create mode 100755 client/functions/ogGetFreeSize create mode 100755 client/functions/ogGetFsSize create mode 100755 client/functions/ogGetFsType create mode 100755 client/functions/ogGetHivePath create mode 100755 client/functions/ogGetHostname create mode 100755 client/functions/ogGetImageCompressor create mode 100755 client/functions/ogGetImageInfo create mode 100755 client/functions/ogGetImageProgram create mode 100755 client/functions/ogGetImageSize create mode 100755 client/functions/ogGetImageType create mode 100755 client/functions/ogGetIpAddress create mode 100755 client/functions/ogGetLastSector create mode 100755 client/functions/ogGetMacAddress create mode 100755 client/functions/ogGetMountPoint create mode 100755 client/functions/ogGetOsType create mode 100755 client/functions/ogGetOsUuid create mode 100755 client/functions/ogGetOsVersion create mode 100755 client/functions/ogGetParentPath create mode 100755 client/functions/ogGetPartitionActive create mode 100755 client/functions/ogGetPartitionId create mode 100755 client/functions/ogGetPartitionSize create mode 100755 client/functions/ogGetPartitionTableType create mode 100755 client/functions/ogGetPartitionType create mode 100755 client/functions/ogGetPartitionsNumber create mode 100755 client/functions/ogGetPath create mode 100755 client/functions/ogGetRegistryValue create mode 100755 client/functions/ogGetRepoIp create mode 100755 client/functions/ogGetSerialNumber create mode 100755 client/functions/ogGetServerIp create mode 100755 client/functions/ogGetSizeParameters create mode 100755 client/functions/ogGrubInstallMbr create mode 100755 client/functions/ogGrubInstallPartition create mode 100755 client/functions/ogHelp create mode 100755 client/functions/ogHidePartition create mode 100755 client/functions/ogIdToType create mode 100755 client/functions/ogInstallFirstBoot create mode 100755 client/functions/ogInstallMiniSetup create mode 100755 client/functions/ogInstallRunonce create mode 100755 client/functions/ogIsDiskLocked create mode 100755 client/functions/ogIsEfiActive create mode 100755 client/functions/ogIsFormated create mode 100755 client/functions/ogIsImageLocked create mode 100755 client/functions/ogIsLocked create mode 100755 client/functions/ogIsMounted create mode 100755 client/functions/ogIsPartitionLocked create mode 100755 client/functions/ogIsReadonly create mode 100755 client/functions/ogIsRepoLocked create mode 100755 client/functions/ogIsVirtualMachine create mode 100755 client/functions/ogIsWritable create mode 100755 client/functions/ogListHardwareInfo create mode 100755 client/functions/ogListLogicalPartitions create mode 100755 client/functions/ogListPartitions create mode 100755 client/functions/ogListPrimaryPartitions create mode 100755 client/functions/ogListRegistryKeys create mode 100755 client/functions/ogListRegistryValues create mode 100755 client/functions/ogListSoftware create mode 100755 client/functions/ogLock create mode 100755 client/functions/ogLockDisk create mode 100755 client/functions/ogLockImage create mode 100755 client/functions/ogLockPartition create mode 100755 client/functions/ogMakeDir create mode 100755 client/functions/ogMcastReceiverFile create mode 100755 client/functions/ogMcastReceiverPartition create mode 100755 client/functions/ogMcastSendFile create mode 100755 client/functions/ogMcastSendPartition create mode 100755 client/functions/ogMcastSyntax create mode 100755 client/functions/ogMount create mode 100755 client/functions/ogMountCache create mode 100755 client/functions/ogMountCdrom create mode 100755 client/functions/ogMountFirstFs create mode 100755 client/functions/ogMountFs create mode 100755 client/functions/ogNvramActiveEntry create mode 100755 client/functions/ogNvramAddEntry create mode 100755 client/functions/ogNvramDeleteEntry create mode 100755 client/functions/ogNvramGetCurrent create mode 100755 client/functions/ogNvramGetNext create mode 100755 client/functions/ogNvramGetOrder create mode 100755 client/functions/ogNvramGetTimeout create mode 100755 client/functions/ogNvramInactiveEntry create mode 100755 client/functions/ogNvramList create mode 100755 client/functions/ogNvramPxeFirstEntry create mode 100755 client/functions/ogNvramSetNext create mode 100755 client/functions/ogNvramSetOrder create mode 100755 client/functions/ogNvramSetTimeout create mode 100755 client/functions/ogRaiseError create mode 100755 client/functions/ogReduceFs create mode 100755 client/functions/ogRestoreEfiBootLoader create mode 100755 client/functions/ogRestoreImage create mode 100755 client/functions/ogRestoreImageSyntax create mode 100755 client/functions/ogSetPartitionActive create mode 100755 client/functions/ogSetPartitionId create mode 100755 client/functions/ogSetPartitionSize create mode 100755 client/functions/ogSetPartitionType create mode 100755 client/functions/ogSetRegistryValue create mode 100755 client/functions/ogSetWindowsName create mode 100755 client/functions/ogTorrentStart create mode 100755 client/functions/ogTypeToId create mode 100755 client/functions/ogUcastReceiverPartition create mode 100755 client/functions/ogUcastSendFile create mode 100755 client/functions/ogUcastSendPartition create mode 100755 client/functions/ogUcastSyntax create mode 100755 client/functions/ogUnhidePartition create mode 100755 client/functions/ogUninstallLinuxClient create mode 100755 client/functions/ogUninstallWindowsClient create mode 100755 client/functions/ogUnlock create mode 100755 client/functions/ogUnlockDisk create mode 100755 client/functions/ogUnlockImage create mode 100755 client/functions/ogUnlockPartition create mode 100755 client/functions/ogUnmount create mode 100755 client/functions/ogUnmountAll create mode 100755 client/functions/ogUnmountCache create mode 100755 client/functions/ogUnmountFs create mode 100755 client/functions/ogUnsetDirtyBit create mode 100755 client/functions/ogUpdateCacheIsNecesary create mode 100755 client/functions/ogUpdatePartitionTable create mode 100755 client/functions/ogWindowsBootParameters create mode 100755 client/functions/ogWindowsRegisterPartition create mode 100644 client/lib/python3/BootLib.py create mode 100644 client/lib/python3/CacheLib.py create mode 100644 client/lib/python3/DiskLib.py create mode 100644 client/lib/python3/FileLib.py create mode 100644 client/lib/python3/FileSystemLib.py create mode 100644 client/lib/python3/ImageLib.py create mode 100644 client/lib/python3/InventoryLib.py create mode 100644 client/lib/python3/NetLib.py create mode 100644 client/lib/python3/PostConfLib.py create mode 100644 client/lib/python3/ProtocolLib.py create mode 100644 client/lib/python3/RegistryLib.py create mode 100644 client/lib/python3/StringLib.py create mode 100644 client/lib/python3/SystemLib.py create mode 100644 client/lib/python3/ogGlobals.py diff --git a/client/functions/ogAddCmd b/client/functions/ogAddCmd new file mode 100755 index 0000000..8c0b913 --- /dev/null +++ b/client/functions/ogAddCmd @@ -0,0 +1,25 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from PostConfLib import ogAddCmd + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') +parser.add_argument ('cmdfile') +parser.add_argument ('cmd') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogAddCmd', 'ogAddCmd int_ndisk int_npartition str_filename str_commands', ['ogAddCmd 1 1 filename.cmd command']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogAddCmd (args.disk, args.par, args.cmdfile, args.cmd) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogAddRegistryKey b/client/functions/ogAddRegistryKey new file mode 100755 index 0000000..8b0c59b --- /dev/null +++ b/client/functions/ogAddRegistryKey @@ -0,0 +1,24 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from RegistryLib import ogAddRegistryKey + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('mntpt') +parser.add_argument ('hive') +parser.add_argument ('k') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogAddRegistryKey', 'ogAddRegistryKey path_mountpoint str_hive str_key', [r'ogAddRegistryKey /mnt/sda1 SOFTWARE "\Microsoft\NewKey"']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogAddRegistryKey (args.mntpt, args.hive, args.k) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogAddRegistryValue b/client/functions/ogAddRegistryValue new file mode 100755 index 0000000..d4dd054 --- /dev/null +++ b/client/functions/ogAddRegistryValue @@ -0,0 +1,25 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from RegistryLib import ogAddRegistryValue + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('mntpt') +parser.add_argument ('hive') +parser.add_argument ('k') +parser.add_argument ('vtype', nargs='?', default='STRING') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogAddRegistryValue', 'ogAddRegistryValue path_mountpoint str_hive str_valuename [str_valuetype]', [r'ogAddRegistryValue /mnt/sda1 SOFTWARE "\Microsoft\NewKey\Value1"', r'ogAddRegistryValue /mnt/sda1 SOFTWARE "\Microsoft\NewKey\Value1" DWORD']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogAddRegistryValue (args.mntpt, args.hive, args.k, args.vtype) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogCalculateChecksum b/client/functions/ogCalculateChecksum new file mode 100755 index 0000000..d55da2e --- /dev/null +++ b/client/functions/ogCalculateChecksum @@ -0,0 +1,32 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from FileLib import ogCalculateChecksum + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogCalculateChecksum', 'ogCalculateChecksum [ str_repo | int_ndisk int_npartition ] path_filepath', ['ogCalculateChecksum REPO ubuntu.img']) + sys.exit (0) + +parser = argparse.ArgumentParser (add_help=False) +if 3 == len (sys.argv): + parser.add_argument ('container', nargs='?', default=None) + parser.add_argument ('file', nargs='?', default=None) +elif 4 == len (sys.argv): + parser.add_argument ('disk', nargs='?', default=None) + parser.add_argument ('par', nargs='?', default=None) + parser.add_argument ('file', nargs='?', default=None) + +args = parser.parse_args() + +if 3 == len (sys.argv): + ret = ogCalculateChecksum (container=args.container, file=args.file) +elif 4 == len (sys.argv): + ret = ogCalculateChecksum (container=f'{args.disk} {args.par}', file=args.file) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogCalculateFullChecksum b/client/functions/ogCalculateFullChecksum new file mode 100755 index 0000000..f871f03 --- /dev/null +++ b/client/functions/ogCalculateFullChecksum @@ -0,0 +1,32 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from FileLib import ogCalculateFullChecksum + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogCalculateFullChecksum', 'ogCalculateFullChecksum [ str_repo | int_ndisk int_npartition ] path_filepath', ['ogCalculateFullChecksum REPO ubuntu.img']) + sys.exit (0) + +parser = argparse.ArgumentParser (add_help=False) +if 3 == len (sys.argv): + parser.add_argument ('container', nargs='?', default=None) + parser.add_argument ('file', nargs='?', default=None) +elif 4 == len (sys.argv): + parser.add_argument ('disk', nargs='?', default=None) + parser.add_argument ('par', nargs='?', default=None) + parser.add_argument ('file', nargs='?', default=None) + +args = parser.parse_args() + +if 3 == len (sys.argv): + ret = ogCalculateFullChecksum (container=args.container, file=args.file) +elif 4 == len (sys.argv): + ret = ogCalculateFullChecksum (container=f'{args.disk} {args.par}', file=args.file) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogChangeRepo b/client/functions/ogChangeRepo new file mode 100755 index 0000000..0a65304 --- /dev/null +++ b/client/functions/ogChangeRepo @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from NetLib import ogChangeRepo + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('ip_repo') +parser.add_argument ('og_unit', nargs='?', default=None) + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogChangeRepo', 'ogChangeRepo IPREPO', ['ogChangeRepo 10.1.120.3']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogChangeRepo (args.ip_repo, args.og_unit) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogCheckFs b/client/functions/ogCheckFs new file mode 100755 index 0000000..7eade6a --- /dev/null +++ b/client/functions/ogCheckFs @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from FileSystemLib import ogCheckFs + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogCheckFs', 'ogCheckFs int_ndisk int_nfilesys', ['ogCheckFs 1 1']) + sys.exit (0) + +args = parser.parse_args() +ret = ogCheckFs (args.disk, args.par) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogCheckIpAddress b/client/functions/ogCheckIpAddress new file mode 100755 index 0000000..0e25310 --- /dev/null +++ b/client/functions/ogCheckIpAddress @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from StringLib import ogCheckIpAddress + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('ip') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogCheckIpAddress') + sys.exit (0) + +args = parser.parse_args() + +ret = ogCheckIpAddress (args.ip) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogCleanLinuxDevices b/client/functions/ogCleanLinuxDevices new file mode 100755 index 0000000..7603084 --- /dev/null +++ b/client/functions/ogCleanLinuxDevices @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from BootLib import ogCleanLinuxDevices + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogCleanLinuxDevices', 'ogCleanLinuxDevices int_ndisk int_nfilesys', ['ogCleanLinuxDevices 1 1']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogCleanLinuxDevices (args.disk, args.par) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogCleanOs b/client/functions/ogCleanOs new file mode 100755 index 0000000..e2309c6 --- /dev/null +++ b/client/functions/ogCleanOs @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from PostConfLib import ogCleanOs + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogCleanOs', 'ogCleanOs int_ndisk int_nfilesys', ['ogCleanOs 1 1']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogCleanOs (args.disk, args.par) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogConfigureFstab b/client/functions/ogConfigureFstab new file mode 100755 index 0000000..03d351d --- /dev/null +++ b/client/functions/ogConfigureFstab @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from BootLib import ogConfigureFstab + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogConfigureFstab', 'ogConfigureFstab int_ndisk int_nfilesys', ['ogConfigureFstab 1 1']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogConfigureFstab (args.disk, args.par) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogConfigureOgagent b/client/functions/ogConfigureOgagent new file mode 100755 index 0000000..7ed9fcd --- /dev/null +++ b/client/functions/ogConfigureOgagent @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from PostConfLib import ogConfigureOgagent + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogConfigureOgagent', 'ogConfigureOgagent int_ndisk int_filesys', ['ogConfigureOgagent 1 1']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogConfigureOgagent (args.disk, args.par) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogCopyEfiBootLoader b/client/functions/ogCopyEfiBootLoader new file mode 100755 index 0000000..41d5d9b --- /dev/null +++ b/client/functions/ogCopyEfiBootLoader @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from UEFILib import ogCopyEfiBootLoader + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogCopyEfiBootLoader', 'ogCopyEfiBootLoader int_ndisk int_part', ['ogCopyEfiBootLoader 1 2']) + sys.exit (0) + +args = parser.parse_args() +ret = ogCopyEfiBootLoader (args.disk, args.par) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogCopyFile b/client/functions/ogCopyFile new file mode 100755 index 0000000..04f1784 --- /dev/null +++ b/client/functions/ogCopyFile @@ -0,0 +1,63 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from FileLib import ogCopyFile + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogCopyFile', 'ogCopyFile [ str_repo | int_ndisk int_npartition ] path_source [ str_repo | int_ndisk int_npartition ] path_target', ['ogCopyFile REPO newfile.txt 1 2 /tmp/newfile.txt']) + sys.exit (0) + +parser = argparse.ArgumentParser (add_help=False) +if 5 == len (sys.argv): + parser.add_argument ('src_container') + parser.add_argument ('src_file') + parser.add_argument ('dst_container') + parser.add_argument ('dst_file') + args = parser.parse_args() + src = { 'container': args.src_container, 'file': args.src_file } + dst = { 'container': args.dst_container, 'file': args.dst_file } +elif 7 == len (sys.argv): + parser.add_argument ('src_disk') + parser.add_argument ('src_par') + parser.add_argument ('src_file') + parser.add_argument ('dst_disk') + parser.add_argument ('dst_par') + parser.add_argument ('dst_file') + args = parser.parse_args() + src = { 'disk': args.src_disk, 'par': args.src_par, 'file': args.src_file } + dst = { 'disk': args.dst_disk, 'par': args.dst_par, 'file': args.dst_file } +elif 6 == len (sys.argv): + ## can be either: + ## - src_disk src_par src_file dst_container dst_file + ## - src_container src_file dst_disk dst_par dst_file + + try: + num = int (sys.argv[1]) ## raises ValueError if sys.argv[1] doesn't look like a number + ## "src_disk src_par src_file dst_container dst_file" + parser.add_argument ('src_disk') + parser.add_argument ('src_par') + parser.add_argument ('src_file') + parser.add_argument ('dst_container') + parser.add_argument ('dst_file') + args = parser.parse_args() + src = { 'disk': args.src_disk, 'par': args.src_par, 'file': args.src_file } + dst = { 'container': args.dst_container, 'file': args.dst_file } + except: + ## "src_container src_file dst_disk dst_par dst_file" + parser.add_argument ('src_container') + parser.add_argument ('src_file') + parser.add_argument ('dst_disk') + parser.add_argument ('dst_par') + parser.add_argument ('dst_file') + args = parser.parse_args() + src = { 'container': args.src_container, 'file': args.src_file } + dst = { 'disk': args.dst_disk, 'par': args.dst_par, 'file': args.dst_file } + +ret = ogCopyFile (src, dst) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogCreateCache b/client/functions/ogCreateCache new file mode 100755 index 0000000..62a97e5 --- /dev/null +++ b/client/functions/ogCreateCache @@ -0,0 +1,36 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from CacheLib import ogCreateCache + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogCreateCache', 'ogCreateCache [int_ndisk [int_npart]] int_partsize', ['ogCreateCache 10000000', 'ogCreateCache 1 10000000', 'ogCreateCache 1 4 10000000']) + sys.exit (0) + +parser = argparse.ArgumentParser (add_help=False) +if 2 == len (sys.argv): + parser.add_argument ('sizecache') +elif 3 == len (sys.argv): + parser.add_argument ('disk') + parser.add_argument ('sizecache') +elif 4 == len (sys.argv): + parser.add_argument ('disk') + parser.add_argument ('part') + parser.add_argument ('sizecache') + +args = parser.parse_args() + +if 2 == len (sys.argv): + ret = ogCreateCache (sizecache=args.sizecache) +elif 3 == len (sys.argv): + ret = ogCreateCache (ndsk=args.disk, sizecache=args.sizecache) +elif 4 == len (sys.argv): + ret = ogCreateCache (ndsk=int(args.disk), part=int(args.part), sizecache=int(args.sizecache)) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogCreateGptPartitions b/client/functions/ogCreateGptPartitions new file mode 100755 index 0000000..0259325 --- /dev/null +++ b/client/functions/ogCreateGptPartitions @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from DiskLib import ogCreateGptPartitions + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogCreateGptPartitions', 'ogCreateGptPartitions int_ndisk str_parttype:int_partsize ...', ['ogCreateGptPartitions 1 NTFS:10000000 EXT3:5000000 LINUX-SWAP:1000000']) + sys.exit (0) + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('parts', nargs='+') +args = parser.parse_args() + +ret = ogCreateGptPartitions (args.disk, args.parts) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogCreateImage b/client/functions/ogCreateImage new file mode 100755 index 0000000..14c9709 --- /dev/null +++ b/client/functions/ogCreateImage @@ -0,0 +1,37 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from ImageLib import ogCreateImage + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogCreateImage', 'ogCreateImage int_ndisk int_npart str_repo path_image', ['ogCreateImage 1 1 REPO /aula1/win7']) + sys.exit (0) + +parser = argparse.ArgumentParser (add_help=False) +if 5 == len (sys.argv): + parser.add_argument ('disk') + parser.add_argument ('par') + parser.add_argument ('container') + parser.add_argument ('imgfile') +elif 7 == len (sys.argv): + parser.add_argument ('disk') + parser.add_argument ('par') + parser.add_argument ('container') + parser.add_argument ('imgfile') + parser.add_argument ('tool') + parser.add_argument ('level') + +args = parser.parse_args() + +if 5 == len (sys.argv): + ret = ogCreateImage (args.disk, args.par, args.container, args.imgfile) +elif 7 == len (sys.argv): + ret = ogCreateImage (args.disk, args.par, args.container, args.imgfile, args.tool, args.level) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogCreateImageSyntax b/client/functions/ogCreateImageSyntax new file mode 100755 index 0000000..bc3651b --- /dev/null +++ b/client/functions/ogCreateImageSyntax @@ -0,0 +1,25 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from ImageLib import ogCreateImageSyntax + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('dev') +parser.add_argument ('imgfile') +parser.add_argument ('tool', nargs='?', default='partclone') +parser.add_argument ('level', nargs='?', default='gzip') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogCreateImageSyntax', 'ogCreateImageSyntax path_device path_imagefile [str_tool] [str_compressionlevel]', ['ogCreateImageSyntax /dev/sda1 /opt/opengnsys/images/prueba.img partclone lzop', 'ogCreateImageSyntax /dev/sda1 /opt/opengnsys/images/prueba.img']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogCreateImageSyntax (args.dev, args.imgfile, args.tool, args.level) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogCreatePartitionTable b/client/functions/ogCreatePartitionTable new file mode 100755 index 0000000..415c10c --- /dev/null +++ b/client/functions/ogCreatePartitionTable @@ -0,0 +1,30 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from DiskLib import ogCreatePartitionTable + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogCreatePartitionTable', 'ogCreatePartitionTable int_ndisk [str_partype]', ['ogCreatePartitionTable 1 GPT', 'ogCreatePartitionTable 1']) + sys.exit (0) + +parser = argparse.ArgumentParser (add_help=False) +if 2 == len (sys.argv): + parser.add_argument ('disk') +elif 3 == len (sys.argv): + parser.add_argument ('disk') + parser.add_argument ('createptt') + +args = parser.parse_args() + +if 2 == len (sys.argv): + ret = ogCreatePartitionTable (args.disk) +elif 3 == len (sys.argv): + ret = ogCreatePartitionTable (args.disk, args.createptt) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogCreatePartitions b/client/functions/ogCreatePartitions new file mode 100755 index 0000000..8269a53 --- /dev/null +++ b/client/functions/ogCreatePartitions @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from DiskLib import ogCreatePartitions + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogCreatePartitions', 'ogCreatePartitions int_ndisk str_parttype:int_partsize ...', ['ogCreatePartitions 1 NTFS:10000000 EXT3:5000000 LINUX-SWAP:1000000']) + sys.exit (0) + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('parts', nargs='+') +args = parser.parse_args() + +ret = ogCreatePartitions (args.disk, args.parts) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogCreateTorrent b/client/functions/ogCreateTorrent new file mode 100755 index 0000000..409bd31 --- /dev/null +++ b/client/functions/ogCreateTorrent @@ -0,0 +1,31 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from ProtocolLib import ogCreateTorrent + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogCreateTorrent', 'ogCreateTorrent [str_REPOSITORY] [int_ndisk int_npart] Relative_path_file IpBttrack', ['ogCreateTorrent 1 1 /aula1/winxp 10.1.15.23', 'ogCreateTorrent REPO /aula1/winxp 10.1.15.45']) + sys.exit (0) + +parser = argparse.ArgumentParser (add_help=False) +if 4 == len (sys.argv): + parser.add_argument ('container') +elif 5 == len (sys.argv): + parser.add_argument ('disk') + parser.add_argument ('par') + +parser.add_argument ('file') +parser.add_argument ('ip_bttrack') +args = parser.parse_args() + +if 4 == len (sys.argv): + ret = ogCreateTorrent (container=args.container, file=args.file, ip_bttrack=args.ip_bttrack) +elif 5 == len (sys.argv): + ret = ogCreateTorrent (disk=args.disk, par=args.par, file=args.file, ip_bttrack=args.ip_bttrack) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogDeleteCache b/client/functions/ogDeleteCache new file mode 100755 index 0000000..80914b8 --- /dev/null +++ b/client/functions/ogDeleteCache @@ -0,0 +1,13 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from CacheLib import ogDeleteCache + +ret = ogDeleteCache() + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogDeleteFile b/client/functions/ogDeleteFile new file mode 100755 index 0000000..32d4763 --- /dev/null +++ b/client/functions/ogDeleteFile @@ -0,0 +1,32 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from FileLib import ogDeleteFile + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogDeleteFile', 'ogDeleteFile [ str_repo | int_ndisk int_npartition ] path_file', ['ogDeleteFile 1 2 /tmp/newfile.txt']) + sys.exit (0) + +parser = argparse.ArgumentParser (add_help=False) +if 3 == len (sys.argv): + parser.add_argument ('container', nargs='?', default=None) + parser.add_argument ('file', nargs='?', default=None) +elif 4 == len (sys.argv): + parser.add_argument ('disk', nargs='?', default=None) + parser.add_argument ('par', nargs='?', default=None) + parser.add_argument ('file', nargs='?', default=None) + +args = parser.parse_args() + +if 3 == len (sys.argv): + ret = ogDeleteFile (container=args.container, file=args.file) +elif 4 == len (sys.argv): + ret = ogDeleteFile (container=f'{args.disk} {args.par}', file=args.file) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogDeletePartitionTable b/client/functions/ogDeletePartitionTable new file mode 100755 index 0000000..5d35b5d --- /dev/null +++ b/client/functions/ogDeletePartitionTable @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from DiskLib import ogDeletePartitionTable + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogDeletePartitionTable', 'ogDeletePartitionTable int_ndisk', ['ogDeletePartitionTable 1']) + sys.exit (0) + +args = parser.parse_args() +ret = ogDeletePartitionTable (args.disk) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogDeleteRegistryKey b/client/functions/ogDeleteRegistryKey new file mode 100755 index 0000000..3eba2e1 --- /dev/null +++ b/client/functions/ogDeleteRegistryKey @@ -0,0 +1,24 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from RegistryLib import ogDeleteRegistryKey + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('mntpt') +parser.add_argument ('hive') +parser.add_argument ('k') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogDeleteRegistryKey', 'ogDeleteRegistryKey path_mountpoint str_hive str_key', [r'ogDeleteRegistryKey /mnt/sda1 SOFTWARE "\Microsoft\NewKey"']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogDeleteRegistryKey (args.mntpt, args.hive, args.k) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogDeleteRegistryValue b/client/functions/ogDeleteRegistryValue new file mode 100755 index 0000000..7721bfa --- /dev/null +++ b/client/functions/ogDeleteRegistryValue @@ -0,0 +1,24 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from RegistryLib import ogDeleteRegistryValue + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('mntpt') +parser.add_argument ('hive') +parser.add_argument ('k') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogDeleteRegistryValue', 'ogDeleteRegistryValue path_mountpoint str_hive str_valuename', [r'ogDeleteRegistryValue /mnt/sda1 SOFTWARE "\Microsoft\NewKey\Value1"']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogDeleteRegistryValue (args.mntpt, args.hive, args.k) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogDeleteTree b/client/functions/ogDeleteTree new file mode 100755 index 0000000..527b1a5 --- /dev/null +++ b/client/functions/ogDeleteTree @@ -0,0 +1,32 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from FileLib import ogDeleteTree + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogDeleteTree', 'ogDeleteTree [ str_repo | int_ndisk int_npartition ] path_dir', ['ogDeleteTree 1 2 /tmp/newdir']) + sys.exit (0) + +parser = argparse.ArgumentParser (add_help=False) +if 3 == len (sys.argv): + parser.add_argument ('container', nargs='?', default=None) + parser.add_argument ('file', nargs='?', default=None) +elif 4 == len (sys.argv): + parser.add_argument ('disk', nargs='?', default=None) + parser.add_argument ('par', nargs='?', default=None) + parser.add_argument ('file', nargs='?', default=None) + +args = parser.parse_args() + +if 3 == len (sys.argv): + ret = ogDeleteTree (container=args.container, file=args.file) +elif 4 == len (sys.argv): + ret = ogDeleteTree (container=f'{args.disk} {args.par}', file=args.file) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogDevToDisk b/client/functions/ogDevToDisk new file mode 100755 index 0000000..b3d6fea --- /dev/null +++ b/client/functions/ogDevToDisk @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from DiskLib import ogDevToDisk + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('arg_dev') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogDevToDisk', 'ogDevToDisk path_device | LABEL=str_label | UUID=str_uuid', ['ogDevToDisk /dev/sda', 'ogDevToDisk /dev/sda1', 'ogDevToDisk LABEL=CACHE']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogDevToDisk (args.arg_dev) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogDiskToDev b/client/functions/ogDiskToDev new file mode 100755 index 0000000..d12736d --- /dev/null +++ b/client/functions/ogDiskToDev @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from DiskLib import ogDiskToDev + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('arg_disk', nargs='?', default=None) +parser.add_argument ('arg_part', nargs='?', default=None) + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogDiskToDev', 'ogDiskToDev int_ndisk [int_npartition]', ['ogDiskToDev', 'ogDiskToDev 1', 'ogDiskToDev 1 1']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogDiskToDev (args.arg_disk, args.arg_part) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogEcho b/client/functions/ogEcho new file mode 100755 index 0000000..14d8bf5 --- /dev/null +++ b/client/functions/ogEcho @@ -0,0 +1,24 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from SystemLib import ogEcho + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('logtypes') +parser.add_argument ('loglevel') +parser.add_argument ('msg') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogEcho') + sys.exit (0) + +args = parser.parse_args() + +ret = ogEcho (args.logtypes, args.loglevel, args.msg) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogExecAndLog b/client/functions/ogExecAndLog new file mode 100755 index 0000000..977f6b4 --- /dev/null +++ b/client/functions/ogExecAndLog @@ -0,0 +1,53 @@ +#!/usr/bin/python3 + +import sys +## lo siento, pero tiene que ser así +from BootLib import * +from CacheLib import * +from DiskLib import * +from FileLib import * +from FileSystemLib import * +from ImageLib import * +from InventoryLib import * +from NetLib import * +from PostConfLib import * +from ProtocolLib import * +from RegistryLib import * +from StringLib import * +from SystemLib import * +from UEFILib import * + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogExecAndLog', 'ogExecAndLog str_logfile ... str_command ...', ['ogExecAndLog COMMAND ls -al /']) + sys.exit (0) + +logtypes = [] +while True: + if sys.argv[1] in ['command', 'log', 'session']: + logtypes.append (sys.argv.pop (1)) + else: break + +fun_name = sys.argv.pop (1) +try: + fun = locals()[fun_name] +except KeyError: + print (f'not implemented: {fun_name}') + sys.exit (1) + +args = [] +kwargs = {} +for arg in sys.argv[1:]: + if '=' in arg: + k, v = arg.split ('=') + kwargs[k] = v + else: + args.append (arg) + +## args has to be a tuple +args = tuple (args) + +ret = ogExecAndLog (logtypes, fun, *args, **kwargs) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) diff --git a/client/functions/ogExtendFs b/client/functions/ogExtendFs new file mode 100755 index 0000000..b706e87 --- /dev/null +++ b/client/functions/ogExtendFs @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from FileSystemLib import ogExtendFs + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogExtendFs', 'ogExtendFs int_ndisk int_nfilesys', ['ogExtendFs 1 1']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogExtendFs (args.disk, args.par) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogFindCache b/client/functions/ogFindCache new file mode 100755 index 0000000..181872d --- /dev/null +++ b/client/functions/ogFindCache @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from CacheLib import ogFindCache + +parser = argparse.ArgumentParser (add_help=False) + + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogFindCache', 'ogFindCache') + sys.exit (0) + +args = parser.parse_args() + +ret = ogFindCache () +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogFixBootSector b/client/functions/ogFixBootSector new file mode 100755 index 0000000..3e9ecb5 --- /dev/null +++ b/client/functions/ogFixBootSector @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from BootLib import ogFixBootSector + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogFixBootSector', 'ogFixBootSector int_ndisk int_partition', ['ogFixBootSector 1 1']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogFixBootSector (args.disk, args.par) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogFormat b/client/functions/ogFormat new file mode 100755 index 0000000..cc955a2 --- /dev/null +++ b/client/functions/ogFormat @@ -0,0 +1,28 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from FileSystemLib import ogFormat +from CacheLib import ogFormatCache + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogFormat', 'ogFormat int_ndisk int_nfilesys [str_label]', ['ogFormat 1 1', 'ogFormat 1 1 EXT4', 'ogFormat 1 1 DATA', 'ogFormat 1 1 EXT4 DATA']) + sys.exit (0) + +if 2 == len (sys.argv) and 'cache' == sys.argv[1]: + ret = ogFormatCache() +else: + parser = argparse.ArgumentParser (add_help=False) + parser.add_argument ('disk') + parser.add_argument ('par') + parser.add_argument ('fs', nargs='?', default=None) + parser.add_argument ('label', nargs='?', default=None) + args = parser.parse_args() + ret = ogFormat (args.disk, args.par, args.fs, args.label) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogFormatCache b/client/functions/ogFormatCache new file mode 100755 index 0000000..375d8b2 --- /dev/null +++ b/client/functions/ogFormatCache @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from CacheLib import ogFormatCache + +parser = argparse.ArgumentParser (add_help=False) + + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogFormatCache', 'ogFormatCache') + sys.exit (0) + +args = parser.parse_args() + +ret = ogFormatCache () +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogFormatFs b/client/functions/ogFormatFs new file mode 100755 index 0000000..babe6ef --- /dev/null +++ b/client/functions/ogFormatFs @@ -0,0 +1,25 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from FileSystemLib import ogFormatFs + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') +parser.add_argument ('type', nargs='?', default=None) +parser.add_argument ('label', nargs='?', default=None) + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogFormatFs', 'ogFormatFs int_ndisk int_nfilesys [str_label]', ['ogFormatFs 1 1', 'ogFormatFs 1 1 EXT4', 'ogFormatFs 1 1 DATA', 'ogFormatFs 1 1 EXT4 DATA']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogFormatFs (args.disk, args.par, args.type, args.label) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogGetArch b/client/functions/ogGetArch new file mode 100755 index 0000000..bf24319 --- /dev/null +++ b/client/functions/ogGetArch @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from InventoryLib import ogGetArch + +parser = argparse.ArgumentParser (add_help=False) + + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogGetArch') + sys.exit (0) + +args = parser.parse_args() + +ret = ogGetArch () +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogGetCacheSize b/client/functions/ogGetCacheSize new file mode 100755 index 0000000..51a73dc --- /dev/null +++ b/client/functions/ogGetCacheSize @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from CacheLib import ogGetCacheSize + +parser = argparse.ArgumentParser (add_help=False) + + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogGetCacheSize', 'ogGetCacheSize') + sys.exit (0) + +args = parser.parse_args() + +ret = ogGetCacheSize () +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogGetCacheSpace b/client/functions/ogGetCacheSpace new file mode 100755 index 0000000..2e5dce3 --- /dev/null +++ b/client/functions/ogGetCacheSpace @@ -0,0 +1,13 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from CacheLib import ogGetCacheSpace + +ret = ogGetCacheSpace() + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogGetCaller b/client/functions/ogGetCaller new file mode 100755 index 0000000..2147fb2 --- /dev/null +++ b/client/functions/ogGetCaller @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from SystemLib import ogGetCaller + +parser = argparse.ArgumentParser (add_help=False) + + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogGetCaller') + sys.exit (0) + +args = parser.parse_args() + +ret = ogGetCaller () +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogGetDiskSize b/client/functions/ogGetDiskSize new file mode 100755 index 0000000..ea7c42e --- /dev/null +++ b/client/functions/ogGetDiskSize @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from DiskLib import ogGetDiskSize + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogGetDiskSize', 'ogGetDiskSize int_ndisk', ['ogGetDiskSize 1']) + sys.exit (0) + +args = parser.parse_args() +ret = ogGetDiskSize (args.disk) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogGetDiskType b/client/functions/ogGetDiskType new file mode 100755 index 0000000..022482f --- /dev/null +++ b/client/functions/ogGetDiskType @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from DiskLib import ogGetDiskType + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('dev') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogGetDiskType', 'ogGetDiskType path_device', ['ogGetDiskType /dev/sdb']) + sys.exit (0) + +args = parser.parse_args() +ret = ogGetDiskType (args.dev) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogGetEsp b/client/functions/ogGetEsp new file mode 100755 index 0000000..3431956 --- /dev/null +++ b/client/functions/ogGetEsp @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from DiskLib import ogGetEsp + +parser = argparse.ArgumentParser (add_help=False) + + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogGetEsp') + sys.exit (0) + +args = parser.parse_args() + +ret = ogGetEsp () +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogGetFreeSize b/client/functions/ogGetFreeSize new file mode 100755 index 0000000..5f1899e --- /dev/null +++ b/client/functions/ogGetFreeSize @@ -0,0 +1,24 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from FileSystemLib import ogGetFreeSize + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('part') +parser.add_argument ('unit', nargs='?', default='KB') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogGetFreeSize', 'ogGetFreeSize int_disco int_partition str_SizeOutput [ kB MB GB ]', ['ogGetFreeSize 1 1 MB']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogGetFreeSize (args.disk, args.part, args.unit) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogGetFsSize b/client/functions/ogGetFsSize new file mode 100755 index 0000000..9227810 --- /dev/null +++ b/client/functions/ogGetFsSize @@ -0,0 +1,24 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from FileSystemLib import ogGetFsSize + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') +parser.add_argument ('unit', nargs='?', default='KB') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogGetFsSize', 'ogGetFsSize int_ndisk int_npartition [str_unit]', ['ogGetFsSize 1 1', 'ogGetFsSize 1 1 KB']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogGetFsSize (args.disk, args.par, args.unit) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogGetFsType b/client/functions/ogGetFsType new file mode 100755 index 0000000..d89b09f --- /dev/null +++ b/client/functions/ogGetFsType @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from FileSystemLib import ogGetFsType + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('part') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogGetFsType', 'ogGetFsType int_ndisk int_nfilesys', ['ogGetFsType 1 1']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogGetFsType (args.disk, args.part) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogGetHivePath b/client/functions/ogGetHivePath new file mode 100755 index 0000000..ebac741 --- /dev/null +++ b/client/functions/ogGetHivePath @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from RegistryLib import ogGetHivePath + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('mntpt') +parser.add_argument ('hive') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogGetHivePath', 'ogGetHivePath path_mountpoint [str_hive|str_user]', ['ogGetHivePath /mnt/sda1 SOFTWARE', 'ogGetHivePath /mnt/sda1 user1']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogGetHivePath (args.mntpt, args.hive) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogGetHostname b/client/functions/ogGetHostname new file mode 100755 index 0000000..d042a3c --- /dev/null +++ b/client/functions/ogGetHostname @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from NetLib import ogGetHostname + +parser = argparse.ArgumentParser (add_help=False) + + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogGetHostname') + sys.exit (0) + +args = parser.parse_args() + +ret = ogGetHostname () +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogGetImageCompressor b/client/functions/ogGetImageCompressor new file mode 100755 index 0000000..7b21fb3 --- /dev/null +++ b/client/functions/ogGetImageCompressor @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from ImageLib import ogGetImageCompressor + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('container') +parser.add_argument ('filename') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogGetImageCompressor', 'ogGetImageCompressor str_repo path_image', ['ogGetImageCompressor REPO prueba']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogGetImageCompressor (args.container, args.filename) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogGetImageInfo b/client/functions/ogGetImageInfo new file mode 100755 index 0000000..f227f2c --- /dev/null +++ b/client/functions/ogGetImageInfo @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from ImageLib import ogGetImageInfo + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('imgfile') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogGetImageInfo', 'ogGetImageInfo path_filename', ['ogGetImageInfo /opt/opengnsys/images/prueba.img']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogGetImageInfo (args.imgfile) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogGetImageProgram b/client/functions/ogGetImageProgram new file mode 100755 index 0000000..80b2a15 --- /dev/null +++ b/client/functions/ogGetImageProgram @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from ImageLib import ogGetImageProgram + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('container') +parser.add_argument ('filename') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogGetImageProgram', 'ogGetImageProgram str_repo path_image', ['ogGetImageProgram REPO prueba']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogGetImageProgram (args.container, args.filename) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogGetImageSize b/client/functions/ogGetImageSize new file mode 100755 index 0000000..da45ec8 --- /dev/null +++ b/client/functions/ogGetImageSize @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from ImageLib import ogGetImageSize + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('repo') +parser.add_argument ('imgname') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogGetImageSize', 'ogGetImageSize str_repo path_image', ['ogGetImageSize REPO prueba']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogGetImageSize (args.repo, args.imgname) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogGetImageType b/client/functions/ogGetImageType new file mode 100755 index 0000000..304f030 --- /dev/null +++ b/client/functions/ogGetImageType @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from ImageLib import ogGetImageType + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('repo') +parser.add_argument ('imgname') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogGetImageType', 'ogGetImageType str_repo path_image', ['ogGetImageType REPO prueba']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogGetImageType (args.repo, args.imgname) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogGetIpAddress b/client/functions/ogGetIpAddress new file mode 100755 index 0000000..4a3e976 --- /dev/null +++ b/client/functions/ogGetIpAddress @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from NetLib import ogGetIpAddress + +parser = argparse.ArgumentParser (add_help=False) + + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogGetIpAddress') + sys.exit (0) + +args = parser.parse_args() + +ret = ogGetIpAddress () +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogGetLastSector b/client/functions/ogGetLastSector new file mode 100755 index 0000000..4723660 --- /dev/null +++ b/client/functions/ogGetLastSector @@ -0,0 +1,30 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from DiskLib import ogGetLastSector + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogGetLastSector', 'ogGetLastSector int_ndisk [int_npart]', ['ogGetLastSector 1'], ['ogGetLastSector 1 1']) + sys.exit (0) + +parser = argparse.ArgumentParser (add_help=False) +if 2 == len (sys.argv): + parser.add_argument ('disk') +elif 3 == len (sys.argv): + parser.add_argument ('disk') + parser.add_argument ('par') + +args = parser.parse_args() + +if 2 == len (sys.argv): + ret = ogGetLastSector (args.disk) +elif 3 == len (sys.argv): + ret = ogGetLastSector (args.disk, args.par) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogGetMacAddress b/client/functions/ogGetMacAddress new file mode 100755 index 0000000..b3585f4 --- /dev/null +++ b/client/functions/ogGetMacAddress @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from NetLib import ogGetMacAddress + +parser = argparse.ArgumentParser (add_help=False) + + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogGetMacAddress') + sys.exit (0) + +args = parser.parse_args() + +ret = ogGetMacAddress () +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogGetMountPoint b/client/functions/ogGetMountPoint new file mode 100755 index 0000000..9cb0625 --- /dev/null +++ b/client/functions/ogGetMountPoint @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from FileSystemLib import ogGetMountPoint + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogGetMountPoint', 'ogGetMountPoint int_ndisk int_nfilesys', ['ogGetMountPoint 1 1']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogGetMountPoint (args.disk, args.par) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogGetOsType b/client/functions/ogGetOsType new file mode 100755 index 0000000..77a6f29 --- /dev/null +++ b/client/functions/ogGetOsType @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from InventoryLib import ogGetOsType + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('partition') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogGetOsType', 'ogGetOsType int_ndisk int_npartition', ['ogGetOsType 1 2']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogGetOsType (args.disk, args.partition) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogGetOsUuid b/client/functions/ogGetOsUuid new file mode 100755 index 0000000..306d96c --- /dev/null +++ b/client/functions/ogGetOsUuid @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from InventoryLib import ogGetOsUuid + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogGetOsUuid', 'ogGetOsUuid int_ndisk int_nfilesys', ['ogGetOsUuid 1 2']) + sys.exit (0) + +args = parser.parse_args() +ret = ogGetOsUuid (args.disk, args.par) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogGetOsVersion b/client/functions/ogGetOsVersion new file mode 100755 index 0000000..a91fec6 --- /dev/null +++ b/client/functions/ogGetOsVersion @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from InventoryLib import ogGetOsVersion + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('part') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogGetOsVersion', 'ogGetOsVersion int_ndisk int_nfilesys', ['ogGetOsVersion 1 2']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogGetOsVersion (args.disk, args.part) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogGetParentPath b/client/functions/ogGetParentPath new file mode 100755 index 0000000..3051970 --- /dev/null +++ b/client/functions/ogGetParentPath @@ -0,0 +1,32 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from FileLib import ogGetParentPath + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogGetParentPath', 'ogGetParentPath [ str_repo | int_ndisk int_npartition ] path_filepath', ['ogGetParentPath /mnt/sda1/windows/system32', 'ogGetPath REPO /etc/fstab', 'ogGetPath 1 1 /windows/system32']) + sys.exit (0) + +parser = argparse.ArgumentParser (add_help=False) +if 3 == len (sys.argv): + parser.add_argument ('src', nargs='?', default=None) + parser.add_argument ('file', nargs='?', default=None) +elif 4 == len (sys.argv): + parser.add_argument ('disk', nargs='?', default=None) + parser.add_argument ('par', nargs='?', default=None) + parser.add_argument ('file', nargs='?', default=None) + +args = parser.parse_args() + +if 3 == len (sys.argv): + ret = ogGetParentPath (src=args.src, file=args.file) +elif 4 == len (sys.argv): + ret = ogGetParentPath (src=f'{args.disk} {args.par}', file=args.file) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogGetPartitionActive b/client/functions/ogGetPartitionActive new file mode 100755 index 0000000..ce516bc --- /dev/null +++ b/client/functions/ogGetPartitionActive @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from DiskLib import ogGetPartitionActive + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogGetPartitionActive', 'ogGetPartitionActive int_ndisk', ['ogGetPartitionActive 1']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogGetPartitionActive (args.disk) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogGetPartitionId b/client/functions/ogGetPartitionId new file mode 100755 index 0000000..dfcae9b --- /dev/null +++ b/client/functions/ogGetPartitionId @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from DiskLib import ogGetPartitionId + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogGetPartitionId', 'ogGetPartitionId int_ndisk int_npartition', ['ogGetPartitionId 1 1']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogGetPartitionId (args.disk, args.par) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogGetPartitionSize b/client/functions/ogGetPartitionSize new file mode 100755 index 0000000..0ea7767 --- /dev/null +++ b/client/functions/ogGetPartitionSize @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from DiskLib import ogGetPartitionSize + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogGetPartitionSize', 'ogGetPartitionSize int_ndisk int_npartition', ['ogGetPartitionSize 1 1']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogGetPartitionSize (args.disk, args.par) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogGetPartitionTableType b/client/functions/ogGetPartitionTableType new file mode 100755 index 0000000..262ad62 --- /dev/null +++ b/client/functions/ogGetPartitionTableType @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from DiskLib import ogGetPartitionTableType + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogGetPartitionTableType', 'ogGetPartitionTableType int_ndisk', ['ogGetPartitionTableType 1']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogGetPartitionTableType (args.disk) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogGetPartitionType b/client/functions/ogGetPartitionType new file mode 100755 index 0000000..51e6644 --- /dev/null +++ b/client/functions/ogGetPartitionType @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from DiskLib import ogGetPartitionType + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogGetPartitionType', 'ogGetPartitionType int_ndisk int_nfilesys', ['ogGetPartitionType 1 1']) + sys.exit (0) + +args = parser.parse_args() +ret = ogGetPartitionType (args.disk, args.par) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogGetPartitionsNumber b/client/functions/ogGetPartitionsNumber new file mode 100755 index 0000000..de6bbe7 --- /dev/null +++ b/client/functions/ogGetPartitionsNumber @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from DiskLib import ogGetPartitionsNumber + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogGetPartitionsNumber', 'ogGetPartitionsNumber int_ndisk', ['ogGetPartitionsNumber 1']) + sys.exit (0) + +args = parser.parse_args() +ret = ogGetPartitionsNumber (args.disk) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogGetPath b/client/functions/ogGetPath new file mode 100755 index 0000000..d40cc5e --- /dev/null +++ b/client/functions/ogGetPath @@ -0,0 +1,36 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from FileLib import ogGetPath + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogGetPath', 'ogGetPath [ str_repo | int_ndisk int_npartition ] path_filepath', ['ogGetPath /mnt/sda1/windows/system32', 'ogGetPath REPO /etc/fstab', 'ogGetPath 1 1 /windows/system32']) + sys.exit (0) + +parser = argparse.ArgumentParser (add_help=False) +if 2 == len (sys.argv): + parser.add_argument ('file', nargs='?', default=None) +elif 3 == len (sys.argv): + parser.add_argument ('src', nargs='?', default=None) + parser.add_argument ('file', nargs='?', default=None) +elif 4 == len (sys.argv): + parser.add_argument ('disk', nargs='?', default=None) + parser.add_argument ('par', nargs='?', default=None) + parser.add_argument ('file', nargs='?', default=None) + +args = parser.parse_args() + +if 2 == len (sys.argv): + ret = ogGetPath (file=args.file) +elif 3 == len (sys.argv): + ret = ogGetPath (src=args.src, file=args.file) +elif 4 == len (sys.argv): + ret = ogGetPath (src=f'{args.disk} {args.par}', file=args.file) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogGetRegistryValue b/client/functions/ogGetRegistryValue new file mode 100755 index 0000000..c3645a5 --- /dev/null +++ b/client/functions/ogGetRegistryValue @@ -0,0 +1,24 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from RegistryLib import ogGetRegistryValue + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('mntpt') +parser.add_argument ('hive') +parser.add_argument ('k') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogGetRegistryValue', 'ogGetRegistryValue path_mountpoint str_hive str_valuename', [r'ogGetRegistryValue /mnt/sda1 SOFTWARE "\Microsoft\NewKey\Value1"']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogGetRegistryValue (args.mntpt, args.hive, args.k) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogGetRepoIp b/client/functions/ogGetRepoIp new file mode 100755 index 0000000..6d89ff6 --- /dev/null +++ b/client/functions/ogGetRepoIp @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from NetLib import ogGetRepoIp + +parser = argparse.ArgumentParser (add_help=False) + + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogGetRepoIp') + sys.exit (0) + +args = parser.parse_args() + +ret = ogGetRepoIp () +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogGetSerialNumber b/client/functions/ogGetSerialNumber new file mode 100755 index 0000000..dc02c8e --- /dev/null +++ b/client/functions/ogGetSerialNumber @@ -0,0 +1,17 @@ +#!/usr/bin/python3 + +import sys +from SystemLib import ogHelp +from InventoryLib import ogGetSerialNumber + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogGetSerialNumber', 'ogGetSerialNumber', ['ogGetSerialNumber']) + sys.exit (0) + +ret = ogGetSerialNumber() + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogGetServerIp b/client/functions/ogGetServerIp new file mode 100755 index 0000000..7f23a8f --- /dev/null +++ b/client/functions/ogGetServerIp @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from NetLib import ogGetServerIp + +parser = argparse.ArgumentParser (add_help=False) + + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogGetServerIp') + sys.exit (0) + +args = parser.parse_args() + +ret = ogGetServerIp () +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogGetSizeParameters b/client/functions/ogGetSizeParameters new file mode 100755 index 0000000..951f7a8 --- /dev/null +++ b/client/functions/ogGetSizeParameters @@ -0,0 +1,21 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from ImageLib import ogGetSizeParameters + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') +parser.add_argument ('container') +parser.add_argument ('imgname') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogGetSizeParameters', 'ogGetSizeParameters num_disk num_part str_repo path_imgname', ['read SIZEDATA SIZEREQUIRED SIZEFREE ISENOUGHSPACE <<< $(ogGetSizeParameters 1 2 REPO Windows10)', 'read SIZEDATA SIZEREQUIRED SIZEFREE ISENOUGHSPACE <<< $(ogGetSizeParameters 1 6 CACHE Ubuntu16']) + sys.exit (0) + +args = parser.parse_args() +sizedata, sizerequired, sizefree, isenoughspace = ogGetSizeParameters (args.disk, args.par, args.container, args.imgname) +print (' '.join ([str(sizedata), str(sizerequired), str(sizefree), str(isenoughspace)])) diff --git a/client/functions/ogGrubInstallMbr b/client/functions/ogGrubInstallMbr new file mode 100755 index 0000000..97a7497 --- /dev/null +++ b/client/functions/ogGrubInstallMbr @@ -0,0 +1,25 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from BootLib import ogGrubInstallMbr + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') +parser.add_argument ('checkos', nargs='?', default='FALSE') +parser.add_argument ('kernelparam', nargs='?', default='') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogGrubInstallMbr', 'ogGrubInstallMbr int_ndiskSecondStage int_partitionSecondStage bolean_Configure_2ndStage "param param"', ['ogGrubInstallMbr 1 1 FALSE', 'ogGrubInstallMbr 1 1 TRUE "nomodeset irqpoll pci=noacpi quiet splash"']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogGrubInstallMbr (args.disk, args.par, args.checkos, args.kernelparam) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogGrubInstallPartition b/client/functions/ogGrubInstallPartition new file mode 100755 index 0000000..eca057a --- /dev/null +++ b/client/functions/ogGrubInstallPartition @@ -0,0 +1,25 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from BootLib import ogGrubInstallPartition + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') +parser.add_argument ('checkos', nargs='?', default='FALSE') +parser.add_argument ('kernelparam', nargs='?', default='') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogGrubInstallPartition', 'ogGrubInstallPartition int_ndiskSecondStage int_partitionSecondStage bolean_Configure_2ndStage "param param"', ['ogGrubInstallPartition 1 1 FALSE', 'ogGrubInstallPartition 1 1 TRUE "nomodeset irqpoll pci=noacpi quiet splash"']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogGrubInstallPartition (args.disk, args.par, args.checkos, args.kernelparam) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogHelp b/client/functions/ogHelp new file mode 100755 index 0000000..e26329f --- /dev/null +++ b/client/functions/ogHelp @@ -0,0 +1,24 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from SystemLib import ogHelp + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('fname') +parser.add_argument ('fmt', nargs='?', default=None) +parser.add_argument ('examples', nargs='?', default=[]) + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogHelp') + sys.exit (0) + +args = parser.parse_args() + +ret = ogHelp (args.fname, args.fmt, args.examples) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogHidePartition b/client/functions/ogHidePartition new file mode 100755 index 0000000..2a97ce1 --- /dev/null +++ b/client/functions/ogHidePartition @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from DiskLib import ogHidePartition + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogHidePartition', 'ogHidePartition int_ndisk int_npartition', ['ogHidePartition 1 1']) + sys.exit (0) + +args = parser.parse_args() +ret = ogHidePartition (args.disk, args.par) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogIdToType b/client/functions/ogIdToType new file mode 100755 index 0000000..50bec6e --- /dev/null +++ b/client/functions/ogIdToType @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from DiskLib import ogIdToType + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('id') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogIdToType', 'ogIdToType int_idpart', ['ogIdToType 83']) + sys.exit (0) + +args = parser.parse_args() +ret = ogIdToType (args.id) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogInstallFirstBoot b/client/functions/ogInstallFirstBoot new file mode 100755 index 0000000..ea54b00 --- /dev/null +++ b/client/functions/ogInstallFirstBoot @@ -0,0 +1,24 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from PostConfLib import ogInstallFirstBoot + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') +parser.add_argument ('cmdfile') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogInstallFirstBoot', 'ogInstallFirstBoot int_ndisk int_npartition str_filename', ['ogInstallFirstBoot 1 1 filename.cmd']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogInstallFirstBoot (args.disk, args.par, args.cmdfile) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogInstallMiniSetup b/client/functions/ogInstallMiniSetup new file mode 100755 index 0000000..4ffcec0 --- /dev/null +++ b/client/functions/ogInstallMiniSetup @@ -0,0 +1,35 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from PostConfLib import ogInstallMiniSetup + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogInstallMiniSetup', 'MSG_SEE ogInstallFirstBoot ogInstallRunonce') + sys.exit (0) + +parser = argparse.ArgumentParser (add_help=False) +if 4 == len (sys.argv): + parser.add_argument ('disk') + parser.add_argument ('par') + parser.add_argument ('cmdfile') + args = parser.parse_args() + ret = ogInstallMiniSetup (args.disk, args.par, args.cmdfile) +else: + parser.add_argument ('disk') + parser.add_argument ('par') + parser.add_argument ('cmdfile') + parser.add_argument ('user', nargs='?', default=None) + parser.add_argument ('pwd', nargs='?', default=None) + parser.add_argument ('autologin', nargs='?', default=False) + parser.add_argument ('userauto', nargs='?', default=None) + parser.add_argument ('pwdauto', nargs='?', default=None) + args = parser.parse_args() + ret = ogInstallMiniSetup (args.disk, args.par, args.cmdfile, args.user, args.pwd, args.autologin, args.userauto, args.pwdauto) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogInstallRunonce b/client/functions/ogInstallRunonce new file mode 100755 index 0000000..5d52354 --- /dev/null +++ b/client/functions/ogInstallRunonce @@ -0,0 +1,29 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from PostConfLib import ogInstallRunonce + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') +parser.add_argument ('cmdfile') +parser.add_argument ('user') +parser.add_argument ('pwd') +parser.add_argument ('autologin') +parser.add_argument ('userauto', nargs='?', default=None) +parser.add_argument ('pwdauto', nargs='?', default=None) + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogInstallRunonce', 'ogInstallRunonce int_ndisk int_npartition str_filename str_adm_user str_adm_password bool_autologin [str_auto_user str_auto_password]', ['ogInstallRunonce 1 1 filename.cmd administrator passadmin 1 userauto passuserauto', 'ogInstallRunonce 1 1 filename.cmd administrator passadmin 0']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogInstallRunonce (args.disk, args.par, args.cmdfile, args.user, args.pwd, args.autologin, args.userauto, args.pwdauto) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogIsDiskLocked b/client/functions/ogIsDiskLocked new file mode 100755 index 0000000..25ebde8 --- /dev/null +++ b/client/functions/ogIsDiskLocked @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from DiskLib import ogIsDiskLocked + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogIsDiskLocked', 'ogIsDiskLocked int_ndisk', ['ogIsDiskLocked 1']) + sys.exit (0) + +args = parser.parse_args() +ret = ogIsDiskLocked (args.disk) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogIsEfiActive b/client/functions/ogIsEfiActive new file mode 100755 index 0000000..099ef7b --- /dev/null +++ b/client/functions/ogIsEfiActive @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from InventoryLib import ogIsEfiActive + +parser = argparse.ArgumentParser (add_help=False) + + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogIsEfiActive') + sys.exit (0) + +args = parser.parse_args() + +ret = ogIsEfiActive () +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogIsFormated b/client/functions/ogIsFormated new file mode 100755 index 0000000..3ab17f0 --- /dev/null +++ b/client/functions/ogIsFormated @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from FileSystemLib import ogIsFormated + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('part') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogIsFormated', 'ogIsFormated int_ndisk int_nfilesys', ['ogIsFormated 1 1']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogIsFormated (args.disk, args.part) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogIsImageLocked b/client/functions/ogIsImageLocked new file mode 100755 index 0000000..5d9bdb6 --- /dev/null +++ b/client/functions/ogIsImageLocked @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from ImageLib import ogIsImageLocked + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('container', nargs='?', default=None) +parser.add_argument ('imgfile', nargs='?', default=None) + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogIsImageLocked', 'ogIsImageLocked [str_repo] path_image', ['ogIsImageLocked /opt/opengnsys/images/aula1/win7.img', 'ogIsImageLocked REPO /aula1/win7.img']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogIsImageLocked (args.container, args.imgfile) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogIsLocked b/client/functions/ogIsLocked new file mode 100755 index 0000000..6523cf8 --- /dev/null +++ b/client/functions/ogIsLocked @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from FileSystemLib import ogIsLocked + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogIsLocked', 'ogIsLocked int_ndisk int_nfilesys', ['ogIsLocked 1 1']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogIsLocked (args.disk, args.par) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogIsMounted b/client/functions/ogIsMounted new file mode 100755 index 0000000..1158a65 --- /dev/null +++ b/client/functions/ogIsMounted @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from FileSystemLib import ogIsMounted + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogIsMounted', 'ogIsMounted int_ndisk int_nfilesys', ['ogIsMounted 1 1']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogIsMounted (args.disk, args.par) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogIsPartitionLocked b/client/functions/ogIsPartitionLocked new file mode 100755 index 0000000..f340264 --- /dev/null +++ b/client/functions/ogIsPartitionLocked @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from FileSystemLib import ogIsPartitionLocked + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogIsPartitionLocked', 'ogIsPartitionLocked int_ndisk int_nfilesys', ['ogIsPartitionLocked 1 1']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogIsPartitionLocked (args.disk, args.par) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogIsReadonly b/client/functions/ogIsReadonly new file mode 100755 index 0000000..6a20026 --- /dev/null +++ b/client/functions/ogIsReadonly @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from FileSystemLib import ogIsReadonly + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogIsReadonly', 'ogIsReadonly int_ndisk int_nfilesys', ['ogIsReadonly 1 1']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogIsReadonly (args.disk, args.par) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogIsRepoLocked b/client/functions/ogIsRepoLocked new file mode 100755 index 0000000..aad48d2 --- /dev/null +++ b/client/functions/ogIsRepoLocked @@ -0,0 +1,17 @@ +#!/usr/bin/python3 + +import sys +from SystemLib import ogHelp +from SystemLib import ogIsRepoLocked + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogIsRepoLocked', 'ogIsRepoLocked', ['ogIsRepoLocked']) + sys.exit (0) + +ret = ogIsRepoLocked() + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogIsVirtualMachine b/client/functions/ogIsVirtualMachine new file mode 100755 index 0000000..9592da3 --- /dev/null +++ b/client/functions/ogIsVirtualMachine @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from SystemLib import ogIsVirtualMachine + +parser = argparse.ArgumentParser (add_help=False) + + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogIsVirtualMachine') + sys.exit (0) + +args = parser.parse_args() + +ret = ogIsVirtualMachine () +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogIsWritable b/client/functions/ogIsWritable new file mode 100755 index 0000000..53e1314 --- /dev/null +++ b/client/functions/ogIsWritable @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from FileSystemLib import ogIsWritable + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogIsWritable', 'ogIsWritable int_ndisk int_nfilesys', ['ogIsWritable 1 1']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogIsWritable (args.disk, args.par) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogListHardwareInfo b/client/functions/ogListHardwareInfo new file mode 100755 index 0000000..2eaa493 --- /dev/null +++ b/client/functions/ogListHardwareInfo @@ -0,0 +1,17 @@ +#!/usr/bin/python3 + +import sys +from SystemLib import ogHelp +from InventoryLib import ogListHardwareInfo + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogListHardwareInfo', 'ogListHardwareInfo', ['ogListHardwareInfo']) + sys.exit (0) + +ret = ogListHardwareInfo() + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogListLogicalPartitions b/client/functions/ogListLogicalPartitions new file mode 100755 index 0000000..c79a006 --- /dev/null +++ b/client/functions/ogListLogicalPartitions @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from DiskLib import ogListLogicalPartitions + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogListLogicalPartitions', 'ogListLogicalPartitions int_ndisk', ['ogListLogicalPartitions 1']) + sys.exit (0) + +args = parser.parse_args() +ret = ogListLogicalPartitions (args.disk) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (' '.join (ret)) diff --git a/client/functions/ogListPartitions b/client/functions/ogListPartitions new file mode 100755 index 0000000..ca0479b --- /dev/null +++ b/client/functions/ogListPartitions @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from DiskLib import ogListPartitions + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogListPartitions', 'ogListPartitions int_ndisk', ['ogListPartitions 1']) + sys.exit (0) + +args = parser.parse_args() +ret = ogListPartitions (args.disk) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (' '.join (ret)) diff --git a/client/functions/ogListPrimaryPartitions b/client/functions/ogListPrimaryPartitions new file mode 100755 index 0000000..045289f --- /dev/null +++ b/client/functions/ogListPrimaryPartitions @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from DiskLib import ogListPrimaryPartitions + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogListPrimaryPartitions', 'ogListPrimaryPartitions int_ndisk', ['ogListPrimaryPartitions 1']) + sys.exit (0) + +args = parser.parse_args() +ret = ogListPrimaryPartitions (args.disk) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (' '.join (ret)) diff --git a/client/functions/ogListRegistryKeys b/client/functions/ogListRegistryKeys new file mode 100755 index 0000000..c56d829 --- /dev/null +++ b/client/functions/ogListRegistryKeys @@ -0,0 +1,25 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from RegistryLib import ogListRegistryKeys + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('mntpt') +parser.add_argument ('hive') +parser.add_argument ('k') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogListRegistryKeys', 'ogListRegistryKeys path_mountpoint str_hive str_key', [r'ogListRegistryKeys /mnt/sda1 SOFTWARE "\Microsoft\Windows\CurrentVersion"']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogListRegistryKeys (args.mntpt, args.hive, args.k) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: + for k in ret: print (k) diff --git a/client/functions/ogListRegistryValues b/client/functions/ogListRegistryValues new file mode 100755 index 0000000..b6dcc78 --- /dev/null +++ b/client/functions/ogListRegistryValues @@ -0,0 +1,25 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from RegistryLib import ogListRegistryValues + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('mntpt') +parser.add_argument ('hive') +parser.add_argument ('k') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogListRegistryValues', 'ogListRegistryValues path_mountpoint str_hive str_key', [r'ogListRegistryValues /mnt/sda1 SOFTWARE "\Microsoft\Windows\CurrentVersion"']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogListRegistryValues (args.mntpt, args.hive, args.k) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: + for k in ret: print (k) diff --git a/client/functions/ogListSoftware b/client/functions/ogListSoftware new file mode 100755 index 0000000..75abb9f --- /dev/null +++ b/client/functions/ogListSoftware @@ -0,0 +1,24 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from InventoryLib import ogListSoftware + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogListSoftware', 'ogListSoftware int_disk int_partition', ['ogListSoftware 1 1']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogListSoftware (args.disk, args.par) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: + for k in ret: print (k) diff --git a/client/functions/ogLock b/client/functions/ogLock new file mode 100755 index 0000000..8bb795f --- /dev/null +++ b/client/functions/ogLock @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from FileSystemLib import ogLock + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogLock', 'ogLock int_ndisk int_nfilesys', ['ogLock 1 1']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogLock (args.disk, args.par) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogLockDisk b/client/functions/ogLockDisk new file mode 100755 index 0000000..1b1e640 --- /dev/null +++ b/client/functions/ogLockDisk @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from DiskLib import ogLockDisk + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogLockDisk', 'ogLockDisk int_ndisk', ['ogLockDisk 1']) + sys.exit (0) + +args = parser.parse_args() +ret = ogLockDisk (args.disk) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogLockImage b/client/functions/ogLockImage new file mode 100755 index 0000000..0c8f505 --- /dev/null +++ b/client/functions/ogLockImage @@ -0,0 +1,30 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from ImageLib import ogLockImage + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogLockImage', 'ogLockImage [str_repo] path_image', ['ogLockImage /opt/opengnsys/images/aula1/win7.img', 'ogLockImage REPO /aula1/win7.img']) + sys.exit (0) + +parser = argparse.ArgumentParser (add_help=False) +if 2 == len (sys.argv): + parser.add_argument ('file') +elif 3 == len (sys.argv): + parser.add_argument ('container') + parser.add_argument ('file') + +args = parser.parse_args() + +if 2 == len (sys.argv): + ret = ogLockImage (imgfile=args.file) +elif 3 == len (sys.argv): + ret = ogLockImage (container=args.container, imgfile=args.file) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogLockPartition b/client/functions/ogLockPartition new file mode 100755 index 0000000..e83da43 --- /dev/null +++ b/client/functions/ogLockPartition @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from FileSystemLib import ogLockPartition + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogLockPartition', 'ogLockPartition int_ndisk int_nfilesys', ['ogLockPartition 1 1']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogLockPartition (args.disk, args.par) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogMakeDir b/client/functions/ogMakeDir new file mode 100755 index 0000000..b12f1c1 --- /dev/null +++ b/client/functions/ogMakeDir @@ -0,0 +1,32 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from FileLib import ogMakeDir + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogMakeDir', 'ogMakeDir [ str_repo | int_ndisk int_npartition ] path_dir', ['ogMakeDir 1 2 /tmp/newdir']) + sys.exit (0) + +parser = argparse.ArgumentParser (add_help=False) +if 3 == len (sys.argv): + parser.add_argument ('container', nargs='?', default=None) + parser.add_argument ('file', nargs='?', default=None) +elif 4 == len (sys.argv): + parser.add_argument ('disk', nargs='?', default=None) + parser.add_argument ('par', nargs='?', default=None) + parser.add_argument ('file', nargs='?', default=None) + +args = parser.parse_args() + +if 3 == len (sys.argv): + ret = ogMakeDir (container=args.container, file=args.file) +elif 4 == len (sys.argv): + ret = ogMakeDir (container=f'{args.disk} {args.par}', file=args.file) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogMcastReceiverFile b/client/functions/ogMcastReceiverFile new file mode 100755 index 0000000..2b33ac2 --- /dev/null +++ b/client/functions/ogMcastReceiverFile @@ -0,0 +1,39 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from ProtocolLib import ogMcastReceiverFile + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogMcastReceiverFile', 'ogMcastReceiverFile [ str_portMcast] [ [Relative_path_file] | [str_REPOSITORY path_file] | [int_ndisk int_npart path_file ] ]', ['ogMcastReceiverFile 9000 /PS1_PH1.img', 'ogMcastReceiverFile 9000 CACHE /aula1/PS2_PH4.img', 'ogMcastReceiverFile 9000 1 1 /isos/linux.iso']) + sys.exit (0) + +parser = argparse.ArgumentParser (add_help=False) +if 3 == len (sys.argv): + parser.add_argument ('sess', nargs='?', default=None) + parser.add_argument ('file', nargs='?', default=None) +elif 4 == len (sys.argv): + parser.add_argument ('sess', nargs='?', default=None) + parser.add_argument ('container', nargs='?', default=None) + parser.add_argument ('file', nargs='?', default=None) +elif 5 == len (sys.argv): + parser.add_argument ('sess', nargs='?', default=None) + parser.add_argument ('disk', nargs='?', default=None) + parser.add_argument ('par', nargs='?', default=None) + parser.add_argument ('file', nargs='?', default=None) + +args = parser.parse_args() + +if 3 == len (sys.argv): + ret = ogMcastReceiverFile (file=args.file, sess=args.sess) +elif 4 == len (sys.argv): + ret = ogMcastReceiverFile (container=args.container, file=args.file, sess=args.sess) +elif 5 == len (sys.argv): + ret = ogMcastReceiverFile (disk=args.disk, par=args.par, file=args.file, sess=args.sess) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogMcastReceiverPartition b/client/functions/ogMcastReceiverPartition new file mode 100755 index 0000000..45a8763 --- /dev/null +++ b/client/functions/ogMcastReceiverPartition @@ -0,0 +1,26 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from ProtocolLib import ogMcastReceiverPartition + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') +parser.add_argument ('sess') +parser.add_argument ('tool') +parser.add_argument ('compressor') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogMcastReceiverPartition', 'ogMcastReceiverPartition int_ndisk int_npart SessionMulticastCLIENT tools compresor', ['ogMcastReceiverPartition 1 1 9000 partclone lzop']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogMcastReceiverPartition (args.disk, args.par, args.sess, args.tool, args.compressor) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogMcastSendFile b/client/functions/ogMcastSendFile new file mode 100755 index 0000000..7ed75ed --- /dev/null +++ b/client/functions/ogMcastSendFile @@ -0,0 +1,46 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from ProtocolLib import ogMcastSendFile + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk', nargs='?', default=None) +parser.add_argument ('par', nargs='?', default=None) +parser.add_argument ('container', nargs='?', default=None) +parser.add_argument ('file', nargs='?', default=None) +parser.add_argument ('sess', nargs='?', default=None) + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogMcastSendFile', 'ogMcastSendFile [str_REPOSITORY] [int_ndisk int_npart] /Relative_path_file sesionMcast', ['ogMcastSendFile 1 1 /aula1/winxp.img sesionMcast', 'ogMcastSendFile REPO /aula1/ubuntu.iso sesionMcast', 'ogMcastSendFile CACHE /aula1/winxp.img sesionMcast', 'ogMcastSendFile /opt/opengnsys/images/aula1/hd500.vmx sesionMcast']) + sys.exit (0) + +parser = argparse.ArgumentParser (add_help=False) +if 3 == len (sys.argv): + parser.add_argument ('file', nargs='?', default=None) + parser.add_argument ('sess', nargs='?', default=None) +elif 4 == len (sys.argv): + parser.add_argument ('container', nargs='?', default=None) + parser.add_argument ('file', nargs='?', default=None) + parser.add_argument ('sess', nargs='?', default=None) +elif 5 == len (sys.argv): + parser.add_argument ('disk', nargs='?', default=None) + parser.add_argument ('par', nargs='?', default=None) + parser.add_argument ('file', nargs='?', default=None) + parser.add_argument ('sess', nargs='?', default=None) + +args = parser.parse_args() + +if 3 == len (sys.argv): + ret = ogMcastSendFile (file=args.file, sess=args.sess) +elif 4 == len (sys.argv): + ret = ogMcastSendFile (container=args.container, file=args.file, sess=args.sess) +elif 5 == len (sys.argv): + ret = ogMcastSendFile (disk=args.disk, par=args.par, file=args.file, sess=args.sess) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogMcastSendPartition b/client/functions/ogMcastSendPartition new file mode 100755 index 0000000..7c8ea6e --- /dev/null +++ b/client/functions/ogMcastSendPartition @@ -0,0 +1,26 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from ProtocolLib import ogMcastSendPartition + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') +parser.add_argument ('sess') +parser.add_argument ('tool') +parser.add_argument ('compressor') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogMcastSendPartition', 'ogMcastSendPartition int_ndisk int_npart SessionMulticastSERVER tools compresor', ['ogMcastSendPartition 1 1 9000:full-duplex:239.194.37.31:50M:20:2 partclone lzop']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogMcastSendPartition (args.disk, args.par, args.sess, args.tool, args.compressor) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogMcastSyntax b/client/functions/ogMcastSyntax new file mode 100755 index 0000000..a595498 --- /dev/null +++ b/client/functions/ogMcastSyntax @@ -0,0 +1,34 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from ProtocolLib import ogMcastSyntax + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogMcastSyntax', 'ogMcastSyntax params', ['ogMcastSyntax SENDPARTITION str_sessionSERVER str_device str_tools str_level', 'ogMcastSyntax RECEIVERPARTITION str_sessionCLIENT str_device str_tools str_level', 'ogMcastSyntax SENDFILE str_sessionSERVER str_file', 'ogMcastSyntax RECEIVERFILE str_sessionCLIENT str_file', 'sessionServer syntax: portbase:method:mcastaddress:speed:nclients:ntimeWaitingUntilNclients', 'sessionServer example: 9000:full-duplex|half-duplex|broadcast:239.194.17.36:80M:50:60', 'sessionClient syntax: portbase', 'sessionClient example: 9000', 'sessionClient syntax: portbase:serverIP:TimeOut_session:TimeOut_transmision', 'sessionClient example: 9000:172.17.88.161:40:120']) + sys.exit (0) + +kwargs = {} +parser = argparse.ArgumentParser (add_help=False) +if 'SENDPARTITION' == sys.argv[1] or 'RECEIVERPARTITION' == sys.argv[1]: + parser.add_argument ('op') + parser.add_argument ('sess') + parser.add_argument ('device') + parser.add_argument ('tool') + parser.add_argument ('level') + args = parser.parse_args() + kwargs = { 'device': args.device, 'tool': args.tool, 'level': args.level } +elif 'SENDFILE' == sys.argv[1] or 'RECEIVERFILE' == sys.argv[1]: + parser.add_argument ('op') + parser.add_argument ('sess') + parser.add_argument ('file') + args = parser.parse_args() + kwargs = { 'file': args.file } + +ret = ogMcastSyntax (args.op, args.sess, **kwargs) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogMount b/client/functions/ogMount new file mode 100755 index 0000000..2a92f44 --- /dev/null +++ b/client/functions/ogMount @@ -0,0 +1,35 @@ +#!/usr/bin/python3 + +## this script doesn't call ogMount. It replicates the functionality instead. + +import sys +import argparse +from SystemLib import ogHelp +from CacheLib import ogMountCache +from FileSystemLib import ogMountCdrom, ogMountFs + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogMount') + sys.exit (0) + +parser = argparse.ArgumentParser (add_help=False) +done = False +if 2 == len (sys.argv): + if 'cache' == sys.argv[1].lower(): + done = True + ret = ogMountCache() + elif 'cdrom' == sys.argv[1].lower(): + done = True + ret = ogMountCdrom() + +if not done: + parser.add_argument ('disk') + parser.add_argument ('par') + args = parser.parse_args() + ret = ogMountFs (args.disk, args.par) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogMountCache b/client/functions/ogMountCache new file mode 100755 index 0000000..8dd7578 --- /dev/null +++ b/client/functions/ogMountCache @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from CacheLib import ogMountCache + +parser = argparse.ArgumentParser (add_help=False) + + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogMountCache', 'ogMountCache') + sys.exit (0) + +args = parser.parse_args() + +ret = ogMountCache () +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogMountCdrom b/client/functions/ogMountCdrom new file mode 100755 index 0000000..24db5f1 --- /dev/null +++ b/client/functions/ogMountCdrom @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from FileSystemLib import ogMountCdrom + +parser = argparse.ArgumentParser (add_help=False) + + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogMountCdrom', 'ogMountCdrom', ['ogMountCdrom']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogMountCdrom () +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogMountFirstFs b/client/functions/ogMountFirstFs new file mode 100755 index 0000000..61f324d --- /dev/null +++ b/client/functions/ogMountFirstFs @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from FileSystemLib import ogMountFirstFs + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogMountFirstFs', 'ogMountFirstFs int_ndisk', ['ogMountFirstFs 1']) + sys.exit (0) + +args = parser.parse_args() +ret = ogMountFirstFs (args.disk) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogMountFs b/client/functions/ogMountFs new file mode 100755 index 0000000..2e2bdcf --- /dev/null +++ b/client/functions/ogMountFs @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from FileSystemLib import ogMountFs + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogMountFs', 'ogMountFs int_ndisk int_nfilesys', ['ogMountFs 1 1']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogMountFs (args.disk, args.par) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogNvramActiveEntry b/client/functions/ogNvramActiveEntry new file mode 100755 index 0000000..9eee20a --- /dev/null +++ b/client/functions/ogNvramActiveEntry @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from UEFILib import ogNvramActiveEntry + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('entry') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogNvramActiveEntry', 'ogNvramActiveEntry [ Num_order_entry | Label_entry ]', ['ogNvramActiveEntry 2', 'ogNvramActiveEntry "Windows Boot Manager"']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogNvramActiveEntry (args.entry) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogNvramAddEntry b/client/functions/ogNvramAddEntry new file mode 100755 index 0000000..1cffa53 --- /dev/null +++ b/client/functions/ogNvramAddEntry @@ -0,0 +1,24 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from UEFILib import ogNvramAddEntry + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('bootlbl') +parser.add_argument ('bootldr') +parser.add_argument ('nvram_set', nargs='?', default=False) + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogNvramAddEntry', 'ogNvramAddEntry Str_label_entry Str_boot_loader [ Bool_add_bootorder ]', ['ogNvramAddEntry 1 2 TRUE', 'ogNvramAddEntry grub /EFI/grub/grubx64.efi TRUE', 'ogNvramAddEntry Windows /EFI/Microsoft/Boot/bootmgfw.efi']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogNvramAddEntry (args.bootlbl, args.bootldr, args.nvram_set) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogNvramDeleteEntry b/client/functions/ogNvramDeleteEntry new file mode 100755 index 0000000..d8f7912 --- /dev/null +++ b/client/functions/ogNvramDeleteEntry @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from UEFILib import ogNvramDeleteEntry + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('entry') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogNvramDeleteEntry', 'ogNvramDeleteEntry [ Num_order_entry | Label_entry ]', ['ogNvramDeleteEntry 2', 'ogNvramAddEntry "Windows Boot Manager"']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogNvramDeleteEntry (args.entry) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogNvramGetCurrent b/client/functions/ogNvramGetCurrent new file mode 100755 index 0000000..c2a393d --- /dev/null +++ b/client/functions/ogNvramGetCurrent @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from UEFILib import ogNvramGetCurrent + +parser = argparse.ArgumentParser (add_help=False) + + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogNvramGetCurrent') + sys.exit (0) + +args = parser.parse_args() + +ret = ogNvramGetCurrent () +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogNvramGetNext b/client/functions/ogNvramGetNext new file mode 100755 index 0000000..4cacb1d --- /dev/null +++ b/client/functions/ogNvramGetNext @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from UEFILib import ogNvramGetNext + +parser = argparse.ArgumentParser (add_help=False) + + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogNvramGetNext') + sys.exit (0) + +args = parser.parse_args() + +ret = ogNvramGetNext () +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogNvramGetOrder b/client/functions/ogNvramGetOrder new file mode 100755 index 0000000..ef80387 --- /dev/null +++ b/client/functions/ogNvramGetOrder @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from UEFILib import ogNvramGetOrder + +parser = argparse.ArgumentParser (add_help=False) + + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogNvramGetOrder') + sys.exit (0) + +args = parser.parse_args() + +ret = ogNvramGetOrder () +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (','.join (ret)) diff --git a/client/functions/ogNvramGetTimeout b/client/functions/ogNvramGetTimeout new file mode 100755 index 0000000..15d1e8d --- /dev/null +++ b/client/functions/ogNvramGetTimeout @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from UEFILib import ogNvramGetTimeout + +parser = argparse.ArgumentParser (add_help=False) + + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogNvramGetTimeout') + sys.exit (0) + +args = parser.parse_args() + +ret = ogNvramGetTimeout () +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogNvramInactiveEntry b/client/functions/ogNvramInactiveEntry new file mode 100755 index 0000000..780213c --- /dev/null +++ b/client/functions/ogNvramInactiveEntry @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from UEFILib import ogNvramInactiveEntry + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('entry') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogNvramInactiveEntry', 'ogNvramInactiveEntry [ Num_order_entry | Label_entry ]', ['ogNvramInactiveEntry 2', 'ogNvramInactiveEntry "Windows Boot Manager"']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogNvramInactiveEntry (args.entry) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogNvramList b/client/functions/ogNvramList new file mode 100755 index 0000000..d44c974 --- /dev/null +++ b/client/functions/ogNvramList @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from UEFILib import ogNvramList + +parser = argparse.ArgumentParser (add_help=False) + + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogNvramList') + sys.exit (0) + +args = parser.parse_args() + +ret = ogNvramList () +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogNvramPxeFirstEntry b/client/functions/ogNvramPxeFirstEntry new file mode 100755 index 0000000..208328e --- /dev/null +++ b/client/functions/ogNvramPxeFirstEntry @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from UEFILib import ogNvramPxeFirstEntry + +parser = argparse.ArgumentParser (add_help=False) + + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogNvramPxeFirstEntry') + sys.exit (0) + +args = parser.parse_args() + +ret = ogNvramPxeFirstEntry () +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogNvramSetNext b/client/functions/ogNvramSetNext new file mode 100755 index 0000000..7c8ee3d --- /dev/null +++ b/client/functions/ogNvramSetNext @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from UEFILib import ogNvramSetNext + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('entry') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogNvramSetNext', 'ogNvramSetNext [ Num_order_entry | Label_entry ]', ['ogNvramSetNext 2', 'ogNvramSetNext "Windows Boot Manager"']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogNvramSetNext (args.entry) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogNvramSetOrder b/client/functions/ogNvramSetOrder new file mode 100755 index 0000000..13c22b4 --- /dev/null +++ b/client/functions/ogNvramSetOrder @@ -0,0 +1,16 @@ +#!/usr/bin/python3 + +import sys +from SystemLib import ogHelp +from UEFILib import ogNvramSetOrder + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogNvramSetOrder', 'ogNvramSetOrder Num_order1 [ Num_order2 ] ...', ['ogNvramSetOrder 1 3']) + sys.exit (0) + +ret = ogNvramSetOrder (sys.argv[1:]) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogNvramSetTimeout b/client/functions/ogNvramSetTimeout new file mode 100755 index 0000000..d54f89a --- /dev/null +++ b/client/functions/ogNvramSetTimeout @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from UEFILib import ogNvramSetTimeout + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('t') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogNvramSetTimeout', 'ogNvramSetTimeout int_Timeout (seg)', ['ogNvramSetTimeout 2']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogNvramSetTimeout (args.t) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogRaiseError b/client/functions/ogRaiseError new file mode 100755 index 0000000..15c94a6 --- /dev/null +++ b/client/functions/ogRaiseError @@ -0,0 +1,34 @@ +#!/usr/bin/python3 + +import sys +import argparse +import ogGlobals +from SystemLib import ogHelp +from SystemLib import ogRaiseError + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogRaiseError', 'ogRaiseError [str_logfile ...] int_errorcode str_errormessage') + sys.exit (0) + +logtypes = [] +while True: + if sys.argv[1] in ['command', 'log', 'session']: + logtypes.append (sys.argv.pop (1)) + else: break + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('code') +parser.add_argument ('msg') +args = parser.parse_args() + +try: + code = getattr (ogGlobals, args.code) +except AttributeError: + print (f'unknown code {args.code}') + sys.exit (1) + +ret = ogRaiseError (logtypes, code, args.msg) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) diff --git a/client/functions/ogReduceFs b/client/functions/ogReduceFs new file mode 100755 index 0000000..999aa90 --- /dev/null +++ b/client/functions/ogReduceFs @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from FileSystemLib import ogReduceFs + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogReduceFs', 'ogReduceFs int_ndisk int_nfilesys', ['ogReduceFs 1 1']) + sys.exit (0) + +args = parser.parse_args() +ret = ogReduceFs (args.disk, args.par) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogRestoreEfiBootLoader b/client/functions/ogRestoreEfiBootLoader new file mode 100755 index 0000000..7e72e37 --- /dev/null +++ b/client/functions/ogRestoreEfiBootLoader @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from UEFILib import ogRestoreEfiBootLoader + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogRestoreEfiBootLoader', 'ogRestoreEfiBootLoader int_ndisk int_part', ['ogRestoreEfiBootLoader 1 2']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogRestoreEfiBootLoader (args.disk, args.par) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogRestoreImage b/client/functions/ogRestoreImage new file mode 100755 index 0000000..57a461c --- /dev/null +++ b/client/functions/ogRestoreImage @@ -0,0 +1,25 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from ImageLib import ogRestoreImage + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('repo') +parser.add_argument ('imgpath') +parser.add_argument ('disk') +parser.add_argument ('par') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogRestoreImage', 'ogRestoreImage str_repo path_image int_ndisk int_npart', ['ogRestoreImage REPO /aula1/win7 1 1']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogRestoreImage (args.repo, args.imgpath, args.disk, args.par) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogRestoreImageSyntax b/client/functions/ogRestoreImageSyntax new file mode 100755 index 0000000..40d80ee --- /dev/null +++ b/client/functions/ogRestoreImageSyntax @@ -0,0 +1,25 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from ImageLib import ogRestoreImageSyntax + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('imgfile') +parser.add_argument ('part') +parser.add_argument ('tool', nargs='?', default=None) +parser.add_argument ('level', nargs='?', default=None) + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogRestoreImageSyntax', 'ogRestoreImageSyntax filename partition [tool] [levelcompresor]', ['ogRestoreImageSyntax /opt/opengnsys/images/prueba.img /dev/sda1 [partclone] [lzop]']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogRestoreImageSyntax (args.imgfile, args.part, args.tool, args.level) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogSetPartitionActive b/client/functions/ogSetPartitionActive new file mode 100755 index 0000000..c8a97b8 --- /dev/null +++ b/client/functions/ogSetPartitionActive @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from DiskLib import ogSetPartitionActive + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogSetPartitionActive', 'ogSetPartitionActive int_ndisk int_npartition', ['ogSetPartitionActive 1 1']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogSetPartitionActive (args.disk, args.par) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogSetPartitionId b/client/functions/ogSetPartitionId new file mode 100755 index 0000000..865051f --- /dev/null +++ b/client/functions/ogSetPartitionId @@ -0,0 +1,24 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from DiskLib import ogSetPartitionId + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') +parser.add_argument ('id') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogSetPartitionId', 'ogSetPartitionId int_ndisk int_npartition hex_partid', ['ogSetPartitionId 1 1 7']) + sys.exit (0) + +args = parser.parse_args() +ret = ogSetPartitionId (args.disk, args.par, args.id) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogSetPartitionSize b/client/functions/ogSetPartitionSize new file mode 100755 index 0000000..84d17b9 --- /dev/null +++ b/client/functions/ogSetPartitionSize @@ -0,0 +1,24 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from DiskLib import ogSetPartitionSize + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') +parser.add_argument ('size') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogSetPartitionSize', 'ogSetPartitionSize int_ndisk int_npartition int_size', ['ogSetPartitionSize 1 1 10000000']) + sys.exit (0) + +args = parser.parse_args() +ret = ogSetPartitionSize (args.disk, args.par, args.size) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogSetPartitionType b/client/functions/ogSetPartitionType new file mode 100755 index 0000000..875d0c4 --- /dev/null +++ b/client/functions/ogSetPartitionType @@ -0,0 +1,24 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from DiskLib import ogSetPartitionType + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') +parser.add_argument ('type') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogSetPartitionType', 'ogSetPartitionType int_ndisk int_npartition str_type', ['ogSetPartitionType 1 1 NTFS']) + sys.exit (0) + +args = parser.parse_args() +ret = ogSetPartitionType (args.disk, args.par, args.type) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogSetRegistryValue b/client/functions/ogSetRegistryValue new file mode 100755 index 0000000..d13d47f --- /dev/null +++ b/client/functions/ogSetRegistryValue @@ -0,0 +1,25 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from RegistryLib import ogSetRegistryValue + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('mntpt') +parser.add_argument ('hive') +parser.add_argument ('k') +parser.add_argument ('v') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogSetRegistryValue', 'ogSetRegistryValue path_mountpoint str_hive str_valuename str_data', [r'ogSetRegistryValue /mnt/sda1 SOFTWARE "\Key\SubKey\StringValue" "Abcde Fghij"', r'ogSetRegistryValue /mnt/sda1 SOFTWARE "\Key\SubKey\DwordValue" 1', r'ogSetRegistryValue /mnt/sda1 SOFTWARE "\Key\SubKey\BinaryValue" "04 08 0C 10"']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogSetRegistryValue (args.mntpt, args.hive, args.k, args.v) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogSetWindowsName b/client/functions/ogSetWindowsName new file mode 100755 index 0000000..d199c9d --- /dev/null +++ b/client/functions/ogSetWindowsName @@ -0,0 +1,24 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from BootLib import ogSetWindowsName + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') +parser.add_argument ('name') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogSetWindowsName', 'ogSetWindowsName int_ndisk int_filesys str_name', ['ogSetWindowsName 1 1 PRACTICA-PC']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogSetWindowsName (args.disk, args.par, args.name) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogTorrentStart b/client/functions/ogTorrentStart new file mode 100755 index 0000000..778d512 --- /dev/null +++ b/client/functions/ogTorrentStart @@ -0,0 +1,39 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from ProtocolLib import ogTorrentStart + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogTorrentStart', 'ogTorrentStart [ str_repo] [ [Relative_path_fileTORRENT] | [str_REPOSITORY path_fileTORRENT] | [int_ndisk int_npart path_fileTORRENT ] ] SessionTorrent', ['ogTorrentStart CACHE /PS1_PH1.img.torrent seeder:10000', 'ogTorrentStart /opt/opengnsys/cache/linux.iso peer:60', 'ogTorrentStart 1 1 /linux.iso.torrent leecher:60']) + sys.exit (0) + +parser = argparse.ArgumentParser (add_help=False) +if 3 == len (sys.argv): + parser.add_argument ('torrentfile', nargs='?', default=None) + parser.add_argument ('torrentsess', nargs='?', default=None) +elif 4 == len (sys.argv): + parser.add_argument ('container', nargs='?', default=None) + parser.add_argument ('torrentfile', nargs='?', default=None) + parser.add_argument ('torrentsess', nargs='?', default=None) +elif 5 == len (sys.argv): + parser.add_argument ('disk', nargs='?', default=None) + parser.add_argument ('par', nargs='?', default=None) + parser.add_argument ('torrentfile', nargs='?', default=None) + parser.add_argument ('torrentsess', nargs='?', default=None) + +args = parser.parse_args() + +if 3 == len (sys.argv): + ret = ogTorrentStart (torrentfile=args.torrentfile, torrentsess=args.torrentsess) +elif 4 == len (sys.argv): + ret = ogTorrentStart (container=args.container, torrentfile=args.torrentfile, torrentsess=args.torrentsess) +elif 5 == len (sys.argv): + ret = ogTorrentStart (disk=args.disk, par=args.par, torrentfile=args.torrentfile, torrentsess=args.torrentsess) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogTypeToId b/client/functions/ogTypeToId new file mode 100755 index 0000000..cce6665 --- /dev/null +++ b/client/functions/ogTypeToId @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from DiskLib import ogTypeToId + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('type') +parser.add_argument ('pttype', nargs='?', default='MSDOS') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogTypeToId', 'ogTypeToId str_parttype [str_tabletype]', ['ogTypeToId LINUX', 'ogTypeToId LINUX MSDOS']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogTypeToId (args.type, args.pttype) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogUcastReceiverPartition b/client/functions/ogUcastReceiverPartition new file mode 100755 index 0000000..31fb177 --- /dev/null +++ b/client/functions/ogUcastReceiverPartition @@ -0,0 +1,26 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from ProtocolLib import ogUcastReceiverPartition + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') +parser.add_argument ('sess') +parser.add_argument ('tool') +parser.add_argument ('level') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogUcastReceiverPartition', 'ogUcastReceiverPartition int_ndisk int_npart SessionMulticastCLIENT tools compresor', ['ogUcastReceiverPartition 1 1 8000:ipMASTER partclone lzop']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogUcastReceiverPartition (args.disk, args.par, args.sess, args.tool, args.level) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogUcastSendFile b/client/functions/ogUcastSendFile new file mode 100755 index 0000000..498965a --- /dev/null +++ b/client/functions/ogUcastSendFile @@ -0,0 +1,39 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from ProtocolLib import ogUcastSendFile + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogUcastSendFile', 'ogUcastSendFile [str_REPOSITORY] [int_ndisk int_npart] /Relative_path_file sesionMcast(puerto:ip:ip:ip)', ['ogUcastSendFile 1 1 /aula1/winxp.img 8000:172.17.36.11:172.17.36.12', 'ogUcastSendFile REPO /aula1/ubuntu.iso sesionUcast', 'ogUcastSendFile CACHE /aula1/winxp.img sesionUcast', 'ogUcastSendFile /opt/opengnsys/images/aula1/hd500.vmx sesionUcast']) + sys.exit (0) + +parser = argparse.ArgumentParser (add_help=False) +if 3 == len (sys.argv): + parser.add_argument ('file', nargs='?', default=None) + parser.add_argument ('sess', nargs='?', default=None) +elif 4 == len (sys.argv): + parser.add_argument ('container', nargs='?', default=None) + parser.add_argument ('file', nargs='?', default=None) + parser.add_argument ('sess', nargs='?', default=None) +elif 5 == len (sys.argv): + parser.add_argument ('disk', nargs='?', default=None) + parser.add_argument ('par', nargs='?', default=None) + parser.add_argument ('file', nargs='?', default=None) + parser.add_argument ('sess', nargs='?', default=None) + +args = parser.parse_args() + +if 3 == len (sys.argv): + ret = ogUcastSendFile (file=args.file, sess=args.sess) +elif 4 == len (sys.argv): + ret = ogUcastSendFile (container=args.container, file=args.file, sess=args.sess) +elif 5 == len (sys.argv): + ret = ogUcastSendFile (disk=args.disk, par=args.par, file=args.file, sess=args.sess) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogUcastSendPartition b/client/functions/ogUcastSendPartition new file mode 100755 index 0000000..8fba6dd --- /dev/null +++ b/client/functions/ogUcastSendPartition @@ -0,0 +1,26 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from ProtocolLib import ogUcastSendPartition + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') +parser.add_argument ('sess') +parser.add_argument ('tool') +parser.add_argument ('level') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogUcastSendPartition', 'ogUcastSendPartition int_ndisk int_npart SessionUNICAST-SERVER tools compresor', ['ogUcastSendPartition 1 1 8000:172.17.36.11:172.17.36.12 partclone lzop']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogUcastSendPartition (args.disk, args.par, args.sess, args.tool, args.level) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogUcastSyntax b/client/functions/ogUcastSyntax new file mode 100755 index 0000000..f360bc5 --- /dev/null +++ b/client/functions/ogUcastSyntax @@ -0,0 +1,34 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from ProtocolLib import ogUcastSyntax + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogUcastSyntax', 'ogUcastSyntax params', ['ogUcastSyntax SENDPARTITION str_sessionSERVER str_device str_tools str_level', 'ogUcastSyntax RECEIVERPARTITION str_sessionCLIENT str_device str_tools str_level', 'ogUcastSyntax SENDFILE str_sessionSERVER str_file', 'ogUcastSyntax RECEIVERFILE str_sessionCLIENT str_file', 'sessionServer syntax: portbase:ipCLIENT-1:ipCLIENT-2:ipCLIENT-N', 'sessionServer example: 8000:172.17.36.11:172.17.36.12', 'sessionClient syntax: portbase:ipMASTER', 'sessionClient example: 8000:172.17.36.249']) + sys.exit (0) + +kwargs = {} +parser = argparse.ArgumentParser (add_help=False) +if 'SENDPARTITION' == sys.argv[1] or 'RECEIVERPARTITION' == sys.argv[1]: + parser.add_argument ('op') + parser.add_argument ('sess') + parser.add_argument ('device') + parser.add_argument ('tool') + parser.add_argument ('level') + args = parser.parse_args() + kwargs = { 'device': args.device, 'tool': args.tool, 'level': args.level } +elif 'SENDFILE' == sys.argv[1] or 'RECEIVERFILE' == sys.argv[1]: + parser.add_argument ('op') + parser.add_argument ('sess') + parser.add_argument ('file') + args = parser.parse_args() + kwargs = { 'file': args.file } + +ret = ogUcastSyntax (args.op, args.sess, **kwargs) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogUnhidePartition b/client/functions/ogUnhidePartition new file mode 100755 index 0000000..539303e --- /dev/null +++ b/client/functions/ogUnhidePartition @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from DiskLib import ogUnhidePartition + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogUnhidePartition', 'ogUnhidePartition int_ndisk int_npartition', ['ogUnhidePartition 1 1']) + sys.exit (0) + +args = parser.parse_args() +ret = ogUnhidePartition (args.disk, args.par) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogUninstallLinuxClient b/client/functions/ogUninstallLinuxClient new file mode 100755 index 0000000..0dfbe27 --- /dev/null +++ b/client/functions/ogUninstallLinuxClient @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from PostConfLib import ogUninstallLinuxClient + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogUninstallLinuxClient', 'ogUninstallLinuxClient int_ndisk int_filesys', ['ogUninstallLinuxClient 1 1']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogUninstallLinuxClient (args.disk, args.par) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogUninstallWindowsClient b/client/functions/ogUninstallWindowsClient new file mode 100755 index 0000000..20ad443 --- /dev/null +++ b/client/functions/ogUninstallWindowsClient @@ -0,0 +1,24 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from PostConfLib import ogUninstallWindowsClient + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') +parser.add_argument ('cmdfile') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogUninstallWindowsClient', 'ogUninstallWindowsClient int_ndisk int_filesys str_filename', ['ogUninstallWindowsClient 1 1 filename.cmd']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogUninstallWindowsClient (args.disk, args.par, args.cmdfile) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogUnlock b/client/functions/ogUnlock new file mode 100755 index 0000000..ac321a2 --- /dev/null +++ b/client/functions/ogUnlock @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from FileSystemLib import ogUnlock + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogUnlock', 'ogUnlock int_ndisk int_npartition', ['ogUnlock 1 1']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogUnlock (args.disk, args.par) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogUnlockDisk b/client/functions/ogUnlockDisk new file mode 100755 index 0000000..04ee6ed --- /dev/null +++ b/client/functions/ogUnlockDisk @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from DiskLib import ogUnlockDisk + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogUnlockDisk', 'ogUnlockDisk int_ndisk', ['ogUnlockDisk 1']) + sys.exit (0) + +args = parser.parse_args() +ret = ogUnlockDisk (args.disk) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogUnlockImage b/client/functions/ogUnlockImage new file mode 100755 index 0000000..c0f5196 --- /dev/null +++ b/client/functions/ogUnlockImage @@ -0,0 +1,30 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from ImageLib import ogUnlockImage + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogUnlockImage', 'ogUnlockImage [str_repo] path_image', ['ogUnlockImage /opt/opengnsys/images/aula1/win7.img', 'ogUnlockImage REPO /aula1/win7.img']) + sys.exit (0) + +parser = argparse.ArgumentParser (add_help=False) +if 2 == len (sys.argv): + parser.add_argument ('file') +elif 3 == len (sys.argv): + parser.add_argument ('container') + parser.add_argument ('file') + +args = parser.parse_args() + +if 2 == len (sys.argv): + ret = ogUnlockImage (imgfile=args.file) +elif 3 == len (sys.argv): + ret = ogUnlockImage (container=args.container, imgfile=args.file) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogUnlockPartition b/client/functions/ogUnlockPartition new file mode 100755 index 0000000..134c072 --- /dev/null +++ b/client/functions/ogUnlockPartition @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from FileSystemLib import ogUnlockPartition + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogUnlockPartition') + sys.exit (0) + +args = parser.parse_args() + +ret = ogUnlockPartition (args.disk, args.par) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogUnmount b/client/functions/ogUnmount new file mode 100755 index 0000000..024294b --- /dev/null +++ b/client/functions/ogUnmount @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from FileSystemLib import ogUnmount + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogUnmount', 'ogUnmount int_ndisk int_npartition', ['ogUnmount 1 1']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogUnmount (args.disk, args.par) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogUnmountAll b/client/functions/ogUnmountAll new file mode 100755 index 0000000..5b69eb7 --- /dev/null +++ b/client/functions/ogUnmountAll @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from FileSystemLib import ogUnmountAll + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogUnmountAll', 'ogUnmountAll int_ndisk', ['ogUnmountAll 1']) + sys.exit (0) + +args = parser.parse_args() +ret = ogUnmountAll (args.disk) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogUnmountCache b/client/functions/ogUnmountCache new file mode 100755 index 0000000..89a35fc --- /dev/null +++ b/client/functions/ogUnmountCache @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from CacheLib import ogUnmountCache + +parser = argparse.ArgumentParser (add_help=False) + + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogUnmountCache', 'ogUnmountCache') + sys.exit (0) + +args = parser.parse_args() + +ret = ogUnmountCache () +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogUnmountFs b/client/functions/ogUnmountFs new file mode 100755 index 0000000..8c83bc0 --- /dev/null +++ b/client/functions/ogUnmountFs @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from FileSystemLib import ogUnmountFs + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogUnmountFs', 'ogUnmountFs int_ndisk int_npartition', ['ogUnmountFs 1 1']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogUnmountFs (args.disk, args.par) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogUnsetDirtyBit b/client/functions/ogUnsetDirtyBit new file mode 100755 index 0000000..c7314f2 --- /dev/null +++ b/client/functions/ogUnsetDirtyBit @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from FileSystemLib import ogUnsetDirtyBit + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogUnsetDirtyBit', 'ogUnsetDirtyBit int_ndisk int_nfilesys', ['ogUnsetDirtyBit 1 1']) + sys.exit (0) + +args = parser.parse_args() +ret = ogUnsetDirtyBit (args.disk, args.par) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogUpdateCacheIsNecesary b/client/functions/ogUpdateCacheIsNecesary new file mode 100755 index 0000000..db05cec --- /dev/null +++ b/client/functions/ogUpdateCacheIsNecesary @@ -0,0 +1,24 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from ProtocolLib import ogUpdateCacheIsNecesary + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('repo') +parser.add_argument ('file') +parser.add_argument ('proto') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogUpdateCacheIsNecesary', 'ogUpdateCacheIsNecesary str_repo relative_path_image [protocol|FULL]', ['ogUpdateCacheIsNecesary REPO /PS1_PH1.img UNICAST', 'ogUpdateCacheIsNecesary REPO /ogclient.sqfs FULL']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogUpdateCacheIsNecesary (args.repo, args.file, args.proto) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogUpdatePartitionTable b/client/functions/ogUpdatePartitionTable new file mode 100755 index 0000000..8200b2a --- /dev/null +++ b/client/functions/ogUpdatePartitionTable @@ -0,0 +1,13 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from DiskLib import ogUpdatePartitionTable + +ret = ogUpdatePartitionTable() + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogWindowsBootParameters b/client/functions/ogWindowsBootParameters new file mode 100755 index 0000000..3a410d9 --- /dev/null +++ b/client/functions/ogWindowsBootParameters @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from BootLib import ogWindowsBootParameters + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('disk') +parser.add_argument ('par') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogWindowsBootParameters', 'ogWindowsBootParameters int_ndisk int_partition', ['ogWindowsBootParameters 1 1']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogWindowsBootParameters (args.disk, args.par) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/functions/ogWindowsRegisterPartition b/client/functions/ogWindowsRegisterPartition new file mode 100755 index 0000000..34036bc --- /dev/null +++ b/client/functions/ogWindowsRegisterPartition @@ -0,0 +1,26 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from BootLib import ogWindowsRegisterPartition + +parser = argparse.ArgumentParser (add_help=False) +parser.add_argument ('registered_disk') +parser.add_argument ('registered_par') +parser.add_argument ('registered_vol') +parser.add_argument ('disk') +parser.add_argument ('par') + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogWindowsRegisterPartition', 'ogWindowsRegisterPartition int_ndisk_TO_register int_partition_TO_register str_NewVolume int_disk int_partition', ['ogWindowsRegisterPartition 1 1 c: 1 1']) + sys.exit (0) + +args = parser.parse_args() + +ret = ogWindowsRegisterPartition (args.registered_disk, args.registered_par, args.registered_vol, args.disk, args.par) +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/client/lib/python3/BootLib.py b/client/lib/python3/BootLib.py new file mode 100644 index 0000000..bf6f0b9 --- /dev/null +++ b/client/lib/python3/BootLib.py @@ -0,0 +1,1018 @@ +#/** +#@file BootLib.py +#@brief Librería o clase Boot +#@class Boot +#@brief Funciones para arranque y post-configuración de sistemas de archivos. +#@warning License: GNU GPLv3+ +#*/ + +import os +import re +import tempfile +import subprocess +import shutil +import glob + +import ogGlobals +import SystemLib +import FileSystemLib +import RegistryLib +import DiskLib +import InventoryLib +import FileLib +import UEFILib + +#/** +# ogBoot int_ndisk int_nfilesys [ NVRAMPERM ] [str_kernel str_initrd str_krnlparams] +#@brief Inicia el proceso de arranque de un sistema de archivos. +#@param int_ndisk nº de orden del disco +#@param int_nfilesys nº de orden del sistema de archivos +#@param str_nvramperm UEFI: La entrada en la NVRAM se incluye en el orden de arranque (opcional) +#@param str_krnlparams parámetros de arranque del kernel (opcional) +#@return (activar el sistema de archivos). +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Disco o particion no corresponden con un dispositivo. +#@exception OG_ERR_PARTITION Tipo de partición desconocido o no se puede montar. +#@exception OG_ERR_NOTOS La partición no tiene instalado un sistema operativo. +#@note En Linux, si no se indican los parámetros de arranque se detectan de la opción por defecto del cargador GRUB. +#@note En Linux, debe arrancarse la partición del directorio \c /boot +#*/ ## + + +#/** +# ogGetWindowsName int_ndisk int_nfilesys +#@brief Muestra el nombre del equipo en el registro de Windows. +#@param int_ndisk nº de orden del disco +#@param int_nfilesys nº de orden del sistema de archivos +#@return str_name - nombre del equipo +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Disco o particion no corresponden con un dispositivo. +#@exception OG_ERR_PARTITION Tipo de partición desconocido o no se puede montar. +#*/ ## + + +#/** +# ogLinuxBootParameters int_ndisk int_nfilesys +#@brief Muestra los parámetros de arranque de un sistema de archivos Linux. +#@param int_ndisk nº de orden del disco +#@param int_nfilesys nº de orden del sistema de archivos +#@return str_kernel str_initrd str_parameters ... +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Disco o particion no corresponden con un dispositivo. +#@exception OG_ERR_PARTITION Tipo de partición desconocido o no se puede montar. +#@warning Función básica usada por \c ogBoot +#*/ ## + + +#/** +# ogSetWindowsName int_ndisk int_nfilesys str_name +#@brief Establece el nombre del equipo en el registro de Windows. +#@param int_ndisk nº de orden del disco +#@param int_nfilesys nº de orden del sistema de archivos +#@param str_name nombre asignado +#@return (nada) +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Disco o particion no corresponden con un dispositivo. +#@exception OG_ERR_PARTITION Tipo de partición desconocido o no se puede montar. +#@exception OG_ERR_OUTOFLIMIT Nombre Netbios con más de 15 caracteres. +#*/ ## +def ogSetWindowsName (disk, par, name): + if len (name) > 15: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_OUTOFLIMIT, f'"{name[0:15]}..."') + return + + mntdir = FileSystemLib.ogMount (disk, par) + if not mntdir: return None + + RegistryLib.ogSetRegistryValue (mntdir, 'system', r'\ControlSet001\Control\ComputerName\ComputerName\ComputerName', name) + RegistryLib.ogSetRegistryValue (mntdir, 'system', r'\ControlSet001\Services\Tcpip\Parameters\Hostname', name) + RegistryLib.ogSetRegistryValue (mntdir, 'system', r'\ControlSet001\Services\Tcpip\Parameters\HostName', name) + RegistryLib.ogSetRegistryValue (mntdir, 'system', r'\ControlSet001\services\Tcpip\Parameters\Hostname', name) + RegistryLib.ogSetRegistryValue (mntdir, 'system', r'\ControlSet001\Services\Tcpip\Parameters\NV Hostname', name) + RegistryLib.ogSetRegistryValue (mntdir, 'system', r'\ControlSet001\Services\Tcpip\Parameters\NV HostName', name) + RegistryLib.ogSetRegistryValue (mntdir, 'system', r'\ControlSet001\services\Tcpip\Parameters\NV Hostname', name) + + + +#/** +# ogSetWinlogonUser int_ndisk int_npartition str_username +#@brief Establece el nombre de usuario por defecto en la entrada de Windows. +#@param int_ndisk nº de orden del disco +#@param int_npartition nº de orden de la partición +#@param str_username nombre de usuario por defecto +#@return (nada) +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Disco o particion no corresponden con un dispositivo. +#@exception OG_ERR_PARTITION Tipo de partición desconocido o no se puede montar. +#*/ ## + + +#/** +# ogBootMbrXP int_ndisk +#@brief Genera un nuevo Master Boot Record en el disco duro indicado, compatible con los SO tipo Windows +#@param int_ndisk nº de orden del disco +#@return salida del programa my-sys +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_PARTITION Tipo de partición desconocido o no se puede montar. +#*/ ## + + +#/** +# ogBootMbrGeneric int_ndisk +#@brief Genera un nuevo Codigo de arranque en el MBR del disco indicado, compatible con los SO tipo Windows, Linux. +#@param int_ndisk nº de orden del disco +#@return salida del programa my-sys +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Tipo de partición desconocido o no se puede montar. +#*/ ## + + + + +#/** +# ogFixBootSector int_ndisk int_parition +#@brief Corrige el boot sector de una particion activa para MS windows/dos -fat-ntfs +#@param int_ndisk nº de orden del disco +#@param int_partition nº de particion +#@return +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_PARTITION Tipo de partición desconocido o no se puede montar. +#*/ ## +def ogFixBootSector (disk, par): + partype = DiskLib.ogGetPartitionId (disk, par) + if partype not in [ '1', '4', '6', '7', 'b', 'c', 'e', 'f', '17', '700', 'EF00' ]: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, '') + return + + if not FileSystemLib.ogUnmount (disk, par): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, '') + return + + disk0 = str (int (disk) - 1) + + tmpfile = tempfile.NamedTemporaryFile (prefix='ogFBS-', mode='w').name + with open (tmpfile, 'w') as fd: + fd.write (f"""disk={disk0} +main_part={par} +fix_first_sector=yes +""") + subprocess.run (['timeout', '--foreground', '--signal=SIGKILL', '5s', 'spartlnx.run', '-cui', '-nm', '-w', '-f', tmpfile], stdin=subprocess.DEVNULL) + + os.remove (tmpfile) + + +#/** +# ogGetBootMbr int_ndisk +#@brief Obtiene el contenido del sector de arranque de un disco. +#@param int_ndisk nº de orden del disco +#@return str_MBR Descripción del contenido del MBR. +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Dispositivo de disco no encontrado. +#*/ ## + +#/** +# ogWindowsBootParameters int_ndisk int_parition +#@brief Configura el gestor de arranque de windows 7 / vista / XP / 2000 +#@param int_ndisk nº de orden del disco +#@param int_partition nº de particion +#@return +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_PARTITION Tipo de partición desconocido o no se puede montar. +#*/ ## + +def ogWindowsBootParameters (disk, par): + if not DiskLib.ogDiskToDev (disk, par): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, '') + return + + disk0 = int (disk) - 1 + + tmpfile = tempfile.NamedTemporaryFile (prefix='ogWBP-', mode='w').name + if InventoryLib.ogIsEfiActive(): + bootdisk, bootpar = DiskLib.ogGetEsp().split() + if not FileSystemLib.ogUnmount (bootdisk, bootpar): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, f'ESP: {bootdisk} {bootpar}') + return + bootdisk = str (int (bootdisk) - 1) + bootlabel = f'Part-{int(bootdisk):02d}-{int(bootpar):02d}' + bcdfile = f'boot_BCD_file=/EFI/{bootlabel}/Boot/BCD' + else: + bootdisk = disk0 + bootpar = par + bcdfile = '' + + +# Obtener versión de Windows. + winver = InventoryLib.ogGetOsVersion (disk, par) + parts = re.split (':| ', winver) + if 'Windows' == parts[0] and 'Server' == parts[2]: + winver = parts[1] + parts[2] + parts[3] + else: + winver = parts[1] + parts[2] + if not winver: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTOS, 'Windows') + return + +# Acciones para Windows XP. + if 'XP' in winver: + m = FileSystemLib.ogMount (disk, par) + if not m or not os.path.exists (f'{m}/boot.ini'): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, 'boot.ini') + return + with open (f'{m}/boot.ini', 'r') as fd: + boot_ini = fd.read() + boot_ini = re.sub (r'partition\([0-9]\)', f'partition({par})', boot_ini) + boot_ini = re.sub (r'rdisk\([0-9]\)', f'rdisk({disk0})', boot_ini) + with open (f'{m}/tmp.boot.ini', 'w') as fd: + fd.write (boot_ini) + os.rename (f'{m}/tmp.boot.ini', f'{m}/boot.ini') + return True + + if not FileSystemLib.ogUnmount (disk, par): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, '') + return + + +#Preparando instruccion Windows Resume Application + with open (tmpfile, 'w') as fd: + fd.write (f"""boot_disk={bootdisk} +boot_main_part={bootpar} +{bcdfile} +disk={disk0} +main_part={par} +boot_entry=Windows Resume Application +""") + subprocess.run (['timeout', '--foreground', '--signal=SIGKILL', '5s', 'spartlnx.run', '-cui', '-nm', '-w', '-f', tmpfile], stdin=subprocess.DEVNULL) + + +#Preparando instruccion tipo windows + with open (tmpfile, 'w') as fd: + fd.write (f"""boot_disk={bootdisk} +boot_main_part={bootpar} +{bcdfile} +disk={disk0} +main_part={par} +boot_entry={winver} +""") + subprocess.run (['timeout', '--foreground', '--signal=SIGKILL', '5s', 'spartlnx.run', '-cui', '-nm', '-w', '-f', tmpfile], stdin=subprocess.DEVNULL) + +##Preparando instruccion Ramdisk Options + with open (tmpfile, 'w') as fd: + fd.write (f"""boot_disk={bootdisk} +boot_main_part={bootpar} +{bcdfile} +disk={disk0} +main_part={par} +boot_entry=Ramdisk Options +""") + subprocess.run (['timeout', '--foreground', '--signal=SIGKILL', '5s', 'spartlnx.run', '-cui', '-nm', '-w', '-f', tmpfile], stdin=subprocess.DEVNULL) + +##Preparando instruccion Recovery Environment + with open (tmpfile, 'w') as fd: + fd.write (f"""boot_disk={bootdisk} +boot_main_part={bootpar} +{bcdfile} +disk={disk0} +main_part={par} +boot_entry=Windows Recovery Environment +""") + subprocess.run (['timeout', '--foreground', '--signal=SIGKILL', '5s', 'spartlnx.run', '-cui', '-nm', '-w', '-f', tmpfile], stdin=subprocess.DEVNULL) + +##Preparando instruccion Recovery + with open (tmpfile, 'w') as fd: + fd.write (f"""boot_disk={bootdisk} +boot_main_part={bootpar} +{bcdfile} +disk={disk0} +main_part={par} +boot_entry=Windows Recovery +""") + subprocess.run (['timeout', '--foreground', '--signal=SIGKILL', '5s', 'spartlnx.run', '-cui', '-nm', '-w', '-f', tmpfile], stdin=subprocess.DEVNULL) + +#Preparando instruccion Windows Boot Manager + with open (tmpfile, 'w') as fd: + fd.write (f"""boot_disk={bootdisk} +boot_main_part={bootpar} +{bcdfile} +disk={bootdisk} +main_part={bootpar} +boot_entry=Windows Boot Manager +""") + subprocess.run (['timeout', '--foreground', '--signal=SIGKILL', '5s', 'spartlnx.run', '-cui', '-nm', '-w', '-f', tmpfile], stdin=subprocess.DEVNULL) + +#Preparando instruccion Herramienta de diagnóstico de memoria de Windows + with open (tmpfile, 'w') as fd: + fd.write (f"""boot_disk={bootdisk} +boot_main_part={bootpar} +{bcdfile} +disk={bootdisk} +main_part={bootpar} +boot_entry=Herramienta de diagnóstico de memoria de Windows +""") + subprocess.run (['timeout', '--foreground', '--signal=SIGKILL', '5s', 'spartlnx.run', '-cui', '-nm', '-w', '-f', tmpfile], stdin=subprocess.DEVNULL) + +#Preparando instruccion Herramienta de diagnóstico de memoria de Windows + with open (tmpfile, 'w') as fd: + fd.write (f"""boot_disk={bootdisk} +boot_main_part={bootpar} +{bcdfile} +disk={bootdisk} +main_part={bootpar} +boot_entry=Herramienta de diagnstico de memoria de Windows +""") + subprocess.run (['timeout', '--foreground', '--signal=SIGKILL', '5s', 'spartlnx.run', '-cui', '-nm', '-w', '-f', tmpfile], stdin=subprocess.DEVNULL) + + os.remove (tmpfile) + + + +#/** +# ogWindowsRegisterPartition int_ndisk int_partiton str_volume int_disk int_partition +#@brief Registra una partición en windows con un determinado volumen. +#@param int_ndisk nº de orden del disco a registrar +#@param int_partition nº de particion a registrar +#@param str_volumen volumen a resgistar +#@param int_ndisk_windows nº de orden del disco donde esta windows +#@param int_partition_windows nº de particion donde esta windows +#@return +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_PARTITION Tipo de partición desconocido o no se puede montar. +#*/ ## +#ogWindowsRegisterPartition ("1", "1", "c:", "1", "1") +def ogWindowsRegisterPartition (registered_disk, registered_par, registered_vol, disk, par): + registered_vol = registered_vol[0].upper() + tmpfile = tempfile.NamedTemporaryFile (prefix='ogWRP-', mode='w').name + + if not DiskLib.ogDiskToDev (registered_disk, registered_par): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, 'particion a registrar') + return + + if not DiskLib.ogDiskToDev (disk, par): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, 'particion de windows') + return + + t = InventoryLib.ogGetOsType (disk, par) + if 'Windows' not in t: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTOS, 'no es windows') + return + + v = InventoryLib.ogGetOsVersion (disk, par) + + if FileLib.ogGetPath (src=f'{disk} {par}', file='WINDOWS'): + systemroot = 'Windows' + elif FileLib.ogGetPath (src=f'{disk} {par}', file='WINNT'): + systemroot = 'winnt' + else: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTOS, '') + return + + FileSystemLib.ogUnmount (disk, par) + disk0 = str (int (disk) - 1) + registered_disk0 = str (int (registered_disk) - 1) + + with open (tmpfile, 'w') as fd: + fd.write (f"""windows_disk={disk0} +windows_main_part={par} +windows_dir={systemroot} +disk={registered_disk0} +main_part={registered_par} +;ext_part +part_letter={registered_vol} +""") + subprocess.run (['timeout', '--foreground', '--signal=SIGKILL', '5s', 'spartlnx.run', '-cui', '-nm', '-w', '-f', tmpfile], stdin=subprocess.DEVNULL) + + os.remove (tmpfile) + + +#/** +# ogGrubInstallMbr int_disk_GRUBCFG int_partition_GRUBCFG +#@brief Instala el grub el el MBR del primer disco duro (FIRSTSTAGE). El fichero de configuración grub.cfg ubicado según parametros disk y part(SECONDSTAGE). Admite sistemas Windows. +#@param int_disk_SecondStage +#@param int_part_SecondStage +#@param bolean_Check_Os_installed_and_Configure_2ndStage true | false[default] +#@return +#@exception OG_ERR_FORMAT Formato incorrecto. +#*/ ## + +#ogGrubInstallMbr ('1', '1', 'FALSE') +#ogGrubInstallMbr ('1', '1', 'TRUE', 'nomodeset irqpoll pci=noacpi quiet splash') +def ogGrubInstallMbr (disk, par, checkos='FALSE', kernelparam=''): + backupname = '.backup.og' + + #error si no es linux. + #version = InventoryLib.ogGetOsVersion (disk, par) + #if not version or 'Linux' not in version: + # SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTOS, 'no es linux') + # return + + firststage = DiskLib.ogDiskToDev ('1') #La primera etapa del grub se fija en el primer disco duro + secondstage = FileSystemLib.ogMount (disk, par) #localizar disco segunda etapa del grub + if not secondstage: return + os.makedirs (f'{secondstage}/boot/grub/', exist_ok=True) + prefixsecondstage = '/boot/grubMBR' #Localizar directorio segunda etapa del grub + efioptgrub = [] + + if InventoryLib.ogIsEfiActive(): + esp = DiskLib.ogGetEsp() + efidisk, efipart = esp.split() + # Comprobamos que exista ESP y el directorio para ubuntu + efisecondstage = FileSystemLib.ogMount (efidisk, efipart) + if not efisecondstage: + FileSystemLib.ogFormat (efidisk, efipart, 'FAT32') + efisecondstage = FileSystemLib.ogMount (efidisk, efipart) + if not efisecondstage: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, 'ESP') + return + efisubdir = 'grub' + if os.path.isdir (f'{efisecondstage}/EFI/{efisubdir}'): # Borramos la configuración anterior + shutil.rmtree (f'{efisecondstage}/EFI/{efisubdir}') + os.makedirs (f'{efisecondstage}/EFI/{efisubdir}/Boot') + arch = InventoryLib.ogGetArch() + efioptgrub = ['--removable', '--no-nvram', '--uefi-secure-boot', '--target', f'{arch}-efi', f'--efi-directory={efisecondstage}/EFI/{efisubdir}'] + + # Si Reconfigurar segunda etapa (grub.cfg) == FALSE + if 'FALSE' == checkos.upper() and (os.path.exists (f'{secondstage}/boot/grub/grub.cfg') or os.path.exists (f'{secondstage}/boot/grub/grub.cfg{backupname}')): + if os.path.exists (f'{secondstage}/boot/grub/grub.cfg{backupname}'): + os.rename (f'{secondstage}/boot/grub/grub.cfg{backupname}', f'{secondstage}/boot/grub/grub.cfg') + if os.path.isdir (f'{secondstage}{prefixsecondstage}'): + shutil.rmtree (f'{secondstage}{prefixsecondstage}') + prefixsecondstage = '' # Reactivamos el grub con el grub.cfg original. + else: # SI Reconfigurar segunda etapa (grub.cfg) == TRUE + #llamada a updateBootCache para que aloje la primera fase del ogLive + subprocess.run ([f'{ogGlobals.OGSCRIPTS}/updateBootCache.py'], check=True) ## TODO: check that this works + + if InventoryLib.ogIsEfiActive(): + # UEFI: grubSintax necesita grub.cfg para detectar los kernels: si no existe recupero backup. + if not os.path.exists (f'{secondstage}/boot/grub/grub.cfg'): + if os.path.exists (f'{secondstage}/boot/grub/grub.cfg{backupname}'): + os.rename (f'{secondstage}/boot/grub/grub.cfg{backupname}', f'{secondstage}/boot/grub/grub.cfg') + else: + os.rename (f'{secondstage}/boot/grub/grub.cfg', f'{secondstage}/boot/grub/grub.cfg{backupname}') + + with open ('/etc/default/grub', 'a') as fd: #Configur la sintaxis grub para evitar menus de "recovery" en el OGLive + fd.write ('GRUB_DISABLE_RECOVERY="true"\n') + fd.write ('GRUB_DISABLE_LINUX_UUID="true"\n') + + os.makedirs (f'{secondstage}{prefixsecondstage}/boot/grub/') #Preparar configuración segunda etapa: crear ubicacion + subprocess.run (['sed', '-i', 's/^set -e/#set -e/', '/etc/grub.d/00_header']) #Preparar configuración segunda etapa: crear cabecera del fichero (ingnorar errores) + # (ogLive 5.0) Si 'pkgdatadir' está vacía ponemos valor de otros ogLive + subprocess.run (['sed', '-i', r'/grub-mkconfig_lib/i\pkgdatadir=${pkgdatadir:-"${datarootdir}/grub"}', '/etc/grub.d/00_header']) + out = subprocess.run (['/etc/grub.d/00_header'], capture_output=True, text=True).stdout + with open (f'{secondstage}{prefixsecondstage}/boot/grub/grub.cfg', 'w') as fd: + fd.write (out + '\n') + + #Preparar configuración segunda etapa: crear entrada del sistema operativo + out = subprocess.run (['grubSyntax', kernelparam], capture_output=True, text=True).stdout + with open (f'{secondstage}{prefixsecondstage}/boot/grub/grub.cfg', 'a') as fd: + fd.write (out + '\n') + + # Renombramos la configuración de grub antigua + if os.path.exists (f'{secondstage}/boot/grub/grub.cfg'): + os.rename (f'{secondstage}/boot/grub/grub.cfg', f'{secondstage}/boot/grub/grub.cfg{backupname}') + + eval = subprocess.run (['grub-install', '--force'] + efioptgrub + [f'--root-directory={secondstage}{prefixsecondstage}', firststage]).returncode + + if InventoryLib.ogIsEfiActive(): # Movemos el grubx64.efi + for b in (glob.glob (f'{efisecondstage}/EFI/{efisubdir}/EFI/BOOT/*')): + bn = os.path.basename (b) + os.rename (f'{efisecondstage}/EFI/{efisubdir}/EFI/BOOT/{bn}', f'{efisecondstage}/EFI/{efisubdir}/Boot/{bn}') + shutil.rmtree (f'{efisecondstage}/EFI/{efisubdir}/EFI') + shutil.copy2 ('/usr/lib/shim/shimx64.efi.signed', f'{efisecondstage}/EFI/{efisubdir}/Boot/shimx64.efi') + # Nombre OpenGnsys para cargador + shutil.copy2 (f'{efisecondstage}/EFI/{efisubdir}/Boot/grubx64.efi', f'{efisecondstage}/EFI/{efisubdir}/Boot/ogloader.efi') + + # Creamos entrada NVRAM y la ponemos en segundo lugar + UEFILib.ogNvramAddEntry ('grub', '/EFI/grub/Boot/shimx64.efi') + grubentry = UEFILib.ogNvramList() + for l in grubentry.splitlines(): + words = l.split() + if len(words) < 2: continue + if 'grub' == words[1]: + grubentry = words[0] + neworder = UEFILib.ogNvramGetOrder() + neworder = neworder[0:1] + [grubentry] + neworder[1:] ## la ponemos en segundo lugar + UEFILib.ogNvramSetOrder (neworder) + + return eval + + +#/** +# ogGrubInstallPartition int_disk_SECONDSTAGE int_partition_SECONDSTAGE bolean_Check_Os_installed_and_Configure_2ndStage +#@brief Instala y actualiza el gestor grub en el bootsector de la particion indicada +#@param int_disk_SecondStage +#@param int_part_SecondStage +#@param bolean_Check_Os_installed_and_Configure_2ndStage true | false[default] +#@param str "kernel param " +#@return +#@exception OG_ERR_FORMAT Formato incorrecto. +#*/ ## + +#ogGrubInstallPartition ('1', '1', 'FALSE') +#ogGrubInstallPartition ('1', '1', 'TRUE', 'nomodeset irqpoll pci=noacpi quiet splash') +def ogGrubInstallPartition (disk, par, checkos='FALSE', kernelparam=''): + backupname = '.backup.og' + + #error si no es linux. + version = InventoryLib.ogGetOsVersion (disk, par) + if not version or 'Linux' not in version: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTOS, 'no es linux') + return + + firststage = DiskLib.ogDiskToDev (disk, par) #Localizar primera etapa del grub + secondstage = FileSystemLib.ogMount (disk, par) #localizar disco segunda etapa del grub + prefixsecondstage = '/boot/grubPARTITION' #Localizar directorio segunda etapa del grub + efioptgrub = [] + + if InventoryLib.ogIsEfiActive(): + esp = DiskLib.ogGetEsp() + efidisk, efipart = esp.split() + # Comprobamos que exista ESP y el directorio para ubuntu + efisecondstage = FileSystemLib.ogMount (efidisk, efipart) + if not efisecondstage: + FileSystemLib.ogFormat (efidisk, efipart, 'FAT32') + efisecondstage = FileSystemLib.ogMount (efidisk, efipart) + if not efisecondstage: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, 'ESP') + return + efisubdir = f'Part-{int(disk):02d}-{int(par):02d}' + if os.path.isdir (f'{efisecondstage}/EFI/{efisubdir}'): # Borramos la configuración anterior + shutil.rmtree (f'{efisecondstage}/EFI/{efisubdir}') + os.makedirs (f'{efisecondstage}/EFI/{efisubdir}/Boot') + arch = InventoryLib.ogGetArch() + efioptgrub = ['--removable', '--no-nvram', '--uefi-secure-boot', '--target', f'{arch}-efi', f'--efi-directory={efisecondstage}/EFI/{efisubdir}'] + + # Si Reconfigurar segunda etapa (grub.cfg) == FALSE + if 'FALSE' == checkos.upper() and (os.path.exists (f'{secondstage}/boot/grub/grub.cfg') or os.path.exists (f'{secondstage}/boot/grub/grub.cfg{backupname}')): + if os.path.exists (f'{secondstage}/boot/grub/grub.cfg{backupname}'): + os.rename (f'{secondstage}/boot/grub/grub.cfg{backupname}', f'{secondstage}/boot/grub/grub.cfg') + if os.path.isdir (f'{secondstage}{prefixsecondstage}'): + shutil.rmtree (f'{secondstage}{prefixsecondstage}') + prefixsecondstage = '' # Reactivamos el grub con el grub.cfg original. + else: # SI Reconfigurar segunda etapa (grub.cfg) == TRUE + if InventoryLib.ogIsEfiActive(): + # UEFI: grubSintax necesita grub.cfg para detectar los kernels: si no existe recupero backup. + if not os.path.exists (f'{secondstage}/boot/grub/grub.cfg'): + if os.path.exists (f'{secondstage}/boot/grub/grub.cfg{backupname}'): + os.rename (f'{secondstage}/boot/grub/grub.cfg{backupname}', f'{secondstage}/boot/grub/grub.cfg') + else: + if (os.path.exists (f'{secondstage}/boot/grub/grub.cfg')): + os.rename (f'{secondstage}/boot/grub/grub.cfg', f'{secondstage}/boot/grub/grub.cfg{backupname}') + + with open ('/etc/default/grub', 'a') as fd: #Configur la sintaxis grub para evitar menus de "recovery" en el OGLive + fd.write ('GRUB_DISABLE_RECOVERY="true"\n') + fd.write ('GRUB_DISABLE_LINUX_UUID="true"\n') + + os.makedirs (f'{secondstage}{prefixsecondstage}/boot/grub/', exist_ok=True) #Preparar configuración segunda etapa: crear ubicacion + subprocess.run (['sed', '-i', 's/^set -e/#set -e/', '/etc/grub.d/00_header']) #Preparar configuración segunda etapa: crear cabecera del fichero (ingnorar errores) + # (ogLive 5.0) Si 'pkgdatadir' está vacía ponemos valor de otros ogLive + subprocess.run (['sed', '-i', r'/grub-mkconfig_lib/i\pkgdatadir=${pkgdatadir:-"${datarootdir}/grub"}', '/etc/grub.d/00_header']) + out = subprocess.run (['/etc/grub.d/00_header'], capture_output=True, text=True).stdout + with open (f'{secondstage}{prefixsecondstage}/boot/grub/grub.cfg', 'w') as fd: + fd.write (out + '\n') + + #Preparar configuración segunda etapa: crear entrada del sistema operativo + out = subprocess.run (['grubSyntax', disk, par, kernelparam], capture_output=True, text=True).stdout + with open (f'{secondstage}{prefixsecondstage}/boot/grub/grub.cfg', 'a') as fd: + fd.write (out + '\n') + + eval = subprocess.run (['grub-install', '--force'] + efioptgrub + [f'--root-directory={secondstage}{prefixsecondstage}', firststage]).returncode + + if InventoryLib.ogIsEfiActive(): # Movemos el grubx64.efi + for b in (glob.glob (f'{efisecondstage}/EFI/{efisubdir}/EFI/BOOT/*')): + os.rename (f'{efisecondstage}/EFI/{efisubdir}/EFI/BOOT/{b}', f'{efisecondstage}/EFI/{efisubdir}/Boot/{b}') + shutil.rmtree (f'{efisecondstage}/EFI/{efisubdir}/EFI') + shutil.copy2 ('/usr/lib/shim/shimx64.efi.signed', f'{efisecondstage}/EFI/{efisubdir}/Boot/shimx64.efi') + # Nombre OpenGnsys para cargador + shutil.copy2 (f'{efisecondstage}/EFI/{efisubdir}/Boot/grubx64.efi', f'{efisecondstage}/EFI/{efisubdir}/Boot/ogloader.efi') + + return eval + + + +#/** +# ogConfigureFstab int_ndisk int_nfilesys +#@brief Configura el fstab según particiones existentes +#@param int_ndisk nº de orden del disco +#@param int_nfilesys nº de orden del sistema de archivos +#@return (nada) +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND No se encuentra el fichero fstab a procesar. +#@warning Puede haber un error si hay más de 1 partición swap. +#*/ ## +def ogConfigureFstab (disk, par): + fstab = FileLib.ogGetPath (src=f'{disk} {par}', file='/etc/fstab') + if not fstab: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f'{disk},{par},/etc/fstab') + return + + efiopt = '' + + shutil.copy2 (fstab, fstab+'.backup') + + with open ('/etc/fstab', 'r') as fd: + while True: + l = fd.readline() + if not l: break + cosas = l.split() + if 6 != len (cosas): continue + fstab_dev, fstab_mntpt, fstab_fstype, fstab_opts, fstab_dump, fstab_pass = cosas + if '#' in fstab_dev: continue + if '/' == fstab_mntpt: + defroot = fstab_dev + elif 'swap' == fstab_fstype: + defswap = fstab_dev + elif '/boot/efi' == fstab_mntpt: + efiopt = '\t'.join ([fstab_fstype, fstab_opts, fstab_dump, fstab_pass]) + + partroot = DiskLib.ogDiskToDev (disk, par) + partswap = subprocess.run (['blkid', '-', ' TYPE=swap'], capture_output=True, text=True).stdout + if partswap: + partswap = partswap.splitlines()[0] + partswap = partswap.split (':')[0] + if defswap: + print ("Hay definicion de SWAP en el FSTAB $DEFSWAP -> modificamos fichero con nuevo valor $DEFSWAP->$PARTSWAP") # Mensaje temporal. + subprocess.run (f'sed "s|{defswap}|{partswap}|g; s|{defroot}|{partroot}|g" {fstab}.backup > {fstab}', shell=True) + else: + print ("No hay definicion de SWAP y si hay partición SWAP -> moficamos fichero") # Mensaje temporal. + subprocess.run (f'sed "s|{defroot}|{partroot}|g" {fstab}.backup > {fstab}', shell=True) + with open ('/etc/fstab', 'a') as fd: + fd.write (f'{partswap} none swap sw 0 0\n') + else: + print ("No hay partición SWAP -> configuramos FSTAB") # Mensaje temporal. + subprocess.run (f'sed "/swap/d" {fstab}.backup > {fstab}', shell=True) +# Si es un sistema EFI incluimos partición ESP (Si existe la modificamos) + if InventoryLib.ogIsEfiActive(): + esp = DiskLib.ogGetEsp() + efidisk, efipart = esp.split() + efidev = DiskLib.ogDiskToDev (efidisk, efipart) + + ## Opciones de la partición ESP: si no existe ponemos un valor por defecto + if not efiopt: + efiopt = '\t'.join (['vfat', 'umask=0077', '0', '1']) + + subprocess.run (f'sed -i /"boot\\/efi"/d {fstab}', shell=True) + with open ('/etc/fstab', 'a') as fd: + fd.write ('{efidev}\t/boot/efi\t{efiopt}\n') + + + +#/** +# ogSetLinuxName int_ndisk int_nfilesys [str_name] +#@brief Establece el nombre del equipo en los ficheros hostname y hosts. +#@param int_ndisk nº de orden del disco +#@param int_nfilesys nº de orden del sistema de archivos +#@param str_name nombre asignado (opcional) +#@return (nada) +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Disco o particion no corresponden con un dispositivo. +#@exception OG_ERR_PARTITION Tipo de partición desconocido o no se puede montar. +#@note Si no se indica nombre, se asigna un valor por defecto. +#*/ ## + + + +#/** +# ogCleanLinuxDevices int_ndisk int_nfilesys +#@brief Limpia los dispositivos del equipo de referencia. Interfaz de red ... +#@param int_ndisk nº de orden del disco +#@param int_nfilesys nº de orden del sistema de archivos +#@return (nada) +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Disco o particion no corresponden con un dispositivo. +#@exception OG_ERR_PARTITION Tipo de partición desconocido o no se puede montar. +#*/ ## +#ogCleanLinuxDevices (1, 1) +def ogCleanLinuxDevices (disk, par): + mntdir = FileSystemLib.ogMount (disk, par) + if not mntdir: return None + + # Eliminar fichero de configuración de udev para dispositivos fijos de red. + if os.path.exists (f'{mntdir}/etc/udev/rules.d/70-persistent-net.rules'): + os.unlink (f'{mntdir}/etc/udev/rules.d/70-persistent-net.rules') + # Eliminar fichero resume (estado previo de hibernación) utilizado por el initrd scripts-premount + if os.path.exists (f'{mntdir}/etc/initramfs-tools/conf.d/resume'): + os.unlink ( f'{mntdir}/etc/initramfs-tools/conf.d/resume') + +#/** +# ogGrubAddOgLive num_disk num_part [ timeout ] [ offline ] +#@brief Crea entrada de menu grub para ogclient, tomando como paramentros del kernel los actuales del cliente. +#@param 1 Numero de disco +#@param 2 Numero de particion +#@param 3 timeout Segundos de espera para iniciar el sistema operativo por defecto (opcional) +#@param 4 offline configura el modo offline [offline|online] (opcional) +#@return (nada) +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND No existe kernel o initrd en cache. +#@exception OG_ERR_NOTFOUND No existe archivo de configuracion del grub. +# /// FIXME: Solo para el grub instalado en MBR por Opengnsys, ampliar para más casos. +#*/ ## + +#/** +# ogGrubHidePartitions num_disk num_part +#@brief ver ogBootLoaderHidePartitions +#@see ogBootLoaderHidePartitions +#*/ ## + +#/** +# ogBurgHidePartitions num_disk num_part +#@brief ver ogBootLoaderHidePartitions +#@see ogBootLoaderHidePartitions +#*/ ## + +#/** +# ogBootLoaderHidePartitions num_disk num_part +#@brief Configura el grub/burg para que oculte las particiones de windows que no se esten iniciando. +#@param 1 Numero de disco +#@param 2 Numero de particion +#@param 3 Numero de disco de la partición de datos (no ocultar) +#@param 4 Numero de particion de datos (no ocultar) +#@return (nada) +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception No existe archivo de configuracion del grub/burg. +#*/ + +#/** +# ogGrubDeleteEntry num_disk num_part num_disk_delete num_part_delete +#@brief ver ogBootLoaderDeleteEntry +#@see ogBootLoaderDeleteEntry +#*/ + +#/** +# ogBurgDeleteEntry num_disk num_part num_disk_delete num_part_delete +#@brief ver ogBootLoaderDeleteEntry +#@see ogBootLoaderDeleteEntry +#*/ + +#/** +# ogRefindDeleteEntry num_disk_delete num_part_delete +#@brief ver ogBootLoaderDeleteEntry +#@see ogBootLoaderDeleteEntry +#*/ + +#/** +# ogBootLoaderDeleteEntry num_disk num_part num_part_delete +#@brief Borra en el grub las entradas para el inicio en una particion. +#@param 1 Numero de disco donde esta el grub +#@param 2 Numero de particion donde esta el grub +#@param 3 Numero del disco del que borramos las entradas +#@param 4 Numero de la particion de la que borramos las entradas +#@note Tiene que ser llamada desde ogGrubDeleteEntry, ogBurgDeleteEntry o ogRefindDeleteEntry +#@return (nada) +#@exception OG_ERR_FORMAT Use ogGrubDeleteEntry or ogBurgDeleteEntry. +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND No existe archivo de configuracion del grub. +#*/ ## + +#/** +# ogBurgInstallMbr int_disk_GRUBCFG int_partition_GRUBCFG +#@param bolean_Check_Os_installed_and_Configure_2ndStage true | false[default] +#@brief Instala y actualiza el gestor grub en el MBR del disco duro donde se encuentra el fichero grub.cfg. Admite sistemas Windows. +#@param int_disk_SecondStage +#@param int_part_SecondStage +#@param bolean_Check_Os_installed_and_Configure_2ndStage true | false[default] +#@return +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_PARTITION Partición no soportada +#*/ ## + +#/** +# ogGrubDefaultEntry int_disk_GRUGCFG int_partition_GRUBCFG int_disk_default_entry int_npartition_default_entry +#@brief ver ogBootLoaderDefaultEntry +#@see ogBootLoaderDefaultEntry +#*/ ## + +#/** +# ogBurgDefaultEntry int_disk_BURGCFG int_partition_BURGCFG int_disk_default_entry int_npartition_default_entry +#@brief ver ogBootLoaderDefaultEntry +#@see ogBootLoaderDefaultEntry +#*/ ## + + +#/** +# ogRefindDefaultEntry int_disk_default_entry int_npartition_default_entry +#@brief ver ogBootLoaderDefaultEntry +#@see ogBootLoaderDefaultEntry +#*/ ## + +#/** +# ogBootLoaderDefaultEntry int_disk_CFG int_partition_CFG int_disk_default_entry int_npartition_default_entry +#@brief Configura la entrada por defecto de Burg +#@param int_disk_SecondStage +#@param int_part_SecondStage +#@param int_disk_default_entry +#@param int_part_default_entry +#@return +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_PARTITION Partición errónea o desconocida (ogMount). +#@exception OG_ERR_OUTOFLIMIT Param $3 no es entero. +#@exception OG_ERR_NOTFOUND Fichero de configuración no encontrado: burg.cfg. +#*/ ## + +#/** +# ogGrubOgliveDefaultEntry num_disk num_part +#@brief ver ogBootLoaderOgliveDefaultEntry +#@see ogBootLoaderOgliveDefaultEntry +#*/ ## + +#/** +# ogBurgOgliveDefaultEntry num_disk num_part +#@brief ver ogBootLoaderOgliveDefaultEntry +#@see ogBootLoaderOgliveDefaultEntry +#*/ ## + + +#/** +# ogRefindOgliveDefaultEntry +#@brief ver ogBootLoaderOgliveDefaultEntry +#@see ogBootLoaderOgliveDefaultEntry +#*/ ## + + +#/** +# ogBootLoaderOgliveDefaultEntry +#@brief Configura la entrada de ogLive como la entrada por defecto de Burg. +#@param int_disk_SecondStage +#@param int_part_SecondStage +#@return +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_PARTITION Partición errónea o desconocida (ogMount). +#@exception OG_ERR_NOTFOUND Fichero de configuración no encontrado: burg.cfg. +#@exception OG_ERR_NOTFOUND Entrada de OgLive no encontrada en burg.cfg. +#*/ ## + + +#/** +# ogGrubSecurity int_disk_GRUBCFG int_partition_GRUBCFG [user] [password] +#@brief Configura grub.cfg para que sólo permita editar entrada o acceder a línea de comandos al usuario especificado +#@param int_disk_SecondStage +#@param int_part_SecondStage +#@param user (default root) +#@param password (default "", no puede entrar) +#@return (nada) +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_PARTITION Tipo de partición desconocido o no se puede montar (ogMount). +#@exception OG_ERR_NOTFOUND No encuentra archivo de configuración del grub. +#*/ ## + + +#/** +# ogGrubSetTheme num_disk num_part str_theme +#@brief ver ogBootLoaderSetTheme +#@see ogBootLoaderSetTheme +#*/ ## + +#/** +# ogBurgSetTheme num_disk num_part str_theme +#@brief ver ogBootLoaderSetTheme +#@see ogBootLoaderSetTheme +#*/ ## + + +#/** +# ogRefindSetTheme str_theme +#@brief ver ogBootLoaderSetTheme +#@see ogBootLoaderSetTheme +#*/ ## + + +#/** +# ogBootLoaderSetTheme +#@brief asigna un tema al BURG +#@param int_disk_SecondStage +#@param int_part_SecondStage +#@param str_theme_name +#@return +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_PARTITION Partición errónea o desconocida (ogMount). +#@exception OG_ERR_NOTFOUND Fichero de configuración no encontrado: grub.cfg burg.cfg refind.conf. +#@exception OG_ERR_NOTFOUND Entrada deltema no encontrada en burg.cfg. +#@exception OG_ERR_NOTFOUND Fichero de configuración del tema no encontrado: theme.conf (sólo refind). +#@note El tema debe situarse en OGLIB/BOOTLOADER/themes +#*/ ## + +#/** +# ogGrubSetAdminKeys num_disk num_part str_theme +#@brief ver ogBootLoaderSetTheme +#@see ogBootLoaderSetTheme +#*/ ## + +#/** +# ogBurgSetAdminKeys num_disk num_part str_bolean +#@brief ver ogBootLoaderSetAdminKeys +#@see ogBootLoaderSetAdminKeys +#*/ ## + + + +#/** +# ogBootLoaderSetAdminKeys +#@brief Activa/Desactica las teclas de administracion +#@param int_disk_SecondStage +#@param int_part_SecondStage +#@param Boolean TRUE/FALSE +#@return +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_PARTITION Partición errónea o desconocida (ogMount). +#@exception OG_ERR_NOTFOUND Fichero de configuración no encontrado: grub.cfg burg.cfg. +#@exception OG_ERR_NOTFOUND Entrada deltema no encontrada en burg.cfg. +#*/ ## + + + +#/** +# ogGrubSetTimeOut num_disk num_part int_timeout_seconds +#@brief ver ogBootLoaderSetTimeOut +#@see ogBootLoaderSetTimeOut +#*/ ## + +#/** +# ogBurgSetTimeOut num_disk num_part str_bolean +#@brief ver ogBootLoaderSetTimeOut +#@see ogBootLoaderSetTimeOut +#*/ ## + + +#/** +# ogRefindSetTimeOut int_timeout_second +#@brief ver ogBootLoaderSetTimeOut +#@see ogBootLoaderSetTimeOut +#*/ ## + +#/** +# ogBootLoaderSetTimeOut +#@brief Define el tiempo (segundos) que se muestran las opciones de inicio +#@param int_disk_SecondStage +#@param int_part_SecondStage +#@param int_timeout_seconds +#@return +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_PARTITION Partición errónea o desconocida (ogMount). +#@exception OG_ERR_NOTFOUND Fichero de configuración no encontrado: grub.cfg burg.cfg. +#@exception OG_ERR_NOTFOUND Entrada deltema no encontrada en burg.cfg. +#*/ ## + + +#/** +# ogGrubSetResolution num_disk num_part int_resolution +#@brief ver ogBootLoaderSetResolution +#@see ogBootLoaderSetResolution +#*/ ## + +#/** +# ogBurgSetResolution num_disk num_part str_bolean +#@brief ver ogBootLoaderSetResolution +#@see ogBootLoaderSetResolution +#*/ ## + + + +#/** +# ogBootLoaderSetResolution +#@brief Define la resolucion que usuara el thema del gestor de arranque +#@param int_disk_SecondStage +#@param int_part_SecondStage +#@param str_resolution (Opcional) +#@return +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_PARTITION Partición errónea o desconocida (ogMount). +#@exception OG_ERR_NOTFOUND Fichero de configuración no encontrado: grub.cfg burg.cfg. +#*/ ## + + + + +#/** +# ogBootLoaderSetResolution +#@brief Define la resolucion que usuara el thema del gestor de arranque +#@param int_resolution1 +#@param int_resolution2 (Opcional) +#@return +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_PARTITION Partición errónea o desconocida (ogMount). +#@exception OG_ERR_NOTFOUND Fichero de configuración no encontrado: grub.cfg burg.cfg. +#*/ ## + +# ogRefindInstall bool_autoconfig +#@brief Instala y actualiza el gestor rEFInd en la particion EFI +#@param bolean_Check__auto_config true | false[default] +#@return +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND No se encuentra la partición ESP. +#@exception OG_ERR_NOTFOUND No se encuentra shimx64.efi.signed. +#@exception OG_ERR_NOTFOUND No se encuentra refind-install o refind en OGLIB +#@exception OG_ERR_PARTITION No se puede montar la partición ESP. +#@note Refind debe estar instalado en el ogLive o compartido en OGLIB +#*/ ## + +#/** +# ogGrub4dosInstallMbr int_ndisk +#@brief Genera un nuevo Codigo de arranque en el MBR del disco indicado, compatible con los SO tipo Windows, Linux. +#@param int_ndisk nº de orden del disco +#@param int_ndisk nº de orden del particion +#@return +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Tipo de partición desconocido o no se puede montar. +#@exception OG_ERR_NOTBIOS Equipo no firmware BIOS legacy +#@exception OG_ERR_NOMSDOS Disco duro no particioniado en modo msdos +#@exception OG_ERR_NOTWRITE Particion no modificable. +#*/ ## diff --git a/client/lib/python3/CacheLib.py b/client/lib/python3/CacheLib.py new file mode 100644 index 0000000..1c4357c --- /dev/null +++ b/client/lib/python3/CacheLib.py @@ -0,0 +1,369 @@ +import os +import re +import subprocess +import shutil +import sys +import platform + +import ogGlobals +import SystemLib +import DiskLib +import FileSystemLib +import CacheLib + +#/** +# ogCreateCache [int_ndisk] int_partsize +#@brief Define la caché local, por defecto en partición 4 del disco 1. +#@param int_ndisk numero de disco donde crear la cache, si no se indica es el 1 por defecto +#@param int_npart número de partición (opcional, 4 por defecto) +#@param int_partsize tamaño de la partición (en KB) +#@return (nada, por determinar) +#@exception OG_ERR_FORMAT formato incorrecto. +#@note Requisitos: sfdisk, parted, awk, sed +#@warning El tamaño de caché debe estar entre 50 MB y la mitad del disco. +#@warning La caché no puede solaparse con las particiones de datos. +#*/ ## +def ogCreateCache (ndsk=1, part=4, sizecache=0): + if not sizecache: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, '') + return None + sizecache = int (sizecache) + + DISK = DiskLib.ogDiskToDev (ndsk) + if not DISK: return None + + # PATCH Para discos nvme la particion debe ser p1, p2, etc...en lugar de 1,2, sino falla sfdisk + NVME_PREFIX = '' + if 'nvme' in DISK: + NVME_PREFIX = 'p' + + END = DiskLib.ogGetLastSector (ndsk) + SIZE = 2 * sizecache +# Inicio partición cache según el disco tenga sectores de 4k o menores + IOSIZE = 0 + fdisk_out = subprocess.run (['fdisk', '-l', DISK], capture_output=True, text=True).stdout + for l in fdisk_out.splitlines(): + items = l.split() + if len(items) < 4: continue + if 'I/O' == items[0]: + IOSIZE = int (items[3]) + break + START = 0 + if 4096 == IOSIZE: + END -= 8192 + START = END - SIZE + 2048 - (END-SIZE)%2048 + else: + START = END - SIZE + 1 + + ENDPREVPART = None + i = 1 + while True: + prev_part = part - i + if prev_part <= 0: break + ENDPREVPART = DiskLib.ogGetLastSector (ndsk, prev_part) + print (f'nati got endprevpart ({ENDPREVPART})') + if ENDPREVPART: break + i += 1 + if not ENDPREVPART: + print (f'nati no endprevpart, assuming 0') + ENDPREVPART=0 + #SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, ndsk) + #return None +# Error si tamaño no está entre límites permitidos o si se solapa con la partición anterior. + MINSIZE = 25000 + MAXSIZE = END + if SIZE < MINSIZE or SIZE > MAXSIZE or START < ENDPREVPART: + print (f'nati size ({SIZE}) < minsize ({MINSIZE}) or size > maxsize ({MAXSIZE}) or start ({START}) < endprevpart ({ENDPREVPART})') + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, ndsk) + return None + +# Desmontar todos los sistemas de archivos del disco. + FileSystemLib.ogUnmountAll (ndsk) +# Definir particiones y notificar al kernel. +# En el caso de ser disco GPT, de momento se borra la particion y se vuelve a crear, +# por lo que se pierden los datos. + pttype = DiskLib.ogGetPartitionTableType (ndsk) + print (f'nati pttype ({pttype})') + if not pttype: + pttype = 'MSDOS' + DiskLib.ogCreatePartitionTable (ndsk, pttype) + + get_ptt = DiskLib.ogGetPartitionTableType (ndsk) + print (f'nati get_ptt ({get_ptt})') + if 'GPT' == get_ptt: + # Si la tabla de particiones no es valida, volver a generarla. + if subprocess.run (['sgdisk', '-p', DISK]).returncode: + subprocess.run (['gdisk', DISK], input='2\nw\nY\n', text=True) + # Si existe la cache se borra previamente + if ogFindCache(): ogDeleteCache() + # Capturamos el codigo de particion GPT para cache + # PATCH - Cuando es GPT, la particion con codigo CACHE (CA00) no existe y no puede crearse, se cambia por LINUX (8300) + ID = DiskLib.ogTypeToId ('LINUX', 'GPT') + print (f'nati ID ({ID})') + subprocess.run (['sgdisk', DISK, f'-n{part}:{START}:{END}', f'-c{part}:CACHE', f'-t{part}:{ID}']) + elif 'MSDOS' == get_ptt: + # Si la tabla de particiones no es valida, volver a generarla. + if subprocess.run (['parted', '-s', DISK, 'print']).returncode: + subprocess.run (['fdisk', DISK], input='w\n', text=True) + # Definir particiones y notificar al kernel. + ID = DiskLib.ogTypeToId ('CACHE', 'MSDOS') + print (f'nati ID ({ID})') + # Salvamos la configuración de las particiones e incluimos la cache. + tmp = subprocess.run (['sfdisk', '--dump', DISK], capture_output=True, text=True).stdout.splitlines() + tmp = [ x for x in tmp if f'{DISK}{part}' not in x ] + tmp.append (f'{DISK}{NVME_PREFIX}{part} : start= {START}, size= {SIZE}, Id={ID}') + # Ordenamos las líneas de los dispositivos + UNIT = [ x for x in tmp if 'unit' in x ][0] + tmp = sorted ([ x for x in tmp if re.match ('^/dev', x) ]) + tmp = [UNIT, ''] + tmp + # Guardamos nueva configuración en el disco. + i = '\n'.join(tmp) + print (f'nati i ({i})') + subprocess.run (['sfdisk', '--no-reread', DISK], input=i, text=True) +# Actualiza la tabla de particiones en el kernel. + DiskLib.ogUpdatePartitionTable() + + + +#/** +# ogDeleteCache +#@brief Elimina la partición de caché local. +#@return (nada, por determinar) +#@exception OG_ERR_FORMAT formato incorrecto. +#@note Requisitos: fdisk, sgdisk, partprobe +#*/ ## +def ogDeleteCache(): + cachepart = ogFindCache() + if not cachepart: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, ogGlobals.lang.MSG_NOCACHE) + return None + + ndisk, npart = cachepart.split() + disk = DiskLib.ogDiskToDev (ndisk) + +# Desmontar todos los sistemas de archivos del disco. + FileSystemLib.ogUnmountAll (ndisk) + + ptt = DiskLib.ogGetPartitionTableType (ndisk) + if 'GPT' == ptt: + # Si la tabla de particiones no es valida, volver a generarla. + if subprocess.run (['sgdisk', '-p', disk]).returncode: + subprocess.run (['gdisk', disk], input='2\nw\nY\n', text=True) + subprocess.run (['sgdisk', disk, f'-d{npart}']) + elif 'MSDOS' == ptt: + # Si la tabla de particiones no es valida, volver a generarla. + if subprocess.run (['parted', '-s', disk, 'print']).returncode: + subprocess.run (['fdisk', disk], input='w', text=True) + # Eliminar la partición de caché. + subprocess.run (['fdisk', disk], input=f'd\n{npart}\nw', text=True) +# Borrar etiqueta de la caché. + if os.path.exists ('/dev/disk/by-label/CACHE'): + os.unlink ('/dev/disk/by-label/CACHE') +#Actualiza la tabla de particiones en el kernel. + DiskLib.ogUpdatePartitionTable() + +#/** +# ogFindCache +#@brief Detecta la partición caché local. +#@param No requiere parametros +#@return int_ndisk int_npart - devuelve el par nº de disco-nº de partición . +#@warning Si no hay cache no devuelve nada +#*/ ## +def ogFindCache(): + # Obtener el dispositivo del sistema de archivos etiquetado como "CACHE". + PART = subprocess.run (['blkid', '-L', 'CACHE'], capture_output=True, text=True).stdout.strip() + + # En discos nvme con particiones GPT la partición se detecta usando el tag PARTLABEL + if not PART: + out = subprocess.run (['blkid', '-t', 'PARTLABEL=CACHE'], capture_output=True, text=True).stdout.strip() + PART = out.split (':')[0] + + # Si no se detecta, obtener particiones marcadas de tipo caché en discos MSDOS. + if not PART: + out = subprocess.run (['sfdisk', '-l'], capture_output=True, text=True).stdout.splitlines() + for l in out: + elems = l.split (maxsplit=5) + if 6 > len (elems): continue + if 'ca' in elems[5] or 'a7' in elems[5]: + PART=elems[0] + break + + # Por último revisar todos los discos GPT y obtener las particiones etiquetadas como caché. + if not PART: + PART = '' + for d in DiskLib.ogDiskToDev(): + out = subprocess.run (['sgdisk', '-p', d], capture_output=True, text=True).stdout.splitlines() + for l in out: + elems = l.split (maxsplit=6) + if 7 > len (elems): continue + if 'CACHE' in elems[6]: + p = 'p' if 'nvme' in d else '' + PART += f' {d}{p}{elems[0]}' + + if not PART: return + return DiskLib.ogDevToDisk (PART.split()[0]) # usar la 1ª partición encontrada. + + +#/** +# ogFormatCache +#@brief Formatea el sistema de ficheros para la caché local. +#@return (por determinar) +#@warning Prueba con formato Reiser. +#@attention +#@note El sistema de archivos de la caché se queda montado. +#*/ ## +def ogFormatCache(): + cachepart = ogFindCache() + if cachepart is None: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, ogGlobals.lang.MSG_NOCACHE) + return + + cachepart = cachepart.split() + disk = DiskLib.ogDiskToDev (cachepart[0], cachepart[1]) + if not disk: return + + ogUnmountCache() + options = "extent,large_file" + if re.match("^5", platform.release()): + options += ",uninit_bg,^metadata_csum,^64bit" + + try: + subprocess.run(["mkfs.ext4", "-q", "-F", disk, "-L", "CACHE", "-O", options], check=True) + except subprocess.CalledProcessError as e: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, "CACHE") + return + + # Crear estructura básica. + mntdir = ogMountCache() + j = '/'.join ([mntdir, ogGlobals.OGIMG]) ## os.path.join doesn't work: "If a segment […] is an absolute path, all previous segments are ignored" + #print (f'cucu mntdir ({mntdir}) OGIMG ({ogGlobals.OGIMG}) j ({j})') + os.makedirs (j, exist_ok=True) + + # Incluir kernel e Initrd del ogLive + ## como lo llamo sin especificar el path entero? + #subprocess.run (['scripts/updateBootCache.py']) ## TODO + + +#/** +# ogGetCacheSize +#@brief Devuelve el tamaño definido para la partición de caché. +#@return int_partsize tamaño de la partición (en KB) +#@exception OG_ERR_PARTITION No existe partición de caché. +#*/ ## +def ogGetCacheSize(): + cachepart = ogFindCache() + if cachepart is None: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, ogGlobals.lang.MSG_NOCACHE) + return None + + disk, par = cachepart.split() + return DiskLib.ogGetPartitionSize (disk, par) + +#/** +# ogGetCacheSpace +#@brief Devuelve el espacio de disco disponible para la partición de caché. +#@return int_size tamaño disponible (en KB) +#@note El espacio disponible es el que hay entre el límite superior de la partición 3 del disco 1 y el final de dicho disco, y no puede ser superior a la mitad de dicho disco. +#*/ ## +def ogGetCacheSpace(): + cachepart = ogFindCache() + if not cachepart: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, ogGlobals.lang.MSG_NOCACHE) + return None + cachepart = cachepart.split() + + disk = DiskLib.ogDiskToDev (cachepart[0]) + if not disk: + return None + + sectors = 0 + disk_bn = os.path.basename (disk) + with open ('/proc/partitions', 'r') as fd: + proc_parts = fd.read() + for l in proc_parts.splitlines(): + items = l.split() + if len(items) < 4: continue + if items[3] == disk_bn: + sectors = 2 * int (items[2]) + if not sectors: return None + + sfdisk_out = subprocess.run (['sfdisk', '-g', disk], capture_output=True, text=True).stdout + cyls = int (sfdisk_out.split()[1]) + sectors = sectors/cyls * cyls - 1 + + ## the original code has a hard dependency on the existence of a third partition + ## if the disk has sda1, sda2 and sda4, the code fails. + ## this is an improved version + endpart3 = 0 + for trypart in [3, 2, 1]: + sfdisk_out = subprocess.run (['sfdisk', '-uS', '-l', disk], capture_output=True, text=True).stdout + for l in sfdisk_out.splitlines(): + items = l.split() + if len(items) < 6: continue + if f'{disk}{trypart}' == items[0]: + endpart3 = int (items[2]) + break + if endpart3: break + if not endpart3: return None + +# Mostrar espacio libre en KB (1 KB = 2 sectores) + if endpart3 > sectors // 2: + return (sectors - endpart3) // 2 + else: + return sectors // 4 + + +#/** +# ogMountCache +#@brief Monta la partición Cache y exporta la variable $OGCAC +#@param sin parametros +#@return path_mountpoint - Punto de montaje del sistema de archivos de cache. +#@warning Salidas de errores no determinada +#*/ ## +def ogMountCache(): + c = ogFindCache() + if not c: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, ogGlobals.lang.MSG_NOCACHE) + return None + + c = c.split() + m = FileSystemLib.ogMountFs (c[0], c[1]) + if not m: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, ogGlobals.lang.MSG_NOCACHE) + return None + return m + + +#/** +# ogUnmountCache +#@brief Desmonta la particion Cache y elimina la variable $OGCAC +#@param sin parametros +#@return nada +#@warning Salidas de errores no determinada +#*/ ## +def ogUnmountCache(): + cachepart = ogFindCache() + if cachepart is None: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, ogGlobals.lang.MSG_NOCACHE) + return + + cachepart = cachepart.split() + if not FileSystemLib.ogIsMounted (cachepart[0], cachepart[1]): return True + FileSystemLib.ogUnmountFs (cachepart[0], cachepart[1]) + + # Eliminar el enlace simbólico de /mnt/ParticiónCache. + dev = DiskLib.ogDiskToDev (cachepart[0], cachepart[1]) + dev = dev.replace ('dev', 'mnt') + if os.path.exists (dev): os.remove (dev) + + +#/** +# initCache +#@brief Simplemente llama al script initCache +#@brief Es necesario tener una función para poder pasársela a ogExecAndLog +#@param los mismos parametros que initCache +#@return lo mismo que devuelve initCache +#*/ ## +def initCache (*args): + p = subprocess.run (['/opt/opengnsys/images/nati/client/shared/scripts/initCache.py'] + list(args)) + return p.returncode diff --git a/client/lib/python3/DiskLib.py b/client/lib/python3/DiskLib.py new file mode 100644 index 0000000..5b95b05 --- /dev/null +++ b/client/lib/python3/DiskLib.py @@ -0,0 +1,1424 @@ +#/** +#@file DiskLib.py +#@brief Librería o clase Disk +#@class Disk +#@brief Funciones para gestión de discos y particiones. +#@warning License: GNU GPLv3+ +#*/ + +import filecmp +import subprocess +import shutil +import os +import re +import stat +from pathlib import Path + +import ogGlobals +import SystemLib +import CacheLib +import FileSystemLib +import InventoryLib + +# Función ficticia para lanzar parted con timeout, evitando cuelgues del programa. +def parted(*args): + parted_path = shutil.which("parted") + if parted_path: + try: + result = subprocess.run( + [parted_path] + list(args), + timeout=3, + capture_output=True, + text=True + ) + return result.stdout + except subprocess.TimeoutExpired: + return "Error: Command 'parted' timed out" + else: + return "Error: 'parted' command not found" + + +#/** +# ogCreatePartitions int_ndisk str_parttype:int_partsize ... +#@brief Define el conjunto de particiones de un disco. +#@param int_ndisk nº de orden del disco +#@param str_parttype mnemónico del tipo de partición +#@param int_partsize tamaño de la partición (en KB) +#@return (nada, por determinar) +#@exception OG_ERR_FORMAT formato incorrecto. +#@exception OG_ERR_NOTFOUND disco o partición no detectado (no es un dispositivo). +#@exception OG_ERR_PARTITION error en partición o en tabla de particiones. +#@attention El nº de partición se indica por el orden de los párametros \c parttype:partsize +#@attention Pueden definirse particiones vacías de tipo \c EMPTY +#@attention No puede definirse partición de cache y no se modifica si existe. +#@note Requisitos: sfdisk, parted, partprobe, awk +#@todo Definir atributos (arranque, oculta) y tamaños en MB, GB, etc. +#*/ ## +def ogCreatePartitions (disk, parts): + ND = disk + DISK = ogDiskToDev (ND) + if not DISK: return None + + PTTYPE = ogGetPartitionTableType (disk) + if not PTTYPE: PTTYPE = 'MSDOS' # Por defecto para discos vacíos. + + if 'GPT' == PTTYPE: + return ogCreateGptPartitions (disk, parts) + elif 'MSDOS' != PTTYPE: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, PTTYPE) + return None + + # Se calcula el ultimo sector del disco (total de sectores usables) + SECTORS = ogGetLastSector (disk) + + # Se recalcula el nº de sectores del disco si existe partición de caché. + CACHESIZE = 0 + CACHEPART = CacheLib.ogFindCache() + if CACHEPART: + cache_disk, cache_part = CACHEPART.split() + if ND == cache_disk: + CACHESIZE = int (CacheLib.ogGetCacheSize()) * 2 + # Sector de inicio (la partición 1 empieza en el sector 63). + IODISCO = ogDiskToDev (disk) + IOSIZE = 0 + fdisk_out = subprocess.run (['fdisk', '-l', IODISCO], capture_output=True, text=True).stdout + for l in fdisk_out.splitlines(): + if 'I/O' not in l: continue + items = l.split() + if len(items) < 4: continue + IOSIZE = items[3] + break + + if '4096' == IOSIZE: + START = 4096 + SECTORS -= 8192 + if CACHESIZE: + SECTORS = SECTORS - CACHESIZE + 2048 - (SECTORS - CACHESIZE) % 2048 - 1 + else: + START = 63 + if CACHESIZE: + SECTORS -= CACHESIZE + + PART = 1 + print (f'START ({START}) SECTORS ({SECTORS}) PART ({PART})') + + sfdisk_input = 'unit: sectors\n\n' + NVME_PREFIX = 'p' if 'nvme' in DISK else '' + for p in parts: + # Conservar los datos de la partición de caché. + if f'{ND} {PART}' == CACHEPART and CACHESIZE: + sfdisk_input += f'{DISK}{NVME_PREFIX}{PART} : start={SECTORS+1}, size={CACHESIZE}, Id=ca\n' + PART += 1 + + # Leer formato de cada parámetro - Tipo:Tamaño + TYPE, SIZE = p.split (':') + try: + SIZE = int (SIZE) + except ValueError: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, SIZE) + return None + + # Obtener identificador de tipo de partición válido. + ID = ogTypeToId (TYPE, 'MSDOS') + if 'CACHE' == TYPE or not ID: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, TYPE) + return None + # Comprobar tamaño numérico y convertir en sectores de 512 B. + SIZE *= 2 + # Comprobar si la partición es extendida. + EXTSTART = EXTSIZE = 0 + if 5 == ID: + if PART > 4: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, '') + return None + # El inicio de la primera partición logica es el de la extendida más 4x512 + EXTSTART = START+2048 + EXTSIZE = SIZE-2048 + # Incluir particiones lógicas dentro de la partición extendida. + if 5 == PART: + if not EXTSTART: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, '') + return None + START = EXTSTART + SECTORS = EXTSTART+EXTSIZE + + # Generar datos para la partición. + # En el caso de que la partición sea EMPTY no se crea nada + if 'EMPTY' != TYPE: + sfdisk_input += f'{DISK}{NVME_PREFIX}{PART} : start={START}, size={SIZE}, Id={ID}\n' + + START += SIZE + + # Error si se supera el nº total de sectores. + if '4096' == IOSIZE and PART > 4: + START += 2048 + if START > SECTORS: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, f'{START//2} > {SECTORS//2}') + return None + PART += 1 + + # Si no se indican las 4 particiones primarias, definirlas como vacías, conservando la partición de caché. + while PART <= 4: + if f'{ND} {PART}' == CACHEPART and CACHESIZE: + sfdisk_input += f'{DISK}{NVME_PREFIX}{PART} : start={SECTORS+1}, size={CACHESIZE}, Id=ca\n' + else: + sfdisk_input += f'{DISK}{NVME_PREFIX}{PART} : start=0, size=0, Id=0\n' + PART += 1 + # Si se define partición extendida sin lógicas, crear particion 5 vacía. + if 5 == PART and EXTSTART: + sfdisk_input += f'{DISK}5 : start={EXTSTART}, SIZE={EXTSIZE}, Id=0\n' + + # Desmontar los sistemas de archivos del disco antes de realizar las operaciones. + FileSystemLib.ogUnmountAll (ND) + if CACHESIZE: CacheLib.ogUnmountCache() + + # Si la tabla de particiones no es valida, volver a generarla. + ogCreatePartitionTable (ND) + + # Definir particiones y notificar al kernel. + #print (f'nati: about to run sfdisk: ({sfdisk_input})') + p = subprocess.run (['sfdisk', DISK], input=sfdisk_input, capture_output=True, text=True) + #print (f'nati: rc ({p.returncode}) stdout ({p.stdout}) stderr ({p.stderr})') + subprocess.run (['partprobe', DISK]) + if CACHESIZE: CacheLib.ogMountCache() + return True + + +#/** +# ogCreateGptPartitions int_ndisk str_parttype:int_partsize ... +#@brief Define el conjunto de particiones de un disco GPT +#@param int_ndisk nº de orden del disco +#@param str_parttype mnemónico del tipo de partición +#@param int_partsize tamaño de la partición (en KB) +#@return (nada, por determinar) +#@exception OG_ERR_FORMAT formato incorrecto. +#@exception OG_ERR_NOTFOUND disco o partición no detectado (no es un dispositivo). +#@exception OG_ERR_PARTITION error en partición o en tabla de particiones. +#@attention El nº de partición se indica por el orden de los párametros \c parttype:partsize +#@attention Pueden definirse particiones vacías de tipo \c EMPTY +#@attention No puede definirse partición de caché y no se modifica si existe. +#@note Requisitos: sfdisk, parted, partprobe, awk +#@todo Definir atributos (arranque, oculta) y tamaños en MB, GB, etc. +#*/ ## +def ogCreateGptPartitions (disk, parts): + ND = disk + DISK = ogDiskToDev (ND) + if not DISK: return None + + # Se calcula el ultimo sector del disco (total de sectores usables) + SECTORS = ogGetLastSector (disk) + + # Se recalcula el nº de sectores del disco si existe partición de caché. + CACHESIZE = 0 + CACHEPART = CacheLib.ogFindCache() + if CACHEPART: + cache_disk, cache_part = CACHEPART.split() + if ND == cache_disk: + CACHESIZE = int (CacheLib.ogGetCacheSize()) * 2 + if CACHESIZE: + SECTORS -= CACHESIZE + + # Si el disco es GPT empieza en el sector 2048 por defecto, pero podria cambiarse + ALIGN = int (subprocess.run (['sgdisk', '-D', DISK], capture_output=True, text=True).stdout) + START = ALIGN + PART = 1 + print (f'START ({START}) SECTORS ({SECTORS}) PART ({PART})') + + # Leer parámetros con definición de particionado. + DELOPTIONS = [] + OPTIONS = [] + for p in parts: + # Conservar los datos de la partición de caché. + if f'{ND} {PART}' == CACHEPART and CACHESIZE: + PART += 1 + + # Leer formato de cada parámetro - Tipo:Tamaño + TYPE, SIZE = p.split (':') + try: + SIZE = int (SIZE) + except ValueError: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, SIZE) + return None + + # Error si la partición es extendida (no válida en discos GPT). + if 'EXTENDED' == TYPE: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, 'EXTENDED') + return None + + # Comprobar si existe la particion actual, capturamos su tamaño para ver si cambio o no + PARTSIZE = ogGetPartitionSize (ND, PART) + # En sgdisk no se pueden redimensionar las particiones, es necesario borrarlas y volver a crealas + if PARTSIZE: + DELOPTIONS.append (f'-d{PART}') + + # Creamos la particion + # Obtener identificador de tipo de partición válido. + ID = ogTypeToId (TYPE, 'GPT') + if 'CACHE' == TYPE or not ID: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, TYPE) + return None + # Comprobar tamaño numérico y convertir en sectores de 512 B. + SIZE *= 2 + # SIZE debe ser múltiplo de ALIGN, si no gdisk lo mueve automáticamente. + SIZE = (SIZE // ALIGN) * ALIGN + + # Generar datos para la partición. + # En el caso de que la partición sea EMPTY no se crea nada + if 'EMPTY' != TYPE: + OPTIONS += [f'-n{PART}:{START}:+{SIZE}', f'-t{PART}:{ID}'] + + START += SIZE + + # Error si se supera el nº total de sectores. + if START > SECTORS: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, f'{START//2} > {SECTORS//2}') + return None + + PART += 1 + + # Desmontar los sistemas de archivos del disco antes de realizar las operaciones. + FileSystemLib.ogUnmountAll (ND) + if CACHESIZE: CacheLib.ogUnmountCache() + + # Si la tabla de particiones no es valida, volver a generarla. + ogCreatePartitionTable (ND, 'GPT') + + # Definir particiones y notificar al kernel. + #print (f'nati: about to run sgdisk ({DELOPTIONS}) ({OPTIONS}) ({DISK})') + p = subprocess.run (['sgdisk'] + DELOPTIONS + OPTIONS + [DISK]) + #print (f'nati: rc ({p.returncode}) stdout ({p.stdout}) stderr ({p.stderr})') + subprocess.run (['partprobe', DISK]) + if CACHESIZE: CacheLib.ogMountCache() + return True + + +#/** +# ogCreatePartitionTable int_ndisk [str_tabletype] +#@brief Genera una tabla de particiones en caso de que no sea valida, si es valida no hace nada. +#@param int_ndisk nº de orden del disco +#@param str_tabletype tipo de tabla de particiones (opcional) +#@return (por determinar) +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Disco o particion no corresponden con un dispositivo. +#@note tabletype: { MSDOS, GPT }, MSDOS por defecto +#@note Requisitos: fdisk, gdisk, parted +#*/ ## +def ogCreatePartitionTable (disk, createptt=None): + DISK = ogDiskToDev (disk) + if not DISK: return None + + pttype = ogGetPartitionTableType (disk) or 'MSDOS' # Por defecto para discos vacíos. + createptt = createptt or pttype + CREATE = None + +# Si la tabla actual y la que se indica son iguales, se comprueba si hay que regenerarla. + if createptt == pttype: + if 'GPT' == pttype: + try: + result = subprocess.run (['sgdisk', '-p', DISK], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + if result.returncode: + CREATE = 'GPT' + except subprocess.CalledProcessError: + CREATE = 'GPT' + elif 'MSDOS' == pttype: + try: + result = subprocess.run (['parted', '-s', DISK, 'print'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + if result.returncode: + CREATE = 'MSDOS' + except subprocess.CalledProcessError: + CREATE = 'MSDOS' + else: + CREATE = createptt.upper() + +# Dependiendo del valor de CREATE, creamos la tabla de particiones en cada caso. + if 'GPT' == CREATE: + if 'MSDOS' == pttype: + subprocess.run (['sgdisk', '-go', DISK]) + else: + subprocess.run (['gdisk', DISK], input='2\nw\nY\n', text=True) + subprocess.run (['partprobe', DISK]) + elif 'MSDOS' == CREATE: + if 'GPT' == pttype: + subprocess.run (['sgdisk', '-Z', DISK]) + # Crear y borrar una partición para que la tabla se genere bien. + subprocess.run (['fdisk', DISK], input='o\nn\np\n\n\n\nd\n\nw', text=True) + subprocess.run (['partprobe', DISK]) + + return None + + +#/** +# ogDeletePartitionTable ndisk +#@brief Borra la tabla de particiones del disco. +#@param int_ndisk nº de orden del disco +#@return la informacion propia del fdisk +#*/ ## +def ogDeletePartitionTable (disk): + DISK = ogDiskToDev (disk) + if not DISK: return None + + PTTYPE = ogGetPartitionTableType (disk) + if 'GPT' == PTTYPE: + subprocess.run (['sgdisk', '--clear', DISK]) + elif 'MSDOS' == PTTYPE: + subprocess.run (['fdisk', DISK], input='o\nw', text=True) + return + +#/** +# ogDevToDisk path_device | LABEL="str_label" | UUID="str_uuid" +#@brief Devuelve el nº de orden de dicso (y partición) correspondiente al nombre de fichero de dispositivo o a la etiqueta o UUID del sistema de archivos asociado. +#@param path_device Camino del fichero de dispositivo. +#@param str_label etiqueta de sistema de archivos. +#@param str_uuid UUID de sistema de archivos. +#@return int_ndisk (para dispositivo de disco) +#@return int_ndisk int_npartition (para dispositivo de partición). +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Dispositivo no detectado. +#@note Solo se acepta en cada llamada 1 de los 3 tipos de parámetros. +#*/ ## +def ogDevToDisk(arg_dev): + CACHEFILE = "/var/cache/disks.cfg" + + if '=' in arg_dev: + # arg_dev is "FOO=bar" + cmd = None + if arg_dev.startswith("LABEL="): cmd = ['blkid', '-L', arg_dev[6:]] + elif arg_dev.startswith("PARTLABEL="): cmd = ['realpath', '/dev/disk/by-partlabel/'+arg_dev[11:]] + elif arg_dev.startswith("PARTUUID="): cmd = ['realpath', '/dev/disk/by-partuuid/'+arg_dev[10:]] + elif arg_dev.startswith("UUID="): cmd = ['blkid', '-U', arg_dev[5:]] + if not cmd: + SystemLib.ogRaiseError([], ogGlobals.OG_ERR_FORMAT, arg_dev) + return + DEV = subprocess.run (cmd, capture_output=True, text=True).stdout.strip() + else: + # arg_dev is "/dev/something" + DEV = arg_dev + + if not os.path.exists(DEV): + SystemLib.ogRaiseError([], ogGlobals.OG_ERR_NOTFOUND, arg_dev) + return + + # Error si no es fichero de bloques o directorio (para LVM). + m = os.stat (DEV, follow_symlinks=True).st_mode + if not stat.S_ISBLK (m) and not stat.S_ISDIR (m): + SystemLib.ogRaiseError([], ogGlobals.OG_ERR_NOTFOUND, arg_dev) + return + + # Buscar en fichero de caché de discos. + PART = None + if os.path.exists(CACHEFILE): + with open(CACHEFILE, 'r') as f: + for line in f: + parts = line.strip().split(':') + if len(parts) == 2 and parts[1] == DEV: + PART = parts[0] + break + + if PART: return PART + + # Si no se encuentra, procesa todos los discos para devolver su nº de orden y de partición. + disks = ogDiskToDev() + n = 1 + for d in disks: + NVME_PREFIX = "p" if "nvme" in d else "" + if DEV.startswith(d): + return f"{n} {DEV[len(d) + len(NVME_PREFIX):]}" + n += 1 + + SystemLib.ogRaiseError([], ogGlobals.OG_ERR_NOTFOUND, arg_dev) + return + +def _getAllDisks(): + ret = [] + all_disks = subprocess.run("lsblk -n -e 1,2 -x MAJ:MIN 2>/dev/null || lsblk -n -e 1,2", shell=True, capture_output=True, text=True).stdout.splitlines() + for line in all_disks: + parts = line.split() + if parts[5] == "disk": + parts[0].replace ('!', '/') + ret.append(f"/dev/{parts[0]}") + return ret + +def _getAllVolGroups(): + vgs = subprocess.run(['vgs', '-a', '--noheadings'], capture_output=True, text=True).stdout.splitlines() + ret = [f"/dev/{line.split()[0]}" for line in vgs] + return ret + +def _getMPath(): + ret = alldisks2remove = [] + try: + mpath = subprocess.run(['multipath', '-l', '-v', '1'], capture_output=True, text=True).stdout.splitlines() + ret = [f"/dev/mapper/{line.split()[0]}" for line in mpath] + for line in subprocess.getoutput("multipath -ll").splitlines(): + if line.split()[5] == "ready": + alldisks2remove.append (f"/dev/{line.split()[2]}") + except FileNotFoundError: + pass + except subprocess.CalledProcessError: + pass + return ret, alldisks2remove + +def _getAllZFSVols(): + zfsvols = subprocess.run(['blkid'], capture_output=True, text=True).stdout.splitlines() + return [line.split(":")[0] for line in zfsvols if "zfs" in line] + +#/** +# ogDiskToDev [int_ndisk [int_npartition]] +#@brief Devuelve la equivalencia entre el nº de orden del dispositivo (dicso o partición) y el nombre de fichero de dispositivo correspondiente. +#@param int_ndisk nº de orden del disco +#@param int_npartition nº de orden de la partición +#@return Para 0 parametros: Devuelve los nombres de ficheros de los dispositivos sata/ata/usb linux encontrados. +#@return Para 1 parametros: Devuelve la ruta del disco duro indicado. +#@return Para 2 parametros: Devuelve la ruta de la particion indicada. +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Dispositivo no detectado. +#*/ ## +def ogDiskToDev (arg_disk=None, arg_part=None): + CACHEFILE = "/var/cache/disks.cfg" + + try: + if arg_part != None: + arg_part = int (arg_part) + except: + SystemLib.ogRaiseError([], ogGlobals.OG_ERR_FORMAT, f"{arg_disk} {arg_part}") + return + + try: + if arg_disk != None: + arg_disk = int (arg_disk) + except: + SystemLib.ogRaiseError([], ogGlobals.OG_ERR_FORMAT, arg_disk) + return + + # Borrar fichero de caché de configuración si hay cambios en las particiones. + proc_partitions = Path ('/proc/partitions').read_text() + tmp_partitions = Path ('/tmp/.partitions').read_text() if os.path.exists ('/tmp/.partitions') else '' + if proc_partitions != tmp_partitions: + # Guardar copia de las particiones definidas para comprobar cambios. + shutil.copy2('/proc/partitions', '/tmp/.partitions') + try: + os.remove(CACHEFILE) + except FileNotFoundError: + pass + + # Si existe una correspondencia con disco/dispositivo en el caché; mostrarlo y salir. + if arg_disk and os.path.exists (CACHEFILE): + with open(CACHEFILE, 'r') as f: + args_joined = ' '.join (map (str, filter (None, [arg_disk,arg_part]))) + for line in f: + parts = line.strip().split(':') + if len(parts) == 2 and parts[0] == args_joined: + return parts[1] + + # Continuar para detectar nuevos dispositivos. + + ALLDISKS = _getAllDisks() + + VOLGROUPS = _getAllVolGroups() + ALLDISKS += VOLGROUPS + + MPATH, ALLDISKS_to_remove =_getMPath() + for d in ALLDISKS_to_remove: + if d in ALLDISKS: ALLDISKS.remove (d) + + ZFSVOLS = _getAllZFSVols() + ALLDISKS += ZFSVOLS + + # No params: return all disks + if arg_disk is None: + return ALLDISKS + + # arg_disk is set: return it + if arg_part is None: + if arg_disk > len (ALLDISKS): + SystemLib.ogRaiseError([], ogGlobals.OG_ERR_NOTFOUND, arg_disk) + return + disk = ALLDISKS[arg_disk-1] + if not os.path.exists(disk): + SystemLib.ogRaiseError([], ogGlobals.OG_ERR_NOTFOUND, arg_disk) + return + # Actualizar caché de configuración y mostrar dispositivo. + with open(CACHEFILE, 'a') as f: + f.write(f"{arg_disk}:{disk}\n") + return disk + + # arg_disk and arg_part are set: there are several possibilities + if arg_disk > len (ALLDISKS): + SystemLib.ogRaiseError([], ogGlobals.OG_ERR_NOTFOUND, arg_disk) + return + disk = ALLDISKS[arg_disk-1] + if not os.path.exists(disk): + SystemLib.ogRaiseError([], ogGlobals.OG_ERR_NOTFOUND, arg_disk) + return + + # Plain partition + part = f"{disk}{arg_part}" + if os.path.exists(part): + # Actualizar caché de configuración y mostrar dispositivo. + with open(CACHEFILE, 'a') as f: + f.write(f"{arg_disk} {arg_part}:{part}\n") + return part + + # RAID/Multipath (tener en cuenta enlace simbólico). + part = f"{disk}p{arg_part}" + if os.path.exists(part) and stat.S_ISBLK (os.stat (part, follow_symlinks=True).st_mode): + # Actualizar caché de configuración y mostrar dispositivo. + with open(CACHEFILE, 'a') as f: + f.write(f"{arg_disk} {arg_part}:{part}\n") + return part + + part = "" + # Logical volume + if disk in VOLGROUPS: + lvscan = subprocess.run (['lvscan', '-a'], capture_output=True, text=True).stdout.splitlines() + i = 0 + for line in lvscan: + parts = line.split("'") + if parts[1].startswith(f"{disk}/") and i == arg_part: + part = parts[1] + break + i += 1 + + # ZFS volume + if disk in ZFSVOLS: + subprocess.run(["zpool", "import", "-f", "-R", "/mnt", "-N", "-a"]) + zpool = subprocess.run(['blkid', '-s', 'LABEL', '-o', 'value', disk], capture_output=True, text=True).stdout + zfs_list = subprocess.run(['zfs', 'list', '-Hp', '-o', 'name,canmount,mountpoint', '-r', zpool], capture_output=True, text=True).stdout.splitlines() + i = 0 + for line in zfs_list: + parts = line.split() + if parts[1] == "on" and parts[2] != "none": + if i == int(args[1]): + part = parts[0] + break + i += 1 + + if not part: + SystemLib.ogRaiseError([], ogGlobals.OG_ERR_NOTFOUND, f"{arg_disk} {arg_part}") + return + + # Actualizar caché de configuración y mostrar dispositivo. + with open(CACHEFILE, 'a') as f: + f.write(f"{arg_disk} {arg_part}:{part}\n") + + return part + + +#/** +# ogGetDiskSize int_ndisk +#@brief Muestra el tamaño en KB de un disco. +#@param int_ndisk nº de orden del disco +#@return int_size - Tamaño en KB del disco. +#@exception OG_ERR_FORMAT formato incorrecto. +#@exception OG_ERR_NOTFOUND disco o particion no detectado (no es un dispositivo). +#@note Requisitos: sfdisk, awk +#*/ ## +def ogGetDiskSize (disk): + DISK = ogDiskToDev (disk) + if not DISK: return None + + bn = os.path.basename (DISK) + SIZE = None + with open ('/proc/partitions', 'r') as fd: + while True: + l = fd.readline() + if not l: break + items = l.split() + if len(items) < 4: continue + if items[3] == bn: + SIZE = int (items[2]) + break + if not SIZE: + vgs_out = subprocess.run (['vgs', '--noheadings', '--units=B', '-o', 'dev_size', DISK], capture_output=True, text=True).stdout + items = vgs_out.split() + SIZE = int (items[0]) // 1024 + + return SIZE + + +#/** +# ogGetDiskType path_device +#@brief Muestra el tipo de disco (real, RAID, meta-disco, USB, etc.). +#@param path_device Dispositivo +#@exception OG_ERR_FORMAT formato incorrecto. +#@exception OG_ERR_NOTFOUND disco no detectado o no es un dispositivo de bloques. +#@note Requisitos: udevadm +#*/ ## +def ogGetDiskType (dev): + DEV = os.path.basename (dev) + + bn = os.path.basename (DEV) + MAJOR = None + with open ('/proc/partitions', 'r') as fd: + while True: + l = fd.readline() + if not l: break + items = l.split() + if len(items) < 4: continue + if items[3] == bn: + MAJOR = items[0] + break + + TYPE = None + with open ('/proc/devices', 'r') as fd: + within_block_section = False + while True: + l = fd.readline() + if not l: break + + if 'Block' in l: + within_block_section = True + continue + if not within_block_section: + continue + + items = l.split() + if len(items) < 2: continue + if items[0] == MAJOR: + TYPE = items[1].upper() + break + + # Devolver mnemónico del driver de dispositivo. + if 'SD' == TYPE: + TYPE = 'DISK' + udevadm_out = subprocess.run (['udevadm', 'info', '-q', 'property', dev], capture_output=True, text=True).stdout + for l in udevadm_out.splitlines(): + if 'ID_BUS=usb' == l[0:10]: + TYPE = 'USB' + elif 'BLKEXT' == TYPE: + TYPE = 'NVM' + elif 'SR' == TYPE or TYPE.startswith ('IDE'): + TYPE = 'CDROM' # FIXME Comprobar discos IDE. + elif 'MD' == TYPE or TYPE.startswith ('CCISS'): + TYPE = 'RAID' + elif 'DEVICE-MAPPER' == TYPE: + TYPE = 'MAPPER' # FIXME Comprobar LVM y RAID. + + return TYPE + + +#/** +# ogGetEsp +#@brief Devuelve números de disco y partición para la partición EFI (ESP). +#*/ ## +def ogGetEsp(): + devices = subprocess.run (['blkid', '-o', 'device'], capture_output=True, text=True).stdout.splitlines() + devices.sort() + for d in devices: + # Previene error para /dev/loop0 + PART = ogDevToDisk (d) + if not PART: continue + + # En discos NVMe blkid devuelve una salida del tipo: + # >/dev/loop0 + # >/dev/nvme0n1 + # >/dev/nvme0n1p1 + # al analizar la particion nvme0n1, PART solo tiene un argumento y hace que ogGetPartitionId lance un error + if len (PART) > 1: + disk, par = PART.split() + if ogGetPartitionId (disk, par) == ogTypeToId ('EFI', 'GPT'): + return PART + return None + + +#/** +# ogGetLastSector int_ndisk [int_npart] +#@brief Devuelve el último sector usable del disco o de una partición. +#@param int_ndisk nº de orden del disco +#@param int_npart nº de orden de la partición (opcional) +#@return Último sector usable. +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Disco o partición no corresponde con un dispositivo. +#@note Requisitos: sfdisk, sgdisk +#*/ ## +def ogGetLastSector (disk, par=None): + DISK = ogDiskToDev (disk) + if not DISK: return None + env = os.environ + env['LANG'] = 'C' + last = None + if par: + PART = ogDiskToDev (disk, par) + if not PART: return None + sgdisk_out = subprocess.run (['sgdisk', '-p', DISK], env=env, capture_output=True, text=True).stdout + for l in sgdisk_out.splitlines(): + items = l.split() + if len(items) < 3: continue + if str (par) != items[0]: continue + last = int (items[2]) + break + + else: + sgdisk_out = subprocess.run (['sgdisk', '-p', DISK], env=env, capture_output=True, text=True).stdout + for l in sgdisk_out.splitlines(): + if 'last usable sector' not in l: continue + items = l.split() + last = int (items[-1]) + + return last + + +#/** +# ogGetPartitionActive int_ndisk +#@brief Muestra que particion de un disco esta marcada como de activa. +#@param int_ndisk nº de orden del disco +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Disco o particion no corresponden con un dispositivo. +#@note Requisitos: parted +#@todo Queda definir formato para atributos (arranque, oculta, ...). +#*/ ## +def ogGetPartitionActive (disk): + DISK = ogDiskToDev (disk) + if DISK is None: return + + if 'LANG' in os.environ: + lang = os.environ['LANG'] + + ret = None + os.environ['LANG'] = 'C' + lines = subprocess.run (['parted', '--script', '--machine', DISK, 'print'], capture_output=True, text=True).stdout.splitlines() + for line in lines: + parts = line.split (':') + if len (parts) < 6: continue + if 'boot' in parts[6]: + ret = parts[0] + break + + if lang is None: + del os.environ['LANG'] + else: + os.environ['LAMG'] = lang + + return ret + + +#/** +# ogGetPartitionId int_ndisk int_npartition +#@brief Devuelve el mnemónico con el tipo de partición. +#@param int_ndisk nº de orden del disco +#@param int_npartition nº de orden de la partición +#@return Identificador de tipo de partición. +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Disco o partición no corresponde con un dispositivo. +#@note Requisitos: sfdisk +#*/ ## +def ogGetPartitionId (disk, par): + DISK = ogDiskToDev (disk) + if DISK is None: return + fsid = None + + pttype = ogGetPartitionTableType (disk) + if 'GPT' == pttype: + lines = subprocess.run (['sgdisk', '-p', DISK], capture_output=True, text=True).stdout.splitlines() + start_index = next (i for i, line in enumerate(lines) if 'Number' in line) + for l in lines[start_index:]: + idx, start, end, sz, sz_units, code, *rest = l.split() + if idx == str(par): + fsid = code + break + if fsid == '8300' and f'{disk} {par}' == CacheLib.ogFindCache(): + fsid = 'CA00' + elif 'MSDOS' == pttype: + fsid = subprocess.run (['sfdisk', '--part-type', DISK, par], capture_output=True, text=True).stdout.strip() + elif 'LVM' == pttype: + fsid = '10000' + elif 'ZPOOL' == pttype: + fsid = '10010' + + return fsid + + +#/** +# ogGetPartitionSize int_ndisk int_npartition +#@brief Muestra el tamano en KB de una particion determinada. +#@param int_ndisk nº de orden del disco +#@param int_npartition nº de orden de la partición +#@return int_partsize - Tamaño en KB de la partición. +#@exception OG_ERR_FORMAT formato incorrecto. +#@exception OG_ERR_NOTFOUND disco o particion no detectado (no es un dispositivo). +#@note Requisitos: sfdisk, awk +#*/ ## +def ogGetPartitionSize (disk, par): + PART = ogDiskToDev (disk, par) + if PART is None: return + + sz = subprocess.run (['partx', '-gbo', 'SIZE', PART], capture_output=True, text=True).stdout.strip() + if sz: return int (int (sz) / 1024) + + sz = subprocess.run (['lvs', '--noheadings', '-o', 'lv_size', '--units', 'k', PART], capture_output=True, text=True).stdout.strip() + if sz: return int (sz) + + return FileSystemLib.ogGetFsSize (disk, par) + + +#/** +# ogGetPartitionsNumber int_ndisk +#@brief Detecta el numero de particiones del disco duro indicado. +#@param int_ndisk nº de orden del disco +#@return Devuelve el numero paritiones del disco duro indicado +#@warning Salidas de errores no determinada +#@attention Requisitos: parted +#@note Notas sin especificar +#*/ ## +def ogGetPartitionsNumber (disk): + DISK = ogDiskToDev (disk) + if not DISK: return None + out = 0 + + pttype = ogGetPartitionTableType (disk) + if pttype in ['GPT', 'MSDOS']: + partx_out = subprocess.run (['partx', '-gso', 'NR', DISK], capture_output=True, text=True).stdout + lines = partx_out.splitlines() + if len(lines): + out = lines[-1].strip() + elif 'LVM' == pttype: + lvs_out = subprocess.run (['lvs', '--noheadings', DISK], capture_output=True, text=True).stdout + lines = lvs_out.splitlines() + out = len (lines) + elif 'ZPOOL' == pttype: + if subprocess.run (['zpool', 'list']).returncode: + subprocess.run (['modprobe', 'zfs']) + subprocess.run (['zpool', 'import', '-f', '-R', '/mnt', '-N', '-a']) + blkid = subprocess.run (['blkid', '-s', 'LABEL', '-o', 'value', DISK], capture_output=True, text=True).stdout + zfs_out = subprocess.run (['zfs', 'list', '-Hp', '-o', 'name,canmount,mountpoint', '-r', blkid], capture_output=True, text=True).stdout + out = 0 + for l in zfs_out.splitlines(): + items = l.split() + if len(items) < 3: continue + if 'on' == items[1] and 'none' != items[2]: out += 1 + + return int (out) + + +#/** +# ogGetPartitionTableType int_ndisk +#@brief Devuelve el tipo de tabla de particiones del disco (GPT o MSDOS) +#@param int_ndisk nº de orden del disco +#@return str_tabletype - Tipo de tabla de paritiones +#@warning Salidas de errores no determinada +#@note tabletype = { MSDOS, GPT } +#@note Requisitos: blkid, parted, vgs +#*/ ## +def ogGetPartitionTableType (disk): + DISK = ogDiskToDev (disk) + if DISK is None: return + + m = os.stat (DISK, follow_symlinks=True).st_mode + if stat.S_ISBLK (m): + lines = subprocess.run (['parted', '--script', '--machine', DISK, 'print'], capture_output=True, text=True).stdout.splitlines() + for l in lines: + elems = l.split (':') + if DISK == elems[0]: + type = elems[5].upper() + break + + # Comprobar si es volumen lógico. + if os.path.isdir (DISK) and 0 == subprocess.run (['vgs', DISK]).returncode: + type = 'LVM' + + # Comprobar si es pool de ZFS. + if not type or 'UNKNOWN' == type: + if 'zfs' in subprocess.run (['blkid', '-s', 'TYPE', DISK], capture_output=True, text=True).stdout: + type = 'ZPOOL' + + return type + + +#/** +# ogGetPartitionType int_ndisk int_npartition +#@brief Devuelve el mnemonico con el tipo de partición. +#@param int_ndisk nº de orden del disco +#@param int_npartition nº de orden de la partición +#@return Mnemonico +#@note Mnemonico: valor devuelto por ogIdToType. +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Disco o particion no corresponden con un dispositivo. +#*/ ## +def ogGetPartitionType (disk, par): + ID = ogGetPartitionId (disk, par) + if ID is None: return None + + return ogIdToType (ID) + + +#/** +# ogHidePartition int_ndisk int_npartition +#@brief Oculta un apartición visible. +#@param int_ndisk nº de orden del disco +#@param int_npartition nº de orden de la partición +#@return (nada) +#@exception OG_ERR_FORMAT formato incorrecto. +#@exception OG_ERR_NOTFOUND disco o particion no detectado (no es un dispositivo). +#@exception OG_ERR_PARTITION tipo de partición no reconocido. +#*/ ## +def ogHidePartition (disk, par): + PART = ogDiskToDev (disk, par) + if not PART: return None + + TYPE = ogGetPartitionType (disk, par) + if 'NTFS' == TYPE: NEWTYPE = 'HNTFS' + elif 'FAT32' == TYPE: NEWTYPE = 'HFAT32' + elif 'FAT16' == TYPE: NEWTYPE = 'HFAT16' + elif 'FAT12' == TYPE: NEWTYPE = 'HFAT12' + elif 'WINDOWS' == TYPE: NEWTYPE = 'WIN-RESERV' + else: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, TYPE) + return None + + ogSetPartitionType (disk, par, NEWTYPE) + + +#/** +# ogIdToType int_idpart +#@brief Devuelve el identificador correspondiente a un tipo de partición. +#@param int_idpart identificador de tipo de partición. +#@return str_parttype mnemónico de tipo de partición. +#@exception OG_ERR_FORMAT Formato incorrecto. +#*/ ## +def ogIdToType (ID): + # Obtener valor hexadecimal de 4 caracteres rellenado con 0 por delante. + ID = ID.zfill(4).lower() + + id2type = { + '0000': 'EMPTY', + '0001': 'FAT12', + '0005': 'EXTENDED', + '000f': 'EXTENDED', + '0006': 'FAT16', + '000e': 'FAT16', + '0007': 'NTFS', + '000b': 'FAT32', + '000c': 'FAT32', + '0011': 'HFAT12', + '0012': 'COMPAQDIAG', + '0016': 'HFAT16', + '001e': 'HFAT16', + '0017': 'HNTFS', + '001b': 'HFAT32', + '001c': 'HFAT32', + '0042': 'WIN-DYNAMIC', + '0082': 'LINUX-SWAP', + '8200': 'LINUX-SWAP', + '0083': 'LINUX', + '8300': 'LINUX', + '008e': 'LINUX-LVM', + '8E00': 'LINUX-LVM', + '00a5': 'FREEBSD', + 'a503': 'FREEBSD', + '00a6': 'OPENBSD', + '00a7': 'CACHE', # (compatibilidad con Brutalix) + '00af': 'HFS', + 'af00': 'HFS', + '00be': 'SOLARIS-BOOT', + 'be00': 'SOLARIS-BOOT', + '00bf': 'SOLARIS', + 'bf00145': 'SOLARIS', + '00ca': 'CACHE', + 'ca00': 'CACHE', + '00da': 'DATA', + '00ee': 'GPT', + '00ef': 'EFI', + 'ef00': 'EFI', + '00fb': 'VMFS', + '00fd': 'LINUX-RAID', + 'fd00': 'LINUX-RAID', + '0700': 'WINDOWS', + '0c01': 'WIN-RESERV', + '7f00': 'CHROMEOS-KRN', + '7f01': 'CHROMEOS', + '7f02': 'CHROMEOS-RESERV', + '8301': 'LINUX-RESERV', + 'a500': 'FREEBSD-DISK', + 'a501': 'FREEBSD-BOOT', + 'a502': 'FREEBSD-SWAP', + 'ab00': 'HFS-BOOT', + 'af01': 'HFS-RAID', + 'bf02': 'SOLARIS-SWAP', + 'bf03': 'SOLARIS-DISK', + 'ef01': 'MBR', + 'ef02': 'BIOS-BOOT', + '10000': 'LVM-LV', + '10010': 'ZFS-VOL', + } + if ID in id2type: + return id2type[ID] + return 'UNKNOWN' + + +# ogIsDiskLocked int_ndisk +#@brief Comprueba si un disco está bloqueado por una operación de uso exclusivo. +#@param int_ndisk nº de orden del disco +#@return Código de salida: 0 - bloqueado, 1 - sin bloquear o error. +#@note Los ficheros de bloqueo se localizan en \c /var/lock/dev, siendo \c dev el dispositivo de la partición o de su disco, sustituyendo el carácter "/" por "-". +#*/ ## +def ogIsDiskLocked (disk): + DISK = ogDiskToDev (disk) + if not DISK: return False + return os.path.isfile (f'/var/lock/lock{DISK.replace("/", "-")}') + + +#/** +# ogListPartitions int_ndisk +#@brief Lista las particiones definidas en un disco. +#@param int_ndisk nº de orden del disco +#@return str_parttype:int_partsize ... +#@exception OG_ERR_FORMAT formato incorrecto. +#@exception OG_ERR_NOTFOUND disco o particion no detectado (no es un dispositivo). +#@note Requisitos: \c parted \c awk +#@attention El nº de partición se indica por el orden de los párametros \c parttype:partsize +#@attention Las tuplas de valores están separadas por espacios. +#*/ ## +def ogListPartitions (disk): + DISK = ogDiskToDev (disk) + if not DISK: return None + + p = [] + NPARTS = ogGetPartitionsNumber (disk) + for PART in range (1, NPARTS + 1): + t = ogGetPartitionType (disk, PART) + TYPE = ogGetPartitionType (disk, PART) or 'EMPTY' + SIZE = ogGetPartitionSize (disk, PART) or 0 + p.append (f'{TYPE}:{SIZE}') + + return p + + +#/** +# ogListPrimaryPartitions int_ndisk +#@brief Metafunción que lista las particiones primarias no vacías de un disco. +#@param int_ndisk nº de orden del disco +#@see ogListPartitions +#*/ ## +def ogListPrimaryPartitions (disk): + PTTYPE = ogGetPartitionTableType (disk) + if not PTTYPE: return None + + PARTS = ogListPartitions (disk) + if not PARTS: return None + + if 'GPT' == PTTYPE: + res = [] + for idx in range (len(PARTS),0,-1): + item = PARTS[idx-1] + if 0==len(res) and 'EMPTY:0' == item: continue + res.insert (0, item) + return res + elif 'MSDOS' == PTTYPE: + return PARTS[0:4] + + +#/** +# ogListLogicalPartitions int_ndisk +#@brief Metafunción que lista las particiones lógicas de una tabla tipo MSDOS. +#@param int_ndisk nº de orden del disco +#@see ogListPartitions +#*/ ## +def ogListLogicalPartitions (disk): + PTTYPE = ogGetPartitionTableType (disk) + if not PTTYPE: return None + + PARTS = ogListPartitions (disk) + if not PARTS: return None + + return PARTS[4:] + + +#/** +# ogLockDisk int_ndisk +#@brief Genera un fichero de bloqueo para un disco en uso exlusivo. +#@param int_ndisk nº de orden del disco +#@return (nada) +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Disco o particion no corresponden con un dispositivo. +#@note El fichero de bloqueo se localiza en \c /var/lock/disk, siendo \c disk el dispositivo del disco, sustituyendo el carácter "/" por "-". +#*/ ## +def ogLockDisk (disk): + DISK = ogDiskToDev (disk) + if not DISK: return None + open (f'/var/lock/lock{DISK.replace("/", "-")}', 'a').close() + + +#/** +# ogSetPartitionActive int_ndisk int_npartition +#@brief Establece cual es la partición activa de un disco. +#@param int_ndisk nº de orden del disco +#@param int_npartition nº de orden de la partición +#@return (nada). +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Disco o partición no corresponden con un dispositivo. +#@note Requisitos: parted +#*/ ## +def ogSetPartitionActive (disk, par): + if InventoryLib.ogIsEfiActive(): + SystemLib.ogEcho (['session', 'log'], 'warning', f'EFI: {ogGlobals.lang.MSG_DONTUSE} ogSetPartitionActive') + return + + DISK = ogDiskToDev (disk) + if DISK is None: return + + PART = ogDiskToDev (disk, par) + if PART is None: return + + subprocess.run (["parted", "-s", DISK, "set", par, "boot", "on"]) + return + + +#/** +# ogSetPartitionId int_ndisk int_npartition hex_partid +#@brief Cambia el identificador de la partición. +#@param int_ndisk nº de orden del disco +#@param int_npartition nº de orden de la partición +#@param hex_partid identificador de tipo de partición +#@return (nada) +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Disco o partición no corresponden con un dispositivo. +#@exception OG_ERR_OUTOFLIMIT Valor no válido. +#@exception OG_ERR_PARTITION Error al cambiar el id. de partición. +#@attention Requisitos: fdisk, sgdisk +#*/ ## +## for GPT partitions, id must be one of the valid types as reported by 'sgdisk --list-types', eg. 0700 or 8200 +def ogSetPartitionId (disk, par, id): + DISK = ogDiskToDev (disk) + if not DISK: return None + + PART = ogDiskToDev (disk, par) + if not PART: return None + +# Error si el id. de partición no es hexadecimal. + if not re.match ('^[0-9A-Fa-f]+$', id): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_OUTOFLIMIT, id) + return None + +# Elección del tipo de partición. + PTTYPE = ogGetPartitionTableType (disk) + if 'GPT' == PTTYPE: + p = subprocess.run (['sgdisk', f'-t{par}:{id.upper()}', DISK]) + elif 'MSDOS' == PTTYPE: + p = subprocess.run (['sfdisk', '--id', DISK, par, id.upper()]) + else: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_OUTOFLIMIT, f'{disk},{PTTYPE}') + return None + +# MSDOS) Correcto si fdisk sin error o con error pero realiza Syncing + if 0 == p.returncode: + subprocess.run (['partprobe', DISK]) + return True + else: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, f'{disk},{part},{id}') + return None + + +#/** +# ogSetPartitionSize int_ndisk int_npartition int_size +#@brief Muestra el tamano en KB de una particion determinada. +#@param int_ndisk nº de orden del disco +#@param int_npartition nº de orden de la partición +#@param int_size tamaño de la partición (en KB) +#@return (nada) +#@exception OG_ERR_FORMAT formato incorrecto. +#@exception OG_ERR_NOTFOUND disco o particion no detectado (no es un dispositivo). +#@note Requisitos: sfdisk, awk +#@todo Compruebar que el tamaño sea numérico positivo y evitar que pueda solaparse con la siguiente partición. +#*/ ## +def ogSetPartitionSize (disk, par, size): + DISK = ogDiskToDev (disk) + if not DISK: return None + + PART = ogDiskToDev (disk, par) + if not PART: return None + +# Convertir tamaño en KB a sectores de 512 B. + SIZE = 2 * int (size) +# Redefinir el tamaño de la partición. + p = subprocess.run (['sfdisk', '-f', '-uS', f'-N{par}', DISK], input=f',{SIZE}', text=True) + if p.returncode: + ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, f'{disk},{par}') + return None + subprocess.run (['partprobe', DISK]) + + +#/** +# ogSetPartitionType int_ndisk int_npartition str_type +#@brief Cambia el identificador de la partición. +#@param int_ndisk nº de orden del disco +#@param int_npartition nº de orden de la partición +#@param str_type mnemónico de tipo de partición +#@return (nada) +#@attention Requisitos: fdisk, sgdisk +#*/ ## +def ogSetPartitionType (disk, par, t): + DISK = ogDiskToDev (disk) + if not DISK: return None + + PART = ogDiskToDev (disk, par) + if not PART: return None + + PTTYPE = ogGetPartitionTableType (disk) + if not PTTYPE: return None + + ID = ogTypeToId (t, PTTYPE) + if not ID: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, f'{t},{PTTYPE}') + return None + + ogSetPartitionId (disk, par, ID) + + +#/** +# ogTypeToId str_parttype [str_tabletype] +#@brief Devuelve el identificador correspondiente a un tipo de partición. +#@param str_parttype mnemónico de tipo de partición. +#@param str_tabletype mnemónico de tipo de tabla de particiones (MSDOS por defecto). +#@return int_idpart identificador de tipo de partición. +#@exception OG_ERR_FORMAT Formato incorrecto. +#@note tabletype = { MSDOS, GPT }, (MSDOS, por defecto) +#*/ ## +#ogTypeToId ('LINUX') => "83" +#ogTypeToId ('LINUX', 'MSDOS') => "83" +def ogTypeToId (type, pttype='MSDOS'): + data = { + 'GPT': { + 'EMPTY': '0', + 'WINDOWS': '0700', + 'NTFS': '0700', + 'EXFAT': '0700', + 'FAT32': '0700', + 'FAT16': '0700', + 'FAT12': '0700', + 'HNTFS': '0700', + 'HFAT32': '0700', + 'HFAT16': '0700', + 'HFAT12': '0700', + 'WIN-RESERV': '0C01', + 'CHROMEOS-KRN': '7F00', + 'CHROMEOS': '7F01', + 'CHROMEOS-RESERV': '7F02', + 'LINUX-SWAP': '8200', + 'LINUX': '8300', + 'EXT2': '8300', + 'EXT3': '8300', + 'EXT4': '8300', + 'REISERFS': '8300', + 'REISER4': '8300', + 'XFS': '8300', + 'JFS': '8300', + 'LINUX-RESERV': '8301', + 'LINUX-LVM': '8E00', + 'FREEBSD-DISK': 'A500', + 'FREEBSD-BOOT': 'A501', + 'FREEBSD-SWAP': 'A502', + 'FREEBSD': 'A503', + 'HFS-BOOT': 'AB00', + 'HFS': 'AF00', + 'HFS+': 'AF00', + 'HFSPLUS': 'AF00', + 'HFS-RAID': 'AF01', + 'SOLARIS-BOOT': 'BE00', + 'SOLARIS': 'BF00', + 'SOLARIS-SWAP': 'BF02', + 'SOLARIS-DISK': 'BF03', + 'CACHE': 'CA00', + 'EFI': 'EF00', + 'LINUX-RAID': 'FD00', + }, + 'MSDOS': { + 'EMPTY': '0', + 'FAT12': '1', + 'EXTENDED': '5', + 'FAT16': '6', + 'WINDOWS': '7', + 'NTFS': '7', + 'EXFAT': '7', + 'FAT32': 'b', + 'HFAT12': '11', + 'HFAT16': '16', + 'HNTFS': '17', + 'HFAT32': '1b', + 'LINUX-SWAP': '82', + 'LINUX': '83', + 'EXT2': '83', + 'EXT3': '83', + 'EXT4': '83', + 'REISERFS': '83', + 'REISER4': '83', + 'XFS': '83', + 'JFS': '83', + 'LINUX-LVM': '8e', + 'FREEBSD': 'a5', + 'OPENBSD': 'a6', + 'HFS': 'af', + 'HFS+': 'af', + 'SOLARIS-BOOT': 'be', + 'SOLARIS': 'bf', + 'CACHE': 'ca', + 'DATA': 'da', + 'GPT': 'ee', + 'EFI': 'ef', + 'VMFS': 'fb', + 'LINUX-RAID': 'fd', + }, + 'LVM': { + 'LVM-LV': '10000', + }, + 'ZVOL': { + 'ZFS-VOL': '10010', + }, + } + + if pttype.upper() not in data: return None + if type.upper() not in data[pttype.upper()]: return None + return data[pttype.upper()][type.upper()] + + +#/** +# ogUnhidePartition int_ndisk int_npartition +#@brief Hace visible una partición oculta. +#@param int_ndisk nº de orden del disco +#@param int_npartition nº de orden de la partición +#@return (nada) +#@exception OG_ERR_FORMAT formato incorrecto. +#@exception OG_ERR_NOTFOUND disco o particion no detectado (no es un dispositivo). +#@exception OG_ERR_PARTITION tipo de partición no reconocido. +#*/ ## +def ogUnhidePartition (disk, par): + PART = ogDiskToDev (disk, par) + if not PART: return None + + TYPE = ogGetPartitionType (disk, par) + if 'HNTFS' == TYPE: NEWTYPE = 'NTFS' + elif 'HFAT32' == TYPE: NEWTYPE = 'FAT32' + elif 'HFAT16' == TYPE: NEWTYPE = 'FAT16' + elif 'HFAT12' == TYPE: NEWTYPE = 'FAT12' + elif 'WIN-RESERV' == TYPE: NEWTYPE = 'WINDOWS' + else: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, TYPE) + return None + + ogSetPartitionType (disk, par, NEWTYPE) + + +#/** +# ogUnlockDisk int_ndisk +#@brief Elimina el fichero de bloqueo para un disco. +#@param int_ndisk nº de orden del disco +#@return (nada) +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Disco o particion no corresponden con un dispositivo. +#@note El fichero de bloqueo se localiza en \c /var/lock/disk, siendo \c disk el dispositivo del disco, sustituyendo el carácter "/" por "-". +#*/ ## +def ogUnlockDisk (disk): + DISK = ogDiskToDev (disk) + if not DISK: return None + os.remove (f'/var/lock/lock{DISK.replace("/", "-")}') + + +#/** +# ogUpdatePartitionTable +#@brief Fuerza al kernel releer la tabla de particiones de los discos duros +#@param no requiere +#@return informacion propia de la herramienta +#@note Requisitos: \c partprobe +#@warning pendiente estructurar la funcion a opengnsys +#*/ ## +def ogUpdatePartitionTable(): + for disk in ogDiskToDev(): + subprocess.run(["partprobe", disk]) diff --git a/client/lib/python3/FileLib.py b/client/lib/python3/FileLib.py new file mode 100644 index 0000000..2a4adf4 --- /dev/null +++ b/client/lib/python3/FileLib.py @@ -0,0 +1,517 @@ +#/** +#@file FileLib.py +#@brief Librería o clase File +#@class File +#@brief Funciones para gestión de archivos y directorios. +#@warning License: GNU GPLv3+ +#*/ + +import subprocess +import os +import shutil +import hashlib + +import ogGlobals +import SystemLib +import CacheLib +import FileSystemLib + +#/** +# ogCalculateChecksum [ str_repo | int_ndisk int_npart ] path_filepath +#@brief Devuelve la suma de comprobación (checksum) de un fichero. +#@param path_filepath camino del fichero (independiente de mayúsculas) +#@param str_repo repositorio de ficheros +#@param int_ndisk nº de orden del disco +#@param int_npartition nº de orden de la partición +#@return hex_checksum Checksum del fichero +#*/ ## +#ogCalculateChecksum ([ str_repo | int_ndisk int_npartition ] path_filepath") +#ogCalculateChecksum (container='REPO', file='ubuntu.img') ==> ef899299caf8b517ce36f1157a93d8bf +#ogCalculateChecksum (disk=1, par=1, file='ubuntu.img') ==> ef899299caf8b517ce36f1157a93d8bf +def ogCalculateChecksum (disk=None, par=None, container=None, file=None): + if file is None: + raise TypeError ('missing required argument: "file"') + + if container is not None: + if disk is None and par is None: + ## we were given container= + f = ogGetPath (src=container, file=file) + dev_err = f'{container} {file}' + print (f'ogGetPath (src=({container}), file=({file})) = f ({f})') + else: + raise TypeError ('argument "container" can be specified along neither "disk" nor "par"') + + else: + if disk is not None and par is not None: + ## we were given disk= par= + f = ogGetPath (src=f'{disk} {par}', file=file) + dev_err = f'{disk} {par} {file}' + print (f'ogGetPath (src=({disk} {par}), file=({file})) = f ({f})') + elif disk is None and par is None: + ## we were given nothing + f = ogGetPath (file=file) + dev_err = file + print (f'ogGetPath (file=({file})) = f ({f})') + else: + raise TypeError ('if one of "disk" and "par" are specified, then both must be') + + if not f: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, dev_err) + return + + last_n_bytes = 1024*1024 + if last_n_bytes >= os.stat (f).st_size: + return ogCalculateFullChecksum (disk, par, container, file) + with open (f, 'rb') as fd: + fd.seek (-last_n_bytes, os.SEEK_END) + data = fd.read() + md5 = hashlib.md5(data).hexdigest() + + return md5 + + +#/** +# ogCompareChecksumFiles [ str_repo | int_ndisk int_npart ] path_source [ str_repo | int_ndisk int_npart ] path_target +#@brief Metafunción que compara las sumas de comprobación almacenadas de 2 ficheros. +#@return bool_compare Valor de comparación. +#@warning No es necesario especificar la extensión ".sum". +#*/ ## +def ogCompareChecksumFiles(*args): + # Variables locales. + ARGS = args + if "help" in args: + SystemLib.ogHelp("$FUNCNAME", "$FUNCNAME [ str_repo | int_ndisk int_npartition ] path_filepath", "if $FUNCNAME REPO ubuntu.img CACHE ubuntu.img; then ...; fi") + return + + ARGS = args + if args[0].startswith("/"): + # Camino completo. */ (Comentario Doxygen) + SOURCE = ogGetPath(*args[:1]) + args = args[1:] + elif args[0].isdigit(): + # ndisco npartición. + SOURCE = ogGetPath(*args[:3]) + args = args[3:] + else: + # Otros: repo, cache, cdrom (no se permiten caminos relativos). + SOURCE = ogGetPath(*args[:2]) + args = args[2:] + + TARGET = ogGetPath(*args) + + try: + with open(f"{SOURCE}.sum", "r") as source_file: + source_checksum = source_file.read().strip() + with open(f"{TARGET}.sum", "r") as target_file: + target_checksum = target_file.read().strip() + + return source_checksum == target_checksum + except FileNotFoundError: + return False + + +#/** +# ogCalculateFullChecksum [ str_repo | int_ndisk int_npart ] path_filepath +#@brief Devuelve la suma COMPLETA de comprobación (checksum) de un fichero. +#@param path_filepath camino del fichero (independiente de mayúsculas) +#@param str_repo repositorio de ficheros +#@param int_ndisk nº de orden del disco +#@param int_npartition nº de orden de la partición +#@return hex_checksum Checksum del fichero +#*/ ## +def ogCalculateFullChecksum (disk=None, par=None, container=None, file=None): + if file is None: + raise TypeError ('missing required argument: "file"') + + if container is not None: + if disk is None and par is None: + ## we were given container= + f = ogGetPath (src=container, file=file) + dev_err = f'{container} {file}' + print (f'ogGetPath (src=({container}), file=({file})) = f ({f})') + else: + raise TypeError ('argument "container" can be specified along neither "disk" nor "par"') + + else: + if disk is not None and par is not None: + ## we were given disk= par= + f = ogGetPath (src=f'{disk} {par}', file=file) + dev_err = f'{disk} {par} {file}' + print (f'ogGetPath (src=({disk} {par}), file=({file})) = f ({f})') + elif disk is None and par is None: + ## we were given nothing + f = ogGetPath (file=file) + dev_err = file + print (f'ogGetPath (file=({file})) = f ({f})') + else: + raise TypeError ('if one of "disk" and "par" are specified, then both must be') + + if not f: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, dev_err) + return + + md5 = hashlib.md5() + with open (f, 'rb') as fd: + for chunk in iter (lambda: fd.read (64*1024), b''): + md5.update (chunk) + return md5.hexdigest() + + + + +#/** +# ogCopyFile [ str_repo | int_ndisk int_npart ] path_source [ str_repo | int_ndisk int_npart ] path_target +#@brief Metafunción para copiar un fichero de sistema OpenGnSys a un directorio. +#@see ogGetPath +#@return Progreso de la copia. +#@warning Deben existir tanto el fichero origen como el directorio destino. +#*/ ## +#ogCopyFile (src, dst) +#ogCopyFile ({container='REPO', file='newfile.txt'}, {disk=1, par=2, file='/tmp/newfile.txt'}) +#ogCopyFile ({disk=1, par=2, file='/tmp/newfile.txt'}, {container='REPO', file='newfile.txt'}) +def ogCopyFile (src, dst): + for elem in src, dst: + if elem == src: which = 'source' + else: which = 'target' + + if 'file' not in elem: + raise TypeError ('missing required argument in {which} dict:: "file"') + + if 'container' in elem: + if 'disk' not in elem and 'par' not in elem: + ## we were given container= + path = ogGetPath (src=elem['container'], file=elem['file']) + dev_err = f'{elem["container"]} {elem["file"]}' + print (f'ogGetPath {which} (src=({elem["container"]}), file=({elem["file"]})) = path ({path})') + else: + raise TypeError ('argument "container" can be specified along neither "disk" nor "par"') + + else: + if 'disk' in elem and 'par' in elem: + ## we were given disk= par= + path = ogGetPath (src=f'{elem["disk"]} {elem["par"]}', file=elem['file']) + dev_err = f'{elem["disk"]} {elem["par"]} {elem["file"]}' + print (f'ogGetPath {which} (src=({elem["disk"]} {elem["par"]}), file=({elem["file"]})) = path ({path})') + elif 'disk' not in elem and 'par' not in elem: + ## we were given nothing + path = ogGetPath (file=elem['file']) + dev_err = elem['file'] + print (f'ogGetPath {which} (file=({elem["file"]})) = path ({path})') + else: + raise TypeError ('if one of "disk" and "par" are specified, then both must be') + + if elem == src: + SOURCE = path + src_dev_err = dev_err + else: + TARGET = path + dst_dev_err = dev_err + + if not SOURCE: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f'device or file {src_dev_err} not found') + return + if not TARGET: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f'device or file {dst_dev_err} not found') + return + print (f'nati: ogCopyFile: SOURCE ({SOURCE}) TARGET ({TARGET})') + + # Copiar fichero (para evitar problemas de comunicaciones las copias se hacen con rsync en vez de cp). + print (f'nati: ogCopyFile: rsync --progress --inplace -avh ({SOURCE}) ({TARGET})') + result = subprocess.run(["rsync", "--progress", "--inplace", "-avh", SOURCE, TARGET], capture_output=True, text=True) + return result.returncode + + +#/** +# ogDeleteFile [ str_repo | int_ndisk int_npartition ] path_filepath +#@brief Metafunción que borra un fichero de un dispositivo. +#@see ogGetPath +#@version 0.9 - Pruebas con OpenGnSys. +#@author Ramon Gomez, ETSII Universidad de Sevilla +#@date 2009-09-29 +#*/ ## +#ogDeleteFile ([ str_repo | int_ndisk int_npartition ] path_file) +#ogDeleteFile (container='REPO', file='/tmp/newfile.txt') +#ogDeleteFile (disk=1, par=2, file='/tmp/newfile.txt') +def ogDeleteFile (disk=None, par=None, container=None, file=None): + if file is None: + raise TypeError ('missing required argument: "file"') + + if container is not None: + if disk is None and par is None: + ## we were given container= + src = container + else: + raise TypeError ('argument "container" can be specified along neither "disk" nor "par"') + else: + if disk is not None and par is not None: + ## we were given disk= par= + src = f'{disk} {par}' + else: + ## we were given nothing + raise TypeError ('either "container" or both "disk" and "par" must be specified') + + f = ogGetPath (src=src, file=file) + if not f: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f'{src} {file}') + return + + try: + os.remove (f) + except OSError as e: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_GENERIC, str (e)) + return + + +#/** +# ogDeleteTree [ str_repo | int_ndisk int_npartition ] path_dirpath +#@brief Metafunción que borra todo un subárbol de directorios de un dispositivo. +#@see ogGetPath +#*/ ## +#ogDeleteTree (container='REPO', file='/tmp/newdir') +#ogDeleteTree (disk=1, par=2, file='/tmp/newdir') +def ogDeleteTree (disk=None, par=None, container=None, file=None): + if file is None: + raise TypeError ('missing required argument: "file"') + + if container is not None: + if disk is None and par is None: + ## we were given container= + src = container + else: + raise TypeError ('argument "container" can be specified along neither "disk" nor "par"') + else: + if disk is not None and par is not None: + ## we were given disk= par= + src = f'{disk} {par}' + else: + ## we were given nothing + raise TypeError ('either "container" or both "disk" and "par" must be specified') + + f = ogGetPath (src=src, file=file) + if not f: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f'{src} {file}') + return + + try: + shutil.rmtree (f) + except OSError as e: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, e) + return + + +#/** +# ogGetPath [ str_repo | int_ndisk int_npartition ] path_filepath +#@brief Inicia el proceso de arranque de un sistema de archivos. +#@param path_filepath camino del fichero (independiente de mayúsculas) +#@param str_repo repositorio de ficheros +#@param int_ndisk nº de orden del disco +#@param int_npartition nº de orden de la partición +#@return path_file - camino completo real del fichero. +#@note repo = { REPO, CACHE, CDROM } +#@note Requisitos: \c grep \c sed +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Fichero o dispositivo no encontrado. +#@exception OG_ERR_PARTITION Tipo de partición desconocido o no se puede montar. +#@warning En caso de error, sólo devuelve el código y no da mensajes. +#@todo Terminar de definir parámetros para acceso a repositorios. +#*/ ## +#ogGetPath (file='/mnt/sda1/windows/system32') ==> '/mnt/sda1/WINDOWS/System32' +#ogGetPath (src='REPO', file='/etc/fstab') ==> '/opt/opengnsys/images/etc/fstab' +#ogGetPath (src='1 1', file='/windows/system32') ==> '/mnt/sda1/WINDOWS/System32' +def ogGetPath (src=None, file=None): + if file is None: + raise TypeError ('missing required argument: "file"') + + f = file + if src is not None: + if 'REPO' == src: + f = os.path.join (ogGlobals.OGIMG, file.strip('/')) + elif 'CACHE' == src: + mntdir = CacheLib.ogMountCache() + if not mntdir: return None + f = os.path.join (mntdir, ogGlobals.OGIMG.strip('/'), file.strip('/')) + elif 'CDROM' == src: + mntdir = FileSystemLib.ogMountCdrom() + if not mntdir: return None + f = os.path.join (mntdir, file.strip('/')) + else: + try: + disk, part = src.split() + except ValueError: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, '') + return + mntdir = FileSystemLib.ogMount (disk, part) + if not mntdir: return None + f = os.path.join (mntdir, file.strip('/')) + + f = os.path.normpath (f) + + if os.path.exists (f): + filepath = f + #print (f'f ({f}) existe, filepath=f ({filepath})') + else: + # Buscar el nombre correcto en cada subdirectorio del camino. + prevfile = '' + filepath = '/' + #print (f'f ({f}) prevfile ({prevfile})') + while f != prevfile: + #print ('\nno son iguales, nueva iteracion...') + f_first_component = f.split ('/')[0] ## take 1st component + ls_path = os.path.join (filepath, f_first_component) ## "ls" makes reference to the original bash version + #print (f'f_first_component ({f_first_component}) ls_path ({ls_path})') + + ## build filepath to return + if os.path.exists (ls_path): + filepath = ls_path + #print (f'ls_path existe, filepath ({filepath})') + else: + filepath = subprocess.run (['find', filepath, '-maxdepth', '1', '-iname', f_first_component, '-print'], capture_output=True, text=True).stdout.strip() + #print (f'ls_path no existe, filepath ({filepath})') + + prevfile = f + if '/' in f: + f = '/'.join (f.split('/')[1:]) ## remove 1st component + #print (f'f ({f}) prevfile ({prevfile})') + + return filepath + + +#/** +# ogGetParentPath [ str_repo | int_ndisk int_npartition ] path_filepath +#@brief Metafunción que devuelve el camino del directorio padre. +#@see ogGetPath +#*/ ## + +#ogGetParentPath ([ str_repo | int_ndisk int_npartition ] path_filepath +#ogGetParentPath ( file='/mnt/sda1/windows/system32') ==> '/mnt/sda1/WINDOWS' +#ogGetParentPath (src='REPO', file='/etc/fstab') ==> '/opt/opengnsys/images/etc' +#ogGetParentPath (src='1 1', file='/windows/system32') ==> '/mnt/sda1/WINDOWS' +def ogGetParentPath (src=None, file=None): + if file is None: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, '') + return + + if src is None: + return ogGetPath (file=os.path.dirname (file)) + else: + return ogGetPath (src=src, file=os.path.dirname('/'+file)) + +#/** +# ogIsNewerFile [ str_repo | int_ndisk int_npart ] path_source [ str_repo | int_ndisk int_npart ] path_target +#@brief Metafunción que indica se un fichero es más nuevo que otro. +#@see ogGetPath +#@return Código de salida: 0 - nuevo, 1 - antiguo o error +#@warning Deben existir tanto el fichero origen como el destino. +#*/ ## +def ogIsNewerFile(*args): + # Variables locales. + ARGS = args + if "help" in args: + SystemLib.ogHelp("$FUNCNAME", "$FUNCNAME [ str_repo | int_ndisk int_npartition ] path_source [ str_repo | int_ndisk int_npartition ] path_target", "if $FUNCNAME REPO ubuntu.img CACHE ubuntu.img; then ... fi") + return + + ARGS = args + if args[0].startswith("/"): + # Camino completo. */ (Comentrio Doxygen) + SOURCE = ogGetPath(*args[:1]) + args = args[1:] + elif args[0].isdigit(): + # ndisco npartición. + SOURCE = ogGetPath(*args[:3]) + args = args[3:] + else: + # Otros: repo, cache, cdrom (no se permiten caminos relativos). + SOURCE = ogGetPath(*args[:2]) + args = args[2:] + + TARGET = ogGetPath(*args) + + # Comprobar que existen los ficheros origen y destino. + if not SOURCE: + SystemLib.ogRaiseError( + "session", + ogGlobals.OG_ERR_NOTFOUND, + f"Not found: {args[:-1]}" + ) + return + if not TARGET: + SystemLib.ogRaiseError( + "session", + ogGlobals.OG_ERR_NOTFOUND, + f"Not found: {args[-1]}" + ) + return + + # Devolver si el primer fichero se ha modificado después que el segundo. + return os.path.getmtime(SOURCE) > os.path.getmtime(TARGET) + +#/** +# ogMakeChecksumFile [ str_repo | int_ndisk int_npart ] path_filepath +#@brief Metafunción que guarda el valor de comprobación de un fichero. +#@see ogCalculateChecksum +#@warning Genera un fichero con extensión ".sum". +#*/ ## +def ogMakeChecksumFile(*args): + # Variables locales. + FILE = None + if "help" in args: + SystemLib.ogHelp("$FUNCNAME", "$FUNCNAME [ str_repo | int_ndisk int_npartition ] path_filepath", "$FUNCNAME REPO ubuntu.img") + return + + # Comprobar que existe el fichero y guardar su checksum. + FILE = ogGetPath(*args) + if not FILE: + SystemLib.ogRaiseError( + "session", + ogGlobals.OG_ERR_NOTFOUND, + f"Not found: {args}" + ) + return + checksum = ogCalculateChecksum(FILE) + with open(f"{FILE}.sum", "w") as f: + f.write(checksum) + + +#/** +# ogMakeDir [ str_repo | int_ndisk int_npartition ] path_dirpath +#@brief Metafunción que crea un subdirectorio vacío en un dispositivo. +#@see ogGetParentPath +#*/ ## +#ogMakeDir (container='REPO', file='/tmp/newdir') +#ogMakeDir (disk='1', par='2', file='/tmp/newdir') +def ogMakeDir (container=None, disk=None, par=None, file=None): + if file is None: + raise TypeError ('missing required argument: "file"') + + if container is not None: + if disk is None and par is None: + ## we were given container= + parent = ogGetParentPath (src=container, file=file) + dev_err = f'{container} {file}' + print (f'ogGetParentPath (src=({container}), file=({file})) = parent ({parent})') + else: + raise TypeError ('argument "container" can be specified along neither "disk" nor "par"') + + else: + if disk is not None and par is not None: + ## we were given disk= par= + parent = ogGetParentPath (src=f'{disk} {par}', file=file) + dev_err = f'{disk} {par} {file}' + print (f'ogGetParentPath (src=({disk} {par}), file=({file})) = parent ({parent})') + elif disk is None and par is None: + ## we were given nothing + parent = ogGetParentPath (file=file) + dev_err = file + print (f'ogGetParentPath (file=({file})) = parent ({parent})') + else: + raise TypeError ('if one of "disk" and "par" are specified, then both must be') + + print (f'nati: ogMakeDir: parent ({parent})') + if not parent: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f'device or file {dev_err} not found') + return None + + dst = os.path.basename (file) + print (f'nati: ogMakeDir: dst ({dst})') + os.makedirs (os.path.join (parent, dst), exist_ok=True) + return True diff --git a/client/lib/python3/FileSystemLib.py b/client/lib/python3/FileSystemLib.py new file mode 100644 index 0000000..15139f3 --- /dev/null +++ b/client/lib/python3/FileSystemLib.py @@ -0,0 +1,906 @@ +#/** +#@file FileSystemLib.py +#@brief Librería o clase FileSystem +#@class FileSystem +#@brief Funciones para gestión de sistemas de archivos. +#@warning License: GNU GPLv3+ +#*/ + +import subprocess +import sys +import os.path + +import ogGlobals +import SystemLib +import DiskLib +import CacheLib +import FileSystemLib + + +#/** +# ogCheckFs int_ndisk int_nfilesys +#@brief Comprueba el estado de un sistema de archivos. +#@param int_ndisk nº de orden del disco +#@param int_nfilesys nº de orden del sistema de archivos +#@return (nada) +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Disco o particion no corresponden con un dispositivo. +#@exception OG_ERR_PARTITION Partición desconocida o no accesible. +#@note Requisitos: *fsck* +#@warning No se comprueban sistemas de archivos montados o bloqueados. +#@todo Definir salidas. +#*/ ## +def ogCheckFs (disk, par): + PART = DiskLib.ogDiskToDev (disk, par) + if not PART: return + + data = { + 'EXT2': { 'prog': 'e2fsck', 'params': '-y', 'codes': (1, 2), }, + 'EXT3': { 'prog': 'e2fsck', 'params': '-y', 'codes': (1, 2), }, + 'EXT4': { 'prog': 'e2fsck', 'params': '-y', 'codes': (1, 2), }, + 'CACHE': { 'prog': 'e2fsck', 'params': '-y', 'codes': (1, 2), }, + 'BTRFS': { 'prog': 'btrfsck', 'codes': (1), }, + 'REISERFS': { 'prog': 'fsck.reiserfs', 'codes': (1, 2), 'input': 'Yes' }, + 'REISER4': { 'prog': 'fsck.reiser4', 'params': '-ay', }, + 'JFS': { 'prog': 'fsck.jfs', 'codes': (1, 2), }, + 'XFS': { 'prog': 'xfs_repair', }, + 'F2FS': { 'prog': 'fsck.f2fs', }, + 'NTFS': { 'prog': 'ntfsfix', }, + 'EXFAT': { 'prog': 'fsck.exfat', }, + 'FAT32': { 'prog': 'dosfsck', 'params': '-a', 'codes': (1), }, + 'FAT16': { 'prog': 'dosfsck', 'params': '-a', 'codes': (1), }, + 'FAT12': { 'prog': 'dosfsck', 'params': '-a', 'codes': (1), }, + 'HFS': { 'prog': 'fsck.hfs', 'params': '-f', }, + 'HFSPLUS': { 'prog': 'fsck.hfs', 'params': '-f', }, + 'UFS': { 'prog': 'fsck.ufs', }, + 'ZFS': { 'prog': 'fsck.zfs', }, + } + type = ogGetFsType (disk, par) + if type in data: + prog = data[type]['prog'] + params = data[type]['params'] if 'params' in data[type] else '' + codes = data[type]['codes'] if 'codes' in data[type] else [] + input = data[type]['input'] if 'input' in data[type] else None + else: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, f'{disk}, {par}, {type}') + return None + + ogUnmount (disk, par) + if ogIsMounted (disk, par): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, f'{disk} {par}') + return None + if ogIsLocked (disk, par): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_LOCKED, f'{disk} {par}') + return None + + ogLock (disk, par) + rc = subprocess.run ([prog] + params.split() + [PART], input=input, text=True).returncode + if 0 == rc or rc in codes: + errcode = 0 + elif 127 == rc: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTEXEC, prog) + errcode = ogGlobals.OG_ERR_NOTEXEC + else: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, f'{disk} {par}') + errcode = ogGlobals.OG_ERR_PARTITION + + ogUnlock (disk, par) + return not errcode ## reverse to indicate success + + +#/** +# ogExtendFs int_ndisk int_nfilesys +#@brief Extiende un sistema de archivos al tamaño de su partición. +#@param int_ndisk nº de orden del disco +#@param int_nfilesys nº de orden del sistema de archivos +#@return (nada) +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Disco o particion no corresponden con un dispositivo. +#@exception OG_ERR_PARTITION Partición desconocida o no accesible. +#@note Requisitos: *resize* +#*/ ## +def ogExtendFs (disk, par): + PART = DiskLib.ogDiskToDev (disk, par) + if not PART: return + + data = { + 'EXT2': { 'prog': 'resize2fs', 'params': '-f', }, + 'EXT3': { 'prog': 'resize2fs', 'params': '-f', }, + 'EXT4': { 'prog': 'resize2fs', 'params': '-f', }, + 'BTRFS': { 'prog': 'btrfs', 'params': 'filesystem resize max', 'domount': True }, + 'REISERFS': { 'prog': 'resize_reiserfs', 'params': '-f', }, + 'REISER4': { 'prog': 'resize_reiserfs', 'params': '-f', }, + 'NTFS': { 'prog': 'ntfsresize', 'params': '-f', 'input': 'y' }, + 'F2FS': { 'unsupported': True }, + 'JFS': { 'unsupported': True }, + 'NILFS2': { 'unsupported': True }, # try "nilfs-resize" + 'XFS': { 'unsupported': True }, + 'EXFAT': { 'unsupported': True }, + 'FAT32': { 'unsupported': True }, # try "fatresize" + 'FAT16': { 'unsupported': True }, # try "fatresize" + 'HFS': { 'unsupported': True }, + 'HFSPLUS': { 'unsupported': True }, + 'UFS': { 'unsupported': True }, + } + + type = ogGetFsType (disk, par) + if type in data: + prog = data[type]['prog'] if 'prog' in data[type] else None + params = data[type]['params'] if 'params' in data[type] else None + domount = data[type]['domount'] if 'domount' in data[type] else False + input = data[type]['input'] if 'input' in data[type] else None + else: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, f'{disk} {par} {type}') + return + + if not prog: return None + + if domount: + PART = ogMount (disk, par) + if not PART: return None + else: + ogUnmount (disk, par) + if ogIsMounted (disk, par): + SystemLib.ogRaiseError([], ogGlobals.OG_ERR_PARTITION, f'{disk} {par}') + return None + +# Error si el sistema de archivos está bloqueado. + if ogIsLocked (disk, par): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_LOCKED, f'{disk} {par}') + return None + +# Redimensionar en modo uso exclusivo. + ogLock (disk, par) + try: + if input: + rc = subprocess.run ([prog] + params.split() + [PART], input=input, text=True).returncode + else: + rc = subprocess.run ([prog] + params.split() + [PART]).returncode + except FileNotFoundError: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTEXEC, prog) + rc = ogGlobals.OG_ERR_NOTEXEC + except: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, f'{disk} {par}') + rc = ogGlobals.OG_ERR_PARTITION + + ogUnlock (disk, par) + return not rc ## reverse to indicate success + + +#/** +# ogFormat int_ndisk int_nfilesys | CACHE +#@see ogFormatFs ogFormatCache +#*/ ## + +def ogFormat (disk, par=None, fs=None, label=None): + if disk.lower() == "cache": + return CacheLib.ogFormatCache() + else: + return ogFormatFs (disk, par, fs=fs, label=label) + + +#/** +# ogFormatFs int_ndisk int_nfilesys [type_fstype] [str_label] +#@brief Formatea un sistema de ficheros según el tipo de su partición. +#@param int_ndisk nº de orden del disco +#@param int_nfilesys nº de orden del sistema de archivos +#@param type_fstype mnemónico de sistema de ficheros a formatear (opcional al reformatear) +#@param str_label etiqueta de volumen (opcional) +#@return (por determinar) +#@exception OG_ERR_FORMAT Formato de ejecución incorrecto. +#@exception OG_ERR_NOTFOUND Disco o particion no corresponden con un dispositivo. +#@exception OG_ERR_PARTITION Partición no accesible o desconocida. +#@note Requisitos: mkfs* +#@warning No formatea particiones montadas ni bloqueadas. +#@todo Definir salidas. +#*/ ## +def ogFormatFs (disk, par, fs=None, label=None): + PART = DiskLib.ogDiskToDev (disk, par) + if not PART: return + + if ogIsMounted (disk, par): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_DONTFORMAT, f'{ogGlobals.lang.MSG_MOUNT}: {disk} {par}') + return None + if ogIsLocked (disk, par): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_LOCKED, f"{disk} {par}") + return None + + if not fs: + fs = ogGetFsType (disk, par) + + if not fs: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, f"{disk} {par} ...") + return None + + data = { + 'EXT2': { 'PROG': 'mkfs.ext2', 'PARAMS': '-F' }, + 'EXT3': { 'PROG': 'mkfs.ext3', 'PARAMS': '-F' }, + 'EXT4': { 'PROG': 'mkfs.ext4', 'PARAMS': '-F' }, + 'BTRFS': { 'PROG': 'mkfs.btrfs', 'PARAMS': '-f' }, + 'REISERFS': { 'PROG': 'mkfs.reiserfs', 'PARAMS': '-f', 'LABELPARAM': '-l' }, + 'REISER4': { 'PROG': 'mkfs.reiser4', 'PARAMS': '-f', 'INPUT': 'y\n' }, + 'XFS': { 'PROG': 'mkfs.xfs', 'PARAMS': '-f' }, + 'JFS': { 'PROG': 'mkfs.jfs', 'INPUT': 'y\n' }, + 'F2FS': { 'PROG': 'mkfs.f2fs', 'LABELPARAM': '-l' }, + 'NILFS2': { 'PROG': 'mkfs.nilfs2', 'PARAMS': '-f' }, + 'LINUX-SWAP': { 'PROG': 'mkswap' }, + 'NTFS': { 'PROG': 'mkntfs', 'PARAMS': '-f' }, + 'EXFAT': { 'PROG': 'mkfs.exfat', 'LABELPARAM': '-n' }, + 'FAT32': { 'PROG': 'mkdosfs', 'PARAMS': '-F 32', 'LABELPARAM': '-n' }, + 'FAT16': { 'PROG': 'mkdosfs', 'PARAMS': '-F 16', 'LABELPARAM': '-n' }, + 'FAT12': { 'PROG': 'mkdosfs', 'PARAMS': '-F 12', 'LABELPARAM': '-n' }, + 'HFS': { 'PROG': 'mkfs.hfs' }, + 'HFSPLUS': { 'PROG': 'mkfs.hfsplus', 'LABELPARAM': '-v' }, + 'UFS': { 'PROG': 'mkfs.ufs', 'PARAMS': '-O 2' }, + } + if fs not in data: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, f"{disk} {par} {fs}") + return + + d = data[fs] + prog = d['PROG'] + params = d['PARAMS'] if 'PARAMS' in d else '' + labelparam = d['LABELPARAM'] if 'LABELPARAM' in d else '' + input = d['INPUT'] if 'INPUT' in d else '' + + if label == "CACHE": + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, f"{ogGlobals.lang.MSG_RESERVEDVALUE}: CACHE") + return + if label: + params = f"{params} {labelparam or '-L'} {label}" + + ogLock (disk, par) + subprocess.run (['umount', PART]) + try: + if input: + errcode = subprocess.run ([prog] + params.split (' ') + [PART]).returncode + else: + errcode = subprocess.run ([prog] + params.split (' ') + [PART], input=input, text=True).returncode + except FileNotFoundError: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTEXEC, prog) + errcode = ogGlobals.OG_ERR_NOTEXEC + except: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, f"{disk} {par}") + errcode = ogGlobals.OG_ERR_PARTITION + + ogUnlock (disk, par) + return errcode + + +#/** +# ogGetFsSize int_ndisk int_npartition [str_unit] +#@brief Muestra el tamanio del sistema de archivos indicado, permite definir la unidad de medida, por defecto GB +#@param int_ndisk nº de orden del disco +#@param int_npartition nº de orden de la partición +#@param str_unit unidad (opcional, por defecto: KB) +#@return float_size - Tamaño del sistema de archivos +#@note str_unit = { KB, MB, GB, TB } +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Disco o partición no corresponden con un dispositivo. +#*/ ## +def ogGetFsSize (disk, par, unit='KB'): + factor = 1 + if unit.upper() == "MB": + factor = 1024 + elif unit.upper() == "GB": + factor = 1024 * 1024 + elif unit.upper() == "TB": + factor = 1024 * 1024 * 1024 + elif unit.upper() != "KB": + SystemLib.ogRaiseError ( + [], + ogGlobals.OG_ERR_FORMAT, + f"{unit} != {{ KB, MB, GB, TB }}" + ) + return + + # Obtener el tamaño del sistema de archivo (si no está formateado; tamaño = 0). + mnt = FileSystemLib.ogMount (disk, par) + if mnt: + result = subprocess.run(["df", "-BK", mnt], capture_output=True, text=True) + val = result.stdout.split("\n")[1].split()[1] + val = val.replace ('K', '') + sz = int (val) / factor + else: + sz = 0 + + return int (sz) + + +#/** +# ogGetFsType int_ndisk int_nfilesys +#@brief Devuelve el mnemonico con el tipo de sistema de archivos. +#@param int_ndisk nº de orden del disco +#@param int_nfilesys nº de orden del sistema de archivos +#@return Mnemonico +#@note Mnemonico: { EXT2, EXT3, EXT4, BTRFS, REISERFS, XFS, JFS, FAT12, FAT16, FAT32, NTFS, LINUX-SWAP, LINUX-LVM, LINUX-RAID, HFS, HFSPLUS, CACHE } +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Disco o particion no corresponden con un dispositivo. +#*/ ## +def ogGetFsType(disk, part): + PART = DiskLib.ogDiskToDev(disk, part) + if not PART: return + + TYPE = None + if PART.startswith("/"): + out = subprocess.run(["blkid", "-o", "export", PART], capture_output=True, text=True).stdout.splitlines() + for line in out: + if line.startswith("TYPE="): + TYPE = line.split("=")[1].upper() + break + else: + try: + subprocess.run(["zfs", "mount", PART]) + except FileNotFoundError: + SystemLib.ogRaiseError ( + [], + ogGlobals.OG_ERR_NOTEXEC, + 'zfs' + ) + return + out = subprocess.run(["mount"], capture_output=True, text=True).stdout.splitlines() + for line in out: + if line.startswith(PART): + TYPE = line.split()[4].upper() + break + + if not TYPE: + SystemLib.ogRaiseError ( + [], + ogGlobals.OG_ERR_NOTFOUND, + f'{disk} {part}' + ) + return + + # Componer valores correctos. + if TYPE == "EXT4": + if f"{disk} {part}" == CacheLib.ogFindCache(): + if ogIsFormated(disk, part): + TYPE = "CACHE" + elif TYPE == "VFAT": + result = subprocess.run(["blkid", "-po", "export", PART], capture_output=True, text=True) + for line in result.stdout.split("\n"): + if line.startswith("VERSION="): + TYPE = line.split("=")[1].upper() + break + elif TYPE == "SWAP": + TYPE = "LINUX-SWAP" + elif TYPE.startswith("LVM"): + TYPE = "LINUX-LVM" + elif "RAID" in TYPE: + TYPE = "LINUX-RAID" + elif TYPE == "ZFS_MEMBER": + TYPE = "ZVOL" + elif "_MEMBER" in TYPE: + TYPE = TYPE.replace("_MEMBER", "") + + return TYPE + + +#/** +# ogGetMountPoint int_ndisk int_nfilesys +#@brief Devuelve el punto de montaje de un sistema de archivos. +#@param int_ndisk nº de orden del disco +#@param int_nfilesys nº de orden del sistema de archivos +#@return Punto de montaje +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Disco o particion no corresponden con un dispositivo. +#@note Requisitos: \c mount* \c awk +#*/ ## + +def ogGetMountPoint(disk, par): + PART = DiskLib.ogDiskToDev(disk, par) + if not PART: return + + return subprocess.run(["findmnt", "-n", "-o", "TARGET", PART], capture_output=True, text=True).stdout.strip() + + + +#/** +# ogIsFormated int_ndisk int_nfilesys +#@brief Comprueba si un sistema de archivos está formateado. +#@param int_ndisk nº de orden del disco o volumen. +#@param int_nfilesys nº de orden del sistema de archivos +#@return Código de salida: True - formateado, False - sin formato o error. +#*/ ## +def ogIsFormated(disk, part): + PART = DiskLib.ogDiskToDev (disk, part) + if not PART: + return + + # Revisar tipo de sistema de archivos. + if PART.startswith("/"): + out = subprocess.run(["blkid", "-s", "TYPE", PART], capture_output=True, text=True).stdout.strip() + if 'swap' in out: return False + if '_member' in out: return False + return bool(out) + else: + out = subprocess.run(["zfs", "list", "-Hp", "-o", "canmount", PART], capture_output=True, text=True).stdout.strip() + return out == "on" + + + +#/** +# ogIsLocked int_ndisk int_npartition +#@see ogIsPartitionLocked +#*/ + +def ogIsLocked(disk, par): + return ogIsPartitionLocked(disk, par) + + +#/** +# ogIsPartitionLocked int_ndisk int_npartition +#@brief Comprueba si una partición o su disco están bloqueados por una operación de uso exclusivo. +#@param int_ndisk nº de orden del disco +#@param int_npartition nº de orden de la partición +#@return Código de salida: 0 - bloqueado, 1 - sin bloquear o error. +#@note Los ficheros de bloqueo se localizan en \c /var/lock/dev, siendo \c dev el dispositivo de la partición o de su disco, sustituyendo el carácter "/" por "-". +#*/ ## +def ogIsPartitionLocked(disk, par): + DISK = DiskLib.ogDiskToDev(disk) + PART = DiskLib.ogDiskToDev(disk, par) + if not PART: return + + LOCKDISK = f"/var/lock/lock{DISK.replace('/', '-')}" + LOCKPART = f"/var/lock/lock{PART.replace('/', '-')}" + rc = os.path.isfile(LOCKDISK) or os.path.isfile(LOCKPART) + return rc + +#/** +# ogIsMounted int_ndisk int_nfilesys +#@brief Comprueba si un sistema de archivos está montado. +#@param int_ndisk nº de orden del disco +#@param int_nfilesys nº de orden del sistema de archivos +#@return Código de salida: 0 - montado, 1 - sin montar o error. +#*/ ## +def ogIsMounted (disk, par): + return bool (ogGetMountPoint (disk, par)) + + +#/** +# ogIsReadonly int_ndisk int_nfilesys +#@brief Comprueba si un sistema de archivos está montado solo de lectura. +#@param int_ndisk nº de orden del disco +#@param int_nfilesys nº de orden del sistema de archivos +#@return Código de salida: 0 - montado solo de lectura, 1 - con escritura o no montado. +#@version 1.1.0 - Primera versión para OpenGnsys. +#@author Ramon Gomez, ETSII Universidad de Sevilla +#@date 2016-01-20 +#*/ ## + +def ogIsReadonly(disk, par): + PART = DiskLib.ogDiskToDev(disk, par) + if not PART: return + + result = subprocess.run(["findmnt", "-n", "-o", "OPTIONS", PART], capture_output=True, text=True) + options = result.stdout.strip().split(",") + return "ro" in options + + +#/** +# ogIsWritable int_ndisk int_nfilesys +#@brief Comprueba si un sistema de archivos está montado de lectura y escritura. +#@param int_ndisk nº de orden del disco +#@param int_nfilesys nº de orden del sistema de archivos +#@return Código de salida: 0 - lectura y escritura, 1 - solo lectura o no montado. +#*/ ## +def ogIsWritable (disk, par): + PART = DiskLib.ogDiskToDev (disk, par) + if not PART: return + + result = subprocess.run(["findmnt", "-n", "-o", "OPTIONS", PART], capture_output=True, text=True) + options = result.stdout.strip().split(",") + return "rw" in options + + +#/** +# ogLock int_ndisk int_npartition +#@see ogLockPartition +#*/ +def ogLock(disk, par): + return ogLockPartition(disk, par) + +#/** +# ogLockPartition int_ndisk int_npartition +#@brief Genera un fichero de bloqueo para una partición en uso exlusivo. +#@param int_ndisk nº de orden del disco +#@param int_npartition nº de orden de la partición +#@return (nada) +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Disco o particion no corresponden con un dispositivo. +#@note El fichero de bloqueo se localiza en \c /var/lock/part, siendo \c part el dispositivo de la partición, sustituyendo el carácter "/" por "-". +#*/ ## +def ogLockPartition (disk, par): + PART = DiskLib.ogDiskToDev (disk, par) + if not PART: return + + LOCKFILE = f"/var/lock/lock{PART.replace('/', '-')}" + try: + open (LOCKFILE, 'w').close() + except: + return False + return True + + +#/** +# ogMount int_ndisk int_nfilesys +#@see ogMountFs ogMountCache ogMountCdrom +#*/ ## +def ogMount(*args): + if 1 == len (args): + if 'cache' == args[0].lower(): + return DiskLib.ogMountCache() + elif 'cdrom' == args[0].lower(): + return ogMountCdrom() + elif 2 == len (args): + return ogMountFs(args[0], args[1]) + + +#/** +# ogMountFirstFs int_ndisk +#@brief Monta el primer sistema de archivos disponible en el disco. +#@param int_ndisk nº de orden del disco +#@return Punto de montaje del primer sistema de archivos detectado +#*/ ## +def ogMountFirstFs (disk): + NPARTS = DiskLib.ogGetPartitionsNumber (disk) + for PART in range (1, NPARTS + 1): + MNTDIR = ogMount (disk, PART) + if MNTDIR: + return MNTDIR + SystemLib.ogRaiseError ('session', ogGlobals.OG_ERR_NOTFOUND, f'{disk}') + return ogGlobals.OG_ERR_NOTFOUND + +#/** +# ogMountFs int_ndisk int_nfilesys +#@brief Monta un sistema de archivos. +#@param int_ndisk nº de orden del disco +#@param int_nfilesys nº de orden del sistema de archivos +#@return Punto de montaje +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Disco o particion no corresponden con un dispositivo. +#@exception OG_ERR_PARTITION Tipo de particion desconocido o no se puede montar. +#*/ ## +def ogMountFs (disk, par): + dev = DiskLib.ogDiskToDev (disk, par) + if not dev: return + + mntdir = ogGetMountPoint (disk, par) + if mntdir: return mntdir + + if ogIsLocked (disk, par): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_LOCKED, f'{ogGlobals.lang.MSG_PARTITION}, {disk} {par}') + return + + # El camino de un dispositivo normal comienza por el carácter "/". + if dev.startswith ('/'): + # Crear punto de montaje o enlace simbólico para caché local. + mntdir = dev.replace ('/dev', '/mnt') + if f"{disk} {par}" == CacheLib.ogFindCache(): + os.makedirs(ogGlobals.OGCAC, exist_ok=True) + try: + os.symlink(ogGlobals.OGCAC, mntdir) + except FileExistsError: + pass + else: + os.makedirs(mntdir, exist_ok=True) + + # Montar sistema de archivos. + try: + rc = subprocess.run(['mount', dev, mntdir], check=True).returncode + except subprocess.CalledProcessError: + try: + rc = subprocess.run(['mount', dev, mntdir, '-o', 'force,remove_hiberfile'], check=True).returncode + except subprocess.CalledProcessError: + SystemLib.ogRaiseError ( + [], + ogGlobals.OG_ERR_PARTITION, + f"{disk}, {par}" + ) + return + + if 0 == rc: + pass + elif 14 == rc: + try: + subprocess.run (['ntfsfix', '-d', par], check=True) + except subprocess.CalledProcessError: + SystemLib.ogRaiseError ( + [], + ogGlobals.OG_ERR_PARTITION, + f"{disk, par}" + ) + #return + else: + try: + subprocess.run (['mount', par, mntdir, '-o', 'ro'], check=True) + except subprocess.CalledProcessError: + SystemLib.ogRaiseError ( + [], + ogGlobals.OG_ERR_PARTITION, + f"{disk, par}" + ) + #return + + # Aviso de montaje de solo lectura. + if ogIsReadonly(disk, par): + SystemLib.ogEcho("warning", f'ogMountFs: {ogGlobals.lang.MSG_MOUNTREADONLY}: "{disk}, {par}"') + else: + # Montar sistema de archivos ZFS (un ZPOOL no comienza por "/"). + subprocess.run(['zfs', 'mount', dev]) + + return mntdir + + + +#/** +# ogMountCdrom +#@brief Monta dispositivo óptico por defecto +#@return Punto de montaje +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_PARTITION Tipo de particion desconocido o no se puede montar. +#@version +#@author +#@date +#*/ ## +def ogMountCdrom(): + DEV = '/dev/cdrom' # Por defecto + outlines = subprocess.run (['mount'], capture_output=True, text=True).stdout.split ('\n') + mntdir = '' + for l in outlines: + items = l.split (' ') + if DEV == items[0]: + mntdir = items[2] + break + + if not mntdir: + mntdir = DEV.replace ('/dev', '/mnt') + os.makedirs (mntdir, exist_ok=True) + try: + subprocess.run (['mount', '-t', 'iso9660', DEV, mntdir], check=True) + except subprocess.CalledProcessError: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, 'cdrom') + return None + + return mntdir + + +#/** +# ogReduceFs int_ndisk int_nfilesys +#@brief Reduce el tamaño del sistema de archivos, sin tener en cuenta el espacio libre. +#@param int_ndisk nº de orden del disco +#@param int_nfilesys nº de orden del sistema de archivos +#@return int_tamañoKB - tamaño en KB +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Disco o particion no corresponden con un dispositivo. +#@exception OG_ERR_PARTITION Partición desconocida o no accesible. +#@warning En Windows, se borran los ficheros de hiberanción y de paginación. +#@warning El sistema de archivos se amplía al mínimo + 10%. +#@note Requisitos: *resize* +#*/ ## +def ogReduceFs (disk, par): + PART = DiskLib.ogDiskToDev (disk, par) + if not PART: return + + # Redimensionar según el tipo de partición. + type = ogGetFsType (disk, par) + if type in ['EXT2', 'EXT3', 'EXT4']: + ogUnmount (disk, par) + rc = subprocess.run (['resize2fs', '-fpM', PART], capture_output=True, text=True).returncode + if rc: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, f"{disk},{par}") + return None + elif 'BTRFS' == type: + mntdir = ogMount (disk, par) + # Calcular tamaño ocupado + 10%, redondeado + 1 (incluyendo letra de unidad). + btrfs_lines = subprocess.run (['btrfs', 'filesystem', 'show', mntdir], capture_output=True, text=True).stdout.splitlines() + for l in btrfs_lines: + if 'devid' not in l: continue + ## 'devid 2 size 8.89GiB used 1.00GiB path /dev/sda4' + devid_str, devid, size_str, size, used_str, used, path_str, path = l.split() + if PART != os.path.realpath (path): continue + (sz, unit) = re.search ('^([^A-Z]+)([A-Z])', used).groups() + sz = float (sz) * 1.1 + 1 + size = f'{str(sz)}{unit}' + subprocess.run (['btrfs', 'filesystem', 'resize', size, mntdir], capture_output=True, text=True) + break + elif type in ['REISERFS', 'REISER4']: + mntdir = ogMount (disk, par) + df_lines = subprocess.run (['df', '-k', mntdir], capture_output=True, text=True).stdout.splitlines() + for l in df_lines: + if 'Filesystem' in l: continue + fs, blocks, used, avail, use_pct, mntpt = l.split() + size = str (int (used) * 1.1) + ogUnmount (disk, par) + subprocess.run (['resize_reiserfs', f'-s{size}K', PART], input='y\n', capture_output=True, text=True) + break + elif type == 'NTFS': + ogUnmount (disk, par) + nr_lines = subprocess.run (['ntfsresize', '-fi', PART], capture_output=True, text=True).stdout.splitlines() + maxsize = None + size = None + for l in nr_lines: + if 'device size' in l: + maxsize = float (l.split()[3]) + if 'resize at' in l: + size = l.split()[4] + size = int ((int (size) * 1.1 / 1024 + 1) * 1024) + + if not maxsize and not size: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, f'{disk},{par}') + return None + + import time + + extrasize = 0 + retval = 1 + while retval != 0 and size+extrasize < maxsize: + nr = subprocess.run (['ntfsresize', '-fns', str(size), PART], capture_output=True, text=True) + for l in nr.stdout.splitlines(): + if 'Needed relocations' not in l: continue + extrasize = int ((int (l.split()[3])*1.1/1024+1)*1024) + break + retval = nr.returncode + size += extrasize + if size < maxsize: + rc = subprocess.run (['ntfsresize', '-fs', str(size), PART], input='y\n', capture_output=True, text=True).returncode + if rc: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, f"{disk},{par}") + return None + elif type in ['FAT32', 'FAT16', 'F2FS', 'JFS', 'NILFS2', 'XFS', 'EXFAT', 'HFS', 'HFSPLUS', 'UFS']: + pass + else: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, f"{disk},{par}") + return None + + return ogGetFsSize (disk, par) + + +#/** +# ogUnlock int_ndisk int_npartition +#@see ogUnlockPartition +#*/ ## +def ogUnlock (disk, par): + return ogUnlockPartition (disk, par) + +#/** +# ogUnlockPartition int_ndisk int_npartition +#@brief Elimina el fichero de bloqueo para una particion. +#@param int_ndisk nº de orden del disco +#@param int_npartition nº de orden de la partición +#@return (nada) +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Disco o particion no corresponden con un dispositivo. +#@note El fichero de bloqueo se localiza en \c /var/lock/part, siendo \c part el dispositivo de la partición, sustituyendo el carácter "/" por "-". +#*/ ## +def ogUnlockPartition (disk, par): + PART = DiskLib.ogDiskToDev (disk, par) + if not PART: return + + LOCKFILE = f"/var/lock/lock{PART.replace('/', '-')}" + if os.path.exists (LOCKFILE): + os.remove (LOCKFILE) + + +#/** +# ogUnmount int_ndisk int_npartition +#@see ogUnmountFs +#*/ ## +def ogUnmount (disk, par): + return ogUnmountFs (disk, par) + +#/** +# ogUnmountFs int_ndisk int_nfilesys +#@brief Desmonta un sistema de archivos. +#@param int_ndisk nº de orden del disco +#@param int_nfilesys nº de orden del sistema de archivos +#@return Nada +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Disco o particion no corresponden con un dispositivo. +#@warning La partición no está previamente montada o no se puede desmontar. +#*/ ## + +def ogUnmountFs(disk, par): + PART = DiskLib.ogDiskToDev (disk, par) + if not PART: return + MNTDIR = ogGetMountPoint (disk, par) + + # Si está montada, desmontarla. + if MNTDIR: + # Error si la particion está bloqueada. + if ogIsLocked (disk, par): + SystemLib.ogRaiseError ( + [], + ogGlobals.OG_ERR_LOCKED, + f"{ogGlobals.lang.MSG_PARTITION}, {disk} {par}" + ) + return + + # Desmontar y borrar punto de montaje. + try: + subprocess.run(["umount", PART], check=True) + except subprocess.CalledProcessError: + SystemLib.ogEcho("warning", f'ogUnmountFs: {ogGlobals.lang.MSG_DONTUNMOUNT}: "{disk}, {par}"') + try: + os.rmdir(MNTDIR) + except: + try: + os.remove(MNTDIR) + except: + pass + return True + else: + SystemLib.ogEcho ([], "warning", f'{ogGlobals.lang.MSG_DONTMOUNT}: "{disk},{par}"') + return True + + +#/** +# ogUnmountAll int_ndisk +#@brief Desmonta todos los sistema de archivos de un disco, excepto el caché local. +#@param int_ndisk nº de orden del disco +#@return Nada +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Disco o particion no corresponden con un dispositivo. +#@warning No se desmonta la partición marcada como caché local. +#*/ ## +def ogUnmountAll (disk): + DISK = DiskLib.ogDiskToDev (disk) + n = DiskLib.ogGetPartitionsNumber (disk) + if not n: return None + for PART in range (1, n+1): + if 'CACHE' != ogGetFsType (disk, PART): + ogUnmount (disk, PART) + + +#/** +# ogUnsetDirtyBit int_ndisk int_npart +#@brief Inhabilita el Dirty Bit del sistema de ficheros NTFS para evitar un CHKDSK en el primer arranque +#@param int_ndisk nº de orden del disco +#@param int_npart nº de orden de partición +#@return Nada +#@exception OG_ERR_FORMAT Formato incorrecto. +#*/ ## +def ogUnsetDirtyBit (disk, par): + PART = DiskLib.ogDiskToDev (disk, par) + if not PART: return None + + TYPE = ogGetFsType (disk, par) + if 'NTFS' == TYPE: + ogUnmount (disk, par) + subprocess.run (['ntfsfix', '--clear-dirty', PART]) + + +#/** +# ogGetFreeSize int_disco int_partition str_SizeOutput +#@brief muestra informacion del tamaño total, datos y libre. +#@param int_ndisk nº de orden del disco +#@param int_npart nº de orden de partición +#@param str_unitSize unidad mostrada +#@return int_size:int_data:int_free +#@TODO Componer corretcamente esta función. +#@exception OG_ERR_FORMAT Formato incorrecto. +#*/ ## + +def ogGetFreeSize(disk, part, unit='KB'): + PART = DiskLib.ogDiskToDev (disk, part) + if not PART: return + + unit2factor = { + 'kb': 1.024 / 1, + 'mb': 1.024 / 1000, + 'gb': 1.024 / 1000000, + } + if unit.lower() not in unit2factor: + kk + factor = unit2factor[unit.lower()] + + particion = FileSystemLib.ogMount (disk, part) + if not particion: + kk + df = subprocess.run (['df'], capture_output=True, text=True).stdout + df_part = list (filter (lambda l: particion in l, df.splitlines())) + if not len (df_part): + kk + _, size, used, free, pct, mntpt = df_part[0].split() + return float (free) * factor diff --git a/client/lib/python3/ImageLib.py b/client/lib/python3/ImageLib.py new file mode 100644 index 0000000..2b5fc8c --- /dev/null +++ b/client/lib/python3/ImageLib.py @@ -0,0 +1,839 @@ +#!/usr/bin/python3 + +import shutil +import subprocess +import os +import os.path +import re +from pathlib import Path + +import DiskLib +import FileSystemLib +import SystemLib +import ogGlobals +import FileLib +import CacheLib +import NetLib + +## ProtocolLib.ogUcastSyntax() calls ogCreateImageSyntax() +## in ogCreateImageSyntax(), param2 may contain a pipe or may be empty +## if param2 is empty, then ogUcastSyntax(): +## - does a .split() +## - accesses the third element of the resulting array (ie. does "parts[2]") +## - promptly gets an IndexError exception +## +## param2 in ogCreateImageSyntax() only contains a pipe if 'mbuffer' is installed +## therefore, a hard dependency on mbuffer is created +## +## raise an Exception at import time if mbuffer is not present +if not shutil.which ('mbuffer'): + raise FileNotFoundError ('"mbuffer" utility must be present') + +#/** +#@file ImageLib.py +#@brief Librería o clase Image +#@class Image +#@brief Funciones para creación, restauración y clonación de imágenes de sistemas. +#@warning License: GNU GPLv3+ +#*/ + + +#/** +# ogCreateImageSyntax path_device path_filename [str_tool] [str_compressionlevel] +#@brief Genera una cadena de texto con la instrucción para crear un fichero imagen +#@param path_device dispositivo Linux del sistema de archivos +#@param path_fileneme path absoluto del fichero imagen +#@param [opcional] str_tool herrmaienta de clonacion [partimage, partclone, ntfsclone] +#@param [opcional] str_compressionlevel nivel de compresion. [0 -none-, 1-lzop-, 2-gzip] +#@return str_command - cadena con el comando que se debe ejecutar. +#@warning Salida nula si se producen errores. +#@TODO introducir las herramientas fsarchiver, dd +#*/ ## +#ogCreateImageSyntax /dev/sda1 /opt/opengnsys/images/prueba.img partclone lzop +#ogCreateImageSyntax /dev/sda1 /opt/opengnsys/images/prueba.img +def ogCreateImageSyntax (dev, imgfile, tool='partclone', level='gzip'): + + if 'ntfsclone' == tool: + param1 = f'ntfsclone --force --save-image -O - {dev}' + elif 'partimage' == tool or 'default' == tool: + param1 = f'partimage -M -f3 -o -d -B gui=no -c -z0 --volume=0 save {dev} stdout' + elif 'partclone' == tool: + disk, part = DiskLib.ogDevToDisk (dev).split() + fs = FileSystemLib.ogGetFsType (disk, part) + param1 = { + 'EXT2': 'partclone.extfs', + 'EXT3': 'partclone.extfs', + 'EXT4': 'partclone.extfs', + 'BTRFS': 'partclone.btrfs', + 'REISERFS': 'partclone.reiserfs', + 'REISER4': 'partclone.reiser4', + 'JFS': 'partclone.jfs', + 'XFS': 'partclone.xfs', + 'F2FS': 'partclone.f2fs', + 'NILFS2': 'partclone.nilfs2', + 'NTFS': 'partclone.ntfs', + 'EXFAT': 'partclone.exfat', + 'FAT16': 'partclone.fat', + 'FAT32': 'partclone.fat', + 'HFS': 'partclone.hfsp', + 'HFSPLUS': 'partclone.hfsp', + 'UFS': 'partclone.ufs', + 'VMFS': 'partclone.vmfs', + }.get (fs, 'partclone.imager') + dash_c = '-c' + if not shutil.which (param1): + param1 = 'partclone.dd' + # Algunas versiones de partclone.dd no tienen opción "-c". + out = subprocess.run (['partclone.dd', '--help'], capture_output=True, text=True).stdout + if ' -c' not in out: dash_c = '' + param1=f'{param1} -d0 -F {dash_c} -s {dev}' + else: + raise Exception (f'unknown tool "{tool}"') + + param2 = '| mbuffer -q -m 40M ' if shutil.which ('mbuffer') else ' ' + + param3 = { + 0: ' > ', + 'none': ' > ', + 1: ' | lzop > ', + 'lzop': ' | lzop > ', + 2: ' | gzip -c > ', + 'gzip': ' | gzip -c > ', + 3: ' | bzip -c > ', + 'bzip': ' | bzip -c > ', + }.get (level, ' > ') + + #print (f'param1 ({param1}) param2 ({param2}) param3 ({param3}) imgfile ({imgfile})') + if param1: return f'{param1} {param2} {param3} {imgfile}' + + +#/** +# ogRestoreImageSyntax path_filename path_device [str_tools] [str_compressionlevel] +#@brief Genera una cadena de texto con la instrucción para crear un fichero imagen +#@param path_device dispositivo Linux del sistema de archivos +#@param path_fileneme path absoluto del fichero imagen +#@param [opcional] str_tools herrmaienta de clonacion [partimage, partclone, ntfsclone] +#@param [opcional] str_compressionlevel nivel de compresion. [0 -none-, 1-lzop-, 2-gzip] +#@return cadena con el comando que se debe ejecutar. +#@exception OG_ERR_FORMAT formato incorrecto. +#@warning En pruebas iniciales +#@TODO introducir las herramientas fsarchiver, dd +#@TODO introducir el nivel de compresion gzip +#*/ ## +#ogRestoreImageSyntax /opt/opengnsys/images/prueba.img /dev/sda1 [partclone] [lzop] +def ogRestoreImageSyntax (imgfile, part, tool=None, level=None): + if not os.path.exists (imgfile): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, imgfile) + ## original bash code is broken: 'return' is never called + #return + + if tool is None or level is None: + infoimg = ogGetImageInfo (imgfile) + if not infoimg: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f'no image {imgfile}') + ## original bash code is broken: 'return' is never called + #return + try: + tool, level = infoimg.split (':')[0:2] + except: + tool, level = '', '' + return ogRestoreImageSyntax (imgfile, part, tool, level) + + tool = tool.lower() + level = level.lower() + compressor = { + 0: ' ', + 'none': ' ', + 1: ' lzop -dc ', + 'lzop': ' lzop -dc ', + 2: ' gzip -dc ', + 'gzip': ' gzip -dc ', + 3: ' bzip -dc ', + 'bzip': ' bzip -dc ', + }.get (level, '') + #print (f'tool ({tool}) level ({level}) compressor ({compressor})') + if compressor == '': + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f'Compressor no valid {level}') + ## original bash code is broken: 'return' is never called + #return + + mbuffer = '| mbuffer -q -m 40M ' if shutil.which ('mbuffer') else ' ' + #print (f'mbuffer ({mbuffer})') + if 'ntfsclone' == tool: + tool = f'| ntfsclone --restore-image --overwrite {part} -' + elif 'partimage' == tool: + tool = f'| partimage -f3 -B gui=no restore {part} stdin' + elif 'partclone' in tool: + # -C para que no compruebe tamaños + tool = f'| partclone.restore -d0 -C -I -o {part}' + elif 'dd' == tool: + tool = f'| pv | dd conv=sync,noerror bs=1M of={part}' + else: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f'Tools imaging no valid {tool}') + ## original bash code is broken: 'return' is never called + #return + #print (f'tool ({tool})') + + return f'{compressor} {imgfile} {mbuffer} {tool}'.strip() + + + + +#/** +# ogCreateDiskImage int_ndisk str_repo path_image [str_tools] [str_compressionlevel] +#@brief Crea una imagen (copia de seguridad) de un disco completo. +#@param int_ndisk nº de orden del disco +#@param str_repo repositorio de imágenes (remoto o caché local) +#@param path_image camino de la imagen (sin extensión) +#@return (nada, por determinar) +#@note repo = { REPO, CACHE } +#@note Esta primera versión crea imágenes con dd comprimidas con gzip. +#@exception OG_ERR_FORMAT formato incorrecto. +#@exception OG_ERR_NOTFOUND fichero o dispositivo no encontrado. +#@exception OG_ERR_LOCKED particion bloqueada por otra operación. +#@exception OG_ERR_IMAGE error al crear la imagen del sistema. +#@warning En pruebas iniciales +#@todo Gestión de bloqueos de disco +#@todo Comprobar si debe desmontarse la caché local +#@todo Comprobar que no se crea la imagen en el propio disco +#*/ ## + + +#/** +# ogCreateImage int_ndisk int_npartition str_repo path_image [str_tools] [str_compressionlevel] +#@brief Crea una imagen a partir de una partición. +#@param int_ndisk nº de orden del disco +#@param int_npartition nº de orden de la partición +#@param str_repo repositorio de imágenes (remoto o caché local) +#@param path_image camino de la imagen (sin extensión) +#@param [opcional] str_tools herrmaienta de clonacion [partimage, partclone, ntfsclone] +#@param [opcional] str_compressionlevel nivel de compresion. [0 -none-, 1-lzop-, 2-gzip] +#@return (nada, por determinar) +#@note repo = { REPO, CACHE } +#@exception OG_ERR_FORMAT formato incorrecto. +#@exception OG_ERR_NOTFOUND fichero o dispositivo no encontrado. +#@exception OG_ERR_PARTITION partición no accesible o no soportada. +#@exception OG_ERR_LOCKED particion bloqueada por otra operación. +#@exception OG_ERR_IMAGE error al crear la imagen del sistema. +#@todo Comprobaciones, control de errores, definir parámetros, etc. +#*/ ## +def ogCreateImage (disk, par, container, imgfile, tool='partclone', level='gzip'): + PART = DiskLib.ogDiskToDev (disk, par) + if not PART: return None + + if FileSystemLib.ogIsLocked (disk, par): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_LOCKED, f'{ogGlobals.lang.MSG_ERR_LOCKED} {disk}, {par}') + return None + + imgtype = 'img' # Extensión genérica de imágenes. + imgdir = FileLib.ogGetParentPath (src=container, file=imgfile) + if not imgdir: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f'{container} {imgfile}') + return None + + bn = os.path.basename (imgfile) + IMGFILE = f'{imgdir}/{bn}.{imgtype}' + if ogIsImageLocked (IMGFILE): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_LOCKED, f'{ogGlobals.lang.MSG_IMAGE} {container}, {imgfile}') + return None + +# Generar la instruccion a ejecutar antes de aplicar los bloqueos. + program = ogCreateImageSyntax (PART, IMGFILE, tool=tool, level=level) +# Desmontar partición, bloquear partición e imagen. + FileSystemLib.ogUnmount (disk, par) + if not FileSystemLib.ogLock (disk, par): + return None + if not ogLockImage (container, f'{imgfile}.{imgtype}'): + return None + +# Crear Imagen. + #trap + print (f'nati program ({program})') + p = subprocess.run (program, shell=True, check=True) + errcode = p.returncode + if 0 == errcode: + i = ogGetImageInfo (IMGFILE) + h = NetLib.ogGetHostname() + with open (f'{IMGFILE}.info', 'w') as fd: + fd.write (f'{i}:{h}\n') + else: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_IMAGE, f'{disk} {par} {IMGFILE}') + if os.path.exists (IMGFILE): + os.unlink (IMGFILE) + + FileSystemLib.ogUnlock (disk, par) + ogUnlockImage (container, f'{imgfile}.{imgtype}') + return not errcode ## reverse to indicate success + + +#/** +# ogCreateMbrImage int_ndisk str_repo path_image +#@brief Crea una imagen a partir del sector de arranque de un disco. +#@param int_ndisk nº de orden del disco +#@param str_repo repositorio de imágenes (remoto o caché local) +#@param path_image camino de la imagen (sin extensión) +#@return (nada, por determinar) +#@note repo = { REPO, CACHE } +#@exception OG_ERR_FORMAT formato incorrecto. +#@exception OG_ERR_NOTFOUND fichero o dispositivo no encontrado. +#@exception OG_ERR_IMAGE error al crear la imagen del sistema. +#*/ ## + + +#/** +# ogCreateBootLoaderImage int_ndisk str_repo path_image +#@brief Crea una imagen del boot loader a partir del sector de arranque de un disco. +#@param int_ndisk nº de orden del disco +#@param str_repo repositorio de imágenes (remoto o caché local) +#@param path_image camino de la imagen (sin extensión) +#@return (nada, por determinar) +#@note repo = { REPO, CACHE } +#@exception OG_ERR_FORMAT formato incorrecto. +#@exception OG_ERR_NOTFOUND fichero o dispositivo no encontrado. +#@exception OG_ERR_IMAGE error al crear la imagen del sistema. +#*/ ## + +#/** +# ogGetSizeParameters int_num_disk int_num_part str_repo [monolit|sync|diff] +#@brief Devuelve el tamaño de los datos de un sistema de ficheros, el espacio necesario para la imagen y si cabe en el repositorio elegido. +#@param int_disk numero de disco +#@param int_part numero de particion +#@param str_repo repositorio de imágenes { REPO, CACHE } +#@param str_imageName Nombre de la imagen +#@param str_imageType Tipo de imagen: monolit (por defecto), sync o diff. (parametro opcional) +#@return SIZEDATA SIZEREQUIRED SIZEFREE ISENOUGHSPACE +#@note si str_imageType= diff necesario /tmp/ogimg.info, que es creado por ogCreateInfoImage. +#@note para el tamaño de la imagen no sigue enlaces simbólicos. +#@exception OG_ERR_FORMAT formato incorrecto. +#*/ ## +#SIZEDATA, SIZEREQUIRED, SIZEFREE, ISENOUGHSPACE = ogGetSizeParameters (1, 1, 'REPO', 'myimg') +def ogGetSizeParameters (disk, par, repo, imgname, imgtype=None): + repo = repo.upper() + ## imgtype se soporta como parametro pero se ignora + + mntdir = FileSystemLib.ogMount (disk, par) + if not mntdir: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, f'{disk} {par}') + return None + + #if [ "$IMGTYPE" == "_DIFF_" ]; then cosas + #else: + sizedata = None + df_out = subprocess.run (['df', '-k'], capture_output=True, text=True).stdout + for l in df_out.splitlines(): + if (re.search (f'{mntdir}$', l)): + sizedata = int (l.split()[2]) + break + if sizedata is None: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_GENERIC, 'sizedata is None') + return None + + #if [ "$IMGTYPE" == "_SYNC_" -o "$IMGTYPE" == "_DIFF_" ]; then cosas + #else: + factorgzip=55/100 + factorlzop=65/100 + sizerequired = sizedata * factorlzop + + #Comprobar espacio libre en el contenedor. + sizefree = None + if 'CACHE' == repo: + CACHEPART = CacheLib.ogFindCache() + if not CACHEPART: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTCACHE, '') + return None + cache_disk, cache_par = CACHEPART.split() + sizefree = FileSystemLib.ogGetFreeSize (cache_disk, cache_par) + if 'REPO' == repo: + df_out = subprocess.run (['df', '-k'], capture_output=True, text=True).stdout + for l in df_out.splitlines(): + if (re.search (f'{ogGlobals.OGIMG}', l)): + sizefree = int (l.split()[3]) + break + if sizefree is None: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_GENERIC, 'sizefree is None') + return None + +# Comprobamos si existe una imagen con el mismo nombre en $REPO +# En sincronizadas restamos tamaño de la imagen y en monoloticas de la .ant + #case "${IMGTYPE}" in blah blah + #*) + imgext = 'img.ant' + + imgdir = FileLib.ogGetParentPath (src=repo, file=f'/{imgname}') + bn = os.path.basename (f'/{imgname}') + imgfile = FileLib.ogGetPath (file=f'{imgdir}/{bn}.{imgext}') + + if not imgfile: + imgsize = 0 + else: + ls_out = subprocess.run (['ls', '-s', imgfile], capture_output=True, text=True).stdout + imgsize = int (ls_out.split()[0]) + + sizefree = sizefree + imgsize + + if sizerequired < sizefree: + isenoughspace = True + else: + isenoughspace = False + + return sizedata, sizerequired, sizefree, isenoughspace + +#/** +# ogIsImageLocked [str_repo] path_image +#@brief Comprueba si una imagen está bloqueada para uso exclusivo. +#@param str_repo repositorio de imágenes (opcional) +#@param path_image camino de la imagen (sin extensión) +#@return Código de salida: 0 - bloqueado, 1 - sin bloquear o error. +#@note repo = { REPO, CACHE } +#@exception OG_ERR_FORMAT formato incorrecto. +#*/ ## +#ogIsImageLocked ('/opt/opengnsys/images/aula1/win7.img') +#ogIsImageLocked ('REPO', '/aula1/win7.img') +def ogIsImageLocked (container=None, imgfile=None): + if container and imgfile: + p = FileLib.ogGetPath (src=container, file=f'{imgfile}.lock') + elif imgfile: + p = FileLib.ogGetPath (file=f'{imgfile}.lock') + else: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, f'{container} {imgfile}') + return + return os.path.exists (p) + + +#/** +# ogLockImage [str_repo] path_image +#@brief Bloquea una imagen para uso exclusivo. +#@param str_repo repositorio de imágenes (opcional) +#@param path_image camino de la imagen (sin extensión) +#@return Nada. +#@note Se genera un fichero con extensión .lock +#@note repo = { REPO, CACHE } +#@exception OG_ERR_FORMAT formato incorrecto. +#*/ ## +def ogLockImage (container=None, imgfile=None): + if not imgfile: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, '') + return None + + if container: + imgdir = FileLib.ogGetParentPath (src=container, file=imgfile) + else: + imgdir = FileLib.ogGetParentPath (file=imgfile) + + try: + bn = os.path.basename (imgfile) + open (f'{imgdir}/{bn}.lock', 'w').close() + return True + except: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTWRITE, f'{container} {imgfile}') + return None + + +#/** +# ogRestoreDiskImage str_repo path_image int_npartition +#@brief Restaura (recupera) una imagen de un disco completo. +#@param str_repo repositorio de imágenes o caché local +#@param path_image camino de la imagen +#@param int_ndisk nº de orden del disco +#@return (por determinar) +#@warning Primera versión en pruebas +#@todo Gestionar bloqueos de disco +#@todo Comprobar que no se intenta restaurar de la caché sobre el mismo disco +#@exception OG_ERR_FORMAT formato incorrecto. +#@exception OG_ERR_NOTFOUND fichero de imagen o partición no detectados. +#@exception OG_ERR_LOCKED partición bloqueada por otra operación. +#@exception OG_ERR_IMAGE error al restaurar la imagen del sistema. +#@exception OG_ERR_IMGSIZEPARTITION Tamaño de la particion es menor al tamaño de la imagen. +#*/ ## + + +#/** +# ogRestoreImage str_repo path_image int_ndisk int_npartition +#@brief Restaura una imagen de sistema de archivos en una partición. +#@param str_repo repositorio de imágenes o caché local +#@param path_image camino de la imagen +#@param int_ndisk nº de orden del disco +#@param int_npartition nº de orden de la partición +#@return (por determinar) +#@exception OG_ERR_FORMAT 1 formato incorrecto. +#@exception OG_ERR_NOTFOUND 2 fichero de imagen o partición no detectados. +#@exception OG_ERR_PARTITION 3 # Error en partición de disco. +#@exception OG_ERR_LOCKED 4 partición bloqueada por otra operación. +#@exception OG_ERR_IMAGE 5 error al restaurar la imagen del sistema. +#@exception OG_ERR_IMGSIZEPARTITION 30 Tamaño de la particion es menor al tamaño de la imagen. +#@todo Comprobar incongruencias partición-imagen, control de errores, definir parámetros, caché/repositorio, etc. +#*/ ## +#ogRestoreImage ('REPO', '/aula1/win7', '1', '1') +def ogRestoreImage (repo, imgpath, disk, par): + PART = DiskLib.ogDiskToDev (disk, par) + if not PART: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f' {disk} {par}') + return None + + imgtype = 'img' + imgfile = FileLib.ogGetPath (repo, f'{imgpath}.{imgtype}') + if not os.path.exists (imgfile): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f' {disk} {par}') + return None + + if not ogGetImageInfo (imgfile): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_IMAGE, f' {repo} {imgpath}') + return None + + # Error si la imagen no cabe en la particion. + imgsize = ogGetImageSize (repo, imgpath) + if not imgsize: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_IMAGE, f' {repo} {imgpath}') + return None + if not FileSystemLib.ogMount (disk, par): + FileSystemLib.ogFormat (disk, par) + partsize = DiskLib.ogGetPartitionSize (disk, par) + if float (imgsize) > float (partsize): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_IMGSIZEPARTITION, f' {partsize} < {imgsize}') + return None + + if ogIsImageLocked (container=None, imgfile=imgfile): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_LOCKED, f'{ogGlobals.lang.MSG_IMAGE} {repo}, {imgpath}.{imgtype}') + return None + + if FileSystemLib.ogIsLocked (disk, par): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_LOCKED, f'{ogGlobals.lang.MSG_PARTITION} {disk}, {par}') + return None + + program = ogRestoreImageSyntax (imgfile, PART) + + if not FileSystemLib.ogUnmount (disk, par): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, f' {disk} {par}') + return None + + if not FileSystemLib.ogLock (disk, par): + print (f'not FileSystemLib.ogLock()') + return None + + rc = None + try: + #print (f'nati: ogRestoreImage: running ({program})') + p = subprocess.run (program, shell=True, capture_output=True, text=True) + rc = p.returncode + #print (f'nati: ogRestoreImage: rc ({rc}) stdout ({p.stdout}) stderr ({p.stderr})') + if not rc: + SystemLib.ogRaiseError ([], ogGlobalsOG_ERR_IMAGE, f'{imgfile}, {disk}, {par}') + except: + pass + finally: + FileSystemLib.ogUnlock (disk, par) + + return rc + + + +#/** +# ogRestoreMbrImage str_repo path_image int_ndisk +#@brief Restaura la imagen del sector de arranque de un disco. +#@param str_repo repositorio de imágenes o caché local +#@param path_image camino de la imagen +#@param int_ndisk nº de orden del disco +#@return (por determinar) +#@exception OG_ERR_FORMAT formato incorrecto. +#@exception OG_ERR_NOTFOUND fichero de imagen o partición no detectados. +#@exception OG_ERR_IMAGE error al restaurar la imagen del sistema. +#*/ ## + + +#/** +# ogRestoreBootLoaderImage str_repo path_image int_ndisk +#@brief Restaura la imagen del boot loader del sector de arranque de un disco. +#@param str_repo repositorio de imágenes o caché local +#@param path_image camino de la imagen +#@param int_ndisk nº de orden del disco +#@return (por determinar) +#@exception OG_ERR_FORMAT formato incorrecto. +#@exception OG_ERR_NOTFOUND fichero de imagen o partición no detectados. +#@exception OG_ERR_IMAGE error al restaurar la imagen del sistema. +#*/ ## + +#/** +# ogUnlockImage [str_repo] path_image +#@brief Desbloquea una imagen con uso exclusivo. +#@param str_repo repositorio de imágenes (opcional) +#@param path_image camino de la imagen (sin extensión) +#@return Nada. +#@note repo = { REPO, CACHE } +#@note Se elimina el fichero de bloqueo con extensión .lock +#@exception OG_ERR_FORMAT formato incorrecto. +#*/ ## +#ogUnlockImage REPO /cucu.img +def ogUnlockImage (container=None, imgfile=None): + f = f'{imgfile}.lock' + if container: + p = FileLib.ogGetPath (src=container, file=f) + else: + p = FileLib.ogGetPath (file=f) + + if os.path.exists (p): + os.unlink (p) + + +#/** +# ogGetImageInfo filename +#@brief muestra información sobre la imagen monolitica. +#@param 1 filename path absoluto del fichero imagen +#@return cadena compuesta por clonacion:compresor:sistemaarchivos:tamañoKB +#@exception OG_ERR_FORMAT formato incorrecto. +#@exception OG_ERR_NOTFOUND fichero no encontrado. +#@exception OG_ERR_IMAGE "Image format is not valid $IMGFILE" +#@warning En pruebas iniciales +#@TODO Definir sintaxis de salida (herramienta y compresor en minuscula) +#@TODO Arreglar loop para ntfsclone +#@TODO insertar parametros entrada tipo OG +#*/ ## +#ogGetImageInfo /opt/opengnsys/images/prueba.img ==> PARTCLONE:LZOP:NTFS:5642158" +def ogGetImageInfo (imgfile): + if not os.path.exists (imgfile): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, imgfile) + return + + imgdetect = False + filehead = f'/tmp/{os.path.basename (imgfile)}.infohead' + compressor = subprocess.run (['file', imgfile], capture_output=True, text=True).stdout.split()[1] + if compressor not in ['gzip', 'lzop']: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_IMAGE, f'Image format is not valid {imgfile}') + return + + ## original bash idiom is: $($COMPRESSOR -dc $IMGFILE 2>/dev/null | head -n 40 > $FILEHEAD) || ogRaiseError + ## the purpose of which I can't fully comprehend + #print (f'shelling out "{compressor} -dc {imgfile} |head -n 40 > {filehead}"') + if subprocess.run (f'{compressor} -dc {imgfile} |head -n 40 > {filehead}', shell=True).returncode: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_IMAGE, f'Image format is not valid {imgfile}') + return + + tools = fs = size = None + + if False == imgdetect: + lc_all = os.getenv ('LC_ALL') + os.environ['LC_ALL'] = 'C' + partclone_info = subprocess.run (['partclone.info', filehead], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True).stdout + #partclone_info = subprocess.run (['cat', '/tmp/foo-partclone'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True).stdout + ## sacado de un email de alberto garcía uma.es + #Partclone v0.3.13 http://partclone.org + #Unknown mode + #File system: NTFS + #Device size: 29.2 GB = 7138019 Blocks + #Space in use: 26.6 GB = 6485355 Blocks + #Free Space: 2.7 GB = 652664 Blocks + #Block size: 4096 Byte + # + #image format: 0002 + #created on a: 64 bits platform + #with partclone: v0.3.13 + #bitmap mode: BIT + #checksum algo: CRC32 + #checksum size: 4 + #blocks/checksum: 256 + if lc_all is not None: + os.environ["LC_ALL"] = lc_all + else: + del os.environ["LC_ALL"] + + if 'size' in partclone_info: + tools = 'PARTCLONE' + m = re.search (r'File system *: *(\S+)', partclone_info) + fs = m.group(1) if m else '' + + sizefactor = 1000000 if 'GB' in partclone_info else 1024 + m = re.search (r'Device size *: *(\S+)', partclone_info) + size = float (m.group(1)) if m else 0 + size = int (size * sizefactor) + + ## why is this? + #if fs in ['HFS', 'HFSPLUS', 'FAT32']: + # #FSPLUS=$(echo $PARTCLONEINFO | awk '{gsub(/\: /,"\n"); print toupper($9);}') + # fsplus = 'PLUS' + # if fsplus: # fs += fsplus ## 'HFS' -> 'HFSPLUS' + + imgdetect = True + + if False == imgdetect and not os.path.exists ('/dev/loop2'): + filehead_contents = Path (filehead).read_bytes() + if b'ntfsclone-image' in filehead_contents: + #print (f'shelling out "cat {filenead} | ntfsclone --restore --overwrite /dev/loop2 - 2>&1"') + ntfscloneinfo = subprocess.run (f'cat {filenead} | ntfsclone --restore --overwrite /dev/loop2 - 2>&1', shell=True, capture_output=True, text=True).stdout + #ntfscloneinfo = subprocess.run (['cat', '/tmp/foo-ntfsclone'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True).stdout + ## sacado de claude 3 haiku + #ntfsclone v2023.4.0 (libntfs-3g) + #NTFS volume version: 3.1 + #Cluster size: 4096 bytes + #Image volume size: 104857600 bytes (105 MB) + #Space in use: 52428800 bytes (52 MB) + #Reading and restoring NTFS... + #100.00 percent completed + #Syncing ... + #Successfully cloned image to device '/dev/loop2'. + else: + ntfscloneinfo = '' + + if 'ntfsclone' in ntfscloneinfo: + tools = 'NTFSCLONE' + m = re.search (r'Image volume size *: *(\S+)', ntfscloneinfo) + size = float (m.group(1))/1000 if m else 0 + fs = 'NTFS' + imgdetect = True + + if False == imgdetect: + partimageinfo = subprocess.run (['partimage', '-B', 'gui=no', 'imginfo', filehead], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True).stdout + #partimageinfo = subprocess.run (['cat', '/tmp/foo-partimage'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True).stdout + ## sacado de un email de alberto garcía uma.es + #Volume number:.........0 + #Volume size:...........1,27 MiB + #Compression level: ....0 -> ninguno + #Identificator:.........12442509728668372730=ACACACACCB9ECEFA + #Filesystem:............ntfs + #Description:...........Sin descripcion + #Original device:......./dev/nvme0n1p2 + #Original filepath:.... stdout + #Flags:.................0: Bandera sin activar + #Creation date:.........Mon Nov 11 21:00:22 2024 + #Partition size:........476,84 GiB + #Hostname:..............ING-LTR-083 + #Compatible Version:....0.6.1 + #Encryption algorithm:..0 -> ninguno + #MBR saved count:.......0 + partimageinfo = re.sub (r':\s*\.+', ' : ', partimageinfo) + if 'Partition' in partimageinfo: + tools = 'PARTIMAGE' + m = re.search (r'Filesystem *: *(\S+)', partimageinfo) + fs = m.group(1).upper() if m else '' + + m = re.search (r'Partition size *: *(\S+)', partimageinfo) + size = m.group(1) if m else '' + size = re.sub (r' [MGT]i?B$', '', size) + size = float (size.replace (',', '.')) + size = int (size*1024*1024) + + imgdetect = True + if 'boot sector' in subprocess.run (['file', filehead], capture_output=True, text=True).stdout: + tools = 'partclone.dd' + fs = '' + size = 0 + imgdetect = True + + if not tools or not compressor or False == imgdetect: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_IMAGE, f'Image format is not valid {imgfile}') + return + + compressor = compressor.upper() + return ':'.join ([tools, compressor, fs, str (size)]) + +#/** +# ogGetImageProgram str_REPO str_imagen +#@brief muestra información sobre la imagen monolitica. +#@see ogGetImageInfo +#@param 1 REPO o CACHE contenedor de la imagen +#@param 2 filename nombre de la imagen sin extension +#@return nombre del programa usado para generar la imagen +#@exception OG_ERR_FORMAT formato incorrecto. +#@exception OG_ERR_NOTFOUND fichero no encontrado. +#@note ogGetImageProgram REPO imagenA -> partclone +#*/ ## + +#ogGetImageProgram ('REPO', 'prueba') ==> 'PARTCLONE' +def ogGetImageProgram (container, filename): + imgfile = FileLib.ogGetPath (container, f'{filename}.img') + if not os.path.exists (imgfile): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, imgfile) + return None + i = ogGetImageInfo (imgfile) + return i.split (':')[0] + +#/** +# ogGetImageCompressor str_REPO str_imagen +#@brief muestra información sobre la imagen monolitica. +#@see ogGetImageInfo +#@param 1 REPO o CACHE contenedor de la imagen +#@param 2 filename nombre de la imagen sin extension +#@return tipo de compresión usada al generar la imagen +#@exception OG_ERR_FORMAT formato incorrecto. +#@exception OG_ERR_NOTFOUND fichero no encontrado. +#@note ogGetImageCompressor REPO imagenA -> lzop +#*/ ## + +#ogGetImageCompressor ('REPO', 'prueba') ==> 'LZOP' +def ogGetImageCompressor (container, filename): + imgfile = FileLib.ogGetPath (container, f'{filename}.img') + if not os.path.exists (imgfile): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, imgfile) + return None + i = ogGetImageInfo (imgfile) + return i.split (':')[1] + + +#/** +# ogGetImageType str_REPO str_imagen +#@brief muestra información sobre el sistema de archivos de imagen monolitica. +#@see ogGetImageInfo +#@param 1 REPO o CACHE contenedor de la imagen +#@param 2 filename nombre de la imagen sin extension +#@return tipo de compresión usada al generar la imagen +#@exception OG_ERR_FORMAT formato incorrecto. +#@exception OG_ERR_NOTFOUND fichero no encontrado. +#@note ogGetImageType REPO imagenA -> NTFS +#*/ ## +#ogGetImageType ('REPO', 'imgprueba') ==> 'NTFS' +#ogGetImageType ('CACHE', 'testimg') ==> 'EXTFS' +def ogGetImageType (repo, imgname): + imgfile = FileLib.ogGetPath (src=repo, file=f'{imgname}.img') + if not os.path.exists (imgfile): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, imgfile) + return None + + i = ogGetImageInfo (imgfile) + return i.split (':')[2] + + + +#/** +# ogGetImageSize str_REPO str_imagen +#@brief muestra información sobre el tamaño (KB) del sistema de archivos de imagen monolitica. +#@see ogGetImageInfo +#@param 1 REPO o CACHE contenedor de la imagen +#@param 2 filename nombre de la imagen sin extension +#@return tipo de compresión usada al generar la imagen +#@exception OG_ERR_FORMAT formato incorrecto. +#@exception OG_ERR_NOTFOUND fichero no encontrado. +#@note ogGetImagesize REPO imagenA -> 56432234 > Kb +#*/ ## +#ogGetImageSize ('REPO', 'prueba') ==> '5642158' +def ogGetImageSize (repo, imgname): + imgfile = FileLib.ogGetPath (src=repo, file=f'{imgname}.img') + if not os.path.exists (imgfile): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, imgfile) + return None + + i = ogGetImageInfo (imgfile) + return i.split (':')[3] + + +#/** +# ogCreateGptImage int_ndisk str_repo path_image +#@brief Crea una imagen de la tabla de particiones GPT de un disco. +#@param int_ndisk nº de orden del disco +#@param str_repo repositorio de imágenes (remoto o caché local) +#@param path_image camino de la imagen (sin extensión) +#@return (nada, por determinar) +#@note repo = { REPO, CACHE } +#@exception OG_ERR_FORMAT formato incorrecto. +#@exception OG_ERR_NOTFOUND fichero o dispositivo no encontrado. +#@exception OG_ERR_IMAGE error al crear la imagen del sistema. +#*/ ## + +#/** +# ogRestoreGptImage str_repo path_image int_ndisk +#@brief Restaura la imagen de la tabla de particiones GPT de un disco. +#@param str_repo repositorio de imágenes o caché local +#@param path_image camino de la imagen +#@param int_ndisk nº de orden del disco +#@return (por determinar) +#@exception OG_ERR_FORMAT formato incorrecto. +#@exception OG_ERR_NOTFOUND fichero de imagen o partición no detectados. +#@exception OG_ERR_IMAGE error al restaurar la imagen del sistema. +#*/ ## diff --git a/client/lib/python3/InventoryLib.py b/client/lib/python3/InventoryLib.py new file mode 100644 index 0000000..2269af3 --- /dev/null +++ b/client/lib/python3/InventoryLib.py @@ -0,0 +1,531 @@ +#/** +#@file InventoryLib.py +#@brief Librería o clase Inventory +#@class Inventory +#@brief Funciones para recogida de datos de inventario de hardware y software de los clientes. +#@warning License: GNU GPLv3+ +#*/ + +import platform +import sys +import os +import subprocess +import re +import json +import shutil +import glob +import plistlib +#import bsddb + +import ogGlobals +import SystemLib +import FileSystemLib +import RegistryLib +import FileLib + + +#/** +# ogGetArch +#@brief Devuelve el tipo de arquitectura del cliente. +#@return str_arch - Arquitectura (i386 para 32 bits, x86_64 para 64 bits). +#*/ +def ogGetArch(): + if os.path.isdir ('/lib64'): + return 'x86_64' + else: + return 'i386' + + +#/** +# ogGetOsType int_ndisk int_npartition +#@brief Devuelve el tipo del sistema operativo instalado. +#@param int_ndisk nº de orden del disco +#@param int_npartition nº de orden de la partición +#@return OSType - Tipo de sistema operativo. +#@see ogGetOsVersion +#*/ ## +def ogGetOsType(disk, partition): + try: + os_version = ogGetOsVersion(disk, partition) + if os_version: + return os_version.split(":", 1)[0] + else: + return None + except Exception as e: + print(f"Error en ogGetOsType: {e}") + return "Unknown" + + +#/** +# ogGetOsUuid int_ndisk int_nfilesys +#@brief Devuelve el UUID del sistema operativo instalado en un sistema de archivos. +#@param int_ndisk nº de orden del disco +#@param int_nfilesys nº de orden de la partición +#@return str_uuid - UUID del sistema operativo. +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Disco o partición no corresponden con un dispositiv +#*/ ## +def ogGetOsUuid (disk, par): + mntdir = FileSystemLib.ogMount (disk, par) + if not mntdir: return None + +# Obtener UUID según el tipo de sistema operativo. + os_type = ogGetOsType (disk, par) + if 'Linux' == os_type: + # Leer el UUID del sistema de ficheros raíz o el fichero de identificador. + uuid = subprocess.run (['findmnt', '-no', 'UUID', mntdir], capture_output=True, text=True).stdout.strip() + if not uuid: + uuid = open (os.path.join (mntdir, 'etc', 'machine-id')).read().strip() + return uuid + elif 'Windows' == os_type: + # Leer identificador en clave de registro. + uuid = RegistryLib.ogGetRegistryValue (mntdir, 'SOFTWARE', r'\Microsoft\Cryptography\MachineGuid') + return uuid + + +#/** +# ogGetSerialNumber +#@brief Obtiene el nº de serie del cliente. +#*/ ## +def ogGetSerialNumber(): + SERIALNO = subprocess.check_output(["dmidecode", "-s", "system-serial-number"]).decode().strip() + SERIALNO = re.sub(r"(not specified|to be filled|invalid entry|default string)", "", SERIALNO, flags=re.IGNORECASE) + SERIALNO = SERIALNO.replace(" ", "") + SERIALNO = SERIALNO[:25] if len(SERIALNO) > 25 else SERIALNO + if SERIALNO: return SERIALNO + return None + + +#/** +# ogIsEfiActive +#@brief Comprueba si el sistema tiene activo el arranque EFI. +#*/ ## +def ogIsEfiActive(): + return os.path.isdir("/sys/firmware/efi") + + +#/** +# ogListHardwareInfo +#@brief Lista el inventario de hardware de la máquina cliente. +#@return TipoDispositivo:Modelo (por determinar) +#@warning Se ignoran los parámetros de entrada. +#@note TipoDispositivo = { bio, boa, bus, cha, cdr, cpu, dis, fir, mem, mod, mul, net, sto, usb, vga } +#@note Requisitos: dmidecode, lshw, awk +#*/ ## +def ogListHardwareInfo(): + ret = '' + + SystemLib.ogEcho ([], 'info', ogGlobals.lang.MSG_HARDWAREINVENTORY) + # Ejecutar dmidecode y obtener tipo de chasis + dmi_out = subprocess.run (['dmidecode', '-s', 'chassis-type'], capture_output=True, text=True).stdout + dmi_out = '\n'.join ([ x for x in dmi_out.splitlines() if 'Other' not in x ]) + ret += f'cha={dmi_out}\n' + + if os.path.exists ('/sys/firmware/efi'): + ret += f'boo=UEFI\n' + else: + ret += f'boo=BIOS\n' + + awk_script = r''' + BEGIN {type="mod";} + /product:/ {sub(/ *product: */,""); prod=$0;} + /vendor:/ {sub(/ *vendor: */,""); vend=$0;} + /version:/ {sub(/ *version: */,"v.");vers=$0;} + /size:/ {size=$2;} + /clock:/ {clock=$2;} + /slot:/ {sub(/ *slot: */,""); slot=$0;} + /\*-/ {if (type=="mem"){ + if (size!=""){ + numbank++; + print type"="vend,prod,size,clock" ("slot")";} + }else{ + if (type=="totalmem"){ + if (size!=""){ + totalmemory="mem="size;} + }else{ + if (type!="" && prod!=""){ + if (prod=="v."vers) + vers=""; + print type"="vend,prod,size,vers;} } + } + type=prod=vend=vers=size=clock=slot="";} + $1~/-core/ {type="boa";} + $1~/-firmware/ {type="bio";} + $1~/-cpu/ {type="cpu";} + $1~/-bank/ {type="mem";} + $1~/-memory/ {type="totalmem";} + $1~/-ide/ {type="ide";} + $1~/-storage/ {type="sto";} + $1~/-disk/ {type="dis";} + $1~/-cdrom/ {type="cdr";} + $1~/-display/ {type="vga";} + $1~/-network/ {type="net";} + $1~/-multimedia/ {type="mul";} + $1~/-usb/ {type="usb";} + $1~/-firewire/ {type="fir";} + $1~/-serial/ {type="bus";} + END {if (type!="" && prod!="") + print type"="vend,prod,size,vers; + if (length(numbank)==0 && length(totalmemory)>=4) + print totalmemory; } + ''' + lshw_out = subprocess.run (['lshw'], capture_output=True, text=True).stdout + awk_out = subprocess.run (['awk', awk_script], input=lshw_out, capture_output=True, text=True).stdout + + ret += awk_out + + return ret + + +#/** +# ogListSoftware int_ndisk int_npartition +#@brief Lista el inventario de software instalado en un sistema operativo. +#@param int_ndisk nº de orden del disco +#@param int_npartition nº de orden de la partición +#@return programa versión ... +#@warning Se ignoran los parámetros de entrada. +#@note Requisitos: ... +#@todo Detectar software en Linux +#*/ ## +def ogListSoftware (disk, par): + mntdir = FileSystemLib.ogMount (disk, par) + if not mntdir: return None + ostype = ogGetOsType (disk, par) + if not ostype: return None + + apps = [] + + if 'Linux' == ostype: + # Procesar paquetes dpkg. + pkgdir = f'{mntdir}/var/lib/dpkg' + if os.path.exists (pkgdir): + status_file = os.path.join(pkgdir, "status") + awk_script = ''' + /Package:/ {if (pack!="") print pack,vers; + sub(/-dev$/,"",$2); + pack=$2} + /Version:/ {sub(/^.*:/,"",$2); sub(/-.*$/,"",$2); + vers=$2} + /Status:/ {if ($2!="install") pack=vers=""} + END {if (pack!="") print pack,vers} + ''' + awk_out = subprocess.run (['awk', awk_script, status_file], capture_output=True, text=True).stdout + apps = awk_out.splitlines() + + # Procesar paquetes RPM. + pkgdir = f'{mntdir}/var/lib/rpm' + if os.path.exists (pkgdir): + if shutil.which ('rpm'): + for f in glob.glob (f'{pkgdir}/__db.*'): + os.unlink (f) + rpm_out = subprocess.run (['rpm', '--dbpath', pkgdir, '-qa', '--qf', '%{NAME} %{VERSION}\n'], capture_output=True, text=True).stdout + for l in rpm_out.splitlines(): + words = l.split() + if (not re.search ('-devel$', words[0])): + words[1] = re.sub ('-.*', '', words[1]) + apps.append (' '.join (words)) + for f in glob.glob (f'{pkgdir}/__db.*'): + os.unlink (f) + else: + pass + #db = bsddb.hashopen (f'{pkgdir}/Name', 'r'); + #for k in db.keys(): + # apps.append (re.sub ('-devel$', '', k)) + + # Procesar paquetes pacman. + pkgdir = f'{mntdir}/var/lib/pacman/local' + if os.path.exists (pkgdir): + for f in glob.glob (f'{pkgdir}/*'): + if '-' not in f: continue + idx = f[0:f.rfind ('-')].rfind ('-') ## index of 2nd-to-last dash + apps.append (f[0:idx] + ' ' + f[idx+1:]) + # Procesar aplicaciones Snappy. + pkgdir = f'{mntdir}/snap' + out = '' + awk_script = ''' + /name:/ {pack=$2} + /version:/ {vers=$2} + END {if (pack!="") print pack,"(snap)",vers} + ''' + files = subprocess.run (['find', f'{pkgdir}/*/current/meta', '-name', 'snap.yaml'], capture_output=True, text=True).stdout.splitlines() + for f in files: + awk_out = subprocess.run (['awk', awk_script, f], capture_output=True, text=True).stdout + out += awk_out + apps += out.splitlines() + # Procesar aplicaciones Flatpak. + pkgdir = f'{mntdir}/var/lib/flatpak' + files = glob.glob (f'{pkgdir}/app/*/current/active/deploy/*') + for f in files: + p = open (f.strip()).read().split ('\0') + try: + if (p[0] != 'flathub'): raise ValueError + apps.append ('{} (flatpak) {}'.format (p[p.index('appdata-name') + 4], p[p.index('appdata-version') + 1])) + except ValueError: + pass + + elif 'Windows' == ostype: + if shutil.which ('hivexregedit'): + hive = RegistryLib.ogGetHivePath (mntdir, 'software') + if hive: + cmd1_out = subprocess.run (['hivexregedit', '--unsafe-printable-strings', '--export', hive, r'\Microsoft\Windows\CurrentVersion\Uninstall'], capture_output=True, text=True).stdout + cmd1_out += subprocess.run (['hivexregedit', '--unsafe-printable-strings', '--export', hive, r'\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall'], capture_output=True, text=True).stdout + out = name = '' + for l in cmd1_out.splitlines(): + words = l.split ('"') + if len(words) < 4: continue + if (re.match (r'\[', words[0])): name='' + if (re.search ('DisplayName', words[1])): name=words[3] + if (re.search ('DisplayVersion', words[1])): apps.append (f'{name} {words[3]}') + else: + keys = RegistryLib.ogListRegistryKeys (mntdir, 'software', r'\Microsoft\Windows\CurrentVersion\Uninstall') + keys32 = RegistryLib.ogListRegistryKeys (mntdir, 'software', r'\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall') + for k in keys: + prog = RegistryLib.ogGetRegistryValue (mntdir, 'software', rf'\Microsoft\Windows\CurrentVersion\Uninstall\{k}\DisplayName') + if prog: + vers = RegistryLib.ogGetRegistryValue (mntdir, 'software', rf'\Microsoft\Windows\CurrentVersion\Uninstall\{k}\DisplayVersion') + apps.append (f'{prog} {vers}') + for k in keys32: + prog = RegistryLib.gGetRegistryValue (mntdir, 'software', rf'\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{k}\DisplayName') + if prog: + vers = RegistryLib.ogGetRegistryValue (mntdir, 'software', rf'\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{k}\DisplayVersion') + apps.append (f'{prog} {vers}') + + elif 'MacOS' == ostype: + files = subprocess.run (['find', f'{mntdir}/Applications', '-type', 'd', '-name', '*.app', '-prune', '-print'], capture_output=True, text=True).stdout.splitlines() + for k in files: + FILE = f'{k}/Contents/version.plist' + if not os.stat (FILE).st_size: + FILE = f'{k}/Contents/version.plist.uncompress' + if os.stat (FILE).st_size: + VERSION = subprocess.run (['awk', '-F[<>]', '/ShortVersionString/ {getline;v=$3} END {print v}', FILE], capture_output=True, text=True).stdout.strip() + bn = os.path.basename (k) + bn = re.sub ('.app$', '', bn) + apps.append (f'{bn} {VERSION}') + + elif 'BSD' == ostype: + sqlite_out = subprocess.run (['sqlite3', f'{mntdir}/var/db/pkg/local.sqlite'], input='SELECT name FROM pkg_search;\n', capture_output=True, text=True).stdout + for l in sqlite_out.splitlines(): + apps.append (' '.join (re.search ('(.*)-(.*)', l).groups())) + + else: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTOS, f'{disk}, {par} ({ostype})') + return None + + os_version = ogGetOsVersion (disk, par).split (':')[1] + return [os_version] + sorted (set (apps)) + +## https://stackoverflow.com/questions/2522651/find-a-key-inside-a-deeply-nested-dictionary/2522706#2522706 +def _find_key_recursive(plist_dict, key_substr): + for k in plist_dict.keys(): + if key_substr in k: return plist_dict[k] + for k, v in plist_dict.items(): + if type(v) is dict: # Only recurse if we hit a dict value + value = _find_key_recursive(v, key_substr) + if value: + return value + return '' + +#/** +# ogGetOsVersion int_ndisk int_nfilesys +#@brief Devuelve la versión del sistema operativo instalado en un sistema de archivos. +#@param int_ndisk nº de orden del disco +#@param int_nfilesys nº de orden de la partición +#@return OSType:OSVersion - tipo y versión del sistema operativo. +#@note OSType = { Android, BSD, GrubLoader, Hurd, Linux, MacOS, Solaris, Windows, WinLoader } +#@note Requisitos: awk, head, chroot +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Disco o partición no corresponden con un dispositiv +#@exception OG_ERR_PARTITION Fallo al montar el sistema de archivos. +#*/ ## +#ogGetOsVersion ("1", "2") => "Linux:Ubuntu precise (12.04 LTS) 64 bits" +def ogGetOsVersion(disk, part): + mntdir = FileSystemLib.ogMount (disk, part) + if not mntdir: + return None + + type = version = None + is64bit = '' + +# Buscar tipo de sistema operativo. +# Para GNU/Linux: leer descripción. + os_release = os.path.join(mntdir, "etc/os-release") + if os.path.isfile(os_release): + type = 'Linux' + with open(os_release, "r") as f: + for line in f: + if line.startswith("PRETTY_NAME"): + version = line.split("=", 1)[1].strip().strip('"') + break + + if not version: + lsb_release = os.path.join(mntdir, "etc/lsb-release") + if os.path.isfile(lsb_release): + type = 'Linux' + with open(lsb_release, "r") as f: + for line in f: + if line.startswith("DISTRIB_DESCRIPTION"): + version = line.split("=", 1)[1].strip().strip('"') + break + + if not version: + for distrib in ["redhat", "SuSE", "mandrake", "gentoo"]: + distrib_file = os.path.join(mntdir, f"etc/{distrib}-release") + if os.path.isfile(distrib_file): + type = 'Linux' + with open(distrib_file, "r") as f: + version = f.readline().strip() + break + + if not version: + arch_release_file = os.path.join(mntdir, "etc/arch-release") + if os.path.isfile(arch_release_file): + type = 'Linux' + version = "Arch Linux" + + if not version: + slack_release_file = os.path.join(mntdir, "slackware-version") + if os.path.isfile(slack_release_file): + type = 'Linux' + with open (slack_release_file, 'r') as fd: + c = fd.read() + version = "Slackware {c}" + +# Si no se encuentra, intentar ejecutar "lsb_release". + if not version: + out = subprocess.run (['chroot', mntdir, 'lsb_release', '-d'], capture_output=True, text=True).stdout + m = re.search (':\t(.*)', out) + if m: + type = 'Linux' + version = m.group(1) +# Comprobar Linux de 64 bits. + if version and os.path.exists(os.path.join(mntdir, "lib64")): + is64bit = ogGlobals.lang.MSG_64BIT +# Para Android, leer fichero de propiedades. + if not version: + type = 'Android' + files = glob.glob (os.path.join (mntdir, 'android*/system/build.prop')) + if files and os.path.isfile (files[0]): + v = [] + with open (files[0], 'r') as f: + for line in f: + if 'product.brand' in line or 'build.version.release' in line: + v.append (line.split('=')[1].strip()) + version = ' '.join (v) + if os.path.exists(os.path.join(mntdir, "lib64")): + is64bit = ogGlobals.lang.MSG_64BIT +# Para GNU/Hurd, comprobar fichero de inicio (basado en os-prober). + if not version: + type = 'Hurd' + if os.path.exists(os.path.join(mntdir, "hurd/init")): + version = 'GNU/Hurd' +# Para Windows: leer la version del registro. + if not version: + type = 'Windows' + build = 0 + file = RegistryLib.ogGetHivePath (mntdir, 'SOFTWARE') + if file: + # Nuevo método más rápido para acceder al registro de Windows.. + i = '\n'.join ([ + f'load {file}', + r'cd \Microsoft\Windows NT\CurrentVersion', + 'lsval ProductName', + 'lsval DisplayVersion', + ]) + version = subprocess.run (['hivexsh'], input=i, capture_output=True, text=True).stdout + version = version.replace ('\n', ' ') +# Recoge el valor del número de compilación para ver si es Windows 10/11 + i = '\n'.join ([ + f'load {file}', + r'cd \Microsoft\Windows NT\CurrentVersion', + 'lsval CurrentBuildNumber', + ]) + build = subprocess.run (['hivexsh'], input=i, capture_output=True, text=True).stdout + + if subprocess.run (['reglookup', '-H', '-p', 'Microsoft/Windows/CurrentVersion/ProgramW6432Dir', file], capture_output=True, text=True).stdout: + is64bit = ogGlobals.lang.MSG_64BIT + + if not version: + # Compatibilidad con métrodo antiguo y más lento de acceder al registro. + version = RegistryLib.ogGetRegistryValue (mntdir, 'software', r'\Microsoft\Windows NT\CurrentVersion\ProductName') + if RegistryLib.ogGetRegistryValue (mntdir, 'software', r'\Microsoft\Windows\CurrentVersion\ProgramW6432Dir'): + is64bit = ogGlobals.lang.MSG_64BIT + # Si la compilación es mayor o igual a 22000 es Windows 11 + if int (build) >= 22000: + version = version.replace ('10', '11') +# Para cargador Windows: buscar versión en fichero BCD (basado en os-prober). + if not version: + type = 'WinLoader' + file = FileLib.ogGetPath (file=f'{mntdir}/boot/bcd') + if not file: + file = FileLib.ogGetPath (file=f'{mntdir}/EFI/Microsoft/boot/bcd') + if file: + for distrib in 'Windows Recovery', 'Windows Boot': + with open (file, 'rb') as fd: + contents = fd.read() + distrib_utf16_regex = re.sub (r'(.)', '\\1.', distrib) + distrib_utf16_regex = bytes (distrib_utf16_regex, 'ascii') + if re.search (distrib_utf16_regex, contents): + version = f'{distrib} loader' +# Para macOS: detectar kernel y completar con fichero plist de información del sistema. + if not version: + type = 'MacOS' + # Kernel de Mac OS (no debe ser fichero de texto). + file = f'{mntdir}/mach_kernel' + out = subprocess.run (['file', '--brief', file], capture_output=True, text=True).stdout + if not 'text' in out: + # Obtener tipo de kernel. + if 'Mach-O' in out: version = 'macOS' + if 'Mach-O 64-bit' in out: is64bit = ogGlobals.lang.MSG_64BIT + # Datos de configuración de versión de Mac OS. + file = f'{mntdir}/System/Library/CoreServices/SystemVersion.plist' + if os.path.exists (file): + with open (file, 'rb') as fd: + contents = fd.read() + plist_dict = plistlib.loads (contents) + n = _find_key_recursive (plist_dict, 'ProductName') + v = _find_key_recursive (plist_dict, 'ProductVersion') + version = f'{n} {v}'.strip() + # Datos de recuperación de macOS. + if version and os.path.exists (f'{mntdir}/com.apple.recovery.boot'): + version += ' recovery' + +# Para FreeBSD: obtener datos del Kernel. +### TODO Revisar solución. + if not version: + type = 'BSD' + file = f'{mntdir}/boot/kernel/kernel' + if os.path.exists (file): + lines = subprocess.run (['strings', file], capture_output=True, text=True).stdout.splitlines() + release_search = list (filter (lambda x: re.search ('@.*RELEASE', x), lines)) + if release_search: + first, second, *rest = release_search[0].split() + first = first.replace ('@(#)', '') + version = f'{first} {second}' + out = subprocess.run (['file', '--brief', file], capture_output=True, text=True).stdout + if 'x86-64' in out: is64bit = ogGlobals.lang.MSG_64BIT +# Para Solaris: leer el fichero de versión. +### TODO Revisar solución. + if not version: + type = 'Solaris' + file = f'{mntdir}/etc/release' + if os.path.exists (file): + with open (file, 'r') as fd: + version = fd.readline().strip +# Para cargador GRUB, comprobar fichero de configuración. + if not version: + type = 'GrubLoader' + for file in f'{mntdir}/grub/menu.lst', f'{mntdir}/boot/grub/menu.lst': + if os.path.exists (file): + VERSION = 'GRUB Loader' + for entry in f'{mntdir}/grub/grub.cfg', f'{mntdir}/grub2/grub.cfg', f'{mntdir}/EFI/*/grub.cfg', f'{mntdir}/boot/grub/grub.cfg', f'{mntdir}/boot/grub2/grub.cfg', f'{mntdir}/boot/EFI/*/grub.cfg': + for file in glob.glob (entry): + if os.path.exists (file): + version = 'GRUB2 Loader' + + + + + +# Mostrar resultado y salir sin errores. + if version: return f"{type}:{version} {is64bit}" + return None diff --git a/client/lib/python3/NetLib.py b/client/lib/python3/NetLib.py new file mode 100644 index 0000000..e36f863 --- /dev/null +++ b/client/lib/python3/NetLib.py @@ -0,0 +1,315 @@ +#/** +#@file NetLib.py +#@brief Librería o clase Net +#@class Net +#@brief Funciones básicas de red. +#@warning License: GNU GPLv3+ +#*/ + +import subprocess +import sys +import os +import json +import re + +import ogGlobals +import SystemLib +import FileLib + +def _ogConnect_options(): + ## the original bash code does: eval $(grep "OPTIONS=" /scripts/ogfunctions) + ## this is highly insecure but we need to keep it for now + opt = subprocess.run (['grep', '-o', 'OPTIONS=.*', '/scripts/ogfunctions'], capture_output=True, text=True).stdout.strip() + exec (opt, globals()) + return OPTIONS + +## defined in /scripts/ogfunctions. We can't tackle that yet. +## this is a quick implementation just to unblock development +def _ogConnect (server, protocol, src, dst, options, readonly): + if 'smb' != protocol: return None ## only supported value right now + write_option = ',ro' if readonly else ',rw' + options += write_option + return not subprocess.run (['mount.cifs', f'//{server}/{src}', dst] + options.split()).returncode + + +#/** +# ogChangeRepo IPREPO [ OgUnit ] +#@brief Cambia el repositorio para el recurso remoto images. +#@param 1 Ip Repositorio +#@param 2 Abreviatura Unidad Organizativa +#@return Cambio recurso remoto en OGIMG. +#*/ +def ogChangeRepo(ip_repo, og_unit=None): + ogprotocol = os.environ['ogprotocol'] or 'smb' + + if og_unit: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_GENERIC, 'the og_unit parameter became unsupported') + return None + + try: + mount = subprocess.run (['mount'], capture_output=True, text=True).stdout + ro = bool (list (filter (lambda line: re.search (r'ogimages.*\bro,', line), mount.splitlines()))) + + current_repo = ogGetRepoIp() + new_repo = current_repo if ip_repo.upper() == "REPO" else ip_repo + if new_repo == current_repo: return True + + subprocess.run(["umount", ogGlobals.OGIMG], check=True) + + SystemLib.ogEcho (['session', 'log'], None, f'{ogGlobals.lang.MSG_HELP_ogChangeRepo} {new_repo}') + options = _ogConnect_options() + if not _ogConnect (new_repo, ogprotocol, 'ogimages', ogGlobals.OGIMG, options, ro): + _ogConnect (current_repo, ogprotocol, 'ogimages', ogGlobals.OGIMG, options, ro) + SystemLib.ogRaiseError( + "session", + ogGlobals.OG_ERR_REPO, + f"Error connecting to the new repository: {new_repo}", + ) + return False + + SystemLib.ogEcho( + ["session", "log"], + None, + f"Repository successfully changed to {new_repo}".strip(), + ) + + return True + + except Exception as e: + SystemLib.ogRaiseError( + "session", + ogGlobals.OG_ERR_GENERIC, + f"Error executing ogChangeRepo: {e}", + ) + return None + +#/** +# ogGetGroupDir [ str_repo ] +#@brief Devuelve el camino del directorio para el grupo del cliente. +#@param str_repo repositorio de imágenes (opcional) +#@return path_dir - Camino al directorio del grupo. +#@note repo = { REPO, CACHE } REPO por defecto +#@exception OG_ERR_FORMAT formato incorrecto. +#*/ +def ogGetGroupDir(repo=None): + try: + repo = repo or "REPO" + + group = ogGetGroupName() + if group: + dir_path = FileLib.ogGetPath(repo, f"/groups/{group}") + if dir_path and os.path.isdir(dir_path): + return dir_path + + return None + + except Exception as e: + SystemLib.ogRaiseError( + "session", + ogGlobals.OG_ERR_FORMAT, + f"Error in ogGetGroupDir: {e}" + ) + return None + +#/** +# ogGetGroupName +#@brief Devuelve el nombre del grupo al que pertenece el cliente. +#@return str_group - Nombre de grupo. +#*/ +def ogGetGroupName(): + try: + group = globals().get("group", None) + return group if group else None + except Exception as e: + print(f"Error in ogGetGroupName: {e}") + return None + +#/** +# ogGetHostname +#@brief Muestra el nombre del cliente. +#@return str_host - nombre de máquina +#*/ ## +def ogGetHostname(): +# Tomar nombre de la variable HOSTNAME + host = os.getenv("HOSTNAME", "").strip() + if host: return host + +# Si no, tomar del DHCP, opción host-name /* (comentario para Doxygen) + dhcp_file = "/var/lib/dhcp3/dhclient.leases" + if os.path.exists(dhcp_file): + with open(dhcp_file, "r") as f: + for line in f: + if 'option host-name' in line: + return line.split('"')[1].strip(";") + +# Si no, leer el parámetro del kernel hostname (comentario para Doxygen) */ + cmdline_file = "/proc/cmdline" + if os.path.exists(cmdline_file): + with open(cmdline_file, "r") as f: + for entry in f.read().split(): + if entry.startswith("hostname="): + return entry.split("=")[1].strip() + + +#/** +# ogGetIpAddress +#@brief Muestra la dirección IP del sistema +#@return str_ip - Dirección IP +#@note Usa las variables utilizadas por el initrd "/etc/net-ethX.conf +#*/ ## +def ogGetIpAddress(): + if "IPV4ADDR" in os.environ: + ip = os.environ["IPV4ADDR"] + if '/' in ip: ip = ip.split ('/')[0] + return ip + + extra_args = [] + if "DEVICE" in os.environ: + extra_args = [ "dev", os.environ["DEVICE"] ] + ipas = subprocess.run (['ip', '-json', 'address', 'show', 'up'] + extra_args, capture_output=True, text=True).stdout + + ipasj = json.loads (ipas) + addresses = [] + for e in ipasj: + if 'lo' == e['ifname']: continue + if 'addr_info' not in e: continue + addrs = e['addr_info'] + for a in addrs: + if 'inet' != a['family']: continue + addresses.append ({ 'local': a['local'], 'prefixlen': a['prefixlen'] }) + + if 1 != len (addresses): + raise Exception ('more than one local IP address found') + return addresses[0] + + +#/** +# ogGetMacAddress +#@brief Muestra la dirección Ethernet del cliente. +#@return str_ether - Dirección Ethernet +#*/ ## +def ogGetMacAddress(): + try: + if "DEVICE" in os.environ: + device = os.environ["DEVICE"] + result = subprocess.run( + ["ip", "-o", "link", "show", "up", "dev", device], + capture_output=True, + text=True, + check=True + ).stdout + else: + result = subprocess.run( + ["ip", "-o", "link", "show", "up"], + capture_output=True, + text=True, + check=True + ).stdout + + mac_addresses = [] + for line in result.splitlines(): + if "link/ether" in line: + parts = line.split() + for i, part in enumerate(parts): + if part == "link/ether": + mac_addresses.append(parts[i + 1].upper()) + + if mac_addresses: + print (f'nati: ogGetMacAddress: {mac_addresses[0]}') + return mac_addresses[0] + else: + print("No active mac address found") + return None + + except subprocess.CalledProcessError as e: + print(f"Error executing ip command: {e.stderr}") + return None + except Exception as e: + print(f"Unexpected error: {str(e)}") + return None + +#/** +# ogGetNetInterface +#@brief Muestra la interfaz de red del sistema +#@return str_interface - interfaz de red +#@note Usa las variables utilizadas por el initrd "/etc/net-ethX.conf +#*/ ## +def ogGetNetInterface(): + if len(sys.argv) >= 2 and sys.argv[1] == "help": + SystemLib.ogHelp("ogGetNetInterface", "ogGetNetInterface", "ogGetNetInterface => eth0") + return + + if "DEVICE" in os.environ: + print(os.environ["DEVICE"]) + + return 0 + +#/** +# ogGetRepoIp +#@brief Muestra la dirección IP del repositorio de datos. +#@return str_ip - Dirección IP +#*/ ## +def ogGetRepoIp(): + out = subprocess.run (["findmnt", "--json", "--output", "SOURCE,FSTYPE", ogGlobals.OGIMG], capture_output=True, text=True).stdout + try: + j = json.loads (out) + except json.decoder.JSONDecodeError: + return None + + if 'filesystems' not in j: return None + source = j['filesystems'][0]['source'] + fstype = j['filesystems'][0]['fstype'] + + if 'nfs' == fstype: return source.split(":")[0] + elif 'cifs' == fstype: return source.split("/")[2] + + return None + +#/** +# ogGetServerIp +#@brief Muestra la dirección IP del Servidor de OpenGnSys. +#@return str_ip - Dirección IP +#@note Comprobacion segun protocolo de conexion al Repo +#*/ ## +def ogGetServerIp(): + return os.environ['ogcore'] + +#/** +# ogGetServerPort +#@brief Muestra el puerto del Servidor de OpenGnSys. +#@return str_port - Puerto +#*/ ## +def ogGetServerPort(): + if 'ogcore_port' in os.environ: + return os.environ['ogcore_port'] + else: + return '8443' + + +#/** +# ogMakeGroupDir [ str_repo ] +#@brief Crea el directorio para el grupo del cliente. +#@param str_repo repositorio de imágenes (opcional) +#@return (nada) +#@note repo = { REPO, CACHE } REPO por defecto +#@exception OG_ERR_FORMAT formato incorrecto. +#*/ +def ogMakeGroupDir(): + REPO = "" + DIR = "" + GROUP = "" + + if len(sys.argv) < 2: + SystemLib.ogHelp("ogMakeGroupDir", "ogMakeGroupDir str_repo", "ogMakeGroupDir", "ogMakeGroupDir REPO") + return + + if len(sys.argv) == 1: + REPO = "REPO" + else: + REPO = sys.argv[1] + + DIR = FileLib.ogGetPath(REPO, "/groups/" + ogGetGroupName(), stderr=subprocess.DEVNULL) + if DIR: + subprocess.run(["mkdir", "-p", DIR], stderr=subprocess.DEVNULL) + + return 0 diff --git a/client/lib/python3/PostConfLib.py b/client/lib/python3/PostConfLib.py new file mode 100644 index 0000000..e97b266 --- /dev/null +++ b/client/lib/python3/PostConfLib.py @@ -0,0 +1,320 @@ +#/** +#@file PostConfLib.py +#@brief Librería o clase PostConf +#@class PostConf +#@brief Funciones para la postconfiguración de sistemas operativos. +#@warning License: GNU GPLv3+ +#*/ + +import os +import glob +import subprocess + +import ogGlobals +import SystemLib +import FileSystemLib +import FileLib +import NetLib +import RegistryLib +import InventoryLib +import BootLib + +#/** +# ogCleanOs int_ndisk int_nfilesys +#@brief Elimina los archivos que no son necesarios en el sistema operativo. +#@param int_ndisk nº de orden del disco +#@param int_nfilesys nº de orden del sistema de archivos +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Disco o particion no corresponden con un dispositivo. +#@exception OG_ERR_PARTITION Partición desconocida o no accesible. +#@note Antes incluido en la funcion ogReduceFs +#@return (nada) +#*/ ## +#ogCleanOs (1, 1) +def ogCleanOs (disk, par): + t = InventoryLib.ogGetOsType (disk, par) + if 'Linux' == t: + BootLib.ogCleanLinuxDevices (disk, par) + mntdir = FileSystemLib.ogMount (disk, par) + for t in glob.glob ('{mntdir}/tmp/*'): + shutil.rmtree (t) + elif 'Windows' == t: + for f in 'pagefile.sys', 'hiberfil.sys', 'swapfile.sys': + p = FileLib.ogGetPath (src=f'{disk} {par}', file=f) + if p: FileLib.ogDeleteFile (disk=disk, par=par, file=f) + + + +#/** +# ogInstallMiniSetup int_ndisk int_npartition str_filename [str_admuser str_admpassword bool_autologin [str_autouser str_autopassword] ] +#@brief Metafunción para instalar archivo que se ejecutará en el arranque de Windows. +#@see ogInstallFirstBoot ogInstallRunonce +#*/ ## +def ogInstallMiniSetup (disk, par, cmdfile, user=None, pwd=None, autologin=False, userauto=None, pwdauto=None): + if user: + ogInstallRunonce (disk, par, cmdfile, user, pwd, autologin, userauto, pwdauto) + else: + ogInstallFirstBoot (disk, par, cmdfile) + + +#/** +# ogInstallFirstBoot int_ndisk int_npartition str_filename +#@brief Crea unas claves del registro y el archivo cmd que se ejecutara en el primer arranque estando la maquina en un estado bloqueado +#@param int_ndisk nº de orden del disco +#@param int_npartition nº de orden de la partición +#@param str_filename nombre del archivo .cmd a ejecutar en el arranque +#@return (nada) +#@exception OG_ERR_FORMAT Formato incorrecto. +#@note El archivo estará en system32 y será visible por el sistema. +#*/ ## +#ogInstallFirstBoot ('1', '1', 'filename.cmd') +def ogInstallFirstBoot (disk, par, cmdfile): + mntdir = FileSystemLib.ogMount (disk, par) + if not mntdir: return + for i in ['winnt', 'windows']: + dir = FileLib.ogGetPath (file=f'{mntdir}/{i}/system32') + if dir: cmddir = dir + if not cmddir: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f'{mntdir}/windows/system32') + return + full_cmdfile = f'{cmddir}/{cmdfile}' + bn = os.path.basename (full_cmdfile) + + # Creamos el archivo cmd y creamos un par de comandos para que una vez acabe la + # postconfiguracion resetee el mini setup, sino lo haria en cada arranque. + with open (full_cmdfile, 'w') as fd: + fd.write (r'REG ADD HKLM\System\Setup /v SystemSetupInProgress /t REG_DWORD /d 0 /f' + '\n') + fd.write (r'REG ADD HKLM\System\Setup /v CmdLine /t REG_SZ /d "" /f' + '\n') + + # Crear los valores de registro necesarios para que se haga la ejecución del .cmd al aranque. + RegistryLib.ogSetRegistryValue (mntdir, 'SYSTEM', r'\Setup\SystemSetupInProgress', 1) + RegistryLib.ogSetRegistryValue (mntdir, 'SYSTEM', r'\Setup\SetupType', 4) + #RegistryLib.ogDeleteRegistryValue (mntdir, 'SYSTEM', r'\Setup\CmdLine') + RegistryLib.ogAddRegistryValue (mntdir, 'SYSTEM', r'\Setup\CmdLine') + RegistryLib.ogSetRegistryValue (mntdir, 'SYSTEM', r'\Setup\CmdLine', f'cmd.exe /c {bn}') + + +#/** +# ogInstallRunonce int_ndisk int_npartition str_filename str_adm_user str_adm_password bool_autologin [str_auto_user str_auto_password] +#@brief Crea el archivo cmd que se ejecutara en el runonce de un usuario administrador +#@param int_ndisk nº de orden del disco +#@param int_npartition nº de orden de la partición +#@param str_filename nombre del archivo .cmd a ejecutar en el arranque (estara en system32 y sera visible por el sistema) +#@param str_adm_user usuario administrador para hacer autologin y ejecutar el runonce +#@param str_adm_password password del usuario administrador +#@param bool_autologin si despues de la postconfiguracion queremos que la maquina haga autologin (0 o 1) +#@param str_auto_user Usuario con el que queremos que haga autologin despues de la postconfiguracion +#@param str_auto_password Password del usuario que hara autologin +#@return (nada) +#@exception OG_ERR_FORMAT Formato incorrecto. +#*/ ## +#ogInstallRunonce ('1', '1', 'filename.cmd', 'administrator', 'passadmin', '1', 'userauto', 'pwdauto') +#ogInstallRunonce ('1', '1', 'filename.cmd', 'administrator', 'passadmin', '0') +def ogInstallRunonce (disk, par, cmdfile, user, pwd, autologin, userauto=None, pwdauto=None): + mountpoint = FileLib.ogGetPath (src=f'{disk} {par}', file='/') + for i in ['winnt', 'windows']: + dir = FileLib.ogGetPath (file=f'{mountpoint}/{i}/system32') + if dir: cmddir = dir + if not cmddir: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f'{mountpoint}/Windows/System32') + return + full_cmdfile = f'{cmddir}/{cmdfile}' + if not autologin: + # Si no queremos hacer autologin despues de la postconfiguracion lo indicamos en las claves de registro + with open (full_cmdfile, 'w') as fd: + fd.write (r'DEL C:\ogboot.*' + '\n') + fd.write (r'REG ADD "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" /v AutoAdminLogon /t REG_SZ /d 0 /f' + '\n') + fd.write (r'REG ADD "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" /v DefaultUserName /t REG_SZ /d "" /f' + '\n') + fd.write (r'REG DELETE "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" /v DefaultPassword /f' + '\n') + else: + # Si queremos hacer autologin despues de la postconfiguracion introducimos la informacion en las claves de registro + with open (full_cmdfile, 'w') as fd: + fd.write (r'DEL C:\ogboot.*' + '\n') + fd.write (r'REG ADD "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" /v AutoAdminLogon /t REG_SZ /d 1 /f' + '\n') + fd.write (fr'REG ADD "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" /v DefaultUserName /t REG_SZ /d "{userauto}" /f' + '\n') + fd.write (fr'REG ADD "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" /v DefaultPassword /t REG_SZ /d "{pwdauto}" /f' + '\n') + #Creamos las claves de registro necesarias para que meter el cmd en el runonce del usuario y este haga autologin + RegistryLib.ogAddRegistryValue (mountpoint, 'software', r'\Microsoft\Windows\CurrentVersion\RunOnce\PostConfiguracion') + RegistryLib.ogSetRegistryValue (mountpoint, 'software', r'\Microsoft\Windows\CurrentVersion\RunOnce\PostConfiguracion', rf'C:\windows\system32\{cmdfile}') + RegistryLib.ogAddRegistryValue (mountpoint, 'software', r'\Microsoft\Windows NT\CurrentVersion\Winlogon\AutoAdminLogon') + RegistryLib.ogSetRegistryValue (mountpoint, 'software', r'\Microsoft\Windows NT\CurrentVersion\Winlogon\AutoAdminLogon', 1) + RegistryLib.ogAddRegistryValue (mountpoint, 'software', r'\Microsoft\Windows NT\CurrentVersion\Winlogon\DefaultUserName') + RegistryLib.ogSetRegistryValue (mountpoint, 'software', r'\Microsoft\Windows NT\CurrentVersion\Winlogon\DefaultUserName', user) + RegistryLib.ogAddRegistryValue (mountpoint, 'software', r'\Microsoft\Windows NT\CurrentVersion\Winlogon\DefaultDomainName') + RegistryLib.ogSetRegistryValue (mountpoint, 'software', r'\Microsoft\Windows NT\CurrentVersion\Winlogon\DefaultDomainName', '.\\') + RegistryLib.ogAddRegistryValue (mountpoint, 'software', r'\Microsoft\Windows NT\CurrentVersion\Winlogon\DefaultPassword') + RegistryLib.ogSetRegistryValue (mountpoint, 'software', r'\Microsoft\Windows NT\CurrentVersion\Winlogon\DefaultPassword', pwd) + RegistryLib.ogDeleteRegistryValue (mountpoint, 'software', r'\Microsoft\Windows NT\CurrentVersion\Winlogon\ForceAutoLockOnLogon') + RegistryLib.ogDeleteRegistryValue (mountpoint, 'software', r'\Microsoft\Windows NT\CurrentVersion\Winlogon\AutoLogonCount') + +#/** +# ogAddCmd int_ndisk int_npartition str_filename str_commands +#@brief Añade comandos al cmd creado por ogInstalMiniSetup +#@param int_ndisk nº de orden del disco +#@param int_npartition nº de orden de la partición +#@param str_filename nombre del fichero cmd (siempre se guardara en windows\system32\para que sea visible por el sistema +#@param str_commands comando o comandos que se añadiran al fichero +#@return +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_PARTITION Tipo de partición desconocido o no se puede montar. +#*/ ## +#ogAddCmd ('1', '1', 'filename.cmd', 'command') +def ogAddCmd (disk, par, cmdfile, cmd): + mountpoint = FileSystemLib.ogMount (disk, par) + if not mountpoint: return + + full_cmdfile = FileLib.ogGetPath (file=f'{mountpoint}/windows/system32') + '/' + cmdfile + if not full_cmdfile: + ogInstallMiniSetup (disk, par, cmdfile) + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f'{mountpoint}/windows/system32/{cmdfile}') + return + + # Concatenamos el comando en el fichero de comandos + with open (full_cmdfile, 'a') as fd: + fd.write (cmd + '\n') + + +#/** +# ogDomainScript int_ndisk int_npartition str_domain str_user str_password +#@brief Crea un script .vbs para unir al dominio una maquina windows y el comando adequado en el archivo cmd creado por ogInstallMiniSetup +#@param int_ndisk nº de orden del disco +#@param int_npartition nº de orden de la partición +#@param str_filename nombre del fichero cmd donde deberemos introducir el comando de ejecutar el script vbs +#@param str_domain dominio donde se quiere conectar +#@param str_user usuario con privilegios para unir al dominio +#@param str_password password del usuario con privilegios +#@return +#@exception OG_ERR_FORMAT Formato incorrecto. +#*/ ## + + +### PRUEBAS. + +#/** +# ogConfigureOgagent int_ndisk int_filesys +#@brief Modifica el fichero de configuración del nuevo agente OGAent para sistemas operativos. +#@param int_ndisk nº de orden del disco +#@param int_filesys nº de orden del sistema de archivos +#@return (nada) +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Fichero o dispositivo no encontrado. +#@exception OG_ERR_LOCKED Sistema de archivos bloqueado. +#*/ ## +def ogConfigureOgagent (disk, par): + mntdir = FileSystemLib.ogMount (disk, par) + if not mntdir: + return + + for agentdir in ['usr/share/OGAgent', 'Program Files/OGAgent', 'Program Files (x86)/OGAgent', 'Applications/OGAgent.app']: + cfgfile = FileLib.ogGetPath (file=f'{mntdir}/{agentdir}/cfg/ogagent.cfg') + if cfgfile: break + + if not cfgfile: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, 'ogagent.cfg') + return + + ogcore_scheme = 'https' + ogcore_ip = NetLib.ogGetServerIp() + ogcore_port = NetLib.ogGetServerPort() + cfgfile_patched = cfgfile + '.patched' + in_opengnsys_section = False + with open (cfgfile, 'r') as fdin: + with open (cfgfile_patched, 'w') as fdout: + while True: + lineout = linein = fdin.readline() + if not linein: break + if in_opengnsys_section: + if 'remote' == linein[0:6]: + lineout = f'remote={ogcore_scheme}://{ogcore_ip}:{ogcore_port}/opengnsys/rest/\n' + if '[' == linein[0:1]: + in_opengnsys_section = False + if '[opengnsys]' == linein[0:11]: + in_opengnsys_section = True + fdout.write (lineout) + os.rename (cfgfile_patched, cfgfile) + + +#/** +# ogInstallLaunchDaemon int_ndisk int_nfilesys str_filename +#@brief Instala archivo que se ejecutará en el arranque de macOS. +#@param int_ndisk nº de orden del disco +#@param int_nfilesys nº de orden del sistema de archivos +#@param str_filename nombre del script +#return (nada) +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Fichero o directorio no encontrado. +#@npte Crea ficheros de configuración /Library/LaunchDaemon/es.opengnsys.Script.plist. +#*/ ## + + +### PRUEBAS. + +#/** +# ogAddToLaunchDaemon int_ndisk int_nfilesys str_filename str_commands +#@brief Añade comandos al script creado por ogInstalLaunchDaemon. +#@param int_ndisk nº de orden del disco +#@param int_nfilesys nº de orden del sistema de archivos +#@param str_filename nombre del script (siempre se guardará en /usr/share para que sea visible por el sistema +#@param str_commands comando o comandos que se añadiran al fichero +#return (nada) +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Fichero o directorio no encontrado. +#*/ ## + + +#/** +# ogUninstallLinuxClient int_ndisk int_filesys +#@brief Desinstala el cliente OpenGnSys para sistemas operativos GNU/Linux. +#@param int_ndisk nº de orden del disco +#@param int_filesys nº de orden del sistema de archivos +#@return (nada) +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_PARTITION Paritición o sistema de archivos incorrectos. +#@exception OG_ERR_LOCKED Sistema de archivos bloqueado. +#*/ ## +def ogUninstallLinuxClient (disk, par): + mntdir = FileSystemLib.ogMount (disk, par) + if not mntdir: + return + + for f in [ + f'{mntdir}/usr/sbin/ogAdmLnxClient', + f'{mntdir}/sbin/ogAdmLnxClient', + f'{mntdir}/usr/local/sbin/ogAdmLnxClient', + f'{mntdir}/etc/ogAdmLnxClient.cfg', + f'{mntdir}/usr/local/etc/ogAdmLnxClient.cfg', + ]: + try: os.remove (f) + except: pass + + for f in [ + f'{mntdir}/etc/rc.local', + f'{mntdir}/etc/rc.d/rc.local', + f'{mntdir}/usr/local/etc/rc.local', + f'{mntdir}/usr/local/etc/rc.d/rc.local', + ]: + subprocess.run (['sed', '-i', '-e', '/ogAdmLnxClient/ d', f], stderr=subprocess.DEVNULL) + + +#/** +# ogUninstallWindowsClient int_ndisk int_filesys str_filename +#@brief Desinstala el cliente OpenGnSys para sistemas operativos Windows. +#@param int_ndisk nº de orden del disco +#@param int_npartition nº de orden de la partición +#@param str_filename nombre del fichero cmd donde deberemos introducir el comando de ejecutar el script vbs +#@return (nada) +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_PARTITION Paritición o sistema de archivos incorrectos. +#@exception OG_ERR_LOCKED Sistema de archivos bloqueado. +#*/ ## +#ogUninstallWindowsClient ('1', '1', 'filename.cmd') +def ogUninstallWindowsClient (disk, par, cmdfile): + mntdir = FileSystemLib.ogMount (disk, par) + if not mntdir: return + + exe1 = FileLib.ogGetPath (file=f'{mntdir}/windows/ogAdmWinClient.exe') + exe2 = FileLib.ogGetPath (file=f'{mntdir}/winnt/ogAdmWinClient.exe') + if exe1 or exe2: + ogAddCmd (disk, par, cmdfile, 'ogAdmWinClient -remove') + ogAddCmd (disk, par, cmdfile, r'DEL C:\Windows\ogAdmWinClient.exe') + ogAddCmd (disk, par, cmdfile, r'DEL C:\Winnt\ogAdmWinClient.exe') diff --git a/client/lib/python3/ProtocolLib.py b/client/lib/python3/ProtocolLib.py new file mode 100644 index 0000000..18c8bb8 --- /dev/null +++ b/client/lib/python3/ProtocolLib.py @@ -0,0 +1,973 @@ +#!/usr/bin/python3 + +import subprocess +import re +import json +import os.path +import shutil + +import ogGlobals +import SystemLib +import ImageLib +import FileSystemLib +import StringLib +import NetLib +import DiskLib +import FileLib +import CacheLib + +#/** +#@file ProtocolLib.py +#@brief Librería o clase Protocol +#@class Protocol +#@brief Funciones para transmisión de datos +#@warning License: GNU GPLv3+ +#*/ + + +##################### FUNCIONES UNICAST ################ + +#/** +# ogUcastSyntax +#@brief Función para generar la instrucción de transferencia de datos unicast +#@param 1 Tipo de operación [ SENDPARTITION RECEIVERPARTITION SENDFILE RECEIVERFILE ] +#@param 2 Sesion Unicast +#@param 3 Dispositivo (opción PARTITION) o fichero(opción FILE) que será enviado. +#@param 4 Tools de clonación (opcion PARTITION) +#@param 5 Tools de compresion (opcion PARTITION) +#@return instrucción para ser ejecutada. +#@exception OG_ERR_FORMAT formato incorrecto. +#@exception OG_ERR_UCASTSYNTAXT formato de la sesion unicast incorrecta. +#@note Requisitos: mbuffer +#@todo: controlar que mbuffer esta disponible para los clientes. +#*/ ## + +#ogUcastSyntax SENDPARTITION 8000:172.17.36.11:172.17.36.12 device tool level +#ogUcastSyntax RECEIVERPARTITION 8000:172.17.36.249 device tool level + +#ogUcastSyntax SENDFILE 8000:172.17.36.11:172.17.36.12 file +#ogUcastSyntax RECEIVERFILE 8000:172.17.36.249 file + +def ogUcastSyntax (op, sess, file=None, device=None, tool=None, level=None): + if 'SENDPARTITION' == op or 'RECEIVERPARTITION' == op: + if device is None: + raise TypeError ('missing required argument: "device"') + if tool is None: + raise TypeError ('missing required argument: "tool"') + if tool.lower() not in ['partclone', 'partimage', 'ntfsclone']: + raise TypeError (f'argument "tool" has unsupported value "{tool}"') + if level is None: + raise TypeError ('missing required argument: "level"') + if level.lower() not in ['lzop', 'gzip', '0', '1']: + raise TypeError (f'argument "level" has unsupported value "{level}"') + elif 'SENDFILE' == op or 'RECEIVERFILE' == op: + if file is None: + raise TypeError ('missing required argument: "file"') + else: + raise TypeError ('first parameter should match (SEND|RECEIVER)(PARTITION|FILE), eg. "SENDFILE"') + + if 'SEND' in op: mode = 'server' + else: mode = 'client' + + session = sess.split (':') + + portbase = int (session[0]) + if portbase not in range (8000, 8006): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, f'McastSession portbase {portbase}') ## || PERROR=3 + return + + if 'server' == mode: + address = '' + for i in range (1, len (session)): + address += f' -O {session[i]}:{portbase}' + else: + address = f'{session[1]}:{portbase}' + #print (f'nati mode ({mode}) address ({address})') + + if 'SENDPARTITION' == op: + syn = ImageLib.ogCreateImageSyntax (device, ' ', tool, level) + ## REQUIRES package mbuffer to be installed!! + ## otherwise, param2 in ImageLib.ogCreateImageSyntax() is not '| mbuffer' but empty + ## and then parts[2] is out of range + parts = syn.split ('|') + #print (f'nati syn ({syn}) parts ({parts})') + prog1 = f'{parts[0]}|{parts[2]}'.strip() + prog1 = prog1.replace ('>', '').strip() + return f'{prog1} | mbuffer {address}' + elif 'RECEIVERPARTITION' == op: + syn = ImageLib.ogRestoreImageSyntax (' ', device, tool, level) + parts = syn.split ('|') + compressor = parts[0].strip() + tools = parts[-1].strip() + return f'mbuffer -I {address} | {compressor} | {tools}' + elif 'SENDFILE' == op: + return f'mbuffer {address} -i {file}' + elif 'RECEIVERFILE' == op: + return f'mbuffer -I {address} -i {file}' + else: + pass ## shouldn't happen + + +#/** +# ogUcastSendPartition +#@brief Función para enviar el contenido de una partición a multiples particiones remotas usando UNICAST. +#@param 1 disk +#@param 2 partition +#@param 3 sesionUcast +#@param 4 tool image +#@param 5 tool compresor +#@return +#@exception $OG_ERR_FORMAT +#@exception $OG_ERR_UCASTSENDPARTITION +#@note +#@todo: ogIsLocked siempre devuelve 1 +#*/ ## +#ogUcastSendPartition 1 2 8001:192.168.1.113 partclone lzop +def ogUcastSendPartition (disk, par, sess, tool, level): + PART = DiskLib.ogDiskToDev (disk, par) + if not PART: return None + + FileSystemLib.ogUnmount (disk, par) + + cmd = ogUcastSyntax ('SENDPARTITION', sess, device=PART, tool=tool, level=level) + if not cmd: return None + print (f'nati cmd ({cmd})') + try: + subprocess.run (cmd, shell=True, check=True) + except subprocess.CalledProcessError: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_UCASTSENDPARTITION, ' ') + return None + + + +#/** +# ogUcastReceiverPartition +#@brief Función para recibir directamente en la partición el contenido de un fichero imagen remoto enviado por UNICAST. +#@param 1 disk +#@param 2 partition +#@param 3 session unicast +#@param 4 tool image +#@param 5 tool compresor +#@return +#@exception OG_ERR_FORMAT +#@exception OG_ERR_UCASTRECEIVERPARTITION +#@note +#@todo: +#*/ ## +#ogUcastReceiverPartition 1 1 8001:192.168.1.111 partclone lzop +def ogUcastReceiverPartition (disk, par, sess, tool, level): + PART = DiskLib.ogDiskToDev (disk, par) + if not PART: return None + + FileSystemLib.ogUnmount (disk, par) + + cmd = ogUcastSyntax ('RECEIVERPARTITION', sess, device=PART, tool=tool, level=level) + if not cmd: return None + print (f'nati cmd ({cmd})') + try: + subprocess.run (cmd, shell=True, check=True) + except subprocess.CalledProcessError: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_UCASTRECEIVERPARTITION, ' ') + return None + + + +#/** +# ogUcastSendFile [ str_repo | int_ndisk int_npart ] /Relative_path_file sessionMulticast +#@brief Envía un fichero por unicast ORIGEN(fichero) DESTINO(sessionmulticast) +#@param (2 parámetros) $1 path_aboluto_fichero $2 sesionMcast +#@param (3 parámetros) $1 Contenedor REPO|CACHE $2 path_absoluto_fichero $3 sesionMulticast +#@param (4 parámetros) $1 disk $2 particion $3 path_absoluto_fichero $4 sesionMulticast +#@return +#@exception OG_ERR_FORMAT formato incorrecto. +#@exception $OG_ERR_NOTFOUND +#@exception OG_ERR_UCASTSENDFILE +#@note Requisitos: +#*/ ## +# + +## TODO esta función es idéntica a ogMcastSendFile pero con s/Ucast/Mcast/; + +#ogUcastSendFile ([str_REPOSITORY] [int_ndisk int_npart] /Relative_path_file sesionMcast(puerto:ip:ip:ip)" \ +#ogUcastSendFile (disk=1, par=1, file='/aula1/winxp.img', sess='8000:172.17.36.11:172.17.36.12') +#ogUcastSendFile (container='REPO', file='/aula1/ubuntu.iso', sess='sesionUcast') +#ogUcastSendFile (container='CACHE', file='/aula1/winxp.img', sess='sesionUcast') +#ogUcastSendFile ( file='/opt/opengnsys/images/aula1/hd500.vmx', sess='sesionUcast') + +#ogUcastSendFile 1 2 /boot/vmlinuz-6.8.0-51-generic 8000:192.168.1.113 +#ogUcastSendFile 1 4 /swapfile.sys 8000:192.168.1.111 +#ogUcastSendFile REPO /ubu24.img 8000:192.168.1.111 +def ogUcastSendFile (disk=None, par=None, container=None, file=None, sess=None): + if file is None: + raise TypeError ('missing required argument: "file"') + if sess is None: + raise TypeError ('missing required argument: "sess"') + + if container is not None: + if disk is None and par is None: + ## we were given container= + source = FileLib.ogGetPath (src=container, file=file) + dev_err = f'{container} {file}' + print (f'nati ogGetPath (src=({container}), file=({file})) = source ({source})') + else: + raise TypeError ('argument "container" can be specified along neither "disk" nor "par"') + + else: + if disk is not None and par is not None: + ## we were given disk= par= + source = FileLib.ogGetPath (src=f'{disk} {par}', file=file) + dev_err = f'{disk} {par} {file}' + print (f'nati ogGetPath (src=({disk} {par}), file=({file})) = source ({source})') + elif disk is None and par is None: + ## we were given nothing + source = FileLib.ogGetPath (file=file) + dev_err = file + print (f'nati ogGetPath (file=({file})) = source ({source})') + else: + raise TypeError ('if one of "disk" and "par" are specified, then both must be') + + if not source: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f'device or file {dev_err} not found') + return + + path2 = FileLib.ogGetPath (file=source) + print (f'nati path2 ({path2})') + if not path2: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f'device or file {dev_err} not found') + return + + cmd = ogUcastSyntax ('SENDFILE', sess, file=source) + if not cmd: return None + print (f'nati cmd ({cmd})') + try: + subprocess.run (cmd, shell=True, check=True) + except subprocess.CalledProcessError: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_UCASTSENDFILE, ' ') + return None + +def _clientip(): + ipas = subprocess.run (['ip', '-json', 'address', 'show', 'up'], capture_output=True, text=True).stdout + ipasj = json.loads (ipas) + addresses = [] + for e in ipasj: + if 'lo' == e['ifname']: continue + if 'addr_info' not in e: continue + addrs = e['addr_info'] + for a in addrs: + if 'inet' != a['family']: continue + addresses.append ({ 'local': a['local'], 'prefixlen': a['prefixlen'] }) + return addresses + +def _binary_ip (ip): + for l in subprocess.run (['ipcalc', '--nocolor', ip ], capture_output=True, text=True).stdout.splitlines(): + if 'Address' not in l: continue + match = re.search (r'^(Address:)\s+(\S+)\s+(.*$)', l).group(3).replace (' ', '').replace ('.', '') + break + return match + +#/** +# ogMcastSyntax +#@brief Función para generar la instrucción de ejucción la transferencia de datos multicast +#@param 1 Tipo de operación [ SENDPARTITION RECEIVERPARTITION SENDFILE RECEIVERFILE ] +#@param 2 Sesión Mulicast +#@param 3 Dispositivo (opción PARTITION) o fichero(opción FILE) que será enviado. +#@param 4 Tools de clonación (opcion PARTITION) +#@param 5 Tools de compresion (opcion PARTITION) +#@return instrucción para ser ejecutada. +#@exception OG_ERR_FORMAT formato incorrecto. +#@exception OG_ERR_NOTEXEC +#@exception OG_ERR_MCASTSYNTAXT +#@note Requisitos: upd-cast 2009 o superior +#@todo localvar check versionudp +#*/ ## +# + +#ogMcastSyntax SENDPARTITION 9000:full-duplex|half-duplex|broadcast:239.194.17.36:80M:50:60 device tools level +#ogMcastSyntax RECEIVERPARTITION 9000 device tools level +#ogMcastSyntax RECEIVERPARTITION 9000:172.17.88.161:40:120 device tools level + +#ogMcastSyntax SENDFILE 9000:full-duplex|half-duplex|broadcast:239.194.17.36:80M:50:60 file +#ogMcastSyntax RECEIVERFILE 9000 file +#ogMcastSyntax RECEIVERFILE 9000:172.17.88.161:40:120 file +def ogMcastSyntax (op, sess, file=None, device=None, tool=None, level=None): + if 'SENDPARTITION' == op or 'RECEIVERPARTITION' == op: + if device is None: + raise TypeError ('missing required argument: "device"') + if tool is None: + raise TypeError ('missing required argument: "tool"') + if tool.lower() not in ['partclone', 'partimage', 'ntfsclone']: + raise TypeError (f'argument "tool" has unsupported value "{tool}"') + if level is None: + raise TypeError ('missing required argument: "level"') + if level.lower() not in ['lzop', 'gzip', '0', '1']: + raise TypeError (f'argument "level" has unsupported value "{level}"') + elif 'SENDFILE' == op or 'RECEIVERFILE' == op: + if file is None: + raise TypeError ('missing required argument: "file"') + else: + raise TypeError ('first parameter should match (SEND|RECEIVER)(PARTITION|FILE), eg. "SENDFILE"') + + if 'SEND' in op: mode = 'server' + else: mode = 'client' + print (f'nati mode ({mode})') + + try: + isudpcast = subprocess.run (['udp-receiver', '--help'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True).stdout + except subprocess.CalledProcessError: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTEXEC, 'upd-cast no existe') + return + + session = sess.split (':') + print (f'nati sess ({sess}) session ({session})') + + PERROR = 0 + if 'server' == mode: + if 6 != len (session): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, 'parametros session de servidor multicast no completa') + PERROR = 2 + elif 'client' == mode: + if 4 < len (session): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, 'parametros session de cliente multicast no completa') + PERROR = 2 + + mbuffer = " --pipe 'mbuffer -q -m 20M' " + portbase = int (session[0]) + print (f'nati portbase ({portbase})') + if portbase not in range (9000, 9100, 2): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, f'McastSession portbase {portbase}') + PERROR = 3 + + if 'server' == mode: + method, address, bitrate, nclients, maxtime = session[1:] + + if method.lower() not in ['full-duplex', 'half-duplex', 'broadcast']: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, f'McastSession method {method}') + PERROR = 4 + + if not StringLib.ogCheckIpAddress (address): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, f'McastSession address {address}') + PERROR = 5 + + ## the original regex has a backslash: ^[0-9]{1,3}\M$ + ## not sure why + if not re.search (r'^[0-9]{1,3}M$', bitrate): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, f'McastSession bitrate {bitrate}') + PERROR = 6 + + if not re.search (r'^[0-9]{1,10}$', nclients): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, f'McastSession nclients {nclients}') + PERROR = 7 + + if not re.search (r'^[0-9]{1,10}$', maxtime): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, f'McastSession maxtime {maxtime}') + PERROR = 8 + + cerror = '8x8/128' + syntaxserver = f'udp-sender {mbuffer} --nokbd --portbase {portbase} --{method} --mcast-data-address {address} --fec {cerror} --max-bitrate {bitrate} --ttl 16 --min-clients {nclients} --max-wait {maxtime} --autostart {maxtime} --log /tmp/mcast.log' + + if PERROR: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_MCASTSYNTAXT, f' {PERROR}') + return + + + if 'client' == mode: + other = session[1:] + print (f'nati session ({session}) other ({other})') + serveraddress = other[0] if len (other) > 0 else '' + starttimeout = other[1] if len (other) > 1 else '' + receivertimeout = other[2] if len (other) > 2 else '' + + ## serveraddres + if StringLib.ogCheckIpAddress (serveraddress): + serveraddress = f' --mcast-rdv-address {serveraddress}' + else: + repoip = NetLib.ogGetRepoIp() + clientip = _clientip() + #print (f'nati repoip ({repoip}) clientip ({clientip})') + if 1 != len (clientip): + raise Exception ('more than one local IP address found') + c = clientip[0] + #print (f'nati c ({c})') + clientip = c['local'] + mascara = c['prefixlen'] + #print (f'nati clientip ({clientip}) mascara ({mascara})') + + ripbt = _binary_ip (repoip) + ipbt = _binary_ip (clientip) + reposubred = ripbt[0:mascara] + clientsubred = ipbt[0:mascara] + #print (f'nati ripbt ({ripbt})') + #print (f'nati ipbt ({ipbt})') + #print (f'nati reposubred ({reposubred})') + #print (f'nati clientsubred ({clientsubred})') + if reposubred == clientsubred: serveraddress = ' ' + else: serveraddress = f' --mcast-rdv-address {repoip}' + + ## starttimeout + if re.search (r'^[0-9]{1,10}$', starttimeout): + if 0 == starttimeout: starttimeout = ' ' + else: starttimeout = f' --start-timeout {starttimeout}' + else: + starttimeout = f' --start-timeout {ogGlobals.MCASTERRORSESSION}' + if 'start-timeout' not in isudpcast: starttimeout = ' ' + + ## receivertimeout + if re.search (r'^[0-9]{1,10}$', receivertimeout): + if 0 == receivertimeout: receivertimeout = ' ' + else: receivertimeout = f' --receive-timeout {receivertimeout}' + else: + receivertimeout = f' --receive-timeout {ogGlobals.MCASTWAIT}' + if 'receive-timeout' not in isudpcast: receivertimeout = ' ' + + syntaxclient = f'udp-receiver {mbuffer} --portbase {portbase} {serveraddress} {starttimeout} {receivertimeout} --log /tmp/mcast.log' + + if 'SENDPARTITION' == op: + syn = ImageLib.ogCreateImageSyntax (device, ' ', tool, level) + ## REQUIRES package mbuffer to be installed!! + ## otherwise, param2 in ImageLib.ogCreateImageSyntax() is not '| mbuffer' but empty + ## and then parts[2] is out of range + parts = syn.split ('|') + #print (f'nati syn ({syn}) parts ({parts})') + prog1 = f'{parts[0]}|{parts[2]}'.strip() + prog1 = prog1.replace ('>', '').strip() + return f'{prog1} | {syntaxserver}' + + elif 'RECEIVERPARTITION' == op: + syn = ImageLib.ogRestoreImageSyntax (' ', device, tool, level) + parts = syn.split ('|') + compressor = parts[0].strip() + tools = parts[-1].strip() + return f'{syntaxclient} | {compressor} | {tools} ' + + elif 'SENDFILE' == op: + return f'{syntaxserver} --file {file}' + + elif 'RECEIVERFILE' == op: + return f'{syntaxclient} --file {file}' + + else: + raise Exception (f'unknown op ({op})--this should not happen') + + + +#/** +# ogMcastSendFile [ str_repo | int_ndisk int_npart ] /Relative_path_file sessionMulticast +#@brief Envía un fichero por multicast ORIGEN(fichero) DESTINO(sessionmulticast) +#@param (2 parámetros) $1 path_aboluto_fichero $2 sesionMcast +#@param (3 parámetros) $1 Contenedor REPO|CACHE $2 path_absoluto_fichero $3 sesionMulticast +#@param (4 parámetros) $1 disk $2 particion $3 path_absoluto_fichero $4 sesionMulticast +#@return +#@exception OG_ERR_FORMAT formato incorrecto. +#@exception $OG_ERR_NOTFOUND +#@exception OG_ERR_MCASTSENDFILE +#*/ ## +# + +#ogMcastSendFile [str_REPOSITORY] [int_ndisk int_npart] /Relative_path_file sesionMcast" \ +#ogMcastSendFile (disk=1, par=1, file='/aula1/winxp.img', sess='sesionMcast') +#ogMcastSendFile (container='REPO', file='/aula1/ubuntu.iso', sess='sesionMcast') +#ogMcastSendFile (container='CACHE', file='/aula1/winxp.img', sess='sesionMcast') +#ogMcastSendFile ( file='/opt/opengnsys/images/aula1/hd500.vmx', sess='sesionMcast') + +#ogMcastSendFile 1 2 /boot/vmlinuz-6.8.0-51-generic 9000:full-duplex:239.194.37.31:50M:20:10 +#ogMcastSendFile REPO /ubu24.img 9000:full-duplex:239.194.37.31:50M:20:10 +#ogMcastSendFile /usr/lib64/ld-linux-x86-64.so.2 9000:full-duplex:239.194.37.31:50M:20:10 +def ogMcastSendFile (disk=None, par=None, container=None, file=None, sess=None): + if file is None: + raise TypeError ('missing required argument: "file"') + if sess is None: + raise TypeError ('missing required argument: "sess"') + + if container is not None: + if disk is None and par is None: + ## we were given container= + source = FileLib.ogGetPath (src=container, file=file) + dev_err = f'{container} {file}' + print (f'nati ogGetPath (src=({container}), file=({file})) = source ({source})') + else: + raise TypeError ('argument "container" can be specified along neither "disk" nor "par"') + + else: + if disk is not None and par is not None: + ## we were given disk= par= + source = FileLib.ogGetPath (src=f'{disk} {par}', file=file) + dev_err = f'{disk} {par} {file}' + print (f'nati ogGetPath (src=({disk} {par}), file=({file})) = source ({source})') + elif disk is None and par is None: + ## we were given nothing + source = FileLib.ogGetPath (file=file) + dev_err = file + print (f'nati ogGetPath (file=({file})) = source ({source})') + else: + raise TypeError ('if one of "disk" and "par" are specified, then both must be') + + if not source: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f'device or file {dev_err} not found') + return + + path2 = FileLib.ogGetPath (file=source) + print (f'nati path2 ({path2})') + if not path2: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f'device or file {dev_err} not found') + return + + cmd = ogMcastSyntax ('SENDFILE', sess, file=source) + if not cmd: return None + print (f'nati cmd ({cmd})') + try: + subprocess.run (cmd, shell=True, check=True) + except subprocess.CalledProcessError: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_MCASTSENDFILE, ' ') + return None + + + +#/** +# ogMcastReceiverFile sesion Multicast [ str_repo | int_ndisk int_npart ] /Relative_path_file +#@brief Recibe un fichero multicast ORIGEN(sesionmulticast) DESTINO(fichero) +#@param (2 parámetros) $1 sesionMcastCLIENT $2 path_aboluto_fichero_destino +#@param (3 parámetros) $1 sesionMcastCLIENT $2 Contenedor REPO|CACHE $3 path_absoluto_fichero_destino +#@param (4 parámetros) $1 sesionMcastCLIENT $2 disk $3 particion $4 path_absoluto_fichero_destino +#@return +#@exception OG_ERR_FORMAT formato incorrecto. +#@exception $OG_ERR_MCASTRECEIVERFILE +#@note Requisitos: +#*/ ## +# + +#ogMcastReceiverFile ([ str_portMcast] [ [Relative_path_file] | [str_REPOSITORY path_file] | [int_ndisk int_npart path_file ] ]" \ +#ogMcastReceiverFile ( file='/PS1_PH1.img', sess='9000') +#ogMcastReceiverFile (container='CACHE', file='/aula1/PS2_PH4.img', sess='9000') +#ogMcastReceiverFile (disk=1, par=1, file='/isos/linux.iso', sess='9000') + +#ogMcastReceiverFile 9000:full-duplex:239.194.37.31 1 4 /hola +def ogMcastReceiverFile (disk=None, par=None, container=None, file=None, sess=None): + if file is None: + raise TypeError ('missing required argument: "file"') + if sess is None: + raise TypeError ('missing required argument: "sess"') + + if container is not None: + if disk is None and par is None: + ## we were given container= + targetdir = FileLib.ogGetParentPath (src=container, file=file) + dev_err = f'{container} {file}' + print (f'nati ogGetParentPath (src=({container}), file=({file})) = targetdir ({targetdir})') + else: + raise TypeError ('argument "container" can be specified along neither "disk" nor "par"') + + else: + if disk is not None and par is not None: + ## we were given disk= par= + targetdir = FileLib.ogGetParentPath (src=f'{disk} {par}', file=file) + dev_err = f'{disk} {par} {file}' + print (f'nati ogGetParentPath (src=({disk} {par}), file=({file})) = targetdir ({targetdir})') + elif disk is None and par is None: + ## we were given nothing + targetdir = FileLib.ogGetParentPath (file=file) + dev_err = file + print (f'nati ogGetParentPath (file=({file})) = targetdir ({targetdir})') + else: + raise TypeError ('if one of "disk" and "par" are specified, then both must be') + + if not targetdir: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f'target directory {targetdir} not found') + return + + targetfile = os.path.basename (file) + print (f'nati targetfile ({targetfile})') + + print (f'nati calling ogMcastSyntax with sess ({sess})') + cmd = ogMcastSyntax ('RECEIVERFILE', sess, file=os.path.join (targetdir, targetfile)) + if not cmd: return None + print (f'nati cmd ({cmd})') + try: + subprocess.run (cmd, shell=True, check=True) + except subprocess.CalledProcessError: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_MCASTRECEIVERFILE, targetfile) + return None + + +#/** +# ogMcastSendPartition +#@brief Función para enviar el contenido de una partición a multiples particiones remotas. +#@param 1 disk +#@param 2 partition +#@param 3 session multicast +#@param 4 tool clone +#@param 5 tool compressor +#@return +#@exception OG_ERR_FORMAT +#@exception OG_ERR_MCASTSENDPARTITION +#@note +#@todo: ogIsLocked siempre devuelve 1. crear ticket +#*/ ## + +#ogMcastSendPartition (disk, par, SessionMulticastSERVER, tools, compresor) +#ogMcastSendPartition (1, 1, '9000:full-duplex:239.194.37.31:50M:20:2', 'partclone', 'lzop') +#ogMcastSendPartition 1 2 9000:full-duplex:239.194.37.31:50M:20:10 partclone lzop +def ogMcastSendPartition (disk, par, sess, tool, compressor): + PART = DiskLib.ogDiskToDev (disk, par) + if not PART: return + + FileSystemLib.ogUnmount (disk, par) + cmd = ogMcastSyntax ('SENDPARTITION', sess, device=PART, tool=tool, level=compressor) + if not cmd: return None + print (f'nati cmd ({cmd})') + try: + subprocess.run (cmd, shell=True, check=True) + except subprocess.CalledProcessError: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_MCASTSENDPARTITION, ' ') + return None + +#/** +# ogMcastReceiverPartition +#@brief Función para recibir directamente en la partición el contenido de un fichero imagen remoto enviado por multicast. +#@param 1 disk +#@param 2 partition +#@param 3 session multicast +#@param 4 tool clone +#@param 5 tool compressor +#@return +#@exception $OG_ERR_FORMAT +#*/ ## +#ogMcastReceiverPartition 1 1 9000:full-duplex:239.194.37.31 partclone lzop +def ogMcastReceiverPartition (disk, par, sess, tool, compressor): + PART = DiskLib.ogDiskToDev (disk, par) + if not PART: return + + FileSystemLib.ogUnmount (disk, par) + cmd = ogMcastSyntax ('RECEIVERPARTITION', sess, device=PART, tool=tool, level=compressor) + if not cmd: return None + print (f'nati cmd ({cmd})') + try: + subprocess.run (cmd, shell=True, check=True) + except subprocess.CalledProcessError: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_MCASTRECEIVERPARTITION, ' ') ## original code has OG_ERR_MCASTSENDPARTITION + return None + + + +#/** +# ogMcastRequest +#@brief Función temporal para solicitar al ogRepoAux el envio de un fichero por multicast +#@param 1 Fichero a enviar ubicado en el REPO. puede ser ruta absoluta o relatica a /opt/opengnsys/images +#@param 2 PROTOOPT opciones protocolo multicast +#*/ ## +## now ogCore takes this responsibility +def ogMcastRequest (img, proto): + return True + + +########################################## +############## funciones torrent +#/** +# ogTorrentStart [ str_repo | int_ndisk int_npart ] Relative_path_file.torrent | SessionProtocol +#@brief Función iniciar P2P - requiere un tracker para todos los modos, y un seeder para los modos peer y leecher y los ficheros .torrent. +#@param str_pathDirectory str_Relative_path_file +#@param int_disk int_partition str_Relative_path_file +#@param str_REPOSITORY(CACHE - LOCAL) str_Relative_path_file +#@param (2 parámetros) $1 path_aboluto_fichero_torrent $2 Parametros_Session_Torrent +#@param (3 parámetros) $1 Contenedor CACHE $2 path_absoluto_fichero_Torrent $3 Parametros_Session_Torrent +#@param (4 parámetros) $1 disk $2 particion $3 path_absoluto_fichero_Torrent 4$ Parametros_Session_Torrent +#@return +#@note protocoloTORRENT=mode:time mode=seeder -> Dejar el equipo seedeando hasta que transcurra el tiempo indicado o un kill desde consola, mode=peer -> seedear mientras descarga mode=leecher -> NO seedear mientras descarga time tiempo que una vez descargada la imagen queremos dejar al cliente como seeder. +#*/ ## +#ogTorrentStart ( torrentfile='/opt/opengnsys/cache/linux.iso', torrentsess='peer:60') +#ogTorrentStart (container='CACHE', torrentfile='/PS1_PH1.img.torrent', torrentsess='seeder:10000') +#ogTorrentStart (disk=1, par=1, torrentfile='/linux.iso.torrent', torrentsess='leecher:60') +#ogTorrentStart /opt/opengnsys/cache/opt/opengnsys/images/ubu24.EXTFS.torrent seeder:10000 +#ogTorrentStart 1 4 /opt/opengnsys/images/ubu24.EXTFS.torrent seeder:10000 +#ogTorrentStart CACHE /ubu24.EXTFS.torrent seeder:10000 +def ogTorrentStart (disk=None, par=None, container=None, torrentfile=None, torrentsess=None): + if torrentfile is None: + raise TypeError ('missing required argument: "torrentfile"') + if torrentsess is None: + raise TypeError ('missing required argument: "torrentsess"') + + if container is not None: + if disk is None and par is None: + ## we were given container= + if 'CACHE' != container.upper(): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, 'La descarga torrent solo se hace desde local, copia el torrent a la cache y realiza la operación desde esa ubicación') + return None + source = FileLib.ogGetPath (src=container, file=torrentfile) + dev_err = f'{container} {torrentfile}' + print (f'nati ogGetPath (src=({container}), file=({torrentfile})) = source ({source})') + else: + raise TypeError ('argument "container" can be specified along neither "disk" nor "par"') + + else: + if disk is not None and par is not None: + ## we were given disk= par= + source = FileLib.ogGetPath (src=f'{disk} {par}', file=torrentfile) + dev_err = f'{disk} {par} {torrentfile}' + print (f'nati ogGetPath (src=({disk} {par}), file=({torrentfile})) = source ({source})') + elif disk is None and par is None: + ## we were given nothing + if torrentfile.startswith ('/opt/opengnsys/images'): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, 'La descarga torrent solo se hace desde local, copia el torrent a la cache y realiza la operación desde esa ubicación') + return None + source = FileLib.ogGetPath (file=torrentfile) + dev_err = torrentfile + print (f'nati ogGetPath (file=({torrentfile})) = source ({source})') + else: + raise TypeError ('if one of "disk" and "par" are specified, then both must be') + + if not source: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f'device or torrentfile {dev_err} not found') + return + + if subprocess.run (['ctorrent', '-x', source]).returncode: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, '') + return None + + target = re.sub (r'\.torrent$', '', source) + dirsource = FileLib.ogGetParentPath (file=source) + + ERROR = None + sess = torrentsess.split (':') + if 2 != len (sess): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, 'parametros session Torrent no completa: modo:tiempo') + return None + + mode = sess[0].lower() + if mode not in ['seeder', 'peer', 'leecher']: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, f'valor modo Torrent no valido {sess[0]}') + return None + + time = sess[1] + if not re.search (r'^[0-9]{1,10}$', time): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, f'valor tiempo no valido {sess[1]}') + return None + time = int (time) + + OPTION = None + cwd = os.getcwd() + # si No fichero .bf, y Si fichero destino imagen ya descargada y su chequeo fue comprobado en su descarga inicial. + if not os.path.exists (f'{source}.bf') and os.path.exists (target): + print ('imagen ya descargada') + if 'seeder' != mode: return 'success' ## return any true value + + print ('MODE seeder ctorrent') + os.chdir (dirsource) + subprocess.run (['timeout', '--signal', 'INT', time, 'ctorrent', '-f', source]) + os.chdir (cwd) + return 'success' + + #Si no existe bf ni fichero destino descarga inicial. + if not os.path.exists (f'{source}.bf') and not os.path.exists (target): + print ('descarga inicial') + OPTION = 'DOWNLOAD' + + # Si fichero bf descarga anterior no completada -. + if os.path.exists (f'{source}.bf') and os.path.exists (target): + print ('Continuar con Descargar inicial no terminada.') + OPTION = 'DOWNLOAD' + + if 'DOWNLOAD' != OPTION: return 'success' + + os.chdir (dirsource) + if 'peer' == mode: + print ('Donwloading Torrent as peer') + # Creamos el fichero de resumen por defecto + open (f'{source}.bf', 'w').close() + # ctorrent controla otro fichero -b ${SOURCE}.bfog + subprocess.run (['ctorrent', '-f', '-c', '-X', f'sleep {time}; kill -2 $(pidof ctorrent)', '-C', '100', source, '-s', target, '-b', f'{source}.bfog']) + elif 'leecher' == mode: + print ('Donwloading Torrent as leecher') + subprocess.run (['ctorrent', '${SOURCE}', '-X', 'sleep 30; kill -2 $(pidof ctorrent)', '-C', '100', '-U', '0']) + elif 'seeder' == mode: + print ('MODE seeder ctorrent') + # Creamos el fichero de resumen por defecto + open (f'{source}.bf', 'w').close() + # ctorrent controla otro fichero -b ${SOURCE}.bfog + subprocess.run (['ctorrent', '-f', '-c', '-X', f'sleep {time}; kill -2 $(pidof ctorrent)', '-C', '100', source, '-s', target, '-b', f'{source}.bfog']) + else: + print ('this should not happen') + return None + + os.chdir (cwd) + + +#/** +# ogCreateTorrent [ str_repo | int_ndisk int_npart ] Relative_path_file +#@brief Función para crear el fichero torrent. +#@param str_pathDirectory str_Relative_path_file +#@param int_disk int_partition str_Relative_path_file +#@param str_REPOSITORY(CACHE - LOCAL) str_Relative_path_file +#@return +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Disco o particion no corresponden con un dispositivo. +#@exception OG_ERR_PARTITION Tipo de partición desconocido o no se puede montar. +#@exception OG_ERR_NOTOS La partición no tiene instalado un sistema operativo. +#*/ ## + +#ogCreateTorrent ([str_REPOSITORY] [int_ndisk int_npart] Relative_path_file IpBttrack) +#ogCreateTorrent (disk=1, par=1, file='/aula1/winxp', ip_bttrack='10.1.15.23') +#ogCreateTorrent (container='REPO', file='/aula1/winxp', ip_bttrack='10.1.15.45') +#ogCreateTorrent (container='CACHE', file='/aula1/winxp', ip_bttrack='10.1.15.45') +def ogCreateTorrent (disk=None, par=None, container=None, file=None, ip_bttrack=None): + if file is None: + raise TypeError ('missing required argument: "file"') + if ip_bttrack is None: + raise TypeError ('missing required argument: "ip_bttrack"') + + from_cache = False + + if container is not None: + if disk is None and par is None: + ## we were given container= + if 'CACHE' == container: from_cache = True + ext = ImageLib.ogGetImageType (container, file) + if ext is None: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f'{container} {file}') + return + f = f'{file}.{ext}' + source = FileLib.ogGetPath (src=container, file=f) + print (f'nati ogGetPath (src=({container}), file=({f})) = source ({source})') + else: + raise TypeError ('argument "container" can be specified along neither "disk" nor "par"') + + else: + if disk is not None and par is not None: + ## we were given disk= par= + f = f'{file}.img' + source = FileLib.ogGetPath (src=f'{disk} {par}', file=f) + print (f'nati ogGetPath (src=({disk} {par}), file=({f})) = source ({source})') + elif disk is None and par is None: + ## we were given nothing + f = f'{file}.img' + source = FileLib.ogGetPath (file=f) + print (f'nati ogGetPath (file=({f})) = source ({source})') + else: + raise TypeError ('if one of "disk" and "par" are specified, then both must be') + + if not source: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, '') + return + + if from_cache: + if not CacheLib.ogFindCache(): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, "CACHE") + #return None + + if os.path.exists (f'{source}.torrent'): + os.rename (f'{source}.torrent', f'{source}.torrent.ant') + print ('Esperamos que se refresque el servidor') + time.sleep (20) + + cwd = os.getcwd() + os.chdir (os.path.dirname (source)) + print (f'ctorrent -t {os.path.basename (source)} -u http://{ip_bttrack}:6969/announce -s {source}.torrent') + subprocess.run (['ctorrent', '-t', os.path.basename (source), '-u', f'http://{ip_bttrack}:6969/announce', '-s', f'{source}.torrent']) + os.chdir (cwd) + + +#/** +# ogUpdateCacheIsNecesary [ str_repo ] Relative_path_file_OGIMG_with_/ +#@brief Comprueba que el fichero que se desea almacenar en la cache del cliente, no esta. +#@param 1 str_REPO +#@param 2 str_Relative_path_file_OGIMG_with_/ +#@param 3 md5 to check: use full to check download image torrent +#@return True cache sin imagen, SI es necesario actualizar el fichero. +#@return False imagen en la cache, NO es necesario actualizar el fichero +#@return None error de sintaxis (TODO) +#@note +#@todo: Proceso en el caso de que el fichero tenga el mismo nombre, pero su contenido sea distinto. +#@todo: Se dejan mensajes mientras se confirma su funcionamiento. +#*/ ## +#ogUpdateCacheIsNecesary ('REPO', '/PS1_PH1.img', 'UNICAST') +#ogUpdateCacheIsNecesary ('REPO', '/ogclient.sqfs', 'FULL') +#ogUpdateCacheIsNecesary ('REPO', '/ogclient.sqfs', 'TORRENT') +def ogUpdateCacheIsNecesary (repo, file, proto): + if not CacheLib.ogFindCache(): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTCACHE, '') + return None + + if repo.lower() != 'repo': + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f' {repo} {file}') + return None + + filesource = FileLib.ogGetPath (src=repo, file=file) + #print (f'nati filesource ({filesource})') + if not filesource: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f' {repo} {file}') + return None + + # paso 1. si no existe la imagen, confirmar que es necesario actualizar la cache. + filetarget = FileLib.ogGetPath (src='CACHE', file=file) + #print (f'nati filetarget ({filetarget})') + if not filetarget: + # borramos el fichero bf del torrent, en el caso de que se hubiese quedado de algun proceso fallido + if FileLib.ogGetPath (src='CACHE', file=f'/{file}.torrent.bf'): ogDeleteFile (container='CACHE', file=f'{file}.torrent.bf') + if FileLib.ogGetPath (src='CACHE', file=f'/{file}.sum'): ogDeleteFile (container='CACHE', file=f'{file}.sum') + if FileLib.ogGetPath (src='CACHE', file=f'/{file}.full.sum'): ogDeleteFile (container='CACHE', file=f'{file}.full.sum') + print ('TRUE(0), es necesario actualizar. Paso 1, la cache no contiene esa imagen ') + return True + + # Paso 2. Comprobamos que la imagen no estuviese en un proceso previo torrent + if FileLib.ogGetPath (file=f'{filetarget}.torrent.bf'): + #TODO: comprobar los md5 del fichero .torrent para asegurarnos que la imagen a descarga es la misma. + print ('TRUE(0), es necesario actualizar. Paso 2, la imagen esta en un estado de descarga torrent interrumpido') + return True + + ## En este punto la imagen en el repo y en la cache se llaman igual, + # paso 4. Obtener los md5 del fichero imagen en la cacha segun PROTOCOLO $3 + if proto.lower() in ['full', 'torrent']: + #Buscamos MD5 en el REPO SOURCE + if os.path.exists (f'{filesource}.full.sum'): + with open (f'{filesource}.full.sum', 'r') as fd: + md5source = fd.read().strip() + else: + md5source = FileLib.ogCalculateFullChecksum (file=filesource) + + # Generamos el MD5 (full) en la CACHE + if not os.path.exists (f'{filetarget}.full.sum'): + fullck = FileLib.ogCalculateFullChecksum (file=filetarget) + with open (f'{filetarget}.full.sum', 'w') as fd: + fd.write (fullck + '\n') + with open (f'{filetarget}.full.sum', 'r') as fd: + md5target = fd.read().strip() + # Generamos el MD5 (little) en la CACHE para posteriores usos del protocolo MULTICAST + if not os.path.exists (f'{filetarget}.sum'): + ck = FileLib.ogCalculateChecksum (file=filetarget) + with open (f'{filetarget}.sum', 'w') as fd: + fd.write (ck + '\n') + else: + #Buscamos MD5 en el REPO SOURCE + if os.path.exists (f'{filesource}.sum'): + with open (f'{filesource}.sum', 'r') as fd: + md5source = fd.read().strip() + else: + md5source = FileLib.ogCalculateChecksum (file=filesource) + + # Generamos el MD5 (little) en la CACHE + if not os.path.exists (f'{filetarget}.sum'): + ck = FileLib.ogCalculateChecksum (file=filetarget) + with open (f'{filetarget}.sum', 'w') as fd: + fd.write (ck + '\n') + with open (f'{filetarget}.sum', 'r') as fd: + md5target = fd.read().strip() + #Generamos o copiamos MD5 (full) en la CACHE para posteriores usos con Torrent + # Si no existe el full.sum y si existe el .sum es porque el upateCACHE multicast o unicast ha sido correcto. + if not os.path.exists (f'{filetarget}.full.sum') and os.path.exists (f'{filetarget}.sum'): + if os.path.exists (f'{filesource}.full.sum'): + #Existe el .full.sum en REPO realizamos COPIA + shutil.copy2 (f'{filesource}.full.sum', f'{filetarget}.full.sum') + else: + #No existe .full.sum no en REPO LO GENERAMOS en la cache: situacion dificil que ocurra + fullck = FileLib.ogCalculateFullChecksum (file=filetarget) + with open (f'{filetarget}.full.sum', 'w') as fd: + fd.write (fullck + '\n') + + # Paso 5. comparar los md5 + if md5source == md5target: + print ('FALSE (1), No es neceario actualizar. Paso5.A la imagen esta en cache') + return False + else: + print ('imagen en cache distinta, borramos la imagen anterior') + for f in [f'{filetarget}', f'{filetarget}.sum', f'{filetarget}.torrent', f'{filetarget}.full.sum']: + os.unlink (f) + print ('TRUE (0), Si es necesario actualizar.') + return True diff --git a/client/lib/python3/RegistryLib.py b/client/lib/python3/RegistryLib.py new file mode 100644 index 0000000..2657bd8 --- /dev/null +++ b/client/lib/python3/RegistryLib.py @@ -0,0 +1,382 @@ +#/** +#@file RegistryLib.py +#@brief Librería o clase Registry +#@class Boot +#@brief Funciones para gestión del registro de Windows. +#@warning License: GNU GPLv3+ +#*/ + +import subprocess +import os +import re +import shutil +import tempfile + +import ogGlobals +import SystemLib +import FileLib + +# Función ficticia para lanzar chntpw con timeout de 5 s., evitando cuelgues del programa. +chntpw_exe = shutil.which ('drbl-chntpw') or shutil.which ('chntpw') +def chntpw (hivefile, input_file): + #print (f'nati: hivefile ({hivefile}) input_file ({input_file}) type ({type(input_file)})') + with open (input_file, 'r') as fd: + input_contents = fd.read() + #print (f'nati: input_contents ({input_contents})') + return subprocess.run ([chntpw_exe, '-e', hivefile], timeout=5, input=input_contents, capture_output=True, text=True).stdout + +## en el codigo bash aparecen "${3%\\*}" y "${3##*\\}" varias veces +## ${3%\\*} es el "dirname" de una key del registro +## ${3##*\\} es el "basename" +def _split_k (k): + k_elems = k.split ('\\') + k_dirname = '\\'.join (k_elems[0:-1]) + k_basename = k_elems[-1] + return k_dirname, k_basename + +#/** +# ogAddRegistryKey path_mountpoint str_hive str_keyname +#@brief Añade una nueva clave al registro de Windows. +#@param path_mountpoint directorio donde está montado el sistema Windows +#@param str_hive sección del registro +#@param str_keyname nombre de la clave +#@return (nada) +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Fichero de registro no encontrado. +#@note hive = { default, sam, security, software, system, components } +#@warning Requisitos: chntpw +#@warning El sistema de archivos de Windows debe estar montada previamente. +#*/ ## +def ogAddRegistryKey (mntpt, hive, k): + hivefile = ogGetHivePath (mntpt, hive) + if not hivefile: return + + k_dirname, k_basename = _split_k (k) + + with tempfile.NamedTemporaryFile (delete_on_close=False, prefix='chntpw-', mode='w') as f: + f.write (f'cd {k_dirname}\n') + f.write (f'nk {k_basename}\n') + f.write ('q\ny\n') + f.close() + chntpw (hivefile, f.name) + os.remove (f.name) + +#/** +# ogAddRegistryValue path_mountpoint str_hive str_valuename [str_valuetype] +#@brief Añade un nuevo valor al registro de Windows, indicando su tipo de datos. +#@param path_mountpoint directorio donde está montado el sistema Windows +#@param str_hive sección del registro +#@param str_valuename nombre del valor +#@param str_valuetype tipo de datos del valor (opcional) +#@return (nada) +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Fichero de registro no encontrado. +#@note hive = { DEFAULT, SAM, SECURITY, SOFTWARE, SYSTEM, COMPONENTS } +#@note valuetype = { STRING, BINARY, DWORD }, por defecto: STRING +#@warning Requisitos: chntpw +#@warning El sistema de archivos de Windows debe estar montada previamente. +#*/ ## +#ogAddRegistryValue ('/mnt/sda1', 'SOFTWARE', '\Microsoft\NewKey\Value1') ## type STRING by default +#ogAddRegistryValue ('/mnt/sda1', 'SOFTWARE', '\Microsoft\NewKey\Value1', 'STRING') +#ogAddRegistryValue ('/mnt/sda1', 'SOFTWARE', '\Microsoft\NewKey\Value1', 'BINARY') +#ogAddRegistryValue ('/mnt/sda1', 'SOFTWARE', '\Microsoft\NewKey\Value1', 'DWORD') +def ogAddRegistryValue (mntpt, hive, k, vtype='STRING'): + hivefile = ogGetHivePath (mntpt, hive) + if not hivefile: return + + k_dirname, k_basename = _split_k (k) + + # Determine the value type. + if 'STRING' == vtype.upper(): TYPE = 1 + elif 'BINARY' == vtype.upper(): TYPE = 3 + elif 'DWORD' == vtype.upper(): TYPE = 4 + else: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_OUTOFLIMIT, vtype) + return + + with tempfile.NamedTemporaryFile (delete_on_close=False, prefix='chntpw-', mode='w') as f: + f.write (f'cd {k_dirname}\n') + f.write (f'nv {TYPE} {k_basename}\n') + f.write ('q\ny\n') + f.close() + chntpw (hivefile, f.name) + os.remove (f.name) + + +#/** +# ogDeleteRegistryKey path_mountpoint str_hive str_keyname +#@brief Elimina una clave del registro de Windows con todo su contenido. +#@param path_mountpoint directorio donde está montado el sistema Windows +#@param str_hive sección del registro +#@param str_keyname nombre de la clave +#@return (nada) +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Fichero de registro no encontrado. +#@note hive = { default, sam, security, software, system, components } +#@warning Requisitos: chntpw +#@warning El sistema de archivos de Windows debe estar montada previamente. +#@warning La clave debe estar vacía para poder ser borrada. +#*/ ## +def ogDeleteRegistryKey (mntpt, hive, k): + hivefile = ogGetHivePath (mntpt, hive) + if not hivefile: return + + k_dirname, k_basename = _split_k (k) + + with tempfile.NamedTemporaryFile (delete_on_close=False, prefix='chntpw-', mode='w') as f: + f.write (f'cd {k_dirname}\n') + f.write (f'dk {k_basename}\n') + f.write ('q\ny\n') + f.close() + chntpw (hivefile, f.name) + os.remove (f.name) + + +#/** +# ogDeleteRegistryValue path_mountpoint str_hive str_valuename +#@brief Elimina un valor del registro de Windows. +#@param path_mountpoint directorio donde está montado el sistema Windows +#@param str_hive sección del registro +#@param str_valuename nombre del valor +#@return (nada) +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Fichero de registro no encontrado. +#@note hive = { default, sam, security, software, system, components } +#@warning Requisitos: chntpw +#@warning El sistema de archivos de Windows debe estar montada previamente. +#*/ ## +#ogDeleteRegistryValue ('/mnt/sda1', 'SOFTWARE', '\Microsoft\NewKey\Value1') +def ogDeleteRegistryValue (mntpt, hive, k): + hivefile = ogGetHivePath (mntpt, hive) + if not hivefile: return + + k_dirname, k_basename = _split_k (k) + + with tempfile.NamedTemporaryFile (delete_on_close=False, prefix='chntpw-', mode='w') as f: + f.write (f'cd {k_dirname}\n') + f.write (f'dv {k_basename}\n') + f.write ('q\ny\n') + f.close() + chntpw (hivefile, f.name) + os.remove(f.name) + + +#/** +# ogGetHivePath path_mountpoint [str_hive|str_user] +#@brief Función básica que devuelve el camino del fichero con una sección del registro. +#@param path_mountpoint directorio donde está montado el sistema Windows +#@param str_hive sección del registro +#@return str_path - camino del fichero de registro +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Fichero de registro no encontrado. +#@note hive = { DEFAULT, SAM, SECURITY, SOFTWARE, SYSTEM, COMPONENTS, NombreDeUsuario } +#@warning El sistema de archivos de Windows debe estar montada previamente. +#*/ ## +#ogGetHivePath ('/mnt/sda1', 'user1') => /mnt/sda1/Users/user1/NTUSER.DAT +#ogGetHivePath ('/mnt/sda1', 'SYSTEM') => //mnt/sda1/Windows/System32/config/SYSTEM +#ogGetHivePath ('/mnt/sda1', 'IEUser') => //mnt/sda1/Users/IEUser/NTUSER.DAT +def ogGetHivePath(mntpt, hive): + # Camino del fichero de registro de usuario o de sistema (de menor a mayor prioridad). + FILE = FileLib.ogGetPath(file=f"/{mntpt}/Windows/System32/config/{hive}") + if not FILE: FILE = FileLib.ogGetPath(file=f"/{mntpt}/Users/{hive}/NTUSER.DAT") + if not FILE: FILE = FileLib.ogGetPath(file=f"/{mntpt}/winnt/system32/config/{hive}") + if not FILE: FILE = FileLib.ogGetPath(file=f"/{mntpt}/Documents and Settings/{hive}/NTUSER.DAT") + if FILE and os.path.isfile(FILE): + return FILE + else: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f'{mntpt} {hive}') + return None + + +## simulate 'grep --after-context 1' +def _grep_A1 (strings, search_term): + results = [] + for i in range (len (strings)): + if search_term in strings[i]: + results.append (strings[i]) + if i + 1 < len(strings): + results.append (strings[i + 1]) + + return results + +#/** +# ogGetRegistryValue path_mountpoint str_hive str_valuename +#@brief Devuelve el dato de un valor del registro de Windows. +#@param path_mountpoint directorio donde está montado el sistema Windows +#@param str_hive sección del registro +#@param str_valuename nombre del valor +#@return str_valuedata - datos del valor. +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Fichero de registro no encontrado. +#@note hive = { default, sam, security, software, system, components } +#@warning Requisitos: chntpw, awk +#@warning El sistema de archivos de Windows debe estar montado previamente. +#*/ ## +def ogGetRegistryValue (mntpt, hive, k): + hivefile = ogGetHivePath(mntpt, hive) + if not hivefile: return + + k_dirname, k_basename = _split_k (k) + + with tempfile.NamedTemporaryFile (delete_on_close=False, prefix='chntpw-', mode='w') as f: + f.write (f'cd {k_dirname}\n') + f.write (f'cat {k_basename}\n') + f.write ('q\n') + f.close() + chntpw_out = chntpw (hivefile, f.name) + os.remove (f.name) + + lines = chntpw_out.splitlines() + lines = _grep_A1 (lines, '> Value') + if 2 != len (lines): + return None + + ret = None + if 'REG_BINARY' in lines[0]: + if re.search ('^:[0-9A-F]+ ', lines[1]): + ret = lines[1][8:56] + else: + ret = lines[1] + return ret + + +#/** +# ogListRegistryKeys path_mountpoint str_hive str_key +#@brief Lista los nombres de subclaves de una determinada clave del registro de Windows. +#@param path_mountpoint directorio donde está montado el sistema Windows +#@param str_hive sección del registro +#@param str_key clave de registro +#@return str_subkey ... - lista de subclaves +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Fichero de registro no encontrado. +#@note hive = { default, sam, security, software, system, components } +#@warning Requisitos: chntpw, awk +#@warning El sistema de archivos de Windows debe estar montado previamente. +#*/ ## +#ogListRegistryKeys ('/mnt/sda1', 'SOFTWARE', '\Microsoft\Windows\CurrentVersion') +def ogListRegistryKeys (mntpt, hive, k): + hivefile = ogGetHivePath(mntpt, hive) + if not hivefile: return + + with tempfile.NamedTemporaryFile (delete_on_close=False, prefix='chntpw-', mode='w') as f: + f.write (f'ls {k}\n') + f.write ('q\n') + f.close() + chntpw_out = chntpw (hivefile, f.name) + os.remove (f.name) + + lines = chntpw_out.splitlines() + ret = [] + for l in lines: + elems = re.split ('[<>]', l) + if len(elems) < 2: continue + if ' ' == elems[0]: + ret.append (elems[1]) + return ret + + +#/** +# ogListRegistryValues path_mountpoint str_hive str_key +#@brief Lista los nombres de valores de una determinada clave del registro de Windows. +#@param path_mountpoint directorio donde está montado el sistema Windows +#@param str_hive sección del registro +#@param str_key clave de registro +#@return str_value ... - lista de valores +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Fichero de registro no encontrado. +#@note hive = { default, sam, security, software, system, components } +#@warning Requisitos: chntpw, awk +#@warning El sistema de archivos de Windows debe estar montado previamente. +#*/ ## +#ogListRegistryValues ('/mnt/sda1', 'SOFTWARE', '\Microsoft\Windows\CurrentVersion') +def ogListRegistryValues (mntpt, hive, k): + hivefile = ogGetHivePath(mntpt, hive) + if not hivefile: return + + with tempfile.NamedTemporaryFile (delete_on_close=False, prefix='chntpw-', mode='w') as f: + f.write (f'ls {k}\n') + f.write ('q\n') + f.close() + chntpw_out = chntpw (hivefile, f.name) + os.remove (f.name) + + lines = chntpw_out.splitlines() + ret = [] + for l in lines: + elems = re.split ('[<>]', l) + if len(elems) < 2: continue + if 'REG_' in elems[0]: + ret.append (elems[1]) + return ret + + +def _format_hex (hex_string): + result = [] + offset = 0 + + hex_values = hex_string.strip().split() + result.append (str (len (hex_values))) + + while hex_values: + chunk = hex_values[:16] + hex_values = hex_values[16:] + + offset_str = f':{offset:05x} ' + hex_line = ' '.join (chunk) + result.append (offset_str + hex_line) + + offset += 16 + + return '\n'.join (result) + +#/** +# ogSetRegistryValue path_mountpoint str_hive str_valuename str_valuedata +#@brief Establece el dato asociado a un valor del registro de Windows. +#@param path_mountpoint directorio donde está montado el sistema Windows +#@param str_hive sección del registro +#@param str_valuename nombre del valor de registro +#@param str_valuedata dato del valor de registro +#@return (nada) +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND Fichero de registro no encontrado. +#@note hive = { default, sam, security, software, system, components } +#@warning Requisitos: chntpw +#@warning El sistema de archivos de Windows debe estar montado previamente. +#*/ ## +#ogSetRegistryValue ('/mnt/sda1', 'SOFTWARE', '\Key\SubKey\StringValue', 'Abcde Fghij') +#ogSetRegistryValue ('/mnt/sda1', 'SOFTWARE', '\Key\SubKey\DwordValue', 1) +#ogSetRegistryValue ('/mnt/sda1', 'SOFTWARE', '\Key\SubKey\BinaryValue', '04 08 0C 10') +def ogSetRegistryValue (mntpt, hive, k, v): + hivefile = ogGetHivePath (mntpt, hive) + if not hivefile: return + + k_dirname, k_basename = _split_k (k) + + with tempfile.NamedTemporaryFile (delete_on_close=False, prefix='chntpw-', mode='w') as f: + f.write (f"ls {k_dirname}\n") + f.write ('q\n') + f.close() + chntpw_out = chntpw (hivefile, f.name) + os.remove(f.name) + + if re.search (f"BINARY.*<{k_basename}>", chntpw_out): + ## the entry in the registry is binary. Our input should be a sequence of bytes + + if ' ' != v[-1]: v += ' ' ## the regex below requires a trailing space + if not re.match (r'^([0-9A-F]{2} )*$', v.upper()): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, f'"{v}"') + return + + formatted = _format_hex (v.upper()) + formatted += '\ns' + else: + formatted = v + + with tempfile.NamedTemporaryFile (delete_on_close=False, prefix='chntpw-', mode='w') as f: + f.write (f'cd {k_dirname}\n') + f.write (f'ed {k_basename}\n') + f.write (f'{formatted}\n') + f.write ('q\ny\n') + f.close() + chntpw (hivefile, f.name) + os.remove(f.name) diff --git a/client/lib/python3/StringLib.py b/client/lib/python3/StringLib.py new file mode 100644 index 0000000..eda1c93 --- /dev/null +++ b/client/lib/python3/StringLib.py @@ -0,0 +1,31 @@ +import re + + + +#/** +# ogCheckIpAddress +#@brief Función para determinar si una cadena es una dirección ipv4 válida +#@param 1 string de la ip a comprobar +#@return 0 si es una dirección válida +#@return 1 si NO es una dirección válida +#@exception OG_ERR_FORMAT formato incorrecto. +#@note +#@todo +#*/ ## +def ogCheckIpAddress(ip): + """ + Función para determinar si una cadena es una dirección ipv4 válida. + + :param ip: string de la ip a comprobar + :return: True si es una dirección válida, False si NO es una dirección válida + """ + if not isinstance(ip, str): + raise ValueError("Formato incorrecto, el parámetro debe ser una cadena.") + + regex = r"^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$" + if re.match(regex, ip): + parts = ip.split('.') + if all(0 <= int(part) <= 255 for part in parts): + return True + return False + diff --git a/client/lib/python3/SystemLib.py b/client/lib/python3/SystemLib.py new file mode 100644 index 0000000..fffd2cb --- /dev/null +++ b/client/lib/python3/SystemLib.py @@ -0,0 +1,310 @@ +import subprocess +import datetime +from zoneinfo import ZoneInfo +import sys +import os +import shutil +import inspect +import glob + +## for ogExecAndLog +from io import StringIO +from contextlib import redirect_stdout, redirect_stderr + +import ogGlobals +import StringLib +import SystemLib + +#NODEBUGFUNCTIONS, OGIMG, OG_ERR_CACHESIZE, OG_ERR_NOTCACHE, OG_ERR_NOTWRITE, OG_ERR_FILESYS +#OG_ERR_REPO, OG_ERR_NOTOS, OG_ERR_NOGPT, OG_ERR_OUTOFLIMIT, OG_ERR_IMAGE, OG_ERR_CACHE +#OGLOGSESSION, OGLOGCOMMAND, OGLOGFILE, OG_ERR_LOCKED, OG_ERR_PARTITION, OG_ERR_FORMAT, OG_ERR_NOTEXEC, OG_ERR_NOTFOUND + +def _logtype2logfile (t): + if 'log' == t.lower(): return ogGlobals.OGLOGFILE + elif 'command' == t.lower(): return ogGlobals.OGLOGCOMMAND + elif 'session' == t.lower(): return ogGlobals.OGLOGSESSION + else: raise Exception (f'unknown log type ({t})') +#/** +# ogEcho [str_logtype ...] [str_loglevel] "str_message" ... +#@brief Muestra mensajes en consola y lo registra en fichero de incidencias. +#@param str_logtype tipo de registro de incidencias ("log", "command", "session") +#@param str_loglevel nivel de registro de incidencias ("info", "warning", "error") +#@param str_message mensaje (puede recibir más de 1 parámetro. +#@return Mensaje mostrado. +#*/ +def ogEcho (logtypes, loglevel, msg): + logfiles = ['/dev/stdout'] + if type (logtypes) is list: + for l in logtypes: + logfiles.append (_logtype2logfile (l)) + else: ## string + logfiles.append (_logtype2logfile (logtypes)) + + if loglevel is None or 'help' == loglevel: + if ogGlobals.DEBUG.lower() != "no": + logfiles.append (ogGlobals.OGLOGFILE) + for f in logfiles: + with open (f, 'a') as fd: + fd.write (msg + '\n') + return + + if 'info' == loglevel or 'warning' == loglevel or 'error' == loglevel: + DATETIME = datetime.datetime.now(ZoneInfo(ogGlobals.TZ)).strftime("%F %T %Z") + + for f in logfiles: + with open (f, 'a') as fd: + fd.write (f"OpenGnsys {loglevel} {DATETIME} {msg}\n") + else: + raise Exception (f'unknown loglevel ({loglevel})') + + +#/** +# ogExecAndLog str_logfile ... str_command ... +#@brief Ejecuta un comando y guarda su salida en fichero de registro. +#@param str_logfile fichero de registro (pueden ser varios). +#@param str_command comando y comandos a ejecutar. +#@return Salida de ejecución del comando. +#@note str_logfile = { LOG, SESSION, COMMAND } +#*/ +#ogHelp (str_logfile ... str_command ...", +#ogHelp ([], ogMyLib.ogSomeMethod, *args, **kwargs) +#ogHelp ('command', ogMyLib.ogSomeMethod, *args, **kwargs) +#ogHelp (['command'], ogMyLib.ogSomeMethod, *args, **kwargs) +#ogHelp (['log', 'command'], ogMyLib.ogSomeMethod, *args, **kwargs) +def ogExecAndLog (logtypes, fun, *args, **kwargs): + logfiles = ['/dev/stdout'] + if type (logtypes) is list: + for l in logtypes: + logtypes = list (map (lambda x: x.lower(), logtypes)) + logfiles.append (_logtype2logfile (l)) + else: ## string + logtypes = logtypes.lower() + logfiles.append (_logtype2logfile (logtypes)) + + if not fun: + ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, 'no function provided') + return + + ## the original bash code does something like this: + #if [ $ISCOMMAND ]; then + # > $OGLOGCOMMAND + # REDIREC="2>&1" + #fi + #eval $COMMAND $REDIREC | tee -a $FILES + + ## a hybrid bash/python pseudocode would end up being like the following: + #if 'command' in logtypes: + # rm $OGLOGCOMMAND + # touch $OGLOGCOMMAND + # + #if 'command' in logtypes: + # ## redirect both stdout and stderr + # eval $COMMAND 2>&1 | tee -a $FILES + #else: + # ## redirect stdout only + # eval $COMMAND | tee -a $FILES + + import time + sout = serr = '' + if 'command' in logtypes: + os.unlink (ogGlobals.OGLOGCOMMAND) + open (ogGlobals.OGLOGCOMMAND, 'w').close() + #print ('nati: ogExecAndLog: about to redirect stdout and stderr') + #time.sleep (1) ## nati + with redirect_stdout (StringIO()) as r_stdout, redirect_stderr (StringIO()) as r_stderr: + rc = fun (*args, **kwargs) + sout = r_stdout.getvalue() + serr = r_stderr.getvalue() + #print (f'nati: ogExecAndLog: end of redirections, rc ({rc}) sout ({sout}) serr ({serr})') + #time.sleep (1) ## nati + else: + #print ('nati: ogExecAndLog: about to redirect stdout only') + #time.sleep (1) ## nati + with redirect_stdout (StringIO()) as r_stdout: + rc = fun (*args, **kwargs) + sout = r_stdout.getvalue() + #print (f'nati: ogExecAndLog: end of redirections, rc ({rc}) sout ({sout})') + #time.sleep (1) ## nati + + rc_str = str (rc) + if sout or serr or ('True' != rc_str and 'False' != rc_str and 'None' != rc_str): + #print ('nati: ogExecAndLog: sout or serr are true') + #time.sleep (1) ## nati + for f in logfiles: + #print (f'nati: ogExecAndLog: logging to logfile ({f})') + with open (f, 'a') as fd: + if sout: fd.write (f'{sout}\n') + if serr: fd.write (f'{serr}\n') + if rc_str: fd.write (f'{rc_str}\n') + #fd.write (f"ogExecAndLog: {fun.__name__} rc:\n{rc_str}\n") + #if sout: fd.write (f"ogExecAndLog: {fun.__name__} stdout:\n{sout}\n") + #else: fd.write (f"ogExecAndLog: {fun.__name__} stdout: (none)\n") + #if serr: fd.write (f"ogExecAndLog: {fun.__name__} stderr:\n{serr}\n") + #else: fd.write (f"ogExecAndLog: {fun.__name__} stderr: (none)\n") + + #print (f'nati: ogExecAndLog: returning rc ({rc})') + return rc + +#/** +# ogGetCaller +#@brief Devuelve nombre del programa o script ejecutor (padre). +#@return str_name - Nombre del programa ejecutor. +#*/ +def ogGetCaller(): + if 'COLUMNS' in os.environ: + cols = os.environ['COLUMNS'] + else: + cols = None + + lines = subprocess.run (["ps", "hp", str(os.getppid()), "-o", "args"], capture_output=True, text=True).stdout.splitlines() + if 0 == len (lines): + return '' + + line = lines[0] + words = line.split() + if "bash" in line and len(words)>1: + caller = words[1] + else: + caller = words[0].lstrip("-") + + if cols is None: + del (os.environ['COLUMNS']) + else: + os.environ['COLUMNS'] = cols + + return os.path.basename(caller) + +#/** +# ogHelp ["str_function" ["str_format" ["str_example" ... ]]] +#@brief Muestra mensaje de ayuda para una función determinda. +#@param str_function Nombre de la función. +#@param str_format Formato de ejecución de la función. +#@param str_example Ejemplo de ejecución de la función. +#@return str_help - Salida de ayuda. +#@note Si no se indican parámetros, la función se toma de la variable \c $FUNCNAME +#@note La descripción de la función se toma de la variable compuesta por \c MSG_FUNC_$función incluida en el fichero de idiomas. +#@note Pueden especificarse varios mensajes con ejemplos. +#*/ +def ogHelp (fname, fmt=None, examples=[]): + FUNC = fname or inspect.stack()[1][3] + MSG = f'ogGlobals.lang.MSG_HELP_{FUNC}' + try: + MSG = eval (MSG) + except: + MSG = '' + ogEcho ([], "help", f"{ogGlobals.lang.MSG_FUNCTION} {FUNC}: {MSG}") + if fmt: + ogEcho([], "help", f" {ogGlobals.lang.MSG_FORMAT}: {fmt}") + + if type (examples) is list: + for example in examples: + ogEcho([], "help", f" {ogGlobals.lang.MSG_EXAMPLE}: {example}") + else: ## string + ogEcho([], "help", f" {ogGlobals.lang.MSG_EXAMPLE}: {examples}") + +#/** +# ogRaiseError [str_logtype ...] int_errcode ["str_errmessage" ...] +#@brief Devuelve el mensaje y el código de error correspondiente. +#@param str_logtype tipo de registro de incidencias. +#@param int_errcode código de error. +#@param str_errmessage mensajes complementarios de error. +#@return str_code - código de error +#*/ +def ogRaiseError (logtypes, code, msg): + if code == ogGlobals.OG_ERR_FORMAT: MSG = f'{ogGlobals.lang.MSG_ERR_FORMAT} "{msg}"' + elif code == ogGlobals.OG_ERR_NOTFOUND: MSG = f'{ogGlobals.lang.MSG_ERR_NOTFOUND} "{msg}"' + elif code == ogGlobals.OG_ERR_OUTOFLIMIT: MSG = f'{ogGlobals.lang.MSG_ERR_OUTOFLIMIT} "{msg}"' + elif code == ogGlobals.OG_ERR_PARTITION: MSG = f'{ogGlobals.lang.MSG_ERR_PARTITION} "{msg}"' + elif code == ogGlobals.OG_ERR_LOCKED: MSG = f'{ogGlobals.lang.MSG_ERR_LOCKED} "{msg}"' + elif code == ogGlobals.OG_ERR_CACHE: MSG = f'{ogGlobals.lang.MSG_ERR_CACHE} "{msg}"' + elif code == ogGlobals.OG_ERR_NOGPT: MSG = f'{ogGlobals.lang.MSG_ERR_NOGPT} "{msg}"' + elif code == ogGlobals.OG_ERR_REPO: MSG = f'{ogGlobals.lang.MSG_ERR_REPO} "{msg}"' + elif code == ogGlobals.OG_ERR_FILESYS: MSG = f'{ogGlobals.lang.MSG_ERR_FILESYS} "{msg}"' + elif code == ogGlobals.OG_ERR_IMAGE: MSG = f'{ogGlobals.lang.MSG_ERR_IMAGE} "{msg}"' + elif code == ogGlobals.OG_ERR_NOTOS: MSG = f'{ogGlobals.lang.MSG_ERR_NOTOS} "{msg}"' + elif code == ogGlobals.OG_ERR_NOTEXEC: MSG = f'{ogGlobals.lang.MSG_ERR_NOTEXEC} "{msg}"' + elif code == ogGlobals.OG_ERR_NOTWRITE: MSG = f'{ogGlobals.lang.MSG_ERR_NOTWRITE} "{msg}"' + elif code == ogGlobals.OG_ERR_NOTCACHE: MSG = f'{ogGlobals.lang.MSG_ERR_NOTCACHE} "{msg}"' + elif code == ogGlobals.OG_ERR_CACHESIZE: MSG = f'{ogGlobals.lang.MSG_ERR_CACHESIZE} "{msg}"' + elif code == ogGlobals.OG_ERR_REDUCEFS: MSG = f'{ogGlobals.lang.MSG_ERR_REDUCEFS} "{msg}"' + elif code == ogGlobals.OG_ERR_EXTENDFS: MSG = f'{ogGlobals.lang.MSG_ERR_EXTENDFS} "{msg}"' + elif code == ogGlobals.OG_ERR_IMGSIZEPARTITION: MSG = f'{ogGlobals.lang.MSG_ERR_IMGSIZEPARTITION} "{msg}"' + elif code == ogGlobals.OG_ERR_UPDATECACHE: MSG = f'{ogGlobals.lang.MSG_ERR_UPDATECACHE} "{msg}"' + elif code == ogGlobals.OG_ERR_DONTFORMAT: MSG = f'{ogGlobals.lang.MSG_ERR_DONTFORMAT} "{msg}"' + elif code == ogGlobals.OG_ERR_IMAGEFILE: MSG = f'{ogGlobals.lang.MSG_ERR_IMAGEFILE} "{msg}"' + elif code == ogGlobals.OG_ERR_UCASTSYNTAXT: MSG = f'{ogGlobals.lang.MSG_ERR_UCASTSYNTAXT} "{msg}"' + elif code == ogGlobals.OG_ERR_UCASTSENDPARTITION: MSG = f'{ogGlobals.lang.MSG_ERR_UCASTSENDPARTITION} "{msg}"' + elif code == ogGlobals.OG_ERR_UCASTSENDFILE: MSG = f'{ogGlobals.lang.MSG_ERR_UCASTSENDFILE} "{msg}"' + elif code == ogGlobals.OG_ERR_UCASTRECEIVERPARTITION: MSG = f'{ogGlobals.lang.MSG_ERR_UCASTRECEIVERPARTITION} "{msg}"' + elif code == ogGlobals.OG_ERR_UCASTRECEIVERFILE: MSG = f'{ogGlobals.lang.MSG_ERR_UCASTRECEIVERFILE} "{msg}"' + elif code == ogGlobals.OG_ERR_MCASTSYNTAXT: MSG = f'{ogGlobals.lang.MSG_ERR_MCASTSYNTAXT} "{msg}"' + elif code == ogGlobals.OG_ERR_MCASTSENDFILE: MSG = f'{ogGlobals.lang.MSG_ERR_MCASTSENDFILE} "{msg}"' + elif code == ogGlobals.OG_ERR_MCASTRECEIVERFILE: MSG = f'{ogGlobals.lang.MSG_ERR_MCASTRECEIVERFILE} "{msg}"' + elif code == ogGlobals.OG_ERR_MCASTSENDPARTITION: MSG = f'{ogGlobals.lang.MSG_ERR_MCASTSENDPARTITION} "{msg}"' + elif code == ogGlobals.OG_ERR_MCASTRECEIVERPARTITION: MSG = f'{ogGlobals.lang.MSG_ERR_MCASTRECEIVERPARTITION} "{msg}"' + elif code == ogGlobals.OG_ERR_PROTOCOLJOINMASTER: MSG = f'{ogGlobals.lang.MSG_ERR_PROTOCOLJOINMASTER} "{msg}"' + elif code == ogGlobals.OG_ERR_DONTMOUNT_IMAGE: MSG = f'{ogGlobals.lang.MSG_ERR_DONTMOUNT_IMAGE} "{msg}"' + elif code == ogGlobals.OG_ERR_DONTUNMOUNT_IMAGE: MSG = f'{ogGlobals.lang.MSG_ERR_DONTUNMOUNT_IMAGE} "{msg}"' + elif code == ogGlobals.OG_ERR_DONTSYNC_IMAGE: MSG = f'{ogGlobals.lang.MSG_ERR_DONTSYNC_IMAGE} "{msg}"' + elif code == ogGlobals.OG_ERR_NOTDIFFERENT: MSG = f'{ogGlobals.lang.MSG_ERR_NOTDIFFERENT} "{msg}"' + elif code == ogGlobals.OG_ERR_SYNCHRONIZING: MSG = f'{ogGlobals.lang.MSG_ERR_SYNCHRONIZING} "{msg}"' + elif code == ogGlobals.OG_ERR_NOTUEFI: MSG = f'{ogGlobals.lang.MSG_ERR_NOTUEFI} "{msg}"' + elif code == ogGlobals.OG_ERR_NOMSDOS: MSG = f'{ogGlobals.lang.MSG_ERR_NOMSDOS} "{msg}"' + elif code == ogGlobals.OG_ERR_NOTBIOS: MSG = f'{ogGlobals.lang.MSG_ERR_NOTBIOS} "{msg}"' + else: + MSG = ogGlobals.lang.MSG_ERR_GENERIC + CODE = ogGlobals.OG_ERR_GENERIC + + # Obtener lista de funciones afectadas, incluyendo el script que las llama. + call_stack = [i[3] for i in inspect.stack()] + if len (call_stack) < 2: return ## shouldn't happen + call_stack.pop() ## remove '' + call_stack.pop(0) ## remove 'ogRaiseError' + str_call_stack = ' '.join (call_stack) + + # Mostrar mensaje de error si es función depurable y salir con el código indicado. + if code == ogGlobals.OG_ERR_FORMAT or \ + (str_call_stack in ogGlobals.NODEBUGFUNCTIONS) or \ + not (len(call_stack)>0 and (call_stack[0] in ogGlobals.NODEBUGFUNCTIONS)): + #print ('nati: ogRaiseError: actually calling ogEcho') + ogEcho (logtypes, "error", f"{str_call_stack.replace(' ', '<-')}: {MSG}") + + return code + +#/** +# ogIsRepoLocked +#@brief Comprueba si el repositorio está siendo usado (tiene ficheros abiertos). +#@param No. +#@return Código de salida: 0 - bloqueado, 1 - sin bloquear o error. +#*/ +def ogIsRepoLocked(): +# No hacer nada, si no está definido el punto de montaje del repositorio. + if not ogGlobals.OGIMG: + return False + +# Comprobar si alguno de los ficheros abiertos por los procesos activos está en el +# punto de montaje del repositorio de imágenes. + proc_entries = glob.glob ('/proc/[0-9]*/fd/*') + for e in proc_entries: + p = os.path.realpath (e) + if ogGlobals.OGIMG in p: + return True + return False + +## has no users +#def ogCheckProgram(program): +# FUNCNAME = ogCheckProgram.__name__ +# +# if not program or not isinstance(program, str): +# SystemLib.ogRaiseError ("session", ogGlobals.OG_ERR_FORMAT, f"Error: {ogGlobals.lang.MSG_ERR_FORMAT} {FUNCNAME} \"program\"") +# return +# +# if not shutil.which(program): +# SystemLib.ogRaiseError ( "session", ogGlobals.OG_ERR_NOTEXEC, f"Error: The program '{program}' is not available on the system.") +# return +# +# return 0 + +def ogIsVirtualMachine(): + output = subprocess.run (["dmidecode", "-s", "system-product-name"], capture_output=True, text=True).stdout + return "KVM" in output or "VirtualBox" in output diff --git a/client/lib/python3/ogGlobals.py b/client/lib/python3/ogGlobals.py new file mode 100644 index 0000000..29eea03 --- /dev/null +++ b/client/lib/python3/ogGlobals.py @@ -0,0 +1,145 @@ +#!/usr/bin/python3 + +import sys +import os +import os.path +import locale +import importlib.util + +def load_lang (name): + global lang + if name in sys.modules: + return True + elif (spec := importlib.util.find_spec (name)) is not None: + lang = importlib.util.module_from_spec (spec) + sys.modules[name] = lang + spec.loader.exec_module (lang) + return True + else: + #print(f"can't find the {name!r} module") + return False + +l = locale.getlocale()[0] +if not l: print (f"couldn't set locale") +if not l or not load_lang (f'lang_{l}'): + if not load_lang ('lang_en_GB'): + raise ModuleNotFoundError (f"can't find the default language module", name=name) + +TZ='Europe/Madrid' + + +## engine.cfg +OGLOGSESSION='/tmp/session.log' +OGLOGCOMMAND='/tmp/command.log' +#OGWINCHKDISK=True #Hacer chkdisk tras la clonacion +ACTIONCACHEFULL='NONE' #Que hacer cuando la cache no tenga espacio libre. [ NONE | FORMAT ] ] +RESTOREPROTOCOLNOTCACHE=None #Que protocolo de restauracion usar en el caso de que no exista cache o no exista espacio sufiente. [NONE | UNICAST | MULTICAST].NONE retorna error +IMGPROG='partclone' +IMGCOMP='lzop' +IMGEXT='img' +IMGREDUCE=True +#OGWINREDUCE=True #Al enviar particion reducir el sistema de archivos previamente. +MCASTERRORSESSION=120 #timeout (segundos) para abortar la sesion de multicast si no contacta con el servidor de multicast. Valor asignado a 0, utiliza los valores por defecto de udp-cast +MCASTWAIT=30 # timeout (segundos) para abortar la la transferencia si se interrumpe. Valor asignado a 0, utiliza los valores por defecto de udp-cast +#CREATESPEED=100000*4 # Factor para calcular el time-out al crear la imagen. 100000k -> 4s +#FACTORSYNC=120 # Factor de compresion para las imagenes (windos en ext4). +#BACKUP=False # Realizar copia de seguridad antes de crear la imagen. +#IMGFS='EXT4' # Sistema de archivo de la imagenes sincronizadas. EXT4 o BTRFS +#OGSLEEP=20 # Tiempo de sleep antes de realizar el reboot +NODEBUGFUNCTIONS=['ogCreateImageSyntax', 'ogGetHivePath', 'ogGetOsType', 'ogRestoreImageSyntax', 'ogUnmountAll', 'ogUnmountCache'] # Funciones que no deben mostrar salida de avisos si son llamadas por otras funciones. +#DEFAULTSPEED='' +## /engine.cfg + + +## loadenviron.sh +OPENGNSYS = '/opt/opengnsys' +OGBIN = os.path.join (OPENGNSYS, 'bin') +OGETC = os.path.join (OPENGNSYS, 'etc') +OGLIB = os.path.join (OPENGNSYS, 'lib') +OGAPI = os.path.join (OGLIB, 'engine', 'bin') +OGSCRIPTS = os.path.join (OPENGNSYS, 'scripts') +OGIMG = os.path.join (OPENGNSYS, 'images') +OGCAC = os.path.join (OPENGNSYS, 'cache') +OGLOG = os.path.join (OPENGNSYS, 'log') +OGLOGFILE = f'{OGLOG}/192.168.42.42' ## TODO import NetLib; OGLOGFILE = f'$OGLOG/{NetLib.ogGetIpAddress()}.log' +DEBUG = 'yes' +_path = os.environ['PATH'] + ':/sbin:/usr/sbin:/usr/local/sbin:/bin:/usr/bin:/usr/local/bin:/opt/oglive/rootfs/opt/drbl/sbin' +os.environ['PATH'] = ':'.join ([OGSCRIPTS, _path, OGAPI, OGBIN]) + +if os.path.exists ('/tmp/initrd.cfg'): + with open ('/tmp/initrd.cfg', 'r') as fd: + initrd_cfg = fd.read() + for l in initrd_cfg.splitlines(): + if not l.startswith ('DEVICECFG='): continue ## eg. "DEVICECFG=/run/net-enp0s3.conf" + device_cfg = l.split ('=')[1] + if os.path.exists (device_cfg): + with open (device_cfg, 'r') as fd: + contents = fd.read() + exec (contents) ## !!! + ## example contents: + #DEVICE='enp0s3' + #PROTO='none' + #IPV4ADDR='192.168.2.11' + #IPV4BROADCAST='192.168.2.255' + #IPV4NETMASK='255.255.255.0' + #IPV4GATEWAY='192.168.2.1' + #IPV4DNS0='0.0.0.0' + #IPV4DNS1='0.0.0.0' + #HOSTNAME='pc1' + #DNSDOMAIN='' + #NISDOMAIN='' + #ROOTSERVER='10.0.2.15' + #ROOTPATH='' + #filename='' + #UPTIME='161' + #DHCPLEASETIME='0' + #DOMAINSEARCH='' + break +## /loadenviron.sh + +# Declaración de códigos de error. +OG_ERR_FORMAT=1 # Formato de ejecución incorrecto. +OG_ERR_NOTFOUND=2 # Fichero o dispositivo no encontrado. +OG_ERR_PARTITION=3 # Error en partición de disco. +OG_ERR_LOCKED=4 # Partición o fichero bloqueado. +OG_ERR_IMAGE=5 # Error al crear o restaurar una imagen. +OG_ERR_NOTOS=6 # Sin sistema operativo. +OG_ERR_NOTEXEC=7 # Programa o función no ejecutable. +# Códigos 8-13 reservados por ogAdmClient.h +OG_ERR_NOTWRITE=14 # No hay acceso de escritura +OG_ERR_NOTCACHE=15 # No hay particion cache en cliente +OG_ERR_CACHESIZE=16 # No hay espacio en la cache para almacenar fichero-imagen +OG_ERR_REDUCEFS=17 # Error al reducir sistema archivos +OG_ERR_EXTENDFS=18 # Error al expandir el sistema de archivos +OG_ERR_OUTOFLIMIT=19 # Valor fuera de rango o no válido. +OG_ERR_FILESYS=20 # Sistema de archivos desconocido o no se puede montar +OG_ERR_CACHE=21 # Error en partición de caché local +OG_ERR_NOGPT=22 # El disco indicado no contiene una particion GPT +OG_ERR_REPO=23 # Error al montar el repositorio de imagenes +OG_ERR_NOMSDOS=24 # El disco indicado no contienen una particion MSDOS + +OG_ERR_IMGSIZEPARTITION=30 # Error al restaurar partición más pequeña que la imagen +OG_ERR_UPDATECACHE=31 # Error al realizar el comando updateCache +OG_ERR_DONTFORMAT=32 # Error al formatear +OG_ERR_IMAGEFILE=33 # Archivo de imagen corrupto o de otra versión de $IMGPROG +OG_ERR_GENERIC=40 # Error imprevisto no definido +OG_ERR_UCASTSYNTAXT=50 # Error en la generación de sintaxis de transferenica UNICAST +OG_ERR_UCASTSENDPARTITION=51 # Error en envío UNICAST de partición +OG_ERR_UCASTSENDFILE=52 # Error en envío UNICAST de un fichero +OG_ERR_UCASTRECEIVERPARTITION=53 # Error en la recepcion UNICAST de una particion +OG_ERR_UCASTRECEIVERFILE=54 # Error en la recepcion UNICAST de un fichero +OG_ERR_MCASTSYNTAXT=55 # Error en la generacion de sintaxis de transferenica Multicast. +OG_ERR_MCASTSENDFILE=56 # Error en envio MULTICAST de un fichero +OG_ERR_MCASTRECEIVERFILE=57 # Error en la recepcion MULTICAST de un fichero +OG_ERR_MCASTSENDPARTITION=58 # Error en envio MULTICAST de una particion +OG_ERR_MCASTRECEIVERPARTITION=59 # Error en la recepcion MULTICAST de una particion +OG_ERR_PROTOCOLJOINMASTER=60 # Error en la conexion de una sesion UNICAST|MULTICAST con el MASTER + +OG_ERR_DONTMOUNT_IMAGE=70 # Error al montar una imagen sincronizada. +OG_ERR_DONTSYNC_IMAGE=71 # Imagen no sincronizable (es monolitica) +OG_ERR_DONTUNMOUNT_IMAGE=72 # Error al desmontar la imagen +OG_ERR_NOTDIFFERENT=73 # No se detectan diferencias entre la imagen basica y la particion. +OG_ERR_SYNCHRONIZING=74 # Error al sincronizar, puede afectar la creacion/restauracion de la imagen + +OG_ERR_NOTUEFI=80 # La interfaz UEFI no está activa +OG_ERR_NOTBIOS=81 # La interfaz BIOS legacy no está activa From fa788953c1fabdf4371b06f235b95b3ed9f7e4d9 Mon Sep 17 00:00:00 2001 From: Natalia Serrano Date: Tue, 25 Feb 2025 14:43:35 +0100 Subject: [PATCH 06/18] refs #1609 set PATH and PYTHONPATH up --- client/etc/preinit/loadenviron.sh | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/client/etc/preinit/loadenviron.sh b/client/etc/preinit/loadenviron.sh index bf3113d..0884404 100755 --- a/client/etc/preinit/loadenviron.sh +++ b/client/etc/preinit/loadenviron.sh @@ -25,20 +25,24 @@ if [ -d $OPENGNSYS ]; then export OGETC=$OPENGNSYS/etc export OGLIB=$OPENGNSYS/lib export OGAPI=$OGLIB/engine/bin + export OGPYFUNCS=$OPENGNSYS/functions export OGSCRIPTS=$OPENGNSYS/scripts export OGIMG=$OPENGNSYS/images export OGCAC=$OPENGNSYS/cache export OGLOG=$OPENGNSYS/log export PATH=$PATH:/sbin:/usr/sbin:/usr/local/sbin:/bin:/usr/bin:/usr/local/bin:/opt/oglive/rootfs/opt/drbl/sbin - - export PATH=$OGSCRIPTS:$PATH:$OGAPI:$OGBIN + export PATH=$OGSCRIPTS:$OGPYFUNCS:$PATH:$OGBIN + export PYTHONPATH=$OPENGNSYS/lib/python3 # Exportar parámetros del kernel. for i in $(cat /proc/cmdline); do echo $i | grep -q "=" && export $i done - + + # Cargar sysctls + sysctl -p &>/dev/null + # Cargar fichero de idioma. LANGFILE=$OGETC/lang.${LANG%@*}.conf if [ -f $LANGFILE ]; then From 4ebe08288ad8ac84792ad538542a3de73a14af82 Mon Sep 17 00:00:00 2001 From: Natalia Serrano Date: Tue, 25 Feb 2025 15:00:19 +0100 Subject: [PATCH 07/18] refs #1593 add UEFILib and python scripts --- client/interfaceAdm/Apagar.py | 10 + client/interfaceAdm/Configurar.py | 196 ++++++++++ client/interfaceAdm/Reiniciar.py | 8 + client/lib/python3/UEFILib.py | 579 +++++++++++++++++++++++++++++ client/lib/python3/lang_ca_ES.py | 400 ++++++++++++++++++++ client/lib/python3/lang_en_GB.py | 387 +++++++++++++++++++ client/lib/python3/lang_es_ES.py | 387 +++++++++++++++++++ client/scripts/configureOs.py | 159 ++++++++ client/scripts/deployImage.py | 182 +++++++++ client/scripts/initCache.py | 128 +++++++ client/scripts/listSoftwareInfo.py | 39 ++ client/scripts/restoreImage.py | 118 ++++++ client/scripts/updateBootCache.py | 62 +++ client/scripts/updateCache.py | 219 +++++++++++ 14 files changed, 2874 insertions(+) create mode 100755 client/interfaceAdm/Apagar.py create mode 100755 client/interfaceAdm/Configurar.py create mode 100755 client/interfaceAdm/Reiniciar.py create mode 100644 client/lib/python3/UEFILib.py create mode 100644 client/lib/python3/lang_ca_ES.py create mode 100644 client/lib/python3/lang_en_GB.py create mode 100644 client/lib/python3/lang_es_ES.py create mode 100755 client/scripts/configureOs.py create mode 100755 client/scripts/deployImage.py create mode 100755 client/scripts/initCache.py create mode 100755 client/scripts/listSoftwareInfo.py create mode 100644 client/scripts/restoreImage.py create mode 100755 client/scripts/updateBootCache.py create mode 100644 client/scripts/updateCache.py diff --git a/client/interfaceAdm/Apagar.py b/client/interfaceAdm/Apagar.py new file mode 100755 index 0000000..eafc875 --- /dev/null +++ b/client/interfaceAdm/Apagar.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python3 +import os +import sys + +def main(): + os.system('poweroff') + sys.exit(0) + +if __name__ == "__main__": + main() diff --git a/client/interfaceAdm/Configurar.py b/client/interfaceAdm/Configurar.py new file mode 100755 index 0000000..58bd9d8 --- /dev/null +++ b/client/interfaceAdm/Configurar.py @@ -0,0 +1,196 @@ +#!/usr/bin/python3 + +import os +import sys +import subprocess + +import ogGlobals +import SystemLib +import CacheLib +import FileSystemLib +import DiskLib + +#Load engine configurator from engine.cfg file. +#Carga el configurador del engine desde el fichero engine.cfg +## (ogGlobals se encarga) + +# Clear temporary file used as log track by httpdlog +# Limpia los ficheros temporales usados como log de seguimiento para httpdlog +open (ogGlobals.OGLOGSESSION, 'w').close() +open (ogGlobals.OGLOGCOMMAND, 'w').close() +open (ogGlobals.OGLOGCOMMAND+'.tmp', 'w').close() + +# Registro de inicio de ejecución +SystemLib.ogEcho (['log', 'session'], None, f'{ogGlobals.lang.MSG_INTERFACE_START} {sys.argv}') + +# Solo ejecutable por OpenGnsys Client. +#path = os.getenv('PATH') +#if path: +# os.environ['PATH'] = f"{path}:{os.path.dirname(__name__)}" +prog = os.path.basename(__name__) + +#____________________________________________________________________ +# +# El parámetro $2 es el que aporta toda la información y el $1 se queda obsoleto +# Formato de entrada: +# dis=Número de disco +# *=caracter de separación +# che=Vale 0 o 1 +# *=caracter de separación +# $tch=tamaño cache +# != caracter de separación +# +# Y un numero indeterminado de cadenas del tipo siguuenteseparadas por el caracter '$': +# par=Número de particion*cod=Código de partición*sfi=Sistema de ficheros*tam=Tamaño de la partición*ope=Operación +# @= caracter de separación +#____________________________________________________________________ + +# Captura de parámetros (se ignora el 1er parámetro y se eliminan espacios y tabuladores). +#param='dis=1*che=0*tch=70000000!par=1*cpt=NTFS*sfi=NTFS*tam=11000000*ope=0%' +#param = ''.join(sys.argv[2:]).replace(' ', '').replace('\t', '') +param = sys.argv[2] + +# Activa navegador para ver progreso +coproc = subprocess.Popen (['/opt/opengnsys/bin/browser', '-qws', 'http://localhost/cgi-bin/httpd-log.sh']) + +# Leer los dos bloques de parámetros, separados por '!'. +tbprm = param.split ('!') +pparam = tbprm[0] # General disk parameters +sparam = tbprm[1] # Partitioning and formatting parameters + +# Toma valores de disco y caché, separados por "*". +# Los valores están en las variables $dis: disco, $che: existe cache (1, 0), $tch: Tamaño de la cache. +tbprm = pparam.split ('*') +dis = ptt = tch = None +for item in tbprm: + if '=' not in item: continue + + k, v = item.split ('=', 1) + if k not in ['dis', 'tch', 'ptt']: ## 'ptt' added, unused 'che' removed + print (f'ignoring unknown disk parameter ({k})') + continue + + if 'dis' == k: dis = int (v) + elif 'ptt' == k: ptt = v + elif 'tch' == k: tch = v + +# Error si no se define el parámetro de disco (dis). +if dis is None: sys.exit (ogGlobals.OG_ERR_FORMAT) +if ptt is None: ptt = 'MSDOS' +if tch is None: tch = '0' + +# Toma valores de distribución de particiones, separados por "%". +tbp = [] # Valores de configuración (parámetros para ogCreatePartitions) +tbf = {} # Tabla de formateo + +tbprm = sparam.split('%') + +maxp=0 +for item in tbprm: + if not item: continue ## por si nos pasan un '%' al final de todo + # Leer datos de la partición, separados por "*". + par = cpt = sfi = tam = None + ope = 0 + for c in item.split ('*'): + if '=' not in c: continue + + k, v = c.split ('=', 1) + if k not in ['par', 'cpt', 'sfi', 'tam', 'ope']: + print (f'ignoring unknown partition parameter ({k})') + continue + + if 'par' == k: par = int (v) + elif 'cpt' == k: cpt = v + elif 'sfi' == k: sfi = v + elif 'tam' == k: tam = v + elif 'ope' == k: ope = int (v) + + missing_params = [] + if par is None: missing_params.append ('par') + if cpt is None: missing_params.append ('cpt') + if sfi is None: missing_params.append ('sfi') + if tam is None: missing_params.append ('tam') + if missing_params: + print (f'partition data ({item}) missing required parameters ({' '.join (missing_params)})') + sys.exit (1) + + # Componer datos de particionado. + if 'CACHE' != cpt: tbp.append (f'{cpt}:{tam}') + print (f'nati: par ({par}) ope ({ope})') + if ope: + print (f'nati: par ({par}) ope ({ope}): dentro del if') + # Si se activa operación de formatear, componer datos de formateo. + if cpt not in ['EMPTY', 'EXTENDED', 'LINUX-LVM', 'LVM', 'ZPOOL']: + tbf[par] = sfi + # Obtener la partición mayor. + if par > maxp: maxp = par + +#____________________________________________________ +# +# Proceso +#____________________________________________________ +print (f'nati: dis ({dis}) ptt ({ptt}) tch ({tch}) || tbp ({tbp}) tbf ({tbf})') +# Tamaño actual de la cache +CACHESIZE=CacheLib.ogGetCacheSize() + +# Desmonta todas las particiones y la caché + +SystemLib.ogEcho (['session', 'log'], None, f'[10] {ogGlobals.lang.MSG_HELP_ogUnmountAll}') +FileSystemLib.ogUnmountAll (dis) +CacheLib.ogUnmountCache() + +# Elimina la tabla de particiones +cur_ptt = DiskLib.ogGetPartitionTableType (dis) +if not cur_ptt or ptt != cur_ptt: + DiskLib.ogDeletePartitionTable (dis) + SystemLib.ogExecAndLog ('command', DiskLib.ogUpdatePartitionTable) + + # Crea tabla de particiones MSDOS (NOTA: adaptar para tablas GPT). + DiskLib.ogCreatePartitionTable (dis, ptt) + +# Inicia la cache. +print (f'nati: evaluating if CACHE in sparam ({sparam})') +if 'CACHE' in sparam: + print (f'nati: dentro del if') + SystemLib.ogEcho (['session', 'log'], None, f'[30] {ogGlobals.lang.MSG_HELP_ogCreateCache}') + SystemLib.ogEcho (['session', 'log'], None, f' initCache {tch}') + SystemLib.ogExecAndLog ('command', CacheLib.initCache, tch) + +# Definir particionado. +SystemLib.ogEcho (['session', 'log'], None, f'[50] {ogGlobals.lang.MSG_HELP_ogCreatePartitions}') +SystemLib.ogEcho (['session', 'log'], None, f' ogCreatePartitions {dis} {' '.join (tbp)}') +res = SystemLib.ogExecAndLog ('command', DiskLib.ogCreatePartitions, dis, tbp) +if not res: + coproc.kill() + SystemLib.ogRaiseError (['log', 'session'], ogGlobals.OG_ERR_GENERIC, f'ogCreatePartitions {dis} {' '.join (tbp)}') + sys.exit (1) +SystemLib.ogExecAndLog ('command', DiskLib.ogUpdatePartitionTable) + +# Formatear particiones +SystemLib.ogEcho (['session', 'log'], None, f'[70] {ogGlobals.lang.MSG_HELP_ogFormat}') +retval = 0 +for p in range (1, maxp+1): + print (f'nati: p ({p})') + if p not in tbf: continue + if 'CACHE' == tbf[p]: + if CACHESIZE == tch: # Si el tamaño es distinto ya se ha formateado. + SystemLib.ogEcho (['session', 'log'], None, ' ogFormatCache') + retval = SystemLib.ogExecAndLog ('command', CacheLib.ogFormatCache) + else: + print (f'nati: p ({p}) no es cache, la formateamos') + SystemLib.ogEcho (['session', 'log'], None, f' ogFormatFs {dis} {p} {tbf[p]}') + retval = SystemLib.ogExecAndLog ('command', FileSystemLib.ogFormatFs, dis, str(p), tbf[p]) + if retval: + coproc.kill() + SystemLib.ogRaiseError (['session', 'log'], ogGlobals.OG_ERR_GENERIC, f'ogFormatFs {dis} {p} {tbf[p]}') + sys.exit (1) +# Registro de fin de ejecución +SystemLib.ogEcho (['session', 'log'], None, f'{ogGlobals.lang.MSG_INTERFACE_END} {retval}') + +#___________________________________________________________________ +# +# Retorno +#___________________________________________________________________ + +coproc.kill() +sys.exit (0) diff --git a/client/interfaceAdm/Reiniciar.py b/client/interfaceAdm/Reiniciar.py new file mode 100755 index 0000000..f1b0e25 --- /dev/null +++ b/client/interfaceAdm/Reiniciar.py @@ -0,0 +1,8 @@ +#!/usr/bin/env python3 +import os + +def reboot_system(): + os.system('reboot') + +if __name__ == "__main__": + reboot_system() diff --git a/client/lib/python3/UEFILib.py b/client/lib/python3/UEFILib.py new file mode 100644 index 0000000..a70b5a7 --- /dev/null +++ b/client/lib/python3/UEFILib.py @@ -0,0 +1,579 @@ +import os.path +import re +import subprocess +import shutil + +import ogGlobals +import SystemLib +import FileSystemLib +import DiskLib +import FileLib +import InventoryLib + +#!/bin/bash +# Libreria provisional para uso de UEFI +# Las funciones se incluirán las librerías ya existentes + +#/** +# ogNvramActiveEntry +#@brief Activa entrada de la NVRAM identificada por la etiqueta o el orden +#@param Num_order_entry | Label_entry Número de orden o la etiqueta de la entrada a borrar. +#@return (nada) +#@exception OG_ERR_FORMAT formato incorrecto. +#@exception OG_ERR_NOTUEFI UEFI no activa. +#@exception OG_ERR_NOTFOUND fichero o dispositivo no encontrado. +#*/ ## +#ogNvramActiveEntry ('2') +#ogNvramActiveEntry ('Windows Boot Manager') +def ogNvramActiveEntry (entry): + if not InventoryLib.ogIsEfiActive(): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTUEFI, '') + return + + numentries = [] + efibootmgr_out = subprocess.run (['efibootmgr'], capture_output=True, text=True).stdout + try: + foo = int (entry, 16) ## raises ValueError if entry doesn't look like hexadecimal + num = f'{foo:04x}'.upper() + for l in efibootmgr_out.splitlines(): + words = l.split() + if len(words) < 2: continue + if num in words[0]: + numentries.append (words[0][4:8]) + except ValueError: + for l in efibootmgr_out.splitlines(): + words = l.split (maxsplit=1) + if len(words) < 2: continue + if words[1] == entry: + numentries.append (words[0][4:8]) + + if not numentries: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f'NVRAM entry "{entry}"') + return + + if 1 != len(numentries): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_GENERIC, f'more than one entry found') + return + + subprocess.run (['efibootmgr', '-a', '-b', numentries[0]], capture_output=True, text=True) + +#/** +# ogNvramAddEntry +#@brief Crea nueva entrada en el gestor de arranque (NVRAM), opcionalmente la incluye al final del orden de arranque. +#@param Str_Label_entry Número de disco o etiqueta de la entrada a crear. +#@param Str_BootLoader Número de partición o cargador de arranque. +#@param Bool_Incluir_Arranque Incluir en el orden de arranque (por defecto FALSE) (opcional) +#@return (nada) +#@exception OG_ERR_FORMAT formato incorrecto. +#@exception OG_ERR_NOTUEFI UEFI no activa. +#@exception OG_ERR_NOTFOUND fichero o dispositivo no encontrado. +#*/ ## +#ogNvramAddEntry ('1', '2', True) +#ogNvramAddEntry ('grub', '/EFI/grub/grubx64.efi', True) +#ogNvramAddEntry ('Windows', '/EFI/Microsoft/Boot/bootmgfw.efi') +def ogNvramAddEntry (bootlbl, bootldr, nvram_set=False): + if not InventoryLib.ogIsEfiActive(): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTUEFI, '') + return + + esp = DiskLib.ogGetEsp() + if not esp: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, 'ESP') + return + efidisk, efipar = esp.split() + + try: + foo = int(bootlbl) + int(bootldr) ## raises ValueError if bootlbl/bootldr don't look like numbers + bootlabel = f'Part-{int(bootlbl):02d}-{int(bootldr):02d}' + bootloader = f'/EFI/{bootlabel}/Boot/ogloader.efi' + except ValueError: + bootlabel = bootlbl + bootloader = bootldr + + ogNvramDeleteEntry (bootlabel) + + dev = DiskLib.ogDiskToDev (efidisk) + subprocess.run (['efibootmgr', '-C', '-d', dev, '-p', efipar, '-L', bootlabel, '-l', bootloader]) + + if nvram_set: + efibootmgr_out = subprocess.run (['efibootmgr'], capture_output=True, text=True).stdout + for l in efibootmgr_out.splitlines(): + words = l.split (maxsplit=1) + if len(words) < 2: continue + if words[1] == bootlabel: + numentry = words[0][4:8] + order = ogNvramGetOrder() + ogNvramSetOrder (order + [numentry]) + + + +#/** +# ogCopyEfiBootLoader int_ndisk str_repo path_image +#@brief Copia el cargador de arranque desde la partición EFI a la de sistema. +#@param int_ndisk nº de orden del disco +#@param int_part nº de partición +#@return (nada, por determinar) +#@exception OG_ERR_FORMAT formato incorrecto. +#@exception OG_ERR_NOTFOUND fichero o dispositivo no encontrado. +#@note Si existe el cargador en la partición de sistema no es válido +#*/ ## +def ogCopyEfiBootLoader (disk, par): + mntdir = FileSystemLib.ogMount (disk, par) + if not mntdir: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, f'{disk} {par}') + return None + + esp = DiskLib.ogGetEsp() + if not esp: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, 'EFI partition') + return + esp_disk, esp_par = esp.split() + efidir = FileSystemLib.ogMount (esp_disk, esp_par) + if not efidir: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, 'ESP') + return + + bootlabel = f'Part-{int(disk):02d}-{int(par):02d}' + osversion = InventoryLib.ogGetOsVersion (disk, par) + print (f'bootlabel ({bootlabel})') + print (f'osversion ({osversion})') + if 'Windows 1' in osversion: + loader = None + for i in f'{efidir}/EFI/Microsoft/Boot/bootmgfw.efi', f'{efidir}/EFI/{bootlabel}/Boot/bootmgfw.efi': + if os.path.exists (i): + loader = i + if not loader: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTOS, f'{disk} {par} ({osversion}, EFI)') + return None + + if (os.path.isdir (f'{mntdir}/ogBoot')): + shutil.rmtree (f'{mntdir}/ogBoot') + dirloader = os.path.realpath (os.path.dirname (loader) + '/..') + shutil.copytree (f'{dirloader}/Boot', f'{mntdir}/ogBoot') + + +#/** +# ogNvramDeleteEntry +#@brief Borra entrada de la NVRAM identificada por la etiqueta o el orden +#@param Num_order_entry | Label_entry Número de orden o la etiqueta de la entrada a borrar. +#@return (nada) +#@exception OG_ERR_FORMAT formato incorrecto. +#@exception OG_ERR_NOTUEFI UEFI no activa. +#@exception OG_ERR_NOTFOUND fichero o dispositivo no encontrado (entrada en NVRAM). +#*/ ## +#ogNvramDeleteEntry ('2') +#ogNvramDeleteEntry ('Windows Boot Manager') +def ogNvramDeleteEntry (entry): + if not InventoryLib.ogIsEfiActive(): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTUEFI, '') + return + + numentries = [] + efibootmgr_out = subprocess.run (['efibootmgr'], capture_output=True, text=True).stdout + try: + foo = int (entry, 16) ## raises ValueError if entry doesn't look like hexadecimal + num = f'{foo:04x}'.upper() + for l in efibootmgr_out.splitlines(): + words = l.split() + if len(words) < 2: continue + if num in words[0]: + numentries.append (words[0][4:8]) + except ValueError: + for l in efibootmgr_out.splitlines(): + words = l.split (maxsplit=1) + if len(words) < 2: continue + if words[1] == entry: + numentries.append (words[0][4:8]) + + if not numentries: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f'NVRAM entry "{entry}"') + return + + if 1 != len(numentries): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_GENERIC, f'more than one entry found') + return + + subprocess.run (['efibootmgr', '-B', '-b', numentries[0]], capture_output=True, text=True) + + + +#/** +# ogNvramGetCurrent +#@brief Muestra la entrada del gestor de arranque (NVRAM) que ha iniciado el equipo. +#@return Entrada con la que se ha iniciado el equipo +#@exception OG_ERR_NOTUEFI UEFI no activa. +#*/ ## +def ogNvramGetCurrent(): + if not InventoryLib.ogIsEfiActive(): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTUEFI, '') + return + + bootentry = '9999' + ret = None + efibootmgr_out = subprocess.run (['efibootmgr'], capture_output=True, text=True).stdout + for l in efibootmgr_out.splitlines(): + words = l.split (maxsplit=1) + if len(words) < 2: continue + if 'BootCurrent' in words[0]: + bootentry = words[1] + continue + if bootentry in words[0]: + num = words[0][4:8].strip ('0') or '0' + ret = f'{num} {words[1]}' + return ret + + +# ogNvramGetNext +#@brief Muestra la entrada del gestor de arranque (NVRAM) que se utilizará en el próximo arranque. +#@return Entrada que se utilizará en el próximo arranque +#@exception OG_ERR_NOTUEFI UEFI no activa. +#*/ ## +def ogNvramGetNext(): + if not InventoryLib.ogIsEfiActive(): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTUEFI, '') + return + + ret = None + efibootmgr_out = subprocess.run (['efibootmgr'], capture_output=True, text=True).stdout + for l in efibootmgr_out.splitlines(): + words = l.split (maxsplit=1) + if len(words) < 2: continue + if 'BootNext' in words[0]: + ret = words[1] + return ret + + +# ogNvramGetOrder +#@brief Muestra el orden de las entradas del gestor de arranque (NVRAM) +#@return Array, orden de las entradas +#@exception OG_ERR_NOTUEFI UEFI no activa. +#*/ ## +def ogNvramGetOrder(): + if not InventoryLib.ogIsEfiActive(): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTUEFI, '') + return + + ret = [] + efibootmgr_out = subprocess.run (['efibootmgr'], capture_output=True, text=True).stdout + for l in efibootmgr_out.splitlines(): + words = l.split() + if len(words) < 2: continue + if 'BootOrder:' == words[0]: + ret = words[1].split (',') + return ret + + +#/** +# ogNvramGetTimeout +#@brief Muestra el tiempo de espera del gestor de arranque (NVRAM) +#@return Timeout de la NVRAM +#@exception OG_ERR_NOTUEFI UEFI no activa. +#*/ ## +def ogNvramGetTimeout(): + if not InventoryLib.ogIsEfiActive(): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTUEFI, '') + return + + ret = None + efibootmgr_out = subprocess.run (['efibootmgr'], capture_output=True, text=True).stdout + for l in efibootmgr_out.splitlines(): + words = l.split (maxsplit=1) + if len(words) < 2: continue + if 'Timeout:' == words[0]: + ret = words[1] + return ret + + +#/** +# ogGrubUefiConf int_ndisk int_part str_dir_grub +#@brief Genera el fichero grub.cfg de la ESP +#@param int_ndisk nº de orden del disco +#@param int_part nº de partición +#@param str_dir_grub prefijo del directorio de grub en la partición de sistema. ej: /boot/grubPARTITION +#@return (nada, por determinar) +#@exception OG_ERR_FORMAT formato incorrecto. +#@exception OG_ERR_NOTFOUND fichero o dispositivo no encontrado. +#@TODO Confirmar si el fichero "$EFIDIR/EFI/$BOOTLABEL/grub.cfg" es necesario. +#*/ ## + + +#/** +# ogNvramInactiveEntry +#@brief Inactiva entrada de la NVRAM identificada por la etiqueta o el orden +#@param Num_order_entry | Label_entry Número de orden o la etiqueta de la entrada a borrar. +#@return (nada) +#@exception OG_ERR_FORMAT formato incorrecto. +#@exception OG_ERR_NOTFOUND fichero o dispositivo no encontrado. +#@exception OG_ERR_NOTUEFI UEFI no activa. +#*/ ## +#ogNvramInactiveEntry ('2') +#ogNvramInactiveEntry ('Windows Boot Manager') +def ogNvramInactiveEntry (entry): + if not InventoryLib.ogIsEfiActive(): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTUEFI, '') + return + + numentries = [] + efibootmgr_out = subprocess.run (['efibootmgr'], capture_output=True, text=True).stdout + try: + foo = int (entry, 16) ## raises ValueError if entry doesn't look like hexadecimal + num = f'{foo:04x}'.upper() + for l in efibootmgr_out.splitlines(): + words = l.split() + if len(words) < 2: continue + if num in words[0]: + numentries.append (words[0][4:8]) + except ValueError: + for l in efibootmgr_out.splitlines(): + words = l.split (maxsplit=1) + if len(words) < 2: continue + if words[1] == entry: + numentries.append (words[0][4:8]) + + if not numentries: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f'NVRAM entry "{entry}"') + return + + if 1 != len(numentries): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_GENERIC, f'more than one entry found') + return + + subprocess.run (['efibootmgr', '-A', '-b', numentries[0]], capture_output=True, text=True) + + +#/** +# ogNvramList +#@brief Lista las entradas de la NVRAN (sólo equipos UEFI) +#@return Multiline string: entradas de la NVRAM con el formato: orden etiqueta [* (si está activa) ] +#@exception OG_ERR_NOTUEFI UEFI no activa. +#*/ ## +def ogNvramList(): + if not InventoryLib.ogIsEfiActive(): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTUEFI, '') + return + + ret = '' + efibootmgr_out = subprocess.run (['efibootmgr'], capture_output=True, text=True).stdout + for l in efibootmgr_out.splitlines(): + words = l.split (maxsplit=1) + if len(words) < 2: continue + if re.search ('Boot[0-9]', words[0]): + active = '*' if '*' in words[0] else '' + num = words[0][4:8].strip ('0') or '0' + ret += '{:>4s} {} {}\n'.format (num, words[1], active) + return ret + + +#/** +# ogNvramPxeFirstEntry +#@brief Sitúa la entrada de la tarjeta de red en el primer lugar en la NVRAM. +#@return (nada) +#@exception OG_ERR_NOTUEFI UEFI no activa. +#*/ ## +def ogNvramPxeFirstEntry(): + if not InventoryLib.ogIsEfiActive(): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTUEFI, '') + return + + num = '' + efibootmgr_out = subprocess.run (['efibootmgr'], capture_output=True, text=True).stdout + for l in efibootmgr_out.splitlines(): + if re.search ('IP[vV]{0,1}4', l): + num = l[4:8].strip ('0') or '0' + numentry = f'{int(num):04x}'.upper() + + o = ogNvramGetOrder() + if not o: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_GENERIC, f'ogNvramGetOrder returned an empty list') + return + + # Si la entrada es la primera nos salimos. + if numentry == o[0]: + return True + + # Si la entrada ya existe la borramos. + order = [numentry] + list (filter (lambda x: x if x!=numentry else [], o)) + ogNvramSetOrder (order) + + return True + + +#/** +# ogRestoreEfiBootLoader int_ndisk str_repo +#@brief Copia el cargador de arranque de la partición de sistema a la partición EFI. +#@param int_ndisk nº de orden del disco +#@param int_part nº de partición +#@return (nada, por determinar) +#@exception OG_ERR_FORMAT formato incorrecto. +#@exception OG_ERR_NOTFOUND fichero o dispositivo no encontrado (partición de sistema o EFI). +#@exception OG_ERR_NOTOS sin sistema operativo. +#*/ ## +def ogRestoreEfiBootLoader (disk, par): + mntdir = FileSystemLib.ogMount (disk, par) + if not mntdir: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, f'{disk} {par}') + return + + esp = DiskLib.ogGetEsp() + if not esp: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, 'EFI partition') + return + esp_disk, esp_par = esp.split() + efidir = FileSystemLib.ogMount (esp_disk, esp_par) + if not efidir: + FileSystemLib.ogFormat (esp_disk, esp_par, 'FAT32') + efidir = FileSystemLib.ogMount (esp_disk, esp_par) + if not efidir: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, 'ESP') + return + + osversion = InventoryLib.ogGetOsVersion (disk, par) + if 'Windows 1' in osversion: + bootlabel = f'Part-{int(disk):02d}-{int(par):02d}' + loader = FileLib.ogGetPath (file=f'{mntdir}/ogBoot/bootmgfw.efi') + if not loader: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTOS, f'{disk} {par} ({osversion}, EFI)') + return + efi_bl = f'{efidir}/EFI/{bootlabel}' + if os.path.exists (efi_bl): + shutil.rmtree (efi_bl) + os.makedirs (efi_bl, exist_ok=True) + shutil.copytree (os.path.dirname (loader), f'{efi_bl}/Boot', symlinks=True) + shutil.copy (loader, f'{efi_bl}/Boot/ogloader.efi') + if '' != FileLib.ogGetPath (file=f'{efidir}/EFI/Microsoft'): + os.rename (f'{efidir}/EFI/Microsoft', f'{efidir}/EFI/Microsoft.backup.og') + + return + + +#/** +# ogRestoreUuidPartitions +#@brief Restaura los uuid de las particiones y la tabla de particiones +#@param int_ndisk nº de orden del disco +#@param int_nfilesys nº de orden del sistema de archivos +#@param REPO|CACHE repositorio +#@param str_imgname nombre de la imagen +#@return (nada) +#@exception OG_ERR_FORMAT Formato incorrecto. +#@exception OG_ERR_NOTFOUND No encontrado fichero de información de la imagen (con uuid) +#*/ ## + + +#/** +# ogNvramSetNext +#@brief Configura el próximo arranque con la entrada del gestor de arranque (NVRAM) identificada por la etiqueta o el orden. +#@param Num_order_entry | Label_entry Número de orden o la etiqueta de la entrada a borrar. +#@return (nada) +#@exception OG_ERR_FORMAT formato incorrecto. +#@exception OG_ERR_NOTUEFI UEFI no activa. +#@exception OG_ERR_NOTFOUND fichero o dispositivo no encontrado. +#*/ ## +#ogNvramSetNext ('2') +#ogNvramSetNext ('Windows Boot Manager') +def ogNvramSetNext (entry): + if not InventoryLib.ogIsEfiActive(): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTUEFI, '') + return + + numentries = [] + efibootmgr_out = subprocess.run (['efibootmgr'], capture_output=True, text=True).stdout + try: + foo = int (entry, 16) ## raises ValueError if entry doesn't look like hexadecimal + num = f'{foo:04x}'.upper() + for l in efibootmgr_out.splitlines(): + words = l.split() + if len(words) < 2: continue + if num in words[0]: + numentries.append (words[0][4:8]) + except ValueError: + for l in efibootmgr_out.splitlines(): + words = l.split (maxsplit=1) + if len(words) < 2: continue + if words[1] == entry: + numentries.append (words[0][4:8]) + + if not numentries: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f'NVRAM entry "{entry}"') + return + + if 1 != len(numentries): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_GENERIC, f'more than one entry found') + return + + subprocess.run (['efibootmgr', '-n', numentries[0]], capture_output=True, text=True) + +#/** +# ogNvramSetOrder +#@brief Configura el orden de las entradas de la NVRAM +#@param Orden de las entradas separadas por espacios +#@return (nada) +#@exception OG_ERR_FORMAT formato incorrecto. +#@exception OG_ERR_NOTUEFI UEFI no activa. +#@exception OG_ERR_NOTFOUND fichero o dispositivo no encontrado (entrada NVRAM). +#*/ ## +#ogNvramSetOrder (['1', '3']) +def ogNvramSetOrder (order): + if not InventoryLib.ogIsEfiActive(): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTUEFI, '') + return + + try: + for i in order: + foo = int (i, 16) + except ValueError: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, 'ogNvramSetOrder ([array or hex values])') + return + + # Entradas de la NVRAM actuales + numentries = [] + efibootmgr_out = subprocess.run (['efibootmgr'], capture_output=True, text=True).stdout + for l in efibootmgr_out.splitlines(): + words = l.split() + if len(words) < 2: continue + if re.search ('Boot[0-9a-fA-F]{4}', words[0]): + numentries.append ('0' + words[0][4:8]) + + new_order = [] + for o in order: + h = f'{int(o,16):05x}'.upper() + if h not in numentries: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f'NVRAM entry order "{h}"') + return + new_order.append (h) + + subprocess.run (['efibootmgr', '-o', ','.join (new_order)]) + + +#/** +# ogNvramSetTimeout +#@brief Configura el tiempo de espera de la NVRAM +#@param Orden de las entradas separadas por espacios +#@return (nada) + +#@exception OG_ERR_FORMAT formato incorrecto. +#@exception OG_ERR_NOTFOUND fichero o dispositivo no encontrado. +#*/ ## +#ogNvramSetTimeout ('2') +def ogNvramSetTimeout (t): + if not InventoryLib.ogIsEfiActive(): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTUEFI, '') + return + + try: + num = int (t) ## raises ValueError if t doesn't look like a number + except ValueError: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, 'ogNvramSetTimeout (timeout)') + return + + subprocess.run (['efibootmgr', '-t', t]) + + +#/** +# ogUuidChange int_ndisk str_repo +#@brief Reemplaza el UUID de un sistema de ficheros. +#@param int_ndisk nº de orden del disco +#@param int_part nº de partición +#@return (nada, por determinar) +#@exception OG_ERR_FORMAT formato incorrecto. +#@exception OG_ERR_NOTFOUND fichero o dispositivo no encontrado. +#*/ ## diff --git a/client/lib/python3/lang_ca_ES.py b/client/lib/python3/lang_ca_ES.py new file mode 100644 index 0000000..81e48ca --- /dev/null +++ b/client/lib/python3/lang_ca_ES.py @@ -0,0 +1,400 @@ +#!/usr/bin/python3 + +# Fichero de idioma: catalá. +#@version 1.1.1 +#@author + + +# Mensajes de error. +MSG_ERR_GENERIC="Error imprevisto no definido" +MSG_ERR_FORMAT="Formato de ejecución incorrecto" +MSG_ERR_OUTOFLIMIT="Valor fuera de rango o no válido" +MSG_ERR_NOTFOUND="Fichero o dispositivo no encontrado" +MSG_ERR_PARTITION="Partición errónea o desconocida" +MSG_ERR_LOCKED="Recurso bloqueado por operación de uso exclusivo" +MSG_ERR_CACHE="Error en partición de caché local" +MSG_ERR_NOGPT="El disco indicado no contiene una particion GPT" +MSG_ERR_REPO="Error al montar el repositorio de imágenes" +MSG_ERR_NOMSDOS="El disco indicado no contiene una partición MSDOS" +MSG_ERR_FILESYS="Sistema de archivos desconocido o no se puede montar" +MSG_ERR_NOTOS="Sistema operativo no detectado o no se puede iniciar" +MSG_ERR_IMAGE="No se puede crear o restaurar una image de sistema" +MSG_ERR_IMAGEFILE="Archivo de imagen corrupto o de otra versión de partclone" +MSG_ERR_NOTEXEC="Programa o función no ejecutable" +MSG_ERR_NOTWRITE="No hay acceso de escritura" +MSG_ERR_NOTCACHE="No existe particion Cache en el cliente" +MSG_ERR_NOTUEFI="La interfaz UEFI no está activa" +MSG_ERR_NOTBIOS="La interfaz BIOS Legacy no está activa" +MSG_ERR_CACHESIZE="El espacio de la cache local o remota no es suficiente" +MSG_ERR_REDUCEFS="Error al reducir el sistema de archivos" +MSG_ERR_EXTENDFS="Error al expandir el sistema de archivos" +MSG_ERR_IMGSIZEPARTITION="Error al restaurar: Particion mas pequeña que la imagen" +MSG_ERR_UPDATECACHE="Error al realizar el comando updateCache" +MSG_ERR_UCASTSYNTAXT="Error en la generación de sintaxis de transferenica unicast" +MSG_ERR_UCASTSENDPARTITION="Error en envio UNICAST de una particion" +MSG_ERR_UCASTSENDFILE="Error en envio UNICAST de un fichero" +MSG_ERR_UCASTRECEIVERPARTITION="Error en la recepcion UNICAST de una particion" +MSG_ERR_UCASTRECEIVERFILE="Error en la recepcion UNICAST de un fichero" +MSG_ERR_MCASTSYNTAXT="Error en la generación de sintaxis de transferenica multicast" +MSG_ERR_MCASTSENDFILE="Error en envio MULTICAST de un fichero" +MSG_ERR_MCASTRECEIVERFILE="Error en la recepcion MULTICAST de un fichero" +MSG_ERR_MCASTSENDPARTITION="Error en envio MULTICAST de una particion" +MSG_ERR_MCASTRECEIVERPARTITION="Error en la recepcion MULTICAST de un fichero" +MSG_ERR_PROTOCOLJOINMASTER="Error en la conexion de una sesion UNICAST|MULTICAST con el MASTER" +MSG_ERR_DONTFORMAT="Error al formatear" +MSG_ERR_DONTMOUNT_IMAGE="Error al montar/reducir la imagen" +MSG_ERR_DONTUNMOUNT_IMAGE="Error al desmontar la imagen" +MSG_ERR_DONTSYNC_IMAGE="Imagen no sincronizable" +MSG_ERR_NOTDIFFERENT="No se detectan diferencias entre la imagen basica y la particion." +MSG_ERR_SYNCHRONIZING="Error al sincronizar, puede afectar la creacion|restauracion de la imagen" + +# Mensajes de avisos. +MSG_DONTUSE="NO USAR" +MSG_DONTMOUNT="Sistema de archivos no montado" +MSG_DONTUNMOUNT="El sistema de archivos no se puede desmontar o no está montado" +MSG_MOUNT="Sistema de archivos montado" +MSG_MOUNTREADONLY="Sistema de archivos montado solo de lectura" +MSG_OBSOLETE="EN DESUSO" + +# Mensajes complementarios para las ayudas. +MSG_64BIT="64 bits" +MSG_DISK="disc" +MSG_ERROR="Error" +MSG_EXAMPLE="Exemple" +MSG_FORMAT="Format" +MSG_FUNCTION="Funció" +MSG_HARDWAREINVENTORY="Inventario de maquinari de la màquina" +MSG_IMAGE="imatge" +MSG_INSTALLED="instal-lat" +MSG_NOCACHE="sense caché local" +MSG_NOEXTENDED="sense partició estensa" +MSG_PARTITION="partició" +MSG_PROTOCOL="protocol" +MSG_RESERVEDVALUE="Valor reservat" +MSG_SEE="Veure" +MSG_UNKNOWN="Desconegut" +MSG_WARNING="Avís" + +# Mensajes del proceso de arranque. +MSG_DETECTLVMRAID="Detectar metadispositivos LVM y RAID." +MSG_ERRBOOTMODE=f"{MSG_ERROR}: Modo de arranque desconocido." +MSG_LAUNCHCLIENT="Ejecutar cliente." +MSG_LOADAPI="Cargar funciones del motor de clonación." +MSG_LOADMODULES="Cargar módulos del kernel." +MSG_MAKELINKS="Crear enlaces simbólicos." +MSG_MOUNTREPO="Montar repositorio por %s en modo %s." +MSG_OFFLINEMODE="Modo de arranque sin conexión." +MSG_OTHERSERVICES="Iniciar servicios complementarios del cliente." +MSG_POWEROFFCONF="Definir parámetros de ahorro de energía." + +# Mensajes del menú por defecto. +MSG_BOOT="Iniciar" +MSG_DUPLEX="Dúplex" +MSG_HOSTNAME="Equipo" +MSG_IPADDR="Dirección IP" +MSG_MACADDR="Dirección MAC" +MSG_MENUTITLE="Menú de opciones" +MSG_POWEROFF="Apagar el equipo" +MSG_SPEED="Velocidad" + +# Mensajes de descripción breve de las funciones de la API. +MSG_HELP_ogAclFilter="Extrae las acl de los ficheros de la diferencial" +MSG_HELP_ogAddCmd="Añade comandos al fichero creado por la función ogInstalMiniSetup." +MSG_HELP_ogAddRegistryKey="Añade una nueva clave al registro de Windows." +MSG_HELP_ogAddRegistryValue="Añade un nuevo valor al registro de Windows." +MSG_HELP_ogAddToLaunchDaemon="" +MSG_HELP_ogBoot="Arranca un sistema operativo instalado." +MSG_HELP_ogBootLoaderDeleteEntry=MSG_DONTUSE +MSG_HELP_ogBootLoaderHidePartitions=MSG_DONTUSE +MSG_HELP_ogBootMbrGeneric="" +MSG_HELP_ogBootMbrXP="" +MSG_HELP_ogBurgDefaultEntry="Configura la entrada por defecto de Burg." +MSG_HELP_ogBurgDeleteEntry="Borra en el Burg del MBR las entradas para el inicio en una particion." +MSG_HELP_ogBurgHidePartitions="Configura el Burg del MBR para que oculte las particiones de windows que no se esten iniciando. Permite definir una partición que no se ocultará (ej: para datos)." +MSG_HELP_ogBurgInstallMbr="Instal·la el carregador d'arrencada BURG al MBR del primer disc dur" +MSG_HELP_ogBurgOgliveDefaultEntry="Configura la entrada de ogLive como la entrada por defecto de Burg." +MSG_HELP_ogCalculateChecksum="Calcula la suma de comprobación (checksum) de un fichero." +MSG_HELP_ogCalculateFullChecksum="" +MSG_HELP_ogChangeRepo="Cambia el repositorio para el recurso remoto images." +MSG_HELP_ogCheckFs="Comprueba la consistencia de un sistema de archivos." +MSG_HELP_ogCheckIpAddress="" +MSG_HELP_ogCheckProgram="" +MSG_HELP_ogCheckStringInGroup="" +MSG_HELP_ogCheckStringInReg="" +MSG_HELP_ogCheckSyncImage="Muestra el contenido de la imagen para comprobarla." +MSG_HELP_ogCleanLinuxDevices="" +MSG_HELP_ogCleanOs="Elimina los archivos que no son necesarios en el sistema operativo." +MSG_HELP_ogCompareChecksumFiles="Compara si coinciden las sumas de comprobación almacenadas de 2 ficheros." +MSG_HELP_ogConfigureFstab="" +MSG_HELP_ogConfigureOgagent="Configura el nuevo agente OGAgent para sistemas ooperativos." +MSG_HELP_ogCopyFile="Copia un fichero a otro almacenamiento." +MSG_HELP_ogCreateBootLoaderImage="" +MSG_HELP_ogCreateCache="Reserva espacio para la partición de caché al final del disco." +MSG_HELP_ogCreateDiskImage="Genera una imagen exacta de un disco completo." +MSG_HELP_ogCreateFileImage="Crea/Redimensiona el archivo de la imagen sincronizada" +MSG_HELP_ogCreateGptPartitions="" +MSG_HELP_ogCreateImage="Genera una imagen exacta de un sistema operativo instalado localmente." +MSG_HELP_ogCreateImageSyntax="" +MSG_HELP_ogCreateInfoImage="Crea informacion del contenido de la imagen" +MSG_HELP_ogCreateMbrImage="Genera una imagen del sector de arranque (MBR)." +MSG_HELP_ogCreatePartitions="Define la estructura de particiones de un disco." +MSG_HELP_ogCreatePartitionTable="Genera una tabla de particiones en caso de que no sea valida." +MSG_HELP_ogCreateTorrent="" +MSG_HELP_ogCopyEfiBootLoader="Copia el cargador de arranque desde la partición EFI a la de sistema." +MSG_HELP_ogDeleteCache="Elimina la partición de caché local." +MSG_HELP_ogDeleteFile="Borra un fichero de un espacio de almacenamiento." +MSG_HELP_ogDeletePartitionTable="Elimina la tabla de particiones del disco" +MSG_HELP_ogDeleteRegistryKey="Borra una clave vacía del registro de Windows." +MSG_HELP_ogDeleteRegistryValue="Borra un valor del registro de Windows." +MSG_HELP_ogDeleteTree="Borra un árbol de directorios de un espacio de almacenamiento." +MSG_HELP_ogDevToDisk="Devuelve el nº de orden de disco o de partición correspondiente al camino del fichero de dispositivo." +MSG_HELP_ogDiskToDev="Devuelve el camino del fichero de dispositivo correspondiente al nº de orden de disco o de partición." +MSG_HELP_ogDomainScript="" +MSG_HELP_ogEcho="" +MSG_HELP_ogExecAndLog="" +MSG_HELP_ogExtendFs="Extiende el tamaño de un sistema de archivo al máximo de su partición." +MSG_HELP_ogFindCache="Indica la partición reservada para caché local." +MSG_HELP_ogFixBootSector="" +MSG_HELP_ogFormatCache="Formatea (inicia) el sistema de caché local." +MSG_HELP_ogFormat="Formatea o reformatea un sistema de archivos." +MSG_HELP_ogFormatFs=MSG_HELP_ogFormat +MSG_HELP_ogGetArch="Devuelve el tipo de arquitectura del cliente." +MSG_HELP_ogGetCacheSize="Devuelve el tamaño de la partición de caché local." +MSG_HELP_ogGetCacheSpace="Devuelve el espacio máximo disponible que puede ser reservado para la partición de caché local." +MSG_HELP_ogGetCaller="" +MSG_HELP_ogGetDiskSize="Devuelve el tamaño del disco." +MSG_HELP_ogGetDiskType="Devuelve el mnemónico de tipo de disco." +MSG_HELP_ogGetFreeSize="" +MSG_HELP_ogGetFsSize="Devuelve el tamaño de un sistema de archivos." +MSG_HELP_ogGetFsType="Devuelve el mnemónico de tipo de sistema de archivos." +MSG_HELP_ogGetGroupDir="Devuelve el camino del directorio por defecto para el grupo del cliente." +MSG_HELP_ogGetGroupName="Devuelve el nombre del grupo al que pertenece el cliente." +MSG_HELP_ogGetHivePath="Devuelve el camino completo del fichero de una sección del registro de Windows." +MSG_HELP_ogGetHostname="Devuelve el nombre de la máquina local." +MSG_HELP_ogGetImageCompressor="Devuelve la herramienta de compresión de la imagen." +MSG_HELP_ogGetImageInfo="Muestra información sobre la imagen monolitica: clonacion:compresor:sistemaarchivos:tamañoKB." +MSG_HELP_ogGetImageProgram="Devuelve el programa usado para crear la imagen." +MSG_HELP_ogGetImageSize="Devuelve el tamaño de una imagen de sistema." +MSG_HELP_ogGetImageType="Devuelve el sistema de ficheros de la imagen." +MSG_HELP_ogGetIpAddress="Devuelve la dirección IP del cliente." +MSG_HELP_ogGetLastSector="Devuelve el último sector usable del disco o de una partición." +MSG_HELP_ogGetMacAddress="Devuelve la dirección Ethernet del cliente." +MSG_HELP_ogGetMountImageDir="Devuelve el directorio de montaje de una imagen." +MSG_HELP_ogGetMountPoint="Devuelve el directorio donde está montado un sistema de archivos local." +MSG_HELP_ogGetNetInterface="" +MSG_HELP_ogGetOsType="Devuelve el tipo de un sistema operativo instalado." +MSG_HELP_ogGetOsUuid="" +MSG_HELP_ogGetOsVersion="Devuelve el tipo y la versión de un sistema operativo instalado." +MSG_HELP_ogGetParentPath="Devuelve el camino completo del directorio padre de un fichero de sistema OpenGnsys." +MSG_HELP_ogGetPartitionActive="Indica cual es la partición marcada como activa en un disco." +MSG_HELP_ogGetPartitionId="Devuelve el identificador de tipo de una partición." +MSG_HELP_ogGetPartitionSize="Devuelve el tamaño de una partición." +MSG_HELP_ogGetPartitionsNumber="" +MSG_HELP_ogGetPartitionTableType="Devuelve el tipo de tabla de particiones del disco" +MSG_HELP_ogGetPartitionType="Devuelve el mnemónico de tipo de una partición." +MSG_HELP_ogGetPath="Devuelve el camino completo de un fichero de sistema OpenGnsys." +MSG_HELP_ogGetRegistryValue="Devuelve el dato de un valor del registro de Windows." +MSG_HELP_ogGetRepoIp="Devuelve la dirección IP del repositorio de datos." +MSG_HELP_ogGetSerialNumber="Devuelve el número de serie del cliente." +MSG_HELP_ogGetServerIp="Devuelve la dirección IP del servidor principal." +MSG_HELP_ogGetSizeParameters="Devuelve el tamaño de los datos de un sistema de ficheros, el espacio necesario para la imagen y si cabe en el repositorio elegido." +MSG_HELP_ogGetWindowsName="Devuelve el nombre del cliente guardado en el registro de Windows." +MSG_HELP_ogGrubAddOgLive="Incluye en el grub del MBR una entrada llamando al cliente de opengnsys." +MSG_HELP_ogGrubDefaultEntry="Configura la entrada por defecto de GRUB." +MSG_HELP_ogGrubDeleteEntry="Borra en el grub del MBR las entradas para el inicio en una particion." +MSG_HELP_ogGrubHidePartitions="Configura el grub del MBR para que oculte las particiones de windows que no se esten iniciando. Permite definir una partición que no se ocultará (ej: para datos)." +MSG_HELP_ogGrubInstallMbr="Instal·la el carregador d'arrencada GRUB al MBR del primer disc dur" +MSG_HELP_ogGrubInstallPartition="Instal·la el carregador d'arrencada BURG al BootSector" +MSG_HELP_ogGrubOgliveDefaultEntry="Configura la entrada de ogLive como la entrada por defecto de GRUB." +MSG_HELP_ogGrubSecurity="Configura usuario y clave para modificar las entradas del menu del Grub." +MSG_HELP_ogGrubUefiConf="Genera el fichero grub.cfg de la partición EFI." +MSG_HELP_ogHelp="Muestra mensajes de ayudas para las funciones." +MSG_HELP_ogHidePartition="Oculta una partición de Windows." +MSG_HELP_ogIdToType="Devuelve el mnemónico asociado al identificador de tipo de partición." +MSG_HELP_ogNvramActiveEntry="Configura a activa entrada del gestor de arranque (NVRAM)." +MSG_HELP_ogNvramAddEntry="Crea nueva entrada del gestor de arranque (NVRAM)." +MSG_HELP_ogNvramDeleteEntry="Borra entrada del gestor de arranque (NVRAM)." +MSG_HELP_ogNvramGetCurrent="Muestra la entrada del gestor de arranque (NVRAM) que ha iniciado el equipo." +MSG_HELP_ogNvramGetNext="Muestra la entrada del gestor de arranque (NVRAM) que se utilizará en el próximo arranque." +MSG_HELP_ogNvramGetOrder="Muestra el orden de las entradas del gestor de arranque (NVRAM)." +MSG_HELP_ogNvramGetTimeout="Muestra el tiempo de espera del gestor de arranque (NVRAM)." +MSG_HELP_ogNvramInactiveEntry="Configura a inactiva entrada del gestor de arranque (NVRAM)." +MSG_HELP_ogNvramList="Lista las entradas del gestor de arranque (NVRAN) marcando con un asterisco las activas" +MSG_HELP_ogNvramSetNext="Configura el próximo arranque con la entrada del gestor de arranque (NVRAM)." +MSG_HELP_ogNvramSetOrder="Configura el orden de las entradas del gestor de arranque (NVRAM)." +MSG_HELP_ogNvramSetTimeout="Configura el tiempo de espera del gestor de arranque (NVRAM)." +MSG_HELP_ogGetOsType="Devuelve el tipo de un sistema operativo instalado." +MSG_HELP_ogInstallFirstBoot="Crea un archivo que se ejecutará en el primer arranque de Windows." +MSG_HELP_ogInstallLaunchDaemon="Instala un archivo que se ejecutará en el arranque de macOS." +MSG_HELP_ogInstallLinuxClient=MSG_OBSOLETE +MSG_HELP_ogInstallMiniSetup="Instala un archivo que se ejecutará en el arranque de Windows." +MSG_HELP_ogInstallRunonce="Crea un archivo que se ejecutará en el inicio de un usuario administrador de Windows." +MSG_HELP_ogInstallWindowsClient=MSG_OBSOLETE +MSG_HELP_ogIsFormated="Comprueba si un sistema de archivos está formateado." +MSG_HELP_ogIsImageLocked="Comprueba si una imagen está bloqueada por una operación de uso exclusivo." +MSG_HELP_ogIsLocked="Comprueba si una partición o su disco están bloqueados por una operación de uso exclusivo." +MSG_HELP_ogIsDiskLocked="Comprueba si un disco está bloqueado por una operación de uso exclusivo." +MSG_HELP_ogIsMounted="Comprueba si un sistema de archivos está montado." +MSG_HELP_ogIsNewerFile="Comprueba si un fichero es más nuevo (se ha modificado después) que otro." +MSG_HELP_ogIsPartitionLocked=MSG_HELP_ogIsLocked +MSG_HELP_ogIsRepoLocked="" +MSG_HELP_ogIsSyncImage="Comprueba si la imagen es sincronizable." +MSG_HELP_ogIsVirtualMachine="" +MSG_HELP_ogIsWritable="Comprueba si un sistema de archivos está montado con permiso de escritura." +MSG_HELP_ogLinuxBootParameters="Devuelve los parámetros de arranque de un sistema operativo Linux instalado." +MSG_HELP_ogListHardwareInfo="Lista el inventario de dispositivos del cliente." +MSG_HELP_ogListLogicalPartitions="" +MSG_HELP_ogListPartitions="Lista la estructura de particiones de un disco." +MSG_HELP_ogListPrimaryPartitions="" +MSG_HELP_ogListRegistryKeys="Lista los nombres de las subclaves incluidas en una clave del registro de Windows." +MSG_HELP_ogListRegistryValues="Lista los nombres de los valores incluidos en una clave del registro de Windows." +MSG_HELP_ogListSoftware="Lista el inventario de programas instalados en un sistema operativo." +MSG_HELP_ogLock="Bloquea una partición para operación de uso exclusivo." +MSG_HELP_ogLockDisk="Bloquea un disco para operación de uso exclusivo." +MSG_HELP_ogLockImage="Bloquea una imagen para operación de uso exclusivo." +MSG_HELP_ogLockPartition=MSG_HELP_ogLock +MSG_HELP_ogMakeChecksumFile="Almacena la suma de comprobación de un fichero." +MSG_HELP_ogMakeDir="Crea un directorio para OpenGnsys." +MSG_HELP_ogMakeGroupDir="Crea el directorio de grupo (aula) en un repositorio." +MSG_HELP_ogMcastReceiverFile="" +MSG_HELP_ogMcastReceiverPartition="" +MSG_HELP_ogMcastRequest="" +MSG_HELP_ogMcastSendFile="" +MSG_HELP_ogMcastSendPartition="" +MSG_HELP_ogMcastSyntax="" +MSG_HELP_ogMountCache="Monta el sistema de archivos dedicado a caché local." +MSG_HELP_ogMountCdrom="Monta dispositivo óptico por defecto." +MSG_HELP_ogMountImage="Monta una imagen sincronizable" +MSG_HELP_ogMount="Monta un sistema de archivos y devuelve el punto de montaje." +MSG_HELP_ogMountFs=MSG_HELP_ogMount +MSG_HELP_ogNvramActiveEntry="Configura a activa entrada del gestor de arranque (NVRAM)." +MSG_HELP_ogNvramAddEntry="Crea nueva entrada del gestor de arranque (NVRAM)." +MSG_HELP_ogNvramDeleteEntry="Borra entrada del gestor de arranque (NVRAM)." +MSG_HELP_ogNvramGetCurrent="Muestra la entrada del gestor de arranque (NVRAM) que ha iniciado el equipo." +MSG_HELP_ogNvramGetNext="Muestra la entrada del gestor de arranque (NVRAM) que se utilizará en el próximo arranque." +MSG_HELP_ogNvramGetOrder="Muestra el orden de las entradas del gestor de arranque (NVRAM)." +MSG_HELP_ogNvramGetTimeout="Muestra el tiempo de espera del gestor de arranque (NVRAM)." +MSG_HELP_ogNvramInactiveEntry="Configura a inactiva entrada del gestor de arranque (NVRAM)." +MSG_HELP_ogNvramList="Lista las entradas del gestor de arranque (NVRAN) marcando con un asterisco las activas" +MSG_HELP_ogNvramPxeFirstEntry="Configura la tarjeta de red como primer arranque en la NVRAM." +MSG_HELP_ogNvramSetNext="Configura el próximo arranque con la entrada del gestor de arranque (NVRAM)." +MSG_HELP_ogNvramSetOrder="Configura el orden de las entradas del gestor de arranque (NVRAM)." +MSG_HELP_ogNvramSetTimeout="Configura el tiempo de espera del gestor de arranque (NVRAM)." +MSG_HELP_ogRaiseError="Muestra y registra mensajes de error y devuelve el código correspondiente." +MSG_HELP_ogReduceFs="Reduce el tamaño del sistema de archivos al mínimo ocupado por sus datos." +MSG_HELP_ogReduceImage="Reduce el tamaño de la imagen" +MSG_HELP_ogRefindDeleteEntry="Borra en rEFInd las entradas para el inicio en una particion." +MSG_HELP_ogRefindDefaultEntry="Configura la entrada por defecto de rEFInd." +MSG_HELP_ogRefindOgliveDefaultEntry="Configura la entrada de ogLive como la entrada por defecto de rEFInd." +MSG_HELP_ogRefindSetTheme="Asigna un tema al rEFInd." +MSG_HELP_ogRefindSetTimeOut="Define el tiempo (segundos) que se muestran las opciones de inicio de rEFInd." +MSG_HELP_ogRefindSetResolution="Define la resolución que usuará el thema del gestor de arranque rEFInd." +MSG_HELP_ogRefindInstall="Instala y configura el gestor rEFInd en la particion EFI" +MSG_HELP_ogRestoreAclImage="" +MSG_HELP_ogRestoreBootLoaderImage="" +MSG_HELP_ogRestoreDiskImage="Restaura una imagen de un disco completo." +MSG_HELP_ogRestoreEfiBootLoader="Copia el cargador de arranque de la partición de sistema a la partición EFI." +MSG_HELP_ogRestoreImage="Restaura una imagen de sistema operativo." +MSG_HELP_ogRestoreInfoImage="Restablece informacion del sistema: acl y enlaces simbolicos" +MSG_HELP_ogRestoreMbrImage="Restaura una imagen del sector de arranque (MBR)." +MSG_HELP_ogRestoreUuidPartitions="Restaura los uuid de las particiones y la tabla de particiones." +MSG_HELP_ogSaveImageInfo="Crea un fichero con la información de la imagen." +MSG_HELP_ogSetLinuxName="" +MSG_HELP_ogSetPartitionActive="Establece el número de partición activa de un disco." +MSG_HELP_ogSetPartitionId="Modifica el tipo de una partición física usando el mnemónico del tipo." +MSG_HELP_ogSetPartitionSize="Establece el tamaño de una partición." +MSG_HELP_ogSetPartitionType="Modifica el identificador de tipo de una partición física." +MSG_HELP_ogSetRegistryValue="Asigna un dato a un valor del registro de Windows." +MSG_HELP_ogSetWindowsName="Asigna el nombre del cliente en el registro de Windows." +MSG_HELP_ogSetWinlogonUser="Asigna el nombre de usuario por defecto para el gestor de entrada de Windows." +MSG_HELP_ogSyncCreate="Sincroniza los datos de la particion a la imagen" +MSG_HELP_ogSyncRestore="Sincroniza los datos de la imagen a la particion" +MSG_HELP_ogTorrentStart="" +MSG_HELP_ogTypeToId="Devuelve el identificador asociado al mnemónico de tipo de partición." +MSG_HELP_ogUcastReceiverPartition="" +MSG_HELP_ogUcastSendFile="" +MSG_HELP_ogUcastSendPartition="" +MSG_HELP_ogUcastSyntax="" +MSG_HELP_ogUnhidePartition="Hace visible una partición de Windows." +MSG_HELP_ogUninstallLinuxClient="Desinstala el cliente OpenGnSys en un sistema operativo Linux." +MSG_HELP_ogUninstallWindowsClient="Desinstala el cliente OpenGnSys en un sistema operativo Windows." +MSG_HELP_ogUnlock="Desbloquea una partición tras finalizar una operación de uso exclusivo." +MSG_HELP_ogUnlockDisk="Desbloquea un disco tras finalizar una operación de uso exclusivo." +MSG_HELP_ogUnlockImage="Desbloquea una imagen tras finalizar una operación de uso exclusivo." +MSG_HELP_ogUnlockPartition=MSG_HELP_ogUnlock +MSG_HELP_ogUnmountAll="Desmonta todos los sistemas de archivos." +MSG_HELP_ogUnmountCache="Desmonta el sistema de archivos de caché local." +MSG_HELP_ogUnmount="Desmonta un sistema de archivos." +MSG_HELP_ogUnmountImage="Desmonta la imagen" +MSG_HELP_ogUnmountFs=MSG_HELP_ogUnmount +MSG_HELP_ogUnsetDirtyBit="" +MSG_HELP_ogUpdateCacheIsNecesary="Comprueba si es necesario actualizar una archivo en la cache local." +MSG_HELP_ogUpdatePartitionTable="Actualiza informacion tabla particiones del disco" +MSG_HELP_ogUuidChange="Reemplaza el UUID de un sistema de ficheros." +MSG_HELP_ogWaitSyncImage="" +MSG_HELP_ogWindowsBootParameters="" +MSG_HELP_ogWindowsRegisterPartition="" + +# Scripts +MSG_HELP_configureOs="Post-configura de arranque del sistema" +MSG_HELP_createBaseImage="Genera imagen basica de la particion" +MSG_HELP_createDiffImage="Genera imagen diferencial de la particion respecto a la imagen basica" +MSG_HELP_installOfflineMode="Prepara el equipo cliente para el modo offline." +MSG_HELP_partclone2sync="Convierte imagen de partclone en imagen sincronizable." +MSG_HELP_restoreBaseImage="Restaura una imagen basica en una particion" +MSG_HELP_restoreDiffImage="Restaura una imagen diferencial en una particion" +MSG_HELP_updateCache="Realiza la actualizacion de la cache" + +# Mensajes de descripción breve de la interfaz. +MSG_INTERFACE_START="[START Interface] Ejecutar comando: " +MSG_INTERFACE_END="[END Interface] Comando terminado con este código: " + +# Mensajes de scripts. +MSG_SCRIPTS_START=" INICIO scripts : " +MSG_SCRIPTS_END=" FIN scripts: " +MSG_SCRIPTS_TASK_END="Fin de la tarea" + +MSG_SCRIPTS_TASK_SLEEP="Esperando para iniciar" +MSG_SCRIPTS_TASK_START="Iniciando" +MSG_SCRIPTS_TASK_ERR="Error" + +# Script createImage. +MSG_SCRIPTS_FILE_RENAME=" Renombrar fichero-imagen previo: " +MSG_SCRIPTS_CREATE_SIZE=" Calcular espacio (KB) requerido para almacenarlo y el disponible: " + +# Script updateCache. +MSG_SCRIPTS_UPDATECACHE_DOUPDATE="comprovar si es necessari actualitzar el fitxer imatge" +MSG_SCRIPTS_UPDATECACHE_CHECKSIZECACHE="Comprobar que el tamaño de la cache es mayor que el fichero a descargar." +# Script updateCache: para las imágenes sincronizadas tipo dir. +MSG_SCRIPTS_UPDATECACHE_CHECKSIZEDIR="Calculamos el tamaño de la imagen." +MSG_SCRIPTS_UPDATECACHE_CHECKSIZEIMG="Comprobamos si hay que la imagen del repositorio es mayor que la de la cache." +MSG_SCRIPTS_UPDATECACHE_IFNOTCACHEDO="Comprobar el espacio libre de la cache y actuar según engine.cfg" + +MSG_SCRIPTS_UPDATECACHE_CHECKMCASTSESSION="Comprobando sesion multicast: ServidorMcast:PuertoDatos" + +# interface sustituye temporalmente al scritp restore +MSG_SCRIPTS_CHECK_ENGINE="Analizar proceso a realizar según engine.cfg" +MSG_SCRIPTS_MULTICAST_PRECHECK_PORT="Determinar puerto principal y auxiliar multicast." +MSG_SCRIPTS_MULTICAST_CHECK_PORT="Comprobar puertos de sesion y datos" +MSG_SCRIPTS_MULTICAST_REQUEST_PORT="Solicitar la apertura: " +MSG_SCRIPTS_OS_CONFIGURE="Iniciar la configuracion del sistema restaurado" + +# TIME MESSAGES +MSG_SCRIPTS_TIME_TOTAL="tiempo total del proceso" +MSG_SCRIPTS_TIME_PARTIAL="tiempo parcial del subproceso" + +# HTTPLOG +MSG_HTTPLOG_NOUSE="No apague este ordenador por favor" + +# Mensajes sincronizadas +MSG_SYNC_RESIZE="Redimensiona la imagen al tamaño necesario" +MSG_SYNC_RESTORE="Trae el listado ficheros y baja la imagen" +MSG_SYNC_DELETE="Diferencial: Borra archivos antiguos" +MSG_SYNC_SLEEP="Espera que se monte/reduzca la imagen" +# Mensajes sincronizadas complementarios a errores +MSG_SYNC_DIFFERENTFS="El sistema de ficheros de destino no coincide con el de la imagen" +MSG_SYNC_EXTENSION="Las extensiones de la imagenes deben ser img o diff" +MSG_SYNC_NOCHECK="La imagen esta montada por otro proceso, no podemos comprobarla" + +MSG_RESTORE="Restaura la imagen en" + diff --git a/client/lib/python3/lang_en_GB.py b/client/lib/python3/lang_en_GB.py new file mode 100644 index 0000000..fbc9eee --- /dev/null +++ b/client/lib/python3/lang_en_GB.py @@ -0,0 +1,387 @@ +#!/usr/bin/python3 + +# English language file. +#@version 1.1.0 +#@author Jose Miguel Hernandez - Universidad de Salamanca +#@date 2018-03-01 + +# Error messages. +MSG_ERR_GENERIC="Undefined unknown error" +MSG_ERR_FORMAT="Wrong execution format" +MSG_ERR_OUTOFLIMIT="Out of range or invalid value" +MSG_ERR_NOTFOUND="File or device not found" +MSG_ERR_PARTITION="Unknown or wrong partition" +MSG_ERR_LOCKED="Resource locked by exclusive use operation" +MSG_ERR_CACHE="Local cache error" +MSG_ERR_NOGPT="Current disk does not include GPT partition" +MSG_ERR_REPO="Failed when mounting images repository" +MSG_ERR_NOMSDOS="Current disk does not include MSDOS partition" +MSG_ERR_FILESYS="Unknown or unmountable file system" +MSG_ERR_NOTOS="Cannot detect or boot OS" +MSG_ERR_IMAGE="Cannot create or restore a system image" +MSG_ERR_IMAGEFILE="Image file corrupt or of other partclone version" +MSG_ERR_NOTEXEC="Non executable program or function" +MSG_ERR_NOTWRITE="Write access denied" +MSG_ERR_NOTCACHE="No client cache partition" +MSG_ERR_NOTUEFI="UEFI isn't active" +MSG_ERR_NOTBIOS="BIOS legacy isn't active" +MSG_ERR_CACHESIZE="Not enough space in local or remote cache" +MSG_ERR_REDUCEFS="Error when reducing file system" +MSG_ERR_EXTENDFS="Error when expanding file system" +MSG_ERR_IMGSIZEPARTITION="Backup error: Partition smaller than image" +MSG_ERR_UPDATECACHE="Error when running `updateCache´ command" +MSG_ERR_UCASTSYNTAXT="Error when generating Unicast transfer syntax" +MSG_ERR_UCASTSENDPARTITION="Error when sending a Unicast partition" +MSG_ERR_UCASTSENDFILE="Error when sending a Unicast file" +MSG_ERR_UCASTRECEIVERPARTITION="Error when receiving an Unicast partition" +MSG_ERR_UCASTRECEIVERFILE="Error when receiving an Unicast file" +MSG_ERR_MCASTSYNTAXT="Error when generating Multicast transfer syntax" +MSG_ERR_MCASTSENDFILE="Error when sending Multicast file" +MSG_ERR_MCASTRECEIVERFILE="Error when receiving Multicast file" +MSG_ERR_MCASTSENDPARTITION="Error when sending Multicast partition" +MSG_ERR_MCASTRECEIVERPARTITION="Error when receiving Multicast partition " +MSG_ERR_PROTOCOLJOINMASTER="Error when connecting Unicast/Multicast session to Master" +MSG_ERR_DONTFORMAT="Formatting Error" +MSG_ERR_DONTMOUNT_IMAGE="Error when mounting/reducing image" +MSG_ERR_DONTUNMOUNT_IMAGE="Error when unmounting image" +MSG_ERR_DONTSYNC_IMAGE="Unsynchronizable image" +MSG_ERR_NOTDIFFERENT="Basic image identical to partition" +MSG_ERR_SYNCHRONIZING="Synchronizing error, it may affect image creation/restoration process" + +# Warning messages. +MSG_DONTUSE="DO NOT USE" +MSG_DONTMOUNT="Unmounted file system" +MSG_DONTUNMOUNT="Cannot unmount file system or it isn't mounted" +MSG_MOUNT="File system already mounted" +MSG_MOUNTREADONLY="Read-only file system mounted" +MSG_OBSOLETE="OBSOLETE" + +# Auxiliary help messages. +MSG_64BIT="64-bit" +MSG_DISK="Disk" +MSG_ERROR="Error" +MSG_EXAMPLE="Example" +MSG_FORMAT="Format" +MSG_FUNCTION="Function" +MSG_HARDWAREINVENTORY="Hardware inventory" +MSG_IMAGE="Image" +MSG_INSTALLED="Installed" +MSG_NOCACHE="No local cache" +MSG_NOEXTENDED="No extended partition" +MSG_PARTITION="Partition" +MSG_PROTOCOL="Protocol" +MSG_RESERVEDVALUE="Reserved value" +MSG_SEE="See" +MSG_UNKNOWN="Unknown" +MSG_WARNING="Warning" + +# Boot process messages. +MSG_DETECTLVMRAID="Detecting LVM and RAID meta-devices." +MSG_ERRBOOTMODE=f"{MSG_ERROR}: Unknown boot mode." +MSG_LAUNCHCLIENT="Launching client browser." +MSG_LOADAPI="Loading cloning-engine functions." +MSG_LOADMODULES="Loading kernel modules." +MSG_MAKELINKS="Creating symbolic links." +MSG_MOUNTREPO="Mounting repository using %s by %s mode." +MSG_OFFLINEMODE="Off-line boot mode." +MSG_OTHERSERVICES="Starting client complementary services." +MSG_POWEROFFCONF="Defining power-saving parameters." + +# Default menu messages. +MSG_BOOT="Boot" +MSG_DUPLEX="Duplex" +MSG_HOSTNAME="Hostname" +MSG_IPADDR="IP Address" +MSG_MACADDR="MAC Address" +MSG_MENUTITLE="Options menu" +MSG_POWEROFF="Shutdown computer" +MSG_SPEED="Speed" + +# API functions messages. +MSG_HELP_ogAclFilter="Draws ACL files from differential image." +MSG_HELP_ogAddCmd="Adds commands to file created by ogInstalMiniSetup." +MSG_HELP_ogAddRegistryKey="Adds new Windows registry key." +MSG_HELP_ogAddRegistryValue="Adds new Windows registry value." +MSG_HELP_ogAddToLaunchDaemon="" +MSG_HELP_ogBoot="Boots installed OS." +MSG_HELP_ogBootLoaderDeleteEntry=MSG_DONTUSE +MSG_HELP_ogBootLoaderHidePartitions=MSG_DONTUSE +MSG_HELP_ogBootMbrGeneric="" +MSG_HELP_ogBootMbrXP="" +MSG_HELP_ogBurgDefaultEntry="Sets default Burg entry." +MSG_HELP_ogBurgDeleteEntry="Deletes partition start-entries from MBR BURG." +MSG_HELP_ogBurgHidePartitions="Sets MBR Burg to hide non starting windows partitions. Allows you to select a partition that will not be hidden (e.g. for data)." +MSG_HELP_ogBurgInstallMbr="Installs BURG boot-loader on 1st HD MBR." +MSG_HELP_ogBurgOgliveDefaultEntry="Sets ogLive input as default Burg input." +MSG_HELP_ogCalculateChecksum="Calculates file checksum." +MSG_HELP_ogCalculateFullChecksum="Calculates file full checksum" +MSG_HELP_ogChangeRepo="Changes repository for remote resource: images." +MSG_HELP_ogCheckFs="Checks file system consistence." +MSG_HELP_ogCheckIpAddress="" +MSG_HELP_ogCheckProgram="" +MSG_HELP_ogCheckStringInGroup="" +MSG_HELP_ogCheckStringInReg="" +MSG_HELP_ogCheckSyncImage="Displays image contents to check it." +MSG_HELP_ogCleanLinuxDevices="" +MSG_HELP_ogCleanOs="Deletes OS unnecessary files." +MSG_HELP_ogCompareChecksumFiles="Compares if the checksums match." +MSG_HELP_ogConfigureFstab="" +MSG_HELP_ogConfigureOgagent="Sets OS new agent: OGAgent." +MSG_HELP_ogCopyFile="Copies file to another storage unit ." +MSG_HELP_ogCreateBootLoaderImage="" +MSG_HELP_ogCreateCache="Saves space for cache partition at the end of disk." +MSG_HELP_ogCreateDiskImage="Creates exact image from local disk." +MSG_HELP_ogCreateFileImage="Creates/Resizes synchronized image file." +MSG_HELP_ogCreateGptPartitions="" +MSG_HELP_ogCreateImage="Creates exact image from local installed OS." +MSG_HELP_ogCreateImageSyntax="" +MSG_HELP_ogCreateInfoImage="Creates image content information." +MSG_HELP_ogCreateMbrImage="Creates MBR image." +MSG_HELP_ogCreatePartitions="Creates disk partition table." +MSG_HELP_ogCreatePartitionTable="Creates partition table, if necessary." +MSG_HELP_ogCreateTorrent="" +MSG_HELP_ogCopyEfiBootLoader="Copy the boot loader from the EFI partition to system partition." +MSG_HELP_ogDeleteCache="Deletes local cache partition." +MSG_HELP_ogDeleteFile="Deletes file from storage." +MSG_HELP_ogDeletePartitionTable="Deletes disk table partition" +MSG_HELP_ogDeleteRegistryKey="Deletes empty Windows registry key." +MSG_HELP_ogDeleteRegistryValue="Deletes Windows registry value." +MSG_HELP_ogDeleteTree="Deletes directory tree." +MSG_HELP_ogDevToDisk="Returns disk or partition ordinal number for device file path." +MSG_HELP_ogDiskToDev="Returns device file path for disk or partition ordinal number." +MSG_HELP_ogDomainScript="" +MSG_HELP_ogEcho="Displays and log messages." +MSG_HELP_ogExecAndLog="Runs and logs command" +MSG_HELP_ogExtendFs="Expands file system size to partition maximum." +MSG_HELP_ogFindCache="Shows local cache reserved partition." +MSG_HELP_ogFixBootSector="" +MSG_HELP_ogFormatCache="Formats (clears) local cache." +MSG_HELP_ogFormat="Formats file system." +MSG_HELP_ogFormatFs=MSG_HELP_ogFormat +MSG_HELP_ogGetArch="Returns client architecture." +MSG_HELP_ogGetCacheSize="Returns local cache partition size." +MSG_HELP_ogGetCacheSpace="Returns maximum available space that can be reserved for local cache partition." +MSG_HELP_ogGetCaller="Returns program or function which is calling to current one" +MSG_HELP_ogGetDiskSize="Returns disk size." +MSG_HELP_ogGetDiskType="Returns disk type." +MSG_HELP_ogGetFreeSize="" +MSG_HELP_ogGetFsSize="Returns file system size." +MSG_HELP_ogGetFsType="Returns file system type." +MSG_HELP_ogGetGroupDir="Returns default directory path for client group." +MSG_HELP_ogGetGroupName="Returns client group name." +MSG_HELP_ogGetHivePath="Returns full path of file from Windows registry section." +MSG_HELP_ogGetHostname="Returns local hostname." +MSG_HELP_ogGetImageCompressor="Returns image compression tool." +MSG_HELP_ogGetImageInfo="Displays monolithic image information: cloning; compressor; file system; size(KB)." +MSG_HELP_ogGetImageProgram="Returns used program to create image." +MSG_HELP_ogGetImageSize="Returns system image size." +MSG_HELP_ogGetImageType="Returns image file system." +MSG_HELP_ogGetIpAddress="Returns client IP." +MSG_HELP_ogGetLastSector="Returns last available sector from disk or partition." +MSG_HELP_ogGetMacAddress="Returns client Ethernet address." +MSG_HELP_ogGetMountImageDir="Returns mounting directory of image." +MSG_HELP_ogGetMountPoint="Returns directory of local file system mount point." +MSG_HELP_ogGetNetInterface="" +MSG_HELP_ogGetOsType="Returns installed OS type." +MSG_HELP_ogGetOsUuid="Returns OS UUID" +MSG_HELP_ogGetOsVersion="Returns OS version." +MSG_HELP_ogGetParentPath="Returns full path of OpenGnsys system file parent directory." +MSG_HELP_ogGetPartitionActive="Returns disk active partition." +MSG_HELP_ogGetPartitionId="Returns partition type ID." +MSG_HELP_ogGetPartitionSize="Returns partition size." +MSG_HELP_ogGetPartitionsNumber="Returns disk partitions number." +MSG_HELP_ogGetPartitionTableType="Returns disk partition table type." +MSG_HELP_ogGetPartitionType="Returns partition type." +MSG_HELP_ogGetPath="Returns full path of OpenGnsys system file." +MSG_HELP_ogGetRegistryValue="Returns data from Windows registry value." +MSG_HELP_ogGetRepoIp="Returns OpenGnsys Repository IP address ." +MSG_HELP_ogGetSerialNumber="Returns host serial number." +MSG_HELP_ogGetServerIp="Returns main OpenGnsys Server IP address." +MSG_HELP_ogGetSizeParameters="Returns file system data size, required space for image and if it fits in the chosen repository." +MSG_HELP_ogGetWindowsName="Returns saved client name on Windows registry." +MSG_HELP_ogGrubAddOgLive="Adds MBR grub an entry calling Opengnsys client." +MSG_HELP_ogGrubDefaultEntry="Sets GRUB default entry." +MSG_HELP_ogGrubDeleteEntry="Deletes partition start entries on MBR grub." +MSG_HELP_ogGrubHidePartitions="Sets MBR grub to hide non starting Windows partitions. Allows you to select a partition that will not be hidden (e.g. for data)." +MSG_HELP_ogGrubInstallMbr="Installs GRUB boot loader on 1st HD MBR" +MSG_HELP_ogGrubInstallPartition="Installs GRUB boot loader on BootSector" +MSG_HELP_ogGrubOgliveDefaultEntry="Sets ogLive entry as default GRUB entry." +MSG_HELP_ogGrubSecurity="Configures user and password for change the menu entries of grub." +MSG_HELP_ogGrubUefiConf="Generates the grub.cfg file of the EFI partition." +MSG_HELP_ogHelp="Shows functions help messages." +MSG_HELP_ogHidePartition="Hides Windows partition." +MSG_HELP_ogIdToType="Returns partition type identifier." +MSG_HELP_ogInstallFirstBoot="Creates file to run on first Windows boot." +MSG_HELP_ogInstallLaunchDaemon="Installs file to run on MACos boot." +MSG_HELP_ogInstallLinuxClient=MSG_OBSOLETE +MSG_HELP_ogInstallMiniSetup="Installs file to run on Windows boot." +MSG_HELP_ogInstallRunonce="Creates file to run on admin-user Windows boot." +MSG_HELP_ogInstallWindowsClient=MSG_OBSOLETE +MSG_HELP_ogIsFormated="Checks file system if formatted." +MSG_HELP_ogIsImageLocked="Checks image if blocked by exclusive use operation." +MSG_HELP_ogIsLocked="Checks partition or disk if blocked by exclusive use operation." +MSG_HELP_ogIsDiskLocked="Checks disk if blocked by exclusive use operation." +MSG_HELP_ogIsMounted="Checks file system if mounted." +MSG_HELP_ogIsNewerFile="Checks if one file is newer (or has been modified later) than another one." +MSG_HELP_ogIsPartitionLocked=MSG_HELP_ogIsLocked +MSG_HELP_ogIsRepoLocked="" +MSG_HELP_ogIsSyncImage="Checks image if synchronizable." +MSG_HELP_ogIsVirtualMachine="Checks if client is a virtual machine" +MSG_HELP_ogIsWritable="Checks if mounted file system has write permissions." +MSG_HELP_ogLinuxBootParameters="Returns installed Linux boot parameters." +MSG_HELP_ogListHardwareInfo="Lists the client hardware inventory." +MSG_HELP_ogListLogicalPartitions="Lists disk logic partitions." +MSG_HELP_ogListPartitions="Lists disk partitions table." +MSG_HELP_ogListPrimaryPartitions="Lists disk primary partitions" +MSG_HELP_ogListRegistryKeys="Lists sub-keys names included on a Windows registry key." +MSG_HELP_ogListRegistryValues="Lists value names included on a Windows registry key." +MSG_HELP_ogListSoftware="Lists OS installed programs inventory." +MSG_HELP_ogLock="Blocks partition for exclusive use operation." +MSG_HELP_ogLockDisk="Blocks disk for exclusive use operation." +MSG_HELP_ogLockImage="Blocks image for exclusive use operation." +MSG_HELP_ogLockPartition=MSG_HELP_ogLock +MSG_HELP_ogMakeChecksumFile="Stores file checksum." +MSG_HELP_ogMakeDir="Makes OpenGnsys directory." +MSG_HELP_ogMakeGroupDir="Makes group (lab) directory on repository." +MSG_HELP_ogMcastReceiverFile="" +MSG_HELP_ogMcastReceiverPartition="" +MSG_HELP_ogMcastRequest="" +MSG_HELP_ogMcastSendFile="" +MSG_HELP_ogMcastSendPartition="" +MSG_HELP_ogMcastSyntax="" +MSG_HELP_ogMountCache="Mounts cache file system." +MSG_HELP_ogMountCdrom="Mounts default optical drive." +MSG_HELP_ogMountImage="Mounts synchronizable image" +MSG_HELP_ogMount="Mounts file system and returns mount point." +MSG_HELP_ogMountFs=MSG_HELP_ogMount +MSG_HELP_ogNvramActiveEntry="Sets active a bootloader (NVRAM) entry." +MSG_HELP_ogNvramAddEntry="Creates new entry in bootloader (NVRAM)." +MSG_HELP_ogNvramDeleteEntry="Deletes a bootloader (NVRAM) entry." +MSG_HELP_ogNvramGetCurrent="Displays the bootloader (NVRAM) entry that was started by the computer." +MSG_HELP_ogNvramGetNext="Displays the bootloader (NVRAM) entry for the boot next." +MSG_HELP_ogNvramGetOrder="Displays the bootloader (NVRAM) entries order." +MSG_HELP_ogNvramGetTimeout="Displays the bootloader (NVRAM) timeout." +MSG_HELP_ogNvramInactiveEntry="Sets inactive bootloader (NVRAM) entry." +MSG_HELP_ogNvramList="Lists bootloader (NVRAM) entries, by staring actives ones." +MSG_HELP_ogNvramPxeFirstEntry="Set the network as the NVRAM first boot." +MSG_HELP_ogNvramSetNext="Set the bootloader (NVRAM) entry for the boot next." +MSG_HELP_ogNvramSetOrder="Sets the bootloader (NVRAM) entries order." +MSG_HELP_ogNvramSetTimeout="Sets the bootloader (NVRAM) timeout." +MSG_HELP_ogRaiseError="Displays and registers error messages and returns code." +MSG_HELP_ogReduceFs="Reduces file system size to minimum." +MSG_HELP_ogReduceImage="Reduces image size." +MSG_HELP_ogRefindDeleteEntry="Deletes the menu entry of a partition in rEFInd." +MSG_HELP_ogRefindDefaultEntry="Configures default menu entry in rEFInd." +MSG_HELP_ogRefindOgliveDefaultEntry="Configures ogLive menu entry as default menu entry in rEFInd." +MSG_HELP_ogRefindSetTheme="Configures rEFInd's theme." +MSG_HELP_ogRefindSetTimeOut="Defines the time that rEFInd shows the menu." +MSG_HELP_ogRefindSetResolution="Defines the resolucion of rEFInd's theme." +MSG_HELP_ogRefindInstall="Installs and configures rEFInd boot loader in ESP." +MSG_HELP_ogRestoreAclImage="Restores Windows ACL (Inf. must be on /tmp)." +MSG_HELP_ogRestoreBootLoaderImage="" +MSG_HELP_ogRestoreDiskImage="Restores disk image." +MSG_HELP_ogRestoreEfiBootLoader="Copy the boot loader from the system partition to the EFI partition." +MSG_HELP_ogRestoreImage="Restore OS image." +MSG_HELP_ogRestoreInfoImage="Restores system information: ACL and symbolic links" +MSG_HELP_ogRestoreMbrImage="Restores boot sector image (MBR)." +MSG_HELP_ogRestoreUuidPartitions="Restores UUID of partitions and partition table." +MSG_HELP_ogSaveImageInfo="Creates the image information file." +MSG_HELP_ogSetLinuxName="" +MSG_HELP_ogSetPartitionActive="Sets active partition number of disk." +MSG_HELP_ogSetPartitionId="Changes partition ID using mnemonic." +MSG_HELP_ogSetPartitionSize="Sets partition size." +MSG_HELP_ogSetPartitionType="Changes partition type ID." +MSG_HELP_ogSetRegistryValue="Assigns data to a Windows registry values." +MSG_HELP_ogSetWindowsName="Assigns client name to Windows registry." +MSG_HELP_ogSetWinlogonUser="Assigns Windows default user name to Windows input manager." +MSG_HELP_ogSyncCreate="Synchronizes partition data to image" +MSG_HELP_ogSyncRestore="Synchronize image data to partition" +MSG_HELP_ogTorrentStart="" +MSG_HELP_ogTypeToId="Returns the ID of partition type mnemonic." +MSG_HELP_ogUcastReceiverPartition="" +MSG_HELP_ogUcastSendFile="" +MSG_HELP_ogUcastSendPartition="" +MSG_HELP_ogUcastSyntax="" +MSG_HELP_ogUnhidePartition="Unhides Windows partition." +MSG_HELP_ogUninstallLinuxClient="Uninstalls old OpenGnSys agent from Linux OS." +MSG_HELP_ogUninstallWindowsClient="Uninstalls oldOpenGnSys agent from Windows OS." +MSG_HELP_ogUnlock="Unlocks partition after exclusive use operation." +MSG_HELP_ogUnlockDisk="Unlocks disk after exclusive use operation." +MSG_HELP_ogUnlockImage="Unlocks image after exclusive use operation." +MSG_HELP_ogUnlockPartition=MSG_HELP_ogUnlock +MSG_HELP_ogUnmountAll="Unmounts all file systems." +MSG_HELP_ogUnmountCache="Unmounts cache file system." +MSG_HELP_ogUnmountImage="Unmounts image" +MSG_HELP_ogUnmount="Unmounts file system." +MSG_HELP_ogUnmountFs=MSG_HELP_ogUnmount +MSG_HELP_ogUnsetDirtyBit="" +MSG_HELP_ogUpdateCacheIsNecesary="Checks if necessary file update in local cache." +MSG_HELP_ogUpdatePartitionTable="Updates disk partition table info " +MSG_HELP_ogUuidChange="Replaces the filesystem UUID" +MSG_HELP_ogWaitSyncImage="" +MSG_HELP_ogWindowsBootParameters="" +MSG_HELP_ogWindowsRegisterPartition="" + +# Scripts +MSG_HELP_configureOs="Post-configure system boot" +MSG_HELP_createBaseImage="Create partition basic image" +MSG_HELP_createDiffImage="Create partition differential image from basic image" +MSG_HELP_installOfflineMode="Prepare client for off-line mode." +MSG_HELP_partclone2sync="Turn part-clone image into synchronizable image." +MSG_HELP_restoreBaseImage="Restore basic image into partition" +MSG_HELP_restoreDiffImage="Restore differential image into partition" +MSG_HELP_updateCache="Update cache" + +# INTERFACE functions messages. +MSG_INTERFACE_START="[START Interface] Run command: " +MSG_INTERFACE_END="[END Interface] Command finished with this code: " + +# SCRIPTS messages. +MSG_SCRIPTS_START=" START scripts: " +MSG_SCRIPTS_END=" END scripts: " +MSG_SCRIPTS_TASK_END="End of task" + +MSG_SCRIPTS_TASK_SLEEP="Waiting to start" +MSG_SCRIPTS_TASK_START="Starting" +MSG_SCRIPTS_TASK_ERR="Error" + +# createImage script +MSG_SCRIPTS_FILE_RENAME="Rename previous image-file: " +MSG_SCRIPTS_CREATE_SIZE="Check required and available storing space(KB): " + +# updateCache script +MSG_SCRIPTS_UPDATECACHE_DOUPDATE="Check if it is necessary to update image file" +MSG_SCRIPTS_UPDATECACHE_CHECKSIZECACHE="Check if Cache size is bigger than image file size." +# Script updateCache: for dir synchronized images . +MSG_SCRIPTS_UPDATECACHE_CHECKSIZEDIR="Calculate image size." +MSG_SCRIPTS_UPDATECACHE_CHECKSIZEIMG="Check if repository image file size is bigger than Cache size." +MSG_SCRIPTS_UPDATECACHE_IFNOTCACHEDO="Check free Cache and apply engine.cfg" + +MSG_SCRIPTS_UPDATECACHE_CHECKMCASTSESSION="Checking Multicast Session McastServer:DataPort" + +# interface temporarily replaces restore script +MSG_SCRIPTS_CHECK_ENGINE="Analyze process to carry out according to engine.cfg" +MSG_SCRIPTS_MULTICAST_PRECHECK_PORT="Check main and auxiliary Multicast port." +MSG_SCRIPTS_MULTICAST_CHECK_PORT="Check session and data ports" +MSG_SCRIPTS_MULTICAST_REQUEST_PORT="Request Multicast port opening: " +MSG_SCRIPTS_OS_CONFIGURE="Start restored system setting" + +# TIME MESSAGES +MSG_SCRIPTS_TIME_TOTAL="Total process time" +MSG_SCRIPTS_TIME_PARTIAL="Partial sub-process time" + +# HTTPLOG +MSG_HTTPLOG_NOUSE="PLEASE DO NOT TURN OFF THIS COMPUTER" + +# Messages for synchronized images (complementary to errors) +MSG_SYNC_RESIZE="Resize image to necessary size" +MSG_SYNC_RESTORE="Get files list and download image" +MSG_SYNC_DELETE="Differential: Delete old files" +MSG_SYNC_SLEEP="Wait for mounting/reducing image" +# Messages for synchronized images (complementary to errors) +MSG_SYNC_DIFFERENTFS="Destination file system does not match image" +MSG_SYNC_EXTENSION="Image extension must be img or diff" +MSG_SYNC_NOCHECK="Image mounted by another process. Cannot verify it" + +MSG_RESTORE="Restore image on " + diff --git a/client/lib/python3/lang_es_ES.py b/client/lib/python3/lang_es_ES.py new file mode 100644 index 0000000..1687e99 --- /dev/null +++ b/client/lib/python3/lang_es_ES.py @@ -0,0 +1,387 @@ +#!/usr/bin/python3 + +# Fichero de idioma: español. +#@version 1.1.1 +#@author + + +# Mensajes de error. +MSG_ERR_GENERIC="Error imprevisto no definido" +MSG_ERR_FORMAT="Formato de ejecución incorrecto" +MSG_ERR_OUTOFLIMIT="Valor fuera de rango o no válido" +MSG_ERR_NOTFOUND="Fichero o dispositivo no encontrado" +MSG_ERR_PARTITION="Partición errónea o desconocida" +MSG_ERR_LOCKED="Recurso bloqueado por operación de uso exclusivo" +MSG_ERR_CACHE="Error en partición de caché local" +MSG_ERR_NOGPT="El disco indicado no contiene una partición GPT" +MSG_ERR_REPO="Error al montar el repositorio de imágenes" +MSG_ERR_NOMSDOS="El disco indicado no contiene una partición MSDOS" +MSG_ERR_FILESYS="Sistema de archivos desconocido o no se puede montar" +MSG_ERR_NOTOS="Sistema operativo no detectado o no se puede iniciar" +MSG_ERR_IMAGE="No se puede crear o restaurar una image de sistema" +MSG_ERR_IMAGEFILE="Archivo de imagen corrupto o de otra versión de partclone" +MSG_ERR_NOTEXEC="Programa o función no ejecutable" +MSG_ERR_NOTWRITE="No hay acceso de escritura" +MSG_ERR_NOTCACHE="No existe partición caché en el cliente" +MSG_ERR_NOTUEFI="La interfaz UEFI no está activa" +MSG_ERR_NOTBIOS="La interfaz BIOS Legacy no está activa" +MSG_ERR_CACHESIZE="El espacio de la caché local o remota no es suficiente" +MSG_ERR_REDUCEFS="Error al reducir el sistema de archivos" +MSG_ERR_EXTENDFS="Error al expandir el sistema de archivos" +MSG_ERR_IMGSIZEPARTITION="Error al restaurar: Partición mas pequeña que la imagen" +MSG_ERR_UPDATECACHE="Error al realizar el comando updateCache" +MSG_ERR_UCASTSYNTAXT="Error en la generación de sintaxis de transferenica Unicast" +MSG_ERR_UCASTSENDPARTITION="Error en envío Unicast de una partición" +MSG_ERR_UCASTSENDFILE="Error en envío Unicast de un fichero" +MSG_ERR_UCASTRECEIVERPARTITION="Error en la recepción Unicast de una partición" +MSG_ERR_UCASTRECEIVERFILE="Error en la recepción Unicast de un fichero" +MSG_ERR_MCASTSYNTAXT="Error en la generación de sintaxis de transferenica Multicast" +MSG_ERR_MCASTSENDFILE="Error en envío Multicast de un fichero" +MSG_ERR_MCASTRECEIVERFILE="Error en la recepción Multicast de un fichero" +MSG_ERR_MCASTSENDPARTITION="Error en envío Multicast de una partición" +MSG_ERR_MCASTRECEIVERPARTITION="Error en la recepción Multicast de un fichero" +MSG_ERR_PROTOCOLJOINMASTER="Error en la conexión de una sesión Unicast|Multicast con el Master" +MSG_ERR_DONTFORMAT="Error al formatear" +MSG_ERR_DONTMOUNT_IMAGE="Error al montar/reducir la imagen" +MSG_ERR_DONTUNMOUNT_IMAGE="Error al desmontar la imagen" +MSG_ERR_DONTSYNC_IMAGE="Imagen no sincronizable" +MSG_ERR_NOTDIFFERENT="No se detectan diferencias entre la imagen básica y la partición" +MSG_ERR_SYNCHRONIZING="Error al sincronizar, puede afectar la creacion|restauracion de la imagen" + +# Mensajes de avisos. +MSG_DONTMOUNT="Sistema de archivos no montado" +MSG_DONTUSE="NO USAR" +MSG_DONTUNMOUNT="El sistema de archivos no se puede desmontar o no está montado" +MSG_MOUNT="Sistema de archivos montado" +MSG_MOUNTREADONLY="Sistema de archivos montado solo de lectura" +MSG_OBSOLETE="EN DESUSO" + +# Mensajes complementarios para las ayudas. +MSG_64BIT="64 bits" +MSG_DISK="disco" +MSG_ERROR="Error" +MSG_EXAMPLE="Ejemplo" +MSG_FORMAT="Formato" +MSG_FUNCTION="Función" +MSG_HARDWAREINVENTORY="Inventario de hardware de la máquina" +MSG_IMAGE="imagen" +MSG_INSTALLED="instalado" +MSG_NOCACHE="sin caché local" +MSG_NOEXTENDED="sin partición extendida" +MSG_PARTITION="partición" +MSG_PROTOCOL="protocolo" +MSG_RESERVEDVALUE="Valor reservado" +MSG_SEE="Ver" +MSG_UNKNOWN="Desconocido" +MSG_WARNING="Aviso" + +# Mensajes del proceso de arranque. +MSG_DETECTLVMRAID="Detectar metadispositivos LVM y RAID." +MSG_ERRBOOTMODE=f"{MSG_ERROR}: Modo de arranque desconocido." +MSG_LAUNCHCLIENT="Ejecutar cliente." +MSG_LOADAPI="Cargar funciones del motor de clonación." +MSG_LOADMODULES="Cargar módulos del kernel." +MSG_MAKELINKS="Crear enlaces simbólicos." +MSG_MOUNTREPO="Montar repositorio por %s en modo %s." +MSG_OFFLINEMODE="Modo de arranque sin conexión." +MSG_OTHERSERVICES="Iniciar servicios complementarios del cliente." +MSG_POWEROFFCONF="Definir parámetros de ahorro de energía." + +# Mensajes del menú por defecto. +MSG_BOOT="Iniciar" +MSG_DUPLEX="Dúplex" +MSG_HOSTNAME="Equipo" +MSG_IPADDR="Dirección IP" +MSG_MACADDR="Dirección MAC" +MSG_MENUTITLE="Menú de opciones" +MSG_POWEROFF="Apagar el equipo" +MSG_SPEED="Velocidad" + +# Mensajes de descripción breve de las funciones de la API. +MSG_HELP_ogAclFilter="Extrae las acl de los ficheros de la diferencial" +MSG_HELP_ogAddCmd="Añade comandos al fichero creado por la función ogInstalMiniSetup." +MSG_HELP_ogAddRegistryKey="Añade una nueva clave al registro de Windows." +MSG_HELP_ogAddRegistryValue="Añade un nuevo valor al registro de Windows." +MSG_HELP_ogAddToLaunchDaemon="" +MSG_HELP_ogBoot="Arranca un sistema operativo instalado." +MSG_HELP_ogBootLoaderDeleteEntry=MSG_DONTUSE +MSG_HELP_ogBootLoaderHidePartitions=MSG_DONTUSE +MSG_HELP_ogBootMbrGeneric="" +MSG_HELP_ogBootMbrXP="" +MSG_HELP_ogBurgDefaultEntry="Configura la entrada por defecto de Burg." +MSG_HELP_ogBurgDeleteEntry="Borra en el Burg del MBR las entradas para el inicio en una particion." +MSG_HELP_ogBurgHidePartitions="Configura el Burg del MBR para que oculte las particiones de windows que no se esten iniciando. Permite definir una partición que no se ocultará (ej: para datos)." +MSG_HELP_ogBurgInstallMbr="Instala el gestor de arranque BURG en el MBR del primer disco duro" +MSG_HELP_ogBurgOgliveDefaultEntry="Configura la entrada de ogLive como la entrada por defecto de Burg." +MSG_HELP_ogCalculateChecksum="Calcula la suma de comprobación (checksum) de un fichero." +MSG_HELP_ogCalculateFullChecksum="Calcula la suma de comprobación completa de un fichero." +MSG_HELP_ogChangeRepo="Cambia el repositorio para el recurso remoto images." +MSG_HELP_ogCheckFs="Comprueba la consistencia de un sistema de archivos." +MSG_HELP_ogCheckIpAddress="" +MSG_HELP_ogCheckProgram="" +MSG_HELP_ogCheckStringInGroup="" +MSG_HELP_ogCheckStringInReg="" +MSG_HELP_ogCheckSyncImage="Muestra el contenido de la imagen para comprobarla." +MSG_HELP_ogCleanLinuxDevices="" +MSG_HELP_ogCleanOs="Elimina los archivos que no son necesarios en el sistema operativo." +MSG_HELP_ogCompareChecksumFiles="Compara si coinciden las sumas de comprobación almacenadas de 2 ficheros." +MSG_HELP_ogConfigureFstab="" +MSG_HELP_ogConfigureOgagent="Configura el nuevo agente OGAgent para sistemas operativos." +MSG_HELP_ogCopyFile="Copia un fichero a otro almacenamiento." +MSG_HELP_ogCreateBootLoaderImage="" +MSG_HELP_ogCreateCache="Reserva espacio para la partición de caché al final del disco." +MSG_HELP_ogCreateDiskImage="Genera una imagen exacta de un disco completo." +MSG_HELP_ogCreateFileImage="Crea/redimensiona el archivo de la imagen sincronizada" +MSG_HELP_ogCreateGptPartitions="" +MSG_HELP_ogCreateImage="Genera una imagen exacta de un sistema operativo instalado localmente." +MSG_HELP_ogCreateImageSyntax="" +MSG_HELP_ogCreateInfoImage="Crea información del contenido de la imagen" +MSG_HELP_ogCreateMbrImage="Genera una imagen del sector de arranque (MBR)." +MSG_HELP_ogCreatePartitions="Define la estructura de particiones de un disco." +MSG_HELP_ogCreatePartitionTable="Genera una tabla de particiones en caso de que no sea valida." +MSG_HELP_ogCreateTorrent="" +MSG_HELP_ogCopyEfiBootLoader="Copia el cargador de arranque desde la partición EFI a la de sistema." +MSG_HELP_ogDeleteCache="Elimina la partición de caché local." +MSG_HELP_ogDeleteFile="Borra un fichero de un espacio de almacenamiento." +MSG_HELP_ogDeletePartitionTable="Elimina la tabla de particiones del disco" +MSG_HELP_ogDeleteRegistryKey="Borra una clave vacía del registro de Windows." +MSG_HELP_ogDeleteRegistryValue="Borra un valor del registro de Windows." +MSG_HELP_ogDeleteTree="Borra un árbol de directorios de un espacio de almacenamiento." +MSG_HELP_ogDevToDisk="Devuelve el nº de orden de disco o de partición correspondiente al camino del fichero de dispositivo." +MSG_HELP_ogDiskToDev="Devuelve el camino del fichero de dispositivo correspondiente al nº de orden de disco o de partición." +MSG_HELP_ogDomainScript="" +MSG_HELP_ogEcho="Muestra un mensaje en pantalla y permite registrarlo en fichero de log" +MSG_HELP_ogExecAndLog="Ejecuta un comando y registra su salida en fichero de log" +MSG_HELP_ogExtendFs="Extiende el tamaño de un sistema de archivo al máximo de su partición." +MSG_HELP_ogFindCache="Indica la partición reservada para caché local." +MSG_HELP_ogFixBootSector="" +MSG_HELP_ogFormatCache="Formatea (inicia) el sistema de caché local." +MSG_HELP_ogFormat="Formatea o reformatea un sistema de archivos." +MSG_HELP_ogFormatFs=MSG_HELP_ogFormat +MSG_HELP_ogGetArch="Devuelve el tipo de arquitectura del cliente." +MSG_HELP_ogGetCacheSize="Devuelve el tamaño de la partición de caché local." +MSG_HELP_ogGetCacheSpace="Devuelve el espacio máximo disponible que puede ser reservado para la partición de caché local." +MSG_HELP_ogGetCaller="Devuelve el programa o función que llama al actual" +MSG_HELP_ogGetDiskSize="Devuelve el tamaño del disco." +MSG_HELP_ogGetDiskType="Devuelve el mnemónico de tipo de disco." +MSG_HELP_ogGetFreeSize="" +MSG_HELP_ogGetFsSize="Devuelve el tamaño de un sistema de archivos." +MSG_HELP_ogGetFsType="Devuelve el mnemónico de tipo de sistema de archivos." +MSG_HELP_ogGetGroupDir="Devuelve el camino del directorio por defecto para el grupo del cliente." +MSG_HELP_ogGetGroupName="Devuelve el nombre del grupo al que pertenece el cliente." +MSG_HELP_ogGetHivePath="Devuelve el camino completo del fichero de una sección del registro de Windows." +MSG_HELP_ogGetHostname="Devuelve el nombre de la máquina local." +MSG_HELP_ogGetImageCompressor="Devuelve la herramienta de compresión de la imagen." +MSG_HELP_ogGetImageInfo="Muestra información sobre la imagen monolitica: clonacion:compresor:sistemaarchivos:tamañoKB." +MSG_HELP_ogGetImageProgram="Devuelve el programa usado para crear la imagen." +MSG_HELP_ogGetImageSize="Devuelve el tamaño de una imagen de sistema." +MSG_HELP_ogGetImageType="Devuelve el sistema de ficheros de la imagen." +MSG_HELP_ogGetIpAddress="Devuelve la dirección IP del cliente." +MSG_HELP_ogGetLastSector="Devuelve el último sector usable del disco o de una partición." +MSG_HELP_ogGetMacAddress="Devuelve la dirección Ethernet del cliente." +MSG_HELP_ogGetMountImageDir="Devuelve el directorio de montaje de una imagen." +MSG_HELP_ogGetMountPoint="Devuelve el directorio donde está montado un sistema de archivos local." +MSG_HELP_ogGetNetInterface="" +MSG_HELP_ogGetOsType="Devuelve el tipo de un sistema operativo instalado." +MSG_HELP_ogGetOsUuid="Devuelve el UUID de un sistema operativo" +MSG_HELP_ogGetOsVersion="Devuelve el tipo y la versión de un sistema operativo instalado." +MSG_HELP_ogGetParentPath="Devuelve el camino completo del directorio padre de un fichero de sistema OpenGnsys." +MSG_HELP_ogGetPartitionActive="Indica cual es la partición marcada como activa en un disco." +MSG_HELP_ogGetPartitionId="Devuelve el identificador de tipo de una partición." +MSG_HELP_ogGetPartitionSize="Devuelve el tamaño de una partición." +MSG_HELP_ogGetPartitionsNumber="Devuelve el número de particiones de un disco" +MSG_HELP_ogGetPartitionTableType="Devuelve el tipo de tabla de particiones del disco" +MSG_HELP_ogGetPartitionType="Devuelve el mnemónico de tipo de una partición." +MSG_HELP_ogGetPath="Devuelve el camino completo de un fichero de sistema OpenGnsys." +MSG_HELP_ogGetRegistryValue="Devuelve el dato de un valor del registro de Windows." +MSG_HELP_ogGetRepoIp="Devuelve la dirección IP del repositorio de datos." +MSG_HELP_ogGetSerialNumber="Devuelve el número de serie del equipo" +MSG_HELP_ogGetServerIp="Devuelve la dirección IP del servidor principal." +MSG_HELP_ogGetSizeParameters="Devuelve el tamaño de los datos de un sistema de ficheros, el espacio necesario para la imagen y si cabe en el repositorio elegido." +MSG_HELP_ogGetWindowsName="Devuelve el nombre del cliente guardado en el registro de Windows." +MSG_HELP_ogGrubAddOgLive="Incluye en el grub del MBR una entrada llamando al cliente de opengnsys." +MSG_HELP_ogGrubDefaultEntry="Configura la entrada por defecto de GRUB." +MSG_HELP_ogGrubDeleteEntry="Borra en el grub del MBR las entradas para el inicio en una particion." +MSG_HELP_ogGrubHidePartitions="Configura el grub del MBR para que oculte las particiones de windows que no se esten iniciando. Permite definir una partición que no se ocultará (ej: para datos)." +MSG_HELP_ogGrubInstallMbr="Instala el gestor de arranque GRUB en el MBR del primer disco duro" +MSG_HELP_ogGrubInstallPartition="Instala el gestor de arranque GRUB en el BootSector" +MSG_HELP_ogGrubOgliveDefaultEntry="Configura la entrada de ogLive como la entrada por defecto de GRUB." +MSG_HELP_ogGrubSecurity="Configura usuario y clave para modificar las entradas del menu del Grub." +MSG_HELP_ogGrubUefiConf="Genera el fichero grub.cfg de la partición EFI." +MSG_HELP_ogHelp="Muestra mensajes de ayudas para las funciones." +MSG_HELP_ogHidePartition="Oculta una partición de Windows." +MSG_HELP_ogIdToType="Devuelve el mnemónico asociado al identificador de tipo de partición." +MSG_HELP_ogInstallFirstBoot="Crea un archivo que se ejecutará en el primer arranque de Windows." +MSG_HELP_ogInstallLaunchDaemon="Instala un archivo que se ejecutará en el arranque de macOS." +MSG_HELP_ogInstallLinuxClient=MSG_OBSOLETE +MSG_HELP_ogInstallMiniSetup="Instala un archivo que se ejecutará en el arranque de Windows." +MSG_HELP_ogInstallRunonce="Crea archivo que se ejecutará en el inicio de un usuario administrador de Windows." +MSG_HELP_ogInstallWindowsClient=MSG_OBSOLETE +MSG_HELP_ogIsFormated="Comprueba si un sistema de archivos está formateado." +MSG_HELP_ogIsImageLocked="Comprueba si una imagen está bloqueada por una operación de uso exclusivo." +MSG_HELP_ogIsLocked="Comprueba si una partición o su disco están bloqueados por una operación de uso exclusivo." +MSG_HELP_ogIsDiskLocked="Comprueba si un disco está bloqueado por una operación de uso exclusivo." +MSG_HELP_ogIsMounted="Comprueba si un sistema de archivos está montado." +MSG_HELP_ogIsNewerFile="Comprueba si un fichero es más nuevo (se ha modificado después) que otro." +MSG_HELP_ogIsPartitionLocked=MSG_HELP_ogIsLocked +MSG_HELP_ogIsRepoLocked="" +MSG_HELP_ogIsSyncImage="Comprueba si la imagen es sincronizable." +MSG_HELP_ogIsVirtualMachine="Comprueba si el cliente es una máquina virtual" +MSG_HELP_ogIsWritable="Comprueba si un sistema de archivos está montado con permiso de escritura." +MSG_HELP_ogLinuxBootParameters="Devuelve los parámetros de arranque de un sistema operativo Linux instalado." +MSG_HELP_ogListHardwareInfo="Lista el inventario de dispositivos del cliente." +MSG_HELP_ogListLogicalPartitions="Lista las particiones lógicas de un disco" +MSG_HELP_ogListPartitions="Lista la estructura de particiones de un disco." +MSG_HELP_ogListPrimaryPartitions="Lista las particiones primarias de un disco" +MSG_HELP_ogListRegistryKeys="Lista los nombres de las subclaves incluidas en una clave del registro de Windows." +MSG_HELP_ogListRegistryValues="Lista los nombres de los valores incluidos en una clave del registro de Windows." +MSG_HELP_ogListSoftware="Lista el inventario de programas instalados en un sistema operativo." +MSG_HELP_ogLock="Bloquea una partición para operación de uso exclusivo." +MSG_HELP_ogLockDisk="Bloquea un disco para operación de uso exclusivo." +MSG_HELP_ogLockImage="Bloquea una imagen para operación de uso exclusivo." +MSG_HELP_ogLockPartition=MSG_HELP_ogLock +MSG_HELP_ogMakeChecksumFile="Almacena la suma de comprobación de un fichero." +MSG_HELP_ogMakeDir="Crea un directorio para OpenGnsys." +MSG_HELP_ogMakeGroupDir="Crea el directorio de grupo (aula) en un repositorio." +MSG_HELP_ogMcastReceiverFile="" +MSG_HELP_ogMcastReceiverPartition="" +MSG_HELP_ogMcastRequest="" +MSG_HELP_ogMcastSendFile="" +MSG_HELP_ogMcastSendPartition="" +MSG_HELP_ogMcastSyntax="" +MSG_HELP_ogMountCache="Monta el sistema de archivos dedicado a caché local." +MSG_HELP_ogMountCdrom="Monta dispositivo óptico por defecto." +MSG_HELP_ogMountImage="Monta una imagen sincronizable" +MSG_HELP_ogMount="Monta un sistema de archivos y devuelve el punto de montaje." +MSG_HELP_ogMountFs=MSG_HELP_ogMount +MSG_HELP_ogNvramActiveEntry="Configura a activa entrada del gestor de arranque (NVRAM)." +MSG_HELP_ogNvramAddEntry="Crea nueva entrada del gestor de arranque (NVRAM)." +MSG_HELP_ogNvramDeleteEntry="Borra entrada del gestor de arranque (NVRAM)." +MSG_HELP_ogNvramGetCurrent="Muestra la entrada del gestor de arranque (NVRAM) que ha iniciado el equipo." +MSG_HELP_ogNvramGetNext="Muestra la entrada del gestor de arranque (NVRAM) que se utilizará en el próximo arranque." +MSG_HELP_ogNvramGetOrder="Muestra el orden de las entradas del gestor de arranque (NVRAM)." +MSG_HELP_ogNvramGetTimeout="Muestra el tiempo de espera del gestor de arranque (NVRAM)." +MSG_HELP_ogNvramInactiveEntry="Configura a inactiva entrada del gestor de arranque (NVRAM)." +MSG_HELP_ogNvramList="Lista las entradas del gestor de arranque (NVRAN) marcando con un asterisco las activas" +MSG_HELP_ogNvramPxeFirstEntry="Configura la tarjeta de red como primer arranque en la NVRAM." +MSG_HELP_ogNvramSetNext="Configura el próximo arranque con la entrada del gestor de arranque (NVRAM)." +MSG_HELP_ogNvramSetOrder="Configura el orden de las entradas del gestor de arranque (NVRAM)." +MSG_HELP_ogNvramSetTimeout="Configura el tiempo de espera del gestor de arranque (NVRAM)." +MSG_HELP_ogRaiseError="Muestra y registra mensajes de error y devuelve el código correspondiente." +MSG_HELP_ogReduceFs="Reduce el tamaño del sistema de archivos al mínimo ocupado por sus datos." +MSG_HELP_ogReduceImage="Reduce el tamaño de la imagen" +MSG_HELP_ogRefindDeleteEntry="Borra en rEFInd las entradas para el inicio en una particion." +MSG_HELP_ogRefindDefaultEntry="Configura la entrada por defecto de rEFInd." +MSG_HELP_ogRefindOgliveDefaultEntry="Configura la entrada de ogLive como la entrada por defecto de rEFInd." +MSG_HELP_ogRefindSetTheme="Asigna un tema al rEFInd." +MSG_HELP_ogRefindSetTimeOut="Define el tiempo (segundos) que se muestran las opciones de inicio de rEFInd." +MSG_HELP_ogRefindSetResolution="Define la resolución que usuará el thema del gestor de arranque rEFInd." +MSG_HELP_ogRefindInstall="Instala y configura el gestor rEFInd en la particion EFI" +MSG_HELP_ogRestoreAclImage="Restaura las ACL de Windows (La informacion debe estar copiada en /tmp)." +MSG_HELP_ogRestoreBootLoaderImage="" +MSG_HELP_ogRestoreDiskImage="Restaura una imagen de un disco completo." +MSG_HELP_ogRestoreEfiBootLoader="Copia el cargador de arranque de la partición de sistema a la partición EFI." +MSG_HELP_ogRestoreImage="Restaura una imagen de sistema operativo." +MSG_HELP_ogRestoreInfoImage="Restablece información del sistema: ACL y enlaces simbolicos" +MSG_HELP_ogRestoreMbrImage="Restaura una imagen del sector de arranque (MBR)." +MSG_HELP_ogRestoreUuidPartitions="Restaura los uuid de las particiones y la tabla de particiones." +MSG_HELP_ogSaveImageInfo="Crea un fichero con la información de la imagen." +MSG_HELP_ogSetLinuxName="" +MSG_HELP_ogSetPartitionActive="Establece el número de partición activa de un disco." +MSG_HELP_ogSetPartitionId="Modifica el tipo de una partición física usando el mnemónico del tipo." +MSG_HELP_ogSetPartitionSize="Establece el tamaño de una partición." +MSG_HELP_ogSetPartitionType="Modifica el identificador de tipo de una partición física." +MSG_HELP_ogSetRegistryValue="Asigna un dato a un valor del registro de Windows." +MSG_HELP_ogSetWindowsName="Asigna el nombre del cliente en el registro de Windows." +MSG_HELP_ogSetWinlogonUser="Asigna el nombre de usuario por defecto para el gestor de entrada de Windows." +MSG_HELP_ogSyncCreate="Sincroniza los datos de la partición a la imagen" +MSG_HELP_ogSyncRestore="Sincroniza los datos de la imagen a la partición" +MSG_HELP_ogTorrentStart="" +MSG_HELP_ogTypeToId="Devuelve el identificador asociado al mnemónico de tipo de partición." +MSG_HELP_ogUcastReceiverPartition="" +MSG_HELP_ogUcastSendFile="" +MSG_HELP_ogUcastSendPartition="" +MSG_HELP_ogUcastSyntax="" +MSG_HELP_ogUnhidePartition="Hace visible una partición de Windows." +MSG_HELP_ogUninstallLinuxClient="Desinstala el antiguo cliente OpenGnSys en un sistema operativo Linux." +MSG_HELP_ogUninstallWindowsClient="Desinstala el antiguo cliente OpenGnSys en un sistema operativo Windows." +MSG_HELP_ogUnlock="Desbloquea una partición tras finalizar una operación de uso exclusivo." +MSG_HELP_ogUnlockDisk="Desbloquea un disco tras finalizar una operación de uso exclusivo." +MSG_HELP_ogUnlockImage="Desbloquea una imagen tras finalizar una operación de uso exclusivo." +MSG_HELP_ogUnlockPartition=MSG_HELP_ogUnlock +MSG_HELP_ogUnmountAll="Desmonta todos los sistemas de archivos." +MSG_HELP_ogUnmountCache="Desmonta el sistema de archivos de caché local." +MSG_HELP_ogUnmount="Desmonta un sistema de archivos." +MSG_HELP_ogUnmountImage="Desmonta la imagen." +MSG_HELP_ogUnmountFs=MSG_HELP_ogUnmount +MSG_HELP_ogUnsetDirtyBit="" +MSG_HELP_ogUpdateCacheIsNecesary="Comprueba si es necesario actualizar una archivo en la cache local." +MSG_HELP_ogUpdatePartitionTable="Actualiza información de la tabla de particiones del disco." +MSG_HELP_ogUuidChange="Reemplaza el UUID de un sistema de ficheros." +MSG_HELP_ogWaitSyncImage="" +MSG_HELP_ogWindowsBootParameters="" +MSG_HELP_ogWindowsRegisterPartition="" + +# Scripts +MSG_HELP_configureOs="Post-configura de arranque del sistema" +MSG_HELP_createBaseImage="Genera imagen básica de la partición" +MSG_HELP_createDiffImage="Genera imagen diferencial de la partición respecto a la imagen básica" +MSG_HELP_installOfflineMode="Prepara el equipo cliente para el modo offline." +MSG_HELP_partclone2sync="Convierte imagen de partclone en imagen sincronizable." +MSG_HELP_restoreBaseImage="Restaura una imagen básica en una partición" +MSG_HELP_restoreDiffImage="Restaura una imagen diferencial en una partición" +MSG_HELP_updateCache="Realiza la actualización de la caché" + +# Mensajes de descripción breve de la interfaz. +MSG_INTERFACE_START="[START Interface] Ejecutar comando: " +MSG_INTERFACE_END="[END Interface] Comando terminado con este código: " + +# Mensajes de scripts. +MSG_SCRIPTS_START=" INICIO scripts: " +MSG_SCRIPTS_END=" FIN scripts: " +MSG_SCRIPTS_TASK_END="Fin de la tarea" + +MSG_SCRIPTS_TASK_SLEEP="Esperando para iniciar" +MSG_SCRIPTS_TASK_START="Iniciando" +MSG_SCRIPTS_TASK_ERR="Error" + +# Script createImage. +MSG_SCRIPTS_FILE_RENAME=" Renombrar fichero-imagen previo: " +MSG_SCRIPTS_CREATE_SIZE=" Calcular espacio (KB) requerido para almacenarlo y el disponible: " + +# Script updateCache. +MSG_SCRIPTS_UPDATECACHE_DOUPDATE="Comprobar si es necesario actualizar el fichero imagen " +MSG_SCRIPTS_UPDATECACHE_CHECKSIZECACHE="Comprobar que el tamaño de la caché es mayor que el fichero a descargar." +# Script updateCache: para las imágenes sincronizadas tipo dir. +MSG_SCRIPTS_UPDATECACHE_CHECKSIZEDIR="Calcular el tamaño de la imagen." +MSG_SCRIPTS_UPDATECACHE_CHECKSIZEIMG="Comprobar si la imagen del repositorio es mayor que la de la caché." +MSG_SCRIPTS_UPDATECACHE_IFNOTCACHEDO="Comprobar el espacio libre de la caché y actuar según engine.cfg" + +MSG_SCRIPTS_UPDATECACHE_CHECKMCASTSESSION="Comprobando sesión Multicast: ServidorMcast:PuertoDatos" + +# interface sustituye temporalmente al scritp restore +MSG_SCRIPTS_CHECK_ENGINE="Analizar proceso a realizar según engine.cfg" +MSG_SCRIPTS_MULTICAST_PRECHECK_PORT="Determinar puerto principal y auxiliar Multicast." +MSG_SCRIPTS_MULTICAST_CHECK_PORT="Comprobar puertos de sesión y datos" +MSG_SCRIPTS_MULTICAST_REQUEST_PORT="Solicitar la apertura: " +MSG_SCRIPTS_OS_CONFIGURE="Iniciar la configuración del sistema restaurado" + +# TIME MESSAGES +MSG_SCRIPTS_TIME_TOTAL="tiempo total del proceso" +MSG_SCRIPTS_TIME_PARTIAL="tiempo parcial del subproceso" + +# HTTPLOG +MSG_HTTPLOG_NOUSE="No apague este ordenador por favor" + +# Mensajes sincronizadas +MSG_SYNC_RESIZE="Redimensiona la imagen al tamaño necesario" +MSG_SYNC_RESTORE="Trae el listado ficheros y baja la imagen" +MSG_SYNC_DELETE="Diferencial: Borra archivos antiguos" +MSG_SYNC_SLEEP="Espera que se monte/reduzca la imagen" +# Mensajes sincronizadas complementarios a errores +MSG_SYNC_DIFFERENTFS="El sistema de ficheros de destino no coincide con el de la imagen" +MSG_SYNC_EXTENSION="Las extensiones de la imagenes deben ser img o diff" +MSG_SYNC_NOCHECK="La imagen esta montada por otro proceso, no podemos comprobarla" + +MSG_RESTORE="Restaura la imagen en" + diff --git a/client/scripts/configureOs.py b/client/scripts/configureOs.py new file mode 100755 index 0000000..bd61865 --- /dev/null +++ b/client/scripts/configureOs.py @@ -0,0 +1,159 @@ +#!/usr/bin/python3 + +#/** +# configureOs +#@brief Script para realizar la configuracion del sistema operativo restaurado. +#@param 1 disco +#@param 2 particion +#@return +#@TODO comprobar que el tipo de particion corresponde con el sistema de archivos. +#@exception OG_ERR_FORMAT # 1 formato incorrecto. +#*/ ## + +import sys +import os +import os.path +import subprocess + +import ogGlobals +import SystemLib +import DiskLib +import FileSystemLib +import NetLib +import InventoryLib +import BootLib +import UEFILib +import PostConfLib +import FileLib + +## el código bash original hace: +## [ -f $DEVICECFG ] && source $DEVICECFG +## pero luego no utiliza ninguna de las variables definidas en el archivo... + +disk = sys.argv[1] +par = sys.argv[2] + +# Si el sistema de archivos no esta extendido, ampliarlo al tamaño de su partición. +partsize = DiskLib.ogGetPartitionSize (disk, par) +if not partsize: sys.exit (1) +fssize = FileSystemLib.ogGetFsSize (disk, par) +if fssize < partsize: + print ('Extender sistema de archivos.') + FileSystemLib.ogExtendFs (disk, par) + +# Si no existe partición activa, activar este sistema. +flagactive = DiskLib.ogGetPartitionActive (disk) +if not flagactive: DiskLib.ogSetPartitionActive (disk, par) + +# Si el sistema de archivos es de solo lectura, no hacer la post-configuración. +mntdir = FileSystemLib.ogMount (disk, par) +if not FileSystemLib.ogIsWritable (disk, par): + print ('AVISO: sistema de archivos de solo lectura, no se ejecuta postconfiguración.') + sys.exit (0) + +# Nombre del cliente. +host = NetLib.ogGetHostname() + +# Post-configuración personalizada para cada tipo de sistema operativo. +ostype = InventoryLib.ogGetOsType (disk, par) +if 'Windows' == ostype: + if not host: host = 'pc' + BootLib.ogSetWindowsName (disk, par, host) # Cambiar nombre en sistemas Windows. + if InventoryLib.ogIsEfiActive(): # Si es UEFI copio el cargador de arranque a la partición EFI e instalo Grub. + UEFILib.ogRestoreEfiBootLoader (disk, par) + esp = DiskLib.ogGetEsp() + if not esp: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, 'ESP') + sys.exit (1) + efidisk, efipart = esp.split() + BootLib.ogGrubInstallMbr (efidisk, efipart, 'TRUE') + else: + BootLib.ogFixBootSector (disk, par) # Configurar el boot sector de la partición Windows. + BootLib.ogWindowsBootParameters (disk, par) # Configurar el gestor de arranque de Windows XP/Vista/7. + BootLib.ogWindowsRegisterPartition (disk, par, 'C', disk, par) # Registrar en Windows que la partición indicada es su nueva unidad C:\ + PostConfLib.ogConfigureOgagent (disk, par) # Configurar nuevo agente OGAgent. + path1 = FileLib.ogGetPath (file=f'{mntdir}/windows/ogAdmWinClient.exe') + path2 = FileLib.ogGetPath (file=f'{mntdir}/winnt/ogAdmWinClient.exe') + if path1 or path2: # Eliminar el antiguo cliente de Windows. + PostConfLib.ogInstallMiniSetup (disk, par, 'postconf.cmd') + PostConfLib.ogUninstallWindowsClient (disk, par, 'postconf.cmd') + +elif 'Linux' == ostype: + BootLib.ogConfigureFstab (disk, par) # Configuro fstab: particion de Swap y si es UEFI además la partición EFI. + if InventoryLib.ogIsEfiActive(): # Si es UEFI instalo Grub en la partición EFI + esp = DiskLib.ogGetEsp() + efidisk, efipart = esp.split() + BootLib.ogGrubInstallMbr (efidisk, efipart, 'TRUE') + BootLib.ogGrubInstallPartition (disk, par) ## Instala (no configura) el codigo de arranque del Grub en la partición (no lo configura, se mantiene el original de la imagen) + find_out = subprocess.run (['find', f'{mntdir}/usr/sbin', f'{mntdir}/sbin', f'{mntdir}/usr/local/sbin', '-name', 'ogAdmLnxClient', '-print'], capture_output=True, text=True).stdout + if find_out: + PostConfLib.ogUninstallLinuxClient (disk, par) # Eliminar el antiguo cliente de Linux. + PostConfLib.ogConfigureOgagent (disk, par) # Configurar nuevo agente OGAgent. + ## Modificar el nombre del equipo + print (f'Asignar nombre Linux "{host}".') + etc = FileLib.ogGetPath (src=f'{disk} {par}', file='/etc') + if os.path.isdir (etc): + with open (f'{etc}/hostname', 'w') as fd: + fd.write (f'{host}\n') + +elif 'MacOS' == ostype: + open (f'{mntdir}/osxpostconf', 'a').close() ## touch, Fichero indicador de activación de postconfiguración. + + # Crear fichero de configuración del servicio de arranque. + with open (f'{mntdir}/Library/LaunchDaemons/es.opengnsys.postconfd.plist', 'w') as fd: + fd.write ('\n') + fd.write ('\n') + fd.write ('\t\n') + fd.write ('\t\tLabel\n') + fd.write ('\t\tes.opengnsys.postconfd.sh\n') + fd.write ('\t\tProgramArguments\n') + fd.write ('\t\t\n') + fd.write ('\t\t\t/var/root/postconfd.sh\n') + fd.write ('\t\t\n') + fd.write ('\t\tRunAtLoad\n') + fd.write ('\t\t\n') + fd.write ('\t\tStandardOutPath\n') + fd.write ('\t\t/var/log/postconfd.log\n') + fd.write ('\t\tStandardErrorPath\n') + fd.write ('\t\t/var/log/postconfd.err\n') + fd.write ('\t\tDebug\n') + fd.write ('\t\t\n') + fd.write ('\t\n') + fd.write ('\n') + + # Programa de inicio que será ejecutado en el arranque de Mac OS X. + with open (f'{mntdir}/var/root/postconfd.sh', 'w') as fd: + fd.write ('#!/bin/bash\n') + fd.write ('# postconfd - ejecución de scripts de inicio.\n') + fd.write ('\n') + fd.write ('# Ejecutar postconfiguración si existe el fichero indicador.\n') + fd.write ('if [ -e /osxpostconf ]; then\n') + fd.write ('\t# Tomar nombre del equipo.\n') + fd.write (f'\tHOST="{host}"\n') + fd.write ('\tif [ -z "$HOST" ]; then\n') + fd.write ('\t\t# Si no hay nombre asociado, activar la red para obtener datos del DHCP.\n') + fd.write ('\t\tsource /etc/rc.common\n') + fd.write ('\t\tCheckForNetwork\n') + fd.write ('\t\twhile [ "$NETWORKUP" != "-YES-" ]; do\n') + fd.write ('\t\t\tsleep 5\n') + fd.write ('\t\t\tNETWORKUP=\n') + fd.write ('\t\t\tCheckForNetwork\n') + fd.write ('\t\tdone\n') + fd.write ('\t\t# Componer nombre del equipo a partir de datos del DHCP.\n') + fd.write ('\t\tIP=$(ifconfig en0 inet | awk \'{if ($1=="inet") print $2}\')\n') + fd.write ('\t\tHOST="mac-$(echo ${IP//./-} | cut -f3-4 -d-)"\n') + fd.write ('\tfi\n') + fd.write ('\t# Asignar nombre del equipo.\n') + fd.write ('\tscutil --set ComputerName "$HOST"\n') + fd.write ('\tscutil --set LocalHostName "$HOST"\n') + fd.write ('\tscutil --set HostName "$HOST"\n') + fd.write ('\thostname "$HOST"\n') + fd.write ('\t# Descromprimir ficheros de versión para obtener inventario de aplicaciones.\n') + fd.write ('\tfind /Applications -type d -name "*.app" -prune -exec \\n') + fd.write ('\t ditto --nopreserveHFSCompression "{}/Contents/version.plist" "{}/Contents/version.plist.uncompress"\n') + fd.write ('\trm -f /osxpostconf # Borrar fichero indicador de psotconfiguración\n') + fd.write ('fi\n') + os.chmod (f'{mntdir}/var/root/postconfd.sh', 0o700) # Dar permiso de ejecución. + PostConfLib.ogConfigureOgagent (disk, par) # Configurar nuevo agente OGAgent de sistema operativo. + +sys.exit (0) diff --git a/client/scripts/deployImage.py b/client/scripts/deployImage.py new file mode 100755 index 0000000..e5a2ec2 --- /dev/null +++ b/client/scripts/deployImage.py @@ -0,0 +1,182 @@ +#!/usr/bin/python3 + +import os +import sys +import time +import subprocess +import shutil + +import ogGlobals +import SystemLib +import FileSystemLib +import DiskLib +import NetLib +import StringLib +import FileLib +import ImageLib + +#Descripcion: +# Si Repositorio es el global (REPO) realiza un deploy. +# Si Repositorio es local (CACHE) realiza un restoreImage CACHE +# El deploy, si detecta que el cliente no tiene una CACHE o no tiene espacio suficiente consulta el engine.cfg RESTOREPROTOCOLNOCACHE + +prog = os.path.basename (sys.argv[0]) + +def main (repo, imgname, disk, par, proto='UNICAST', protoopt=''): + if repo: repo = repo.upper() + else: repo = 'REPO' + proto = proto.upper() + protoopt = protoopt.upper() + time1 = time.time() + +# Clear temporary file used as log track by httpdlog +# Limpia los ficheros temporales usados como log de seguimiento para httpdlog + with open (ogGlobals.OGLOGCOMMAND, 'w') as fd: + fd.write (' ') + if 'EjecutarScript' != SystemLib.ogGetCaller(): + with open (ogGlobals.OGLOGSESSION, 'w') as fd: + fd.write ('') + +# Registro de inicio de ejecución + SystemLib.ogEcho (['log', 'session'], None, f'[1] {ogGlobals.lang.MSG_SCRIPTS_START} {prog} {" ".join(sys.argv)}') + +# Si el origen(pariticion) esta bloqueada salir. + if FileSystemLib.ogIsLocked (disk, par): + SystemLib.ogRaiseError ('session', ogGlobals.OG_ERR_LOCKED, f'{ogGlobals.lang.MSG_PARTITION}, {disk} {par}') + sys.exit (1) + + SystemLib.ogEcho (['log', 'session'], None, f'{ogGlobals.lang.MSG_HELP_ogUnmount} {disk} {par}') + FileSystemLib.ogUnmount (disk, par) + +# Valor por defecto para el repositorio. + mode = None + if NetLib.ogGetIpAddress() == repo or 'CACHE' == repo: + mode = 'CACHE' + else: + if StringLib.ogCheckIpAddress (repo) or 'REPO' == repo: + if not NetLib.ogChangeRepo (repo): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, repo) + sys.exit (1) + mode = 'REPO' + +#Informacioin previa de la imagen + if mode: + imgpath = FileLib.ogGetPath (src=mode, file=f'{imgname}.img') + else: + imgpath = FileLib.ogGetPath (file=f'{imgname}.img') + imgos = ImageLib.ogGetImageInfo (imgpath) + #if imgos == 1: sys.exit(SystemLib.ogRaiseError("session", OG_ERR_NOTFOUND, f"{repo} {imgname}")) + #elif imgos == 5: sys.exit(SystemLib.ogRaiseError("session", OG_ERR_IMAGEFILE, f"{repo} {imgname}")) + #elif imgos != 0: sys.exit(SystemLib.ogRaiseError("session", OG_ERR_GENERIC)) + if not imgos: + SystemLib.ogRaiseError ('session', ogGlobals.OG_ERR_GENERIC, '') + sys.exit (1) + + imgsize = os.path.getsize (imgpath) // 1024 + SystemLib.ogEcho (['log', 'session'], None, f'[1] REPO={repo} IMG-FILE={imgname}.img SIZE={imgsize} (KB) METADATA={imgos}') + +# Procesar repositorio. + if 'CACHE' == mode: + nextoperation = 'CACHE' + elif 'REPO' == mode: + if 'MULTICAST-DIRECT' == proto: + nextoperation = 'MULTICAST' + elif 'UNICAST-DIRECT' == proto: + nextoperation = 'UNICAST' + # Si protocolo es torrent|torrent-cache o multicast|multicast-cache + elif proto in ['TORRENT', 'TORRENT-CACHE', 'MULTICAST', 'MULTICAST-CACHE', 'UNICAST', 'UNICAST-CACHE']: + # Eliminamos CACHE o DIRECT + proto = proto.split ('-')[0] + SystemLib.ogEcho (['log', 'session'], None, f'[2] updateCache {repo} "/{imgname}.img" {proto} {protoopt}') + time2 = time.time() + retval = subprocess.run (['updateCache.py', repo, f'/{imgname}.img', proto, protoopt]).returncode + time2 = time.time() - time2 + SystemLib.ogEcho (['log', 'session'], None, f' [ ] {ogGlobals.lang.MSG_SCRIPTS_TIME_PARTIAL} updateCache {int (time2/60)}m {int (time2%60)}s') + if 0 == retval: + SystemLib.ogEcho (['log', 'session'], None, '[50] updateCache (OK)') + nextoperation = 'CACHE' + elif retval in [15, 16]: + # no se permite usar la cache (no existe(15) o no espacio sufiente (16). Se consulta engine.cfg para RESTOREPROTOCOLNOCACHE [ multicast unicast none ] + SystemLib.ogEcho (['log', 'session'], None, f'[50] {ogGlobals.lang.MSG_ERR_NOTCACHE} ; {ogGlobals.lang.MSG_ERR_CACHESIZE}') + SystemLib.ogEcho (['log', 'session'], None, f'[50] {ogGlobals.lang.MSG_SCRIPTS_CHECK_ENGINE}: RESTOREPROTOCOLNOTCACHE={ogGlobals.RESTOREPROTOCOLNOTCACHE}') + if 'MULTICAST' == RESTOREPROTOCOLNOTCACHE: + if 'MULTICAST' == proto: nextoperation = 'MULTICAST' + elif 'TORRENT' == proto: nextoperation = 'UNICAST' + elif 'UNICAST' == proto: nextoperation = 'UNICAST' + elif 'UNICAST' == RESTOREPROTOCOLNOTCACHE: + nextoperation = 'UNICAST' + elif RESTOREPROTOCOLNOTCACHE is None: + if 15 == retval: + SystemLib.ogEcho (['log', 'session'], None, f'[100] {ogGlobals.lang.MSG_ERR_NOTCACHE}') + SystemLib.ogRaiseError ('session', ogGlobals.OG_ERR_NOTCACHE, 'NOT CACHE') + sys.exit (1) + elif 16 == retval: + SystemLib.ogEcho (['log', 'session'], None, f'[100] {ogGlobals.lang.MSG_ERR_CACHESIZE}') + SystemLib.ogRaiseError ('session', ogGlobals.OG_ERR_CACHESIZE, 'CACHE FULL') + sys.exit (1) + elif retval in [57, 60]: + # Time-out en la transferencia multicast (El mensaje de error está enviado) + sys.exit (retval) + else: + # Error desconocido + sys.exit (retval) + else: + SystemLib.ogRaiseError ('session', ogGlobals.OG_ERR_FORMAT, f'{ogGlobals.lang.MSG_ERR_FORMAT}, {proto}') + sys.exit (1) + else: + SystemLib.ogRaiseError ('session', ogGlobals.OG_ERR_FORMAT, f'{ogGlobals.lang.MSG_ERR_FORMAT}, {repo}') + sys.exit (1) + + time3 = time.time() + +# Obtener parámetros de restauración. + if 'CACHE' == nextoperation: + params = ['CACHE', imgname, disk, par] + elif 'UNICAST' == nextoperation: + params = [repo, imgname, disk, par] + elif 'MULTICAST' == nextoperation: + params = [repo, imgname, disk, par, proto, protoopt] + +# Si existe, ejecuta script personalizado "restoreImageCustom"; si no, llama al genérico "restoreImage". + if shutil.which ('restoreImageCustom'): + SystemLib.ogEcho (['log', 'session'], None, f'[55] {ogGlobals.lang.MSG_HELP_ogRestoreImage}: restoreImageCustom {params}') + retval = subprocess.run (['restoreImageCustom'] + params).returncode + else: + SystemLib.ogEcho (['log', 'session'], None, f'[55] {ogGlobals.lang.MSG_HELP_ogRestoreImage}: restoreImage {params}') + retval = subprocess.run (['restoreImage.py'] + params).returncode + +# Mostrar resultados. + resumerestoreimage = subprocess.run (['grep', '--max-count', '1', 'Total Time:', ogGlobals.OGLOGCOMMAND], capture_output=True, text=True).stdout + SystemLib.ogEcho (['log', 'session'], None, f' [ ] {resumerestoreimage} ') +# Si la transferencia ha dado error me salgo. + if retval: + SystemLib.ogRaiseError ('session', ogGlobals.OG_ERR_IMAGE, f'{repo} {imgname}') + if SystemLib.ogGetCaller() != 'EjecutarScript': + SystemLib.ogEcho (['log', 'session'], None, f'{ogGlobals.lang.MSG_INTERFACE_END} {ogGlobals.OG_ERR_IMAGE}') + sys.exit (ogGlobals.OG_ERR_IMAGE) + + time3 = time.time() - time3 + SystemLib.ogEcho (['log', 'session'], None, f' [ ] {ogGlobals.lang.MSG_SCRIPTS_TIME_PARTIAL} : {int (time3/60)}m {int (time3%60)}s') + +# Si existe, ejecuta script personalizado de postconfiguración "configureOsCustom"; si no, llama al genérico "configureOs". + if shutil.which ('configureOsCustom'): + SystemLib.ogEcho (['log', 'session'], None, '[90] configureOsCustom') + subprocess.run (['configureOsCustom', disk, par, repo, imgname]) + else: + SystemLib.ogEcho (['log', 'session'], None, f'[90] {ogGlobals.lang.MSG_SCRIPTS_OS_CONFIGURE}') + subprocess.run (['configureOs.py', disk, par]) + + time_total = time.time() - time1 + SystemLib.ogEcho (['log', 'session'], None, f'[100] {ogGlobals.lang.MSG_SCRIPTS_TIME_TOTAL} {int (time_total/60)}m {int (time_total%60)}s') + +# Registro de fin de ejecución +# Si se ha llamado desde ejecutar script no lo muestro para no repetir. + if SystemLib.ogGetCaller() != 'EjecutarScript': + SystemLib.ogEcho (['log', 'session'], None, f'{ogGlobals.lang.MSG_INTERFACE_END} {retval}') + sys.exit (retval) + +if __name__ == '__main__': + if len (sys.argv) < 6: + SystemLib.ogRaiseError ('session', ogGlobals.OG_ERR_FORMAT, f'{ogGlobals.lang.MSG_FORMAT}: {prog} REPO imagen ndisco nparticion [ UNICAST-DIRECT|UNICAST|UNICAST-CACHE|MULTICAST-DIRECT|MULTICAST|MULTICAST-CACHE|TORRENT [opciones protocolo] ]') + sys.exit (1) + main (*sys.argv[1:]) diff --git a/client/scripts/initCache.py b/client/scripts/initCache.py new file mode 100755 index 0000000..8a45ca8 --- /dev/null +++ b/client/scripts/initCache.py @@ -0,0 +1,128 @@ +#!/usr/bin/python3 +# Scirpt de iniciación de la caché local de disco. +# Nota: se usa como base para el programa de configuración de equipos de OpenGnsys Admin). +# Formato: initCache [int_ndisk [int_npart]] {-1 | 0 | int_size} [NOMOUNT] + +import sys +import time +import subprocess + +import ogGlobals +import SystemLib +import FileSystemLib +import CacheLib +import DiskLib + +def main (NDISK, NPART, SIZE, MOUNT): + TIME1 = time.time() + +# Si tamaño=0, no hacer nada. + if 0 == SIZE: + print("No modificar la caché local.") + return True + +# Si tamaño=-1, borrar caché. + if -1 == SIZE: + print("[10] Trabajar sin caché local.") + CacheLib.ogUnmountCache() + CacheLib.ogDeleteCache() + else: + # Si la caché actual está definida en otro disco y partición, se elimina. + current_cache = CacheLib.ogFindCache() + if current_cache and f"{NDISK} {NPART}" != current_cache: + print("[10] Detectada otra caché, eliminarla") + CacheLib.ogUnmountCache() + CacheLib.ogDeleteCache() + + # Tomamos el tamaño actual. Si no existe cache será 0. + CACHESIZE = CacheLib.ogGetCacheSize() + if not CACHESIZE: CACHESIZE = 0 ## may be None + + # Error si tamaño definido no es >0. + if SIZE <= 0: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, f"!({SIZE}>0)") + return False + + # Si no existe caché o si cambia su tamaño, crearla. + if SIZE != CACHESIZE: + print("[10] Crear partición de caché local.") + CacheLib.ogUnmountCache() + print (f'nati: calling ogCreateCache ({NDISK}, {NPART}, {SIZE})') + CacheLib.ogCreateCache (NDISK, NPART, SIZE) + DiskLib.ogUpdatePartitionTable() + + # Si caché no montada y no formateada o cambia el tamaño: formatear. + cache = CacheLib.ogFindCache() + if not cache: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTCACHE, '') + return False + cache = cache.split() + if not FileSystemLib.ogIsFormated (cache[0], cache[1]) or SIZE != CACHESIZE: + print("[50] Formatear caché local.") + CacheLib.ogFormatCache() + print("[70] Comprobar montaje de caché local.") + # Si error al montar, chequear sistema de archivos y volver a montar. + if not CacheLib.ogMountCache(): + print("[80] Comprobar consistencia y volver a montar caché local.") + FileSystem.ogCheckFs (cache[0], cache[1]) + if not CacheLib.ogMountCache(): + return False + + # Dejar desmontada la caché si se ha solicitado. + if not MOUNT: + print("[90] Dejar desmontada la caché local.") + CacheLib.ogUnmountCache() + +# Duración del proceso. + TIME = time.time() - TIME1 + print (f"[100] Duración de la operación {int(TIME // 60)}m {int(TIME % 60)}s") + return True + +if __name__ == "__main__": + PROG = sys.argv[0] + EXECFORMAT = f"{PROG} [int_ndisk [int_npart]] {{-1 | 0 | int_size}} [NOMOUNT]" + + args = sys.argv[1:] + if args and 'NOMOUNT' == args[-1].upper(): + MOUNT = False + args = args[:-1] + else: + MOUNT = True + + PARAMS = len (args) + try: + if 1 == PARAMS: + NDISK = 1 + NPART = 4 + SIZE = int (args[0]) + elif 2 == PARAMS: + NDISK = int (args[0]) + NPART = 4 + SIZE = int (args[1]) + elif 3 == PARAMS: + NDISK = int (args[0]) + NPART = int (args[1]) + SIZE = int (args[2]) + else: + print (f'nati: params no es ni 1 ni 2 ni 3 sino ({PARAMS})') + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, EXECFORMAT) + sys.exit (1) + except ValueError: + print (f'nati: ValueError') + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, EXECFORMAT) + sys.exit (1) + + # Si disco o partición no son mayores o iguales que 1, error. + if NDISK < 1 or NPART < 1: + print (f'nati: ndisk<1 or npart<1, ({NDISK}) ({NPART})') + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, EXECFORMAT) + sys.exit (1) + + # Si tamaño no es numérico o tamaño<-1, error. + if SIZE < -1: + print (f'nati: SIZE<-1 ({SIZE})') + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, EXECFORMAT) + sys.exit (1) + + rc = main (NDISK, NPART, SIZE, MOUNT) + sys.exit (not rc) diff --git a/client/scripts/listSoftwareInfo.py b/client/scripts/listSoftwareInfo.py new file mode 100755 index 0000000..1c2756e --- /dev/null +++ b/client/scripts/listSoftwareInfo.py @@ -0,0 +1,39 @@ +#!/usr/bin/python3 +# Scirpt de ejemplo para almacenear en fichero temporal el listado de software. +# Nota: se usa como base para el programa de recogida de listado de software de OpenGnsys Admin. +# Formato: listSoftwareInfo [-r] ndisk npart +# -r listado reducido (sin parches de Windows) + +import os +import sys +import re +import argparse + +import ogGlobals +import SystemLib +import InventoryLib +import NetLib + +prog = os.path.basename (sys.argv[0]) + +def main (disk, par, reduced): + ip = NetLib.ogGetIpAddress() + softfile = f'{ogGlobals.OGLOG}/soft-{ip}-{disk}-{par}' + software_list = InventoryLib.ogListSoftware (disk, par) + + if reduced: + software_list = [line for line in software_list if not re.search (r'(KB[0-9]{6})', line)] + + with open (softfile, 'w') as f: + for line in software_list: + f.write (f'{line}\n') + + print (softfile) + +if __name__ == '__main__': + parser = argparse.ArgumentParser (add_help=False) + parser.add_argument ('-r', '--reduced', action='store_true', default=False) + parser.add_argument ('disk') + parser.add_argument ('par') + args = parser.parse_args() + main (args.disk, args.par, args.reduced) diff --git a/client/scripts/restoreImage.py b/client/scripts/restoreImage.py new file mode 100644 index 0000000..a5f0ff6 --- /dev/null +++ b/client/scripts/restoreImage.py @@ -0,0 +1,118 @@ +#!/usr/bin/python3 +#/** +#@file restoreImage.py +#@brief Script de ejemplo para restaurar una imagen. +#@param $1 Repositorio (CACHE, REPO o dirección IP) +#@param $2 Nombre canónico de la imagen (sin extensión) +#@param $3 Número de disco +#@param $4 Número de particion +#@param $5 Protocolo (UNICAST, UNICAST-DIRECT, MULTICAST o MULTICAST-DIRECT) +#@param $6 Opciones del protocolo +#@ejemplo restoreImage.py REPO imgname 2 2 unicast +#@exception OG_ERR_FORMAT 1 formato incorrecto. +#@exception OG_ERR_NOTFOUND 2 cambio de repositorio: repositorio no encontrado +#@exception OG_ERR_NOTFOUND 2 fichero de imagen o partición no detectados. +#@exception $OG_ERR_MCASTRECEIVERFILE 57 Error en la recepción Multicast de un fichero +#@exception $OG_ERR_PROTOCOLJOINMASTER 60 Error en la conexión de una sesión Unicast|Multicast con el Master +#**/ + +import os +import os.path +import sys +import re +import time + +import ogGlobals +import SystemLib +import NetLib +import StringLib +import FileLib +import ImageLib +import ProtocolLib + +t0 = time.time() +prog = os.path.basename (sys.argv[0]) + +#Load engine configurator from engine.cfg file. +#Carga el configurador del engine desde el fichero engine.cfg +# Valores por defecto: #IMGPROG="partclone" ; #IMGCOMP="lzop" ; #IMGEXT="img" #IMGREDUCE="TRUE" +## (ogGlobals se encarga) + +# Clear temporary file used as log track by httpdlog +# Limpia los ficheros temporales usados como log de seguimiento para httpdlog +open (ogGlobals.OGLOGCOMMAND, 'w').close() +if SystemLib.ogGetCaller() not in ['deployImage', 'restoreImageCustom']: + open (ogGlobals.OGLOGSESSION, 'w').close() + +SystemLib.ogEcho (['log', 'session'], None, f'[1] {ogGlobals.lang.MSG_SCRIPTS_START} {prog} ({sys.argv})') + +# Procesar parámetros de entrada +print (f'argv ({sys.argv}) len ({len (sys.argv)})') +if len (sys.argv) < 5: + SystemLib.ogRaiseError ('session', ogGlobals.OG_ERR_FORMAT, f'{ogGlobals.lang.MSG_FORMAT}: {prog} REPO|CACHE imagen ndisco nparticion [ UNICAST|MULTICAST opciones protocolo]') + sys.exit (1) + +_, repo, imgname, disk, par, *other = sys.argv +proto = other[0].upper() if len (other) > 0 else 'UNICAST' +protoopt = other[1] if len (other) > 1 else '' +repo = repo.upper() +# Si MCASTWAIT menos que tiempo de espera del servidor lo aumento +MCASTWAIT = ogGlobals.MCASTWAIT +if ':' in protoopt: + port, wait = protoopt.split (':') +else: + port, wait = ('', '') +if proto.startswith ('MULTICAST') and re.match (r'^-?\d+$', wait): + if int (MCASTWAIT or 0) < int (wait): + MCASTWAIT = int (wait) + 5 +imgtype = 'img' +print (f'repo ({repo}) imgname ({imgname}) disk ({disk}) par ({par}) proto ({proto}) protoopt ({protoopt}) MCASTWAIT ({MCASTWAIT})') + +# Si es una ip y es igual a la del equipo restaura desde cache +if repo == NetLib.ogGetIpAddress(): repo = 'CACHE' +# Si es una ip y es distinta a la del recurso samba cambiamos de REPO. +if StringLib.ogCheckIpAddress (repo) or 'REPO' == repo: + # Si falla el cambio -> salimos con error repositorio no valido + if not NetLib.ogChangeRepo (repo): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, repo) + sys.exit (1) + REPO = 'REPO' + +# Comprobar que existe la imagen del origen. +imgfile = FileLib.ogGetPath (repo, f'{imgname}.{imgtype}') +imgdir = FileLib.ogGetParentPath (repo, imgname) +print (f'imgfile ({imgfile}) imgdir ({imgdir})') +if not imgfile or not imgdir: + SystemLib.ogRaiseError ('session', ogGlobals.OG_ERR_NOTFOUND, f'{repo}, {imgname}') + sys.exit (1) + +# Procesar protocolos de transferencia. +retval = 0 +if proto in ['UNICAST', 'UNICAST-DIRECT']: + SystemLib.ogEcho (['log', 'session'], None, f'[40] ogRestoreImage {repo} {imgname} {disk} {par} UNICAST') + retval = SystemLib.ogExecAndLog ('command', ImageLib.ogRestoreImage, repo, imgname, disk, par) +elif proto in ['MULTICAST', 'MULTICAST-DIRECT']: + tool = ImageLib.ogGetImageProgram ('REPO', imgname) + if not tool: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_IMAGE, f'could not get tool used for image {imgname}') + sys.exit (1) + + compress = ImageLib.ogGetImageCompressor ('REPO', imgname) + if not compress: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_IMAGE, f'could not get compressor used for image {imgname}') + sys.exit (1) + + SystemLib.ogEcho (['log', 'session'], None, f'[40] ogMcastReceiverPartition {disk} {par} {port} {tool} {compress}') + if not ProtocolLib.ogMcastRequest (f'{imgname}.img', protoopt): + sys.exit (1) + retval = SystemLib.ogExecAndLog ('command', ProtocolLib.ogMcastReceiverPartition, disk, par, port, tool, compress) +else: + SystemLib.ogRaiseError ('session', ogGlobals.OG_ERR_FORMAT, f'{ogGlobals.lang.MSG_FORMAT}: {prog} REPO|CACHE imagen ndisco nparticion [ UNICAST|MULTICAST opciones protocolo]') + sys.exit (1) + +t = time.time() - t0 +SystemLib.ogEcho (['log', 'session'], None, f'[100] Duracion de la operacion {int (t/60)}m {int (t%60)}s') + +# Código de salida del comando prinicpal de restauración. +sys.exit (retval) + diff --git a/client/scripts/updateBootCache.py b/client/scripts/updateBootCache.py new file mode 100755 index 0000000..854b9c9 --- /dev/null +++ b/client/scripts/updateBootCache.py @@ -0,0 +1,62 @@ +#!/usr/bin/python3 + +import os +import shutil + +import ogGlobals +import SystemLib +import CacheLib + +#/** +# updateBootCache +#@brief acelerador arranque pxe. incorpora a la cache el initrd y el kernel. +#@param 1 +#@param ejemplo: +#@return +#@exception OG_ERR_NOTCACHE # 15 si cache no existe 15 +#@exception OG_ERR_NOTFOUND=2 # Fichero o dispositivo no encontrado. +#@note +#@todo: +#*/ ## + +oglivedir = os.environ.get ('oglivedir', 'ogLive') +ogbtftp = f'/opt/oglive/tftpboot/{oglivedir}' +ogbcache = f'{ogGlobals.OGCAC}/boot/{oglivedir}' + +#control de errores +if not os.path.isdir (ogbtftp): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, ogbtftp) + os.exit (1) + +if not CacheLib.ogMountCache(): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTCACHE, 'CACHE') + os.exit (1) + +os.makedirs (ogbcache, exist_ok=True) + + # comparamos los del server +with open (f'{ogbtftp}/ogvmlinuz.sum', 'rb') as fd: servervmlinuz = fd.read() +with open (f'{ogbtftp}/oginitrd.img.sum', 'rb') as fd: serverinitrd = fd.read() + + #comparamos los de la cache +with open (f'{ogbcache}/ogvmlinuz.sum', 'rb') as fd: cachevmlinuz = fd.read() +with open (f'{ogbcache}/oginitrd.img.sum', 'rb') as fd: cacheinitrd = fd.read() + +print (f'MD5 on SERVER: {servervmlinuz} {serverinitrd}') +print (f'MD5 on CACHE: {cachevmlinuz} {cacheinitrd}') + +do_reboot = '0' +if cachevmlinuz != servervmlinuz: + print ('ogvmlinuz updating') + shutil.copy2 (f'{ogbtftp}/ogvmlinuz', f'{ogbcache}/ogvmlinuz') + shutil.copy2 (f'{ogbtftp}/ogvmlinuz.sum', f'{ogbcache}/ogvmlinuz.sum') + do_reboot = '1' + +if cacheinitrd != serverinitrd: + print ('oginitrd updating') + shutil.copy2 (f'{ogbtftp}/oginitrd.img', f'{ogbcache}/oginitrd.img') + shutil.copy2 (f'{ogbtftp}/oginitrd.img.sum', f'{ogbcache}/oginitrd.img.sum') + do_reboot = '1' + +print (do_reboot) +os.exit (0) diff --git a/client/scripts/updateCache.py b/client/scripts/updateCache.py new file mode 100644 index 0000000..179781c --- /dev/null +++ b/client/scripts/updateCache.py @@ -0,0 +1,219 @@ +#!/usr/bin/python3 +#/** +# updateCache +#@brief Actualiza la cache del cliente con imagen o fichero iso. +#@param 1 REPO Origen del fichero. -accesible por nfs-samba- +#@param 2 str_fichero nombre del fichero a actualizar. +#@param 3 str_protoco. TORRENT | MULTICAST | UNICAST. +#@param 4 str_opcionesprotocolo +#@param 4 str_opcionesupdatecache +#@ejemplo: updateCache.py REPO imgname.img UNICAST 8042:42 +#@return +#@exception OG_ERR_FORMAT formato incorrecto. +#@exception OG_ERR_NOTCACHE No existe cache -15- +#@exception $OG_ERR_CACHESIZE Tamaño de la paticion menor al archivo a descargar -16- +#@exception $OG_ERR_MCASTRECEIVERFILE Error en la recepción Multicast de un fichero -57- +#@exception $OG_ERR_PROTOCOLJOINMASTER Error en la conexión de una sesión Unicast|Multicast con el Master -60- +#@note +#@todo: +#*/ ## + +import os.path +import sys +import re +import time +import subprocess +import shutil +import glob +import random + +import ogGlobals +import SystemLib +import StringLib +import NetLib +import CacheLib +import FileLib +import ProtocolLib +import FileSystemLib + +prog = os.path.basename (sys.argv[0]) +print (f'argv ({sys.argv}) len ({len (sys.argv)})') +if len (sys.argv) < 3: + SystemLib.ogRaiseError ('session', ogGlobals.OG_ERR_FORMAT, f'{ogGlobals.lang.MSG_FORMAT}: {prog} str_REPO _str_Relative_Path_OGIMG_with_/ PROTOCOLO OPCIONES_PROTOCOLO OPCIONES_UPDATECACHE') + sys.exit (1) +_, repositorio, path, protocolo, *other = sys.argv +optprotocolo = other[0] if len (other) > 0 else '' +cacheopts = other[1] if len (other) > 1 else '' + +if 'RSYNC' == protocolo: + raise Exception ('synchronised images are no longer supported') + +#Carga del configurador del engine +## (ogGlobals se encarga) + +# Clear temporary file used as log track by httpdlog +# Limpia los ficheros temporales usados como log de seguimiento para httpdlog +open (ogGlobals.OGLOGCOMMAND, 'w').close() +if SystemLib.ogGetCaller() not in ['deployImage', 'restoreBaseImage', 'restoreDiffImage']: + open (ogGlobals.OGLOGSESSION, 'w').close() + SystemLib.ogEcho (['log', 'session'], None, f'[1] {ogGlobals.lang.MSG_SCRIPTS_START} {prog} {sys.argv}') + +# Si MCASTWAIT menos que tiempo de espera del servidor lo aumento +MCASTWAIT = ogGlobals.MCASTWAIT +if ':' in optprotocolo: + port, wait, *other = optprotocolo.split (':') +else: + port, wait = ('', '') +if protocolo.startswith ('MULTICAST') and re.match (r'^-?\d+$', wait): + if int (MCASTWAIT or 0) < int (wait): + MCASTWAIT = int (wait) + 5 +# Unidad organizativa. +## (no longer supported) +#print (f'repositorio ({repositorio}) path ({path}) protocolo ({protocolo}) optprotocolo ({optprotocolo}) cacheopts ({cacheopts}) MCASTWAIT ({MCASTWAIT})') + +# Si es una ip y es distinta a la del recurso samba cambiamos de REPO. +if StringLib.ogCheckIpAddress (repositorio) or 'REPO' == repositorio: + if not NetLib.ogChangeRepo (repositorio): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, repositorio) + sys.exit (1) + repositorio = 'REPO' +repoip = NetLib.ogGetRepoIp() +SystemLib.ogEcho (['log', 'session'], None, f'{repositorio} {repoip} {protocolo} {optprotocolo}') + +# Si el repositorio local CACHE no existe error 15. +if not CacheLib.ogFindCache(): + SystemLib.ogRaiseError ('session', ogGlobals.OG_ERR_NOTCACHE, 'CACHE') + sys.exit (1) + +# comprobar si la imagen existe (.img, .img.diff o directorio) +repofile = FileLib.ogGetPath ('REPO', f'/{path}') +if not os.path.exists (repofile): + SystemLib.ogRaiseError ('session', ogGlobals.OG_ERR_NOTFOUND, f'REPO /{path}') + sys.exit (1) + +SystemLib.ogEcho (['log', 'session'], None, ogGlobals.lang.MSG_SCRIPTS_UPDATECACHE_DOUPDATE) +# Distingo si es monolitica o sincronizable +f = subprocess.run (['file', repofile], capture_output=True, text=True).stdout.lower() +if ' btrfs filesystem ' in f or ' ext4 filesystem ' in f or ' directory' in f: + raise Exception ('synchronised images are no longer supported') +rc = ProtocolLib.ogUpdateCacheIsNecesary (repositorio, path, protocolo) +# si rc=True: actualizamos; si rc=False: no actualizamos (exit 0); si rc=None: exit error +if rc == True: pass ## es necesario actualizar +elif rc == False: sys.exit (0) ## no es necesario actualizar +elif rc == None: sys.exit (ogGlobals.OG_ERR_UPDATECACHE) ## hubo errores + +SystemLib.ogEcho (['log', 'session'], None, ogGlobals.lang.MSG_SCRIPTS_UPDATECACHE_CHECKSIZECACHE) +cachesize = CacheLib.ogGetCacheSize() +cache_disk, cache_par = CacheLib.ogFindCache().split() +cachesizefree = FileSystemLib.ogGetFreeSize (cache_disk, cache_par) +path_repo = FileLib.ogGetPath ('REPO', path) +filesize = int (subprocess.run (['ls', '-sk', path_repo], capture_output=True, text=True).stdout.split()[0]) +realfilesize = subprocess.run (['stat', '--format', '%s', repofile], capture_output=True, text=True).stdout +realfilesize = int (int (realfilesize) / 1024) + +# La sincronizada, si existe la imagen en cache el espacio necesario +# es la nueva menos lo que ocupa la que ya hay. +sizerequired = filesize + +#ERROR CACHESIZE 16 (tamanyo de la CACHE insuficiente) +if sizerequired >= cachesize: + SystemLib.ogEcho (['log', 'session'], None, f'{ogGlobals.lang.MSG_WARNING}: {ogGlobals.lang.MSG_ERR_CACHESIZE}: {path} = {sizerequired} > CACHE = {cachesize}') + SystemLib.ogRaiseError ('session', ogGlobals.OG_ERR_CACHESIZE, 'CACHE') + sys.exit (1) + + +#ERROR CACHESIZE 16 (Espacio libre en CACHE insuficiente) +if sizerequired >= cachesizefree: + SystemLib.ogEcho (['log', 'session'], None, f'{ogGlobals.lang.MSG_SCRIPTS_UPDATECACHE_IFNOTCACHEDO}: ACTIONCACHEFULL={ogGlobals.ACTIONCACHEFULL}') + if 'NONE' == ogGlobals.ACTIONCACHEFULL: + SystemLib.ogEcho (['log', 'session'], None, f'{ogGlobals.lang.MSG_WARNING}: {ogGlobals.lang.MSG_ERR_CACHESIZE}: {path} = {sizerequired} > FREE SPACE CACHE = {cachesizefree}') + SystemLib.ogRaiseError ('session', ogGlobals.OG_ERR_CACHESIZE, 'CACHE FULL, NO SPACE FREE') + sys.exit (1) + elif 'FORMAT' == ogGlobals.ACTIONCACHEFULL: + SystemLib.ogEcho (['log', 'session'], None, f'[51] {ogGlobals.lang.MSG_HELP_ogFormatCache}') + CacheLib.ogUnmountCache() + CacheLib.ogFormatCache() + CacheLib.ogMountCache() + elif 'DELETE' == ogGlobals.ACTIONCACHEFULL: + SystemLib.ogEcho (['log', 'session'], None, f'[51] {ogGlobals.lang.MSG_HELP_ogDeleteTree} {ogGlobals.OGCAC}{ogGlobals.OGIMG}/*') + for d in glob.glob (f'{ogGlobals.OGCAC}{ogGlobals.OGIMG}/*'): + shutil.rmtree (d) + else: + SystemLib.ogEcho (['log', 'session'], None, f'{ogGlobals.lang.MSG_WARNING}: {ogGlobals.lang.MSG_ERR_CACHESIZE}: {path} = {filesize} > CACHE = {cachesizefree}') + SystemLib.ogRaiseError ('session', ogGlobals.OG_ERR_CACHESIZE, 'CACHE') + sys.exit (1) + +# Comprobamos que imagen cache igual a la del repo. Si sincronizada no podemos comprobar. +## nati: esto ya lo hicimos mas arriba... +rc = ProtocolLib.ogUpdateCacheIsNecesary (repositorio, path, protocolo) +# si rc=True: actualizamos; si rc=False: no actualizamos (exit 0); si rc=None: exit error +if rc == True: pass ## es necesario actualizar +elif rc == False: sys.exit (0) ## no es necesario actualizar +elif rc == None: sys.exit (ogGlobals.OG_ERR_UPDATECACHE) ## hubo errores + +CacheLib.ogMountCache() + +## Si no existe, crear subdirectorio para el fichero en la cache. +imgdir = FileLib.ogGetParentPath ('CACHE', f'/{path}') +if not imgdir: + SystemLib.ogEcho (['log', 'session'], None, f'[5] {ogGlobals.lang.MSG_HELP_ogMakeDir} "{path} {os.path.dirname (path)}".') + FileLib.ogMakeDir ('CACHE', os.path.dirname (f'/{path}')) + imgdir = ogGetParentPath ('CACHE', f'/{path}') + if not imgdir: + sys.exit (1) + +t0 = time.time() + +if 'TORRENT' == protocolo: + SystemLib.ogEcho (['log', 'session'], None, f'ogCopyFile {repositorio} {path}.torrent absolute {ogGlobals.OGCAC}/{ogGlobals.OGIMG}') + mac_digits = NetLib.ogGetMacAddress().split (':') + timewait = int ('0x' + mac_digits[4] + mac_digits[5], 16) * 120 / 65535 + if not SystemLib.ogExecAndLog ('command', FileLib.ogCopyFile, {'container':repositorio, 'file':f'{path}.torrent'}, {'file':imgdir}): + sys.exit (1) + p2pwait = random.randint (1, 121) + SystemLib.ogEcho (['log', 'session'], None, f' [ ] {ogGlobals.lang.MSG_SCRIPTS_TASK_SLEEP} : {p2pwait} seconds') + time.sleep (p2pwait) + SystemLib.ogEcho (['log', 'session'], None, f' [ ] {ogGlobals.lang.MSG_SCRIPTS_TASK_START}: ogTorrentStart CACHE {path}.torrent {optprotocolo}') + SystemLib.ogExecAndLog ('command', ProtocolLib.ogTorrentStart, 'CACHE', f'{path}.torrent', optprotocolo) + resumeupdatecache = subprocess.run (['grep', '--max-count', '1', '--before-context', '1', 'Download', ogGlobals.OGLOGCOMMAND], capture_output=True, text=True).stdout + resumeupdatecachebf = subprocess.run (['grep', '--max-count', '1', 'Download', ogGlobals.OGLOGCOMMAND], capture_output=True, text=True).stdout + if 'Download complete.' == resumeupdatecachebf: + os.unlink (imgdir + path + '.torrent.bf') +elif 'MULTICAST' == protocolo: + SystemLib.ogEcho (['log', 'session'], None, f'{ogGlobals.lang.MSG_SCRIPTS_UPDATECACHE_CHECKMCASTSESSION}: {repoip}:{port}') + time.sleep (random.randint (1, 31)) + + SystemLib.ogEcho (['log', 'session'], None, f'ogMcastRequest {path} {optprotocolo}') + if not SystemLib.ogExecAndLog ('command', ProtocolLib.ogMcastRequest, path, optprotocolo): + sys.exit (1) + + SystemLib.ogEcho (['log', 'session'], None, f'ogMcastReceiverFile {port} CACHE {path}') + if not SystemLib.ogExecAndLog ('command', ProtocolLib.ogMcastReceiverFile, sess=port, container='CACHE', file=path): + sys.exit (1) + + resumeupdatecache = subprocess.run (['grep', '--max-count', '1', '--before-context', '1', 'Transfer complete', f'{ogGlobals.OGLOGCOMMAND}.tmp'], capture_output=True, text=True).stdout + +elif 'UNICAST' == protocolo: + print (f'ogExecAndLog ("command", FileLib.ogCopyFile, {{"container":{repositorio}, "file":{path}}}, {{"file":{imgdir}}})') + SystemLib.ogExecAndLog ('command', FileLib.ogCopyFile, {'container':repositorio, 'file':path}, {'file':imgdir}) + time.sleep (5) + resumeupdatecache = subprocess.run (['grep', '--max-count', '1', '100%', f'{ogGlobals.OGLOGCOMMAND}.tmp'], capture_output=True, text=True).stdout + +elif 'RSYNC' == protocolo: + raise Exception ('synchronised images are no longer supported') + +t = time.time() - t0 +SystemLib.ogEcho (['log', 'session'], None, f' [ ] {resumeupdatecache} ') +SystemLib.ogEcho (['log', 'session'], None, f' [ ] {ogGlobals.lang.MSG_SCRIPTS_TIME_PARTIAL} updateCache {int (t/60)}m {int (t%60)}s') +SystemLib.ogEcho (['log', 'session'], None, f' [ ] {ogGlobals.lang.MSG_SCRIPTS_TASK_START} {ogGlobals.lang.MSG_HELP_ogCalculateChecksum} ') +t0 = time.time() +# Si es imagen sincronizada siempre da distinto md5. No podemos comprobar +rc = ProtocolLib.ogUpdateCacheIsNecesary (repositorio, path, protocolo) +if 'deployImage' != SystemLib.ogGetCaller(): + t = time.time() - t0 + SystemLib.ogEcho (['log', 'session'], None, f' [ ] {ogGlobals.lang.MSG_SCRIPTS_TIME_PARTIAL} {ogGlobals.lang.MSG_HELP_ogCalculateChecksum} {int (t/60)}m {int (t%60)}s') + +# si rc todavia es True: exit error; si rc=False: todo bien (exit 0); si rc=None: exit error +if rc == True: sys.exit (ogGlobals.OG_ERR_UPDATECACHE) +elif rc == False: sys.exit (0) +elif rc == None: sys.exit (ogGlobals.OG_ERR_UPDATECACHE) From c875f1c4d23d888ae3d3ae502f4ce90146fb3901 Mon Sep 17 00:00:00 2001 From: Natalia Serrano Date: Wed, 26 Feb 2025 13:02:55 +0100 Subject: [PATCH 08/18] refs #1615 remove client stuff --- client/LICENSE.en.txt | 674 - client/README.es.txt | 23 - client/bin/EACInterfaces | Bin 9682 -> 0 bytes client/bin/browser | Bin 554987 -> 0 bytes client/bin/grub-probe1.99_i686 | Bin 358216 -> 0 bytes client/bin/grub-probe1.99_x86_64 | Bin 336600 -> 0 bytes client/bin/poweroffconf | 82 - client/bin/rsync-31 | Bin 1417064 -> 0 bytes client/bin/runtest | 246 - client/etc/engine.cfg | 56 - client/etc/engine.json | 804 - client/etc/es.qmap | Bin 102808 -> 0 bytes client/etc/init/default.sh | 40 - client/etc/lang.ca_ES.UTF-8.conf | 1 - client/etc/lang.ca_ES.conf | 398 - client/etc/lang.en_GB.UTF-8.conf | 1 - client/etc/lang.en_GB.conf | 385 - client/etc/lang.es_ES.UTF-8.conf | 1 - client/etc/lang.es_ES.conf | 385 - client/etc/preinit/default.sh | 25 - client/etc/preinit/fileslinks.sh | 53 - client/etc/preinit/loadenviron.sh | 151 - client/etc/preinit/loadmodules.sh | 23 - client/etc/preinit/metadevs.sh | 28 - client/etc/preinit/mountrepo.sh | 54 - client/etc/preinit/otherservices.sh | 38 - client/etc/preinit/poweroff.sh | 40 - client/functions/ogAddCmd | 25 - client/functions/ogAddRegistryKey | 24 - client/functions/ogAddRegistryValue | 25 - client/functions/ogCalculateChecksum | 32 - client/functions/ogCalculateFullChecksum | 32 - client/functions/ogChangeRepo | 23 - client/functions/ogCheckFs | 23 - client/functions/ogCheckIpAddress | 22 - client/functions/ogCleanLinuxDevices | 23 - client/functions/ogCleanOs | 23 - client/functions/ogConfigureFstab | 23 - client/functions/ogConfigureOgagent | 23 - client/functions/ogCopyEfiBootLoader | 23 - client/functions/ogCopyFile | 63 - client/functions/ogCreateCache | 36 - client/functions/ogCreateGptPartitions | 23 - client/functions/ogCreateImage | 37 - client/functions/ogCreateImageSyntax | 25 - client/functions/ogCreatePartitionTable | 30 - client/functions/ogCreatePartitions | 23 - client/functions/ogCreateTorrent | 31 - client/functions/ogDeleteCache | 13 - client/functions/ogDeleteFile | 32 - client/functions/ogDeletePartitionTable | 22 - client/functions/ogDeleteRegistryKey | 24 - client/functions/ogDeleteRegistryValue | 24 - client/functions/ogDeleteTree | 32 - client/functions/ogDevToDisk | 22 - client/functions/ogDiskToDev | 23 - client/functions/ogEcho | 24 - client/functions/ogExecAndLog | 53 - client/functions/ogExtendFs | 23 - client/functions/ogFindCache | 22 - client/functions/ogFixBootSector | 23 - client/functions/ogFormat | 28 - client/functions/ogFormatCache | 22 - client/functions/ogFormatFs | 25 - client/functions/ogGetArch | 22 - client/functions/ogGetCacheSize | 22 - client/functions/ogGetCacheSpace | 13 - client/functions/ogGetCaller | 22 - client/functions/ogGetDiskSize | 22 - client/functions/ogGetDiskType | 22 - client/functions/ogGetEsp | 22 - client/functions/ogGetFreeSize | 24 - client/functions/ogGetFsSize | 24 - client/functions/ogGetFsType | 23 - client/functions/ogGetHivePath | 23 - client/functions/ogGetHostname | 22 - client/functions/ogGetImageCompressor | 23 - client/functions/ogGetImageInfo | 22 - client/functions/ogGetImageProgram | 23 - client/functions/ogGetImageSize | 23 - client/functions/ogGetImageType | 23 - client/functions/ogGetIpAddress | 22 - client/functions/ogGetLastSector | 30 - client/functions/ogGetMacAddress | 22 - client/functions/ogGetMountPoint | 23 - client/functions/ogGetOsType | 23 - client/functions/ogGetOsUuid | 23 - client/functions/ogGetOsVersion | 23 - client/functions/ogGetParentPath | 32 - client/functions/ogGetPartitionActive | 22 - client/functions/ogGetPartitionId | 23 - client/functions/ogGetPartitionSize | 23 - client/functions/ogGetPartitionTableType | 22 - client/functions/ogGetPartitionType | 23 - client/functions/ogGetPartitionsNumber | 22 - client/functions/ogGetPath | 36 - client/functions/ogGetRegistryValue | 24 - client/functions/ogGetRepoIp | 22 - client/functions/ogGetSerialNumber | 17 - client/functions/ogGetServerIp | 22 - client/functions/ogGetSizeParameters | 21 - client/functions/ogGrubInstallMbr | 25 - client/functions/ogGrubInstallPartition | 25 - client/functions/ogHelp | 24 - client/functions/ogHidePartition | 23 - client/functions/ogIdToType | 22 - client/functions/ogInstallFirstBoot | 24 - client/functions/ogInstallMiniSetup | 35 - client/functions/ogInstallRunonce | 29 - client/functions/ogIsDiskLocked | 22 - client/functions/ogIsEfiActive | 22 - client/functions/ogIsFormated | 23 - client/functions/ogIsImageLocked | 23 - client/functions/ogIsLocked | 23 - client/functions/ogIsMounted | 23 - client/functions/ogIsPartitionLocked | 23 - client/functions/ogIsReadonly | 23 - client/functions/ogIsRepoLocked | 17 - client/functions/ogIsVirtualMachine | 22 - client/functions/ogIsWritable | 23 - client/functions/ogListHardwareInfo | 17 - client/functions/ogListLogicalPartitions | 22 - client/functions/ogListPartitions | 22 - client/functions/ogListPrimaryPartitions | 22 - client/functions/ogListRegistryKeys | 25 - client/functions/ogListRegistryValues | 25 - client/functions/ogListSoftware | 24 - client/functions/ogLock | 23 - client/functions/ogLockDisk | 22 - client/functions/ogLockImage | 30 - client/functions/ogLockPartition | 23 - client/functions/ogMakeDir | 32 - client/functions/ogMcastReceiverFile | 39 - client/functions/ogMcastReceiverPartition | 26 - client/functions/ogMcastSendFile | 46 - client/functions/ogMcastSendPartition | 26 - client/functions/ogMcastSyntax | 34 - client/functions/ogMount | 35 - client/functions/ogMountCache | 22 - client/functions/ogMountCdrom | 22 - client/functions/ogMountFirstFs | 22 - client/functions/ogMountFs | 23 - client/functions/ogNvramActiveEntry | 22 - client/functions/ogNvramAddEntry | 24 - client/functions/ogNvramDeleteEntry | 22 - client/functions/ogNvramGetCurrent | 22 - client/functions/ogNvramGetNext | 22 - client/functions/ogNvramGetOrder | 22 - client/functions/ogNvramGetTimeout | 22 - client/functions/ogNvramInactiveEntry | 22 - client/functions/ogNvramList | 22 - client/functions/ogNvramPxeFirstEntry | 22 - client/functions/ogNvramSetNext | 22 - client/functions/ogNvramSetOrder | 16 - client/functions/ogNvramSetTimeout | 22 - client/functions/ogRaiseError | 34 - client/functions/ogReduceFs | 23 - client/functions/ogRestoreEfiBootLoader | 23 - client/functions/ogRestoreImage | 25 - client/functions/ogRestoreImageSyntax | 25 - client/functions/ogSetPartitionActive | 23 - client/functions/ogSetPartitionId | 24 - client/functions/ogSetPartitionSize | 24 - client/functions/ogSetPartitionType | 24 - client/functions/ogSetRegistryValue | 25 - client/functions/ogSetWindowsName | 24 - client/functions/ogTorrentStart | 39 - client/functions/ogTypeToId | 23 - client/functions/ogUcastReceiverPartition | 26 - client/functions/ogUcastSendFile | 39 - client/functions/ogUcastSendPartition | 26 - client/functions/ogUcastSyntax | 34 - client/functions/ogUnhidePartition | 23 - client/functions/ogUninstallLinuxClient | 23 - client/functions/ogUninstallWindowsClient | 24 - client/functions/ogUnlock | 23 - client/functions/ogUnlockDisk | 22 - client/functions/ogUnlockImage | 30 - client/functions/ogUnlockPartition | 23 - client/functions/ogUnmount | 23 - client/functions/ogUnmountAll | 22 - client/functions/ogUnmountCache | 22 - client/functions/ogUnmountFs | 23 - client/functions/ogUnsetDirtyBit | 23 - client/functions/ogUpdateCacheIsNecesary | 24 - client/functions/ogUpdatePartitionTable | 13 - client/functions/ogWindowsBootParameters | 23 - client/functions/ogWindowsRegisterPartition | 26 - client/interfaceAdm/Apagar | 3 - client/interfaceAdm/Apagar.py | 10 - client/interfaceAdm/CambiarAcceso | 54 - client/interfaceAdm/Configurar | 159 - client/interfaceAdm/Configurar.py | 196 - client/interfaceAdm/ConsolaRemota | 3 - client/interfaceAdm/CrearImagen | 91 - client/interfaceAdm/EjecutarScript | 44 - client/interfaceAdm/IniciarSesion | 13 - client/interfaceAdm/InventarioHardware | 6 - client/interfaceAdm/InventarioSoftware | 19 - client/interfaceAdm/Reiniciar | 3 - client/interfaceAdm/Reiniciar.py | 8 - client/interfaceAdm/RestaurarImagen | 15 - client/interfaceAdm/getConfiguration | 88 - client/interfaceAdm/getIpAddress | 2 - client/interfaceAdm/procesaCache | 2 - .../themes/OpenGnsys/background-original.png | Bin 136 -> 0 bytes .../lib/burg/themes/OpenGnsys/background.png | Bin 74953 -> 0 bytes client/lib/burg/themes/OpenGnsys/extended | 79 - .../themes/OpenGnsys/icons/hover_debian.png | Bin 3715 -> 0 bytes .../OpenGnsys/icons/hover_elementary.png | Bin 5496 -> 0 bytes .../themes/OpenGnsys/icons/hover_freebsd.png | Bin 4416 -> 0 bytes .../themes/OpenGnsys/icons/hover_haiku.png | Bin 1912 -> 0 bytes .../themes/OpenGnsys/icons/hover_linux.png | Bin 33041 -> 0 bytes .../OpenGnsys/icons/hover_opengnsys.png | Bin 11389 -> 0 bytes .../themes/OpenGnsys/icons/hover_opensuse.png | Bin 4429 -> 0 bytes .../burg/themes/OpenGnsys/icons/hover_os.png | Bin 4586 -> 0 bytes .../burg/themes/OpenGnsys/icons/hover_osx.png | Bin 2457 -> 0 bytes .../themes/OpenGnsys/icons/hover_recovery.png | Bin 4442 -> 0 bytes .../themes/OpenGnsys/icons/hover_restart.png | Bin 5057 -> 0 bytes .../themes/OpenGnsys/icons/hover_shutdown.png | Bin 4539 -> 0 bytes .../themes/OpenGnsys/icons/hover_ubuntu.png | Bin 14374 -> 0 bytes .../themes/OpenGnsys/icons/hover_windows.png | Bin 20160 -> 0 bytes .../OpenGnsys/icons/hover_windows10.png | Bin 5101 -> 0 bytes .../themes/OpenGnsys/icons/hover_windows7.png | Bin 18024 -> 0 bytes .../OpenGnsys/icons/hover_windows_metro.png | Bin 1583 -> 0 bytes client/lib/burg/themes/OpenGnsys/icons/icons | 21 - .../themes/OpenGnsys/icons/normal_debian.png | Bin 3451 -> 0 bytes .../OpenGnsys/icons/normal_elementary.png | Bin 5204 -> 0 bytes .../themes/OpenGnsys/icons/normal_freebsd.png | Bin 4080 -> 0 bytes .../themes/OpenGnsys/icons/normal_haiku.png | Bin 1652 -> 0 bytes .../themes/OpenGnsys/icons/normal_linux.png | Bin 33691 -> 0 bytes .../OpenGnsys/icons/normal_opengnsys.png | Bin 11119 -> 0 bytes .../OpenGnsys/icons/normal_opensuse.png | Bin 4059 -> 0 bytes .../burg/themes/OpenGnsys/icons/normal_os.png | Bin 4242 -> 0 bytes .../themes/OpenGnsys/icons/normal_osx.png | Bin 2066 -> 0 bytes .../OpenGnsys/icons/normal_recovery.png | Bin 4081 -> 0 bytes .../themes/OpenGnsys/icons/normal_restart.png | Bin 4718 -> 0 bytes .../OpenGnsys/icons/normal_shutdown.png | Bin 4182 -> 0 bytes .../themes/OpenGnsys/icons/normal_ubuntu.png | Bin 14036 -> 0 bytes .../themes/OpenGnsys/icons/normal_windows.png | Bin 20637 -> 0 bytes .../OpenGnsys/icons/normal_windows10.png | Bin 4791 -> 0 bytes .../OpenGnsys/icons/normal_windows7.png | Bin 17786 -> 0 bytes .../OpenGnsys/icons/normal_windows_metro.png | Bin 1280 -> 0 bytes .../themes/OpenGnsys/images/000-70opaque.png | Bin 109 -> 0 bytes .../themes/OpenGnsys/images/button-bg.png | Bin 146 -> 0 bytes .../OpenGnsys/images/button-hover-bg.png | Bin 146 -> 0 bytes .../OpenGnsys/images/button-hover-l.png | Bin 525 -> 0 bytes .../OpenGnsys/images/button-hover-r.png | Bin 671 -> 0 bytes .../burg/themes/OpenGnsys/images/button-l.png | Bin 725 -> 0 bytes .../burg/themes/OpenGnsys/images/button-r.png | Bin 562 -> 0 bytes .../OpenGnsys/images/button-tools-hover.png | Bin 1550 -> 0 bytes .../themes/OpenGnsys/images/button-tools.png | Bin 1398 -> 0 bytes .../themes/OpenGnsys/images/container-b.png | Bin 153 -> 0 bytes .../themes/OpenGnsys/images/container-bg.png | Bin 146 -> 0 bytes .../themes/OpenGnsys/images/container-bl.png | Bin 154 -> 0 bytes .../themes/OpenGnsys/images/container-br.png | Bin 154 -> 0 bytes .../themes/OpenGnsys/images/container-l.png | Bin 155 -> 0 bytes .../themes/OpenGnsys/images/container-r.png | Bin 148 -> 0 bytes .../themes/OpenGnsys/images/container-t.png | Bin 159 -> 0 bytes .../OpenGnsys/images/container-title-bg.png | Bin 144 -> 0 bytes .../OpenGnsys/images/container-title-l.png | Bin 139 -> 0 bytes .../OpenGnsys/images/container-title-r.png | Bin 139 -> 0 bytes .../OpenGnsys/images/container-title-t.png | Bin 151 -> 0 bytes .../OpenGnsys/images/container-title-tl.png | Bin 181 -> 0 bytes .../OpenGnsys/images/container-title-tr.png | Bin 188 -> 0 bytes .../themes/OpenGnsys/images/container-tl.png | Bin 154 -> 0 bytes .../themes/OpenGnsys/images/container-tr.png | Bin 154 -> 0 bytes .../burg/themes/OpenGnsys/images/dialog-b.png | Bin 135 -> 0 bytes .../themes/OpenGnsys/images/dialog-bg.png | Bin 144 -> 0 bytes .../themes/OpenGnsys/images/dialog-bl.png | Bin 177 -> 0 bytes .../themes/OpenGnsys/images/dialog-bl.xcf | Bin 889 -> 0 bytes .../themes/OpenGnsys/images/dialog-br.png | Bin 168 -> 0 bytes .../themes/OpenGnsys/images/dialog-lr.png | Bin 131 -> 0 bytes .../themes/OpenGnsys/images/dialog-spacer.png | Bin 140 -> 0 bytes .../burg/themes/OpenGnsys/images/dialog-t.png | Bin 135 -> 0 bytes .../themes/OpenGnsys/images/dialog-tl.png | Bin 181 -> 0 bytes .../themes/OpenGnsys/images/dialog-tr.png | Bin 188 -> 0 bytes .../OpenGnsys/images/progressbar-bg-b.png | Bin 156 -> 0 bytes .../OpenGnsys/images/progressbar-bg-bl.png | Bin 169 -> 0 bytes .../OpenGnsys/images/progressbar-bg-br.png | Bin 215 -> 0 bytes .../OpenGnsys/images/progressbar-bg-l.png | Bin 346 -> 0 bytes .../OpenGnsys/images/progressbar-bg-r.png | Bin 165 -> 0 bytes .../OpenGnsys/images/progressbar-bg-t.png | Bin 155 -> 0 bytes .../OpenGnsys/images/progressbar-bg-tl.png | Bin 183 -> 0 bytes .../OpenGnsys/images/progressbar-bg-tr.png | Bin 186 -> 0 bytes .../OpenGnsys/images/progressbar-bg.png | Bin 152 -> 0 bytes .../themes/OpenGnsys/images/text-line-l.png | Bin 254 -> 0 bytes .../themes/OpenGnsys/images/text-line-r.png | Bin 260 -> 0 bytes .../lib/burg/themes/OpenGnsys/images/tick.png | Bin 253 -> 0 bytes .../themes/OpenGnsys/images/txt-about.png | Bin 905 -> 0 bytes .../burg/themes/OpenGnsys/images/txt-help.png | Bin 423 -> 0 bytes .../themes/OpenGnsys/images/txt-select.png | Bin 4367 -> 0 bytes .../themes/OpenGnsys/images/txt-tools.png | Bin 474 -> 0 bytes .../OpenGnsys/images/ubuntu-glow-96.png | Bin 9155 -> 0 bytes client/lib/burg/themes/OpenGnsys/menus | 188 - client/lib/burg/themes/OpenGnsys/style | 158 - client/lib/burg/themes/OpenGnsys/theme | 231 - client/lib/engine/bin/Boot.lib | 2973 --- client/lib/engine/bin/Cache.lib | 439 - client/lib/engine/bin/Disk.lib | 1715 -- client/lib/engine/bin/File.lib | 422 - client/lib/engine/bin/FileSystem.lib | 1205 -- client/lib/engine/bin/Image.lib | 1209 -- client/lib/engine/bin/Inventory.lib | 528 - client/lib/engine/bin/Net.lib | 345 - client/lib/engine/bin/PostConf.lib | 543 - client/lib/engine/bin/PostConfEAC.lib | 699 - client/lib/engine/bin/Protocol.lib | 1182 - client/lib/engine/bin/README.es.txt | 38 - client/lib/engine/bin/Registry.lib | 455 - client/lib/engine/bin/String.lib | 122 - client/lib/engine/bin/System.lib | 339 - client/lib/engine/bin/ToolsGNU.c | 147 - client/lib/engine/bin/UEFI.lib | 679 - client/lib/engine/tests/Modify/Cache.shtest | 93 - client/lib/engine/tests/NoModify/File1.shtest | 75 - client/lib/engine/tests/NoModify/Lock1.shtest | 85 - client/lib/engine/tests/NoModify/Net1.shtest | 21 - client/lib/engine/tests/README | 6 - client/lib/engine/tests/crearTestDisk1 | 160 - client/lib/engine/tests/crearTestLock2 | 82 - client/lib/fonts/DejaVuSans-Bold.ttf | Bin 466696 -> 0 bytes client/lib/fonts/DejaVuSans-BoldOblique.ttf | Bin 441736 -> 0 bytes client/lib/fonts/DejaVuSans-Oblique.ttf | Bin 434576 -> 0 bytes client/lib/fonts/DejaVuSans.ttf | Bin 493564 -> 0 bytes client/lib/fonts/DejaVuSansMono-Bold.ttf | Bin 229460 -> 0 bytes .../lib/fonts/DejaVuSansMono-BoldOblique.ttf | Bin 177780 -> 0 bytes client/lib/fonts/DejaVuSansMono-Oblique.ttf | Bin 184896 -> 0 bytes client/lib/fonts/DejaVuSansMono.ttf | Bin 237788 -> 0 bytes client/lib/fonts/DejaVuSerif-Bold.ttf | Bin 201516 -> 0 bytes client/lib/fonts/DejaVuSerif-BoldOblique.ttf | Bin 180948 -> 0 bytes client/lib/fonts/DejaVuSerif-Oblique.ttf | Bin 179872 -> 0 bytes client/lib/fonts/DejaVuSerif.ttf | Bin 210416 -> 0 bytes client/lib/fonts/README | 21 - client/lib/fonts/UTBI____.pfa | 1172 - client/lib/fonts/UTB_____.pfa | 1134 - client/lib/fonts/UTI_____.pfa | 1165 - client/lib/fonts/UTRG____.pfa | 1126 - client/lib/fonts/Vera.ttf | Bin 65932 -> 0 bytes client/lib/fonts/VeraBI.ttf | Bin 63208 -> 0 bytes client/lib/fonts/VeraBd.ttf | Bin 58716 -> 0 bytes client/lib/fonts/VeraIt.ttf | Bin 63684 -> 0 bytes client/lib/fonts/VeraMoBI.ttf | Bin 55032 -> 0 bytes client/lib/fonts/VeraMoBd.ttf | Bin 49052 -> 0 bytes client/lib/fonts/VeraMoIt.ttf | Bin 54508 -> 0 bytes client/lib/fonts/VeraMono.ttf | Bin 49224 -> 0 bytes client/lib/fonts/VeraSe.ttf | Bin 60280 -> 0 bytes client/lib/fonts/VeraSeBd.ttf | Bin 58736 -> 0 bytes client/lib/fonts/c0419bt_.pfb | Bin 40766 -> 0 bytes client/lib/fonts/c0582bt_.pfb | Bin 39511 -> 0 bytes client/lib/fonts/c0583bt_.pfb | Bin 40008 -> 0 bytes client/lib/fonts/c0611bt_.pfb | Bin 39871 -> 0 bytes client/lib/fonts/c0632bt_.pfb | Bin 33799 -> 0 bytes client/lib/fonts/c0633bt_.pfb | Bin 35229 -> 0 bytes client/lib/fonts/c0648bt_.pfb | Bin 34869 -> 0 bytes client/lib/fonts/c0649bt_.pfb | Bin 35118 -> 0 bytes client/lib/fonts/cour.pfa | 1954 -- client/lib/fonts/courb.pfa | 1966 -- client/lib/fonts/courbi.pfa | 1940 -- client/lib/fonts/couri.pfa | 1893 -- client/lib/fonts/cursor.pfa | 954 - client/lib/fonts/fixed_120_50.qpf | Bin 3109 -> 0 bytes client/lib/fonts/fixed_70_50.qpf | Bin 2567 -> 0 bytes client/lib/fonts/helvetica_100_50.qpf | Bin 3046 -> 0 bytes client/lib/fonts/helvetica_100_50i.qpf | Bin 3052 -> 0 bytes client/lib/fonts/helvetica_100_75.qpf | Bin 3040 -> 0 bytes client/lib/fonts/helvetica_100_75i.qpf | Bin 3081 -> 0 bytes client/lib/fonts/helvetica_120_50.qpf | Bin 3301 -> 0 bytes client/lib/fonts/helvetica_120_50i.qpf | Bin 3560 -> 0 bytes client/lib/fonts/helvetica_120_75.qpf | Bin 3326 -> 0 bytes client/lib/fonts/helvetica_120_75i.qpf | Bin 3759 -> 0 bytes client/lib/fonts/helvetica_140_50.qpf | Bin 3860 -> 0 bytes client/lib/fonts/helvetica_140_50i.qpf | Bin 4208 -> 0 bytes client/lib/fonts/helvetica_140_75.qpf | Bin 4035 -> 0 bytes client/lib/fonts/helvetica_140_75i.qpf | Bin 4498 -> 0 bytes client/lib/fonts/helvetica_180_50.qpf | Bin 5179 -> 0 bytes client/lib/fonts/helvetica_180_50i.qpf | Bin 5778 -> 0 bytes client/lib/fonts/helvetica_180_75.qpf | Bin 5712 -> 0 bytes client/lib/fonts/helvetica_180_75i.qpf | Bin 5977 -> 0 bytes client/lib/fonts/helvetica_240_50.qpf | Bin 7691 -> 0 bytes client/lib/fonts/helvetica_240_50i.qpf | Bin 8333 -> 0 bytes client/lib/fonts/helvetica_240_75.qpf | Bin 7912 -> 0 bytes client/lib/fonts/helvetica_240_75i.qpf | Bin 8588 -> 0 bytes client/lib/fonts/helvetica_80_50.qpf | Bin 2735 -> 0 bytes client/lib/fonts/helvetica_80_50i.qpf | Bin 2742 -> 0 bytes client/lib/fonts/helvetica_80_75.qpf | Bin 2745 -> 0 bytes client/lib/fonts/helvetica_80_75i.qpf | Bin 2750 -> 0 bytes client/lib/fonts/japanese_230_50.qpf | Bin 263331 -> 0 bytes client/lib/fonts/l047013t.pfa | 1346 -- client/lib/fonts/l047016t.pfa | 1356 -- client/lib/fonts/l047033t.pfa | 1353 -- client/lib/fonts/l047036t.pfa | 1361 -- client/lib/fonts/l048013t.pfa | 1233 -- client/lib/fonts/l048016t.pfa | 1269 -- client/lib/fonts/l048033t.pfa | 1267 -- client/lib/fonts/l048036t.pfa | 1260 -- client/lib/fonts/l049013t.pfa | 1598 -- client/lib/fonts/l049016t.pfa | 1582 -- client/lib/fonts/l049033t.pfa | 1735 -- client/lib/fonts/l049036t.pfa | 1613 -- client/lib/fonts/micro_40_50.qpf | Bin 1602 -> 0 bytes client/lib/fonts/unifont_160_50.qpf | Bin 1215089 -> 0 bytes client/lib/grub4dos/COPYING | 340 - client/lib/grub4dos/ChangeLog_GRUB4DOS.txt | 585 - .../lib/grub4dos/Get_Source_of_This_Build.txt | 11 - client/lib/grub4dos/README_GRUB4DOS.txt | 3866 ---- client/lib/grub4dos/badgrub.exe | Bin 235441 -> 0 bytes client/lib/grub4dos/bootlace.com | Bin 37092 -> 0 bytes client/lib/grub4dos/config.sys | 6 - client/lib/grub4dos/default | 46 - client/lib/grub4dos/example.menu.lst | 108 - client/lib/grub4dos/grldr | Bin 220049 -> 0 bytes client/lib/grub4dos/grldr.mbr | Bin 9216 -> 0 bytes client/lib/grub4dos/grub.exe | Bin 236977 -> 0 bytes client/lib/grub4dos/grub.pif | Bin 967 -> 0 bytes client/lib/grub4dos/grub4dos-0.4.5b/COPYING | 340 - .../grub4dos-0.4.5b/ChangeLog_GRUB4DOS.txt | 704 - .../grub4dos-0.4.5b/ChangeLog_chenall.txt | 645 - .../grub4dos-0.4.5b/README_GRUB4DOS.txt | 4138 ---- .../grub4dos-0.4.5b/README_GRUB4DOS_CN.txt | 4314 ---- .../lib/grub4dos/grub4dos-0.4.5b/badgrub.exe | Bin 289455 -> 0 bytes .../lib/grub4dos/grub4dos-0.4.5b/bootlace.com | Bin 37092 -> 0 bytes .../lib/grub4dos/grub4dos-0.4.5b/config.sys | 6 - client/lib/grub4dos/grub4dos-0.4.5b/default | 46 - client/lib/grub4dos/grub4dos-0.4.5b/grldr | Bin 273039 -> 0 bytes client/lib/grub4dos/grub4dos-0.4.5b/grldr.mbr | Bin 9216 -> 0 bytes client/lib/grub4dos/grub4dos-0.4.5b/grub.exe | Bin 292015 -> 0 bytes client/lib/grub4dos/grub4dos-0.4.5b/grub.pif | Bin 967 -> 0 bytes .../lib/grub4dos/grub4dos-0.4.5b/hmload.com | Bin 1856 -> 0 bytes client/lib/grub4dos/grub4dos-0.4.5b/menu.lst | 132 - client/lib/grub4dos/grub4dos-0.4.6a/COPYING | 340 - .../Get_Source_of_This_Build.txt | 8 - .../lib/grub4dos/grub4dos-0.4.6a/badgrub.exe | Bin 347277 -> 0 bytes .../lib/grub4dos/grub4dos-0.4.6a/bootlace.com | Bin 43520 -> 0 bytes .../grub4dos/grub4dos-0.4.6a/bootlace64.com | Bin 43520 -> 0 bytes .../lib/grub4dos/grub4dos-0.4.6a/eltorito.sys | Bin 1724 -> 0 bytes client/lib/grub4dos/grub4dos-0.4.6a/grldr | Bin 330861 -> 0 bytes client/lib/grub4dos/grub4dos-0.4.6a/grldr.mbr | Bin 8192 -> 0 bytes client/lib/grub4dos/grub4dos-0.4.6a/grldr.pbr | Bin 5632 -> 0 bytes .../lib/grub4dos/grub4dos-0.4.6a/grldr_cd.bin | Bin 512 -> 0 bytes client/lib/grub4dos/grub4dos-0.4.6a/grub.exe | Bin 349837 -> 0 bytes client/lib/grub4dos/grub4dos-0.4.6a/grub.pif | Bin 967 -> 0 bytes .../lib/grub4dos/grub4dos-0.4.6a/hmload.com | Bin 1856 -> 0 bytes client/lib/grub4dos/grub4dos-0.4.6a/ipxegrldr | Bin 228045 -> 0 bytes .../grub4dos-0.4.6a/sample/config.sys | 6 - .../grub4dos/grub4dos-0.4.6a/sample/default | 46 - .../grub4dos/grub4dos-0.4.6a/sample/menu.lst | 151 - client/lib/grub4dos/hmload.com | Bin 1856 -> 0 bytes client/lib/httpd/10-cgi.conf | 18 - client/lib/httpd/LogCommand.sh | 41 - client/lib/httpd/LogSession.sh | 30 - client/lib/httpd/bandwidth.sh | 12 - client/lib/httpd/cache.sh | 22 - client/lib/httpd/httpd-log.sh | 17 - client/lib/httpd/httpd-menu.sh | 14 - client/lib/httpd/httpd-runengine.sh | 10 - client/lib/httpd/lighttpd.conf | 167 - client/lib/httpd/oglive.css | 11 - client/lib/locale/ca/LC_MESSAGES/browser.mo | Bin 1021 -> 0 bytes client/lib/locale/en/LC_MESSAGES/browser.mo | Bin 2251 -> 0 bytes client/lib/modules/psmouse.ko | Bin 78700 -> 0 bytes client/lib/os-probes/10zvol-test | 14 - client/lib/os-probes/50mounted-tests | 99 - client/lib/os-probes/init/10filesystems | 39 - client/lib/os-probes/mounted/05efi | 71 - client/lib/os-probes/mounted/10freedos | 23 - client/lib/os-probes/mounted/10qnx | 21 - client/lib/os-probes/mounted/20macosx | 30 - client/lib/os-probes/mounted/20microsoft | 140 - client/lib/os-probes/mounted/30utility | 33 - client/lib/os-probes/mounted/40lsb | 48 - client/lib/os-probes/mounted/70hurd | 16 - client/lib/os-probes/mounted/80minix | 28 - client/lib/os-probes/mounted/83haiku | 35 - client/lib/os-probes/mounted/90linux-distro | 138 - client/lib/os-probes/mounted/90solaris | 19 - client/lib/os-probes/mounted/efi/10elilo | 24 - client/lib/os-probes/mounted/efi/20microsoft | 28 - client/lib/os-probes/mounted/efi/31part-x-y | 28 - client/lib/pci.ids | 17910 ---------------- client/lib/pictures/oglogo.png | Bin 736 -> 0 bytes client/lib/python3/BootLib.py | 1018 - client/lib/python3/CacheLib.py | 369 - client/lib/python3/DiskLib.py | 1424 -- client/lib/python3/FileLib.py | 517 - client/lib/python3/FileSystemLib.py | 906 - client/lib/python3/ImageLib.py | 839 - client/lib/python3/InventoryLib.py | 531 - client/lib/python3/NetLib.py | 315 - client/lib/python3/PostConfLib.py | 320 - client/lib/python3/ProtocolLib.py | 973 - client/lib/python3/RegistryLib.py | 382 - client/lib/python3/StringLib.py | 31 - client/lib/python3/SystemLib.py | 310 - client/lib/python3/UEFILib.py | 579 - client/lib/python3/lang_ca_ES.py | 400 - client/lib/python3/lang_en_GB.py | 387 - client/lib/python3/lang_es_ES.py | 387 - client/lib/python3/ogGlobals.py | 145 - client/lib/qtlib/libQtCore.so.4 | Bin 3222280 -> 0 bytes client/lib/qtlib/libQtGui.so.4 | Bin 11154352 -> 0 bytes client/lib/qtlib/libQtNetwork.so.4 | Bin 1116108 -> 0 bytes client/lib/qtlib/libQtWebKit.so.4 | Bin 21958488 -> 0 bytes client/lib/qtlib/libcrypto.so.1.0.0 | Bin 1742840 -> 0 bytes client/lib/qtlib/libssl.so.1.0.0 | Bin 354520 -> 0 bytes client/lib/qtplugins/imageformats/libqjpeg.so | Bin 239404 -> 0 bytes client/scripts/README.es.txt | 53 - client/scripts/bootLinux | 1 - client/scripts/bootOs | 47 - client/scripts/bootOsCustom.template | 92 - client/scripts/bootWindows | 1 - client/scripts/buildToOrder | 79 - client/scripts/cloneRemoteFromMaster | 324 - client/scripts/configureOs | 175 - client/scripts/configureOs.py | 159 - client/scripts/configureOsCustom.template | 81 - client/scripts/createImage | 182 - client/scripts/createImageCustom.template | 35 - client/scripts/createLogicalPartitions | 3 - client/scripts/createPrimaryPartitions | 3 - client/scripts/deployImage | 246 - client/scripts/deployImage.py | 182 - client/scripts/formatFs | 19 - client/scripts/generateMenuDefault | 81 - client/scripts/getFsType | 3 - client/scripts/getIpAddress | 3 - client/scripts/getOsVersion | 3 - client/scripts/grubSyntax | 483 - client/scripts/initCache | 111 - client/scripts/initCache.py | 128 - client/scripts/installOfflineMode | 60 - client/scripts/launchOgagentInstaller | 145 - client/scripts/listHardwareInfo | 24 - client/scripts/listPartitions | 3 - client/scripts/listPrimaryPartitions | 3 - client/scripts/listSoftwareInfo | 35 - client/scripts/listSoftwareInfo.py | 39 - client/scripts/menuBrowser | 11 - client/scripts/poweroff | 39 - client/scripts/reboot | 54 - client/scripts/remoteConsole | 22 - client/scripts/restoreImage | 106 - client/scripts/restoreImage.py | 118 - client/scripts/restoreImageCustom.template | 37 - client/scripts/runAplicationX.sh | 8 - client/scripts/runhttplog.sh | 15 - client/scripts/samples/configureGroup | 65 - client/scripts/samples/firstRunOnceWindows | 68 - client/scripts/samples/smartPartition | 62 - client/scripts/sendFileMcast | 56 - client/scripts/setBootMode | 47 - client/scripts/updateBootCache | 65 - client/scripts/updateBootCache.py | 62 - client/scripts/updateCache | 315 - client/scripts/updateCache.py | 219 - etc/ogAdmClient.cfg.tmpl | 5 - etc/smb-ogboot.conf.tmpl | 17 - installer/ogboot_installer.py | 81 - 558 files changed, 101787 deletions(-) delete mode 100644 client/LICENSE.en.txt delete mode 100644 client/README.es.txt delete mode 100755 client/bin/EACInterfaces delete mode 100755 client/bin/browser delete mode 100755 client/bin/grub-probe1.99_i686 delete mode 100755 client/bin/grub-probe1.99_x86_64 delete mode 100755 client/bin/poweroffconf delete mode 100755 client/bin/rsync-31 delete mode 100755 client/bin/runtest delete mode 100644 client/etc/engine.cfg delete mode 100644 client/etc/engine.json delete mode 100644 client/etc/es.qmap delete mode 100755 client/etc/init/default.sh delete mode 120000 client/etc/lang.ca_ES.UTF-8.conf delete mode 100644 client/etc/lang.ca_ES.conf delete mode 120000 client/etc/lang.en_GB.UTF-8.conf delete mode 100644 client/etc/lang.en_GB.conf delete mode 120000 client/etc/lang.es_ES.UTF-8.conf delete mode 100644 client/etc/lang.es_ES.conf delete mode 100755 client/etc/preinit/default.sh delete mode 100755 client/etc/preinit/fileslinks.sh delete mode 100755 client/etc/preinit/loadenviron.sh delete mode 100755 client/etc/preinit/loadmodules.sh delete mode 100755 client/etc/preinit/metadevs.sh delete mode 100755 client/etc/preinit/mountrepo.sh delete mode 100755 client/etc/preinit/otherservices.sh delete mode 100755 client/etc/preinit/poweroff.sh delete mode 100755 client/functions/ogAddCmd delete mode 100755 client/functions/ogAddRegistryKey delete mode 100755 client/functions/ogAddRegistryValue delete mode 100755 client/functions/ogCalculateChecksum delete mode 100755 client/functions/ogCalculateFullChecksum delete mode 100755 client/functions/ogChangeRepo delete mode 100755 client/functions/ogCheckFs delete mode 100755 client/functions/ogCheckIpAddress delete mode 100755 client/functions/ogCleanLinuxDevices delete mode 100755 client/functions/ogCleanOs delete mode 100755 client/functions/ogConfigureFstab delete mode 100755 client/functions/ogConfigureOgagent delete mode 100755 client/functions/ogCopyEfiBootLoader delete mode 100755 client/functions/ogCopyFile delete mode 100755 client/functions/ogCreateCache delete mode 100755 client/functions/ogCreateGptPartitions delete mode 100755 client/functions/ogCreateImage delete mode 100755 client/functions/ogCreateImageSyntax delete mode 100755 client/functions/ogCreatePartitionTable delete mode 100755 client/functions/ogCreatePartitions delete mode 100755 client/functions/ogCreateTorrent delete mode 100755 client/functions/ogDeleteCache delete mode 100755 client/functions/ogDeleteFile delete mode 100755 client/functions/ogDeletePartitionTable delete mode 100755 client/functions/ogDeleteRegistryKey delete mode 100755 client/functions/ogDeleteRegistryValue delete mode 100755 client/functions/ogDeleteTree delete mode 100755 client/functions/ogDevToDisk delete mode 100755 client/functions/ogDiskToDev delete mode 100755 client/functions/ogEcho delete mode 100755 client/functions/ogExecAndLog delete mode 100755 client/functions/ogExtendFs delete mode 100755 client/functions/ogFindCache delete mode 100755 client/functions/ogFixBootSector delete mode 100755 client/functions/ogFormat delete mode 100755 client/functions/ogFormatCache delete mode 100755 client/functions/ogFormatFs delete mode 100755 client/functions/ogGetArch delete mode 100755 client/functions/ogGetCacheSize delete mode 100755 client/functions/ogGetCacheSpace delete mode 100755 client/functions/ogGetCaller delete mode 100755 client/functions/ogGetDiskSize delete mode 100755 client/functions/ogGetDiskType delete mode 100755 client/functions/ogGetEsp delete mode 100755 client/functions/ogGetFreeSize delete mode 100755 client/functions/ogGetFsSize delete mode 100755 client/functions/ogGetFsType delete mode 100755 client/functions/ogGetHivePath delete mode 100755 client/functions/ogGetHostname delete mode 100755 client/functions/ogGetImageCompressor delete mode 100755 client/functions/ogGetImageInfo delete mode 100755 client/functions/ogGetImageProgram delete mode 100755 client/functions/ogGetImageSize delete mode 100755 client/functions/ogGetImageType delete mode 100755 client/functions/ogGetIpAddress delete mode 100755 client/functions/ogGetLastSector delete mode 100755 client/functions/ogGetMacAddress delete mode 100755 client/functions/ogGetMountPoint delete mode 100755 client/functions/ogGetOsType delete mode 100755 client/functions/ogGetOsUuid delete mode 100755 client/functions/ogGetOsVersion delete mode 100755 client/functions/ogGetParentPath delete mode 100755 client/functions/ogGetPartitionActive delete mode 100755 client/functions/ogGetPartitionId delete mode 100755 client/functions/ogGetPartitionSize delete mode 100755 client/functions/ogGetPartitionTableType delete mode 100755 client/functions/ogGetPartitionType delete mode 100755 client/functions/ogGetPartitionsNumber delete mode 100755 client/functions/ogGetPath delete mode 100755 client/functions/ogGetRegistryValue delete mode 100755 client/functions/ogGetRepoIp delete mode 100755 client/functions/ogGetSerialNumber delete mode 100755 client/functions/ogGetServerIp delete mode 100755 client/functions/ogGetSizeParameters delete mode 100755 client/functions/ogGrubInstallMbr delete mode 100755 client/functions/ogGrubInstallPartition delete mode 100755 client/functions/ogHelp delete mode 100755 client/functions/ogHidePartition delete mode 100755 client/functions/ogIdToType delete mode 100755 client/functions/ogInstallFirstBoot delete mode 100755 client/functions/ogInstallMiniSetup delete mode 100755 client/functions/ogInstallRunonce delete mode 100755 client/functions/ogIsDiskLocked delete mode 100755 client/functions/ogIsEfiActive delete mode 100755 client/functions/ogIsFormated delete mode 100755 client/functions/ogIsImageLocked delete mode 100755 client/functions/ogIsLocked delete mode 100755 client/functions/ogIsMounted delete mode 100755 client/functions/ogIsPartitionLocked delete mode 100755 client/functions/ogIsReadonly delete mode 100755 client/functions/ogIsRepoLocked delete mode 100755 client/functions/ogIsVirtualMachine delete mode 100755 client/functions/ogIsWritable delete mode 100755 client/functions/ogListHardwareInfo delete mode 100755 client/functions/ogListLogicalPartitions delete mode 100755 client/functions/ogListPartitions delete mode 100755 client/functions/ogListPrimaryPartitions delete mode 100755 client/functions/ogListRegistryKeys delete mode 100755 client/functions/ogListRegistryValues delete mode 100755 client/functions/ogListSoftware delete mode 100755 client/functions/ogLock delete mode 100755 client/functions/ogLockDisk delete mode 100755 client/functions/ogLockImage delete mode 100755 client/functions/ogLockPartition delete mode 100755 client/functions/ogMakeDir delete mode 100755 client/functions/ogMcastReceiverFile delete mode 100755 client/functions/ogMcastReceiverPartition delete mode 100755 client/functions/ogMcastSendFile delete mode 100755 client/functions/ogMcastSendPartition delete mode 100755 client/functions/ogMcastSyntax delete mode 100755 client/functions/ogMount delete mode 100755 client/functions/ogMountCache delete mode 100755 client/functions/ogMountCdrom delete mode 100755 client/functions/ogMountFirstFs delete mode 100755 client/functions/ogMountFs delete mode 100755 client/functions/ogNvramActiveEntry delete mode 100755 client/functions/ogNvramAddEntry delete mode 100755 client/functions/ogNvramDeleteEntry delete mode 100755 client/functions/ogNvramGetCurrent delete mode 100755 client/functions/ogNvramGetNext delete mode 100755 client/functions/ogNvramGetOrder delete mode 100755 client/functions/ogNvramGetTimeout delete mode 100755 client/functions/ogNvramInactiveEntry delete mode 100755 client/functions/ogNvramList delete mode 100755 client/functions/ogNvramPxeFirstEntry delete mode 100755 client/functions/ogNvramSetNext delete mode 100755 client/functions/ogNvramSetOrder delete mode 100755 client/functions/ogNvramSetTimeout delete mode 100755 client/functions/ogRaiseError delete mode 100755 client/functions/ogReduceFs delete mode 100755 client/functions/ogRestoreEfiBootLoader delete mode 100755 client/functions/ogRestoreImage delete mode 100755 client/functions/ogRestoreImageSyntax delete mode 100755 client/functions/ogSetPartitionActive delete mode 100755 client/functions/ogSetPartitionId delete mode 100755 client/functions/ogSetPartitionSize delete mode 100755 client/functions/ogSetPartitionType delete mode 100755 client/functions/ogSetRegistryValue delete mode 100755 client/functions/ogSetWindowsName delete mode 100755 client/functions/ogTorrentStart delete mode 100755 client/functions/ogTypeToId delete mode 100755 client/functions/ogUcastReceiverPartition delete mode 100755 client/functions/ogUcastSendFile delete mode 100755 client/functions/ogUcastSendPartition delete mode 100755 client/functions/ogUcastSyntax delete mode 100755 client/functions/ogUnhidePartition delete mode 100755 client/functions/ogUninstallLinuxClient delete mode 100755 client/functions/ogUninstallWindowsClient delete mode 100755 client/functions/ogUnlock delete mode 100755 client/functions/ogUnlockDisk delete mode 100755 client/functions/ogUnlockImage delete mode 100755 client/functions/ogUnlockPartition delete mode 100755 client/functions/ogUnmount delete mode 100755 client/functions/ogUnmountAll delete mode 100755 client/functions/ogUnmountCache delete mode 100755 client/functions/ogUnmountFs delete mode 100755 client/functions/ogUnsetDirtyBit delete mode 100755 client/functions/ogUpdateCacheIsNecesary delete mode 100755 client/functions/ogUpdatePartitionTable delete mode 100755 client/functions/ogWindowsBootParameters delete mode 100755 client/functions/ogWindowsRegisterPartition delete mode 100755 client/interfaceAdm/Apagar delete mode 100755 client/interfaceAdm/Apagar.py delete mode 100755 client/interfaceAdm/CambiarAcceso delete mode 100755 client/interfaceAdm/Configurar delete mode 100755 client/interfaceAdm/Configurar.py delete mode 100755 client/interfaceAdm/ConsolaRemota delete mode 100755 client/interfaceAdm/CrearImagen delete mode 100755 client/interfaceAdm/EjecutarScript delete mode 100755 client/interfaceAdm/IniciarSesion delete mode 100755 client/interfaceAdm/InventarioHardware delete mode 100755 client/interfaceAdm/InventarioSoftware delete mode 100755 client/interfaceAdm/Reiniciar delete mode 100755 client/interfaceAdm/Reiniciar.py delete mode 100755 client/interfaceAdm/RestaurarImagen delete mode 100755 client/interfaceAdm/getConfiguration delete mode 100755 client/interfaceAdm/getIpAddress delete mode 100755 client/interfaceAdm/procesaCache delete mode 100644 client/lib/burg/themes/OpenGnsys/background-original.png delete mode 100755 client/lib/burg/themes/OpenGnsys/background.png delete mode 100644 client/lib/burg/themes/OpenGnsys/extended delete mode 100644 client/lib/burg/themes/OpenGnsys/icons/hover_debian.png delete mode 100644 client/lib/burg/themes/OpenGnsys/icons/hover_elementary.png delete mode 100644 client/lib/burg/themes/OpenGnsys/icons/hover_freebsd.png delete mode 100644 client/lib/burg/themes/OpenGnsys/icons/hover_haiku.png delete mode 100644 client/lib/burg/themes/OpenGnsys/icons/hover_linux.png delete mode 100644 client/lib/burg/themes/OpenGnsys/icons/hover_opengnsys.png delete mode 100644 client/lib/burg/themes/OpenGnsys/icons/hover_opensuse.png delete mode 100644 client/lib/burg/themes/OpenGnsys/icons/hover_os.png delete mode 100644 client/lib/burg/themes/OpenGnsys/icons/hover_osx.png delete mode 100644 client/lib/burg/themes/OpenGnsys/icons/hover_recovery.png delete mode 100644 client/lib/burg/themes/OpenGnsys/icons/hover_restart.png delete mode 100644 client/lib/burg/themes/OpenGnsys/icons/hover_shutdown.png delete mode 100644 client/lib/burg/themes/OpenGnsys/icons/hover_ubuntu.png delete mode 100644 client/lib/burg/themes/OpenGnsys/icons/hover_windows.png delete mode 100644 client/lib/burg/themes/OpenGnsys/icons/hover_windows10.png delete mode 100644 client/lib/burg/themes/OpenGnsys/icons/hover_windows7.png delete mode 100644 client/lib/burg/themes/OpenGnsys/icons/hover_windows_metro.png delete mode 100644 client/lib/burg/themes/OpenGnsys/icons/icons delete mode 100644 client/lib/burg/themes/OpenGnsys/icons/normal_debian.png delete mode 100644 client/lib/burg/themes/OpenGnsys/icons/normal_elementary.png delete mode 100644 client/lib/burg/themes/OpenGnsys/icons/normal_freebsd.png delete mode 100644 client/lib/burg/themes/OpenGnsys/icons/normal_haiku.png delete mode 100644 client/lib/burg/themes/OpenGnsys/icons/normal_linux.png delete mode 100644 client/lib/burg/themes/OpenGnsys/icons/normal_opengnsys.png delete mode 100644 client/lib/burg/themes/OpenGnsys/icons/normal_opensuse.png delete mode 100644 client/lib/burg/themes/OpenGnsys/icons/normal_os.png delete mode 100644 client/lib/burg/themes/OpenGnsys/icons/normal_osx.png delete mode 100644 client/lib/burg/themes/OpenGnsys/icons/normal_recovery.png delete mode 100644 client/lib/burg/themes/OpenGnsys/icons/normal_restart.png delete mode 100644 client/lib/burg/themes/OpenGnsys/icons/normal_shutdown.png delete mode 100644 client/lib/burg/themes/OpenGnsys/icons/normal_ubuntu.png delete mode 100644 client/lib/burg/themes/OpenGnsys/icons/normal_windows.png delete mode 100644 client/lib/burg/themes/OpenGnsys/icons/normal_windows10.png delete mode 100644 client/lib/burg/themes/OpenGnsys/icons/normal_windows7.png delete mode 100644 client/lib/burg/themes/OpenGnsys/icons/normal_windows_metro.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/000-70opaque.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/button-bg.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/button-hover-bg.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/button-hover-l.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/button-hover-r.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/button-l.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/button-r.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/button-tools-hover.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/button-tools.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/container-b.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/container-bg.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/container-bl.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/container-br.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/container-l.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/container-r.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/container-t.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/container-title-bg.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/container-title-l.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/container-title-r.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/container-title-t.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/container-title-tl.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/container-title-tr.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/container-tl.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/container-tr.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/dialog-b.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/dialog-bg.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/dialog-bl.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/dialog-bl.xcf delete mode 100644 client/lib/burg/themes/OpenGnsys/images/dialog-br.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/dialog-lr.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/dialog-spacer.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/dialog-t.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/dialog-tl.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/dialog-tr.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/progressbar-bg-b.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/progressbar-bg-bl.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/progressbar-bg-br.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/progressbar-bg-l.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/progressbar-bg-r.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/progressbar-bg-t.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/progressbar-bg-tl.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/progressbar-bg-tr.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/progressbar-bg.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/text-line-l.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/text-line-r.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/tick.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/txt-about.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/txt-help.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/txt-select.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/txt-tools.png delete mode 100644 client/lib/burg/themes/OpenGnsys/images/ubuntu-glow-96.png delete mode 100644 client/lib/burg/themes/OpenGnsys/menus delete mode 100644 client/lib/burg/themes/OpenGnsys/style delete mode 100644 client/lib/burg/themes/OpenGnsys/theme delete mode 100755 client/lib/engine/bin/Boot.lib delete mode 100755 client/lib/engine/bin/Cache.lib delete mode 100755 client/lib/engine/bin/Disk.lib delete mode 100755 client/lib/engine/bin/File.lib delete mode 100755 client/lib/engine/bin/FileSystem.lib delete mode 100755 client/lib/engine/bin/Image.lib delete mode 100755 client/lib/engine/bin/Inventory.lib delete mode 100755 client/lib/engine/bin/Net.lib delete mode 100755 client/lib/engine/bin/PostConf.lib delete mode 100755 client/lib/engine/bin/PostConfEAC.lib delete mode 100755 client/lib/engine/bin/Protocol.lib delete mode 100644 client/lib/engine/bin/README.es.txt delete mode 100755 client/lib/engine/bin/Registry.lib delete mode 100755 client/lib/engine/bin/String.lib delete mode 100755 client/lib/engine/bin/System.lib delete mode 100644 client/lib/engine/bin/ToolsGNU.c delete mode 100755 client/lib/engine/bin/UEFI.lib delete mode 100644 client/lib/engine/tests/Modify/Cache.shtest delete mode 100644 client/lib/engine/tests/NoModify/File1.shtest delete mode 100644 client/lib/engine/tests/NoModify/Lock1.shtest delete mode 100644 client/lib/engine/tests/NoModify/Net1.shtest delete mode 100644 client/lib/engine/tests/README delete mode 100755 client/lib/engine/tests/crearTestDisk1 delete mode 100755 client/lib/engine/tests/crearTestLock2 delete mode 100644 client/lib/fonts/DejaVuSans-Bold.ttf delete mode 100644 client/lib/fonts/DejaVuSans-BoldOblique.ttf delete mode 100644 client/lib/fonts/DejaVuSans-Oblique.ttf delete mode 100644 client/lib/fonts/DejaVuSans.ttf delete mode 100644 client/lib/fonts/DejaVuSansMono-Bold.ttf delete mode 100644 client/lib/fonts/DejaVuSansMono-BoldOblique.ttf delete mode 100644 client/lib/fonts/DejaVuSansMono-Oblique.ttf delete mode 100644 client/lib/fonts/DejaVuSansMono.ttf delete mode 100644 client/lib/fonts/DejaVuSerif-Bold.ttf delete mode 100644 client/lib/fonts/DejaVuSerif-BoldOblique.ttf delete mode 100644 client/lib/fonts/DejaVuSerif-Oblique.ttf delete mode 100644 client/lib/fonts/DejaVuSerif.ttf delete mode 100644 client/lib/fonts/README delete mode 100644 client/lib/fonts/UTBI____.pfa delete mode 100644 client/lib/fonts/UTB_____.pfa delete mode 100644 client/lib/fonts/UTI_____.pfa delete mode 100644 client/lib/fonts/UTRG____.pfa delete mode 100644 client/lib/fonts/Vera.ttf delete mode 100644 client/lib/fonts/VeraBI.ttf delete mode 100644 client/lib/fonts/VeraBd.ttf delete mode 100644 client/lib/fonts/VeraIt.ttf delete mode 100644 client/lib/fonts/VeraMoBI.ttf delete mode 100644 client/lib/fonts/VeraMoBd.ttf delete mode 100644 client/lib/fonts/VeraMoIt.ttf delete mode 100644 client/lib/fonts/VeraMono.ttf delete mode 100644 client/lib/fonts/VeraSe.ttf delete mode 100644 client/lib/fonts/VeraSeBd.ttf delete mode 100644 client/lib/fonts/c0419bt_.pfb delete mode 100644 client/lib/fonts/c0582bt_.pfb delete mode 100644 client/lib/fonts/c0583bt_.pfb delete mode 100644 client/lib/fonts/c0611bt_.pfb delete mode 100644 client/lib/fonts/c0632bt_.pfb delete mode 100644 client/lib/fonts/c0633bt_.pfb delete mode 100644 client/lib/fonts/c0648bt_.pfb delete mode 100644 client/lib/fonts/c0649bt_.pfb delete mode 100644 client/lib/fonts/cour.pfa delete mode 100644 client/lib/fonts/courb.pfa delete mode 100644 client/lib/fonts/courbi.pfa delete mode 100644 client/lib/fonts/couri.pfa delete mode 100644 client/lib/fonts/cursor.pfa delete mode 100644 client/lib/fonts/fixed_120_50.qpf delete mode 100644 client/lib/fonts/fixed_70_50.qpf delete mode 100644 client/lib/fonts/helvetica_100_50.qpf delete mode 100644 client/lib/fonts/helvetica_100_50i.qpf delete mode 100644 client/lib/fonts/helvetica_100_75.qpf delete mode 100644 client/lib/fonts/helvetica_100_75i.qpf delete mode 100644 client/lib/fonts/helvetica_120_50.qpf delete mode 100644 client/lib/fonts/helvetica_120_50i.qpf delete mode 100644 client/lib/fonts/helvetica_120_75.qpf delete mode 100644 client/lib/fonts/helvetica_120_75i.qpf delete mode 100644 client/lib/fonts/helvetica_140_50.qpf delete mode 100644 client/lib/fonts/helvetica_140_50i.qpf delete mode 100644 client/lib/fonts/helvetica_140_75.qpf delete mode 100644 client/lib/fonts/helvetica_140_75i.qpf delete mode 100644 client/lib/fonts/helvetica_180_50.qpf delete mode 100644 client/lib/fonts/helvetica_180_50i.qpf delete mode 100644 client/lib/fonts/helvetica_180_75.qpf delete mode 100644 client/lib/fonts/helvetica_180_75i.qpf delete mode 100644 client/lib/fonts/helvetica_240_50.qpf delete mode 100644 client/lib/fonts/helvetica_240_50i.qpf delete mode 100644 client/lib/fonts/helvetica_240_75.qpf delete mode 100644 client/lib/fonts/helvetica_240_75i.qpf delete mode 100644 client/lib/fonts/helvetica_80_50.qpf delete mode 100644 client/lib/fonts/helvetica_80_50i.qpf delete mode 100644 client/lib/fonts/helvetica_80_75.qpf delete mode 100644 client/lib/fonts/helvetica_80_75i.qpf delete mode 100644 client/lib/fonts/japanese_230_50.qpf delete mode 100644 client/lib/fonts/l047013t.pfa delete mode 100644 client/lib/fonts/l047016t.pfa delete mode 100644 client/lib/fonts/l047033t.pfa delete mode 100644 client/lib/fonts/l047036t.pfa delete mode 100644 client/lib/fonts/l048013t.pfa delete mode 100644 client/lib/fonts/l048016t.pfa delete mode 100644 client/lib/fonts/l048033t.pfa delete mode 100644 client/lib/fonts/l048036t.pfa delete mode 100644 client/lib/fonts/l049013t.pfa delete mode 100644 client/lib/fonts/l049016t.pfa delete mode 100644 client/lib/fonts/l049033t.pfa delete mode 100644 client/lib/fonts/l049036t.pfa delete mode 100644 client/lib/fonts/micro_40_50.qpf delete mode 100644 client/lib/fonts/unifont_160_50.qpf delete mode 100644 client/lib/grub4dos/COPYING delete mode 100644 client/lib/grub4dos/ChangeLog_GRUB4DOS.txt delete mode 100644 client/lib/grub4dos/Get_Source_of_This_Build.txt delete mode 100644 client/lib/grub4dos/README_GRUB4DOS.txt delete mode 100644 client/lib/grub4dos/badgrub.exe delete mode 100644 client/lib/grub4dos/bootlace.com delete mode 100644 client/lib/grub4dos/config.sys delete mode 100644 client/lib/grub4dos/default delete mode 100644 client/lib/grub4dos/example.menu.lst delete mode 100644 client/lib/grub4dos/grldr delete mode 100644 client/lib/grub4dos/grldr.mbr delete mode 100644 client/lib/grub4dos/grub.exe delete mode 100644 client/lib/grub4dos/grub.pif delete mode 100644 client/lib/grub4dos/grub4dos-0.4.5b/COPYING delete mode 100644 client/lib/grub4dos/grub4dos-0.4.5b/ChangeLog_GRUB4DOS.txt delete mode 100644 client/lib/grub4dos/grub4dos-0.4.5b/ChangeLog_chenall.txt delete mode 100644 client/lib/grub4dos/grub4dos-0.4.5b/README_GRUB4DOS.txt delete mode 100644 client/lib/grub4dos/grub4dos-0.4.5b/README_GRUB4DOS_CN.txt delete mode 100644 client/lib/grub4dos/grub4dos-0.4.5b/badgrub.exe delete mode 100755 client/lib/grub4dos/grub4dos-0.4.5b/bootlace.com delete mode 100644 client/lib/grub4dos/grub4dos-0.4.5b/config.sys delete mode 100644 client/lib/grub4dos/grub4dos-0.4.5b/default delete mode 100644 client/lib/grub4dos/grub4dos-0.4.5b/grldr delete mode 100644 client/lib/grub4dos/grub4dos-0.4.5b/grldr.mbr delete mode 100644 client/lib/grub4dos/grub4dos-0.4.5b/grub.exe delete mode 100644 client/lib/grub4dos/grub4dos-0.4.5b/grub.pif delete mode 100644 client/lib/grub4dos/grub4dos-0.4.5b/hmload.com delete mode 100644 client/lib/grub4dos/grub4dos-0.4.5b/menu.lst delete mode 100755 client/lib/grub4dos/grub4dos-0.4.6a/COPYING delete mode 100755 client/lib/grub4dos/grub4dos-0.4.6a/Get_Source_of_This_Build.txt delete mode 100755 client/lib/grub4dos/grub4dos-0.4.6a/badgrub.exe delete mode 100755 client/lib/grub4dos/grub4dos-0.4.6a/bootlace.com delete mode 100755 client/lib/grub4dos/grub4dos-0.4.6a/bootlace64.com delete mode 100755 client/lib/grub4dos/grub4dos-0.4.6a/eltorito.sys delete mode 100755 client/lib/grub4dos/grub4dos-0.4.6a/grldr delete mode 100755 client/lib/grub4dos/grub4dos-0.4.6a/grldr.mbr delete mode 100755 client/lib/grub4dos/grub4dos-0.4.6a/grldr.pbr delete mode 100755 client/lib/grub4dos/grub4dos-0.4.6a/grldr_cd.bin delete mode 100755 client/lib/grub4dos/grub4dos-0.4.6a/grub.exe delete mode 100755 client/lib/grub4dos/grub4dos-0.4.6a/grub.pif delete mode 100755 client/lib/grub4dos/grub4dos-0.4.6a/hmload.com delete mode 100755 client/lib/grub4dos/grub4dos-0.4.6a/ipxegrldr delete mode 100755 client/lib/grub4dos/grub4dos-0.4.6a/sample/config.sys delete mode 100755 client/lib/grub4dos/grub4dos-0.4.6a/sample/default delete mode 100755 client/lib/grub4dos/grub4dos-0.4.6a/sample/menu.lst delete mode 100644 client/lib/grub4dos/hmload.com delete mode 100644 client/lib/httpd/10-cgi.conf delete mode 100755 client/lib/httpd/LogCommand.sh delete mode 100755 client/lib/httpd/LogSession.sh delete mode 100755 client/lib/httpd/bandwidth.sh delete mode 100755 client/lib/httpd/cache.sh delete mode 100755 client/lib/httpd/httpd-log.sh delete mode 100755 client/lib/httpd/httpd-menu.sh delete mode 100755 client/lib/httpd/httpd-runengine.sh delete mode 100644 client/lib/httpd/lighttpd.conf delete mode 100644 client/lib/httpd/oglive.css delete mode 100644 client/lib/locale/ca/LC_MESSAGES/browser.mo delete mode 100644 client/lib/locale/en/LC_MESSAGES/browser.mo delete mode 100644 client/lib/modules/psmouse.ko delete mode 100755 client/lib/os-probes/10zvol-test delete mode 100755 client/lib/os-probes/50mounted-tests delete mode 100755 client/lib/os-probes/init/10filesystems delete mode 100755 client/lib/os-probes/mounted/05efi delete mode 100755 client/lib/os-probes/mounted/10freedos delete mode 100755 client/lib/os-probes/mounted/10qnx delete mode 100755 client/lib/os-probes/mounted/20macosx delete mode 100755 client/lib/os-probes/mounted/20microsoft delete mode 100755 client/lib/os-probes/mounted/30utility delete mode 100755 client/lib/os-probes/mounted/40lsb delete mode 100755 client/lib/os-probes/mounted/70hurd delete mode 100755 client/lib/os-probes/mounted/80minix delete mode 100755 client/lib/os-probes/mounted/83haiku delete mode 100755 client/lib/os-probes/mounted/90linux-distro delete mode 100755 client/lib/os-probes/mounted/90solaris delete mode 100755 client/lib/os-probes/mounted/efi/10elilo delete mode 100755 client/lib/os-probes/mounted/efi/20microsoft delete mode 100755 client/lib/os-probes/mounted/efi/31part-x-y delete mode 100644 client/lib/pci.ids delete mode 100644 client/lib/pictures/oglogo.png delete mode 100644 client/lib/python3/BootLib.py delete mode 100644 client/lib/python3/CacheLib.py delete mode 100644 client/lib/python3/DiskLib.py delete mode 100644 client/lib/python3/FileLib.py delete mode 100644 client/lib/python3/FileSystemLib.py delete mode 100644 client/lib/python3/ImageLib.py delete mode 100644 client/lib/python3/InventoryLib.py delete mode 100644 client/lib/python3/NetLib.py delete mode 100644 client/lib/python3/PostConfLib.py delete mode 100644 client/lib/python3/ProtocolLib.py delete mode 100644 client/lib/python3/RegistryLib.py delete mode 100644 client/lib/python3/StringLib.py delete mode 100644 client/lib/python3/SystemLib.py delete mode 100644 client/lib/python3/UEFILib.py delete mode 100644 client/lib/python3/lang_ca_ES.py delete mode 100644 client/lib/python3/lang_en_GB.py delete mode 100644 client/lib/python3/lang_es_ES.py delete mode 100644 client/lib/python3/ogGlobals.py delete mode 100755 client/lib/qtlib/libQtCore.so.4 delete mode 100755 client/lib/qtlib/libQtGui.so.4 delete mode 100755 client/lib/qtlib/libQtNetwork.so.4 delete mode 100755 client/lib/qtlib/libQtWebKit.so.4 delete mode 100755 client/lib/qtlib/libcrypto.so.1.0.0 delete mode 100755 client/lib/qtlib/libssl.so.1.0.0 delete mode 100755 client/lib/qtplugins/imageformats/libqjpeg.so delete mode 100644 client/scripts/README.es.txt delete mode 120000 client/scripts/bootLinux delete mode 100755 client/scripts/bootOs delete mode 100755 client/scripts/bootOsCustom.template delete mode 120000 client/scripts/bootWindows delete mode 100755 client/scripts/buildToOrder delete mode 100755 client/scripts/cloneRemoteFromMaster delete mode 100755 client/scripts/configureOs delete mode 100755 client/scripts/configureOs.py delete mode 100644 client/scripts/configureOsCustom.template delete mode 100755 client/scripts/createImage delete mode 100644 client/scripts/createImageCustom.template delete mode 100755 client/scripts/createLogicalPartitions delete mode 100755 client/scripts/createPrimaryPartitions delete mode 100755 client/scripts/deployImage delete mode 100755 client/scripts/deployImage.py delete mode 100755 client/scripts/formatFs delete mode 100755 client/scripts/generateMenuDefault delete mode 100755 client/scripts/getFsType delete mode 100755 client/scripts/getIpAddress delete mode 100755 client/scripts/getOsVersion delete mode 100755 client/scripts/grubSyntax delete mode 100755 client/scripts/initCache delete mode 100755 client/scripts/initCache.py delete mode 100755 client/scripts/installOfflineMode delete mode 100755 client/scripts/launchOgagentInstaller delete mode 100755 client/scripts/listHardwareInfo delete mode 100755 client/scripts/listPartitions delete mode 100755 client/scripts/listPrimaryPartitions delete mode 100755 client/scripts/listSoftwareInfo delete mode 100755 client/scripts/listSoftwareInfo.py delete mode 100755 client/scripts/menuBrowser delete mode 100755 client/scripts/poweroff delete mode 100755 client/scripts/reboot delete mode 100755 client/scripts/remoteConsole delete mode 100755 client/scripts/restoreImage delete mode 100644 client/scripts/restoreImage.py delete mode 100644 client/scripts/restoreImageCustom.template delete mode 100755 client/scripts/runAplicationX.sh delete mode 100755 client/scripts/runhttplog.sh delete mode 100755 client/scripts/samples/configureGroup delete mode 100644 client/scripts/samples/firstRunOnceWindows delete mode 100644 client/scripts/samples/smartPartition delete mode 100755 client/scripts/sendFileMcast delete mode 100755 client/scripts/setBootMode delete mode 100755 client/scripts/updateBootCache delete mode 100755 client/scripts/updateBootCache.py delete mode 100755 client/scripts/updateCache delete mode 100644 client/scripts/updateCache.py delete mode 100644 etc/ogAdmClient.cfg.tmpl diff --git a/client/LICENSE.en.txt b/client/LICENSE.en.txt deleted file mode 100644 index 94a9ed0..0000000 --- a/client/LICENSE.en.txt +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/client/README.es.txt b/client/README.es.txt deleted file mode 100644 index 374bf3c..0000000 --- a/client/README.es.txt +++ /dev/null @@ -1,23 +0,0 @@ -OpenGnsys Client README -======================== - -Este directorio contiene la estructura principal de datos que será -importada por los clientes OpenGnsys mediante Samba. - -Los subdirectorios se copian íntegramente al servidor bajo -/opt/opengnsys/client y serán importados por los clientes en -/opt/opengnsys. - -La estructura de datos es la siguiente: - -- bin scripts o binarios ejecutables por el cliente (compilados estáticamente). -- etc ficheros de configuración del cliente. -- interfaceAdm interface con el agente -- lib librerías de funciones. - - engine/bin ficheros con las funciones del motor de clonación. - - httpd ficheros de configuración del servicio lighttpd. - - modules módulos extra para el Kernel del cliente. - - qtlib librerías Qt complementarias del Browser. - - qtplugins plugins Qt para el Browser. -- scripts funciones de alto nivel ejecutables por OpenGnsys Browser - y OpenGnsys Admin. diff --git a/client/bin/EACInterfaces b/client/bin/EACInterfaces deleted file mode 100755 index b63b1db15230b13bf5205d08670c62603bf26cd3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9682 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

#U@;w33p!k4TO^(bSu&I4W}5g0;xOmbCfT2uGXU zb6*J2HG1bTkV-T@+s%4mEpBgrTHiMRSUt8{^L?!WzScZ- z<1ggvE;62ZZu4;&{h5DAaIK?RN~p2Vdb!4|+C&3u`F9G!uc-1-0hKeA^itvTBz?Ap z#$+;jDZg-w{a@OZl&A_~ozir%KDZH+E2mDWD04TDjKt9lCjkR6LFo-JupcOlf7kdM zDzojM0@Sv8S1-+D>jq~>apIL@^y`PCK00ElXCx-N>V@Z~xz-G(8k#?zb;02k znIot<6bP4~5IE1rUl;S2&hKi;#ONS|boWJN@XL4XdgBu)H5gxRs2}w2{oCQU_Bi;=i)ro)Sj|(_a_MOy?;hqndWCYfq&{XrRCyIf_}v zmgk4XFTrV6_cZDzKN!l0)IftK7E3bCte&0dcEXNj04fCN27QketN1-UI&!bUwg-eq zeQgo;DqO4HG(FY^&O$DP!Im#&QW@$VN++rF*2ufvQxoU&P`)b4h{VFPQGM%3YZA zuKtN_fE)k;8=&5ZVB>2VmW{hIEIl)a!SK3RaC`b493eL8>HE8D7xmimDSn$q@O|2& zw=FMr>8;x@YrXv<`2V-J?!pMKmHhnSXzRw&bVKn4HpH>JVe%>sU$YCU1)nk=vdi@B zLU@3eZQ{$D>nRr+?sH_8i@^g;+gFL0kK%2|9%eG@r|!-VMFr`AMR4(2+&E3j8Qtie z-0?MdEM>vu58;WeTpGVtu4>muXjex!=fXacy9>4CXYYuGxISzX`iO^kCyi0W-M~GE z53%0<{Hh1R=ium9b0<+Vnzxd?Ok(xaDK%w@A$rL%)}og)-TNB97S!TIsre#dbMVJN z<99d#FFO@$ezaiLsy%WhI+%ji{IFdDDnF)EsQet|5aO9S9sN_NM`nZS#aEka;iqKB zQ9hrRI$`?P7LJuB(3gv{Zcb=#CHxT2$n=SuY_H-GKlyRoHyd9qkAUlDgh18XmL596 z&AZciJpk)OlFb7Xb>(}~;i56JemD);--Tx_8I z3@2}gD+G%Ek1r1@)~TK4SZ=$8m6Tni9xP=gsVjWB8>wokS@*7>1wyO*Gf0ObX%45IM z)4?G--$^3VNjz$?v)%C`k!g>wGLi)??mgi%+npV6w>yPDpR@r1*;HTd!hg z#P4=|luUeuaP>_2-X@VI;)s7oMnAs7vGX3nFT&1yDALM?bQF(*1NT!on*F8dJbA^` zYii13Ef3LidxQKCclG>}0$hBDJeHbGf{+^fFFMZe#dz~}v6Sfbch>I_dw+D;#YZCh z0Bx7jC z^ViwBQLTmzQ2ktw>>B;lQM6%aSG=5qk#|Ssz=U?FbTt}U=8hCA&q5O z=cum}%!);ierR23A;3b#@BnQ~k-b679Gx!aDAaM2%SX5+jcj5e zlI#~3h+=@Eoc4;~)J{=pVS!(E(G1kjVXho2DlY`@%!p}y2pTw5CzVh|R;kDw{B3Z< zx)le#4=EA0kEM3sRtW{UPwe8O&_6;G7VOQO+-u_N2SrS^^uk8DP0(AZ$$rwVGyx-7 zSZv0-Fjjni7G8+YQrp?&JStM8u#RP2RYYlauwHBk%zfUAhTFo}PjaRQ`UKf7! z6K;}dTq}K++ef%OEE;DY6bA_A!5&ZT{m&+ka;l+#=LgtfB2hdzU%*qtxD`B4_IO@F z?}}l;E%4mk@SIn~^D5d_NCu|bL3k2KFW@P*6`t1tTl8!&JP+@J=Lo}dZZ9%@v;KqAzpv`4085hR^f8hU`Heo{(W!WDUpb6Gzl4tpXNb!yi1#vsx#K=|r%fxj;z3B2YD+|IzEhXHmC2pFBl8 zzbLj~oS>R#ae?VcySB_G(X;+Z^`dl%0ADvr96BP*zt>R`hp0{IMT$vcYlz1yNG>i^ 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