É possível definir o Ubuntu de uma maneira que não desligue antes que um script seja concluído?

15

Eu uso um script para fazer backups incrementais de uma partição btrfs de um disco para outro.

O script é iniciado por cron.weekly em horário aleatório do dia.

Se eu desligar o sistema enquanto o script estiver em execução, estou tendo problemas com os backups antigos removidos e os novos não criados.

Existe uma maneira de configurar o sistema para esperar até o script terminar?

Estou usando o Ubuntu 16.04 com systemd.

Pilot6
fonte
Existe uma maneira de bloquear comandos da GUI. Eu tenho uma abordagem de script para isso. Mas a linha de comando não é possível bloquear se feita pelo sudo usuário. Vou vincular uma resposta anterior para a GUI. Deixe-me saber se você quer-lo personalizado para atender às suas necessidades
Sergiy Kolodyazhnyy
11
@ByteCommander cuidado: esses são pré-systemd.
Rinzwind
11
@ Berg bom :) Mas não é systemd-inhibitum pouco mais fácil para os olhos? >: - D
Rinzwind
11
O que acontece se o script trava? Não seria melhor não remover seus backups antigos até que os novos sejam concluídos? Embora você possa impedir o desligamento, não pode impedir uma situação em que haja uma falha no sistema ou perda geral de energia. Nos dois casos, você ainda terá o backup antigo excluído e o novo não criado.
21816 Joe W

Respostas:

20

Para o Ubuntu 16.04+ usando systemd (o padrão).

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

===

Teste:

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

===

Existem 7 bloqueios :

  • sleep inibe a suspensão e a hibernação do sistema solicitadas por usuários (sem privilégios)
  • shutdown inibe o desligamento e a reinicialização de alto nível do sistema solicitados por usuários (não privilegiados)
  • idle inibe que o sistema entre no modo inativo, possivelmente resultando em suspensão ou desligamento automático do sistema, dependendo da configuração.
  • handle-power-key inibe o manuseio de baixo nível (interno de logind) da chave de hardware de energia do sistema, permitindo que código externo (possivelmente sem privilégios) manipule o evento.
  • handle-suspend-key inibe a manipulação de baixo nível da chave de suspensão do hardware do sistema.
  • handle-hibernate-key inibe a manipulação de baixo nível da chave de hibernação do hardware do sistema.
  • handle-lid-switch inibe o manuseio de baixo nível do interruptor da tampa do hardware do systemd.

Você provavelmente também quer impedir suspend, idlee hibernate.


Exemplo usando o "gerenciador de pacotes" :

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

Semelhante a isso, você pode codificar sua versão e adicionar um "desligamento" no final deste script (ou adicionar uma maneira de determinar se um desligamento precisa ser a próxima ação).

Rinzwind
fonte
Comentários não são para discussão prolongada; a conversa que estava ocorrendo aqui foi movida para o bate-papo .
Thomas Ward
2

No BackInTime , estou usando dois métodos DBus diferentes para trabalhar em todos os principais DEs. A única desvantagem é que isso não funcionará rootporque rootnão tem 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
fonte