¿Es posible configurar Ubuntu de manera que no se apague antes de que finalice un script?

15

Utilizo un script para hacer copias de seguridad incrementales de una partición btrfs de un disco a otro.

El script lo inicia cron.weekly a una hora aleatoria de un día.

Si apago el sistema mientras se ejecuta el script, me estoy metiendo en problemas con las copias de seguridad antiguas eliminadas y las nuevas no creadas.

¿Hay alguna manera de configurar el sistema para que espere hasta que finalice el script?

Estoy usando Ubuntu 16.04 con systemd.

Piloto6
fuente
Hay una manera de bloquear los comandos de la GUI. Tengo un enfoque de secuencias de comandos para eso. Pero la línea de comando no es posible bloquear si lo hace el sudo usuario. Voy a vincular una respuesta anterior para GUI. Avíseme si desea personalizarlo para satisfacer sus necesidades
Sergiy Kolodyazhnyy
1
@ByteCommander cuidado: esos son pre-systemd.
Rinzwind
1
@Serg nice :) Pero, ¿no es systemd-inhibitun poco más fácil a la vista? >: - D
Rinzwind
1
¿Qué sucede si el script se bloquea? ¿No sería mejor no eliminar las copias de seguridad anteriores hasta que las nuevas estén terminadas? Si bien puede evitar el apagado, no puede evitar una situación en la que haya una falla del sistema o una pérdida general de energía. En cualquier caso, todavía queda con su copia de seguridad anterior eliminada y la nueva no creada.
Joe W

Respuestas:

20

Para Ubuntu 16.04+ con systemd (el valor predeterminado).

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

===

Prueba:

$ 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.

===

Hay 7 cerraduras :

  • sleep inhibe la suspensión del sistema y la hibernación solicitada por usuarios (no privilegiados)
  • shutdown inhibe el apagado del sistema de alto nivel y el reinicio solicitado por los usuarios (sin privilegios)
  • idle inhibe que el sistema entre en modo inactivo, posiblemente resultando en la suspensión o apagado automático del sistema según la configuración
  • handle-power-key inhibe el manejo de bajo nivel (es decir, logind-internal) de la llave de hardware del sistema, permitiendo que el código externo (posiblemente sin privilegios) maneje el evento en su lugar.
  • handle-suspend-key inhibe el manejo de bajo nivel de la clave de suspensión del hardware del sistema.
  • handle-hibernate-key inhibe el manejo de bajo nivel de la clave de hibernación del hardware del sistema.
  • handle-lid-switch inhibe el manejo de bajo nivel del interruptor de la tapa del hardware del sistema.

Probablemente también quieras prevenir suspend, idley hibernate.


Ejemplo usando "administrador de paquetes" :

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

De manera similar, puede codificar su versión y agregar un "apagado" al final de este script (o agregar una forma de determinar que un apagado debe ser la siguiente acción).

Rinzwind
fuente
Los comentarios no son para discusión extendida; La conversación que estaba teniendo lugar aquí se ha trasladado al chat .
Thomas Ward
2

En BackInTime estoy usando un par de métodos DBus diferentes para trabajar en todos los DE principales. El único inconveniente es que esto no funcionará rootporque rootno tiene 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)
Germar
fuente