Adventures in Open Source

In the past couple weeks I contributed to a bunch of different open source projects in different ways and I thought I'd write about some of them.

Syncthing

I switched from Dropbox to Syncthing a while ago and so far it's been pretty great. I run it on my macOS server in the basement which mirrors everything on its large disks, and also on my various laptops where I selectively sync certain directories that I need.

Dropbox has a feature in its iOS app where it can upload photos taken to Dropbox, which would then sync to my Mac. I've mostly replicated this functionality with Syncthing by using the PhotoSync app which has SFTP support. It automatically uploads new photos to my macOS server via SFTP, which Syncthing picks up and can sync to my laptops.

Recently Syncthing gained kqueue support so it doesn't have to walk the directory structure every n minutes looking for changes. When I opened the Syncthing web interface to add synced photos to my OpenBSD laptop, it prompted me with a "you can use kqueue now, do you want to?" dialog so I enabled it.

Unfortunately a few minutes later, my laptop started getting hot and I noticed that the syncthing processes were using 100% CPU. After doing a ktrace I noticed it was caught in a tight loop trying to monitor a new directory, failing because it was out of file descriptors, and then retrying with no delay, causing the 100% CPU.

This class of problem was actually one that OpenBSD audited for not too long ago. System daemons that spun when running out of file descriptors (such as from an accept) were fixed not to do that as it could cause a remotely triggered DoS.

I opened an issue on the Syncthing issue tracker complaining about the problem, and a fix was committed 5 days later. These kinds of contributions are the easiest to do; just complain about a problem with the proper details, to the right people via the right channel, and they fix it for you.

Crystal

While listening to an episode of the Bike Shed podcast the other week, I was reminded to look into the Crystal programming language once again.

Crystal gained OpenBSD support long ago, but there is no port for it in the OpenBSD ports tree. I found a work-in-progress and tried to get it to compile, but on my 6.3-current laptop, it would crash while trying to compile its compiler.

After the OpenBSD 6.3 release, the OpenBSD kernel was changed to require that allocations from mmap used for thread stacks be done with a new MAP_STACK flag. This allows the kernel to do enforcement of the use of the allocation and kill any program that doesn't do things properly.

This required changes in OpenBSD's ports of Go, sbcl, and some others. I patched Crystal to pass the MAP_STACK flag on OpenBSD, and submitted a pull request to the author of the work-in-progress port. Since Crystal should continue working on OpenBSD out of the box, I submitted a pull request to the upstream Crystal project for formal MAP_STACK support, which was accepted for their 0.25.1 release. Now that the port would no longer require patches, I again updated the work-in-progress port for the new 0.25.1 release.

After running Crystal's std_spec test suite, I noticed some garbage printed when running on OpenBSD because it uses an alternative method to determine the number of CPUs. I submitted another pull request to clean this up, which was quickly merged.

These kinds of contributions are time consuming and technical, but as an OpenBSD developer I find it rewarding to get proper OpenBSD support in upstream projects.

bitwarden-ruby

At some point during this work, someone submitted a pull request to my bitwarden-ruby project to update its Sinatra dependency to fix a security issue in Sinatra. After merging it, another user reported breakage because the Sinatra update pulled in ActiveSupport which somehow broke the .try() functionality I was already using (which doesn't make sense because ActiveSupport includes a .try() implementation). Not wanting to fight things in the future or spend much time on it, I just removed my use of .try() .

Maintaining public projects and accepting contributions from others is not one of my strong points (which is partly why I gave up maintaining Lobsters). Users want to take the project in a different direction and add things that I don't want or care about, and I feel bad telling them "no" just because I don't like it, not really for any technical reason.

OpenBSD

Back to running Crystal's std_spec test suite, I had noticed one test failed on OpenBSD where it was expecting realpath(3) on a dangling symlink to return ENOENT.

After looking into the OpenBSD libc code and seeing how other OSes handled it, I realized it was actually an incompatibility in OpenBSD's libc. I posted about it on our tech@ mailing list and another developer helped come up with a fix.

One of our system tools, installboot(8) was actually relying on the incompatible behavior and would need to be changed before realpath(3) could be fixed in libc. Martijn and I worked together to develop a fix and I committed it a few days ago. Our ports team is doing a full package build of our entire ports tree with the proposed libc patch to determine any fallout. Since OpenBSD is behaving differently than most other OSes, hopefully there won't be anything else relying on our incompatible behavior.

OpenBSD's structure shines here; an issue in our libc was identified, we could easily see which of our own software relied on the behavior and fix it, and we could then build the thousands of 3rd party software packages that work on OpenBSD to identify any fallout before it's committed.

VNC

While testing the realpath behavior on other OSes, I needed to access my macOS server through VNC. OpenBSD's current VNC port is ssvnc which is a modified version of TightVNC. I noticed that when connecting to my Mac even over my fast network, the window would draw very slowly and mouse movements would take a few seconds to respond on the other end.

I had chalked it up to some weird incompatibility between TightVNC's viewer and macOS's native VNC server, but since I was on a streak of fixing things that bothered me this week, I spent some time digging into why it was so slow.

After connecting, vncviewer spit out a warning that it was unable to create a shared-memory image, but continued on its way. I traced this error in the ssvnc code and thought it may have been some problem in the MIT-SHM Xorg extension on OpenBSD. Some hours later debugging Xorg's XShm* functions and OpenBSD's kernel shm functionality, it all turned out to be for nothing. The ssvnc package was simply not being compiled with SHM support because its authors only enabled it on Linux (as Theo sarcastically said a decade ago, "all the world is an i386 running Linux").

Once I built ssvnc with -DMITSHM, the window would draw nearly instantly and my mouse movements reflected in near real-time.

I sent a patch to the ssvnc OpenBSD port maintainer to properly enable -DMITSHM on OpenBSD, who quickly ok'd it and I committed it.

Since we'd prefer to have as few patches as possible in our ports tree, I should really send this patch upstream but it seems like the kind of project where the patch will languish for years because nobody is maintaining it anymore.

cmus

While doing most of this work, I was using the console-based cmus audio player to play music (which is synced from my Mac via Syncthing, see how this is all connected?). One thing that has always bothered me about cmus was that it didn't support album shuffle. Not many music players do, but iPods and iTunes have always supported it and I've gotten used to it.

I don't like listening to songs on normal shuffle mode and prefer to listen to an album all the way through from its first track to the last, then skip to another random album by a different artist and continue playing from its first track.

Continuing my streak of digging in and fixing things, I spent an hour or so reading code and implementing album shuffle in cmus. I'm not sure whether it should be implemented the way I did it, so I'm waiting for the project maintainer to chime in before submitting a pull request for merging.

I like these kinds of contributions where I implement something for myself but it's also something a bunch of other people were waiting for someone to come along and do. I did this when adding configurable margins to iTerm2 and it seemed well received (hi /r/unixporn).

Questions or comments?
Please feel free to contact me.