Переопределение основных сигналов (SIGINT, SIGQUIT, SIGKILL ??) в Python

1

Я пишу программу, которая добавляет обычные учетные записи UNIX (например, изменение /etc/passwd,/etc/group и/etc/shadow) в соответствии с нашей корпоративной политикой. Он также делает некоторые немного причудливые вещи, такие как отправка электронной почты пользователю.

У меня есть весь код, но есть три фрагмента кода, которые очень критичны, которые обновляют три файла выше. Код уже достаточно надежный, поскольку он блокирует эти файлы (например,/etc/passwd.lock), записывает во временные файлы (например,/etc/passwd.tmp), а затем перезаписывает исходный файл временным. Я довольно доволен, что он не будет мешать другим исполняемым версиям моей программы или программам useradd, usermod, passwd и т.д.

То, что меня больше всего волнует, - это блуждающая команда ctrl + c, ctrl + d или kill в середине этих разделов. Это привело меня к сигнальному модулю, который, кажется, делает именно то, что я хочу: игнорировать определенные сигналы во время "критического" региона. Я использую более старую версию Python, у которой нет signal.SIG_IGN, поэтому у меня есть потрясающая функция "pass":

def passer(*a):
    pass

Проблема, которую я вижу, заключается в том, что обработчики сигналов работают не так, как я ожидаю. Учитывая следующий тестовый код:

def passer(a=None, b=None):
pass

def signalhander(enable):
    signallist = (signal.SIGINT, signal.SIGQUIT, signal.SIGABRT, signal.SIGPIPE,       signal.SIGALRM, signal.SIGTERM, signal.SIGKILL)
    if enable:
        for i in signallist:
            signal.signal(i, passer)
    else:
        for i in signallist:
            signal.signal(i, abort)
    return


def abort(a=None, b=None):
    sys.exit('\nAccount was not created.\n')
    return
signalhander(True)                                                                                                                                                                                                                 
print('Enabled')
time.sleep(10)   # ^C during this sleep

Проблема с этим кодом заключается в том, что a ^ C (SIGINT) во время вызова time.sleep(10) заставляет эту функцию останавливаться, а затем мой обработчик сигнала берет на себя по желанию. Тем не менее, это не решает мою проблему "критического" региона выше, потому что я не могу терпеть любое утверждение, которое встречает сигнал с ошибкой.

Мне нужен какой-то обработчик сигналов, который просто полностью игнорирует SIGINT и SIGQUIT. Команда Fedora/RH "yum" написана как Python и в основном выполняет именно то, что я хочу. Если вы делаете a ^ C при установке чего-либо, он печатает сообщение типа "Нажмите ^ C в течение двух секунд, чтобы заставить убить". В противном случае ^ C игнорируется. Меня не волнует второе предупреждение, так как моя программа завершается за долю секунды.

Может ли кто-нибудь помочь мне реализовать обработчик сигналов для CPython 2.3, который не вызывает отмену текущего оператора/функции до того, как сигнал будет проигнорирован?

Как всегда, спасибо заранее.


Изменить: после ответа S.Lott я решил отказаться от сигнального модуля.

Я собираюсь вернуться к блокам try: except:. В моем коде есть две вещи, которые происходят для каждой критической области, которая не может быть прервана: перезапись файла с файлом file.tmp и удаление блокировки после завершения (или другие инструменты не смогут изменить файл, пока он не будет удален вручную). Я поместил каждую из них в свою собственную функцию внутри блока try:, а except: просто вызывает функцию снова. Таким образом, функция будет просто повторно звонить сама в случае KeyBoardInterrupt или EOFError, пока не будет завершен критический код. Я не думаю, что у меня могут возникнуть проблемы, так как я только улавливаю пользовательские команды выхода, и даже тогда, только для двух-трех строк кода. Теоретически, если бы эти исключения могли быть подняты достаточно быстро, я полагаю, что могу получить ошибку "максимальная ошибка повторного прослушивания", но это кажется далеко.

Любые другие проблемы?

Pesudo-код:

def criticalRemoveLock(file):
    try:
        if os.path.isFile(file):
            os.remove(file)
        else:
            return True
    except (KeyboardInterrupt, EOFError):
        return criticalRemoveLock(file)
def criticalOverwrite(tmp, file):
    try:
        if os.path.isFile(tmp):
            shutil.copy2(tmp, file)
            os.remove(tmp)
        else:
            return True
     except (KeyboardInterrupt, EOFError):
        return criticalOverwrite(tmp, file)
  • 0
    Повторная попытка - действительно плохая идея. Вы хотите отменить всю работу и восстановить все, как было, когда вы начинали. Принудительное завершение операции при наличии прерываний клавиатуры приводит к тому, что ваша программа ведет себя плохо. Уборка и хорошая ошибка "Ничего не сделано" намного лучше. Почему бы просто не очистить, как любая другая программа, которая пытается критические вещи?
  • 0
    @S.Lott.С. Лотт. Есть две ситуации, когда повторная попытка абсолютно обязательна: снятие блокировки и файлов tmp. Вы просто не можете оставить эти файлы, иначе пользователи не смогут правильно запустить программу в следующий раз. Это убирает. Я сделаю небольшой шаг вперед, потому что очистка (rm file.tmp file.lock) не займет больше времени, чем файл mv file.tmp; рм file.lock. У меня есть «учетная запись не была создана» для большинства приложений. Это просто три важные части. Как только приложение попадет туда, я хочу рассматривать изменения как атомарные. Даже если я решу вернуться, нужно повторить попытку до успешного завершения.
Показать ещё 3 комментария
Теги:
signals

1 ответ

3

Нет реального способа сделать ваш script действительно сохраненным. Конечно, вы можете игнорировать сигналы и перехватывать прерывание клавиатуры с помощью try: except:, но ваше приложение должно быть идемпотентным против таких прерываний, и оно должно иметь возможность возобновлять операции после работы с прерыванием в какой-либо точке сохранения.

Единственное, что вы действительно можете сделать - это работать с временными файлами (а не с оригинальными файлами) и перемещать их после выполнения работы в конечный пункт назначения. Я думаю, что такие файловые операции должны быть "атомарными" из файловой системы. В противном случае в случае прерывания: перезапустите свою обработку с начала с чистых данных.

  • 0
    Временные файлы. Вот как это должно быть сделано.
  • 0
    @ S.Lott Спасибо за ответ. В настоящее время я использую временные и блокирующие файлы. Смотрите мое редактирование выше с моим решением.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню