Skip to content

Instantly share code, notes, and snippets.

@libcrack
Created May 17, 2024 12:00
Show Gist options
  • Select an option

  • Save libcrack/8bb4aa2668f67dc2c411ee2f7322dea6 to your computer and use it in GitHub Desktop.

Select an option

Save libcrack/8bb4aa2668f67dc2c411ee2f7322dea6 to your computer and use it in GitHub Desktop.

Revisions

  1. libcrack created this gist May 17, 2024.
    341 changes: 341 additions & 0 deletions timemachine.sh
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,341 @@
    # Fri May 17 13:52:40 CEST 2024
    # [email protected]
    #
    # timemachine helper functions:
    # timemachine-notify
    # timemachine-notify-loop
    # timemachine-speedup
    # timemachine-log
    # timemachine-logstream
    # timemachine-log-show
    # timemachine-log-stream
    # timemachine-dialog
    #
    # Usage:
    # source timemachine.sh
    #
    # Note: for timemachine-dialog to work you need to install dialog:
    # brew install dialog
    #

    # ——————————————————————————————————————————————————————————————————————————————
    # msg
    # ——————————————————————————————————————————————————————————————————————————————
    msg(){
    [[ ! -n "${1}" ]] && { msg="${_}"; } || { msg="${*}"; }
    printf "\e[1m[➤]\e[0m ${msg}\n" > /dev/stdout
    return $?
    }

    # ——————————————————————————————————————————————————————————————————————————————
    # error
    # ——————————————————————————————————————————————————————————————————————————————
    error(){
    [[ ! -n "${1}" ]] && { msg="${_}"; } || { msg="${*}"; }
    printf "${RED}[✕]${RESET} ${msg}\n" > /dev/stderr;
    return $?
    }

    # ——————————————————————————————————————————————————————————————————————————————
    # success
    # ——————————————————————————————————————————————————————————————————————————————
    success(){
    [[ ! -n "${1}" ]] && { msg="${_}"; } || { msg="${*}"; }
    printf "${IGREEN}[✓]${RESET} ${msg}\n" > /dev/stdout;
    return $?
    }

    # ——————————————————————————————————————————————————————————————————————————————
    # timemachine-notify-loop
    # ——————————————————————————————————————————————————————————————————————————————
    timemachine-notify(){

    /usr/bin/tmutil status | /usr/bin/grep -q "Running = 0;" && {
    error "TimeMachine Backup is not running (1)" > /dev/stderr
    return 1
    }

    /usr/bin/tmutil status | /usr/bin/grep -q "Running = 1;" || {
    error "TimeMachine Backup is not running (2)" > /dev/stderr
    return 2
    }

    # tmutil status
    # Backup session status:
    # {
    # ClientID = "com.apple.backupd";
    # Running = 0;
    # }
    # -----------------------------------
    # BackupPhase = FindingChanges;
    # ClientID = "com.apple.backupd";
    # DateOfStateChange = "2023-03-26 03:31:30 +0000";
    # DestinationID = "DACD0E28-5A80-46BB-9A1E-4F246FB53B8B";
    # DestinationMountPoint = "/Volumes/Time Machine Backups";
    # Percent = "-1";
    # Running = 1;
    # Stopping = 0;
    # -----------------------------------
    # BackupPhase = SizingChanges;
    # ClientID = "com.apple.backupd";
    # DateOfStateChange = "2023-03-26 04:44:46 +0000";
    # DestinationID = "DACD0E28-5A80-46BB-9A1E-4F246FB53B8B";
    # DestinationMountPoint = "/Volumes/Time Machine Backups";
    # Percent = "-1";
    # Running = 1;
    # Stopping = 0;
    # -----------------------------------
    # Backup session status:
    # BackupPhase = PreparingSourceVolumes;
    # ClientID = "com.apple.backupd";
    # DateOfStateChange = "2023-04-11 19:07:54 +0000";
    # DestinationID = "284420B7-08AE-48B4-8330-81D4456263E1";
    # DestinationMountPoint = "/Volumes/SSD";
    # FirstBackup = 1;
    # Percent = "-1";
    # Running = 1;
    # Stopping = 0;
    # -----------------------------------
    # BackupPhase = MountingBackupVol;
    # Percent = "-1";
    # Running = 1;
    # Stopping = 0;
    # -----------------------------------
    # BackupPhase = Starting;
    # Percent = "-1";
    # Running = 1;
    # Stopping = 0;
    # -----------------------------------
    # BackupPhase = ThinningPreBackup;
    # Percent = "-1";
    # Running = 1;
    # Stopping = 0;
    # -----------------------------------
    # BackupPhase = ThinningPostBackup;
    # Percent = "0.8174361601788563";
    # Running = 1;
    # Stopping = 0;
    # -----------------------------------
    # BackupPhase = HealthCheckFsck;
    # ClientID = "com.apple.backupd";
    # DateOfStateChange = "2022-10-31 00:04:22 +0000";
    # DestinationID = "04C75A92-CDBE-440A-B16D-55263971730E";
    # Percent = "0.78";
    # Running = 1;
    # Stopping = 0;
    # -----------------------------------
    # BackupPhase = Copying;
    # Progress = {
    # Percent = "0.984929769770861";
    # TimeRemaining = 54;
    # "_raw_Percent" = "0.984929769770861";
    # "_raw_totalBytes" = 4614522535;
    # bytes = 4544980618;
    # files = 118548;
    # totalBytes = 4614522535;
    # totalFiles = 98774;
    # };
    # Running = 1;
    # Stopping = 0;
    # -----------------------------------
    # bytes = 1616504327;
    # totalBytes = 10757451460;
    # -----------------------------------
    # Progress = {
    # Percent = 1;
    # "_raw_Percent" = "1.07504395858368";
    # "_raw_totalBytes" = 4614522535;
    # bytes = 4960814573;
    # files = 277411;
    # totalBytes = 4614522535;
    # totalFiles = 257637;
    # }
    # -----------------------------------
    # -----------------------------------
    # -----------------------------------

    local phase="$(/usr/bin/tmutil status | /usr/bin/grep BackupPhase | awk '{print $3}' | tr -d ';')"

    case "${phase}" in
    Copying|ThinningPostBackup)
    ;;
    Starting|PreparingSourceVolumes|HealthCheckFsck|MountingBackupVol|FindingChanges|SizingChanges|ThinningPreBackup)
    alert "Yet not copying files. Current phase: \"${phase}\"\n"
    return 10
    ;;
    *)
    error "No phase detected: \"${phase}\"\n"
    #printf "Error: No phase detected: \"${phase}\"\n"
    return 5
    ;;
    esac

    # case "${phase}" in
    # "Starting") ;;
    # "PreparingSourceVolumes") alert "Current phase: \"${phase}\"\n"; return 10; ;;
    # "HealthCheckFsck") ;;
    # "MountingBackupVol") ;;
    # "FindingChanges") ;;
    # "SizingChanges") ;;
    # "ThinningPreBackup") ;;
    # "ThinningPostBackup") ;;
    # "Copying") ;;
    # *)
    # error "No phase detected: \"${phase}\"\n"
    # #printf "Error: No phase detected: \"${phase}\"\n"
    # return 5
    # ;;
    # esac

    local threshold=60
    local percent="$(/usr/bin/tmutil status | /usr/bin/grep 'Percent = ' | awk '{print $3}' | tr -d ';' | tr -d '"')"
    local remainig_secs="$(/usr/bin/tmutil status | /usr/bin/grep TimeRemaining | awk '{print $3}' | tr -d ';' | tr -d '"')"
    # [[ -z "${remainig_secs}" ]] && remainig_secs=0
    local remainig_minutes="$(echo "${remainig_secs}/60" | /usr/bin/bc -l)"
    local remainig_hours="$(echo "${remainig_secs}/3600" | /usr/bin/bc -l)"
    local running="$(/usr/bin/tmutil status | /usr/bin/grep Running | awk '{print $3}' | tr -d ';')"
    local stopping="$(/usr/bin/tmutil status | /usr/bin/grep Stopping | awk '{print $3}' | tr -d ';')"
    local bytes="$(/usr/bin/tmutil status | /usr/bin/grep 'bytes = ' | awk '{print $3}' | tr -d ';')"
    local totalBytes="$(/usr/bin/tmutil status | /usr/bin/grep 'totalBytes = ' | awk '{print $3}' | tr -d ';')"
    local Mbytes=$((${bytes}/1024/1024))
    # .bashrc: line 1240: /1024/1024: syntax error: operand expected (error token is "/1024/1024")
    local totalMBytes=$((${totalBytes}/1024/1024))
    local remaining_mbytes="$((${totalMBytes}-${Mbytes}))"
    local total_percent="$(echo ${percent}*100 | bc -l)"

    printf "Running: ${running}\n"
    printf "Stopping: ${stopping}\n"
    printf "BackupPhase: ${phase}\n"
    # printf "Percent: ${percent}\n"
    printf "Percent: \e[1m%.2f %%\e[0m\n" "${total_percent}"
    # printf "TimeRemaining: ${remainig_secs} secs\n"
    printf "Remaining Seconds: \e[1m%d secs\e[0m\n" "${remainig_secs}"
    printf "Remaining Minutes: \e[1m%.2f mins\e[0m\n" "${remainig_minutes}"
    printf "Remaining Hours: \e[1m%.2f hours\e[0m\n" "${remainig_hours}"
    #printf "bytes: ${bytes}\n"
    #printf "total bytes: ${totalBytes}\n"
    printf "Megabytes: ${Mbytes} MB\n"
    printf "Total Megabytes: ${totalMBytes} MB\n"
    printf "Remaining Megabytes: \e[1m${remaining_mbytes} MB\e[0m\n"

    [[ -z "${remainig_secs}" ]] && {
    error "TimeMachine Backup running but no TimeRemaining (3)" > /dev/stderr
    /usr/bin/tmutil status
    return 3
    }

    [[ ${remainig_secs} -le ${threshold} ]] && {
    msg "${remainig_secs} < ${threshold}"
    terminal-notifier \
    -title "TimeMachine Backup" \
    -message "Backup finish in less than a minute (${remainig_secs} secs)"
    }

    return ${remainig_secs}

    return $?
    }

    # ——————————————————————————————————————————————————————————————————————————————
    # timemachine-notify-loop
    # ——————————————————————————————————————————————————————————————————————————————
    timemachine-notify-loop(){

    local interval=$((60*5))
    # local message="$(timemachine-notify \
    # | grep -E '^(Percent|Remaining (Minutes|Hours))' \
    # | sed -e 's/\x1B\[[0-9;]\{1,\}[A-Za-z]//g')"

    [[ -n "${1}" ]] && interval="${1}"

    while true; do
    local message="$(timemachine-notify \
    | grep -E '^(Percent|Remaining (Minutes|Hours))' \
    | sed -e 's/\x1B\[[0-9;]\{1,\}[A-Za-z]//g')"
    notifier-terminal \
    -title "Time Machine Backup" \
    -message "${message}"
    sleep ${interval}
    done

    return $?
    }

    # ——————————————————————————————————————————————————————————————————————————————
    # timemachine-notify-loop
    # ——————————————————————————————————————————————————————————————————————————————
    timemachine-speedup(){
    /usr/bin/tmutil status | /usr/bin/grep -q "Running = 1;" && {
    warning "TimeMachine Backup is running" > /dev/stdout
    msg "Disabling debug.lowpri_throttle_enabled=0" > /dev/stdout
    sudo sysctl debug.lowpri_throttle_enabled=0
    msg "Increasing task priority renice -20" > /dev/stdout
    sudo renice -20 "$(pgrep '^backupd$')"
    }

    # } || {
    /usr/bin/tmutil status | /usr/bin/grep -q "Running = 0;" && {
    warning "TimeMachine Backup is not running" > /dev/stdout
    msg "Enabling debug.lowpri_throttle_enabled=1" > /dev/stdout
    sudo sysctl debug.lowpri_throttle_enabled=1
    }

    return $?
    }

    # ——————————————————————————————————————————————————————————————————————————————
    # timemachine-log
    # ——————————————————————————————————————————————————————————————————————————————
    timemachine-log(){
    /usr/bin/log show \
    --predicate 'subsystem == "com.apple.TimeMachine"' \
    --style compact \
    --last 1h \
    --info \
    --debug
    # --info | grep 'upd: (' | cut -c 1-19,87-999
    return $?
    }

    # ——————————————————————————————————————————————————————————————————————————————
    # timemachine-logstream
    # ——————————————————————————————————————————————————————————————————————————————
    timemachine-logstream(){
    /usr/bin/log stream \
    --predicate 'subsystem == "com.apple.TimeMachine"' \
    --style compact \
    --level debug
    return $?
    }

    # ——————————————————————————————————————————————————————————————————————————————
    # timemachine-log-show
    # ——————————————————————————————————————————————————————————————————————————————
    timemachine-log-show(){
    /usr/bin/log show \
    --predicate "(process == \"backupd\") || (process == \"backupd-helper\")" \
    --debug
    return $?
    }

    # ——————————————————————————————————————————————————————————————————————————————
    # timemachine-log-stream
    # ——————————————————————————————————————————————————————————————————————————————
    timemachine-log-stream(){
    /usr/bin/log stream \
    --predicate "(process == \"backupd\") || (process == \"backupd-helper\")" \
    --level debug
    return $?
    }

    # ——————————————————————————————————————————————————————————————————————————————
    # timemachine-dialog
    # XXX brew install dialog
    # ——————————————————————————————————————————————————————————————————————————————
    timemachine-dialog(){
    while true; do
    echo "$(tmutil status | grep "Percent =" | awk "{print \$3}" | cut -f2 -d \")*100" | bc -l | cut -f1 -d.
    sleep 5
    done | dialog --guage "TimeMachine Backup" 6 $((COLUMNS-20))
    }