Prevent Lubuntu 22.04 (SDDM and LXQt) from leaving an external drive mounted incorrectly for other users

An external USB disk drive is connected permanently to my family’s PC that currently has Lubuntu 22.04 installed. There are several user accounts on this multi-user single-seat machine. If a user does not unmount the external drive before logging out, when another user logs in, the external drive is still mounted with the priviledges of the previous user. In the days when Lubuntu used LightDM and LXDE, I devised a scheme to unmount automatically the external drive when each user logged out (see an earlier post). However, that method is no longer possible now Lubuntu no longer uses LightDM. Therefore I devised a different scheme. This time, the external drive is unmounted automatically every time a user logs in, and udisks2 automatically mounts it for the current user. Below I explain the new scheme. In the example below the external USB drive happens to be an NTFS drive and have the label ‘FREECOM HDD’. The machine actually has several user accounts but, for the sake of brevity, the commands for only two users are shown. All the steps shown below are performed by the account holder with the ability to use the sudo command to get superuser priviledges.

1. Create a root script to unmount the external USB drive

$ sudo nano /usr/local/sbin/unmount_FREECOM_HDD.sh

The file contains the following lines:

#!/bin/bash
# Unmount the external USB HDD if mounted with another username in the path:
umount /media/*/FREECOM\ HDD 2>/dev/null

2. Make sure the script has the correct priviledges

$ sudo chmod 755 /usr/local/sbin/unmount_FREECOM_HDD.sh

3. Give each user permission to use the sudo command to run the abovementioned script

$ sudo visudo -f /etc/sudoers.d/unmount_FREECOM_HDD

Add a line for each user:

fitzcarraldo ALL=NOPASSWD: /usr/local/sbin/unmount_FREECOM_HDD.sh
molly ALL=NOPASSWD: /usr/local/sbin/unmount_FREECOM_HDD.sh

4. Create an autostart Desktop Configuration File for each user

$ sudo nano "/home/fitzcarraldo/.config/autostart/Unmount FREECOM HDD.desktop"
$ sudo nano "/home/molly/.config/autostart/Unmount FREECOM HDD.desktop"

Each user’s autostart file should contain the following:

[Desktop Entry]
Exec=sudo /usr/local/sbin/unmount_FREECOM_HDD.sh
Name=Unmount FREECOM HDD
OnlyShowIn=LXQt;
Type=Application
Version=1.0

Make sure the autostart files have the correct ownership and priviledges

$ sudo chown fitzcarraldo:fitzcarraldo "/home/fitzcarraldo/.config/autostart/Unmount FREECOM HDD.desktop"
$ sudo chmod 664 "/home/fitzcarraldo/.config/autostart/Unmount FREECOM HDD.desktop"
$ sudo chown molly:molly "/home/molly/.config/autostart/Unmount FREECOM HDD.desktop"
$ sudo chmod 664 "/home/molly/.config/autostart/Unmount FREECOM HDD.desktop"

(If each user logs in to their LXQt Desktop, clicks on the Application Menu icon and selects ‘Preferences’ > ‘LXQt Settings’ > ‘Session Settings’ and clicks on Autostart’, there should be the ticked entry ‘Unmount FREECOM HDD’ in the ‘LXQt Autostart’ section.)

5. Reboot and the scheme should take effect.

Advertisement

Using GeckoLinux to resurrect my old nettop

Clementine music player in GeckoLinux LXQt on my 43-inch TV screen

Clementine music player in GeckoLinux LXQt on my 43-inch TV screen.

 
 
Background/History

Back in early 2010, when nettops were the latest thing, I bought an ASRock ION 330HT nettop, billed as an ‘HTPC‘ (Home Theatre PC):

  • CPU: Intel Atom 330 1.6GHz (Dual core)
  • Memory: Supports DDR2 800MHz, 2 x SO-DIMM slots, default 2GB (2 x 1GB), maximum up to 4GB (due to the CPU limitation, the actual memory size available to the OS may be less than 4GB).
  • Chipset: NVIDIA ION graphics processor
  • Graphics: NVIDIA ION Graphics, supports DX10 / HD 1080p playback
  • Audio: 7.1 CH HD Audio with DTS
  • HDD: 320GB 2.5″ HDD, capable of supporting RAID 0 and 1 by adopting a second 2.5″ HDD
  • ODD: DVD Super Multi
  • LAN: Gigabit Ethernet
  • WLAN: 802.11b/g/n Wi-Fi
  • Rear I/O: 1 x HDMI, 1 x D-Sub VGA, 6 x USB 2.0, 1 x S/PDIF, 1 x powered eSATA/USB (For powered eSATA function, Hot Plug function is supported in RAID / AHCI mode only. IDE mode does not support Hot Plug function.)
  • Remote Controller: MCE remote controller
  • External Power Unit: 65W/19V adapter
  • Firmware: PC BIOS (no UEFI)

In 2010 so-called smart TVs were not really that smart. I still had a large Sony Trinitron TV with a CRT, and I wanted to see if I could use the nettop with it. I bought a DVB-T USB adapter to enable the nettop to access digital terrestrial television, and I installed XBMC (now called KODI). I installed the now-defunct Sabayon Linux, and had a hell of a job getting ASRock’s CIR [Windows] MCE (Media Center Edition) remote to work. ASRock only released a driver (lirc_wb677) for the Nuvoton w836x7hg CIR chip in the nettop for Ubuntu 9.10, 10.04 and 10.10, and I had to patch it to get it to work with LIRC in Sabayon Linux. Later that year developer Jarrod Wilson released the first version of a new driver named nuvoton-cir for the Nuvoton w836x7hg chip, and in 2011 I had another struggle to get that working with LIRC and XBMC in Sabayon Linux.

To be able to use the DVB-T USB adapter I installed Tvheadend in Sabayon Linux, which worked well, although the adapter needed to be connected to the house TV aerial in order to provide good reception, i.e. the small indoor aerial supplied with the DVB-T adapter was next to useless.

I bought a VGA-to-Composite Video converter to connect the nettop’s D-Sub VGA socket to the TV’s composite video input. The Linux Desktop displayed on the CRT TV screen was OK-ish but, as you would expect, not comparable to the display on a TFT monitor.

Basically, I was not satisfied with the result, and the nettop went back into its box after very little use. I did get it out briefly in 2016 to upgrade the 2GB RAM (two 1GB modules) to the maximum allowable 4GB (two 2GB modules) in case I might want to use the nettop in future. With two 2GB RAM modules the nettop detects 3327MB of RAM, which limits what can be done with it.

When ‘proper’ smart TVs came onto the market, there was no longer any incentive to use an HTPC; everything and more that a nettop HTPC did could be done by a smart TV. In 2015 I succumbed and bought an LG smart TV, added a USB 1TB HDD, connected my DVD player to the TV and forgot about the nettop. The LG TV developed a fault three years later. I fixed it but its lack of catch-up TV apps for some of the main TV stations became irritating so, three years ago, I bought a new TV. The media player on the TV (a FINLUX TV) cannot play FLAC music files, and the Web browser is very slow with a buggy UI, so I began thinking about resurrecting the ASRock nettop in order to be able to browse the Web properly on my TV and to play my music flles through the TV’s sound bar. I finally got around to doing this recently, so here is the story…
 
 
Connections

I have a Rii i8 mini wireless keyboard which I used with my smart TV, so I connected its lead with USB wireless receiver pigtail and micro-USB charging plug pigtail to one of the USB ports on the back of the nettop. The lead is long enough to enable the USB wireless receiver (about the size of a USB Type A plug) to sit between the sound bar and the TV stand. The micro-USB charging plug pigtail lies out of sight on the TV stand behind the sound bar, ready to charge the mini keyboard when needed. Excellent wireless mini keyboard with touchpad, by the way.

The nettop is connected to the TV by an HDMI cable. The sound bar is connected to the TV by a 3.5mm jack plug cable, and connected to the nettop by an S/PDIF (optical) cable. I use the sound bar’s remote to switch easily between TV audio and nettop audio.
 
 
Finding a suitable Linux distribution

Given the limitations of the nettop’s CPU and memory, I wanted to install a distribution with a lightweight Desktop Environment. I like LXQt, so that would have been my choice if possible. Gentoo Linux is installed on my laptops, and Lubuntu 21.04 on my family’s desktop. LXQt is available for Gentoo Linux but I would not dream of installing Gentoo Linux on a relatively slow nettop with less than 4GB accessible, but Lubuntu seemed a good candidate. Therefore I created a Live USB pendrive with Lubuntu 21.10, which booted fine on all my other machines (including a legacy machine with PC BIOS only, not UEFI) but would not boot on the ASRock nettop. It would get as far as the GRUB menu then stall. So I tried Mageia (the Xfce release, as there is no Live LXQt release), but the result was similar. So then I tried PCLinuxOS (also the Xfce release, as there is no Live LXQt release), and that did install and run nicely (although the edges of the file manager’s windows were thick dashed lines). Everything worked well until I selected Suspend and tried to Resume, which resulted in the following messages on the screen and the nettop hung:

[ 1774.594461] IRQ 26: no longer affine to CPU1
[ 1774.602213] IRQ 16: no longer affine to CPU3
[ 1774.602227] IRQ 18: no longer affine to CPU3
[ 1774.613499] TSC synchronization [CPU#0 -> CPU#1]:
[ 1774.613504] Measured 377387956 cycles TSC warp between CPUs, turning off TSC clock.
[ 1774.613552] TSC found unstable after boot, most likely due to broken BIOS. Use 'tsc=unstable'.
[ 1774.609000] clocksource: Checking clocksource tsc synchronization from CPU 1 to CPUs 0.
[ 1774.609000] clocksource:         CPUs 0 ahead of CPU 1 for clocksource tsc.
[ 1774.609000] clocksource:         CPU 1 check durations 6592ns - 6592ns for clocksource tsc.
_

I could get rid of the clock-related messages by adding ‘tsc=unstable‘ to the kernel boot line in /boot/grub/grub.cfg, but I could not get rid of the ‘no longer affine’ messages and the hanging every time the nettop resumed from suspension. I wondered if the BIOS was to blame, so I downloaded onto a FAT32-formatted USB pendrive the latest version (1.2) of the 330HT BIOS from the ASRock Web site and installed it on the nettop (easy: press F6 at boot), but the problem remained. I began to wonder it any modern Linux release would work on this nettop.

So it was time to try another distribution. My searches on DistroWatch showed that GeckoLinux (“a Linux spin based on the openSUSE distribution, with a focus on polish and out-of-the-box usability on the desktop” according to its Web site) has static and rolling editions based on openSUSE Leap and openSUSE Tumbleweed respectively, and has many Desktop Environment releases, including LXQt. The availability of LXQt attracted my attention, but I was also curious to try openSUSE and the Btrfs file system. I did try openSUSE briefly many years ago (possibly more than a decade), but I have never used Btrfs. So I decided it was worth a shot.

I downloaded the latest available ISO for GeckoLinux ROLLING LXQt and used dd on one of my Linux machines to create a bootable USB pendrive:

user $ sudo blkid # Find out which device is the pen drive
user $ sudo dd if=/home/fitzcarraldo/Downloads/GeckoLinux_ROLLING_LXQt.x86_64-999.220105.0.iso of=/dev/sdd bs=4M status=progress && sync

I booted the pendrive on the nettop and launched the GeckoLinux installer, which had no trouble installing the OS on the nettop’s HDD. Further on I point out a couple of minor niggles I found with the application menu but, by and large, I find GeckoLinux Rolling LXQt provides a good, polished user interface and experience.
 
 
Setting up auto login and disabling a lock screen

LXQt Desktop in GeckoLinux LXQt on my 43-inch TV screen

LXQt Desktop in GeckoLinux LXQt on my 43-inch TV screen.

I found that, in order to get auto login working correctly in the installation, I needed to specify a user password during installation and then set up auto login after booting into the new installation:

‘Application Menu’ > ‘Preferences’ > ‘YaST User and Group Management’

  1. Select the user and click on ‘Expert Options’
  2. Select ‘Login Settings’
  3. Tick ‘Auto Login’
  4. Tick ‘Passwordless Logins’

One needs to be a little careful not to end up with both light-locker and XScreenSaver providing lock screens. I wanted only a screensaver and no locking of the user session after a period of inactivity. Any press of a key or tap of the touchpad on my Rii i8 mini wireless keyboard will simply stop the screensaver animation and then display the Desktop.

‘Application Menu’ > ‘Preferences’ > ‘LXQt Settings’ > ‘Session Settings’

  1. In ‘Basic Settings’, untick ‘Lock screen before suspending/hibernating’.
  2. In ‘Autostart’, ‘XScreenSaver’ under ‘LXQt Autostart’ needs to be ticked.

‘Application Menu’ > ‘Preferences’ > ‘Screensaver’

If a window appears informing you that the XScreenSaver daemon is not running and offering to launch it, click ‘OK’.

  1. Select ‘Mode: Only One Screen Saver’.
  2. Select a screensaver animation (I use ‘GL Matrix’).

‘Application Menu’ > ‘System Tools’ > ‘dconf Editor’

Configure the following settings for light-locker:

idle-hint false
late-locking false
lock-after-screensaver 0
lock-on-lid false
lock-on-suspend false

‘Application Menu’ > ‘Preferences’ > ‘LXQt Settings’ > ‘Power Management’

Untick ‘Enable Battery Watcher’, ‘Enable Lid Watcher’ and ‘Enable Idleness Watcher’ on the respective tabs.
 
 
Setting the hostname

I set a static hostname (I opted for ‘ion330ht’) by selecting ‘Application Menu’ > ‘Preferences’ > ‘YaST Network’ and entering the hostname on the ‘Hostname/DNS’ tab.
 
 
Package Management

Both the YaST Software Management GUI and the zypper command are new to me, so I still have a lot to learn.

The main package repositories were already added, but to learn how to add other repositories manually see the following articles:

Anyway, these are the repositories currently in use on this nettop:

ion330ht:/home/fitzcarraldo # zypper repos
Repository priorities in effect:                                      (See 'zypper lr -P' for details)
      90 (raised priority)  :  1 repository
      97 (raised priority)  :  1 repository
      98 (raised priority)  :  2 repositories
      99 (default priority) :  4 repositories
     115 (lowered priority) :  4 repositories

#  | Alias                                | Name                   | Enabled | GPG Check | Refresh
---+--------------------------------------+------------------------+---------+-----------+--------
 1 | Google-chrome                        | Google-chrome          | Yes     | (r ) Yes  | Yes
 2 | Google-talkplugin                    | Google-talkplugin      | Yes     | (r ) Yes  | Yes
 3 | Nvidia                               | Nvidia                 | Yes     | (r ) Yes  | Yes
 4 | Packman_Tumbleweed                   | Packman_Tumbleweed     | Yes     | (r ) Yes  | Yes
 5 | Tumbleweed_OSS                       | Tumbleweed_OSS         | Yes     | (r ) Yes  | Yes
 6 | Tumbleweed_OSS-updates               | Tumbleweed_OSS-updates | Yes     | (r ) Yes  | Yes
 7 | Tumbleweed_non-OSS                   | Tumbleweed_non-OSS     | Yes     | (r ) Yes  | Yes
 8 | http-download.opensuse.org-f6f93dd3  | openSUSE:Tumbleweed    | Yes     | (r ) Yes  | Yes
 9 | http-opensuse-guide.org-a78c9b99     | libdvdcss repository   | Yes     | (r ) Yes  | Yes
10 | https-download.opensuse.org-96367b31 | network:im:signal      | Yes     | (r ) Yes  | Yes
11 | https-download.opensuse.org-a5f414ff | openSUSE:Tumbleweed    | Yes     | (r ) Yes  | Yes
12 | skype-stable                         | Skype-stable           | Yes     | (  ) No   | Yes

Repositories 8 to 11 in the above list were added when I used ‘1 Cick Install’ on an openSUSE Software Web page for a specific package.

Most of what I needed was already installed, and I installed a few other packages using either the YaST Software Management GUI, the zypper command or ‘1 Click Install’:

● To be able to use the locate command to search for specific files:

   mlocate

● To be able to configure the LXQt Keyboard State Indicator on the Panel to display the flag of the keyboard language:

   iso-country-flags-png

● I was not sure if online updates would be advisable, but it looked potentially useful:

   yast2-online-update-configuration

● Some Web sites are not displayed correctly in Firefox, and I use Google’s Chrome browser for those:

   google-chrome-stable

● The Clementine music player (already installed) has the ability to display visualisations using projectM:

   projectM
   projectM-data

However, I could not get projectM to load its visualisation files, but I need to tinker more with it.

● I wanted to implement my scheme to scan automatically any files downloaded into the ~/Downloads/ directory (see my 2017 post), so I installed the following packages:

   clamav
   kdialog
   inotify-tools
   acl

(See further down for the addtional steps I took in order to get my scheme to work in GeckoLinux/openSUSE.)

● A GUI front-end to ClamAV in case I wanted to scan any files or directories manually:

   clamtk

● Although not essential, I installed the package monitoring-plugins-clamav in case I wanted to use it to check if the virus signatures are current, although my Bash script in a 2021 post serves the same purpose.

● To provide the commands dig, host and nslookup in case I need them in future:

   bind-utils

● To provide the man command and pages from the Linux Documentation Project:

   man-pages
   man

● To enable me to specify the window colour and size etc. in xterm, if I wish:

   xtermset

● To provide a GUI utility to show the amount of used and unused space in each partition:

   filelight

● Various multimedia codecs were already installed, but I had to install the package libdvdcss2 in order to be able to play commercial DVDs, as VLC would not play them. I installed it by using ‘1 Click Install’:

https://software.opensuse.org/package/libdvdcss2

● I use Signal Messenger, so I installed the package signal-messenger by using ‘1 Click Install’:

https://software.opensuse.org/package/signal-desktop

However, a subsequent rolling update flagged a dependency conflict requiring it to be uninstalled.

● To enable machines running Windows to browse SMB shares in File Explorer I installed the WS-Discovery daemon:

   wsdd

● To be able to edit tags in my music files:

   kid3-qt

● To be able to copy characters not available on the keyboard:

   kcharselect

● To install ir-keytable, *.toml files and 70-infrared.rules so that I could try to get the ASRock CIR MCE remote working using the in-kernel support for IR decoders, instead of LIRC:

   v4l-utils

● I no longer use KODI but I wanted to see if I could get the ASRock CIR MCE remote to control it using the in-kernel support for IR decoders instead of LIRC:

   kodi

● I prefer SMPlayer to VLC (which came installed in GeckoLinux Rolling LXQt):

   smplayer

● To be able to edit .mkv files, e.g. to change the default audio language etc.:

   mkvtoolnix
   mkvtoolnix-gui
 
 
Web Service Discovery host daemon (wsdd)

Having installed the package wsdd by using ‘Preferences’ > ‘YaST Software Management’ I performed the following steps as root user:

ion330ht:/home/fitzcarraldo # systemctl enable wsdd

I edited /etc/systemd/system/multi-user.target.wants/wsdd.service and added ‘--workgroup=HOME‘ to the ExecStart line, as my Windows workgroup is HOME rather than WORKGROUP:

ExecStart=/usr/sbin/wsdd --shortlog --workgroup=HOME -c /run/wsdd $WSDD_ARGS

ion330ht:/home/fitzcarraldo # systemctl daemon-reload
ion330ht:/home/fitzcarraldo # systemctl start wsdd

Although not necessary (and nothing to do with wsdd on the nettop), I performed the steps given in my 2020 blog post ‘A Linux command-line utility to discover and list WSD-enabled computers and printers on a home network‘. Works a treat.
 
 
SMB

This SMB configuration is for my home network that uses Broadcast NetBIOS Name Resolution, SMB and WS-Discovery. See the following posts (and all the comments on each, some of which contain important updates) for an explanation of how I set these up, making it relatively straightforward to add a device that uses the SMB protocol and enable it to browse shares on the other machines, and vice versa.

Note also that the smb, nmb and wsdd services must be running (see the next section).

I used the command ‘ip address‘ to find out the names of the wired and wireless interfaces, then I edited the file /etc/samba/smb.conf to contain the following (the Workgroup name in my home network is ‘HOME’ rather than the usual default of ‘WORKGROUP’):

[global]
;no need to specify 'smb ports' as ports 139 & 445 used by default
workgroup = HOME
netbios name = ion330ht
case sensitive = no
browseable = yes

;If this machine becomes a Master Browser, the following parameter allows it to hold the browse list
browse list = yes

printcap name = cups
printing = cups

log file = /var/log/samba/log.%m
max log size = 50

security = user
map to guest = bad user

encrypt passwords = yes
passdb backend = tdbsam

domain master = no
local master = yes
preferred master = yes
name resolve order = bcast
dns proxy = no

;Listen for NetBIOS on Ethernet and Wireless interfaces
;Names of the interfaces found using ifconfig command
interfaces = enp0s10 wlp2s0
server string = Samba Server on ion330ht
log level = 2

[netlogon]
comment = Network Logon Service
path = /var/lib/samba/netlogon
guest ok = yes

[printers]
comment = All Printers
path = /var/spool/samba
guest ok = yes
printable = yes
create mask = 0700

[print$]
path = /var/lib/samba/printers
write list = @adm root
guest ok = yes

[fitzcarraldo]
path = /home/fitzcarraldo/Public-fitzcarraldo
comment = To pass files to and from ion330ht
browseable = yes
public = yes
writable = yes
valid users = fitzcarraldo

I used the command ‘smbpasswd -a fitzcarraldo‘ to specify my SMB password, which has to be the same as my Linux password.
 
 
Starting Services

‘Application Menu’ > ‘Preferences’ > ‘YaST Services Manager’

In addition to any services already configured to start ‘On Boot’, make sure the following are set to start ‘On Boot’:

  • clamd
  • cups
  • nmb
  • ntpd
  • smb
  • wsdd

 
 
User’s Locale

Even though I had configured during installation (and confirmed after installation using YaST) the language, keyboard language and location as British English and Europe/London respectively, the dates of files displayed by PCManFM-Qt were still in US format. I added the following lines to the file ~/.profile to fix that:

export LANG="en_GB.UTF-8"
export LC_CTYPE="en_GB.UTF-8"
export LC_NUMERIC="en_GB.UTF-8"
export LC_TIME="en_GB.UTF-8"
export LC_COLLATE="en_GB.UTF-8"
export LC_MONETARY="en_GB.UTF-8"
export LC_MESSAGES="en_GB.UTF-8"
export LC_PAPER="en_GB.UTF-8"
export LC_NAME="en_GB.UTF-8"
export LC_ADDRESS="en_GB.UTF-8"
export LC_TELEPHONE="en_GB.UTF-8"
export LC_MEASUREMENT="en_GB.UTF-8"
export LC_IDENTIFICATION="en_GB.UTF-8"
export LC_ALL=""

Susequently I discovered that the file /etc/sysconfig/language contains variables that I probably could have edited manually to achieve the same thing for users’ accounts.
 
 
GUI Appearance

As I am sitting on a sofa viewing the TV screen from a distance, text and icons have to be larger than on a normal desktop or laptop machine. This was easy enough to configure.

I right-clicked on the LXQt Panel and selected ‘Configure Panel’ to increase the height of the Panel and the size of the Panel icons and Panel font. I selected ‘Preferences’ > ‘LXQt Settings’ > ‘Appearance’ to increase the size of the icons and font in the rest of the UI, to change the icon theme to Oxygen, and to change the mouse cursor size to 50. I selected ‘Preferences’ > ‘LXQt Settings’ > ‘Desktop’ (or right-click on the Desktop and select ‘Desktop Preferences’) to increase the icon size and font size on the Desktop.

I increased the font size of the Firefox address bar, bookmarks toolbar, tabs and page using the two methods (devp and userChrome.css) described on the following Mozilla Support page: Text size of menus and tool bars way too small. I want to be able to increase the size.

Firefox font size on my 43-inch TV screen

Firefox font size on my 43-inch TV screen.

 
 
ClamAV virus signatures database was not being updated

The ClamAV signatures database was not being updated automatically after I installed ClamAV, so I needed to fix that.

Using the following commands listed on the openSUSE Wiki page for ClamAV did not work, because there is no freshclam.service file:

fitzcarraldo@ion330ht:~> sudo systemctl start freshclam
fitzcarraldo@ion330ht:~> sudo systemctl enable freshclam

I suspected that GeckoLinux/openSUSE Tumbleweed uses systemd timers instead of cron, and indeed I found a timer file for freshclam:

fitzcarraldo@ion330ht:~> locate timer | grep fresh
/usr/lib/systemd/system/freshclam.timer
fitzcarraldo@ion330ht:~> cat /usr/lib/systemd/system/freshclam.timer
[Unit]
Description=Timer for freshclam virus definitions downloader

[Timer]
OnBootSec=5m
OnUnitActiveSec=2h
Persistent=true

[Install]
WantedBy=timers.target

I enabled it as follows:

fitzcarraldo@ion330ht:~> sudo systemctl enable freshclam.timer
[sudo] password for root: 
Created symlink /etc/systemd/system/timers.target.wants/freshclam.timer → /usr/lib/systemd/system/freshclam.timer.
fitzcarraldo@ion330ht:~> sudo systemctl start freshclam.timer
fitzcarraldo@ion330ht:~> sudo systemctl is-active freshclam.timer
active

systemd now runs freshclam 5 minutes after the machine boots and every 2 hours thereafter.
 
 
Automatic scanning for viruses in the Downloads directory

See my 2017 blog post Using the ClamAV daemon to scan files placed in my Downloads directory in Gentoo Linux, which I have implemented successfully on machines running Gentoo Linux and Lubuntu. However, in GeckoLinux it took a bit more effort to get the scheme working.

GeckoLinux Rolling (and, I assume, also openSUSE Tumbleweed) allocates clamav to a user named ‘vscan‘ and a group named ‘vscan‘ instead of a user named ‘clamav‘ and a group named ‘clamav‘.

fitzcarraldo@ion330ht:~> ls -la /var/lib/clamav
total 343504
drwxr-xr-x 1 vscan vscan        84 Jan 14 20:27 .
drwxr-xr-x 1 root  root        534 Jan 11 12:08 ..
-rw-r--r-- 1 vscan vscan    293670 Jan 11 12:36 bytecode.cvd
-rw-r--r-- 1 vscan vscan 180965376 Jan 14 10:29 daily.cld
-rw-r--r-- 1 vscan vscan        69 Jan 11 12:33 freshclam.dat
-rw-r--r-- 1 vscan vscan 170479789 Jan 11 12:35 main.cvd

Why GeckoLinux (and, I assume, openSUSE) is different from Gentoo Linux and *buntu I don’t know, but I wish Linux distributions were consistent in such cases.

This test command did not work:

fitzcarraldo@ion330ht:~> clamdscan --fdpass --move=/home/fitzcarraldo/virus-quarantine /home/fitzcarraldo/eicarcom2.zip
WARNING: Ignoring deprecated option AllowSupplementaryGroups at /etc/clamd.conf:790
/home/fitzcarraldo/eicarcom2.zip: File path check failure: Permission denied. ERROR
/home/fitzcarraldo/eicarcom2.zip: File path check failure: Permission denied. ERROR

----------- SCAN SUMMARY -----------
Infected files: 0
Total errors: 2
Time: 0.003 sec (0 m 0 s)
Start Date: 2022:01:14 20:36:05
End Date:   2022:01:14 20:36:05

Anyway, this is what I did (I am not sure precisely which command or commands below were necessary to get things working):

fitzcarraldo@ion330ht:~> setfacl -Rd -m 'u:vscan:rx' /home/fitzcarraldo
fitzcarraldo@ion330ht:~> sudo setfacl -Rd -m 'u:vscan:rx' /home/fitzcarraldo
fitzcarraldo@ion330ht:~> sudo usermod -a -G fitzcarraldo vscan
fitzcarraldo@ion330ht:~> sudo usermod -a -G vscan fitzcarraldo
fitzcarraldo@ion330ht:~> groups
fitzcarraldo vscan users video lp audio network storage wheel autologin
fitzcarraldo@ion330ht:~> sudo reboot

(This was the reason I installed the package acl I mentioned earlier.)

After the above changes, this test command does work:

fitzcarraldo@ion330ht:~> clamdscan --fdpass --move=/home/fitzcarraldo/virus-quarantine /home/fitzcarraldo/eicarcom2.zip
WARNING: Ignoring deprecated option AllowSupplementaryGroups at /etc/clamd.conf:790
/home/fitzcarraldo/eicarcom2.zip: Win.Test.EICAR_HDB-1 FOUND
/home/fitzcarraldo/eicarcom2.zip: moved to '/home/fitzcarraldo/virus-quarantine/eicarcom2.zip'

----------- SCAN SUMMARY -----------
Infected files: 1
Time: 0.020 sec (0 m 0 s)
Start Date: 2022:01:14 20:41:27
End Date:   2022:01:14 20:41:27

Also the scheme described in my aforementioned 2017 post now works in this installation.
 
 
Corrupted filesystem

Not long after I installed GeckoLinux I left the nettop running unattended on several occasions, and a couple of times I returned to find the HDD clicking rapidly (I assume this was the noise of the head continually seeking unsuccessfully), and had to press the machine’s Power switch in order to stop this. As the nettop had been used very little and was in almost new condition, I suspected that the problem was not caused by the HDD but rather by the software installation. I have read about corruption of Btrfs filesystems on several occasions in the past, so I wondered if the problem was caused by Btrfs itself.

I booted the Live pendrive that I had used to install GeckoLinux Rolling LXQt, became the root user (‘sudo su‘) and entered the command ‘btrfs check /dev/sda1‘, which returned no errors. I did some searching on the Web and came across commands such as ‘btrfs check --repair‘ which appeared to be analagous to ‘fsck‘ for other filesystems. It was only later that I found an article ‘How to recover a BTRFS partition‘ with a dire warning about only using that command as a last resort. Before finding that article I ran the following commands:

localhost:/home/linux # btrfs rescue zero-log /dev/sda1
Clearing log on /dev/sda1, previous log_root 0, level 0
localhost:/home/linux # btrfs check --repair /dev/sda1
enabling repair mode
WARNING:

        Do not use --repair unless you are advised to do so by a developer
        or an experienced user, and then only after having accepted that no
        fsck can successfully repair all types of filesystem corruption. Eg.
        some software or hardware bugs can fatally damage a volume.
        The operation will start in 10 seconds.
        Use Ctrl-C to stop it.
10 9 8 7 6 5 4 3 2 1
Starting repair.
Opening filesystem to check...
Checking filesystem on /dev/sda1
UUID: 82a56d4a-a234-4543-8596-99d98b84c581
ERROR: Corrupted fs, no valid METADATA block group found

Darn it! I tried the following command to see if it returned an error message:

localhost:/home/linux # btrfs rescue zero-log /dev/sda1
Clearing log on /dev/sda1, previous log_root 0, level 0

Then I found the aforementioned article ‘How to recover a BTRFS partition’ and entered the following command which the article states can be used to rebuild the filesystem metadata tree:

localhost:/home/linux # btrfs rescue chunk-recover /dev/sda1/

It was very slow, occasionally displaying lines ‘Scanning: <n> in dev0‘, so I didn’t hang around to wait for it to complete. When I came back several hours later I found that it had finished and was displaying the final lines of output in the terminal window:

[...]
Scanning: 14673166336 in dev0
Scanning: 14742372352 in dev0
Scanning: 14813675520 in dev0
Scanning: 14884454400 in dev0
Scanning: 14954708992 in dev0
Scanning: 15025487872 in dev0
Scanning: 15094693888 in dev0
Scanning: 15143624704 in dev0
Scanning: 15213707264 in dev0
Scanning: 15284486144 in dev0
Scanning: DONE in dev0
Check chunks successfully with no orphans
Chunk tree recovered successfully

I then ran the ‘rescue zero-log‘ and ‘check‘ commands again to see if there would be an error message:

localhost:/home/linux # btrfs rescue zero-log /dev/sda1
Clearing log on /dev/sda1, previous log_root 0, level 0
localhost:/home/linux # btrfs check /dev/sda1
Opening filesystem to check...
Checking filesystem on /dev/sda1
UUID: 82a56d4a-a234-4543-8596-99d98b84c581
[1/7] checking root items
Fixed 0 roots.
[2/7] checking extents
No device size related problem found
[3/7] checking free space tree
[4/7] checking fs roots
[5/7] checking only csums items (without verifying data)
[6/7] checking root refs
[7/7] checking quota groups skipped (not enabled on this FS)
found 159583424512 bytes used, no error found
total csum bytes: 155206908
total tree bytes: 614842368
total fs tree bytes: 389431296
total extent tree bytes: 28753920
btree space waste bytes: 131658663
file data blocks allocated: 188270157824
 referenced 182537080832

I hoped the filesystem had indeed been repaired. I then rebooted the machine from the HDD and it has been fine ever since.
 
 
Minor bug in the applications menu entry YaST Filesystem Snapshots

Preferences menu in GeckoLinux LXQt on my 43-inch TV screen

Preferences menu in GeckoLinux LXQt on my 43-inch TV screen.

The entry ‘Preferences’ > ‘YaST Filesystem Snapshots’ in the LXQt Application Menu would not launch Snapper (there was not even an authentication prompt to enter the root user’s password). All the other menu entries under ‘Preferences’ worked as expected. What made this more perplexing was that ‘Filesystem Snapshots’ in the ‘Miscellaneous’ section of the YaST Control Centre (‘Preferences’ > ‘YaST’ in the LXQt application menu) did launch Snapper, and I believe that selection also uses the desktop configuration file /usr/share/applications/YaST2/org.opensuse.yast.Snapper.desktop).

To check if there was something wrong with the desktop configuration file I copied /usr/share/applications/YaST2/org.opensuse.yast.Snapper.desktop to the Desktop, changed its ownership to fitzcarraldo.fitzcarraldo, right-clicked on it and selected ‘Trust this executable’, and it worked as expected when I double-clicked on it. So why did it not work when selected from the application menu?

I then compared the Snapper desktop file to the other YaST2 desktop files in the directory:

ion330ht:/home/fitzcarraldo # grep Exec /usr/share/applications/YaST2/org.opensuse.yast*
/usr/share/applications/YaST2/org.opensuse.yast.Alternatives.desktop:Exec=/usr/bin/xdg-su -c "/sbin/yast2 alternatives"
/usr/share/applications/YaST2/org.opensuse.yast.Bootloader.desktop:Exec=xdg-su -c "/sbin/yast2 bootloader"
/usr/share/applications/YaST2/org.opensuse.yast.CheckMedia.desktop:Exec=/sbin/yast2 checkmedia
/usr/share/applications/YaST2/org.opensuse.yast.Disk.desktop:Exec=xdg-su -c "/sbin/yast2 partitioner"
/usr/share/applications/YaST2/org.opensuse.yast.Firewall.desktop:Exec=xdg-su -c "/sbin/yast2 firewall"
/usr/share/applications/YaST2/org.opensuse.yast.Host.desktop:Exec=xdg-su -c "/sbin/yast2 host"
/usr/share/applications/YaST2/org.opensuse.yast.Installation.desktop:Exec=/bin/true
/usr/share/applications/YaST2/org.opensuse.yast.Keyboard.desktop:Exec=xdg-su -c "/sbin/yast2 keyboard"
/usr/share/applications/YaST2/org.opensuse.yast.LAN.desktop:Exec=xdg-su -c "/sbin/yast2 lan"
/usr/share/applications/YaST2/org.opensuse.yast.Language.desktop:Exec=xdg-su -c "/sbin/yast2 language"
/usr/share/applications/YaST2/org.opensuse.yast.NTPClient.desktop:Exec=xdg-su -c "/sbin/yast2 ntp-client"
/usr/share/applications/YaST2/org.opensuse.yast.OnlineUpdateConfiguration.desktop:Exec=/sbin/yast2 online_update_configuration
/usr/share/applications/YaST2/org.opensuse.yast.Printer.desktop:Exec=xdg-su -c "/sbin/yast2 printer"
/usr/share/applications/YaST2/org.opensuse.yast.Proxy.desktop:Exec=xdg-su -c "/sbin/yast2 proxy"
/usr/share/applications/YaST2/org.opensuse.yast.ReleaseNotes.desktop:Exec=/sbin/yast2 inst_release_notes
/usr/share/applications/YaST2/org.opensuse.yast.Remote.desktop:Exec=xdg-su -c "/sbin/yast2 remote"
/usr/share/applications/YaST2/org.opensuse.yast.Security.desktop:Exec=xdg-su -c "/sbin/yast2 security"
/usr/share/applications/YaST2/org.opensuse.yast.ServicesManager.desktop:Exec=xdg-su -c "/sbin/yast2 services-manager"
/usr/share/applications/YaST2/org.opensuse.yast.Snapper.desktop:Exec=/usr/bin/xdg-su -c '/sbin/yast2 snapper'
/usr/share/applications/YaST2/org.opensuse.yast.Sudo.desktop:Exec=xdg-su -c "/sbin/yast2 sudo"
/usr/share/applications/YaST2/org.opensuse.yast.SWSingle.desktop:Exec=xdg-su -c "/sbin/yast2 sw_single"
/usr/share/applications/YaST2/org.opensuse.yast.SWSource.desktop:Exec=xdg-su -c "/sbin/yast2 repositories"
/usr/share/applications/YaST2/org.opensuse.yast.Sysconfig.desktop:Exec=xdg-su -c "/sbin/yast2 sysconfig"
/usr/share/applications/YaST2/org.opensuse.yast.Timezone.desktop:Exec=xdg-su -c "/sbin/yast2 timezone"
/usr/share/applications/YaST2/org.opensuse.yast.Upgrade.desktop:Exec=/bin/true
/usr/share/applications/YaST2/org.opensuse.yast.Users.desktop:Exec=xdg-su -c "/sbin/yast2 users"

To get the LXQt application menu item ‘Preferences’ > ‘YaST Filesystem Snapshots’ to work I had to do the following:

1. Edit /usr/share/applications/YaST2/org.opensuse.yast.Snapper.desktop and change the following line:

Exec=/usr/bin/xdg-su -c '/sbin/yast2 snapper'

to:

Exec=xdg-su -c "/sbin/yast2 snapper"

2. Run the following command (as root user):

ion330ht:/home/fitzcarraldo # update-desktop-database /usr/share/applications

The file org.opensuse.yast.Alternatives.desktop contains Exec=/usr/bin/xdg-su -c "/sbin/yast2 alternatives" and works, and xdg-su is indeed in the directory /usr/bin/, so I don’t know why the original Snapper desktop file would not work from the LXQt application menu. Anyway, the modified file works, although I could have done without wasting several hours trying to fix the problem, even though it was an inconvenience rather than a show-stopper.
 
 
Applications Menu entries for YaST

I personally found the large number of YaST entries in the Application Menu confusing and unecessary (see the screenshots above and below). It also looks cluttered. The individual YaST entries can also be accessed via ‘Preferences’ > ‘YaST Control Center’, so a shorter menu could have been implemented instead. Also, the three entries ‘Other’ > ‘YaST Software’, ‘Preferences’ > ‘YaST Software’ and ‘Preferences’ > ‘YaST Software Management’ all do the same thing.

Other menu in GeckoLinux LXQt on my 43-inch TV screen

Other menu in GeckoLinux LXQt on my 43-inch TV screen.

 
 
ASRock CIR MCE Remote

I should point out that I tinkered with the infrared remote to scratch an itch, because the Rii i8 mini wireless keyboard with touchpad is far superior to a CIR MCE remote and can be used to control the Desktop Environment and any application, including KODI, with ease. MCE remotes are a pain in the neck to configure. The KODI Wiki states: “MCE Remotes – Infrared remote controls made for computers that follow the MCE standard. These remotes should work with Kodi out-of-the-box on Windows and Linux.” Good luck with that!

Anyway, the following are useful background reading on configuring Linux to use infrared remotes:

Here is what I had to do to configure GeckoLinux to recognise the ASRock MCE remote:

1. Ensure the IR receiver is enabled in the ASRock ION 330HT BIOS.

2. Do not install lirc. If it is installed, uninstall it and any associated LIRC packages (except liblirc_client0 which is a dependency of vlc in GeckoLinux/openSUSE, unless you don’t want VLC).

3. The nuvoton-cir module should be loaded automatically at boot if the IR receiver is enabled in the BIOS:

ion330ht:/home/fitzcarraldo # lsmod | grep nuvoton
nuvoton_cir            32768  0
rc_core                65536  6 ir_rc6_decoder,rc_rc6_mce,cec,ir_rc5_decoder,nuvoton_cir
ion330ht:/home/fitzcarraldo # lsmod | grep lirc
ion330ht:/home/fitzcarraldo #

4. Install the package v4l-utils to install the ir-keytable utility, the files /etc/rc_maps.cfg, /lib/udev/rc_keymaps/*.toml and /usr/lib/udev/rules.d/70-infrared.rules

5. Enter the command ‘ir-keytable‘ and you should see some output similar to the following:

ion330ht:/home/fitzcarraldo # ir-keytable
Found /sys/class/rc/rc0/ with:
        Name: Nuvoton w836x7hg Infrared Remote Transceiver
        Driver: nuvoton-cir
        Default keymap: rc-rc6-mce
        Input device: /dev/input/event6
        LIRC device: /dev/lirc0
        Supported kernel protocols: lirc rc-5 rc-5-sz jvc sony nec sanyo mce_kbd rc-6 sharp xmp imon rc-mm 
        Enabled kernel protocols: lirc rc-6 
        bus: 25, vendor/product: 1050:00b4, version: 0x0073
        Repeat delay = 500 ms, repeat period = 125 ms

6. Enter the command ‘ir-keytable -t‘ and press some keys on the remote. You should see something like the following:

ion330ht:/home/fitzcarraldo # ir-keytable -t
Testing events. Please, press CTRL-C to abort.
297.938077: lirc protocol(rc6_mce): scancode = 0x800f0401
297.938119: event type EV_MSC(0x04): scancode = 0x800f0401
297.938119: event type EV_KEY(0x01) key_down: KEY_NUMERIC_1(0x0201)
297.938119: event type EV_SYN(0x00).
298.154989: event type EV_KEY(0x01) key_up: KEY_NUMERIC_1(0x0201)
298.154989: event type EV_SYN(0x00).
301.628475: lirc protocol(rc6_mce): scancode = 0x800f0402 toggle=1
301.628516: event type EV_MSC(0x04): scancode = 0x800f0402
301.628516: event type EV_KEY(0x01) key_down: KEY_NUMERIC_2(0x0202)
301.628516: event type EV_SYN(0x00).
301.846981: event type EV_KEY(0x01) key_up: KEY_NUMERIC_2(0x0202)
301.846981: event type EV_SYN(0x00).
307.577177: lirc protocol(rc6_mce): scancode = 0x800f0422
307.577219: event type EV_MSC(0x04): scancode = 0x800f0422
307.577219: event type EV_KEY(0x01) key_down: KEY_OK(0x0160)
307.577219: event type EV_SYN(0x00).
307.725639: lirc protocol(rc6_mce): scancode = 0x800f0422
307.725671: event type EV_MSC(0x04): scancode = 0x800f0422
307.725671: event type EV_SYN(0x00).
307.943009: event type EV_KEY(0x01) key_up: KEY_OK(0x0160)
307.943009: event type EV_SYN(0x00).
311.272866: lirc protocol(rc6_mce): scancode = 0x800f040d toggle=1
311.272930: event type EV_MSC(0x04): scancode = 0x800f040d
311.272930: event type EV_KEY(0x01) key_down: KEY_MEDIA(0x00e2)
311.272930: event type EV_SYN(0x00).
311.420857: lirc protocol(rc6_mce): scancode = 0x800f040d toggle=1
311.420900: event type EV_MSC(0x04): scancode = 0x800f040d
311.420900: event type EV_SYN(0x00).
311.638978: event type EV_KEY(0x01) key_up: KEY_MEDIA(0x00e2)
311.638978: event type EV_SYN(0x00).
^C

7. Check the file /etc/rc_maps.cfg exists and includes the following line:

*       rc-rc6-mce               rc6_mce.toml

 

ion330ht:/home/fitzcarraldo # cat /etc/rc_maps.cfg
#
# Keymaps table
#
# This table creates an association between a keycode file and a kernel
# driver. It can be used to automatically override a keycode definition.
#
# Although not yet tested, it is mented to be added at udev.
#
# To use, you just need to run:
#       ./ir-keytable -a
#
# Or, if the remote is not the first device:
#       ./ir-keytable -a -s rc1         # for RC at rc1
#

# Format:
#       driver - name of the driver provided via uevent - use * for any driver
#       table -  RC keymap table, provided via uevent - use * for any table
#       file - file name. If directory is not specified, it will default to
#               /etc/rc_keymaps.
# For example:
# driver        table                           file
# cx8800        *                               ./keycodes/rc5_hauppauge_new.toml
# *             rc-avermedia-m135a-rm-jx        ./keycodes/kworld_315u.toml
# saa7134       rc-avermedia-m135a-rm-jx        ./keycodes/keycodes/nec_terratec_cinergy_xs.toml
# em28xx        *                               ./keycodes/kworld_315u.toml
# *             *                               ./keycodes/rc5_hauppauge_new.toml

# Table to automatically load the rc maps for the bundled IR's provided with the
# devices supported by the linux kernel

#driver table                    file
*       rc-adstech-dvb-t-pci     adstech_dvb_t_pci.toml
*       rc-alink-dtu-m           alink_dtu_m.toml
*       rc-anysee                anysee.toml
*       rc-apac-viewcomp         apac_viewcomp.toml
*       rc-astrometa-t2hybrid    astrometa_t2hybrid.toml
*       rc-asus-pc39             asus_pc39.toml
*       rc-asus-ps3-100          asus_ps3_100.toml
*       rc-ati-tv-wonder-hd-600  ati_tv_wonder_hd_600.toml
*       rc-ati-x10               ati_x10.toml
*       rc-avermedia-a16d        avermedia_a16d.toml
*       rc-avermedia-cardbus     avermedia_cardbus.toml
*       rc-avermedia-dvbt        avermedia_dvbt.toml
*       rc-avermedia-m135a       avermedia_m135a.toml
*       rc-avermedia-m733a-rm-k6 avermedia_m733a_rm_k6.toml
*       rc-avermedia-rm-ks       avermedia_rm_ks.toml
*       rc-avermedia             avermedia.toml
*       rc-avertv-303            avertv_303.toml
*       rc-azurewave-ad-tu700    azurewave_ad_tu700.toml
*       rc-beelink-gs1           beelink_gs1.toml
*       rc-behold-columbus       behold_columbus.toml
*       rc-behold                behold.toml
*       rc-budget-ci-old         budget_ci_old.toml
*       rc-cec                   cec.toml
*       rc-cinergy-1400          cinergy_1400.toml
*       rc-cinergy               cinergy.toml
*       rc-ct-90405              ct_90405.toml
*       rc-d680-dmb              d680_dmb.toml
*       rc-delock-61959          delock_61959.toml
*       rc-dib0700-nec           dib0700_nec.toml
*       rc-dib0700-rc5           dib0700_rc5.toml
*       rc-digitalnow-tinytwin   digitalnow_tinytwin.toml
*       rc-digittrade            digittrade.toml
*       rc-dm1105-nec            dm1105_nec.toml
*       rc-dntv-live-dvb-t       dntv_live_dvb_t.toml
*       rc-dntv-live-dvbt-pro    dntv_live_dvbt_pro.toml
*       rc-dtt200u               dtt200u.toml
*       rc-dvbsky                dvbsky.toml
*       rc-dvico-mce             dvico_mce.toml
*       rc-dvico-portable        dvico_portable.toml
*       rc-em-terratec           em_terratec.toml
*       rc-encore-enltv-fm53     encore_enltv_fm53.toml
*       rc-encore-enltv          encore_enltv.toml
*       rc-encore-enltv2         encore_enltv2.toml
*       rc-evga-indtube          evga_indtube.toml
*       rc-eztv                  eztv.toml
*       rc-flydvb                flydvb.toml
*       rc-flyvideo              flyvideo.toml
*       rc-fusionhdtv-mce        fusionhdtv_mce.toml
*       rc-gadmei-rm008z         gadmei_rm008z.toml
*       rc-geekbox               geekbox.toml
*       rc-genius-tvgo-a11mce    genius_tvgo_a11mce.toml
*       rc-gotview7135           gotview7135.toml
*       rc-hauppauge             hauppauge.toml
*       rc-hisi-poplar           hisi_poplar.toml
*       rc-hisi-tv-demo          hisi_tv_demo.toml
*       rc-imon-mce              imon_mce.toml
*       rc-imon-pad              imon_pad.toml
*       rc-imon-rsc              imon_rsc.toml
*       rc-iodata-bctv7e         iodata_bctv7e.toml
*       rc-it913x-v1             it913x_v1.toml
*       rc-it913x-v2             it913x_v2.toml
*       rc-kaiomy                kaiomy.toml
*       rc-khadas                khadas.toml
*       rc-khamsin               khamsin.toml
*       rc-kworld-315u           kworld_315u.toml
*       rc-kworld-pc150u         kworld_pc150u.toml
*       rc-kworld-plus-tv-analog kworld_plus_tv_analog.toml
*       rc-leadtek-y04g0051      leadtek_y04g0051.toml
*       rc-lme2510               lme2510.toml
*       rc-manli                 manli.toml
*       rc-mecool-kii-pro        mecool_kii_pro.toml
*       rc-mecool-kiii-pro       mecool_kiii_pro.toml
*       rc-medion-x10-digitainer medion_x10_digitainer.toml
*       rc-medion-x10-or2x       medion_x10_or2x.toml
*       rc-medion-x10            medion_x10.toml
*       rc-minix-neo             minix_neo.toml
*       rc-msi-digivox-ii        msi_digivox_ii.toml
*       rc-msi-digivox-iii       msi_digivox_iii.toml
*       rc-msi-tvanywhere-plus   msi_tvanywhere_plus.toml
*       rc-msi-tvanywhere        msi_tvanywhere.toml
*       rc-nebula                nebula.toml
*       rc-nec-terratec-cinergy-xs nec_terratec_cinergy_xs.toml
*       rc-norwood               norwood.toml
*       rc-npgtech               npgtech.toml
*       rc-odroid                odroid.toml
*       rc-pctv-sedna            pctv_sedna.toml
*       rc-pine64                pine64.toml
*       rc-pinnacle-color        pinnacle_color.toml
*       rc-pinnacle-grey         pinnacle_grey.toml
*       rc-pinnacle-pctv-hd      pinnacle_pctv_hd.toml
*       rc-pixelview-002t        pixelview_002t.toml
*       rc-pixelview-mk12        pixelview_mk12.toml
*       rc-pixelview-new         pixelview_new.toml
*       rc-pixelview             pixelview.toml
*       rc-powercolor-real-angel powercolor_real_angel.toml
*       rc-proteus-2309          proteus_2309.toml
*       rc-purpletv              purpletv.toml
*       rc-pv951                 pv951.toml
*       rc-rc6-mce               rc6_mce.toml
*       rc-real-audio-220-32-keys real_audio_220_32_keys.toml
*       rc-reddo                 reddo.toml
*       rc-snapstream-firefly    snapstream_firefly.toml
*       rc-streamzap             streamzap.toml
*       rc-su3000                su3000.toml
*       rc-tanix-tx3mini         tanix_tx3mini.toml
*       rc-tanix-tx5max          tanix_tx5max.toml
*       rc-tbs-nec               tbs_nec.toml
*       rc-technisat-ts35        technisat_ts35.toml
*       rc-technisat-usb2        technisat_usb2.toml
*       rc-terratec-cinergy-c-pci terratec_cinergy_c_pci.toml
*       rc-terratec-cinergy-s2-hd terratec_cinergy_s2_hd.toml
*       rc-terratec-cinergy-xs   terratec_cinergy_xs.toml
*       rc-terratec-slim-2       terratec_slim_2.toml
*       rc-terratec-slim         terratec_slim.toml
*       rc-tevii-nec             tevii_nec.toml
*       rc-tivo                  tivo.toml
*       rc-total-media-in-hand-02 total_media_in_hand_02.toml
*       rc-total-media-in-hand   total_media_in_hand.toml
*       rc-trekstor              trekstor.toml
*       rc-tt-1500               tt_1500.toml
*       rc-twinhan-dtv-cab-ci    twinhan_dtv_cab_ci.toml
*       rc-twinhan1027           twinhan_vp1027_dvbs.toml
*       rc-vega-s9x              vega_s9x.toml
*       rc-videomate-k100        videomate_k100.toml
*       rc-videomate-s350        videomate_s350.toml
*       rc-videomate-tv-pvr      videomate_tv_pvr.toml
*       rc-videostrong-kii-pro   kii_pro.toml
*       rc-wetek-hub             wetek_hub.toml
*       rc-wetek-play2           wetek_play2.toml
*       rc-winfast-usbii-deluxe  winfast_usbii_deluxe.toml
*       rc-winfast               winfast.toml
*       rc-x96max                x96max.toml
*       rc-xbox-dvd              xbox_dvd.toml
*       rc-zx-irdec              zx_irdec.toml
# *     *                        af9005.toml          # found in af9005-remote.c
# *     *                        az6027.toml          # found in az6027.c
# *     *                        cinergyt2.toml       # found in cinergyT2-core.c
# *     *                        dibusb.toml          # found in dibusb-common.c
# *     *                        digitv.toml          # found in digitv.c
# *     *                        megasky.toml         # found in m920x.c
# *     *                        tvwalkertwin.toml    # found in m920x.c
# *     *                        pinnacle310e.toml    # found in m920x.c
# *     *                        haupp.toml           # found in nova-t-usb2.c
# *     *                        opera1.toml          # found in opera1.c
# *     *                        vp702x.toml          # found in vp702x.c

8. Copy the file /lib/udev/rc_keymaps/rc6_mce.toml to /etc/rc_keymaps/rc6_mce.toml and edit the latter. For example:

[[protocols]]
name = "rc6_mce"
protocol = "rc6"
variant = "rc6_mce"
[protocols.scancodes]
0x800f0400 = "KEY_KP0"
0x800f0401 = "KEY_KP1"
0x800f0402 = "KEY_KP2"
0x800f0403 = "KEY_KP3"
0x800f0404 = "KEY_KP4"
0x800f0405 = "KEY_KP5"
0x800f0406 = "KEY_KP6"
0x800f0407 = "KEY_KP7"
0x800f0408 = "KEY_KP8"
0x800f0409 = "KEY_KP9"
0x800f040a = "KEY_DELETE"
0x800f040b = "KEY_ENTER"
0x800f040c = "KEY_SLEEP"                  # Power
0x800f040d = "KEY_MEDIA"                  # Left Meta, Start
0x800f040e = "KEY_MUTE"
0x800f040f = "KEY_I"                      # Info
0x800f0410 = "KEY_VOLUMEUP"               # Volume Up
0x800f0411 = "KEY_VOLUMEDOWN"             # Volume Down
0x800f0412 = "KEY_CHANNELUP"
0x800f0413 = "KEY_CHANNELDOWN"
0x800f0414 = "KEY_FORWARD"                # Fast forward
0x800f0415 = "KEY_REWIND"                 # Rewind
0x800f0416 = "KEY_PLAY"
0x800f0417 = "KEY_RECORD"
0x800f0418 = "KEY_PLAYPAUSE"              # Was KEY_PLAY but didn't pause in Clementine
0x800f0419 = "KEY_STOP"
0x800f041a = "KEY_NEXTSONG"               # Skip Next
0x800f041b = "KEY_PREVIOUSSONG"           # Skip Previous
0x800f041c = "KEY_NUMERIC_POUND"
0x800f041d = "KEY_NUMERIC_STAR"
0x800f041e = "KEY_UP"
0x800f041f = "KEY_DOWN"
0x800f0420 = "KEY_LEFT"
0x800f0421 = "KEY_RIGHT"
0x800f0422 = "KEY_ENTER"                  # OK
0x800f0423 = "KEY_BACKSPACE"              # Back / Exit
0x800f0424 = "KEY_DVD"
0x800f0425 = "KEY_TUNER"
0x800f0426 = "KEY_EPG"
0x800f0427 = "KEY_ZOOM"
0x800f043a = "KEY_BRIGHTNESSUP"
0x800f0446 = "KEY_TV"
0x800f0447 = "KEY_AUDIO"
0x800f0448 = "KEY_PVR"
0x800f0449 = "KEY_CAMERA"
0x800f044a = "KEY_VIDEO"
0x800f044c = "KEY_LANGUAGE"
0x800f044d = "KEY_TITLE"
0x800f044e = "KEY_PRINT"
0x800f0450 = "KEY_RADIO"
0x800f045a = "KEY_SUBTITLE"
0x800f045b = "KEY_RED"
0x800f045c = "KEY_GREEN"                  # Green
0x800f045d = "KEY_YELLOW"
0x800f045e = "KEY_BLUE"                   # Blue
0x800f0465 = "KEY_POWER2"
0x800f046e = "KEY_PLAYPAUSE"
0x800f046f = "KEY_MEDIA"
0x800f0480 = "KEY_BRIGHTNESSDOWN"
0x800f0481 = "KEY_PLAYPAUSE"

9. Run the following command to load the edited keymap and check that it works:

ion330ht:/home/fitzcarraldo # ir-keytable -c -w /etc/rc_keymaps/rc6_mce.toml
Read rc6_mce table
Old keytable cleared
Wrote 60 keycode(s) to driver
Protocols changed to rc-6

By the way, adding ‘-p RC-5,RC-6‘ to that command would select the rc-5 and rc-6 protocols:

ion330ht:/home/fitzcarraldo # ir-keytable -c -p RC-5,RC-6 -w /etc/rc_keymaps/rc6_mce.toml
Read rc6_mce table
Old keytable cleared
Wrote 60 keycode(s) to driver
Protocols changed to rc-5 rc-6

10. Check that the protocols have been enabled and the keymap loaded:

ion330ht:/home/fitzcarraldo # ir-keytable
Found /sys/class/rc/rc0/ with:
        Name: Nuvoton w836x7hg Infrared Remote Transceiver
        Driver: nuvoton-cir
        Default keymap: rc-rc6-mce
        Input device: /dev/input/event7
        LIRC device: /dev/lirc0
        Supported kernel protocols: lirc rc-5 rc-5-sz jvc sony nec sanyo mce_kbd rc-6 sharp xmp imon rc-mm 
        Enabled kernel protocols: lirc rc-6 
        bus: 25, vendor/product: 1050:00b4, version: 0x0073
        Repeat delay = 500 ms, repeat period = 125 ms

11. Reboot.

12. Check that the modified keymap has been loaded:

ion330ht:/home/fitzcarraldo # ir-keytable
Found /sys/class/rc/rc0/ with:
        Name: Nuvoton w836x7hg Infrared Remote Transceiver
        Driver: nuvoton-cir
        Default keymap: rc-rc6-mce
        Input device: /dev/input/event7
        LIRC device: /dev/lirc0
        Supported kernel protocols: lirc rc-5 rc-5-sz jvc sony nec sanyo mce_kbd rc-6 sharp xmp imon rc-mm 
        Enabled kernel protocols: lirc rc-6 
        bus: 25, vendor/product: 1050:00b4, version: 0x0073
        Repeat delay = 500 ms, repeat period = 125 ms
ion330ht:/home/fitzcarraldo # ir-keytable -t
Testing events. Please, press CTRL-C to abort.
1392.769850: lirc protocol(rc6_mce): scancode = 0x800f040d toggle=1
1392.769898: event type EV_MSC(0x04): scancode = 0x800f040d
1392.769898: event type EV_KEY(0x01) key_down: KEY_MEDIA(0x002e)
1392.769898: event type EV_SYN(0x00).
c1392.917858: lirc protocol(rc6_mce): scancode = 0x800f040d toggle=1
1392.917899: event type EV_MSC(0x04): scancode = 0x800f040d
1392.917899: event type EV_SYN(0x00).
1393.137843: event type EV_KEY(0x01) key_up: KEY_MEDIA(0x002e)
1393.137843: event type EV_SYN(0x00).
1409.275700: lirc protocol(rc6_mce): scancode = 0x800f0418
1409.275756: event type EV_MSC(0x04): scancode = 0x800f0418
1409.275756: event type EV_KEY(0x01) key_down: KEY_PLAYPAUSE(0x00a4)
1409.275756: event type EV_SYN(0x00).
1409.425095: lirc protocol(rc6_mce): scancode = 0x800f0418
1409.425131: event type EV_MSC(0x04): scancode = 0x800f0418
1409.425131: event type EV_SYN(0x00).
1409.641846: event type EV_KEY(0x01) key_up: KEY_PLAYPAUSE(0x00a4)
1409.641846: event type EV_SYN(0x00).
1411.757874: lirc protocol(rc6_mce): scancode = 0x800f0418 toggle=1
1411.757928: event type EV_MSC(0x04): scancode = 0x800f0418
1411.757928: event type EV_KEY(0x01) key_down: KEY_PLAYPAUSE(0x00a4)
1411.757928: event type EV_SYN(0x00).
1411.907269: lirc protocol(rc6_mce): scancode = 0x800f0418 toggle=1
1411.907296: event type EV_MSC(0x04): scancode = 0x800f0418
1411.907296: event type EV_SYN(0x00).
1412.125848: event type EV_KEY(0x01) key_up: KEY_PLAYPAUSE(0x00a4)
1412.125848: event type EV_SYN(0x00).
^C

However, not all the keys on the ASRock remote work in KODI when using the key names in the file rc6_mce.toml listed above. I might have been able to change some of the key names in the file to see if they would have the desired effect in KODI, but it is not worth the hassle when my Rii i8 wireless mini keyboard works perfectly with KODI, all other apps, and the Linux Desktop. I find KODI unintuitive in any case, so there is even less incentive to tinker further with the ASRock CIR MCE remote.

Furthermore, I have now disabled the CIR port in the BIOS because I found that sometimes the nettop was resuming from suspension without me triggering it from either the ASRock CIR MCE remote or the Rii i8 mini keyboard. ‘Boot From Onboard LAN’ is not enabled in the BIOS, so that was not the cause.
 
 
Disabling the nettop’s LEDs

The nettop is on my TV stand and its Power LED, LAN LED and SATA LED could become annoying, especially the blinking power LED when the nettop is in Suspend mode, so I disabled these in the BIOS (‘Good Night LED’ is Enabled to turn them all off).
 
 
Conclusion

After over a decade I am actually using the ASRock ION 330HT nettop and have it connected to my 43-inch TV so that I can browse the Web properly from the comfort of my sofa and play all my FLAC (and MP3, OGG etc.) music files through the sound bar also connected to my TV. The Rii i8 wireless mini keyboard/touchpad works perfectly with the nettop, so the ASRock CIR MCE remote is redundant. Although I have a dedicated DVD player connected to the TV via a Composite Video cable, the nettop is connected via an HDMI cable so the image is nice and sharp.

GeckoLinux Rolling LXQt performs well on the nettop, and looks polished and crisp on the TV screen. I like it a lot so far. Other machines in my home network can browse SMB shares on the nettop, and vice versa, and the nettop can also be accessed using SSH. I need to become familiar with the package manager (GUI and command line) but have not had any trouble so far. The Btrfs filesystem ‘hiccup’ I mentioned earlier worries me a little, but I have had no further trouble since I repaired the filesystem. And I have actually used Snapper a couple of times to recover files I deleted too hastily. So GeckoLinux gets a thumbs up from me.

Implementing a quick and easy way to check from the Linux Desktop Environment if the ClamAV signatures database is up-to-date

If you use ClamAV with the Freshclam daemon and your Linux installation does not hide the console output during boot, you might see a message similar to the following on the console briefly during boot if the signatures database has not been updated recently:

LibClamAV Warning: **************************************************
LibClamAV Warning: ***  The virus database is older than 7 days.  ***
LibClamAV Warning: ***        Please update it IMMEDIATELY!       ***
LibClamAV Warning: **************************************************

This can happen for a number of reasons. The Freshclam daemon may not have been enabled, for example. Or you purposely configured your installation not to use the Freshclam daemon but forgot to run Freshclam manually (either from the command line or via ClamTk) during the past seven days to update the database. Or there is a problem with the Freshclam configuration or software installation itself. Or everything is configured correctly but you have not powered up the installation for over seven days. And so on.

This happened to me recently simply because I had forgotten to enable the Freshclam service in one of my Linux installations but had not noticed the error message on the console at boot. Anyway, I fixed it quickly and ran Freshclam from the command line to update the database. The database was very out-of-date and I had to run Freshclam several times – do not enter the sudo freshclam command more frequently than once per hour otherwise Cisco Systems’ ClamAV server will block you for several hours due to excessive use of their bandwidth – but I got everything working in the end.

If Freshclam is actually running, the situation with database updating can be checked by looking in the file /var/log/clamav/freshclam.log. However, as all my Linux machines use ClamAV I decided it would be worth adding a quicker way of checking on the database status that is easy to do from the Desktop. I created a Bash script which can be launched by double-clicking on an icon on the Desktop. It opens a terminal window and reports the current status of the ClamAV signatures database. The current status will depend on the frequency you update the database, so you would expect the database to be out of date briefly from time to time; there is nothing wrong with that. But if it consistently reports that the database is out of date longer than the update frequency specified in freshclam.conf (don’t forget to look in the system freshclam.conf file and, if it exists, the user freshclam.conf file) then further investigation would be warranted.

I created a Bash script ~/.clamav_db_up-to-date_check.sh containing the following:

#!/bin/bash
echo
echo "+--------------------------------------------------------------+"
echo "|    Check if ClamAV database is up-to-date on this machine    |"
echo "+--------------------------------------------------------------+"
((ping -w5 -c3 8.8.8.8 || ping -w5 -c3 4.2.2.1) > /dev/null 2>&1) && INTERNET="y" || (INTERNET="n")
if [ "$INTERNET" = "y" ]; then
  echo
  echo "       ** Internet check for latest update available **"
  echo
  echo -n "    Date update available: "
  DNSLKUP=$( host -t txt current.cvd.clamav.net )
  date -d @$( echo $DNSLKUP | awk '{ print $4 }' | awk -F ":" '{ print $4 }' )
  echo
  echo -n "    Signatures version:    "
  RMTSIGV=$( echo $DNSLKUP | awk '{ print $4 }' | awk -F ":" '{ print $3 }' )
  echo $RMTSIGV
else
  echo
  echo "** No connection to the Internet - Cannot check remote server **"
fi
echo
echo -n "    Date when checked:     "
date
echo
echo "----------------------------------------------------------------"
echo
echo "         ** Currently installed on this machine **"
echo
CLAMINST=$( clamscan --version )
echo -n "    Signatures version:    "
LCLSIGV=$( echo $CLAMINST | awk -F "/" '{ print $2 }' )
echo $LCLSIGV
echo
echo -n "    Date of signatures:    "
echo $CLAMINST | awk -F "/" '{ print $3 }'
echo
echo -n "    ClamAV version:        "
echo $CLAMINST | awk -F "/" '{ print $1 }'
echo
echo "----------------------------------------------------------------"
echo
if [ "$INTERNET" = "y" ]; then
  if [ "$LCLSIGV" = "$RMTSIGV" ]; then
    echo " Same version of signatures as the latest on the remote server"
  else
    echo " Different version of signatures to latest on the remote server"
  fi
fi
echo
read -p "Press any key to exit..." -n1 -s
exit

and made it executable:

user $ chmod +x ~/.clamav_db_up-to-date_check.sh

On a machine running Lubuntu 20.10 (LXQt Desktop Environment), I created the Desktop Configuration File ~/Desktop/ClamAV_DB_check.desktop containing the following:

[Desktop Entry]
Name=ClamAV_DB_check
GenericName=ClamAV_DB_check
Comment=Check if ClamAV database is up-to-date
Exec=qterminal -e '/home/fitzcarraldo/.clamav_db_up-to-date_check.sh'
Type=Application
Icon=/home/fitzcarraldo/Pictures/Icons/clamav-icon.png
Terminal=false

I downloaded from the Web a nice ClamAV icon and specified it in the Desktop Configuration File.

I right-clicked on the icon on the Desktop and selected ‘Trust this executable’.

In my Gentoo Linux installations that use KDE, the Desktop Configuration File looks like this:

[Desktop Entry]
Comment[en_GB]=Check if ClamAV database is up-to-date
Comment=Check if ClamAV database is up-to-date
Exec=konsole -e '/home/fitzcarraldo/.clamav_db_up-to-date_check.sh'
GenericName[en_GB]=Run ClamAV DB check in Konsole
GenericName=Run ClamAV DB check in Konsole
Icon=/home/fitzcarraldo/Pictures/Icons/clamav-icon.png
MimeType=
Name[en_GB]=ClamAV_DB_check
Name=ClamAV_DB_check
Path=
StartupNotify=true
Terminal=true
TerminalOptions=
Type=Application
X-DBUS-ServiceName=
X-DBUS-StartupType=none
X-KDE-SubstituteUID=false
X-KDE-Username=

When I checked earlier today on one of my machines, the output of the script looked like this:


+--------------------------------------------------------------+
|    Check if ClamAV database is up-to-date on this machine    |
+--------------------------------------------------------------+

       ** Internet check for latest update available **

    Date update available: Tue 27 Apr 12:29:00 BST 2021

    Signatures version:    26153

    Date when checked:     Tue 27 Apr 12:52:49 BST 2021

----------------------------------------------------------------

         ** Currently installed on this machine **

    Signatures version:    26152

    Date of signatures:    Mon Apr 26 12:04:28 2021

    ClamAV version:        ClamAV 0.103.2

----------------------------------------------------------------

 Different version of signatures to latest on the remote server

Press any key to exit...


The next time I checked, roughly 50 minutes later, the output of the script then looked like this:


+--------------------------------------------------------------+
|    Check if ClamAV database is up-to-date on this machine    |
+--------------------------------------------------------------+

       ** Internet check for latest update available **

    Date update available: Tue 27 Apr 12:29:00 BST 2021

    Signatures version:    26153

    Date when checked:     Tue 27 Apr 13:41:38 BST 2021

----------------------------------------------------------------

         ** Currently installed on this machine **

    Signatures version:    26153

    Date of signatures:    Tue Apr 27 12:09:27 2021

    ClamAV version:        ClamAV 0.103.2

----------------------------------------------------------------

 Same version of signatures as the latest on the remote server

Press any key to exit...


As you can see, the signatures database had been updated automatically by Freshclam in the intervening period.

Notes on keyboard configuration in X Windows: Keyboard layout, Modifier Key and Compose Key

Before I dive into X Windows, I need to mention Miguel Farah’s excellent and comprehensive Web pages on keyboard layouts and standards:

http://www.farah.cl/Keyboardery/

There are umpteen articles, blog and forum posts available on the Web covering keyboard configuration for X Windows, but my notes below may be of help to someone. I briefly cover keyboard layout configuration (non-persistent) from the command line in a pseudo terminal in an X Windows session, and also how to make the configuration persist. I also cover how to configure a ‘Modifier Key‘ and a ‘Compose Key‘, two different things.

1. Changing the layout

Look in the file /usr/share/X11/xkb/rules/xorg.lst to find out what settings are available in X Windows. The file is divided into four sections listing the different keyboard models, layouts, variants and options that X Windows allows:

user $ grep "^! " /usr/share/X11/xkb/rules/xorg.lst
! model
! layout
! variant
! option

For example, the following X Windows German-language keyboard layouts are available in the Linux installation I am using now:

user $ awk '/\!\ layout/{flag=1;next}/\!\ variant/{flag=0}flag' /usr/share/X11/xkb/rules/xorg.lst | grep German
  at              German (Austria)
  de              German
  ch              German (Switzerland)

And the following variants to those three keyboard layouts are available:

user $ awk '/\!\ variant/{flag=1;next}/\!\ option/{flag=0}flag' /usr/share/X11/xkb/rules/xorg.lst | grep "at: German"
  nodeadkeys      at: German (Austria, no dead keys)
  sundeadkeys     at: German (Austria, with Sun dead keys)
  mac             at: German (Austria, Macintosh)
user $ awk '/\!\ variant/{flag=1;next}/\!\ option/{flag=0}flag' /usr/share/X11/xkb/rules/xorg.lst | grep "de: German"
  deadacute       de: German (dead acute)
  deadgraveacute  de: German (dead grave acute)
  nodeadkeys      de: German (no dead keys)
  T3              de: German (T3)
  dvorak          de: German (Dvorak)
  sundeadkeys     de: German (with Sun dead keys)
  neo             de: German (Neo 2)
  mac             de: German (Macintosh)
  mac_nodeadkeys  de: German (Macintosh, no dead keys)
  qwerty          de: German (QWERTY)
  deadtilde       de: German (dead tilde)
user $ awk '/\!\ variant/{flag=1;next}/\!\ option/{flag=0}flag' /usr/share/X11/xkb/rules/xorg.lst | grep "ch: German"
  legacy          ch: German (Switzerland, legacy)
  de_nodeadkeys   ch: German (Switzerland, no dead keys)
  de_sundeadkeys  ch: German (Switzerland, with Sun dead keys)
  de_mac          ch: German (Switzerland, Macintosh)

Let’s say I had a desktop machine with a 104-key Swiss German keyboard. By looking through the list of keyboard models in the models section of the file /usr/share/X11/xkb/rules/xorg.lst, I think the following model best describes the keyboard:

user $ awk '/\!\ model/{flag=1;next}/\!\ layout/{flag=0}flag' /usr/share/X11/xkb/rules/xorg.lst | grep 104
  pc104           Generic 104-key PC

To inform X Windows of the keyboard’s characteristics I could, for example, enter the following command in an X Windows terminal window, which would apply for that session only:

user $ setxkbmap -model pc104 -layout ch -variant legacy

and/or I could configure X Windows permanently by creating/editing a file /etc/X11/xorg.conf.d/00-keyboard.conf containing the following:

Section "InputClass"
Identifier "system-keyboard"
MatchIsKeyboard "on"
Option "XkbModel" "pc104"
Option "XkbLayout" "ch"
Option "XkbVariant" "legacy"
EndSection

My laptop has a UK keyboard but, depending where I am, I sometimes connect an external US, Brazilian or Spanish keyboard to it.

Left side of HP UK keyboard

Left side of HP UK keyboard

Left side of HP US keyboard

Left side of HP US keyboard

Left side of HP Brazilian keyboard

Left side of HP Brazilian keyboard

Left side of HP Iberian Spanish keyboard

Left side of HP Iberian Spanish keyboard

To be able to switch the layout to the keyboard I am currently using, the following two methods achieve the same effect in X Windows:

Current session only

user $ setxkbmap -layout gb,us,br,es -model pc105 -option grp:alt_shift_toggle

Persistent

The file /etc/X11/xorg.conf.d/00-keyboard.conf contains:

Section "InputClass"
Identifier "system-keyboard"
MatchIsKeyboard "on"
Option "XkbLayout" "gb,us,br,es"
Option "XkbModel" "pc105"
Option "XkbOptions" "grp:alt_shift_toggle"
EndSection

Either of the above methods will enable me to toggle between UK, US, Brazilian and Iberian Spanish keyboard layouts in X Windows by pressing Alt+Shft. If the laptop had, say, a Brazilian keyboard instead of a UK keyboard then I could change the order of the layouts to ‘br,gb,us,es‘ or whatever order I prefer.

In fact, even when an external keyboard is not connected to my laptop I select the layout using Alt+Shft if I want to type in English, Portuguese or Spanish. For example, to type ‘ã‘ (the letter ‘a‘ with a tilde accent) I press Alt+Shft to switch to the Brazilian Portuguese layout then press the ' (apostrophe) key followed by the A key on the laptop’s UK keyboard. Transparent key-cap stickers can be purchased for various language layouts so that users can see which keys on the keyboard correspond to keys in another layout. However I don’t bother with key-cap stickers because I can remember the layouts for the few languages I use.
 
2. Using a Modifier Key and/or a Compose Key

If you do not connect external keyboards with different layouts, or you want to be able to type letters with accents – or type different symbols – that are not on the keyboard, a Modifier Key and/or a Compose Key can be used. These are two different things. You might use a Modifier Key to add an accent to a letter, for example. If you were to configure, say, AltGr as the Modifier Key, pressing AltGr and the ` (grave accent) key simultaneously then releasing them and pressing the A key could – depending on which keyboard layout you are using – result in à (‘a‘ with the grave accent) being displayed. The ` (grave accent) key is a ‘dead key’ in this case because it is not displayed by itself when pressed in conjunction with the AltGr key; it is only displayed when the next key is pressed, i.e. à, not `a, is displayed on the screen.

You might use a Compose Key to display a symbol that is not on the keyboard. If you were to configure, say, the Pause key as the Compose Key, pressing and releasing the Pause key, then the O key and then the C key could – depending on which keyboard layout you have specified – result in the © (copyright) symbol being displayed.

Let’s say that you want a US keyboard layout with AltGr dead keys, and the Windows key as the Compose key. The setxkbmap command would be:

user $ setxkbmap -layout us -variant altgr-intl -option compose:lwin

Alternatively, the file /etc/X11/xorg.conf.d/00-keyboard.conf to make that configuration permanent would contain:

Section "InputClass"
Identifier "keyboard"
MatchIsKeyboard "yes"
Option "XkbModel" "pc105"
Option "XkbLayout" "us"
Option "XkbVariant" "altgr-intl"
Option "XkbOptions" "compose:lwin"
EndSection

However, the problem with specifying the Windows key as the Compose Key is that the Windows key is usually the key that makes a desktop environment display the applications menu, so an alternative Compose Key needs to be chosen.

You can play around with the XkbModel, XkbLayout, XkbVariant and XkbOptions options to see what works. Look in the file /usr/share/X11/xkb/rules/xorg.lst to find out what are permissible/available.

Using the example of a generic US International keyboard layout with AltGr dead keys, let’s check what options for the model, layout, variant, option and Compose Key are available:

model

user $ awk '/\!\ model/{flag=1;next}/\!\ layout/{flag=0}flag' /usr/share/X11/xkb/rules/xorg.lst | grep Generic
  pc101           Generic 101-key PC
  pc102           Generic 102-key PC
  pc104           Generic 104-key PC
  pc104alt        Generic 104-key PC with L-shaped Enter key
  pc105           Generic 105-key PC

layout

user $ awk '/\!\ layout/{flag=1;next}/\!\ variant/{flag=0}flag' /usr/share/X11/xkb/rules/xorg.lst | grep "US"
  us              English (US)

variant

user $ awk '/\!\ variant/{flag=1;next}/\!\ option/{flag=0}flag' /usr/share/X11/xkb/rules/xorg.lst | grep dead | grep "us:"
  intl            us: English (US, intl., with dead keys)
  dvorak-intl     us: English (Dvorak, intl., with dead keys)
  altgr-intl      us: English (intl., with AltGr dead keys)
  workman-intl    us: English (Workman, intl., with dead keys)

option

user $ tac /usr/share/X11/xkb/rules/xorg.lst | awk '/\!\ option/ {exit} 1' | tac | grep ralt
  lv3:ralt_switch      Right Alt
  lv3:ralt_switch_multikey Right Alt; Shift+Right Alt as Compose
  lv3:ralt_alt         Right Alt never chooses 3rd level
  ctrl:rctrl_ralt      Right Ctrl as Right Alt
  compose:ralt         Right Alt
  lv5:ralt_switch      Right Alt chooses 5th level
  lv5:ralt_switch_lock Right Alt chooses 5th level and acts as a one-time lock if pressed with another 5th level chooser
  lv5:ralt_switch      Right Alt chooses 5th level
  lv5:ralt_switch_lock Right Alt chooses 5th level and acts as a one-time lock if pressed with another 5th level chooser
  korean:ralt_hangul   Make right Alt a Hangul key
  korean:ralt_hanja    Make right Alt a Hanja key

Compose Key

user $ grep "compose:" /usr/share/X11/xkb/rules/base.lst
  compose:ralt         Right Alt
  compose:lwin         Left Win
  compose:lwin-altgr   3rd level of Left Win
  compose:rwin         Right Win
  compose:rwin-altgr   3rd level of Right Win
  compose:menu         Menu
  compose:menu-altgr   3rd level of Menu
  compose:lctrl        Left Ctrl
  compose:lctrl-altgr  3rd level of Left Ctrl
  compose:rctrl        Right Ctrl
  compose:rctrl-altgr  3rd level of Right Ctrl
  compose:caps         Caps Lock
  compose:caps-altgr   3rd level of Caps Lock
  compose:102          The "<Less/Greater>" key
  compose:102-altgr    3rd level of "<Less/Greater>" key
  compose:paus         Pause
  compose:prsc         PrtSc
  compose:sclk         Scroll Lock

(Not all keyboard layouts have a ‘<Less/Greater>’ key, a single key with both < and > symbols on it.)

The following works for me in LXQt with a US keyboard layout:

user $ setxkbmap -layout us -variant altgr-intl -option compose:paus

With the above configuration, I press:

AltGr+a to get á
AltGr+` then a to get à
AltGr+~ then a to get ã
AltGr+e to get é
AltGr+` then e to get è
AltGr+^ then e to get ê
AltGr+~ then e to get
AltGr+o to get ó
AltGr+n to get ñ
AltGr+c to get ©
AltGr+< to get ç
AltGr+s to get ß
AltGr+? to get ¿

and so on, and I press:

Pause then o then o to get °
Pause then o then c to get ©
Pause then ~ then a to get ã
Pause then ~ then e to get
Pause then ^ then 2 to get ²
Pause then _ then 2 to get
Pause then 8 then 8 to get
Pause then E then = to get
Pause then . then . to get
Pause then then > to get
Pause then < then to get
Pause then < then 3 to get
Pause then CCCP to get

and so on. Notice that some characters are available using either method (©, ã and are three examples shown above). A full list of Compose Key characters can be found in the file /usr/share/X11/locale/<locale>/Compose in your installation. For the US layout keyboard the list is in the file /usr/share/X11/locale/en_US.UTF-8/Compose. Various lists of Compose Key sequences and the resulting symbols can also be found on the Web.

To make the configuration in the aforementioned setxkbmap command permanent I would edit the file /etc/X11/xorg.conf.d/00-keyboard.conf to contain the following:

Section "InputClass"
Identifier "keyboard"
MatchIsKeyboard "yes"
Option "XkbModel" "pc105"
Option "XkbLayout" "us"
Option "XkbVariant" "altgr-intl"
Option "XkbOptions" "compose:paus"
EndSection

Let’s say I want to be able to switch between British (gb), US (us), Brazilian (br) and Iberian Spanish (es) keyboard layouts by using Alt+Shft on my laptop with a UK keyboard. I could use the command:

user $ setxkbmap -model pc105 -layout gb,us,br,es -variant ,altgr-intl,, -option grp:alt_shift_toggle,compose:paus

The commas in the -variant option means the ‘altgr-intl‘ option applies solely to the US layout. The Compose Key option in the -option options will work for all layouts.

I could make that configuration permanent in /etc/X11/xorg.conf.d/00-keyboard.conf:

Section "InputClass"
Identifier "keyboard"
MatchIsKeyboard "yes"
Option "XkbModel" "pc105"
Option "XkbLayout" "gb,us,br,es"
Option "XkbVariant" ",altgr-intl,,"
Option "XkbOptions" "grp:alt_shift_toggle,compose:paus"
EndSection

Note that I would not be able to specify ‘altgr-intl‘ as a variant for the gb, br and es layouts I use because the variant ‘altgr-intl‘ is not available in those layouts:

user $ awk '/\!\ variant/{flag=1;next}/\!\ option/{flag=0}flag' /usr/share/X11/xkb/rules/xorg.lst | grep dead | grep "gb:"
  intl            gb: English (UK, intl., with dead keys)
user $ awk '/\!\ variant/{flag=1;next}/\!\ option/{flag=0}flag' /usr/share/X11/xkb/rules/xorg.lst | grep dead | grep "br:"
  nodeadkeys      br: Portuguese (Brazil, no dead keys)
user $ awk '/\!\ variant/{flag=1;next}/\!\ option/{flag=0}flag' /usr/share/X11/xkb/rules/xorg.lst | grep dead | grep "es:"
  nodeadkeys      es: Spanish (no dead keys)
  deadtilde       es: Spanish (dead tilde)
  sundeadkeys     es: Spanish (with Sun dead keys)

 
3. Virtual Terminal (TTY console) keyboard configuration

Although this post is about keyboard configuration for X Windows, I should briefly mention that configurations for X Windows do not apply to virtual terminals (TTY consoles).

If you’re using a Linux distribution running OpenRC, you specify the persistent console keymap in the file /etc/conf.d/keymaps. You can find out which console keymaps are available by examining the directories under /usr/share/keymaps/. For example, the following console keymaps are available for US keyboards in Gentoo Linux:

user $ ls /usr/share/keymaps/i386/qwerty/us*
/usr/share/keymaps/i386/qwerty/us-acentos.map.gz
/usr/share/keymaps/i386/qwerty/us.map.gz
/usr/share/keymaps/i386/qwerty/us1.map.gz

so you would be able to specify one of the following in /etc/conf.d/keymaps:

keymap="us-acentos"

keymap="us"

keymap="us1"

It is also possible to change the console keymap (non-persistent) from the command line. For example, to switch to a UK keyboard layout for a TTY console:

root # loadkeys uk

(notice it is not ‘gb‘ in the case of TTY consoles), or to switch to an Italian Apple Macintosh keyboard layout for a TTY console:

root # loadkeys mac-it

and so on.

If you’re using a Linux distribution running systemd, see my 2020 blog post ‘Reconfiguring the time zone, locales and keymaps in Sabayon Linux‘ for the commands to list and configure TTY console keymaps. The persistent TTY console keymap is specified in the file /etc/vconsole.conf, which can be edited directly and is also edited by the ‘localectl set-keymap‘ command mentioned in that post. The loadkeys command can also be used as described above to change (non-persistent) the keyboard layout for the TTY console.

How to patch kde-plasma/plasma-firewall-5.21.2 for UFW in Gentoo Linux with OpenRC

Unfortunately plasma-firewall-5.21.2, a new Plasma frontend for firewalld and UFW, has been written only for Linux installations with systemd. However, I use OpenRC and syslog-ng in Gentoo Linux and wanted to try to get plasma-firewall to work on my laptop which uses UFW. I therefore set about patching plasma-firewall-5.21.2. I did not touch the firewalld part of plasma-firewall, as I do not use firewalld (and the plasma-firewall code for firewalld is more complicated). Below is what I did.

root # wget https://invent.kde.org/plasma/plasma-firewall/-/archive/Plasma/5.21/plasma-firewall-Plasma-5.21.tar.gz
root # tar -xzf plasma-firewall-Plasma-5.21.tar.gz
root # cp -pr plasma-firewall-Plasma-5.21 a
root # cp -pr plasma-firewall-Plasma-5.21 b
root # nano b/kcm/backends/ufw/ufwclient.cpp # Apply changes shown in Part 1 below.
root # nano b/kcm/backends/ufw/helper/helper.cpp # Apply changes shown in Part 2 below.
root # nano /usr/bin/print_ufw_messages # Create Bash script shown in Part 2 below.
root # chmod 755 /usr/bin/print_ufw_messages
root # nano b/kcm/backends/ufw/ufwlogmodel.cpp # Apply changes shown in Part 3 below.
root # diff -ruN a b > plasma-firewall-5.21.2-ufw.patch
root # mkdir -p /etc/portage/patches/kde-plasma/plasma-firewall-5.21.2
root # cp plasma-firewall-5.21.2-ufw.patch /etc/portage/patches/kde-plasma/plasma-firewall-5.21.2/
root # emerge -1v plasma-firewall
root # nano /etc/syslog-ng/syslog-ng.conf # Apply changes shown in Part 4 below.

You should now be able to use plasma-firewall for UFW in KDE Plasma’s ‘System Settings’ > ‘Firewall’ in the Network section, although I have not tried all the functions. Additionally, I believe there may be some outstanding bugs in the original 5.21.2 version of the Plasma module when using it with systemd.

Part 1

In /kcm/backends/ufw/ufwclient.cpp change:

bool UfwClient::isCurrentlyLoaded() const
{
QProcess process;
const QString name = "systemctl";
const QStringList args = {"status", "ufw"};

process.start(name, args);
process.waitForFinished();

// systemctl returns 0 for status if the app is loaded, and 3 otherwise.
qDebug() << "Ufw is loaded?" << (process.exitCode() == EXIT_SUCCESS);

return process.exitCode() == EXIT_SUCCESS;
}

to:

bool UfwClient::isCurrentlyLoaded() const
{
QProcess process;
const QString name = "rc-service";
const QStringList args = {"--exists", "ufw"};

process.start(name, args);
process.waitForFinished();

// "rc-service --exists" returns 0 if the app is loaded, and -1 otherwise.
qDebug() << "Ufw is loaded?" << (process.exitCode() == EXIT_SUCCESS);

return process.exitCode() == EXIT_SUCCESS;
}

Part 2

In /kcm/backends/ufw/helper/helper.cpp change:

QStringList getLogFromSystemd(const QString &lastLine)
{
QString program = "journalctl";
QStringList arguments {"-xb","-n", "100","-g", "UFW"};

QProcess myProcess;
myProcess.start(program, arguments);
myProcess.waitForFinished();

auto resultString = QString(myProcess.readAllStandardOutput());
auto resultList = resultString.split("\n");

// Example Line from Systemd:
// Dec 06 17:42:45 tomatoland kernel: [UFW BLOCK] IN=wlan0 OUT= MAC= SRC=192.168.50.181 DST=224.0.0.252 LEN=56 TOS=0x00
//     PREC=0x00 TTL=255 ID=52151 PROTO=UDP SPT=5355 DPT=5355 LEN=36
// We need to remove everything up to the space after ']'.

QStringList result;
for(const QString& line : resultList) {
if (!lastLine.isEmpty() && line == lastLine) {
result.clear();
continue;
}
result.append(line);
}
return result;
}

to:

QStringList getLogFromSystemd(const QString &lastLine)
{
QString program = "print_ufw_messages";
QStringList arguments {"UFW", "100"};

QProcess myProcess;
myProcess.start(program, arguments);
myProcess.waitForFinished();

auto resultString = QString(myProcess.readAllStandardOutput());
auto resultList = resultString.split("\n");

// Example line from /var/log/messages populated by sylog-ng:
// Mar  6 00:10:19 localhost kernel: [UFW BLOCK] IN=wlan0 OUT= MAC=00:12:5b:8a:83:6d:b7:2a:da:59:d4:10:09:00 SRC=192.168.1.27
//      DST=192.168.1.139 LEN=52 TOS=0x00 PREC=0x00 TTL=64 ID=41659 DF PROTO=TCP SPT=445 DPT=52140 WINDOW=260 RES=0x00 ACK URGP=0
// We need to remove everything up to the space after ']'.

QStringList result;
for(const QString& line : resultList) {
if (!lastLine.isEmpty() && line == lastLine) {
result.clear();
continue;
}
result.append(line);
}
return result;
}

where the program print_ufw_messages is a user-created Bash script /usr/bin/print_ufw_messages (-rwxr-xr-x root.root) containing:

#!/bin/bash
awk '{if (/localhost syslog-ng/ && /syslog-ng starting up/ && !/COMMAND/) {chunk=""} else {chunk=chunk $0 RS}} END {printf "%s", chunk}' /var/log/messages | grep "$1" | head -n "$2" | grep -v print_ufw_messages

Part 3

During my investigations into how to modify the plasma-firewall-5.21.2 source code, I discovered a bug in the source code. In /kcm/backends/ufw/ufwlogmodel.cpp change:

for (const QString& key : {"IN", "SRC", "DST", "PROTO", "STP", "DPT"}) {

to:

for (const QString& key : {"IN", "SRC", "DST", "PROTO", "SPT", "DPT"}) {

i.e. “STP” needs to be changed to “SPT“.

Part 4

I am not sure if this makes a difference to plasma-firewall (which was coded assuming systemd-journald is installed), but the default date format for messages in /var/log/messages printed by syslog-ng has only one digit in the day of the month when it is less than the 10th day of the month. For example:

Mar  9 03:09:39 clevow230ss syslog-ng[23735]:  syslog-ng starting up; version='3.30.1'

However, systemd-journalctl always outputs two-digit days of the month, and I think (but am not certain) the following date format might be needed in order for the existing code in /kcm/backends/ufw/ufwlogmodel.cpp to parse the syslog-ng output correctly:

Mar 09 03:09:39 clevow230ss syslog-ng[23735]:  syslog-ng starting up; version='3.30.1'

Therefore edit /etc/syslog-ng/syslog-ng.conf and add a template:

template template_date_format {
template("${MONTH_ABBREV} ${DAY} ${HOUR}:${MIN}:${SEC} ${HOST} ${MSGHDR}${MSG}\n");
template_escape(no);
};

and change the line:

destination messages { file("/var/log/messages"); };

to:

destination messages { file("/var/log/messages" template(template_date_format)); };

Then restart syslog-ng:

root # rc-service syslog-ng restart

From now on the day of the month is always two digits (01, 02,…31) in /var/log/messages.

Recreating missing WINE menu entries and Desktop Configuration Files in Lubuntu 20.10

I use a few Windows applications I installed via WINE in my user account on my family’s desktop machine running Lubuntu 20.10 (LXQt Desktop Environment). A few days ago I logged in and found that the icons for the Windows applications had disappeared from my Desktop, and the ‘Wine’ entry in the LXQt applications menu had also disappeared. This was rather bizarre and I still have no idea why it happened. However, the directories for each WINEPREFIX were still present so I set about recreating the missing menu entries and Desktop Configuration Files. I reinstalled one of the Windows applications, and its icon reappeared on my Desktop but the ‘Wine’ entry in the LXQt applications menu did not reappear. I had to delve into WINE menu structures to fix everything.

Three key directories are involved in defining the ‘Wine’ menu entries:

~/.config/menus/applications-merged/

~/.local/share/applications/wine/Programs/

~/.local/share/desktop-directories/

The role and contents of these directories are best explained by studying an example of an application in the ‘Wine’ menu. One of the Windows applications I had installed previously via WINE is Visio Professional 5, and I will use it as an example to illustrate how I got everything working again. I had installed the application using a WINEPREFIX of ~/.wine-visio, and the missing icon on my Desktop had been labelled ‘Visio Professional’.

1. I recreated the directory ~/.local/share/applications/wine/Programs/Visio Professional/:

user $ mkdir -p ~/.local/share/applications/wine/Programs/Visio\ Professional

2. I recreated the file ~/.config/menus/applications-merged/wine-Programs-Visio Professional-Visio Professional.menu (chmod 664) containing the following:

<!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
"http://www.freedesktop.org/standards/menu-spec/menu-1.0.dtd">
<Menu>
  <Name>Applications</Name>
  <Menu>
    <Name>wine-wine</Name>
    <Directory>wine-wine.directory</Directory>
  <Menu>
    <Name>wine-Programs</Name>
    <Directory>wine-Programs.directory</Directory>
  <Menu>
    <Name>wine-Programs-Visio Professional</Name>
    <Directory>wine-Programs-Visio Professional.directory</Directory>
    <Include>
      <Filename>wine-Programs-Visio Professional-Visio Professional.desktop</Filename>
    </Include>
  </Menu>
  </Menu>
  </Menu>
</Menu>

wine-wine‘ corresponds to the ‘Wine’ entry in the top-level LXQt applications menu.

wine-Programs‘ corresponds to the second-level menu entry ‘Programs’ (i.e. ‘Wine’ > ‘Programs’).

wine-Programs-Visio Professional‘ corresponds to the third-level menu entry ‘Visio Professional’ (i.e. ‘Wine’ > ‘Programs’ > ‘Visio Professional’).

wine-Programs-Visio Professional-Visio Professional‘ corresponds to the fourth-level menu entry ‘Visio Professional’ for the application itself (i.e. ‘Wine’ > ‘Programs’ > ‘Visio Professional’ > ‘Visio Professional’).

3. Notice in the above file the syntax for menu directory files corresponding to menu entries. I had to recreate the directory files as follows:

~/.local/share/desktop-directories/wine-wine.directory (chmod 664) containing:

[Desktop Entry]
Type=Directory
Name=Wine
Icon=wine

~/.local/share/desktop-directories/wine-Programs.directory (chmod 664) containing:

[Desktop Entry]
Type=Directory
Name=Programs
Icon=folder

~/.local/share/desktop-directories/wine-Programs-Visio Professional.directory (chmod 664) containing:

[Desktop Entry]
Type=Directory
Name=Visio Professional
Icon=folder

4. I recreated the file ~/.local/share/applications/wine/Programs/Visio Professional/Visio Professional.desktop (chmod 664) containing:

[Desktop Entry]
Name=Visio Professional
Exec=env WINEPREFIX="/home/fitzcarraldo/.wine-visio" wine-stable /home/fitzcarraldo/.wine-visio/drive_c/Program\ Files/Visio/Visio32.EXE
Type=Application
StartupNotify=true
Path=/home/fitzcarraldo/.wine-visio/dosdevices/c:/Program Files/Visio
Comment=Visio Professional
Icon=AAE3_Visio32.0
StartupWMClass=visio32.exe

and I copied the file to ~/Desktop/Visio Professional.desktop (chmod 755). I right-clicked on ~/Desktop/Visio Professional.desktop and ticked ‘Trust this executable’. It is not necessary to do that for .desktop files in ~/.local/share/applications/wine/Programs/ and its sub-directories.

I used the command ‘locate -i visio | grep -i png‘ to find the name of the existing icon file (AAE3_Visio32.0.png) that WINE had created when I originally installed the application. The StartupWMClass variable seems to be the same as the application’s executable file name but all in lower case. I found the Exec and Path entries by examining the existing sub-directories and files in ~/.wine-visio/drive_c/.

The ‘Wine’ menu entry and sub-entries all reappeared correctly after I logged out and back in, and I could again launch the application either by selecting the application from the LXQt application menu or by double-clicking on the application’s icon on my Desktop.

Resulting application menu entry for Windows application Visio Professional 5

Resulting application menu entry for Windows application Visio Professional 5

The Windows applications are now all usable again, although I wish I knew what caused the problem in the first place.

Anyway the exercise was not a waste of time because I now know how to modify WINE menus. Some Windows application installation programs in WINE result in a menu entry ‘Wine’ > ‘Programs’ > ‘<application>’ > ‘<application>’ whereas others result in a menu entry ‘Wine’ > ‘Programs’ > ‘<application>’, and I now know how to change the menu hierarchy if I want to. For example, I have just now installed the Windows application SumatraPDF to read e-books. The SumatraPDF installation program launched using WINE resulted in a menu entry ‘Wine’ > ‘Programs’ > ‘SumatraPDF’. The resulting file ~/.config/menus/applications-merged/wine-Programs-SumatraPDF.menu contained the following:

<!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
"http://www.freedesktop.org/standards/menu-spec/menu-1.0.dtd">
<Menu>
  <Name>Applications</Name>
  <Menu>
    <Name>wine-wine</Name>
    <Directory>wine-wine.directory</Directory>
  <Menu>
    <Name>wine-Programs</Name>
    <Directory>wine-Programs.directory</Directory>
    <Include>
      <Filename>wine-Programs-SumatraPDF.desktop</Filename>
    </Include>
  </Menu>
  </Menu>
</Menu>
Original application menu entry for Windows application SumatraPDF installed via WINE

Original application menu entry for Windows application SumatraPDF installed via WINE

There was no .directory file for SumatraPDF in ~/.local/share/desktop-directories/ because the menu entry to launch SumatraPDF is under ‘Wine’ > ‘Programs’. If I wanted to change the menu entry to be under ‘Wine’ > ‘Programs’ > ‘SumatraPDF’ I could modify the contents of the file ~/.config/menus/applications-merged/wine-Programs-SumatraPDF.menu, create the file ~/.local/share/desktop-directories/wine-Programs-SumatraPDF.directory, create the directory ~/.local/share/applications/wine/Programs/SumatraPDF/ and move the file ~/.local/share/applications/wine/Programs/SumatraPDF.desktop to ~/.local/share/applications/wine/Programs/SumatraPDF/SumatraPDF.desktop. I decided to do this as an exercise:

user $ mkdir -p ~/.local/share/applications/wine/Programs/SumatraPDF/
$ mv ~/.local/share/applications/wine/Programs/SumatraPDF.desktop ~/.local/share/applications/wine/Programs/SumatraPDF/SumatraPDF.desktop

I edited the file ~/.config/menus/applications-merged/wine-Programs-SumatraPDF.menu so it now contains the following:

<!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
"http://www.freedesktop.org/standards/menu-spec/menu-1.0.dtd">
<Menu>
  <Name>Applications</Name>
  <Menu>
    <Name>wine-wine</Name>
    <Directory>wine-wine.directory</Directory>
  <Menu>
    <Name>wine-Programs</Name>
    <Directory>wine-Programs.directory</Directory>
  <Menu>
    <Name>wine-Programs-SumatraPDF</Name>
    <Directory>wine-Programs-SumatraPDF.directory</Directory>
    <Include>
      <Filename>wine-Programs-SumatraPDF-SumatraPDF.desktop</Filename>
    </Include>
  </Menu>
  </Menu>
  </Menu>
</Menu>

I created the file ~/.local/share/desktop-directories/wine-Programs-SumatraPDF.directory containing the following:

[Desktop Entry]
Type=Directory
Name=SumatraPDF
Icon=folder

I logged out and back in, and the application menu entry for SumatraPDF had changed from:

‘Wine’ > ‘Programs’ > ‘SumatraPDF’

where the second-level entry in the ‘Wine’ menu has a folder icon,

to:

‘Wine’ > ‘Programs’ > ‘SumatraPDF’ > ‘SumatraPDF’

where the second-level and third-level entries in the ‘Wine’ menu have folder icons. The other Windows applications in my user account are at the fourth level of the WINE menu, so the Wine menu for SumatraPDF is now consistent with the other Windows applications.

Modified application menu entry for Windows application SumatraPDF installed via WINE

Modified application menu entry for Windows application SumatraPDF installed via WINE

By the way, the Desktop Configuration File ~/Desktop/SumatraPDF.desktop created by WINE contains the following:

[Desktop Entry]
Name=SumatraPDF
Exec=env WINEPREFIX="/home/fitzcarraldo/.wine-sumatra" wine-stable C:\\\\users\\\\fitzcarraldo\\\\Local\\ Settings\\\\Application\\ Data\\\\SumatraPDF\\\\SumatraPDF.exe 
Type=Application
StartupNotify=true
Path=/home/fitzcarraldo/.wine-sumatra/dosdevices/c:/users/fitzcarraldo/Local Settings/Application Data/SumatraPDF
Icon=3EBA_SumatraPDF.0
StartupWMClass=sumatrapdf.exe

and the Desktop Configuration File ~/.local/share/applications/wine/Programs/SumatraPDF.desktop created by WINE contains the following:

[Desktop Entry]
Name=SumatraPDF
Exec=env WINEPREFIX="/home/fitzcarraldo/.wine-sumatra" wine-stable C:\\\\windows\\\\command\\\\start.exe /Unix /home/fitzcarraldo/.wine-sumatra/dosdevices/c:/users/fitzcarraldo/Start\\ Menu/Programs/SumatraPDF.lnk
Type=Application
StartupNotify=true
Path=/home/fitzcarraldo/.wine-sumatra/dosdevices/c:/users/fitzcarraldo/Local Settings/Application Data/SumatraPDF
Icon=3EBA_SumatraPDF.0
StartupWMClass=sumatrapdf.exe

I am not sure why there is a difference in the Exec command in the two files, but that is an investigation for another day.

Addendum (13 March 2021): KDE in Gentoo Linux on my laptops has essentially the same menu structure and files for Windows applications installed via WINE. However, unlike LXQt in Lubuntu 20.10, in addition to the individual .menu file per Windows application KDE has a file (~/.config/menus/applications-kmenuedit.menu) that defines the entire KDE applications menu, not just the Windows applications under ‘Wine’ in the applications menu. To make changes to the menu structure of Windows applications in KDE I therefore have to perform a further step; I have to edit the file ~/.config/menus/applications-kmenuedit.menu, which I have found to be a hassle. The file seems to collect cruft every time a menu entry is created, moved, changed, or deleted. Over time the file can become very large and confusing to read, and it can still contain entries for applications removed years ago. Also, some of the edits I make in the file are not accepted and KDE either reverts the contents or alters the contents in a way I do not want. Therefore I make a copy of the file before editing it, just in case I make a mistake and have to put things back to the way they were.

Removing qtwebengine from a Gentoo Linux installation

At the beginning of March I updated the world set in Gentoo Testing (~amd64) running the KDE suite (Plasma, Frameworks and Applications) on my secondary laptop, an eleven-year-old Compal NBLB2. It has a first-generation Core i7 CPU and the maximum amount of RAM that can be installed in that model (8 GB).

root # uname -a
Linux meshedgedx 5.0.11-gentoo #1 SMP Fri Jun 7 15:33:06 BST 2019 x86_64 Intel(R) Core(TM) i7 CPU Q 720 @ 1.60GHz GenuineIntel GNU/Linux

Gentoo Linux being a source-based distribution, updates to the largest packages take hours to build on older machines. Actually, some packages can take hours to build on newer machines too. On this older laptop I therefore merge the www-client/firefox-bin binary package instead of the www-client/firefox source-code package, and have installed Microsoft Office 2007 running in WINE instead of trying to merge the app-office/libreoffice source-code package (app-office/libreoffice-bin cannot be merged in this Testing installation because of incompatibility with the versions of installed dependencies, so it would only be a viable alternative binary package in a Stable installation).

Possibly the worst source-code package to build is dev-qt/qtwebengine. Nowadays it takes a ridiculous amount of time to build on this laptop, even with the jumbo-build USE flag set and MAKEOPTS="-j4" or even MAKEOPTS="-j1". The latest merge on the laptop took more than 14 hours:

root # genlop -t qtwebengine | tail -n 3
     Fri Mar  5 02:02:07 2021 >>> dev-qt/qtwebengine-5.15.2_p20210224
       merge time: 14 hours, 14 minutes and 7 seconds.


That is actually quite fast for that laptop; qtwebengine has sometimes taken two days to merge in the past.

What a waste of time and electricity, not to mention the unnecessary wear on the laptop (fan bearing; prolonged heat on components; etc.).

This one package is such a hassle to merge that it had me wondering if I should switch from Gentoo Linux to a binary distribution. Even on my six-year-old Compal W230SS laptop with a fourth-generation Core i7 CPU and 16 GB of RAM, qtwebengine takes circa five hours to merge. After several years putting up with this scourge of source-based Linux distributions on my secondary laptop, I had finally had enough and decided to excise the package, which did not look like an easy task with the full KDE suite installed. This is how I did it…

1. First I made sure the installation was up-to-date (see my earlier post ‘My system upgrade procedure for Gentoo Linux‘ for the steps I normally use to update all packages to their latest versions).

2. I ascertained which packages depended on qtwebengine:

root # equery depends qtwebengine
 * These packages depend on qtwebengine:
kde-apps/kaccounts-providers-20.12.2 (>=dev-qt/qtwebengine-5.15.2:5)
kde-apps/kalgebra-20.12.2 (>=dev-qt/qtwebengine-5.15.2:5[widgets])
kde-apps/kdenlive-20.12.2 (webengine ? >=dev-qt/qtwebengine-5.15.2:5)
kde-apps/kimagemapeditor-20.12.2 (>=dev-qt/qtwebengine-5.15.2:5[widgets])
kde-apps/ktp-text-ui-20.12.2 (>=dev-qt/qtwebengine-5.15.2:5[widgets])
kde-apps/marble-20.12.2 (webengine ? >=dev-qt/qtwebengine-5.15.2:5[widgets])
kde-apps/parley-20.12.2 (>=dev-qt/qtwebengine-5.15.2:5[widgets])
kde-plasma/kdeplasma-addons-5.21.1 (webengine ? >=dev-qt/qtwebengine-5.15.2:5)
kde-plasma/libksysguard-5.21.1 (webengine ? >=dev-qt/qtwebengine-5.15.2:5)
net-libs/signon-ui-0.15_p20171022-r1 (dev-qt/qtwebengine:5)
net-p2p/ktorrent-20.12.2 (rss ? >=dev-qt/qtwebengine-5.15.2:5)
                         (webengine ? >=dev-qt/qtwebengine-5.15.2:5)
www-client/falkon-3.1.0-r1 (>=dev-qt/qtwebengine-5.12.3:5[widgets])

3. I disabled the USE flag ‘webengine‘ globally:

root # nano /etc/portage/make.conf # Add -webengine to the list of USE flags

4. I merged the world set in order to incorporate the USE flag change:

root # emerge -uvDN @world

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

Calculating dependencies... done!
[ebuild   R    ] kde-apps/marble-20.12.2:5/20.12::gentoo  USE="dbus geolocation kde nls pbf phonon -aprs -debug -designer -gps -handbook -shapefile -test -webengine*" 0 KiB
[ebuild   R    ] kde-apps/kdeedu-meta-20.12.2:5::gentoo  USE="-webengine*" 0 KiB
[ebuild   R    ] kde-apps/kdecore-meta-20.12.2:5::gentoo  USE="share thumbnail -handbook -webengine*" 0 KiB
[ebuild   R    ] net-p2p/ktorrent-20.12.2:5::gentoo  USE="bwscheduler downloadorder infowidget ipfilter kross logviewer magnetgenerator mediaplayer rss scanfolder shutdown stats upnp zeroconf -debug -handbook -test -webengine*" 0 KiB
[ebuild   R    ] kde-apps/kdenetwork-meta-20.12.2:5::gentoo  USE="bittorrent -dropbox -webengine*" 0 KiB
[ebuild   R    ] kde-apps/kdeutils-meta-20.12.2:5::gentoo  USE="cups rar -7zip -floppy -gpg -lrz -webengine*" 0 KiB

Total: 6 packages (6 reinstalls), Size of downloads: 0 KiB

>>> Verifying ebuild manifests
>>> Emerging (1 of 6) kde-apps/marble-20.12.2::gentoo
>>> Emerging (2 of 6) kde-apps/kdecore-meta-20.12.2::gentoo
>>> Emerging (3 of 6) net-p2p/ktorrent-20.12.2::gentoo
>>> Emerging (4 of 6) kde-apps/kdeutils-meta-20.12.2::gentoo
>>> Installing (2 of 6) kde-apps/kdecore-meta-20.12.2::gentoo
>>> Installing (4 of 6) kde-apps/kdeutils-meta-20.12.2::gentoo
>>> Installing (3 of 6) net-p2p/ktorrent-20.12.2::gentoo
>>> Emerging (5 of 6) kde-apps/kdenetwork-meta-20.12.2::gentoo
>>> Installing (5 of 6) kde-apps/kdenetwork-meta-20.12.2::gentoo
>>> Installing (1 of 6) kde-apps/marble-20.12.2::gentoo
>>> Emerging (6 of 6) kde-apps/kdeedu-meta-20.12.2::gentoo
>>> Installing (6 of 6) kde-apps/kdeedu-meta-20.12.2::gentoo
>>> Jobs: 6 of 6 complete                           Load avg: 1.93, 3.62, 3.86
>>> Auto-cleaning packages...

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

 * GNU info directory index is up-to-date.
 * After world updates, it is important to remove obsolete packages with
 * emerge --depclean. Refer to `man emerge` for more information.

5. I uninstalled packages that were no longer required by any other packages and also not required by me (I do not use the Falkon browser, Telepathy and KAlgebra, to give a few examples, and so did not mind various specific packages being removed):

root # emerge --ask --depclean

 * Always study the list of packages to be cleaned for any obvious
 * mistakes. Packages that are part of the world set will always
 * be kept.  They can be manually added to this set with
 * `emerge --noreplace `.  Packages that are listed in
 * package.provided (see portage(5)) will be removed by
 * depclean, even if they are part of the world set.
 * 
 * As a safety measure, depclean will not remove any packages
 * unless *all* required dependencies have been resolved.  As a
 * consequence of this, it often becomes necessary to run 
 * `emerge --update --newuse --deep @world` prior to depclean.

Calculating dependencies... done!
>>> Calculating removal order...

>>> These are the packages that would be unmerged:                                                                                                                                                                                                

 kde-apps/parley
    selected: 20.12.2 
   protected: none 
     omitted: none 

 www-client/falkon
    selected: 3.1.0-r1 
   protected: none 
     omitted: none 

 kde-apps/kimagemapeditor
    selected: 20.12.2 
   protected: none 
     omitted: none 

 kde-apps/plasma-telepathy-meta
    selected: 20.12.2 
   protected: none 
     omitted: none 

 kde-apps/kalgebra
    selected: 20.12.2 
   protected: none 
     omitted: none 

 kde-apps/ktp-kded-module
    selected: 20.12.2 
   protected: none 
     omitted: none 

 kde-apps/ktp-desktop-applets
    selected: 20.12.2 
   protected: none 
     omitted: none 

 kde-apps/ktp-accounts-kcm
    selected: 20.12.2 
   protected: none 
     omitted: none 

 kde-apps/ktp-send-file
    selected: 20.12.2 
   protected: none 
     omitted: none 

 kde-apps/ktp-approver
    selected: 20.12.2 
   protected: none 
     omitted: none 

 kde-apps/ktp-auth-handler
    selected: 20.12.2 
   protected: none 
     omitted: none 

 kde-apps/ktp-contact-runner
    selected: 20.12.2 
   protected: none 
     omitted: none 

 kde-apps/ktp-text-ui
    selected: 20.12.2 
   protected: none 
     omitted: none 

 kde-apps/signon-kwallet-extension
    selected: 20.12.2 
   protected: none 
     omitted: none 

 net-im/telepathy-connection-managers
    selected: 2-r2 
   protected: none 
     omitted: none 

 kde-apps/ktp-filetransfer-handler
    selected: 20.12.2 
   protected: none 
     omitted: none 

 kde-apps/ktp-contact-list
    selected: 20.12.2 
   protected: none 
     omitted: none 

 net-irc/telepathy-idle
    selected: 0.2.0-r3 
   protected: none 
     omitted: none 

 net-voip/telepathy-salut
    selected: 0.8.1-r3 
   protected: none 
     omitted: none 

 net-voip/telepathy-gabble
    selected: 0.18.4-r2 
   protected: none 
     omitted: none 

 kde-apps/ktp-common-internals
    selected: 20.12.2 
   protected: none 
     omitted: none 

 net-libs/telepathy-accounts-signon
    selected: 2.1 
   protected: none 
     omitted: none 

 net-libs/libnice
    selected: 0.1.15 
   protected: none 
     omitted: none 

 net-libs/telepathy-logger-qt
    selected: 17.09.0 
   protected: none 
     omitted: none 

 net-im/telepathy-logger
    selected: 0.8.2-r1 
   protected: none 
     omitted: none 

 net-libs/gupnp-igd
    selected: 0.2.5-r10 
   protected: none 
     omitted: none 

 net-libs/libsignon-glib
    selected: 2.1 
   protected: none 
     omitted: none 

 net-libs/telepathy-qt
    selected: 0.9.8 
   protected: none 
     omitted: none 

 net-libs/gupnp
    selected: 1.2.4 
   protected: none 
     omitted: none 

 net-libs/gssdp
    selected: 1.2.3 
   protected: none 
     omitted: none 

 net-libs/libsoup
    selected: 2.70.0 
   protected: none 
     omitted: none 

 net-libs/libpsl
    selected: 0.21.1 
   protected: none 
     omitted: none 

 net-libs/glib-networking
    selected: 2.66.0 
   protected: none 
     omitted: none 

 net-im/telepathy-mission-control
    selected: 5.16.5 
   protected: none 
     omitted: none 

 net-libs/telepathy-glib
    selected: 0.24.1-r1 
   protected: none 
     omitted: none 

All selected packages: =kde-apps/ktp-desktop-applets-20.12.2 =kde-apps/ktp-contact-runner-20.12.2 =kde-apps/ktp-contact-list-20.12.2 =net-libs/telepathy-accounts-signon-2.1 =net-libs/telepathy-glib-0.24.1-r1 =net-voip/telepathy-salut-0.8.1-r3 =kde-apps/ktp-text-ui-20.12.2 =net-libs/libsignon-glib-2.1 =net-im/telepathy-connection-managers-2-r2 =kde-apps/ktp-accounts-kcm-20.12.2 =kde-apps/kimagemapeditor-20.12.2 =kde-apps/ktp-common-internals-20.12.2 =kde-apps/parley-20.12.2 =net-libs/libnice-0.1.15 =net-libs/libsoup-2.70.0 =kde-apps/ktp-auth-handler-20.12.2 =net-libs/gssdp-1.2.3 =net-irc/telepathy-idle-0.2.0-r3 =net-libs/libpsl-0.21.1 =kde-apps/kalgebra-20.12.2 =net-libs/gupnp-igd-0.2.5-r10 =kde-apps/ktp-filetransfer-handler-20.12.2 =kde-apps/ktp-send-file-20.12.2 =net-libs/gupnp-1.2.4 =kde-apps/ktp-kded-module-20.12.2 =net-im/telepathy-mission-control-5.16.5 =kde-apps/plasma-telepathy-meta-20.12.2 =net-voip/telepathy-gabble-0.18.4-r2 =net-im/telepathy-logger-0.8.2-r1 =kde-apps/signon-kwallet-extension-20.12.2 =net-libs/telepathy-logger-qt-17.09.0 =net-libs/telepathy-qt-0.9.8 =net-libs/glib-networking-2.66.0 =kde-apps/ktp-approver-20.12.2 =www-client/falkon-3.1.0-r1

>>> 'Selected' packages are slated for removal.
>>> 'Protected' and 'omitted' packages will not be removed.

Would you like to unmerge these packages? [Yes/No] Yes 
>>> Waiting 5 seconds before starting...
>>> (Control-C to abort)...
>>> Unmerging in: 5 4 3 2 1
>>> Unmerging (1 of 35) kde-apps/parley-20.12.2...
>>> Unmerging (2 of 35) www-client/falkon-3.1.0-r1...
>>> Unmerging (3 of 35) kde-apps/kimagemapeditor-20.12.2...
>>> Unmerging (4 of 35) kde-apps/plasma-telepathy-meta-20.12.2...
>>> Unmerging (5 of 35) kde-apps/kalgebra-20.12.2...
>>> Unmerging (6 of 35) kde-apps/ktp-kded-module-20.12.2...
>>> Unmerging (7 of 35) kde-apps/ktp-desktop-applets-20.12.2...
>>> Unmerging (8 of 35) kde-apps/ktp-accounts-kcm-20.12.2...
>>> Unmerging (9 of 35) kde-apps/ktp-send-file-20.12.2...
>>> Unmerging (10 of 35) kde-apps/ktp-approver-20.12.2...
>>> Unmerging (11 of 35) kde-apps/ktp-auth-handler-20.12.2...
>>> Unmerging (12 of 35) kde-apps/ktp-contact-runner-20.12.2...
>>> Unmerging (13 of 35) kde-apps/ktp-text-ui-20.12.2...
>>> Unmerging (14 of 35) kde-apps/signon-kwallet-extension-20.12.2...
>>> Unmerging (15 of 35) net-im/telepathy-connection-managers-2-r2...
>>> Unmerging (16 of 35) kde-apps/ktp-filetransfer-handler-20.12.2...
>>> Unmerging (17 of 35) kde-apps/ktp-contact-list-20.12.2...
>>> Unmerging (18 of 35) net-irc/telepathy-idle-0.2.0-r3...
>>> Unmerging (19 of 35) net-voip/telepathy-salut-0.8.1-r3...
>>> Unmerging (20 of 35) net-voip/telepathy-gabble-0.18.4-r2...
>>> Unmerging (21 of 35) kde-apps/ktp-common-internals-20.12.2...
>>> Unmerging (22 of 35) net-libs/telepathy-accounts-signon-2.1...
>>> Unmerging (23 of 35) net-libs/libnice-0.1.15...
>>> Unmerging (24 of 35) net-libs/telepathy-logger-qt-17.09.0...
>>> Unmerging (25 of 35) net-im/telepathy-logger-0.8.2-r1...
>>> Unmerging (26 of 35) net-libs/gupnp-igd-0.2.5-r10...
>>> Unmerging (27 of 35) net-libs/libsignon-glib-2.1...
>>> Unmerging (28 of 35) net-libs/telepathy-qt-0.9.8...
>>> Unmerging (29 of 35) net-libs/gupnp-1.2.4...
>>> Unmerging (30 of 35) net-libs/gssdp-1.2.3...
>>> Unmerging (31 of 35) net-libs/libsoup-2.70.0...
>>> Unmerging (32 of 35) net-libs/libpsl-0.21.1...
>>> Unmerging (33 of 35) net-libs/glib-networking-2.66.0...
>>> Unmerging (34 of 35) net-im/telepathy-mission-control-5.16.5...
>>> Unmerging (35 of 35) net-libs/telepathy-glib-0.24.1-r1...
Packages installed:   1651
Packages in world:    329
Packages in system:   43
Required packages:    1651
Number removed:       35

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

Notice that the package qtwebengine had not been removed, so something still depended on it.

6. I checked if there were any packages still installed with a dependency on qtwebengine:

root # equery depends qtwebengine
 * These packages depend on qtwebengine:
kde-apps/kaccounts-providers-20.12.2 (>=dev-qt/qtwebengine-5.15.2:5)
kde-apps/kdenlive-20.12.2 (webengine ? >=dev-qt/qtwebengine-5.15.2:5)
kde-apps/marble-20.12.2 (webengine ? >=dev-qt/qtwebengine-5.15.2:5[widgets])
kde-plasma/kdeplasma-addons-5.21.1 (webengine ? >=dev-qt/qtwebengine-5.15.2:5)
kde-plasma/libksysguard-5.21.1 (webengine ? >=dev-qt/qtwebengine-5.15.2:5)
net-libs/signon-ui-0.15_p20171022-r1 (dev-qt/qtwebengine:5)
net-p2p/ktorrent-20.12.2 (rss ? >=dev-qt/qtwebengine-5.15.2:5)
                         (webengine ? >=dev-qt/qtwebengine-5.15.2:5)

As can be seen from the above output, the only remaining installed packages that ‘hard-depended’ on the ‘webengine‘ USE flag were kde-apps/kaccounts-providers-20.12.2 and net-libs/signon-ui-0.15_p20171022-r1.

Additionally, the package net-p2p/ktorrent-20.12.2 still depended on qtwebengine because the rss USE flag was enabled. So I added the line ‘net-p2p/ktorrent -rss‘ to the file /etc/portage/package.use/package.use and re-merged net-p2p/ktorrent. Actually, I re-merged the following packages just in case they needed to be rebuilt, although in retrospect I believe that was unnecessary:

     Fri Mar  5 05:37:26 2021 >>> kde-apps/kdecore-meta-20.12.2
     Fri Mar  5 05:37:55 2021 >>> kde-apps/kdeutils-meta-20.12.2
     Fri Mar  5 05:45:49 2021 >>> net-p2p/ktorrent-20.12.2
     Fri Mar  5 05:46:49 2021 >>> kde-apps/kdenetwork-meta-20.12.2
     Fri Mar  5 05:57:41 2021 >>> kde-apps/marble-20.12.2
     Fri Mar  5 05:58:15 2021 >>> kde-apps/kdeedu-meta-20.12.2

7. By now another day had dawned, so I checked if new versions of the ebuilds for any KDE packages had been uploaded to the Portage repositories:

root # emaint sync -a
root # eix-update && updatedb

8. I rebooted the laptop and checked which packages still depended on qtwebengine. It turned out that only the two packages with a hard-dependency on qtwebengine were still preventing me from removing it:

root # equery depends qtwebengine
 * These packages depend on qtwebengine:
kde-apps/kaccounts-providers-20.12.2 (>=dev-qt/qtwebengine-5.15.2:5)
net-libs/signon-ui-0.15_p20171022-r1 (dev-qt/qtwebengine:5)

9. I checked if any packages depended on those two packages:

root # equery depends kaccounts-providers
 * These packages depend on kaccounts-providers:
kde-misc/kio-gdrive-20.12.2 (>=kde-apps/kaccounts-providers-20.12.2:5)
# equery depends kio-gdrive
 * These packages depend on kio-gdrive:
kde-apps/kdenetwork-meta-20.12.2 (>=kde-misc/kio-gdrive-20.12.2:5)
root # equery depends signon-ui
 * These packages depend on signon-ui:
kde-apps/kaccounts-providers-20.12.2 (net-libs/signon-ui)

So kdenetwork-meta hard-depends on kio-gdrive, which does not make much sense, really, given that not all KDE users have a Google Drive account and those users therefore do not need the kio-gdrive package to be installed.

10. The contents of the kdenetwork-meta-20.12.3 ebuild look like this:

root # cat /usr/portage/kde-apps/kdenetwork-meta/kdenetwork-meta-20.12.3.ebuild
# Copyright 1999-2021 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2

EAPI=7

DESCRIPTION="kdenetwork - merge this to pull in all kdenetwork-derived packages"
HOMEPAGE="https://kde.org/"

LICENSE="metapackage"
SLOT="5"
KEYWORDS="~amd64 ~arm64 ~ppc64 ~x86"
IUSE="+bittorrent dropbox +webengine"

RDEPEND="
        >=kde-apps/kdenetwork-filesharing-${PV}:${SLOT}
        >=kde-apps/kget-${PV}:${SLOT}
        >=kde-apps/kopete-${PV}:${SLOT}
        >=kde-apps/krdc-${PV}:${SLOT}
        >=kde-apps/krfb-${PV}:${SLOT}
        >=kde-apps/zeroconf-ioslave-${PV}:${SLOT}
        >=kde-misc/kdeconnect-${PV}:${SLOT}
        >=kde-misc/kio-gdrive-${PV}:${SLOT}
        >=net-irc/konversation-${PV}:${SLOT}
        bittorrent? (
                >=net-libs/libktorrent-${PV}:${SLOT}
                >=net-p2p/ktorrent-${PV}:${SLOT}
        )
        dropbox? ( >=kde-apps/dolphin-plugins-dropbox-${PV}:${SLOT} )
"

so I created an ebuild for kdenetwork-meta-20.12.3 in my local overlay with the dependency on kio-gdrive removed:

root # mkdir -p /usr/local/portage/kde-apps/kdenetwork-meta
root # cd /usr/local/portage/kde-apps/kdenetwork-meta
root # cp /usr/portage/kde-apps/kdenetwork-meta/kdenetwork-meta-20.12.3.ebuild .
root # nano kdenetwork-meta-20.12.3.ebuild # Delete the line containing ">=kde-misc/kio-gdrive-${PV}:${SLOT}"
root # ebuild kdenetwork-meta-20.12.3.ebuild manifest
>>> Creating Manifest for /usr/local/portage/kde-apps/kdenetwork-meta
root # # eix-update && updatedb

11. I re-merged the world set in order to update all KDE packages that now had a newer ebuild version:

root # emerge -uvDN @world

12. I rechecked the three packages that had depended on qtwebengine:

root # equery depends signon-ui
 * These packages depend on signon-ui:
kde-apps/kaccounts-providers-20.12.3 (net-libs/signon-ui)
root # equery depends kaccounts-providers
 * These packages depend on kaccounts-providers:
kde-misc/kio-gdrive-20.12.3 (kaccounts ? >=kde-apps/kaccounts-providers-20.08.3:5)
root # equery depends kio-gdrive
 * These packages depend on kio-gdrive:
root #

As can be seen above, my modified ebuild for kdenetwork-meta-20.12.3 had indeed removed the impediment to uninstalling kio-gdrive and therefore the impediment to uninstalling kaccount-providers and signon-ui.

13. I merged my modified version of kdenetwork-meta-20.12.3:

Up to this point kde-apps/kdenetwork-meta-20.12.3 had been merged from the main Portage tree:

root # eix -I kde-apps/kdenetwork-meta
[I] kde-apps/kdenetwork-meta
     Available versions:  (5) 20.08.3-r1 (~)20.12.3 (~)20.12.3[1]
       {+bittorrent dropbox +webengine}
     Installed versions:  20.12.3(5)(15:23:08 05/03/21)(bittorrent -dropbox -webengine)
     Homepage:            https://kde.org/
     Description:         kdenetwork - merge this to pull in all kdenetwork-derived packages

[1] "local_overlay" /usr/local/portage

I then merged the version from my local overlay:

root # emerge -1v kdenetwork-meta::local_overlay

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

Calculating dependencies... done!
[ebuild   R    ] kde-apps/kdenetwork-meta-20.12.3:5::local_overlay [20.12.3:5::gentoo] USE="bittorrent -dropbox -webengine" 0 KiB

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

>>> Verifying ebuild manifests
>>> Emerging (1 of 1) kde-apps/kdenetwork-meta-20.12.3::local_overlay
>>> Installing (1 of 1) kde-apps/kdenetwork-meta-20.12.3::local_overlay
>>> Jobs: 1 of 1 complete                           Load avg: 1.76, 0.88, 0.61
>>> Auto-cleaning packages...

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

 * GNU info directory index is up-to-date.
root # eix -I kde-apps/kdenetwork-meta
[I] kde-apps/kdenetwork-meta
     Available versions:  (5) 20.08.3-r1 (~)20.12.3 (~)20.12.3[1]
       {+bittorrent dropbox +webengine}
     Installed versions:  20.12.3(5)[1](16:40:43 05/03/21)(bittorrent -dropbox -webengine)
     Homepage:            https://kde.org/
     Description:         kdenetwork - merge this to pull in all kdenetwork-derived packages

[1] "local_overlay" /usr/local/portage

14. I checked which packages still depended on qtwebengine:

root # equery depends qtwebengine
 * These packages depend on qtwebengine:
kde-apps/kaccounts-providers-20.12.3 (>=dev-qt/qtwebengine-5.15.2:5)
kde-apps/kdenlive-20.12.3 (webengine ? >=dev-qt/qtwebengine-5.15.2:5)
kde-apps/marble-20.12.3 (webengine ? >=dev-qt/qtwebengine-5.15.2:5[widgets])
kde-plasma/kdeplasma-addons-5.21.2 (webengine ? >=dev-qt/qtwebengine-5.15.2:5)
kde-plasma/libksysguard-5.21.2 (webengine ? >=dev-qt/qtwebengine-5.15.2:5)
net-libs/signon-ui-0.15_p20171022-r1 (dev-qt/qtwebengine:5)
net-p2p/ktorrent-20.12.3 (rss ? >=dev-qt/qtwebengine-5.15.2:5)
                         (webengine ? >=dev-qt/qtwebengine-5.15.2:5)

Eureka! kdenetwork-meta no longer depends on qtwebengine.

15. I was then able to remove qtwebengine and the remaining packages that hard-depend on it:

root # emerge --ask --depclean qtwebengine kaccounts-providers signon-ui kio-gdrive

Calculating dependencies... done!
>>> Calculating removal order...

>>> These are the packages that would be unmerged:                                                                                                                                                                                                

 kde-misc/kio-gdrive
    selected: 20.12.3 
   protected: none 
     omitted: none 

 kde-apps/kaccounts-providers
    selected: 20.12.3 
   protected: none 
     omitted: none 

 net-libs/signon-ui
    selected: 0.15_p20171022-r1 
   protected: none 
     omitted: none 

 dev-qt/qtwebengine
    selected: 5.15.2_p20210224 
   protected: none 
     omitted: none 

All selected packages: =dev-qt/qtwebengine-5.15.2_p20210224 =kde-apps/kaccounts-providers-20.12.3 =kde-misc/kio-gdrive-20.12.3 =net-libs/signon-ui-0.15_p20171022-r1

>>> 'Selected' packages are slated for removal.
>>> 'Protected' and 'omitted' packages will not be removed.

Would you like to unmerge these packages? [Yes/No] Yes
>>> Waiting 5 seconds before starting...
>>> (Control-C to abort)...
>>> Unmerging in: 5 4 3 2 1
>>> Unmerging (1 of 4) kde-misc/kio-gdrive-20.12.3...
>>> Unmerging (2 of 4) kde-apps/kaccounts-providers-20.12.3...
>>> Unmerging (3 of 4) net-libs/signon-ui-0.15_p20171022-r1...
>>> Unmerging (4 of 4) dev-qt/qtwebengine-5.15.2_p20210224...
Packages installed:   1648
Packages in world:    329
Packages in system:   43
Required packages:    1648
Number removed:       4

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

\o/ \o/ \o/ \o/ No more qtwebengine in Gentoo Linux Testing (~amd64) running KDE.

Of course this was only possible because I do not need the specific packages that had been uninstalled during this entire procedure. Other people may not be in the same position.

16. I added the following lines to the file /etc/portage/package.mask/package.mask so that the packages are not pulled in automatically when merging the world set in future:

dev-qt/qtwebengine
kde-apps/kdenetwork-meta::gentoo
kde-misc/kio-gdrive
kde-apps/kaccounts-providers
net-libs/signon-ui

17. In future I will have to modify new versions of the kdenetwork-meta ebuild and add them to my local overlay. Furthermore, if other packages become dependent on qtwebengine in future and I do not require them, I will have to repeat the above steps in order to remove them (if viable). I just hope I can keep the qtwebengine package from ever being installed again.

Enabling other users to login from the xscreensaver lockscreen in Lubuntu 20.10

If Lubuntu 20.10 suspends to RAM, xscreensaver displays a lockscreen with login window when the system resumes. However, LXQt and SDDM currently do not provide a ‘Switch User’ option, so, if you are not the currently logged-in user and you do not know that user’s password, you will be stuck on the xscreensaver lockscreen. If you click on ‘New Login’ in the xscreensaver window, a message similar to the following is displayed and there is no way for a different user to login:

xscreensaver: 19:01:52: could not execute "gdmflexiserver": No such file or directory

To get around this problem so that other users can login, create the file /usr/local/bin/gdmflexiserver containing the following two lines:

#!/bin/bash
who | awk '!/root/{ cmd="/usr/bin/pkill -KILL -u " $1; system(cmd)}'

Make it executable:

$ sudo chmod +x /usr/local/bin/gdmflexiserver

Now, when the xscreensaver login window appears, if you do not know the currently logged-in user’s password you can click on the ‘New Login’ button instead and the SDDM greeter screen will be displayed so that another user can login. Note that clicking on ‘New Login’ will lose all the open windows and any running applications in the current user’s session, but at least a different user will not be prevented from logging-in and using the machine if the original user is not available to login then logout from his/her session. Of course, if the current user is still available, he/she can simply unlock the current session as usual via the xscreensaver lockscreen window.

Implementing a scheme for system-wide automatic Suspend to RAM in Lubuntu 20.10

Lubuntu 20.10 is installed on my family’s desktop PC (single-seat, multi-user). This version of the distribution uses the SDDM display manager and the LXQt desktop environment. Previously the machine had Lubuntu 18.04 installed, which used the LightDM display manager and the LXDE desktop environment.

In Lubuntu 18.04 each user could configure the power manager in their LXDE session to make the machine suspend to RAM if that user was logged-in but had not used the keyboard and mouse for a user-specified number of minutes. The problem with that approach was that, if two or more users happened to be logged-in simultaneously because a user did not log out and another user used ‘Switch User’ in LXDE to log-in, the power manager in a logged-in but unused session would eventually suspend the machine even though another user was still active in a different session. Very annoying. Secondly, if nobody was logged-in and the LightDM greeter screen was displayed, the machine would not suspend to RAM automatically after a period of inactivity; the only way to get the machine to suspend to RAM if nobody was logged-in was to suspend it manually from the greeter screen. I implemented my own fix for both problems in Lubuntu 18.04 (see my previous blog posts How to make LightDM suspend to RAM automatically from the login screen and lock screen in Lubuntu 18.04 and How to stop inactive user sessions triggering Suspend to RAM in a single-seat, multi-user installation of Lubuntu 18.04) but I had to use a different approach in Lubuntu 20.10 because it uses SDDM and LXQt.

The new scheme I implemented in Lubuntu 20.10 is partly an academic exercise because LXQt in Lubuntu 20.10 does not have a ‘Switch User’ option that would enable more than one user to be logged in simultaneously. LXQt Issue 14 and SDDM Issue 991 appear to be about this limitation. Furthermore, I think systemd may also be a factor (Lubuntu uses Version 246, which no longer includes seat_can_multi_session() that the version of SDDM in Lubuntu 20.10 may depend on for the ‘Switch User’ option). The disappearance of a ‘Switch User’ option has been reported previously for various Linux distributions using KDE and systemd 246, such as Arch Linux, Gentoo Linux and Kubuntu. Therefore it may be that all three packages would need to be altered in order for Lubuntu to provide a ‘Switch User’ option. Anyway, the scheme I have implemented in Lubuntu 20.10 could handle more than one user logged-in simultaneously if that were possible, as I catered for that possibility in case future updates to LXQt and SDDM have a ‘Switch User’ option. Even without a ‘Switch User’ option in LXQt, my scheme enables Lubuntu 20.10 to suspend to RAM automatically when no one is logged in and the SDDM greeter screen is displayed, which is also useful. Furthermore, my scheme also provides system-wide Suspend to RAM control, removing the need for users to touch the LXQt power manager settings in their individual user accounts.

Note that, as was the case in Lubuntu 18.04, Lubuntu 20.10 does not terminate all the user’s processes when the user logs out. This can be seen if you use the ‘ps -ef‘ command when logged in as a different user. Also, the user’s session is not closed; its state is always shown as ‘closing’ if you use the ‘loginctl list-sessions‘ and ‘loginctl session-status <ID>‘ commands. I suspect this behaviour is a ‘feature’ of systemd rather than Lubuntu itself, but I could be wrong. Anyway, in order to cater for this behaviour my scheme is more complex than it would be otherwise.

By the way, I believe this scheme would probably work in other Linux distributions that use systemd and have a desktop environment that can be configured to launch scripts automatically at login. In fact, the scheme could also be adapted for use in a distribution that does not use systemd. For example, in an installation that uses OpenRC the suspend.sh script (see further down) could be launched at boot from a script in the /etc/local.d/ directory. In distributions without systemd the logger command could be used to log messages in the system log file.

My scheme to provide system-wide automatic Suspend to RAM in Lubuntu 20.10 is detailed below. If new versions of LXQt, SDDM and systemd (whichever of those is required for a ‘Switch User’ option) are added to the Lubuntu repositories in future, hopefully the scheme detailed below already caters for that option.

1. Select ‘Preferences’ > ‘LXQt settings’ > ‘Power Management’ in each user’s LXQt account, click on ‘Idle’ and untick ‘Enable Idleness Watcher’ to prevent LXQt Power Manager in each user’s LXQt session from suspending to RAM independently.

Note: It is possible for the system administrator to configure this for all users via the command line instead. For example:

$ nano ~/.config/lxqt/lxqt-powermanagement.conf

Change ‘enableIdlenessWatcher=true‘ to ‘enableIdlenessWatcher=false‘:

[General]
__userfile__=true
backlightIdleness=0
backlightIdlenessOnBatteryDischarging=true
enableBatteryWatcher=false
enableIdlenessBacklightWatcher=false
enableIdlenessWatcher=false
enableLidWatcher=false
idlenessAction=-1
idlenessTime=@Variant(\0\0\0\xf\0\0\x3\xe8)
idlenessTimeSecs=900
runCheckLevel=1
$ $ ls -la ~/.config/lxqt/lxqt-powermanagement.conf
-rw-rw-r-- 1 fitzcarraldo fitzcarraldo 304 Feb  8 22:10 /home/bill/.config/lxqt/lxqt-powermanagement.conf
$ sudo cp ~/.config/lxqt/lxqt-powermanagement.conf /home/aquilino/.config/lxqt/lxqt-powermanagement.conf
$ sudo chown aquilino.aquilino /home/aquilino/.config/lxqt/lxqt-powermanagement.conf
$ sudo chmod 664 /home/aquilino/.config/lxqt/lxqt-powermanagement.conf

and so on for every user. For example, if the installation has accounts for five users with usernames aquilino, cholo, fitzcarraldo, molly and paul:

$ sudo updatedb; sudo locate lxqt-powermanagement.conf | grep .config                                      
/home/aquilino/.config/lxqt/lxqt-powermanagement.conf
/home/cholo/.config/lxqt/lxqt-powermanagement.conf
/home/fitzcarraldo/.config/lxqt/lxqt-powermanagement.conf
/home/molly/.config/lxqt/lxqt-powermanagement.conf
/home/paul/.config/lxqt/lxqt-powermanagement.conf

2. Install the X Windows utility xprintidle so it can be used in a Bash script to determine when a user is idle or active:

$ sudo apt install xprintidle

3. Create the script /usr/local/bin/check_if_idle.sh

An instance of this script will be launched when each user logs in to the desktop environment (see Step 4). Each instance of the script will run continuously and its purpose is to create periodically a file with a specific name, or periodically delete files with specific names, in the RAM-based /tmp/ directory. These zero-length files are used to inform the suspend.sh script (see Step 5) whether or not the applicable user has been using the keyboard or mouse within a specified timeout period (specified as 15 minutes in the script). From now on I will use the term ‘flag-files’ when referring to such files, as they are effectively Boolean flags to pass binary information from a Bash script in each user’s session to a Bash script that actually initiates suspending to RAM. If the active user’s session has been idle for the specified timeout period, the script deletes all that user’s flag-files in order to indicate to the suspend.sh script that the specific user is no longer an impediment to suspending to RAM. Similarly, when the user logs out, the applicable flag-file is deleted in order to indicate to the suspend.sh script that the user is no longer an impediment to suspending. The flag-file is not deleted if the user is logged in but not the active user, in order to indicate to the suspend.sh script that it must not suspend to RAM unless the active user has been idle for the specified timeout period (notice that the script check_if_idle.sh deletes all the user’s non-VT (Virtual Terminal, a.k.a. ‘TTY console’) flag-files when the active user has been idle for the specified timeout period).

$ sudo nano /usr/local/bin/check_if_idle.sh
#!/bin/bash
CurrentUser=$(whoami)
touch /tmp/not_idle_$CurrentUser$BASHPID
IdleAfter=900000 # consider idle after 900000 ms (15 min) of inactivity
while true; do
  State=$( loginctl session-status | grep -m 1 State | awk -F " " '{ print $2 }' )
  if [[ "$State" == "active" ]]; then
  IdleTimeMillisecs=$(xprintidle 2>/dev/null)
    if [[ $IdleTimeMillisecs -ge $IdleAfter ]]; then
      rm /tmp/not_idle_$CurrentUser* 2>/dev/null
    fi
    if [[ $IdleTimeMillisecs -lt $IdleAfter ]]; then
      touch /tmp/not_idle_$CurrentUser$BASHPID
    fi
  elif [[ "$State" == "online" ]]; then
    touch /tmp/not_idle_$CurrentUser$BASHPID
  elif [[ "$State" == "closing" ]]; then
    rm /tmp/not_idle_$CurrentUser$BASHPID 2>/dev/null
  fi
  sleep 1
done
$ chmod 755 /usr/local/bin/check_if_idle.sh

As far as I have been able to ascertain from systemd documentation, currently the following three session states are possible: “online” (session logged in, but session not active, i.e. not in the foreground), “active” (session logged in and active, i.e. in the foreground), “closing” (session nominally logged out, but some processes belonging to it are still around).

Note: I could have created a separate configuration file and made the Bash script read $IdleAfter from it, but I decided to keep things simple and specify the time-out period in the script itself.

4. Add the following command to LXQt Application Menu > Preferences > LXQt settings > Session Settings > Autostart > LXQt Autostart for each user (give it the name ‘Check_if_idle’) so that an instance of check_if_idle.sh is launched automatically when a user logs in:

/usr/local/bin/check_if_idle.sh

Note: It is possible for the system administrator to configure this for all users via the command line instead. For example:

$ nano ~/.config/autostart/Check_if_idle.desktop
[Desktop Entry]
Exec=/usr/local/bin/check_if_idle.sh
Name=Check_if_idle
OnlyShowIn=LXQt;
Type=Application
Version=1.0
$ ls -la ~/.config/autostart/Check_if_idle.desktop
-rw-rw-r-- 1 fitzcarraldo fitzcarraldo 118 Feb  9 13:27 /home/fitzcarraldo/.config/autostart/Check_if_idle.desktop
$ sudo cp ~/.config/autostart/Check_if_idle.desktop /home/aquilino/.config/autostart/Check_if_idle.desktop
$ sudo chown aquilino.aquilino /home/aquilino/.config/autostart/Check_if_idle.desktop
$ sudo chmod 664 /home/aquilino/.config/autostart/Check_if_idle.desktop

and so on for every user. For example, if the installation has accounts for five users with usernames aquilino, cholo, fitzcarraldo, molly and paul:

$ sudo updatedb; sudo locate Check_if_idle.desktop
/home/aquilino/.config/autostart/Check_if_idle.desktop
/home/cholo/.config/autostart/Check_if_idle.desktop
/home/fitzcarraldo/.config/autostart/Check_if_idle.desktop
/home/molly/.config/autostart/Check_if_idle.desktop
/home/paul/.config/autostart/Check_if_idle.desktop

5. Create the script /usr/local/bin/suspend.sh

This script will be a service launched at boot via a systemd unit file. Its purpose is twofold:

  • to check periodically if there has been keyboard and mouse activity while the SDDM greeter screen is displayed and to suspend to RAM if there has not been any activity within a specificed timeout period (which I have also specified as 15 minutes);
  • to check periodically for flag-files in the /tmp/ directory to ascertain if there has been any activity by logged-in LXQt users within a specified timeout period, and to ascertain if any user is logged in to a VT, and to suspend to RAM if neither of those are the case.
#!/bin/bash
rm /tmp/not_idle_* 2>/dev/null
rm /tmp/triggered_suspend_greeter 2>/dev/null
rm /tmp/triggered_suspend 2>/dev/null
IdleAfter=900000 # Consider SDDM Greeter idle after 900000 ms (15 min) of inactivity
while true; do
  sleep 30 # Do not delete this line or reduce the time
  NumLoggedInUsers=$(who -qs | grep -o '[0-9]\+')
  if [[ $NumLoggedInUsers -eq 0 ]]; then
    rm /tmp/not_idle_* 2>/dev/null
    AuthFile=$(find /var/run/sddm/ -type f)
    if IdleTimeMillisecs=$(DISPLAY=:0 XAUTHORITY=$AuthFile xprintidle 2>/dev/null) ; then
      Running_in_X_Windows=true
    else
      echo "Cannot calc Idle time for Greeter screen." # Log the problem in systemd journal.
      IdleTimeMillisecs=0
      Running_in_X_Windows=false
    fi
    if [[ $IdleTimeMillisecs -gt $IdleAfter ]] ; then
      touch /tmp/triggered_suspend_greeter
      systemctl suspend -i # Comment out when debugging
    fi
  else
    # Only suspend if all users have been idle for the time specified in /usr/local/bin/check_if_idle.sh
    if [[ `ls -1 /tmp/not_idle_* 2>/dev/null | wc -l` -eq 0 ]]; then
      touch /tmp/triggered_suspend
      systemctl suspend -i
    fi
  fi
done
$ sudo chmod 755 /usr/local/bin/suspend.sh

Note: I could have created a separate configuration file and made the Bash script read $IdleAfter from it, but I decided to keep things simple and specify the timeout period in the script itself.

6. Create the systemd unit file /etc/systemd/system/suspend.service so that systemd can launch the suspend.sh script at boot. Note that this service is different to, and independent from, systemd’s service for suspending to RAM (systemd-suspend.service).

[Unit]
Description=Suspend to RAM if idle for specified time

Wants=network.target
After=syslog.target network-online.target

[Service]
Type=simple
ExecStart=/usr/local/bin/suspend.sh
Restart=on-failure
RestartSec=10
KillMode=process

[Install]
WantedBy=multi-user.target
$ sudo chmod 640 /etc/systemd/system/suspend.service
$ sudo systemctl daemon-reload
$ sudo systemctl enable suspend.service

7. Add the following lines to the end of each user’s ~/.profile file:

# Stop /usr/local/bin/suspend.sh suspending to RAM if this user logs in to a VT
CurrentUser=$(whoami)
CurrentVT=$(tty | grep -o '[0-9]\+') # Will be zero if not run on a VT
rm /tmp/not_idle_$CurrentUser* 2>/dev/null
if [[ $CurrentVT -ne 0 ]]; then
  touch /tmp/not_idle_VT"$CurrentVT"_"$CurrentUser"
fi

This addition only creates a flag-file if the applicable user logs in to a VT, and the flag-file is created using a different naming convention to flag-files for LXQt processes in order to be able to distinguish between specific VT console use and pseudo-terminal use, to prevent the suspend.sh script from suspending to RAM while a user is logged in to a VT.

8. Add the following lines to the end of each user’s ~/.bash_logout file:

# Allow /usr/local/bin/suspend.sh to suspend to RAM after this user logs out from a VT session
CurrentUser=$(whoami)
CurrentVT=$(tty | grep -o '[0-9]\+')
if [[ $CurrentVT -ne 0 ]]; then
  rm /tmp/not_idle_VT"$CurrentVT"_"$CurrentUser" 2>/dev/null
fi

The flag-file uses a different naming convention to flag-files used for LXQt processes, in order to be able to indicate to the suspend.sh script that the specific VT user has logged out and therefore is no longer an impediment to suspending to RAM.

9. Create the file /usr/lib/systemd/system-sleep/automatic-suspend-scheme to log when the scheme causes the system to suspend to RAM and also when the system wakes from that suspension.

$ sudo nano /usr/lib/systemd/system-sleep/automatic_suspend_scheme
#!/bin/bash
# This hook script will run when suspending/hibernating and when resuming/thawing.
# Check $1 ('pre' or 'post') and $2 ('suspend', 'hibernate', or 'hybrid-sleep') to ascertain
# which is happening.
case $1/$2 in
  pre/*)
    # Put here any commands you want to be run when suspending or hibernating.
    if [[ -f /tmp/triggered_suspend_greeter ]]; then
      echo "Automatic Suspend Scheme - Suspend to RAM (No users logged in)." # Log the event in the systemd journal
    fi
    if [[ -f /tmp/triggered_suspend ]]; then
      echo "Automatic Suspend Scheme - Suspend to RAM." # Log the event in the systemd journal
    fi
    ;;
  post/*)
    # Put here any commands you want to be run when resuming from suspension or thawing from hibernation.
    if [[ -f /tmp/triggered_suspend_greeter ]]; then
      echo "Automatic Suspend Scheme - Resuming from Suspend to RAM (No users logged in)." # Log the event in the systemd journal
      rm /tmp/triggered_suspend_greeter
    fi
    if [[ -f /tmp/triggered_suspend ]]; then
      echo "Automatic Suspend Scheme - Resuming from Suspend to RAM." # Log the event in the systemd journal
      rm /tmp/triggered_suspend
    fi
    ;;
esac
$ sudo chmod 755 /usr/lib/systemd/system-sleep/automatic_suspend_scheme

10. Reboot and test

From now on, the installation should suspend to RAM if all logged-in LXQt users have been inactive for 15 minutes or more. This does not preclude any user selecting Leave > Suspend from the LXQt Application Menu to suspend to RAM manually. If no users are logged in — i.e. the SDDM greeter screen is displayed on the monitor — the installation will suspend to RAM if there is no keyboard and mouse activity for 15 minutes. This does not preclude any user clicking on the icon on the SDDM greeter screen to suspend the machine to RAM manually. The Bash scripts suspend.sh and check_if_idle.sh can be edited to alter the timeout period if a different timeout is desired. The scheme will not trigger suspending to RAM while someone is logged in via a VT (a.k.a. TTY console). I am the only person in my family who knows how to use a VT; all the other family members use only the LXQt desktop environment.

Caveat: The suspend.sh script can initiate suspending to RAM if no /tmp/not_idle_* files exist, so do not delete manually any of the not_idle_* files in the /tmp/ directory.

If you want to check what is happing to the sessions, processes and flag-files, the following commands are useful:

$ systemctl status suspend
$ ps -ef | grep check
$ ps -ef | grep suspend
$ ls /tmp/not_idle*
$ loginctl list-sessions
$ loginctl session-status <session number> | grep State

For example, below is some output from those commands when users paul and aquilino were using the machine. User paul booted the machine and logged-in on the SDDM greeter screen, then logged out to let user aquilino login on the SDDM greeter screen. Then user aquilino logged out and user paul logged in again on the SDDM greeter screen and entered the following commands in a pseudo-terminal (e.g. QTerminal) window:

$ systemctl status suspend
● suspend.service - Suspend to RAM if idle for specified time
     Loaded: loaded (/etc/systemd/system/suspend.service; enabled; vendor preset: enabled)
     Active: active (running) since Thu 2021-02-11 16:25:53 GMT; 35min ago
   Main PID: 966 (suspend.sh)
      Tasks: 2 (limit: 4476)
     Memory: 1.5M
     CGroup: /system.slice/suspend.service
             ├─  966 /bin/bash /usr/local/bin/suspend.sh
             └─36254 sleep 30

Feb 11 16:25:53 aspirexc600 systemd[1]: Started Suspend to RAM if idle for specified time.
$ ps -ef | grep check
paul        1337       1  0 16:27 ?        00:00:01 /bin/bash /usr/local/bin/check_if_idle.sh
aquilino    4904       1  0 16:34 ?        00:00:01 /bin/bash /usr/local/bin/check_if_idle.sh
paul        9187       1  0 16:38 ?        00:00:01 /bin/bash /usr/local/bin/check_if_idle.sh
paul       37124    9236  0 17:01 pts/0    00:00:00 grep --color=auto check
$ ps -ef | grep suspend
root         966       1  0 16:25 ?        00:00:00 /bin/bash /usr/local/bin/suspend.sh
paul       38515    9236  0 17:02 pts/0    00:00:00 grep --color=auto suspend
$ ls /tmp/not_idle*
/tmp/not_idle_paul9187
$ loginctl list-sessions
SESSION  UID USER     SEAT  TTY
     12 1000 paul     seat0    
      3 1000 paul     seat0    
      8 1001 aquilino seat0    

3 sessions listed.
$ loginctl session-status 12 | grep State
           State: active
                  └─41171 grep --color=auto State
$ loginctl session-status 3 | grep State
           State: closing
$ loginctl session-status 8 | grep State
           State: closing

Then user paul pressed Ctrl+Alt+F2 to switch to VT2 (tty2), logged in as user paul and entered the commands:

$ ps -ef | grep check
paul        1337       1  0 16:27 ?        00:00:01 /bin/bash /usr/local/bin/check_if_idle.sh
aquilino    4904       1  0 16:34 ?        00:00:01 /bin/bash /usr/local/bin/check_if_idle.sh
paul        9187       1  0 16:38 ?        00:00:01 /bin/bash /usr/local/bin/check_if_idle.sh
paul       50191   48181  0 17:12 tty2     00:00:00 grep --color=auto check
$ ps -ef | grep suspend
root         966       1  0 16:25 ?        00:00:00 /bin/bash /usr/local/bin/suspend.sh
paul       50295   48181  0 17:12 tty2     00:00:00 grep --color=auto suspend
$ ls /tmp/not_idle*
/tmp/not_idle_paul9187  /tmp/not_idle_VT2_paul
$ loginctl list-sessions
SESSION  UID USER     SEAT  TTY
     12 1000 paul     seat0    
     15 1000 paul     seat0 tty2
      3 1000 paul     seat0    
      8 1001 aquilino seat0    

4 sessions listed.
$ loginctl session-status 12 | grep State
           State: online
$ loginctl session-status 15 | grep State
           State: active
                  └─50741 grep --color=auto State
$ loginctl session-status 3 | grep State
           State: closing
$ loginctl session-status 8 | grep State
           State: closing

Then user paul pressed Ctrl+Alt+F1 to switch back to his LXQt session and entered the following commands in the already open pseudo-terminal window:

$ ps -ef | grep check
paul        1337       1  0 16:27 ?        00:00:02 /bin/bash /usr/local/bin/check_if_idle.sh
aquilino    4904       1  0 16:34 ?        00:00:02 /bin/bash /usr/local/bin/check_if_idle.sh
paul        9187       1  0 16:38 ?        00:00:02 /bin/bash /usr/local/bin/check_if_idle.sh
paul       61359    9236  0 17:22 pts/0    00:00:00 grep --color=auto check
$ ps -ef | grep suspend
root         966       1  0 16:25 ?        00:00:00 /bin/bash /usr/local/bin/suspend.sh
paul       61473    9236  0 17:22 pts/0    00:00:00 grep --color=auto suspend
$ ls /tmp/not_idle*
/tmp/not_idle_paul9187  /tmp/not_idle_VT2_paul
$ loginctl list-sessions
SESSION  UID USER     SEAT  TTY 
     12 1000 paul     seat0     
     15 1000 paul     seat0 tty2
      3 1000 paul     seat0     
      8 1001 aquilino seat0     

4 sessions listed.
$ loginctl session-status 12 | grep State
           State: active
                  └─62165 grep --color=auto State
$ loginctl session-status 15 | grep State
           State: online
$ loginctl session-status 3 | grep State
           State: closing
$ loginctl session-status 8 | grep State
           State: closing

Then user paul pressed Ctrl+Alt+F2 to switch back to tty2, entered ‘exit‘ to log out of his VT session, then pressed Ctrl+Alt+F1 to switch back to his LXQt session and entered the following commands:

$ ps -ef | grep check
paul        1337       1  0 16:27 ?        00:00:03 /bin/bash /usr/local/bin/check_if_idle.sh
aquilino    4904       1  0 16:34 ?        00:00:03 /bin/bash /usr/local/bin/check_if_idle.sh
paul        9187       1  0 16:38 ?        00:00:03 /bin/bash /usr/local/bin/check_if_idle.sh
paul       92759    9236  0 17:48 pts/0    00:00:00 grep --color=auto check
$ ps -ef | grep suspend
root         966       1  0 16:25 ?        00:00:00 /bin/bash /usr/local/bin/suspend.sh
paul       92922    9236  0 17:48 pts/0    00:00:00 grep --color=auto suspend
$ ls /tmp/not_idle*
/tmp/not_idle_paul9187
$ loginctl list-sessions
SESSION  UID USER     SEAT  TTY
     12 1000 paul     seat0    
      3 1000 paul     seat0    
      8 1001 aquilino seat0    

3 sessions listed.
$ loginctl session-status 12 | grep State
           State: active
                  └─93554 grep --color=auto State
$ loginctl session-status 3 | grep State
           State: closing
$ loginctl session-status 8 | grep State
           State: closing

As you can see in the output above, even though user paul logged out of his first LXQt session (Session 3), and user aquilino logged out of his only LXQt session (Session 8), those two sessions did not end, remaining in the state ‘closing’. You can also see that user paul‘s VT session (Session 15) did close when he logged out on VT2 (tty2). Addiionally, you can see in the output above the flag-files that were created and deleted during that period.

The following command shows the entries in the systemd journal for the scheme’s service (suspend.service) since the system last booted:

$ journalctl -b -u suspend.service
-- Logs begin at Sat 2021-01-02 01:52:36 GMT, end at Sat 2021-02-13 01:16:09 GMT. --
Feb 13 00:24:18 aspirexc600 systemd[1]: Started Suspend to RAM if idle for specified time.

The above output shows no error messages since the service started at 00:24:18 when the system was last booted.

If you want to check the systemd journal to see when the scheme suspended the system to RAM automatically and was manually woken up from that sleep:

$ journalctl | grep "Automatic Suspend Scheme"

Add ‘-b‘ if you want to check only since the system last booted:

$ journalctl -b | grep "Automatic Suspend Scheme"
Feb 13 00:39:49 aspirexc600 systemd-sleep[1611]: Automatic Suspend Scheme - Suspend to RAM (No users logged in).
Feb 13 00:44:32 aspirexc600 systemd-sleep[1691]: Automatic Suspend Scheme - Resuming from Suspend to RAM (No users logged in).
Feb 13 01:01:59 aspirexc600 systemd-sleep[10470]: Automatic Suspend Scheme - Suspend to RAM.
Feb 13 01:04:52 aspirexc600 systemd-sleep[10556]: Automatic Suspend Scheme - Resuming from Suspend to RAM.

The following command shows entries in the systemd journal by the systemd suspend service (systemd-suspend.service) and systemd-sleep command since the system last booted, so it is possible to check both the times at which the automated scheme suspended and the times at which the system was manually suspended, and also the times at which the system was woken manually:

$ journalctl -b -u systemd-suspend.service
-- Logs begin at Sat 2021-01-02 01:52:36 GMT, end at Sat 2021-02-13 01:16:09 GMT. --
Feb 13 00:39:49 aspirexc600 systemd[1]: Starting Suspend...
Feb 13 00:39:49 aspirexc600 systemd-sleep[1611]: Automatic Suspend Scheme - Suspend to RAM (No users logged in).
Feb 13 00:39:49 aspirexc600 systemd-sleep[1609]: Suspending system...
Feb 13 00:44:32 aspirexc600 systemd-sleep[1609]: System resumed.
Feb 13 00:44:32 aspirexc600 systemd-sleep[1691]: Automatic Suspend Scheme - Resuming from Suspend to RAM (No users logged in).
Feb 13 00:44:32 aspirexc600 systemd[1]: systemd-suspend.service: Succeeded.
Feb 13 00:44:32 aspirexc600 systemd[1]: Finished Suspend.
Feb 13 01:01:59 aspirexc600 systemd[1]: Starting Suspend...
Feb 13 01:01:59 aspirexc600 systemd-sleep[10470]: Automatic Suspend Scheme - Suspend to RAM.
Feb 13 01:01:59 aspirexc600 systemd-sleep[10468]: Suspending system...
Feb 13 01:04:51 aspirexc600 systemd-sleep[10468]: System resumed.
Feb 13 01:04:52 aspirexc600 systemd-sleep[10556]: Automatic Suspend Scheme - Resuming from Suspend to RAM.
Feb 13 01:04:51 aspirexc600 systemd[1]: systemd-suspend.service: Succeeded.
Feb 13 01:04:51 aspirexc600 systemd[1]: Finished Suspend.
Feb 13 01:12:51 aspirexc600 systemd[1]: Starting Suspend...
Feb 13 01:12:51 aspirexc600 systemd-sleep[14964]: Suspending system...
Feb 13 01:15:50 aspirexc600 systemd-sleep[14964]: System resumed.
Feb 13 01:15:53 aspirexc600 systemd[1]: systemd-suspend.service: Succeeded.
Feb 13 01:15:53 aspirexc600 systemd[1]: Finished Suspend.

You can see above that the system automatically suspended to RAM at 00:39:49 after 15 minutes of inactivity on the SDDM greeter screen. Someone then pressed the space bar on the keyboard to wake the system at 00:44:32, and logged in to the LXQt desktop. The system suspended to RAM automatically again at 01:01:59 after that user had not touched the keyboard and mouse for 15 minutes in the active LXQt session. That user then woke the system manually again at 01:04:52, then manually suspended the system at 01:12:51 using the LXQt Application Menu (Leave > Suspend). Finally, at 01:15:50 the same user woke the system manually again.

11. Make /tmp/ RAM-based (optional)

By default, the Lubuntu 20.10 Installer creates the directory /tmp/ on the same disk partition as the root directory:

$ df -h /tmp
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda2       916G  139G  731G  16% /

If your machine has plenty of RAM, if you want you could configure the system to mount a tmpfs filesystem on /tmp/ instead, as show below.

I decided to specify a tmpfs maximum size of 200MB for /tmp/ (tmpfs doesn’t reserve this memory, it allocates only the memory actually needed):

$ echo "tmpfs /tmp tmpfs rw,nosuid,nodev,size=200M" | sudo tee -a /etc/fstab
$ grep tmp /etc/fstab
tmpfs /tmp tmpfs rw,nosuid,nodev,size=200M

Note: If I had not included ‘,size=200M‘ in /etc/fstab, the maximum size for the tmpfs filesystem mounted on /tmp/ would have defaulted to half the available RAM in the machine.

Then I rebooted to check that /tmp/ is now in RAM:

$ df -h /tmp/
Filesystem      Size  Used Avail Use% Mounted on
tmpfs           200M  4.0K  200M   1% /tmp
$ findmnt /tmp/
TARGET SOURCE FSTYPE OPTIONS
/tmp   tmpfs  tmpfs  rw,nosuid,nodev,relatime,size=204800k

The space used in /tmp/ will vary depending on what is running. For example, editing a large LibreOffice document will require space in /tmp/, so keep an eye on the amount of space used in /tmp/ and adjust the maximum size of the tmpfs partition if necessary.

Moving from Lubuntu 18.04 to 20.10

The Lubuntu developers will no longer be supporting Lubuntu 18.04, the final release of Lubuntu to use LXDE, after April this year. I therefore decided to replace it with Lubuntu 20.10 on the family desktop machine (a single-seat, multi-user installation). Lubuntu 20.10 uses LXQt so it was not feasible to upgrade the existing installation, as explained on the official Lubuntu Website:

Note, due to the extensive changes required for the shift in desktop environments, the Lubuntu team does not support upgrading from 18.04 or below to any greater release. Doing so will result in a broken system. If you are on 18.04 or below and would like to upgrade, please do a fresh install.

Thus I had to install Lubuntu 20.10 from scratch. I downloaded the ISO file (1.8 GB) from the official Lubuntu Website and used the dd command to create a LivePendrive:

user $ sudo blkid # Ascertain which device is the pendrive.
user $ sudo dd bs=4M if=/home/fitzcarraldo/lubuntu-20.10-desktop-amd64.iso of=/dev/sdb conv=fdatasync status=progress

Some of my blog posts during the last three years covered various additions and fixes I made to the Lubuntu 18.04 installation on the family desktop machine. Below I explain how I implemented those, if necessary, in the fresh 20.10 installation.

Due to the automated backup routine I implemented in 2018, a permanently-connected external USB HDD (filesystem label ‘FREECOM HDD’) contained a backup of each users’ home directory from the Lubuntu 18.04 installation:

Lubuntu_home_folders_backup_LXDE/fitzcarraldo/
Lubuntu_home_folders_backup_LXDE/molly/
Lubuntu_home_folders_backup_LXDE/aquilino/
Lubuntu_home_folders_backup_LXDE/cholo/
Lubuntu_home_folders_backup_LXDE/paul/

I installed Lubuntu 20.10, overwriting the Lubuntu 18.04 installation. The resulting 20.10 installation had me as the sole user:

user $ whoami
fitzcarraldo
user $ groups
fitzcarraldo adm cdrom sudo dip plugdev lpadmin sambashare

All the files in my home directory are owned by fitzcarraldo.fitzcarraldo (user.group). I want all users to be members of the group ‘users‘ in addition to a group with their own username, so first I added myself to that group:

user $ sudo usermod -a -G users fitzcarraldo
user $ groups
fitzcarraldo adm cdrom sudo dip plugdev users lpadmin sambashare

Before using the groupadd and useradd commands I double-checked their options, as these can differ between distributions:

user $ groupadd --help
user $ useradd --help

Then I added the other users (N.B. GID and UID are not the same value per username):

user $ sudo groupadd -g 1002 molly 
user $ sudo useradd -m -c "Molly" -g molly -G adm,cdrom,dip,lpadmin,plugdev,sambashare,users -p strawberryfields -s /bin/bash -u 1001 molly
user $ sudo groupadd -g 1003 aquilino
user $ sudo useradd -m -c "Aquilino" -g aquilino -G adm,cdrom,dip,lpadmin,plugdev,sambashare,users -p paperbackwriter -s /bin/bash -u 1002 aquilino
user $ sudo groupadd -g 1004 cholo
user $ sudo useradd -m -c "Cholo" -g cholo -G adm,cdrom,dip,lpadmin,plugdev,sambashare,users -p mysweetlord -s /bin/bash -u 1003 cholo
user $ sudo groupadd -g 1005 paul
user $ sudo useradd -m -c "Paul" -g paul -G adm,cdrom,dip,lpadmin,plugdev,sambashare,users -p sweet16 -s /bin/bash -u 1004 paul

WARNING: Do not add other users to the sudo group, otherwise Muon Package Manager in Lubuntu 20.10 will not allow you to install any more packages (see [Bug 1875346] Re: Muon does not recognize user password).

Lubuntu 18.04 used the LightDM display manager, whereas Lubuntu 20.10 uses the less-sophisticated SDDM display manager. To display the avatar of each user on the SDDM login page I copied a portrait photograph of each user to the relevant SDDM directory:

user $ sudo cp /home/fitzcarraldo/Pictures/Portraits/fitzcarraldo.png /usr/share/sddm/faces/fitzcarraldo.face.icon
user $ sudo cp /home/fitzcarraldo/Pictures/Portraits/molly.png /usr/share/sddm/faces/molly.face.icon
user $ sudo cp /home/fitzcarraldo/Pictures/Portraits/aquilino.png /usr/share/sddm/faces/aquilino.face.icon
user $ sudo cp /home/fitzcarraldo/Pictures/Portraits/cholo.png /usr/share/sddm/faces/cholo.face.icon
user $ sudo cp /home/fitzcarraldo/Pictures/Portraits/paul.png /usr/share/sddm/faces/paul.face.icon

I also configured a different wallpaper for each user’s Desktop:

user $ sudo mkdir /home/molly/Pictures/Wallpaper
user $ sudo chown molly.molly /home/molly/Pictures/Wallpaper/
user $ sudo mkdir /home/aquilino/Pictures/Wallpaper
user $ sudo chown aquilino.aquilino /home/aquilino/Pictures/Wallpaper/
user $ sudo mkdir /home/cholo/Pictures/Wallpaper
user $ sudo chown cholo.cholo /home/cholo/Pictures/Wallpaper/
user $ sudo mkdir /home/paul/Pictures/Wallpaper
user $ sudo chown paul.paul /home/paul/Pictures/Wallpaper/
user $ cd Pictures/Wallpaper/
user $ sudo cp wallpaper2.jpg /home/molly/Pictures/Wallpaper/
user $ sudo chown molly.molly /home/molly/Pictures/Wallpaper/wallpaper2.jpg 
user $ sudo cp wallpaper3.jpg /home/aquilino/Pictures/Wallpaper/
user $ sudo chown aquilino.aquilino /home/aquilino/Pictures/Wallpaper/wallpaper3.jpg 
user $ sudo cp wallpaper4.jpg /home/cholo/Pictures/Wallpaper/
user $ sudo chown cholo.cholo /home/cholo/Pictures/Wallpaper/wallpaper4.jpg
user $ sudo cp wallpaper5.jpg /home/paul/Pictures/Wallpaper/
user $ sudo chown paul.paul /home/paul/Pictures/Wallpaper/wallpaper5.jpg

Note that, unlike Lubuntu 18.04, Lubuntu 20.10 does not have a ‘Switch User’ option, so it is not possible for users to switch between sessions without logging out. I actually prefer it this way, as other family members would usually switch user rather than logging out in Lubuntu 18.04, leaving open sessions.

Lubuntu 20.10 still does not automatically unmount the external HDD when a user logs out, thus making it impossible for the next user who logs in to access the external HDD (see Prevent Lubuntu 17.10 from leaving an external HDD mounted incorrectly for other users). The fix in this version of Lubuntu differs from the fix in 18.04; I added the following two lines to the end of /usr/share/sddm/scripts/Xsetup:

# Fix to make sure each user can access the external HDD
udisksctl unmount --block-device '/dev/disk/by-label/FREECOM\x20HDD'

Virus scan

I set up my automated system to scan files downloaded to the ~/Downloads directory as explained below.

user $ sudo apt install clamtk
user $ sudo apt install clamav
user $ sudo apt install clamav-daemon
user $ sudo apt install clamdscan
user $ sudo apt install inotify-tools
user $ sudo apt install kdialog # In Ubuntu this would be zenity instead.

I copied the script ~/.monitorDownloadsGUI listed below into each user’s home directory, and made the ownership <username>.<username> and the script executable:

#!/bin/bash

DIR=$HOME/Downloads

# Get rid of old log file, if any
rm $HOME/virus-scan.log 2> /dev/null

IFS=$(echo -en "\n\b")

# Optionally, you can use shopt to avoid creating two processes due to the pipe
shopt -s lastpipe
inotifywait --quiet --monitor --event close_write,moved_to --recursive --format '%w%f' $DIR | while read FILE
# Added '--recursive' so that a directory copied into $DIR also triggers clamscan/clamdscan, although downloads
# from the Web would just be files, not directories.
do
     sleep 1s
     # Have to check file length is nonzero otherwise commands may be repeated
     if [ -s $FILE ]; then
          # Replace 'date >' with 'date >>' if you want to keep log file entries for previous scans.
          date > $HOME/virus-scan.log
          clamdscan --move=$HOME/virus-quarantine $FILE >> $HOME/virus-scan.log
#         Use zenity in GNOME; kdialog in KDE and LXQt:
#          zenity --info --title="Virus scan of $FILE" --text="$(cat $HOME/virus-scan.log)"
          kdialog --title "Virus scan of $FILE" --msgbox "$(cat $HOME/virus-scan.log)"
     fi
done

I configured each user’s account to execute ~/.monitorDownloadsGUI at login. To do this in LXQt, in each user’s account I selected ‘Preferences’ > ‘LXQt settings’ > ‘Session Settings’ > ‘Autostart’.

Other useful packages

I installed the Elementary Icon Theme, as I saw some error messages about it not being installed:

user $ sudo apt install elementary-icon-theme

I installed a utility I find useful for searching for the location of a file, and ran the command to update its database of file names (you can run it at any time, or add it to e.g. crontab):

user $ sudo apt install mlocate
user $ sudo updatedb

I use Buku bookmark manager and Thunderbird e-mail client, so I installed those too:

user $ sudo apt install buku
user $ sudo apt install thunderbird
user $ sudo apt install thunderbird-locale-en

I added Buku to my crontab (the location of the binary has changed to /usr/bin/buku since I wrote my above-mentioned blog post on Buku in 2018):

1,21,41 * * * * rm /home/fitzcarraldo/ownCloud/Bookmarks/*.txt; sleep 30s && /usr/bin/buku -p --nc > /home/fitzcarraldo/ownCloud/Bookmarks/Buku_bookmarks_backup.txt

I prefer Signal to WhatsApp so I installed Signal Desktop by following the instructions at https://signal.org/download/#

Some of my family prefer the Google Chrome browser to Firefox, so I installed that:

user $ wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
user $ sudo apt install ./google-chrome-stable_current_amd64.deb

I right-clicked on the Desktop and selected ‘Create Launcher’ to create the file ~/Desktop/Google_Chrome_Web_browser.desktop for each user (I edited it to change the icon, and ticked ‘Trust this executable’ if not ticked):

[Desktop Entry]
Name=Google Chrome Web browser
GenericName=Web browser
Comment=Google Chrome Web browser
Exec=google-chrome
Type=Application
Icon=google-chrome
Terminal=false

I right-clicked on the Desktop and selected ‘Create Launcher’ to create the file ~/Desktop/Firefox_Web_browser.desktop for each user (I edited it to change the icon, and ticked ‘Trust this executable’ if not ticked):

[Desktop Entry]
Name=Firefox Web browser
GenericName=Web browser
Comment=Firefox Web browser
Exec=firefox
Type=Application
Icon=firefox
Terminal=false

I right-clicked on the Desktop and selected ‘Create Launcher’ to create the file ~/Desktop/Signal.desktop (I edited it to change the icon, and ticked ‘Trust this executable’ if not ticked):

[Desktop Entry]
Name=Signal
GenericName=Signal messenger
Comment=Signal messenger
Exec=signal-desktop
Type=Application
Icon=signal-desktop
Terminal=false

All the family use Skype, so I downloaded the .deb file from https://www.skype.com/en/get-skype/download-skype-for-desktop/ and double-clicked on it to install it.

I right-clicked on the Desktop and selected ‘Create Launcher’ to create the file ~/Desktop/Skype_for_Linux.desktop (I edited it to change the icon, and ticked ‘Trust this executable’ if not ticked):

[Desktop Entry]
Name=Skype for Linux
GenericName=Skype messaging
Comment=Skype for Linux
Exec=skypeforlinux
Type=Application
Icon=skypeforlinux
Terminal=false

I also dragged some of the icons from the Desktop to the Panel in order to create quick-launch items on the Panel, useful when frequently used items on the Desktop are obscured by open windows.

I installed and configured the utility Neofetch that can be used to display system information in a terminal window:

user $ sudo apt install xterm
user $ sudo apt install neofetch

I right-clicked on the Desktop and selected ‘Create Launcher’ to create the file ~/Desktop/Neofetch.desktop (I edited it to change the icon, and ticked ‘Trust this executable’ if not ticked):

[Desktop Entry]
Name=Neofetch
GenericName=A command-line system information tool written in Bash
Comment=A command-line system information tool written in Bash
Exec=xterm -e /bin/bash -c "neofetch; exec /bin/bash"
Type=Application
Icon=/home/fitzcarraldo/Pictures/Icons/neofetch.png
Terminal=false

Firewall

UFW (Uncomplicated Fire Wall) was already installed in Lubuntu 20.10 but not enabled, so I enabled it:

user $ sudo enable ufw
user $ sudo ufw status verbose

I edited /etc/modules-load.d/modules.conf to add two firewall-related modules needed to enable Samba commands and wsdd (see further on) to function correctly:

# /etc/modules: kernel modules to load at boot time.
#
# This file contains the names of kernel modules that should be loaded
# at boot time, one per line. Lines beginning with "#" are ignored.
iptable_raw
xt_pkttype

Bluetooth
The desktop machine has a USB Bluetooth adapter so I installed Blueman as it had not been installed by the Lubuntu 20.10 Installer:

user $ sudo apt install blueman
user $ sudo apt install --reinstall bluez

See also my blog post The phone name assigned automatically by Android on my new phone prevented Bluetooth pairing and connecting in Linux.

Samba

All the machines in my home network use broadcast NetBIOS name resolution and SMB (v2 and v3, not v1) for file sharing, so I installed the packages shown below and copied the smb.conf from the previous installation, and edited the UFW configuration file as explained in an earlier blog post.

user $ sudo apt install samba
user $ sudo apt install cifs-utils
user $ sudo apt install nbtscan
user $ sudo apt install smbclient
user $ sudo cp /media/fitzcarraldo/FREECOM\ HDD/Lubuntu_home_folders_backup_LXDE/fitzcarraldo/Samba/smb.conf /etc/samba/smb.conf

See the following blog posts for details:

As my home network includes a NAS, I also configured the user accounts to be able to mount the NAS shared folder for Windows applications running under WINE (see How to enable a Windows application in WINE to access a Samba share on a NAS (continued)):

~/Desktop/Mount_server1_user1_share.desktop

[Desktop Entry]
Name=Mount_server1_user1_share
GenericName=Mount server1 user1 share
Comment=Mount server1 user1 share
Exec=/home/fitzcarraldo/.mount_server1_user1_share.sh
Type=Application
Icon=/home/fitzcarraldo/Pictures/Icons/nuvola/128x128/devices/samba_mount.png
Terminal=true

~/.mount_server1_user1_share.sh

#!/bin/bash
mount_share () {
    echo
    echo "Enter your Linux account password below..."
    echo
    sudo ln -s /media/server1/user1 ~/$PREFIX/dosdevices/y:
    sudo mount.cifs //server1/user1/ -o user=user1,pass=<password of user1>,uid=$(id -u),gid=$(id -g) ~/$PREFIX/dosdevices/y:
}
echo
echo "This will mount the Samba share folder user1 on the server1 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/server1/user1" /proc/mounts; then
    echo "Samba share //server1/user1 is mounted for WINEPREFIX ~/$PREFIX ."
else
    echo "Samba share //server1/user1 is not mounted."
fi
echo
echo "You may now close this window."
read ANSWER
exit

~/Desktop/Unmount_server1_user1_share.desktop

[Desktop Entry]
Name=Unmount_server1_user1_share
GenericName=Unmount server1 user1 share
Comment=Unmount server1 user1 share
Exec=/home/fitzcarraldo/.umount_server1_user1_share.sh
Type=Application
Icon=/home/fitzcarraldo/Pictures/Icons/nuvola/128x128/devices/samba_unmount.png
Terminal=true

~/.umount_server1_user1_share.sh

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

WS-Discovery

As WS-Discovery is required to be able to browse SMB shares in File Explorer in Windows 10 installations these days, I also installed the WS-Discovery daemon. See an earlier blog post for details, including how to configure UFW: Using WS-Discovery to enable Windows 10 to browse SMB shares in my home network of Linux computers

user $ wget https://github.com/christgau/wsdd/archive/master.zip
user $ unzip master.zip
user $ sudo cp wsdd-master/src/wsdd.py /usr/bin/wsdd
user $ sudo cp wsdd-master/etc/systemd/wsdd.service /etc/systemd/system/

I added ‘--workgroup HOME‘ (the workgroup of all the machines in my home network) to /etc/systemd/system/wsdd.service and changed the group to ‘nogroup‘:

[Unit]
Description=Web Services Dynamic Discovery host daemon
; Start after the network has been configured
After=network-online.target
Wants=network-online.target
; It makes sense to have Samba running when wsdd starts, but is not required
;Wants=smb.service

[Service]
Type=simple
ExecStart=/usr/bin/wsdd --shortlog --workgroup HOME
; Replace those with an unprivledged user/group that matches your environment,
; like nobody/nogroup or daemon:daemon or a dedicated user for wsdd
User=nobody
Group=nogroup
; The following lines can be used for a chroot execution of wsdd.
; Also append '--chroot /run/wsdd/chroot' to ExecStart to enable chrooting
;AmbientCapabilities=CAP_SYS_CHROOT
;ExecStartPre=/usr/bin/install -d -o nobody -g nogroup -m 0700 /run/wsdd/chroot
;ExecStopPost=rmdir /run/wsdd/chroot

[Install]
WantedBy=multi-user.target

user $ sudo systemctl enable wsdd
user $ sudo systemctl start wsdd

I also installed a script to list WS-Discovery devices on my home network (see A Linux command-line utility to discover and list WSD-enabled computers and printers on a home network). I first copied the directory /media/fitzcarraldo/FREECOM HDD/Lubuntu_home_folders_backup_LXDE/fitzcarraldo/discover from the backup HDD to my new home directory, then I set up ~/discover/wsd-discover.sh:

user $ sudo apt install curl # curl is needed in wsd-discover.sh

As one of the awk commands in ~/discover/wsd-discover.sh would not work in Lubuntu 20.10 I changed the line:

cat /tmp/wsd-probe6.txt | awk -F "******" '{print $1 $3}' > /tmp/wsd-probe7.txt

to:

cat /tmp/wsd-probe6.txt | awk -F "\*\*\*\*\*\*" '{print $1 $3}' > /tmp/wsd-probe7.txt

The Desktop Configuration File (a.k.a. ‘Launcher’ in LXQt) ~/Desktop/Discover_WSD_devices.desktop contains the following:

[Desktop Entry]
Name=Discover_WSD_devices
GenericName=Discover WSD devices
Comment=Discover WSD devices
Exec=/home/fitzcarraldo/discover/wsd-discover.sh
Type=Application
Icon=/home/fitzcarraldo/Pictures/Icons/Crystal_Clear/png/actions/find.png
Terminal=true

I right-clicked on the Desktop and used ‘Create Launcher’ to create the above file. I edited it to change the icon, and ticked ‘Trust this executable’ if not ticked.

WINE

I installed WINE:

user $ sudo apt install wine

Then I copied across all my WINE prefix directories from the backup HDD and reconfigured them. For example, for MyPhoneExplorer:

user $ cd
user $ cp -r /media/fitzcarraldo/FREECOM\ HDD/Lubuntu_home_folders_backup_LXDE/fitzcarraldo/.wine-myphoneexplorer .
user $ export WINEPREFIX=~/.wine-myphoneexplorer
user $ winecfg
user $ cd ~/.wine-myphoneexplorer/drive_c/
user $ wine MyPhoneExplorer_Setup_v1.8.15.exe

Installing the Windows application creates a Desktop Configuration File on the Desktop. For example, the file ~/Desktop/MyPhoneExplorer.desktop contains:

[Desktop Entry]
Name=MyPhoneExplorer
Exec=env WINEPREFIX="/home/fitzcarraldo/.wine-myphoneexplorer" wine-stable C:\\\\windows\\\\command\\\\start.exe /Unix /home/fitzcarraldo/.wine-myphoneexplorer/dosdevices/c:/users/Public/Desktop/MyPhoneExplorer.lnk
Type=Application
StartupNotify=true
Path=/home/fitzcarraldo/.wine-myphoneexplorer/dosdevices/c:/users/Public/Start Menu/Programs/MyPhoneExplorer
Icon=5A9F_MyPhoneExplorer.0
StartupWMClass=myphoneexplorer.exe

user $ chmod 664 ~/Desktop/MyPhoneExplorer.desktop
user $ ls -la ~/Desktop/MyPhoneExplorer.desktop
-rw-rw-r-- 1 fitzcarraldo fitzcarraldo 436 Jan  2 02:54 /home/fitzcarraldo/Desktop/MyPhoneExplorer.desktop

ownCloud

As I use ownCloud on a server, I installed the ownCloud client (instructions are available on the ownCloud Web site):

user $ wget -nv https://download.owncloud.com/desktop/ownCloud/stable/latest/linux/Ubuntu_20.10/Release.key -O - | sudo apt-key add -
user $ echo 'deb https://download.owncloud.com/desktop/ownCloud/stable/latest/linux/Ubuntu_20.10/ /' | sudo tee -a /etc/apt/sources.list.d/owncloud.list
user $ sudo apt update
user $ sudo apt install owncloud-client

In order to be able to browse my ownCloud folder using WebDAV I installed davfs2:

user $ sudo apt install davfs2
user $ mkdir /home/fitzcarraldo/webdav

I added the following line in ~/.davfs2/secrets:

https://bsfitzgerald.ddns.net/owncloud/remote.php/webdav bsf <password for my account on my server>

I uncommented the following two lines in /etc/davfs2/davfs2.conf:

dav_user        davfs2            # system wide config file only
dav_group       davfs2            # system wide config file only

I added the following line to /etc/fstab:

https://bsfitzgerald.ddns.net/owncloud/remote.php/webdav /home/fitzcarraldo/webdav davfs noauto,user,rw 0 0

See also the following articles regarding configuring Ubuntu for WebDAV browsing:

I created the following Desktop Configuration Files to enable me to mount and unmount the WebDAV-shared folder by double-clicking on Desktop icons:

~/Desktop/Mount_server1_WebDAV_share.desktop

[Desktop Entry]
Name=Mount_server1_WebDAV_share
GenericName=Mount server1 WebDAV share
Comment=Mount server1 WebDAV share
Exec=mount /home/fitzcarraldo/webdav
Type=Application
Icon=/home/fitzcarraldo/Pictures/Icons/Crystal_Clear/png/devices/nfs_mount.png
Terminal=true

~/Desktop/Unmount_server1_WebDAV_share.desktop

[Desktop Entry]
Name=Unmount_server1_WebDAV_share
GenericName=Unmount server1 WebDAV share
Comment=Unmount server1 WebDAV share
Exec=fusermount -u /home/fitzcarraldo/webdav
Type=Application
Icon=/home/fitzcarraldo/Pictures/Icons/Crystal_Clear/png/devices/nfs_unmount.png
Terminal=true

Prevent suspending to RAM

I configured each user’s LXQt Power Management settings so none of the users’ accounts would suspend to RAM automatically. I may change this in future so I installed a script to enable me to prevent the installation suspending if desired (see How to move a mouse pointer automatically in Linux to simulate user activity):

user $ sudo apt install xdotool # My script to keep the mouse pointer moving automatically (when I run it) uses this.

I right-clicked on the Desktop and selected ‘Create Launcher’ to create the file ~/Desktop/Keep_mouse_moving.desktop below (and edited it to change the icon, and ticked ‘Trust this executable’):

[Desktop Entry]
Name=Keep_mouse_moving
GenericName=Keep mouse moving automatically
Comment=Keep mouse moving automatically
Exec=xterm -iconic -e "bash -c /home/fitzcarraldo/.keep_mouse_moving.sh"
Type=Application
Icon=input-mouse
Terminal=false

I created the script ~/.keep_mouse_moving.sh containing the following, which I can launch by double-clicking on the desktop icon when I wish:

#!/bin/bash
#
# Script to keep mouse pointer moving so that, for example, Suspend to RAM timeout does not occur.
# 
# The mouse pointer will move around its current position on the screen, i.e. around any position
# on the screen where you place the pointer. However, if you prefer it to move around the centre
# of the screen then change mousemove_relative to mousemove in the xdotool command below.
#
# Set LENGTH to 0 if you do not want the mouse pointer to move.
# Set LENGTH to 1 if you want the mouse pointer to move just a tiny fraction.
# Set LENGTH to e.g. 100 if you want to see more easily the mouse pointer move.
LENGTH=1
#
# Set DELAY to the desired number of seconds between each move of the mouse pointer.
DELAY=5
#
while true
do
#    if [[ `cat /proc/asound/card*/pcm*/sub*/status | grep RUNNING | wc -l` -ne 0 ]]; then
        for ANGLE in 0 90 180 270
        do
            xdotool mousemove_relative --polar $ANGLE $LENGTH
            sleep $DELAY
        done
#    fi
done

Automated backup of users’ home directories

I set up the same backup method that I implemented in the earlier Lubuntu installations (see Backing up users’ home directories in a Linux installation that uses systemd):

/etc/systemd/system/backup-to-usb-hdd.service

[Unit]
Description=Backup home directories of all users to USB HDD
DefaultDependencies=no
Before=shutdown.target halt.target
RequiresMountsFor=/home

[Service]
Type=oneshot
ExecStart=/usr/local/sbin/backup_home_directories.sh

[Install]
WantedBy=halt.target shutdown.target

/usr/local/sbin/backup_home_directories.sh

#!/bin/bash

# This script backs up to an external USB HDD (NTFS) labelled "FREECOM HDD" the contents of the home directories
# of the users of this Lubuntu 20.10 installation if the system is shutting down but not rebooting.
# It is launched by a systemd service /etc/systemd/system/backup-to-usb-hdd.service.

# Find out if the system is rebooting (as opposed to being shut down):
REBOOT=$( systemctl list-jobs | egrep -q 'reboot.target.*start' && echo "rebooting" || echo "not_rebooting" )
if [ $REBOOT = "not_rebooting" ]; then
# Only execute the following steps if the system is shutting down but not rebooting:
   # Clean up if the backup did not complete last time:
    umount /media/usbhdd 2>/dev/null # Make sure you enter this line correctly.
    rm -rf /media/usbhdd/* # Make sure you enter this line correctly.
    # Unmount the external USB HDD if mounted by udisks2 with the logged-in username in the path:
    umount /media/*/FREECOM\ HDD 2>/dev/null
    # Find out the USB HDD device:
    DEVICE=$( blkid | grep "FREECOM\ HDD" | cut -d ":" -f1 )
    # Create a suitable mount point if it does not already exist, and mount the device on it:
    mkdir /media/usbhdd 2>/dev/null
    mount -t ntfs-3g -o async,noexec,nodev,noatime,nodiratime $DEVICE /media/usbhdd 2>/dev/null
    # Create the backup directory on the USB HDD if it does not already exist:
    mkdir /media/usbhdd/Lubuntu_home_folders_backup 2>/dev/null
    # Backup recursively all the home directories of all the users, and add a time-stamped summary to the log file:
    echo "********** Backing up Acer Aspire XC600 users' home directories **********" >> /home/fitzcarraldo/backup.log
    date >> /home/fitzcarraldo/backup.log
    # Log username of user shutting down the PC (may not be this user if Switch User was used):
    echo -ne "User who shutdown PC (may not be this user if Switch User has been used): " >> /home/fitzcarraldo/backup.log
    last | cut -d " " -f1 | head -1 >> /home/fitzcarraldo/backup.log
    sleep 2s
    # cp --recursive --update --preserve=all --no-dereference --force /home/ /media/usbhdd/Lubuntu_home_folders_backup 2>> /home/fitzcarraldo/backup.log
    rsync --ignore-errors --recursive --times --perms --links --protect-args --exclude '/*/.cache/mozilla' --exclude '/*/.cache/google-chrome' --exclude '/*/.cache/chromium' --delete-excluded --bwlimit=22500 /home/ /media/usbhdd/Lubuntu_home_folders_backup 2>> /home/fitzcarraldo/backup.log
    echo "Copying completed" >> /home/fitzcarraldo/backup.log
    date >> /home/fitzcarraldo/backup.log
    echo "********** Backup completed **********" >> /home/fitzcarraldo/backup.log
    cp /home/fitzcarraldo/backup.log /media/usbhdd/Lubuntu_home_folders_backup/fitzcarraldo/
    # Unmount the USB HDD so that udisks2 can subsequently re-mount it with the user's username in the path:
    umount /media/usbhdd
fi
exit 0

user $ sudo systemctl enable backup-to-usb-hdd
user $ sudo systemctl start backup-to-usb-hdd

Keyboard layouts

As I want to be able to switch between English, Brazilian Portuguese and Spanish keyboard layouts, I did the following:

I added the following lines to the file /usr/share/sddm/scripts/Xsetup:

#Make sure the keyboards I use are selectable on the SDDM greeter screen
setxkbmap gb,us,br,es
#

I edited the file /etc/X11/xorg.conf.d/00-keyboard.conf so that it contains the following:

# Read and parsed by systemd-localed. It's probably wise not to edit this file
# manually too freely.
Section "InputClass"
        Identifier "system-keyboard"
        MatchIsKeyboard "on"
        Option "XkbLayout" "gb,us,br,es"
        Option "XkbModel" "pc105"
        Option "XkbOptions" "grp:alt_shift_toggle"
EndSection

I right-clicked on the Panel for each user, selected ‘Manage Widgets’ and added the ‘Keyboard state indicator’ to the Panel.

Conclusion

The resulting Lubuntu 20.10 installation is working fine so far. It has a more contemporary look than the LXDE Desktop in Lubuntu 18.04. My family is already using it and, so far, there have been no adverse comments or problems.