CALENDAR: The UNIX Reminder Service

As a part of my general interest in time management, I wanted to take a look into the UNIX utility CALENDAR, which is a reminder service, that is used on the console.

What is CALENDAR?

CALENDAR is a reminder service and default system utility on FreeBSD and UNIX operating systems. CALENDAR offers simple, effective and secure time management for systems administrators and users. CALENDAR is based on text files, which makes it easy to update, synchronize and backup.

$ calendar
Aug 20 Weather forecast says 11-17 °C 💧
Aug 20* Daily coffee meeting with David Lightman at 15:00
Aug 20 Patch WOPR mainframe in data center
Aug 20 Simulate first-strike on WOPR
Aug 20 Change password for listing games on WOPR

Calendar files

The CALENDAR reminder service uses one or more calendar files. Much like the good old days, when you would have one or more calendars. You might have a private one for personal events, one for recurring birthdays, a shared one for family events and a shared one for project events. Each calendar can hold tasks, recurring events.

By default, CALENDAR will search for a calendar file in the user home. If that is not found, then it will look for a calendar file in the hidden dot calendar directory and the following shared directories. The default name for a calendar is “calendar”.

~
~/.calendar
/usr/local/share/calendar
/usr/share/calendar

Creating calendar files

I recommend, that the hidden calendar directory is used. This protect the calendars from accidental deletion. In this example, a calender for one-time events and a calendar for recurring events is created.

$ mkdir -m 700 ~/.calendar
$ touch ~/.calendar/calendar
$ touch ~/.calendar/calendar.recurring
$ chmod 600 ~/.calendar/*

Calendar file syntax and format

The calendar file format is text based and consist of a list of dates and description. These are separated by a tab. Other calendars can be included.

$ nano -T 12 ~/calendar/calendar
#include <calender.recurring>
#include <calendar.freebsd>
#include <calendar.weather>
15 * Every 15th
10/2 2nd of October
July Every 1st of July
Thursday Every Thursday
2025/9/15 15th of September 2025
04/TueLast Every last Tuesday in April

Using CALENDAR

If you want to see todays calendar events, then run CALENDAR without any arguments. CALENDAR will read the calendar, and any included calendars, and output todays events. You might want to format the date to your local preference in the shell configuration or on the command line.

$ export LC_TIME=C
$ calendar

If you want to see events, that is planned days ahead in time, or happened days back in time, you can use the ahead argument -A and back argument -B. They can be also combined.

$ calendar -B 1 -A 3

By default, the weekend is shown as one, but by using the argument -W, weekends can be treated as any other day. By default, the output lists today and tomorrow, but by using -W 0, only today is show. If you want to see or test a specific day, you can use the argument -t.

The manual is very well written.

$ man calendar

The FreeBSD calendar

On FreeBSD systems, there is a calendar file, that has birthdays of FreeBSD contributers.

$ cat /usr/share/calendar/calendar.freebsd | grep Percival
02/24 Colin Percival <> born in Burnaby, Canada, 1981

Todays events at login

If you would like, that each user gets todays events at the login console, then configure it in the global shell configuration. In this example, the SH is configured.

# nano /etc/profile
events=$(calendar -W 0 2> /dev/null)
if [ -n "$events" ]; then
echo "$events"
fi

Todays events via email

If you want an automatic daily email with todays events, you can configure CRON to do this. In the following example, CRON on a FreeBSD server is configured to automatically send todays events to each system user.

# nano /etc/crontab
0 8 * * * root LC_TIME=C /usr/bin/calendar -a

Todays weather forecast in calendar

If you want to include todays weather forecast, for today and the next days, you can use an online service, that generate ICS calendar files and convert them to CALENDAR format.

In this example, a special URL to the weather forecast was generated on the brilliant website of Meteomatics, which is a free service. The weather forecast comes as a file in ICS format. This file contains the weather forecast for each day through 15 days. Python is used to convert it to CALENDAR format and saved as an optional calender, that local users can include in their calendars.

# nano ~/bin/meteomatics
#!/usr/bin/env python3.11
from icalendar import Calendar
from datetime import datetime
import re
with open("/tmp/meteomat.ics", "rb") as f:
cal = Calendar.from_ical(f.read())
for component in cal.walk("vevent"):
dtstart = component.decoded("dtstart")
if isinstance(dtstart, datetime):
dt = dtstart.date()
else:
dt = dtstart
desc = component.get("description")
if not desc:
continue
temps = [int(x) for x in re.findall(r"([-]?\d+)°C", desc)]
if not temps:
continue
tmin, tmax = min(temps), max(temps)
rain = "💧" if "💧" in desc else ""
print(f"{dt.month}/{dt.day}\tWeather forecast says {tmin}-{tmax} °C {rain}")

CRON is used to update the weather forecast calendar automatically. In this example, it happens daily.

# nano /etc/crontab
@daily root fetch -o /tmp/meteomat.ics "https://ical.meteomatics.com/calendar/Knippelsbro%202%2C%201409%20K%C3%B8benhavn%20K%2C%20Denmark/55.674679_12.587408/en/meteomat.ics" && /root/bin/meteomatics > /usr/share/calendar/calendar.weather

More about CALENDAR

Fujifilm X100VI vs Mobile Phone

This is a real-world test of how well (or how bad) the Fujifilm X100VI camera performs versus a mobile phone camera.

Challenges

Will the dynamic range of the Fujifilm X100VI perform better than a mobile phone? Will the colors of the Fujifilm X100VI be more accurate than a mobile phone? Will sharpness and skin tone in portraits of the Fujifilm X100VI be more pleasing than a mobile phone? Will the Fujifilm X100VI perform better than a mobile phone? Will the Fujifilm X100VI live up to the marketing hype?

Conditions

The pictures were taken as snapshots (no fiddling with special deep settings for each picture) on a common sunny day with harsh light at the beach of Hellerup in Denmark. A challenging scene with contrast and reflecting light. The Fujifilm X100VI and the mobile phone, an older inexpensive Samsung Galaxy A54, were both using default settings. The pictures are straight out of camera (SOOC) JPEGs. There has not been done any editing. The pictures has been automatically cropped to 1:1 aspect ratio and formatted for this web page.

Sample Pictures

Fujifilm X100VI
Samsung Galaxy A54
Fujifilm X100VI
Samsung Galaxy A54
Fujifilm X100VI
Samsung Galaxy A54
Fujifilm X100VI
Samsung Galaxy A54
Fujifilm X100VI
Samsung Galaxy A54
Samsung Galaxy A54 Front

Conclusion

The dynamic range of the Fujifilm X100VI does not outperform a mobile phone. Despite the insane price, the engineers were not able to make the internal computer automatically detect a sunny day and then enable all of its dynamic range, shadow and highlight special tweaks.

The colors of the Fujifilm X100VI are not more accurate than a mobile phone. The colors come out close to accurate, but tends to get muddy from bad contrast or too dark from bad dynamic range.

The sharpness and skin tone in portraits of the Fujifilm X100VI are slightly warmer and more pleasing than a mobile phone, if there is some distance to the subject (not selfie), but the Fujifilm X100VI is, however, unable to capture eyes. In my test, the blue eyes became black and so did facial features! This renders subjects worse than they actual are on scene. The sharpness tends to appear out-of-focus, despite automatic focus enabled, and skin color a bit sluggish red, when taking a selfie.

The Fujifilm X100VI does not perform better than a mobile phone. This sad result is probably not because of hardware limits, but because the Fujifilm X100VI is unable to automatically detect and apply its best settings for the picture. A feature, you would rightfully expect for such an expensive fixed lens camera, with powerful internal computing, for casual fun snapshot photography. Just like a less expensive mobile phone does. It is also worth mentioning, that none of the film simulations is of any help. The resulting images remain worse than a mobile phone. If you really want to get peak performance, you would have to spend a lot of fiddling in and out of deep settings, before each shot. Your friends – and the motive – would be long gone by then. You could also shoot in RAW and spend a huge amount of time in expensive AI driven color grading and editing software, but that would take away the fun casual photography.

The Fujifilm X100VI does not live up to the marketing hype by Fujifilm and the associated influencers. Fujifilm describes the X100VI as “Using a timeless dial-based design, passed down from model to model, the stunning 6th-generation X100VI offers an indulgent, tactile image-making experience that delivers unforgettable content in every creative moment”. The words must be well-thought out, because the “unforgettable” could describe the bad feeling, when you come home and watch the disappointing pictures, that falls behind a less expensive mobile phone. The theory and datasheets (megapixel marketing hype) might be “stunning”, but the actual performance – on the street – is not.

Comments

I have requested Fujifilm and influencers for comments. I am always happy learn, if there are things, I have missed. If you have comments, I will be very happy to read them.

References

System Administrator Appreciation Day

Today is international system administrator appreciation day. Take some time to thank all those people, who work behind the scenes and screens to maintain the advanced UNIX computer systems, that keeps us all online, while keeping our data available, secure and recoverable, when disaster strikes. Thanks.

WOPR.

WOPR

The WOPR mainframe, that stages a massive Soviet first strike, in the movie WarGames from 1983 with Matthew Broderick as David Lightman.

Alias-based webroot method for Let’s Encrypt

This is the procedure for configuring Apache HTTP Server and Let’s Encrypt to use the alias-based webroot method for the ACME challenge by Let’s Encrypt. The benefit of this over stop of Apache is no downtime.

Be aware, that this method does not work, if some virtual hosts redirects port 80.

Create the webroot directory for the ACME challenge by Let’s Encrypt.

# mkdir -p /usr/local/www/letsencrypt/.well-known/acme-challenge/
# chmod -R 755 /usr/local/www/letsencrypt
# chown -R www:www /usr/local/www/letsencrypt

Edit the virtual host configuration file for Apache.

# nano /usr/local/etc/apache24/extra/httpd-vhosts.conf

Add the global alias to the webroot directory for the ACME challenge by Let’s Encrypt. If the Apache server is hosting HTTP as well as HTTPS sites, then a global alias is best.

Alias /.well-known/acme-challenge/ "/usr/local/www/letsencrypt/.well-known/acme-challenge/"
<Directory "/usr/local/www/letsencrypt/.well-known/acme-challenge/">
AllowOverride None
Options None
Require all granted
</Directory>

Perform a sanity check of the configuration file and restart Apache.

# service apache24 configtest
# service apache24 restart

Switch to webroot method for domains. This can be automated with a script.

#!/bin/sh
for f in /usr/local/etc/letsencrypt/renewal/*.conf; do
sed -i '' \
-e '/^authenticator =/d' \
-e '/^webroot_path =/d' \
-e '/^pre_hook *=/d' \
-e '/^post_hook *=/d' \
"$f"
echo "authenticator = webroot" >> "$f"
echo "webroot_path = /usr/local/www/letsencrypt" >> "$f"
done

Edit the crontab file.

# nano /etc/crontab

Configure automatic renew by Let’s Encrypt. In this example, every Monday at 13:13.

13 13 * * 1 root /usr/local/bin/certbot renew

Activate the change.

# service cron restart

Creating a quick backup of a directory with TAR

The is the procedure for creating a quick backup of a directory, and the files in it, by creating an archive with TAR on FreeBSD.

What is TAR on FreeBSD?

TAR is an archive utility, that can be used to archive directories and files into a single archive file. TAR is a fast, safe and native utililty on FreeBSD. TAR works much like ZIP, which is not a native utility on FreeBSD. The benefit of no compression is faster speed and less risc of data corruption.

Creating a backup of a directory with TAR.

In the following example, the BIND DNS directory /usr/local/etc/namedb, and the directories and files in it, will be archived with TAR. The file name for the archive will be named after the ISO 8601 date and the directory. This naming convension is a good practice for quick backup archives. The archive will be stored in current directory, which by default is the home directory. It is a good practice to not leave archives in the active system directories, despite being convenient.

# tar -cf 2025-06-24-namedb.tar /usr/local/etc/namedb

The directory, and the directories and files in it, has now been archived and stored in current directory.

# ls *.tar
2025-06-24-namedb.tar

Listing files in a TAR archive.

In the following example, the contents of the archive is listed to standard out. The archived directories and files are relative to the current directory by default.

# tar -tf 2025-06-24-namedb.tar
usr/local/etc/namedb/
usr/local/etc/namedb/named.conf

Reading (extracting) a file in a TAR archive.

Reading a file in a TAR archive, which means extracting it to default out, can be used for inspecting without extracting to file system first. In the following example, the named.conf is extracted from the archive to standard out. This will show the contents of the file and the output can be piped to utilities, such as GREP, MORE, LESS and TAIL.

# tar -xOf 2025-06-24-namedb.tar usr/local/etc/namedb/named.conf

Extracting from a TAR archive.

In the following example, the directory, and its directories and files, is extracted from the archive. The directory will be extracted to the current directory by default.

# tar -xf 2025-06-24-namedb.tar

In the following example, the directory, and its directories and files, is extracted from the archive to an optional temporary directory.

# tar -xf 2025-06-24-namedb.tar -C tmp/

In the following example, the directory, and its directories and files, is extracted from the archive to the root directory. The will restore the backup to original location. Warning! This will overwrite the original files.

# tar -xf 2025-06-24-namedb.tar -C /

Encrypting a TAR archive with GPG.

The TAR archive, or any file, can be encrypted with a password by using GPG. In the following example, the an encrypted copy of the TAR archive is created with GPG.

# tar -cf - /usr/local/etc/namedb | gpg --symmetric --pinentry-mode loopback > 2025-06-24-namedb.tar.gpg"
Enter passphrase: Pencil

This will create an encrypted copy, that can only be decrypted with the same password. No GPG keys will be needed.

# ls *.gpg
2025-06-24-namedb.tar.gpg

The encrypted TAR archive can be decrypted with GPG and extracted with TAR.

# gpg --decrypt 2025-06-24-namedb.tar.gpg | tar -xf -
Please enter the passphrase for decryption.
Passphrase: Pencil

References.

Safe erasure with zero padding on FreeBSD

What is zero padding?

Zero padding or random padding is the practice of secure wiping of removable storage devices, such as USB sticks, SD cards and external SSDs, so they can be safely re-used within an organization. Zero padding or random padding fills the storage device with zeroes or random bytes. This over-writes existing documents and files on the storage device.

$ dd if=/dev/zero bs=1 count=16 status=none | hexdump -C
00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
$ dd if=/dev/urandom bs=1 count=16 status=none | hexdump -C
00000000 24 e0 e2 a0 69 90 e4 8a 9a d4 7d b9 95 d3 60 c4 |$...i.....}...`.|

If the storage devices was just quickly re-formatted, then documents and files would be recoverable by forensic software or basic operating system utilities, because modern storage devices distribute data to new places on the storage device to ensure optimal life time.

# dd if=/dev/da0 bs=1 skip=1048576 count=512 status=none | hexdump -C
00000000 4e 1e 40 db 85 ab 9f 63 48 9d 51 e2 8f ef 6f a8 |.Pass.is.Pencil.|

Modern SSDs and larger storage devices might require specialized commands or vendor utilities. In such cases, or when the storage device was used to store highly classified documents, consider physically schredding or destroying the storage device instead.

Identifying storage device to be zero padded.

Identify the storage device. In this example, an external 32 GB USB 3.0 stick was attached and identified as da0.

$ dmesg
da0: <Generic Flash Disk 8.13> Removable Direct Access SPC-4 SCSI device
da0: Serial Number 42011337510013
da0: 40.000MB/s transfers
da0: 29600MB (60620800 512 byte sectors)
da0: quirks=0x2<NO_6_BYTE>

Unmounting file systems on storage device.

Make sure, that none of its file systems are mounted.

# umount /dev/da0*

Zero padding a storage device.

Zero padding will fill the entire storage device with zeroes and thereby defeat any attempts of casual recovery of documents of files on it. If you require an even more secure erasure, you can use random padding instead.

# dd if=/dev/zero of=/dev/da0 bs=1M status=progress
dd: /dev/da0: end of device 29 GiB) transferred 2207.307s, 14 MB/s
29601+0 records in
29600+0 records out

In this example, the zero padding of a 32 GB USB 3.0 stick took 37 min. Low write speeds are not uncommon for lower quality USB sticks, despite labeled as USB 3.0.

Random padding a storage device.

Random padding will fill the entire storage device with random data and thereby not only defeat any attempts of casual recovery of documents of files on it, but also provide stronger protection by preventing pattern detection from hardware residue. Random padding takes slightly longer time to complete.

# dd if=/dev/urandom of=/dev/da0 bs=1M status=progress
dd: /dev/da0: end of device 29 GiB) transferred 2450.009s, 13 MB/s bytes (25 GB, 24 GiB) transferred 2008.177s, 13 MB/s
29601+0 records in
29600+0 records out
31037849600 bytes transferred in 2450.640376 secs (12665200 bytes/sec)

In this example, the random padding of a 32 GB USB 3.0 stick took 41 min. This is only 4 min longer than zero padding.

If the storage medium is of magnetic type or you require an even more secure erase, you can perform the random padding several times. However, multiple passes on modern storage media has dimishing returns. In this example, the storage device is random padded 3 times. This is, however, not necessary on modern drivess.

# for i in 1 2 3; do dd if=/dev/urandom of=/dev/da0 bs=1M status=progress; done

Formatting an USB stick with EXFAT.

The storage device has now been safely erased and it can be formatted with a new file system, so it can be used again. In this example, the USB stick is formatted with the well-supported EXFAT file system. The EXFAT file system willl not have a partition scheme. This ensures compatibility.

# mkexfatfs /dev/da0
mkexfatfs 1.4.0
Creating... done.
Flushing... done.
File system created successfully.

Confirm, that it works.

# kldload fusefs
# mount.exfat /dev/da0 /mnt
FUSE exfat 1.4.0 (libfuse2)
# umount /mnt

References.

Encrypting files with GPG

If you will be transferring a secret file from one file system to another, you might want to protect the file from theft or exposure during the transfer. This could be an encryption key file for decrypting a multi-challenge encrypted file system or similar secret file, that are to be transferred on an unencrypted storage medium, such as an USB stick with EXFAT, NTFS or MSDOS file system on it. If you will be using the GNU Privacy Guard (GPG) without GPG key pairs, then you can encrypt with a symmetric cipher by specifying a pass phrase.

Encrypting with symmetric cipher

If you will be using GPG without GPG key pairs, then you can encrypt with a symmetric cipher by specifying a pass phrase. In cryptography, this means, that the same pass phrase will be used to encrypt and decrypt. The user, who will encrypt the file, will choose a pass phrase, which will be used to write a new encrypted file, that contain the secret file. The pass phrase should be as long and non-dictionairy complex as possible. The default cipher is AES-128.

$ gpg --symmetric keyfile.bin

The encrypted file can now be transferred safely.

$ ls keyfile.bin.gpg

You can test, that the file can be decrypted and compare it to the original file.

$ gpg --yes --output keyfile.bin.decrypted --decrypt keyfile.bin.gpg
$ cmp -s keyfile.bin keyfile.bin.decrypted && echo "Pass" || echo "Fail"

The encrypted file can be decrypted on the target file system by anyone, who know the pass phrase, that was used to encrypt the file. It is not required, that the target user has a GPG key pair.

$ gpg --yes --output keyfile.bin --decrypt keyfile.bin.gpg

The decrypted secret file can now be used.

$ ls keyfile.bin

References

Mounting ZFS failed with error 6 during FreeBSD boot.

On a machine with FreeBSD 13.5, that was originally prepared with GELI encryption and ZFS file system by the FreeBSD 11 installer, the following error message appears during the automatic FreeBSD boot proces, before it stops at the moutroot prompt.

Mounting from zfs:zroot/ROOT/default failed with error 6.
Loader variables:
vfs.root.mountfrom=zfs:zroot/ROOT/default
Manual root filesystem specification:
mountroot>

Trying to mount gives label error?

mountroot> zfs: zroot/ROOT/default ?
Sun Solaris label '' ?

The problem occurred after a second, and never used before, SSD was added to the machine with the plan to provide extra storage to the existing ZFS storage pool. The secondary SSD was prepared with GELI and ZFS. The machine was then rebooted.

If the machine is booted from a FreeBSD USB memory stick, then GELI can attach each SSD, if ZFS imports the boot partition with the GELI encryption keyfile first.

POSIX error code 6? Missing device?

# cat loader.conf 
geli_ada0p4_keyfile0_load="YES"
geli_ada0p4_keyfile0_type="ada0p4:geli_keyfile0"
geli_ada0p4_keyfile0_name="/boot/encryption.key"
aesni_load="YES"
geom_eli_load="YES"
geom_eli_passphrase_prompt="YES"
vfs.root.mountfrom="zfs:zroot/ROOT/default"
kern.geom.label.gptid.enable="0"
zpool_cache_load="YES"
zpool_cache_type="/boot/zfs/zpool.cache"
zpool_cache_name="/boot/zfs/zpool.cache"
zfs_load="YES"
pf_load="YES"
pflog_load="YES"

Setting boot flag.

# kldload geom_eli
# kldload zfs
# zpool import -R /mnt bootpool
# geli attach -k /mnt/boot/encryption.key ada0p4
GEOM_ELI: Device ada0p4.eli created.
# geli attach ada1p1
# zpool export bootpool
GEOM_ELI: Device ada1p1.eli created.
# geli info ada0p4.eli
Flags: BOOT
# geli info ada1p1
Flags: AUTORESIZE
# geli configure -b ada1p1
# geli info ada1p1
Flags: BOOT, AUTORESIZE
# geli detach ..
# geli detach ..
# reboot

The solved the issue about the error 6. However, further action was needed, as it stopped at mounting filesystems.

ZFS cache.

Import the ZFS root storage pool. This ensures, that ZFS can see all the devices, and, that the pool is cleanly imported.

# kbdcontrol -l /usr/share/syscons/keymaps/danish.iso.kbd
# kldload geom_eli
# kldload zfs
# mdmfs -s 100m md0 /mnt
# zpool import -f -R /mnt bootpool
# geli attach -k /mnt/boot/encryption.key ada0p4
GEOM_ELI: Device ada0p4.eli created.
# geli attach ada1p1
GEOM_ELI: Device ada1p1.eli created.
# zpool import
  pool: zroot
 state: ONLINE
config:
  zroot         ONLINE
    ada0p4.eli  ONLINE
    ada1p1.eli  ONLINE
# zpool import -f -R /mnt zpool
# zpool status
  pool: zroot
 state: ONLINE
errors: No known data errors

Replace the symbolic link with a directory, that can be used for mounting the boot partition. Export and import the ZFS storage pools, but this time, so the root is complete with the boot partition, where the ZFS cache is stored. Create a backup of the old ZFS cache files and remove them. Generate a new ZFS cache file.

# file /mnt/boot
/mnt/boot: broken symbolic link to /bootpool/pool
# rm /mnt/boot
# mkdir -p /mnt/boot
# zpool export bootpool
# zpool export zroot
# zpool import -f -R /mnt zpool
# zpool import -f -R /mnt/boot bootpool
# find /mnt -type f -name 'zpool.cache'
# cp /mnt/boot/boot/zfs/zpool.cache /mnt/boot/boot/zfs/zpool.cache.bak
# rm /mnt/boot/boot/zfs/zpool.cache
# cp /mnt/etc/zfs/zpool.cache /mnt/etc/zfs/zpool.cache.bak
# rm /mnt/etc/zfs/zpool.cache
# zpool set cachefile=/mnt/boot/boot/zfs/zpool.cache zroot
# zpool set comment="Test" zroot
# find /mnt -type f -name 'zpool.cache'
# zpool export bootpool
# rmdir /mnt/boot
# ln -s /bootpool/boot /mnt/boot ?
# zpool export zroot
# geli detach ada1p1
# geli detach ada0p4
# reboot

This caused bootpool to not get imported. When imported, it mounted as root and bricked.

Boot not mounted.

If /boot is a symbolic link, then delete it.

# ls -ld /boot
/boot -> /bootpool/boot
# zfs get mountpoint bootpool
# zpool set cachefile=/boot/zfs/zpool.cache zroot
# find / -type f -name 'zpool.cache' -delete

X?

Could this be caused, if GELI on the second SSD was configured with same passphrase as primary SSD, but without the requirement for the keyfile component? Could this be caused by the ZFS cache?

Ideas?

  • Suspect that the GELI boot flag are required on secondary SSD for the boot loader to decrypt it. The boot flag might be required for the boot loader to decrypt partitions, that are required for mouting the root.
  • Suspect that ZFS cache causes the error. Refresh it? Delete it?
  • Suspect that incorrect or lack of ZFS labels causes the error. Clear or set?
  • Un-merge the secondary SSD from the ZFS storage pool and attempt to revert to previous working state of the machine.
  • Suspect that lack of GELI keyfile requirement for secondary SSD causes the error. Enable the use of the GELI keyfile for secondary SSD, so both SSDs can be attached with the same user passphrase and the same keyfile by the FreeBSD automatic boot proces.
  • Suspect that lack of GELI keyfile requirement for secondary SSD causes the error. Disable the use of the GELI keyfile for the primary SSD, so both SSDs can be attached with the same user passphrase by the boot loader.

Any ideas?

Conclusion.

Do not add extra storage devices to a FreeBSD machine, that use GELI and ZFS, by simply merging it into the existing pool. You will end up with a bricked system, that is full of catch 22 type critical errors. This is caused by, how the ZFS cache works, and, how the boot partition and root partitions are inter-connected through a symbolic link, that blocks mounting. The way to add extra storage devices is a fresh re-install of the machine. You get the current FreeBSD, which include file system related changes, such as GELI and ZFS changes. You also save yourself a lot of time.

References.

English Language and Danish Regional Setting in FreeBSD

This is the procedure for configuring localization (locale) for English language and Danish regional setting in FreeBSD. Tested on FreeBSD 14.2 with XFCE 4.20 on 2025-03-31.

Introduction.

I have tested the official FreeBSD handbook procedure. I have tested countless guides and advice from forums, discussions and website. They have all failed to meet my basic requirements: English language and Danish regional setting in system console, window manager and applications. See my test below. I wanted Danish ISO 8601 standard format for date, time and numbers. This is also known as YYYY-MM-DD HH:MM:SS. I did not want to see US imperial format for date, time and numbers. I suddently had an idea of hacking an existing English locale and simply linking to a Danish for regional setting. It turned out to work perfect.

What is a locale setting?

A locale setting is made up of the language, the regional setting and the character encoding standard. As an example, en_DK.UTF-8 locale would be for user, who prefer English language in a Danish regional setting with the UTF-8 character encoding standard.

Locale specific information can be proveded with the locale utility. It is important to know, that the utility respect the PATH_LOCALE environment variable. If this is set, then this is used instead of the default locale directory.

# locale -a | grep DK
da_DK.ISO8859-1
da_DK.ISO8859-15
da_DK.UTF-8

Create a new locale.

Create a new locale definition, that is based on English. This new locale definition will be English language with Danish regional setting and UTF-8 character encoding standard.

# cd /usr/share/locale/
# mkdir -p en_DK.UTF-8
# cp -r en_GB.UTF-8/* en_DK.UTF-8/

Link to Danish regional setting.

For time format, numeric format and monetary format, create symbolic links to the Danish locale definition.

# cd en_DK.UTF-8/
# ls
LC_COLLATE LC_CTYPE LC_MESSAGES LC_MONETARY LC_NUMERIC LC_TIME
# ln -sf ../da_DK.UTF-8/LC_TIME .
# ln -sf ../da_DK.UTF-8/LC_NUMERIC .
# ln -sf ../da_DK.UTF-8/LC_MONETARY .

Confirm new locale.

# locale -a | grep DK
da_DK.ISO8859-1
da_DK.ISO8859-15
da_DK.UTF-8
en_DK.UTF-8

Configure FreeBSD login class for English language and Danish regional setting locale.

Configure the FreeBSD login class for English language, Danish regional setting and UTF-8 character encoding standard. This will work for system console, window manager and applications without further configuration. Examples are XFCE and Thunderbird Mail.

# nano /etc/login.conf
default:\
:passwd_format=sha512:\
:copyright=/etc/COPYRIGHT:\
:welcome=/var/run/motd:\
:setenv=BLOCKSIZE=K:\
:mail=/var/mail/$:\
:path=/sbin /bin /usr/sbin /usr/bin /usr/local/sbin /usr/local/bin ~/bin:\
:nologin=/var/run/nologin:\
:cputime=unlimited:\
:datasize=unlimited:\
:stacksize=unlimited:\
:memorylocked=64K:\
:memoryuse=unlimited:\
:filesize=unlimited:\
:coredumpsize=unlimited:\
:openfiles=unlimited:\
:maxproc=unlimited:\
:sbsize=unlimited:\
:vmemoryuse=unlimited:\
:swapuse=unlimited:\
:pseudoterminals=unlimited:\
:kqueues=unlimited:\
:umtxp=unlimited:\
:pipebuf=unlimited:\
:priority=0:\
:ignoretime@:\
:umask=022:\
:charset=UTF-8:\
:lang=en_DK.UTF-8:

Apply the changes by running the capability database utility.

# cap_mkdb /etc/login.conf

Test English language and Danish regional settings.

You can now log out of all virtual terminals and system consoles and test, that the new locale works.

  • English language in FreeBSD system console.
  • Danish special characters input in FreeBSD system console.
  • Danish special characters output in FreeBSD system console.
  • Danish ISO 8601 date and time format in FreeBSD system console.
  • Danish number format in FreeBSD system console.
  • English language in XFCE window manager menu, panels and dialogs.
  • Danish ISO 8601 date and time format in XFCE window manager.
  • English language in Thunderbird Mail.
  • Danish ISO 8601 date and time format in Thunderbird Mail.
  • Danish number format in Thunderbird Mail.

Backup and restore the new locale.

Create a backup of the new locale. This ensures, that it can be restored, if it should get deleted. It can also be used to install on another host.

# tar -cvpzf ~/en_DK.UTF-8.tar.gz -C /usr/share/locale en_DK.UTF-8
a en_DK.UTF-8
a en_DK.UTF-8/LC_COLLATE
a en_DK.UTF-8/LC_TIME
a en_DK.UTF-8/LC_CTYPE
a en_DK.UTF-8/LC_MONETARY
a en_DK.UTF-8/LC_NUMERIC
a en_DK.UTF-8/LC_MESSAGES

The content can be confirmed.

# tar -tvf ~/en_DK.UTF-8.tar.gz

The locale setting can be restored.

# tar -xvpzf ~/en_DK.UTF-8.tar.gz -C /usr/share/locale
x en_DK.UTF-8/
x en_DK.UTF-8/LC_COLLATE
x en_DK.UTF-8/LC_TIME
x en_DK.UTF-8/LC_CTYPE
x en_DK.UTF-8/LC_MONETARY
x en_DK.UTF-8/LC_NUMERIC
x en_DK.UTF-8/LC_MESSAGES

Issue: Port misc/locale-en_DK not working.

I tested the official port and package misc/locale-en_DK. This failed Danish special characters input in FreeBSD system console. I even spent extra time testing custom environment variables.

# pkg install locale-en_DK
Message from locale-en_DK-0.1.1:
In order to set the en_DK.UTF-8 locale for the login shell of an single
user add the following configuration to ~/.login_conf:
me:\
:charset=UTF-8:\
:lang=en_DK.UTF-8:\
:setenv=PATH_LOCALE=/usr/local/share/locale:
More information about the process of configuring login class methods in
available in the handbook.
More information about the PATH_LOCALE environment variable is available in the
locale(1) manual page.

References.