A Fortnight With ASCII

I upgraded from jessie two weeks ago with any trouble. I've been using ASCII without any major issues since but there are a few wrinkles I thought I'd mention. Mind you, ASCII is still a work in progress and most of the wrinkles are not of Devuan's making.

The Good

Let's start off on a positive note and mention what definitely works fine. Networking (both wired and wireless), printing and scanning (using an EPSON PM-A750) and transferring data from my Suunto Ambit sportswatch all work as before. Playing ripped CDs in FLAC format with quodlibet still works (via speakers and headphones). The microphone also works (testing via appear.in) but I haven't been able to test video input (for lack of a webcam).

The Bad

I have been using lv as my preferred pager on jessie but after the upgrade less had become the default. I vaguely recall the same happening when I was still using Debian's testing so this cannot really be blamed on Devuan. Anyway, this was easily fixed with

sudo update-alternatives --set pager /usr/bin/lv

Oh, in case you wonder why I use lv, it does on-the-fly decompression and recognizes a large set of CJK encodings automatically. For me, that's a big plus.

Above, I said that wired networking works fine and it does. It's just that if the cable is not present, the eth0 configuration now halts the boot for about a minute before timing out. This was mentioned on the DNG list recently (it's a Debian introduced issue) and reverting the change to /etc/init.d/networking that broke things, as suggested on the list, made the wait go away.

The Ugly

Middle Button Mouse Copying

For well over twenty years I have been high-lighting text with the mouse and copy it with the mouse's middle button under the X Windows System. That no longer works 😲, without so much as a mention as to why and/or how to re-enable it. This seriously interrupts my workflow when copy-and-pasting links into blog posts and issue trackers.

There has been some talk about this being unilaterally imposed by the "GNOME toolkit" (it hasn't been the GIMP toolkit for a while now) but it seems that this may also have something to do with different copy-and-paste buffer approaches between Wayland and X11. I'm trying out the .config/gtk-3.0/settings.ini mentioned in the first link but I'm seeing inconsistent behaviour.

GUI Rendering Issues

Again, the above is not a Devuan introduced issue but what comes next is and it has to do with the Clearlooks-Phenix-Purpy theme. This is the Devuan default and when used on ASCII makes some applications rather hard to use. It does not affect all applications but for some, Emacs and quodlibet for example, the menu and icon bars are missing spacing between entries. As you can see from the screenshots below, jessie is on the left, ASCII on the right, the menu bar items, icons and menu entries are all squeezed together on ASCII. In addition, the separator lines, check boxes and submenu expanders have disappeared compared to the menu as displayed under jessie.

/images/a-fortnight-with-ascii/emacs-menu.png

Also, scrollbars are not displayed at all or are missing the slider making it just about impossible to get an idea of where in the display you are or move around using the scrollbar. Firefox is also afflicted by the scrollbar problem but even more troublesome is the absence of button, checkbox and entryfield decorations as illustrated in the comparison below. It's a cutout of the Debian packages' search page with the ASCII default theme at the bottom.

/images/a-fortnight-with-ascii/firefox-decorations.png

Fortunately, these problems are easily worked around. All you have to do is select another style via xfce4-appearance-settings. If you prefer to use the desktop menu, it's at "Applications" ▶ "Settings" ▶ "Appearance". A quick tests on my system indicates that you can choose whatever you like except Clearlooks-Phenix-Purpy. To be honest, the Firefox screenshots were both taken on Devuan ASCII 😇 but using the Clearlooks theme for the image on top.

All that said, work is underway on a new default theme for ASCII called Clearlooks-Phenix-DarkPurpy. I pulled the package mentioned in the mail and installed it with

sudo dpkg -i "clearlooks-phenix-darkpurpy-theme_7.0.1-1+devuan2.0_all (2).deb"

and gave it a quick spin. Seems to address all the issues mentioned above 👍. Now it just has to get into the Devuan package repositories.

Upgrading Devuan from Jessie to ASCII

I jumped the Debian steamship on 2017-01-11 and made off in the Devuan lifeboat towards init freedom. Jessie was still beta at the time (it was announced stable on 2017-05-25) but installed without a hitch and it has been smooth sailing from there for me. Progress on ASCII appears to be going fine and seeing the call for upgrade path testing I figured I would start off 2018 on ASCII.

Target System

The machine I have updated is a Libreboot T400 with 8GB of memory. It has two 240GB SSDs in an encrypted RAID1 setup. On top of that LVM manages /, /home, /var/lib/docker and swap in four separate volumes. Note that there is no separate, unencrypted /boot partition because libreboot supports full disk encryption.

I use this laptop for all of my private computing needs (except when I need a VM because hardware virtualization without blobs does not work). It uses Xfce for the desktop environment, is used in wired and wireless environments, connects to an aging EPSON PM-A750 for my printing and scanning needs and my Suunto Ambit sportswatch.

I keep my /etc under version control with etckeeper so I have a full record of any changes there. I've tweaked it a bit so it always records package changes, even if there are no changes below /etc, so I have a persistent record of all package changes as well. The logs in /var/log/apt/history.log* will eventually (after a year in the default setup) be rotated into oblivion.

A full list of the packages I had installed before the upgrade can be found here.

The Upgrade

I made sure that my jessie system was up-to-date like so

$ sed '/^$/d; /^#/d' /etc/apt/sources.list /etc/apt/sources.list.d/*.list
deb http://auto.mirror.devuan.org/merged/ jessie-security  main
deb http://auto.mirror.devuan.org/merged/ jessie-updates   main
deb http://auto.mirror.devuan.org/merged/ jessie           main
deb http://auto.mirror.devuan.org/merged/ jessie-backports main
deb https://download.docker.com/linux/debian jessie edge
$ sudo apt-get update
[...]
$ sudo apt-get dist-upgrade
[...]
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.

Then I changed my APT sources to

$ sed '/^$/d; /^#/d' /etc/apt/sources.list /etc/apt/sources.list.d/*.list
deb http://pkgmaster.devuan.org/merged/ ascii-security  main
deb http://pkgmaster.devuan.org/merged/ ascii-updates   main
deb http://pkgmaster.devuan.org/merged/ ascii           main
deb https://download.docker.com/linux/debian stretch edge

As you can see, I dropped the backports section and also bumped the Docker sources to use the Debian release that most closely corresponds to Devuan's ASCII.

After that it is basically just a matter of running

sudo apt-get update
sudo apt-get dist-upgrade

and answering any prompts you get. I opted to restart any services during the installation when needed but defaulted everything else.

However, before hitting the Enter key on the dist-upgrade, I first checked what would be necessary in terms of disk space and which packages would be removed (purged in my setup). The upgrade needed to download 1GB of packages and would use an additional 714MB of disk space. Seeing that I did not have enough free space in / to accommodate that, I moved APT's package cache temporarily.

sudo mv /var/cache/apt /home
sudo ln -s /home/apt /var/cache/apt

In terms of packages to be removed I only "salvaged" ifupdown. I had this marked automatically installed and my APT setup gleefully purges any such packages unless needed by something else. It seems that changed dependencies have made it "optional".

The upgrade itself was uneventful and everything appears to be working just fine for me. For the record, I have included the full upgrade log (redacted for readability) as well as a list of the packages installed immediately after the upgrade. Seeing that some packages had become unnecessary after the upgrade, I ran

sudo apt-get autoremove
sudo apt-get clean             # zap 1GB of downloads
sudo rm /var/cache/apt
sudo mv /home/apt /var/cache

to finish things up and put APT's cache back in its original place.

Conffile Cleanup

During the upgrade, I was asked what to do with changed configuration files for etckeeper. This happened because I've made a few tweaks of my own to some of those files. I'd opted to keep the files as is but after the upgrade I intergrated most of the changes manually. The files can be found with

sudo find /etc -name '*.dpkg*'

This not only turned up the etckeeper files but also a file for a SANE backend that is no longer provided. The removal of the v4l is mentioned in the apt-listchanges output during the upgrade. I never used it and etckeeper has a copy of the file in its repository anyway so

sudo rm /etc/sane.d/v4l.conf.dpkg-remove

Two more files turned up in the find output and these seem to be the result of issues with the way the Devuan Purpy theme is handled. I've moved those files to what I think is their intended location.

for name in \
    /etc/xdg/xfce4/xfconf/xfce-perchannel-xml/xsettings.xml
    /etc/xdg/xfce4/panel/default.xml; do
    mv $name.dpkg-new $name-xfce4
done

Docker Images Diff'ed

After I announced my Devuan Docker Images, Jaromil suggested that I'd run some kind of (binary) comparison on the images. I said I would but have taken my time getting around to doing so. Better late than never, so here you have it.

Size Matters

The Docker community is slightly obsessed with size. Smaller is considered better. So I compared my Devuan base images with their closest Debian counterparts in terms of download (compressed) and on-disk size.

Devuan Debian
Tag Compressed On Disk Compressed On Disk Tag
jessie-2017-12-12 39.93 MiB 91.11 MiB 53MB 117.67 MiB jessie-20171210
ascii-2017-12-12 41.76 MiB 92.49 MiB 45MB 95.44 MiB stretch-20171210
beowulf-2017-12-12 N/A N/A 48MB 100.58 MiB buster-20171210
ceres-2017-12-12 44.52 MiB 97.07 MiB 48MB 100.77 MiB sid-20171210

For the slim variants the picture is as follows

Devuan Debian
Tag Compressed On Disk Compressed On Disk Tag
jessie-2017-12-12 20.35 MiB 52.01 MiB 30MB 75.45 MiB jessie-slim
ascii-2017-12-12 21.35 MiB 52.43 MiB 22MB 52.68 MiB stretch-slim
beowulf-2017-12-12 N/A N/A 25MB 58.93 MiB buster-slim
ceres-2017-12-12 23.74 MiB 58.12 MiB 25MB 58.96 MiB sid-slim

From the above, I think it's fair to say that the Debian jessie images are a bit of a hog and that stretch and later have cleaned up their act. More on that later. The Devuan images for jessie are a good deal smaller. For the other tags they are a bit smaller than their Debian cousins but not much. The download size differences may just be the difference between Mebibytes (2^20 bytes) and Megabytes (10^6 bytes). What's really different between these images? Well, let's find out!

Comparing Against Upstream

Jaromil's post contained a link to a Linux Weekly News article about Google's container-diff. That looked interesting and just right for the kind of information I was after, so I gave it a spin. Since I had already pulled all the relevant Docker images to my local cache I used

container-diff diff --type apt --type file \
    daemon://debian:jessie-20171210 \
    daemon://registry.gitlab.com/paddy-hack/devuan:jessie-2017-12-12

and similar to make comparisons based on packages installed and the files they contain. I didn't bother with --type history because all images just have a history of a single ADD instruction with a file that is guaranteed to be different (as a simple docker history --no-trunc $image will confirm).

The output shows what has been added with respect to the first image, what has been removed in the second image and lists what is different between the two. It does this for each --type given. The output is rather bulky so here is a sample limited to the Apt section.

-----Apt-----

Packages found only in debian:jessie-20171210:
NAME                       VERSION                      SIZE
-acl                       2.2.52-2                     258K
-adduser                   3.113 nmu3                   1M
-dmsetup                   2:1.02.90-2.2 deb8u1         123K
-inetutils-ping            2:1.9.2.39.3a460-3           307K
-iproute2                  3.16.0-2                     1.1M
-libcap2                   1:2.24-8                     61K
-libcap2-bin               1:2.24-8                     110K
-libcryptsetup4            2:1.6.6-5                    227K
-libdevmapper1.02.1        2:1.02.90-2.2 deb8u1         330K
-libgcrypt20               1.6.3-2 deb8u4               998K
-libgpg-error0             1.17-3                       444K
-libkmod2                  18-3                         134K
-libncursesw5              5.9 20140913-1+deb8u2        353K
-libprocps3                2:3.3.9-9                    132K
-libsystemd0               215-17 deb8u7                183K
-libudev1                  215-17 deb8u7                101K
-netbase                   5.3                          66K
-procps                    2:3.3.9-9                    670K
-systemd                   215-17 deb8u7                11.2M
-systemd-sysv              215-17 deb8u7                40K
-udev                      215-17 deb8u7                5.8M

Packages found only in registry.gitlab.com/paddy-hack/devuan:jessie-2017-12-12:
NAME                   VERSION                     SIZE
-devuan-keyring        2017.10.03                  64K
-libfdisk1             2.26.2-6 devuan1            458K
-sysvinit-core         2.88dsf-59.2 devuan2        238K

Version differences:
PACKAGE                IMAGE1 (debian:jessie-20171210)        IMAGE2 (registry.gitlab.com/paddy-hack/devuan:jessie-2017-12-12)
-base-files            8 deb8u10, 413K                        8 devuan7, 447K
-bsdutils              1:2.25.2-6, 181K                       1:2.26.2-6 devuan1, 240K
-init                  1.22, 29K                              1.24 devuan1.0, 30K
-initscripts           2.88dsf-59, 165K                       2.88dsf-59.2 devuan2, 260K
-libblkid1             2.25.2-6, 326K                         2.26.2-6 devuan1, 371K
-libmount1             2.25.2-6, 357K                         2.26.2-6 devuan1, 402K
-libsmartcols1         2.25.2-6, 209K                         2.26.2-6 devuan1, 246K
-libuuid1              2.25.2-6, 89K                          2.26.2-6 devuan1, 122K
-lsb-base              4.1 Debian13+nmu1, 72K                 4.1 devuan2, 72K
-mount                 2.25.2-6, 357K                         2.26.2-6 devuan1, 430K
-sysv-rc               2.88dsf-59, 125K                       2.88dsf-59.2 devuan2, 223K
-sysvinit-utils        2.88dsf-59, 147K                       2.88dsf-59.2 devuan2, 149K
-util-linux            2.25.2-6, 2.7M                         2.26.2-6 devuan1, 4.7M

You can reproduce the results yourself by replacing the daemon:// prefix by remote:// (and assuming the relevant versions are still available). The container-diff utility stores retrieved images below $HOME/.container-diff/cache/ so you may want to reclaim some disk space by running

rm $HOME/.container-diff/cache/*

once you're done.

jessiejessie

The details are available in jessie-jessie.txt, but comparing Debian's jessie with Devuan's jessie it is immediately clear that the main difference is the absence of systemd (and udev) in the latter. This is of course no big surprise as Devuan set out to remove systemd as the default init system. With the notable exception of iproute2 (which is added to the list of packages for debootstrap to install!) and some smaller network related packages, all of the other package removals are systemd and/or udev dependencies.

The fact that Devuan's jessie installs an init package doesn't really make a dent in the size difference but more on this later.

stretchascii

In this case the difference is a lot smaller. For one thing, systemd (and udev) have disappeared from Debian's stretch image. They don't show up anymore as a difference between the images. The image differences are mostly limited to iproute2 and libsystemd0 being installed only in stretch. The init and libeudev1 (bound to replace libudev1 in the long run) packages are only present in ascii. Nothing particularly interesting or it must be the absence of a libsystemd0 package in ascii.

The full output of the comparsion is in stretch-ascii.txt.

sidceres

This is mostly a repeat of the previous section, with the exception of libeudev1 not being in ceres. It looks like that package has not made it into ceres yet and was "shoe horned" into ascii.

The full output of the comparsion is in sid-ceres.txt.

Upstream Got Smart, So Should We

We should keep in mind that Docker containers have no real need for an init system. Containers are normally supposed to run a single process. For those containers where something "init system"-like is required, tini is or used to be a fairly popular choice. It has been integrated into Docker as of 1.13 and is now available via the --init option to docker run.

It appears that the Debian developers realized that as well and dropped it from their images starting with stretch. Seeing that the Devuan images still contain an init package, I guess there is some work to be done.

Sandwiching Docker Between Devuan

I had been using Docker on Debian for quite a while already when I chose init freedom and switched to Devuan. I use it to keep my development environments from getting in each others way and prevent my Free Software Explorations from making a mess of my carefully configured machine(s). So it was only natural (for me) that I wanted to run Docker on top of Devuan and then run Devuan containers on top of Docker. The result? A delicious Devuan-Docker-Devuan sandwich.

Docker On Devuan

There already was a docker package when some Debian maintainers started packaging Docker. Since docker was already taken, they had to pick something else and settled on docker.io. Naturally, the situation on Devuan is the same and looking through its package repositories I found two versions.

$ apt-cache policy docker.io
docker.io:
  Installed: (none)
  Candidate: 1.13.1~ds1-2
  Version table:
     1.13.1~ds1-2 0
        500 http://auto.mirror.devuan.org/merged/ ceres/main amd64 Packages
     1.6.2~dfsg1-1~bpo8+1 0
        100 http://auto.mirror.devuan.org/merged/ jessie-backports/main amd64 Packages

Docker, like so many exciting projects, changes rapidly and releases often. Seeing that upstream released 1.6.2 on 2015-05-14 was not exactly encouraging. Especially 1.12.0 (released 2016-07-28) has greatly improved networking support and embeds Swarm mode, both features I'd like to use.

So should I then run something from ceres (i.e. unstable)? That prospect didn't seem particularly inviting, so I went for the upstream Docker CE for Debian package instead. Notwithstanding the fact it targets Debian, I have found that it runs without problems on Devuan (keeping fingers crossed).

Note

I guess I should mention that I use docker-ce with the btrfs storage driver. This works with both the stock 3.16 Linux image from jessie as well as the 4.9 version from jessie-backports. Note, the 4.9 Linux image does not support the default AUFS storage driver. I've purged the recommended aufs-tools package because of that.

Devuan On Docker

We have Docker Engine up and running so now we can just

docker run --rm -it devuan /bin/bash

right? Not so fast Watson. First of all, as of writing, there is no such image. Lacking official images, I had a look at the unofficial ones. The search results included five images that were Devuan plus some application, one image for i386 and the remaining nine were either stale or so devoid of details that I didn't feel comfortable using them. Call me paranoid but if there's no way to check how an image is made short of running it I prefer to roll my own. So that's exactly what I did.

I had wanted to use one of the Devuan virtual machine images but after re-discovering that my Libreboot T400 does not do hardware virtualization without blobs and finding software emulation excruciatingly slow, I opted for applying the migration route to a Debian Docker image. The migrated image can then be used to create a real Devuan image using the native debootstrap.

Using debootstrap to build Docker base images is well-documented in the docker-ce package's contributed scripts (take a peek in /usr/share/docker-ce/contrib/) and the debuerreotype utility's implementation. The latter tool is used to create the official Debian Docker images using the Debian project's snapshot package archives. Because the Devuan infrastructure doesn't have something like that, I could not just use debuerreotype and change a few URLs. Besides, I also wanted to make sure that there wouldn't be any systemd-isms in my images.

After a couple of iterations, I now have a decent Devuan Docker base image for stable. It has all tweaks from the Docker CE contrib scripts and debuerreotype that I thought relevant applied. You can pull it with

docker pull registry.gitlab.com/paddy-hack/devuan

There is also a builder "flavour" and I am thinking of making a helper "flavour" as well so you can get started with d1h in a jiffy. In addition, having testing and unstable versions would be neat too.

The gory details can be found at my docker-devuan project over at GitLab.com. By all means check out the project's registry for what is available.

Reading Mail With Mu4e

After using Gnus for well over fifteen years, I switched to mu4e (a part of mu) in the summer of 2012. My main reason for giving it a try was that its Org Mode links were not connected with the mail folder a message was stored in. That means that mail can be moved around without breaking any links. The Org Mode links made by Gnus do not support this. Additionally, the mu4e search functionality also looked a lot easier to understand. I have never really gotten used to Gnus' search.

Basic configuration for mu4e is, well, basic. You just need to tell Emacs that you need it and then tell mu4e where you keep your mail. I keep all messages, be they mail or news, below ~/msg/ and have a few special purpose mail folders below that. Conveniently, mu4e has dedicated variables for these so they can be customized easily. Note that their values are relative to mu4e-maildir.

(require 'mu4e)
(setq mu4e-maildir "~/msg")
(setq mu4e-drafts-folder "/DRAFTS"
      mu4e-refile-folder "/~ARCHIVE"
      mu4e-sent-folder   "/~SENT"
      mu4e-trash-folder  "/~TRASH")

I have named these folders in such a way that they sort nicely with LC_COLLATE=C. My unfinished messages are kept in a DRAFTS folder that sorts at the top. Anything I deem important enough to keep for posterity is refiled to my ~ARCHIVE. Messages that I've dashed off or scheduled for removal end up in ~SENT and ~TRASH folders that sort towards the end. In addition, I have an INBOX that holds unsorted and/or unprocessed messages and split stuff from mailing lists in list.* folders.

This keeps messages that need attention most urgently near the top and the low(er) priority stuff near the bottom of the folder list. By the way, that list is available via M-x speedbar, and if you were to add a ~SPAM folder the sort order still works out quite nice.

For the headers view I prefer a date format like that used by Org Mode for inactive timestamps. I also prefer to see whom I sent mail to if the message was sent by me rather than one of my own mail addresses.

(setq mu4e-headers-date-format "[%Y-%m-%d %a %R]"
      mu4e-headers-fields '((:date . 23)
                           (:flags . 6)
                           (:from-or-to . 25)
                           (:mailing-list . 10)
                           (:subject)))

Note that the mailing-list field requires version 0.9.9.5 or later and may not be all that useful if you split mailing lists into their own folders.

When looking at a single message, the date should look similar to the format used for Org Mode timestamps. However, this time I like to see seconds and time zone information as well. That's achieved with

(setq mu4e-date-format-long "%Y-%m-%d %a %T%z")

Finally, to activate the functionality that made me switch to mu4e in the first place

(require 'org-mu4e)

Putting Holidays On The Calendar

So we have just tweaked my Emacs calendar a bit for better integration with Org Mode, but to make planning and scheduling easier, I want the local, Japanese holidays as well as the corporate ones to be shown and stand out. Both in the calendar as well as agenda views.

Japanese Holidays

Good thing that Takashi Hattori wrote japanese-holidays.el a while ago. That saves us the trouble. This little Emacs add-on is now kept up to date at GitHub. It doesn't change often but I keep up with this repository via mr with a stanza in my vcsh pim module and just add the checked out location to my load-path.

(add-to-list 'load-path "~/.emacs.d/lib/japanese-holidays" t)

Unlike the recommendations in the upstream README, I prefer to initialize the local holidays with the Japanese ones because, well, they are local for me.

(require 'japanese-holidays)
(setq holiday-local-holidays japanese-holidays)

Corporate And Other Holidays

I use the holiday-other-holidays variable to define company and other holidays in a separate file. These are mostly related to the Obon (お盆) and Year's End and New Year's (年末年始) periods but you could also add your days off there is so inclined.

(load "~/.emacs.d/other-holidays.el" t t t t)

While the recurring holidays can just use a holiday-fixed entry, one-off holidays should be added to holiday-filter-visible-calendar. This gives something like:

(setq holiday-other-holidays
      '((holiday-fixed 12 31 "大晦日")
        (holiday-filter-visible-calendar
         '(((12 30 2016) "会社休日")
           (( 1  3 2017) "会社休日")))))

Putting Things Together

Now we are ready to define the total list of calendar-holidays. At the same time, we tweak a few look-and-feel variables. These result in Sundays getting marked as holidays and Saturdays as something that can be thought of as a semi-holiday. Not everyone lives with the luxury of a five day workweek.

(setq calendar-holidays
      (append holiday-local-holidays holiday-other-holidays))

(setq mark-holidays-in-calendar t
      japanese-holiday-weekend '(0 6)
      japanese-holiday-weekend-marker
      '(holiday nil nil nil nil nil japanese-holiday-saturday))

(add-hook 'calendar-today-visible-hook 'calendar-mark-today)
(add-hook 'calendar-today-visible-hook 'japanese-holiday-mark-weekend)
(add-hook 'calendar-today-invisible-hook 'japanese-holiday-mark-weekend)

Finally, to make Org Mode aware of all the holidays as well, just put a headline with content like below in one of your agenda files.

* Holidays
  :PROPERTIES:
  :CATEGORY: holiday
  :END:
  #%%(org-calendar-holiday)

Emacs Calendar And Org Mode

Org Mode integrates quite nicely with Emacs' calendar but the two have rather different views on how to format dates. Let's adjust the calendar to follow the Org Mode conventions: ISO:8601 dates with a three letter abbreviated day name. Note that ISO:8601 uses Monday as the first day of the week and does not abbreviate the year (remember Y2K?).

(require 'calendar)

(setq calendar-week-start-day    1      ; 0 == Sunday
      calendar-date-style       'iso
      abbreviated-calendar-year 'nil)

(setq calendar-date-display-form
      '((format "%s-%.2d-%.2d"
                year (string-to-number month) (string-to-number day))
        (if dayname (format " %s" (substring dayname 0 3)))))

The same ISO standard stipulates a 24-hour clock and allows several ways of formatting the time. The format used by Org Mode corresponds to one of these, so we're good. All we have to do is make the Emacs' calendar use the same formatting for display.

(setq calendar-time-display-form
      '(24-hours ":" minutes            ; both always two digits
                 (if time-zone (format "+%s" time-zone))))

As a final touch, we add some key bindings that let us move around the calendar using the same key bindings as Org Mode uses to change dates at its date/time prompt.

(define-key calendar-mode-map [M-S-left]  'calendar-backward-month)
(define-key calendar-mode-map [M-S-right] 'calendar-forward-month)
(define-key calendar-mode-map [M-S-up]    'calendar-backward-year)
(define-key calendar-mode-map [M-S-down]  'calendar-forward-year)

A use-package based variant of the above can be found in my dotter/pim module.

Cross-Editor Configuration

Someone strongly recommended using a certain IDE for a project at the office. I'm not a big fan of IDEs because they tend to impose a text editor on you or integrate your preferred one sub-optimally. Either way, your text editing experience suffers. Looking into how good or bad the integration might be for my preferred editor (it's still a bit of an uphill battle for that recommended IDE) I came across something tangentially related: EditorConfig.

EditorConfig let's you define a number of preferences with respect to text files independent of the text editor you use. If you're the only one working with those text files that may not be a big deal, but the picture changes when you are collaborating on those files with others. Having to change your preferred text editor for something else just to stick to the common text file conventions for a project is a drag.

Simply adding a suitable .editorconfig file to all your projects' top-level directories could work wonders, assuming your preferred text editor is supported by EditorConfig. A large number have out of the box support and more yet only require a small(?) plugin.

I enthusiastically set up an .editorconfig file in my $HOME directory and integrated that with my preferred text editor. Worked like a charm. So much so that I quickly found out that I also had to add .editorconfig files to just about every project to keep it from changing all the files I touched all over the place. Not all people take kindly to "spurious" or unrelated changes in VCS commits.

Oh well, I guess I'm going to have to introduce EditorConfig in a few places and see if we can settle on a convention for the project-wide settings. Some places seem to have none whatsoever.

BTW, in case you're curious, my personal preferences can be found in my dotter/emacs repository. Emacs and I go back for more than a quarter century, so I am quite hard pressed to change habits.

Decorating Your $HOME

After my Alpine Linux Encrypted Disk Installation, I wanted to import quickly the account customizations I maintain via dotter. Problem was, I had no recollection of how to do so. After I bit of tinkering I got to where I wanted to be but I figured I'd write it down for next time. Here's goes.

Since all the repositories are maintained in my-repos, that is the first thing you need to set up.

vcsh init my-repos
vcsh my-repos remote add origin https://gitlab.com/dotter/my-repos.git
vcsh my-repos pull origin master

Use the git@gitlab.com:dotter/my-repos.git SSH form of the remote if you plan on committing things. No need to fret, it can be changed easily later if need be with

vcsh my-repos remote set-url origin git@gitlab.com/dotter/my-repos.git

Next, you will need to make the desired modules available. Assuming all are desired, that looks like

cd ${XDG_CONFIG_HOME:-$HOME/.config}/my-repos
mkdir enabled.d
for d in available.d/*; do ln -s ../$d enabled.d/; done

Feel free to modify things below enabled.d/ after this and before you update all enabled modules with

cd
mr up

There you have it. Your account has been seeded with dotter customizations.

Caveat

If some of the files provided by the dotter customizations already exist in your $HOME, you will get failures when running mr up. Move the existing files out of the way, rerun mr up and reconcile the differences.

Alpine Linux Encrypted Disk Installation

Pursuing ever smaller size, many Docker images have changed their base image from Ubuntu to Debian GNU/Linux to Alpine Linux. I have built a few Alpine Linux based images here and there of my own and, upon finding out that it sports a desktop, figured I'd give it a try on an aging laptop.

I had one complicating requirement though. The install had to be on an encrypted disk. The whole installation process turned out to be fairly straightforward, despite that encryption requirement. It was just a bit of command-line work. So, for those not afraid of a CLI, read on!

Standard Hard-Disk Installation

Forgetting about the encryption requirement for a bit, installing Alpine Linux onto your hard disk can be as simple as

setup-alpine -m sys

after you boot from an ISO CD-image or USB stick. I used an USB stick to boot the alpine-3.4.4-x86_64.iso image. You can login as root without a password at the prompt.

The questions you will be asked by the program aren't too complicated if you've installed a Linux distribution before. What is of interest though, is the step-by-step breakdown of what this little program does. Looking through that information, it looks like running it in quick mode, with the -q option, does most of what needs to be done anyway but skips the setup of the disk. That's just what we need!

Encrypted Hard-Disk Installation

Okay then, let's setup-alpine, quickly!

setup-alpine -q
jp                   # keyboard
xeno                 # hostname
eth0                 # network interface
dhcp                 # dynamically configured IP address
done                 # skip the wlan interface for now
no                   # manual network configuration
                     # hit <Enter> and leave the root password empty
                     # confirm empty root password
none                 # proxy
                     # apk mirror, take your pick

I used an empty password for the root user as I intend to disable that account later. Feel free to use a password of your liking if you want to keep the account enabled.

Since quick mode skips this

setup-timezone -z Asia/Tokyo

Adjust that value to your situation of course. If you are not sure about the timezone you're in, just run the command without the -z option and its argument and select ? to get a list.

Now that we've got the basics out of the way, it's time to configure the disk. Most of what follows below is based on the LVM on LUKS information. There are scripts that claim to automate this a fair bit but I did things by hand.

Randomizing The Disk

Before setting things up for an encrypted hard-disk installation, it is a good idea to randomize the content of the target disk. This makes it harder for anyone without knowledge of the passphrase used for the encryption later on to access your data.

Warning

In case this isn't obvious, the following commands will completely and totally destroy whatever information is currently stored on the target disk, /dev/sda below.

My laptop has a 160GB hard disk so that requires a lot of random data. The /dev/urandom device will happily provide that but it can be a bit slow for lack of "entropy". You can speed up things with the help of haveged.

apk add haveged
haveged -n 0 | dd bs=64k of=/dev/sda

The bs=64k option to dd aims to improve writing performance a bit. Replace /dev/sda with whatever device you are targetting.

The randomization of my 160GB hard disk took about 45 minutes. I don't know how good the randomization is, but it's definetely better than all zero bytes (which you can get by piping the output of a cat /dev/zero invocation into the dd command if so inclined).

Most of us have no need for haveged installed on our hard disk. Run

apk del haveged

to remove it. All packages that you apk add before you do the installation to disk will end up on your disk.

Partitioning The Disk

With the disk content randomized, we are ready to partition. Ideally, you'd set up a single LVM partition and install there, but boot loaders typically work better when the /boot directory is not encrypted. So, we want to break out a smallish, unencrypted, bootable partition for /boot and stuff the rest in a large LVM partition.

Replace /dev/sda with your target device.

fdisk /dev/sda
n
p
1
             # accept the default by hitting <Enter>
+100M
a
1            # makes /dev/sda1 bootable
n
p
2
             # use the default → <Enter>
             # <Enter>
t
2
8e           # makes /dev/sda2 a Linux LVM partition
w

Running fdisk -l /dev/sda after this produces something like this

   Device Boot      Start         End      Blocks  Id System
/dev/sda1   *           1          13      104391  83 Linux
/dev/sda2              14       19457   156183930  8e Linux LVM

The exact Start, End and Blocks values will probably differ in your case but the important parts are in the Device, Boot, Id and System columns.

Linux Unified Key Setup With Cryptsetup

With the disk partitioned, we are ready to set up /dev/sda2 for use with encryption. Picking a set of suitable parameters to use in the cryptsetup command is not too difficult. You should at least run a cryptsetup benchmark to find a cipher that is fast at both encryption and decryption. At the same time, you should consider whether a longer key is worth a slight performance decrease. Longer keys usually mean the encryption is stronger. For more information, consult the cryptsetup manual page and dmcrypt wiki page.

Here's what I used to install all the packages needed (some we will use in later stages) and ran cryptsetup on /dev/sda2 to make it work with LUKS. The first cryptsetup command will ask if you are sure if you want to do this and, if so, to enter and confirm a passphrase. You will need this passphrase every time you boot the system so memorize it. The second cryptsetup command will ask you for the passphrase and make the /dev/sda2 partition available as /dev/mapper/lvmcrypt.

apk add cryptsetup lvm2 e2fsprogs syslinux
cryptsetup -v -c serpent-xts-plain64 -s 512 --hash whirlpool \
    --iter-time 5000 --use-random luksFormat /dev/sda2
cryptsetup open --type luks /dev/sda2 lvmcrypt

Setting Up Logical Volumes And File Systems

At this point we have unencrypted access to a /dev/mapper/lvmcrypt device. Everything written to this device, is encrypted before it ends up on disk in the /dev/sda2 partition. Everything read from that device is fetched from the partition and decrypted before you get to see it.

First we set up LVM. We create a phyiscal volume, a vg0 volume group and 5G logical volume by the name of root. In the last step we activate the volume group.

pvcreate /dev/mapper/lvmcrypt
vgcreate vg0 /dev/mapper/lvmcrypt
lvcreate -L 5G vg0 -n root
vgchange -a y

Next, we make and mount file systems. The /dev/sda1 partition doesn't need journalling, so ext2 will do. The root logical volume gets an ext4 file system. For the purpose of installation, we mount it on /mnt and create a boot directory there to mount the file system we made on /dev/sda1.

mkfs.ext2 /dev/sda1
mkfs.ext4 /dev/mapper/vg0-root
mount -t ext4 /dev/mapper/vg0-root /mnt
mkdir /mnt/boot
mount -t ext2 /dev/sda1 /mnt/boot

If desired, you can make additional logical volumes and mount them in this step. You can also add swap and activate it here. I have opted to do these things after installation.

Installing Alpine Linux

Ready for an anti-climax? It's as simple as

setup-disk -m sys /mnt

Yup. That's all that there is to installing. But don't reboot just yet. There are a few things that need taking care of first.

Before You Reboot

First of all, let's take care of that You might need fix the MBR to be able to boot message at the end of the installation. Update the master boot record (MBR) with

dd bs=$(stat -c %c /mnt/usr/share/syslinux/mbr.bin) \
   count=1 conv=notrunc \
   if=/mnt/usr/share/syslinux/mbr.bin \
   of=/dev/sda

Next, you have to make sure your encrypted partition is found and set up during the boot. That is done via a /etc/crypttab file (see the crypttab manual page for details). This file doesn't exist yet in our freshly installed system below /mnt, so

echo "lvmcrypt /dev/sda2 none luks" > /mnt/etc/crypttab

will create one that causes the boot to prompt you for the encrypted partition's passphrase.

You also have to ensure that the cryptsetup module is included in the initramfs image. Add the module and regenerate the image with

sed -i '/^features=/s/base /base cryptsetup /' \
    /mnt/etc/mkinitfs/mkinitfs.conf
mkinitfs -c /mnt/etc/mkinitfs/mkinitfs.conf -b /mnt

In order for the kernel to use /dev/sda2 as your encrypted root, instruct the extlinux bootloader to pass a few extra arguments to the kernel at boot time.

sed -i '/^default_kernel_opts=/{
    s/"$/ cryptroot=\/dev\/sda2 cryptdm=lvmcrypt"/
    }' /mnt/etc/update-extlinux.conf
extlinux --install /mnt/boot --update
sed -i '/APPEND/s|$| cryptroot=/dev/sda2 cryptdm=lvmcrypt|' \
    /mnt/boot/extlinux.conf

I needed that last sed command because for some reason the extra kernel options weren't added by extlinux and caused my reboot to fail.

Finally, unmount file systems, deactivate the volume group and close the lvmcrypt before you reboot. This only needs to be done by hand here. During later boots all this will be done automatically.

umount /mnt/boot
umount /mnt
vgchange -a n
cryptsetup close --type luks lvmcrypt
reboot

Troubleshooting

I had a bit of trouble rebooting until I found out that the extra kernel options had been added in /boot/extlinux.conf. When the boot fails, you will be dropped in an initramfs emergency recovery shell. That looks like

mount: mounting UUID=53f6da24-[...] on /sysroot failed: No such file or directory
Mounting root failed.
initramfs emergency recovery shell launched. Type 'exit' to continue boot
sh: can't access tty; job control turned off
/ #

where I shortened the UUID. Yours is different anyway.

At this point you can continue the boot with

cryptsetup open --type luks /dev/sda2 lvmcrypt
lvm vgchange -a y
mount /dev/mapper/vg0-root /sysroot
exit

You will be prompted for the passphrase by cryptsetup.

Login as root at the prompt and check the following files for any errors:

  • /etc/crypttab
  • /etc/mkinitfs/mkinitfs.conf
  • /etc/update-extlinux.conf
  • /boot/extlinux.conf

Fix them and rerun mkinitfs and extlinux if necessary, like so:

mkinitfs
extlinux --install /boot --update