Writing Systemd Service Units

Nice work, so let’s break it… :rofl:

I suggest you change the scripts path to

~/.local/bin/

to conform with the Arch file-hierarchy.


Please explain/add a use case scenario, where this unit would be a solution.


I have USB keyboard and mouse on my desktop PC. Have you checked that after system sleep, a USB keyboard or mouse can wake the system up?


Would it be better or proper to use “ExecStop” instead of “ExecStartPost”?


For the security precautions, I don’t know, but if the system/root is compromised, then the USB devices sound like a small defect… :laughing:


Keep them coming!!

1 Like

Hierarchy, smierarchy, now that’s just niggling. :smile:

Any device that does not power down properly when it receives the suspend command can prevent a successful sleep/hibernate/resume cycle.

Many devices that may interfere with the sleep function are on the USB bus. By completely disabling all USB devices before suspending, there should be no way a USB device can interfere with the sleep function.

This will only eliminate issues caused by USB devices. Anything on the PCI bus such as the GPU or internal WiFi adapters will still be able to cause sleep issues. If an internal WiFi adapter is the cause I have many other service files written to address that. Graphics drivers can still cause big issues and that will be my next systemd project.

I do not use those methods, so I never tested for that. It would be my assumption that those options would not be available as the entire bus should be shut down during suspend. If that function was important to you then there are other options available in the scripts. You could simply insert the device ID of the problematic device into the script as in my earlier example in the OP. By doing that, only the device that is causing the problem on the USB bus will be shut down.

You are probably correct, but we are all creatures of habit, and I’ve gotten used to using ExecStartPost and made an oversight.

I enjoy the challenge of solving difficult problems with a systemd unit, so I suspect you will be seeing more very soon. :smile:

(edit)

Your suggestions were very good @AgentS , I was simply tired and not in the mood to edit my tutorial again. I have revised my post to conform with the path and exec suggestions you made. As always your suggestions were very good and I should not have been dismissive of them. I apologize for being ungracious towards your comments. Your suggestions are always welcome. Thank you for your feedback.

2 Likes

I wrote a simplified version of the last service earlier today. This service disables the internal Bluetooth adapter on a laptop for someone that requested that. I thought I’d post it here to show a variety of applications for these services…

Systemd Disable Bluetooth Service File:

With a text editor create:

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

Systemd service file contents:

#/etc/systemd/system/disable-bt.service
#sudo systemctl enable disable-bt.service
#sudo systemctl start disable-bt.service
#systemctl list-unit-files --state=enabled
#sudo systemctl stop disable-bt.service
#sudo systemctl disable disable-bt.service
#systemctl status disable-bt.service
#sudo systemctl daemon-reload

[Unit]
Description=Disable Internal Bluetooth
Before=multi-user.target 
StopWhenUnneeded=yes

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/sbin/disable-bt.sh

[Install]
WantedBy=multi-user.target 

Disable Bluetooth Script

Create the script to disable the desired device at startup.

With a text editor create:

/usr/local/sbin/disable-bt.sh

Disable Bluetooth script contents:

#!/bin/bash
#Disable internal BT
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.

Make the script executable:

sudo chmod +x /usr/local/sbin/disable-bt.sh

Then, enable the service:

sudo systemctl enable disable-bt.service

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"
3 Likes

I posted the following service on another topic already, but just to contribute to this topic, which I find very useful and interesting, I’ll post my nilfs auto clean service. It cleans deleted data older than one month in order to keep the garbage collector from running due to lack of space. This has been keeping my Data partition fairly clean and lean.

[mbb@mbb-laptop ~]$ cat /etc/systemd/system/nilfs2-autoclean_mnt-Data.service 
## nilfs2-autoclean unit file for /mnt/Data

[Unit]
Description=Trigger nilfs-clean on /mnt/Data
Requires=mnt-Data.mount
After=mnt-Data.mount

[Service]
Type=oneshot
ExecStart=/usr/bin/sh -c "nilfs-clean -p 1M $(cat /proc/mounts | grep /mnt/Data | cut -d ' ' -f1)"

[Install]
WantedBy=mnt-Data.mount

This is run upon mounting /mnt/Data as indicated by WantedBy=mnt-Data.mount and Requires=mnt-Data.mount

EDIT: note $(cat /proc/mounts | grep /mnt/Data | cut -d ' ' -f1) can be substituted by /dev/sda2 (in this case). I opted for this format because I intend to turn this into a generic nilfs clean service somewhere in the future.

2 Likes

I really like your service. It is very different than the type of units I usually write. That’s what I was hoping for when I started this thread. I was hoping to see the various ways others use their services they write.

1 Like

How to disable/enable a device using its USB bus path location using a service.

This is an alternate method to disable/enable a device at suspend.

I have found three ways to enable/disable devices on the USB bus. One method is to unbind/bind the USB device via its unique ID.

The two other methods I am aware of use the bus address as the way of identifying the device to disable/enable. One of the methods uses the bus address and the “authorized” value to disable/enable the desired device.

The other method I am going to show in this example also uses the bus address, but with the “ConfigurationValue” parameter to disable/enable the desired device.

There are numerous methods to find the desired devices bus address, here are some of them:

lsusb
lsusb -t
for device in $(ls /sys/bus/usb/devices/*/product); do echo $device;cat $device;done
for d in /sys/bus/usb/devices/[0-9]* ; do if [[ -e $d/product ]] ; then echo -e "`basename $d`\t`cat $d/power/control`\t`cat $d/speed`\t`cat $d/product`" ; fi ; done

Here is another handy code snippit you can paste into the terminal to identify your device and its location on the USB bus. This will also give a listing of how the bus paths relate to different vendor/product ID pairs:

Paste into terminal:

for X in /sys/bus/usb/devices/*; do 
    echo "$X"
    cat "$X/idVendor" 2>/dev/null 
    cat "$X/idProduct" 2>/dev/null
    echo
done

The specific device I am looking to enable/disable in this example is my USB keyboard. Be aware that if the device on the USB bus is external it is possible that the bus location may change (in which case using the device ID method may be preferable).

The above commands returned this information about my device:

/sys/bus/usb/devices/7-2
04ca
002f

The command “lsusb” returned this information:

/sys/bus/usb/devices/7-2/product
USB Multimedia Keyboard

We can be pretty sure now from the above information that “7-2” is the bus address of my keyboard I wish to enable/disable in my service. The service is very simple to write once we know the correct bus address. This service method is much easier to write because it only requires a single service file, and no external scripts.

Warning:

Do not experiment with disabling your keyboard without first installing an onscreen keyboard program.

Substitute the bus address of the device you wish to enable/disable in the following lines in the service file below:

ExecStart=/bin/bash -lc 'echo 0 | sudo tee /sys/bus/usb/devices/7-2/bConfigurationValue'
ExecStop=/usr/bin/sudo -E /bin/bash -lc 'echo 1 | sudo tee /sys/bus/usb/devices/7-2/bConfigurationValue'

Replace “7-2” with “1-1.6” or whichever usb port your device is located on.

Keyboard Restart Service

With a text editor create:

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

Service file contents:

#/etc/systemd/system/keyboard-restart.service
#systemctl enable keyboard-restart.service
#systemctl start keyboard-restart.service
#systemctl stop keyboard-restart.service
#systemctl disable keyboard-restart.service
#systemctl status keyboard-restart.service
#systemctl list-unit-files --state=enabled
#sudo systemctl daemon-reload

[Unit]
Description=Restart keyboard after resuming
After=suspend.target
StopWhenUnneeded=yes

[Service]
User=root
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/bash -lc 'echo 0 | sudo tee /sys/bus/usb/devices/7-2/bConfigurationValue'
ExecStop=/usr/bin/sudo -E /bin/bash -lc 'echo 1 | sudo tee /sys/bus/usb/devices/7-2/bConfigurationValue'

[Install]
WantedBy=sleep.target

Enable the keyboard-restart.service:

sudo systemctl enable keyboard-restart.service

Reboot.

BTW, i received my first confirmation that my multi USB device shutdown/restart service solved a tricky suspend problem on a Mac today. I could not find the bus address of the MultiMedia cardreader device in the Mac. That’s what gave me the idea to shutdown the entire USB bus to apply a fix and luckily it worked.

I think these services are going to fix a lot of suspend problems in Manjaro (hopefully). The thread relating to the suspend issue on the Mac is here.

4 Likes

Lol!
In Russian we have something similar: Shakespeare, Miks(X)peare, but usually (sh) is used for such purpose.

Ty for the unit, bookmarked just in case.

1 Like

I have a question for those more experienced than myself at writing systemd services. How do you go about running a service that needs root privileges to execute, but needs to be loaded into the users environment (not the system environment).

I have tried countless methods and and so far the only ones I’ve been able to get working is using “sudo -E” and “/bin/bash -lc” to allow some commands to run as root (yet be executed in the user environment). This method only works in some cases, and is a rather hackish solution and probably not best practices.

It took me a long time to come up with this method, but there must be a better way to go about this. Loading important drivers or other important system functions into user space from a systemd service is very hard to accomplish after a suspend cycle.

Any tips?

1 Like

Is that permissible at all?

OT
Dress in a (bash) script, run as a cronjob, … the user install it?, or who will change my system?

I don’t think systemd was deigned to do that, but my hacks manged to accomplish that function. Yes a cronjob can be run at boot as root, but I don’t think it can be initiated at a resume event can it.

To test this I open a root terminal and issue commands that I want to execute as the logged in user. Some combinations of commands can be succesfully run in user space without entering a password and some return errors. The commands I find that are succesful I then test via a systemd service. Some will work as a service the vast majority won’t. Countless commands were tested before I found a workaround to this problem.

The service that reloads all USB drivers requires root permissions to execute, but must also run in the user environment to work. It probably shouldn’t work, but yet it does? Perhaps I found a backdoor that was never intended to be used.

1 Like

So, I think, dpkg is your friend?
Full service for manjaro-user :slight_smile:

Update: Are Ubuntu/Mint thoughts breaking through again? :wink:
You know what I mean, pkg. :slight_smile:
Or whatever

I guess I will have to investigate that avenue. Thanks for the suggestion.

My solution to restart the full USB bus is a hackish hybrid between a user and a system service. I found it would not execute when run with root scripts, but I could get it to execute with user owned scripts. I’m sure that’s not recommended. That’s why I made the user scripts immutable.

1 Like

One day. It shall not be today. It may not be tomorrow. It could be next week. It could be months from now. But one day. One day I will respond to you about systemd units.

But before that day I shall proclaim that ye who holds the dpkg shall forever be abominable in the eyes of heaven. Take heed, lest ye be cast down among the wicked.

C’mon I know you like writing service files. Don’t be so coy.

Ha Ha, dpkg. The wisest statement I’ve ever read from you on the forum. No worries I haven’t installed anything like that in many many many years.

1 Like

The basic understanding is

  • whatever command needs root privs, a system unit should be used
  • for commands a user can run, a user unit should be able to do
  • logind is a factor in the middle of user and system session, that needs RTFM to find a setting that will help for the intended scope
  • for PM actions, there are suspend.@user and suspend.@root (or similar names) that need to be enabled and manipulated to our relevant scopes. RTFM
2 Likes

That is helpful. Thank you for the feedback. Scouring the internet turns up very little on this topic. Systemd documentation while in depth is very obtuse and lacking in usage examples in a lot of cases.

I hope the direction you’ve pointed me turns up some solid leeds on what I want to accomplish. To be perfectly honest I haven’t found any kind of solid leads except there is info on the Arch wiki about some ways it can be accomplished.

Unfortunately, if I recall correctly it requires software from the AUR and is far more complex than a user would ever attempt to simply fix a suspend issue. I basically took one read and realized it was never going to be workable for usage on forum support requests.

The solution needs to be self contained within the script or service unit. Extra AUR programs and major configuration steps are just not a realistic solution for the average user.

Do you think it would be possible to use fakeroot in any way to have a user service elevate its permission level enough to accomplish what I want to do. User services are useless for loading drivers after suspend, and root services are problematic to load in the user environment. Kind of catch-22.

1 Like

As I said, you shouldn’t need them for user scope. Else they have to be clean root/system, not user related/activated.
Have you tried with the special user/root in the link? They maybe what you want. Some testing is worth doing IMO…

1 Like

Are you referring to using the @.service route. If that is what you are referring to, I haven’t spent a lot of time testing that method yet. I will definitely explore that direction more fully now.

Sorry I didn’t realize you’d posted a link. I use a custom dark color scheme for the forum and links are not often apparent when embedded in your text. It is annoying in that respect, but far easier on my eyes.

Just checked now. The first link returned

“Oops! That page doesn’t exist or is private”

1 Like

https://wiki.archlinux.org/index.php/Power_management#Sleep_hooks

2 Likes

I have read that Arch documentation you posted quite a few times, the Manjaro Link was dead as well.

Thanks anyways. I appreciated your responses. I will continue searching. Systemd takes a long time to increase your level of skill writing units, but I find it very interesting to research. Unfortunately it’s one subject there is not a ton of information out there, because of it’s relative newness.

2 Likes

Forum kindly sponsored by Bytemark