6

I did an experiment. I used K3b to burn an iso on a CD-RW. During that process, I issued systemctl suspend.

The computer did suspend.

Am I misunderstanding systemd-inhibit?

I looked at this and not sure if it could help.

How to inhibit suspend temporarily?

  systemd-inhibit may be used to execute a program with a shutdown, sleep, or idle inhibitor
       lock taken. The lock will be acquired before the specified command line is executed and
       released afterwards.
   Inhibitor locks may be used to block or delay system sleep and shutdown requests from the
   user, as well as automatic idle handling of the OS. This is useful to avoid system
   suspends while an optical disc is being recorded, or similar operations that should not be
   interrupted.

Artur Meinild
  • 31,035
fixit7
  • 3,399

2 Answers2

3

The essense of confusion

You provided a citation from the systemd-inhibit manual. However, you seem to be confused about how it's supposed to work, probably due to the manual wording.

The manual mentions that systemd-inhibit is useful for preventing system suspends during operations like burning an optical disc.

So, while you're using K3b (a disc-burning program), you might wrongly expect the system to automatically inhibit sleep while burning.

Unfortunately, this is not how systemd-inhibit works by default. It is not a daemon that automatically detects and prevents sleep for all critical operations. While you might be led to believe that all systemd programs are daemons running in brackgrount, systemd-inhibit is a command line tool that allows you as a user to explicitly inhibit sleep, shutdown, or idle states while running a specific command.

Another misunderstanding, is that you issued the systemd-inhibit command after your started K3b. systemd-inhibit accepts the name of a program to start, not the process id of a program already running.

If you want to prevent the system from suspending while K3b is running, you need to plan ahead and use systemd-inhibit like this:

systemd-inhibit --what=sleep --who="K3b" --why="Burning an ISO" k3b

This command ensures that the system will not suspend while K3b is running.

Wrong expectation

If you start K3b first and then try to use systemd-inhibit on the already running process, it will not work. For example, the following is incorrect:

[wrong] systemd-inhibit --what=sleep --who="K3b" --why="Burning an ISO" --pid=<K3b_PID> [/wrong]

This will fail because systemd-inhibit does not support attaching to existing processes.

CLI arguments (from the manual)

  • --what=: Specifies what to inhibit (e.g., sleep, shutdown, idle). Defaults to idle:sleep:shutdown if omitted.
  • --who=: A short description of the program taking the lock (defaults to the command line).
  • --why=: A short description of the reason for the lock (defaults to "Unknown reason").
  • --mode=: Specifies how the lock is applied (block, delay, or block-weak). The default is block.

Automation

While systemd-inhibit is a powerful tool, it requires manual intervention to work effectively. Ideally, applications like K3b should handle this themselves by programmatically acquiring inhibitor locks during critical operations (e.g., burning a disc). This would provide a better user experience and prevent data loss or hardware damage caused by unexpected system suspends.

If K3b were designed to do this, it would use the D-Bus interface to acquire and release inhibitor locks automatically. Until then, you need to use systemd-inhibit explicitly to ensure the critical tasks aren't interrupted by system suspend.

Vanuan
  • 151
1

I had the same problem - after calling command wrapped by systemd-inhibit my laptop successfully went to a sleep state. I haven't found a solution to this issue, but i have a good workaround. (I'm using archlinux, yet, i believe it should work in most cases.)

The main idea is to wrap the main service (binary or script) that is implementing sleeping functionality.

1. Find the main service

$ systemctl status systemd-suspend.service
○ systemd-suspend.service - System Suspend
     Loaded: loaded (/usr/lib/systemd/system/systemd-suspend.service; static)

Check contents of /usr/lib/systemd/system/systemd-suspend.service

$ cat /usr/lib/systemd/system/systemd-suspend.service
...
[Service]
Type=oneshot
ExecStart=/usr/lib/systemd/systemd-sleep suspend

And the winner is /usr/lib/systemd/systemd-sleep. Note that it consumes one argument, so wrapper script shouldn't lose it. As a bonus, we will fix all other modes (poweroff and hibernate) in a one place.

$ file /usr/lib/systemd/systemd-sleep
/usr/lib/systemd/systemd-sleep: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=fa6da37bf95f4a7255909a8e18fecbc1f3910acc, for GNU/Linux 4.4.0, stripped

2. Move it

sudo mv /usr/lib/systemd/systemd-sleep /usr/lib/systemd/systemd-sleep.bin

3. Create a wrapper script

For example, in my .bash_aliases i have:
alias lxcexport="systemd-inhibit lxc image export". Export called by this alias should block sleep service until lxc finishes container export.

In systemd-inhibit report it's marked blocked by a systemd-inhibit COMMand:

$ systemd-inhibit --list --no-pager
WHO                                UID  USER PID    COMM            WHAT                                WHY      MODE 
...
lxc image export server:container… 1000 cot 868971 systemd-inhibit shutdown:sleep:idle                 Unknown… block
...

So in wrapper script we delay called sleep action for a predefined delay (e.g. 5 sec) while systemd-inhibit --list --no-pager --no-legend|grep systemd-inhibit returns non-empty result. After lock is released, grep will return nothing and the script will continue and call the main sleep service binary with passed argument.

NB Keep in mind once it fires, it won't automatically stop on user re-activation. i.e. it will continue action ignoring you.

sudo vi /usr/lib/systemd/systemd-sleep.wrapper

#!/bin/sh
while test -n "$(systemd-inhibit --list --no-pager --no-legend|grep systemd-inhibit)" ; do 
    printf '.'
    sleep 5
done

/usr/lib/systemd/systemd-sleep.bin $1

Make it executable.

sudo chmod +x /usr/lib/systemd/systemd-sleep

Finally, create a link to the wrapper script. That way, you won't lose the wrapper script if a planned system update overwrites systemd-sleep.

sudo ln -s /usr/lib/systemd/systemd-sleep.wrapper /usr/lib/systemd/systemd-sleep

In log it looks smth like that:

May 09 08:54:53 LIVE.ARCH.BOX systemd-logind[798]: The system will suspend now!
May 09 08:54:53 LIVE.ARCH.BOX systemd[1]: Reached target Sleep.
May 09 08:54:53 LIVE.ARCH.BOX systemd[1]: Starting System Suspend...
May 09 08:54:53 LIVE.ARCH.BOX systemd-logind[798]: Operation 'sleep' finished.
May 09 08:58:27 LIVE.ARCH.BOX systemd[1545]: snap.lxd.lxc.9a9d4293-449f-4314-91b3-02aca1c4006b.scope: Consumed 4min 39.349s CPU time.
May 09 08:58:29 LIVE.ARCH.BOX systemd-sleep[859442]: ...........................................
May 09 08:58:29 LIVE.ARCH.BOX systemd-sleep[863081]: Going to suspend...
May 09 08:58:29 LIVE.ARCH.BOX systemd-sleep[863080]: Going to suspend...
May 09 08:58:29 LIVE.ARCH.BOX systemd-sleep.bin[863078]: Entering sleep state 'suspend'...
May 09 08:58:29 LIVE.ARCH.BOX kernel: PM: suspend entry (deep)
May 09 08:58:29 LIVE.ARCH.BOX kernel: Filesystems sync: 0.487 seconds

TIP The same can be done with pm-utils (but this time i've modified action script directly) :

$ which pm-hibernate
/usr/bin/pm-hibernate
10:24:34 [cot@LIVE ~]$ ls -la /usr/bin/pm-hibernate
lrwxrwxrwx 1 root root 31 May  8  2021 /usr/bin/pm-hibernate -> /usr/lib/pm-utils/bin/pm-action

.

$ cat /usr/lib/pm-utils/bin/pm-action

... command_exists "check_$METHOD" && command_exists "do_$METHOD" || { log "pm-utils does not know how to $METHOD on this system." exit 1 }

##delay if systemd-inhibit was called by someone while test -n "$(systemd-inhibit --list --no-pager --no-legend|grep systemd-inhibit)" ; do printf '.' sleep 55 done ...

diabolusss
  • 103
  • 5