15

I use a script to do incremental backups of a btrfs partition from one disk to another.

The script is started by cron.weekly at random time of a day.

If I shut down the system while the script is running, I am getting into trouble with old backups removed and new not created.

Is there a way to setup the system to wait till the script is finished?

I am using Ubuntu 16.04 with systemd.

9
  • There is a way to block GUI commands. I've a scripting approach for that. But command line is not possible to block if done by sudo user . I'll link a past answer for GUI. Let me know if you want it customized to suit your needs Jul 22, 2016 at 9:37
  • 1
    @ByteCommander careful: those are pre-systemd.
    – Rinzwind
    Jul 22, 2016 at 9:39
  • 1
    @Serg nice one :) But isn't systemd-inhibit a bit easier on the eyes? >:-D
    – Rinzwind
    Jul 22, 2016 at 9:44
  • 1
    What happens if the script locks up? Wouldn't it be better to not remove your old backups until the new ones are finished anyway? While you may be able to prevent shutdown you can't prevent a situation where there is a system failure or general power loss. In either case you are still left with your old backup deleted and the new one not created.
    – Joe W
    Jul 22, 2016 at 13:58

2 Answers 2

20

For Ubuntu 16.04+ using systemd (the default).

systemd-inhibit --why="Wait for this script to finish" bash script.sh

===

Test:

$ systemctl poweroff
Operation inhibited by "bash script.sh" (PID 23912 "systemd-inhibit", user rinzwind),
reason is "Wait for this script to finish".
Please retry operation after closing inhibitors and logging out other users.

===

There are 7 locks:

  • sleep inhibits system suspend and hibernation requested by (unprivileged) users
  • shutdown inhibits high-level system power-off and reboot requested by (unprivileged) users
  • idle inhibits that the system goes into idle mode, possibly resulting in automatic system suspend or shutdown depending on configuration.
  • handle-power-key inhibits the low-level (i.e. logind-internal) handling of the system power hardware key, allowing (possibly unprivileged) external code to handle the event instead.
  • handle-suspend-key inhibits the low-level handling of the system hardware suspend key.
  • handle-hibernate-key inhibits the low-level handling of the system hardware hibernate key.
  • handle-lid-switch inhibits the low-level handling of the systemd hardware lid switch.

You probably also want to prevent suspend, idle and hibernate.


Example using "package manager":

fd = Inhibit("shutdown:idle", "Package Manager", "Upgrade in progress...", "block");
/* ...
      do your work
                 ... */
close(fd);

Similar to this you can code your version and add a "shutdown" at the end of this script (or add a way to determine a shutdown needs to be the next action).

1
  • Comments are not for extended discussion; the conversation that was taking place here has been moved to chat.
    – Thomas Ward
    Jul 25, 2016 at 22:48
2

In BackInTime I'm using couple different DBus methods to work on all major DEs. Only downside is this won't work for root because root has no dbus.SessionBus.

#!/usr/bin/env python3
import sys
import dbus
from time import sleep

INHIBIT_LOGGING_OUT = 1
INHIBIT_USER_SWITCHING = 2
INHIBIT_SUSPENDING = 4
INHIBIT_IDLE = 8

INHIBIT_DBUS = (
               {'service':      'org.gnome.SessionManager',
                'objectPath':   '/org/gnome/SessionManager',
                'methodSet':    'Inhibit',
                'methodUnSet':  'Uninhibit',
                'interface':    'org.gnome.SessionManager',
                'arguments':    (0, 1, 2, 3)
               },
               {'service':      'org.mate.SessionManager',
                'objectPath':   '/org/mate/SessionManager',
                'methodSet':    'Inhibit',
                'methodUnSet':  'Uninhibit',
                'interface':    'org.mate.SessionManager',
                'arguments':    (0, 1, 2, 3)
               },
               {'service':      'org.freedesktop.PowerManagement',
                'objectPath':   '/org/freedesktop/PowerManagement/Inhibit',
                'methodSet':    'Inhibit',
                'methodUnSet':  'UnInhibit',
                'interface':    'org.freedesktop.PowerManagement.Inhibit',
                'arguments':    (0, 2)
               })

def inhibitSuspend(app_id = sys.argv[0],
                    toplevel_xid = None,
                    reason = 'take snapshot',
                    flags = INHIBIT_SUSPENDING | INHIBIT_IDLE):
    """
    Prevent machine to go to suspend or hibernate.
    Returns the inhibit cookie which is used to end the inhibitor.
    """
    if not app_id:
        app_id = 'backintime'
    if not toplevel_xid:
        toplevel_xid = 0

    for dbus_props in INHIBIT_DBUS:
        try:
            bus = dbus.SessionBus()
            interface = bus.get_object(dbus_props['service'], dbus_props['objectPath'])
            proxy = interface.get_dbus_method(dbus_props['methodSet'], dbus_props['interface'])
            cookie = proxy(*[(app_id, dbus.UInt32(toplevel_xid), reason, dbus.UInt32(flags))[i] for i in dbus_props['arguments']])
            print('Inhibit Suspend started. Reason: %s' % reason)
            return (cookie, bus, dbus_props)
        except dbus.exceptions.DBusException:
            pass
    print('Inhibit Suspend failed.')

def unInhibitSuspend(cookie, bus, dbus_props):
    """
    Release inhibit.
    """
    assert isinstance(cookie, int), 'cookie is not int type: %s' % cookie
    assert isinstance(bus, dbus.bus.BusConnection), 'bus is not dbus.bus.BusConnection type: %s' % bus
    assert isinstance(dbus_props, dict), 'dbus_props is not dict type: %s' % dbus_props
    try:
        interface = bus.get_object(dbus_props['service'], dbus_props['objectPath'])
        proxy = interface.get_dbus_method(dbus_props['methodUnSet'], dbus_props['interface'])
        proxy(cookie)
        print('Release inhibit Suspend')
        return None
    except dbus.exceptions.DBusException:
        print('Release inhibit Suspend failed.')
        return (cookie, bus, dbus_props)

if __name__ == '__main__':
    cookie, bus, dbus_props = inhibitSuspend()
    print('do something here')
    sleep(10)
    unInhibitSuspend(cookie, bus, dbus_props)

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .