Replacing the KDE Plasma widget ‘Thermal Monitor’ with ‘Kargos’ in Gentoo Linux

The KDE Plasma widget Thermal Monitor has not been working correctly in my Gentoo Linux installations for quite some time. I notice Thermal Monitor’s repository has not been updated for a couple of years, despite several new versions of KDE Plasma having been released. Perhaps that is the reason.

On my laptop running the Stable Branch of Gentoo Linux, Thermal Monitor displays the GPU and HDD temperatures automatically but CPU temperatures were only displayed if I right-clicked on the widget and selected ‘Reload Temperature Sources’. I managed to get the widget to display the CPU temperatures automatically by editing the file ~/.local/share/plasma/plasmoids/org.kde.thermalMonitor/contents/ui/main.qml and commenting out a line as shown in the file excerpt below:

[...]
        onSourceAdded: {

            if (source.indexOf(lmSensorsStart) === 0 || source.indexOf(acpiStart) === 0) {
/*
 *                systemmonitorAvailableSources.push(source)
 */
                var staIndex = systemmonitorSourcesToAdd.indexOf(source)
                if (staIndex > -1) {
                    addToSourcesOfDatasource(systemmonitorDS, source)
                    systemmonitorSourcesToAdd.splice(staIndex, 1)
                }

            }

        }
[...]

The above modification is suggested in a comment to Issue #53 in the widget’s repository.

However, the above-mentioned edit does not fix Thermal Monitor on my laptop running the Testing Branch of Gentoo Linux, and Thermal Monitor no longer displays the GPU temperature either. Actually, the CPU’s four core temperatures and the GPU temperature are no longer listed in the Thermal Monitor configuration window, only a single CPU temperature. Not surprisingly, none of the suggested changes to the file ~/.local/share/plasma/plasmoids/org.kde.thermalMonitor/contents/ui/main.qml that I found in Web searches made a difference. However, while researching the problem I came across a Manjaro Forums post by user bogdancovaciu about the Kargos Plasma widget, a KDE Plasma port of GNOME Argos and OSX BitBar. Kargos enables you to create a Plasma widget that runs your own script, which can be written in any language, providing its output adheres to a specified format. I also found a repository named k-argos-plugins containing further example scripts for Kargos. As none of the solutions suggested for Thermal Monitor in that Manjaro thread worked for me, I decided to try the Kargos widget instead. It works a treat.

kargos widget on KDE Plasma Panel

kargos widget on KDE Plasma Panel of my Compal NBLB2 laptop

Below I explain what I did to install and configure the Kargos widget on my KDE Panel in Gentoo Linux (see screenshot). The packages lm-sensors and hddtemp were already installed in my case, but if they had not been, I would have needed to install and configure them, so I have included those steps below.

1. Install and configure lm-sensors

root # emerge lm-sensors
root # rc-update add lm_sensors default
root # sensors-detect

In my case sensors-detect created the file /etc/modules-load.d/lm_sensors.conf containing only the following:

# Generated by sensors-detect on Sun Oct 27 03:07:08 2019
coretemp

2. Start lm-sensors now, rather than rebooting

root # /etc/init.d/lm_sensors start

3. I wanted to use the nc command in my shell script for Kargos, so I installed its package

root # emerge netcat

4. Install and configure hddtemp

root # emerge hddtemp
root # rc-update add hddtemp default

Specify in the config file /etc/conf.d/hddtemp which drives to check:

# Copyright 1999-2012 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2

# the hddtemp executable
HDDTEMP_EXEC=/usr/sbin/hddtemp

# various options to pass to the daemon
HDDTEMP_OPTS="--listen=127.0.0.1"

# a list of drives to check
HDDTEMP_DRIVES="/dev/sda"

5. Start hddtemp now, rather than rebooting

root # /etc/init.d/hddtemp start

6. Install Kargos

On the KDE Plasma Desktop, click on the ‘Desktop’ menu icon (the three horizontal lines in the top right corner of the Desktop) and select: ‘Unlock Widgets’ > ‘Add Widgets…’ > ‘Get New Widgets…’ > ‘Download New Plasma Widgets’. Search for, and install, ‘kargos’ widget.

7. Create the Bash script ~/temperatures.3s.sh containing the following:

#!/bin/bash
temp=$(sensors | grep -oP 'Core.*?\+\K[0-9.]+')
temp0=$(sensors | grep 'Core 0' | cut -c '16-17')
temp1=$(sensors | grep 'Core 1' | cut -c '16-17')
temp2=$(sensors | grep 'Core 2' | cut -c '16-17')
temp3=$(sensors | grep 'Core 3' | cut -c '16-17')
hdd_temp=$(nc localhost 7634 | cut -c '33-34')
gpu_temp=$(sensors | grep -A 2 'radeon' | grep 'temp1' | cut -c '16-17')
echo "<br><font size='1'>CPU1&nbsp;&nbsp;CPU2&nbsp;&nbsp;CPU3&nbsp;&nbsp;CPU4&nbsp;&nbsp;GPU&nbsp;&nbsp;HDD</font><br>${temp0%%.*}°&nbsp;&nbsp;${temp1%%.*}°&nbsp;&nbsp;${temp2%%.*}°&nbsp;&nbsp;${temp3%%.*}°&nbsp;${gpu_temp}°&nbsp;${hdd_temp}°| font=Hack-Regular size=10"
# Uncomment the lines below if you want to be able to click on the kargos widget and display a pop-up TOP
#echo "---"
#TOP_OUTPUT=$(top -b -n 1 | head -n 20 | awk 1 ORS="\\\\n")
#echo "$TOP_OUTPUT | font=monospace iconName=htop"

The script above is specifically for the temperature sensors in my Clevo NBLB2 laptop. To find out which temperatures are available, and which characters to extract, use the following command:

root # sensors

Don’t forget to make the script executable:

user $ chmod +x ~/temperatures.3s.sh

Note that the ‘.3s‘ in the script name is optional but, if included, will override the kargos configuration (see further on) and run the script every 3 seconds. I could have specified another frequency, such as ‘.5s‘ or whatever.

8. Add the kargos widget to the KDE Panel.

9. Right-click on the kargos widget on the KDE Panel and select ‘Configure kargos…’.

10. Configure the kargos widget

In the first box in the configuration window, enter the full path of the script:

/home/fitzcarraldo/temperatures.3s.sh

In the second box leave ‘Interval in seconds’ as ‘1‘. This is overridden anyway if the script filename includes the interval.

In the third box leave ‘Rotation delay in seconds’ as ‘6‘.

On the KDE Plasma Desktop, click on the Desktop menu icon (three horizontal lines) and select: ‘Lock Widgets’.

11. Depending on the font configuration for the KDE Desktop, it may be necessary to edit the Bash script ~/temperatures.3s.sh to change the font name or size, the number of non-breaking spaces between the names displayed on the top line, and the number of non-breaking spaces between the temperature values displayed on the bottom line.

How to enable a Windows application in WINE to access a Samba share on a NAS (continued)

In a 2016 post ‘How to enable a Windows application in WINE to access a Samba share on a NAS‘ I explained how to mount in Linux a networked SMB shared folder so that a Windows application running via WINE could access the folder as Drive Y: in order to open and save files in it. In that blog post I also listed a couple of Bash scripts to facilitate the mounting and unmounting of the SMB share for the WINEPREFIX used for the Windows application (~/.wine-pdfxve6 in the example I gave for PDF-XChange Editor, Version 6). However, as I have several Windows applications running via WINE on my machines, and I have used a different WINEPREFIX for each of them, I wanted to be able to mount the SMB share for whichever of those applications I happen to be using at the time. Therefore I modified the original Bash scripts as shown below. The Desktop Configuration files (.desktop files) to launch the scripts are essentially the same as in my earlier blog post; I have just removed the references to the specific Windows application. The four modified files are listed below. Obviously change the username, SMB share name and SMB server name to suit your own situation.

1. Bash script ~/mount_bsfnas1_brianfolder_share.sh

#!/bin/bash
mount_share () {
    echo
    echo "Enter your Linux account password below..."
    echo
    sudo ln -s /media/bsfnas1/brianfolder ~/$PREFIX/dosdevices/y:
    sudo mount.cifs //bsfnas1/brianfolder/ -o user=brianfolder,pass=enricocaruso,uid=$(id -u),gid=$(id -g) ~/$PREFIX/dosdevices/y:
}
echo
echo "This will mount the Samba share folder brianfolder on the bsfnas1 machine."
echo
echo
echo "== Select which WINEPREFIX you wish to use =="
echo
ls ~/.wine-* | grep .wine | awk -F'/' '{print NR " " substr($4, 1, length($4)-1)}'
NUMPREFIXES=$(ls ~/.wine-* | grep .wine | wc -l)
echo
read -p "Enter number (q to abort) and press ENTER: " CHOICE
if [ "$CHOICE" != "q" ] && [ "$CHOICE" -gt 0 ] && [ "$CHOICE" -le $NUMPREFIXES ]; then
    PREFIX=$(ls ~/.wine-* | grep .wine | awk -F'/' '{print NR " " substr($4, 1, length($4)-1)}' | grep "$CHOICE " | awk -F' ' '{print $2}')
    echo
    if [ ! -e ~/$PREFIX/dosdevices/y: ]; then
        mount_share
    else
        echo -n "~/$PREFIX/dosdevices/y: already exists. Is it OK to proceed anyway (y/n)? "
        read ANSWER
        if [ $ANSWER = "y" ]; then
            rm ~/$PREFIX/dosdevices/y:
            mount_share
        fi
    fi
    echo
fi
if grep -q "/media/bsfnas1/brianfolder" /proc/mounts; then
    echo "Samba share //bsfnas1/brianfolder is mounted for WINEPREFIX ~/$PREFIX ."
else
    echo "Samba share //bsfnas1/brianfolder is not mounted."
fi
echo
echo "You may now close this window."
read ANSWER
exit

2. Bash script ~/umount_bsfnas1_brianfolder_share.sh

#!/bin/bash
echo
echo "This will unmount the Samba share folder brianfolder on the bsfnas1 machine."
echo
echo "Enter your Linux account password below..."
echo
sudo umount ~/.wine-*/dosdevices/y: 2>/dev/null
echo
if grep -q "/media/bsfnas1/brianfolder" /proc/mounts; then
  echo "Samba share //bsfnas1/brianfolder is mounted."
else
  echo "Samba share //bsfnas1/brianfolder is not mounted."
fi
echo
echo "You may now close this window."
exit

3. Desktop Configuration file ~/Desktop/mount_bsfnas1_brianfolder_share.desktop

[Desktop Entry]
Comment[en_GB]=Mount bsfnas1 brianfolder share for current WINEPREFIX
Comment=Mount bsfnas1 brianfolder share for current WINEPREFIX
Exec=sh /home/fitzcarraldo/mount_bsfnas1_brianfolder_share.sh
GenericName[en_GB]=Mount bsfnas1 brianfolder share for current WINEPREFIX
GenericName=Mount bsfnas1 brianfolder share for current WINEPREFIX
Icon=media-mount
MimeType=
Name[en_GB]=mount_bsfnas1_brianfolder_share
Name=mount_bsfnas1_brianfolder_share
Path=
StartupNotify=true
Terminal=true
TerminalOptions=\s--noclose
Type=Application
X-DBUS-ServiceName=
X-DBUS-StartupType=none
X-KDE-SubstituteUID=false
X-KDE-Username=fitzcarraldo

4. Desktop Configuration file ~/Desktop/umount_bsfnas1_brianfolder_share.desktop

[Desktop Entry]
Comment[en_GB]=Unmount bsfnas1 brianfolder share for current WINEPREFIX
Comment=Unmount bsfnas1 brianfolder share for current WINEPREFIX
Exec=sh /home/fitzcarraldo/umount_bsfnas1_brianfolder_share.sh
GenericName[en_GB]=Unmount bsfnas1 brianfolder share for current WINEPREFIX
GenericName=Unmount bsfnas1 brianfolder share for current WINEPREFIX
Icon=media-eject
MimeType=
Name[en_GB]=umount_bsfnas1_brianfolder_share
Name=umount_bsfnas1_brianfolder_share
Path=
StartupNotify=true
Terminal=true
TerminalOptions=\s--noclose
Type=Application
X-DBUS-ServiceName=
X-DBUS-StartupType=none
X-KDE-SubstituteUID=false
X-KDE-Username=fitzcarraldo

Now when I double-click on the icon to mount the SMB share for a Windows application running via WINE, a terminal window pops up displaying the WINEPREFIXs currently installed on my machine:


This will mount the Samba share folder brianfolder on the bsfnas1 machine.


== Select which WINEPREFIX you wish to use ==

1 .wine-3dimviewer
2 .wine-myphoneexplorer
3 .wine-nbtscan
4 .wine-pdfxve6
5 .wine-PortableApps
6 .wine-radiant
7 .wine-symmetry
8 .wine-visio
9 .wine-xnviewmp

Enter number (q to abort) and press ENTER: 

Let’s say I want to use the Windows application XnViewMP. I would enter ‘9’ and press ‘Enter’. The rest of the interaction should be obvious:


This will mount the Samba share folder brianfolder on the bsfnas1 machine.


== Select which WINEPREFIX you wish to use ==

1 .wine-3dimviewer
2 .wine-myphoneexplorer
3 .wine-nbtscan
4 .wine-pdfxve6
5 .wine-PortableApps
6 .wine-radiant
7 .wine-symmetry
8 .wine-visio
9 .wine-xnviewmp

Enter number (q to abort) and press ENTER: 9

~/.wine-xnviewmp/dosdevices/y: already exists. Is it OK to proceed anyway (y/n)? y

Enter your Linux account password below...

[sudo] password for fitzcarraldo: 

Samba share //bsfnas1/brianfolder is mounted for WINEPREFIX ~/.wine-xnviewmp .

You may now close this window.

Henceforth the Windows application XnViewMP will be able to access the Y: drive which is actually the SMB share //bsfnas1/brianfolder.

Once I have finished using the application, I just double-click on the the icon to unmount the SMB share, and a terminal window pops up displaying the following:


This will unmount the Samba share folder brianfolder on the bsfnas1 machine.

Enter your Linux account password below...

[sudo] password for fitzcarraldo: 

Samba share //bsfnas1/brianfolder is not mounted.

You may now close this window.

Once I have entered my Linux password for the local machine, the script will unmount the SMB share and the terminal window will close automatically if you have configured the Desktop Configuration file by right-clicking on the icon and unticking ‘Do not close when command exits’ in KDE, ‘Keep terminal window open after command execution’ in LXDE, or similar in other desktop environments.

Note: If you use Microsoft Office via WINE, you also might be interested in a comment on my earlier blog post about a Microsoft Office problem in saving files to a remote SMB share.

Creating a RAID of USB pendrives in Linux

USB hub and USB pendrives used as RAID10 with my laptop

USB hub and pendrives used as RAID10 with my laptop.

If you’re not familiar with the RAID (Redundant Array of Inexpensive Disks) concept and the different types of array, the article ‘RAID 0, RAID 1, RAID 5, RAID 10 Explained with Diagrams‘ gives a quick summary (and links to another article ‘RAID 2, RAID 3, RAID 4, RAID 6 Explained with Diagram‘). Another helpful article is ‘RAID Levels Explained‘.

A few years ago I came across a YouTube video by a Mac user, titled ‘Use a bunch of USB Flash drives in a RAID array‘. Purely out of interest he had experimented with creating RAIDs using USB pendrives (also known as ‘USB flash drives’ or ‘USB memory sticks’). The creation of a RAID using USB pendrives for his Apple Macs was very easy, and, since then, I had wanted to try this using one of my laptops running Linux, just to satisfy my curiosity. I have previously created software RAIDs in a Linux server using internal 3.5-inch HDDs, for the root, home and swap partitions, and for file storage partitions for a Cloud server and NAS. However, I had never created a RAID using external USB drives. This week I happened to have a spare four-port USB 3.0 hub and four old 4GB USB 2.0 pendrives, so I finally got the chance to create a RAID with USB pendrives (see photo). I decided to use my main laptop, which has Gentoo Linux with OpenRC, elogind, eudev and KDE installed. That installation does not have an initramfs so I did not need to rebuild an initramfs to assemble the RAID. Anyway, early assembly of a RAID by an initramfs would only be needed if the RAID were being used to hold the directories required by the OS (the root partition, for example). As my RAID would be pluggable external storage, I wanted to mount it manually rather than adding it to /etc/fstab to be mounted automatically at boot. As I had not used a RAID on this laptop before, I had not enabled the RAID drivers in the kernel configuration, so I needed to do that and rebuild the kernel. I opted to make the RAID drivers kernel modules rather than built into the kernel, so that I could load only the relevant module for whichever type of RAID I wished to create.

I had to decide which filesystem to use in the RAID. I have always used ext4 in my RAIDs using HDDs. However, F2FS is an interesting filesystem developed by Samsung for devices using flash memory, such as SD cards, USB pendrives and SSDs. So I decided to format the pendrives to use F2FS, and create an F2FS RAID. As I had not used F2FS previously on this laptop, I had not enabled the F2FS driver in the kernel configuration, so I enabled the F2FS driver in the kernel at the same time as I enabled the RAID drivers. As with the RAID drivers, I opted to make the F2FS driver a kernel module rather than built into the kernel, so that I could load it and unload it whenever I wanted.

Not only did it turn out to be easy to create a RAID using USB pendrives, I found that the Linux RAID module gets loaded automatically when I connect the USB hub. Furthermore the RAID is recognised by KDE and listed under ‘Places’ in the Dolphin file manager’s windows, which I can click on to mount and unmount the RAID. So I did not even need to configure the OS to load the RAID module at boot (the OS does not load the module automatically at boot if the hub is not connected).

DigitalOcean produced a good tutorial on creating RAIDs in Ubuntu: ‘How To Create RAID Arrays with mdadm on Ubuntu 16.04‘. The procedure is essentially the same in Gentoo Linux, the only differences being the path of the mdadm.conf file and the method of updating an initramfs (which I did not need to do anyway in this particular installation).

As I had four spare USB pendrives and a four-port hub, I decided to create a RAID10 array. Below is a summary of the steps I took.

1. I rebuilt the kernel in order to build the RAID and F2FS modules. The relevant kernel configuration parameters I set are shown below:

root # grep RAID /usr/src/linux/.config | grep -v "#"
CONFIG_MD_RAID0=m
CONFIG_MD_RAID1=m
CONFIG_MD_RAID10=m
CONFIG_MD_RAID456=m
CONFIG_ASYNC_RAID6_RECOV=m
CONFIG_RAID6_PQ=m
root # grep F2FS /usr/src/linux/.config | grep -v "#"
CONFIG_F2FS_FS=m
CONFIG_F2FS_STAT_FS=y
CONFIG_F2FS_FS_XATTR=y
CONFIG_F2FS_FS_POSIX_ACL=y
root # uname -a
Linux clevow230ss 4.19.72-gentoo #2 SMP Tue Oct 15 01:36:57 BST 2019 x86_64 Intel(R) Core(TM) i7-4810MQ CPU @ 2.80GHz GenuineIntel GNU/Linux

2. I installed the mdadm tool:

root # eix -I mdadm
[I] sys-fs/mdadm
     Available versions:  4.1^t {static}
     Installed versions:  4.1^t(01:52:17 15/10/19)(-static)
     Homepage:            https://git.kernel.org/pub/scm/utils/mdadm/mdadm.git/
     Description:         Tool for running RAID systems - replacement for the raidtools

3. I installed the F2FS tools:

root # eix -I f2fs
[I] sys-fs/f2fs-tools
     Available versions:  1.10.0(0/4) 1.11.0-r1(0/5) 1.12.0-r1(0/6) ~1.13.0(0/6) {selinux}
     Installed versions:  1.12.0-r1(0/6)(02:05:17 15/10/19)(-selinux)
     Homepage:            https://git.kernel.org/cgit/linux/kernel/git/jaegeuk/f2fs-tools.git/about/
     Description:         Tools for Flash-Friendly File System (F2FS)

4. I rebooted the laptop.

5. The f2fs module was not loaded automatically, therefore I loaded it manually and edited /etc/conf.d/modules to add the module name so that it would be loaded automatically in future:

root # modprobe f2fs
root # lsmod | grep f2fs
f2fs                  466944  0
root # nano /etc/conf.d/modules
root # grep ^modules /etc/conf.d/modules
modules="fuse bnep rfcomm hidp uvcvideo cifs mmc_block snd-seq-midi iptable_raw xt_CT uinput f2fs"

6. I plugged the four USB pendrives into the USB hub, and connected the hub to the laptop.

7. I launched GParted, deleted the existing partition on each pendrive (three had been formatted as FAT32, one as exFAT), reformatted them individually as F2FS and gave them each a label (USBPD01 to USBPD04). I could have done all that from the command line but it is easier using GParted, and I like an easy life.

Note that the mdadm USE flag in Gentoo Linux needed to be set when GParted was merged, so GParted would need to be re-merged with USE="mdadm" if that is not the case. Furthermore, GParted will only include F2FS in the list of available filesystems if either the F2FS module is loaded or the F2FS driver has been built into the kernel.

8. I ascertained the names of the USB pendrives:

root # lsblk -o NAME,SIZE,FSTYPE,TYPE,MOUNTPOINT
NAME     SIZE FSTYPE TYPE MOUNTPOINT
sda    698.7G        disk
├─sda1   128M ext2   part
├─sda2    16G swap   part [SWAP]
├─sda5   128G ext4   part /
├─sda6   256G ext4   part /home
└─sda7 298.5G ntfs   part /media/NTFS
sdb      3.8G        disk
└─sdb1   3.8G f2fs   part
sdc      3.8G        disk
└─sdc1   3.8G f2fs   part
sdd      3.8G        disk
└─sdd1   3.8G f2fs   part
sde      3.8G        disk
└─sde1   3.8G f2fs   part

As you can see above, the four USB pendrives are sdb to sde.

9. I loaded the raid10 module:

root # modprobe raid10
root # lsmod | grep raid
raid10                 57344  1

10. I created the RAID10 array:

root # mdadm --create --verbose /dev/md0 --level=10 --raid-devices=4 /dev/sdb /dev/sdc /dev/sdd /dev/sde
mdadm: layout defaults to n2
mdadm: layout defaults to n2
mdadm: chunk size defaults to 512K
mdadm: partition table exists on /dev/sdb
mdadm: partition table exists on /dev/sdb but will be lost or
       meaningless after creating array
mdadm: partition table exists on /dev/sdc
mdadm: partition table exists on /dev/sdc but will be lost or
       meaningless after creating array
mdadm: partition table exists on /dev/sdd
mdadm: partition table exists on /dev/sdd but will be lost or
       meaningless after creating array
mdadm: partition table exists on /dev/sde
mdadm: partition table exists on /dev/sde but will be lost or
       meaningless after creating array
mdadm: size set to 3913728K
Continue creating array? y
mdadm: Defaulting to version 1.2 metadata
mdadm: array /dev/md0 started.

It takes a while for the RAID to be created, so I checked progress periodically as follows:

root # cat /proc/mdstat
Personalities : [raid10]
md0 : active raid10 sde[3] sdd[2] sdc[1] sdb[0]
      7827456 blocks super 1.2 512K chunks 2 near-copies [4/4] [UUUU]
      [>....................]  resync =  2.8% (222272/7827456) finish=23.8min speed=5308K/sec
      
unused devices: <none>
root # cat /proc/mdstat
Personalities : [raid10]
md0 : active raid10 sde[3] sdd[2] sdc[1] sdb[0]
      7827456 blocks super 1.2 512K chunks 2 near-copies [4/4] [UUUU]
      [========>............]  resync = 44.0% (3449856/7827456) finish=12.9min speed=5637K/sec
      
unused devices: <none>
root # cat /proc/mdstat
Personalities : [raid10]
md0 : active raid10 sde[3] sdd[2] sdc[1] sdb[0]
      7827456 blocks super 1.2 512K chunks 2 near-copies [4/4] [UUUU]
      [==============>......]  resync = 74.0% (5797760/7827456) finish=5.9min speed=5698K/sec
      
unused devices: <none>
root # cat /proc/mdstat
Personalities : [raid10]
md0 : active raid10 sde[3] sdd[2] sdc[1] sdb[0]
      7827456 blocks super 1.2 512K chunks 2 near-copies [4/4] [UUUU]
      
unused devices: <none>

11. I formatted the RAID:

root # sudo mkfs.f2fs -f /dev/md0

        F2FS-tools: mkfs.f2fs Ver: 1.12.0 (2018-11-12)

Info: Disable heap-based policy
Info: Debug level = 0
Info: Trim is enabled
Info: Segments per section = 1
Info: Sections per zone = 1
Info: sector size = 512
Info: total sectors = 15654912 (7644 MB)
Info: zone aligned segment0 blkaddr: 512
Info: format version with
  "Linux version 4.19.72-gentoo (root@clevow230ss) (gcc version 8.3.0 (Gentoo 8.3.0-r1 p1.1)) #2 SMP Tue Oct 15 01:36:57 BST 2019"
Info: [/dev/md0] Discarding device
Info: This device doesn't support BLKSECDISCARD
Info: This device doesn't support BLKDISCARD
Info: Overprovision ratio = 2.300%
Info: Overprovision segments = 179 (GC reserved = 94)
Info: format successful

The option ‘-f‘ forces mkfs to overwrite any existing filesystem. (I believe the same option is ‘-F‘ in Ubuntu, rather than ‘-f‘.)

12. I created a mount point so I could mount the RAID from the command line if I wanted:

root # mkdir -p /mnt/md0

13. I mounted the RAID from the command line and checked its size. In the case of RAID10 I would expect the size to be double the size of one of the formatted USB pendrives, i.e. approximtely 2 x 3.8GB = 7.6GB):

root # mount /dev/md0 /mnt/md0
root # df -h -x devtmpfs -x tmpfs
Filesystem      Size  Used Avail Use% Mounted on
/dev/root       126G   36G   84G  31% /
/dev/sda6       252G  137G  103G  57% /home
/dev/sda7       299G  257G   43G  86% /media/NTFS
/dev/md0        7.5G  419M  7.1G   6% /mnt/md0
root # blkid | grep -v sda
/dev/md0: UUID="d565c117-37e0-48eb-b635-a2fe70b83272" TYPE="f2fs"
/dev/sdb: UUID="d1288120-a161-4809-3e89-bb5f967df69b" UUID_SUB="45a488a0-5126-0b95-0c28-eb1f743f77c7" LABEL="clevow230ss:0" TYPE="linux_raid_member"
/dev/sdc: UUID="d1288120-a161-4809-3e89-bb5f967df69b" UUID_SUB="ef7de228-cf4d-c6bf-c74a-462a0e27f8bd" LABEL="clevow230ss:0" TYPE="linux_raid_member"
/dev/sdd: UUID="d1288120-a161-4809-3e89-bb5f967df69b" UUID_SUB="b5dd5c41-3ab2-fa38-bd28-0b965883775c" LABEL="clevow230ss:0" TYPE="linux_raid_member"
/dev/sde: UUID="d1288120-a161-4809-3e89-bb5f967df69b" UUID_SUB="16149e7e-5a96-ece6-65ba-25721bcee49f" LABEL="clevow230ss:0" TYPE="linux_raid_member"

So /dev/md0 looked correct.

14. I checked that nothing was already configured in mdadm.conf and added the array’s details to it:

root # grep -v "#" /etc/mdadm.conf
root # mdadm --detail --scan | sudo tee -a /etc/mdadm.conf
ARRAY /dev/md0 metadata=1.2 name=clevow230ss:0 UUID=d1288120:a1614809:3e89bb5f:967df69b
root # grep -v "#" /etc/mdadm.conf
ARRAY /dev/md0 metadata=1.2 name=clevow230ss:0 UUID=d1288120:a1614809:3e89bb5f:967df69b

15. As the RAID will have only a partition for file storage, and as the RAID array will not always be connected to the laptop, it does not need to be assembled automatically early during boot, so there is no need to add mdadm.conf to an initramfs (which this laptop does not have anyway) and no need to specify /dev/md0 in /etc/fstab to be mounted at boot.

16. I left the USB hub connected to the laptop and rebooted.

17. I checked that the modules were loaded at boot:

root # lsmod | grep raid
raid10                 57344  1
root # lsmod | grep f2fs
f2fs                  466944  0

18. I checked that the RAID had been assembled correctly at boot:

root # blkid | grep -v sda
/dev/sdb: UUID="d1288120-a161-4809-3e89-bb5f967df69b" UUID_SUB="45a488a0-5126-0b95-0c28-eb1f743f77c7" LABEL="clevow230ss:0" TYPE="linux_raid_member"
/dev/sdc: UUID="d1288120-a161-4809-3e89-bb5f967df69b" UUID_SUB="ef7de228-cf4d-c6bf-c74a-462a0e27f8bd" LABEL="clevow230ss:0" TYPE="linux_raid_member"
/dev/sdd: UUID="d1288120-a161-4809-3e89-bb5f967df69b" UUID_SUB="b5dd5c41-3ab2-fa38-bd28-0b965883775c" LABEL="clevow230ss:0" TYPE="linux_raid_member"
/dev/md0: UUID="d565c117-37e0-48eb-b635-a2fe70b83272" TYPE="f2fs"
/dev/sde: UUID="d1288120-a161-4809-3e89-bb5f967df69b" UUID_SUB="16149e7e-5a96-ece6-65ba-25721bcee49f" LABEL="clevow230ss:0" TYPE="linux_raid_member"

19. I rebooted a few times with and without the USB hub connected. The module raid10 only gets loaded if the USB hub is connected. If I reboot without the hub connected, raid10 is no longer loaded automatically at boot. If I plug in the hub after the laptop has booted, raid10 gets loaded and the RAID array is recognised by the OS.

20. I mounted the RAID from the command line and copied a file to it as root user:

root # mount /dev/md0 /mnt/md0
root # ls -la /mnt/md0
total 8
drwxr-xr-x 2 root root 4096 Oct 15 07:40 .
drwxr-xr-x 7 root root 4096 Oct 15 07:42 ..
root # cp ./Paper_sheet_sizes.png /mnt/md0
root # ls -la /mnt/md0
total 268
drwxr-xr-x 2 root root   4096 Oct 15 08:07 .
drwxr-xr-x 7 root root   4096 Oct 15 07:42 ..
-rw-r--r-- 1 root root 265760 Oct 15 08:07 Paper_sheet_sizes.png
root # umount /dev/md0
root # ls -la /mnt/md0
total 8
drwxr-xr-x 2 root root 4096 Oct 15 07:42 .
drwxr-xr-x 7 root root 4096 Oct 15 07:42 ..

However, /mnt/md0/ is owned by the root user, so user fitzcarraldo cannot copy files into it. Therefore I changed the ownership:

root # mount /dev/md0 /mnt/md0
root # ls -la /mnt/
total 28
drwxr-xr-x  7 root root 4096 Oct 15 07:42 .
drwxr-xr-x 22 root root 4096 Oct  6 08:31 ..
-rw-r--r--  1 root root    0 Apr  9  2015 .keep
drwxr-xr-x  2 root root 4096 Apr 19  2015 cdrom
drwxr-xr-x  2 root root 4096 Jan 16  2017 floppy
drwxr-xr-x  2 root root 4096 Oct 15 08:07 md0
drwxr-xr-x  2 root root 4096 Apr 17  2015 pendrive
drwxr-xr-x  2 root root 4096 Mar 18  2016 usbstick
root # chown fitzcarraldo:fitzcarraldo /mnt/md0
root # ls -la /mnt/
total 28
drwxr-xr-x  7 root         root         4096 Oct 15 07:42 .
drwxr-xr-x 22 root         root         4096 Oct  6 08:31 ..
-rw-r--r--  1 root         root            0 Apr  9  2015 .keep
drwxr-xr-x  2 root         root         4096 Apr 19  2015 cdrom
drwxr-xr-x  2 root         root         4096 Jan 16  2017 floppy
drwxr-xr-x  2 fitzcarraldo fitzcarraldo 4096 Oct 15 08:07 md0
drwxr-xr-x  2 root         root         4096 Apr 17  2015 pendrive
drwxr-xr-x  2 root         root         4096 Mar 18  2016 usbstick
root # umount /dev/md0

21. ‘Places’ in Dolphin shows /mnt/md0 as ‘7.5 GiB Hard Drive’.

22. I can still mount the RAID from the command line:

root # mount /dev/md0 /mnt/md0
root # df -h /dev/md0
Filesystem      Size  Used Avail Use% Mounted on
/dev/md0        7.5G  420M  7.1G   6% /mnt/md0
root # umount /dev/md0

23. If I want to use the RAID in KDE I must use Dolphin to mount it, not mount it from the command line. To do this I click on the RAID ‘7.5 GiB Hard Drive’ listed under ‘Places’, and a window pop-ups prompting me to enter the root user’s password.

If I mount /dev/md0 via Dolphin instead of via the command line, KDE mounts it on a different directory:

root # df -h /run/media/fitzcarraldo/d565c117-37e0-48eb-b635-a2fe70b83272/
Filesystem      Size  Used Avail Use% Mounted on
/dev/md0        7.5G  420M  7.1G   6% /run/media/fitzcarraldo/d565c117-37e0-48eb-b635-a2fe70b83272

If I want to unmount it, I right-click on the RAID in ‘Places’ and select ‘Unmount’ in the right-click menu. Once it has been unmounted, I can unplug the hub from the laptop. If I plug the hub back into the laptop, the RAID is detected and can be mounted as usual.

So, it works! A USB hub and pendrives are a handy way to:

  • experiment with creating the various types of RAID;
  • compare the capacity of the RAID with the capacity of the USB pendrives used;
  • measure the time to write and read a large file to/from the RAID and compare those times with the time to write and read the same file to/from a single USB pendrive of the same model.

Firewall zones (profiles) in Linux, and how to switch them automatically if you use UFW

Firstly, a note on terminology: UFW (Uncomplicated Firewall) and its two GUI front-ends Gufw and UFW Frontends use the term ‘application profile’ to refer to a pre-configured set of rules specified in a file. Files containing UFW application profiles are placed in the directory /etc/ufw/applications.d/. An application profile for SMB, for example, enables the root user to use the UFW command ‘ufw allow Samba‘ (‘ufw allow CIFS‘ in Gentoo Linux) rather than having to enter UFW commands specifying the precise ports and network protocols that SMB uses. However, this blog post is not about UFW’s application profiles; it is about what Gufw calls ‘profiles’ and firewalld calls ‘zones’.

In essence a profile/zone is a collection of firewall policies and rules. Both Gufw and firewalld include the concept of a ‘zone’, although Gufw uses the term ‘profile’ rather than ‘zone’. UFW Frontends does not have the concept of a ‘zone’; rules entered via UFW Frontends apply to any network to which you connect your laptop. The ability to define different zones for different networks is handy. For example, you can have certain policies and rules when your laptop is connected to your home network, and different policies and rules when your laptop is connected to the network in a café, hotel, airport or other public place.

An attractive feature of firewalld when used in conjunction with NetworkManager and KDE Plasma is that it is possible to use the desktop environment’s network management module (‘System Settings’ > ‘Connections’) to specify a particular firewalld zone for a particular network connection. For example, let’s say you used firewalld to specify certain policies and rules for a zone you named ‘office’, and you then specified in the System Settings – Connections GUI that a connection named ‘ACM’ should use the zone ‘office’. Thereafter, whenever you connect your laptop to the network named ‘ACM’, firewalld will use the policies and rules you previously configured for the zone ‘office’.

Unlike firewalld, Gufw does not have the ability to switch profiles automatically according to which network the laptop is connected. You have to select manually the profile you wish to use. You would launch Gufw prior to connecting to, for example, your office’s network, select the profile ‘Office’ (or whatever you have named it), then connect your laptop to that network.

I think many people would be satisfied with the functionality currently provided by Gufw. I could use the Gufw GUI to create Gufw profiles with names such as ‘Home’, ‘HomeDave’, ‘Public’, ‘HQoffice’, ‘USoffice’, ‘PestanaRio’ and so on, and specify the different policies and rules I want for each profile. At home I would launch Gufw on my laptop and select the Home profile then connect to my home network; in the office at work I would launch Gufw on my laptop and select the HQoffice profile then connect to the office network; at my friend Dave’s house I would launch Gufw on my laptop and select the HomeDave profile then connect to the house network; and so on. Nevertheless I do see the attraction of automated zone switching, as provided by firewalld in conjunction with NetworkManager and KDE. It would be handy if my laptop could switch automatically to the Home profile when my laptop connected to the network at my home with the name ‘BTHub5-8EUQ’, automatically switch to the HQoffice profile when my laptop connected to the network named ‘HQ-Office2’ in the office, and so on.

I use UFW on my two laptops running Gentoo Linux. The package ufw-frontends is also installed but normally I use UFW directly via the command line. However I wanted to learn about zones/profiles while using UFW, and I also wanted to see if I could automate the switching of zones without resorting to installing firewalld. NetworkManager has the ability to launch ‘hook’ scripts when certain things happen — when a network connection changes, for example — and this seemed to me to be a way of switching profiles automatically.

I had not used Gufw before, so I decided to install it. A package is available in many Linux distributions but there is no ebuild for Gufw in Gentoo’s main Portage tree and I could not find an up-to-date ebuild for it in any Portage overlays. Therefore I created the ebuild for net-firewall/gufw-19.10.0 shown below. It probably needs improving, but it does install a working Gufw in Gentoo Linux.

# Copyright 1999-2019 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# $Header: $

EAPI=7
PYTHON_COMPAT=( python3_{5,6,7} )
DISTUTILS_IN_SOURCE_BUILD=1

inherit distutils-r1

MY_PN="gui-ufw"
MY_PV="$(ver_cut 1-2)"

DESCRIPTION="GUI frontend for managing ufw."
HOMEPAGE="https://gufw.org/"
SRC_URI="https://launchpad.net/${MY_PN}/trunk/${MY_PV}/+download/${MY_PN}-${PV}.tar.gz"

LICENSE="GPL-3"
SLOT="0"
KEYWORDS="~amd64"
IUSE=""

DEPEND="dev-python/python-distutils-extra"
RDEPEND="net-firewall/ufw
	dev-python/netifaces
	dev-python/pygobject:3
	net-libs/webkit-gtk[introspection]
	sys-auth/elogind
	sys-auth/polkit
	x11-libs/gtk+:3[introspection]
	x11-themes/gnome-icon-theme-symbolic
"
S=${WORKDIR}/${MY_PN}-${PV}

pkg_postinst() {
	sed '/dist-packages/d' -i /usr/bin/gufw-pkexec
	sed -E '/\/share\//d' -i /usr/bin/gufw-pkexec
	local PYTHONVERSION="$(python -c 'import sys; print("{}.{}".format(sys.version_info.major, sys.version_info.minor))')"
	sed -E "s|python3\.[0-9]|python${PYTHONVERSION}|g" -i /usr/bin/gufw-pkexec
	sed -E 's|\/lib\/|\/lib64\/|g' -i /usr/bin/gufw-pkexec
}

How To Set Up a Firewall with GUFW on Linux‘ is a good tutorial on Gufw.

As I had not used Gufw previously, I had to play around with it to understand better its functional design. I found that if I configure rules directly via UFW on the command line without using Gufw, Gufw does not allow me to edit those rules (but does allow me to delete them) and those rules exist whichever Gufw profile is selected in the Gufw GUI. Gufw profiles are stored in files named ‘/etc/gufw/*.profile‘ (e.g. /etc/gufw/Home.profile) and these files will not include UFW rules entered via the command line. On the other hand, UFW rules created via the Gufw GUI apply solely to the currently-selected Gufw profile, which is what I would have expected. In other words, I can create a different set of policies and rules in each Gufw profile. Therefore I believe Gufw profiles (as distinct from UFW application profiles) are basically analogous to firewalld’s zones. It also appears to me that Gufw maintains configuration files specifying policies and rules independently of UFW, which Gufw then applies to UFW. In other words, if you are a Gufw user you should not use UFW directly to configure policies and rules, otherwise Gufw’s configuration files will not include what you did directly using UFW. To reiterate, use only Gufw or only UFW, not both.

The current Gufw profile’s name is listed in the file /etc/gufw/gufw.cfg. For example, I currently have the Home profile selected in the Gufw GUI, and the file gufw.cfg contains the following:

[GufwConfiguration]
profile = Home
windowwidth = 542
windowheight = 530
confirmdetelerule = yes

If I examine the contents of the file /etc/gufw/Home.profle I see that it contains the UFW policies and rules I specified for the Gufw Home profile:

[fwBasic]
status = enabled
incoming = deny
outgoing = allow
routed = disabled

[Rule0]
ufw_rule = 137,138/udp ALLOW IN 192.168.1.0/24
description = Samba
command = /usr/sbin/ufw allow in proto udp from 192.168.1.0/24 to any port 137,138
policy = allow
direction = in
protocol = 
from_ip = 192.168.1.0/24
from_port = 
to_ip = 
to_port = 137,138/udp
iface = 
routed = 
logging = 

[Rule1]
ufw_rule = 139,445/tcp ALLOW IN 192.168.1.0/24
description = Samba
command = /usr/sbin/ufw allow in proto tcp from 192.168.1.0/24 to any port 139,445
policy = allow
direction = in
protocol = 
from_ip = 192.168.1.0/24
from_port = 
to_ip = 
to_port = 139,445/tcp
iface = 
routed = 
logging =

I also notice that the other Gufw profiles can differ. For example, my Office.profile file contains the following:

[fwBasic]
status = enabled
incoming = deny
outgoing = allow
routed = allow

The profile name listed in gufw.cfg gets changed when the user changes the profile using the Gufw GUI. It appears to me that only at the point in time when the user selects a certain Gufw profile in the Gufw GUI does Gufw parse the applicable *.profile file and issue commands to UFW to implement the policies and rules specified in the *.profile file.

Initially I tried to automate the process of changing the Gufw profile by doing the following:

  1. I created a NetworkManager Dispatcher hook script to:

    1. detect when the laptop connects to a network;

    2. determine whether the network is at my home, at my workplace or in a public place (café, airport or wherever) by looking at the connection name;

    3. edit gufw.cfg to change the name of the Gufw profile according to the network connected.
  2. I configured KDE to launch Gufw automatically at login, hoping that would implement the Gufw profile specified in gufw.cfg.

When I connected the laptop to various networks, Gufw did indeed show the name of the profile selected by the NetworkManager Dispatcher hook script, but the associated Gufw profile’s rules had not been applied. They were only applied if I clicked on the ‘Profile’ pull-down menu in Gufw, selected a different Gufw profile, then re-selected the desired Gufw profile. Therefore driving Gufw from a NetworkManager Dispatcher hook script is not possible. This is a pity, as Gufw is an easy way to manage UFW from a GUI; it allows the user to create, delete and edit zones (Gufw profiles) and to select them manually. What Gufw doesn’t do is enable the user to associate those zones with connection names, nor trigger specific zone automatically based on the selected network connection. firewalld, on the other hand, does enable the user to do both those things.

As my attempt at automating the switching of zones in Gufw had failed, I decided to create a NetworkManager Dispatcher hook script to switch zones automatically by using UFW commands. Initially I though about creating a bespoke UFW application profile for each zone and allowing/denying those in the script, but it is actually easier to use the fundamental UFW commands in the script, especially as UFW commands are relatively easy to understand. Also, this approach means everything is in a single file, which facilitates configuration. I can simply edit the script in order to: a) add or delete a zone; b) change a zone’s name; c) change policies and rules for a zone; d) add or delete a connection; e) change the name of a connection; f) change the zone a connection uses. Granted, editing a script is not as user-friendly as using the firewalld GUI to configure a zone and then using KDE Plasma’s system settings module Connections to specify that zone for a specific connection, but my script is not particularly difficult to understand and edit. And by using such a script I can continue to use UFW rather than installing firewalld and having to learn how to use it.

My NetworkManager Dispatcher hook script /etc/NetworkManager/dispatcher.d/20_ufw-zones is listed below. In the main body of the script I define the zone I wish to use for each connection, and in the function select_zone I define the policies and rules I want each zone to use.

#!/bin/bash
INTERFACE=$1
STATUS=$2
WIRED=enp4s0f1
WIFI=wlp3s0

CT_helper_rule() {
    echo "# The following is needed to enable Samba commands to" >> /etc/ufw/before.rules
    echo "# work properly for broadcast NetBIOS name resolution" >> /etc/ufw/before.rules
    echo "#"  >> /etc/ufw/before.rules
    echo "# raw table rules" >> /etc/ufw/before.rules
    echo "*raw" >> /etc/ufw/before.rules
    echo ":OUTPUT ACCEPT [0:0]" >> /etc/ufw/before.rules
    echo "-F OUTPUT" >> /etc/ufw/before.rules
    echo "-A OUTPUT -p udp -m udp --dport 137 -j CT --helper netbios-ns" >> /etc/ufw/before.rules
    echo "COMMIT" >> /etc/ufw/before.rules
}
 
select_zone() {
    ufw --force reset
    ufw --force enable
    ZONE=$1
    case "$ZONE" in
    'Home')
        ufw default deny incoming
        ufw default allow outgoing
        #
        # Rules for SMB
        ufw allow from 192.168.1.0/24 to any port 137,138 proto udp
        ufw allow from 192.168.1.0/24 to any port 139,445 proto tcp
        CT_helper_rule
        #
        # Rules for KDEConnect
        ufw allow from 192.168.1.0/24 to any port 1714:1764 proto udp
        ufw allow from 192.168.1.0/24 to any port 1714:1764 proto tcp
    ;;
    'Office')
        ufw default deny incoming
        ufw default allow outgoing
    ;;
    'Public')
        ufw default reject incoming
        ufw default allow outgoing
    ;;
    'JohnsHouse')
        ufw default deny incoming
        ufw default allow outgoing
        #
        # Rules for SMB
        ufw allow from 192.168.42.0/24 to any port 137,138 proto udp
        ufw allow from 192.168.42.0/24 to any port 139,445 proto tcp
        CT_helper_rule
        #
        # Rules for KDEConnect
        ufw allow from 192.168.42.0/24 to any port 1714:1764 proto udp
        ufw allow from 192.168.42.0/24 to any port 1714:1764 proto tcp
    ;;
    esac
    ufw --force reload
    rm /etc/ufw/*.rules.20* # Delete backups of *.rules files ufw makes every time it is reset
    echo -n `date +"[%F %T %Z]"` >> /var/log/ufw-zones.log
    echo " Zone $ZONE selected for connection $ACTIVE on interface $INTERFACE." >> /var/log/ufw-zones.log
}
 
# Check if either the wired or wireless interface is up
if [ "$INTERFACE" = "$WIRED" -o "$INTERFACE" = "$WIFI" ] && [ "$STATUS" = "up" ]; then
 
    # Check if a single connection is active
    if [ `nmcli c | grep -v "\-\-" | grep -v "NAME.*UUID.*TYPE.*DEVICE" | wc -l` -eq 1 ]; then
 
        # Ascertain the name of the active connection
        ACTIVE=`nmcli c | grep -v "\-\-" | grep -v "NAME.*UUID.*TYPE.*DEVICE" | awk -F' ' '{print $1}'`
 
        case "$ACTIVE" in
 
        'eth0')
            ZONE="Home"
        ;;
        'POR1-wired')
            ZONE="Office"
        ;;
        'BTHub5-8EUQ')
            ZONE="Home"
        ;;
        'BTHub5-8EUQ-5GHz')
            ZONE="Home"
        ;;
        'John1')
            ZONE="JohnsHouse"
        ;;
        'GRAND MERCURE')
            ZONE="Public"
        ;;
        *)
            # If connection name is not in above list
            ZONE="Public"
        ;;

        esac

        select_zone $ZONE
        exit $?

    fi
fi

The log file that the script uses contains a chronological record of the connections made and the zones selected:

$ cat /var/log/ufw-zones.log 
[2019-09-30 20:13:52 BST] Zone Home selected for connection eth0 on interface enp4s0f1.
[2019-10-01 22:59:18 BST] Zone Home selected for connection BTHub5-8EUQ-5GHz on interface wlp3s0.
[2019-10-02 17:59:23 EDT] Zone Public selected for connection loganwifi on interface wlp3s0.
[2019-10-03 10:12:46 EDT] Zone Office selected for connection POR1-wired on interface enp4s0f1.

Paul Gideon Dann’s patchset for Poppler to enable Okular (Qt5) to use Cairo rather than Splash to render PDF files

If you view the same PDF file in Okular (KDE) and Evince (GNOME), you may notice that fonts and lines are rendered better in Evince. Both applications use Poppler to render text and graphics in PDF files, but Poppler uses a different rendering backend in the two applications. For Evince Poppler uses the Cairo library, whereas for Okular Poppler uses Splash, a backend inherited from Poppler’s predecessor Xpdf (still in development). Unfortunately for KDE users, Cairo often does a better job than Splash. However, independent software engineer Paul Gideon Dann came to the rescue by producing the patchset poppler-cairo-backend to modify Poppler in order to make it use the Cairo library instead of Splash when Poppler is used by Okular. To quote the README file for Paul’s patchset:

Purpose of this Patchset

Currently, the default backend for the Qt5 wrapper (used by Okular) is Splash. Unfortunately, Splash does not support subpixel rendering of fonts, so those of us using KDE are stuck with somewhat ugly-looking fonts. This patchset adds support for the Cairo backend to the Qt5 wrapper. It also forces subpixel rendering in the Cairo backend. The upshot of this is that we get beautiful fonts in Okular.

The README focuses on fonts, but in fact the rendering of lines in graphics in PDF files can also be improved by the application of the patchset.

Apparently the Poppler maintainer feels that the introduction of a dependency on Cairo to the Qt5 wrapper (even an optional dependency) in Poppler would be controversial, and he is not willing to merge the patchset. For Okular users who already have Cairo installed (e.g. for Firefox, Inkscape, Scribus and so on), and who are noticing inadequate rendering of some PDF files, Paul’s patchset is worth trying.

In Gentoo Linux, which is a source code-based distribution, it is very easy to apply the patchset. For example, I did the following to apply the patchset for Poppler 0.80.0 in a ~amd64 (Testing Branch) installation:

1. Created a package-specific and version-specific directory to hold the patchset:

root # mkdir -p /etc/portage/patches/app-text/poppler-0.80.0

2. Downloaded the patchset for Poppler 0.80.0 from the following Web page:

https://github.com/giddie/poppler-cairo-backend/tree/76e607bcf010d6d9b8df5cb0f851ef9c91d4caf2

3. Copied the patchset to the directory created in Step 1:

root # cp /home/fitzcarraldo/Downloads/*.patch /etc/portage/patches/app-text/poppler-0.80.0/
root # ls -1 /etc/portage/patches/app-text/poppler-0.80.0
0001-Cairo-backend-added-to-Qt5-wrapper.patch
0002-Setting-default-Qt5-backend-to-Cairo.patch
0003-Apply-subpixel-rendering-in-Cairo-Backend.patch

4. Checked first that the patchset could be applied successfully before actually using it:

root # cd /usr/portage/app-text/poppler
root # ebuild poppler-0.80.0.ebuild clean prepare
 * poppler-0.80.0.tar.xz BLAKE2B SHA512 size ;-) ...                                     [ ok ]
 * checking ebuild checksums ;-) ...                                                     [ ok ]
 * checking auxfile checksums ;-) ...                                                    [ ok ]
 * checking miscfile checksums ;-) ...                                                   [ ok ]
>>> Unpacking source...
>>> Unpacking poppler-0.80.0.tar.xz to /var/tmp/portage/app-text/poppler-0.80.0/work
>>> Source unpacked in /var/tmp/portage/app-text/poppler-0.80.0/work
>>> Preparing source in /var/tmp/portage/app-text/poppler-0.80.0/work/poppler-0.80.0 ...
 * Applying poppler-0.60.1-qt5-dependencies.patch ...                                    [ ok ]
 * Applying poppler-0.28.1-fix-multilib-configuration.patch ...                          [ ok ]
 * Applying poppler-0.78.0-respect-cflags.patch ...                                      [ ok ]
 * Applying poppler-0.61.0-respect-cflags.patch ...                                      [ ok ]
 * Applying poppler-0.57.0-disable-internal-jpx.patch ...                                [ ok ]
 * Applying 0001-Cairo-backend-added-to-Qt5-wrapper.patch ...                            [ ok ]
 * Applying 0002-Setting-default-Qt5-backend-to-Cairo.patch ...                          [ ok ]
 * Applying 0003-Apply-subpixel-rendering-in-Cairo-Backend.patch ...                     [ ok ]
 * User patches applied.
>>> Source prepared.

5. Re-merged Poppler to apply the patchset to the Poppler source code and rebuild the patched package:

root # emerge -1v poppler

These are the packages that would be merged, in order:

Calculating dependencies... done!
[ebuild   R    ] app-text/poppler-0.80.0:0/90::gentoo  USE="cairo cjk cxx introspection jpeg jpeg2k lcms png qt5 tiff utils -curl -debug -doc -nss" 0 KiB

Total: 1 package (1 reinstall), Size of downloads: 0 KiB

>>> Verifying ebuild manifests
>>> Emerging (1 of 1) app-text/poppler-0.80.0::gentoo
>>> Installing (1 of 1) app-text/poppler-0.80.0::gentoo
>>> Jobs: 1 of 1 complete                           Load avg: 1.06, 1.11, 0.95
>>> Auto-cleaning packages...

>>> No outdated packages were found on your system.

 * GNU info directory index is up-to-date.

6. Re-merged Okular so that it uses the patched Poppler dependency:

root # emerge -1v okular

These are the packages that would be merged, in order:

Calculating dependencies... done!
[ebuild   R    ] kde-apps/okular-19.08.1:5::gentoo  USE="chm crypt djvu image-backend pdf postscript tiff -debug -epub -handbook -markdown -mobi -mobile -plucker -share -speech -test" 0 KiB

Total: 1 package (1 reinstall), Size of downloads: 0 KiB

>>> Verifying ebuild manifests
>>> Emerging (1 of 1) kde-apps/okular-19.08.1::gentoo
>>> Installing (1 of 1) kde-apps/okular-19.08.1::gentoo
>>> Jobs: 1 of 1 complete                           Load avg: 1.17, 1.13, 1.04
>>> Auto-cleaning packages...

>>> No outdated packages were found on your system.

 * GNU info directory index is up-to-date.

My thanks go to Paul for taking the time to produce the patchset.

Preventing Lubuntu 18.04 from leaving a user process running after the user logs out

My family’s desktop machine has Lubuntu 18.04 installed, which uses systemd and the LXDE desktop environment. Each family member has their own user account, thus the installation is a single-seat, multi-user installation. For each user’s account I set up the virus-checking scheme described in an earlier post, suitably modified to take into account the differences between Lubuntu 18.04 and Gentoo Linux running KDE. For example, the monitorDownloadsGUI script in Lubuntu 18.04 uses zenity rather than kdialog, and, as Lubuntu 18.04 uses systemd, the ClamAV daemon’s service file in Lubuntu 18.04 is /lib/systemd/system/clamav-daemon.service rather than the OpenRC init file /etc/init.d/clamd used in my Gentoo Linux installations.

The virus-checking script ~/.monitorDownloadGUI in each user’s home directory is launched automatically by LXDE at login because I created a Desktop Configuration File ~/.config/autostart/monitorDownloadsGUI.desktop in each user’s account. For example, the contents of the file in my account are as follows:

[Desktop Entry]
Type=Application
Exec=/home/fitzcarraldo/.monitorDownloadsGUI

However, I recently noticed that Lubuntu 18.04 does not terminate the monitorDownloadsGUI process when the user logs out. I do not see this behaviour on my laptops running Gentoo Linux with OpenRC and KDE, so I am not sure why this is happening in Lubuntu 18.04 with systemd and LXDE. The output of the ‘ps -ef‘ command after each of the three example steps shown below illustrates the behaviour.

Step 1. george is the only user who is logged-in.

$ ps -ef | grep bash | grep -v grep
george    1410     1  0 02:05 ?        00:00:00 /bin/bash /home/george/.monitorDownloadsGUI
george    1597  1358  0 02:05 pts/0    00:00:00 /bin/bash

Step 2. ringo uses ‘Logout’ > ‘Switch User’ to login to his account.

$ ps -ef | grep bash | grep -v grep
george    1410     1  0 02:05 ?        00:00:00 /bin/bash /home/george/.monitorDownloadsGUI
george    1597  1358  0 02:05 pts/0    00:00:00 /bin/bash
ringo     2382     1  0 02:06 ?        00:00:00 /bin/bash /home/ringo/.monitorDownloadsGUI

Step 3. ringo logs out of his account.

$ ps -ef | grep bash | grep -v grep
george    1410     1  0 02:05 ?        00:00:00 /bin/bash /home/george/.monitorDownloadsGUI
george    1597  1358  0 02:05 pts/0    00:00:00 /bin/bash
ringo     2382     1  0 02:06 ?        00:00:00 /bin/bash /home/ringo/.monitorDownloadsGUI

Notice that the process with PID 2382 is still running, even though user ringo is no longer logged in.

If a user logs out and logs in again, or if users switch between sessions using ‘Logout’ > ‘Switch User’, it is also possible for multiple instances of the script per user to be running. For example:

$ ps -ef | grep bash | grep -v grep
george    1564     1  0 11:14 ?        00:00:00 /bin/bash /home/george/.monitorDownloadsGUI
ringo     2522     1  0 11:16 ?        00:00:00 /bin/bash /home/ringo/.monitorDownloadsGUI
george    3803     1  0 11:17 ?        00:00:00 /bin/bash /home/george/.monitorDownloadsGUI
george    5997     1  0 11:19 ?        00:00:00 /bin/bash /home/george/.monitorDownloadsGUI
george    6054  5881  0 11:19 pts/0    00:00:00 /bin/bash

Notice that several instances of the script are running for user george. There should only be one instance.

In order to prevent these multiple instances, I added the shell script lines below to the existing LightDM session-cleanup-script that I had created previously to solve a different problem in the Lubuntu 18.04 installation (see an earlier blog post).

# Get rid of duplicate instances (if any) per user of the virus-checker script's process
who -u | grep -v "\." > /tmp/logged-in_users
while IFS=: read -r f1 f2 f3 f4 f5 f6 f7
# $f1 is username
# $f2 is password ('x')
# $f3 is UID
# $f4 is GID
# $f5 is UID info
# $f6 is home directory
# $f7 is command/shell
do
    match=0
    while read a b c d e f g h # Use this if this script is launched by LightDM in Lubuntu 18.04
#    while read a b c d e f g # Use this if you launch this script from a terminal in Lubuntu 18.04
    #
    # If this script is launched by a user, 'who -u' returns the following fields:
    # "john     tty7         2019-08-31 17:08 00:01        1624 (:0)"
    # If this script is launched by LightDM, 'who -u' returns the following fields:
    # "john     tty7        Aug 31 17:08 00:01        1624 (:0)"
    #
    do
        if [[ $f6 == *"/home/"* ]] && [[ $f7 == "/bin/bash" ]] && [[ $a == $f1 ]]; then
            match=1
            user=$f1
            tty=$b
        fi
    done < /tmp/logged-in_users
    if [[ $match -eq 1 ]] && [[ $(echo $tty | sed 's/[^0-9]*//g') -gt 6 ]]; then
        if [[ `ps -ef | grep bash | grep "$user" | grep monitorDownloadsGUI | awk -F' ' '{print $2}' | wc -l` -gt 1 ]]; then
            kill `ps -ef | grep bash | grep "$user" | grep monitorDownloadsGUI | awk -F' ' '{print $2}' | tail -n +2`
        fi
    elif [[ $match -ne 1 ]]; then
        if [[ $f6 == *"/home/"* ]] && [[ $f7 == "/bin/bash" ]] && [[ `ps -ef | grep bash | grep "$f1" | grep monitorDownloadsGUI | awk -F' ' '{print $2}' | wc -l` -gt 1 ]]; then
            kill `ps -ef | grep bash | grep "$f1" | grep monitorDownloadsGUI | awk -F' ' '{print $2}' | tail -n +2`
        elif [[ $f6 == *"/home/"* ]] && [[ $f7 == "/bin/bash" ]] && [[ `ps -ef | grep bash | grep "$f1" | grep monitorDownloadsGUI | awk -F' ' '{print $2}' | wc -l` -eq 1 ]]; then
            kill `ps -ef | grep bash | grep "$f1" | grep monitorDownloadsGUI | awk -F' ' '{print $2}'`
        fi
    fi
done < /etc/passwd
rm /tmp/logged-in_users

The above lines of Bash script kill additional instances of monitorDownloadGUI on a per-user basis when a user session ends. If LightDM’s session-cleanup-script does this, there will be no more than one instance of a monitorDownloadsGUI process per logged-in user, and no instances of a monitorDownloadGUI process for users who have logged out:

$ ps -ef | grep bash | grep -v grep
george    1473     1  0 12:32 ?        00:00:00 /bin/bash /home/george/.monitorDownloadsGUI
george    1693  1412  0 12:32 pts/0    00:00:00 /bin/bash

Problem solved. Well, worked around. I would like to know what causes the problem to happen in the first place. I assume it is either systemd or LXDE.

How to run KDE Dolphin, Kate and KWrite as root user

When using KDE I occasionally wish to launch KWrite or Kate as root user in order to edit system files more easily than using a TUI editor in a terminal window (either launched as root user or by using the sudoedit command). Being able to browse using Dolphin as the root user occasionally is also useful. These all used to be possible by launching the application with the kdesu command, but in 2017 KDE developer Martin Gräßlin removed this option on security grounds (see his blog post ‘Editing files as root‘). Attempting to launch e.g. Kate using the sudo command results in the following message:

$ sudo kate
Executing Kate with sudo is not possible due to unfixable security vulnerabilities.

Attempting to launch e.g. Kate using the kdesu command results in a pop-up window prompting me to enter the root user’s password, but then does not launch Kate:

$ kdesu kate
$

I am willing to accept a small risk despite the ‘unfixable security vulnerabilities’ , and a 2018 Kubuntu Forums post by KDE user Rog131 provided me with a solution. It is possible to launch Dolphin, Kate and KWrite as root from your user account by using the pkexec command. For example, to launch Dolphin you can enter:

$ pkexec env DISPLAY=$DISPLAY XAUTHORITY=$XAUTHORITY KDE_SESSION_VERSION=5 KDE_FULL_SESSION=true dolphin

Dolphin first displays an orange-coloured box with the warning message ‘Running Dolphin as root can be dangerous. Please be careful.’ and you can then browse and open root-owned directories and files.

You can also launch Kate and KWrite as root from your user account in the same way:

$ pkexec env DISPLAY=$DISPLAY XAUTHORITY=$XAUTHORITY KDE_SESSION_VERSION=5 KDE_FULL_SESSION=true kate
$ pkexec env DISPLAY=$DISPLAY XAUTHORITY=$XAUTHORITY KDE_SESSION_VERSION=5 KDE_FULL_SESSION=true kwrite

To make it easy to launch them as root user from e.g. Konsole or Yakuake you could set aliases for the three commands in your ~/.bashrc file:

$ tail -n 3 ~/.bashrc
alias dolroot="pkexec env DISPLAY=$DISPLAY XAUTHORITY=$XAUTHORITY KDE_SESSION_VERSION=5 KDE_FULL_SESSION=true dolphin"
alias kateroot="pkexec env DISPLAY=$DISPLAY XAUTHORITY=$XAUTHORITY KDE_SESSION_VERSION=5 KDE_FULL_SESSION=true kate"
alias kwriteroot="pkexec env DISPLAY=$DISPLAY XAUTHORITY=$XAUTHORITY KDE_SESSION_VERSION=5 KDE_FULL_SESSION=true kwrite"

Then all you would need to type in a terminal window would be:

$ dolroot
$ kateroot
$ kwriteroot

which are no more difficult than having to type:

$ kdesu dolphin
$ kdesu kate
$ kdesu kwrite

If an alias is used, rooted-Dolphin/Kate/KWrite can be launched from the command line but cannot be launched via KDE’s Application Launcher menu or KRunner. On the other hand, if a wrapper script is used, rooted-Dolphin/Kate/KWrite can be launched from the user’s command line and via KDE’s Application Launcher menu (and therefore via KRunner too). For example, I created three tiny Bash scripts dolroot, kateroot and kwriteroot. The scripts simply contain the aforementioned pkexec command. For example, dolroot contains:

#!/bin/bash
pkexec env DISPLAY=$DISPLAY XAUTHORITY=$XAUTHORITY KDE_SESSION_VERSION=5 KDE_FULL_SESSION=true dolphin

Don’t forget to make them executable:

$ chmod 700 dolroot
$ chmod 700 kateroot
$ chmod 700 kwriteroot
$ ls -la *root
-rwx------ 1 fitzcarraldo fitzcarraldo 115 Jul 30 15:33 dolroot
-rwx------ 1 fitzcarraldo fitzcarraldo 112 Jul 30 15:34 kateroot
-rwx------ 1 fitzcarraldo fitzcarraldo 114 Jul 30 15:34 kwriteroot	

After adding entries for dolroot, kateroot and kwriteroot to the KDE Application Launcher’s menu, you can press Alt+F2 as usual to display the KRunner launcher then enter ‘dolroot’, ‘kateroot’ or ‘kwriteroot’ (without the quotes, obviously) in the KRunner window to launch Dolphin/Kate/KWrite as root user. A window will pop-up for you to enter the root user’s password. Once you have entered the root user’s password, the application will be launched.

Thankfully KDE’s Nathaniel Graham is pragmatic:

D12795 – Re-allow running Dolphin as the root user (but still not using sudo)
D12732 – Show a warning when running as the root user

How to change the height of the Kickoff Application Launcher menu in KDE Plasma

The height of the KDE Plasma Kickoff Application Launcher menu is not user-configurable, which is odd in a Desktop Environment with a reputation for being highly user-configurable.

It turns out that the height and width of the pop-up menu are hard-coded in the ASCII file /usr/share/plasma/plasmoids/org.kde.plasma.kickoff/contents/ui/FullRepresentation.qml:

root # grep -E "Layout.minimumHeight.*units.gridUnit" /usr/share/plasma/plasmoids/org.kde.plasma.kickoff/contents/ui/FullRepresentation.qml
    Layout.minimumHeight: units.gridUnit * 34
root # grep -E "Layout.minimumWidth.*units.gridUnit" /usr/share/plasma/plasmoids/org.kde.plasma.kickoff/contents/ui/FullRepresentation.qml
    Layout.minimumWidth: units.gridUnit * 26

Now, I was a bit fed up having to scroll up and down the launcher menu to see all fourteen entries in my Favourites list, so I decided to increase the height of the menu, which I did by editing /usr/share/plasma/plasmoids/org.kde.plasma.kickoff/contents/ui/FullRepresentation.qml as root user:

root # nano /usr/share/plasma/plasmoids/org.kde.plasma.kickoff/contents/ui/FullRepresentation.qml
root # grep -E "Layout.minimumHeight.*units.gridUnit" /usr/share/plasma/plasmoids/org.kde.plasma.kickoff/contents/ui/FullRepresentation.qml
    Layout.minimumHeight: units.gridUnit * 44

The only downside to this is that the file will be overwritten when the package kde-plasma/plasma-desktop is upgraded.

The following command would allow me to make sure the file contains the height value of ’44’ that I want:

root # sed -i '/Layout.minimumHeight: units.gridUnit/ c\    Layout.minimumHeight: units.gridUnit * 44' /usr/share/plasma/plasmoids/org.kde.plasma.kickoff/contents/ui/FullRepresentation.qml

Therefore, to automate the editing of the file in my Gentoo installations that use OpenRC I created a shell script /etc/local.d/50-set_Kickoff_height.start with the following contents:

#!/bin/bash
if [ -e /usr/share/plasma/plasmoids/org.kde.plasma.kickoff/contents/ui/FullRepresentation.qml ]; then
    sed -i '/Layout.minimumHeight: units.gridUnit/ c\    Layout.minimumHeight: units.gridUnit * 44' /usr/share/plasma/plasmoids/org.kde.plasma.kickoff/contents/ui/FullRepresentation.qml
fi

The FullRepresentation.qml file will then be edited every time the machine boots, which is a tad inefficient but not a big overhead.

This is not a perfect solution because the menu will revert to its default height following an upgrade to the package kde-plasma/plasma-desktop until I reboot the machine, but it is good enough for me.

How to stop inactive user sessions triggering Suspend to RAM in a single-seat, multi-user installation of Lubuntu 18.04

In my previous post I mentioned a problem that I had still not been able to fix in a single-seat, multi-user installation of Lubuntu 18.04: Xfce Power Manager in each user’s account can cause the installation to suspend to RAM if a user has not logged out of his/her session and another user is using a different session. Each user account in Lubuntu 18.04 has its own XfcePower Manager settings, stored in the file ~/.config/xfce4/xfconf/xfce-perchannel-xml/xfce4-power-manager.xml. If the property /xfce4-power-manager/inactivity-on-ac has a value of 15 (minutes) or higher, that session can cause the machine to suspend to RAM even if the session is not active while someone else’s session is active. The example below illustrates the effect.

Consider five users mick, christine, john, stevie and lindsey with the following settings for the number of minutes of inactivity that will trigger suspension to RAM:

mick@aspirexc600:~$ xfconf-query -c xfce4-power-manager -p /xfce4-power-manager/inactivity-on-ac
30
christine@aspirexc600:~$ xfconf-query -c xfce4-power-manager -p /xfce4-power-manager/inactivity-on-ac
25
john@aspirexc600:~$ xfconf-query -c xfce4-power-manager -p /xfce4-power-manager/inactivity-on-ac
45
stevie@aspirexc600:~$ xfconf-query -c xfce4-power-manager -p /xfce4-power-manager/inactivity-on-ac
15
lindsey@aspirexc600:~$ xfconf-query -c xfce4-power-manager -p /xfce4-power-manager/inactivity-on-ac
30

Now, suppose that john boots the machine, logs in to his account to check his e-mail, leaves the e-mail client open and goes off to grab lunch without logging out. Then stevie comes along and clicks on ‘Logout’ > ‘Switch User’ to display the LightDM greeter screen (or the greeter screen is already displayed because john‘s session has already been locked), logs in to her account and begins to use, say, LibreOffice Writer. Even though stevie is busy typing, the machine will suspend to RAM after 45 minutes of inactivity by john. This can be very annoying.

In addition to the individual users’ Xfce Power Manager configuration files in Lubuntu 18.04, I found the following Xfce Power Manager configuration files which appear to be system-wide:

/etc/xdg/xdg-Lubuntu/xfce4/xfconf/xfce-perchannel-xml/xfce4-power-manager.xml
/etc/xdg/xfce4/xfconf/xfce-perchannel-xml/xfce4-power-manager.xml

First attempt at fixing the problem

I asked all the users to configure their accounts to never cause the machine to suspend, by using the Xfce Power Manager settings GUI in their session and selecting ‘Never’. I noticed this caused each user’s /xfce4-power-manager/inactivity-on-ac property to become ‘14‘:

user $ xfconf-query -c xfce4-power-manager -p /xfce4-power-manager/inactivity-on-ac
14

Note that users must not edit their file ~/.config/xfce4/xfconf/xfce-perchannel-xml/xfce4-power-manager.xml; if they do, the settings shown in the Xfce Power Manager settings GUI will not be updated. Users must either use the Xfce Power Manager settings GUI or xfconf-query commands as explained on the askubuntu Web page ‘Change xfce4-power-manager option from terminal‘.

Then I edited the file /etc/xdg/xdg-Lubuntu/xfce4/xfconf/xfce-perchannel-xml/xfce4-power-manager.xml to make its contents the same as the previous contents of the individual users’ settings when Suspend to RAM was enabled individually):

<?xml version="1.0" encoding="UTF-8"?>

<channel name="xfce4-power-manager" version="1.0">
  <property name="xfce4-power-manager" type="empty">
    <property name="power-button-action" type="uint" value="3"/>
    <property name="show-tray-icon" type="bool" value="true"/>
    <property name="brightness-switch-restore-on-exit" type="int" value="1"/>
    <property name="brightness-switch" type="int" value="0"/>
    <property name="presentation-mode" type="bool" value="false"/>
    <property name="inactivity-on-ac" type="uint" value="30"/>
    <property name="blank-on-ac" type="int" value="10"/>
    <property name="dpms-on-ac-sleep" type="uint" value="0"/>
    <property name="dpms-on-ac-off" type="uint" value="0"/>
    <property name="brightness-on-ac" type="uint" value="9"/>
    <property name="lock-screen-suspend-hibernate" type="bool" value="true"/>
    <property name="logind-handle-lid-switch" type="bool" value="false"/>
    <property name="dpms-enabled" type="bool" value="false"/>
    <property name="general-notification" type="bool" value="true"/>
    <property name="sleep-button-action" type="uint" value="0"/>
    <property name="hibernate-button-action" type="uint" value="0"/>
  </property>
</channel>

After rebooting, leaving one or more users logged in without any activity did not cause the installation to suspend to RAM after 30 minutes of no activity in any session.

So I then edited the file /etc/xdg/xfce4/xfconf/xfce-perchannel-xml/xfce4-power-manager.xml to make its contents the same as the above. After rebooting, leaving one or more users logged in without any activity did not cause the installation to suspend to RAM after 30 minutes of no activity in any session.

I therefore assume that the above two files are ignored by Xfce Power Manager during normal operation.

Second attempt at fixing the problem

I used the procedure given in the Xfce4-power-manager FAQs to check if Xfce Power Manager in Lubuntu 18.04 uses systemd-logind to suspend the installation, and indeed it does:

TRACE[xfpm-polkit.c:366] xfpm_polkit_check_auth_intern(): Action=org.freedesktop.login1.suspend is authorized=TRUE

Therefore I edited /etc/systemd/logind.conf to add IdleAction=suspend and IdleActionSec=30min, and rebooted. However, this had no discernable effect either. Leaving one or more users logged in without any activity did not cause the installation to suspend to RAM after 30 minutes of no activity in any session.

Third attempt (successful) at fixing the problem

So, what to do?! In my previous post I explained how I had fixed the problem of not being able to suspend to RAM automatically from the LightDM greeter screen. I decided to keep the scripts from that post and add a new script sessions_sleep.sh to the root crontab. The contents of all the files and the crontab are shown below.

user $ cd /etc/lightdm/lightdm.conf.d/
user $ cat 10_lubuntu.conf 
[Seat:*]
greeter-setup-script=/etc/lightdm/lightdm.conf.d/lightdm_sleep.sh
session-setup-script=/etc/lightdm/lightdm.conf.d/lightdm_kill_sleep.sh
session-cleanup-script=/etc/lightdm/lightdm.conf.d/unmount_FREECOM_HDD.sh
user $ ls
05_lubuntu.conf  lightdm_kill_sleep.sh       sessions_sleep.sh
10_lubuntu.conf  lightdm_sleep.sh            unmount_FREECOM_HDD.sh
user $ cat lightdm_sleep.sh 
#!/bin/bash
# This forms part of the scheme to provide automatic suspension while the greeter screen is displayed
file="/tmp/unique_identifier"
(while true; do sleep 30m; systemctl suspend; done) &
echo $! > $file
user $ cat lightdm_kill_sleep.sh
#!/bin/bash
# This forms part of the scheme to provide automatic suspension while the greeter screen is displayed
file="/tmp/unique_identifier"
if [ -f "$file" ]; then
    kill `cat $file`
    rm $file
fi
user $ tail -n 11 unmount_FREECOM_HDD.sh
#----------------------------------------------------------------------------------------------------
#
# This forms part of the scheme to provide automatic suspension while the greeter screen is displayed
file="/tmp/unique_identifier"
if [ -f "$file" ]; then
    kill `cat $file`
    rm $file
fi
#
#----------------------------------------------------------------------------------------------------
exit 0

If the machine did not already have a permanently-connected external USB HDD (LABEL=”FREECOM HDD”) then it would have sufficed to specify a script named, for example, lightdm_kill_sleep2.sh instead of unmount_FREECOM_HDD.sh:

user $ cat 10_lubuntu.conf 
[Seat:*]
greeter-setup-script=/etc/lightdm/lightdm.conf.d/lightdm_sleep.sh
session-setup-script=/etc/lightdm/lightdm.conf.d/lightdm_kill_sleep.sh
session-cleanup-script=/etc/lightdm/lightdm.conf.d/lightdm_kill_sleep2.sh
user $ cat lightdm_kill_sleep2.sh
#!/bin/bash
# This forms part of the scheme to provide automatic suspension while the greeter screen is displayed
file="/tmp/unique_identifier"
if [ -f "$file" ]; then
    kill `cat $file`
    rm $file
fi
user $ sudo nano sessions_sleep.sh
user $ sudo chmod +x sessions_sleep.sh
user $ cat sessions_sleep.sh 
#!/bin/bash
date +%s > /tmp/datetime_suspended # Initialise variable
while true
do
    # Only monitor idle time and suspend after specified inactivity if lightdm_sleep.sh is not taking care of those
    if [[ `ps -ef | grep bash | grep lightdm_sleep.sh | wc -l` -eq 0 ]]; then
        #-------------------------------STAGE 1: FIND OUT WHO IS THE ACTIVE USER--------------------------------------
        #
        while IFS=: read -r f1 f2 f3 f4 f5 f6 f7
        # $f1 is username
        # $f2 is password ('x')
        # $f3 is UID
        # $f4 is GID
        # $f5 is UID info
        # $f6 is home directory
        # $f7 is command/shell
        do
            if [[ $f6 == *"/home/"* ]] && [[ $f7 == "/bin/bash" ]]; then
                if `loginctl list-users | grep -ve '^$\|USER\|listed' | awk -F' ' '{print $2}' | grep -q $f1`; then
                    state=`loginctl show-user $f3 | grep State | awk -F'=' '{print $2}'`
                    if [[ $state != "active" ]]; then
                        inactive_user=$f1
                    elif [[ $state == "active" ]]; then
                        active_user=$f1
                    fi
                fi
            fi
        done < /etc/passwd
        #
        #-------------------------------STAGE 2: ASCERTAIN USER SESSIONS---------------------------------------------
        #
        # Find idle time for each X Windows session and suspend to RAM if the active user has been idle for >=30min.
        #
        who -u | grep -v "\." > /tmp/logged-in_users
        #
        while read a b c d e f g
        # $a is username
        # $b is the tty (tty1 to tty12)
        # $c is the date (yyyy-mm-dd)
        # $d is the time (hh:mm)
        # $e is the idle time (hh:mm) which does not reflect reality in this installation, for some reason
        # $f is the PID
        # $g is the display e.g. "(:1)"
        # Example: "john     tty7         2019-08-31 17:08 00:01        1624 (:0)"
        do
            if [[ $(echo $b | sed 's/[^0-9]*//g') -gt 6 ]]; then
                display=$(echo $g | sed 's/[^0-9]*//g')
                idle_millisecs=$(env DISPLAY=:$display sudo -u $a xprintidle)
                let idle_minutes=$idle_millisecs/60000
                if [[ $idle_minutes -ge 30 ]] && [[ $a == "$active_user" ]]; then
                    datetime_now=$(date +%s)
                    diffsecs=$(expr $datetime_now - $(cat /tmp/datetime_suspended))
                    # Prevent suspending immediately after resuming
                    if [ $diffsecs -gt 180 ]; then
                        date +%s > /tmp/datetime_suspended
                        systemctl suspend
                    fi
                fi
            fi
        done < /tmp/logged-in_users
        rm /tmp/logged-in_users
        #
        #------------------------------------------------------------------------------------------------------------
        sleep 10 # Frequency to repeat check
    fi
done

I installed the utility xprintidle via the Linux distribution’s package manager. As the name of the utility suggests, it returns the time (in milliseconds) that an X Windows session has been idle. Nice utility, by the way.

user $ sudo crontab -e
user $ sudo crontab -l | grep -v ^#
@reboot sudo /etc/lightdm/lightdm.conf.d/sessions_sleep.sh

Note that, despite its name, ‘@reboot‘ in the cron job will run the script after a cold boot as well as after a warm boot (reboot). Also note that the use of ‘sudo‘ in the root cron job is not an error; it makes the root cron job use the root user’s environment variables.

Remember that the property /xfce4-power-manager/inactivity-on-ac has to be configured to have a value of 14 (which corresponds to ‘Never’ in the Xfce Power Manager settings GUI) for every user. This should be done by each user using the Xfce Power Manager settings GUI in their own session.

Basically, the scheme works as follows: At boot, Lubuntu 18.04 launches the looping Bash script sessions_sleep.sh, which remains running but does nothing because no X Windows users are logged in. When LightDM runs the greeter-setup-script (lightdm_sleep.sh) and displays the greeter screen, sessions_sleep.sh still does nothing while lightdm_sleep.sh is running and taking care of managing suspension. When an X Windows user logs in and LightDM’s session-setup-script (lightdm_kill_sleep.sh) kills the running script lightdm_sleep.sh, the script sessions_sleep.sh then takes over monitoring users’ activity in X Windows and triggers suspension if the active user has not used his/her session for 30 minutes. If an X Windows user logs out, LightDM’s session-cleanup-script (unmount_FREECOM_HDD.sh) also kills lightdm_sleep.sh if it is running. When LightDM again runs its greeter-setup-script (lightdm_sleep.sh) and displays the greeter screen, that again inhibits sessions_sleep.sh from taking any action if no X Windows user is logged in. This all sounds convoluted, but it seems to work fine so far.

Because Xfce Power Manager is no longer used to monitor idle time and trigger suspension, ‘Presentation mode’ in Xfce Power Manager can no longer prevent the system from suspending after 30 minutes of inactivity while someone is watching a long video or playing music, for example. However this is not a problem; to temporarily inhibit suspension the user can use the method given in my earlier post ‘How to move a mouse pointer automatically in Linux to simulate user activity‘.

The Lubuntu 18.04 architecture

I suspect most Lubuntu 18.04 installations are on laptops or desktop machines with a single user, i.e. single-seat, single-user installations. In such a case, unless the user has created multiple user accounts that he/she logs into concurrently (by using ‘Switch User’, for example), the machine will never suspend unexpectedly while the user is logged in and using the session. I think the way LightDM, light-locker, systemd-logind and Xfce Power Manager have been bundled in Lubuntu 18.04 to manage suspending to RAM is a dog’s breakfast. The design apparently does not take into consideration that different people could be logged in concurrently in a single-seat installation. Try forcing people to log off so that only one person is ever logged in — it won’t happen! To be interrupted by Suspend to RAM triggered by Xfce Power Manager due to inactivity in a different session is illogical; the system should not suspend when someone is actively using the system. Therefore, in my opinion, management of suspension (and hibernation) ought to be configured and managed system-wide, not on a per-user basis, and a design should not require users to hack the installation to the extent I have described above. I was ‘scratching an itch’, but users should not have to jump through hoops to get an installation to function in a sensible manner. For all I know there may be a simpler way of achieving the functionality in Lubuntu 18.04 that I have described in this post and my previous post, but, if there is, it is not obvious. LightDM, light-locker, systemd and Xfce Power Manager are developed by different people, and functionality such as suspension and hibernation does not seem to have been considered using ‘helicopter vision’. Designing disparate applications developed separately to work together holistically is not a trivial task.

Anyway, hopefully I have fixed the problem and also ‘scratched my itch’. No more unexpected suspensions while I am using the family desktop machine!

How to make LightDM suspend to RAM automatically from the login screen and lock screen in Lubuntu 18.04

My family’s desktop machine has Lubuntu 18.04 installed, which generally works well. Each family member has their own account, therefore the installation is a single-seat, multi-user system. Lubuntu 18.04 uses LightDM for the display manager, light-locker (which uses LightDM) for the screen locker, and Xfce Power Manager for power management. Xfce Power Manager enables each user to specify for their session that the machine will suspend to RAM, and to configure the duration of inactivity in their session that will trigger suspension.

However, a couple of things about this arrangement are annoying. Firstly, if two or more users happen to be logged-in simultaneously because a family member does not bother to log out, Xfce Power Manager in an inactive session will eventually suspend the machine even when another user is actively using a different session. Secondly, if nobody is logged-in and the LightDM greeter screen is displayed, the machine will not suspend to RAM automatically after a period of inactivity. The only way to get the machine to suspend to RAM if nobody is logged-in is to click on the power indicator in the greeter’s system tray and select ‘Suspend’ from the drop-down menu.

I still have not figured out how to fix the first of the above-mentioned problems, but a Web search finally turned up a fix for the second problem: a post by Linux user boyi in Arch Linux Forums thread ‘need lightdm to suspend system‘. Below I explain how I implemented this in my family’s Lubuntu 18.04 installation. Basically, when the LightDM greeter screen is displayed LightDM runs a looping shell script (lightdm_sleep.sh) that will suspend the machine after a specified time has elapsed, and either logging in or unlocking the screen will run another shell script (lightdm_kill_sleep.sh) that kills the first script. Once a user has either logged in or unlocked the screen, Xfce Power Manager in that user’s session takes over monitoring activity.

1. Pre-existing situation
When I originally installed Lubuntu 18.04 I made sure each user used the Xfce Power Manager GUI to configure suspension to RAM. Each user’s own settings are shown below:

user $ cat ~/.config/xfce4/xfconf/xfce-perchannel-xml/xfce4-power-manager.xml
<?xml version="1.0" encoding="UTF-8"?>

<channel name="xfce4-power-manager" version="1.0">
  <property name="xfce4-power-manager" type="empty">
    <property name="power-button-action" type="uint" value="3"/>
    <property name="show-tray-icon" type="bool" value="true"/>
    <property name="brightness-switch-restore-on-exit" type="int" value="1"/>
    <property name="brightness-switch" type="int" value="0"/>
    <property name="presentation-mode" type="bool" value="false"/>
    <property name="inactivity-on-ac" type="uint" value="30"/>
    <property name="blank-on-ac" type="int" value="10"/>
    <property name="dpms-on-ac-sleep" type="uint" value="0"/>
    <property name="dpms-on-ac-off" type="uint" value="0"/>
    <property name="brightness-on-ac" type="uint" value="9"/>
    <property name="lock-screen-suspend-hibernate" type="bool" value="true"/>
    <property name="logind-handle-lid-switch" type="bool" value="false"/>
    <property name="dpms-enabled" type="bool" value="false"/>
    <property name="general-notification" type="bool" value="true"/>
    <property name="sleep-button-action" type="uint" value="3"/>
    <property name="hibernate-button-action" type="uint" value="3"/>
  </property>
</channel>

Note that each user must not edit their file ~/.config/xfce4/xfconf/xfce-perchannel-xml/xfce4-power-manager.xml directly, as the properties in the Xfce Power Manager settings GUI will not be updated if they do. Users must either use the Xfce Power Manager settings GUI or use xfconf-query commands on the command line as explained on the askubuntu Web page ‘Change xfce4-power-manager option from terminal‘.

The LightDM configuration files in /etc/lightdm/lightdm.conf.d/ were as follows:

user $ ls /etc/lightdm/lightdm.conf.d/
05_lubuntu.conf  10_lubuntu.conf  unmount_FREECOM_HDD.sh

The file 05_lubuntu.conf was installed when I installed Lubuntu 18.04. The two files 10_lubuntu.conf and unmount_FREECOM_HDD.sh were previously created by me in order to unmount a permanently-connected external USB HDD when a user logs out, to avoid an access problem when another user logs in (see an earlier blog post).

user $ cat /etc/lightdm/lightdm.conf.d/10_lubuntu.conf
[Seat:*]
session-cleanup-script=/etc/lightdm/lightdm.conf.d/unmount_FREECOM_HDD.sh

2. Modifications to enable installation to suspend when no user is logged in

2.1 Specify the scripts

user $ cd /etc/lightdm/lightdm.conf.d/
user $ sudo nano 10_lubuntu.conf
user $ cat 10_lubuntu.conf 
[Seat:*]
greeter-setup-script =/etc/lightdm/lightdm.conf.d/lightdm_sleep.sh
session-setup-script=/etc/lightdm/lightdm.conf.d/lightdm_kill_sleep.sh
session-cleanup-script=/etc/lightdm/lightdm.conf.d/unmount_FREECOM_HDD.sh

2.2 Create the two scripts

user $ sudo nano lightdm_sleep.sh
user $ sudo chmod +x lightdm_sleep.sh
user $ cat lightdm_sleep.sh 
#!/bin/sh
file="/tmp/unique_identifier"
(while true; do sleep 30m; systemctl suspend; done) &
echo $! > $file
user $ sudo nano lightdm_kill_sleep.sh
user $ sudo chmod +x lightdm_kill_sleep.sh
user $ cat lightdm_kill_sleep.sh 
#!/bin/sh
file="/tmp/unique_identifier"
if [ -f "$file" ]
then
    kill `cat $file`
    rm $file
fi

2.3 Modify the existing session cleanup script to include the lightdm_kill_sleep.sh code

user $ tail -n 11 unmount_FREECOM_HDD.sh
################################################
# Code below copied from lightdm_kill_sleep.sh
file="/tmp/unique_identifier"
if [ -f "$file" ]
then
    kill `cat $file`
    rm $file
fi
# End of code copied from lightdm_kill_sleep.sh
################################################
exit 0

Of course, specifying lightdm_kill_sleep.sh would have sufficed if the installation did not already have a session cleanup script:

user $ cat 10_lubuntu.conf
[Seat:*]
greeter-setup-script =/etc/lightdm/lightdm.conf.d/lightdm_sleep.sh
session-setup-script=/etc/lightdm/lightdm.conf.d/lightdm_kill_sleep.sh
session-cleanup-script=/etc/lightdm/lightdm.conf.d/lightdm_kill_sleep.sh

3. Summary
Thus the file situation is now as follows:

user $ ls /etc/lightdm/lightdm.conf.d/
05_lubuntu.conf  10_lubuntu.conf  lightdm_kill_sleep.sh  lightdm_sleep.sh  unmount_FREECOM_HDD.sh
  • If nobody logs in after booting the machine, the machine will suspend to RAM after 30 minutes* due to greeter-setup-script.
  • If a user logs in, session-setup-script ensures only Xfce Power Manager controls suspension to RAM while the LightDM greeter screen is not displayed.
  • If a user logs out and no other user is logged in, the machine will suspend to RAM after 30 minutes* due to greeter-setup-script.
  • If a user locks the screen, the machine will suspend to RAM after 30 minutes* due to greeter-setup-script.

*Obviously the period of inactivity to trigger suspension to RAM can be configured by changing the time specified in the lightdm_sleep.sh script.