#!/usr/bin/env bash #********************************************# # # # ZshSyncTools v0.2.8 # # A CLI tool to backup and restore termux # # https://github.com/tuxgyver/termuxsynctools # # By Johnny et Noe Fontaine # # # #********************************************# # Variables readonly SOURCE="$HOME" readonly DEST="$HOME/Backup" readonly PKGS_SRC="/var/log/apt/history.log" readonly GDRIVE_REMOTE="Gdrive:/Termux/Backup" readonly VERSION="0.2.8" readonly TEMP_DIR="/tmp" readonly program_name=$(basename "$0") # Colors B='\e[1;34m' # blue R='\e[1;31m' # red G='\e[1;32m' # green W='\e[1;37m' # white bold Y='\e[1;33m' # yellow off='\e[0m' # reset color t=' ' # tab OK=" $W[$G ✓ $W]$off" EXC=" $W[$Y • $W]$off" ERR=" $W[$R ✗ $W]$off" # Initialize environment function init_environment() { # Vérifier/créer le répertoire temporaire if [[ ! -d "${TEMP_DIR}" ]]; then mkdir -p "${TEMP_DIR}" chmod 700 "${TEMP_DIR}" fi # Vérifier/créer le répertoire de destination if [[ ! -d "${DEST}" ]]; then mkdir -p "${DEST}" fi } # Banner function display_banner() { figlet "TermSyncTools" | lolcat -i printf "\n\n" } # Help function show_help() { printf "${W}Usage: ${G}${program_name}${off} [-hvq] [-b|-r [home|pkgs|proot]] [-d] [-s] [-a]\n" printf "${W}-h${off} ${G}affiche cette aide${off}\n" printf "${W}-v${off} ${G}affiche la version${off}\n" printf "${W}-b [home|pkgs|proot]${off} ${G}sauvegarde home, packages ou proot-distrib${off}\n" printf "${W}-r [home|pkgs|proot]${off} ${G}restaure home, packages ou proot-distrib${off}\n" printf "${W}-d${off} ${G}supprime les backups${off}\n" printf "${W}-s${off} ${G}sauvegarde automatique${off}\n" printf "${W}-a${off} ${G}supprime la programmation de la sauvegarde automatique${off}\n" printf "${W}-q${off} ${G}quitte pendant la restauration${off}\n" printf "By Fontaine ${B}Johnny${off} & ${G}Noe${off} \n" } # Version function show_version() { printf "TermSyncTools version ${VERSION}\n" printf "Outil de sauvegarde et restauration pour Termux\n" printf "https://github.com/Noewolf5449/termux-sync\n" printf "By Fontaine ${B}Johnny${off} & ${G}Noe${off} \n" } # Error handling function error() { printf "${ERR} ${R}Erreur:${off} $*\n" >&2 } # Success message function success() { printf "${OK} $*\n" } # Info message function info() { printf "${EXC} $*\n" } # Check Internet function check_internet() { if ping -q -w 1 -c 1 8.8.8.8 &> /dev/null; then return 0 fi return 1 } # Google Drive sync function sync_with_gdrive() { local direction=$1 local file=$2 info "Synchronisation avec Google Drive..." if ! rclone listremotes | grep -q "Gdrive:"; then error "Google Drive non configuré. Exécutez 'rclone config'" return 1 fi case "$direction" in "up") if [[ -n "$file" && -f "${DEST}/${file}" ]]; then if rclone copy "${DEST}/${file}" "${GDRIVE_REMOTE}" --progress; then success "Synchronisation terminée" return 0 fi fi ;; "down") if rclone sync "${GDRIVE_REMOTE}" "${DEST}" --progress; then success "Synchronisation terminée" return 0 fi ;; *) error "Direction de synchronisation invalide" return 1 ;; esac error "Échec de la synchronisation" return 1 } # Backup home function backup_home() { info "Sauvegarde du home" printf "${t}Veuillez patienter...\n" local temp_file="${TEMP_DIR}/${backup_name}_home.tmp" local backup_file="${DEST}/${backup_name}_home.bak" # Liste des exclusions local exclude_dirs=( ".cache" "storage" "*/node_modules" "*/venv" "${TEMP_DIR}" "${DEST}" ) local exclude_args="" for dir in "${exclude_dirs[@]}"; do exclude_args+="--exclude='${dir}' " done # Sauvegarde if eval tar -I pigz -cf "${temp_file}" -C "${SOURCE}" ${exclude_args} . ; then mv "${temp_file}" "${backup_file}" success "Home sauvegardé dans: ${backup_file}" sync_with_gdrive "up" "${backup_name}_home.bak" return 0 else rm -f "${temp_file}" error "Échec de la sauvegarde du home" return 1 fi } # Backup packages function backup_pkgs() { info "Sauvegarde des packages" printf "${t}Veuillez patienter...\n" local pkg_file="${DEST}/${backup_name}_pkgs.bak" # Liste des packages installés if dpkg --get-selections | grep -v deinstall | cut -f1 > "${pkg_file}"; then success "Packages sauvegardés dans: ${pkg_file}" sync_with_gdrive "up" "${backup_name}_pkgs.bak" return 0 else error "Échec de la sauvegarde des packages" return 1 fi } # Backup proot-distrib function backup_proot() { info "Sauvegarde de proot-distrib" printf "${t}Veuillez patienter...\n" local proot_file="${DEST}/${backup_name}_proot.bak" # Sauvegarde de proot-distrib if tar -I pigz -cf "${proot_file}" -C "${SOURCE}" proot-distro ; then success "proot-distrib sauvegardé dans: ${proot_file}" sync_with_gdrive "up" "${backup_name}_proot.bak" return 0 else error "Échec de la sauvegarde de proot-distrib" return 1 fi } # Restore home function restore_home() { info "Restauration du home" printf "${t}Veuillez patienter...\n" # Synchronisation avec Google Drive sync_with_gdrive "down" # Vérifie les sauvegardes disponibles local backups=($(ls -1 ${DEST}/*_home.bak 2>/dev/null)) if [ ${#backups[@]} -eq 0 ]; then error "Aucune sauvegarde disponible" return 1 fi # Liste les sauvegardes printf "\n${t}Sauvegardes disponibles:\n" for i in "${!backups[@]}"; do printf "${t}$((i+1)). ${backups[$i]##*/}\n" done printf "\n" # Sélection de la sauvegarde local backup_number read -p "Numéro de la sauvegarde à restaurer (ou 'q' pour quitter): " backup_number if [[ "$backup_number" == "q" ]]; then info "Quitter la restauration" return 0 fi if ! [[ "$backup_number" =~ ^[0-9]+$ ]] || \ [ "$backup_number" -lt 1 ] || \ [ "$backup_number" -gt ${#backups[@]} ]; then error "Sélection invalide" return 1 fi local selected_backup="${backups[$((backup_number-1))]}" local safety_backup="${TEMP_DIR}/home_backup_before_restore_$(date +%Y%m%d_%H%M%S).bak" # Crée une sauvegarde de sécurité info "Création d'une sauvegarde de sécurité..." if ! tar -I pigz -cf "${safety_backup}" -C "${SOURCE}" . ; then error "Impossible de créer la sauvegarde de sécurité" return 1 fi # Restaure la sauvegarde info "Restauration en cours..." if tar -I pigz -xf "${selected_backup}" -C "${SOURCE}"; then success "Home restauré avec succès" printf "${t}Redémarrez votre session pour appliquer les changements\n" rm -f "${safety_backup}" return 0 else error "Échec de la restauration" info "Restauration de la sauvegarde de sécurité..." tar -I pigz -xf "${safety_backup}" -C "${SOURCE}" rm -f "${safety_backup}" return 1 fi } # Restore packages function restore_pkgs() { if ! check_internet; then error "Pas de connexion internet" return 1 fi info "Restauration des packages" printf "${t}Veuillez patienter...\n" # Synchronisation avec Google Drive sync_with_gdrive "down" # Vérifie les sauvegardes disponibles local backups=($(ls -1 ${DEST}/*_pkgs.bak 2>/dev/null)) if [ ${#backups[@]} -eq 0 ]; then error "Aucune sauvegarde disponible" return 1 fi # Liste les sauvegardes printf "\n${t}Sauvegardes disponibles:\n" for i in "${!backups[@]}"; do printf "${t}$((i+1)). ${backups[$i]##*/}\n" done printf "\n" # Sélection de la sauvegarde local backup_number read -p "Numéro de la sauvegarde à restaurer (ou 'q' pour quitter): " backup_number if [[ "$backup_number" == "q" ]]; then info "Quitter la restauration" return 0 fi if ! [[ "$backup_number" =~ ^[0-9]+$ ]] || \ [ "$backup_number" -lt 1 ] || \ [ "$backup_number" -gt ${#backups[@]} ]; then error "Sélection invalide" return 1 fi local selected_backup="${backups[$((backup_number-1))]}" # Mise à jour et installation des packages if apt-get update && apt-get install -y $(cat "${selected_backup}"); then success "Packages restaurés" printf "${t}Redémarrez votre session pour appliquer les changements\n" return 0 else error "Échec de la restauration des packages" return 1 fi } # Restore proot-distrib function restore_proot() { info "Restauration de proot-distrib" printf "${t}Veuillez patienter...\n" # Synchronisation avec Google Drive sync_with_gdrive "down" # Vérifie les sauvegardes disponibles local backups=($(ls -1 ${DEST}/*_proot.bak 2>/dev/null)) if [ ${#backups[@]} -eq 0 ]; then error "Aucune sauvegarde disponible" return 1 fi # Liste les sauvegardes printf "\n${t}Sauvegardes disponibles:\n" for i in "${!backups[@]}"; do printf "${t}$((i+1)). ${backups[$i]##*/}\n" done printf "\n" # Sélection de la sauvegarde local backup_number read -p "Numéro de la sauvegarde à restaurer (ou 'q' pour quitter): " backup_number if [[ "$backup_number" == "q" ]]; then info "Quitter la restauration" return 0 fi if ! [[ "$backup_number" =~ ^[0-9]+$ ]] || \ [ "$backup_number" -lt 1 ] || \ [ "$backup_number" -gt ${#backups[@]} ]; then error "Sélection invalide" return 1 fi local selected_backup="${backups[$((backup_number-1))]}" local safety_backup="${TEMP_DIR}/proot_backup_before_restore_$(date +%Y%m%d_%H%M%S).bak" # Crée une sauvegarde de sécurité info "Création d'une sauvegarde de sécurité..." if ! tar -I pigz -cf "${safety_backup}" -C "${SOURCE}" proot-distro ; then error "Impossible de créer la sauvegarde de sécurité" return 1 fi # Restaure la sauvegarde info "Restauration en cours..." if tar -I pigz -xf "${selected_backup}" -C "${SOURCE}"; then success "proot-distrib restauré avec succès" printf "${t}Redémarrez votre session pour appliquer les changements\n" rm -f "${safety_backup}" return 0 else error "Échec de la restauration" info "Restauration de la sauvegarde de sécurité..." tar -I pigz -xf "${safety_backup}" -C "${SOURCE}" rm -f "${safety_backup}" return 1 fi } # Backup function function backup() { local type=$1 # Demande le nom de la sauvegarde read -p "Nom de la sauvegarde (vide pour nom par défaut): " backup_name if [[ -z "$backup_name" ]]; then backup_name="backup_$(date +%Y%m%d_%H%M%S)" fi case "$type" in "home") backup_home ;; "pkgs") backup_pkgs ;; "proot") backup_proot ;; "") backup_home && backup_pkgs && backup_proot ;; *) error "Type de sauvegarde invalide" show_help return 1 ;; esac } # Restore function function restore() { local type=$1 case "$type" in "home") restore_home ;; "pkgs") restore_pkgs ;; "proot") restore_proot ;; "") restore_home && restore_pkgs && restore_proot ;; *) error "Type de restauration invalide" show_help return 1 ;; esac } # Delete backups function delete_backups() { info "Suppression des backups" printf "${t}Veuillez patienter...\n" # Synchronisation avec Google Drive sync_with_gdrive "down" # Vérifie les sauvegardes disponibles local backups=($(ls -1 ${DEST}/*.bak 2>/dev/null)) if [ ${#backups[@]} -eq 0 ]; then error "Aucune sauvegarde disponible" return 1 fi # Liste les sauvegardes printf "\n${t}Sauvegardes disponibles:\n" for i in "${!backups[@]}"; do printf "${t}$((i+1)). ${backups[$i]##*/}\n" done printf "\n" # Sélection de la sauvegarde à supprimer local backup_number read -p "Numéro de la sauvegarde à supprimer: " backup_number if ! [[ "$backup_number" =~ ^[0-9]+$ ]] || \ [ "$backup_number" -lt 1 ] || \ [ "$backup_number" -gt ${#backups[@]} ]; then error "Sélection invalide" return 1 fi local selected_backup="${backups[$((backup_number-1))]}" local backup_name="${selected_backup##*/}" # Suppression locale if rm -f "${selected_backup}"; then success "Backup local supprimé: ${backup_name}" else error "Échec de la suppression du backup local: ${backup_name}" return 1 fi # Suppression distante if rclone delete "${GDRIVE_REMOTE}/${backup_name}"; then success "Backup distant supprimé: ${backup_name}" else error "Échec de la suppression du backup distant: ${backup_name}" return 1 fi # Affiche la liste des backups restants local remaining_backups=($(ls -1 ${DEST}/*.bak 2>/dev/null)) if [ ${#remaining_backups[@]} -eq 0 ]; then info "Aucune sauvegarde restante" else printf "\n${t}Sauvegardes restantes:\n" for i in "${!remaining_backups[@]}"; do printf "${t}$((i+1)). ${remaining_backups[$i]##*/}\n" done printf "\n" fi return 0 } # Auto backup function auto_backup() { info "Configuration de la sauvegarde automatique" printf "${t}Veuillez patienter...\n" local cron_job="0 7 * * * /usr/bin/env bash termsynctools -b" if (crontab -l 2>/dev/null; echo "$cron_job" ) | crontab -; then success "Sauvegarde automatique configurée" return 0 else error "Échec de la configuration de la sauvegarde automatique" return 1 fi } # Delete auto backup function delete_auto_backup() { info "Suppression de la sauvegarde automatique" printf "${t}Veuillez patienter...\n" local cron_job="0 7 * * * /usr/bin/env bash termsynctools -b" local temp_crontab=$(mktemp) # Lire le fichier crontab actuel crontab -l > "$temp_crontab" # Supprimer la ligne spécifique de la tâche cron grep -vF "$cron_job" "$temp_crontab" > "$temp_crontab.tmp" && mv "$temp_crontab.tmp" "$temp_crontab" # Écrire le fichier crontab mis à jour crontab "$temp_crontab" # Supprimer le fichier temporaire rm -f "$temp_crontab" success "Sauvegarde automatique supprimée" return 0 } # Install dependencies function install_dependencies() { if check_internet; then info "Installation des dépendances..." if apt-get update && \ apt-get install -y tar pigz rclone figlet lolcat cronie; then success "Dépendances installées" return 0 fi else error "Pas de connexion internet" fi return 1 } # Configure rclone function configure_rclone() { if ! rclone listremotes | grep -q "Gdrive:"; then info "Configuration de rclone avec Google Drive..." rclone config else success "rclone déjà configuré avec Google Drive" fi } # Get optional arguments function getopts_get_optional_argument() { eval next_token=\${$OPTIND} if [[ -n $next_token && $next_token != -* ]]; then OPTIND=$((OPTIND + 1)) OPTARG=$next_token else OPTARG="" fi } # Cleanup function function cleanup() { rm -f "${TEMP_DIR}/${backup_name}_home.tmp" "${TEMP_DIR}/${backup_name}_pkgs.tmp" error "Programme terminé de manière inattendue" exit 1 } # Main function main() { # Trap signals trap cleanup 1 2 3 15 20 # Initialize environment init_environment # Check and install dependencies if ! [[ $(type -P tar) && $(type -P pigz) && \ $(type -P rclone) && $(type -P figlet) && \ $(type -P lolcat) && $(type -P crontab) ]]; then install_dependencies fi # Configure rclone configure_rclone # Display banner display_banner # Parse arguments local OPTIND while getopts ":hvbrdqsa" opt; do case $opt in h) show_help return 0 ;; v) show_version return 0 ;; b) getopts_get_optional_argument "$@" backup "${OPTARG}" return $? ;; r) getopts_get_optional_argument "$@" restore "${OPTARG}" return $? ;; d) delete_backups return $? ;; s) auto_backup return $? ;; a) delete_auto_backup return $? ;; q) info "Quitter la restauration" return 0 ;; \?) error "Option invalide" return 1 ;; :) error "Option nécessitant un argument" show_help return 1 ;; esac done if [[ $OPTIND -eq 1 ]]; then error "Aucune option fournie" printf "Utilisez '${program_name} -h' pour l'aide\n" return 1 fi } main "$@" exit $?