<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.7.4">Jekyll</generator><link href="http://cmsj.net/feed.xml" rel="self" type="application/atom+xml" /><link href="http://cmsj.net/" rel="alternate" type="text/html" /><updated>2018-09-20T08:29:11+00:00</updated><id>http://cmsj.net/</id><title type="html">cmsj.net</title><subtitle>As what as you like</subtitle><author><name>Chris Jones</name></author><entry><title type="html">Fixing an error in Xcode Instruments’s Leaks profile</title><link href="http://cmsj.net/2018/07/12/xcode-leaks-error.html" rel="alternate" type="text/html" title="Fixing an error in Xcode Instruments's Leaks profile" /><published>2018-07-12T00:00:00+00:00</published><updated>2018-07-12T00:00:00+00:00</updated><id>http://cmsj.net/2018/07/12/xcode-leaks-error</id><content type="html" xml:base="http://cmsj.net/2018/07/12/xcode-leaks-error.html">&lt;p&gt;As part of our general effort to try and raise the quality of Hammerspoon, I’ve been working with &lt;a href=&quot;https://twitter.com/latenitefilms&quot;&gt;@latenitefilms&lt;/a&gt; to track down some memory leaks, which can be very easy if you use the Leaks profile in Xcode’s “Instruments” tool. I tried this various ways, but I kept running into this error:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://cmsj.net/assets/2018-07-12-xcode-leaks-error.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;After asking on the &lt;a href=&quot;https://forums.developer.apple.com/thread/104011&quot;&gt;Apple Developer Forums&lt;/a&gt; we got an interesting response from an Apple employee that code signing might be involved. One change later to not do codesigning on Profile builds and Leaks is working again!&lt;/p&gt;

&lt;p&gt;So there we go, if you see “An error occurred trying to capture Leaks data” and “Unable to acquire required task port”, one thing to check is your code signing setup. I don’t know what specifically was wrong, but it’s easy enough to just not sign local debug/profile builds most of the time anyway.&lt;/p&gt;</content><author><name>Chris Jones</name></author><summary type="html">As part of our general effort to try and raise the quality of Hammerspoon, I’ve been working with @latenitefilms to track down some memory leaks, which can be very easy if you use the Leaks profile in Xcode’s “Instruments” tool. I tried this various ways, but I kept running into this error:</summary></entry><entry><title type="html">Abusing Gmail as a ghetto dashboard</title><link href="http://cmsj.net/2018/07/12/gmail-as-dashboard.html" rel="alternate" type="text/html" title="Abusing Gmail as a ghetto dashboard" /><published>2018-07-12T00:00:00+00:00</published><updated>2018-07-12T00:00:00+00:00</updated><id>http://cmsj.net/2018/07/12/gmail-as-dashboard</id><content type="html" xml:base="http://cmsj.net/2018/07/12/gmail-as-dashboard.html">&lt;p&gt;I’m sure many of us receive regular emails from the same source - by which I mean things like a daily status email from a backup system, or a weekly newsletter from a blogger/journalist we like, etc.&lt;/p&gt;

&lt;p&gt;These are a great way of getting notified or kept up to date, but every one of these you receive is also a piece of work you need to do, to keep your Inbox under control. Gmail has a lot of powerful filtering primitives, but as far as I am able to tell, none of them let you manage this kind of email without compromise.&lt;/p&gt;

&lt;p&gt;My ideal scenario would be that, for example, my daily backup status email would keep the most recent copy in my Inbox, and automatically archive older ones. Same for newsletters - if I didn’t read last week’s one, I’m realistically never going to, so once it’s more than a couple of weeks stale, just get it out of my Inbox.&lt;/p&gt;

&lt;p&gt;Thankfully, Google has an indirect way of making this sort of thing work - &lt;a href=&quot;https://developers.google.com/apps-script/&quot;&gt;Google Apps Script&lt;/a&gt;. You can trigger small JavaScript scripts to run every so often, and operate on your data in various Google apps, including Gmail.&lt;/p&gt;

&lt;p&gt;So, I quickly wrote &lt;a href=&quot;https://gist.github.com/cmsj/0d12c452277f32704f347c7fe117215a&quot;&gt;this script&lt;/a&gt; and it runs every few hours now:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Configuration data&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Each config should have the following keys:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//  * age_min: maps to 'older_than:' in gmail query terms&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//  * age_max: maps to 'newer_than:' in gmail query terms&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//  * query: freeform gmail query terms to match against&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// The age_min/age_max values don't need to exist, given the freeform query value, &lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// but age_min forces you to think about how frequent the emails are, and age_max &lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// forces you to not search for every single email tha matches the query&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// TODO:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//  * Add a per-config flag that skips the archiving if there's only one matching thread (so the most recent matching email always stays in Inbox)&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;configs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;age_min&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;14d&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;age_max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;90d&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;subject:(Benedict's Newsletter)&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;age_min&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;7d&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;na&quot;&gt;age_max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;30d&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;from:hello@visualping.io subject:gnubert&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;age_min&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;1d&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;na&quot;&gt;age_max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;7d&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;na&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;subject:(Nightly clone to Thunderbay4 Successfully)&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;age_min&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;1d&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;na&quot;&gt;age_max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;7d&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;na&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;from:Amazon subject:(Arriving today)&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;processInbox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;config_key&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;configs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;config&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;configs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;config_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;Logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Processing query: &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;query&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;threads&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;GmailApp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;search&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;in:inbox &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;query&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot; newer_than:&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;age_max&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot; older_than:&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;age_min&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;thread_key&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;threads&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;thread&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;threads&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;thread_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;Logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;  Archiving: &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getFirstMessageSubject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

      &lt;span class=&quot;nx&quot;&gt;thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;markRead&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;moveToArchive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;(apologies for the very basic JavaScript - it’s not a language I have any real desire to be good at. Don’t @ me).&lt;/p&gt;</content><author><name>Chris Jones</name></author><summary type="html">I’m sure many of us receive regular emails from the same source - by which I mean things like a daily status email from a backup system, or a weekly newsletter from a blogger/journalist we like, etc.</summary></entry><entry><title type="html">AmigaOS 4.1 Final Edition in Qemu</title><link href="http://cmsj.net/2018/07/05/amigaos41-in-qemu.html" rel="alternate" type="text/html" title="AmigaOS 4.1 Final Edition in Qemu" /><published>2018-07-05T00:00:00+00:00</published><updated>2018-07-05T00:00:00+00:00</updated><id>http://cmsj.net/2018/07/05/amigaos41-in-qemu</id><content type="html" xml:base="http://cmsj.net/2018/07/05/amigaos41-in-qemu.html">&lt;p&gt;So this is a fun one, some marvellous hackers, including Zoltan Balaton and Sebastien Mauer have been working on Qemu to add support for the &lt;a href=&quot;https://en.wikipedia.org/wiki/Sam460ex&quot;&gt;Sam460ex motherboard&lt;/a&gt;, a PowerPC system from 2010. Of particular interest to me is that this was a board which received an official port of Amiga OS 4, the spiritual successor to AmigaOS, one of my very favourite operating systems.&lt;/p&gt;

&lt;p&gt;I’ll probably write more about this later, but for now, here is a simple screenshot of the install CD having just booted.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Update&lt;/em&gt;: Zoltan has published a page with information about how to get it working, &lt;a href=&quot;http://zero.eik.bme.hu/~balaton/qemu/amiga/&quot;&gt;see here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://cmsj.net/assets/2018-07-05-amigaos-qemu.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;</content><author><name>Chris Jones</name></author><summary type="html">So this is a fun one, some marvellous hackers, including Zoltan Balaton and Sebastien Mauer have been working on Qemu to add support for the Sam460ex motherboard, a PowerPC system from 2010. Of particular interest to me is that this was a board which received an official port of Amiga OS 4, the spiritual successor to AmigaOS, one of my very favourite operating systems.</summary></entry><entry><title type="html">Home networking like a pro - Part 1.1 - Network Storage Redux</title><link href="http://cmsj.net/2018/07/04/home-pro-part1-update.html" rel="alternate" type="text/html" title="Home networking like a pro - Part 1.1 - Network Storage Redux" /><published>2018-07-04T00:00:00+00:00</published><updated>2018-07-04T00:00:00+00:00</updated><id>http://cmsj.net/2018/07/04/home-pro-part1-update</id><content type="html" xml:base="http://cmsj.net/2018/07/04/home-pro-part1-update.html">&lt;p&gt;Back in &lt;a href=&quot;/2017/06/22/home-pro-part-1-nas.html&quot;&gt;this post&lt;/a&gt; I described having switched from a Mac Mini + DAS setup, to a Synology and an Intel NUC setup, for my file storage and server needs.&lt;/p&gt;

&lt;p&gt;For a time it was good, but I found myself wanting to run more server daemons, and the NUC wasn’t really able to keep up. The Synology was plodding along fine, but I made the decision to unify them all into a more beefy Linux machine.&lt;/p&gt;

&lt;p&gt;So, I bought an AMD Ryzen 5 1600 CPU and an A320M motherboard, 16GB of RAM and a micro ATX case with 8 drive bays, and set to work. That quickly proved to be a disaster because Linux wasn’t stable on the AMD CPU - I hadn’t even thought to check, because why wouldn’t Linux be stable on an x86_64 CPU in 2018?! With that lesson learned, I swapped out the board/CPU for an Intel i7-8700 and a Z370 motherboard.&lt;/p&gt;

&lt;p&gt;I didn’t go with FreeNAS as my previous post suggested I might, because ultimately I wanted complete control, so it’s a plain Ubuntu Server machine that is fully managed by Ansible playbooks. In retrospect it was a mistake to try and delegate server tasks to an appliance like the Synology, and it was a further mistake to try and deal with that by getting the NUC - I should have just cut my losses and gone straight to a Linux server. Lesson learned!&lt;/p&gt;

&lt;p&gt;Instead of getting lost in the weeds of purchase choices and justifications, instead let’s look at some of the things I’m doing to the server with Ansible.&lt;/p&gt;

&lt;p&gt;First up is root disk encryption - it’s nice to know that your data is private when at rest, but a headless machine in a cupboard is not a fun place to be typing a password on boot. Fortunately I have two ways round this - firstly, a KVM (a Lantronix Spider) and secondly, one can add dropbear to an initramfs so you can ssh into the initramfs to enter the password.&lt;/p&gt;

&lt;p&gt;Here’s the playbook tasks that put dropbear into the initramfs:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Install dropbear-initramfs&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;apt&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;dropbear-initramfs&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;present&lt;/span&gt;

&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Install busybox-static&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;apt&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;busybox-static&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;present&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# This is necessary because of https://bugs.launchpad.net/ubuntu/+source/busybox/+bug/1651818&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Add initramfs hook to fix cryptroot-unlock&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;copy&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;dest&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/etc/initramfs-tools/hooks/zz-busybox-initramfs-fix&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;dropbear-initramfs/zz-busybox-initramfs-fix&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;0744&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;owner&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;root&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;root&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;notify&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;update initramfs&lt;/span&gt;

&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Configure dropbear-initramfs&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;lineinfile&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/etc/dropbear-initramfs/config&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;regexp&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;DROPBEAR_OPTIONS'&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;DROPBEAR_OPTIONS=&quot;-p&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;31337&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;-s&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;-j&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;-k&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;-I&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;60&quot;'&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;notify&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;update initramfs&lt;/span&gt;

&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Add dropbear authorized_keys&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;copy&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;dest&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/etc/dropbear-initramfs/authorized_keys&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;dropbear-initramfs/dropbear-authorized_keys&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;0600&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;owner&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;root&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;root&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;notify&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;update initramfs&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# The format of the ip= kernel parameter is: &amp;lt;client-ip&amp;gt;:&amp;lt;server-ip&amp;gt;:&amp;lt;gw-ip&amp;gt;:&amp;lt;netmask&amp;gt;:&amp;lt;hostname&amp;gt;:&amp;lt;device&amp;gt;:&amp;lt;autoconf&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# It comes from https://git.kernel.org/pub/scm/libs/klibc/klibc.git/tree/usr/kinit/ipconfig/README.ipconfig?id=HEAD&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Configure boot IP and consoleblanking&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;lineinfile&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/etc/default/grub&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;regexp&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;GRUB_CMDLINE_LINUX_DEFAULT'&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;GRUB_CMDLINE_LINUX_DEFAULT=&quot;ip=10.0.88.11::10.0.88.1:255.255.255.0:gnubert:enp0s31f6:none&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;loglevel=7&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;consoleblank=0&quot;'&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;notify&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;update grub&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;While this does rely on some external files, the important one is &lt;code class=&quot;highlighter-rouge&quot;&gt;zz-busybox-initramfs-fix&lt;/code&gt; which works around &lt;a href=&quot;https://bugs.launchpad.net/ubuntu/+source/busybox/+bug/1651818&quot;&gt;a bug&lt;/a&gt; in the busybox build that Ubuntu is currently using. Rather than paste the whole script here, you can see it &lt;a href=&quot;https://gist.github.com/cmsj/515fbf602f983e796ea11f95ce32d537&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The last task in the playbook configures Linux to boot with a particular networking config on a particular NIC, so you can ssh in. Once you’re in, just run &lt;code class=&quot;highlighter-rouge&quot;&gt;cryptsetup-unlock&lt;/code&gt; and your encrypted root is unlocked!&lt;/p&gt;

&lt;p&gt;Another interesting thing I’m doing, is using &lt;a href=&quot;https://github.com/borgbackup&quot;&gt;Borg&lt;/a&gt; for some backups. It’s a pretty clever backup system, and it works over SSH, so I use the following Ansible task to allow a particular SSH key to log in to the server as root, in a way that forces it to use Borg:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Deploy ssh borg access&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;authorized_key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;root&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;present&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;key_options&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;command=&quot;/usr/bin/borg&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;serve&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;--restrict-to-path&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/srv/tank/backups/borg&quot;,restrict'&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ssh-rsa&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;BLAHBLAH&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;cmsj@foo&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now on client machines I can run &lt;code class=&quot;highlighter-rouge&quot;&gt;borg create --exclude-caches --compression=zlib -v -p -s ssh://gnuborg:22/srv/tank/backups/borg/foo/backups.borg::cmsj-{utcnow} $HOME&lt;/code&gt; and because &lt;code class=&quot;highlighter-rouge&quot;&gt;gnuborg&lt;/code&gt; is defined in &lt;code class=&quot;highlighter-rouge&quot;&gt;~/.ssh/config&lt;/code&gt; it will use all the right ssh options (username, hostname and the SSH key created for this purpose):&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Host gnuborg
  User root
  Hostname gnubert.local
  IdentityFile ~/.ssh/id_rsa_herborg
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;</content><author><name>Chris Jones</name></author><summary type="html">Back in this post I described having switched from a Mac Mini + DAS setup, to a Synology and an Intel NUC setup, for my file storage and server needs.</summary></entry><entry><title type="html">Homebridge server monitoring</title><link href="http://cmsj.net/2018/07/02/homebridge-server-monitoring.html" rel="alternate" type="text/html" title="Homebridge server monitoring" /><published>2018-07-02T00:00:00+00:00</published><updated>2018-07-02T00:00:00+00:00</updated><id>http://cmsj.net/2018/07/02/homebridge-server-monitoring</id><content type="html" xml:base="http://cmsj.net/2018/07/02/homebridge-server-monitoring.html">&lt;p&gt;&lt;a href=&quot;https://github.com/nfarina/homebridge&quot;&gt;Homebridge&lt;/a&gt; is a great way to expose arbitrary devices to Apple’s HomeKit platform. It has helped bridge the Google Nest and Netgear Arlo devices I have in my home, into my iOS devices, since neither of those manufacturers appear to be interested in becoming officially HomeKit compatible.&lt;/p&gt;

&lt;p&gt;London has been having a little bit of a heatwave recently and it got me thinking about the Linux server I have running in a closet under the stairs - it has pretty poor airflow available to it, and I didn’t know how hot its CPU was getting.&lt;/p&gt;

&lt;p&gt;So, by the power of JavaScript, Homebridge and Linux’s &lt;code class=&quot;highlighter-rouge&quot;&gt;/sys&lt;/code&gt; filesystem, I was able to quickly whip up &lt;a href=&quot;https://github.com/cmsj/homebridge-linux-temperature&quot;&gt;a plugin&lt;/a&gt; for Homebridge that will read an entry from Linux’s temperature monitoring interface, and present it to HomeKit. In theory I could use it for sending notifications, but in practice I’m doing that via &lt;a href=&quot;https://grafana.com/&quot;&gt;Grafana&lt;/a&gt; - the purpose of getting the information in HomeKit is so I can ask Siri what the server’s temperature is.&lt;/p&gt;

&lt;p&gt;The configuration is very simple, allowing you to configure one temperature sensor per instance of the plugin (but you could define multiple instances in your Homebridge &lt;code class=&quot;highlighter-rouge&quot;&gt;config.json&lt;/code&gt;):&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;accessory&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;LinuxTemperature&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;gnubert&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;sensor_path&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/sys/bus/platform/devices/coretemp.0/hwmon/hwmon0/temp1_input&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;divisor&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;(&lt;code class=&quot;highlighter-rouge&quot;&gt;gnubert&lt;/code&gt; is the hostname of my server).&lt;/p&gt;

&lt;p&gt;Below is a screenshot showing the server’s CPU temperature mingling with all of the Nest and Arlo items :)&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://cmsj.net/assets/2018-07-02-server-temp-homekit.jpg&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;</content><author><name>Chris Jones</name></author><summary type="html">Homebridge is a great way to expose arbitrary devices to Apple’s HomeKit platform. It has helped bridge the Google Nest and Netgear Arlo devices I have in my home, into my iOS devices, since neither of those manufacturers appear to be interested in becoming officially HomeKit compatible.</summary></entry><entry><title type="html">A little bit of automation of the Trello Mac App</title><link href="http://cmsj.net/2018/06/19/trello-mac-app-automation.html" rel="alternate" type="text/html" title="A little bit of automation of the Trello Mac App" /><published>2018-06-19T00:00:00+00:00</published><updated>2018-06-19T00:00:00+00:00</updated><id>http://cmsj.net/2018/06/19/trello-mac-app-automation</id><content type="html" xml:base="http://cmsj.net/2018/06/19/trello-mac-app-automation.html">&lt;p&gt;&lt;a href=&quot;https://www.trello.com&quot;&gt;Trello&lt;/a&gt; have a Mac app, which I use for work and it struck me this morning that several recurring calendar events I have, which exist to remind me to review a particular board, would be much more pleasant if they contained a link that would open the board directly.&lt;/p&gt;

&lt;p&gt;That would be easy if I used the Trello website, but I quite like the app (even though it’s really just a browser pretending to be an app), so I went spelunking.&lt;/p&gt;

&lt;p&gt;To cut a long story short, the Trello Mac app registers itself as a handler for &lt;code class=&quot;highlighter-rouge&quot;&gt;trello://&lt;/code&gt; URLs, so if you take any &lt;code class=&quot;highlighter-rouge&quot;&gt;trello.com&lt;/code&gt; board URL and replace the &lt;code class=&quot;highlighter-rouge&quot;&gt;https://&lt;/code&gt; part with &lt;code class=&quot;highlighter-rouge&quot;&gt;trello://&lt;/code&gt; you can use it as a link in your calendar (or anywhere else) and it will open the board in the app.&lt;/p&gt;</content><author><name>Chris Jones</name></author><summary type="html">Trello have a Mac app, which I use for work and it struck me this morning that several recurring calendar events I have, which exist to remind me to review a particular board, would be much more pleasant if they contained a link that would open the board directly.</summary></entry><entry><title type="html">Receiving remote syslog events with systemd</title><link href="http://cmsj.net/2018/06/15/systemd-remote-syslog.html" rel="alternate" type="text/html" title="Receiving remote syslog events with systemd" /><published>2018-06-15T00:00:00+00:00</published><updated>2018-06-15T00:00:00+00:00</updated><id>http://cmsj.net/2018/06/15/systemd-remote-syslog</id><content type="html" xml:base="http://cmsj.net/2018/06/15/systemd-remote-syslog.html">&lt;p&gt;&lt;a href=&quot;https://www.freedesktop.org/wiki/Software/systemd/&quot;&gt;Systemd&lt;/a&gt; includes &lt;code class=&quot;highlighter-rouge&quot;&gt;journald&lt;/code&gt;, a fancy replacement for the venerable &lt;code class=&quot;highlighter-rouge&quot;&gt;syslog&lt;/code&gt; daemon (and its descendents, &lt;code class=&quot;highlighter-rouge&quot;&gt;syslog-ng&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;rsyslog&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;One interesting, but frustrating, decision by &lt;code class=&quot;highlighter-rouge&quot;&gt;journald&lt;/code&gt;’s maintainers is that it does not speak the syslog network protocol, so it’s unable to receive remote syslog events. Remote syslog is a tremendously useful feature for aggregating log data from many hosts on a network - I’ve always used it so my network devices can log somewhere I’m likely to look at, but I haven’t been able to do that since &lt;code class=&quot;highlighter-rouge&quot;&gt;journald&lt;/code&gt; arrived.&lt;/p&gt;

&lt;p&gt;While there are many ways to skin this goose, the method I’ve chosen is a tiny Python daemon that listens on syslog’s UDP port (514), does minimal processing of the data and then feeds it into &lt;code class=&quot;highlighter-rouge&quot;&gt;journald&lt;/code&gt; via its API, to get the data as rich as possible (since one of &lt;code class=&quot;highlighter-rouge&quot;&gt;journald&lt;/code&gt;’s strengths is that it can store a lot more metadata about a log entry).&lt;/p&gt;

&lt;p&gt;So, &lt;a href=&quot;https://gist.github.com/cmsj/e03b6d28325ce5c3d5b255256278a330&quot;&gt;here is the source&lt;/a&gt; for the daemon, and &lt;a href=&quot;https://gist.github.com/cmsj/71f987d1129c5dc693243dd1aa5f8f4f&quot;&gt;here is the systemd service file&lt;/a&gt; that manages it - note that it runs as an unprivileged user, with the sole privilege escalation of being able to bind to low port numbers (something only root can do normally).&lt;/p&gt;

&lt;p&gt;The daemon is certainly not perfect (patches welcome!), but it works. Here is a &lt;code class=&quot;highlighter-rouge&quot;&gt;journald&lt;/code&gt; log entry from one of my UniFi access points:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Jun 15 21:28:26 gnubert (&quot;U7PG2,802aa8d48ab3,v3.9.27.8537&quot;)[23506]: kernel: [4251792.410000] [wifi1] FWLOG: [58855274] BEACON_EVENT_SWBA_SEND_FAILED (  )
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;(the more syslog-obsessed among you will notice that I’m setting the &lt;code class=&quot;highlighter-rouge&quot;&gt;identifier&lt;/code&gt; to the hostname of the device that sent the message. Internally, the &lt;code class=&quot;highlighter-rouge&quot;&gt;facility&lt;/code&gt; is mapped correctly, as is the &lt;code class=&quot;highlighter-rouge&quot;&gt;priority&lt;/code&gt;. The text of the message then appears, prepended by its &lt;code class=&quot;highlighter-rouge&quot;&gt;identifier&lt;/code&gt;.&lt;/p&gt;</content><author><name>Chris Jones</name></author><summary type="html">Systemd includes journald, a fancy replacement for the venerable syslog daemon (and its descendents, syslog-ng and rsyslog).</summary></entry><entry><title type="html">Homebridge in Docker, an adventure in networking</title><link href="http://cmsj.net/2018/06/15/homebridge-in-docker.html" rel="alternate" type="text/html" title="Homebridge in Docker, an adventure in networking" /><published>2018-06-15T00:00:00+00:00</published><updated>2018-06-15T00:00:00+00:00</updated><id>http://cmsj.net/2018/06/15/homebridge-in-docker</id><content type="html" xml:base="http://cmsj.net/2018/06/15/homebridge-in-docker.html">&lt;p&gt;&lt;a href=&quot;https://github.com/nfarina/homebridge&quot;&gt;Homebridge&lt;/a&gt; is a great way of connecting &lt;a href=&quot;https://www.npmjs.com/search?q=homebridge&quot;&gt;loads&lt;/a&gt; of devices that don’t support Apple’s &lt;a href=&quot;https://www.apple.com/uk/ios/home/&quot;&gt;HomeKit&lt;/a&gt;, to your iOS devices. It consists of a daemon that understands the &lt;a href=&quot;https://developer.apple.com/support/homekit-accessory-protocol/&quot;&gt;HomeKit Accessory Protocol&lt;/a&gt; and &lt;a href=&quot;https://www.npmjs.com/search?q=homebridge&quot;&gt;many plugins&lt;/a&gt; that talk to other devices/services.&lt;/p&gt;

&lt;p&gt;My home server is running Ubuntu, so installing Homebridge is fairly trivial, except I run all my services in &lt;a href=&quot;https://www.docker.com&quot;&gt;Docker&lt;/a&gt; containers. To make things even more fun, I don’t build or manage the containers by hand - the building is done by &lt;a href=&quot;https://hub.docker.com/u/cmsj/&quot;&gt;Docker Hub&lt;/a&gt; and the containers are deployed and managed by &lt;a href=&quot;https://www.ansible.com/&quot;&gt;Ansible&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So far so good, except that for a long time Homebridge used &lt;a href=&quot;https://www.avahi.org/&quot;&gt;Avahi&lt;/a&gt; (an Open Source implementation of Apple’s Bonjour host/service discovery protocol) to announce its devices. That presented a small challenge in that I didn’t want to have Avahi running only in that container, so I had to bind mount &lt;code class=&quot;highlighter-rouge&quot;&gt;/var/run/avahi-daemon/&lt;/code&gt; into the container.&lt;/p&gt;

&lt;p&gt;I recently rebuilt my Homebridge container to pull it up to the latest versions of Homebridge and the plugins I use, but it was no longer announcing devices on my LAN, and there were no mentions of Avahi in its log. After some digging, it turns out that the HomeKit Accessory Protocol (HAP) library that Homebridge uses, now instantiates its own multicast DNS stack rather than using Avahi.&lt;/p&gt;

&lt;p&gt;Apart from not actually working, this was great news, I could remove the &lt;code class=&quot;highlighter-rouge&quot;&gt;/var/run&lt;/code&gt; bind mount from the container, making things more secure, I just needed to figure out why it wasn’t showing up.&lt;/p&gt;

&lt;p&gt;The HAP library that Homebridge uses, ends up depending on &lt;a href=&quot;https://github.com/mafintosh/multicast-dns&quot;&gt;this library&lt;/a&gt; to implement mDNS and it makes &lt;a href=&quot;https://github.com/mafintosh/multicast-dns/blob/master/index.js#L147&quot;&gt;a very simple&lt;/a&gt; decision about which network interface it should use. In my case, it was choosing the &lt;code class=&quot;highlighter-rouge&quot;&gt;docker0&lt;/code&gt; bridge interface which explicitly isn’t connected to the outside world. With no configuration options at the Homebridge level to influence the choice of interface, I had to solve the problem at the Docker network layer.&lt;/p&gt;

&lt;p&gt;So, the answer was the following Ansible task to create a Docker network that is attached to my LAN interface (&lt;code class=&quot;highlighter-rouge&quot;&gt;bridge0&lt;/code&gt;) and give it a small portion of a reserved segment in the IP subnet I use:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-YAML&quot;&gt;- name: Configure LANbridge network
  docker_network:
    name: lanbridge
    driver: macvlan
    driver_options:
      parent: bridge0
    ipam_options:
      subnet: '10.0.88.0/24'
      gateway: '10.0.88.1'
      iprange: '10.0.88.32/29'
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;then change the task for the Homebridge container to use this network:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  network_mode: lanbridge
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and now Homebridge is up to date, and working, plus I have a Docker network I can use in the future if any other containerised services need to be very close to the LAN.&lt;/p&gt;</content><author><name>Chris Jones</name></author><summary type="html">Homebridge is a great way of connecting loads of devices that don’t support Apple’s HomeKit, to your iOS devices. It consists of a daemon that understands the HomeKit Accessory Protocol and many plugins that talk to other devices/services.</summary></entry><entry><title type="html">Adventures in Lua stack overflows</title><link href="http://cmsj.net/2018/04/13/lua-stack-adventures.html" rel="alternate" type="text/html" title="Adventures in Lua stack overflows" /><published>2018-04-13T00:00:00+00:00</published><updated>2018-04-13T00:00:00+00:00</updated><id>http://cmsj.net/2018/04/13/lua-stack-adventures</id><content type="html" xml:base="http://cmsj.net/2018/04/13/lua-stack-adventures.html">&lt;p&gt;&lt;a href=&quot;http://www.hammerspoon.org&quot;&gt;Hammerspoon&lt;/a&gt; is heavily dependent on &lt;a href=&quot;http://www.lua.org&quot;&gt;Lua&lt;/a&gt; - it’s the true core of the application, so it’s unavoidable that we have to interact with Lua’s C API in a lot of places. If you’ve never used it before, Lua’s C API is designed to be very simple to integrate with other code, but it also places a fairly high burden on developers to integrate it properly.&lt;/p&gt;

&lt;p&gt;One of the ways that Lua remains simple is by being stack based - when you give Lua a C function and make it available to call from Lua code, you have to conform to a particular way of working. The function arguments supplied by the user will be presented to you on a stack, and when your C code has finished its work, the return values must have been pushed onto the stack. Here’s an example:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;static int someUsefulFunction(lua_State *L) {
    // Fetch our first argument from the stack
    int someNumber = lua_tointeger(L, 1);

    // Fetch our second argument from the stack
    char *someString = lua_tostring(L, 2);

    /* Do some useful work here */

    // Push two return values onto the stack and return 2 so Lua knows how many return values we provided
    lua_pushstring(L, &quot;some result text&quot;);
    lua_pushinteger(L, 42);
    return 2;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;All simple enough.&lt;/p&gt;

&lt;p&gt;In this scenario of calling from Lua→C, Lua creates a pseudo-stack for you, so while it’s good practice to keep the stack neat and tidy (i.e. remove things from it that you don’t need), it’s not critical because apart from the return values, the rest of the stack is thrown away. That pseudo-stack only has 20 slots by default though, so if you’re pushing a lot of return arguments, or using the stack for other things, you may need to use &lt;code class=&quot;highlighter-rouge&quot;&gt;lua_checkstack()&lt;/code&gt; to grow it larger, up to the maximum (2048 slots).&lt;/p&gt;

&lt;p&gt;Where things get more interesting, is when you’re interacting with the Lua stack without having crossed a Lua→C boundary. For example, maybe you’re in a callback function that’s been triggered by some event in your C program, and now you need to call a Lua function that the user gave you earlier. This might look something like this:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;int globalLuaFunction;
void someCallback(int aValue, char* aString) {
    // Fetch a pointer to the shared Lua state object
    lua_State *L = some_shared_lua_state_provider();

    // Push onto the stack, the Lua function previously supplied by the user, from Lua's global registry
    lua_rawgeti(L, LUA_REGISTRYINDEX, globalLuaFunction);

    // Push the two arguments for the Lua function
    lua_pushinteger(L, aValue);
    lua_pushstring(L, aString);

    // Call the Lua function, telling Lua to expect two arguments
    lua_call(L, 2, 0);

    return;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Slightly more complex than the last example, but still manageable. Unfortunately in practice this is a fairly suboptimal implementation of a C→Lua call - storing things in the &lt;code class=&quot;highlighter-rouge&quot;&gt;LUA_REGISTRYINDEX&lt;/code&gt; table is fine, but it’s often nicer to use multiple tables for different things. The big problem here though is that &lt;code class=&quot;highlighter-rouge&quot;&gt;lua_call()&lt;/code&gt; doesn’t trap errors. If the Lua code raises an exception, Lua will &lt;code class=&quot;highlighter-rouge&quot;&gt;longjmp&lt;/code&gt; to a panic handler and &lt;code class=&quot;highlighter-rouge&quot;&gt;abort()&lt;/code&gt; your app.&lt;/p&gt;

&lt;p&gt;So, writing this a bit more completely, we get:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;int luaCallbackTable;
int globalLuaFunctionRef;
void someCallback(int aValue, char* aString) {
    // Fetch a pointer to the shared Lua state object
    lua_State *L = some_shared_lua_state_provider();

    // Push onto the stack, the table we keep callback references in, from Lua's global registry
    lua_rawgeti(L, LUA_REGISTRYINDEX, luaCallbackTable);

    // Push onto the stack, from our callback reference table, the Lua function previously supplied by the user
    lua_rawgeti(L, -1, globalLuaFunctionRef);

    // Push the two arguments for the Lua function
    lua_pushinteger(L, aValue);
    lua_pushstring(L, aString);

    // Protected call to the Lua function, telling Lua to expect two arguments
    lua_pcall(L, 2, 0, 0);

    return;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Ok so this is looking better, we have our own table for neatly storing function references and we’ll no longer &lt;code class=&quot;highlighter-rouge&quot;&gt;abort()&lt;/code&gt; if the Lua function throws an error.&lt;/p&gt;

&lt;p&gt;However, we now have a problem, we’re leaking at least one item onto Lua’s stack and possibly two. Unlike in the Lua→C case, we are not operating within the safe confines of a pseudo-stack, so anything we leak here will stay permanently on the stack, and at some point that’s likely to cause the stack to overflow.&lt;/p&gt;

&lt;p&gt;Now here is the kicker - stack overflows are really hard to find by default, you don’t typically get a nice error, your program will simply leak stack slots until the stack overflows, far from the place where the leak is happening, then segfault, and your backtraces will have very normal looking Lua API calls in them.&lt;/p&gt;

&lt;p&gt;If we were to handle the stack properly, the above could would actually look like this (and note that we’ve gone from four Lua API calls in the first C→Lua example, to eight here):&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;int luaCallbackTable;
int globalLuaFunctionRef;
void someCallback(int aValue, char* aString) {
    // Fetch a pointer to the shared Lua state object
    lua_State *L = some_shared_lua_state_provider();

    // Find luaCallbackTable in the Lua registry, and push it onto the stack
    lua_rawgeti(L, LUA_REGISTRYINDEX, luaCallbackTable);

    // Find globalLuaFunctionRef in luaCallbackTable, and push it onto the stack
    lua_rawgeti(L, -1, globalLuaFunctionRef);

    // Remove luaCallbackTable from the stack *THIS WAS LEAKED IN THE ABOVE EXAMPLE*
    lua_remove(L, -2);

    // Push the two arguments for the Lua function
    lua_pushinteger(L, aValue);
    lua_pushstring(L, aString);

    if (lua_pcall(L, 2, 0, 0) == false) {
        // Fetch the Lua error message from the stack
        char *someError = lua_tostring(L, -1);
        printf(&quot;ERROR: %s\n&quot;, someError);

        // Remove the Lua error message from the stack *THIS WAS LEAKED IN THE ABOVE EXAMPLE*
        lua_pop(L, -1);
    }

    return;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Hammerspoon has been having problems like this for the last few months - lots of crash reports that on the surface, look like completely valid code was executing. I have to admit that it took me a lot longer than it should have, to realise that these were Lua stack overflows rather than my initial suspicion (C heap corruption), but we figured it out eventually and have hopefully fixed all of the leaks.&lt;/p&gt;

&lt;p&gt;So, how did we discover that the problem was stack overflows, and how did we discover where all of the leaks were without manually auditing all of the places where we make C→Lua transitions (of which there are over 100). The answer to the first question is very simple, by defining &lt;code class=&quot;highlighter-rouge&quot;&gt;LUA_USE_APICHECK&lt;/code&gt; when compiling Lua, it will do a little extra work to verify its consistency. Crucially, this includes calling &lt;code class=&quot;highlighter-rouge&quot;&gt;abort()&lt;/code&gt; with a helpful message when the stack overflows. We turned this on for developers in March and then released 0.9.61 with it enabled, in early April. It’s not normally recommended to have the API checker enabled in production because it calls &lt;code class=&quot;highlighter-rouge&quot;&gt;abort()&lt;/code&gt;, but we felt that it was important to get more information about the crashes we couldn’t reproduce.&lt;/p&gt;

&lt;p&gt;Within a few days we started getting crash reports with the words &lt;code class=&quot;highlighter-rouge&quot;&gt;stack overflow&lt;/code&gt; in them (as well as a few other errors, which we were able to fix), but that is only half the battle.&lt;/p&gt;

&lt;p&gt;Having discovered that we did definitely have a stack leak somewhere, how did we discover where it was? This did involve a little brute force effort, but thankfully not a full manual audit of all 107 C→Lua call sites. Instead, I wrote two macros:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;#define _lua_stackguard_entry(L) int __lua_stackguard_entry=lua_gettop(L);
#define _lua_stackguard_exit(L) assert(__lua_stackguard_entry == lua_gettop(L));
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;These are very simple to use - you call &lt;code class=&quot;highlighter-rouge&quot;&gt;_lua_stackguard_entry()&lt;/code&gt; just after you’ve obtained a pointer to the Lua state object, and then you call &lt;code class=&quot;highlighter-rouge&quot;&gt;_lua_stackguard_exit()&lt;/code&gt; at every point where the function can return after that. It records the size of the stack (&lt;code class=&quot;highlighter-rouge&quot;&gt;lua_gettop()&lt;/code&gt;) at the entry point and &lt;code class=&quot;highlighter-rouge&quot;&gt;assert()&lt;/code&gt;s that it’s the same at the exit point (&lt;code class=&quot;highlighter-rouge&quot;&gt;assert()&lt;/code&gt; also calls &lt;code class=&quot;highlighter-rouge&quot;&gt;abort()&lt;/code&gt; if something is wrong, so now we would get crash logs with the crash in the actual function where the leak is happening).
These entry/exit calls were then added to all 107 call sites 4 days after the 0.9.61 was released and I spent 3 evenings testing or manually verifying every site, before releasing 0.9.65 (0.9.62-0.9.64 fixed some of the other bugs found by the API checker in the mean time).&lt;/p&gt;

&lt;p&gt;At the time of writing we’re only 24 hours past the release of 0.9.65, but so far things are looking good - no strange Lua segfault crash reports as yet. There was one issue found today where I’d placed a &lt;code class=&quot;highlighter-rouge&quot;&gt;_lua_stackguard_exit()&lt;/code&gt; call after a C statement that seemed unimportant, but actually caused an important object to be freed, but that is &lt;a href=&quot;https://github.com/Hammerspoon/hammerspoon/commit/95a13554c65568aca2ee6db040895c6345b01b50&quot;&gt;already fixed&lt;/a&gt; and will be included in 0.9.66.&lt;/p&gt;

&lt;p&gt;Assuming we have now fixed the problem, after months of head-scratching, and a few weeks of research, testing and coding, it turns out that across the 107 call sites we only had two stack leaks - &lt;a href=&quot;https://github.com/Hammerspoon/hammerspoon/commit/2b7abf2b33e3ddb17d87e548725959a8bba1ac40#diff-d0e4e7c56ae114494056acc9758d118fR797&quot;&gt;one was in the code that handles tab completion in Hammerspoon’s Console window&lt;/a&gt;, and &lt;a href=&quot;https://github.com/Hammerspoon/hammerspoon/commit/f199351538d7b81bd4a01f349ddeb2e33e76d8e7&quot;&gt;the other was in &lt;code class=&quot;highlighter-rouge&quot;&gt;hs.notify&lt;/code&gt;&lt;/a&gt;. Hopefully you’re all enjoying a more stable Hammerspoon experience, but I think we’ll be leaving both the API checker and the stack guard macros enabled since they make it very easy to find/fix these sorts of bugs. I’d rather get a smaller number of crashes sooner, than have more months of head-scratching!&lt;/p&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Discuss on &lt;a href=&quot;https://twitter.com/cmsj/status/984592229472833536&quot;&gt;Twitter&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;Discuss on &lt;a href=&quot;https://news.ycombinator.com/item?id=16826199&quot;&gt;Hacker News&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;</content><author><name>Chris Jones</name></author><summary type="html">Hammerspoon is heavily dependent on Lua - it’s the true core of the application, so it’s unavoidable that we have to interact with Lua’s C API in a lot of places. If you’ve never used it before, Lua’s C API is designed to be very simple to integrate with other code, but it also places a fairly high burden on developers to integrate it properly.</summary></entry><entry><title type="html">Getting battery data from AirPods in macOS</title><link href="http://cmsj.net/2017/11/27/airpod-battery-ear-data.html" rel="alternate" type="text/html" title="Getting battery data from AirPods in macOS" /><published>2017-11-27T00:00:00+00:00</published><updated>2017-11-27T00:00:00+00:00</updated><id>http://cmsj.net/2017/11/27/airpod-battery-ear-data</id><content type="html" xml:base="http://cmsj.net/2017/11/27/airpod-battery-ear-data.html">&lt;p&gt;A recent &lt;a href=&quot;https://github.com/Hammerspoon/hammerspoon/issues/1608&quot;&gt;feature request&lt;/a&gt; for &lt;a href=&quot;http://www.hammerspoon.org&quot;&gt;Hammerspoon&lt;/a&gt; requested that we add support for reading battery information about AirPods (&lt;a href=&quot;http://amzn.to/2zxsZSt&quot;&gt;UK&lt;/a&gt; &lt;a href=&quot;http://amzn.to/2zxl2wn&quot;&gt;US&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Unfortunately because their battery status is quite complex (two earbuds and the case), this information is not reported via the normal IOKit APIs, but with a bit of poking around in the results of &lt;a href=&quot;http://stevenygard.com/projects/class-dump/&quot;&gt;class-dump&lt;/a&gt; for macOS High Sierra I was able to find some relevant methods/properties on &lt;a href=&quot;https://developer.apple.com/documentation/iobluetooth/iobluetoothdevice&quot;&gt;IOBluetoothDevice&lt;/a&gt; that let you read information about the battery level of individual AirPods and the case, plus determine which of the buds are currently in an ear!&lt;/p&gt;

&lt;p&gt;So, the next release of Hammerspoon should include &lt;a href=&quot;https://github.com/Hammerspoon/hammerspoon/commit/e5738e8231b90b0506bbacf62cef6491364c5c22&quot;&gt;this code&lt;/a&gt; to expose all of this information neatly via &lt;code class=&quot;highlighter-rouge&quot;&gt;hs.battery.privateBluetoothBatteryInfo()&lt;/code&gt; 😁&lt;/p&gt;</content><author><name>Chris Jones</name></author><summary type="html">A recent feature request for Hammerspoon requested that we add support for reading battery information about AirPods (UK US).</summary></entry></feed>