Writing Systemd Service Units

I usually use a Require in parallel with After:

After=network.target
Require=network.target

From the systemd unit docs:

It is a common pattern to include a unit name in both the After= and Requires= options, in which case the unit listed will be started before the unit that is configured with these options.
2 Likes

A user yesterday was having suspend issues with Nvidia and KDE, so I advised to disable compositing temporarily. That fixed the problem, but the user wanted to have the process automated of stopping/restarting Kwin, (so I wrote a service to do that).

This service automatically restarts KDE's Kwin compositor after suspend. For those KDE users who experience video problems related to the compositor when they resume, this should correct it.

Kwin Compositor Restart Service

With a root capable text editor create:

/etc/systemd/system/kwin-restart@.service

With the following contents:

#/etc/systemd/system/kwin-restart@.service
#sudo systemctl enable kwin-restart@$USER.service
#sudo systemctl start kwin-restart@$USER.service
#sudo systemctl stop kwin-restart@$USER.service
#sudo systemctl disable kwin-restart@$USER.service
#systemctl status kwin-restart@$USER.service
#sudo systemctl daemon-reload
 
[Unit]
Description=Kwin Suspend/Resume Service 
Before=sleep.target
StopWhenUnneeded=yes

[Service]
User=%i
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/bash -lc "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/%U/bus DESKTOP_SESSION=/usr/share/xsessions/plasma sudo -iu %i DISPLAY=:0 qdbus org.kde.KWin /Compositor suspend"
ExecStop=/bin/bash -lc "sleep 10; DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/%U/bus DESKTOP_SESSION=/usr/share/xsessions/plasma sudo -iu %i DISPLAY=:0 qdbus org.kde.KWin /Compositor resume"

[Install]
WantedBy=sleep.target

Save the service file with root permission, and exit the text editor.

Then, enable the service:

sudo systemctl enable kwin-restart@$USER.service

Then restart.

I added a 10 second sleep after resuming before kwin is restarted. A short sleep unit before executing the kwin restart ensures that everything else has had a chance to fully come out of suspend. Without a sleep unit sometimes the service will choke on resume. You can reduce or eliminate the sleep time if your system responds well without it.

(edit) It has just come to my attention from a user that tested this script that it may need to be modified if you are not using bash as your default shell. If the service does not executute properly you may want to try replacing some of the variables in the "Exec Start=" lines of the service.

Try replacing "%U" with your Users ID (UID) number (usually 1000). Also try replacing "%i" with your username. Hopefully these changes will result in the service running successfully for you if it is failing.

(edit)

Those using shells other than bash seem to have problems with this service even when the variables "%U" and "%i" are replaced.

If you are still having issues, perhaps try this variation:

ExecStart=/usr/bin/env bash -lc  "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/%U/bus DESKTOP_SESSION=/usr/share/xsessions/plasma sudo -iu %i DISPLAY=:0 qdbus org.kde.KWin /Compositor suspend"
ExecStop=/usr/bin/env bash -lc "sleep 10; DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/%U/bus DESKTOP_SESSION=/usr/share/xsessions/plasma sudo -iu %i DISPLAY=:0 qdbus org.kde.KWin /Compositor resume"

Again, you can try swapping the "%U" and "%i" variables with your valid User ID (UID) and username.

Any users who find this variation is working, please report on this thread if successful.



Edit:



This is a modified version of my original kwin restart service you could also test.


Kwin Compositor Restart Service


Create the service file with a text editor:

/etc/systemd/system/kwin-restart@.service 

With the following contents:

#cat etc/systemd/system/kwin-restart@.service 
#systemctl enable kwin-restart@$USER.service 
#systemctl start kwin-restart@$USER.service 
#systemctl stop kwin-restart@$USER.service 
#systemctl disable kwin-restart@$USER.service #systemctl status kwin-restart@$USER.service 
#systemctl daemon-reload

[Unit]
Description=Stop/Start Compositor Before/After Suspension
Before=sleep.target

[Service]
User=%I
Type=oneshot
Environment=DISPLAY=:0
ExecStart=/bin/bash -lc "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(/usr/bin/id -u %I)/bus /usr/bin/qdbus org.kde.KWin /Compositor suspend"
ExecStop=/bin/bash -lc "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u %I)/bus /usr/bin/qdbus org.kde.KWin /Compositor resume"

[Install]
WantedBy=sleep.target

Enable the service with this command:

systemctl enable kwin-restart@$USER.service 


4 Likes

Create a centralized media library with a service:

I keep very little data on my small SSD that I use strictly for the OS. I have 6 other drives in this computer not including my mounted network shares. I tend to keep stuff split up between different drives and I've often thought it would be nice to be able to have my movies, music, pictures from different sources symlinked to a central library. This is normally not doable using symlinks as a single symlink cannot link to multiple sources.

I found a simple python script today that will symlink multiple sources into a single destination directory. It allows all sources to be viewed and accessed together in one single library. The script auto generates all the symlinks very quickly when launched and it also removes old dead links at the same time. I have only used it briefly but it has really impressed me. I have been looking for a way to centralize thousands of movies into one all encompassing movie library. This script seems to accomplish that perfectly.

My python skills are pretty much non-existent. I did not write this script I was merely lucky enough to stumble across it on the internet. This script seemed a natural for a systemd timer to run on a schedule hourly. You could also use a bash alias to run the script and update the library on demand.

These are my notes on how this is best accomplished.

Create the following python script (choose any name you wish):

/home/$USER/.local/bin/movie_library.py

Substitute your username for "$USER" in the above example.

Add the following contents to the script:

#!/usr/bin/env python3
import os
import sys

if len(sys.argv)<3:
    print("target and source not found")
    exit(1)

target = sys.argv[1] 
sources = sys.argv[2:] 

currlinks = os.listdir(target)
compare = []
for dr in sources:
    for f in os.listdir(dr):
        compare.append(f)
        if not f in currlinks:
            # create link
            os.symlink(dr+"/"+f, target+"/"+f)
# clean up possible broken links
for link in currlinks:
    if not link in compare:
        os.remove(target+"/"+link) 

Information regarding customizing and executing the script:

Substitute your username for "$USER" in any examples here.

The script is called via its directory path and then additional arguments for the source directories are passed sequentially.

Script path:

/home/$USER/.local/bin/movie_library.py

"sys.argv[1]" is passed to the script first (which is the destination directory) where you want all source directories symlinked.

/home/$USER/Movies

The following line in the script adds the directories you wish to link together.

sources = sys.argv[2:] 

"target = sys.argv[1]" is the destination path, and it is the first argument. The "sources = sys.argv[2:] line reflects how many sources you wish to symlink into the destination directory. I am symlinking four directories, so the fields will be "2, 3, 4, 5". This is more simply expressed as [2:] which includes all source paths from 2 to the end of the last path specified. Simply add on any path you wish linked into your library directory.

My destination directory for the movie library is:

/home/$USER/Movies

These are my video sources:

/run/media/$USER/6TB_SG18/Movies

/run/media/$USER/6TB_SG18/Recent_Movies

/run/media/$USER/1TB_WD_ext4/Transfers/Movies-Scraped

/run/media/$USER/1TB_WD_ext4/Transfers/Movies-Unscraped

To call the python script via arguments to link four separate locations into one directory:

/python3 /home/$USER/.local/bin/movie_library.py /home/$USER/Movies /run/media/$USER/6TB_SG18/Movies /run/media/$USER/1TB_WD_ext4/Transfers/Movies-Scraped /run/media/$USER/1TB_WD_ext4/Transfers/Movies-Unscraped

The python script can be started as above from the terminal, or from the "ExecStart=" line of a systemd service.

Substitute your username for "$USER" in any of the above examples.

This can be called by a bash alias on demand, or by a systemd timer to update the library contents on a regular schedule.

The script will automatically symlink any new contents added since the last time it was run to the destination directory. It will also automatically delete the symlinks to any files that have been removed since its last execution. The script will display an error on duplicate files but the symlink will still be created and functional.

Create a systemd service and timer to execute the python script at a predetermined interval to update your library contents.

I have chosen to update the Library on an hourly basis. You may alter the time interval if you would prefer this done more or less frequently.

Substitute your username for "$USER" in any of the below examples.

Create the service file:

/home/$USER/.config/systemd/user/library-update.service

With the following contents:

#~/.config/systemd/user/library-update.service
#systemctl --user enable library-update.service
#systemctl --user disable library-update.service
#systemctl --user start library-update.service
#systemctl --user stop library-update.service
#systemctl --user status library-update.service
#systemctl --user daemon-reload

[Unit]
Description=Library service
StopWhenUnneeded=yes

[Service]
Type=oneshot
RemainAfterExit=yes 
ExecStart=/python3 /home/$USER/.local/bin/movie_library.py /home/$USER/Movies /run/media/$USER/6TB_SG18/Movies /run/media/$USER/1TB_WD_ext4/Transfers/Movies-Scraped /run/media/$USER/1TB_WD_ext4/Transfers/Movies-Unscraped


[Install]
WantedBy=multi-user.target

Create the timer file:

/home/$USER/.config/systemd/user/library-update.timer

With the following contents:

#~/.config/systemd/user/library-update.timer
#systemctl --user enable library-update.timer
#systemctl --user disable library-update.timer
#systemctl --user start library-update.timer
#systemctl --user stop library-update.timer
#systemctl --user status library-update.timer
#systemctl --user daemon-reload
#systemctl --user list-timers

[Unit]
Description=Runs library update hourly

[Timer]
OnUnitActiveSec=1h
Unit=library-update.service
OnBootSec=180

[Install]
WantedBy=timers.target

Once you have finished creating the service and timer, enable them both:

systemctl --user enable library-update.timer && systemctl --user enable library-update.service

Then restart.

(edit)

This is a modified version of the above service that also includes a combined library for my network shares mounted in /media.

I have written this service so that it should not fail if the network shares are offline when the service starts.

Service to include mounted network shares:

#~/.config/systemd/user/library-update.service
#systemctl --user enable library-update.service
#systemctl --user disable library-update.service
#systemctl --user start library-update.service
#systemctl --user stop library-update.service
#systemctl --user status library-update.service
#systemctl --user daemon-reload

[Unit]
Description=Library service
StopWhenUnneeded=yes
After=network.target

[Service]
Type=oneshot
RemainAfterExit=yes 
ExecStartPre=sleep 20
ExecStart=/home/htpc/.local/bin/movie_library.py /home/htpc/Videos/Movies-Local /run/media/htpc/6TB_SG18/Movies /run/media/htpc/1TB_WD_ext4/Transfers/Movies-Scraped /run/media/htpc/1TB_WD_ext4/Transfers/Movies-Unscraped /run/media/htpc/1TB_WD_ext4/Transfers/Movies-New /run/media/htpc/1TB_WD_ext4/Transfers/Movies-Newer /run/media/htpc/1TB_WD_ext4/Transfers/Movies-Newest
ExecStartPost=-/home/htpc/.local/bin/movie_library.py /home/htpc/Videos/Movies-Remote /media/nfs/3tb/Movies_A-M /media/nfs/4tb/Movies /media/nfs/4tb/Movies-New /media/nfs/4tb/Movies-Newer /media/nfs/4tb/Movies-Newest

[Install]
WantedBy=multi-user.target

Be sure to substitute in your username in any path statements that require modification.

4 Likes

Missing / between $USER and .local :wink:

Thank you @sgs. That must have happened when I edited out my username. Awesome catch.

Have you tested out the script yet it works amazingly well considering I added about 3000 directories to symlink. It created all the symlinks in no time flat.

1 Like

No, sorry :wink: I am on my Tablet and admire your work. :slight_smile:
Typos always occur and force beginners to think. :smiley:

1 Like

no, we can get parameters 2 to "end"

if len(sys.argv)<3:
    print("target and source not found")
    exit(1)

target = sys.argv[1] 
sources = sys.argv[2:]
  • add test if source directory exists (mounted)
  • make a recursive find in source directory
  • why ExecStart=/bin/bash -c "/home/htpc/.local/bin/movie_library.py and not ExecStart=/home/htpc/.local/bin/movie_library.py ... ?

1 hour for film ? for me is best 7 days :smile: and for me is most usefull for images ...

3 Likes

Hey I said to adjust the time however you like. :smile:

I like to make sure I don't duplicate stuff when my memories not so good, and I've got files scattered in a bunch of different places.

How the heck did you get so damn good at all this stuff @papajoke. Maybe one day I could dream to be half as good as you. As I mentioned I know very little about python, so all your tips are greatly appreciated.

I will adjust it as you suggested from 2 to end. That definitely makes it simpler for people testing the script.

Why? because as I said I know next to nothing about python, so I'm kind of fumbling around in the dark. If I get things to work at all I'm thrilled. I don't know best practices. I'm just getting some practice. :smile:

I used "bash -c" because I thought that was the best method for multiple arguments containing white spaces. I will modify it to this if you think this is preferable:

python3 /home/htpc/.local/bin/movie_library.py /home/htpc/Movies /run/media/htpc/6TB_SG18/Movies /run/media/htpc/1TB_WD_ext4/Transfers/Movies-Scraped /run/media/htpc/1TB_WD_ext4/Transfers/Movies-Unscraped

Thanks again for all the advice.

3 Likes

Auto Restart Network Service


Firstly, let me state it is always preferable to fix your connectivity issues by changing driver options, firmware, kernel version etc etc. Unfortunately some adapters still have disconnection issues regardless of the fixes you apply. It is a massive inconvenience if your adapter will only reconnect to the internet by rebooting. This service will hopefully alleviate that inconvenience.

If your internet connection drops on an intermittent basis this service may be of great help. The service will ping Google's servers (change if you wish) to determine if your connection is working. If your connection is down, then the script will automatically initiate a restart of all network components. This will hopefully revive your connection without the need to restart your computer. Sometimes a recurring ping may also help to keep a flakey connection alive.

This service should automatically restart your network connection shortly after it drops. From the time when it detects a loss of connectivity the script can be as quick as 6 seconds in re-initiating a connection. The script performs a full restart of all network components to avoid requiring a reboot. This is much quicker than the time it takes to reboot. It also does not require closing your desktop session, so it is far more convenient.

To get this functionality you must create one service file and one script.


Network Restart Service:


#/etc/systemd/system/network-restart.service
#sudo systemctl enable network-restart.service
#sudo systemctl start network-restart.service
#sudo systemctl stop network-restart.service
#sudo systemctl disable network-restart.service
#systemctl status network-restart.service
#sudo systemctl daemon-reload

[Unit]
Description=Network Restart Service 
WantedBy=network-pre.target 

[Service]
User=root
Type=simple
Restart=always
RestartSec=3
Restart=on-failure
StartLimitIntervalSec=0
ExecStartPre=-sleep 15
ExecStart=/usr/local/sbin/network_restart.sh

[Install]
WantedBy=network.target

Once you have created and saved the service file, enable the service:

sudo systemctl enable network-restart.service

Network Restart Script:


With a text editor create:

/usr/local/sbin/network_restart.sh

network_restart.sh script contents:

#!/bin/bash
#/usr/local/sbin/network_restart.sh
while true; do
    ping -c 1 8.8.8.8  | grep received
    if [ $? -eq 0 ]; then > /dev/null 2>&1 &  sleep 2
else
 echo "Connection broken, restarting network connection"
    /bin/sh -c 'nmcli networking off'
      systemctl stop NetworkManager
       ip link set wlan0 down
         modprobe -r 8812au
          sleep 1
            modprobe 8812au
            sleep .5
             ip link set wlan0 up
             sleep .5
             systemctl start NetworkManager
             /bin/sh -c 'nmcli networking on'
            /bin/sh -c 'nmcli r wifi off'
           sleep .5
         /bin/sh -c 'nmcli r wifi on'
       sleep 15
     break  
  fi
done
$(basename $0) && exit

Make the script executable:

sudo chmod +x /usr/local/sbin/network_restart.sh

The group of numbers "8.8.8.8" is Googles server address. If you would prefer to ping another server such as Cloudflare then change the address to "1.1.1.1" instead.

There are lines in the script that must be modified to reflect your own networking components. You can find the required information by running the command "inxi -n". The lines in the script (as below) you must replace "wlan0" with your adapters designation returned from the "inxi -n" command. Your wireless adapter may have a designation such as "wlp3s0" or something similar.

ip link set wlan0 down
ip link set wlan0 up

You must also insert your driver module in place of "8812au" in both scripts for this service to work with your network adapter. Modify these lines (as below) in the script to reflect the driver module your adapter uses.

modprobe -r 8812au
modprobe 8812au

After creating both the files reboot the computer.

After rebooting you can use the following command to test how well the connection is reestablished after it is broken:

nmcli networking off

I hope this helps some people with flakey adapters that drop the connection regularly. While this is really only a workaround hopefully it will make your adapter usable until a permanent fix is found.

(edit)

I initially wrote this service requiring two scripts. I have since pared it down to only using one script (which is less complicated and uses less resources).

Please give me feedback on this service if you test it out. I have found it to work extremely well at automatically restarting a down connection in very little time.


6 Likes

This service was requested for a mouse that would not work after resuming from suspend. This service is written specifically for a Logitech mouse controlled by the Logitech Unifying Receiver. This service should work with any other brand of mouse by substituting the specific device ID of your mouse in the scripts below.


Mouse Restart Service:


Create the service required to stop/start your mouse at suspend/resume.

With a root capable text editor create the following file:

/etc/systemd/system/mouse-restart.service

With the the following contents:

#/etc/systemd/system/mouse-restart.service
#sudo systemctl enable mouse-restart.service
#sudo systemctl start mouse-restart.service
#sudo systemctl stop mouse-restart.service
#sudo systemctl disable mouse-restart.service
#systemctl status mouse-restart.service
#sudo systemctl daemon-reload

[Unit]
Description=Stop/start mouse at suspend/resume
Before=sleep.target
StopWhenUnneeded=yes

[Service]
User=root
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/sbin/mouse_stop.sh
ExecStop=/usr/local/sbin/mouse_start.sh

[Install]
WantedBy=sleep.target

To stop/start your mouse at suspend/resume you must first know your devices ID.

To find your device ID issue the command:

lsusb

The lsusb command should output something similar to this below:

lsusb
Bus 004 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 010 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 009 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 008 Device 002: ID 046d:c52b Logitech, Inc. Unifying Receiver

My mouse is controlled through the "Logitech, Inc. Unifying Receiver".

The Logitech Unifying Receiver has 046d:c52b as its ID.

The scripts below can be used with various USB devices by simply altering the vendor & product ID contained in the scripts.

The device ID is inserted in the scripts in the following fields:

VENDOR="046d"
PRODUCT="c52b"

You must substitute your device ID in these fields if you are not using a Logitech Unifying Receiver with your mouse.


Create the following script to turn your mouse off at suspend:

/usr/local/sbin/mouse_stop.sh

With the the following content:

#!/bin/bash
#/usr/local/sbin/mouse_stop.sh
#Stop Logitech USB Receiver
set -euo pipefail
IFS=$'\n\t'

VENDOR="046d"
PRODUCT="c52b"

for DIR in $(find /sys/bus/usb/devices/ -maxdepth 1 -type l); do
  if [[ -f $DIR/idVendor && -f $DIR/idProduct &&
        $(cat $DIR/idVendor) == $VENDOR && $(cat $DIR/idProduct) == $PRODUCT ]]; then
    echo 0 > $DIR/authorized
  fi
done


Create the following script to turn the mouse back on at resume:

/usr/local/sbin/mouse_start.sh

With the the following content:

#!/bin/bash
#/usr/local/sbin/mouse_start.sh
#Start Logitech USB Receiver
set -euo pipefail
IFS=$'\n\t'

VENDOR="046d"
PRODUCT="c52b"

for DIR in $(find /sys/bus/usb/devices/ -maxdepth 1 -type l); do
  if [[ -f $DIR/idVendor && -f $DIR/idProduct &&
        $(cat $DIR/idVendor) == $VENDOR && $(cat $DIR/idProduct) == $PRODUCT ]]; then
    echo 1 > $DIR/authorized
  fi
done

Make both scripts executable and enable the service:

sudo chmod +x /usr/local/sbin/mouse_stop.sh
sudo chmod +x /usr/local/sbin/mouse_start.sh
sudo systemctl enable mouse-restart.service

Restart the computer.

Then test the service's functionality.


4 Likes

Some touchpads, typically ELAN ones, aren't perfectly supported yet even in the latest kernel (5.2.8 as I write this). They are much better than before, but not quite there yet. Specifically for ELAN1200 which is used in GL503 series and probably other ASUS laptops from the same generation, this touchpad will lose its mind when resuming from suspend, hence the workaround is to remove its corresponding module (i2c_hid) upon suspend then probe it again on resume. These two tasks can be easily accomplished by the following 2 units (edited from https://wiki.archlinux.org/index.php/Power_management#Suspend/resume_service_files):

$ cat /etc/systemd/system/root-suspend.service 
[Unit]
Description=Local system suspend actions
Before=sleep.target

[Service]
Type=simple
ExecStart=-/usr/bin/modprobe -r -v i2c_hid

[Install]
WantedBy=sleep.target
$ cat /etc/systemd/system/root-resume.service 
[Unit]
Description=Local system resume actions
After=suspend.target

[Service]
Type=simple
ExecStart=-/usr/bin/modprobe -v i2c_hid

[Install]
WantedBy=suspend.target

but that is not enough. The default touchpad configuration is not satisfying to me, I want to enable tapping, don't like the middle mouse emulation in the center of the two click areas (I often ended up triggering middle click whenever I want left or right click) and also want natural scrolling. Due to module reloading above, upon resume the touchpad configuration is restored to default, so I need to do the reconfiguration automatically. After knowing the properties to set via xinput list-props, I made the following script:

$ cat /path/to/reapply-touchpad-config.sh 
#!/bin/bash

sleep 1 # increase if modprobe takes more than 1 second on your machine
declare -x DISPLAY=":0.0"
declare -x XAUTHORITY="/home/myusername/.Xauthority"

touchpadid="ELAN1200:00 04F3:3090 Touchpad"
declare -A configmap=(
  [Device Enabled]="1"
  [libinput Tapping Drag Enabled]="1"
  [libinput Natural Scrolling Enabled]="1"
  [libinput Drag Lock Buttons]=""
  [libinput Tapping Drag Lock Enabled]="0"
  [libinput Accel Speed]="1.000000"
  [libinput Horizontal Scroll Enabled]="1"
  [libinput Disable While Typing Enabled]="1"
  [libinput Left Handed Enabled]="0"
  [libinput Middle Emulation Enabled]="1"
  [libinput Tapping Enabled]="1"
)

for key in "${!configmap[@]}"
do
  xinput set-prop "$touchpadid" "$key" "${configmap[$key]}"
done

and another systemd unit:

$ cat /etc/systemd/system/reapply-touchpad-config.service 
[Unit]
Description=Reapply touchpad configuration upon resume
After=suspend.target

[Service]
Type=simple
User=myusername
ExecStart=/path/to/reapply-touchpad-config.sh

[Install]
WantedBy=suspend.target

roughly similar to root-resume.service above, but executed under my username. Now resume will bring my touchpad back the way I want it.

EDIT:
As of kernel 5.4.30 / 5.5.15, these services are no longer needed and even interfere with the now better built-in driver. Please disable or remove if you experience bad behaving touchpad. With the said kernels, the touchpad now survives sleep/resume cycle.

5 Likes

As always, great stuff @tbg. :grin:

Since I have a functional suspend now I noticed my USB Wifi doesn't wake up when resuming.

Device-2: Ralink RT5372 Wireless Adapter type: USB driver: rt2800usb bus ID: 3-2:6 
chip ID: 148f:5372 IF: wlp9s0f3u2 state: up mac: <filter>

Would I be safe assuming that this script will work for waking it up after suspend?

Ralink rt2800usb WiFi restart service:

Create the service file:

/etc/systemd/system/network-restart.service

With this content:

#/etc/systemd/system/network-restart.service
#sudo systemctl enable network-restart.service
#sudo systemctl start network-restart.service
#sudo systemctl stop network-restart.service
#sudo systemctl disable network-restart.service
#systemctl status network-restart.service
#sudo systemctl daemon-reload

[Unit]
Description=Network Suspend/Resume Service 
Before=sleep.target
StopWhenUnneeded=yes

[Service]
User=root
Type=oneshot
RemainAfterExit=yes
ExecStartPre=-/usr/bin/sudo -u $USER /bin/bash -lc 'nmcli networking off'
ExecStart=/usr/bin/sleep 1
ExecStart=-/usr/bin/systemctl stop NetworkManager
ExecStart=/usr/bin/sleep 1
ExecStart=-/usr/bin/ip link set wlp9s0f3u2 down
ExecStart=/usr/bin/sleep 1
ExecStart=-/usr/bin/modprobe -r rt2800usb
ExecStop=/usr/bin/sleep 3
ExecStop=-/usr/bin/modprobe rt2800usb
ExecStop=/usr/bin/sleep 2
ExecStop=-/usr/bin/ip link set wlp9s0f3u2 up
ExecStop=/usr/bin/sleep 2
ExecStop=-/usr/bin/systemctl start NetworkManager
ExecStop=/usr/bin/sleep 2
ExecStop=-/usr/bin/sudo -u $USER /bin/bash -lc 'nmcli networking on'
ExecStop=/usr/bin/sleep 1
ExecStop=-/usr/bin/sudo -u $USER /bin/bash -lc 'nmcli r wifi off'
ExecStop=/usr/bin/sleep 1
ExecStop=-/usr/bin/sudo -u $USER /bin/bash -lc 'nmcli r wifi on'

[Install]
WantedBy=sleep.target


If your adapter's ID is different than “wlp9s0f3u2” you will need to substitute you own adapter’s ID into the service file. If you are using a different driver module you will also need to substitute it in place of “rt2800usb”.

You can find your adapters driver/module and network device identification with the following command:

inxi -n

The sleep units may be reduced (or eliminated) if you do not like the delay it creates. Be aware though, that doing so may reduce the reliability of the service.

Once you have finished creating, (and saved) the service file, enable the service:

sudo systemctl enable network-restart.service

Then restart.


5 Likes

Worked like a charm.
I have to give it a few seconds after coming back from suspend but that's fine with me.
I usually hit the keyboard and walk off to start coffee.

Thank you very much! :grin:

3 Likes

You're very welcome, glad that did the trick. :smile:

3 Likes

This service should resolve suspend issues with Bluetooth. The service disables Wifi and Bluetooth on suspend, then re-enables both upon resume.


Bluetooth Suspend Service


With a text editor create:

/etc/systemd/system/bt-restart.service

bt restart service file contents:

#/etc/systemd/system/bt-restart.service
#sudo systemctl enable bt-restart.service
#sudo systemctl start bt-restart.service
#sudo systemctl stop bt-restart.service
#sudo systemctl disable bt-restart.service
#systemctl status bt-restart.service
#sudo systemctl daemon-reload

[Unit]
Description=Disable, then restart bt
Before=sleep.target
StopWhenUnneeded=yes

[Service]
User=root
Type=oneshot
RemainAfterExit=yes
ExecStartPre=-/usr/bin/sudo -u $USER /bin/bash -lc 'nmcli networking off'
ExecStart=/usr/bin/sleep 1
ExecStart=/usr/bin/sudo -E  /usr/local/bin/disable_bt.sh
ExecStop=/usr/bin/sudo -E  /usr/local/bin/enable_bt.sh
ExecStop=/usr/bin/sleep 2
ExecStopPost=-/usr/bin/sudo -u $USER /bin/bash -lc 'nmcli networking on'

[Install]
WantedBy=sleep.target


Disable BT Suspend Script


Create the script that is to disable BT before suspending.

With a text editor create:

/usr/local/bin/disable_bt.sh

Suspend script contents:

#!/bin/bash
#Disable BT device
#/usr/local/bin/disable_bt.sh
set -euo pipefail
IFS=$'\n\t'

VENDOR="0489"
PRODUCT="e0a2"

for DIR in $(find /sys/bus/usb/devices/ -maxdepth 1 -type l); do
  if [[ -f $DIR/idVendor && -f $DIR/idProduct &&
        $(cat $DIR/idVendor) == $VENDOR && $(cat $DIR/idProduct) == $PRODUCT ]]; then
    echo 0 > $DIR/authorized
  fi
done

Save the file, and exit the text editor.


Enable BT Resume Script


Create the script that is to be executed after coming out of suspend.

With a text editor create:

/usr/local/bin/enable_bt.sh

Resume script contents:

#!/bin/bash
#Enable BT device
#/usr/local/bin/enable_bt.sh
set -euo pipefail
IFS=$'\n\t'

VENDOR="0489"
PRODUCT="e0a2"

for DIR in $(find /sys/bus/usb/devices/ -maxdepth 1 -type l); do
  if [[ -f $DIR/idVendor && -f $DIR/idProduct &&
        $(cat $DIR/idVendor) == $VENDOR && $(cat $DIR/idProduct) == $PRODUCT ]]; then
    echo 1 > $DIR/authorized
  fi
done

Enable the service.

sudo systemctl enable bt-restart.service

Then, ensure both scripts are executable:

chmod +x /usr/local/bin/disable_bt.sh
chmod +x /usr/local/bin/enable_bt.sh

Then restart.


Anyone wishing to disable a different USB device can easily do so by altering the script, inserting your devices ID.

You can find the device ID of of the USB component you wish disabled with the command "lsusb".

Substitute your devices ID you wish disabled in the fields for "VENDOR" and "PRODUCT" in the script above.

These are the fields you must insert your own device ID:

VENDOR="0489"
PRODUCT="e0a2"

4 Likes

I have suspend problem due to bluetooth either not fast enough or not willing to suspend, hopefully this will solve that problem.

Give it a go. Hope it works for you. Usually I always test my services before posting them, but I do not use bluetooth. I have it totally disabled on my systems.

It should hopefully work as it is a combination of 2 methods that have been well tested for solving suspend issues.

1 Like

Bluetooth Startup Service

Sometimes network drivers can conflict with each other. In some cases the drivers must be loaded in a specific sequence or they will fail to load properly. This Bluetooth startup service in combination with a Bluetooth blacklist file will ensure that Bluetooth is not started until after the other networking components are initialized.

The following is a simple system service to start your Bluetooth after your WiFi at system startup.

The best way to ensure there is no driver conflict with WiFi or Ethernet at startup is to blacklist the btusb module.

Run this command to prevent Bluetooth from initiating early in the startup sequence:

echo 'blacklist btusb' | sudo tee /etc/modprobe.d/blacklist_btusb.conf

This will create /etc/modprobe.d/blacklist_btusb.conf which will prevent the bluetooth module from automatic early startup.


Then create the following service which will restart the Bluetooth components automatically after the other network components:


Restart Bluetooth Service

With a text editor create:

/etc/systemd/system/restart-bt.service

Service file contents:

#/etc/systemd/system/restart-bt.service
#systemctl enable restart-bt.service
#systemctl start restart-bt.service
#systemctl stop restart-bt.service
#systemctl disable restart-bt.service
#systemctl status restart-bt.service
#systemctl daemon-reload

[Unit]
Description=Restart Bluetooth Service
WantedBy=multi-user.target 
After=network.target
Wants=bluetooth.target
StopWhenUnneeded=yes

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStartPre=/usr/bin/sleep 3
ExecStart=/usr/bin/modprobe btusb
ExecStop=/usr/bin/systemctl restart bluetooth

[Install]
WantedBy=multi-user.target 

Save the newly created service file, then enable the service:

systemctl enable restart-bt.service

Then restart.

Bluetooth should now be automatically started after your other network components have been loaded at startup.


4 Likes

Automatically Restart Dropped WiFi Connections


Here is a revised service for those who have issues with their WiFi routinely disconnecting. For those who have tried everything to fix their connection drops without success this service may help.

It is always preferable to fix your connectivity issues by changing driver options, firmware, kernel version, etc, etc. Unfortunately some adapters still have disconnection issues regardless of the fixes you apply. This is a massive annoyance if your adapter disconnects regularly. This service will hopefully alleviate that inconvenience somewhat by automatically restarting your network connection for you.

Some users are unlucky enough to have to reboot every time their WiFi goes down. This service should restart your networking components without requiring a reboot. Even if you don't need to reboot, you will find this service far more convenient than manually restarting your connection.

This service pings Google's servers to determine if your connection is up. If your connection is down, then the service will automatically initiate a restart of all network components. This should revive your connection without any need for manual intervention. Sometimes a recurring ping as used in this service also helps to keep a shaky connection alive.

This service should automatically restart your network connection shortly after it drops. From the time when it detects a loss of connectivity the script can be as quick as 5 seconds in re-initiating a connection.

To get this network auto restart functionality you must create one service file and one associated script.


Network Restart Service:


With a text editor create the systemd service file:

/etc/systemd/system/network-restart.service

With the following contents:

# to terminate running service & script:  sudo systemctl stop network-restart.service && sudo killall -9 network_restart
#/etc/systemd/system/network-restart.service
#sudo systemctl enable network-restart.service
#sudo systemctl start network-restart.service
#sudo systemctl stop network-restart.service
#sudo systemctl disable network-restart.service
#systemctl status network-restart.service
#sudo systemctl daemon-reload

[Unit]
Description=Network Restart Service 
WantedBy=network.target 

[Service]
User=root
Type=simple
Restart=always
RestartSec=3
RemainAfterExit=yes
Restart=on-failure
StartLimitIntervalSec=0
ExecStartPre=-sleep 15
ExecStart=/usr/local/sbin/network_restart.sh

[Install]
WantedBy=network.target

Once you have created and saved the service file, enable the service:

sudo systemctl enable network-restart.service

Network Restart Script:


With a text editor create:

/usr/local/sbin/network_restart.sh

network_restart.sh script contents:

#!/bin/bash
# to terminate running service & script:  sudo systemctl stop network-restart.service && sudo killall -9 network_restart
#/usr/local/sbin/network_restart.sh
while true; do
     ping -c 1 8.8.8.8  | grep received
     if [ $? -eq 0 ]; then sleep 2
else
  echo "Connection broken, restarting network connection"
     /bin/sh -c 'nmcli networking off'
       systemctl stop NetworkManager
        ip link set wlp3s0 down
         modprobe -r rtl8723be
           sleep 1
            modprobe rtl8723be
             sleep .5
             ip link set wlp3s0 up
             sleep .5
            systemctl start NetworkManager
           /bin/sh -c 'nmcli networking on'
          /bin/sh -c 'nmcli r wifi off'
         sleep .5
       /bin/sh -c 'nmcli r wifi on'
      sleep 10
     break
   fi
 done
exec "$ScriptLoc"/usr/local/sbin/network_restart.sh && exit

Make the script executable:

sudo chmod +x /usr/local/sbin/network_restart.sh

The group of numbers "8.8.8.8" is Googles server address. If you would prefer to ping another server such as Cloudflare then change the address to "1.1.1.1" instead.

There are several lines in the script that will need to be modified if you have different networking components. You can find the required information by running the command "inxi -n". You must replace "wlp3s0" with your adapters designation returned from the "inxi -n" command if your identification is different.

ip link set wlp3s0 down
ip link set wlp3s0 up

You must also insert your driver module in place of "rtl8723be" in the script if your adapter uses a different module. Modify these lines (as below) in the script to reflect the driver module your adapter uses:

modprobe -r rtl8723be
modprobe rtl8723be

Drivers such as the Atheros ath10k or Intel iwlwifi drivers use several modules rather than only one. If your adapter uses several drivers the script will need to be modified to list both of the modules it uses.

After creating the service file and the associated script reboot the computer.

After rebooting you can use the following command to test how well the connection is reestablished after it is broken:

nmcli networking off

This service and script are designed to be self-respawning. This makes the service and script available to continuously refresh your internet connection. It also makes it very difficult to interrupt the script temporarily. To terminate the running service and script you must issue this command:

sudo systemctl stop network-restart.service && sudo killall -9 network_restart 

I hope this helps some people with flakey adapters that drop the connection on a regular basis. While this is only a workaround hopefully it will make your adapter more usable until a permanent fix is found.


Note:

If you use suspend or hibernate this service could possibly interfere with proper suspension. However, tests on my system turned up no negative side effects from the service.


Please leave feedback if the script works well for you, (or even if it doesn't. It may simply require a little tweaking to get it to work properly on your system. Hopefully I can assist you to get it working if need be.


9 Likes

Forum kindly sponsored by