No.

It doesn’t really do anything I particularly need.

#!/bin/bash
read_settings() {
  settings_file="$HOME/.config/loci/settings"
  if [[ -f "$settings_file" ]]; then
    while IFS='=' read -r key value || [[ -n "$key" ]]; do
      if [[ ! -z "$key" && ! "$key" =~ ^# && ! "$key" =~ ^\[ ]]; then
        key=$(echo "$key" | xargs)
        value=$(echo "$value" | xargs)
        declare -g "$key"="$value"
      fi
    done < "$settings_file"
  else
    echo "Settings file not found: $settings_file"
    exit 1
  fi
}

# Function to perform the backup
backup() {
  local tag="$1"
  read_settings
  
  log_path="$HOME/.backuplog"
  
  # Check if header exists in log file, if not, create it
  if [[ ! -f "$log_path" ]]; then
    echo "\"tag\",\"timestamp\",\"command\",\"completion_time\"" > "$log_path"
  elif [[ $(head -1 "$log_path") != "\"tag\",\"timestamp\",\"command\",\"completion_time\"" ]]; then
    # Add header if it doesn't exist
    temp_file=$(mktemp)
    echo "\"tag\",\"timestamp\",\"command\",\"completion_time\"" > "$temp_file"
    cat "$log_path" >> "$temp_file"
    mv "$temp_file" "$log_path"
  fi
  
  # Create backup directory if it doesn't exist
  backup_dest="$backup_root/$tag"
  mkdir -p "$backup_dest" 2>/dev/null
  
  # Rsync command for backup
  target="$user@$server:/home/$user/$backup_root/$tag"
  rsync_cmd="rsync -avh $source_dir $target"
  # If exclude_files is defined and not empty, add it to rsync command
  if [[ -n "$exclude_files" ]]; then
    rsync_cmd="rsync -avh --exclude='$exclude_files' $source_dir $target"
  fi
  
  echo "Starting backup for tag '$tag' at $(date '+%Y-%m-%d %H:%M:%S')"
  echo "Command: $rsync_cmd"
  
  # Record start time
  start_timestamp=$(date +"%Y-%m-%d %H:%M:%S")
  
  # Execute the backup
  eval "$rsync_cmd"
  backup_status=$?
  
  # Record completion time
  completion_timestamp=$(date +"%Y-%m-%d %H:%M:%S")
  
  # Calculate duration
  start_seconds=$(date -d "$start_timestamp" +%s)
  end_seconds=$(date -d "$completion_timestamp" +%s)
  duration=$((end_seconds - start_seconds))
  
  # Format duration
  if [[ $duration -ge 3600 ]]; then
    formatted_duration="$((duration / 3600))h $((duration % 3600 / 60))m $((duration % 60))s"
  elif [[ $duration -ge 60 ]]; then
    formatted_duration="$((duration / 60))m $((duration % 60))s"
  else
    formatted_duration="${duration}s"
  fi
  
  # Log the backup information as proper CSV
  echo "\"$tag\",\"$start_timestamp\",\"$rsync_cmd\",\"$completion_timestamp\"" >> "$log_path"
  
  if [[ $backup_status -eq 0 ]]; then
    echo -e "\e[32mBackup for '$tag' completed successfully\e[0m"
    echo "Duration: $formatted_duration"
    echo "Logged to: $log_path"
  else
    echo -e "\e[31mBackup for '$tag' failed with status $backup_status\e[0m"
  fi
}

# Function to remove the backup
remove_backup() {
  local tag="$1"
  read_settings
  
  echo "Removing backup for tag '$tag'..."
  
  # Rsync remove command
  rmfile="/home/$user/$backup_root/$tag"
  rm_cmd="ssh $user@$server rm -rf $rmfile"
  
  # Execute the removal command
  eval "$rm_cmd"
  rm_status=$?
  
  if [[ $rm_status -ne 0 ]]; then
    echo -e "\e[31mError: Failed to remove remote backup for tag '$tag'\e[0m"
    echo "Command failed: $rm_cmd"
    return 1
  fi
  
  # Remove log entries while preserving header
  log_path="$HOME/.backuplog"
  if [[ -f "$log_path" ]]; then
    # Create a temporary file
    temp_file=$(mktemp)
    
    # Copy header (first line) if it exists
    if [[ -s "$log_path" ]]; then
      head -1 "$log_path" > "$temp_file"
      # Only copy non-matching lines after header
      tail -n +2 "$log_path" | grep -v "^\"$tag\"," >> "$temp_file"
    else
      # If log is empty, add header
      echo "\"tag\",\"timestamp\",\"command\",\"completion_time\"" > "$temp_file"
    fi
    
    # Replace the original with filtered content
    mv "$temp_file" "$log_path"
    
    echo -e "\e[32mBackup '$tag' removed successfully\e[0m"
    echo "Log entries for '$tag' have been removed from $log_path"
  else
    echo -e "\e[32mBackup '$tag' removed successfully\e[0m"
    echo "No log file found at $log_path"
  fi
}

# Function to list the backups with detailed timing information
list_backups() {
  read_settings
  log_path="$HOME/.backuplog"
  
  echo "Backup Status Report ($(date '+%Y-%m-%d %H:%M:%S'))"
  echo "========================================================="
  printf "%-8s %-15s %-10s %-20s %-15s\n" "TAG" "STATUS" "COUNT" "LAST BACKUP" "DAYS AGO"
  echo "--------------------------------------------------------"
  
  # Check if header exists in log file, if not, create it
  if [[ ! -f "$log_path" ]]; then
    echo "\"tag\",\"timestamp\",\"command\",\"completion_time\"" > "$log_path"
    echo "Created new log file with CSV headers."
  elif [[ $(head -1 "$log_path") != "\"tag\",\"timestamp\",\"command\",\"completion_time\"" ]]; then
    # Add header if it doesn't exist
    temp_file=$(mktemp)
    echo "\"tag\",\"timestamp\",\"command\",\"completion_time\"" > "$temp_file"
    cat "$log_path" >> "$temp_file"
    mv "$temp_file" "$log_path"
    echo "Added CSV headers to existing log file."
  fi
  
  # Loop through each tag in the taglist
  for tag in $taglist; do
    # Count occurrences of the tag in the log
    count=0
    youngest=""
    days_ago="N/A"
    
    if [[ -f "$log_path" ]]; then
      # Skip header when counting
      count=$(grep -c "^\"$tag\"," "$log_path")
      
      # Get the newest backup date for this tag
      if [[ $count -gt 0 ]]; then
        # Extract dates and find the newest one
        dates=$(grep "^\"$tag\"," "$log_path" | cut -d',' -f2)
        youngest=$(echo "$dates" | sort -r | head -1)
        
        # Calculate days since last backup
        if [[ ! -z "$youngest" ]]; then
          youngest_seconds=$(date -d "$youngest" +%s)
          now_seconds=$(date +%s)
          days_diff=$(( (now_seconds - youngest_seconds) / 86400 ))
          days_ago="$days_diff days"
        fi
      fi
    fi
    
    # Determine status with colored output
    if [[ $count -eq 0 ]]; then
      status="Missing"
      status_color="\e[31m$status\e[0m" # Red
    elif [[ $count -gt 5 ]]; then
      status="Needs renewal"
      status_color="\e[33m$status\e[0m" # Yellow
    elif [[ ! -z "$youngest" ]]; then
      # Calculate days since last backup
      youngest_seconds=$(date -d "$youngest" +%s)
      now_seconds=$(date +%s)
      days_diff=$(( (now_seconds - youngest_seconds) / 86400 ))
      
      if [[ $days_diff -gt 7 ]]; then
        status="Needs to be run"
        status_color="\e[33m$status\e[0m" # Yellow
      else
        status="Up to date"
        status_color="\e[32m$status\e[0m" # Green
      fi
    else
      status="Missing"
      status_color="\e[31m$status\e[0m" # Red
    fi
    
    printf "%-8s %-15b %-10s %-20s %-15s\n" "$tag" "$status_color" "$count" "${youngest:-N/A}" "$days_ago"
  done
  
  echo "--------------------------------------------------------"
  echo "CSV log file: $log_path"
  echo "Run 'loci -l' to refresh this status report"
}

# Function to show backup stats
show_stats() {
  read_settings
  log_path="$HOME/.backuplog"
  
  if [[ ! -f "$log_path" ]]; then
    echo "No backup log found at $log_path"
    return 1
  fi
  
  echo "Backup Statistics"
  echo "================="
  
  # Total number of backups
  total_backups=$(grep -v "^\"tag\"" "$log_path" | wc -l)
  echo "Total backups: $total_backups"
  
  # Backups per tag
  echo -e "\nBackups per tag:"
  for tag in $taglist; do
    count=$(grep "^\"$tag\"," "$log_path" | wc -l)
    echo "  $tag: $count"
  done
  
  # Last backup time for each tag
  echo -e "\nLast backup time:"
  for tag in $taglist; do
    latest=$(grep "^\"$tag\"," "$log_path" | cut -d',' -f2 | sort -r | head -1)
    if [[ -z "$latest" ]]; then
      echo "  $tag: Never"
    else
      # Calculate days ago
      latest_seconds=$(date -d "$latest" +%s)
      now_seconds=$(date +%s)
      days_diff=$(( (now_seconds - latest_seconds) / 86400 ))
      echo "  $tag: $latest ($days_diff days ago)"
    fi
  done
  
  echo -e "\nBackup log file: $log_path"
  echo "To view in a spreadsheet: cp $log_path ~/backups.csv"
}

# Function to export log to CSV
export_csv() {
  read_settings
  log_path="$HOME/.backuplog"
  export_path="${1:-$HOME/backup_export.csv}"
  
  if [[ ! -f "$log_path" ]]; then
    echo "No backup log found at $log_path"
    return 1
  fi
  
  # Copy the log file to export location
  cp "$log_path" "$export_path"
  echo "Backup log exported to: $export_path"
  echo "You can now open this file in your spreadsheet application."
}

# Main function
main() {
  if [[ "$1" == "-b" || "$1" == "--backup" ]] && [[ ! -z "$2" ]]; then
    backup "$2"
  elif [[ "$1" == "-r" || "$1" == "--remove" ]] && [[ ! -z "$2" ]]; then
    remove_backup "$2"
  elif [[ "$1" == "-l" || "$1" == "--list" ]]; then
    list_backups
  elif [[ "$1" == "-s" || "$1" == "--stats" ]]; then
    show_stats
  elif [[ "$1" == "-e" || "$1" == "--export" ]]; then
    export_csv "$2"
  elif [[ "$1" == "-h" || "$1" == "--help" ]]; then
    echo "Loci Backup Management Tool"
    echo "Usage:"
    echo "  loci -b, --backup <tag>   Create a backup with the specified tag"
    echo "  loci -r, --remove <tag>   Remove a backup with the specified tag"
    echo "  loci -l, --list           List all backup statuses"
    echo "  loci -s, --stats          Show backup statistics"
    echo "  loci -e, --export [path]  Export backup log to CSV (default: ~/backup_export.csv)"
    echo "  loci -h, --help           Show this help message"
  else
    echo "Usage: loci -b <tag> | loci -r <tag> | loci -l | loci -s | loci -e [path] | loci -h"
  fi
}
[-] demeaning_casually@infosec.pub 0 points 1 week ago* (last edited 1 week ago)

One needs to be ~~compiled~~ installed and the other is literally the de facto scripting language installed everywhere and intended for exactly this purpose.

[-] demeaning_casually@infosec.pub -1 points 1 week ago* (last edited 1 week ago)

A hilariously unnecessary Python script that could have easily been done in bash since it’s literally just a wrapper around rsync. 😅

When you’ve only got a Python-sized hammer in your toolbox, everything looks like a Python nail, I guess.

#!/bin/bash

# Function to read settings
# Settings file format:
# ~/.config/loci/settings
# [backup]
#
# server = <<Name of server>>
# user = <<server user login>>
# backup_root = <<Directory off user's home Directory>>
# taglist = mon tue wed thu fri sat sun spc
# exclude_files = <<not implemented yet - leave blank>>
# source_dir = <<the local directory we are backing up>>
read_settings() {
  settings_file="$HOME/.config/loci/settings"
  if [[ -f "$settings_file" ]]; then
    while IFS='=' read -r key value || [[ -n "$key" ]]; do
      if [[ ! -z "$key" && ! "$key" =~ ^# && ! "$key" =~ ^\[ ]]; then
        key=$(echo "$key" | xargs)
        value=$(echo "$value" | xargs)
        declare -g "$key"="$value"
      fi
    done < "$settings_file"
  else
    echo "Settings file not found: $settings_file"
    exit 1
  fi
}

# Function to perform the backup
backup() {
  local tag="$1"
  read_settings
  
  # Create backup directory if it doesn't exist
  backup_dest="$backup_root/$tag"
  mkdir -p "$backup_dest" 2>/dev/null
  
  # Rsync command for backup
  target="$user@$server:/home/$user/$backup_root/$tag"
  rsync_cmd="rsync -avh $source_dir $target"
  # If exclude_files is defined and not empty, add it to rsync command
  if [[ -n "$exclude_files" ]]; then
    rsync_cmd="rsync -avh --exclude='$exclude_files' $source_dir $target"
  fi
  echo "Command:$rsync_cmd"
  eval "$rsync_cmd"
  
  # Log the backup information
  log_path="$HOME/.backuplog"
  timestamp=$(date +"%Y-%m-%d %H:%M")
  echo "\"$tag\",$timestamp,$rsync_cmd,$timestamp" >> "$log_path"
  
  echo "Backup for '$tag' completed and logged."
}

# Function to remove the backup
remove_backup() {
  local tag="$1"
  read_settings
  
  # Rsync remove command
  rmfile="/home/$user/$backup_root/$tag"
  rm_cmd="ssh $user@$server rm -rf $rmfile"
  eval "$rm_cmd"
  
  # Remove log entries
  log_path="$HOME/.backuplog"
  if [[ -f "$log_path" ]]; then
    # Create a temporary file
    temp_file=$(mktemp)
    # Copy lines not starting with the tag to temp file
    grep -v "^\"$tag\"," "$log_path" > "$temp_file"
    # Replace the original with filtered content
    mv "$temp_file" "$log_path"
  fi
  
  echo "Backup '$tag' removed."
}

# Function to list the backups
list_backups() {
  read_settings
  log_path="$HOME/.backuplog"
  
  # Loop through each tag in the taglist
  for tag in $taglist; do
    # Count occurrences of the tag in the log
    count=0
    youngest=""
    
    if [[ -f "$log_path" ]]; then
      # Get count of tag occurrences
      count=$(grep -c "^\"$tag\"," "$log_path")
      
      # Get the newest backup date for this tag
      if [[ $count -gt 0 ]]; then
        # Extract dates and find the newest one
        dates=$(grep "^\"$tag\"," "$log_path" | cut -d',' -f2)
        youngest=$(echo "$dates" | sort -r | head -1)
      fi
    fi
    
    # Determine status
    if [[ $count -eq 0 ]]; then
      status="Missing"
    elif [[ $count -gt 5 ]]; then
      status="Needs renewal"
    elif [[ ! -z "$youngest" ]]; then
      # Calculate days since last backup
      youngest_seconds=$(date -d "$youngest" +%s)
      now_seconds=$(date +%s)
      days_diff=$(( (now_seconds - youngest_seconds) / 86400 ))
      
      if [[ $days_diff -gt 7 ]]; then
        status="Needs to be run"
      else
        status="Up to date"
      fi
    else
      status="Missing"
    fi
    
    echo "Tag: $tag, Status: $status, Count: $count, Last Backup: ${youngest:-N/A}"
  done
}

# Main function
main() {
  if [[ "$1" == "-b" || "$1" == "--backup" ]] && [[ ! -z "$2" ]]; then
    backup "$2"
  elif [[ "$1" == "-r" || "$1" == "--remove" ]] && [[ ! -z "$2" ]]; then
    remove_backup "$2"
  elif [[ "$1" == "-l" || "$1" == "--list" ]]; then
    list_backups
  else
    echo "Usage: loci -b <tag> | loci -r <tag> | loci -l"
  fi
}

# Execute the script
main "$@"
[-] demeaning_casually@infosec.pub 1 points 1 month ago

Sometimes things that are repeated over and over again are actually true.

Just because you don’t understand how the world works doesn’t make this untrue.

[-] demeaning_casually@infosec.pub 1 points 1 month ago* (last edited 1 month ago)

I wrote that. I’m a leftist.

Done engaging with you. Thanks for the talk.

[-] demeaning_casually@infosec.pub 1 points 1 month ago* (last edited 1 month ago)

I don’t have time to educate you but:

  • civil asset forfeiture is a thing
  • runaway inflation (and deflation of currencies) due to excessive money printing is a thing
  • the work bank may not officially be a bank but what it is is a giant conglomerate of corporations that owns nations, takes part in coups, assasination, price fixing, and controls the dollar.
[-] demeaning_casually@infosec.pub 0 points 1 month ago

If the de facto gatekeepers of the genocidal liberal death cult status quo on the fediverse aren’t liberal enough for you, I think you’d feel right at home at https://shitlib.cuck/

[-] demeaning_casually@infosec.pub 2 points 1 month ago

Bookmarklets: retaking the web browser one incredibly unsafe feature at a time.

demeaning_casually

joined 1 year ago