--- /dev/null
+# Makefile
+
+MK_PATH:=$(dir $(realpath $(lastword $(MAKEFILE_LIST))))
+WORKSPACE_LOC:=$(MK_PATH)../../
+
+WorkFiles := checksums.json AM-GlobalFunc.rsc
+WORK_DIR := /var/www/ros.vados.ru
+
+DATE ?= $(shell (date '+%T %d.%m.%Y'))
+DTSTAMP ?= $(shell (date '+%Y%d%m%H%M%S'))
+VERSION ?= $(shell git symbolic-ref --short HEAD 2>/dev/null)/$(shell git rev-list --count HEAD 2>/dev/null)/$(shell git rev-parse --short=8 HEAD 2>/dev/null)
+OWNER ?= $(shell whoami)
+
+#SEDCMD ?= $(shell sed -e "/^:global CommitId/c :global CommitId \"${COMMITID:-unknown}\";")/$(shell sed )
+
+all: $(WorkFiles)
+
+.PHONY: $(WorkFiles)
+
+checksums.json : ALL_RSC := $(wildcard *.rsc */*.rsc)
+AM-GlobalFunc.rsc : COMMITID := $(VERSION)
+AM-GlobalFunc.rsc : COMMITINFO := "$(DATE) - Commit owner: $(OWNER)"
+
+ifeq ($@,AM-GlobalFunc.rsc)
+define sed-cmd =
+ cd $(WORK_DIR) && \
+ sed \
+ -e "/^:global CommitId/c :global CommitId \"${COMMITID:-unknown}\";" \
+ -e "/^:global CommitInfo/c :global CommitInfo \"${COMMITINFO:-unknown}\";" \
+ < "./html/${1}"
+ endef
+endif
+
+ifeq ($@,checksums.json)
+ define get-sums =
+ cd $(WORK_DIR)/html && \
+ md5sum $(find -name '*.rsc' | sort) | \
+ sed -e "s| \./||" -e 's|.rsc$||' | \
+ jq --raw-input --null-input '[ inputs | split (" ") | { (.[1]): (.[0]) }] | add' > $@
+ endef
+endif
+
+$(WorkFiles):
+ifeq ($@,checksums.json)
+ $(get-sums) $(ALL_RSC); > $@
+endif
+
+checksums: checksums.json
+ cd $(WORK_DIR)/html
+
+checksums.json: $(WORK_DIR)/bin/checksums.sh $(ALL_RSC)
+ $(WORK_DIR)/bin/checksums.sh > $(WORK_DIR)/html/$@
+
+
+$(FuncFile): ./html/$@
+ $(sed-cmd) $< > $<~
+ mv $<~ $<
+
+#$(WORK_DIR)/html/AM-GlobalFunc.rsc
+# cd $(WORK_DIR)/html && $(WORK_DIR)/bin/commitinfo.sh $< > $<~
+# mv $<~ $<
+
+#commitinfo: push
+# $(WORK_DIR)/bin/$@.sh
+#clean
+
+#checkout: git checkout HEAD .
+
+#push:
+# git $@ origin
+
+#ifeq (git,$(firstword $(MAKECMDGOALS)))
+# # Remaining arguments form the commit string
+# GIT_ARGS := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))
+# # ...and turn them into do-nothing targets
+# $(eval $(GIT_ARGS):;@:)
+#endif
+
+# git function to push to repo
+#git:
+# git add . && git commit -m "$(GIT_ARGS)" && git push
+
+
+#clean:
+# rm -f $(HTML) checksums.json
+# make -C tmpl/ clean
--- /dev/null
+#!/bin/bash
+#
+# https://dzen.ru/a/ZXWKq1L35Em2EHex?ysclid=mlt10qkrsb471483043
+#
+# Use: ./ROSdiff.sh RoutrName SSH_IP SSH_Port
+# Last Backups: ls -l $backuppath
+# History: ls -l $backuppath/history
+# Add to cron
+# crontab -e
+# 0 20 * * * /var/www/ros.vados.ru/Mikrotiks/.bin/ROSdiff.sh MikroMaster 10.30.30.81 2222
+#
+
+routers=( 10.30.30.10 10.30.30.81 )
+
+date=$(date +%d-%m-%Y)
+time=$(date +%H-%M)
+
+GEN_DATE=$(date '+%Y-%m-%d %H:%M:%S %z')
+DTStamp=$(date '+%Y%m%d%H%M%S')
+
+SSHhost="undefined"
+SSHport="2222"
+SSHuser="mikro_bak"
+SSHpassword="Peskar55"
+
+BaseDir="/var/www/ros.vados.ru/Mikrotiks"
+#$HOME
+
+HostsFile="${BaseDir}/.mikrotiks"
+BackupDir="${BaseDir}/backups"
+BackTemp="${BackupDir}/tmp"
+
+Log="undefined"
+RouterName="undefined"
+
+SSH_KEY=$BaseDir"/.ssh/id_rsa"
+SSH_OPTS="-i $SSH_KEY -o PasswordAuthentication=no -p $SSHport"
+# Require router-specific SSH key
+if [ ! -f "$SSH_KEY" ]; then
+ echo "Error: SSH key not found at $SSH_KEY"
+ echo "Run ./setup-ssh-keys.sh to generate the key"
+ exit 1
+fi
+###############################
+# Backup listing
+###############################
+# Get Names Addresses and Ports
+# for SSHhost in $( cat $HostsFile ); do
+#IFS=$'\n' # make newlines the only separator
+readarray -t lines < $HostsFile
+set -f
+for HR in "${lines[@]}"; do
+while IFS=$' ' read -a line || [ -n "$line" ]; do
+[[ -z ${line[0]} ]] && continue
+${BaseDir}/.bin/rdiff.sh ${line[0]} ${SSHuser} ${line[1]} ${line[2]}
+#echo ${line[0]}
+#echo ${SSHuser}
+#echo ${line[1]}
+#echo ${line[2]}
+#readarray -t lines < $HostsFile
+
+#while IFS=" " read -r "${lines[@]}" -a line ; do
+
+#for line in "${lines[@]}";
+#do
+# echo "$line"
+done <<<"$(echo -e $HR)"
+done
+exit 0
+
+#cat $HostsFile | while IFS=" " read -a line ; do
+# || [ -n "$line" ]; do
+#[[ -z ${line[0]} ]] && continue
+
+for SSHhost in ${routers[@]}; do
+RouterName=$(ssh ${SSH_OPTS} ${SSHuser}@${SSHhost} /system/identity/get name | echo $1);
+# awk '{print $2}');
+sleep 2
+echo $RouterName
+#${BaseDir}/.bin/rdiff.sh ${RouterName} ${SSHuser} ${SSHhost} ${SSHport}
+# ${line[0]} ${line[1]} ${line[2]}
+
+done
+exit 0
+
+for SSHhost in ${routers[@]}; do
+
+RouterName=$(ssh ${SSH_OPTS} ${SSHuser}@${SSHhost} system identity print | awk '{print $2}');
+
+RouterDir="${BackupDir}/${RouterName}"
+HistoryDir="${RouterDir}history"
+echo $HistoryDir
+RouterTemp="${BackTemp}/${RouterName}"
+Log="${RouterTemp}log.txt"
+echo $Log
+echo ${RouterName}
+
+precedent_export_name=${RouterName}"-config-export-precedent.rsc"
+pen=${RouterName}'-config-export-precedent.rsc'
+echo ${pen}
+
+current_export_name="$RouterName-config-export-current.rsc"
+current_backup_name="$routername-config-backup-current.backup"
+diff_export_name="$routername-config-export-diff.rsc"
+
+current_export_name_history="$routername-$(date +%Y-%m-%d-%H-%M)-config-export-current.rsc"
+current_backup_name_history="$routername-$(date +%Y-%m-%d-%H-%M)-config-backup-current.backup"
+diff_export_name_history="$routername-$(date +%Y-%m-%d-%H-%M)-config-export-diff.rsc"
+exit 0
+
+echo "Create tmp directory..."
+mkdir -p $routertmp
+echo "tmp directory created $routername $routertmp ($time)." >> $routerlog
+echo
+echo "Create backup directory..."
+mkdir -p $routerpath
+echo "Backups directory created $routername $routerpath ($time)." >> $routerlog
+echo
+echo "Create history directory..."
+mkdir -p $routerhistory
+echo "Backups directory created $routername $routerhistory ($time)." >> $routerlog
+echo
+
+cd $routerpath
+echo "SSH connect to $routername..."
+echo "Create configuration $routername..."
+#ssh $SSH_OPTS $username"@"$i" /export file=export.rsc";
+sshpass -p $sshpassword ssh -o StrictHostKeyChecking=no $sshuser@$sshhost -p $sshport export >$current_export_name
+echo
+
+diff -I "RouterOS" $current_export_name $routerpath/$precedent_export_name > $diff_export_name
+
+if [ "$?" -ne "0" ]; then
+sshpass -p $sshpassword ssh -o StrictHostKeyChecking=no $sshuser@$sshhost -p $sshport export file=$current_export_name
+sshpass -p $sshpassword ssh -o StrictHostKeyChecking=no $sshuser@$sshhost -p $sshport system backup save name=$current_backup_name
+sshpass -p $sshpassword sftp -o StrictHostKeyChecking=no -oPort=$sshport $sshuser@$sshhost:$current_backup_name
+
+rm -f $precedent_export_name
+
+cp $current_export_name $backuppath"/history/"$current_export_name_history
+cp $current_backup_name $backuppath"/history/"$current_backup_name_history
+mv $diff_export_name $backuppath"/history/"$diff_export_name_history
+
+mv -f $current_export_name $precedent_export_name
+rm -f $diff_export_name
+#exit 1
+fi
+
+mv -f $current_export_name $precedent_export_name
+rm -f $diff_export_name
+
+#exit 0
+done
--- /dev/null
+#!/bin/bash
+date=$(date +%d-%m-%Y)
+time=$(date +%H-%M)
+username="mikro_bak"
+mikrotik=$HOME"/.mikrotiks"
+backup_path=$HOME"/backups"
+tmp=$HOME"/tmp"
+log=$tmp"/log.txt"
+SSH_KEY="$HOME/.ssh/id_rsa"
+SSH_OPTS="-i $SSH_KEY -o PasswordAuthentication=no -p 2222"
+#-------------------------------#
+smb_path_backup="//192.168.1.2/Backups"
+domain="WORKGROUP"
+usrname="backuper"
+passwd="root.mikrots.backuper"
+# Require router-specific SSH key
+if [ ! -f "$SSH_KEY" ]; then
+ echo "Error: SSH key not found at $SSH_KEY"
+ echo "Run ./setup-ssh-keys.sh to generate the key"
+ exit 1
+fi
+################
+# Backup listing
+################
+# Get addresses
+for i in $( cat $mikrotik ); do
+mkdir -p $tmp"/"$i
+# Get Devices Names
+#ssh $SSH_OPTS "$ROUTER_USER@$ROUTER_IP"
+RESULT=$(ssh $SSH_OPTS $username"@"$i" /system/identity print" | awk ' {print $2} ');
+#RESULT=$(ssh $username"@"$i" -p 2222 system identity print" | awk ' {print $2} ');
+echo "Start backup Devices"
+echo "Start backup Devices Mikrotiks ($time) $RESULT" > $log
+echo "Create Backup $i..."
+ssh $SSH_OPTS $username"@"$i" /system/backup save name=binary.backup";
+#ssh $username"@"$i" -p 2222 system backup save name=binary.backup";
+if [ $? -eq 0 ]; then
+echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
+echo "Create Backup $i success ($time)" >> $log
+echo
+else
+echo -n "$(tput hpa $(tput cols))$(tput cub 6)[ERROR]"
+echo "Createbackup $i failed ($time)" >> $log
+echo
+fi
+
+echo "Create configuration $i..."
+#ssh $SSH_OPTS $username"@"$i" /export file=export.rsc;"
+#ssh $username"@"$i" -p 2222 export file=export.rsc";
+ssh $SSH_OPTS $username"@"$i" export file=export.rsc; delay 3s;"
+if [ $? -eq 0 ]; then
+echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
+echo "Create configuration $i success ($time)" >> $log
+echo
+else
+echo -n "$(tput hpa $(tput cols))$(tput cub 6)[ERROR]"
+echo "Create configuration $i failed ($time)" >> $log
+echo
+fi
+echo "Create backups directory..."
+mkdir -p $tmp/$i/$date/
+echo "Backups directory created $i ($time)" >> $log
+echo
+echo "Download backup files $i..."
+sftp $SSH_OPTS "sftp://"$username"@"$i"/binary.backup" $tmp/$i/$date/$i"-"$time".backup";
+scp $SSH_OPTS "$username@$i:export.rsc" $tmp/$i/$date/$i"-"$time".rsc";
+#sftp "ssh://"$username"@"$i":2222/binary.backup" $tmp/$i/$date/$i"-"$time".backup";
+#sftp "ssh://"$username"@"$i":2222/export.rsc" $tmp/$i/$date/$i"-"$time".rsc";
+if [ $? -eq 0 ]; then
+echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
+echo "Download backups $i success ($time)" >> $log
+echo
+else
+echo -n "$(tput hpa $(tput cols))$(tput cub 6)[ERROR]"
+echo "Download backups $i failed ($time)" >> $log
+echo
+fi
+
+echo "Compress backups..."
+cd $tmp/$i/
+RESULT=$(tar -czvf $date".tar.gz" $date)
+echo "Backups compressed ($time)" >> $log
+
+#echo "Connect to network share $smb_path_backup..."
+#RESULT=$(cat /home/Mikrotiks/sudos | sudo -S -u root mount -t cifs $smb_path_backup /mnt/samba -o username=$usrname,password=$passwd,domain=$domain)
+#echo "Networkshare $smb_path_backup connected ($time)" >> $log
+
+echo "Create directory for backups device $i..."
+#RESULT=$(cat /home/Mikrotiks/sudos | sudo -S -u root mkdir -p /mnt/samba/Mikrotik/$i)
+RESULT=$(mkdir -p $backup_path"/"$i)
+echo "Directory for backup device $i created in network share ($time)" >> $log
+
+echo "Place backups in network share"
+#RESULT=$(cat /home/Mikrotiks/sudos | sudo -S -u root mv $tmp/$i/$date".tar.gz" "/mnt/samba/Mikrotik/$i/$date.tar.gz")
+RESULT=$(mv $tmp/$i/$date".tar.gz" $backup_path"/"$i"/"$date".tar.gz")
+echo "Backups $i moved to $backup_path"/"$i ($time)" >> $log
+echo "" >> $log
+
+#RESULT=$(cat /home/Mikrotiks/sudos | sudo -S -u root mv $log "/mnt/samba/MIkrotik/$i/$date.log.txt")
+RESULT=$(mv $log $backup_path"/"$i/$date".log.txt")
+
+echo "Remove local backup files device $i"
+ssh $SSH_OPTS "$username@$i /file remove binary.backup";
+ssh $SSH_OPTS "$username@$i /file remove export.rsc";
+#rm -r -f $tmp
+
+#cat /home/Mikrotiks/sudos | sudo -S -u root umount $smb_path_backup
+#cat /home/Mikrotiks/sudos | sudo -S -u root umount $smb_path_backup
+done
--- /dev/null
+#!/bin/bash
+
+while IFS=" " read -a line -r || [ -n "$line" ]; do
+
+
+
+date=$(date +%d-%m-%Y)
+time=$(date +%H-%M)
+
+sshuser="mikro_bak"
+sshpassword="Peskar55"
+
+homepath="/var/www/ros.vados.ru/Mikrotiks"
+
+mikrotik_file=$homepath"/.mikrotiks"
+backuppath=$homepath"/backups"
+tmp="."
+log=$tmp"/log.txt"
+
+SSH_KEY=$homepath"/.ssh/id_rsa"
+# Require router-specific SSH key
+if [ ! -f "$SSH_KEY" ]; then
+ echo "Error: SSH key not found at $SSH_KEY"
+ echo "Run ./setup-ssh-keys.sh to generate the key"
+ exit 1
+fi
+#export IFS=" "
+SSH_OPTS="-i "$SSH_KEY" -o PasswordAuthentication=no -p 2222"
+
+RESULT=$(ssh $SSH_OPTS $sshuser@10.30.30.10 system identity print | awk ' {print $2} ');
+echo
+echo $RESULT
+echo
+exit 0
+#RESULT=(ssh $SSH_OPTS $sshuser"@10.30.30.10" /system/identity print") >> ./ident.txt
+# | awk ' {print $2} ' >> ./ident.txt
+
+#readarray -t lines < "$mikrotik_file"
+cat $mikrotik_file | while read -r lines ; do
+for line in "${lines}" ; do
+echo
+#echo $line[$1]
+#echo $line[$2]
+echo $1
+echo
+done
+done
+exit 0
+
+#for i in $( cat $mikrotik_file ) ; do
+#cat $mikrotik_file | while read -r line ; do
+#-a HR ; do
+# [[ -z $0 ]] && continue # skip empty lines
+set -- $line
+echo
+echo "line = "$line
+echo
+# [[ -z ${line[0]} ]] && continue # skip empty lines
+echo $@
+echo
+routername=$1
+#${HR[0]}
+sshhost=$2
+#${HR[1]}
+sshport=$3
+#${HR[2]}
+
+SSH_OPTS="-i "$SSH_KEY" -o PasswordAuthentication=no -p "$sshport" "$sshuser"@"$sshhost
+
+current_export_name=$routername"-config-export-current.rsc"
+
+echo
+echo $routername
+echo
+echo $current_export_name
+echo
+
+
+#echo "Get current export file ($time) "$RESULT
+# >> $log
+#sshpass -p $sshpassword ssh -o StrictHostKeyChecking=no $sshuser@$sshhost -p $sshport export >$current_export_name
+ssh $SSH_OPTS " export file="$current_export_name"; delay 3s;" && continue
+if [ $? -eq 0 ]; then
+echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
+echo "Create configuration $routername success ($time)"
+# >> $log
+echo
+else
+echo -n "$(tput hpa $(tput cols))$(tput cub 6)[ERROR]"
+echo "Create configuration $i failed ($time)"
+# >> $log
+echo
+fi
+
+done
+#done
+#exit 0
+# cat $mikrotik_file | while read -a HR ; do
+# [[ -z ${HR[0]} ]] && continue # skip empty lines
+#while IFS=" " read -a line -r || [ -n "$line" ]; do
+#echo $line
+#echo ${line[0]}
+#echo ${line[1]}
+#echo ${line[2]}
+#echo ${line[3]}
+#RouterName=${line[0]}
+#SSHhost=${line[1]}
+#SSHport=${line[2]}
+
+#SSH_OPTS="-i $SSH_KEY -o PasswordAuthentication=no -p $SSHport"
+#current_export_name="$RouterName-config-export-current.rsc"
+#ssh $SSH_OPTS $SSHuser@$SSHhost export >$current_export_name
+# 2>&1
+# "; delay 3s;" && continue
+# sshpass -p $SSHpassword ssh -o StrictHostKeyChecking=no $SSHuser@$SSHhost -p $SSHport export >$current_export_name
+
+# Escape backslashes, dollar signs, and quotes
+# line=$(echo "$line" | sed 's/\\/\\\\/g' | sed 's/\$/\\$/g' | sed 's/"/\\"/g')
+# if [ -z "$SCRIPT_CONTENT" ]; then
+# SCRIPT_CONTENT="$line"
+# else
+# SCRIPT_CONTENT="$SCRIPT_CONTENT\\n$line"
+# fi
+#continue
+#done < "$HostsFile"
+exit 0
+
--- /dev/null
+# Makefile
+
+BUILD_DIR = $(shell pwd)
+
+BIN_DIR = bin
+commitinfo.sh := $(wildcard bin/commitinfo.sh)
+
+HTML_DIR = html
+ALL_RSC := $(wildcard html/*.rsc html/*/*.rsc)
+GLOB_RSC := $(wildcard html/AM-GlobalFunc.rsc)
+
+DATE ?= $(shell (date '+%T %d.%m.%Y'))
+DTSTAMP_FMT ?= "+%Y%m%d%H%M"
+DTSTAMP ?= $(shell (date $(DTSTAMP_FMT)))
+VERSION ?= $(shell git symbolic-ref --short HEAD 2>/dev/null)/$(shell git rev-list --count HEAD 2>/dev/null)/$(shell git rev-parse --short=8 HEAD 2>/dev/null)
+RUN_OWNER := $(shell whoami 2>/dev/null)
+COMM_REPO := $(shell git rev-parse --show-toplevel | rev | cut -d/ -f1 | rev 2>/dev/nul)
+COMM_BRANCH := $(shell git symbolic-ref --short HEAD 2>/dev/null)
+
+COMM_TAG := $(shell git rev-list --abbrev-commit --tags --max-count=1)
+# `2>/dev/null` suppress errors and `|| true` suppress the error codes.
+TAG := $(shell git describe --abbrev=0 --tags ${COMM_TAG} 2>/dev/null || true)
+# here we strip the version prefix
+TAG_VERS :=$(TAG:v%=%)
+# get the latest commit hash in the short form
+COMM_HASH := $(shell git rev-parse --short=8 HEAD 2>/dev/null) # Get short commit hash
+#COMMIT := $(shell git rev-parse --short HEAD)
+# get the latest commit date in the form of YYYYmmddHHMM ($(DTSTAMP_FMT))
+COMM_DATE := $(shell git log -1 --format=%cd --date=format:$(DTSTAMP_FMT))
+ifneq ($(COMM_HASH), $(COMM_TAG))
+ TAG_VERS :=$(TAG_VERS)-next-$(COMM_HASH)-$(COMM_DATE)
+endif
+ifeq ($(TAG_VERS), )
+ TAG_VERS :=$(COMM_HASH)-$(COMM_DATE)
+endif
+ifneq ($(shell git status --porcelain), )
+ TAG_VERS :=$(TAG_VERS)-dirty
+endif
+
+COMMIT_ID := $(VERSION)
+COMM_INFO := $(shell echo -e "Autocommit in repo: $(COMM_REPO), generated by owner: $(RUN_OWNER), in $(DATE)." 2>/dev/null)
+
+export BUILD_DIR BIN_DIR HTML_DIR DATE DTSTAMP VERSION COMMIT_ID COMM_TAG COMM_HASH COMM_INFO
+
+SHELL := /bin/bash
+
+export BUILD_DIR BIN_DIR HTML_DIR DATE DTSTAMP VERSION COMMIT_ID COMM_TAG COMM_HASH COMM_INFO COMM_DATE
+
+.ONESHELL:
+
+.DEFAULT_GOAL := run
+
+export BUILD_DIR BIN_DIR HTML_DIR DATE DTSTAMP VERSION COMMIT_ID COMM_TAG COMM_HASH COMM_INFO
+
+include $(BUILD_DIR)/.config/config.mk
+
+.PHONY: run check notify checksums commitinfo test
+
+run: check
+ @echo "[run] start main flow"
+ checksums
+ commitinfo
+ @echo "[run] done"
+
+check:
+ @bash bin/check_repo.sh
+
+notify:
+ @bash bin/notify_telegram.sh "manual notify from make"
+
+checksums: checksums.json
+
+checksums.json: bin/checksums.sh $(ALL_RSC)
+ bin/checksums.sh > html/tmp/$@
+
+test:
+ @bash -n bin/check_repo.sh
+ @bash -n bin/notify_telegram.sh
+ @bash -n bin/checksums.sh
+ @bash -n bin/commitinfo.sh
+ @echo "syntax ok"
+
+
+.DEFAULT_GOAL := all
+#.PHONY: all checksums git-check commitinfo clean
+#all: checksums git-check commitinfo
+
+
+gitcheck: $(BUILD_DIR)
+ cd $ &&
+ @if git diff --quiet && git diff --cached --quiet && [ -z "$$(git ls-files --others --exclude-standard)" ]; then \
+ echo "The first checks have been passed, the repo seems to be clean... But... Still, let's check a little more!"; \
+ git add . && \
+ if [[ -n "$$(git status --porcelain)" ]]; then \
+ echo "Uncommitted changes detected! Commit it..."; \
+# git commit -m "$(COMM_INFO)"; \
+# git push; \
+ exit 1; \
+ else \
+ echo "repo clean"; \
+ fi \
+ else \
+ @echo "Warning: Uncommitted changes detected! Go to autocommit steps..."; \
+ git branch -v; \
+# git commit -am "$(COMM_INFO)"; \
+# git push; \
+ fi
+
+
+#@if [ -n "$$(git diff --quiet)" ] && [ -n "$$(git diff --cached --quiet)" ] && [ -n "$$(git ls-files --others --exclude-standard)" ]; then \
+#[ -n "$$(git status --porcelain)" ]
+
+check-uncommitted:
+ @if [ -n "$$(git diff --quiet)" ] && [ -n "$$(git diff --cached --quiet)" ] && [ -n "$$(git ls-files --others --exclude-standard)" ]; then \
+ echo "Warning: Uncommitted changes detected! Go to autocommit steps..."; \
+ else \
+ echo "The first checks have been passed, the repo seems to be clean... But... Still, let's check a little more!"; \
+ if git status --porcelain; then \
+ echo "Repository is clean (no uncommitted or untracked changes). Exit 0."; \
+ exit 0; \
+ else \
+ echo "Error! Git status is not porcelain! Check it mannualy. Exit 1."; \
+ exit 1; \
+ fi \
+ fi
+
+
+# if [[ -n "`$(rungit) status --porcelain`" ]];: then \
+ echo "Warning: Uncommitted changes detected! Go to autocmmit..."; \
+ git commit -m "$(COMM_INFO)"; \
+ git push origin $(COMM_BANCH); \
+ else \
+ echo "Repository is clean (no uncommitted or untracked changes). Exit 0."; \
+ exit 0; \
+ fi
+
+commitinfo: $(commitinfo.sh)
+
+.PHONY: $(commitinfo.sh)
+$(commitinfo.sh): $(GLOB_RSC)
+ $(commitinfo.sh) $< > $<~
+ mv $<~ $<
+
+
+clean:
+ git checkout HEAD -- .
+
+# rm -f $(HTML) checksums.json
+# make -C contrib/ clean
--- /dev/null
+# ~/.bashrc: executed by bash(1) for non-login shells.
+# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)
+# for examples
+
+# If not running interactively, don't do anything
+case $- in
+ *i*) ;;
+ *) return;;
+esac
+
+# don't put duplicate lines or lines starting with space in the history.
+# See bash(1) for more options
+HISTCONTROL=ignoreboth
+
+# append to the history file, don't overwrite it
+shopt -s histappend
+
+# for setting history length see HISTSIZE and HISTFILESIZE in bash(1)
+HISTSIZE=1000
+HISTFILESIZE=2000
+
+# check the window size after each command and, if necessary,
+# update the values of LINES and COLUMNS.
+shopt -s checkwinsize
+
+# If set, the pattern "**" used in a pathname expansion context will
+# match all files and zero or more directories and subdirectories.
+#shopt -s globstar
+
+# make less more friendly for non-text input files, see lesspipe(1)
+[ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)"
+
+# set variable identifying the chroot you work in (used in the prompt below)
+if [ -z "${debian_chroot:-}" ] && [ -r /etc/debian_chroot ]; then
+ debian_chroot=$(cat /etc/debian_chroot)
+fi
+
+# set a fancy prompt (non-color, unless we know we "want" color)
+case "$TERM" in
+ xterm-color|*-256color) color_prompt=yes;;
+esac
+
+# uncomment for a colored prompt, if the terminal has the capability; turned
+# off by default to not distract the user: the focus in a terminal window
+# should be on the output of commands, not on the prompt
+#force_color_prompt=yes
+
+if [ -n "$force_color_prompt" ]; then
+ if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then
+ # We have color support; assume it's compliant with Ecma-48
+ # (ISO/IEC-6429). (Lack of such support is extremely rare, and such
+ # a case would tend to support setf rather than setaf.)
+ color_prompt=yes
+ else
+ color_prompt=
+ fi
+fi
+
+if [ "$color_prompt" = yes ]; then
+ PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
+else
+ PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
+fi
+unset color_prompt force_color_prompt
+
+# If this is an xterm set the title to user@host:dir
+case "$TERM" in
+xterm*|rxvt*)
+ PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1"
+ ;;
+*)
+ ;;
+esac
+
+# enable color support of ls and also add handy aliases
+if [ -x /usr/bin/dircolors ]; then
+ test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)"
+ alias ls='ls --color=auto'
+ #alias dir='dir --color=auto'
+ #alias vdir='vdir --color=auto'
+
+ alias grep='grep --color=auto'
+ alias fgrep='fgrep --color=auto'
+ alias egrep='egrep --color=auto'
+fi
+
+# colored GCC warnings and errors
+#export GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01'
+
+# some more ls aliases
+alias ll='ls -alF'
+alias la='ls -A'
+alias l='ls -CF'
+
+# Add an "alert" alias for long running commands. Use like so:
+# sleep 10; alert
+alias alert='n8otify-send --urgency=low -i "$([ $? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e '\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"'
+
+# Alias definitions.
+# You may want to put all your additions into a separate file like
+# ~/.bash_aliases, instead of adding them here directly.
+# See /usr/share/doc/bash-doc/examples in the bash-doc package.
+
+if [ -f ~/.bash_aliases ]; then
+ . ~/.bash_aliases
+fi
+
+# enable programmable completion features (you don't need to enable
+# this, if it's already enabled in /etc/bash.bashrc and /etc/profile
+# sources /etc/bash.bashrc).
+if ! shopt -oq posix; then
+ if [ -f /usr/share/bash-completion/bash_completion ]; then
+ . /usr/share/bash-completion/bash_completion
+ elif [ -f /etc/bash_completion ]; then
+ . /etc/bash_completion
+ fi
+fi
+
+# Terminal Prompt full paths
+export PROMPT_DIRTRIM=5
+export force_color_prompt=yes
+export GIT_SSH=$(which ssh)
+#'"c:\WINDOWS\System32\OpenSSH\ssh.exe" -T'
+eval $(ssh-agent)
+ssh-add
+GPG_TTY=`tty`
+export GPG_TTY
--- /dev/null
+MikroNUC 10.30.30.10 2222 mikro_bak Peskar55
+MikroMaster 10.30.30.81 2222 mikro_bak Peskar55
+MikroYellow 10.30.30.86 2222 mikro_bak Peskar55
--- /dev/null
+[Midnight-Commander]
+verbose=true
+shell_patterns=true
+auto_save_setup=true
+preallocate_space=false
+auto_menu=false
+use_internal_view=true
+use_internal_edit=true
+clear_before_exec=true
+confirm_delete=true
+confirm_overwrite=true
+confirm_execute=false
+confirm_history_cleanup=true
+confirm_exit=false
+confirm_directory_hotlist_delete=false
+confirm_view_dir=false
+safe_delete=false
+safe_overwrite=false
+use_8th_bit_as_meta=false
+mouse_move_pages_viewer=true
+mouse_close_dialog=false
+fast_refresh=false
+drop_menus=true
+wrap_mode=true
+old_esc_mode=true
+cd_symlinks=true
+show_all_if_ambiguous=false
+use_file_to_guess_type=true
+alternate_plus_minus=false
+only_leading_plus_minus=true
+show_output_starts_shell=false
+xtree_mode=false
+file_op_compute_totals=true
+classic_progressbar=true
+use_netrc=true
+ftpfs_always_use_proxy=false
+ftpfs_use_passive_connections=true
+ftpfs_use_passive_connections_over_proxy=false
+ftpfs_use_unix_list_options=true
+ftpfs_first_cd_then_ls=true
+ignore_ftp_chattr_errors=true
+editor_fill_tabs_with_spaces=false
+editor_return_does_auto_indent=false
+editor_backspace_through_tabs=false
+editor_fake_half_tabs=true
+editor_option_save_position=true
+editor_option_auto_para_formatting=false
+editor_option_typewriter_wrap=false
+editor_edit_confirm_save=true
+editor_syntax_highlighting=true
+editor_persistent_selections=true
+editor_drop_selection_on_copy=true
+editor_cursor_beyond_eol=false
+editor_cursor_after_inserted_block=false
+editor_visible_tabs=true
+editor_visible_spaces=true
+editor_line_state=false
+editor_simple_statusbar=false
+editor_check_new_line=false
+editor_show_right_margin=false
+editor_group_undo=true
+editor_state_full_filename=true
+editor_ask_filename_before_edit=false
+nice_rotating_dash=true
+mcview_remember_file_position=false
+auto_fill_mkdir_name=true
+copymove_persistent_attr=true
+pause_after_run=2
+mouse_repeat_rate=100
+double_click_speed=250
+old_esc_mode_timeout=1000000
+max_dirt_limit=10
+num_history_items_recorded=60
+vfs_timeout=60
+ftpfs_directory_timeout=900
+ftpfs_retry_seconds=30
+fish_directory_timeout=900
+editor_tab_spacing=8
+editor_word_wrap_line_length=72
+editor_option_save_mode=0
+editor_backup_extension=~
+editor_filesize_threshold=64M
+editor_stop_format_chars=-+*\\,.;:&>
+mcview_eof=
+skin=default
+
+filepos_max_saved_entries=1024
+shadows=true
+shell_directory_timeout=900
+
+[Layout]
+output_lines=0
+left_panel_size=118
+top_panel_size=0
+message_visible=false
+keybar_visible=false
+xterm_title=true
+command_prompt=true
+menubar_visible=false
+free_space=true
+horizontal_split=false
+vertical_equal=true
+horizontal_equal=true
+
+[Misc]
+timeformat_recent=%b %e %H:%M
+timeformat_old=%b %e %Y
+ftp_proxy_host=gate
+ftpfs_password=anonymous@
+display_codepage=ASCII
+source_codepage=ASCII
+autodetect_codeset=
+spell_language=en
+clipboard_store=
+clipboard_paste=
+
+[Colors]
+base_color=
+xterm-256color=
+color_terminals=
+linux=
+xterm=
+
+[Panels]
+simple_swap=false
+show_mini_info=true
+kilobyte_si=false
+mix_all_files=false
+show_backups=true
+show_dot_files=true
+fast_reload=false
+fast_reload_msg_shown=false
+mark_moves_down=true
+reverse_files_only=true
+auto_save_setup_panels=false
+navigate_with_arrows=true
+panel_scroll_pages=true
+panel_scroll_center=false
+mouse_move_pages=true
+filetype_mode=true
+permission_mode=false
+torben_fj_mode=false
+quick_search_mode=2
+select_flags=6
+
+[FindFile]
+file_case_sens=true
+file_shell_pattern=true
+file_find_recurs=true
+follow_symlinks=true
+file_skip_hidden=false
+file_all_charsets=false
+content_case_sens=true
+content_regexp=false
+content_first_hit=false
+content_whole_words=false
+content_all_charsets=false
+ignore_dirs_enable=true
+ignore_dirs=
+
+[Panelize]
+Find *.orig after patching=find . -name \\*.orig -print
+Find rejects after patching=find . -name \\*.rej -print
+Modified git files=git ls-files --modified
+Find SUID and SGID programs=find . \\( \\( -perm -04000 -a -perm /011 \\) -o \\( -perm -02000 -a -perm /01 \\) \\) -print
--- /dev/null
+[New Left Panel]
+display=listing
+reverse=false
+case_sensitive=true
+exec_first=false
+sort_order=name
+list_mode=full
+brief_cols=2
+user_format=half type name | size | perm
+user_status0=half type name | size | perm
+user_status1=half type name | size | perm
+user_status2=half type name | size | perm
+user_status3=half type name | size | perm
+user_mini_status=false
+list_format=full
+
+[New Right Panel]
+display=listing
+reverse=false
+case_sensitive=true
+exec_first=false
+sort_order=name
+list_mode=full
+brief_cols=2
+user_format=half type name | size | perm
+user_status0=half type name | size | perm
+user_status1=half type name | size | perm
+user_status2=half type name | size | perm
+user_status3=half type name | size | perm
+user_mini_status=false
+list_format=full
+
+[Dirs]
+current_is_left=true
+other_dir=/mnt/d/dst/D41/vbmeta-tools
--- /dev/null
+[user]
+ name = vados-dev
+ email = vados@vados.ru
+ signingkey = 1FD2694762FCE438
+[color]
+ ui = auto
+[safe]
+ directory = /home/vados/sl8541e_cus_go
+ directory = /var/www/ros.vados.ru
+ directory = /var/www/ros.vados.ru/html
+ directory = /var/www/ros.vados.ru/Mikrotiks
+ directory = /var/www/ros.vados.ru/Mikrotiks/backups
+ directory = /.data/nfs/repos/Mikrotiks
+ directory = /.data/nfs/repos/Mikrotiks/Mikrotiks.git
+ directory = /.data/nfs/repos/Mikrotiks/ros.vados.ru.git
+[init]
+ defaultBranch = main
+[core]
+ autocrlf = false
+[protocol]
+ version = 2
+[commit]
+ gpgsign = true
+[gpg]
+ program = gpg2
--- /dev/null
+# folder for backups repo
+backups/
+backups/**
+bin/logs/
+.gnupg/
+.local/
+.cache/
+oxidized/
+upload/
+test/
+.cache/
+.docker/
+.gnupg/
+.local/
+.ssh/
+.vscode/
+**/Precedent-Config.rsc
+
+.bash_history
+#.bak
+# backup and temporary files
+*~
+# patches and related files
+*.orig
+*.patch
+*.rej
--- /dev/null
+.less-history-file:
--- /dev/null
+# ~/.profile: executed by the command interpreter for login shells.
+# This file is not read by bash(1), if ~/.bash_profile or ~/.bash_login
+# exists.
+# see /usr/share/doc/bash/examples/startup-files for examples.
+# the files are located in the bash-doc package.
+
+# the default umask is set in /etc/profile; for setting the umask
+# for ssh logins, install and configure the libpam-umask package.
+#umask 022
+
+# if running bash
+if [ -n "$BASH_VERSION" ]; then
+ # include .bashrc if it exists
+ if [ -f "$HOME/.bashrc" ]; then
+ . "$HOME/.bashrc"
+ fi
+fi
+
+# set PATH so it includes user's private bin if it exists
+if [ -d "$HOME/bin" ] ; then
+ PATH="$HOME/bin:$PATH"
+fi
+
+if [ -d "$HOME/.bin" ] ; then
+ PATH="$HOME/.bin:$PATH"
+fi
+
+# set PATH so it includes user's private bin if it exists
+if [ -d "$HOME/.local/bin" ] ; then
+ PATH="$HOME/.local/bin:$PATH"
+fi
--- /dev/null
+{
+ "user.name": [
+ "vados-dev"
+ ],
+ "user.email": [
+ "vados@vados.ru"
+ ],
+ "color.ui": [
+ "auto"
+ ],
+ "safe.directory": [
+ "/var/www/ros.vados.ru/Mikrotiks"
+ ],
+ "init.defaultbranch": [
+ "main"
+ ],
+ "core.autocrlf": [
+ "false"
+ ]
+}
\ No newline at end of file
--- /dev/null
+# Generated by /usr/bin/select-editor
+SELECTED_EDITOR="/usr/bin/mcedit"
--- /dev/null
+# HSTS 1.0 Known Hosts database for GNU Wget.
+# Edit at your own risk.
+# <hostname> <port> <incl. subdomains> <created> <max-age>
+github.com 0 1 1771536570 31536000
+codeload.github.com 0 0 1771536571 31536000
--- /dev/null
+# Makefile
+
+BUILD_DIR = $(shell pwd)
+REPO_DIR ?= $(BUILD_DIR)
+
+ALL_RSC := $(wildcard html/*.rsc html/*/*.rsc)
+commitinfo.sh := $(wildcard bin/commitinfo.sh)
+GLOB_RSC := $(wildcard html/AM-GlobalFunc.rsc)
+
+SHELL := /bin/bash
+
+include ../.config/config.mk
+
+COMM_TAG := $(shell git rev-list --abbrev-commit --tags --max-count=1)
+# `2>/dev/null` suppress errors and `|| true` suppress the error codes.
+TAG := $(shell git describe --abbrev=0 --tags ${COMM_TAG} 2>/dev/null || true)
+# here we strip the version prefix
+TAG_VERS :=$(TAG:v%=%)
+# get the latest commit hash in the short form
+COMM_HASH := $(shell git rev-parse --short=8 HEAD 2>/dev/null) # Get short commit hash
+#COMMIT := $(shell git rev-parse --short HEAD)
+# get the latest commit date in the form of YYYYmmddHHMM ($(DTSTAMP_FMT))
+COMM_DATE := $(shell git log -1 --format=%cd --date=format:$(DTSTAMP_FMT))
+ifneq ($(COMM_HASH), $(COMM_TAG))
+ TAG_VERS :=$(TAG_VERS)-next-$(COMM_HASH)-$(COMM_DATE)
+endif
+ifeq ($(TAG_VERS), )
+ TAG_VERS :=$(COMM_HASH)-$(COMM_DATE)
+endif
+ifneq ($(shell git status --porcelain), )
+ TAG_VERS :=$(TAG_VERS)-dirty
+endif
+
+.ONESHELL:
+
+export BUILD_DIR REPO_DIR ALL_RSC GLOB_RSC
+
+.DEFAULT_GOAL := all
+
+.PHONY: all check commit notify test
+
+all:
+ @$(MAKE) check; rc=$$?; \
+ if [ $$rc -eq 0 ]; then \
+ echo "[add] check $$rc, go to commit..."; \
+ $(MAKE) commit; cm=$$?; \
+ if [ $$cm -eq 0 ]; then \
+ echo "[all] commit exit $$cm. Exit Ok."; \
+ else \
+ $(MAKE) notify MSG="[all] commit exit $$cm. Check it!"; \
+ fi \
+ else \
+ echo "[all] check exit $$rc, repo is clean exit Ok."; \
+ fi
+ @echo "[all] done"
+
+run:
+ @$(MAKE) check; rc=$$?; \
+ if [ $$rc -eq 0 ]; then \
+ echo "[run] check $$rc, go to commitinfo.sh..."; \
+ $(MAKE) $(commitinfo.sh); ci=$$?; \
+ if [ $$ci -eq 0 ]; then \
+ echo "[run] commitinfo.sh exit $$ci. Go to checksums.sh..."; \
+ $(MAKE) checksums; cs=$$?; \
+ if [ $$cs -eq 0 ]; then \
+ echo "[run] checksums.sh exit $$cs. Do git add commit and push..."; \
+ git add .; \
+ git commit -m '$(COMM_INFO)'; \
+ git push; \
+ else \
+ $(MAKE) notify MSG="[run] checksums.sh exit $$cs, check it!"; \
+ fi \
+ else \
+ $(MAKE) notify MSG="[run] commitinfo.sh exit $$ci, check it!"; \
+ fi \
+ else \
+ echo "[run] check exit $$rc, repo is clean exit Ok."; \
+ fi
+ @echo "[run] done"
+
+check:
+ @bash bin/check_repo.sh
+
+commit:
+ cd $(BUILD_DIR)
+ git add .
+ git commit -m '$(COMM_INFO)'
+ git push
+
+notify:
+ @bash bin/notify_telegram.sh "$(MSG)"
+
+checksums: checksums.json
+
+checksums.json: bin/checksums.sh $(ALL_RSC)
+ bin/checksums.sh > html/tmp/$@
+
+$(commitinfo.sh): $(GLOB_RSC)
+ $(commitinfo.sh) $< > $<~
+ mv $<~ $<
+
+test:
+ @bash -n bin/check_repo.sh
+ @bash -n bin/notify_telegram.sh
+ @bash -n bin/checksums.sh
+ @bash -n bin/commitinfo.sh
+ @echo "syntax ok"
+
+#clean:
+# git checkout HEAD -- .
--- /dev/null
+MIT License
+
+Copyright (c) 2018 Leandro Nunes
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
--- /dev/null
+# bash-progress-indicator
+Pure Bash script progress indicators
+
+## Usage
+```
+chmod +x main.sh
+./main.sh {progress_indicator_type}
+```
+
+## progress_indicator_type
+#### progress
+
+
+#### progress_advanced
+
+
+#### spinner1
+
+#### spinner2
+
+#### spinner3
+
+
+## I wrote an article about this technique [pt-br]
+[Medium - [Bash] Construindo indicador de progresso](https://medium.com/meninunes/bash-construindo-indicador-de-progresso-8de94c37683)
+
+## License
+[MIT License](LICENSE) © [Leandro Nunes](https://lnfnunes.com.br)
--- /dev/null
+#!/bin/bash -e
+# shellcheck disable=SC1091
+
+declare -rx STEPS=(
+ 'pre-install'
+ 'install'
+ 'post-install'
+)
+declare -rx CMDS=(
+ 'sleep 10'
+ 'sleep 10'
+ 'sleep 10'
+)
+
+case $1 in
+ progress)
+ . 'progress.sh'
+ ;;
+ progress_advanced)
+ . 'progress_advanced.sh'
+ ;;
+ spinner*)
+ . 'spinner.sh'
+ ;;
+ *)
+ echo "Invalid option $1!"
+ exit 1
+esac
+
+start
--- /dev/null
+#!/bin/bash -e
+
+declare -rx BAR_SIZE="##########"
+declare -rx CLEAR_LINE="\\033[K"
+
+start() {
+ local MAX_STEPS=${#STEPS[@]}
+ local MAX_BAR_SIZE="${#BAR_SIZE}"
+
+ tput civis -- invisible
+
+ echo -ne "\\r[${BAR_SIZE:0:0}] 0 %$CLEAR_LINE"
+ for step in "${!STEPS[@]}"; do
+ ${CMDS[$step]}
+
+ perc=$(((step + 1) * 100 / MAX_STEPS))
+ percBar=$((perc * MAX_BAR_SIZE / 100))
+ echo -ne "\\r[${BAR_SIZE:0:percBar}] $perc %$CLEAR_LINE"
+ done
+ echo ""
+
+ tput cnorm -- normal
+}
--- /dev/null
+#!/bin/bash -e
+
+start() {
+ local MAX_STEPS=${#STEPS[@]}
+ local BAR_SIZE="##########"
+ local MAX_BAR_SIZE="${#BAR_SIZE}"
+ local CLEAR_LINE="\\033[K"
+
+ tput civis -- invisible
+
+ for step in "${!STEPS[@]}"; do
+ perc=$((step * 100 / MAX_STEPS))
+ percBar=$((perc * MAX_BAR_SIZE / 100))
+ echo -ne "\\r- ${STEPS[step]} [ ]$CLEAR_LINE\\n"
+ echo -ne "\\r[${BAR_SIZE:0:percBar}] $perc %$CLEAR_LINE"
+
+ ${CMDS[$step]}
+
+ perc=$(((step + 1) * 100 / MAX_STEPS))
+ percBar=$((perc * MAX_BAR_SIZE / 100))
+ echo -ne "\\r\\033[1A- ${STEPS[step]} [✔]$CLEAR_LINE\\n"
+ echo -ne "\\r[${BAR_SIZE:0:percBar}] $perc %$CLEAR_LINE"
+ done
+ echo ""
+
+ tput cnorm -- normal
+}
--- /dev/null
+#!/bin/bash -e
+
+declare -x FRAME
+declare -x FRAME_INTERVAL
+
+set_spinner() {
+ case $1 in
+ spinner1)
+ FRAME=("⠋" "⠙" "⠹" "⠸" "⠼" "⠴" "⠦" "⠧" "⠇" "⠏")
+ FRAME_INTERVAL=0.1
+ ;;
+ spinner2)
+ FRAME=("-" "\\" "|" "/")
+ FRAME_INTERVAL=0.25
+ ;;
+ spinner3)
+ FRAME=("◐" "◓" "◑" "◒")
+ FRAME_INTERVAL=0.5
+ ;;
+ spinner4)
+ FRAME=(":(" ":|" ":)" ":D")
+ FRAME_INTERVAL=0.5
+ ;;
+ spinner5)
+ FRAME=("◇" "◈" "◆")
+ FRAME_INTERVAL=0.5
+ ;;
+ spinner6)
+ FRAME=("⚬" "⚭" "⚮" "⚯")
+ FRAME_INTERVAL=0.25
+ ;;
+ spinner7)
+ FRAME=("░" "▒" "▓" "█" "▓" "▒")
+ FRAME_INTERVAL=0.25
+ ;;
+ spinner8)
+ FRAME=("☉" "◎" "◉" "●" "◉")
+ FRAME_INTERVAL=0.1
+ ;;
+ spinner9)
+ FRAME=("❤" "♥" "♡")
+ FRAME_INTERVAL=0.15
+ ;;
+ spinner10)
+ FRAME=("✧" "☆" "★" "✪" "◌" "✲")
+ FRAME_INTERVAL=0.1
+ ;;
+ spinner11)
+ FRAME=("●" "◕" "☯" "◔" "◕")
+ FRAME_INTERVAL=0.25
+ ;;
+ *)
+ echo "No spinner is defined for $1"
+ exit 1
+ esac
+}
+
+start() {
+ local step=0
+
+# tput civis -- invisible
+
+ while [ "$step" -lt "${#CMDS[@]}" ]; do
+ ${CMDS[$step]} & pid=$!
+
+ while ps -p $pid &>/dev/null; do
+ echo -ne "\\r[ ] ${STEPS[$step]} ..."
+
+ for k in "${!FRAME[@]}"; do
+ echo -ne "\\r[ ${FRAME[k]} ]"
+ sleep $FRAME_INTERVAL
+ done
+ done
+
+ echo -ne "\\r[ ✔ ] ${STEPS[$step]}\\n"
+ step=$((step + 1))
+ done
+
+# tput cnorm -- normal
+}
+
+set_spinner "$1"
--- /dev/null
+#!/usr/bin/env bash
+
+cd "${REPO_DIR:-.}"
+
+dirty="$(git ls-files -u || true)"
+lsfiles="$(git ls-files --others --exclude-standard || true)"
+
+# Conflict or unresolved state: stop and notify.
+if [[ -n "$dirty" ]]; then
+ echo "[check_repo] repo has conflicts, stop"
+ echo "[check_repo] unmerged files:"
+ printf "%s\n" "$dirty"
+ echo "[check_repo] resolve conflicts and run: git add <files> && git commit"
+ bash bin/notify_telegram.sh "STOP: repo conflicts in $(pwd)"
+ exit 1
+fi
+
+# Exit 0 only when repo is NOT clean to allow autocommit flow.
+if git diff --quiet && git diff --cached --quiet && [[ -z "$lsfiles" ]]; then
+ echo "[check_repo] repo clean. Exit 1 from check_repo.sh."
+ exit 1
+else
+ echo "[check_repo] repo not clean. Exit 0 from check_repo.sh."
+ exit 0
+fi
--- /dev/null
+/var/log/Mikrotiks
\ No newline at end of file
--- /dev/null
+# RouterOS Configuration Scripts
+
+Personal RouterOS configuration scripts for home network management.
+
+## Overview
+
+This repository contains RouterOS scripts to configure and diagnose a MikroTik router. Configuration scripts set up router modes (routing, bridging, NAT, DHCP). Check scripts display router settings and diagnostics.
+
+## Quick Start
+
+### Running Scripts
+
+Run scripts on the router using forward slashes:
+
+```routeros
+/system script run config/firewall
+/system script run check/nat
+```
+
+## Network Configuration
+
+The `combo1` port is renamed to `combo1-WAN` for routing mode or `combo1-bridge` for switching mode. The `bridge` interface is used for the internal network.
+
+The WAN interface uses `192.168.200.2/24` with gateway `192.168.200.1`. The bridge interface uses `192.168.88.1/24` as the gateway for the internal network. DHCP serves addresses from `192.168.88.100-192.168.88.199`.
+
+## Hardware
+
+These scripts are tested on CRS106, a MikroTik switch/router.
+
+## Repository Structure
+
+```text
+routeros-config/
+├── scripts/
+│ ├── config/ # Configuration scripts
+│ ├── check/ # Diagnostic scripts
+│ └── README.md # Format and syntax guidelines
+└── README.md # This file
+```
+
+For format and syntax guidelines, see [scripts/README.md](scripts/README.md).
--- /dev/null
+#!/bin/bash
+# Setup SSH keys for RouterOS router
+
+ROUTER_IP="10.30.30.81"
+ROUTER_USER="mikro_bak"
+KEY_NAME=$ROUTER_USER"_rsa@"$ROUTER_IP
+KEY_PATH="$HOME/.ssh/$KEY_NAME"
+
+# Check/create .ssh directory
+if [ ! -d "$HOME/.ssh" ]; then
+ mkdir -p "$HOME/.ssh"
+ chmod 700 "$HOME/.ssh"
+fi
+
+# Generate SSH key if it doesn't exist
+if [ ! -f "$KEY_PATH" ]; then
+ ssh-keygen -t rsa -b 2048 -f "$KEY_PATH" -N "" -C "$ROUTER_USER@$ROUTER_IP"
+fi
+
+# Upload and import key on router
+scp "$KEY_PATH.pub" "$ROUTER_USER@$ROUTER_IP -p 2222":/
+ssh "$ROUTER_USER@$ROUTER_IP -p 2222" "/user ssh-keys import public-key-file=$KEY_NAME.pub user=$ROUTER_USER"
+
+# Verify key works
+if ! ssh -i "$KEY_PATH" -o PasswordAuthentication=no "$ROUTER_USER@$ROUTER_IP -p 2222" "/system identity print" >/dev/null 2>&1; then
+ echo "Error: SSH key authentication failed"
+ exit 1
+fi
--- /dev/null
+#!/bin/bash
+# Upload all scripts and sync to RouterOS
+
+ROUTER_IP="10.30.30.10"
+ROUTER_USER="mikro_bak"
+SSH_KEY="$HOME/.ssh/mikro_bak_rsa"
+RSC_FILE="import_scripts.rsc"
+
+# Require router-specific SSH key
+if [ ! -f "$SSH_KEY" ]; then
+ echo "Error: SSH key not found at $SSH_KEY"
+ echo "Run ./setup-ssh-keys.sh to generate the key"
+ exit 1
+fi
+
+SSH_OPTS="-i $SSH_KEY -o PasswordAuthentication=no"
+
+# Generate .rsc import file
+echo "Generating import file..."
+
+# Get git commit hash and dirty status
+GIT_HASH=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown")
+if [ -z "$(git status --porcelain 2>/dev/null)" ]; then
+ GIT_STATUS="$GIT_HASH"
+else
+ GIT_STATUS="$GIT_HASH-dirty"
+fi
+
+# Get current date with timezone offset
+GEN_DATE=$(date '+%Y-%m-%d %H:%M:%S %z')
+
+# Write header
+cat > "$RSC_FILE" << RSC_HEADER
+# RouterOS script import file
+# Generated by sync-script.sh
+# Date: $GEN_DATE
+# Git: $GIT_STATUS
+
+RSC_HEADER
+
+# Write header for creating scripts section
+printf ':put "=== Creating scripts ==="\n' >> "$RSC_FILE"
+printf '\n' >> "$RSC_FILE"
+
+# Create function for script creation
+printf ':global createScriptIfMissing do={\n' >> "$RSC_FILE"
+printf ' :if ([/system script print count-only where name=$scriptName] = 0) do={\n' >> "$RSC_FILE"
+printf ' :put ("Creating script: " . $scriptName);\n' >> "$RSC_FILE"
+printf ' /system script add name=$scriptName\n' >> "$RSC_FILE"
+printf ' }\n' >> "$RSC_FILE"
+printf '}\n\n' >> "$RSC_FILE"
+
+# Write all add commands first
+for SCRIPT_FILE in scripts/config/*.rsc scripts/check/*.rsc; do
+ if [ ! -f "$SCRIPT_FILE" ]; then
+ continue
+ fi
+
+ SCRIPT_NAME=$(sed -n '1p' "$SCRIPT_FILE" | sed 's/^# //')
+ POLICY=$(sed -n '3p' "$SCRIPT_FILE" | sed -n 's/^# policy=\(.*\)/\1/p')
+
+ if [ -z "$POLICY" ]; then
+ echo "Error: Missing policy in $SCRIPT_FILE (line 3 should be '# policy=read,write')"
+ exit 1
+ fi
+
+ # Call function with script name
+ printf '$createScriptIfMissing scriptName="%s"\n' "$SCRIPT_NAME" >> "$RSC_FILE"
+done
+
+# Empty line between sections
+printf '\n' >> "$RSC_FILE"
+printf ':put ""\n' >> "$RSC_FILE"
+printf ':put "=== Updating scripts ==="\n' >> "$RSC_FILE"
+printf '\n' >> "$RSC_FILE"
+
+# Write all set commands
+for SCRIPT_FILE in scripts/config/*.rsc scripts/check/*.rsc; do
+ if [ ! -f "$SCRIPT_FILE" ]; then
+ continue
+ fi
+
+ SCRIPT_NAME=$(sed -n '1p' "$SCRIPT_FILE" | sed 's/^# //')
+ COMMENT=$(sed -n '2p' "$SCRIPT_FILE" | sed 's/^# //')
+ POLICY=$(sed -n '3p' "$SCRIPT_FILE" | sed -n 's/^# policy=\(.*\)/\1/p')
+
+ # Escape comment for RouterOS (escape quotes)
+ ESCAPED_COMMENT=$(echo "$COMMENT" | sed 's/"/\\"/g')
+
+ # Read script content and escape for RouterOS .rsc format
+ # Loop through file line by line, escape special chars, join with \n
+ SCRIPT_CONTENT=""
+ while IFS= read -r line || [ -n "$line" ]; do
+ # Escape backslashes, dollar signs, and quotes
+ line=$(echo "$line" | sed 's/\\/\\\\/g' | sed 's/\$/\\$/g' | sed 's/"/\\"/g')
+ if [ -z "$SCRIPT_CONTENT" ]; then
+ SCRIPT_CONTENT="$line"
+ else
+ SCRIPT_CONTENT="$SCRIPT_CONTENT\\n$line"
+ fi
+ done < "$SCRIPT_FILE"
+
+ # Write set command directly to file
+ printf ':put "Updating script: %s"; /system script set "%s" source="' "$SCRIPT_NAME" "$SCRIPT_NAME" >> "$RSC_FILE"
+ echo "$SCRIPT_CONTENT" | sed 's/\\n/\\n\\\n /g' >> "$RSC_FILE"
+ printf '" comment="%s" policy=%s\n\n' "$ESCAPED_COMMENT" "$POLICY" >> "$RSC_FILE"
+done
+
+# Delete the helper function
+printf '\n:set createScriptIfMissing;\n' >> "$RSC_FILE"
+
+# Print all scripts (excluding source/contents)
+printf ':put ""\n:put "=== All system scripts ==="\n/system script print proplist=name,comment,owner,policy,dont-require-permissions,run-count,last-started,invalid\n' >> "$RSC_FILE"
+
+# Show generated .rsc file and ask for confirmation
+echo ""
+echo "=== Generated import file ($RSC_FILE) ==="
+head -20 "$RSC_FILE"
+echo "..."
+echo ""
+read -p "Upload and import this file? (y/N): " -n 1 -r
+echo
+if [[ ! $REPLY =~ ^[Yy]$ ]]; then
+ echo "Aborted"
+ rm -f "$RSC_FILE"
+ exit 1
+fi
+
+# Upload .rsc file
+echo "Uploading import file..."
+scp $SSH_OPTS "$RSC_FILE" "$ROUTER_USER@$ROUTER_IP":/
+
+# Import on router
+echo "Importing scripts..."
+ssh $SSH_OPTS "$ROUTER_USER@$ROUTER_IP" "/import file-name=$RSC_FILE"
+
+# Cleanup
+rm -f "$RSC_FILE"
+echo "Done!"
--- /dev/null
+#!/bin/bash
+#set +e -u
+#o pipefail
+# Add to cron
+# crontab -e
+#*/20 * * * * /var/www/ros.vados.ru/Mikrotiks/.bin/ros-diff.sh
+#set +eo pipefail
+#set -x
+#GEN_DATE=$(date '+%Y-%m-%d %H:%M:%S %z')
+#GEN_DATE_TIME=$(date '+%d.%m.%Y %T %Z')
+#
+#ASK_NEXT=true
+#ask_forNext() {
+# local ask $1
+# local item $2
+# echo -n $ask
+#read item
+#case "$item" in
+# y|Y) echo "Enter «y», continue..."
+# ;;
+# n|N) echo "Enter «n», exit..."
+# exit 0
+# ;;
+# *) echo "Nothing was entered. We perform the default action..."
+# ;;
+#esac
+#}
+#${CMD_SSH} ${SSH_OPTS} ${SSHuser}@${SSHhost} system script run MkTmpfs;
+#
+#### Utils #############################################################################################
+CMD_FIND=$(which find)
+CMD_MV=$(which mv)
+CMD_GZ=$(which gzip)
+CMD_CHO=$(which chown)
+CMD_CHM=$(which chmod)
+CMD_MKD=$(which mkdir)" -p "
+CMD_RM=$(which rm)
+CMD_DATE=$(date +%Y%m%d_%H%M) # date in format YYYYMMDD_HHmm
+CMD_SSL=$(which openssl)
+CMD_SSH=$(which ssh)
+CMD_SCP=$(which scp)
+CMD_SFTP=$(which sftp)
+
+ScriptName=$(basename -- "$0")
+# HomeDir=$HOME
+HomeDir="/var/www/ros.vados.ru/Mikrotiks"
+HostsFile=${HomeDir}"/.config/.mikrotiks"
+BinDir=${HomeDir}"/bin"
+GitDir=${HomeDir}"/backups"
+
+# Set SSH defaults:
+DEFAULT_SSH_PORT="22"
+DEFAULT_SSH_USER="admin"
+DEFAULT_CONNECT_ATTEMPTS=5
+DEFAULT_CONNECT_TIMEOUT=5
+DEFAULT_SSH_KEY=${HomeDir}"/.ssh/id_rsa"
+# Set default diff args:
+DEFAULT_DIFF_ARGS=("-I \"RouterOS\"")
+#
+# Set debug and log options:
+DEBUG_OUT=true
+SAVE_LOG=true
+# Set default LogFile:
+DEFAULT_LOG="/var/log/Mikrotiks/ros-diff.log"
+#
+# Default backup variables:
+DEFAULT_BIN_CRYPT=true
+DEFAULT_BINPWD="BinPwd123"
+DEFAULT_EXP_CRYPT=false
+DEFAULT_EXPPWD="ExpPwd321"
+
+# Handy tput commands:
+#tput bold - Bold effect #'\033[1m'
+#tput rev - Display inverse colors
+#tput sgr0 - Reset everything #'\033[0m'
+#tput setaf {CODE}- Set foreground color, see color {CODE} below
+#tput setab {CODE}- Set background color, see color {CODE} below
+# Colors:
+#0 Black
+#1 Red #'\033[1;31m'
+#2 Green #'\033[1;32m'
+#3 Yellow #'\033[1;33m'
+#4 Blue # '\033[1;34m'
+#5 Magenta # '\033[1;35m'
+#6 Cyan # '\033[1;36m'
+#7 White # '\033[1;37m'
+if [ -t 1 ] && $DEBUG_OUT; then
+ reset=$(tput sgr0)
+ bold=$(tput bold)
+ red=$(tput setaf 1)
+ green=$(tput setaf 2)
+ yellow=$(tput setaf 3)
+ blue=$(tput setaf 4)
+ magenta=$(tput setaf 5)
+ cyan=$(tput setaf 6)
+ white=$(tput setaf 7)
+ toend=$(tput hpa $(tput cols))$(tput cub 6)
+else
+ reset='' bold='' red='' green='' yellow='' blue='' magenta='' cyan='' white='' toend=''
+fi
+
+info() {
+ echo -e "\n${blue}ℹ $*${reset}"
+}
+success() {
+ echo -e "\n${green}✅ $*${reset}"
+}
+error() {
+ echo -e "\n${red}❌ $*${reset}" >&2
+}
+warning() {
+ echo -e "${yellow}⚠ $*${reset}" >&2
+}
+section() {
+ echo -e "\n${magenta}................[$(tput rev) $* ${reset}${magenta}]................${reset}\n"
+}
+
+FRAME=("⠋" "⠙" "⠹" "⠸" "⠼" "⠴" "⠦" "⠧" "⠇" "⠏")
+FRAME_INTERVAL=0.1
+
+start() {
+ local step=0
+ while [ "$step" -lt "${#CMDS[@]}" ]; do
+ ${CMDS[$step]} & pid=$!
+ while ps -p $pid &>/dev/null; do
+ echo -ne "\\r[ ] ${STEPS[$step]} ..."
+ for k in "${!FRAME[@]}"; do
+ echo -ne "\\r[ ${FRAME[k]} ]"
+ sleep $FRAME_INTERVAL
+ done
+ done
+ echo -ne "\\r[ ✔ ] ${STEPS[$step]}\\n"
+ step=$((step + 1))
+ done
+}
+#echo '⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏'
+spinner()
+{
+ local pid=$!
+ local delay=0.75
+ local spinstr='|/-\'
+ #'⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏'
+ while [ "$(ps a | awk '{print $1}' | grep $pid)" ]; do
+ local temp=${spinstr#?}
+ printf " [%c] " "${spinstr}"
+ local spinstr=$temp${spinstr%"$temp"}
+ sleep $delay
+ printf "\b\b\b\b\b\b"
+ done
+ printf " \b\b\b\b"
+}
+
+save_log() {
+ local log="${DEFAULT_LOG}"
+ if [ -n "${LogFile}" ]; then
+ log="${LogFile}"
+ fi
+ if ${SAVE_LOG}; then
+ echo "$(date '+%b %d %T') - $*" >> ${log}
+ return 0
+ else
+ return 1
+ fi
+}
+
+get_connections_str() {
+ local opts
+ local log_msg
+ local host
+ local ssh_str
+ local scp_str
+
+# Require router-specific SSH key
+ if [ -n "$DEFAULT_SSH_KEY" ]; then
+ opts=("-i ${DEFAULT_SSH_KEY}" "-o PasswordAuthentication=no")
+ else
+ log_msg="- Error: SSH key not found at $DEFAULT_SSH_KEY\nRun ./setup-ssh-keys.sh to generate the key"
+ if [ -t 1 ] && ${DEBUG_OUT}; then
+ error "${RouterName} ${log_msg}"
+ fi
+ save_log "${log_msg}"
+ exit 1
+ fi
+# if [ -n "$DEFAULT_CONNECT_ATTEMPTS" ]; then
+# opts+=("-o ConnectionAttempts=${DEFAULT_CONNECT_ATTEMPTS}")
+# fi
+# if [ -n "$DEFAULT_CONNECT_TIMEOUT" ]; then
+# opts+=("-o ConnectTimeout=${DEFAULT_CONNECT_TIMEOUT}")
+# fi
+ #opts+=("-o PreferredAuthentications=publickey" "-o StrictHostKeyChecking=no" "-o UserKnownHostsFile=/dev/null" \
+ #"-o GlobalKnownHostsFile=/dev/null" "-o CheckHostIP=no")
+SSH_OPTS="${opts[*]}"
+
+# Require router host IP
+ if [ -n "${SSHhost}" ]; then
+ host="${SSHhost}"
+ else
+ log_msg="- Error! SSH Host not found. Exit 1."
+ if [ -t 1 ] && ${DEBUG_OUT}; then
+ error "${RouterName} ${log_msg}"
+ fi
+ save_log "${log_msg}"
+ exit 1
+ fi
+
+ ssh_str=("$CMD_SSH" "-2" "-4")
+ scp_str=("${CMD_SCP}" "-2" "-4" "-B ${SSH_OPTS}")
+
+ if [ -n "${SSHport}" ]; then
+ ssh_str+=("-p ${SSHport}")
+ scp_str+=("-P ${SSHport}")
+ else
+ ssh_str+=("-p ${DEFAULT_SSH_PORT}")
+ scp_str+=("-P ${DEFAULT_SSH_PORT}")
+ fi
+
+ if [ -n "${SSHuser}" ]; then
+ ssh_str+=("-l ${SSHuser}")
+ scp_str+=("${SSHuser}@${host}")
+ else
+ ssh_str+=("-l ${DEFAULT_SSH_USER}")
+ scp_str+=("${DEFAULT_SSH_USER}@${host}")
+ fi
+
+ ssh_str+=("${host}")
+
+ssh_str+=("${SSH_OPTS}")
+SSH_STR="${ssh_str[*]}"
+SCP_STR="${scp_str[*]}"
+#SFTP_STR="${sftp_str[*]}"
+#SFTP_OPTS="-i ${DEFAULT_SSH_KEY} -o PasswordAuthentication=no -oPort=${SSHport}"
+}
+
+#>/dev/null 2>&1
+###############################
+# Backup listing
+###############################
+# Get Names Addresses and Ports in $HostsFile
+#declare -rx STEPS=(
+# 'download'
+# 'check'
+# 'backup'
+#)
+#IFS=$'\n'
+run_ssh_cmd() {
+ local run="${1}"
+ warning "${run}"
+ if ${SSH_STR} ${run}; then
+ return 0
+ else
+ return 1
+ fi
+}
+
+download_config() {
+ local conf="${1}"
+ local log_msg
+
+ if ${SSH_STR} export >${conf}; then
+ log_msg="- Config download success!"
+ if [ -t 1 ] && $DEBUG_OUT; then
+ success "${RouterName} ${log_msg}"
+ fi
+ save_log "${log_msg}"
+ return 0
+ else
+ log_msg="- Config download ${conf} error! Exit Error 1."
+ if [ -t 1 ] && $DEBUG_OUT; then
+ error "${RouterName} ${log_msg}"
+ fi
+ save_log "${log_msg}"
+ exit 1
+ fi
+}
+
+compare_config() {
+ local config_file="${1}"
+ local precedent_file="${2}"
+ local diff_file="${3}"
+ local diff_args=${DEFAULT_DIFF_ARGS}
+ local log_msg
+
+ diff ${diff_args} ${config_file} ${precedent_file} >${diff_file}
+ if [ "$?" -ne "0" ]; then
+ log_msg="- There is a difference! Create backup..."
+ if [ -t 1 ] && $DEBUG_OUT; then
+ section " CREATE BACKUP "
+ sleep 1
+ info "${RouterName} ${log_msg}"
+ fi
+ save_log "${log_msg}"
+ return 0
+ else
+ log_msg="- The files are identical. No backup is required."
+ if [ -t 1 ] && $DEBUG_OUT; then
+ section " START CLEANUP "
+ sleep 1
+ info "${RouterName} ${log_msg}"
+ fi
+ save_log "${log_msg}"
+ log_msg="- Remove ${diff_file} file."
+ if [ -t 1 ] && $DEBUG_OUT; then
+ info "${RouterName} ${log_msg}"
+ fi
+ save_log "${log_msg}"
+ ${CMD_RM} -f ${diff_file}
+ log_msg="- Remove ${config_file} file."
+ if [ -t 1 ] && $DEBUG_OUT; then
+ info "${RouterName} ${log_msg}"
+ fi
+ save_log "${log_msg}"
+ ${CMD_RM} -f ${config_file}
+
+ return 1
+ fi
+}
+
+check_directory() {
+ local chk_dir="${1}"
+ local log_msg
+
+ if [[ -d ${chk_dir} && -r ${chk_dir} ]]
+ then
+ return 0
+ else
+ if $CMD_MKD ${chk_dir}; then
+ log_msg="- Direcory ${chk_dir} created."
+ if [ -t 1 ] && $DEBUG_OUT; then
+ success "${RouterName} ${log_msg}"
+ fi
+ save_log "${log_msg}"
+ return 0
+ else
+ log_msg="- Error create direcory ${chk_dir}! Exit 1."
+ if [ -t 1 ] && $DEBUG_OUT; then
+ error "${RouterName} ${log_msg}"
+ fi
+ save_log "${log_msg}"
+ exit 1
+ fi
+# This script must be run by a special user in his home directory.
+# Therefore, the following lines are not needed.
+# $CMD_CHO root:root $dir
+# $CMD_CHM 755 $ST_FULL
+ fi
+}
+
+#${SSH_STR} /system script environment get [find name=BackupPassword] value;
+#exit 0
+backup_binary() {
+ local bin_name="${1}"
+ local crypt="${2:-$DEFAULT_BIN_CRYPT}"
+ local bin_pass="${3:-$DEFAULT_BINPWD}"
+ local log_msg
+ local crypt_str
+ #${SSH_STR} :put [/system script environment get [find name=BackupPassword] value];
+
+ if ${SSH_STR} /system/script environment find where name=BackupEncrypt && ${SSH_STR} /system/script environment find where name=BackupPassword; then
+
+ bin_pass=${SSH_STR} /system/script/environment/get ([ /system/script/environment find name='BackupPassword' ]->value);
+ crypt_str="dont-encrypt=no password=${bin_pass}"
+ warning "${crypt_str}"
+ else
+ if [ -n ${crypt} ] && [ -n {passwd} ]; then
+ crypt_str="dont-encrypt=no password=${passwd}"
+ else
+ crypt_str="dont-encrypt=yes"
+ fi
+
+ fi
+ # if [ -n $]; then
+
+# local BKPSTR="system backup save name=${bin_name} dont-encrypt=no password=$BKP_BINPWD"
+# T_BKPCLN="file remove [find name=$TGT_BKPNAME_BIN]"
+}
+
+readarray -t lines < $HostsFile
+#declare -r STEPS=($lines)
+#declare -r MAX_STEPS=${#lines[@]}
+#declare -r BAR_SIZE="##########"
+#declare -r MAX_BAR_SIZE=${#BAR_SIZE}
+
+for HR in "${lines[@]}"; do
+# perc=$(((HR + 1) * 100 / MAX_STEPS))
+# percBar=$((perc * MAX_BAR_SIZE / 100))
+# declare -rx CMDS=(
+# "${CMD_SSH} ${SSH_OPTS} ${SSHuser}@${SSHhost} export >$current_config"
+# 'sleep 10'
+# 'sleep 1'
+#)
+while IFS=$' ' read -a HR ; do
+#|| [ -n "$HR" ]; do
+[[ -z ${HR[0]} ]] && continue
+
+DStamp=$(date '+%Y%m%d')
+DTStamp=$(date '+%Y%m%d%H%M%S')
+
+RouterName=${HR[0]}
+SSHhost=${HR[1]}
+SSHport=${HR[2]}
+SSHuser=${HR[3]}
+SSHpassword=${HR[4]}
+
+get_connections_str
+backup_binary "BinaryTest.backup"
+
+exit 0
+#warning "SSH_STR = ${SSH_STR}"
+#warning "SCP_STR = ${SCP_STR}"
+#exit 0
+
+BaseDir=${HomeDir}"/backups/${RouterName}/"
+TempDir=${BaseDir}"tmp/"
+HistoryDir=${BaseDir}"history/"
+LogFile="/var/log/Mikrotiks/${RouterName}.log"
+log_msg="undefined"
+
+log_msg="- Start backup script ${ScriptName} for ${RouterName}"
+if [ -t 1 ] && $DEBUG_OUT; then
+ info "${RouterName} ${log_msg}"
+ echo
+fi
+save_log "${log_msg}"
+
+log_msg="- Check and creatre services directories for ${RouterName} if not exsist..."
+if [ -t 1 ] && $DEBUG_OUT; then
+ section " CHECK DIRS "
+ info "${RouterName} ${log_msg}"
+fi
+save_log "${log_msg}"
+
+CheckDirs=("${BaseDir}" "${TempDir}" "${HistoryDir}")
+for dir in "${CheckDirs[@]}"; do
+(check_directory "${dir[@]}")
+done
+
+log_msg="All Directories for ${RouterName} was created sucessfully!"
+if [ -t 1 ] && $DEBUG_OUT; then
+ success "${log_msg}"
+fi
+save_log "- ${log_msg}"
+
+log_msg="Set variables for ${RouterName}..."
+if [ -t 1 ] && $DEBUG_OUT; then
+ info "${log_msg}"
+fi
+save_log "- ${log_msg}"
+
+precedent_config=${BaseDir}"Precedent-Config.rsc"
+current_config=${TempDir}"Config.rsc"
+diff_config=${TempDir}"Diff-Config.rsc"
+current_backup="Binary.backup"
+
+log_msg="Set variables for ${RouterName} success!"
+if [ -t 1 ] && $DEBUG_OUT; then
+ success "${log_msg}"
+fi
+save_log "- ${log_msg}"
+
+section " CHECK CONFIG "
+log_msg="- Download ${current_config} file to Temp Directory..."
+if [ -t 1 ] && $DEBUG_OUT; then
+ info "${RouterName} ${log_msg}"
+fi
+save_log "- ${log_msg}"
+
+download_config "${current_config}"
+
+log_msg="- Comparing with the previous config..."
+if [ -t 1 ] && $DEBUG_OUT; then
+ info "${RouterName} ${log_msg}"
+fi
+save_log "- ${log_msg}"
+
+if compare_config ${current_config} ${precedent_config} ${diff_config} ${RouterName} ${LogFile}; then
+ BackupDir=${BaseDir}"${DStamp}/"
+ ${CMD_MKD} ${BackupDir}
+
+ save_config=${BackupDir}"${DTStamp}-Config.rsc"
+ save_backup=${BackupDir}"${DTStamp}-Binary.backup"
+
+ log_msg="- Copy ${current_config} file to ${BackupDir} directory..."
+ if [ -t 1 ] && $DEBUG_OUT; then
+ info "${RouterName} ${log_msg}"
+ fi
+ save_log "- ${log_msg}"
+
+ cp ${current_config} ${save_config}
+
+ ${CMD_RM} -f ${precedent_config}
+
+ ${CMD_MV} -f ${current_config} ${precedent_config}
+
+ log_msg="- Copy configuration to ${BackupDir}/${save_config} and ${precedent_config} success."
+ if [ -t 1 ] && $DEBUG_OUT; then
+ success "${RouterName} ${log_msg}"
+ fi
+ save_log "- ${log_msg}"
+
+ log_msg="Create ${current_backup} file on ${RouterName}..."
+ if [ -t 1 ] && $DEBUG_OUT; then
+ info "${log_msg}"
+ fi
+ save_log "- ${log_msg}"
+# if [ $? -eq 0 ]; then
+ if ${SSH_STR} system backup save name=${current_backup}; then
+ log_msg="Create binary backup ${current_backup} on ${RouterName} success."
+ if [ -t 1 ] && $DEBUG_OUT; then
+ success "${log_msg}"
+ fi
+ save_log "- ${log_msg}"
+
+ log_msg="- Start download ${current_backup} file to ${BackupDir}..."
+ if [ -t 1 ] && $DEBUG_OUT; then
+ info "${RouterName} ${log_msg}"
+ fi
+ save_log "- ${log_msg}"
+
+# if ${SFTP_STR}:$current_backup; then
+ # ${CMD_SFTP} ${SFTP_OPTS} ${SSHuser}@${SSHhost}:$current_backup
+ if ${SCP_STR}:/${current_backup} ${save_backup}; then
+ sleep 2
+ log_msg="- Binary Backup download complete!"
+ if [ -t 1 ] && $DEBUG_OUT; then
+ success "${RouterName} ${log_msg}"
+ fi
+ save_log "${log_msg}"
+ else
+ log_msg="- Error! Save binary backup on ${RoutrName} failed!"
+ if [ -t 1 ] && $DEBUG_OUT; then
+ error "${log_msg}"
+ fi
+ save_log "${log_msg}"
+ fi
+ else
+ log_msg="Error create binary backup ${current_backup} on ${RouterName}!"
+ if [ -t 1 ] && $DEBUG_OUT; then
+ error "${log_msg}"
+ fi
+ save_log "- ${log_msg}"
+ fi
+
+ log_msg="- Remove ${current_backup} file on ${RouterName}."
+ if [ -t 1 ] && $DEBUG_OUT; then
+ section "CLEANUP"
+ info "${log_msg}"
+ fi
+ save_log "- ${log_msg}"
+
+ if ${SSH_STR} /file remove ${current_backup}; then
+ log_msg="- Remove ${current_backup} file on ${RouterName} complete succesfully!"
+ if [ -t 1 ] && $DEBUG_OUT; then
+ success "${RouterName} ${log_msg}"
+ fi
+ save_log "${log_msg}"
+ else
+ log_msg="- Error Remove ${current_backup} file on ${RouterName}!"
+ if [ -t 1 ] && $DEBUG_OUT; then
+ error "${RouterName} ${log_msg}"
+ fi
+ save_log "${log_msg}"
+ fi
+
+ cd "${BaseDir}"
+
+ CurrentHistoryDir=${HistoryDir}"${DStamp}/"
+ ${CMD_MKD} -p ${CurrentHistoryDir}
+ diff_config_history=${CurrentHistoryDir}"${DTStamp}-Diff-Config.rsc"
+ log_msg="- Move ${diff_config} file to ${CurrentHistoryDir}."
+ if [ -t 1 ] && $DEBUG_OUT; then
+ info "${RouterName} ${log_msg}"
+ fi
+ save_log "${log_msg}"
+
+ if ${CMD_MV} -f ${diff_config} ${diff_config_history}; then
+ log_msg="- Move ${diff_config} file to ${CurrentHistoryDir} complete succesfully!"
+ if [ -t 1 ] && $DEBUG_OUT; then
+ success "${RouterName} ${log_msg}"
+ fi
+ save_log "${log_msg}"
+ else
+ log_msg="- Error move ${diff_config} file to ${CurrentHistoryDir}!"
+ if [ -t 1 ] && $DEBUG_OUT; then
+ error "${RouterName} ${log_msg}"
+ fi
+ save_log "${log_msg}"
+ fi
+
+ log_msg="- Go to Git directory (${GitDir}), add backups files and commit him."
+ if [ -t 1 ] && $DEBUG_OUT; then
+ section " COMMIT "
+ info "${RouterName} ${log_msg}"
+ fi
+ save_log "${log_msg}"
+ cd "${GitDir}/"
+ git add .
+ log_msg="- Git add commit..."
+ if [ -t 1 ] && $DEBUG_OUT; then
+ info "${RouterName} ${log_msg}"
+ fi
+ save_log "${log_msg}"
+ if git commit -m "Backup for ${RouterName} sucessfully creatd at $(date '+%b %d %T')."; then
+ log_msg=" - Git add commit success."
+ if [ -t 1 ] && $DEBUG_OUT; then
+ success "${RouterName} ${log_msg}"
+ fi
+ save_log "${log_msg}"
+ else
+ log_msg="- Git add commit Error!"
+ if [ -t 1 ] && $DEBUG_OUT; then
+ error "${RouterName} ${log_msg}"
+ fi
+ save_log "${log_msg}"
+ fi
+ log_msg="- Git push commit..."
+ if [ -t 1 ] && $DEBUG_OUT; then
+ info "${RouterName} ${log_msg}"
+ fi
+ save_log "${log_msg}"
+ if git push origin main; then
+ log_msg=" - Git push commit success."
+ if [ -t 1 ] && $DEBUG_OUT; then
+ success "${RouterName} ${log_msg}"
+ fi
+ save_log "${log_msg}"
+ else
+ log_msg="- Git push commit Error!"
+ if [ -t 1 ] && $DEBUG_OUT; then
+ error "${RouterName} ${log_msg}"
+ fi
+ save_log "${log_msg}"
+ fi
+else
+ log_msg="- ${RouterName} backup complete!"
+ if [ -t 1 ] && $DEBUG_OUT; then
+ info "${RouterName} ${log_msg}"
+ fi
+ save_log "${log_msg}"
+fi
+
+log_msg="- Remove Temp directory."
+if [ -t 1 ] && $DEBUG_OUT; then
+ info "${RouterName} ${log_msg}"
+fi
+ save_log "${log_msg}"
+
+${CMD_RM} -d ${TempDir}
+log_msg="- backup complete!"
+if [ -t 1 ] && $DEBUG_OUT; then
+ success "${RouterName} ${log_msg}"
+fi
+save_log "${log_msg}"
+
+done <<<"$(echo -e $HR)"
+LogFile="${DEFAULT_LOG}"
+# echo -ne "\\r[${BAR_SIZE:0:percBar}] $perc %"
+#echo "[] $perc %"
+done
+log_msg="All Done! Exit 0."
+if [ -t 1 ] && $DEBUG_OUT; then
+ section " DONE "
+ success "${log_msg}"
+fi
+save_log "${log_msg}"
+exit 0
--- /dev/null
+:global MkDir;
+:put [($MkDir /usb1/tmpfs)];
+:log warning ("\$MkDir = " . $MkDir . ".");
--- /dev/null
+#!/bin/bash
+# Add to cron
+# crontab -e
+#*/20 * * * * /var/www/ros.vados.ru/Mikrotiks/.bin/ros-diff.sh
+#set +eo pipefail
+#set -x
+#
+DEBUG_OUT=true
+if [ -t 1 ] && $DEBUG_OUT; then
+ reset='\033[0m'
+ bold=$(tput bold)
+ #'\033[1m'
+ red=$(tput setaf 7)$(tput setab 1)
+ #'\033[1;31m'
+ green=$(tput setaf 4)$(tput setab 7)$(tput bold)
+ #'\033[1;32m'
+ yellow='\033[1;33m'
+ blue='\033[1;34m'
+ magenta='\033[1;35m'
+ cyan='\033[1;36m'
+ white='\033[1;37m'
+else
+ reset='' bold='' red='' green='' yellow='' blue='' cyan='' magenta='' white=''
+fi
+
+info() {
+ if $DEBUG_OUT; then
+ echo -e "${blue}ℹ $*${reset}"
+ fi
+}
+success() {
+ if $DEBUG_OUT; then
+ echo -e "${bold}${green}✅ $*${reset}"
+ fi
+}
+error() {
+ if $DEBUG_OUT; then
+ echo -e "${red}❌ Error: $*${reset}" >&2
+ fi
+}
+warning() {
+ if $DEBUG_OUT; then
+ echo -e "${yellow}⚠ $*${reset}" >&2
+ fi
+}
+section() {
+ if $DEBUG_OUT; then
+ echo -e "\n${magenta}====$*====${reset}"
+ fi
+}
+toend=$(tput hpa $(tput cols))$(tput cub 6)
+toend_ok() {
+ if $DEBUG_OUT; then
+ echo -n "${toend}${white}[${green}OK${white}]"
+ fi
+}
+toend_error() {
+ if $DEBUG_OUT; then
+ echo -n "${toend}${white}[${red}ERROR${white}]"
+ fi
+}
+BR() {
+ if $DEBUG_OUT; then
+ echo -e "\n"
+ fi
+}
+
+error "error"
+success "success"
+exit 0
+
+# HomeDir=$HOME
+HomeDir="/var/www/ros.vados.ru/Mikrotiks"
+HostsFile=${HomeDir}"/.config/.mikrotiks"
+BinDir=${HomeDir}"/.bin"
+GitDir=${HomeDir}"/backups"
+
+SSH_KEY=${HomeDir}"/.ssh/id_rsa"
+# Require router-specific SSH key
+if [ ! -f "$SSH_KEY" ]; then
+ error "Error: SSH key not found at $SSH_KEY"
+ error "Run ./setup-ssh-keys.sh to generate the key"
+ exit 1
+fi
+
+###############################
+# Backup listing
+###############################
+# Get Names Addresses and Ports in $HostsFile
+
+readarray -t lines < $HostsFile
+
+for HR in "${lines[@]}"; do
+while IFS=$' ' read -a HR ; do
+#|| [ -n "$HR" ]; do
+[[ -z ${HR[0]} ]] && continue
+
+#GEN_DATE=$(date '+%Y-%m-%d %H:%M:%S %z')
+GEN_DATE=$(date '+%d.%m.%Y')
+GEN_TIME=$(date '+%b %d %T')
+GEN_DATE_TIME=$(date '+%d.%m.%Y %T %Z')
+DStamp=$(date '+%Y%m%d')
+
+RouterName=${HR[0]}
+SSHhost=${HR[1]}
+SSHport=${HR[2]}
+SSHuser=${HR[3]}
+SSHpassword=${HR[4]}
+
+BaseDir=${HomeDir}"/backups/"${RouterName}
+TempDir=${BaseDir}"/tmp"
+HistoryDir=${BaseDir}"/history"
+LogFile="/var/log/Mikrotiks/"${RouterName}".log"
+
+SSH_OPTS="-i $SSH_KEY -o PasswordAuthentication=no -p ${SSHport}"
+SFTP_OPTS="-i $SSH_KEY -o PasswordAuthentication=no -oPort=${SSHport}"
+echo "${GEN_DATE_TIME} - Start backup script ${ARGV[0]} for ${RouterName}" >> ${LogFile}
+section "Create dirs"
+info "Create servise directories for ${RouterName}..."
+echo "${GEN_TIME} - Create services directories for ${RouterName}..." >> ${LogFile}
+mkdir -p ${BaseDir}
+mkdir -p ${TempDir}
+mkdir -p ${HistoryDir}
+echo "${GEN_TIME} - All Directories for ${RouterName} was created sucessfully!" >> ${LogFile}
+success "All directories for ${RouterName} was created sucessfully!"
+echo "${GEN_TIME} - Set variables for ${RouterName}..." >> ${LogFile}
+info "Set variables for ${RouterName}..."
+precedent_config=${BaseDir}"/Precedent-Config.rsc"
+current_config="Config.rsc"
+current_backup="Binary.backup"
+diff_config=${TempDir}"/Diff-Config.rsc"
+echo "${GEN_TIME} - Set variables for ${RouterName} success!" >> ${LogFile}
+success "Set variables for ${RouterName} success!"
+#ssh ${SSH_OPTS} ${SSHuser}@${SSHhost} system script run MkTmpfs;
+cd ${TempDir}
+section ""
+BR
+section "Start checkout process"
+info "Download ${current_config} file to Temp Directory..."
+echo "${GEN_TIME} - Download ${current_config} file to ${TempDir}" >> ${LogFile}
+ssh ${SSH_OPTS} ${SSHuser}@${SSHhost} export >$current_config
+if [ $? -eq 0 ]; then
+echo
+toend_ok
+echo
+echo "${GEN_TIME} - Download complete!" >> ${LogFile}
+else
+echo
+toend_error
+echo
+echo "${GEN_TIME} - Download ${current_config} failed!" >> ${LogFile}
+exit 1
+fi
+info "Comparing with the previous config..."
+echo "${GEN_TIME} - Comparing with the previous config..." >> ${LogFile}
+diff -I "RouterOS" ${current_config} ${precedent_config} >${diff_config}
+if [ "$?" -ne "0" ]; then
+echo "${GEN_TIME} - There is a difference! Create backup..." >> ${LogFile}
+section "Create backup for ${RouterName}"
+BackupDir=${BaseDir}"/"${DStamp}
+mkdir -p ${BackupDir}
+DTStamp=$(date '+%Y%m%d%H%M%S')
+save_config=${DTStamp}"-Config.rsc"
+save_backup=${DTStamp}"-Binary.backup"
+diff_config_history=${DTStamp}"-Diff-Config.rsc"
+echo "${GEN_TIME} - Copy ${current_config} file to ${BackupDir} directory..." >> ${LogFile}
+cd ${TempDir}
+rm -f ${precedent_config}
+cp ${current_config} ${BackupDir}"/"${save_config}
+mv -f ${current_config} ${precedent_config}
+echo "${GEN_TIME} - Copy configuration to ${BackupDir}/${save_config} and ${precedent_config} success." >> ${LogFile}
+success "Copy configuration to ${BackupDir}/${save_config} and ${precedent_config} success."
+info "Create ${current_backup} file on ${RouterName}..."
+echo "${GEN_TIME} - Create ${current_backup} file on ${RouterName}..." >> ${LogFile}
+ssh ${SSH_OPTS} ${SSHuser}@${SSHhost} system backup save name=${current_backup}
+if [ $? -eq 0 ]; then
+echo
+toend_ok
+echo
+echo "${GEN_TIME} - Create binary backup ${current_backup} on ${RouterName} success." >> ${LogFile}
+info "Start download ${current_backup} file to ${BackupDir}..."
+echo "${GEN_TIME} - Start download ${current_backup} file to ${BackupDir}..." >> ${LogFile}
+sftp ${SFTP_OPTS} ${SSHuser}@${SSHhost}:$current_backup
+sleep 1
+success "Download complete!"
+echo "${GEN_TIME} - Download complete!" >> ${LogFile}
+cd ${BackupDir}
+sleep 2
+mv -f ${TempDir}"/"${current_backup} ${save_backup}
+echo "${GEN_TIME} - Remove ${current_backup} file on ${RouterName}." >> ${LogFile}
+ssh ${SSH_OPTS} ${SSHuser}@${SSHhost} /file remove ${current_backup}
+else
+echo
+toend_error
+echo
+echo "${GEN_TIME} - Create binary backup ${current_backup} failed!" >> ${LogFile}
+ssh ${SSH_OPTS} ${SSHuser}@${SSHhost} /file remove $current_backup
+exit 1
+fi
+cd ${TempDir}
+CurrentHistoryDir=${HistoryDir}"/"${DStamp}
+mkdir -p ${CurrentHistoryDir}
+echo "${GEN_TIME} - Move ${diff_config} file to ${CurrentHistoryDir}." >> ${LogFile}
+mv -f ${diff_config} ${CurrentHistoryDir}"/"${diff_config_history}
+echo "${GEN_TIME} - Move ${diff_config} file to ${CurrentHistoryDir} complete." >> ${LogFile}
+echo "${GEN_TIME} - Go to Git directory (${GitDir}), add backups files and commit him." >> ${LogFile}
+rm -d ${TempDir}
+cd ${GitDir}
+git add .
+echo "${GEN_TIME} - Git add commit..." >> ${LogFile}
+git commit -m "Backup for ${RouterName} sucessfully creatd at ${GEN_TIME}."
+echo "${GEN_TIME} - Done." >> ${LogFile}
+echo "${GEN_TIME} - Git push..." >> ${LogFile}
+git push origin main
+echo "${GEN_TIME} - Done." >> ${LogFile}
+echo "${GEN_TIME} - ${RouterName} backup complete!" >> ${LogFile}
+success "${RouterName} backup complete!"
+fi
+section "The files are identical. No backup is required."
+echo "${GEN_TIME} - The files are identical. No backup is required." >> ${LogFile}
+echo "${GEN_TIME} - Remove ${diff_config} file." >> ${LogFile}
+info "Remove ${diff_config} file."
+rm -f ${diff_config}
+echo "${GEN_TIME} - Remove ${current_config} file." >> ${LogFile}
+info "Remove ${current_config} file."
+rm -f ${current_config}
+echo "${GEN_TIME} - Remove Temp directory." >> ${LogFile}
+info "Remove Temp directory."
+rm -d ${TempDir}
+section ""
+success "${RouterName} backup complete!"
+echo "${GEN_TIME} - ${RouterName} backup complete!" >> ${LogFile}
+done <<<"$(echo -e $HR)"
+done
+section "All Done!"
+echo "All Done!" >> ${LogFile}
+exit 0