<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Haui's Bytes</title><link>https://haui.pdes-net.org/</link><description>news, diary, journal, whatever</description><atom:link href="https://haui.pdes-net.org/rss.xml" rel="self" type="application/rss+xml"></atom:link><language>en</language><copyright>Contents © 2019 &lt;a href="mailto:haui45@web.de"&gt;Haui&lt;/a&gt; </copyright><lastBuildDate>Sat, 24 Aug 2019 18:02:58 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>Migrating to Nikola</title><link>https://haui.pdes-net.org/posts/migrating-to-nikola/</link><dc:creator>Haui</dc:creator><description>&lt;div&gt;&lt;p&gt;I've written my last posting on this blog more than 5 years ago. When I recently wanted to share some details of my &lt;a class="reference external" href="https://haui.pdes-net.org/posts/backup/"&gt;backup system&lt;/a&gt; with you, I noticed that tinkerer, the blog compiler I've been using previously did not receive any updates since 2017 and won't run flawlessly on my system any more. So, once again I had to migrate the old blog posts to a new system: &lt;a class="reference external" href="https://getnikola.com/"&gt;Nikola&lt;/a&gt;. This time however, the migration was easier than the &lt;a class="reference external" href="https://haui.pdes-net.org/posts/new_design/"&gt;previous time&lt;/a&gt; as tinkerer's blog posts are stored as &lt;a class="reference external" href="https://en.wikipedia.org/wiki/ReStructuredText"&gt;reStructuredText&lt;/a&gt; - a format supported by Nikola as well.
All I had to do was to create a small script that creates &lt;cite&gt;*.rst&lt;/cite&gt; files containing the appropriate headers for Nikola, especially the &lt;cite&gt;title, slug and date&lt;/cite&gt;:&lt;/p&gt;
&lt;pre class="code bash"&gt;&lt;a name="rest_code_1bbaffdc74fb444c95c6d91c2a3ff060-1"&gt;&lt;/a&gt;&lt;span class="ch"&gt;#!/bin/bash&lt;/span&gt;
&lt;a name="rest_code_1bbaffdc74fb444c95c6d91c2a3ff060-2"&gt;&lt;/a&gt;&lt;span class="nv"&gt;FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
&lt;a name="rest_code_1bbaffdc74fb444c95c6d91c2a3ff060-3"&gt;&lt;/a&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; ! &lt;span class="nv"&gt;$FILE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;~ &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;-9&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;-9&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;-9&lt;span class="o"&gt;]&lt;/span&gt;/&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;-9&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;-9&lt;span class="o"&gt;]&lt;/span&gt;/&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;-9&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;-9&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;a name="rest_code_1bbaffdc74fb444c95c6d91c2a3ff060-4"&gt;&lt;/a&gt;        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"file path must include the date!"&lt;/span&gt;
&lt;a name="rest_code_1bbaffdc74fb444c95c6d91c2a3ff060-5"&gt;&lt;/a&gt;        &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a name="rest_code_1bbaffdc74fb444c95c6d91c2a3ff060-6"&gt;&lt;/a&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;a name="rest_code_1bbaffdc74fb444c95c6d91c2a3ff060-7"&gt;&lt;/a&gt;&lt;span class="nv"&gt;DATE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;egrep -o &lt;span class="s2"&gt;"2[0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9]"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;a name="rest_code_1bbaffdc74fb444c95c6d91c2a3ff060-8"&gt;&lt;/a&gt;&lt;span class="nv"&gt;YY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;cut -d &lt;span class="s1"&gt;'/'&lt;/span&gt; -f &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DATE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;a name="rest_code_1bbaffdc74fb444c95c6d91c2a3ff060-9"&gt;&lt;/a&gt;&lt;span class="nv"&gt;MM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;cut -d &lt;span class="s1"&gt;'/'&lt;/span&gt; -f &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DATE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;a name="rest_code_1bbaffdc74fb444c95c6d91c2a3ff060-10"&gt;&lt;/a&gt;&lt;span class="nv"&gt;DD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;cut -d &lt;span class="s1"&gt;'/'&lt;/span&gt; -f &lt;span class="m"&gt;3&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DATE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;a name="rest_code_1bbaffdc74fb444c95c6d91c2a3ff060-11"&gt;&lt;/a&gt;
&lt;a name="rest_code_1bbaffdc74fb444c95c6d91c2a3ff060-12"&gt;&lt;/a&gt;
&lt;a name="rest_code_1bbaffdc74fb444c95c6d91c2a3ff060-13"&gt;&lt;/a&gt;&lt;span class="nv"&gt;TITLE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;head -1 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;a name="rest_code_1bbaffdc74fb444c95c6d91c2a3ff060-14"&gt;&lt;/a&gt;&lt;span class="nv"&gt;SLUG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;tr &lt;span class="s1"&gt;'[:upper:]'&lt;/span&gt; &lt;span class="s1"&gt;'[:lower:]'&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TITLE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; sed &lt;span class="s1"&gt;'s#[^0-9a-z]#_#g'&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;a name="rest_code_1bbaffdc74fb444c95c6d91c2a3ff060-15"&gt;&lt;/a&gt;&lt;span class="nv"&gt;CATEGORIES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;grep &lt;span class="s2"&gt;".. categories::"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;  cut -d &lt;span class="s2"&gt;":"&lt;/span&gt; -f &lt;span class="m"&gt;3&lt;/span&gt;-&lt;span class="k"&gt;)&lt;/span&gt;
&lt;a name="rest_code_1bbaffdc74fb444c95c6d91c2a3ff060-16"&gt;&lt;/a&gt;&lt;span class="nv"&gt;TAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;grep &lt;span class="s2"&gt;".. tags::"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;  cut -d &lt;span class="s2"&gt;":"&lt;/span&gt; -f &lt;span class="m"&gt;3&lt;/span&gt;-&lt;span class="k"&gt;)&lt;/span&gt;
&lt;a name="rest_code_1bbaffdc74fb444c95c6d91c2a3ff060-17"&gt;&lt;/a&gt;&lt;span class="nv"&gt;LINES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;wc -l &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;  &lt;span class="p"&gt;|&lt;/span&gt; awk &lt;span class="s1"&gt;'{print $1}'&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;a name="rest_code_1bbaffdc74fb444c95c6d91c2a3ff060-18"&gt;&lt;/a&gt;&lt;span class="nv"&gt;LINES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;LINES-2&lt;span class="k"&gt;))&lt;/span&gt;
&lt;a name="rest_code_1bbaffdc74fb444c95c6d91c2a3ff060-19"&gt;&lt;/a&gt;
&lt;a name="rest_code_1bbaffdc74fb444c95c6d91c2a3ff060-20"&gt;&lt;/a&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;".. title: &lt;/span&gt;&lt;span class="nv"&gt;$TITLE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;a name="rest_code_1bbaffdc74fb444c95c6d91c2a3ff060-21"&gt;&lt;/a&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;".. slug: &lt;/span&gt;&lt;span class="nv"&gt;$SLUG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;a name="rest_code_1bbaffdc74fb444c95c6d91c2a3ff060-22"&gt;&lt;/a&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;".. date: &lt;/span&gt;&lt;span class="nv"&gt;$YY&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;$MM&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;$DD&lt;/span&gt;&lt;span class="s2"&gt; 12:00:00 UTC+02:00"&lt;/span&gt;
&lt;a name="rest_code_1bbaffdc74fb444c95c6d91c2a3ff060-23"&gt;&lt;/a&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;".. tags: &lt;/span&gt;&lt;span class="nv"&gt;$TAGS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;a name="rest_code_1bbaffdc74fb444c95c6d91c2a3ff060-24"&gt;&lt;/a&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;".. category: &lt;/span&gt;&lt;span class="nv"&gt;$CATEGORIES&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;a name="rest_code_1bbaffdc74fb444c95c6d91c2a3ff060-25"&gt;&lt;/a&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;".. link: "&lt;/span&gt;
&lt;a name="rest_code_1bbaffdc74fb444c95c6d91c2a3ff060-26"&gt;&lt;/a&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;".. description: "&lt;/span&gt;
&lt;a name="rest_code_1bbaffdc74fb444c95c6d91c2a3ff060-27"&gt;&lt;/a&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;".. type: text"&lt;/span&gt;
&lt;a name="rest_code_1bbaffdc74fb444c95c6d91c2a3ff060-28"&gt;&lt;/a&gt;
&lt;a name="rest_code_1bbaffdc74fb444c95c6d91c2a3ff060-29"&gt;&lt;/a&gt;
&lt;a name="rest_code_1bbaffdc74fb444c95c6d91c2a3ff060-30"&gt;&lt;/a&gt;tail -n &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LINES&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; egrep -v &lt;span class="s2"&gt;"\.\. author::|\.\. categories::|\.\. tags::|\.\. comments::|\.\ highlight:: none"&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; sed &lt;span class="s1"&gt;'s#.. [h|H]ighlight::#.. code-block::#g'&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Because tinkerer stored the date of the blog posting via the directory structure, the script needs this information as well:&lt;/p&gt;
&lt;pre class="code bash"&gt;&lt;a name="rest_code_f79706679425454d8f1c2abf3106ef75-1"&gt;&lt;/a&gt;find . -name &lt;span class="s2"&gt;"*rst"&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; grep ./2 &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; l&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; ./script.sh &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$l&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &amp;gt; &lt;span class="s2"&gt;"~/blog/posts/&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;basename &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$l&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;done&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Unfortunately, back in 2013/2014, I did some strange things in the original &lt;cite&gt;*.rst&lt;/cite&gt; files, so I had to fix several files manually. Nevertheless, the script shown above saved me lots of work.&lt;/p&gt;&lt;/div&gt;</description><category>linux</category><category>nikola</category><category>script</category><guid>https://haui.pdes-net.org/posts/migrating-to-nikola/</guid><pubDate>Fri, 19 Apr 2019 17:36:28 GMT</pubDate></item><item><title>Backup</title><link>https://haui.pdes-net.org/posts/backup/</link><dc:creator>Haui</dc:creator><description>&lt;div&gt;&lt;p&gt;When being asked about my hobbies, photography is usually among the top three of my answers. However, besides being a quite expensive spare time activity,
it is accompanied by an ever-increasing amount of digital images (currently around 280GB). Storing these images in a secure manner is an important matter,
because no one wants to lose these digital treasures, especially since they are often connected with precious memories.&lt;/p&gt;
&lt;p&gt;Although my setup may not be safe against catastrophes such as fires or &lt;a class="reference external" href="https://cobra.pdes-net.org/posts/a-simple-automated-backup-scheme.html"&gt;nuclear blasts&lt;/a&gt;, I claim that I am safe against most remaining data loss scenarios.
After taking pictures with my &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Sony_Alpha_58"&gt;SLT A58&lt;/a&gt; the first step is to copy the data to my workstation.
Once this is finished, all files are renamed based on the recording timestamp via &lt;a class="reference external" href="https://en.wikipedia.org/wiki/ExifTool"&gt;Exiftool&lt;/a&gt;&lt;/p&gt;
&lt;pre class="code bash"&gt;&lt;a name="rest_code_9e7e7345fc3f405f85f5c9d393d4b87d-1"&gt;&lt;/a&gt;exiftool &lt;span class="s1"&gt;'-filename&amp;lt;CreateDate'&lt;/span&gt; -d %Y%m%d_%H%M%S%%-c.%%e -r .
&lt;/pre&gt;&lt;p&gt;This is necessary because the default file names produced by my camera are neither descriptive nor unique (the image counter wraps around at 10000).
After this is finished, the real work begins as I want my files to be properly sorted in folders describing the occasion the
pictures were taken. While this is a very basic approach for file management, it actually fits my needs fairly well as it does not depend
on any kind of further software for metadata management. Additionally, tagging the photos, e.g. with &lt;a class="reference external" href="https://www.digikam.org/"&gt;digiKam&lt;/a&gt;, is still possible.&lt;/p&gt;
&lt;p&gt;The storage device that holds the first copy of the files is my HP Microserver running &lt;a class="reference external" href="https://freenas.org"&gt;FreeNAS&lt;/a&gt;. The system is configured in a way, that
all data written to the ZFS pool is mirrored between the two enterprise grade hard disks, similar to a RAID-1. Snapshots of the &lt;cite&gt;photo dataset&lt;/cite&gt; are taken automatically every day.
However, mirroring and snapshots are fine, but another level of redundancy is still needed. That is why I sync my ZFS datasets to the workstation via &lt;a class="reference external" href="http://www.bolthole.com/solaris/zrep/"&gt;zrep&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The intial setup requires some commands to be executed on the workstation (WS) and on the NAS:&lt;/p&gt;
&lt;pre class="code bash"&gt;&lt;a name="rest_code_a551266af68a47e28900445e81f76fd3-1"&gt;&lt;/a&gt;zfs create nasbackup/photos &lt;span class="c1"&gt;# WS: create the dataset 'photos' in the pool 'nasbackup'&lt;/span&gt;
&lt;a name="rest_code_a551266af68a47e28900445e81f76fd3-2"&gt;&lt;/a&gt;zrep changeconfig -f -d nasbackup/photos &amp;lt;NAS-IP&amp;gt; pool1/photos &lt;span class="c1"&gt;# WS: set the backup properties&lt;/span&gt;
&lt;a name="rest_code_a551266af68a47e28900445e81f76fd3-3"&gt;&lt;/a&gt;zfs &lt;span class="nb"&gt;set&lt;/span&gt; zrep:savecount&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;2000&lt;/span&gt; nasbackup/photos &lt;span class="c1"&gt;# WS: set the number of snapshots to keep&lt;/span&gt;
&lt;a name="rest_code_a551266af68a47e28900445e81f76fd3-4"&gt;&lt;/a&gt;zrep changeconfig -f pool1/photos &amp;lt;WORKSTATION-IP&amp;gt; nasbackup/photos &lt;span class="c1"&gt;# NAS: set the backup properties&lt;/span&gt;
&lt;a name="rest_code_a551266af68a47e28900445e81f76fd3-5"&gt;&lt;/a&gt;zfs &lt;span class="nb"&gt;set&lt;/span&gt; zrep:savecount&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;2000&lt;/span&gt; pool1/photos &lt;span class="c1"&gt;# NAS: set the number of snapshots to keep&lt;/span&gt;
&lt;a name="rest_code_a551266af68a47e28900445e81f76fd3-6"&gt;&lt;/a&gt;zfs snap pool1/photos@zrep_000001 &lt;span class="c1"&gt;# NAS: create the initial snapshot&lt;/span&gt;
&lt;a name="rest_code_a551266af68a47e28900445e81f76fd3-7"&gt;&lt;/a&gt;ssh &amp;lt;NAS-IP&amp;gt; zfs send pool1/photos@zrep_000001 &lt;span class="p"&gt;|&lt;/span&gt; pv &lt;span class="p"&gt;|&lt;/span&gt; zfs recv -F nasbackup/photos &lt;span class="c1"&gt;# WS: sync the initial snapshot to the local disk&lt;/span&gt;
&lt;a name="rest_code_a551266af68a47e28900445e81f76fd3-8"&gt;&lt;/a&gt;zrep sentsync pool1/photos@zrep_000001 &lt;span class="c1"&gt;# NAS: announce that the initial sync was successful once it's completed&lt;/span&gt;
&lt;a name="rest_code_a551266af68a47e28900445e81f76fd3-9"&gt;&lt;/a&gt;zfs rollback nasbackup/photos@zrep_000001 &lt;span class="c1"&gt;# WS: rollback the dataset to the snapshot&lt;/span&gt;
&lt;a name="rest_code_a551266af68a47e28900445e81f76fd3-10"&gt;&lt;/a&gt;zfs &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;readonly&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;on nasbackup/photos &lt;span class="c1"&gt;# WS: set the dataset so read-only&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;After these initial steps, all further backups boil down to one simple command:&lt;/p&gt;
&lt;pre class="code bash"&gt;&lt;a name="rest_code_43273ee9f13c4e6f9a41f32e1b67fd49-1"&gt;&lt;/a&gt;zrep refresh nasbackup/photos
&lt;/pre&gt;&lt;p&gt;This creates a fresh snapshot on the NAS and syncs all new snapshots (i.e. also the ones created automatically by FreeNAS) to the local dataset. In order to get a progress indicator for &lt;code&gt;zrep&lt;/code&gt;, I've set the &lt;code&gt;ZREP_INFILTER&lt;/code&gt; variable to &lt;a class="reference external" href="https://linux.die.net/man/1/pv"&gt;pv&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As I have multiple datasets on my systems, I wrapped the &lt;code&gt;zrep&lt;/code&gt; command in a small Bash script &lt;code&gt;nasbackup&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code bash"&gt;&lt;a name="rest_code_4413fc906e274a2a8ee9d705e26068e6-1"&gt;&lt;/a&gt;&lt;span class="ch"&gt;#!/bin/bash&lt;/span&gt;
&lt;a name="rest_code_4413fc906e274a2a8ee9d705e26068e6-2"&gt;&lt;/a&gt;&lt;span class="nv"&gt;NASIP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;192&lt;/span&gt;...
&lt;a name="rest_code_4413fc906e274a2a8ee9d705e26068e6-3"&gt;&lt;/a&gt;&lt;span class="nv"&gt;SYNCFOLDERS&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;photos video mobile ...&lt;span class="o"&gt;)&lt;/span&gt;
&lt;a name="rest_code_4413fc906e274a2a8ee9d705e26068e6-4"&gt;&lt;/a&gt;
&lt;a name="rest_code_4413fc906e274a2a8ee9d705e26068e6-5"&gt;&lt;/a&gt;&lt;span class="k"&gt;if&lt;/span&gt; ! ping -c1 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NASIP&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;a name="rest_code_4413fc906e274a2a8ee9d705e26068e6-6"&gt;&lt;/a&gt;        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"NAS not available"&lt;/span&gt;
&lt;a name="rest_code_4413fc906e274a2a8ee9d705e26068e6-7"&gt;&lt;/a&gt;        &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;a name="rest_code_4413fc906e274a2a8ee9d705e26068e6-8"&gt;&lt;/a&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;a name="rest_code_4413fc906e274a2a8ee9d705e26068e6-9"&gt;&lt;/a&gt;
&lt;a name="rest_code_4413fc906e274a2a8ee9d705e26068e6-10"&gt;&lt;/a&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$#&lt;/span&gt; -gt &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;a name="rest_code_4413fc906e274a2a8ee9d705e26068e6-11"&gt;&lt;/a&gt;        &lt;span class="k"&gt;for&lt;/span&gt; arg&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;a name="rest_code_4413fc906e274a2a8ee9d705e26068e6-12"&gt;&lt;/a&gt;                &lt;span class="k"&gt;if&lt;/span&gt; ! grep -q &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$arg&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SYNCFOLDERS&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;a name="rest_code_4413fc906e274a2a8ee9d705e26068e6-13"&gt;&lt;/a&gt;                        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$arg&lt;/span&gt;&lt;span class="s2"&gt; is not a valid dataset!"&lt;/span&gt;
&lt;a name="rest_code_4413fc906e274a2a8ee9d705e26068e6-14"&gt;&lt;/a&gt;                        &lt;span class="k"&gt;continue&lt;/span&gt;
&lt;a name="rest_code_4413fc906e274a2a8ee9d705e26068e6-15"&gt;&lt;/a&gt;                &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;a name="rest_code_4413fc906e274a2a8ee9d705e26068e6-16"&gt;&lt;/a&gt;                &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"===== Backing up nasbackup/&lt;/span&gt;&lt;span class="nv"&gt;$arg&lt;/span&gt;&lt;span class="s2"&gt; ====="&lt;/span&gt;
&lt;a name="rest_code_4413fc906e274a2a8ee9d705e26068e6-17"&gt;&lt;/a&gt;                &lt;span class="nb"&gt;time&lt;/span&gt; zrep refresh &lt;span class="s2"&gt;"nasbackup/&lt;/span&gt;&lt;span class="nv"&gt;$arg&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;a name="rest_code_4413fc906e274a2a8ee9d705e26068e6-18"&gt;&lt;/a&gt;                &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"===== END ====="&lt;/span&gt;
&lt;a name="rest_code_4413fc906e274a2a8ee9d705e26068e6-19"&gt;&lt;/a&gt;        &lt;span class="k"&gt;done&lt;/span&gt;
&lt;a name="rest_code_4413fc906e274a2a8ee9d705e26068e6-20"&gt;&lt;/a&gt;&lt;span class="k"&gt;else&lt;/span&gt;
&lt;a name="rest_code_4413fc906e274a2a8ee9d705e26068e6-21"&gt;&lt;/a&gt;        &lt;span class="k"&gt;for&lt;/span&gt; arg in &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SYNCFOLDERS&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;a name="rest_code_4413fc906e274a2a8ee9d705e26068e6-22"&gt;&lt;/a&gt;                &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"===== Backing up nasbackup/&lt;/span&gt;&lt;span class="nv"&gt;$arg&lt;/span&gt;&lt;span class="s2"&gt; ====="&lt;/span&gt;
&lt;a name="rest_code_4413fc906e274a2a8ee9d705e26068e6-23"&gt;&lt;/a&gt;                &lt;span class="nb"&gt;time&lt;/span&gt; zrep refresh &lt;span class="s2"&gt;"nasbackup/&lt;/span&gt;&lt;span class="nv"&gt;$arg&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;a name="rest_code_4413fc906e274a2a8ee9d705e26068e6-24"&gt;&lt;/a&gt;                &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"===== END ====="&lt;/span&gt;
&lt;a name="rest_code_4413fc906e274a2a8ee9d705e26068e6-25"&gt;&lt;/a&gt;        &lt;span class="k"&gt;done&lt;/span&gt;
&lt;a name="rest_code_4413fc906e274a2a8ee9d705e26068e6-26"&gt;&lt;/a&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;I could easily create a cron job for the backup script, but as I've explained, the process of adding new images always involves manual work - so I run the script manually.&lt;/p&gt;
&lt;p&gt;A list of snapshots for a specific dataset can be obtained via:&lt;/p&gt;
&lt;pre class="code bash"&gt;&lt;a name="rest_code_853fecc728d24bb09bad4e7c6602c0cd-1"&gt;&lt;/a&gt;zfs list -r -t snapshot -o name,creation,used,refer nasbackup/photos
&lt;/pre&gt;&lt;p&gt;Any snapshot from this list can be mounted with the standard &lt;code&gt;mount&lt;/code&gt; command, e.g. &lt;code&gt;mount -t zfs nasbackup/photos@zrep_000001 /mnt/backup&lt;/code&gt; would mount the initial snapshot created above to &lt;cite&gt;/mnt/backup&lt;/cite&gt;. This is especially helpful if you would like to restore specific files from a snapshot. For this task, it is sometimes quite handy to know what changed between two snapshots - a simple  &lt;code&gt;zfs diff &amp;lt;snapshot1&amp;gt; &amp;lt;snapshot2&amp;gt;&lt;/code&gt; gives the answer. After all, I always hope I won't need the backup, but every time I run the backup script I'm pleased to see how smooth and fast it works.&lt;/p&gt;&lt;/div&gt;</description><category>backup</category><category>zfs</category><category>zrep</category><guid>https://haui.pdes-net.org/posts/backup/</guid><pubDate>Fri, 19 Apr 2019 14:30:35 GMT</pubDate></item><item><title>Who Is Phoning Home</title><link>https://haui.pdes-net.org/posts/who_is_phoning_home/</link><dc:creator>Haui</dc:creator><description>&lt;div&gt;&lt;p&gt;The current development in consumer electronics like tablets or mobile phones couldn't be worse.
At least if you care a little bit about privacy. A few years ago, Spy- and Adware that came bundled
with other software was unwelcome to (almost) every Windows user. Internet Discussion Boards were overrun
by people trying to get rid of annoying web browser bars that were installed together with their favorite
file sharing tool. Others even refused to use the Opera web browser because it was ad-sponsored.&lt;/p&gt;
&lt;p&gt;Nowadays, we don't have these problems anymore. Spy- and Adware is now socially accepted and more popular than ever before.
Naturally, the name has changed - the euphemism &lt;em&gt;App&lt;/em&gt; just sounds cool and innocent.&lt;/p&gt;
&lt;p&gt;Many (or even most) &lt;em&gt;Apps&lt;/em&gt; for Android or IOS, the two major operating systems found on mobile phones and tablets,
come with some important built-in consumer information right away. To retrieve these ads, an internet connection is needed
because the advertising is tailored specifically for your needs (I know, it's great). Apart from this, an active internet connection
introduces another great field of application. While Ads are transmitted &lt;strong&gt;to&lt;/strong&gt; your device, your personal data may be sent &lt;strong&gt;from&lt;/strong&gt; your device to
trustworthy servers around the world. So it's a win-win situation for everybody - except for you.&lt;/p&gt;
&lt;p&gt;The permission concepts of Android and IOS are, of course, completely useless because nobody cares anyway -
"A flashlight app that requires access to Internet, Email accounts and GPS information - seems legit!".
In addition, the modern operating systems are an ideal breeding ground for these privacy nightmare applications because of their standardized
APIs. In contrast to a classical desktop computer system, it's extremely easy to collect data like calender events or Emails
automatically because the APIs already include some ready-to-run methods for these purposes.&lt;/p&gt;
&lt;p&gt;However, apart from avoiding this new technology, there isn't that much you can do about the aforementioned issues.
Still, there are ways to figure out which data is sent from and to your device and it's even possible to filter this traffic.
I'll describe on of these ways in the following. But be warned - there's no App for this and you will need a root shell ;)&lt;/p&gt;
&lt;img alt="/images/bridge.png" src="https://haui.pdes-net.org/images/bridge.png"&gt;
&lt;p&gt;The image describes the basic setup. The PC acts as a Wi-Fi hotspot and forwards the traffic received from a tablet to the
DSL router. To turn the PC into a hotspot, a Wi-Fi USB adapter that can act as a hotspot (&lt;tt class="docutils literal"&gt;iw list |grep AP&lt;/tt&gt;) is required. Once the adapter is properly
installed on your system, it's easy to create a hotspot with &lt;tt class="docutils literal"&gt;hostapd&lt;/tt&gt;. The &lt;a class="reference external" href="https://wiki.archlinux.org/index.php/Software_access_point"&gt;Arch Wiki&lt;/a&gt; contains some information
about the configuration, but the default configuration file is quite self-explanatory and just needs a few adjustments. After the setup is done,
&lt;tt class="docutils literal"&gt;brctl&lt;/tt&gt; (on Debian contained in the package &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;bridge-utils&lt;/span&gt;&lt;/tt&gt;) is used to create a bridge that connects the wireless and the non-wireless network interfaces:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
brctl addbr br0
brctl addif br0 eth0
&lt;/pre&gt;
&lt;p&gt;Don't forget to add the correct bridge configuration line in your &lt;em&gt;hostapd.conf&lt;/em&gt;:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
bridge=br0
&lt;/pre&gt;
&lt;p&gt;After the interfaces are brought up, you may start &lt;tt class="docutils literal"&gt;hostapd&lt;/tt&gt;:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
ip link set dev eth0 up
ip link set dev wlan0 up
ip link set dev br0 up
hostapd -B /etc/hostapd/hostapd.conf
&lt;/pre&gt;
&lt;p&gt;If no errors occurred, you should be able to connect your wireless device to the newly created hotspot. As all traffic now flows through the PC system,
we're able to record and inspect the network packets. &lt;a class="reference external" href="http://www.tcpdump.org/"&gt;tcpdump&lt;/a&gt; is the tool of choice for this task:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
tcpdump -ni br0 -w tablet.pcap
&lt;/pre&gt;
&lt;p&gt;The command collects all packets passing the bridge interface and writes them to a file &lt;em&gt;tablet.pcap&lt;/em&gt;. No need to mention that the command must be run with root
privileges. Once enough packets are collected, the PCAP file can be inspected with &lt;a class="reference external" href="http://www.wireshark.org/"&gt;Wireshark&lt;/a&gt;. Hence we may, for example,
check if the login data for our favorite shopping app is sent via a SSL secured connection or as plain text. As a thorough explanation of Wireshark's
(and tcpdump's) capabilities could easily fill an entire book, I recommend you take a look at the documentation if you're interested in topics like
&lt;em&gt;filter expressions&lt;/em&gt;. However, basic knowledge of the TCP/IP protocol suite is mandatory for this.&lt;/p&gt;
&lt;p&gt;I've mentioned earlier, that the setup not only allows us to capture all network traffic, but also enables us to filter (and even modify) the traffic.
A few basic firewall rules are enough to stop all communication between the tablet and a specific IP/IP range:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
iptables -A FORWARD --dest 192.168.1.0/24 -j DROP
iptables -A FORWARD --src 192.168.1.0/24 -j DROP
&lt;/pre&gt;
&lt;p&gt;In the example, I've used a locally assigned IP address range - in a real world example you would most likely pick a non-private IP. Filtering by IP addresses
however is not always a satisfying solution. Sometimes the packet payload is way more important than the destination or source information. To filter out all
packets containing a specific string like "adserver", iptables' string matching extension is very useful:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
iptables -A FORWARD -m string --string "adserver" --algo=kmp -j DROP
&lt;/pre&gt;
&lt;p&gt;For this deep packet inspection, better tools might exist, but I'm not familiar with these (and my iptables skills have also become quite rusty).&lt;/p&gt;
&lt;p&gt;All in all, a Linux driven hotspot opens up completely new possibilities when compared to a standard Access Point. Still, inspecting the traffic and
creating proper firewall rules is a very cumbersome procedure.&lt;/p&gt;&lt;/div&gt;</description><category>Android</category><category>hotspot</category><category>IOS</category><category>iptables</category><category>tcpdump</category><category>wifi</category><category>wireshark</category><guid>https://haui.pdes-net.org/posts/who_is_phoning_home/</guid><pubDate>Wed, 12 Mar 2014 10:00:00 GMT</pubDate></item><item><title>Custom Git Prompt</title><link>https://haui.pdes-net.org/posts/custom_git_prompt/</link><dc:creator>Haui</dc:creator><description>&lt;div&gt;&lt;p&gt;From all the available &lt;a class="reference external" href="http://en.wikipedia.org/wiki/Revision_control"&gt;Revision Control Systems&lt;/a&gt; I know, &lt;a class="reference external" href="http://git-scm.com/"&gt;Git&lt;/a&gt; is my personal favorite. I use it for all my revision control needs at work and at home. As a consequence, I got quite a few git repositories residing in my home directory. To keep track of all these repositories,  I modified my bash prompt in a way that it displays the most important git infos in the current directory (of course only if the directory is a git repo).
My solution basically consists of two parts. First, I wrote a simple Perl script, that summarizes and colors the output of &lt;em&gt;git status -s&lt;/em&gt;. Second, I created a bash script that is sourced at the end of the &lt;em&gt;~/.bashrc&lt;/em&gt;. This bash script overwrites the standard &lt;em&gt;cd&lt;/em&gt; command and checks whether the new directory is a git repository. If the check doesn't fail, the output of the Perl script is integrated in the prompt.&lt;/p&gt;
&lt;p&gt;In a first version, the bash prompt was completely refreshed after every command executed in the shell. For very large git repositories, however, this led to a noticeable lag after every command. So, I decided to rerun the git status script only if the last command executed in the shell was likely to modify the
status of the git repo. My list of commands, that makes no claim to be complete, includes &lt;em&gt;vim&lt;/em&gt;, &lt;em&gt;cp&lt;/em&gt;, &lt;em&gt;mv&lt;/em&gt;, and, of course, &lt;em&gt;git&lt;/em&gt;. To manually rerun the status script, and, thus, update the prompt, I included the command &lt;em&gt;g&lt;/em&gt;, that actually only triggers the git status script. As a drawback of this solution, changes made in the same git repo are not visible across multiple concurrent shell sessions. For my needs, however, it works well enough. See the following screenshot for a short demonstration:&lt;/p&gt;
&lt;img alt="/images/gitstatus.png" src="https://haui.pdes-net.org/images/gitstatus.png"&gt;
&lt;p&gt;Please note that my prompt spans across two lines by default (over three when inside a git directory).&lt;/p&gt;
&lt;p&gt;You can download the two scripts &lt;a class="reference external" href="http://pdes-net.org/x-haui/scripts/misc/gitstatus.zip"&gt;here&lt;/a&gt;. Move &lt;em&gt;gitstatus&lt;/em&gt; into &lt;em&gt;~/bin/&lt;/em&gt; and make it executable. Drop &lt;em&gt;gitstatus.sh&lt;/em&gt; in your home directory as &lt;em&gt;.gitstatus&lt;/em&gt; and add the following line to your &lt;em&gt;.bashrc&lt;/em&gt;&lt;/p&gt;
&lt;pre class="literal-block"&gt;
. ~/.gitstatus
&lt;/pre&gt;
&lt;p&gt;You may of course modify the &lt;em&gt;GITPREFIX&lt;/em&gt; and &lt;em&gt;GITSUFFIX&lt;/em&gt; variables in &lt;em&gt;gitstatus.sh&lt;/em&gt; to fit your needs.&lt;/p&gt;&lt;/div&gt;</description><category>bash</category><category>git</category><category>perl</category><guid>https://haui.pdes-net.org/posts/custom_git_prompt/</guid><pubDate>Wed, 05 Mar 2014 10:00:00 GMT</pubDate></item><item><title>Find files in APT packages</title><link>https://haui.pdes-net.org/posts/find_files_in_apt_packages/</link><dc:creator>Haui</dc:creator><description>&lt;div&gt;&lt;p&gt;On a Debian based Linux distribution like Crunchbang, it's quite easy to determine which package a specific file on the system belongs to.
Issuing &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;dpkg-query&lt;/span&gt; &lt;span class="pre"&gt;-S&lt;/span&gt; /usr/lib32/gconv/UNICODE.so&lt;/tt&gt; on my desktop system tells me, that the file belongs to the &lt;em&gt;libc6-i386&lt;/em&gt; package - the 32 bit shared libraries for AMD64.&lt;/p&gt;
&lt;p&gt;Sometimes, however, it comes handy to know what package a file not present on the filesystem belongs to. One prominent example is the &lt;em&gt;tunctl&lt;/em&gt; program, that allows the
creation of TUN/TAP interfaces on Linux. A search for &lt;em&gt;tunctl&lt;/em&gt; via &lt;tt class="docutils literal"&gt;aptitude search tunctl&lt;/tt&gt; doesn't yield any results. That's where &lt;em&gt;apt-file&lt;/em&gt; comes into play. After installing it
via &lt;tt class="docutils literal"&gt;aptitude install &lt;span class="pre"&gt;apt-file&lt;/span&gt;&lt;/tt&gt; and updating the index with &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;apt-file&lt;/span&gt; update&lt;/tt&gt;, we can start a more advanced search. Using &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;apt-file&lt;/span&gt; find tunctl&lt;/tt&gt;, we get the info that the
file &lt;em&gt;/usr/sbin/tunctl&lt;/em&gt;  is included in the &lt;em&gt;uml-utilities&lt;/em&gt; package. To allow more sophisticated searches, &lt;em&gt;apt-file&lt;/em&gt; offers various advanced options, e.g., for case
insensitive searches based on regular expressions.&lt;/p&gt;&lt;/div&gt;</description><category>apt-file</category><category>Debian</category><category>linux</category><guid>https://haui.pdes-net.org/posts/find_files_in_apt_packages/</guid><pubDate>Thu, 12 Sep 2013 10:00:00 GMT</pubDate></item><item><title>Gnuplot</title><link>https://haui.pdes-net.org/posts/gnuplot/</link><dc:creator>Haui</dc:creator><description>&lt;div&gt;&lt;p&gt;Some years ago, when I had to create plots from measurement data for a student research project I first started using
&lt;a class="reference external" href="http://www.gnuplot.info/"&gt;Gnuplot&lt;/a&gt;. Although several powerful alternatives exist, I never felt the need to switch away from Gnuplot
and I'm still using it for all my plotting tasks. In almost all cases, these tasks consist of visualizing data that was acquired with
the help of other tools, such as &lt;a class="reference external" href="http://en.wikipedia.org/wiki/Iperf"&gt;Iperf&lt;/a&gt;. Sometimes this data is not suitable to be feed directly into Gnuplot, but requires some preprocessing first.
So my usual approach was to write a Bash or Perl script to preprocess and sanitize the collected data and then dynamically generate a Gnuplot script for this specific data set. Depending on
data, however, this felt like a cumbersome and suboptimal approach. The two scripts &lt;a class="reference external" href="http://pdes-net.org/x-haui/scripts/misc/gnuplot/res.pl"&gt;res.pl&lt;/a&gt;
and &lt;a class="reference external" href="http://pdes-net.org/x-haui/scripts/misc/gnuplot/wrapper.sh"&gt;wrapper.sh&lt;/a&gt; illustrate this point. The Perl script is called by the bash script and
transforms the input data into a whitespace seperated data set. The bash script then extracts several additional information, e.g. the axis labels. Then, a
Gnuplot script is generated and executed to finally create the plot.
Although this works, a more elegant way would have been the utilization of a gnuplot library, such as &lt;a class="reference external" href="http://search.cpan.org/~caidaperl/Chart-Graph-3.2/Graph/Gnuplot.pm"&gt;Chart::Graph::Gnuplot&lt;/a&gt;.
I've written the small demo script &lt;a class="reference external" href="http://pdes-net.org/x-haui/scripts/misc/gnuplot/frequency.pl"&gt;frequency.pl&lt;/a&gt; to illustrate the usage of the aforementioned library.
The script counts the character frequency of an input file and creates a plot like the following from the collected data.&lt;/p&gt;
&lt;img alt="/images/frequency.png" src="https://haui.pdes-net.org/images/frequency.png"&gt;
&lt;p&gt;All in all, the usage of this library feels much more comfortable, especially when dealing with poorly formatted input data that requires a great amount of preprocessing.
Of course, Gnuplot bindings for languages other than Perl do exist as well.&lt;/p&gt;&lt;/div&gt;</description><category>bash</category><category>Gnuplot</category><category>linux</category><category>perl</category><guid>https://haui.pdes-net.org/posts/gnuplot/</guid><pubDate>Fri, 06 Sep 2013 10:00:00 GMT</pubDate></item><item><title>Sudoku</title><link>https://haui.pdes-net.org/posts/sudoku/</link><dc:creator>Haui</dc:creator><description>&lt;div&gt;&lt;p&gt;Solving sudoku puzzles usually requires nothing more than a pen and some time. Solving 50 sudoku puzzles, however,
requires a huge amount of time. 50 sudoku puzzles...? Yep, to get the solution for &lt;a class="reference external" href="http://projecteuler.net/problem=96"&gt;Problem 96&lt;/a&gt; on ProjectEuler,
50 sudokus need to be solved first. I've solved my first 49 problems on ProjectEuler a few years ago and recently rediscovered the website. So I started
with the sudoku problem and got the solution quite fast by using a simple brute force algorithm. I'm not going to post the solution for the problem,
but just the C++ code for my &lt;a class="reference external" href="http://pdes-net.org/x-haui/scripts/misc/sudokusolver.cpp"&gt;sudoku solver&lt;/a&gt;. After compiling the program with
&lt;tt class="docutils literal"&gt;g++ &lt;span class="pre"&gt;-std=c++11&lt;/span&gt; &lt;span class="pre"&gt;-O6&lt;/span&gt; &lt;span class="pre"&gt;-o&lt;/span&gt; sudokusolver sudokusolver.cpp&lt;/tt&gt;, sudokus given in an input file are solved with a simple recursion based algorithm.
An example is given below:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Trying to solve:
---------
003020600
900305001
001806400
008102900
700000008
006708200
002609500
800203009
005010300
---------
    |
    V
---------
483921657
967345821
251876493
548132976
729564138
136798245
372689514
814253769
695417382
---------
&lt;/pre&gt;
&lt;p&gt;The sudokus in the input file must consist of 9 consecutive lines containing the initial values inside the sudoku. Blank fields are represented by zero.
Multiple sudokus have to be separated by at least one dash in a single line.&lt;/p&gt;&lt;/div&gt;</description><category>ProjectEuler</category><category>Sudoku</category><guid>https://haui.pdes-net.org/posts/sudoku/</guid><pubDate>Fri, 06 Sep 2013 10:00:00 GMT</pubDate></item><item><title>Encoding Hell</title><link>https://haui.pdes-net.org/posts/encoding_hell/</link><dc:creator>Haui</dc:creator><description>&lt;div&gt;&lt;p&gt;Some years ago, when I first started using Linux, my system's locale was set to ISO8859-15. Over the years
I switched to UTF-8 of course. Though I now tend to use &lt;a class="reference external" href="http://pdes-net.org/x-haui/blog/html/2009/06/15/rename.html"&gt;proper filenames&lt;/a&gt;
for all my files, I come across witnesses of the old days, when I was littering my filenames with crappy [1], or even
crappier[2] characters, once in a while.
In my defence I have to say, that lots of these files carry names I didn't choose by myself, because they were auto-generated by CD rippers or
other software. Some files even date back to the time when I was exclusively using Windows and didn't care about filenames or encodings at all.&lt;/p&gt;
&lt;p&gt;Using the command from my posting about rename can usually fix all these filenames, but this might not always be what you want - a folder named
&lt;em&gt;glückliche_kühe&lt;/em&gt; is renamed to &lt;em&gt;gl_ckliche_k_he&lt;/em&gt; - not a perfect solution. What you might really want is to convert the filename from one
encoding to another, and good for you, somebody already did all the work and created a nifty little program called &lt;a class="reference external" href="http://www.j3e.de/linux/convmv/man/"&gt;convmv&lt;/a&gt;,
which supports 124 different encodings. The syntax is quiet easy:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
convmv -f iso8859-15 -t utf-8 *
&lt;/pre&gt;
&lt;p&gt;This would show which filenames in the current directory would have been converted from ISO8859-15 to UTF-8 if
you'd explicitly added the &lt;em&gt;- -notest&lt;/em&gt; option to the command-line.&lt;/p&gt;
&lt;p&gt;Thats the easy way, but let's assume you want to work with the &lt;em&gt;glückliche_kühe&lt;/em&gt; folder without
re-encoding the filename. Be aware of the fact, that some graphical file mangers may not handle filenames with
wrong encodings correctly. On my system, krusader couldn't open the ISO8859-15 encoded test folder, while gentoo (yes, this is indeed a file manger)
only displayed a warning. Additionally, there are situations, where no graphical environment is available at all.&lt;/p&gt;
&lt;p&gt;So, the far more interesting question is how to work with these files in a shell environment. The naive approach &lt;em&gt;cd glückliche_kühe&lt;/em&gt;
fails because the ISO8859-15 &lt;em&gt;ü&lt;/em&gt; is different from the UTF-8 &lt;em&gt;ü&lt;/em&gt; - our UTF-8 environment will correctly respond that there's no such folder.
A simple &lt;em&gt;ls&lt;/em&gt; will show a question mark for every crappier character in the filename and that's not exactly useful either, since we can't uniquely identify
the names this way. How would you change into &lt;em&gt;glückliche_kühe&lt;/em&gt; if there's also a folder called &lt;em&gt;gl_ckliche_k_he&lt;/em&gt;?
Typing &lt;em&gt;cd gl?ckliche_k?he&lt;/em&gt; is ambiguous since the question mark is treated as a special character by the Bash and expands to match any character.
Depending on the situation, this might or might not work as the Bash returns a list of all matching filenames for your input sequence &lt;em&gt;gl?ckliche_k?he&lt;/em&gt;.
One solution is to run &lt;em&gt;ls&lt;/em&gt; with the &lt;em&gt;-b&lt;/em&gt; option - this way, we instruct &lt;em&gt;ls&lt;/em&gt; to print unprintable characters as octal escapes:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
user@localhost /tmp/test $ ls -b
gl\374ckliche_k\374he
&lt;/pre&gt;
&lt;p&gt;This gives us something to work with. &lt;em&gt;echo&lt;/em&gt; can interpret these escape sequences and Bash's command substitution offers
a way to use &lt;em&gt;echo&lt;/em&gt;'s output as a value.&lt;/p&gt;
&lt;pre class="literal-block"&gt;
user@localhost /tmp/test $ cd "$(echo -e "gl\0374ckliche_k\0374he")"
user@localhost /tmp/test/glückliche_kühe $ pwd
/tmp/test/glückliche_kühe
&lt;/pre&gt;
&lt;p&gt;There are three things you should note here. First of all, in order to mark the escaped sequences as octal numbers, you need to add
a leading zero in the way I did in this example. Secondly, the &lt;em&gt;-e&lt;/em&gt; parameter is required to tell &lt;em&gt;echo&lt;/em&gt; to interpret
escaped sequences rather than printing the literal characters. The last thing is not exactly related to the encoding problem, but
always worth mentioning: &lt;a class="reference external" href="http://mywiki.wooledge.org/BashPitfalls#cd_.24.28dirname_.22.24f.22.29"&gt;the quotes are supposed to be there for a reason&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;So, now the encoding hell shouldn't look so scary anymore - at least not with respect to filenames. ;)&lt;/p&gt;
&lt;p&gt;Oh, and by the way, if you just want to check if you got any wrongly encoded filenames, this one-liner could help:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
find . -print0 | xargs -0 ls -db  | egrep "\\\[0-9\]{3}"
&lt;/pre&gt;
&lt;p&gt;[1] every character c, that is not in [a-zA-Z0-9._-]+&lt;/p&gt;
&lt;p&gt;[2] every character c, where utf8(c) != iso8859-15(c)&lt;/p&gt;&lt;/div&gt;</description><category>bash</category><category>encodings</category><category>linux</category><category>UTF-8</category><guid>https://haui.pdes-net.org/posts/encoding_hell/</guid><pubDate>Wed, 26 Sep 2012 10:00:00 GMT</pubDate></item><item><title>Rebuilding Debian packages</title><link>https://haui.pdes-net.org/posts/rebuilding_debian_packages/</link><dc:creator>Haui</dc:creator><description>&lt;div&gt;&lt;p&gt;Most of the software installed via &lt;em&gt;APT&lt;/em&gt;, Debian's package management system, runs perfectly fine
without any reason to complain. In some rare cases however, you might find yourself unsatisfied with
a package and have the itch to recompile it. For me, Debian's package for the Vim text editor is
one of these cases - the package available in the repositories was compiled without support for
the Perl interface. Of course, one could just visit vim.org, download the latest sources for Vim,
check the build requirements and install the missing libraries manually, call &lt;em&gt;./configure&lt;/em&gt; with the
correct parameters, compile the program and finally install it. Apart from being a quiet cumbersome procedure,
APT would not include this version of Vim into its database.
So, there has to be a better way to do this, and indeed, there is one.&lt;/p&gt;
&lt;p&gt;First of all, two packages and their dependencies are required for the next steps -  &lt;em&gt;build-essential&lt;/em&gt; and &lt;em&gt;devscripts&lt;/em&gt;.
They should be available in the repositories and can be installed as usual:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
su root -c "apt-get install build-essential devscripts"
&lt;/pre&gt;
&lt;p&gt;Once this is done, we'll change to our developer directory and download the sources for Vim
as well as the build dependencies.&lt;/p&gt;
&lt;pre class="literal-block"&gt;
mkdir -p ~/devel
cd ~/devel
apt-get source vim
apt-get build-dep vim
&lt;/pre&gt;
&lt;p&gt;When this is finished, a new directory &lt;em&gt;~/devel/vim-*VERSION*/&lt;/em&gt; should contain the sources for Vim as well as Debian specific
patches/configurations. Now, one could do all kinds of changes tho Vim's source code, but we just want to
to modify a configuration parameter. This is done by editing the &lt;em&gt;debin/rules&lt;/em&gt; file, which contains the default configure
flags for the package. The flags defined here are passed to the &lt;em&gt;configure script&lt;/em&gt; during the build process.
The Perl interface can be enabled by swapping a parameter from &lt;em&gt;--disable-perlinterp&lt;/em&gt; to &lt;em&gt;--enable-perlinterp&lt;/em&gt;.
Thereafter, you just need to invoke the following command and wait until the compilation process is finished:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
debuild -us -uc
&lt;/pre&gt;
&lt;p&gt;If no errors occurred, you'll find several &lt;em&gt;*.deb&lt;/em&gt; files inside your &lt;em&gt;~/devel&lt;/em&gt; directory. To install
Vim, just pick &lt;em&gt;vim-*VERSION*_*ARCH*.deb&lt;/em&gt; and install it via dpkg, e.g. on my box:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
su root -c "dpkg -i vim_7.3.547-4_amd64.deb"
&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;vim --version&lt;/em&gt; should now show &lt;em&gt;+perl&lt;/em&gt; instead of &lt;em&gt;-perl&lt;/em&gt;, and &lt;em&gt;:perldo&lt;/em&gt; is finally available. ;)&lt;/p&gt;&lt;/div&gt;</description><category>apt</category><category>Debian</category><category>vim</category><guid>https://haui.pdes-net.org/posts/rebuilding_debian_packages/</guid><pubDate>Wed, 12 Sep 2012 10:00:00 GMT</pubDate></item><item><title>Delete all files except one </title><link>https://haui.pdes-net.org/posts/delete_all_files_except_one_/</link><dc:creator>Haui</dc:creator><description>&lt;div&gt;&lt;p&gt;A couple of days I was asked if knew an easy way to delete all but one files in a directory.
If you didn't already guess it from this blog entry's title, there is a simple way - or to be more precise -
there are several ways. The first one is quiet straightforward and uses the find command:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
find . -not -name do_not_delete_me -delete
&lt;/pre&gt;
&lt;p&gt;This works recursively and also preserves files named &lt;em&gt;do_not_delete_me&lt;/em&gt; contained in sub-folders of the
current directory:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
user@host /tmp/test $ ls -R
.:
a  b  c  do_not_delete_me  foo

./a:
foo

./b:
bar  do_not_delete_me

./c:
baz
user@host /tmp/test $ find . -not -name do_not_delete_me -delete
find: cannot delete `./b': Directory not empty
user@host /tmp/test $ ls -R
.:
b  do_not_delete_me

./b:
do_not_delete_me
&lt;/pre&gt;
&lt;p&gt;As you can see, &lt;em&gt;find&lt;/em&gt; tries to delete the folder &lt;em&gt;b&lt;/em&gt; but fails because the folder is not empty.
If you don't care for files in sub-directories, it gets a bit more complicated with find:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
find . -mindepth 1 -maxdepth 1 -not -name do_not_delete_me -exec rm -rf -- {} +
&lt;/pre&gt;
&lt;p&gt;The &lt;em&gt;-mindepth&lt;/em&gt;/&lt;em&gt;-maxdepth&lt;/em&gt; parameters tell &lt;em&gt;find&lt;/em&gt; to ignore sub-directories, because we're not interested
in their contents. This should also save some execution time - especially if the directory hierarchy is really deep.&lt;/p&gt;
&lt;p&gt;While this works well, Bash's pattern matching offers an easier solution for this:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
rm -rf !(do_not_delete_me)
&lt;/pre&gt;
&lt;p&gt;As the manpage explains, the text enclosed by brackets is considered to be a pattern list, i.e.
constructs like &lt;em&gt;!(*.jpg|*.png)&lt;/em&gt; are perfectly valid.
If you don't care for files in sub-directories, this might be the preferred way - it's shorter and
maybe even faster than the solutions using &lt;em&gt;find&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;No matter which solution you choose, &lt;a class="reference external" href="http://mywiki.wooledge.org/BashPitfalls#for_i_in_.24.28ls_.2A.mp3.29"&gt;refrain from error-prone constructs&lt;/a&gt; like &lt;strong&gt;rm -rf `ls | grep -v do_not_delete_me`&lt;/strong&gt;.&lt;/p&gt;&lt;/div&gt;</description><category>bash</category><category>delete</category><category>find</category><category>rm</category><guid>https://haui.pdes-net.org/posts/delete_all_files_except_one_/</guid><pubDate>Fri, 07 Sep 2012 10:00:00 GMT</pubDate></item></channel></rss>