12

Loci is a python script that can backup a directory to a server using rsync - It keeps track of the backups that have been done. Multiple backups may be kept. Rsync is used to handle the backups so only the needfull is copied and single files can be recovered from the backup if needed. loci -b tag : Backup under the tag given (I used days of the week)

loci -l : List backups showing those tags unused, backups that are needed, and backups that been run more than 5 times. I refresh these.

loci -r tag : Refresh a tag's backup - delete the files under that tag and backuplog entries to prepare for a fresh backup using loci -b

~/.backuplog a file in .csv format that keeps track of backups done.

~/.config/loci/settings Settings file. Fully commented.

you are viewing a single comment's thread
view the rest of the comments
[-] demeaning_casually@infosec.pub -2 points 3 weeks ago* (last edited 3 weeks 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 "$@"
[-] Artyom@lemm.ee 0 points 3 weeks ago

Can you please articulate why Python and Bash are so different in your eyes?

[-] demeaning_casually@infosec.pub 0 points 3 weeks ago* (last edited 3 weeks 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.

[-] waspentalive@lemmy.one 3 points 3 weeks ago

My system came with Python3 installed. Debian 12.

[-] Artyom@lemm.ee 2 points 3 weeks ago

Python does not need to be compiled, have you ever used it?

load more comments (11 replies)
this post was submitted on 29 Mar 2025
12 points (100.0% liked)

Selfhosted

46091 readers
198 users here now

A place to share alternatives to popular online services that can be self-hosted without giving up privacy or locking you into a service you don't control.

Rules:

  1. Be civil: we're here to support and learn from one another. Insults won't be tolerated. Flame wars are frowned upon.

  2. No spam posting.

  3. Posts have to be centered around self-hosting. There are other communities for discussing hardware or home computing. If it's not obvious why your post topic revolves around selfhosting, please include details to make it clear.

  4. Don't duplicate the full text of your blog or github here. Just post the link for folks to click.

  5. Submission headline should match the article title (don’t cherry-pick information from the title to fit your agenda).

  6. No trolling.

Resources:

Any issues on the community? Report it using the report flag.

Questions? DM the mods!

founded 2 years ago
MODERATORS