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.
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.
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…
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.
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.
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.
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:
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
cat "$X/idVendor" 2>/dev/null
cat "$X/idProduct" 2>/dev/null
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:
The command “lsusb” returned this information:
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.
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.
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.
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.
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.
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.
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.
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…
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.
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.