Recently I had a bad memory module in my main storage system which is also the backup target of many of my systems. To be on the safe side, I validated all backed up files against their origin via checksums. Interestingly, multiple systems showed differing checksums for all DejaVu font files, i.e. the files in the backup did not match those in the system. Clearly, this was not caused by the bad memory module. It looked very much like a systematic issue.
Read MoreTinkering with the StarFive VisionFive 2
With RISC-V getting more and more attention, I was curious and got myself a StarFive VisionFive 2, a single board computer equipped with a SiFive JH7110 SoC which implements the RV64GC ISA. While the performance is not on par with ARM SBCs yet, the overall experience has been quite good so far. In this article, I share some of my experiences with it.

Stuck UART on the ZedBoard
Recently I grabbed my trusted old Avnet / Digilent ZedBoard to test how well the current Xilinx tools work with this now a decade old platform. However, I was facing a weird issue: The serial output by the Zynq PL was only received via USB on my computer when I was sending characters via UART from my computer to the PL as well. As soon as there was a pause in communication, any further serial output by the PL was silently dropped.
Read MoreHow not to use the HackRF One as a spectrum analyzer
A while ago I wrote about how to use the HackRF One as a spectrum analyzer, and mentioned that apart from using its sweep functionality, it could also be used as a real-time 20 MHz spectrum analyzer using any SDR software. I now had to learn about its limitations and possible pitfalls when using it this way.
I wanted to look at the spectrum put out by an RF transmitter when transmitting a simple sine wave, i.e., an unmodulated carrier. In particular, I wanted to know about any nearby spurs, so looking at it with a high-bandwidth SDR seemed to be a good idea. So I connected the transmitter to the HackRF One, using appropriate attenuation. Looking at the spectrum put out by the transmitter gave me a bit of a shock:
Read Morelibc’s random number generators and what to be aware of when seeding them
When you need pseudo-random numbers in C code, commonly used routines are random()
, rand()
and rand_r(unsigned int*)
. First and foremost, it is important to know that none of these routines produce high quality random numbers suitable for cryptographic use. But there are cases where you just need some random looking numbers. Let’s focus on these use-cases in this blog post.
For example, let’s assume you have a multi-threaded OpenMP program where you need to generate different pseudo-random numbers in each thread. A natural approach to this would be the following:
#pragma omp parallel { unsigned seed = omp_get_thread_num(); #pragma omp for for (...) { int rnd = rand_r(&seed); ... } }
Each thread stores its own state of the random number generator and initializes (seeds) it with its thread number. Therefore, each thread should obtain different random numbers, although they will be idential on each run due to the deterministically chosen initial values. So, what’s wrong with this code? Let’s have a look at the very first random numbers generated for each thread on macOS 12:
Read MoreUnderstanding USB Microphone Whine
I recently got my hands on a USB microphone, namely the Samson Meteor Mic, a large diaphragm electret condenser microphone that not only looks cute but also has overall good reviews online. However, when testing the microphone I was rather disappointed by the annoying background noise that is clearly audible when turning up the volume of the microphone. Have a listen:
We hear two things here: One is relatively normal microphone hiss. The other is a high-pitched noise which you really do not want to have in your recordings. Let’s have a look at the spectrum of the recorded noise, using Audacity and its spectrum analysis (i.e., an FFT):
Read MoreMaking Touchpads with Builtin Windows Gestures Less Annoying on Raspberry Pi OS
I use a Bluetooth keyboard with included touchpad to control a Raspberry Pi 4. To be specific, I use the Keysonic KSK-5220BT by RaidSonic. One thing that annoys me for some time now is: Whenever I touch the touchpad at the very edge, the LXDE menu will open and the cursor won’t move.
The reason is that the touchpad supports Windows swipe gestures. For whatever reason these gestures are (at least with this touchpad) not recognized in software but in hardware and are translated as follows:
- Swipe from right edge > Windows + A
- Swipe from bottom edge > Windows + B
- Swipe from left edge > Windows + Tab
- Swipe from top edge > Windows + Down
Raspberry Pi OS (formerly Raspbian) has a large set of key binding preconfigured in /etc/xdg/openbox/lxde-pi-rc.xml. In particular, every press of the Windows / Super key will open the application menu. So each of these swipe gestures will open the menu and issue an additional key press (A / B / Tab / Down).
A workaround is to edit that file and replace
<keybind key="Super_L"> <action name="Execute"> <command>lxpanelctl menu</command> </action> </keybind>
by
<keybind key="W-a"> <action name="Execute"> <command>true</command> </action> </keybind> <keybind key="W-b"> <action name="Execute"> <command>true</command> </action> </keybind> <keybind key="W-Down"> <action name="Execute"> <command>true</command> </action> </keybind> <keybind key="W-Tab"> <action name="Execute"> <command>true</command> </action> </keybind>
With this modification, the cursor still won’t move when touching the edge of the touchpad. But at least no action is triggered and no key press is issued. But be aware that an update of the raspberrypi-ui-mods package might overwrite these changes.
Update Mar 11, 2023:
In 2021 Raspberry Pi OS updated to Debian Bullseye as a foundation. Additionally, they changed the default window manager from Openbox to Mutter on all devices with more than 2 GiB of memory. For Mutter, the procedure for disabling the annoying key bindings is slightly different:
- Disable Mutters key binding for the Windows / Super key:
gsettings set org.gnome.mutter.keybindings lxpanel-menu "[]"
- Install xbindkeys:
apt install xbindkeys
- Create a file called .xbindkeysrc in your home directory with the following content:
"true" Super_L a "true" Super_L b "true" Super_L Tab "true" Super_L Down
- Manually start xbindkeys or logout and login again.
Figuring out the used space_cache version of a btrfs file system
Btrfs contains different implementations of a free space cache. The default currently is space_cache=v1
. A newer tree-based cache can be activated by the mount option space_cache=v2
. As soon as the new version has been used once, it will automatically be used for any future mount. But how do you figure out which version your file system is actually using?
There are multiple ways to determine the used space cache version. The easiest one is to just mount the file system and look into the kernel log. If the default space cache v1 is used, it will say something like
BTRFS info (device yourdevice): disk space caching is enabled
However, if it already has been configured to use the new space cache v2, it will say something like
BTRFS info (device yourdevice): using free space tree
One can also determine the used space cache version without mounting the file system by peeking into the superblock:
btrfs inspect-internal dump-super /dev/yourdev
If the file system already uses space_cache=v2
, the compat_ro_flags
will contain the flag FREE_SPACE_TREE
.
For completeness: When playing with space_cache=v2
, be aware of the risks (see the remarks on the space_cache mount option in btrfs(5)).
Risks of nginx fastcgi buffering or, how iTunes can mess with your Nextcloud server
As suggested by the title, this post consists of two parts. The first part will explain how nginx fastcgi buffering can be exploited relatively easily to fill up a servers hard drive. The second part will tell you how I had to learn this the hard way…
Read MoreModifying environment variables in the Atom editor
If you are using the Atom editor, you may at some point need to set or modify certain environment variables within your editor, e.g., to allow packages to locate binaries that are not located within your normal $PATH. A solution you often find on the internet is to add a small snippet to your init.coffee script:
process.env.PATH = [ '/my/special/path/for/atom' process.env.PATH ].join(':')
However, this does not always work. Even worse: If it works or not may change each time you launch Atom. So, what is the problem here?
During launch, Atom runs a routine called updateProcessEnv() that configures the environment. However, to not delay startup unnecessarily, this is an asynchronous function. During launch, it is called, then the user’s init.coffee script is run, and only after that the completion of updateProcessEnv is awaited. So if you modify the environment within init.coffee, there is a good chance that Atom’s startup procedure will overwrite it again a couple of milliseconds later.
So, how can we deal with this and reliably modify the environment? Luckily, Atom emits an event as soon as the environment has been setup. So we can use a construct like the following in init.coffee:
modEnv = -> return unless atom.shellEnvironmentLoaded process.env.PATH = [ '/my/special/path/for/atom' process.env.PATH ].join(':') modEnv() atom.emitter.on 'loaded-shell-environment', modEnv