croc – another file transfer method

I have lost count of the number of times I have had to send a large file to someone at work, usually in a hurry. I’ve used Dropbox, ownCloud, Firefox Send (no longer available) etc. Transferring large files became a bit easier when e-mail service providers increased the size limit for attachments, but that is still not a solution for very large files. The xkcd cartoon FILE TRANSFER sums up the situation nicely.

I recently discovered the command line utility croc, which the author claims is a way to ‘easily and securely transfer stuff from one computer to another.’ I thought I’d give it a try, if only to have another tool to fall back on in an emergency. It does rely on both ends having croc installed, but hopefully that should not be a show-stopper as croc is available for Linux, Windows, macOS and BSD. To quote the author:

croc differs from a utility like scp because it doesn’t require any two computers to have enabled port-forwarding. Instead, croc will uses a relay – a temporary server setup locally (if both computers are on lan) or publicly (default is at croc4.schollz.com). Any two computers can connect to the relay, and after securing their channel with PAKE [password authenticated key exchange], they can transfer encrypted metadata and data through the relay. The relay works by first having the computers communicate the PAKE protocol via websockets, and then exchanging encrypted metadata, and then stapling the TCP connections directly so that they can transfer directly.

So, to use croc you will be dependent on the public relay provided by the author unless you set up your own relay (instructions are provided in the author’s original 2018 blog post introducing croc – see link above – and in various third-party articles about croc, such as ‘Securely Transfer Files and Folders Between Computers Using Croc‘ and ‘Transfer Files And Folders Between Computers With Croc‘).

Anyway, I installed croc in Lubuntu and Gentoo Linux from the author’s GitHub repository and indeed it is easy to use and works fine. The binary releases for the various OSs and Linux distributions can be found on the Releases page of the GitHub repository or via the OS package manager.

Lubuntu 20.10:

user $ wget https://github.com/schollz/croc/releases/download/v9.1.6/croc_9.1.6_Linux-64bit.deb
user $ sudo dpkg -i croc_9.1.6_Linux-64bit.deb

Gentoo Linux:

root # emerge net-misc/croc

(Note that croc ebuilds are not currently marked as Stable in the Gentoo Linux Portage tree, so you’ll have to unmask them by keyword if you are using the Stable branch.)

Termux:

I even installed croc in Termux on my Samsung Galaxy Note 20 Ultra 5G, and it works in Android too:

$ pkg install croc

Other OSs and other Linux distributions:

See the instructions in the README file online.

Using croc

Using croc is as simple as entering a command on one computer, informing (via e-mail, telephone, SMS, Signal or other social media) the person using the other computer of the command to use, and entering that command on the other computer. For example:

Sender

user $ croc send Documents/flight-times.ods
Sending 'flight-times.ods' (16.6 kB)
Code is: 8878-salary-courage-roger
On the other computer run

croc 8878-salary-courage-roger

Receiver

user $ croc 8878-salary-courage-roger
Accept 'flight-times.ods' (16.6 kB)? (Y/n) 

If the receiving user then enters ‘Y’, the sending user sees something similar to this:

user $ croc send Documents/flight-times.ods
Sending 'flight-times.ods' (16.6 kB)
Code is: 8878-salary-courage-roger
On the other computer run

croc 8878-salary-courage-roger

Sending (->192.168.1.74:60740)
 100% |████████████████████| (17/17 kB, 10.918 MB/s)
user $ 

and the receiving user sees something similar to this:

user $ croc 8878-salary-courage-roger
Accept 'flight-times.ods' (16.6 kB)? (Y/n) Y

Receiving (<-[::1]:39442)
 100% |████████████████████| (17/17 kB, 3.989 MB/s)
user $ 

The observant reader will notice that the above example shows a file being transferred on the same computer. When transferred between different computers the IP addresses of each computer will be displayed instead. I have used croc to transfer files between different computers on my home network (I would normally just use my NAS for this, though), between remote computers on the Internet, and between my computers and my phone via mobile broadband, and croc works in all cases.

I have not mentioned all croc’s features. I’ll leave you to read up on croc in more detail in the links I’ve given above. It looks like it might be a useful tool to have installed.

Using adb tools in Linux to remove bloatware from my Samsung Galaxy Note 20 Ultra

Samsung included a lot of bloatware on my Galaxy Note 20 Ultra 5G, and it is not possible to uninstall it using Play Store. However, it is possible to remove this stuff using adb tools. I got rid of the bloatware I don’t want very easily using the Linux version of the adb tools.

I have never had a Facebook account and never will, so I decided to remove all trace of it as follows:

1. Installed adb tools

In Lubuntu 20.10:

user $ sudo apt install android-tools-adb

In Gentoo Linux:

root # emerge dev-util/android-tools

2. Enabled ‘Developer Options’ on the phone

‘Settings’ > ‘About Phone’ > ‘Software Information’ and quickly tapped 7 times on ‘Build number’.

3. Enabled USB Debugging on the phone

‘Settings’ > ‘Developer options’, scrolled down and tapped on ‘USB debugging’.

4. Launched adb

user $ adb start-server
* daemon not running; starting now at tcp:5037
* daemon started successfully

5. Connected the phone to the computer using the USB cable

A few prompts on the phone asked whether or not I wanted to allow USB debugging. Tapped ‘Always allow from this computer’ and tapped ‘OK’.

6. Uninstalled Facebook

The packages I needed to uninstall were:

com.facebook.appmanager
com.facebook.katana
com.facebook.services
com.facebook.system

First I tried to uninstall with the ‘-k‘ option:

user $ adb uninstall -k --user 0 com.facebook.appmanager
The -k option uninstalls the application while retaining the data/cache.
At the moment, there is no way to remove the remaining data.
You will have to reinstall the application with the same signature, and fully uninstall it.
If you truly wish to continue, execute 'adb shell cmd package uninstall -k'.

See ‘Difference between pm clear and pm uninstall -k on Android

I have never been a member of Facebook and never will, so I dispensed with the ‘-k‘ option and entered the following commands:

user $ adb uninstall --user 0 com.facebook.appmanager
Success
user $ adb uninstall --user 0 com.facebook.katana
Success
user $ adb uninstall --user 0 com.facebook.services
Success
user $ adb uninstall --user 0 com.facebook.system
Success

I didn’t want the LinkedIn, Samsung Global Goals and Spotify apps either, so I uninstalled those too:

user $ adb uninstall --user 0 com.linkedin.android
Success
user $ adb uninstall --user 0 com.samsung.sree
Success
user $ adb uninstall --user 0 com.spotify.music
Success

7. Stopped the adb server on the computer

user $ adb kill-server

8. Unplugged the phone from the computer.

That’s it.

In order to disable the apps using this method, you will need to know the exact package name of the app you want to get rid of. For this, use Play Store and install App Inspector (there are several apps with this name in Play Store; I installed the app by Projectoria Ltd but the others look OK too). Launch App Inspector and you can find the package name under the name of the app. This starts with a ‘com‘ or ‘net‘ followed by words separated by dots.

For example, App Inspector shows the package name for LinkedIn as ‘com.linkedin.android‘.

Some useful links:

To get a list of all the packages installed on my phone:

user $ adb shell pm list packages

To get a list of system apps only:

user $ adb shell pm list packages -s

To get a list of only Samsung packages:

user $ adb shell pm list packages | grep samsung

To search for e.g. facebook packages:

user $ adb shell pm list packages | grep facebook

(Returns nothing now, as I already deleted all the Facebook packages. Yay!)

To search for other packages, e.g.:

user $ adb shell pm list packages | grep kids
package:com.samsung.android.kidsinstaller
package:com.sec.android.app.kidshome

Resurrecting my Iomega Zip 100 parallel-port drive – Linux comes to the rescue

Top view of Z100P2 drive with 100 MB Zip disk in front.

Top view of Z100P2 drive with 100 MB Zip disk in front.

Z100P2 drive with disk inserted.

Z100P2 drive with disk inserted.

Rear sockets of Z100P2 drive.

Rear sockets of Z100P2 drive.

Back in 1998 I purchased what was then a state-of-the-art storage medium: an external Iomega Zip 100 drive, which used removable 100 MB ‘SuperFloppy’ disks. Until 2002 I backed up my important files on removable Zip 100 MB disks. Over several years in the 1990s Iomega released various models of the Zip 100 MB drive: internal SCSI; internal IDE; internal ATAPI; external DB-25 IEEE 1284 parallel port; external USB 1.1. I bought the external DB-25 IEEE 1284 parallel port model Z100P2. When affordable CD drives and external hard disk drives started to appear I began using those for backups instead, and the Zip drive and a box full of Zip 100 MB disks had been gathering dust on a shelf at home since I stopped using them in 2002.

Now, I was fairly sure I had copied all the files off those Zip disks all those years ago, but recently I wanted to check the contents and then wipe the disks prior to disposing of them and the drive. The trouble was, I have not owned a computer with a legacy parallel port for many years. This is the story of how I managed to use the Zip 100 drive again after a hiatus of some nineteen years.

Notice that the drive has a second DB-25 port with the icon of a printer above it. That socket is to allow a legacy parallel port printer to be connected (‘daisy chained’) to the computer at the same time as the Zip 100 drive. I have not owned a parallel port printer for many years, so that port is of no interest to me.

By the way, the Iomega Zip 100 drive gained rather a bad reputation because of the so-called click of death, although Iomega stated that it affected less than 0.5 percent of all Jaz and Zip drives. I never experienced this problem with my Zip 100 drive and it is still working.

PART 1 – HARDWARE

Power supply for Z100P2

When I purchased it in 1998, the Zip 100 drive was supplied with a chunky and rather heavy 240 VAC to 5 VDC PSU. However, I gave that away several years ago with an old 250 MB external USB HDD that required a 5 VDC power supply. So my first job was to get a 5 VDC supply for the Zip 100 drive. I decided to buy a USB-to-barrel-plug cable to power the Zip drive from a USB port on a computer. So I purchased a ‘USB to 5V DC power cable compatible with the Iomega Z100P2 ZIP drive’ from Amazon. The LEDs on the drive lit up and the drive briefly made the expected noise when I connected the drive to a computer using this power cable, so I was making progress. If a computer happens to have USB Type-A ports, this turns out to be a much neater approach than having to use a 5 VDC PSU.

5 Volts DC power socket on Z100P2 and barrel connector of the cable that is connected to the computer via USB Type-A at the other end.

5 Volts DC power socket on Z100P2 and barrel connector of the cable that is connected to the computer via USB Type-A at the other end.

 
Failed first attempt: USB to legacy parallel port printer adapters do NOT work with parallel Zip drives!

None of my laptops and desktop machines have the legacy DB-25 parallel port that the Z100P2 drive requires. No problem, I thought to myself, I’ll just buy a ‘USB to Printer DB25 25-Pin Parallel Port Cable Adapter’ – there are umpteen of these adapters available on eBay and Amazon. It wasn’t expensive, but I found out the hard way that these cable adapters usually work with parallel printers but definitely do not work with Iomega Zip 100 drives. So I needed to do one of the following:

  • get a parallel printer interface card for a PCIe slot in my modern desktop machines – and hope it would work with a Z100P2 drive;
  • get a legacy computer with a bidirectional parallel port with a DB-25 socket;
  • get a legacy computer with a PCI slot into which I could insert a legacy parallel printer PCI interface card (assuming I could get hold of one).

Computer with legacy parallel port

I searched eBay and found a second-hand Dell OptiPlex 780 SFF (Small Form Factor) with a legacy DB-25 parallel port (connected to the motherboard rather than to a card in one of its PCI slots), Intel Pentium E5800 CPU (3.20 GHz, 800 Mz FSB), 4 GB of PC3-10600U (1333 MHz) DDR3 DIMM memory and Windows 10 Pro installed with a valid licence. It also has plenty of USB 2.0 Type-A ports, convenient for the USB-to-barrel-plug cable I bought to power the Z100P2 drive. The price was very reasonable indeed, so I bought it in the hope that it would be usable. The vendor assured me that Windows 10 detected the parallel port and no errors were reported, but the vendor had no legacy devices (e.g. parallel port printer) with which to actually test the port. Anyway, as it was so cheap I took a gamble and purchased it, although my research on the Web had already indicated that Windows 10 does not support parallel port Iomega Zip drives. I was thinking I could either try using a virtual machine or just wipe Windows 10 and install Linux on the machine.

The FSB speed of the legacy CPU actually limits the memory speed to 800 MHz, but performance is not too bad. I actually replaced the 4 GB of PC3-10600U memory with 8 GB of PC3-12800U (1600 MHz) memory (Crucial CT51264BD160B.C16FED2) which I purchased for a very good price on eBay, although upgrading to 8 GB of memory was not necessary for the purpose of getting the Zip 100 drive working. I decided to increase the memory because the machine is in a nice condition so I will keep it for future projects, which might need more memory.

By the way, the Dell documentation for the OptiPlex 780 SFF that I downloaded from Dell’s Web site states that the machine can only use 1066 MHz memory modules or 1333 MHz memory modules, and the 1333 MHz memory modules would only be able to have a speed of 1066 MHz. What is not obvious is that the documentation assumes that one of the E6xxx series or E7xxx series Wolfdale-3M CPUs (45 nm) is installed, as the speed of the FSB (Front Side Bus) of those CPUs is 1066 MHz. The earlier Wolfdale-3M CPUs which are installed in some OptiPlex 780 SFF machines have a FSB speed of 800 MHz, so even 1066 MHz memory modules are only going to have a speed of 800 MHz in those machines. The Wolfdale-3M CPU in my Dell machine is an E5800, which has a FSB speed of 800 MHz, so the memory speed is limited to 800 MHz (as confirmed on the BIOS System Setup screen, by the CPU-Z utility program running in Windows 10 (2 x 399.0 MHz), and by the Linux commands ‘sudo dmidecode --type 17‘ and ‘sudo lshw -short -C memory‘). The Crucial CT51264BD160B.C16FED2 PC3-12800 modules work fine in the machine, albeit limited to 800 MHz due to the CPU bus speed. On another note, if you happen to be looking for memory for a Dell OptiPlex 780 SFF, do NOT buy CT51264BD160BJ modules: the ‘J’ stands for ‘high-density’, and high-density modules do not work in this model.

Parallel port settings in the PC BIOS

The refurbished Dell OptiPlex 780 SFF has the following user-selectable options:

  1. Disable = Port is disabled
  2. AT = Port is configured for IBM AT compatibility
  3. PS/2 = Port is configured for IBM PS/2 compatibility
  4. EPP = Enhanced Parallel Port protocol
  5. ECP No DMA = Extended Capability Port protocol with no DMA
  6. ECP DMA 1 = Extended Capability Port protocol with DMA 1
  7. ECP DMA 3 = Extended Capability Port protocol with DMA 3

The BIOS had option ‘PS/2’ selected when I received the machine, which I eventually changed to ‘ECP No DMA’ but I think that was unnecessary.

The BIOS also had the Parallel Port Address set to 378h when I received it, and I left it as that.

Data connection

Fortunately I still had the original parallel cable to connect the Zip drive to a DB-25 parallel port on a computer.

Z100P2 end of cable connected to computer parallel port.

Z100P2 end of cable connected to computer parallel port.

Rear of legacy Dell PC with Z100P2 cable connected to the parallel port, and USB-to-barrel-plug power cable connected to a USB port.

Rear of legacy Dell PC with Z100P2 cable connected to the parallel port, and USB-to-barrel-plug power cable connected to a USB port.

PART 2 – SOFTWARE

First attempt – Failure: Windows XP in a VirtualBox virtual machine

My original intention was to wipe Windows 10 from the Dell machine and install Linux to see if I could get Linux to access the Zip drive. But, on second thoughts, I decided I might have a better chance in Windows because my research on the Web had already indicated that several people had successfully used Iomega Zip 100 parallel-port drives with Windows XP running in a virtual machine under Windows 10. I carefully followed a detailed article on how to do this using VirtualBox (How to use iomega zip 100 with parallel port on a windows 10 computer (so long as you have a free PCI slot)), but the Zip drive would not work with the Dell machine. I tried every BIOS option for the parallel port; I tried allowing Windows XP to install the driver; I installed the last official Iomega issue of the driver for Windows XP. Nothing worked.

Second attempt – Failure: Lubuntu 20.10 in a VirtualBox virtual machine

Then I decided to try installing Linux in a VirtualBox virtual machine under Windows 10. I chose Lubuntu 20.10 because it already has the necessary ppa (for older Zip parallel-port drives like mine) and the imm (for later versions of Zip 100 parallel-port drives than mine) modules built and either could simply be loaded from the command line. But that couldn’t access the drive either. Again, I tried without success every BIOS option for the parallel port.

Third attempt – Success: Live Lubuntu 20.10 on a USB pendrive

I was resigned to wiping Windows 10 and installing a Linux distribution when I had a brainwave: Why not try a Live Linux distribution? I used the mkusb utility to create a persistent installation of Live Lubuntu 20.10 on a USB pendrive (it had to use PC BIOS, as the legacy Dell machine does not support UEFI), booted it and used the command modprobe ppa to load the ppa parallel port driver. Shazam! The drive became device /dev/sdc4 and was auto-mounted as ‘ZIP-100’ in the LXQt file manager window. I can browse all the files on the 100 MB ZIP disks. It’s fast, too. I wish I’d thought of trying that first. I could have reformatted the disks with a Linux filesystem (ext4 or whatever) if I wanted to do that.

I then downloaded from a Debian amd64 repository the binary package for a 1996 Linux GUI utility named ‘jaZip‘ that someone named Jarrod Smith (thank you!) wrote in 1996 for Iomega Jaz and Zip drives, and I installed it easily in the Live Lubuntu 20.10 environment. It works perfectly, allowing me to mount, unmount, lock, unlock and eject Zip 100 MB disks. Linux came to the rescue again. I’m chuffed. Below are details of the steps I took to create a persistent Live USB pendrive with Lubuntu 20.10 with the ability to use my Iomega Z100P2 drive connected to the Dell OptiPlex 780 SFF PC.

By the way, a persistent Live Linux USB pendrive is not essential, it just means you don’t have to manually load the ppa module, re-install jaZip and configure it every time you boot the Live Linux environment.

1. Download the ISO of Lubuntu 20.10 from the official Lubuntu Web site.

2. Use the procedure in the following ‘How To’ article to create a persistent Live pendrive of Lubuntu 20.10 by using the utility mkusb:

Create a persistent Ubuntu USB which boots to RAM

The mkusb windows in that 2016 article are a bit different to those in the version of mkusb (12.3.9) that was installed by following the procedure, but it is fairly obvious what to do. Select the old user interface (Option e: Old User Interface). There is no need to perform the steps in ‘Extra: Boot the Live USB to RAM’ because it is now done automatically for you and added to the GRUB boot menu as an additional option.

3. Once I had created the persistent Live pendrive, I booted it and performed the installation procedure for jaZip, and configured the persistent Live installation. The console output for all these steps is shown below:

lubuntu@lubuntu:~$ sudo apt install libforms2
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following NEW packages will be installed:
  libforms2
0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
Need to get 327 kB of archives.
After this operation, 975 kB of additional disk space will be used.
Get:1 http://archive.ubuntu.com/ubuntu groovy/universe amd64 libforms2 amd64 1.2.3-1.4 [327 kB]
Fetched 327 kB in 0s (807 kB/s)  
Selecting previously unselected package libforms2.
(Reading database ... 240052 files and directories currently installed.)
Preparing to unpack .../libforms2_1.2.3-1.4_amd64.deb ...
Unpacking libforms2 (1.2.3-1.4) ...
Setting up libforms2 (1.2.3-1.4) ...
Processing triggers for libc-bin (2.32-0ubuntu3) ...
lubuntu@lubuntu:~$ cd ~/Downloads
lubuntu@lubuntu:~/Downloads$ wget http://ftp.uk.debian.org/debian/pool/main/j/jazip/jazip_0.34-15.1+b2_amd64.deb
--2021-04-14 15:09:15--  http://ftp.uk.debian.org/debian/pool/main/j/jazip/jazip_0.34-15.1+b2_amd64.deb
Resolving ftp.uk.debian.org (ftp.uk.debian.org)... 2001:1b40:5600:ff80:f8ee::1, 78.129.164.123
Connecting to ftp.uk.debian.org (ftp.uk.debian.org)|2001:1b40:5600:ff80:f8ee::1|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 81280 (79K) [application/octet-stream]
Saving to: ‘jazip_0.34-15.1+b2_amd64.deb’

jazip_0.34-15.1+b2_amd64.de 100%[===========================================>]  79.38K  --.-KB/s    in 0.08s

2021-04-14 15:09:15 (941 KB/s) - ‘jazip_0.34-15.1+b2_amd64.deb’ saved [81280/81280]

lubuntu@lubuntu:~/Downloads$ sudo dpkg -i jazip_0.34-15.1+b2_amd64.deb
Selecting previously unselected package jazip.
(Reading database ... 240059 files and directories currently installed.)
Preparing to unpack jazip_0.34-15.1+b2_amd64.deb ...
Unpacking jazip (0.34-15.1+b2) ...
Setting up jazip (0.34-15.1+b2) ...
Processing triggers for man-db (2.9.3-2) ...
lubuntu@lubuntu:~/Downloads$ sudo adduser lubuntu floppy
Adding user `lubuntu' to group `floppy' ...
Adding user lubuntu to group floppy
Done.
lubuntu@lubuntu:~/Downloads$ sudo modprobe ppa # Load the parallel port driver for the Zip drive.
lubuntu@lubuntu:~/Downloads$ sudo blkid # Check if the Zip drive has now been detected.
/dev/sda1: LABEL="system" BLOCK_SIZE="512" UUID="BCF27E52F27E10BE" TYPE="ntfs" PARTUUID="6da119a3-01"
/dev/sda2: LABEL="windows" BLOCK_SIZE="512" UUID="527280DF7280C8E5" TYPE="ntfs" PARTUUID="6da119a3-02"
/dev/sdb1: LABEL="usbdata" BLOCK_SIZE="512" UUID="347345C33A9B90D1" TYPE="ntfs" PARTUUID="793c91c2-01"
/dev/sdb3: LABEL_FATBOOT="lub201064" LABEL="lub201064" UUID="7EAA-D59C" BLOCK_SIZE="512" TYPE="vfat" PARTUUID="793c91c2-03"
/dev/sdb4: BLOCK_SIZE="2048" UUID="2020-10-22-14-26-38-00" LABEL="Lubuntu 20.10 amd64" TYPE="iso9660" PTUUID="509643ab-f22d-4d70-8a47-8708c562cbfe" PTTYPE="gpt" PARTUUID="793c91c2-04"
/dev/loop0: TYPE="squashfs"
/dev/sdb5: LABEL="casper-rw" UUID="55459d4d-48f3-4b50-bd9b-3fd71e552bb2" BLOCK_SIZE="4096" TYPE="ext4" PARTUUID="793c91c2-05"
/dev/zram0: UUID="073aa55f-241b-4deb-b6a0-907676dfff65" TYPE="swap"
/dev/zram1: UUID="692d4cc6-21fa-48b8-8ef7-948dc13dec53" TYPE="swap"
/dev/sdc4: SEC_TYPE="msdos" LABEL_FATBOOT="ZIP-100" LABEL="ZIP-100" UUID="15F9-2C71" BLOCK_SIZE="512" TYPE="vfat" PARTUUID="726a014e-04"
lubuntu@lubuntu:~/Downloads$ sudo mkdir -p /media/lubuntu/ZIP-100
lubuntu@lubuntu:~/Downloads$ sudo /usr/sbin/jazipconfig
There are currently no entries in /etc/jazip.conf.

Zip devices detected on the system:

  1:  Device /dev/sdc

There are no Jaz devices detected on the system.

Available commands:
 (a)dd an entry listed from detected devices.
 (c)reate an entry from scratch.
 (q)uit without saving.
 (e)xit and save changes.
                           ? a

What mount point? (e.g. /zip) /media/lubuntu/ZIP-100
--------------------------------------------
These are the entries currently selected for /etc/jazip.conf:

  1:   Device /dev/sdc   Mount point /media/lubuntu/ZIP-100

There are no other Zip devices detected on the system.

There are no Jaz devices detected on the system.

Available commands:
 (d)elete an entry from /etc/jazip.conf
 (c)reate an entry from scratch.
 (q)uit without saving.
 (e)xit and save changes.
                           ? e
Creating /etc/jazip.conf
lubuntu@lubuntu:~/Downloads$ cat /etc/jazip.conf
# Configuration file for jaZip
#
# Raw Device         Mount Point                  Read but ignored
  /dev/sdc              /media/lubuntu/ZIP-100                      auto    auto        0 0
lubuntu@lubuntu:~/Downloads$ sudo jazip # Launch jaZip.
ERROR! Couldn't write entry to /etc/mtab.
lubuntu@lubuntu:~/Downloads$ sudo jazip # Launch jaZip.
lubuntu@lubuntu:~/Downloads$ sudo nano /etc/modules # Add ppa so it gets loaded automatically.

 
4. Add a jaZip icon on the Linux Desktop so that you can launch jaZip easily:

4.1 Create the file /home/lubuntu/Desktop/jazip.desktop containing:

[Desktop Entry]
Name=jazip
GenericName=Manage Iomega Jaz and Zip drives
Comment=
Exec=/home/lubuntu/.launch_jazip.sh
Type=Application
Icon=/usr/share/doc/jazip/icons/jazip1.gif
Terminal=false

4.2 Right-click on the icon on the Desktop and tick ‘Trust this executable’.

4.3 Create the file /home/lubuntu/.launch_jazip.sh containing:

#!/bin/bash
lxqt-sudo nohup jazip &

4.4 Make it executable:

lubuntu@lubuntu:~/Downloads$ chmod +x ~/.launch_jazip.sh
jaZip window open on the Lubuntu 20.10 Desktop.

jaZip window open on the Lubuntu 20.10 Desktop.

What a pleasure to find that the ppa module, which has been part of the kernel distribution since sometime in the 1.3.x series, is still available and working in today’s Linux kernels, and that jaZip, a utility program for Linux originally released in 1996 and last updated (as far as I can tell) in the year 2001, still works in today’s Linux to manage hardware that has been obsolete for almost as long.

Using jaZip to mount a Zip disk will mount the disk with ownership root:root. Therefore, if I want to copy files to a Zip disk, instead of using jaZip to mount and unmount the disk I click on the device ‘101 MB Volume’ that appears in the Lists pane of the PCManFM-Qt file manager window after a Zip disk is inserted in the drive. I just use jaZip to eject the Zip disk from the drive after unmounting it by clicking on the Unmount icon in the Lists pane of PCManFM-Qt.

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

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

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

~/.config/menus/applications-merged/

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

~/.local/share/desktop-directories/

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Resulting application menu entry for Windows application Visio Professional 5

Resulting application menu entry for Windows application Visio Professional 5

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

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

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

Original application menu entry for Windows application SumatraPDF installed via WINE

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

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

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

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

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

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

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

‘Wine’ > ‘Programs’ > ‘SumatraPDF’

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

to:

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

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

Modified application menu entry for Windows application SumatraPDF installed via WINE

Modified application menu entry for Windows application SumatraPDF installed via WINE

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

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

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

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

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

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

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

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

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

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

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

Make it executable:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

$ sudo apt install xprintidle

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

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

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

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

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

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

/usr/local/bin/check_if_idle.sh

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

10. Reboot and test

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

$ journalctl | grep "Automatic Suspend Scheme"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Installing the Interactive Deep Colorization application in Linux

There are plenty of articles and videos on the Web regarding colourising old black and white photographs. Some of the resulting colourised photographs look amazing. Several Web sites offer free or commercial automated colourisation of B&W photographs using AI (artificial intelligence) techniques. The free-use sites watermark the result or limit the size of the original image. Some of the resulting colourised images are reasonable, others not so good.

Last year I scanned some 35 mm slides which are over 60 years old. The chemicals in some had degraded so much that the images are tinted red (‘redscale’ rather than ‘greyscale’!), too much to be able to fix using the GIMP. Out of curiosity I tried processing one of the scanned slides using some of the free online B&W photograph automated colourisers. The results in some cases were promising, alhough they would still require a lot of manual adjustment.

Scan of original 35 mm slide about 60 years old

Scan of original 35 mm slide about 60 years old

The image after processing using one of the free online B&W colouriser Websites

The image after processing using one of the free online B&W colouriser Websites

A few years ago Richard Zhang and colleagues at the University of California, Berkeley wrote software that uses similar AI techniques to colourise photographs but allows the user to manually influence the colourisation — see the Smithsonian Magazine article New App Makes It Easier to Colorize Old Photos for further information and a link to the GitHub repository of Jun-Yan Zhu with the team’s open-source software, written in Python. Zhang went on to join Adobe, and the software is now incorporated in Adobe PhotoShop Elements.

All my machines run Linux, and I wanted to try to install the open-source application from Jun-Yan Zhu’s GitHub repository. However, the Python code uses Qt4 but all my Linux installations use Qt5. Jun-Yan Zhu created a GitHub branch for the Python code to be modified for Qt5 but, to date, that branch still contains only Python code using Qt4:

https://github.com/junyanz/interactive-deep-colorization/tree/qt5

However, another GitHub user named Vishwaesh Rajiv cloned the code and ported it to Qt5 for use in a Docker container:

https://github.com/vwrj/interactive-deep-colorization

I decided to have a go at getting the application to work in Lubuntu 20.10 on my family’s desktop machine. Below are the results of my efforts, which unfortunately stalled because the machine only has 4 GB of RAM (the application apparently requires a lot of memory).

The installation instructions in the README.md file in both users’ repositories apply to the version using Qt4. Below is what I had to do to install the Qt5 version of the application from Vishwaesh Rajiv’s GitHub repository. Qt5 and PyQt5 are already installed in Lubuntu 20.10, so these are not included in the steps below (read Jun-Yan Zhu’s GitHub page for details). My family’s desktop machine does not have an NVIDIA GPU (it has an Intel IGP) so I used ‘CPU mode’ (see the README.md file for details).

user $ wget https://github.com/vwrj/interactive-deep-colorization/archive/master.zip
user $ unzip master.zip
user $ cp -r -p interactive-deep-colorization-master ideepcolor
user $ cd ideepcolor
user $ cp docker/ideepcolor_docker.py ideepcolor.py
user $ cp -r docker/ui_PyQt5/* ui/
user $ cp -r docker/data/* data/
user $ nano ideepcolor.py # Change the line 'from ui_PyQt5 import gui_design' to 'from ui import gui_design'
user $ bash ./models/fetch_models.sh
user $ sudo apt update
user $ sudo apt install caffe python3-caffe
user $ sudo apt install python3-opencv python3-sklearn python3-skimage
user $ sudo apt install python3-qdarkstyle
user $ sudo apt install python3-opencv

I used the following command in ~/ideepcolor/ to launch the application:

user $ python3 ideepcolor.py --cpu_mode --backend caffe --image_file test_imgs/parrot.jpg

From the output displayed in the terminal window the application seems to launch correctly:

[...]
Setting ab cluster centers in layer: pred_ab
Setting upsampling layer kernel: pred_313_us
b'test_imgs/parrot.jpg'
scale = 2.000000

but after a minute or two the memory used increases significantly (as seen in htop), no GUI is displayed and the terminal displays ‘Killed‘:

[...]
Setting ab cluster centers in layer: pred_ab
Setting upsampling layer kernel: pred_313_us
b'test_imgs/parrot.jpg'
scale = 2.000000
Killed

I could be wrong, but I assume the reason the application does not continue is because of insufficient RAM. When I get time I will try to install the various packages in Gentoo Linux on my main laptop with 16 GB RAM, to see if I can get it to work. If you are using a Linux installation that has Qt5 installed and your machine has plenty of RAM, you might be interested to try and install the Interactive Deep Colorization software to see if you can get it to work. If you do, please comment below.

Moving from Lubuntu 18.04 to 20.10

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Virus scan

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

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

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

#!/bin/bash

DIR=$HOME/Downloads

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

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

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

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

Other useful packages

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

user $ sudo apt install elementary-icon-theme

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

user $ sudo apt install mlocate
user $ sudo updatedb

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Firewall

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

user $ sudo enable ufw
user $ sudo ufw status verbose

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

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

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

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

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

Samba

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

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

See the following blog posts for details:

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

~/Desktop/Mount_server1_user1_share.desktop

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

~/.mount_server1_user1_share.sh

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

~/Desktop/Unmount_server1_user1_share.desktop

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

~/.umount_server1_user1_share.sh

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

WS-Discovery

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

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

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

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

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

[Install]
WantedBy=multi-user.target

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

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

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

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

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

to:

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

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

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

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

WINE

I installed WINE:

user $ sudo apt install wine

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

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

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

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

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

ownCloud

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

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

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

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

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

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

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

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

I added the following line to /etc/fstab:

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

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

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

~/Desktop/Mount_server1_WebDAV_share.desktop

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

~/Desktop/Unmount_server1_WebDAV_share.desktop

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

Prevent suspending to RAM

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

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

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

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

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

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

Automated backup of users’ home directories

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

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

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

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

[Install]
WantedBy=halt.target shutdown.target

/usr/local/sbin/backup_home_directories.sh

#!/bin/bash

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

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

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

Keyboard layouts

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

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

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

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

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

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

Conclusion

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

The phone name assigned automatically by Android on my new phone prevented Bluetooth pairing and connecting in Linux

I recently installed Lubuntu 20.10 on a desktop machine, but Bluetooth did not work with my new phone (Samsung Galaxy Note 20 Ultra with Android 11). Bluetooth had worked fine in Lubuntu 18.04 on the same desktop machine with my previous Android phone (Samsung Galaxy Note 8 with Android 9).

The first thing I discovered was that, although the Lubuntu 20.10 Installer had installed Bluez, it had not installed a Bluetooth manager, so I installed Blueman:

$ sudo apt install blueman

Then, I re-installed Bluez just to be sure:

$ sudo apt install --reinstall bluez

The Bluetooth device was detected but Lubuntu 20.10 would not pair with my new phone.

The Bluetooth device was definitely unblocked:

$ rfkill --output-all
ID TYPE      DEVICE TYPE-DESC         SOFT      HARD
 1 wlan      phy0   Wireless LAN unblocked unblocked
 2 bluetooth hci0   Bluetooth    unblocked unblocked

Now, the phone name Android 11 had assigned automatically to my new phone was Fitzcarraldo’s Galaxy Note20 Ultra 5G. After trying many things, I began to wonder if the apostrophe in the phone name was causing the problem, so I changed the name in the phone (Settings > About phone > Edit) to Fitzcarraldo Galaxy Note20 Ultra 5G. Blueman/Bluez were then able to pair with, and connect to, the phone. Problem solved, but what a silly cause.

Using WS-Discovery to enable Windows 10 to browse SMB shares in my home network of Linux computers

I have not used Windows 10 for more than two years now (see ‘Bye bye Windows 10, and good riddance‘ regarding my failed attempts to upgrade Windows 10 Version 1607 to 1703 and 1709). Nevertheless I am aware that, since Version 1709, Windows 10 no longer has SMBv1 and Computer Browser service installed by default. Computer Browser service used NetBIOS and SMBv1 to provide what Microsoft named ‘My Network Places‘ or ‘Network Neighborhood’. Thus Microsoft has dropped the concepts of network ‘workgroups’, ‘master browsers’, NetBIOS, NetBIOS broadcasts, WINS and so on. SMB has not been dropped, though; Versions 2 and 3 of the SMB protocol are now used, albeit using a different mechanism for device discovery.

Although they perform different jobs, Microsoft bundled the Computer Browser service software with the SMBv1 software. Microsoft could have provided them separately, but it made some sense to bundle them together in the early days of Windows networking. Thus, as SMBv1 is not installed by default in Windows 10 Version 1709 and later versions, neither is Computer Browser service. To put it another way, if you install SMBv1 in Windows 10 you automatically install Computer Browser service as well. None of that interested me since I stopped using Windows 10 after Version 1607. Since then my home network has comprised a server, desktop and laptops running various Linux distributions with Samba and using broadcast NetBIOS for name resolution. Of course I know that NetBIOS — especially broadcast NetBIOS for name resolution — is ancient networking technology, but it works well for my home networking needs. All my machines can browse each other’s SMB shares and create/copy/move/delete remote files and folders. The File Manager + app on my phone running Android 9 can also browse SMB shares on the Linux machines and create/copy/move/delete remote files and folders.

Two of my blog posts from 2016 and 2017 explain how I set up my home network for file sharing. One of the machines in the network had Windows 10 1607 installed, but that was replaced with Lubuntu in 2018.

SMBv1 is an inherently insecure protocol, so, after I dropped Windows, I reconfigured Samba on my Linux machines to use only SMBv3, which works fine. Subsequently I found that Android 9 on my Samsung Galaxy Note 8 phone apparently does not support SMBv3, only SMBv1 and SMBv2, so I reconfigured Samba on my Linux machines to allow SMBv2 as well as SMBv3. In other words, the Linux machines use SMBv3 with each other but SMBv2 with the phone (see my comments in the Comments section of my 2016 post ‘A correct method of configuring Samba for browsing SMB shares in a home network‘).

Anyway, I happen to have an evaluation copy of Windows 10 Enterprise Version 1709 installed in a VirtualBox VM (virtual machine) on one of my Linux laptops and, purely to satisfy my curiosity, I decided to try to get Windows 10 Version 1709 to browse and access SMB shares on the Linux machines in my home network, and vice versa, without having to dispense with broadcast NetBIOS name resolution for the Linux machines and without having to install SMBv1 (and Computer Browser service) in Windows 10.

When I first booted Windows 10 Enterprise 1709, SMB shares on my Linux machines were not displayed in File Explorer, and Windows 10 could not find them if I entered the UNC (Universal Naming Convention) address ‘\\hostname\foldername‘ or ‘\\IPaddress\foldername‘ (e.g. ‘\\AKHANATEN\anne‘ or ‘\\192.168.1.70\anne‘) in File Explorer’s address bar. My Web searches indicated that many people cannot see SMB shares in File Explorer either but can access a share by entering the UNC address in the File Explorer address bar. Apparently the advice from Microsoft these days is to use ‘Map a Network Drive…’ in File Explorer. Therefore, given that I wanted to be able to browse SMB shares in ‘File Explorer’ > ‘Network’, I clearly had some work to do. My goal for Windows 10 was twofold: to be able to view my remote SMB shares in Windows 10 File Explorer automatically and to be able to access (copy/move/delete/open) my remote SMB shares in Windows 10 File Explorer. Of course I also wanted to be able to browse and access SMB shares on the Windows 10 machine from the Linux machines.

Now, Windows 10 comes with Web Services Dynamic Discovery (WS-Discovery) installed. This enables SMB hosts running WS-Discovery software to be found by clients running WS-Discovery software. I believe Version 20.04 of the KDE Applications package kio-extras will support SMB host discovery using WS-Discovery, but that version is not available in the Stable Branch of Gentoo Linux installed on my main laptop, nor in Lubuntu 18.04 which is installed on my family’s desktop machine. So I thought I would have a look at what is currently available for those two distributions. I was particularly interested to see if I could find an implementation of WS-Discovery for Linux that would run in parallel with broadcast NetBIOS name resolution currently installed on the Linux machines in my home network, as broadcast NetBIOS name resolution works fine with SMBv2 and SMBv3 for Linux and Android devices in a home network (my Samsung Galaxy Note 8 phone can browse the SMB shares on any of the Linux machines in my home network).

Thanks are due to Steffen Christgau for creating a daemon that can be used in Linux installations to enable Windows 10 to discover SMB shares on Linux machines via WS-Discovery: wsdd – A Web Service Discovery host daemon. The README file for wsdd states:

wsdd implements a Web Service Discovery host daemon. This enables (Samba) hosts, like your local NAS device, to be found by Web Service Discovery Clients like Windows.

It also implements the client side of the discovery protocol which allows to search for Windows machines and other devices implementing WSD. This mode of operation is called discovery mode.

wsdd only depends on Python 3 and can be installed in many Linux distributions. If no wsdd package exists for a specific distribution, it can simply be run from the command line or from a Bash script. The following blog post by Ralph Mönchmeyer explains how to use wsdd (although not a complete solution for my specific case): Samba 4, shares, wsdd and Windows 10 – how to list Linux Samba servers in the Win 10 Explorer.

Below I list the steps I took to enable me to browse SMB shares in an evaluation copy of Windows 10 Enterprise Version 1709 running in a VM on one of my Linux laptops. I don’t have access to the latest version of Windows 10 (2004), but hopefully some or most of the following will still be applicable.

Step 1. Disable firewalls temporarily

I disabled the firewall in the Linux machine and in the Windows 10 machine so that the firewalls could be ruled out if there were any problems getting share browsing to work. Once all the steps were completed I re-enabled the firewalls.

Step 2. Specify the workgroup in Windows 10

Select ‘Control Panel’ > ‘System and Security’ > ‘System’ and, under ‘Computer name, domain, and workgroup settings’, if necessary click ‘Change settings’ to rename the workgroup. The default workgroup name was ‘WORKGROUP‘ so I renamed it to ‘HOME‘, my current network’s workgroup.

Step 3. Ensure the correct SMB protocol in Windows 10

SMBv1 (and Computer Browser service) are disabled by default in Windows 10 Version 1709 and later (see ‘SMBv1 is not installed by default in Windows 10 version 1709, Windows Server version 1709 and later versions‘) but I nevertheless made sure that SMBv1 is disabled and that SMBv2 and SMBv3 are installed (see ‘How to detect, enable and disable SMBv1, SMBv2, and SMBv3 in Windows‘). I did the following in PowerShell (Run as administrator):

PS C:\WINDOWS\system32> Disable-WindowsOptionalFeature -Online -FeatureName SMB1Protocol
PS C:\WINDOWS\system32> Set-SmbServerConfiguration -EnableSMB2Protocol $true

Step 4. Disable NetBIOS-over-TCP/IP in Windows 10

Select ‘Settings’ > ‘Network & Internet’ > ‘Ethernet’ > ‘Change adapter options’.

Right-click ‘Ethernet’, click ‘Properties’, select ‘Internet Protocol Version 4 (TCP/IPv4)’ and click ‘Properties’. Click ‘Advanced’. Click on the WINS tab (even though my network does not use WINS), select ‘Disable NetBIOS over TCP/IP’ and click ‘OK’, ‘OK’ and ‘Close’.

Step 5. Configure ‘Function Discovery’ in Windows 10

See the article ‘SMBv1 is not installed by default in Windows 10 version 1709, Windows Server version 1709 and later versions | Microsoft Docs‘, in particular the following:

Explorer Network Browsing

The Computer Browser service relies on the SMBv1 protocol to populate the Windows Explorer Network node (also known as “Network Neighborhood”). This legacy protocol is long deprecated, doesn’t route, and has limited security. Because the service cannot function without SMBv1, it is removed at the same time.

However, if you still have to use the Explorer Network in home and small business workgroup environments to locate Windows-based computers, you can follow these steps on your Windows-based computers that no longer use SMBv1:

  1. Start the “Function Discovery Provider Host” and “Function Discovery Resource Publication” services, and then set them to Automatic (Delayed Start).
  2. When you open Explorer Network, enable network discovery when you are prompted.

All Windows devices within that subnet that have these settings will now appear in Network for browsing. This uses the WS-DISCOVERY protocol. Contact your other vendors and manufacturers if their devices still don’t appear in this browse list after the Windows devices appear. It is possible they have this protocol disabled or that they support only SMBv1.

Press Windows Key+R, enter ‘services.msc‘ (without the quotes) and click ‘OK’.

Change the ‘Startup type’ of ‘Functions Discovery Provider Host’ to ‘Automatic (Delayed Start)’.

Change the ‘Startup type’ of ‘Function Discovery Resource Publication’ to ‘Automatic (Delayed Start)’.

Step 6. Configure the sharing options in Windows 10

Select ‘Settings’ > ‘Network & Internet’ > ‘Sharing options’ and configure the options as follows:

Private (current profile)
  1. Network discovery
    • ‘Turn on network discovery’ is selected.
    • ‘Turn on automatic setup of network connected devices.’ is ticked.
  2. File and printer sharing
    • ‘Turn on file and printer sharing’ is selected.
  3. HomeGroup connections
    • ‘Allow Windows to manage homegroup connections (recommended)’ is selected.
Guest or Public
  1. Network discovery
    • ‘Turn on network discovery’ is selected.
  2. File and printer sharing
    • ‘Turn on file and printer sharing’ is selected.
All Networks
  1. Public folder sharing
    • ‘Turn on sharing so anyone with network access can read and write files in the Public folders’ is selected.
  2. Media streaming
    • Nothing is selected.
  3. File sharing connections
    • ‘Use 128-bit encryption to help protect file sharing connections (recommended)’ is selected.
  4. Password protected sharing
    • ‘Turn off password protected sharing’ is selected.

Step 7. Install WS-Discovery daemon on the Linux machines

Gentoo Linux
In Gentoo I simply installed the package net-misc/wsdd from the guru overlay:

root # eix -I wsdd
[I] net-misc/wsdd [1]
     Available versions:  (~)0.5 (~)0.6 {samba PYTHON_TARGETS="python3_6 python3_7 python3_8"}
     Installed versions:  0.6(00:39:07 07/06/20)(-samba PYTHON_TARGETS="python3_7 -python3_6 -python3_8")
     Homepage:            https://github.com/christgau/wsdd
     Description:         A Web Service Discovery host daemon.

[1] "guru" /var/lib/layman/guru

and, as I use OpenRC in Gentoo, I configured /etc/conf.d/wsdd.conf as follows:

# /etc/conf.d/wsdd

# Override the default user/group under which wsdd runs.
# Must follow the user[:group] notation.
#WSDD_USER="daemon:daemon"

# Specify alternative log file location.
#WSDD_LOG_FILE="/var/log/wsdd.log"

# Disable automatic detection of the workgroup from samba configuration.
#WSDD_WORKGROUP="MYGROUP"
WSDD_WORKGROUP="HOME"

# Additional options for the daemon, e.g. to listen on interface eth0 only.
# Refer to wsdd(1) for details.
#WSDD_OPTS="-i eth0"
WSDD_OPTS="--shortlog --interface enp4s0f1 --interface wlp3s0 --hostname tutankhamun --discovery"

Specifying the interfaces and hostname are optional, but wsdd seemed to work better when I specified them explicitly. You can ascertain the interfaces by using the command ‘ip address‘ or the deprecated command ‘ifconfig‘.

I added the service to the default runlevel so that it is started automatically when I boot the machine, and then I started it:

root # rc-update add wsdd default
root # rc-service wsdd start

Lubuntu 18.04

In Lubuntu 18.04 (which uses systemd) wsdd can be installed either manually or from a package:

a) Manual installation

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

Edit the systemd service file /etc/systemd/system/wsdd.service to add desired options to the ExecStart command and to change the group from ‘nobody‘ to ‘nogroup‘:

...
ExecStart=/usr/bin/wsdd --workgroup HOME --shortlog --interface eno1 --interface wlp2s0 --hostname thutmoseiii --discovery
...
User=nobody
Group=nogroup
...

You can check whether the user and group exist in your installation as follows:

user $ grep ^nobody /etc/passwd
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
user $ grep ^nobody /etc/group
user $ grep ^nogroup /etc/group
nogroup:x:65534:

Actually, I prefer to specify ‘daemon‘ for the user and group in the wsdd.service file (which is also what the Gentoo Linux ebuild uses and what the .deb package uses):

...
ExecStart=/usr/bin/wsdd --workgroup HOME --shortlog --interface eno1 --interface wlp2s0 --hostname thutmoseiii --discovery
...
User=daemon
Group=daemon
...

You can check that this user and group also exist:

user $ grep ^daemon /etc/passwd
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
user $ grep ^daemon /etc/group
daemon:x:1:

(I tried both nobody:nogroup and daemon:daemon, and there was no apparent difference in behaviour.)

Enable the service so that it starts automatically when the machine is booted, and also start it now:

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

b) Installing from a package

Here is a link to a .deb package for wsdd Version 0.6.0:

https://pkg.ltec.ch/public/pool/main/w/wsdd/

The resulting installation differs slightly from the manual procedure; the package creates a configuration file /etc/wsdd.conf and you declare the wsdd options in that file instead:

# command line parameters for wsdd (consult man page)
WSDD_PARAMS=""

The package also installs a systemd service file /lib/systemd/system/wsdd.service containing the following:

[Unit]
Description=Web Services Dynamic Discovery host daemon
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
EnvironmentFile=/etc/wsdd.conf
ExecStart=/usr/bin/wsdd $WSDD_PARAMS
User=daemon
Group=daemon

[Install]
WantedBy=multi-user.target

The package installs the Python 3 executable wsdd in the directory /usr/bin/. It’s a very straightforward package.

Step 8. Configure Samba to make Windows 10 prompt for username and password

When you click on a network share in Windows 10’s File Explorer, Windows 10 uses the Windows 10 username and password to try to access the SMB share on the remote machine (see ‘Samba share does not ask for credentials from Windows Client‘). This will obviously not work unless the usernames/passwords on both machines match. To make Windows 10 prompt the user to enter the remote username and password, edit the file /etc/samba/smb.conf on each Linux machine and comment out the line ‘map to guest = bad user‘ (see the smb.conf files listed in my 2016 article ‘A correct method of configuring Samba for browsing SMB shares in a home network‘).

Step 9. Enable guest access in Windows 10

If I enter a SMB share’s UNC address in File Explorer’s address bar, or if I double-click on the remote machine’s icon in File Explorer (after WS-Discovery has made the SMB share visible in File Explorer), Windows 10 displays the following error message:

Network Error

Windows cannot access \\hostname

Check the spelling of the name. Otherwise, there might be a problem with your network. To try to identify and resolve network problems, click Diagnose.

Error Code: 0x80070035
The network path was not found.

This has nothing to do with the fact that SMBv1 is disabled in Windows 10. It happens because Windows 10 1709 and onwards have guest logins disabled:

To enable guest logins I edited the Windows 10 Registry and changed the following key from zero to one:

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters]

“AllowInsecureGuestAuth”=dword:1

Step 10. Configure the Windows 10 firewall

Select ‘Windows Defender Security Center’ > ‘Firewall & network protection’.

Click ‘Allow an app through the firewall’.

If not already ticked, select ‘Private’ and ‘Public’ for ‘Network Discovery’ and for ‘File and Printer Sharing’.

Step 11. Configure the Linux firewall

This is where things get more complicated. According to the README for wsdd:

Firewall Setup

Both incoming and outgoing multicast traffic on port 3702 must be allowed. For IPv4, the multicast address is 239.255.255.250, for IPv6 the link local SSDP multicast address (ff02::c) is used.

Incoming TCP traffic (and related outgoing traffic) on port 5357 must be allowed.

My laptops and desktop use UFW, and below I explain how I configured UFW to satisfy the above requirements.

Firstly, as my firewall is configured to deny incoming traffic and allow outgoing traffic by default, I enabled UFW and added the following DNS rules to UFW’s main rules (the following two commands add rules for both IPv4 and IPv6):

user $ sudo ufw allow 53/tcp
user $ sudo ufw allow 53/udp

Note that, in order for the multicast rule I use to work, xt_pkttype must either have been built into the kernel or built as a kernel module and have been loaded:

user $ lsmod | grep pkttype
xt_pkttype             16384  2
x_tables               40960  17 ip6table_filter,xt_conntrack,iptable_filter,xt_LOG,xt_multiport,xt_tcpudp,xt_addrtype,ip6t_rt,ip6_tables,ipt_REJECT,xt_CT,xt_pkttype,iptable_raw,ip_tables,xt_limit,xt_hl,ip6t_REJECT

To load the module automatically at boot, in Gentoo Linux I added ‘xt_pkttype‘ to the list of modules in the file /etc/conf.d/modules, and in Lubuntu 18.04 I added ‘xt_pkttype‘ to the list of modules in the file /etc/modules-load.d/modules.conf.

Also note that my firewall had previously already been configured for NetBIOS and SMB by using the following commands:

user $ # Rules for SMB
user $ # IPv4:
user $ sudo ufw allow from 192.168.1.0/24 to any port 137,138 proto udp
user $ sudo ufw allow from 192.168.1.0/24 to any port 139,445 proto tcp
user $ # IPv6:
user $ # (NetBIOS is undefined for IPv6 but I believe SMB uses Port 445 in IPv6)
user $ sudo ufw allow from ff80::/10 to any port 445 proto tcp

IPv4

The end of the file /etc/ufw/before.rules previously looked like this:

...
# allow MULTICAST mDNS for service discovery (be sure the MULTICAST line above
# is uncommented)
-A ufw-before-input -p udp -d 224.0.0.251 --dport 5353 -j ACCEPT

# allow MULTICAST UPnP for service discovery (be sure the MULTICAST line above
# is uncommented)
-A ufw-before-input -p udp -d 239.255.255.250 --dport 1900 -j ACCEPT

# don't delete the 'COMMIT' line or these rules won't be processed
COMMIT
# The following is needed to enable Samba commands to
# work properly for broadcast NetBIOS name resolution
#
# raw table rules
*raw
:OUTPUT ACCEPT [0:0]
-F OUTPUT
-A OUTPUT -p udp -m udp --dport 137 -j CT --helper netbios-ns
COMMIT

I inserted seven lines as shown below:

...
# allow MULTICAST mDNS for service discovery (be sure the MULTICAST line above
# is uncommented)
-A ufw-before-input -p udp -d 224.0.0.251 --dport 5353 -j ACCEPT

# allow MULTICAST UPnP for service discovery (be sure the MULTICAST line above
# is uncommented)
-A ufw-before-input -p udp -d 239.255.255.250 --dport 1900 -j ACCEPT

# allow MULTICAST WS-Discovery for service discovery (be sure the MULTICAST line above
# is uncommented)
-A ufw-before-input -m pkttype --pkt-type multicast -j ACCEPT
-A ufw-before-input -p udp -s 192.168.1.0/24 --dport 3702 -j ACCEPT
-A ufw-before-input -p udp -s 192.168.1.0/24 --sport 3702 -j ACCEPT
-A ufw-before-input -p tcp -s 192.168.1.0/24 --dport 5357 -j ACCEPT
-A ufw-before-input -p tcp -s 192.168.1.0/24 --sport 5357 -j ACCEPT

# don't delete the 'COMMIT' line or these rules won't be processed
COMMIT
# The following is needed to enable Samba commands to
# work properly for broadcast NetBIOS name resolution
#
# raw table rules
*raw
:OUTPUT ACCEPT [0:0]
-F OUTPUT
-A OUTPUT -p udp -m udp --dport 137 -j CT --helper netbios-ns
COMMIT

Actually the two IPv4 rules shown above for mDNS and UPnP that were already in the file /etc/ufw/before.rules have become redundant because the first of the five new rules I added encompasses them. It does no harm to leave those two rules in the file, though.

IPv6

The end of the file /etc/ufw/before6.rules previously looked like this:

...
# allow MULTICAST mDNS for service discovery
-A ufw6-before-input -p udp -d ff02::fb --dport 5353 -j ACCEPT

# allow MULTICAST UPnP for service discovery
-A ufw6-before-input -p udp -d ff02::f --dport 1900 -j ACCEPT

# don't delete the 'COMMIT' line or these rules won't be processed
COMMIT

I inserted six lines as shown below:

...
# allow MULTICAST mDNS for service discovery
-A ufw6-before-input -p udp -d ff02::fb --dport 5353 -j ACCEPT

# allow MULTICAST UPnP for service discovery
-A ufw6-before-input -p udp -d ff02::f --dport 1900 -j ACCEPT

# allow MULTICAST WS-Discovery for service discovery
-A ufw6-before-input -m pkttype --pkt-type multicast -j ACCEPT
-A ufw6-before-input -p udp -s fe80::/10 --dport 3702 -j ACCEPT
-A ufw6-before-input -p udp -s fe80::/10 --sport 3702 -j ACCEPT
-A ufw6-before-input -p tcp -s fe80::/10 --dport 5357 -j ACCEPT
-A ufw6-before-input -p tcp -s fe80::/10 --sport 5357 -j ACCEPT

# don't delete the 'COMMIT' line or these rules won't be processed
COMMIT

Actually the two IPv6 rules shown above for mDNS and UPnP that were already in the file /etc/ufw/before6.rules have become redundant because the first of the five new rules I added encompasses them. It does no harm to leave those two rules in the file, though.

Because the Linux machines in my network still use broadcast NetBIOS for name resolution I left all the NetBIOS rules in UFW as they were, including the extra lines I previously added to /etc/ufw/before.rules (see the raw table rule at the end of /etc/ufw/before.rules listed above and my blog post ‘Prevent Linux firewalls interfering with Samba commands in a home network that uses broadcast NetBIOS name resolution‘).

Actually, as my laptops change firewall zones automatically (see my post ‘Firewall zones (profiles) in Linux, and how to switch them automatically if you use UFW‘), on my laptops I added the new rules to the zone for my home network specified in my NetworkManager Dispatcher hook script /etc/NetworkManager/dispatcher.d/20_ufw-zones.

After reloading UFW, the UFW status on my machines now looks like this (I’ve excluded rules unrelated to this topic):

user $ sudo ufw status verbose
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip

To                         Action      From
--                         ------      ----
137,138/udp                ALLOW IN    192.168.1.0/24
139,445/tcp                ALLOW IN    192.168.1.0/24
53/tcp                     ALLOW IN    Anywhere
53/udp                     ALLOW IN    Anywhere
445/tcp                    ALLOW IN    ff80::/10
53/tcp (v6)                ALLOW IN    Anywhere (v6)
53/udp (v6)                ALLOW IN    Anywhere (v6)

Note that UFW does not display rules declared in /etc/ufw/{before,before6}.rules

Step 12. Re-enable the Windows 10 firewall

Select ‘Settings’ > ‘Network & Internet’ > ‘Windows Firewall’.

Step 13. Check that wsdd is working as expected

To check that wsdd is actually detecting other machines running WS-Discovery, you can stop the daemon running and instead launch wsdd manually in a terminal window with verbose logging enabled.

For example, on my laptop running Gentoo Linux I did the following:

user $ sudo rc-service wsdd stop
user $ wsdd --workgroup HOME --verbose --interface enp4s0f1 --interface wlp3s0 --hostname tutankhamun --discovery

And on my family’s desktop running Lubuntu 18.04 I did the following:

user $ sudo systemctl stop wsdd
user $ wsdd --workgroup HOME --verbose --interface eno1 --interface wlp2s0 --hostname thutmoseiii --discovery

Check the output in the terminal window includes a discovered line for each machine running Windows 10 and for each Linux machine running wsdd. For example:

...
2020-06-16 00:31:09,331:wsdd INFO(pid 17574): discovered MSWIN10PC in Workgroup:HOME on 192.168.1.111%eno1
...
2020-06-16 00:31:10,013:wsdd INFO(pid 17574): discovered MSWIN10PC in Workgroup:HOME on [fe80::fc7e:7068:8c2c:e664]%eno1
...

After pressing Ctrl+C to stop wsdd running in the terminal, you can restart the daemon:

Gentoo Linux

user $ sudo rc-service wsdd start

Lubuntu 18.04

user $ sudo systemctl start wsdd

With wsdd running on the Linux machines they become visible in File Explorer on Windows 10 machines connected to the network. However, the converse is not necessarily true, as explained further on.

As I had previously configured Samba on my Linux machines to use broadcast NetBIOS to resolve names, Samba on the Linux machines fails to resolve the hostnames of the Windows 10 machines because Windows 10 no longer supports NetBIOS name resolution (neither broadcast nor WINS). I confirmed this by using the smbclient command in a terminal window:

user $ sudo smbclient //MSEDGEWIN10/TestSMBShare1 --debuglevel=10
...
added interface eno1 ip=192.168.1.111 bcast=192.168.1.255 netmask=255.255.255.0
Netbios name list:-
my_netbios_names[0]="THUTMOSEIII"
Client started (version 4.7.6-Ubuntu).
Opening cache file at /var/cache/samba/gencache.tdb
Opening cache file at /var/run/samba/gencache_notrans.tdb
sitename_fetch: No stored sitename for realm ''
internal_resolve_name: looking up MSEDGEWIN10#20 (sitename (null))
no entry for MSEDGEWIN10#20 found.
name_resolve_bcast: Attempting broadcast lookup for name MSEDGEWIN10
Connection to MSEDGEWIN10 failed (Error NT_STATUS_UNSUCCESSFUL)

However, in Gentoo Linux (Stable Branch, KDE Plasma 5.18.5, KDE Applications 19.12.3) on my main laptop I can enter ‘smb://hostname/sharename‘ (e.g. smb://msedgewin10/Users/Public) in the Dolphin file manager’s address bar and browse the contents of the SMB share on the Window 10 machine. I assume this is because Avahi on the Linux machine performs name resolution anyway even though the broadcast NetBIOS lookup has failed. Although Lubuntu 18.04 also has the Avahi daemon running, it does not resolve the hostname when I enter ‘smb://hostname/sharename‘ in PCManFM’s address bar; I have to enter ‘smb://IPaddress/sharename‘ (e.g. smb://192.168.1.64/Users/Public) to be able to browse the contents of the Windows 10 shared folder.

Conclusion

wsdd running on Linux machines enables Windows 10 to view networked Linux machines in File Explorer and browse SMBv2 and SMBv3 shares residing on Linux machines. It does not guarantee I will be able to view Windows 10 machines in Linux file managers automatically, though. But I can access Windows 10 machines by entering ‘smb://IPaddress/sharename‘ in the Linux file manager’s address bar, or, depending on what has been installed in the Linux installation and how it has been configured, by entering ‘smb://hostname/sharename‘.

To access a Linux SMB shared folder (as declared in that machine’s smb.conf file) in Windows 10 File Explorer, either I double-click on the Linux machine’s icon in the Network view or I enter the UNC address (e.g. \\tutankhamun\Users\Public) in the address bar. I can then access the files and sub-folders.

To browse a Windows 10 SMB shared folder and files in KDE Dolphin in Gentoo Linux current Stable Branch on my main laptop, I enter the UNC address (e.g. smb://msedgewin10/Users/Public) or click on the location I previously bookmarked under ‘Places’ in the left pane of the Dolphin window. I can then access the files and sub-folders. To browse a Windows 10 SMB shared folder and files in LXDE PCManFM in Lubuntu 18.04, I enter the UNC address with an IP address instead of a hostname (e.g. smb://192.168.1.64/Users/Public). I can then access the files and sub-folders. I am going to have to do some more digging to try to find out why KDE Dolphin in Gentoo Linux on my main laptop (kio-extras installed from Gentoo ebuild kio-extras-19.12.3-r2) can access Windows 10 by hostname but PCManFM in Lubuntu 18.04 cannot.

To enable machines running Window 10 to browse SMB shares on my other Linux machines I would need to perform the same Linux-related steps in each of those installations. My server firewall uses IPTABLES directly, rather than UFW, so the syntax of the additional firewall rules would be different.

Addendum, 16 June 2020: I suspected the problem browsing the Windows 10 SMB shares from Lubuntu 18.04 is due to PCManFM, so I installed a different file manager: SpaceFM (Version 1.0.5 for GTK2) and its associated utility udevil (Version 0.4.4). SpaceFM allows me to enter UNC addresses such as ‘smb://mswin10pc/Users/Public‘ without any problems. So, problem solved in Lubuntu 18.04 now as well.