Можно ли настроить Ubuntu так, чтобы он не выключался до завершения работы скрипта?

15

Я использую скрипт для инкрементного резервного копирования раздела btrfs с одного диска на другой.

Сценарий запускается cron.weekly в случайное время суток.

Если я выключаю систему во время работы сценария, у меня возникают проблемы со удалением старых резервных копий и созданием новых.

Есть ли способ настроить систему, чтобы дождаться завершения сценария?

Я использую Ubuntu 16.04 с systemd.

Pilot6
источник
Есть способ заблокировать команды GUI. У меня есть подход сценария для этого. Но командную строку невозможно заблокировать, если она сделана sudo пользователем. Я свяжу прошлый ответ для GUI. Дайте мне знать, если вы хотите, чтобы он был настроен в соответствии с вашими потребностями
Сергей Колодяжный
1
@ByteCommander осторожен: они предсистемные.
Rinzwind
1
@Серг хороший :) Но не systemd-inhibitпроще ли на глазах? >: - D
Rinzwind
1
Что произойдет, если скрипт заблокирован? Не лучше ли не удалять старые резервные копии, пока новые не будут закончены? Хотя вы можете предотвратить отключение, вы не можете предотвратить ситуацию, когда происходит сбой системы или общая потеря питания. В любом случае у вас все еще остается старая резервная копия, а новая не создана.
Джо У,

Ответы:

20

Для Ubuntu 16.04+ используется systemd (по умолчанию).

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

===

Тестовое задание:

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

===

Есть 7 замков :

  • sleep запрещает приостановку системы и спящий режим, запрошенные (непривилегированными) пользователями
  • shutdown блокирует отключение и перезагрузку системы высокого уровня, запрошенные (непривилегированными) пользователями
  • idle запрещает переход системы в режим ожидания, что может привести к автоматическому приостановке или отключению системы в зависимости от конфигурации.
  • handle-power-key запрещает низкоуровневую (т. е. logind-internal) обработку аппаратного ключа питания системы, позволяя (возможно, непривилегированному) внешнему коду обрабатывать событие вместо этого.
  • handle-suspend-key блокирует низкоуровневую обработку системного аппаратного ключа приостановки.
  • handle-hibernate-key блокирует низкоуровневую обработку системного аппаратного ключа гибернации.
  • handle-lid-switch блокирует низкоуровневое управление переключателем крышки systemd.

Вы, вероятно, также хотите предотвратить suspend, idleи hibernate.


Пример использования "менеджера пакетов" :

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

Аналогично этому вы можете закодировать свою версию и добавить «завершение» в конце этого сценария (или добавить способ определить, что выключение должно быть следующим действием).

Rinzwind
источник
Комментарии не для расширенного обсуждения; разговор, который происходил здесь, был перенесен в чат .
Томас Уорд
2

В BackInTime я использую несколько различных методов DBus для работы со всеми основными DE. Единственным недостатком является то, что это не будет работать, rootпотому что rootне имеет 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
источник