languitar / autosuspend

A daemon to automatically suspend and wake up a system
https://autosuspend.readthedocs.io
GNU General Public License v2.0
75 stars 15 forks source link

New feature: Don't suspend if cron/at jobs about to run & resume when cron/at jobs due to run #96

Open neeto33 opened 3 years ago

neeto33 commented 3 years ago

Two parts to this feature

  1. Don't suspend if cron/at jobs about to run This will need a configurable parameter on how many seconds in future the cron/at task must be so that suspend will happen
  2. Resume when cron/at jobs due to run This will need a configurable parameter on how many second to wake up before the cron/at task is to be run
languitar commented 3 years ago

Interesting idea. This would basically result in an activity check that determines whether a cron job will become active soon.

Is it really cron that you need or systemd timers?

neeto33 commented 3 years ago

In reality probably both now that I think about it. Things like the unattended upgrades are under systemd, other packages and users will put in cron & at tasks. I've been trying to make my Ubuntu HTPCs just like they were under windows only better - go to sleep until a user uses it or there is scheduled task (TV recording, upgrade, log mgt, etc) to do. I've been really surprised that Linux hasn't tackled the problem at a more foundation level - just saving a bit of electricity across millions of computers could help the planet. I was seriously thinking about writing something along the lines of of autosuspend before I found it - so thanks, great work. Also found this package which has the idle check, the cron/at and wakes up: https://sourceforge.net/projects/rtcidle/ And how to find when the next systemd timer will run - but there are issues https://unix.stackexchange.com/questions/329996/is-there-a-way-to-know-when-a-systemd-timer-will-run-next In looking into the problem I was thinking about coming at the "low" level of the OS e.g. cron/at and then using addon scripts to TVheadend to push recording tasks in to cron/at - TvHeadend already has a callout script capability. The lower in Linux autosuspend can work, the more likely it is to be transparent to packages/GUIs/etc and the less "specials" need to be done. I really like the approach you've taken with the XML parsing of returned values, etc.

languitar commented 3 years ago

What kind of cron job / systemd timer do you think about. Because timers - depending on the configuration - are executed also after waking up again. So I don't see why for an unattended update the system has to stay awake if things continue after waking up again. This feature would probably only be useful for things like recordings that are time critical? Or which exact use cases do you have?

For scheduled tasks on things like tvheadend, the respective wake up check should be configured because a known wake up point will already prevent suspending.

languitar commented 3 years ago

and another side note: you can also let systemd timers or cron jobs program the wakeup point of the system.

neeto33 commented 3 years ago

Yeh I had a look at doing that - it means you've got to change the cron/at tasks submitted and change the systemd files which is not good because they get updated as part of standard package updates.

languitar commented 3 years ago

I'll think about this feature. But time is currently quite limited. Might take a moment.

noctux commented 3 years ago

Interesting enough, I was just thinking about opening a feature request for the SystemD-timer aspect, so it is nice to see this discussed :)

For Cron, I imagine this to be an involved implementation, as the implementation would either be daemon-dependent or require a reimplementation of cron's configuration format (including all the different cron.daily etc.pp. conventions). Another question would be how one references individual cron-jobs. It's hardly desireable to wake up for all cron jobs (e.g. rebuilding man db)...

At least for SystemD timers, both problems should be solveable... Identifyability by canonical name is given, and an API exists to query the next execution instance for a given timer. What is nice is that this is nearly expressable with wakeup checks already (It's a variant of the solutions in the stackoverflowlink that @neeto33 has provided above):

[wakeup.RsnapshotMonthly]
class = Command
enabled = true
command = date -d "$$(LANG=C LC_TIME=C systemctl show rsnapshot-monthly.timer --property=NextElapseUSecRealtime --value | cut -d' ' -f 2-)" +%s

And it nearly works: If freshly configured, the box will wake up ONCE at the timers start time, but the PC will not be woken up again when autosuspend has put my NAS to sleep again after the rsnapshot run has finished. My guess is: while the timers SystemD-Unit is active, the above command reports the current activation's time, so a date in the past. And so there might be an race between "reexecute the wakeup scheduler" and "put the box to sleep". But as said: this is just a guess, I've not yet had time to look into the code... But it seems plausible, as, when I wake up my NAS manually between two timer activations, the wakup-check fires again. So if my assumption is correct, implementing support for systemd-timers might be possible with a manageable effort (if we can make sure that all wakeup checks are executed once more before we go to sleep).

languitar commented 3 years ago

Just to be sure regarding the potential race: did you also enable the autosuspend-detect-suspend.service unit in addition to autosuspend.service?

noctux commented 3 years ago

Ah, thanks for the pointer. Yes, it is enabled, but the logs are helpful. I definitely should have checked the suspend-hook's logs earlier, and not just those of the main unit :)

Mar 31 10:09:31 xxx autosuspend[799275]: 2021-03-31 10:09:31,245 - autosuspend - INFO - Configuring check RsnapshotWeekly with class Command from module autosuspend.checks.wakeup using config parameters {'class': 'Command', 'enabled': 'true', 'command': 'date -d "$(LANG=C LC_TIME=C systemctl show rsnapshot-weekly.timer --property=NextElapseUSecRealtime --value | cut -d\' \' -f 2-)" +%s'}
Mar 31 10:09:31 xxx autosuspend[799275]: 2021-03-31 10:09:31,251 - autosuspend - INFO - Configuring check RsnapshotMonthly with class Command from module autosuspend.checks.wakeup using config parameters {'class': 'Command', 'enabled': 'true', 'command': 'date -d "$(LANG=C LC_TIME=C systemctl show rsnapshot-monthly.timer --property=NextElapseUSecRealtime --value | cut -d\' \' -f 2-)" +%s'}
Mar 31 10:09:31 xxx autosuspend[799275]: 2021-03-31 10:09:31,251 - autosuspend - INFO - Pre-suspend hook starting, trying to acquire lock
Mar 31 10:09:31 xxx autosuspend[799275]: 2021-03-31 10:09:31,327 - autosuspend - INFO - Scheduling next wake up at 2021-04-01 19:59:00+00:00
Mar 31 10:09:31 xxx autosuspend[799275]: 2021-03-31 10:09:31,328 - autosuspend - INFO - Scheduling wakeup using command: echo 1617307140 > /sys/class/rtc/rtc0/wakealarm
Mar 31 10:09:31 xxx autosuspend[799343]: /bin/sh: line 1: echo: write error: Invalid argument
Mar 31 10:09:31 xxx autosuspend[799275]: 2021-03-31 10:09:31,345 - autosuspend - WARNING - Unable to execute wakeup scheduling command: echo 1617307140 > /sys/class/rtc/rtc0/wakealarm
Mar 31 10:09:31 xxx autosuspend[799275]: Traceback (most recent call last):
Mar 31 10:09:31 xxx autosuspend[799275]:   File "/usr/lib/python3.9/site-packages/autosuspend/__init__.py", line 116, in schedule_wakeup
Mar 31 10:09:31 xxx autosuspend[799275]:     subprocess.check_call(command, shell=True)  # noqa: S602
Mar 31 10:09:31 xxx autosuspend[799275]:   File "/usr/lib/python3.9/subprocess.py", line 373, in check_call
Mar 31 10:09:31 xxx autosuspend[799275]:     raise CalledProcessError(retcode, cmd)
Mar 31 10:09:31 xxx autosuspend[799275]: subprocess.CalledProcessError: Command 'echo 1617307140 > /sys/class/rtc/rtc0/wakealarm' returned non-zero exit status 1.
Mar 31 10:09:31 xxx systemd[1]: autosuspend-detect-suspend.service: Succeeded.

Apparently, my NAS is at least one Intel-generation short of having a wake-alarm that works more than one day into the future:

[1441875.766080] rtc_cmos 00:00: Alarms can be up to one day in the future

So it seems that the above works with systemd-timers, just not on my system :)

@languitar: Thanks for the help (and the software in general, it's great)

languitar commented 3 years ago

What a weird restriction. You probably have to clamp this to get intermediate wakeups for rescheduling until the actual event.

noctux commented 3 years ago

What a weird restriction. You probably have to clamp this to get intermediate wakeups for rescheduling until the actual event.

Well, apparently acpi declares wakeups for intervals longer than one day as optional, guarded by the "DAY_ALRM" field within the FADT part of ACPI. What I've read, it actually is not tooo uncommon with older chip(sets), so approx. pre-sandybridge ones to miss it.

So yeah, as I did not want to clamp, I've written a small wake-on-lan--scheduler that can be run on my openwrt-router and accepts wakeup-scheduling requests from my NAS, I'll have to see how well this works out.

But that leaves the scope of this issue, sorry for the small hijacking, and thanks once more for your help!

languitar commented 3 years ago

Using wake on lan sounds like a good solution then if it's not too brittle. You have changed wakeup_cmd to inform your router on the required wakeup then to realize this?

noctux commented 3 years ago

Yep, that is pretty much it. Currently, it's

[general]
wakeup_cmd = curl --data "mac=XX:XX:XX:XX:XX:XX" --data "timestamp={timestamp:.0f}" http://routerhostname.lan/cgi-bin/disorganizer-cgi

but in the long term I'm toying around with the idea of using a wrapper script that first tries to write to rtc, catch the invalid argument error if necessary and only then requesting wake-on-lan wakeups. And probably some error handling around curl (such as actually clamping if something goes wrong there) might be nice as well :)

languitar commented 3 years ago

Nice, if you got anything running in that direction it would be nice to add this to the documentation as an FAQ entry