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.

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.

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.

Re-enabling OpenGL compositing automatically after it crashes KWin at login to KDE Plasma

One of my laptops has NVIDIA Optimus hardware and runs Gentoo Linux with the closed-source NVIDIA driver. Almost every time I logged-in to KDE Plasma for the first time after booting, OpenGL and compositing would be disabled (see screenshot below), and the usual methods of toggling compositing on/off would not work.

KDE Plasma - System Settings - Compositor Settings for Desktop Effects

KDE Plasma - System Settings - Compositor Settings for Desktop Effects.

I had to perform the following ritual in order to get ‘wobbly windows’ working again:

  1. select ‘System Settings’ > ‘Display and Monitor’ > ‘Compositor’
  2. click on ‘Re-enable OpenGL detection’
  3. deselect ‘Enable compositor on startup’
  4. click ‘Apply’
  5. select ‘Enable compositor on startup’
  6. click ‘Apply’

After having to perform this tedious process almost every time I logged in to KDE Plasma following boot-up, I finally decided to find an automated method of re-enabling OpenGL detection and compositing. I discovered that, when the problem occurred, the value of the variable OpenGLIsUnsafe in the file ~/.config/kwinrc had become ‘false‘. To get things working again I created the Bash script ~/restart_compositing.sh listed below. The script reverts the value of the variable OpenGLIsUnsafe to ‘true‘, reverts the value of the variable Enabled in the same section to ‘true‘ if it happens to be ‘false‘, and restarts KWin. Simple as that.

#!/bin/bash
#
# OpenGL compositing usually crashes KWin when I login, and compositing is then disabled.
# I have to select 'System Settings' > 'Display and Monitor' > 'Compositor' and perform
# the following steps to get compositing to work in the session:
#
# 1. click 'Re-enable OpenGL detection'
# 2. deselect 'Enable compositor on startup'
# 3. click 'Apply'
# 4. select  'Enable compositor on startup'
# 5. click 'Apply
#
# This script enables me to avoid having to perform the above manual procedure.
# This script is configured to run automatically at Plasma Startup - see:
# 'System Settings' > 'Startup and Shutdown' > 'Autostart'
#
edit_kwinrc () {
                # Extract the [Compositing] section from kwinrc
                awk '/\[Compositing\]/,/^$/' $HOME/.config/kwinrc > /tmp/kwinrc-extract
                # Remove the header in the extracted section
                sed -i '/\[Compositing\]/d' /tmp/kwinrc-extract
                # Remove the empty line at the end of the extracted section
                sed -i '/^$/d' /tmp/kwinrc-extract
                # Change the state configured for next login
                if [ $1 == "disablecompositing" ]; then
                    sed -i 's/Enabled=true/Enabled=false/g' /tmp/kwinrc-extract
                elif [ $1 == "enablecompositing" ]; then
                    sed -i 's/Enabled=false/Enabled=true/g' /tmp/kwinrc-extract
                elif [ $1 == "openglunsafe" ]; then
                    sed -i 's/OpenGLIsUnsafe=false/OpenGLIsUnsafe=true/g' /tmp/kwinrc-extract
                elif [ $1 == "openglsafe" ]; then
                    sed -i 's/OpenGLIsUnsafe=true/OpenGLIsUnsafe=false/g' /tmp/kwinrc-extract
                fi
                # Replace the [Compositing] section in kwinrc
                awk 'BEGIN {p=1} /^\[Compositing\]/ {print;system("cat /tmp/kwinrc-extract");p=0} /^$/ {p=1} p' $HOME/.config/kwinrc > /tmp/kwinrc
                cp /tmp/kwinrc $HOME/.config/kwinrc
}
#
# Avoid backing up an incorrectly-edited file
if [ ! -f $HOME/.config/kwinrc.bak ]; then
    cp $HOME/.config/kwinrc $HOME/.config/kwinrc.bak
fi
#
sleep 120s # This delay works for my specific laptop but might need to be adjusted on other machines.
if $( grep -q "OpenGLIsUnsafe=true" $HOME/.config/kwinrc ); then
    edit_kwinrc openglsafe
    edit_kwinrc enablecompositing # Just in case it was disabled as well.
    kwin_x11 --replace & > /dev/null 2>&1
fi
exit 0

I then selected ‘System Settings’ > ‘Startup and Shutdown’ > ‘Autostart’, clicked on ‘Add Script…’ and specified that /home/fitzcarraldo/restart_compositing.sh has to be run at ‘Startup’ (of Plasma). Problem solved.

Reconfiguring the time zone, locales and keymaps in Sabayon Linux

This is an example of how to reconfigure the time zone, locales and keymaps in a Sabayon Linux installation from the command line. Sabayon Linux uses systemd, therefore much of this example should also be applicable in other Linux distributions that use systemd, and will certainly be applicable in Gentoo Linux installations that use systemd rather than OpenRC.

You can check the currently selected keymaps (console and X Windows) and locale using the ‘localectl status‘ command. For example:

user $ localectl status
   System Locale: LANG=en_GB.UTF-8
       VC Keymap: uk
      X11 Layout: gb
       X11 Model: pc105

Let’s say I had previously configured my installation to use only the en_GB and en_US locales but I now want to add Swiss Italian. The steps would be as shown below. I will assume the system being configured is in Switzerland and therefore I will also reconfigure the time zone accordingly, but that is not essential.

Check if the desired time zone exists:

root # timedatectl list-timezones | grep Zurich
Europe/Zurich

Set the desired time zone:

root # timedatectl set-timezone Europe/Zurich

Check if the Swiss Italian locale (it_CH) has already been added:

root # localectl list-locales
C.utf8
en_GB
en_GB.iso88591
en_GB.utf8
en_US
en_US.iso88591
en_US.utf8

If the desired locale is not present, add it:

root # nano /etc/locale.gen
root # grep -v "^#\|^$" /etc/locale.gen
C.UTF-8 UTF-8
en_GB.UTF-8 UTF-8
en_GB ISO-8859-1
en_US.UTF-8 UTF-8
en_US ISO-8859-1
it_CH.UTF-8 UTF-8
it_CH ISO-8859-1

Generate the locales:

root # locale-gen
 * Generating 7 locales (this might take a while) with 1 jobs
 *  (1/7) Generating C.UTF-8 ...                                          [ ok ]
 *  (2/7) Generating en_GB.ISO-8859-1 ...                                 [ ok ]
 *  (3/7) Generating en_GB.UTF-8 ...                                      [ ok ]
 *  (4/7) Generating en_US.ISO-8859-1 ...                                 [ ok ]
 *  (5/7) Generating en_US.UTF-8 ...                                      [ ok ]
 *  (6/7) Generating it_CH.ISO-8859-1 ...                                 [ ok ]
 *  (7/7) Generating it_CH.UTF-8 ...                                      [ ok ]
 * Generation complete
 * Adding locales to archive ...                                          [ ok ]

Check the locales have been added:

root # localectl list-locales
C.utf8
en_GB
en_GB.iso88591
en_GB.utf8
en_US
en_US.iso88591
en_US.utf8
it_CH
it_CH.iso88591
it_CH.utf8

Set the desired locale:

root # localectl set-locale LANG=it_CH.UTF-8

Check which Italian console keymaps are available:

root # localectl list-keymaps | grep it
it
it-ibm
it2
mac-it

But let’s say I want to use a Swiss German keymap (sg) for the console instead of an Italian keymap. Check if a console keymap for Swiss German exists:

root # localectl list-keymaps | grep sg
sg
sg-latin1
sg-latin1-lk450

By the way, Debian, Ubuntu and its derivatives store console keymaps differently to some distributions, and the command ‘localectl list-keymaps‘ in Debian, Ubuntu and its derivatives will return an error message (the command ‘localectl set-keymap’ will still work though):

user $ localectl list-keymaps
Failed to read list of keymaps: No such file or directory

Set the console keymap to Swiss German:

root # localectl set-keymap sg

Let’s say I want to use a Swiss keymap in X Windows. Check if it exists:

root # localectl list-x11-keymap-layouts | grep sg
root # localectl list-x11-keymap-layouts | grep ch
ch

Set the X Windows keymap to Swiss:

root # localectl set-x11-keymap ch

Update the environment variables and profile:

root # env-update && source /etc/profile
>>> Regenerating /etc/ld.so.cache...

Edit /etc/default/grub and change (or add, if none exists) the console keymap entry in GRUB_CMDLINE_LINUX_DEFAULT to be vconsole.keymap=sg, and also rd.vconsole.keymap=sg (‘rd‘ stands for ‘RAM disk’) because Sabayon Linux uses an initramfs:

root # nano /etc/default/grub

Regenerate grub.cfg:

root # grub-mkconfig -o /boot/grub/grub.cfg
Generazione file di configurazione GRUB...
Trovato sfondo: /boot/grub/default-splash.png
Trovata immagine linux: /boot/kernel-genkernel-x86_64-5.4.0-sabayon
Trovata immagine initrd: /boot/initramfs-genkernel-x86_64-5.4.0-sabayon
fatto

Reboot to check if everything is working:

root # systemctl reboot

Check that the list of locales is as expected:

root # eselect locale list
Available targets for the LANG variable:
  [1]   C
  [2]   C.utf8
  [3]   en_GB
  [4]   en_GB.iso88591
  [5]   en_GB.utf8
  [6]   en_US
  [7]   en_US.iso88591
  [8]   en_US.utf8
  [9]   it_CH
  [10]  it_CH.iso88591
  [11]  it_CH.utf8
  [12]  POSIX
  [13]  it_CH.UTF-8 *
  [ ]   (free form)

Check if the current configuration is as expected:

root # localectl status
   System Locale: LANG=it_CH.UTF-8
       VC Keymap: sg
      X11 Layout: ch

If the Desktop Environment is KDE, check the file ~/.config/plasma-localerc to see if the LANG variable is set to the locale just configured.

root # cat /home/fitzcarraldo/.config/plasma-localerc
[Formats]
LANG=en_GB.UTF-8

If it is not, delete the file:

root # rm /home/fitzcarraldo/.config/plasma-localerc

then logout, login again and re-check the file:

root # cat /home/fitzcarraldo/.config/plasma-localerc
[Formats]
LANG=it_CH.UTF-8

The system should now be ready for use with the new time zone, locale and keymaps.

user $ date
gio 2 lug 2020, 15:55:21, CEST
user $ localectl status
   System Locale: LANG=it_CH.UTF-8
       VC Keymap: sg
      X11 Layout: ch

Using a mixture of locale variables

It is not mandatory for all the locale variables to be for the same locale. For example, suppose I want to use the currency and number formats of one of the other locales I added. That is not so outlandish: I could be a Swiss national whose mother tongue is Swiss Italian, working in the Swiss branch of a British company and I want the currency format and number format on my work computer to be British, but everything else to be Swiss. To achieve this I can additionally do the following:

root # localectl set-locale LC_MONETARY=en_GB.UTF-8
root # localectl set-locale LC_NUMERIC=en_GB.UTF-8
root # env-update && source /etc/profile

Check that the main locale and keymaps remain as they were but that the two locale variables have been changed to the British locale:

root # localectl status
   System Locale: LANG=it_CH.UTF-8
                  LC_NUMERIC=en_GB.UTF-8
                  LC_MONETARY=en_GB.UTF-8
       VC Keymap: sg
      X11 Layout: ch
root # locale
LANG=it_CH.UTF-8
LC_CTYPE="it_CH.UTF-8"
LC_NUMERIC=en_GB.UTF-8
LC_TIME="it_CH.UTF-8"
LC_COLLATE="it_CH.UTF-8"
LC_MONETARY=en_GB.UTF-8
LC_MESSAGES="it_CH.UTF-8"
LC_PAPER="it_CH.UTF-8"
LC_NAME="it_CH.UTF-8"
LC_ADDRESS="it_CH.UTF-8"
LC_TELEPHONE="it_CH.UTF-8"
LC_MEASUREMENT="it_CH.UTF-8"
LC_IDENTIFICATION="it_CH.UTF-8"
LC_ALL=

You can see above that only $LC_NUMERIC and $LC_MONETARY have changed, as I wanted. As I did not change the time zone, the command I used earlier to set the time zone to Europe/Zurich is still in force:

root # date
gio 2 lug 2020, 16:12:18, CEST

By the way, if you try to change one of the variables from en_GB.UTF-8 back to it_CH.UTF-8, the change does not show in the output of the locale command. For example, let’s say you want to change LC_NUMERIC back to it_CH.UTF-8:

root # localectl status
   System Locale: LANG=it_CH.UTF-8
                  LC_NUMERIC=en_GB.UTF-8
                  LC_MONETARY=en_GB.UTF-8
       VC Keymap: sg
      X11 Layout: ch
root # localectl set-locale LC_NUMERIC=it_CH.UTF-8
root # cat /etc/locale.conf   
LANG=it_CH.UTF-8
LC_MONETARY=en_GB.UTF-8
root # cat /etc/env.d/02locale
LANG=it_CH.UTF-8
LC_MONETARY=en_GB.UTF-8
root # localectl status
   System Locale: LANG=it_CH.UTF-8
                  LC_MONETARY=en_GB.UTF-8
       VC Keymap: sg
      X11 Layout: ch
root # locale
LANG=it_CH.UTF-8
LC_CTYPE="it_CH.UTF-8"
LC_NUMERIC=en_GB.UTF-8  <-- Notice it didn't change
LC_TIME="it_CH.UTF-8"
LC_COLLATE="it_CH.UTF-8"
LC_MONETARY=en_GB.UTF-8
LC_MESSAGES="it_CH.UTF-8"
LC_PAPER="it_CH.UTF-8"
LC_NAME="it_CH.UTF-8"
LC_ADDRESS="it_CH.UTF-8"
LC_TELEPHONE="it_CH.UTF-8"
LC_MEASUREMENT="it_CH.UTF-8"
LC_IDENTIFICATION="it_CH.UTF-8"
LC_ALL=
root # env-update && source /etc/profile
>>> Regenerating /etc/ld.so.cache...
root # locale
LANG=it_CH.UTF-8
LC_CTYPE="it_CH.UTF-8"
LC_NUMERIC=en_GB.UTF-8 <-- Notice it still didn't change
LC_TIME="en_GB.UTF-8"
LC_COLLATE="it_CH.UTF-8"
LC_MONETARY=en_GB.UTF-8
LC_MESSAGES="it_CH.UTF-8"
LC_PAPER="it_CH.UTF-8"
LC_NAME="it_CH.UTF-8"
LC_ADDRESS="it_CH.UTF-8"
LC_TELEPHONE="it_CH.UTF-8"
LC_MEASUREMENT="it_CH.UTF-8"
LC_IDENTIFICATION="it_CH.UTF-8"
LC_ALL=

This is one of the reasons I’m not keen on the layer of abstraction added by systemd. The way to get LC_NUMERIC back to it_CH.UTF-8 is to change all the locale variables to en_GB.UTF-8 then back to it_CH.UTF-8:

root # localectl set-locale LANG=en_GB.UTF-8
root # env-update && source /etc/profile
root # localectl set-locale LANG=it_CH.UTF-8
root # env-update && source /etc/profile
root # reboot

After rebooting, the change will have been applied:

root # locale
LANG=it_CH.UTF-8
LC_CTYPE="it_CH.UTF-8"
LC_NUMERIC="it_CH.UTF-8"
LC_TIME="it_CH.UTF-8"
LC_COLLATE="it_CH.UTF-8"
LC_MONETARY="it_CH.UTF-8"
LC_MESSAGES="it_CH.UTF-8"
LC_PAPER="it_CH.UTF-8"
LC_NAME="it_CH.UTF-8"
LC_ADDRESS="it_CH.UTF-8"
LC_TELEPHONE="it_CH.UTF-8"
LC_MEASUREMENT="it_CH.UTF-8"
LC_IDENTIFICATION="it_CH.UTF-8"
LC_ALL=
root # localectl status
   System Locale: LANG=it_CH.UTF-8
       VC Keymap: sh
      X11 Layout: ch

Notice that everything has changed back to it_CH.UTF-8, including $LC_MONETARY, so you’d have to repeat the command ‘localectl set-locale LC_MONETARY=en_GB.UTF-8‘ if you wanted that to still be the British format.

If you use KDE, also check the contents of the file ~/.config/plasma-localerc to make sure it contains the correct locale:

user $ cat ~/.config/plasma-localerc
[Formats]
LANG=it_CH.UTF-8

Optionally you could edit that file to add desired settings. For example:

[Formats]
LANG=it_CH.UTF-8
LC_CTYPE=it_CH.UTF-8
LC_NUMERIC=en_GB.UTF-8
LC_TIME=it_CH.UTF-8
LC_COLLATE=it_CH.UTF-8
LC_MONETARY=en_GB.UTF-8
LC_MESSAGES=it_CH.UTF-8
LC_PAPER=it_CH.UTF-8
LC_NAME=it_CH.UTF-8
LC_ADDRESS=it_CH.UTF-8
LC_TELEPHONE=it_CH.UTF-8
LC_MEASUREMENT=it_CH.UTF-8
LC_IDENTIFICATION=it_CH.UTF-8
useDetailed=true

Alternatively, delete that file then logout and login again to make KDE Plasma pick up the values of the variables from the existing configuration. KDE Plasma will recreate the file.

Quick work-around for bug in KDE Plasma 5.19.1 (Restart and Shut Down from system menu result in Log Out instead)

See the recent post on 17 June 2020 by reddit and Arch Linux user SnowGigs re KDE Plasma 5.19.1: Cannot shutdown or restart from system menu. Actually I experience the same problem with Plasma 5.19.0 in Gentoo Linux.

Until you can install a newer version of Plasma which fixes the bug, here is a quick and easy work-around that is perfectly usable:

Install kde-apps/kdialog and add the following Desktop Configuration file Shutdown.desktop to your Desktop directory — also drag it to the Panel if you wish — and make it executable:

[Desktop Entry]
Comment[en_GB]=Shutdown system
Comment=Shutdown system
Exec=kdialog --title "Shutdown system" --warningcontinuecancel "Are you sure you want to shutdown?" && loginctl poweroff
GenericName[en_GB]=Shutdown
GenericName=Shutdown
Icon=system-shutdown
MimeType=
Name[en_GB]=Shutdown
Name=Shutdown
Path=
StartupNotify=true
Terminal=false
TerminalOptions=
Type=Application
X-DBUS-ServiceName=
X-DBUS-StartupType=none
X-KDE-SubstituteUID=false
X-KDE-Username=fitzcarraldo

(Obviously replace ‘fitzcarraldo‘ with your own username.)

user $ chown 744 ~/Desktop/Shutdown.desktop

I have assumed your installation has elogind installed, hence the command ‘loginctl poweroff‘ in the above-mentioned file. If systemd is installed instead of elogind, use the command ‘systemctl poweroff‘ instead.

When you click/double-click on the ‘Shutdown’ icon, a window will pop-up asking you ‘Are you sure you want to shutdown?’ with ‘Continue’ and ‘Cancel’ buttons. If you click ‘Continue’ then the system will execute the ‘loginctl poweroff‘ command; if you click on ‘Cancel’ then it won’t. Simple as that.

If you want, you can also create a file Reboot.desktop to reboot the system:

[Desktop Entry]
Comment[en_GB]=Reboot system
Comment=Reboot system
Exec=kdialog --title "Reboot system" --warningcontinuecancel "Are you sure you want to reboot?" && loginctl reboot
GenericName[en_GB]=Reboot
GenericName=Reboot
Icon=system-reboot
MimeType=
Name[en_GB]=Reboot
Name=Reboot
Path=
StartupNotify=true
Terminal=false
TerminalOptions=
Type=Application
X-DBUS-ServiceName=
X-DBUS-StartupType=none
X-KDE-SubstituteUID=false
X-KDE-Username=fitzcarraldo

Similarly, if systemd is installed instead of elogind, use the command ‘systemctl reboot‘ instead.

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

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

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

[...]
        onSourceAdded: {

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

            }

        }
[...]

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

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

kargos widget on KDE Plasma Panel

kargos widget on KDE Plasma Panel of my Compal NBLB2 laptop

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

1. Install and configure lm-sensors

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

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

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

2. Start lm-sensors now, rather than rebooting

root # /etc/init.d/lm_sensors start

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

root # emerge netcat

4. Install and configure hddtemp

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

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

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

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

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

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

5. Start hddtemp now, rather than rebooting

root # /etc/init.d/hddtemp start

6. Install Kargos

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

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

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

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

root # sensors

Don’t forget to make the script executable:

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

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

8. Add the kargos widget to the KDE Panel.

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

10. Configure the kargos widget

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

/home/fitzcarraldo/temperatures.3s.sh

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

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

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

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

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

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

1. Bash script ~/mount_bsfnas1_brianfolder_share.sh

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

2. Bash script ~/umount_bsfnas1_brianfolder_share.sh

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

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

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

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

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

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


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


== Select which WINEPREFIX you wish to use ==

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

Enter number (q to abort) and press ENTER: 

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


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


== Select which WINEPREFIX you wish to use ==

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

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

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

Enter your Linux account password below...

[sudo] password for fitzcarraldo: 

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

You may now close this window.

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

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


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

Enter your Linux account password below...

[sudo] password for fitzcarraldo: 

Samba share //bsfnas1/brianfolder is not mounted.

You may now close this window.

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

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

Creating a RAID of USB pendrives in Linux

USB hub and USB pendrives used as RAID10 with my laptop

USB hub and pendrives used as RAID10 with my laptop.

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

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

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

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

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

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

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

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

2. I installed the mdadm tool:

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

3. I installed the F2FS tools:

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

4. I rebooted the laptop.

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

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

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

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

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

8. I ascertained the names of the USB pendrives:

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

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

9. I loaded the raid10 module:

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

10. I created the RAID10 array:

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

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

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

11. I formatted the RAID:

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

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

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

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

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

root # mkdir -p /mnt/md0

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

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

So /dev/md0 looked correct.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

inherit distutils-r1

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  1. I created a NetworkManager Dispatcher hook script to:

    1. detect when the laptop connects to a network;

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

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

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

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

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

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

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

        esac

        select_zone $ZONE
        exit $?

    fi
fi

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

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