diff --git a/.gitignore b/.gitignore index dfe6f0e..fd73820 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ *.txt -build/ +dist/ tests/helpers/* src/backups/ src/clean -plan.sh +NOTE.todo diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 0000000..5b4f13c --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,8 @@ +CHANGELOG + + All notable changes to this project will be documented in this file. + Version numbering uses Semantic Versioning 2.0.0 + +0.1.0 + + Initial release. diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index ea50feb..0000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,9 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -Version numbering uses [Semantic Versioning 2.0.0](https://semver.org/). - -# 0.1.0 - -Initial release. diff --git a/Makefile b/Makefile index af2a8e3..89a1867 100644 --- a/Makefile +++ b/Makefile @@ -1,24 +1,28 @@ -SRC_DIR := ./src -DOCS_DIR := ./docs -TESTS_DIR := ./tests -BUILD_DIR := ./build -DOCS_BUILD_DIR := ./build/docs +SRC_DIR := ./src +DOCS_DIR := ./docs +TESTS_DIR := ./tests +BUILD_DIR := ./dist -.PHONY: help tests docs man install uninstall +.PHONY: help tests manpages install uninstall fixpath -all: man +all: manpages help: - @echo Usage: make TARGET + @echo 'Usage: make TARGET' + @echo ' make prefix=/path install' + @echo ' make prefix=/path uninstall' @echo @echo Available targets: @echo - @echo 'help print this help message' - @echo 'tests run tests from $(TESTS_DIR)' - @echo 'lint run shellcheck' - @echo 'man build manual pages from $(DOCS_DIR)' + @echo ' help print this help message' + @echo ' tests run tests from $(TESTS_DIR)' + @echo ' lint run shellcheck' + @echo ' manpages build manual pages from $(DOCS_DIR)' + @echo ' install install boring-backup with prefix' + @echo ' uninstall uninstall boring-backup with prefix' @echo - @echo See README.md for more info. + @echo 'prefix examples: $$HOME/.local, /usr/local, /usr/bin' + @echo See README for more info. tests: # See bats(1), https://bats-core.readthedocs.io/en/latest/index.html @@ -32,24 +36,63 @@ lint: shellcheck $(SRC_DIR)/lib/handlers/sources/*.sh shellcheck $(SRC_DIR)/lib/handlers/targets/*.sh -man: build_dir - # See rst2man(1), rst2html(1), +manpages: + mkdir -pv $(BUILD_DIR)/share/man/ru/man1 + # See rst2man(1) # https://docutils.sourceforge.io/docs/index.html - rst2man $(DOCS_DIR)/boring-backup.ru.1.rst \ - > $(DOCS_BUILD_DIR)/boring-backup.ru.1 + rst2man -v $(DOCS_DIR)/manpages/boring-backup.ru.1.rst \ + > $(BUILD_DIR)/share/man/ru/man1/boring-backup.1 sed -e 's/.SH NAME/.SH ИМЯ/' \ -e 's/.SH AUTHOR/.SH АВТОРЫ/' \ -e 's/.SH COPYRIGHT/.SH АВТОРСКИЕ ПРАВА/' \ - -i $(DOCS_BUILD_DIR)/boring-backup.ru.1 - gzip -9 $(DOCS_BUILD_DIR)/boring-backup.ru.1 + -i $(BUILD_DIR)/share/man/ru/man1/boring-backup.1 + gzip -vf9 $(BUILD_DIR)/share/man/ru/man1/boring-backup.1 -build_dir: - mkdir -p $(BUILD_DIR) - mkdir -p $(DOCS_BUILD_DIR) +html: manpages + mkdir -p $(BUILD_DIR)/share/doc/boring-backup + zcat $(BUILD_DIR)/share/man/ru/man1/boring-backup.1.gz | \ + groff -man -Kutf8 -Thtml \ + > $(BUILD_DIR)/share/doc/boring-backup/boring-backup.1.html 2>/dev/null + rm -v grohtml-*.png -install: - # install +install: manpages + @echo prefix: $$prefix + install -Dm755 $(SRC_DIR)/boring-backup $$prefix/bin/boring-backup + install -Dm644 $(SRC_DIR)/lib/lib.sh $$prefix/share/boring-backup/lib.sh + install -Dm644 $(SRC_DIR)/lib/backup.sh $$prefix/share/boring-backup/backup.sh + install -Dm644 $(SRC_DIR)/lib/common.sh $$prefix/share/boring-backup/common.sh + install -Dm644 $(SRC_DIR)/lib/source.sh $$prefix/share/boring-backup/source.sh + install -Dm644 $(SRC_DIR)/lib/uri.sh $$prefix/share/boring-backup/uri.sh + install -Dm644 $(SRC_DIR)/lib/handlers/sources/tar.sh $$prefix/share/boring-backup/handlers/sources/tar.sh + install -Dm644 $(SRC_DIR)/lib/handlers/sources/mysqldump.sh $$prefix/share/boring-backup/handlers/sources/mysqldump.sh + install -Dm644 $(SRC_DIR)/lib/handlers/sources/pg_dump.sh $$prefix/share/boring-backup/handlers/sources/pg_dump.sh + install -Dm644 $(SRC_DIR)/lib/handlers/targets/cp.sh $$prefix/share/boring-backup/handlers/targets/cp.sh + install -Dm644 $(SRC_DIR)/lib/handlers/targets/s3cmd.sh $$prefix/share/boring-backup/handlers/targets/s3cmd.sh + sed -e "s%LIBRARY=\"\$${LIBRARY:-.\/lib}\"%LIBRARY=\"\$${LIBRARY:-$$prefix\/share\/boring-backup}\"%" \ + -i $$prefix/share/boring-backup/lib.sh \ + -i $$prefix/bin/boring-backup + install -Dm664 $(BUILD_DIR)/share/man/ru/man1/boring-backup.1.gz $$prefix/share/man/ru/man1/boring-backup.1.gz uninstall: - # uninstall + @echo prefix: $$prefix + rm $$prefix/bin/boring-backup + rm $$prefix/share/boring-backup/lib.sh + rm $$prefix/share/boring-backup/backup.sh + rm $$prefix/share/boring-backup/common.sh + rm $$prefix/share/boring-backup/source.sh + rm $$prefix/share/boring-backup/uri.sh + rm $$prefix/share/boring-backup/handlers/sources/tar.sh + rm $$prefix/share/boring-backup/handlers/sources/mysqldump.sh + rm $$prefix/share/boring-backup/handlers/sources/pg_dump.sh + rm $$prefix/share/boring-backup/handlers/targets/cp.sh + rm $$prefix/share/boring-backup/handlers/targets/s3cmd.sh + rm -rv $$prefix/share/boring-backup + rm $$prefix/share/man/ru/man1/boring-backup.1.gz +fixpath: + # Set boring-backup library path + @echo prefix: $$prefix + @echo path: $$path + sed -e "s%LIBRARY=\"\$${LIBRARY:-.\/lib}\"%LIBRARY=\"\$${LIBRARY:-$$path}\"%" \ + -i $$prefix/share/boring-backup/lib.sh \ + -i $$prefix/bin/boring-backup diff --git a/README b/README new file mode 100644 index 0000000..a15584b --- /dev/null +++ b/README @@ -0,0 +1,28 @@ +BORING BACKUP + + Bash powered backuping tool. + +INSTALLATION + + Install for user locally: + make prefix=$HOME/.local install + + Uninstall: + make prefix=$HOME/.local uninstall + +DEVELOPMENT + + TESTING + + BB uses amazing test suite: https://bats-core.readthedocs.io/ + + Install Bats: + git clone https://github.com/bats-core/bats-core.git + bash bats-core/install.sh ~/.local + + Add some Bats extensions: + git clone https://github.com/bats-core/bats-assert.git tests/helpers/bats-assert + git clone https://github.com/bats-core/bats-support.git tests/helpers/bats-support + + Run tests: + make test diff --git a/README.md b/README.md deleted file mode 100644 index a7ba869..0000000 --- a/README.md +++ /dev/null @@ -1,33 +0,0 @@ -# Boring Backup - -Bash powered backup tool. - -# Testing - -BB uses amazing test suite: https://bats-core.readthedocs.io/ - -Install Bats: - - git clone https://github.com/bats-core/bats-core.git - bash bats-core/install.sh ~/.local - -Add some Bats extensions: - - git clone https://github.com/bats-core/bats-assert.git tests/helpers/bats-assert - git clone https://github.com/bats-core/bats-support.git tests/helpers/bats-support - -Run tests: - - make test - -# Linting - -Run shellcheck: - - make lint - -# Building - -Build manpages: - - make man diff --git a/docs/boring-backup.ru.1.rst b/docs/boring-backup.ru.1.rst deleted file mode 100644 index 6cc3277..0000000 --- a/docs/boring-backup.ru.1.rst +++ /dev/null @@ -1,509 +0,0 @@ -:Title: boring-backup -:Title upper: BORING-BACKUP -:Subtitle: backup files and databases -:Author: ge -:Copyright: \(C) 2022, ge , GPLv3+ -:Date: 2022 May 15 -:Manual section: 1 -:Version: boring-backup 0.1.0 - -Синопсис --------- - -``boring-backup`` [-cvlphV] FILES.. - -Описание --------- - -boring-backup - это расширяемая утилита для резервного копирования на основе -сценариев Bash. - -Опции ------ - --c, --config config file. --v, --verbose verbose output. --l, --log-file log file. --p, --pid-file PID file. --h, --help print this help messagea and exit. --V, --version print version and exit. - -Быстрый старт -------------- - -boring-backup можно рассматривать как фреймворк или библиотеку для создания -сценариев резервного копирования. Сценарии должны содержать валидный код на -Bash. Сценарий импортируется в основной скрипт с помощью команды ``source`` -(см. ``bash``\(1) п. SHELL BUILTIN COMMANDS). - -Простейший сценарий резервного копирования выглядит следующим образом:: - - sources=(file:/home/user) - targets=(file:/var/backup) - -Здесь массивы `sources` и `targets` определяют точки, они же поинты (`points`) -резервного копирования: источники (`sources`) и назначения (`targets`). Это -основные сущности, с которыми работает boring-backup. Вот некоторые особенности -поинтов: - -- Все поинты указываются в формате URI. -- Поинты могут указывать как на локальные (размещённые на текущей машине), так - и на удалённые (размещённые на удалённой машине) ресурсы. -- Можно указать как несколько источников, так и несколько назначений. - -Для выполнения бэкапа директорий или отдельных файлов применяется схема URI -`file`. В схеме `file` нельзя указать директорию, размещённую на удалённом -хранилище (за исключением случая, когда удалённое хранилище примонтировано как -файловая система), для этого используйте другие схемы. В примере выше будет -выполнена резервная копия локальной директории /home/user в другую локальную -директорию /var/backup. Поскольку в сценарии из примера нет ничего, кроме -указания точек `sources` и `targets`, бэкап будет выполнен с параметрами по -умолчанию: директория /home/user будет заархивирована с помощью утилиты -``tar``\(1) и помещён в директорию /var/backup. Имя архива будет сложено по -шаблону:: - - ${name_prefix}${name}${name_date_fmt}${name_suffix}${name_ext} - -Описание переменных см. в разделе ПЕРЕМЕННЫЕ. Пример имени файла:: - - example.sh_example_2022.05.15-0953.tar - -Обзор URI ---------- - -Источники (`sources`) и назначения (`targets`) должны быть указаны в виде -URI. Это позовляет наглядно в одной сроке видеть весь набор данных. Парсер -реализует поддержку следующего синтаксиса URI:: - - scheme:[//[username[:password]@]hostname[:port]]/[path]?[query]#[fragment] - -Парсер URI соответствует RFC 3986 не полностью - разбиение на составные части -URI происходит верно, но интерпретация в отдельных ситуациях может отличаться. -См. примеры URI и реузультаты их интерпретации парсером в файле -`tests/parse_uri.bats`. На практике рекомендую придерживаться примеров, которые -приведены ниже и в файле `tests/parse_uri.bats`. В случае, если изменение -логики парсера необходимо, вы можете прислать правки в pull-реквесте или -завести feature-реквест в репозитории проекта. На текущий момент компоненты -`query` и `fragment` не используются. - -Примеры URI для массива `sources`:: - - mysql://wordpress:1jobrRRjtLYs@localhost/wordpress - postgres://django:9%3F2%3D%40gHW@localhost:5432:/django - sqlite:///var/www/app/data/database.db - file:///var/www/html/ - -Примеры URI для `targets`:: - - file:/var/backup - ftp://jhon:%24t%D0%AFo%7C%5C%7C6@[fe80::5054:ff:fe24:382]:2021/backups/ - rsync://backup_user@192.168.3.12:2022/backups - s3://my_bucket - -Пароли содержащие специальные символы недопустимые в URI (включая пробелы) -необходимо закодировать в percent code. См. примеры кодирования строки на -разных языках программирования ниже. - -Python 2:: - - python2 -c "import urllib, sys; \ - print urllib.quote(sys.argv[1], safe='')" 'p@$$w0rd' - -Python 3:: - - python3 -c "import urllib.parse, sys; \ - print(urllib.parse.quote(sys.argv[1], safe=''))" 'p@$$w0rd' - -Perl 5:: - - echo 'p@$$w0rd' | perl -MURI::Escape -wlne 'print uri_escape $_' - -Поддерживаемые протоколы и схемы URI ------------------------------------- -Схемы, которые могут быть использованы как sources -`````````````````````````````````````````````````` - -file - Может содержать путь к локальному файлу или директории. Файлы - архивируются при помощи ``tar`` и по умолчанию не сжимаются. Cжатие - архива может быть включено через переменную `compression`. Примеры:: - - /var/www/www-root/data - file:/var/www/html - file:///home/jhon/cool_stuff.txt - - Не используйте двойной слэш для этой схемы, используйте один или три - слэша. Двойной слэш означает наличие компонента Authority, это приводит - к неверной интерпретации адреса. - - См. также ``handler::tar`` - -mysql - URI содержит реквизиты для подключения к БД MySQL/MariaDB. Формат:: - - mysql://[username[:password]@]hostname[:port]/database - - С помощью утилиты ``mysqldump``\(1) формируется дамп в формате SQL. Файл - по умолчанию не сжимается, но сжатие может быть включено через переменную - `compression`. - - См. также ``handler::mysqldump`` - -postgres - Аналогично `mysql`, но для PostgreSQL. Для создания дампа используется - ``pg_dump``\(1) с расширением .psql. Файл по умолчанию не сжимается, но - сжатие может быть включено через переменную `compression`. - - См. также ``handler::pg_dump`` - -sqlite - Схема для баз данных SQLite. Мало отличается от `file`, добавлена для - наглядного обозначения, что источником является БД SQLite, а не иной файл. - - См. также ``handler::sqlite`` - -Схемы, которые могут быть использованы как targets -`````````````````````````````````````````````````` - -file - В контексте `targets` указывает директорию для сохранения бэкапов. В - массиве `targets` обязательно должен быть хотя бы один поинт со схемой - `file`. Этот поинт будет использован как основной и сохранён в переменные - ``__main_target`` (содержит URI) и ``__main_target_path`` (содержит - компонент URI path). Если в массиве присутствует несколько поинтов со - схемой `file`, то в качестве основного будет выбран первый по порядку - поинт. Все бэкапы первоначально будут сохраняться в директорию - ``__main_target_path`` и затем копироваться в другой таргет с помощью - соответствующего обработчика. В случае копировани из одной директории в - другую используется утилита ``cp``\(1). - - Избегайте указания в одном сценарии таргетов, которые ведут одновременно на - примонтированный к локальной машине сетевой диск и другое удалённое - хранилище. В таком случае архивы будут создаваться на сетевом диске и далее - снова копироваться по сети, что будет очень медленно. - - См. также ``handler::cp`` - -ftp - Не реализовано. - -sftp - Не реализовано. - -rsync - Не реализовано. - -s3 - Не реализовано. - -swift - Не реализовано. - -sj - Не реализовано. - -dav, davs - Не реализовано. - -Создание резервных копий ------------------------- - -boring-backup предполагает, что для всех резервных копий необходимо создавать -архивы. Поэтому вам нужно следить за тем, чтобы в локальном хранилище всегда -хватало дискового пространства для создания новых архивов. boring-backup также -не удаляет старые архивы и вам также надо позаботиться об удалении устаревших -резервных копий. Изменить это поведение можно с помощью пользовательских -функций в сценариях. В этом разделе речь пойдёт о поведении, которое -установлено по умолчанию. См. функции ``builtin_backup``, ``process_sources``, -``process_targets``. - -Базовая концепция строится на том, что существует несколько источников и -несколько точек назначения. Создаётся резервная копия источника и копируется в -точку назначения. - -Для всех источников в сценарии выполняется локальная резервная копия. После -этого локальная копия переносится в удалённые точки назначения, если они -заданы. Копирование локального бэкапа будет осуществлёно столько раз, сколько -точек назначения задано. В каждом сценарии обязательно должен быть задан хотя -бы один источник и одна точка назначения. При этом в списке точек назначения -обязательно должна присутствовать точка со схемой `file`. Именно в неё будут -сохранены архивы. - -Например, имеется следующий сценарий:: - - sources=( - mysql://wordpress:1jobrRRjtLYs@localhost/wordpress - file:///var/www/wordpress - ) - targets=( - file:///var/backup - ftp://jhon:%24t%D0%AFo%7C%5C%7C6@[fe80::5054:ff:fe24:382]:2021/backups/ - ) - -Здесь будут созданы архивы файлов и базы данных и сохранены в локальную -директорию /var/backup. Затем эти файлы будут переданы по протоколу FTP на -сервер fe80::5054:ff:fe24:382 в директорию /backups. Файлы из директории -/var/backup не будут удалены или перемещены. Если добавить дополнительную точку -назначения, то файлы из /var/backup будут скопированы и туда. Обратите -внимание, что boring-backup не выполняет повторного создания архивов для каждой -точки назначения — архивы создаются один раз и далее распространяются по всем -указанным назначениям. Если в массиве `targets` имеется несколько точек -назначения со схемой `file`, то архивы будут сохранены в первую по порядку -точку, а затем скопированы оттуда в остальные. - -Резервное копирование на удалённое хранилище --------------------------------------------- -FTP -``` -SFTP -```` -Rsync over SSH -`````````````` -Rsync with rsyncd -````````````````` -Amazon S3 -````````` -S3 compatible storage -````````````````````` -OpenStack Swift -``````````````` -Storj DCS -````````` -WebDAV -`````` -Переменные ----------- - -``boring-backup`` условно разделяет переменные на "внутренние" ("защищённые") и -"обычные". К "защищённым" переменным относятся все перемеменные, имена которых -начинаются с двух знаков подчёркивания, например: ``__main_target``. Все -остальные переменные считаются "обычными". - -Обычные переменные могут быть перезаписаны в пользовательском скрипте. -Защищённые переменные технически ничем от них не отличаются, однако не нужно -переопределять такие переменные в пользовательском скрипте, так как это может -привести к неожиданным ошибкам во время выполнения скрипта. Точно также очень -внимательно нужно относится к перезаписи обычных переменных — в обоих случаях -есть риск что-то поломать. - -``backups`` - Массив со списком созданных бэкапов. Содержит абсолютные пути к файлам. - Заполняется функциями-обработчиками `sources`. - - | Тип: массив - | Умолчание: () - -``errors`` - Массив с текстами ошибок. Заполняется функцией ``err`` с опцией ``-a``. - - | Тип: массив - | Умолчание: () - -``log_date_fmt`` - Формат даты в логе. См. ``date``\(1). - - | Тип: строка - | Умолчание: %d/%b/%Y:%H:%M:%S %z - -``name_prefix`` - Префикс имени файла. Может быть задан в скрипте. - - | Тип: строка - | Умолчание: имя_скрипта\_ - -``name`` - Соответствует имени архивируемой директории/файла полученного из `path` с - помощью утилиты ``basename``\(1). - - | Тип: строка - | Умолчание: нет - -``name_date_fmt`` - Дата, интерпретируемая утилитой ``date``\(1). - - | Тип: строка - | Умолчание: _%Y%m%d - -``name_suffix`` - Суффикс, добавляемый к имени файла перед расширением. Строка - интерпретируется утилитой ``date``. - - | Тип: строка - | Умолчание: -%H%M - -``name_ext`` - Расширение имени файла, соответствующее формату архива. - - | Тип: строка - | Умолчание: .tar.gz - -``tar_options`` - Опции ``tar``. См. ``tar``\(1). - - | Тип: строка - | Умолчание: -acf - -``tar_exclude`` - Список имён для опции ``tar`` ``--exclude``. - - | Тип: массив - | Умолчание: нет - -``compression`` - В контексте ``tar`` работа этой фичи базируется на опции ``tar`` - ``--auto-compress``. Переменная может принимать значения, в соответствии с - табилцей ниже. Если переменная имеет значение, отличное от перечисленныых, - то будет использован ``gzip``. Если переменная пуста или не задана, то - сжатие будет отключено. - - | Тип: строка - | Умолчание: нет - - ==================== ================ ======== - Значение compression Расширение файла Утилита - ==================== ================ ======== - gzip, gz .tar.gz gzip - tgz .tgz gzip - taz .taz gzip - compress, Z .tar.Z compress - taZ .taZ compress - bzip2, bz2 .tar.bz2 bzip2 - tz2 .tz2 bzip2 - tbz2 .tbz2 bzip2 - tbz .tbz bzip2 - lzip, lz .tar.lz lzip - lzma .tar.lzma lzma - tlz .tlz lzma - lzop, lzo .tar.lzo lzop - xz .tar.xz xz - zstd, zst .tar.zst zstd - tzst .tzst zstd - ==================== ================ ======== - -Функции -------- - -``backup`` - Пользовательская функция резервного копирования. Если она задана в скрипте, - то вызывается вместо ``builtin_backup``. - -``builtin_backup`` - Встроенная функция, запускает резервное копирвоание. Вызывает функции - ``process_source`` и ``process_target``. - -``err [-eao] MESSAGE`` - Печатает сообщение об ошибке MESSAGE на экран и в лог и выполняет обработку - ошибок. - - -e Выйти из скрипта с кодом выхода 1. - -a Добавить сообщение MESSAGE в массив ``errors``. - -o Вызвать функцию ``on_error``. - -``finalise`` - Пользовательская функция. Если задана, то запускается после функции - ``backup`` или ``builtin_backup()``. - -``gen_backup_name FILE_EXT`` - Печатает полученное имя файла в STDOUT. Имя является выводом команды - ``date`` и строки ``${prefix}${name}${date_fmt}${name_ext}``. - См. переменные ``name_prefix``, ``name``, ``name_date_fmt``, ``name_ext``. - -``try COMMAND`` - Функция выполняет команду COMMAND и в случае ненулевого кода выхода - вызывает ``err()`` с ключами ``-eao``. Ошибка при выполнении COMMAND - приведёт к вызову функции ``on_error``. Рекомендуется для использования в - качестве обёртки для команд правильное выполнение которых критично. - -``handler::tar URI`` - Архивация файлов с помощью ``tar``. - -``handler::mysqldump URI`` - Не реализовано. - -``handler::pg_dump URI`` - Не реализовано. - -``handler::sqlite URI`` - Не реализовано. - -``handler::cp URI`` - Копирует файлы в новое назначение с помощью ``cp``. - -``handler::ftp URI`` - Не реализовано. - -``handler::sftp URI`` - Не реализовано. - -``handler::rsync URI`` - Не реализовано. - -``handler::s3 URI`` - Не реализовано. - -``handler::sj URI`` - Не реализовано. - -``handler::swift URI`` - Не реализовано. - -``handler::dav URI`` - Не реализовано. - -``handler::davs URI`` - Не реализовано. - -``is_installed COMMAND`` - Проверяет установлена ли программа COMMAND. - -``is_function_set FUNCTION`` - Проверяет задана ли функция FUNCTION. - -``log [-p] MESSAGE`` - Печатает лог в файл ``__log_file`` (лог задаётся опцией --log-file) и на - экран, если указана опция ``-p``. Формат даты в логе можно изменить с - помощью переменной ``log_date_fmt``. - -``on_error`` - Пользовательская функция для обработки ошибок. Если задана, то запускается - при обработке ошибки в функции ``err`` с опцией ``-o``. - -``parse_uri URI`` - Парсер URI. Задаёт переменные ``scheme``, ``username``, ``password``, - ``hostname``, ``port``, ``path``, ``query``, ``fragment`` и выполняет - декодинг пароля, если он закодирован в percent code. - -``prepare`` - Пользовательская функция. Если задана, то запускается до функции ``backup`` - или ``builtin_backup``. - -``process_source URI`` - На основе схемы запускает соответсвующую функцию-обработчик. - -``process_target URI`` - На основе схемы запускает соответсвующую функцию-обработчик. - -``source_script FILE`` - Выполняет ``source`` пользовательского скрипта и проверяет его - корректность. Проверяется синтаксис Bash, наличие непустых массивов - `sources` и `targets`, наличие поинта `file` в `targets`. Во - вспомогательной функции ``validate_targets`` задаются значения переменных - ``__main_target`` и ``__main_target_path``. - -Использование функций в сценариях ---------------------------------- -Переменные окружения --------------------- - -``LIBRARY`` - Путь до библиотеки функций boring-backup. - -Примеры -------- -См. также ---------- - -``bash``\(1), ``tar``\(1), ``cp``\(1), ``date``\(1), ``mysqldump``\(1), -``pg_dump``\(1) - -RFC 3986 https://datatracker.ietf.org/doc/html/rfc3986 diff --git a/docs/manpages/boring-backup.ru.1.rst b/docs/manpages/boring-backup.ru.1.rst new file mode 100644 index 0000000..e226308 --- /dev/null +++ b/docs/manpages/boring-backup.ru.1.rst @@ -0,0 +1,640 @@ +:Title: boring_backup +:Title upper: BORING_BACKUP +:Subtitle: бэкап файлов и баз данных +:Author: ge +:Copyright: \(C) 2022, ge , GPLv3+ +:Date: 2022 May 15 +:Manual section: 1 +:Version: boring_backup 0.1.0 + +СИНТАКСИС +--------- + +**boring_backup** [-hVvs] [-c `файл`] [-l `файл`] [-p `файл`] файл ... + +ОПИСАНИЕ +-------- + +boring_backup - это расширяемая утилита для резервного копирования на основе +сценариев Bash. + +ОПЦИИ +----- + +-h, --help + Показать справку и выйти. + +-V, --version + Показать информацию о версии и выйти. + +-v, --verbose + Печатать подробный вывод. + +-c, --config=\ `файл` + Путь до файла конфигурации. См. **КОНФИГУРИРОВАНИЕ**. + +-l, --log-file=\ `файл` + Путь до файла лога. + +-s, --use-syslog + Записывать лог в syslog вместо файла. Эта опция несовместима с опцией + --log-file и переменной log_date_format. + +-p, --pid-file=\ `файл` + Путь до PID-файла. + +ОБЗОР +----- + +**boring_backup** можно рассматривать как фреймворк или библиотеку для создания +сценариев резервного копирования. Сценарии (скрипты) должны содержать валидный +код на Bash. + +Скрипты передаются в качестве аргументов **boring_backup** и исполняются в +цикле. Скрипты загружается с помощью команды ``source`` (см. ``bash``\(1) +п. SHELL BUILTIN COMMANDS). + +Простейший сценарий резервного копирования выглядит следующим образом:: + + sources=(file:/home/john) + targets=(file:/var/backups) + +Массивы `sources` и `targets` определяют источники и назначения соответственно. +Ресурсы из `sources` нужно сохранить в точку(и) назначения — `targets`. См. +больше примеров в разделе **ПРИМЕРЫ**. + +Все ресурсы записываются в формате URI. См. подробности в разделе +**СХЕМЫ URI** и в описании функции ``parse_uri`` ниже. + +Каждой схеме URI соответствует функция-обработчик, которая выполняет действие +с ресурсом. Например, URI со схемой `mysql` будет обработан функцией +``src_mysqldump``. + +Обработчики ресурсов для массива `sources` имеют в имени префикс ``src_``, а +обработчики для массива `targets` префикс ``tgt_``. См. описания обработчиков в +разделе **ВСТРОЕННЫЕ ФУНКЦИИ**. + +Поскольку скрипты резервного копирования это обычные скрипты Bash, то здесь +применимы все те же приёмы. В **boring_backup** предусмотрено несколько +специальных пользовательских функций и ряд специальных переменных. См. разделы +**ПЕРЕМЕННЫЕ** и **ПОЛЬЗОВАТЕЛЬСКИЕ ФУНКЦИИ**. + +СХЕМЫ URI +--------- + +file + Схема для указания ресурсов в файловой системе. Примеры записи:: + + /var/www/www-root/data + file:/var/www/html + file:///home/jhon/cool_stuff.txt + + Не используйте двойной слэш для этой схемы, используйте один или три + слэша. Двойной слэш означает наличие компонента Authority, который здесь + не обрабатывается, что приводит к неверной интерпретации адреса. + + Схема `file` может быть указана как в массиве `sources`, так и `targets`. + + В массиве `sources` схема обрабатывается функцией ``src_tar``. Файлы + архивируются и сжимаются согласно значению переменной $compression. + + В контексте `targets` `file` указывает директорию для сохранения бэкапов. + В `targets` обязательно должен быть хотя бы один URI со схемой `file`. + Этот URI будет использован как основной и сохранён в переменные + $__main_target (содержит URI целиком) и $__main_target_path (содержит + компонент URI path). + + Если в массиве `targets` присутствует несколько URI со схемой `file`, то в + качестве основного будет выбран первый по порядку URI. Все бэкапы + первоначально будут сохраняться туда и затем копироваться в другой таргет с + помощью соответствующего обработчика. В случае копирования бэкапов между + директориями будет использован обработчик ``tgt_cp``. + + Избегайте указания в одном сценарии таргетов, которые ведут одновременно на + примонтированный к локальной машине сетевой диск и на другое удалённое + хранилище. В таком случае архивы будут создаваться на сетевом диске и далее + снова копироваться по сети. Это может быть очень медленно. + + См. также ``src_tar``, ``tgt_cp``. + +mysql, mariadb + URI содержит реквизиты для подключения к БД MySQL/MariaDB. Иногда такой + формат ещё называют DSN (data source name):: + + mysql://[username[:password]@]hostname[:port]/database + + См. также ``src_mysqldump``. + +postgres + Аналогично `mysql`, но для PostgreSQL. Для создания дампа используется + ``pg_dump``. Расширение несжатых файлов — .psql. + + См. также ``src_pg_dump`` + +sqlite [НЕ РЕАЛИЗОВАН] + Схема для баз данных SQLite. Мало отличается от `file`, добавлена для + наглядного обозначения, что ресурсом является БД SQLite, а не иной файл. + + См. также ``src_sqlite`` + +s3 + Схема Simple Storage Service (S3). Пример использования:: + + s3://bucket + + URI содержит только схему и путь. Все остальные реквизиты для подключения к + хранилищу задаются через конфигурационный файл или отдельные переменные. + См. ``tgt_s3cmd`` ниже. + + В S3 важно наличие слэшей в конце URI. Пример:: + + s3://bucket/backups (1) + s3://bucket/backups/ (2) + + В случае (1) при загрузке файла с именем dump.sql.gz файл будет сохранён + как, s3://bucket/backups, а не s3://bucket/backups/dump.sql.gz. Чтобы + такого не происходило следует добавлять слэш в конце (2), чтобы обозначить, + что backups в данном случае является директорией. Обработчик ``tgt_s3cmd`` + сам добавляет слэш в конец URI, если его нет. + + Ещё одной особенностью является то, что в URI можно записывать + несуществующие в хранилище директории. Они будут созданы при загрузке + файла. Таким образом можно делать следующее:: + + today="$(date +%d_%b_%Y)" + targets=( + file:///var/backups + s3://bucket/backups/$today + ) + + Бэкап будет сохранён как:: + + s3://bucket/backups/22_Aug_2022/dump.sql.gz + +ПЕРЕМЕННЫЕ +---------- + +``sources`` + | Тип: массив + | Умолчание: нет + + Массив из строк с URI ресурсов, резервную копию которых нужно выполнить. + Массив обязатально должен быть непустой. Пример:: + + sources=( + file:///home/john + mysql://user:password@localhost:3306/database + ) + + См. **СХЕМЫ URI**. + +``targets`` + | Тип: массив + | Умолчание: нет + + Массив из строк URI хранилищ, куда необходимо доставить резервные копии. + Обязательно должен содержать хотя бы один URI со схемой `file`. Пример:: + + targets=( + file:///var/backups + s3://bucket/backups/$today + ) + + См. **СХЕМЫ URI**. + +``backups`` + | Тип: массив + | Умолчание: нет + + Массив со списком созданных бэкапов. Содержит абсолютные пути к файлам. + Заполняется функциями-обработчиками `sources` при успешном создании бэкапа. + + При использовании пользовательской функции ``backup`` массив следует + заполнять вручную для корректного вызова функций-обработчиков для + `targets`. См. пример в разделе **ПРИМЕРЫ**. + +``compression`` + | Тип: строка + | Умолчание: нет + + Тип компрессии для сжатия файлов. + + В контексте ``tar`` работа этой фичи базируется на опции ``tar`` + ``--auto-compress``. Переменная может принимать значения, в соответствии с + табилцей ниже. Если переменная не задана, имеет пустое или отличное от + перечисленныых в таблице значение, то сжатие будет отключено. + + Для файлов, которые не являются архивами tar (не созданы функцией + ``src_tar``) расширение из таблицы будет добавлено к оригинальному + рассширению файла. + + ==================== ========== =================== ===================== + Значение compression Утилита Расширение файла Расширение архива tar + ==================== ========== =================== ===================== + gzip, gz gzip .gz .tar.gz + tgz gzip .gz .tgz + taz gzip .gz .taz + bzip2, bz2 bzip2 .bz2 .tar.bz2 + tz2 bzip2 .bz2 .tz2 + tbz2 bzip2 .bz2 .tbz2 + tbz bzip2 .bz2 .tbz + lzip, lz lzip .lz .tar.lz + lzma lzma .lzma .tar.lzma + tlz lzma .lzma .tlz + lzop, lzo lzop .lzo .tar.lzo + xz xz .xz .tar.xz + zstd, zst zstd .zst .tar.zst + tzst zstd .zst .tzst + ==================== ========== =================== ===================== + +``log_date_format`` + | Тип: строка + | Умолчание: %d/%b/%Y:%H:%M:%S %z + + Формат даты в логе. См. ``date``. См. **КОНФИГУРИРОВАНИЕ**. + +``name`` + | Тип: строка + | Умолчание: нет + + Соответствует имени архивируемой директории/файла полученного из `path` с + помощью утилиты ``basename``. Вычисляется автоматически, не может быть + задан в скрипте. + +``name_date_format`` + | Тип: строка + | Умолчание: _%Y%m%d + + Дата, интерпретируемая утилитой ``date``. Полученная строка используется в + имени файла бэкапа. + +``name_prefix`` + | Тип: строка + | Умолчание: имя_скрипта\_ + + Префикс имени файла. Может быть задан в скрипте. Значение по-умолчанию + состоит из имени скрипта резервного копирования и знака "_". + +``name_suffix`` + | Тип: строка + | Умолчание: -%H%M + + Суффикс, добавляемый к имени файла перед расширением. Строка + интерпретируется утилитой ``date``. + +``name_ext`` + | Тип: строка + | Умолчание: нет + + Расширение имени файла соответствующее формату архива. Вычисляется + автоматически и не может быть задано в скрипте резервного копирования. + +``mysqldump_options`` + | Тип: строка + | Умолчание: нет + + Опции для утилиты ``mysqldump``. + +``pg_dump_options`` + | Тип: строка + | Умолчание: нет + + Опции для утилиты ``pg_dump``. + +``s3cmd_config`` + | Тип: строка + | Умолчание: нет + + Путь к файлу конфигурации утилиты ``s3cmd``. См. соответствующую страницу + страницу справки ``s3cmd``\(1). + +``s3cmd_options`` + | Тип: строка + | Умолчание: --preserve --quiet --no-progress + + Опции для утилиты ``s3cmd``. + +``s3_access_key`` + | Тип: строка + | Умолчание: нет + + Значение для опции ``s3cmd`` ``--access_key``. + +``s3_secret_key`` + | Тип: строка + | Умолчание: нет + + Значение для опции ``s3cmd`` ``--secret_key``. + +``s3_host`` + | Тип: строка + | Умолчание: нет + + Значение для опции ``s3cmd`` ``--host``. Должен содержать адрес эндпоинта + для доступа к объектному хранилищу без схемы (только домен). + +``s3_host_bucket`` + | Тип: строка + | Умолчание: нет + + Значение для опции ``s3cmd`` ``--host-bucket``. Шаблон для `virtual-hosted + style` адреса бакета. Для `path style` это значение должно совпадать с + `s3_host`. + +``s3_region`` + | Тип: строка + | Умолчание: na + + Значение для опции ``s3cmd`` ``--region`` или ``--bucket_location``. + Переменная необязательна к заполнению, так как некоторые S3-совместимые + хранилища не используют регионы. В таком случае будет использован фиктивный + регион `na` (not available). + +``tar_options`` + | Тип: строка + | Умолчание: -acf + + Опции ``tar``. См. ``tar``\(1). + +``tar_exclude`` + | Тип: массив + | Умолчание: нет + + Список имён для опции ``tar`` ``--exclude``. Пример:: + + tar_exclude=( foo bar ) + + Массив разворачивается в строку вида '--exclude=foo --exclude=bar' и + подставляется в строку опций ``tar``. + +ВСТРОЕННЫЕ ФУНКЦИИ +------------------ + +``set_compression`` + Определить утилиту и расширение файла на основе строки. + + При вызове этой функции в качестве аргумента ей передаётся значение + переменной $compression. На её основе функция задаёт переменные $cmpr_ext, + $tar_ext и $cmpr_cmd. $tar_ext используется только в функции ``src_tar``, + другие две в функции ``compress_file``. + + См. таблицу в описании переменной $compression. + +``compress_file`` + Выполнить сжатие файла и напечатать имя сжатого файла. Удалить несжатый + файл. + + Внутри себя вызывает функцию ``set_compression`` для определния утилиты и + расширения файла. Если переменная $compression не задана, то сжатие не + производится. + +``gen_backup_name`` + Функция печатает строку, содержащую имя файла. Имя генерируется с помощью + команды:: + + date +"${name_prefix}${name}${name_date_format}${name_suffix}${name_ext}" + + $name_ext принимает значение, переданное в качестве аргумента. См. описания + переменных выше и также в исходном коде функции. + +``handle_error`` + Обработать ошибку. Делает проверку и вызывает пользовательскую функцию + ``on_error``. + +``is_function_set`` + Проверить задана ли функция и вернуть код выхода. Применима в конструкции + **if**. + +``is_installed`` + Проверить установлена ли утилита и выйти из скрипта при неудаче. + +``log`` + Записать сообщение в лог. + + Синтаксис: **log** [-pV] сообщение + + | Опции: + | -p напечатать сообщение также на экран (STDOUT) + | -V игнорировать опцию **--verbose** + + Функция записывает сообщения в лог-файл. Лог-файл может быть задан через + опцию **--log-file**, имя этого файла будет сохранено в переменную + $__log_file. **log** перед записью в файл удаляет из текста + escape-последовательности ANSI. + + Формат лога '[%s] %s\\n', где первое вхождение %s заменяется на дату, а + второе на текст сообщения. Формат даты задаётся через переменную + $log_date_format (см. описание выше). + + Все сообщения, отправляемые в **log** печатаются в терминал, если + **boring_backup** запущен с опцией **--verbose**. Избежать этого можно + вызвав логгер с опцией **-V**. + +``parse_uri`` + Разобрать строку URI на компоненты. Парсер соответствует RFC 3986. + + Функция задаёт переменные: $scheme, $username, $password, $hostname, + $port, $path, $query, $fragment + + Поддерживается работа с IPv6 адресами, если они заключены в квадратные + скобки. + + Поддерживается работа с паролями, содержащими зарезервированные символы. + Такие пароли необходимо закодировать в так называемый percent code. + **parse_uri** возвращает в переменной $password уже раскодированный пароль. + Ниже представлены примеры кодирования паролей в командной строке. + + Python 2:: + + python2 -c "import urllib, sys; \ + print urllib.quote(sys.argv[1], safe='')" 'p@$$w0rd' + + Python 3:: + + python3 -c "import urllib.parse, sys; \ + print(urllib.parse.quote(sys.argv[1], safe=''))" 'p@$$w0rd' + + Perl 5:: + + echo 'p@$$w0rd' | perl -MURI::Escape -wlne 'print uri_escape $_' + +``remove_if_empty`` + Удалить файл, если он пустой. + +``src_mysqldump`` + Получить SQL дамп базы данных MariaDB или MySQL. + + Дамп выполняется с помощью утилиты ``mysqldump``. Ей можно передать + дополнительные опции через переменную $mysqldump_options (см. описание + выше). + +``src_pg_dump`` + Получить SQL дамп базы данных PostgreSQL. + + Дамп выполняется с помощью утилиты ``pg_dump``. Ей можно передать + дополнительные опции через переменную $pg_dump_options (см. описание выше). + +``src_tar`` + Создать архив tar. Используемые переменные: $tar_options, $tar_exclude. + +``tgt_cp`` + Скопировать файлы в новое назначение. + + Эта функция вызывается при каждом запуске резервного копирования и + обрабатывает схему `file` в массиве `targets`. Если в массиве есть + несколько URI со схемой `file`, то ``tgt_cp`` скопирует файлы из первой + директории во все остальные. + +``tgt_s3cmd`` + Загрузить файлы в S3-совместимое объектное хранилище с помощью ``s3cmd``. + + Реквизиты для подключения к хранилищу можно передать в конфигурационном + файл ``s3cmd`` указав путь до него в переменной $s3cmd_config. + + Помимо этого реквизиты можно записать прямо в скрипт резервного копирования + в переменные: $s3_access_key, $s3_secret_key, $s3_host, $s3_host_bucket, + $s3_region. Дополнительные опции для ``s3cmd`` можно задать через + $s3cmd_options. См. описание всех переменных в разделе **ПЕРЕМЕННЫЕ**. + +``builtin_backup`` + Запустить функции ``process_source`` и ``process_target`` для каждого URI + из соответствующих массивов. + +``process_source`` + Запустить соответствующую функцию-обработчик для массива `sources`. + +``process_target`` + Запустить соответствующую функцию-обработчик для массива `targets`. + +``source_script`` + Выполнить проверки и загрузить (``source``) пользовательский скрипт + резервного копирования. + +``validate_sources`` + Проверить массив `sources`. Выполняются проверки на пустой массив и на + использование правильного набора схем URI. + +``validate_targets`` + Проверить массив `targets`. Выполняются проверки на пустой массив и на + использование правильного набора схем URI. Для успешной проверки массив + должен содержать хотя бы одну строку URI со схемой `file`. + +ПОЛЬЗОВАТЕЛЬСКИЕ ФУНКЦИИ +------------------------ + +``prepare`` + В этой функции можно запрограммировать действия, необходимые перед + выполнением бэкапа. + +``backup`` + Эта функция позволяет переопределить логику резервной копии. + + Если функция **backup** объявлена, то она будет вызвана вместо встроенной + функции **builtin_backup**. + + Используйте **backup**, когда вам необходимо использовать собственную + логику вместо заложенной разработчиком **boring_backup**. + +``finalise`` + В этой функции можно запрограммировать действия, выполнение которых + необходимо после выполнения бэкапа. + + Пример использования: удаление локальных резервных копий после их загрузки + на удалённое хранилище. + +``on_error`` + В этой функции можно запрограммировать действия при возникновении ошибки. + В качестве аргумента для неё передаётся строка с текстом произошедшей + ошибки. + + Пример использования: отправка отчёта об ошибке на имейл. + +КОНФИГУРИРОВАНИЕ +---------------- + +По умолчанию **boring_backup** пытается прочитать файл по пути +~/.config/boring_backup. Также путь до конфигурационного файла можно +передать через опцию **--config**. + +Конфиг имеет те же свойства, что и скрипт резервного копированиия, но +загружается всякий раз после загрузки скрипта резевного копирования. Таким +образом он может переопределять заданные в скриптах функции и переменные. + +Параметры, которые нежелательно определять в скриптах резервного копирования: + +* log_date_format +* name_date_format + +Переопределять их следует через конфигурационный файл. + +ПРИМЕРЫ +------- + +Скрипт для бэкапа сайта со сжатием файлов с помощью ``xz``, загрузкой в S3 и +удалением локальных бэкапов после загрузки:: + + compression=xz + s3cmd_config=~/.s3cfg + sources=( + /var/www/www-data/example.com + mysql://example_user:Smk3mVH2@localhost/example_db + ) + targets=( + /var/backups/example.com/ + s3://mybucket/backups/example-com + ) + finalise() { + log -p "\tClean up local backups" + log -p "\tFiles to delete: ${backups[@]}" + rm -- "${backups[@]}" + log -p "\tLocal backups deleted" + } + +Бэкап домашней папки на примонтированный в /mnt/backups сетевой диск:: + + compression=xz + tar_exclude=(.cache) + sources=(/home/john) + targets=(/mnt/backups) + +Бэкап приложения Gitea:: + + sources=(.) # just pass validation + targets=(.) + today="$(date +%d_%b_%Y)" + s3cmd_config=~/.s3cfg + prepare() { + systemctl stop gitea.service + sleep 5 + } + backup() { + log -p "Dumping Gitea" + su -c "/usr/local/bin/gitea dump -c /etc/gitea/app.ini \ + -f /home/git/.cache/gitea_dump.zip" - git 2>> "$__log_file" + backups+=(/home/git/.cache/gitea_dump.zip) + tgt_s3cmd s3://mybucket/backups/gitea-$today + } + finalise() { + systemctl start gitea.service + rm "${backups[@]}" + } + +ОКРУЖЕНИЕ +--------- + +LIBRARY + Если задана переменная окружения $LIBRARY, то её значение будет + использовано в качестве пути для поиска библиотеки **boring_backup**. + +СМ. ТАКЖЕ +--------- + +``bash``\(1), ``date``\(1), ``cp``\(1), ``tar``\(1), ``mysqldump``\(1), +``pg_dump``\(1), ``s3cmd``\(1) + +RFC 3986 https://datatracker.ietf.org/doc/html/rfc3986 + +ОШИБКИ +------ + +https://git.nxhs.cloud/ge/boring_backup/issues diff --git a/src/boring-backup b/src/boring_backup similarity index 58% rename from src/boring-backup rename to src/boring_backup index 6512a78..a7e2f7a 100755 --- a/src/boring-backup +++ b/src/boring_backup @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# boring-backup -- backup files and databases. +# boring_backup -- backup files and databases. # Copyright (c) 2022 ge # # This program is free software: you can redistribute it and/or modify @@ -16,12 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -__version='0.1.0' -__config= -__verbose= -__log_file='./log.txt' -__pid_file='/tmp/boring-backup.pid' - +VERSION='0.1.0' LIBRARY="${LIBRARY:-./lib}" # Source library @@ -30,24 +25,47 @@ if [ -f "$LIBRARY/lib.sh" ]; then . "$LIBRARY/lib.sh" else echo "Error: Cannot source library $LIBRARY/lib.sh: No such file" >&2 + __exit=1 fi print_help() { - cat <<- EOF -Backup files and databases. + printf << EOF \ +"USAGE: -Usage: $0 [-cvlphV] FILES.. + $0 [-hVvs] [-c \033[4mfile\033[0m] [-l \033[4mfile\033[0m] \ +[-p \033[4mfile\033[0m] file ... -Options: - -c, --config config file. - -v, --verbose verbose output. - -l, --log-file log file [default: $__log_file] - -p, --pid-file PID file [default: $__pid_file] - -h, --help print this help messagea and exit. - -V, --version print version and exit. +OPTIONS: -Environment: - LIBRARY path to bb library [current: $LIBRARY] + -h, --help + Print this help message and exit. + + -V, --version + Print version and exit. + + -v, --verbose + Enable verbose output. + + -c, --config=\033[4mfile\033[0m + Path to configuration file. Default: ~/.config/boring_backup + Configuration file actually is Bash-script similar to backup + script. See boring_backup(1) CONFIGURATION. + + -l, --log-file=\033[4mfile\033[0m + Log file. + + -s, --use-syslog + Write log into syslog via logger instead of log file. This option + is incompatible with --log-file option and log_date_format variable. + + -p, --pid-file=\033[4mfile\033[0m + PID file. Default: /tmp/boring_backup.pid + +ENVIRONMENT: + + LIBRARY Path to boring_backup library. Current path is + $LIBRARY +" EOF } @@ -88,33 +106,70 @@ for args in "$@"; do shift case "$args" in --*) - set -- "$@" "$args";; # save long options + set -- "$@" "$args" # save long options + ;; -*) - args="$(echo "${args:1}" | grep -o . | xargs -I {} echo -n '-{} ')" + args="$(echo "${args:1}" | + grep -o . | xargs -I {} echo -n '-{} ')" # shellcheck disable=SC2086 - set -- "$@" $args;; # 'args' must be unquoted! + set -- "$@" $args # 'args' must be unquoted! + ;; *) - set -- "$@" "$args";; # save positional arguments + set -- "$@" "$args" # save positional arguments + ;; esac done # Final arguments parser while (( "$#" )); do case "$1" in + -h|--help) + print_help + exit 0 + ;; + -V|--version) + echo "$VERSION" + exit 0 + ;; + -v|--verbose) + verbose_output=1 + shift + ;; -c|--config|--config=*) - optval "$1" "$2"; __config="$val"; shift "$sft";; - -v|--verbose) __verbose=1; shift;; + optval "$1" "$2" + bb_config="$val" + shift "$sft" + ;; -l|--log-file|--log-file=*) - optval "$1" "$2"; __log_file="$val"; shift "$sft";; + optval "$1" "$2" + log_file="$val" + shift "$sft" + ;; + -s|--use-syslog) + use_syslog=1 + shift + ;; -p|--pid-file|--pid-file=*) - optval "$1" "$2"; __pid_file="$val"; shift "$sft";; - -h|--help) print_help; exit 0;; - -V|--version) echo "$__version"; exit 0;; - -*) echo "Error: Unknown option: $1" >&2; exit 1;; - *) __args+=("$1"); shift;; # Save positional args + optval "$1" "$2" + pid_file="$val" + shift "$sft" + ;; + -*) + echo "Error: Unknown option: $1" >&2 + exit 1 + ;; + *) + __args+=("$1") # Save positional args + shift + ;; esac done +# -- Set defaults -- # +log_file="${log_file:-./log.txt}" +bb_config="${bb_config:-$HOME/.config/boring_backup}" +pid_file="${pid_file:-/tmp/boring_backup.pid}" + # Exit if library is not loaded if ! declare -F -- log > /dev/null 2>&1; then exit 1 @@ -127,20 +182,20 @@ fi log -V "Backup STARTED" # Check PID file -if [ -e "$__pid_file" ]; then +if [ -e "$pid_file" ]; then # shellcheck disable=SC2009 # shellcheck disable=SC2143 - if [ -z "$(ps ax -o pid | grep "$(cat "$__pid_file")")" ]; then - log -p "Process $(cat "$__pid_file") died." >&2 - rm "$__pid_file" + if [ -z "$(ps ax -o pid | grep "$(cat "$pid_file")")" ]; then + log -p "Process $(cat "$pid_file") died." >&2 + rm "$pid_file" else - echo "Process $(cat "$__pid_file") still running." >&2 + echo "Process $(cat "$pid_file") still running." >&2 exit 1 fi fi # Touch PID file -echo "$$" > "$__pid_file" +echo "$$" > "$pid_file" # Scripts counter. __count="${#__args[@]}" # count @@ -149,9 +204,12 @@ __i=1 # iterator # Startup log. date +'Start: %d %b %Y %T %z' log -p "Library path: $LIBRARY" -log -p "Log file: $__log_file" -log -p "Configuration file:" \ - "$([ "$__config" ] || echo not specified && echo "$__config")" +log -p "Log file: $log_file" +log -p "Configuration file:" "$(\ + if [ -f "$bb_config" ]; + then echo "$bb_config"; + else echo not specified; + fi)" log -p "Total scripts: $__count" log "Scripts to process (${__count}): ${__args[*]}" @@ -171,7 +229,7 @@ for script in "${__args[@]}"; do # Config can ovewrite script functions and variables # shellcheck source=/dev/null - [ -n "$__config" ] && . "$__config" + [ -f "$bb_config" ] && . "$bb_config" # --- Run user script -- # @@ -208,17 +266,10 @@ for script in "${__args[@]}"; do # Unset user defined variables unset compression - unset name_prefix - unset name_date_format - unset s3cmd_options - unset s3cmd_config - unset s3_access_key - unset s3_secret_key - unset s3_region - unset s3_host - unset s3_host_bucket - unset tar_options - unset tar_exclude + unset name_prefix name_date_format name_prefix name_suffix + unset s3cmd_config s3cmd_options + unset s3_access_key s3_secret_key s3_region s3_host s3_host_bucket + unset tar_options tar_exclude unset mysqldump_options unset pg_dump_options done @@ -227,4 +278,6 @@ echo -e "\nBackup [Done]" log -V "Backup FINISHED" # Remove PID file -rm "$__pid_file" +rm "$pid_file" + +exit "${__exit:-0}" diff --git a/src/lib/handlers/targets/s3cmd.sh b/src/lib/handlers/targets/s3cmd.sh index 2be2cbd..097a865 100644 --- a/src/lib/handlers/targets/s3cmd.sh +++ b/src/lib/handlers/targets/s3cmd.sh @@ -25,7 +25,6 @@ tgt_s3cmd() { log "Run handler ${FUNCNAME[0]}()" local uri - local src_path uri="$1" @@ -44,7 +43,7 @@ tgt_s3cmd() { # Set s3cmd comand. See s3cmd(1) if [ -n "$s3cmd_config" ]; then # Use configuration file - # shellcheck disable=SC2154 + # shellcheck disable=SC2154,SC2086 set -- s3cmd $s3cmd_options --config "$s3cmd_config" \ put "${backups[@]}" "$uri" elif [ -n "$s3_access_key" ] && \ @@ -54,7 +53,7 @@ tgt_s3cmd() { then s3_region="${s3_region:-na}" # fallback to 'no available' region # Use parameters provided from backup script - # shellcheck disable=SC2154 + # shellcheck disable=SC2154,SC2086 set -- s3cmd $s3cmd_options \ --access_key="$s3_access_key" \ --secret_key="$s3_secret_key" \ @@ -74,6 +73,7 @@ tgt_s3cmd() { # ^^^ hide secret_key from output ^^^ # Upload backups + # shellcheck disable=SC2154 if "$@" 2>> "$__log_file"; then : # Success else