Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Removed need for sudo/root permissions on Linux #22

Merged
merged 4 commits into from
Feb 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,16 +92,16 @@ The program launches a [`caffeinate`](https://ss64.com/osx/caffeinate.html) in a

### Summary table

| | Windows | Linux | Mac |
| ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- |
| wakepy uses | [SetThreadExecutionState](https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setthreadexecutionstate?redirectedfrom=MSDN) with the `ES_SYSTEM_REQUIRED` flag | `systemctl mask` | [`caffeinate`](https://ss64.com/osx/caffeinate.html) |
| sudo / admin needed? | No | Yes | No |
| `keep_screen_awake` option | Optional<br>Default: `False` | Always `True` | Optional<br>Default: `False` |
| When `keep_screen_awake = True` | Screen is kept awake. <br><br>Windows will not be locked automatically. | Screen is kept awake.<br>Automatic locking: on some distros, depending on how the lock screen is implemented. | Screen is kept awake.<br>Automatic locking = ? |
| Multiprocessing support | Yes | No | No |
| When process calling `set_keepawake` dies | All flags set by the process are removed. See: [ How will killing while lock set affect it?](https://github.com/np-8/wakepy/issues/16) | The wakelock is held even over a reboot until `unset_keepawake` is called or `sleep.target`, `suspend.target`, `hibernate.target` and `hybrid-sleep.target` are unmasked otherwise. | Nothing happens |
| How to debug or see the changes<br>done by wakepy in the OS? | Run `powercfg -requests` in<br>elevated PowerShell | run `sudo systemctl status sleep.target suspend.target hibernate.target hybrid-sleep.target` in Terminal and see if the "Loaded" status for the units is set to "masked" or "loaded". | ? |
| If on laptop, and battery low? | Sleep | Most distros will (or if set to "suspend", attempt to) do their set "when battery low" action, if that is set to none they will just crash as soon as the battery becomes too weak. | ? |
| | Windows | Linux | Mac |
| ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- |
| wakepy uses | [SetThreadExecutionState](https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setthreadexecutionstate?redirectedfrom=MSDN) with the `ES_SYSTEM_REQUIRED` flag | DBus Inhibit/UnInhibit or as a fallback `systemctl mask` | [`caffeinate`](https://ss64.com/osx/caffeinate.html) |
| sudo / admin needed? | No | No | No |
| `keep_screen_awake` option | Optional<br>Default: `False` | Always `True` | Optional<br>Default: `False` |
| When `keep_screen_awake = True` | Screen is kept awake. <br><br>Windows will not be locked automatically. | Screen is kept awake.<br>Automatic locking: on some distros, depending on how the lock screen is implemented. | Screen is kept awake.<br>Automatic locking = ? |
| Multiprocessing support | Yes | Yes | No |
| When process calling `set_keepawake` dies | All flags set by the process are removed. See: [ How will killing while lock set affect it?](https://github.com/np-8/wakepy/issues/16) | The wakelock is immediately released except if the `systemctl mask` fallback is used, in which case the wakelock will be held even over a reboot until it's released. | Nothing happens |
| How to debug or see the changes<br>done by wakepy in the OS? | Run `powercfg -requests` in<br>elevated PowerShell | ?<br>If the `systemctl mask` fallback is used, run `sudo systemctl status sleep.target suspend.target hibernate.target hybrid-sleep.target` in Terminal. | ? |
| If on laptop, and battery low? | Sleep | Default 'when battery low' action will be triggered.<bt>If the `systemctl mask` fallback is used, most distros will do their set 'when battery low' action but fail if that is suspend. | ? |

# ⚖️ Pros and Cons
### 👑💯 Advantages of wakepy
Expand All @@ -112,7 +112,7 @@ The program launches a [`caffeinate`](https://ss64.com/osx/caffeinate.html) in a
- You can use it directly from command line, or within your python scripts
- It runs without admin/sudo priviledges on Windows and Mac.
### 🔍❕ Disadvantages / pitfalls with wakepy
- On Linux, the current solution using `systemctl` needs sudo priviledges. PRs to circumvent this are welcome.
- Currently multiprocessing is not well supported on Mac and Linux (?); the first function calling `unset_keepawake` or releasing the `keepawake` context manager will allow the PC to sleep even if you have called `set_keepawake` multiple times. For these kind of cases, perhaps an implementation making mouse movement or pressing keyboard keys would work better.
- On Linux, if DBus unavailable, the fallback solution using `systemctl` needs sudo priviledges.
- Currently multiprocessing is not well supported on Mac (?); the first function calling `unset_keepawake` or releasing the `keepawake` context manager will allow the PC to sleep even if you have called `set_keepawake` multiple times. For these kind of cases, perhaps an implementation making mouse movement or pressing keyboard keys would work better.
## Changelog
- See [CHANGELOG.md](CHANGELOG.md)
26 changes: 24 additions & 2 deletions wakepy/_linux.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
try:
import dbus, os, psutil
except ImportError as e:
print(f'Error when importing DBus, os and psutil module: {e}\n\
Root permissions will be needed to set/unset the wakelock!')
import subprocess

COMMAND = u"systemctl"
ARGS = [u"sleep.target", u"suspend.target", u"hibernate.target", u"hybrid-sleep.target"]
# https://www.man7.org/linux/man-pages/man1/systemctl.1.html

dbus_inhibit = None # Variable that stores the DBus inhibit for later controlled release.
try: # Try to initialize the freedesktop inhibitor
pm_interface = dbus.Interface(dbus.SessionBus().get_object('org.freedesktop.ScreenSaver','/org/freedesktop/ScreenSaver'), 'org.freedesktop.ScreenSaver')
except Exception as e:
print(f"Wakepy can't use DBus Inhibit on this system because of a {type(e).__name__}: {e}\n\
root permissions will be needed to set/release the wakelock.")

try:
subprocess.check_output(["pidof", "systemd"])
except subprocess.CalledProcessError:
Expand All @@ -24,8 +36,18 @@ def set_keepawake(keep_screen_awake=False):
keep_screen_awake: bool
Currently unused as the screen will remain active as a byproduct of preventing sleep.
"""
subprocess.run([COMMAND, u"mask", *ARGS])
try:
global dbus_inhibit
dbus_inhibit = pm_interface.Inhibit(psutil.Process(os.getpid()).name(), f'wakepy.set_keepawake(keep_screen_awake={keep_screen_awake})')
# print(power_manager.HasInhibit()) # Prints out if the inhibitor is set ot not.
except Exception as e:
print(f"DBus Inhibit failed with a {type(e).__name__}: {e}\nFalling back to systemctl mask...")
subprocess.run([COMMAND, u"mask", *ARGS])


def unset_keepawake():
subprocess.run([COMMAND, u"unmask", *ARGS])
try:
pm_interface.UnInhibit(dbus_inhibit)
except Exception as e:
print(f"DBus UnInhibit failed with a {type(e).__name__}: {e}\nFalling back to systemctl mask...")
subprocess.run([COMMAND, u"unmask", *ARGS])