<?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>Max Mehl (Code)</title>
    <link>https://mehl.mx/tags/code/</link>
    <description>Recent content in Code on Max Mehl</description>
    <generator>Hugo</generator>
    <language>en-GB</language>
    <lastBuildDate>Thu, 07 Nov 2024 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://mehl.mx/tags/code/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>INWX DNS Recordmaster - Manage your DNS nameserver records via files in Git</title>
      <link>https://mehl.mx/blog/2024/inwx-dns-recordmaster-manage-your-dns-nameserver-records-via-files-in-git/</link>
      <pubDate>Thu, 07 Nov 2024 00:00:00 +0000</pubDate>
      <guid>https://mehl.mx/blog/2024/inwx-dns-recordmaster-manage-your-dns-nameserver-records-via-files-in-git/</guid>
      <description>&lt;p&gt;I own and manage 30+ domains at &lt;a href=&#34;https://www.inwx.com/&#34;&gt;INWX&lt;/a&gt;, a large and professional domain registrar. Although INWX has a somewhat decent web interface, it became a burden for me to keep an overview of each domain&amp;rsquo;s sometimes dozens of records. Especially when e.g. changing an IP address for more than one domain, it caused multiple error-prone clicks and copy/pastes that couldn&amp;rsquo;t be reverted in the worst case. This is why I created &lt;a href=&#34;https://github.com/mxmehl/inwx-dns-recordmaster&#34;&gt;&lt;strong&gt;INWX DNS Recordmaster&lt;/strong&gt;&lt;/a&gt; which I will shortly present here.&lt;/p&gt;</description>
      <content:encoded>&lt;p&gt;I own and manage 30+ domains at &lt;a href=&#34;https://www.inwx.com/&#34;&gt;INWX&lt;/a&gt;, a large and professional domain registrar. Although INWX has a somewhat decent web interface, it became a burden for me to keep an overview of each domain&amp;rsquo;s sometimes dozens of records. Especially when e.g. changing an IP address for more than one domain, it caused multiple error-prone clicks and copy/pastes that couldn&amp;rsquo;t be reverted in the worst case. This is why I created &lt;a href=&#34;https://github.com/mxmehl/inwx-dns-recordmaster&#34;&gt;&lt;strong&gt;INWX DNS Recordmaster&lt;/strong&gt;&lt;/a&gt; which I will shortly present here.&lt;/p&gt;&#xA;&lt;p&gt;If you are an INWX customer, you can use this tool to manage all your DNS records in YAML files. Ideally, you will store these files in a Git repository which you can use to track changes and roll back in case of a mistake. Having one file per domain provides you a number of further advantages:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;You can easily copy/paste records from other domains, e.g. for &lt;code&gt;SPF&lt;/code&gt;, &lt;code&gt;DKIM&lt;/code&gt; or &lt;code&gt;NS&lt;/code&gt; records&lt;/li&gt;&#xA;&lt;li&gt;Overall search/replace of certain values becomes much easier, e.g. of IP addresses&lt;/li&gt;&#xA;&lt;li&gt;You can prepare larger changes offline and can synchronise once you feel it&amp;rsquo;s done&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;INWX DNS Recordmaster takes care of making the required changes of the live records so that it matches the local state. This is done via the INWX API, ensuring that the amount of API calls is minimal.&lt;/p&gt;&#xA;&lt;p&gt;This even allows you to set up a pipeline that takes care of the synchronisation&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;wait-there-is-more&#34;&gt;Wait, there is more&lt;/h2&gt;&#xA;&lt;p&gt;As written above, I already had a large stack of domains that I previously managed via the web interface. This is why some additional convenience features found their way into the tool.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;You can convert all records of an existing and already configured domain at INWX into the file format. This made onboarding my 30+ domains a matter of a few minutes.&lt;/li&gt;&#xA;&lt;li&gt;On a global or per-domain level, you can ignore certain record types. For example, if you don&amp;rsquo;t want to touch any &lt;code&gt;NS&lt;/code&gt; records, you can configure that. By default, &lt;code&gt;SOA&lt;/code&gt; records are ignored. You may even ignore all live records that don&amp;rsquo;t exist in your local configuration.&lt;/li&gt;&#xA;&lt;li&gt;Of course, you can make a dry run to see which effects your configuration will have in practice.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Did I miss something to make it more productive for you? Let me know!&lt;/p&gt;&#xA;&lt;h2 id=&#34;install-use-contribute&#34;&gt;Install, use, contribute&lt;/h2&gt;&#xA;&lt;p&gt;You are welcome to &lt;a href=&#34;https://github.com/mxmehl/inwx-dns-recordmaster?tab=readme-ov-file#install&#34;&gt;install this tool&lt;/a&gt;, it&amp;rsquo;s Free and Open Source Software after all. All you need is Python installed.&lt;/p&gt;&#xA;&lt;p&gt;One of the tool&amp;rsquo;s users is the &lt;a href=&#34;https://openrailassociation.org&#34;&gt;OpenRail Association&lt;/a&gt; which manages some of its domains with this program and &lt;a href=&#34;https://github.com/OpenRailAssociation/openrail-dns&#34;&gt;published its configuration&lt;/a&gt;. This is a prime example of how organisation can make the management of records transparent and easy to change at least internally, if not even externally.&lt;/p&gt;&#xA;&lt;p&gt;While the tool is not perfect, it already is a huge gain for efficiency and stability of my IT operations, and it already proves its capabilities for other users. To reach the remaining 20% to perfection (that will take 80% of the time, as always), you are most welcome to add issues with enhancement proposals, and if possible, also pull requests.&lt;/p&gt;&#xA;&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;&#xA;&lt;hr&gt;&#xA;&lt;ol&gt;&#xA;&lt;li id=&#34;fn:1&#34;&gt;&#xA;&lt;p&gt;For example, see the &lt;a href=&#34;https://github.com/OpenRailAssociation/openrail-dns/blob/main/.github/workflows/sync-records.yaml&#34;&gt;workflow file of the OpenRail Association&lt;/a&gt;.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;/div&gt;&#xA;</content:encoded>
    </item>
    <item>
      <title>Seafile Mirror - Simple automatic backup of your Seafile libraries</title>
      <link>https://mehl.mx/blog/2023/seafile-mirror-simple-automatic-backup-of-your-seafile-libraries/</link>
      <pubDate>Fri, 22 Sep 2023 00:00:00 +0000</pubDate>
      <guid>https://mehl.mx/blog/2023/seafile-mirror-simple-automatic-backup-of-your-seafile-libraries/</guid>
      <description>&lt;p&gt;I have been using &lt;a href=&#34;https://www.seafile.com/&#34;&gt;Seafile&lt;/a&gt; for years to host and&#xA;synchronise files on my own server. It&amp;rsquo;s fast and reliable, especially when&#xA;dealing with a large number and size of files. But making reliable backups of&#xA;all its files isn&amp;rsquo;t so trivial. This is because the files are stored in a layout&#xA;similar to bare Git repositories, and Seafile&amp;rsquo;s headless tool, seafile-cli,&#xA;is&amp;hellip; suboptimal. So I created what started out as a wrapper for it and ended up&#xA;as a full-blown tool for automatically synchronising your libraries to a backup&#xA;location: &lt;a href=&#34;https://src.mehl.mx/mxmehl/seafile-mirror&#34;&gt;&lt;strong&gt;Seafile Mirror&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;</description>
      <content:encoded>&lt;p&gt;I have been using &lt;a href=&#34;https://www.seafile.com/&#34;&gt;Seafile&lt;/a&gt; for years to host and&#xA;synchronise files on my own server. It&amp;rsquo;s fast and reliable, especially when&#xA;dealing with a large number and size of files. But making reliable backups of&#xA;all its files isn&amp;rsquo;t so trivial. This is because the files are stored in a layout&#xA;similar to bare Git repositories, and Seafile&amp;rsquo;s headless tool, seafile-cli,&#xA;is&amp;hellip; suboptimal. So I created what started out as a wrapper for it and ended up&#xA;as a full-blown tool for automatically synchronising your libraries to a backup&#xA;location: &lt;a href=&#34;https://src.mehl.mx/mxmehl/seafile-mirror&#34;&gt;&lt;strong&gt;Seafile Mirror&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;my-requirements&#34;&gt;My requirements&lt;/h2&gt;&#xA;&lt;p&gt;Of course, you could just take snapshots of the whole server, or copy the raw&#xA;Seafile data files and import them into a newly created Seafile instance as a&#xA;disaster recovery, but I want to be able to &lt;strong&gt;directly access the current&#xA;state of the files&lt;/strong&gt; whenever I need them in case of an emergency.&lt;/p&gt;&#xA;&lt;p&gt;It was also important for me to have a &lt;strong&gt;snapshot&lt;/strong&gt;, not just another real-time&#xA;sync of a library. This is because I also want to have a backup in case I (or an&#xA;attacker) mess up a Seafile library. A real-time sync would immediately fetch&#xA;that failed state.&lt;/p&gt;&#xA;&lt;p&gt;I also want to take a snapshot at a &lt;strong&gt;configurable interval&lt;/strong&gt;. Some libraries&#xA;should be synchronised more often than others. For example, my picture albums do&#xA;not change as often as my miscellaneous documents, but they use at least 20&#xA;times the disk space and therefore network traffic when running a full sync.&lt;/p&gt;&#xA;&lt;p&gt;Also, the backup service must have &lt;strong&gt;read-only access&lt;/strong&gt; to the files.&lt;/p&gt;&#xA;&lt;p&gt;A version controlled backup of the backup (i.e. the plain files) wasn&amp;rsquo;t in&#xA;scope. I handle this separately by backing up my backup location, which also&#xA;contains similar backups of other services and machines. For this reason, my&#xA;current solution does not do incremental backups, even though this may be&#xA;relevant for other use cases.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-problems&#34;&gt;The problems&lt;/h2&gt;&#xA;&lt;p&gt;Actually, &lt;a href=&#34;https://help.seafile.com/syncing_client/linux-cli/&#34;&gt;seafile-cli&lt;/a&gt;&#xA;should have been everything you&amp;rsquo;d need to fulfill the requirements. But no. It&#xA;turned out that this tool has a number of fundamental issues:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;You can make the host the tool is running on a sync peer. However, it easily&#xA;leads to sync errors if the user just has read-only permissions to the&#xA;library.&lt;/li&gt;&#xA;&lt;li&gt;You can also download a library but then again it may lead to strange sync&#xA;errors.&lt;/li&gt;&#xA;&lt;li&gt;It requires a running daemon which crashes irregularly during larger sync&#xA;tasks or has other issues.&lt;/li&gt;&#xA;&lt;li&gt;Download/sync intervals cannot be set manually.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;the-solution&#34;&gt;The solution&lt;/h2&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://src.mehl.mx/mxmehl/seafile-mirror&#34;&gt;seafile-mirror&lt;/a&gt; takes care of all&#xA;these stumbling blocks:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;It downloads/syncs defined libraries in customisable intervals&lt;/li&gt;&#xA;&lt;li&gt;It de-syncs libaries immediately after they have been downloaded to avoid sync&#xA;errors&lt;/li&gt;&#xA;&lt;li&gt;You can force-re-sync a library even if its re-sync interval hasn&amp;rsquo;t reached&#xA;yet&lt;/li&gt;&#xA;&lt;li&gt;Extensive informative and error logging is provided&lt;/li&gt;&#xA;&lt;li&gt;Of course created with automation in mind so you can run it in cronjobs or&#xA;systemd triggers&lt;/li&gt;&#xA;&lt;li&gt;And as explained, it deals with the numerous caveats of &lt;code&gt;seaf-cli&lt;/code&gt; and Seafile&#xA;in general&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Full installation and usage documentation can be found in the project&#xA;repository. Installation is as simple as running &lt;code&gt;pip3 install seafile-mirror&lt;/code&gt;,&#xA;and a sample configuration is provided.&lt;/p&gt;&#xA;&lt;p&gt;In my setup, I run this application on a headless server with systemd under a&#xA;separate user account. Therefore the systemd service needs to be set up first.&#xA;This is also covered in the tool&amp;rsquo;s documentation. And as an Ansible power user,&#xA;I also provide an &lt;a href=&#34;https://src.mehl.mx/mxmehl/seafile-mirror-ansible&#34;&gt;Ansible&#xA;role&lt;/a&gt; that does all the setup&#xA;and configuration.&lt;/p&gt;&#xA;&lt;h2 id=&#34;possible-next-steps&#34;&gt;Possible next steps&lt;/h2&gt;&#xA;&lt;p&gt;The tool has been running every day since a couple of months without any issues.&#xA;However, I could imagine a few more features to be helpful for more people:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Support of login tokens: Currently, only user/password auth is supported which&#xA;is fine for my use-case as it&amp;rsquo;s just a read-only user. This wouldn&amp;rsquo;t be hard&#xA;to fix either, seafile-cli supports it (at least in theory).&#xA;(&lt;a href=&#34;https://src.mehl.mx/mxmehl/seafile-mirror/issues/2&#34;&gt;#2&lt;/a&gt;)&lt;/li&gt;&#xA;&lt;li&gt;Support of encrypted libraries: Shouldn&amp;rsquo;t be a big issue, it would require&#xA;passing the password to the underlying seafile-cli command.&#xA;(&lt;a href=&#34;https://src.mehl.mx/mxmehl/seafile-mirror/issues/3&#34;&gt;#3&lt;/a&gt;)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;If you have encountered problems or would like to point out the need for&#xA;specific features, please feel free to contact me or comment on the Mastodon&#xA;post. I&amp;rsquo;d also love to hear if you&amp;rsquo;ve become a happy user of the tool 😊.&lt;/p&gt;&#xA;</content:encoded>
    </item>
    <item>
      <title>Docker2Caddy - An automatic Reverse Proxy for Docker containers</title>
      <link>https://mehl.mx/blog/2022/docker2caddy-an-automatic-reverse-proxy-for-docker-containers/</link>
      <pubDate>Mon, 25 Apr 2022 00:00:00 +0000</pubDate>
      <guid>https://mehl.mx/blog/2022/docker2caddy-an-automatic-reverse-proxy-for-docker-containers/</guid>
      <description>&lt;p&gt;So you have a number of Docker containers running web services which you would&#xA;like to expose to the outside? Well, you probably will at least have considered&#xA;a reverse proxy already. Doing this manually for one, two or even five&#xA;containers may be feasible, but everything above that will be a PITA for sure.&#xA;At the &lt;a href=&#34;https://fsfe.org&#34;&gt;FSFE&lt;/a&gt; we ran into the same issue with our &lt;a href=&#34;https://git.fsfe.org/fsfe-system-hackers/container-server/&#34;&gt;own&#xA;distributed container&#xA;infrastructure&lt;/a&gt; at&#xA;and crafted a neat solution that I would like to present to you in the next few&#xA;minutes.&lt;/p&gt;</description>
      <content:encoded>&lt;p&gt;So you have a number of Docker containers running web services which you would&#xA;like to expose to the outside? Well, you probably will at least have considered&#xA;a reverse proxy already. Doing this manually for one, two or even five&#xA;containers may be feasible, but everything above that will be a PITA for sure.&#xA;At the &lt;a href=&#34;https://fsfe.org&#34;&gt;FSFE&lt;/a&gt; we ran into the same issue with our &lt;a href=&#34;https://git.fsfe.org/fsfe-system-hackers/container-server/&#34;&gt;own&#xA;distributed container&#xA;infrastructure&lt;/a&gt; at&#xA;and crafted a neat solution that I would like to present to you in the next few&#xA;minutes.&lt;/p&gt;&#xA;&lt;p&gt;The result is&#xA;&lt;a href=&#34;https://git.fsfe.org/fsfe-system-hackers/docker2caddy/&#34;&gt;Docker2Caddy&lt;/a&gt; that&#xA;provides a workflow in which you can spin up new containers anytime (e.g. via a&#xA;CI) and the reverse proxy will just do the rest for you magically.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-assumptions&#34;&gt;The assumptions&lt;/h2&gt;&#xA;&lt;p&gt;Let&amp;rsquo;s assume you want to go with reverse proxies to make your web services&#xA;accessible via ports 80 and 443. There are other possibilities, and in more&#xA;complex environments there may be already integrated solutions, but for this&#xA;article we&amp;rsquo;ll wade in a rather simple environment spun up with &lt;code&gt;docker-compose&lt;/code&gt;&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Let&amp;rsquo;s also assume you care about security and go with a rootless installation of&#xA;Docker. So the daemon will run as an unprivileged user. That&amp;rsquo;s possible but much&#xA;more complex than the default rootful installation&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;. Because of this, a few&#xA;other solutions will not work, we&amp;rsquo;ll check that later.&lt;/p&gt;&#xA;&lt;p&gt;Finally, each container shall at least have one separate domain assigned to it&#xA;for which you obviously want to have a valid certificate, e.g. by Let&amp;rsquo;s Encrypt.&lt;/p&gt;&#xA;&lt;p&gt;In the examples below, we have two containers running, each running a webserver&#xA;listening to port &lt;code&gt;8080&lt;/code&gt;. The first container shall be available via&#xA;&lt;code&gt;first.com&lt;/code&gt;, the second via &lt;code&gt;second.net&lt;/code&gt;. The latter shall also be available via&#xA;&lt;code&gt;www.second.net&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-problems&#34;&gt;The problems&lt;/h2&gt;&#xA;&lt;p&gt;In the described scenario, there are a number of problem for automating the&#xA;configuration of the reverse proxy in order to direct a domain to the correct&#xA;container, starting with &lt;strong&gt;container discovery&lt;/strong&gt; to &lt;strong&gt;IPv6 routing&lt;/strong&gt; to&#xA;&lt;strong&gt;handling offline containers&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;The reverse proxy has to be able to discover the currently running containers&#xA;and ideally monitor for changes regularly so that a newly created container with&#xA;a new domain is reachable within a short time without manual intervention.&lt;/p&gt;&#xA;&lt;p&gt;Before Docker2Caddy we have used&#xA;&lt;a href=&#34;https://github.com/nginx-proxy/nginx-proxy&#34;&gt;nginx-proxy&lt;/a&gt; combined with&#xA;&lt;a href=&#34;https://github.com/nginx-proxy/acme-companion&#34;&gt;acme-companion&lt;/a&gt; (formerly known&#xA;as &lt;em&gt;docker-letsencrypt-nginx-proxy-companion&lt;/em&gt;). These are Docker containers that&#xA;query all containers connected to the &lt;code&gt;bridge&lt;/code&gt; Docker network. For this to work,&#xA;the containers have to run with environment variables indicating the desired&#xA;domains and local ports that shall be proxied.&lt;/p&gt;&#xA;&lt;p&gt;In a rootless Docker setup this finally reaches its limits although discovery&#xA;still works. But already before that we did not like the fact that we had to&#xA;connect containers to the bridge network upon creation and therefore lost a bit&#xA;more isolation (which is dubious in Docker anyway).&lt;/p&gt;&#xA;&lt;p&gt;Now, with rootless, IPv6 was the turning point. Even in rootful Docker setups,&#xA;IPv6 – a 20+ years old, well defined standard protocol – is a pain in the butt.&#xA;But with rootless, the FSFE System Hackers team did not manage to get IPv6&#xA;working in containers to the degree that we needed. While IPv6 traffic reached&#xA;the &lt;code&gt;nginx-proxy&lt;/code&gt;, it was then treated as IPv4 traffic with the internal Docker&#xA;IP address. That bits you ultimately if you limit requests based on IP&#xA;addresses, e.g. for signups or payments. All traffic via IPv6 will be treated&#xA;as the same internal IPv4 address, therefore triggering the limits regularly.&lt;/p&gt;&#xA;&lt;p&gt;The easiest solution therefore is to use a reverse proxy running on the host&#xA;system, not as a Docker container with its severe limitations. While the first&#xA;intuition lead us to nginx, we decided to go with&#xA;&lt;a href=&#34;https://caddyserver.com/&#34;&gt;Caddy&lt;/a&gt;. The main advantages we saw are that a virtual&#xA;host in Caddy is very simple to configure and that TLS certificates are&#xA;generated and maintained automatically without extra dependencies like&#xA;&lt;code&gt;certbot&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;In this setup, containers would need to open their webserver port to the host.&#xA;This “public” port has to be unique per host, but the internal port can stay the&#xA;same, e.g. port &lt;code&gt;1234&lt;/code&gt; could be mapped to port &lt;code&gt;8080&lt;/code&gt; inside the container. In&#xA;Caddy you would then configure the domain &lt;code&gt;first.org&lt;/code&gt; to forward to&#xA;&lt;code&gt;localhost:1234&lt;/code&gt;. A more or less identical second example container could then&#xA;expose the port &lt;code&gt;5678&lt;/code&gt; to the host, again listen on &lt;code&gt;8080&lt;/code&gt; internally, and Caddy&#xA;would redirect &lt;code&gt;second.net&lt;/code&gt; and &lt;code&gt;www.second.net&lt;/code&gt; to &lt;code&gt;localhost:5678&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;But how does Caddy know about the currently running containers and the ports via&#xA;which they want to receive traffic? And how can we handle containers that are&#xA;unavailable, for instance because they crashed or have been deleted for good?&#xA;Docker2Caddy to the rescue!&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-solution&#34;&gt;The solution&lt;/h2&gt;&#xA;&lt;p&gt;I already concluded that Caddy is a suitable reverse proxy for the outlined use&#xA;case. But in order to be care-free, the configuration has to be generated&#xA;automatically. For this to work, I wrote a rather simple Python application&#xA;called &lt;a href=&#34;https://git.fsfe.org/fsfe-system-hackers/docker2caddy&#34;&gt;Docker2Caddy&lt;/a&gt;&#xA;that is kept running in the background via a systemd service and writes proper&#xA;logs that are also rotated nicely.&lt;/p&gt;&#xA;&lt;p&gt;This is how it works internally: it queries (in a configurable interval) the&#xA;Docker daemon for running containers. For each container it looks for specific&#xA;labels (that are also configurable), by default &lt;code&gt;proxy.host&lt;/code&gt;, &lt;code&gt;proxy.host_alias&lt;/code&gt;&#xA;and &lt;code&gt;proxy.port&lt;/code&gt;. If one or multiple containers are found – in our case two –&#xA;one Caddy configuration file per container is created. This is based on a freely&#xA;configurable Jinja2 template. If the configuration changed, e.g. by a new host,&#xA;Caddy will be reloaded and will create a TLS certificate if needed.&lt;/p&gt;&#xA;&lt;p&gt;But what happens if a container is unavailable? In Docker2Caddy you can&#xA;configure a grace period. Until this is reached, the Caddy configuration for the&#xA;container in question is not removed but could forward to a local or remote&#xA;error page. Only afterwards, the configuration is removed, and Caddy reloaded&#xA;subsequently.&lt;/p&gt;&#xA;&lt;p&gt;So, what makes Docker2Caddy special? I am biased but see a number of points:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;Simplicity&lt;/strong&gt;: fundamentally it&amp;rsquo;s a 188 pure lines of code Python script.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Configurability&lt;/strong&gt;: albeit it&amp;rsquo;s simplicity, it&amp;rsquo;s easy to configure for&#xA;various needs thanks to the templates and the support for rootless Docker&#xA;setups.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Adaptability&lt;/strong&gt;: it should be rather simple to make Docker2Caddy also work&#xA;for Podman, or even use different reverse proxies. Feel free to extend it&#xA;before I&amp;rsquo;ll do it myself someday ;)&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Performance&lt;/strong&gt;: while I did not perform before/after benchmarks, Caddy is&#xA;blazingly fast and will surely perform better on the host than in a limited&#xA;Docker container.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;If you&amp;rsquo;re facing the same challenges in your setup, please feel free to try it&#xA;out. Installation is quite simple and there&amp;rsquo;s even a &lt;a href=&#34;https://git.fsfe.org/fsfe-system-hackers/docker2caddy/src/branch/master/install.yml&#34;&gt;minimal Ansible&#xA;playbook&lt;/a&gt;.&#xA;If you have feedback, I appreciate reading it via comments on Mastodon (see&#xA;below), &lt;a href=&#34;https://mehl.mx/contact/&#34;&gt;email&lt;/a&gt;, or, if you have an FSFE account, as a new issue or&#xA;patch at the &lt;a href=&#34;https://git.fsfe.org/fsfe-system-hackers/docker2caddy&#34;&gt;main repo&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;&#xA;&lt;hr&gt;&#xA;&lt;ol&gt;&#xA;&lt;li id=&#34;fn:1&#34;&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://git.fsfe.org/fsfe-system-hackers/minimal-docker&#34;&gt;This is&lt;/a&gt; how a&#xA;very minimal Docker service in the FSFE infrastructure looks like. For&#xA;Docker2Caddy, only the &lt;code&gt;docker-compose.yml&lt;/code&gt; file with its labels is&#xA;relevant.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li id=&#34;fn:2&#34;&gt;&#xA;&lt;p&gt;If you&amp;rsquo;re interested in setting this up via Ansible, I can recommend the&#xA;&lt;a href=&#34;https://github.com/konstruktoid/ansible-docker-rootless&#34;&gt;ansible-docker-rootless&lt;/a&gt;&#xA;role which we integrated in our full-blown&#xA;&lt;a href=&#34;https://git.fsfe.org/fsfe-system-hackers/container-server/&#34;&gt;playbook&lt;/a&gt; for&#xA;the container servers.&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;/div&gt;&#xA;</content:encoded>
    </item>
    <item>
      <title>The power of git-sed</title>
      <link>https://mehl.mx/blog/2020/the-power-of-git-sed/</link>
      <pubDate>Tue, 28 Jul 2020 00:00:00 +0000</pubDate>
      <guid>https://mehl.mx/blog/2020/the-power-of-git-sed/</guid>
      <description>&lt;p&gt;In the recent weeks and months, the &lt;a href=&#34;https://fsfe.org/contribute/web/&#34;&gt;FSFE Web Team&lt;/a&gt; has been doing some heavy work on the &lt;a href=&#34;https://fsfe.org&#34;&gt;FSFE website&lt;/a&gt;. We moved and replaced thousands of files and their respective links to improve the structure of a historically grown website (19+ years, 23243 files, almost 39k commits). But how to do that most efficiently in a version controlled system like Git?&lt;/p&gt;</description>
      <content:encoded>&lt;p&gt;In the recent weeks and months, the &lt;a href=&#34;https://fsfe.org/contribute/web/&#34;&gt;FSFE Web Team&lt;/a&gt; has been doing some heavy work on the &lt;a href=&#34;https://fsfe.org&#34;&gt;FSFE website&lt;/a&gt;. We moved and replaced thousands of files and their respective links to improve the structure of a historically grown website (19+ years, 23243 files, almost 39k commits). But how to do that most efficiently in a version controlled system like Git?&lt;/p&gt;&#xA;&lt;p&gt;In our scenarios, the steps executed often looked like the following:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Move/rename a directory full of XML files representing website pages&lt;/li&gt;&#xA;&lt;li&gt;Find all links that pointed to this directory, and change them&lt;/li&gt;&#xA;&lt;li&gt;Create a rewrite rule&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;For the first step, using the included &lt;code&gt;git mv&lt;/code&gt; is perfectly fine.&lt;/p&gt;&#xA;&lt;p&gt;For the second, we would usually need a combination of &lt;code&gt;grep&lt;/code&gt; and &lt;code&gt;sed&lt;/code&gt;, e.g.:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;grep -lr &lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;/old/page.html&amp;#34;&lt;/span&gt; | xargs sed &lt;span style=&#34;color:#b44&#34;&gt;&amp;#39;s;/old/page.html;/new/page.html;g&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This has a few major flaws:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;In a Git repository, this also greps inside the &lt;code&gt;.git&lt;/code&gt; directory where we do not want to edit files directly&lt;/li&gt;&#xA;&lt;li&gt;The grep is slow in huge repositories&lt;/li&gt;&#xA;&lt;li&gt;The searched old link has to be mentioned two times, so hard for semi-manual replacement of a large number of links&lt;/li&gt;&#xA;&lt;li&gt;Depending on the Regex complexity we need, the command becomes long, and we need to take care of using the correct flags for grep and sed.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;git-sed-to-the-rescue&#34;&gt;git-sed to the rescue&lt;/h2&gt;&#xA;&lt;p&gt;After some research, I found &lt;code&gt;git-sed&lt;/code&gt;, basically a &lt;a href=&#34;https://github.com/tj/git-extras/blob/master/bin/git-sed&#34;&gt;Bash file in the git-extras project&lt;/a&gt;. With some modifications (&lt;a href=&#34;https://github.com/tj/git-extras/pull/859&#34;&gt;pull request pending&lt;/a&gt;) it&amp;rsquo;s the perfect tool for mass search and replacement.&lt;/p&gt;&#xA;&lt;p&gt;It solves all of the above problems:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;It uses &lt;code&gt;git grep&lt;/code&gt; that ignores the &lt;code&gt;.git/&lt;/code&gt; directory, and is much faster because it uses git&amp;rsquo;s index.&lt;/li&gt;&#xA;&lt;li&gt;The command is much shorter and easier to understand and write&lt;/li&gt;&#xA;&lt;li&gt;Flags are easy to add, and this only has to be done once per command&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;install&#34;&gt;Install&lt;/h3&gt;&#xA;&lt;p&gt;You can just &lt;a href=&#34;https://github.com/tj/git-extras/blob/master/Installation.md&#34;&gt;install the git-extras package&lt;/a&gt; which also contains a few other scripts.&lt;/p&gt;&#xA;&lt;p&gt;I opted for using it standalone, so downloaded the shell file, put it in a directory which is in my &lt;code&gt;$PATH&lt;/code&gt;, and removed one dependency on a script which is only available in git-extras (see my aforementioned &lt;a href=&#34;https://github.com/tj/git-extras/pull/859&#34;&gt;PR&lt;/a&gt;). So for instance, you could copy &lt;code&gt;git-sed.sh&lt;/code&gt; in &lt;code&gt;/usr/local/bin/&lt;/code&gt; and make it executable. To enable calling it via &lt;code&gt;git sed&lt;/code&gt;, put in your &lt;code&gt;~/.gitconfig&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;[alias]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#b44&#34;&gt;sed&lt;/span&gt; &lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#b44&#34;&gt;!sh git-sed.sh&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;usage&#34;&gt;Usage&lt;/h3&gt;&#xA;&lt;p&gt;After installing git-sed, the command above would become:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git sed -f g &lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;/old/page.html&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;/new/page.html&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;My modifications also allow people to use extended Regex, so things like reference captures, so I hope these will be merged soon. With this, some more advanced replacements are possible:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-style:italic&#34;&gt;# Use reference capture (save absolute link as \1)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git sed -f g &lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;http://fsfe.org(/.*?\.html)&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;https://fsfe.org\1&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-style:italic&#34;&gt;# Optional tokens (.html is optional here)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git sed -f g &lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;/old/page(\.html)?&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;/new/page.html&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And if you would like to limit git-sed to a certain directory, e.g. &lt;code&gt;news/&lt;/code&gt;, that&amp;rsquo;s also no big deal:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git sed -f g &lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;oldstring&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;newstring&amp;#34;&lt;/span&gt; -- news/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You may have notived the &lt;code&gt;-f&lt;/code&gt; flag with the &lt;code&gt;g&lt;/code&gt; argument. People used to sed know that &lt;code&gt;g&lt;/code&gt; replaces all appearances of the searched pattern in a file, not only the first one. You could also make it &lt;code&gt;gi&lt;/code&gt; if you want a case-insensitive search and replace.&lt;/p&gt;&#xA;&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;&#xA;&lt;p&gt;As you can see, using git-sed is really a time and nerve saver when doing mass changes on your repositories. Of course, there is also room for improvement. For instance, it could be useful to use the Perl Regex library (PCRE) for the grep and sed to also allow for look-aheads or look-behinds. I encourage you to try git-sed and make suggestions to upstream directly to improve this handy tool.&lt;/p&gt;&#xA;</content:encoded>
    </item>
    <item>
      <title>I love the hidden champions</title>
      <link>https://mehl.mx/blog/2020/i-love-the-hidden-champions/</link>
      <pubDate>Fri, 14 Feb 2020 00:00:00 +0000</pubDate>
      <guid>https://mehl.mx/blog/2020/i-love-the-hidden-champions/</guid>
      <description>&lt;p&gt;A few days ago I&amp;rsquo;ve sent an announcement email for today&amp;rsquo;s &lt;a href=&#34;https://ilovefs.org&#34;&gt;I Love Free Software Day&lt;/a&gt; to a large bunch of people. Most of the remarkably many replies have been positive and a pure joy to read, but some were a bit sceptical and critical. These came from Free Software contributors who are maintaining and helping projects that they think nobody knows and sees – not because these software projects are unused, but because they are small, a building block for other, more popular applications.&lt;/p&gt;</description>
      <content:encoded>&lt;p&gt;A few days ago I&amp;rsquo;ve sent an announcement email for today&amp;rsquo;s &lt;a href=&#34;https://ilovefs.org&#34;&gt;I Love Free Software Day&lt;/a&gt; to a large bunch of people. Most of the remarkably many replies have been positive and a pure joy to read, but some were a bit sceptical and critical. These came from Free Software contributors who are maintaining and helping projects that they think nobody knows and sees – not because these software projects are unused, but because they are small, a building block for other, more popular applications.&lt;/p&gt;&#xA;&lt;p&gt;When we ask people to participate in #ilovefs (this year for the 10th time in a row!) by expressing their gratitude to contributors of their favourite Free Software projects, many think about the applications they often use and come up with obvious ones like Mozilla&amp;rsquo;s Firefox and Thunderbird, LibreOffice, their Linux-based distribution, or CMSs like WordPress and Drupal. Not that I think this is not deserved, but what about the projects that actually form the foundations for these popular suites?&lt;/p&gt;&#xA;&lt;p&gt;I researched a bit on my own system (based on Arch Linux) and checked on how many packages some of the aforementioned applications depend (including dependencies of their dependencies)&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Firefox: 221&lt;/li&gt;&#xA;&lt;li&gt;Thunderbird: 179&lt;/li&gt;&#xA;&lt;li&gt;LibreOffice: 185&lt;/li&gt;&#xA;&lt;li&gt;GIMP: 166&lt;/li&gt;&#xA;&lt;li&gt;Inkscape: 164&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Phew! Looking through the list of dependencies, there a dozens of programmes and libraries that I couldn&amp;rsquo;t even imagine what they could be about. But they make a big application, be it Firefox, Thunderbird or GIMP, actually possible. Isn&amp;rsquo;t it a bit unfair that we often don&amp;rsquo;t see these small (or sometimes huge) projects and the people who take care of it?&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&#xA;&lt;p&gt;I decided to change that, at least for one day! I&amp;rsquo;ve analysed which packages are most used as dependencies of other packages (similar for Debian/Ubuntu &lt;sup id=&#34;fnref:3&#34;&gt;&lt;a href=&#34;#fn:3&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;3&lt;/a&gt;&lt;/sup&gt;):&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;for&lt;/span&gt; p in &lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;$(&lt;/span&gt;pacman -Q | cut -d&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt; -f1&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;)&lt;/span&gt;; &lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;do&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a2f&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;$(&lt;/span&gt;pactree -r -l &lt;span style=&#34;color:#b8860b&#34;&gt;$p&lt;/span&gt; | tail -n+2 | sort | uniq | wc -l&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#b44&#34;&gt;–&lt;/span&gt;&lt;span style=&#34;color:#b8860b&#34;&gt;$p&lt;/span&gt;&lt;span style=&#34;color:#b44&#34;&gt;–&lt;/span&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;$(&lt;/span&gt;pacman -Qi &lt;span style=&#34;color:#b8860b&#34;&gt;$p&lt;/span&gt; | grep &lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;^Description&amp;#34;&lt;/span&gt; | grep -oP &lt;span style=&#34;color:#b44&#34;&gt;&amp;#39;(?&amp;lt;=: ).*&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;done&lt;/span&gt; | column -t -s&lt;span style=&#34;color:#b44&#34;&gt;&amp;#39;–&amp;#39;&lt;/span&gt; | sort -nr&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Output:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;1621  iana-etc                   /etc/protocols and /etc/services provided by IANA&#xA;1620  tzdata                     Sources for time zone and daylight saving time data&#xA;1620  linux-api-headers          Kernel headers sanitized for use in userspace&#xA;1620  filesystem                 Base Arch Linux files&#xA;1619  glibc                      GNU C Library&#xA;1349  gcc-libs                   Runtime libraries shipped by GCC&#xA;1287  ncurses                    System V Release 4.0 curses emulation library&#xA;1267  readline                   GNU readline library&#xA;1261  bash                       The GNU Bourne Again shell&#xA;...&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As you might expect, on the very top I found a lot of GNU and Linux sub-projects, some widely known (like bash), some which I as a more-user-than-developer never heard of before (like libffi). This alone has been an interesting journey during which I learnt a lot about projects and their maintainers which play a crucial role on my laptop.&lt;/p&gt;&#xA;&lt;p&gt;In the end, I decided to express my thanks today to the following projects and people:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;The development team behind &lt;strong&gt;acl/attr&lt;/strong&gt; which controls access permissions&lt;/li&gt;&#xA;&lt;li&gt;The four initial creators of &lt;strong&gt;argon2&lt;/strong&gt;, Jean-Philippe, Samuel, Dmitry and Daniel, for their password hasing function&lt;/li&gt;&#xA;&lt;li&gt;Jan Dittberner (who also is a FSFE supporter!) and Nathan Neulinger, developers of &lt;strong&gt;CrackLib&lt;/strong&gt; which checks and enforces strong passwords&lt;/li&gt;&#xA;&lt;li&gt;Reuben Thomas and Dom Lachowicz for their &lt;strong&gt;enchant&lt;/strong&gt; project, a wrapper for various spell checking engines&lt;/li&gt;&#xA;&lt;li&gt;Maintainers of &lt;strong&gt;glibc&lt;/strong&gt; and &lt;strong&gt;gcc&lt;/strong&gt;, important tools for the C library and compiler&lt;/li&gt;&#xA;&lt;li&gt;The &lt;strong&gt;HarfBuzz&lt;/strong&gt; team which can shape glyphs from Unicode texts&lt;/li&gt;&#xA;&lt;li&gt;The &lt;strong&gt;libmnl/netfilter&lt;/strong&gt; people, who provide tools for network-related operations&lt;/li&gt;&#xA;&lt;li&gt;The contributors of &lt;strong&gt;libxml2&lt;/strong&gt; for their library and tools that are crucial for the FSFE website&lt;/li&gt;&#xA;&lt;li&gt;Martin Mitáš who more or less alone maintains &lt;strong&gt;md4c&lt;/strong&gt;, a Markdown parser&lt;/li&gt;&#xA;&lt;li&gt;Thomas Dickey who maintains &lt;strong&gt;ncurses&lt;/strong&gt; which provides a text-based interface for the command line&lt;/li&gt;&#xA;&lt;li&gt;Chet Ramey as representative of &lt;strong&gt;readline&lt;/strong&gt;, a programme for interactive user input&lt;/li&gt;&#xA;&lt;li&gt;And last but not least Lasse Collin who maintains &lt;strong&gt;xz&lt;/strong&gt;, a compression tool&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;But of course, that&amp;rsquo;s only a small fraction of the many interesting Free Software components that enable my daily work. However, if we all do the same and think about the hidden champions – not only during #ILoveFs day but beyond – we can make the humans behind it enjoy their invaluable contributions a bit more.&lt;/p&gt;&#xA;&lt;p&gt;Happy I Love Free Software Day everyone! ❤&lt;/p&gt;&#xA;&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;&#xA;&lt;hr&gt;&#xA;&lt;ol&gt;&#xA;&lt;li id=&#34;fn:1&#34;&gt;&#xA;&lt;p&gt;&lt;code&gt;pactree -l firefox | sort | uniq&lt;/code&gt;&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li id=&#34;fn:2&#34;&gt;&#xA;&lt;p&gt;During the writing of this blog post I remembered Matthias &lt;a href=&#34;https://k7r.eu/hug-a-developer-today-peter-stuge/&#34;&gt;hugging Peter Stuge for #ilovefs 2013&lt;/a&gt; who also contributes to widely used Free Software projects.&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li id=&#34;fn:3&#34;&gt;&#xA;&lt;p&gt;If you want to try the same with apt (with another separator):&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;for&lt;/span&gt; p in &lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;$(&lt;/span&gt;dpkg --get-selections | cut -f1 | cut -d&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;:&amp;#34;&lt;/span&gt; -f1&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;)&lt;/span&gt;; &lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;do&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a2f&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;$(&lt;/span&gt;apt-cache rdepends &lt;span style=&#34;color:#b8860b&#34;&gt;$p&lt;/span&gt; | tr -d &lt;span style=&#34;color:#b44&#34;&gt;&amp;#39;|&amp;#39;&lt;/span&gt; | tail -n+3 | sort | uniq | wc -l&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#b44&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#b8860b&#34;&gt;$p&lt;/span&gt;&lt;span style=&#34;color:#b44&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;$(&lt;/span&gt;apt-cache show &lt;span style=&#34;color:#b8860b&#34;&gt;$p&lt;/span&gt; | grep -m &lt;span style=&#34;color:#666&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;^Description:&amp;#34;&lt;/span&gt; | grep -oP &lt;span style=&#34;color:#b44&#34;&gt;&amp;#39;(?&amp;lt;=: ).*&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;done&lt;/span&gt; | column -t -s&lt;span style=&#34;color:#b44&#34;&gt;&amp;#39;*&amp;#39;&lt;/span&gt; | sort -nr&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&amp;#160;&lt;a href=&#34;#fnref:3&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;/div&gt;&#xA;</content:encoded>
    </item>
    <item>
      <title>Build FSFE websites locally</title>
      <link>https://mehl.mx/blog/2016/build-fsfe-websites-locally/</link>
      <pubDate>Sun, 13 Nov 2016 23:00:44 +0000</pubDate>
      <guid>https://mehl.mx/blog/2016/build-fsfe-websites-locally/</guid>
      <description>&lt;p&gt;&lt;em&gt;Note: This guide is also available &lt;a href=&#34;https://wiki.fsfe.org/TechDocs/Mainpage/BuildLocally&#34;&gt;in FSFE’s wiki&lt;/a&gt; now, and it will be the only version maintained. So please head over to the wiki if you’re planning to follow this guide.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;Those who create, edit, and translate &lt;a href=&#34;https://fsfe.org&#34;&gt;FSFE websites&lt;/a&gt; already know that the source files are XHTML files which are build with a XSLT processor, including a lot of custom stuff. One of the huge advantages from that is that we don’t have to rely on dynamic website processors and databases, on the other hand there are a few drawbacks as well: websites need a few minutes to be generated by the central build system, and it’s quite easy to mess up with the XML syntax.&lt;/p&gt;</description>
      <content:encoded>&lt;p&gt;&lt;em&gt;Note: This guide is also available &lt;a href=&#34;https://wiki.fsfe.org/TechDocs/Mainpage/BuildLocally&#34;&gt;in FSFE’s wiki&lt;/a&gt; now, and it will be the only version maintained. So please head over to the wiki if you’re planning to follow this guide.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;Those who create, edit, and translate &lt;a href=&#34;https://fsfe.org&#34;&gt;FSFE websites&lt;/a&gt; already know that the source files are XHTML files which are build with a XSLT processor, including a lot of custom stuff. One of the huge advantages from that is that we don’t have to rely on dynamic website processors and databases, on the other hand there are a few drawbacks as well: websites need a few minutes to be generated by the central build system, and it’s quite easy to mess up with the XML syntax.&lt;/p&gt;&#xA;&lt;p&gt;Now if an editor wants to create or edit a page, she needs to wait a few minutes until the build system has finished everytime she wants to test how the website looks like. So in this guide I will show how to build single websites on your own computer in a fraction of the FSFE’s system build time, so you’ll only need to commit your changes as soon as the file looks as you want it. All you need is a bit hard disk space and around one hour time to set up everything.&lt;/p&gt;&#xA;&lt;p&gt;&lt;em&gt;The whole idea is based on what FSFE’s webmaster Paul Hänsch has coded and written. &lt;a href=&#34;http://blog.plutz.net/The_FSFE_org_buildscript.html&#34;&gt;On his blog&lt;/a&gt; he explains the new build script. He explains how to build files locally, too. However, this guide aims to make it a bit easier and more verbose.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;Before we’re getting started, let me shortly explain the concept of what we’ll be doing. Basically, we’ll have three directories: &lt;code&gt;trunk&lt;/code&gt;, &lt;code&gt;status&lt;/code&gt;, and &lt;code&gt;fsfe.org&lt;/code&gt;. Most likely you already have &lt;code&gt;trunk&lt;/code&gt;, it’s a clone of the FSFE’s main SVN repository, and the source of all operations. All those files in there have to be compiled to generate the final HTML files we can browse. The location of these finished files will be &lt;code&gt;fsfe.org&lt;/code&gt;. &lt;code&gt;status&lt;/code&gt;, the third directory, contains error messages and temporary files.&lt;/p&gt;&#xA;&lt;p&gt;After we (1) created these directories, partly by downloading a repository with some useful scripts and configuration files, we’ll (2) build the whole FSFE website on our own computer. In the next step, we’ll (3) set up a local webserver so you can actually browse these files. And lastly we’ll (4) set up a small script which you can use to quickly build single XHTML files. Last but not least I’ll give some real-world examples.&lt;/p&gt;&#xA;&lt;h3 id=&#34;1-clone-helper-repository&#34;&gt;1. Clone helper repository&lt;/h3&gt;&#xA;&lt;p&gt;Firstly, clone a &lt;a href=&#34;https://src.mehl.mx/mxmehl/fsfe-local-build&#34;&gt;git repository&lt;/a&gt; which will give you most needed files and directories for the further operations. It has been created by me and contains configuration files and the script that will make building of single files easier. Of course, you can also do everything manually.&lt;/p&gt;&#xA;&lt;p&gt;In general, this is the directory structure I propose. In the following I’ll stick to this scheme. Please adapt all changes if your folder tree looks differently.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;trunk (~700 MB):      ~/subversion/fsfe/fsfe-web/trunk/&#xA;status (~150 MB):     ~/subversion/fsfe/local-build/status/&#xA;fsfe.org (~1000 MB):  ~/subversion/fsfe/local-build/fsfe.org/&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;(For those not so familiar with the GNU/Linux terminal: &lt;code&gt;~&lt;/code&gt; is the short version of your home directory, so for example &lt;code&gt;/home/user&lt;/code&gt;. &lt;code&gt;~/subversion&lt;/code&gt; is the same as &lt;code&gt;/home/USER/subversion&lt;/code&gt;, given that your username is &lt;code&gt;USER&lt;/code&gt;)&lt;/p&gt;&#xA;&lt;p&gt;To continue, you have to have &lt;code&gt;git&lt;/code&gt; installed on your computer (&lt;code&gt;sudo apt-get install git&lt;/code&gt;). Then, please execute via terminal following command. It will copy the files from &lt;a href=&#34;https://src.mehl.mx/mxmehl/fsfe-local-build&#34;&gt;my git repository&lt;/a&gt; to your computer and already contains the folders &lt;code&gt;status&lt;/code&gt; and &lt;code&gt;fsfe.org&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git clone https://src.mehl.mx/mxmehl/fsfe-local-build.git ~/subversion/fsfe/local-build&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we take care of &lt;code&gt;trunk&lt;/code&gt;. In case you already have a copy of &lt;code&gt;trunk&lt;/code&gt; on your computer, you can use this location, but please do a &lt;code&gt;svn up&lt;/code&gt; beforehand and be sure that the output of &lt;code&gt;svn status&lt;/code&gt; is empty (so no new or modified files on your side). If you don’t have &lt;code&gt;trunk&lt;/code&gt; yet, download the repository to the proposed location:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;svn --username &lt;span style=&#34;color:#b8860b&#34;&gt;$YourFSFEUsername&lt;/span&gt; co https://svn.fsfe.org/fsfe-web/trunk ~/subversion/fsfe/fsfe-web/trunk&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;2-build-full-website&#34;&gt;2. Build full website&lt;/h3&gt;&#xA;&lt;p&gt;Now we have to build the whole FSFE website locally. This will take a longer time but we’ll only have to do it once. Later, you’ll just build single files and not &amp;gt;14000 as we do now.&lt;/p&gt;&#xA;&lt;p&gt;But first, we have to install a few applications which are needed by the build script (&lt;strong&gt;Warning&lt;/strong&gt;: it’s possible your system lacks some other required applications which were already installed on mine. If you encounter any &lt;code&gt;command not found&lt;/code&gt; errors, please report them in the comments or by mail). So let’s install them via the terminal:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;sudo apt-get install make libxslt&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note: &lt;code&gt;libxslt&lt;/code&gt; may have a different name in your distribution, e.g. &lt;code&gt;libxslt1.1&lt;/code&gt; or &lt;code&gt;libxslt2&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Now we can start building.The full website build can be started with&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;~/subversion/fsfe/fsfe-web/trunk/build/build_main.sh --statusdir ~/subversion/fsfe/local-build/status/ build_into ~/subversion/fsfe/local-build/fsfe.org/&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;See? We use the build routine from &lt;code&gt;trunk&lt;/code&gt; to launch building &lt;code&gt;trunk&lt;/code&gt;. All status messages are written to &lt;code&gt;status&lt;/code&gt;, and the final website will reside in &lt;code&gt;fsfe.org&lt;/code&gt;. Mind differing directory names if you have another structure than I do. This process will take a long time, depending on your CPU power. Don’t be afraid of strange messages and massive walls of text ;-)&lt;/p&gt;&#xA;&lt;p&gt;After the long process has finished, navigate to the &lt;code&gt;trunk&lt;/code&gt; directory and execute &lt;code&gt;svn status&lt;/code&gt;. You may see a few files which are new:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;max@bistromath ~/s/f/f/trunk&amp;gt; svn status&#xA;?       about/printable/archive/printable.en.xml&#xA;?       d_day.en.xml&#xA;?       d_month.en.xml&#xA;?       d_year.en.xml&#xA;?       localmenuinfo.en.xml&#xA;[...]&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;These are leftover from the full website build. Because &lt;code&gt;trunk&lt;/code&gt; is supposed to be your productive source directory where you also make commits to the FSFE SVN, let’s delete these files. You won’t need them anymore.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;rm about/printable/archive/printable.en.xml d_day.en.xml d_month.en.xml d_year.en.xml localmenuinfo.en.xml&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;rm tools/tagmaps/*.map&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Afterwards, the output of &lt;code&gt;svn status&lt;/code&gt; should be empty again. It is? Fine, let’s go on! If not, please also remove those files (and tell me which files were missing).&lt;/p&gt;&#xA;&lt;h3 id=&#34;3-set-up-local-webserver&#34;&gt;3. Set up local webserver&lt;/h3&gt;&#xA;&lt;p&gt;After the full build is completed, you can install a local webserver. This is necessary to actually display the locally built files in your browser. In this example, I assume you don’t already have a webserver installed, and that you’re using a Debian-based operating system. So let’s install &lt;code&gt;lighttpd&lt;/code&gt; which is a thin and fast webserver, plus &lt;code&gt;gamin&lt;/code&gt; which &lt;code&gt;lighttpd&lt;/code&gt; needs in some setups:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;sudo apt-get install lighttpd gamin&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To make Lighttpd running properly we need a configuration file. This has to point the webserver to show files in the &lt;code&gt;fsfe.org&lt;/code&gt; directory. You already downloaded my recommended config file (&lt;code&gt;lighttpd-fsfe.conf.sample&lt;/code&gt;) by cloning the git repository. But you’ll have to modify the path accordingly and rename it. So rename the file to &lt;code&gt;lighttpd-fsfe.conf&lt;/code&gt;, open it and change following line to match the actual and absolute path of the &lt;code&gt;fsfe.org&lt;/code&gt; directory (~ does not work here):&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;server.document-root = &amp;#34;/home/USER/subversion/fsfe/local-build/fsfe.org&amp;#34;&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now you can test whether the webserver is correctly configured. To start a temporary webserver process, execute the next command in the terminal:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;lighttpd -Df ~/subversion/fsfe/local-build/lighttpd-fsfe.conf&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Until you press Ctrl+C, you should be able to open your local FSFE website in any browser using the URL &lt;code&gt;http://localhost:5080&lt;/code&gt;. For example, open the URL &lt;code&gt;http://localhost:5080/contribute/contribute.en.html&lt;/code&gt; in your browser. You should see basically the same website as the original &lt;a href=&#34;https://fsfe.org/contribute/contribute.en.html&#34;&gt;fsfe.org website&lt;/a&gt;. If not, double-check the paths, if the lighttpd process is still running, or if the full website build is already finished.&lt;/p&gt;&#xA;&lt;h3 id=&#34;4-single-page-build-script&#34;&gt;4. Single page build script&lt;/h3&gt;&#xA;&lt;p&gt;Until now, you didn’t see much more than you can see on the original website. But in this step, we’ll configure and start using a Bash script (&lt;code&gt;fsfe-preview.sh&lt;/code&gt;) I’ve written to make a preview of a locally edited XHTML file as comfortable as possible. You already downloaded it by cloning the repository.&lt;/p&gt;&#xA;&lt;p&gt;First, rename and edit the script’s configuration file &lt;code&gt;config.cfg.sample&lt;/code&gt;. Rename it to &lt;code&gt;config.cfg&lt;/code&gt; and open it. The file contains all paths we already used here, so please adapt them to your structure if necessary. Normally, it should be sufficient to modify the values for &lt;code&gt;LOC_trunk&lt;/code&gt; (&lt;code&gt;trunk&lt;/code&gt; directory) and &lt;code&gt;LOC_out&lt;/code&gt; (&lt;code&gt;fsfe.org&lt;/code&gt; directory), the rest can be left with the default values.&lt;/p&gt;&#xA;&lt;p&gt;Another feature of the fsfe-preview is to automatically check the XML syntax of the files. For this, &lt;code&gt;libxml2-utils&lt;/code&gt; has to be installed which contains &lt;code&gt;xmllint&lt;/code&gt;. Please execute:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;sudo apt-get install libxml2-utils&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now let’s make the script easy to access via the terminal for future usage. For this, we’ll create a short link to the script from one of the binary path directories. Type in the terminal:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;sudo ln -s ~/subversion/fsfe/local-build/fsfe-preview.sh /usr/bin/fsfe-preview&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;From this moment on, you should be able to call &lt;code&gt;fsfe-preview&lt;/code&gt; from anywhere in your terminal. Let’s make a test run. Modify the XHTML source file contribute/contribute.en.xhtml and edit some obvious text or alter the title. Now do:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-fsfe-preview&#34; data-lang=&#34;fsfe-preview&#34;&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As output, you should see something like:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[INFO] Using file /home/max/subversion/fsfe/fsfe-web/trunk/contribute/contribute.en.xhtml as source...&#xA;[INFO] XHTML file detected. Going to build into /home/max/subversion/fsfe/local-build/fsfe.org/contribute/contribute.en.html ...&#xA;[INFO] Starting webserver&#xA;&#xA;[SUCCESS] Finished. File can be viewed at http://localhost:5080/contribute/contribute.en.html&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now open the mentioned URL &lt;code&gt;http://localhost:5080/contribute/contribute.en.html&lt;/code&gt; and take a look whether your changes had an effect.&lt;/p&gt;&#xA;&lt;h3 id=&#34;recommended-workflows&#34;&gt;Recommended workflows&lt;/h3&gt;&#xA;&lt;p&gt;In this section I’ll present a few of the cases you might face and how to solve them with the script. I presume you have your terminal opened in the &lt;code&gt;trunk&lt;/code&gt; directory.&lt;/p&gt;&#xA;&lt;h5 id=&#34;preview-a-single-file&#34;&gt;&lt;strong&gt;Preview a single file&lt;/strong&gt;&lt;/h5&gt;&#xA;&lt;p&gt;To preview a single file before uploading it, just edit it locally. The file has to be located in the &lt;code&gt;trunk&lt;/code&gt; directory, so I suggest to only use one SVN trunk on your computer. It makes almost no sense to store your edited files in different folders. To preview it, just give the path to the edited file as argument for &lt;code&gt;fsfe-preview&lt;/code&gt;, just as we did in the preceding step:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;fsfe-preview activities/radiodirective/statement.en.xhtml&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The script detects whether the file has to be built with the XSLT processor (.xhtml files), or if it just can be copied to the website without any modification (e.g. images).&lt;/p&gt;&#xA;&lt;h5 id=&#34;copy-many-files-at-once&#34;&gt;Copy many files at once&lt;/h5&gt;&#xA;&lt;p&gt;Beware that all files you added in your session have to be processed with the script. For example, if you create a report with many images included and want to preview it, you will have to copy all these images to the output directory as well, and not only the XHTML file. For this, there is the &lt;code&gt;-copy&lt;/code&gt; argument. This circumvents the whole XSLT build process and just plainly copies the given files (or folders). In this example, the workflow could look like the following: The first line copies some images, the second builds the corresponding XHTML file which makes use of these images:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;fsfe-preview --copy news/2016/graphics/report1.png news/2016/graphics/report2.jpg&#xA;fsfe-preview news/2016/news-20161231-01.en.xhtml&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h5 id=&#34;syntax-check&#34;&gt;Syntax check&lt;/h5&gt;&#xA;&lt;p&gt;In general, it’s good to check the XHTML syntax before editing and commiting files to the SVN. The script &lt;code&gt;fsfe-preview&lt;/code&gt; already contains these checks but it’s good to be able to use it anyway. If you didn’t already do it before, install &lt;code&gt;libxml2-utils&lt;/code&gt; on your computer. It contains &lt;code&gt;xmllint&lt;/code&gt;, a syntax checker for XML files. You can use it like this:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;xmllint --noout work.en.xhtml&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If there’s no output (–noout), the file has a correct syntax and you’re ready to continue. But you may also see something like&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;work.en.xhtml:55: parser error : Opening and ending tag mismatch: p line 41 and li&#xA;      &amp;lt;/li&amp;gt;&#xA;           ^&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In this case, this means that the &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; tag starting in line 41 isn’t closed properly.&lt;/p&gt;&#xA;&lt;h3 id=&#34;drawbacks&#34;&gt;Drawbacks&lt;/h3&gt;&#xA;&lt;p&gt;The presented process and script has a few drawbacks. For example you aren’t able to preview certain very dynamic pages or parts of pages, or those depending on CGI scripts. In most cases you’ll never encounter these, but if you’re getting active with the FSFE’s webmaster team it may happen that you’ll have to fall back on the standard central build system.&lt;/p&gt;&#xA;&lt;p&gt;Any other issues? Feel free to report them as they will help to improve FSFE’s editors to work more efficiently :-)&lt;/p&gt;&#xA;&lt;h5 id=&#34;changelog&#34;&gt;Changelog&lt;/h5&gt;&#xA;&lt;p&gt;29 November 2016: Jonas has pointed out a few bugs and issues with a different GNU/Linux distribution. Should be resolved.&lt;/p&gt;</content:encoded>
    </item>
    <item>
      <title>splitDL – Downloading huge files from slow and unstable internet connections</title>
      <link>https://mehl.mx/blog/2015/splitdl-downloading-huge-files-from-slow-and-unstable-internet-connections/</link>
      <pubDate>Fri, 26 Jun 2015 15:59:03 +0000</pubDate>
      <guid>https://mehl.mx/blog/2015/splitdl-downloading-huge-files-from-slow-and-unstable-internet-connections/</guid>
      <description>&lt;p&gt;Imagine you want install GNU/Linux but your bandwidth won’t let you…&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;tl;dr: I wrote a rather small Bash script which splits huge files into several smaller ones and downloads them. To ensure the integrity, every small files is being checked for its hashsum and file size.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;That’s the problem I was facing in the past days. In the school I’m working at (&lt;a href=&#34;http://www.tareo-tz.org/mit/&#34;&gt;Moshi Institute of Technology&lt;/a&gt;, MIT) I set up a GNU/Linux server to provide services like file sharing, website design (on local servers to avoid the slow internet) and central backups. The ongoing plan is the setup of 5-10 (and later more) new computers with a GNU/Linux OS in contrast to the ancient and non-free WindowsXP installations – project „Linux Classroom“ is officially born.&lt;/p&gt;</description>
      <content:encoded>&lt;p&gt;Imagine you want install GNU/Linux but your bandwidth won’t let you…&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;tl;dr: I wrote a rather small Bash script which splits huge files into several smaller ones and downloads them. To ensure the integrity, every small files is being checked for its hashsum and file size.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;That’s the problem I was facing in the past days. In the school I’m working at (&lt;a href=&#34;http://www.tareo-tz.org/mit/&#34;&gt;Moshi Institute of Technology&lt;/a&gt;, MIT) I set up a GNU/Linux server to provide services like file sharing, website design (on local servers to avoid the slow internet) and central backups. The ongoing plan is the setup of 5-10 (and later more) new computers with a GNU/Linux OS in contrast to the ancient and non-free WindowsXP installations – project „Linux Classroom“ is officially born.&lt;/p&gt;&#xA;&lt;p&gt;But to install an operating system on a computer you need an installation medium. In the school a lot of (dubious) WindowsXP installation CD-ROMs are flying around but no current GNU/Linux. In the first world you would just download &lt;a href=&#34;https://en.wikipedia.org/wiki/ISO_image&#34;&gt;an .iso file&lt;/a&gt; and ~10 minutes later you could start installing it on your computer.&lt;/p&gt;&#xA;&lt;p&gt;But not here in Tanzania. With download rates of average 10kb/s it needs a hell of a time to download only one image file (not to mention the costs for the internet usage, ~1-3$ per 1GB). And that’s not all: Periodical power cuts cancel ongoing downloads abruptly. Of course you can restart a download but the large file may be already damaged and you loose even more time.&lt;/p&gt;&#xA;&lt;h2 id=&#34;my-solution--splitdl&#34;&gt;My solution – splitDL&lt;/h2&gt;&#xA;&lt;p&gt;To circumvent this drawback I coded a rather small Bash program called &lt;a href=&#34;http://src.mehl.mx/mxmehl/split-dl&#34;&gt;&lt;strong&gt;splitDL&lt;/strong&gt;&lt;/a&gt;. With this helper script, one is able to split a huge file into smaller pieces. If during the download the power cuts off and damages the file, one just has to re-download this single small file instead of the huge complete file. To detect whether a small file is unharmed the script creates hashsums of the original huge and the several small files. The script also supports continuation of the download thanks to the great default built-in application &lt;a href=&#34;https://en.wikipedia.org/wiki/Wget&#34;&gt;wget&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;You might now think „BitTorrent (or any other program) is also able to do the same, if not more!“. Yes, but this requires a) the installation of another program and b) a download source which supports this protocol. On the contrary &lt;strong&gt;splitDL&lt;/strong&gt; can handle every HTTP, HTTPS or FTP download.&lt;/p&gt;&#xA;&lt;p&gt;The downside in the current state is that &lt;strong&gt;splitDL&lt;/strong&gt; requires shell access to the server where the file is saved to be able to split the file and create the necessary hashsums. So in my current situation I use my own virtual server in Germany on which I download the wanted file with high-speed and then use &lt;strong&gt;splitDL&lt;/strong&gt; to prepare the file for the slow download from my server to the Tanzanian school.&lt;/p&gt;&#xA;&lt;p&gt;The project is of course still in an ongoing phase and only tested in my own environment. Please feel free to have a look at it and &lt;a href=&#34;http://src.mehl.mx/mxmehl/split-dl&#34;&gt;download it via my Git instance&lt;/a&gt;. I’m always looking forward to feedback. The application is licensed under GPLv3 or later.&lt;/p&gt;&#xA;&lt;h2 id=&#34;some-examples&#34;&gt;Some examples&lt;/h2&gt;&#xA;&lt;h3 id=&#34;server-side&#34;&gt;Server-side&lt;/h3&gt;&#xA;&lt;p&gt;Split the file &lt;em&gt;debian.iso&lt;/em&gt; into smaller parts with the default options (MD5 hashsum, 10MB size)&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;split-dl.sh -m server -f debian.iso&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Split the file but use the SHA1 hashsum and split the file into pieces with 50MB.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;split-dl.sh -m server -f debian.iso -c sha1sum -s 50M&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;After one of the commands, a new folder called &lt;em&gt;dl-debian.iso/&lt;/em&gt; will be created. There, the splitted files and a document containing the hashsums and file sizes are located. You just have to move the folder to a web-accessible location on your server.&lt;/p&gt;&#xA;&lt;h3 id=&#34;client-side&#34;&gt;Client-side&lt;/h3&gt;&#xA;&lt;p&gt;Download the splitted files with the default options.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;split-dl.sh -m client -f http://server.tld/dl-debian.iso/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Download the splitted files but use SHA1 hashsum (has to be the same than what was used on the creation process) and override the wget options (default: &lt;em&gt;-nv –show-progress&lt;/em&gt;).&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;split-dl.sh -m client -f http://server.tld/dl-debian.iso/ -c sha1sum -w --limit-date&lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt;100k&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;current-bugsdrawbacks&#34;&gt;Current bugs/drawbacks&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Currently only single files are possible to split. This will be fixed soon.&lt;/li&gt;&#xA;&lt;li&gt;Currently the script only works with files in the current directory. This is also only a matter of some lines of code.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</content:encoded>
    </item>
    <item>
      <title>I love Taskwarrior, therefore I love Free Software</title>
      <link>https://mehl.mx/blog/2015/i-love-taskwarrior-therefore-i-love-free-software/</link>
      <pubDate>Sat, 14 Feb 2015 12:05:42 +0000</pubDate>
      <guid>https://mehl.mx/blog/2015/i-love-taskwarrior-therefore-i-love-free-software/</guid>
      <description>&lt;p&gt;“&lt;em&gt;It’s Valentine’s day and you’re writing a blog post? Are you nuts?&lt;/em&gt;” you might ask. Well, but it’s not only Valentine’s day but also &lt;a href=&#34;http://ilovefs.org&#34;&gt;I love Free Software&lt;/a&gt; day. This day is proclaimed every year on February 14 by the Free Software Foundation Europe to thank all developers and contributors of Free Software (software you can use for any purpose, which source code you or others can analyze, which can be modified and distributed).&lt;/p&gt;</description>
      <content:encoded>&lt;p&gt;“&lt;em&gt;It’s Valentine’s day and you’re writing a blog post? Are you nuts?&lt;/em&gt;” you might ask. Well, but it’s not only Valentine’s day but also &lt;a href=&#34;http://ilovefs.org&#34;&gt;I love Free Software&lt;/a&gt; day. This day is proclaimed every year on February 14 by the Free Software Foundation Europe to thank all developers and contributors of Free Software (software you can use for any purpose, which source code you or others can analyze, which can be modified and distributed).&lt;/p&gt;&#xA;&#xA;&lt;link rel=&#34;stylesheet&#34; href=&#34;https://mehl.mx/css/snap-gallery.css&#34; /&gt;&lt;figure class=&#34;sm pull-right&#34;&gt;&#xA;  &lt;div class=&#34;snap-wrapper&#34;&gt;&#xA;    &lt;a href=&#34;#fig1&#34;&gt;&#xA;      &lt;img src=&#34;https://mehl.mx/img/blog/ilovefs-heart-px.png&#34; class=&#34;snap-thumb&#34;/&gt;&#xA;    &lt;/a&gt;&#xA;    &lt;div class=&#34;snap-lightbox&#34; id=&#34;fig1&#34;&gt;&#xA;        &lt;a href=&#34;#_&#34; class=&#34;snap-lightbox-close&#34;&gt;&lt;/a&gt;&#xA;        &lt;div class=&#34;snap-lightbox-inner&#34;&gt;&#xA;          &lt;img src=&#34;https://mehl.mx/img/blog/ilovefs-heart-px.png&#34;  /&gt;&#xA;          &lt;p&gt;&#xA;          &lt;/p&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;a href=&#34;#_&#34; class=&#34;snap-lightbox-close-button&#34;&gt;&lt;i class=&#34;snap-lightbox-x&#34;&gt;&lt;/i&gt;&lt;/a&gt;&#xA;      &lt;/div&gt;&lt;/div&gt;&#xA;&lt;/figure&gt;&#xA;&#xA;&lt;p&gt;As &lt;a href=&#34;http://blog.mehl.mx/2014/i-love-znc-because-ilovefs/&#34; title=&#34;I love ZNC because #ilovefs&#34;&gt;last year with ZNC&lt;/a&gt;, I want to say thank you to a specific project which easies my daily life. As you might know by other blog posts here, organisation of tasks, mails and almost everything else is a very important issue for me. So this year I want to write some lines about &lt;a href=&#34;http://taskwarrior.org/&#34;&gt;Taskwarrior&lt;/a&gt;, taskd and &lt;a href=&#34;http://mirakel.azapps.de/index.html&#34;&gt;Mirakel&lt;/a&gt; which enable me to take some free time without thinking of task which I could possibly forget to accomplish later on.&lt;/p&gt;&#xA;&lt;p&gt;My head is full of ideas and mental To-Do lists and so I’m in need of a handy tool which allows me to write down and organise items at any place and time: At my desk, in bus or train, when I’m offline or abroad. And its important that I don’t have (analog and digital) bits of paper everywhere, so I need a &lt;strong&gt;system that syncs all task inputs and outputs&lt;/strong&gt;. I tried a lot of tools but Taskwarrior was the best so far. It used the well-known „Getting Things Done“ concept with different priorities. Taskwarrior also supports tagging tasks, organising them in projects, due dates, postponing, making tasks dependend on others and much more. And Taskwarrior has a (modifiable) &lt;strong&gt;algorhythm that sorts your tasks by urgency levels&lt;/strong&gt;, so that the most important tasks always are on the top of the list. Even now I just took a glance at what Taskwarrior is able to do!&lt;/p&gt;&#xA;&#xA;&lt;figure class=&#34;md&#34;&gt;&#xA;  &lt;div class=&#34;snap-wrapper&#34;&gt;&#xA;    &lt;a href=&#34;#fig2&#34;&gt;&#xA;      &lt;img src=&#34;https://mehl.mx/img/blog/ilovefs-taskwarrior-gallery.jpg&#34; class=&#34;snap-thumb&#34;/&gt;&#xA;        &lt;figcaption&gt;Someone who loves Taskwarrior as much as I do&lt;/figcaption&gt;&#xA;    &lt;/a&gt;&#xA;    &lt;div class=&#34;snap-lightbox&#34; id=&#34;fig2&#34;&gt;&#xA;        &lt;a href=&#34;#_&#34; class=&#34;snap-lightbox-close&#34;&gt;&lt;/a&gt;&#xA;        &lt;div class=&#34;snap-lightbox-inner&#34;&gt;&#xA;          &lt;img src=&#34;https://mehl.mx/img/blog/ilovefs-taskwarrior-gallery.jpg&#34;  /&gt;&#xA;          &lt;p&gt;Someone who loves Taskwarrior as much as I do&#xA;          &lt;/p&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;a href=&#34;#_&#34; class=&#34;snap-lightbox-close-button&#34;&gt;&lt;i class=&#34;snap-lightbox-x&#34;&gt;&lt;/i&gt;&lt;/a&gt;&#xA;      &lt;/div&gt;&lt;/div&gt;&#xA;&lt;/figure&gt;&#xA;&#xA;&lt;p&gt;“&lt;em&gt;Services and programs that organise tasks aren’t very special!&lt;/em&gt;” one might think. But if you prefer sorting tasks digitally, you cannot simply chose a random todo-organising service provider. &lt;strong&gt;Most of the tools and services on the market aren’t free and transparent&lt;/strong&gt;. All input may no longer belong to you, all the gathered information (which is a lot if you think of it!) could be used for targeted ads or worse. You cannot modify the algorhythm to suit your needs. And what happens if the service provider goes bankrupt? All data, all project history and all pending tasks would be lost at once. So using a free (as in freedom), decentralised, maybe self-hosted service is the best idea to organise your tasks decentrally.&lt;/p&gt;&#xA;&lt;p&gt;But one thing at a time, let’s start from the very basic. You can install Taskwarrior and almost any operating system. After the installation, taskwarrior isn’t much more than a black window with white letters in it. And even when you’re a pro-user, you won’t find much more than white or colourful text on black background – and this is a good thing! I’ve seen no graphical user interface which can handle Taskwarrior’s complexity and the users‘ needs sufficiently (but &lt;a href=&#34;http://taskwarrior.org/tools/&#34;&gt;there are some&lt;/a&gt;, feel free to test them!). Nevertheless, &lt;strong&gt;it’s quite easy to use Taskwarrior&lt;/strong&gt; from your terminal:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;task add &lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;This is my first task&amp;#34;&lt;/span&gt;          &lt;span style=&#34;color:#080;font-style:italic&#34;&gt;# Add your first item&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;task long                                 &lt;span style=&#34;color:#080;font-style:italic&#34;&gt;# Show all pending tasks&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;task add &lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;Second VIP task!&amp;#34;&lt;/span&gt; pri:H         &lt;span style=&#34;color:#080;font-style:italic&#34;&gt;# Add a task with priority&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;task add &lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;Third task with tag&amp;#34;&lt;/span&gt; +test      &lt;span style=&#34;color:#080;font-style:italic&#34;&gt;# Add a task with a tag&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;task add &lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;Fourth projected task&amp;#34;&lt;/span&gt; pro:Blog &lt;span style=&#34;color:#080;font-style:italic&#34;&gt;# Add a task with a project&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;task long                                 &lt;span style=&#34;color:#080;font-style:italic&#34;&gt;# Show all pending tasks&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;task &lt;span style=&#34;color:#666&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;done&lt;/span&gt;                               &lt;span style=&#34;color:#080;font-style:italic&#34;&gt;# Mark first task as done (ID = 1)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There are many useful and well understandable guides in the &lt;a href=&#34;http://taskwarrior.org/docs/&#34;&gt;project’s documentation&lt;/a&gt;. Most likely you do not need every command but maybe it’s useful to read something about techniques which might help you to organise your tasks your way.&lt;/p&gt;&#xA;&#xA;&lt;figure class=&#34;lg&#34;&gt;&#xA;  &lt;div class=&#34;snap-wrapper&#34;&gt;&#xA;    &lt;a href=&#34;#fig3&#34;&gt;&#xA;      &lt;img src=&#34;https://mehl.mx/img/blog/ilovefs-taskwarrior2.gif&#34; class=&#34;snap-thumb&#34;/&gt;&#xA;        &lt;figcaption&gt;Some useful commands of Taskwarrior using some fish shell features&lt;/figcaption&gt;&#xA;    &lt;/a&gt;&#xA;    &lt;div class=&#34;snap-lightbox&#34; id=&#34;fig3&#34;&gt;&#xA;        &lt;a href=&#34;#_&#34; class=&#34;snap-lightbox-close&#34;&gt;&lt;/a&gt;&#xA;        &lt;div class=&#34;snap-lightbox-inner&#34;&gt;&#xA;          &lt;img src=&#34;https://mehl.mx/img/blog/ilovefs-taskwarrior2.gif&#34;  /&gt;&#xA;          &lt;p&gt;Some useful commands of Taskwarrior using some fish shell features&#xA;          &lt;/p&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;a href=&#34;#_&#34; class=&#34;snap-lightbox-close-button&#34;&gt;&lt;i class=&#34;snap-lightbox-x&#34;&gt;&lt;/i&gt;&lt;/a&gt;&#xA;      &lt;/div&gt;&lt;/div&gt;&#xA;&lt;/figure&gt;&#xA;&#xA;&lt;p&gt;But Taskwarrior is only for your local computer. What’s if you want to use it when sitting in the bus and don’t want to forget a ToDo item you want to write down at the very moment? Then there’s a &lt;strong&gt;handy application for Android called&lt;/strong&gt; &lt;a href=&#34;http://mirakel.azapps.de/&#34;&gt;Mirakel&lt;/a&gt;. Even the app itself is powerful, but it’s full potential is unleashed when combining it with Taskwarrior. For this, &lt;strong&gt;we need a central instance&lt;/strong&gt; which synchronises the tasks you add or edit on your devices. The Taskwarrior project developed taskd for it which you can easily setup on a server. You can also use Mirakel’s own public taskd server (at least in the past) if you don’t own a server or don’t want to maintain this service.&lt;/p&gt;&#xA;&lt;p&gt;So if you connect both Taskwarrior and Mirakel to the new taskd server, you can easily share all tasks among them. When marking a task done on your smartphone, it’s marked as done on your home computer some seconds or minutes later if you want to. Security is an important part of taskwarrior as well, so transport encryption is on by default. And if you want, you can also try a &lt;a href=&#34;http://taskwarrior.org/tools/&#34;&gt;web interface&lt;/a&gt; or other handy tools and extensions for your server and client which I haven’t tested yet.&lt;/p&gt;&#xA;&lt;p&gt;Hopefully you now know a bit more about Taskwarrior and Mirakel and the great tools they designed. Of course I do not only want to recommend some software but also use this opportunity to say a big &lt;strong&gt;THANK YOU&lt;/strong&gt; to all the people behind these projects! Thank your for developing the software and making it compatible to each other. Thanks to the various contributors which are writing the important documentation, adding new languages, writing tools and bridges for other usage scenarios and thank you for reacting to bug reports. People like you make Free Software possible!&lt;/p&gt;</content:encoded>
    </item>
    <item>
      <title>Mounting a SFTP storage in GNU/Linux</title>
      <link>https://mehl.mx/blog/2014/mounting-a-sftp-storage-in-gnu/linux/</link>
      <pubDate>Mon, 13 Jan 2014 14:42:01 +0000</pubDate>
      <guid>https://mehl.mx/blog/2014/mounting-a-sftp-storage-in-gnu/linux/</guid>
      <description>&lt;p&gt;This (longer than expected) post explains how to transfer files securely between your device and an external storage. The first part may be useful for you if you only have little knowledge of terms like (S)FTP(S) and want to learn something about widely used technologies. The second part will help you to mount an external storage so you can manage all files as if they are on your local device and the third, fourth and fifth part will concentrate on easing the mounting process by the help of hostnames, Private/Public Keys and a shell script.&lt;/p&gt;</description>
      <content:encoded>&lt;p&gt;This (longer than expected) post explains how to transfer files securely between your device and an external storage. The first part may be useful for you if you only have little knowledge of terms like (S)FTP(S) and want to learn something about widely used technologies. The second part will help you to mount an external storage so you can manage all files as if they are on your local device and the third, fourth and fifth part will concentrate on easing the mounting process by the help of hostnames, Private/Public Keys and a shell script.&lt;/p&gt;&#xA;&lt;p&gt;This guide will be very detailed and is also (and especially) suited for beginners. Maybe also some advanced users can learn something or give hints for improvements.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: With improving Bash skills and more time, I was able to heavily improve the script in the end. Have a look at my &lt;a href=&#34;https://src.mehl.mx/mxmehl/network-mount&#34;&gt;Git instance&lt;/a&gt; to download the latest version.&lt;/p&gt;&#xA;&lt;p&gt;But let’s be honest: All in all, this post will show you again, why Free Software, GNU/Linux and Open Standards are great, easy to use and why Windows users are to be pitied.&lt;/p&gt;&#xA;&lt;h2 id=&#34;short-excursus&#34;&gt;Short excursus&lt;/h2&gt;&#xA;&lt;p&gt;(Nearly) everybody knows FTP. FTP is a protocol which enables you to transfer files between your device and a remote space. Maybe you want to present your documents or images to visitors of your homepage and simply want to upload these files on your webspace. In most cases this could be done by the use of a seperate program like FileZilla.&lt;/p&gt;&#xA;&lt;p&gt;So far so good, but there’re several problems. Two of them:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/File_Transfer_Protocol#Security&#34;&gt;FTP is insecure&lt;/a&gt;. Period.&lt;/li&gt;&#xA;&lt;li&gt;Using an external program (and not your personal file manager) is really annoying if you want to edit the files very often. A realistic example: You have a complicated script running on your website which you’d like to edit in a graphical editor. Using an external client forces you to download the file, open it in your editor, save it and upload it again. Some FTP clients like FileZilla have the functionality to ease this pain in the a**, but trust me: after the twentieth reupload you want to toss your computer away&amp;hellip;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;Now we know why FTP is insecure. So what alternatives do we have?&lt;/p&gt;&#xA;&lt;p&gt;There is FTPS – using the FTP protocoll in connection with SSL. Well, that sounds great because, you know, SSL is great to ensure the safety of your online banking and frightens away &lt;a href=&#34;https://en.wikipedia.org/wiki/Packet_analyzer&#34;&gt;sniffing script kiddies&lt;/a&gt;. Forget that. FTPS is only securing the authentication process, the transfer of all files remains in plain text. So if you upload a sensitive document, it is fairly easy to intercept it on different levels in the uploading/downloading process. There is also the fact that FTPS is horrible to implement, not very compatible, and not very smart designed.&lt;/p&gt;&#xA;&lt;p&gt;The best alternative in my opinion is &lt;a href=&#34;https://en.wikipedia.org/wiki/SSH_File_Transfer_Protocol&#34;&gt;SFTP&lt;/a&gt;  (you are confused by all the abbreviations now? Wait for it, it gets better). SFTP uses a SSH connection to build a safe tunnel between your device and the remote storage. SSH is very smart, widely used and on most servers running a &lt;a href=&#34;https://en.wikipedia.org/wiki/Free_software&#34;&gt;Free&lt;/a&gt; operating system it is preinstalled and needs no further configuration. SFTP enforces you to transfer your files fully encrypted through a secure channel.&lt;/p&gt;&#xA;&lt;p&gt;But how to use it? You can use clients like FileZilla but do you remember the first problem I mentioned? Correct, an external editor is not what suits the needs of someone who wants to work with his files comfortably.&lt;/p&gt;&#xA;&lt;h2 id=&#34;mounting-an-external-storage-with-ssh&#34;&gt;Mounting an external storage with SSH&lt;/h2&gt;&#xA;&lt;p&gt;This sounds more complicated than it really is. The theory behind it is very easy: You mount (link) a remote directory over SSH (a secure channel) in any desired directory on your machine. For instance, you can simply edit all files in &lt;em&gt;/home/user/remote/&lt;/em&gt; with your programs, but finally all changes will happen (nearly) instantly on your remote storage. This even works with watching a video file that is located on the server: Open it like a normal video file in a player like VLC and it behaves like it is locally on your PC (if your internet connection is fast enough, just test it!).&lt;/p&gt;&#xA;&lt;p&gt;The only prerequisite: You need a server/webspace/storage with full SSH access. Unfortunately many webhosters don’t provide SSH access. If you belong to the unlucky ones, I recommend you to look for alternatives – it is worth it! (below this post, I added a very small list of webhosting providers offering SSH access)&lt;/p&gt;&#xA;&lt;p&gt;Now we come to the technical part. For this post, following data is used. Most likely, this will look different in your case.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;SSH-Server: server1.net&#xA;Username on server: client&#xA;Home directory of user on server: /home/client&#xA;Username local machine: user&#xA;Local mount directory: /home/user/remote/server1&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;On your GNU/Linux, please assure that following programs are installed. Some of them (ssh-askpass, zenity) are only used in step IV and V:&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;openssh-client, sshfs, ssh-askpass, zenity&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;Now create a directory in which the remote storage should be mounted in. Don’t worry about the space on your hard disk: This directory is remote and there are only temporary files stored on your local device. In this example I’ll use &lt;em&gt;/home/user/remote/server1&lt;/em&gt;. It could also be &lt;em&gt;/fsfe/lol/wtf/nsa/&lt;/em&gt; if this directory would exist.&lt;/p&gt;&#xA;&lt;p&gt;Now, start a terminal/shell to fill in following commands as a normal user.&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;ssh -p 22 client@server1.net&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;If asked for a password, fill in the password of ‚&lt;em&gt;client&lt;/em&gt;‚ (the webhoster should have given it to you). If you are now connected to the server1.net, you are just one step away from mounting it. Close the connection or terminal, open it again and type:&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;sshfs -p 22 client@server1.net: /home/user/remote/server1/ -o follow_symlinks&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;This command connects to ‚&lt;em&gt;server1.net&lt;/em&gt;‚ as the user ‚&lt;em&gt;client&lt;/em&gt;‚ using the SSH protocol over the standard &lt;em&gt;port&lt;/em&gt; 22. Then it mounts the home directory of ‚&lt;em&gt;client&lt;/em&gt;‚ on the local directory ‚&lt;em&gt;/home/user/remote/server1&lt;/em&gt;‚ which we created beforehand. Additionally we added the option ‚&lt;em&gt;follow_symlinks&lt;/em&gt;‚ so that links of the server work on our local machine as well. If you have a look at &lt;em&gt;/home/user/remote/server1&lt;/em&gt;, you should see the exact same content than in the home directory on your server or if you connect via FTP/SFTP. Congratulation! Now play around with it, try to edit, upload and download files.&lt;/p&gt;&#xA;&lt;p&gt;To unmount (speak: disconnect) the directory, type&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;fusermount -u /home/user/remote/server1&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;Now you mount the home directory of ‚client‘. If you want to mount another directory on the server (e.g. &lt;em&gt;/home/client/exchange&lt;/em&gt;), use this modified command:&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;sshfs -p 22 client@server1.net:/home/client/exchange/ /home/user/remote/server1/ -o follow_symlinks&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;In the next step, we will make the connection and mounting more comfortable, even if you are handling with more than one server.&lt;/p&gt;&#xA;&lt;h2 id=&#34;using-hostnames&#34;&gt;Using hostnames&lt;/h2&gt;&#xA;&lt;p&gt;Now that you know how (and that) the system works, we will make it easier. Of course it is quite annoying to type in the whole server-address and port each time. Instead of &lt;em&gt;sshfs -p 22 &lt;a href=&#34;mailto:client@server1.net&#34;&gt;client@server1.net&lt;/a&gt;[…]&lt;/em&gt;, you can simply type &lt;em&gt;sshfs server1&lt;/em&gt;. How? Just open the SSH configuration file &lt;em&gt;/home/user/.ssh/config&lt;/em&gt; with you desired text editor and add:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Host server1&#xA;  HostName server1.net&#xA;  Port 22&#xA;  User client&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Save it and try to use this shortcut. It also works for normal SSH connection like ‚&lt;em&gt;ssh server1&lt;/em&gt;‚. For the &lt;em&gt;Host&lt;/em&gt; variable, you could use any name you can remember easily, for instance &lt;em&gt;privateserver&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;using-public-and-private-keys&#34;&gt;Using Public and Private Keys&lt;/h2&gt;&#xA;&lt;p&gt;Pretty smooth, isn’t it? Well, it gets even better! You have to admit that typing a password each time you connect is quite harrassing. This gets even worse if you are used to connect to many servers. On the one hand, you could use password managing tools&amp;hellip; or &lt;a href=&#34;https://en.wikipedia.org/wiki/Public-key_cryptography&#34;&gt;Public/Private key authentication&lt;/a&gt;! For those who don’t know much about it, let me say: This sounds terribly complicated, but you don’t have to understand it completely.&lt;/p&gt;&#xA;&lt;p&gt;For this guide, you only have to know: There are two keys (files). The one is private, you should never give it to anyone! It’s like a key to your safe, if someone achieves it, he can get everything that is secured in this safe. But the other file is public, you can give it to everyone if you want so. Now it gets a bit weird, so I try to make it as simple and abstract as possible.&lt;/p&gt;&#xA;&lt;p&gt;Let’s say, the public key is a chest that no one except the owner can open. Inside this chest, there is a supersecret word that no one except the owner knows. You put an exact copy of this chest on your server and each time you try to connect, you say „Hey, I’m the legitimate user of this server. Give me my chest, I will open it with my secret key and tell you, what the supersecret word is!“. The server will give you the chest (public key), you open it with your private key and tell the server the supersecret word. If you were right, the server lets you in.&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: This example is so ridiculously abstract that I had to laugh while writing it down. Every security expert, IT guy and cryptography scientist would knock me down for this but I hope it made you understand the principle of this very smart method. And if you ask yourself now „Why does the server know the supersecret word? Isn’t this insecure?“ or „Isn’t is insecure to send such a sensitive code word over the internet?“: You’re absolutely right! But be assured, that the public-key cryptography uses some awesome tricks to avoid security leaks like these and others. If you want to, read about it!&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;It is quite easy to make this system happen. Again we need a terminal to generate the two keys:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a2f&#34;&gt;cd&lt;/span&gt; ~/.ssh/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ssh-keygen -t dsa&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This neat script asks a.) in which file you want to save the new keys. The standard is set to &lt;em&gt;/home/user/.ssh/id_dsa&lt;/em&gt; and I recommend this. Most programs automatically search for keys in this destination. In step b.) it asks you for a password. This is quite important because if someone achieves your private key, he still needs a password to use it. After this short procedure, you have two new files in &lt;em&gt;/home/user/.ssh&lt;/em&gt; – id_dsa and id_dsa.pub. As you can imagine, id_dsa.pub is your public key, the other is the secret one.&lt;/p&gt;&#xA;&lt;p&gt;Now we have to put the public key (chest) on the server we want to connect to. This could be done by a simple command:&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;ssh-copy-id -i /home/user/.ssh/id_dsa.pub client@server1.net&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;By this, you automatically connect as a legitimate user to your server (with the password used in step II) and put the public key in the &lt;em&gt;/home/client/.ssh/authorized_keys&lt;/em&gt;. This file simply collects all public keys (chests) of users that are allowed to connect to the server. If a stranger tries to connect to your server but there is no chest his secret key belongs to, he of course cannot open the connection to your server.&lt;/p&gt;&#xA;&lt;p&gt;Now disconnect from your server and try to connect via &lt;em&gt;ssh server1&lt;/em&gt;. Your device should automatically search for your private key and open the connection with it. As you notice, you are still asked for a password. This is because you (hopefully) put a password on your private key (remember?). But after you typed in the password once, it should be saved for duration of your local session in a local keyring.&lt;/p&gt;&#xA;&lt;p&gt;If this is not the case or you want to make your configuration even better, add the following lines on the bottom of your &lt;em&gt;/home/user/.bashrc&lt;/em&gt; (or if you’re not using bash, the respective rc file of your shell):&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;alias ssh=&#39;ssh-add -l &amp;gt; /dev/null || ssh-add -t 7200; ssh&#39;&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;By this, you say to your shell: „Everytime I type the command ’ssh‘, you should instead (alias) 1.) check if I already added my key to the local keyring. If not so (||), 2.) add my key to the keyring, but only keep it there for 7200 seconds (you can also keep this away or increase it). Only if you made this sure, use the command ssh to connect to my server“. Every command line program that uses ssh to connect to a server (and there are some, e.g. git, sshfs…) now uses this procedure – no matter if you close the terminal or lock the screen.&lt;/p&gt;&#xA;&lt;p&gt;Of course, this also works with sshfs to mount your remote storages. If you remember our first line to mount the server, we have a much shorter line now without any password request:&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;sshfs server1: /home/user/remote/server1/ -o follow_symlinks&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;And these steps were quite important for the next section where we will write a shell script to make it much more easier – and even graphical!&lt;/p&gt;&#xA;&lt;h2 id=&#34;using-a-shell-script&#34;&gt;Using a shell script&lt;/h2&gt;&#xA;&lt;p&gt;Our setting is very smooth now, but it could still be improved. If you want to connect to many servers and don’t want to use your shell every time or don’t want to remember the HOSTs you used in your .ssh/config, you’re free to modify and use this shell script:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080&#34;&gt;#!/bin/bash&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-style:italic&#34;&gt;### VARIABLES TO BE CHANGED ###&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-style:italic&#34;&gt;# Preconfigured HOSTs in ~/.ssh/config that should be used&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PRESSH&lt;span style=&#34;color:#666&#34;&gt;[&lt;/span&gt;0&lt;span style=&#34;color:#666&#34;&gt;]=&lt;/span&gt;server1&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PRESSH&lt;span style=&#34;color:#666&#34;&gt;[&lt;/span&gt;1&lt;span style=&#34;color:#666&#34;&gt;]=&lt;/span&gt;server2&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PRESSH&lt;span style=&#34;color:#666&#34;&gt;[&lt;/span&gt;2&lt;span style=&#34;color:#666&#34;&gt;]=&lt;/span&gt;server3&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-style:italic&#34;&gt;# Local directory where the remote storages should be mounted to&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b8860b&#34;&gt;LOCALMOUNTDIR&lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt;/home/user/remote&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-style:italic&#34;&gt;### THE SCRIPT BEGINS HERE ###&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-style:italic&#34;&gt;# Add SSH key to local keyring if not already happened&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;function&lt;/span&gt; sshadd &lt;span style=&#34;color:#666&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;ssh-add -l &amp;gt; /dev/null &lt;span style=&#34;color:#666&#34;&gt;||&lt;/span&gt; ssh-add&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-style:italic&#34;&gt;# Choose preconfigured HOST to mount&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;function&lt;/span&gt; mount &lt;span style=&#34;color:#666&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;if&lt;/span&gt; ! &lt;span style=&#34;color:#b8860b&#34;&gt;SSH&lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;$(&lt;/span&gt;zenity --list &lt;span style=&#34;color:#b62;font-weight:bold&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;--height&lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;300&lt;/span&gt; &lt;span style=&#34;color:#b62;font-weight:bold&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;--text&lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;Please choose. Cancel to unmount drives.&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#b62;font-weight:bold&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;--title&lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;Choose SSH server&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#b62;font-weight:bold&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;--column &lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;Preconfigured SSH servers&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#b62;font-weight:bold&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#b68;font-weight:bold&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#b8860b&#34;&gt;PRESSH&lt;/span&gt;[*]&lt;span style=&#34;color:#b68;font-weight:bold&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;)&lt;/span&gt;; &lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;then&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;unmountquestion&#x9;&#x9;&lt;span style=&#34;color:#080;font-style:italic&#34;&gt;# If you press cancel, it should ask you to unmount all drives&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;fi&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#080;font-style:italic&#34;&gt;# If you double click on an entry, it gives &amp;#39;server1|server1&amp;#39; as result instead of &amp;#39;server1&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#080;font-style:italic&#34;&gt;# This command cuts of everything after |&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#b8860b&#34;&gt;SSH&lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;$(&lt;/span&gt;&lt;span style=&#34;color:#a2f&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#b8860b&#34;&gt;$SSH&lt;/span&gt; | awk -F&lt;span style=&#34;color:#b62;font-weight:bold&#34;&gt;\|&lt;/span&gt; &lt;span style=&#34;color:#b44&#34;&gt;&amp;#39;{ print $1 }&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#080;font-style:italic&#34;&gt;# Make a local directory if not available&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#666&#34;&gt;[&lt;/span&gt; ! -e &lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b8860b&#34;&gt;$LOCALMOUNTDIR&lt;/span&gt;&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;&lt;/span&gt;/&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b8860b&#34;&gt;$SSH&lt;/span&gt;&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#666&#34;&gt;]&lt;/span&gt;; &lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;then&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;mkdir -p &lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b8860b&#34;&gt;$LOCALMOUNTDIR&lt;/span&gt;&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;&lt;/span&gt;/&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b8860b&#34;&gt;$SSH&lt;/span&gt;&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;fi&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#080;font-style:italic&#34;&gt;# Command to mount actually&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;sshfs &lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b8860b&#34;&gt;$SSH&lt;/span&gt;&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b8860b&#34;&gt;$LOCALMOUNTDIR&lt;/span&gt;&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;&lt;/span&gt;/&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b8860b&#34;&gt;$SSH&lt;/span&gt;&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;&lt;/span&gt;/ -o follow_symlinks &amp;amp;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;quitquestion&#x9;&lt;span style=&#34;color:#080;font-style:italic&#34;&gt;# one more ssh server or quit?&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-style:italic&#34;&gt;# Ask if all preconfigured SSHFS drives should be unmounted&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;function&lt;/span&gt; unmountquestion &lt;span style=&#34;color:#666&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;zenity --question --text&lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;Unmount all preconfigured\nSSHFS drives now?&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#666&#34;&gt;[&lt;/span&gt; &lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b8860b&#34;&gt;$?&lt;/span&gt;&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;0&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#666&#34;&gt;]&lt;/span&gt;; &lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;then&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;unmount&#x9;&#x9;&lt;span style=&#34;color:#080;font-style:italic&#34;&gt;# unmount function&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;else&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a2f&#34;&gt;exit&lt;/span&gt; &lt;span style=&#34;color:#666&#34;&gt;0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;fi&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-style:italic&#34;&gt;# Procedure to unmount all preconfigured SSHFS drives and exit program afterwards&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;function&lt;/span&gt; unmount &lt;span style=&#34;color:#666&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;for&lt;/span&gt; &lt;span style=&#34;color:#666&#34;&gt;((&lt;/span&gt;&lt;span style=&#34;color:#b8860b&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt; 0; i &amp;lt; &lt;span style=&#34;color:#b68;font-weight:bold&#34;&gt;${#&lt;/span&gt;&lt;span style=&#34;color:#b8860b&#34;&gt;PRESSH&lt;/span&gt;[*]&lt;span style=&#34;color:#b68;font-weight:bold&#34;&gt;}&lt;/span&gt;; i++&lt;span style=&#34;color:#666&#34;&gt;))&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;do&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;fusermount -u &lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b8860b&#34;&gt;$LOCALMOUNTDIR&lt;/span&gt;&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;&lt;/span&gt;/&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b68;font-weight:bold&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#b8860b&#34;&gt;PRESSH&lt;/span&gt;[&lt;span style=&#34;color:#b8860b&#34;&gt;$i&lt;/span&gt;]&lt;span style=&#34;color:#b68;font-weight:bold&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a2f&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b68;font-weight:bold&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#b8860b&#34;&gt;PRESSH&lt;/span&gt;[&lt;span style=&#34;color:#b8860b&#34;&gt;$i&lt;/span&gt;]&lt;span style=&#34;color:#b68;font-weight:bold&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34; unmounted.&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;done&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a2f&#34;&gt;exit&lt;/span&gt; &lt;span style=&#34;color:#666&#34;&gt;0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-style:italic&#34;&gt;# Should another SSHFS storage be mounted?&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;function&lt;/span&gt; quitquestion &lt;span style=&#34;color:#666&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;zenity --question &lt;span style=&#34;color:#b62;font-weight:bold&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;--text&lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;Mount another SSHFS storage?&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#666&#34;&gt;[&lt;/span&gt; &lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b8860b&#34;&gt;$?&lt;/span&gt;&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#666&#34;&gt;]&lt;/span&gt;; &lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;then&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a2f&#34;&gt;exit&lt;/span&gt; &lt;span style=&#34;color:#666&#34;&gt;0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;fi&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sshadd&#x9;&#x9;&lt;span style=&#34;color:#080;font-style:italic&#34;&gt;# sshadd function&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-style:italic&#34;&gt;# Loop for endless mounts until stopped by unmount or unmountquestion&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;while&lt;/span&gt; :&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;do&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;mount&#x9;&lt;span style=&#34;color:#080;font-style:italic&#34;&gt;# mount function&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;done&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Save this script to some place (e.g. &lt;em&gt;/home/user/sftp-mount.sh&lt;/em&gt;) and make it executable (chmod +x /home/user/sftp-mount.sh). For example, you are now ready put a shortcut in your panel or on your desktop to make it easily accessible. Please note that zenity, sshfs and ssh-askpass should be installed.&lt;/p&gt;&#xA;&lt;p&gt;Please test the procedures written above on your servers and (if they have SSH access) webspaces. On my 4 webspaces and my vServer it works perfectly with my Debian Sid system. Please contact me or write in the comments if you have any problems or if some steps could be improved – nobody’s perfect :)&lt;/p&gt;&#xA;&lt;h2 id=&#34;appendix&#34;&gt;Appendix&lt;/h2&gt;&#xA;&lt;p&gt;I know following hosters that provide webspace with SSH access. This list is very short so please add more by writing in the comments!&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;uberspace.de (All plans, starting from 1,00€/mon, focussed on german residents) &amp;lt;– great hoster by the way!!&lt;/li&gt;&#xA;&lt;li&gt;OVH.com (&amp;gt;= Business Hosting, 5,94€/mon)&lt;/li&gt;&#xA;&lt;li&gt;HostGator.com (All plans, starting from 3,96$/mon)&lt;/li&gt;&#xA;&lt;li&gt;All-Inkl.com (&amp;gt;= Premium, 9,95€/mon)&lt;/li&gt;&#xA;&lt;li&gt;Hetzner.de (&amp;gt;= Level19, 19,90€/mon)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</content:encoded>
    </item>
  </channel>
</rss>
