<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Scripting on /dev/random</title>
    <link>https://log.2027a.net/tags/scripting/</link>
    <description>Recent content in Scripting on /dev/random</description>
    <image>
      <title>/dev/random</title>
      <url>https://log.2027a.net/img/cover.jpg</url>
      <link>https://log.2027a.net/img/cover.jpg</link>
    </image>
    <generator>Hugo -- 0.149.1</generator>
    <language>fr</language>
    <lastBuildDate>Sat, 22 Mar 2025 09:03:28 -0400</lastBuildDate>
    <atom:link href="https://log.2027a.net/tags/scripting/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Sauvegardes avec rsync</title>
      <link>https://log.2027a.net/posts/sauvegardes-avec-rsync/</link>
      <pubDate>Sat, 22 Mar 2025 09:03:28 -0400</pubDate>
      <guid>https://log.2027a.net/posts/sauvegardes-avec-rsync/</guid>
      <description>&lt;p&gt;J&amp;rsquo;ai récement eu quelques petits problèmes avec un serveur (une sombre histoire de
RAM corrompue), et ça a été l&amp;rsquo;occasion de peaufiner un &lt;em&gt;helper script&lt;/em&gt; pour &lt;code&gt;rsync&lt;/code&gt;
que j&amp;rsquo;utilise depuis un moment.&lt;/p&gt;
&lt;p&gt;Ce script est loin d&amp;rsquo;être parfait (en particulier, il ne vérifie pas le contenu des
source &amp;amp; exclude files), mais c&amp;rsquo;est une bonne base pour un système de sauvegarde
robuste.&lt;/p&gt;
&lt;p&gt;Ici, je m&amp;rsquo;en sers en conjonction avec &lt;a href=&#34;https://docs.duplicati.com/&#34;&gt;duplicati&lt;/a&gt; pour
une sauvegarde automatique du contenu de mes serveurs et de mon laptop, mais vous
pourriez l&amp;rsquo;utiliser autrement. Vous le trouverez
&lt;a href=&#34;https://github.com/isingasimplesong/duct-tape/blob/main/rsync_backup.sh&#34;&gt;dans mon repo &lt;em&gt;duct-tape&lt;/em&gt; sur github&lt;/a&gt;&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>J&rsquo;ai récement eu quelques petits problèmes avec un serveur (une sombre histoire de
RAM corrompue), et ça a été l&rsquo;occasion de peaufiner un <em>helper script</em> pour <code>rsync</code>
que j&rsquo;utilise depuis un moment.</p>
<p>Ce script est loin d&rsquo;être parfait (en particulier, il ne vérifie pas le contenu des
source &amp; exclude files), mais c&rsquo;est une bonne base pour un système de sauvegarde
robuste.</p>
<p>Ici, je m&rsquo;en sers en conjonction avec <a href="https://docs.duplicati.com/">duplicati</a> pour
une sauvegarde automatique du contenu de mes serveurs et de mon laptop, mais vous
pourriez l&rsquo;utiliser autrement. Vous le trouverez
<a href="https://github.com/isingasimplesong/duct-tape/blob/main/rsync_backup.sh">dans mon repo <em>duct-tape</em> sur github</a></p>
<h2 id="le-fichier-de-configuration">Le fichier de configuration</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-txt" data-lang="txt"><span class="line"><span class="cl">BACKUP_MOUNT_POINT=&#34;/path/to/mountpoint&#34;
</span></span><span class="line"><span class="cl">BACKUP_DESTINATION=&#34;/path/to/mointpoint/and/final/destination&#34;
</span></span><span class="line"><span class="cl">BACKUP_SOURCE_FILE=&#34;/home/user/.config/rsync_source.list&#34;
</span></span><span class="line"><span class="cl">EXCLUDE_FILE=&#34;/home/user/.config/rsync_exclude.list&#34;
</span></span><span class="line"><span class="cl">BACKUP_LOG_FILE=&#34;/home/user/.local/logs/rsync_backup.log&#34;
</span></span><span class="line"><span class="cl">LOGROTATE_MAX_SIZE=2  # en Mo
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Notifications (facultatif)
</span></span><span class="line"><span class="cl">PUSHOVER_API_TOKEN=&#34;xxx-xxx-xxx&#34;
</span></span><span class="line"><span class="cl">PUSHOVER_USER_KEY=&#34;xxx-xxx-xxx&#34;
</span></span><span class="line"><span class="cl">NOTIFY_TITLE=&#34;Rsync Backup&#34;
</span></span></code></pre></div><h2 id="le-script">Le script</h2>
<p><code>rsync_backup.sh</code> :</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="cp">#!/usr/bin/env bash
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1"># Rsync backup script with dry-run, log rotation, locking, and pushover notification (on failure only)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">set</span> -euo pipefail
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">CONF</span><span class="o">=</span><span class="s2">&#34;</span><span class="si">${</span><span class="nv">RSYNC_CONF</span><span class="k">:-</span><span class="nv">$HOME</span><span class="p">/.config/rsync_backup.conf</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">LOCKFILE</span><span class="o">=</span><span class="s2">&#34;/tmp/rsync_backup.lock&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">DRY_RUN</span><span class="o">=</span><span class="m">0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">usage<span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    cat <span class="s">&lt;&lt;EOF
</span></span></span><span class="line"><span class="cl"><span class="s">Usage: $0 [--dry-run] [--help]
</span></span></span><span class="line"><span class="cl"><span class="s">
</span></span></span><span class="line"><span class="cl"><span class="s">Options:
</span></span></span><span class="line"><span class="cl"><span class="s">  --dry-run        Simulate the rsync backup without modifying any files
</span></span></span><span class="line"><span class="cl"><span class="s">  --help, -h       Show this help message and exit
</span></span></span><span class="line"><span class="cl"><span class="s">
</span></span></span><span class="line"><span class="cl"><span class="s">Description:
</span></span></span><span class="line"><span class="cl"><span class="s">  This script performs an rsync-based backup based on paths listed in a source file.
</span></span></span><span class="line"><span class="cl"><span class="s">  It supports log rotation, pushover notifications (only on failure), and ensures only one instance runs at a time.
</span></span></span><span class="line"><span class="cl"><span class="s">
</span></span></span><span class="line"><span class="cl"><span class="s">Configuration:
</span></span></span><span class="line"><span class="cl"><span class="s">  The script expects a configuration file exporting the following variables:
</span></span></span><span class="line"><span class="cl"><span class="s">
</span></span></span><span class="line"><span class="cl"><span class="s">    BACKUP_MOUNT_POINT     # mount point where backup destination is available
</span></span></span><span class="line"><span class="cl"><span class="s">    BACKUP_DESTINATION     # destination directory for rsync backup
</span></span></span><span class="line"><span class="cl"><span class="s">    BACKUP_SOURCE_FILE     # file listing source paths to back up (one per line)
</span></span></span><span class="line"><span class="cl"><span class="s">    EXCLUDE_FILE           # rsync exclude patterns (one per line)
</span></span></span><span class="line"><span class="cl"><span class="s">    BACKUP_LOG_FILE        # path to log file
</span></span></span><span class="line"><span class="cl"><span class="s">    LOGROTATE_MAX_SIZE     # max log file size in MB before rotation
</span></span></span><span class="line"><span class="cl"><span class="s">
</span></span></span><span class="line"><span class="cl"><span class="s">  Optional (for pushover notifications):
</span></span></span><span class="line"><span class="cl"><span class="s">
</span></span></span><span class="line"><span class="cl"><span class="s">    PUSHOVER_USER_KEY
</span></span></span><span class="line"><span class="cl"><span class="s">    PUSHOVER_API_TOKEN
</span></span></span><span class="line"><span class="cl"><span class="s">    NOTIFY_TITLE           # (optional) title shown in push notifications
</span></span></span><span class="line"><span class="cl"><span class="s">
</span></span></span><span class="line"><span class="cl"><span class="s">Default configuration file path: \$HOME/.config/rsync_backup.conf
</span></span></span><span class="line"><span class="cl"><span class="s">Example configuration file : https://log.2027a.net/posts/sauvegardes-avec-rsync/#le-fichier-de-configuration
</span></span></span><span class="line"><span class="cl"><span class="s">
</span></span></span><span class="line"><span class="cl"><span class="s">EOF</span>
</span></span><span class="line"><span class="cl">    <span class="nb">exit</span> <span class="m">0</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Parse arguments</span>
</span></span><span class="line"><span class="cl"><span class="k">while</span> <span class="o">[[</span> <span class="nv">$#</span> -gt <span class="m">0</span> <span class="o">]]</span><span class="p">;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="s2">&#34;</span><span class="nv">$1</span><span class="s2">&#34;</span> in
</span></span><span class="line"><span class="cl">    --dry-run<span class="o">)</span>
</span></span><span class="line"><span class="cl">        <span class="nv">DRY_RUN</span><span class="o">=</span><span class="m">1</span>
</span></span><span class="line"><span class="cl">        <span class="nb">shift</span>
</span></span><span class="line"><span class="cl">        <span class="p">;;</span>
</span></span><span class="line"><span class="cl">    --help <span class="p">|</span> -h<span class="o">)</span>
</span></span><span class="line"><span class="cl">        usage
</span></span><span class="line"><span class="cl">        <span class="p">;;</span>
</span></span><span class="line"><span class="cl">    *<span class="o">)</span>
</span></span><span class="line"><span class="cl">        <span class="nb">echo</span> <span class="s2">&#34;Unknown option: </span><span class="nv">$1</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">        usage
</span></span><span class="line"><span class="cl">        <span class="p">;;</span>
</span></span><span class="line"><span class="cl">    <span class="k">esac</span>
</span></span><span class="line"><span class="cl"><span class="k">done</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[</span> ! -f <span class="s2">&#34;</span><span class="nv">$CONF</span><span class="s2">&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;Configuration file not found: </span><span class="nv">$CONF</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">source</span> <span class="s2">&#34;</span><span class="nv">$CONF</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Check required commands</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> cmd in rsync mountpoint curl gzip stat flock<span class="p">;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="nb">command</span> -v <span class="s2">&#34;</span><span class="nv">$cmd</span><span class="s2">&#34;</span> &gt;/dev/null <span class="o">||</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="nb">echo</span> <span class="s2">&#34;Missing command: </span><span class="nv">$cmd</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="k">done</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Check required vars</span>
</span></span><span class="line"><span class="cl"><span class="nv">required_vars</span><span class="o">=(</span>BACKUP_MOUNT_POINT BACKUP_DESTINATION BACKUP_SOURCE_FILE EXCLUDE_FILE BACKUP_LOG_FILE LOGROTATE_MAX_SIZE<span class="o">)</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> var in <span class="s2">&#34;</span><span class="si">${</span><span class="nv">required_vars</span><span class="p">[@]</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="o">[</span> -z <span class="s2">&#34;</span><span class="si">${</span><span class="p">!var</span><span class="k">:-</span><span class="si">}</span><span class="s2">&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">        <span class="nb">echo</span> <span class="s2">&#34;Missing required variable in config: </span><span class="nv">$var</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl">    <span class="k">fi</span>
</span></span><span class="line"><span class="cl"><span class="k">done</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Optional pushover</span>
</span></span><span class="line"><span class="cl"><span class="nv">HAS_PUSHOVER</span><span class="o">=</span><span class="m">0</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[[</span> -n <span class="s2">&#34;</span><span class="si">${</span><span class="nv">PUSHOVER_USER_KEY</span><span class="k">:-</span><span class="si">}</span><span class="s2">&#34;</span> <span class="o">&amp;&amp;</span> -n <span class="s2">&#34;</span><span class="si">${</span><span class="nv">PUSHOVER_API_TOKEN</span><span class="k">:-</span><span class="si">}</span><span class="s2">&#34;</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="nv">HAS_PUSHOVER</span><span class="o">=</span><span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Functions</span>
</span></span><span class="line"><span class="cl">log<span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;</span><span class="k">$(</span>date <span class="s1">&#39;+%Y-%m-%d %H:%M:%S&#39;</span><span class="k">)</span><span class="s2"> - </span><span class="nv">$1</span><span class="s2"> - </span><span class="nv">$2</span><span class="s2">&#34;</span> &gt;&gt;<span class="s2">&#34;</span><span class="nv">$BACKUP_LOG_FILE</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">notify_pushover<span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="o">[</span> <span class="s2">&#34;</span><span class="nv">$HAS_PUSHOVER</span><span class="s2">&#34;</span> -eq <span class="m">1</span> <span class="o">]</span> <span class="o">||</span> <span class="k">return</span>
</span></span><span class="line"><span class="cl">    curl -s <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>        --form-string <span class="s2">&#34;token=</span><span class="nv">$PUSHOVER_API_TOKEN</span><span class="s2">&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>        --form-string <span class="s2">&#34;user=</span><span class="nv">$PUSHOVER_USER_KEY</span><span class="s2">&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>        --form-string <span class="s2">&#34;message=</span><span class="nv">$1</span><span class="s2">&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>        --form-string <span class="s2">&#34;title=</span><span class="si">${</span><span class="nv">NOTIFY_TITLE</span><span class="k">:-</span><span class="nv">rsync_backup</span><span class="si">}</span><span class="s2">&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>        https://api.pushover.net/1/messages.json &gt;/dev/null
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">log_rotate<span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="nb">local</span> <span class="nv">max_size</span><span class="o">=</span><span class="k">$((</span>LOGROTATE_MAX_SIZE <span class="o">*</span> <span class="m">1024</span> <span class="o">*</span> <span class="m">1024</span><span class="k">))</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="o">[</span> -f <span class="s2">&#34;</span><span class="nv">$BACKUP_LOG_FILE</span><span class="s2">&#34;</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="o">[</span> <span class="s2">&#34;</span><span class="k">$(</span>stat -c%s <span class="s2">&#34;</span><span class="nv">$BACKUP_LOG_FILE</span><span class="s2">&#34;</span><span class="k">)</span><span class="s2">&#34;</span> -ge <span class="s2">&#34;</span><span class="nv">$max_size</span><span class="s2">&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> i in <span class="m">3</span> <span class="m">2</span> 1<span class="p">;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">            <span class="o">[</span> -f <span class="s2">&#34;</span><span class="si">${</span><span class="nv">BACKUP_LOG_FILE</span><span class="p">%.log</span><span class="si">}</span><span class="s2">.</span><span class="nv">$i</span><span class="s2">.gz&#34;</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> mv <span class="s2">&#34;</span><span class="si">${</span><span class="nv">BACKUP_LOG_FILE</span><span class="p">%.log</span><span class="si">}</span><span class="s2">.</span><span class="nv">$i</span><span class="s2">.gz&#34;</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">BACKUP_LOG_FILE</span><span class="p">%.log</span><span class="si">}</span><span class="s2">.</span><span class="k">$((</span>i <span class="o">+</span> <span class="m">1</span><span class="k">))</span><span class="s2">.gz&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="k">done</span>
</span></span><span class="line"><span class="cl">        gzip -c <span class="s2">&#34;</span><span class="nv">$BACKUP_LOG_FILE</span><span class="s2">&#34;</span> &gt;<span class="s2">&#34;</span><span class="si">${</span><span class="nv">BACKUP_LOG_FILE</span><span class="p">%.log</span><span class="si">}</span><span class="s2">.1.gz&#34;</span>
</span></span><span class="line"><span class="cl">        : &gt;<span class="s2">&#34;</span><span class="nv">$BACKUP_LOG_FILE</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">fi</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">run_rsync<span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="nb">local</span> <span class="nv">failures</span><span class="o">=</span><span class="m">0</span>
</span></span><span class="line"><span class="cl">    <span class="nb">local</span> <span class="nv">success</span><span class="o">=</span><span class="m">0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="nv">IFS</span><span class="o">=</span> <span class="nb">read</span> -r src_path <span class="o">||</span> <span class="o">[[</span> -n <span class="s2">&#34;</span><span class="nv">$src_path</span><span class="s2">&#34;</span> <span class="o">]]</span><span class="p">;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">        <span class="o">[</span> -z <span class="s2">&#34;</span><span class="nv">$src_path</span><span class="s2">&#34;</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="k">continue</span>
</span></span><span class="line"><span class="cl">        <span class="o">[</span> ! -e <span class="s2">&#34;</span><span class="nv">$src_path</span><span class="s2">&#34;</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> log <span class="s2">&#34;SKIP&#34;</span> <span class="s2">&#34;Path not found: </span><span class="nv">$src_path</span><span class="s2">&#34;</span> <span class="o">&amp;&amp;</span> <span class="k">continue</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="o">[</span> <span class="s2">&#34;</span><span class="nv">$DRY_RUN</span><span class="s2">&#34;</span> -eq <span class="m">1</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">            <span class="nv">rsync_args</span><span class="o">=(</span>-anx --delete --exclude-from <span class="s2">&#34;</span><span class="nv">$EXCLUDE_FILE</span><span class="s2">&#34;</span> <span class="s2">&#34;</span><span class="nv">$src_path</span><span class="s2">&#34;</span> <span class="s2">&#34;</span><span class="nv">$BACKUP_DESTINATION</span><span class="s2">&#34;</span><span class="o">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">else</span>
</span></span><span class="line"><span class="cl">            <span class="nv">rsync_args</span><span class="o">=(</span>-axs --delete --exclude-from <span class="s2">&#34;</span><span class="nv">$EXCLUDE_FILE</span><span class="s2">&#34;</span> <span class="s2">&#34;</span><span class="nv">$src_path</span><span class="s2">&#34;</span> <span class="s2">&#34;</span><span class="nv">$BACKUP_DESTINATION</span><span class="s2">&#34;</span><span class="o">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nv">RSYNC_OUTPUT</span><span class="o">=</span><span class="k">$(</span>rsync <span class="s2">&#34;</span><span class="si">${</span><span class="nv">rsync_args</span><span class="p">[@]</span><span class="si">}</span><span class="s2">&#34;</span> 2&gt;<span class="p">&amp;</span>1<span class="k">)</span>
</span></span><span class="line"><span class="cl">        <span class="nv">rsync_exit</span><span class="o">=</span><span class="nv">$?</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="o">[</span> <span class="nv">$rsync_exit</span> -eq <span class="m">0</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">            log <span class="s2">&#34;OK&#34;</span> <span class="s2">&#34;Synced: </span><span class="nv">$src_path</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="nv">success</span><span class="o">=</span><span class="k">$((</span>success <span class="o">+</span> <span class="m">1</span><span class="k">))</span>
</span></span><span class="line"><span class="cl">        <span class="k">else</span>
</span></span><span class="line"><span class="cl">            log <span class="s2">&#34;ERROR&#34;</span> <span class="s2">&#34;Failed: </span><span class="nv">$src_path</span><span class="s2">: </span><span class="nv">$RSYNC_OUTPUT</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="nv">failures</span><span class="o">=</span><span class="k">$((</span>failures <span class="o">+</span> <span class="m">1</span><span class="k">))</span>
</span></span><span class="line"><span class="cl">        <span class="k">fi</span>
</span></span><span class="line"><span class="cl">    <span class="k">done</span> &lt;<span class="s2">&#34;</span><span class="nv">$BACKUP_SOURCE_FILE</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;</span><span class="nv">$success</span><span class="s2"> success, </span><span class="nv">$failures</span><span class="s2"> failure(s)&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nv">$failures</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">main<span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    log_rotate
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Secure lockfile creation</span>
</span></span><span class="line"><span class="cl">    <span class="nb">umask</span> <span class="m">0077</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> ! touch <span class="s2">&#34;</span><span class="nv">$LOCKFILE</span><span class="s2">&#34;</span> 2&gt;/dev/null<span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">        <span class="nb">echo</span> <span class="s2">&#34;Error: Cannot create lockfile at </span><span class="nv">$LOCKFILE</span><span class="s2">&#34;</span> &gt;<span class="p">&amp;</span><span class="m">2</span>
</span></span><span class="line"><span class="cl">        <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl">    <span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Open the lock file and assign it to FD9</span>
</span></span><span class="line"><span class="cl">    <span class="nb">exec</span> 9&gt;<span class="s2">&#34;</span><span class="nv">$LOCKFILE</span><span class="s2">&#34;</span> <span class="o">||</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="nb">echo</span> <span class="s2">&#34;Error: Cannot open lockfile: </span><span class="nv">$LOCKFILE</span><span class="s2">&#34;</span> &gt;<span class="p">&amp;</span><span class="m">2</span>
</span></span><span class="line"><span class="cl">        <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Try to acquire the lock on FD9</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> ! flock -n 9<span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">        <span class="nb">echo</span> <span class="s2">&#34;Another backup is already running (lockfile in use).&#34;</span> &gt;<span class="p">&amp;</span><span class="m">2</span>
</span></span><span class="line"><span class="cl">        <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl">    <span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Ensure lockfile is deleted on exit</span>
</span></span><span class="line"><span class="cl">    cleanup<span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        rm -f <span class="s2">&#34;</span><span class="nv">$LOCKFILE</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="nb">trap</span> cleanup EXIT
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="o">[</span> ! -f <span class="s2">&#34;</span><span class="nv">$BACKUP_SOURCE_FILE</span><span class="s2">&#34;</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> log <span class="s2">&#34;ERROR&#34;</span> <span class="s2">&#34;Missing source file&#34;</span> <span class="o">&amp;&amp;</span> <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl">    <span class="o">[</span> ! -f <span class="s2">&#34;</span><span class="nv">$EXCLUDE_FILE</span><span class="s2">&#34;</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> log <span class="s2">&#34;ERROR&#34;</span> <span class="s2">&#34;Missing exclude file&#34;</span> <span class="o">&amp;&amp;</span> <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> ! mountpoint -q <span class="s2">&#34;</span><span class="nv">$BACKUP_MOUNT_POINT</span><span class="s2">&#34;</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">        log <span class="s2">&#34;ERROR&#34;</span> <span class="s2">&#34;Mount point </span><span class="nv">$BACKUP_MOUNT_POINT</span><span class="s2"> not found&#34;</span>
</span></span><span class="line"><span class="cl">        notify_pushover <span class="s2">&#34;Backup failed: </span><span class="nv">$BACKUP_MOUNT_POINT</span><span class="s2"> not mounted&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl">    <span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    log <span class="s2">&#34;START&#34;</span> <span class="s2">&#34;Rsync backup started (dry-run=</span><span class="nv">$DRY_RUN</span><span class="s2">)&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nv">result</span><span class="o">=</span><span class="k">$(</span>run_rsync<span class="k">)</span>
</span></span><span class="line"><span class="cl">    <span class="nv">status</span><span class="o">=</span><span class="nv">$?</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    log <span class="s2">&#34;END&#34;</span> <span class="s2">&#34;Backup complete. </span><span class="nv">$result</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="o">[</span> <span class="nv">$status</span> -ne <span class="m">0</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">        notify_pushover <span class="s2">&#34;Rsync backup failed: </span><span class="nv">$result</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;</span><span class="nv">$result</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">exit</span> <span class="nv">$status</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">main
</span></span></code></pre></div>]]></content:encoded>
    </item>
  </channel>
</rss>
