Я пишу программу, которая добавляет обычные учетные записи 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)
Нет реального способа сделать ваш script действительно сохраненным. Конечно, вы можете игнорировать сигналы и перехватывать прерывание клавиатуры с помощью try: except:
, но ваше приложение должно быть идемпотентным против таких прерываний, и оно должно иметь возможность возобновлять операции после работы с прерыванием в какой-либо точке сохранения.
Единственное, что вы действительно можете сделать - это работать с временными файлами (а не с оригинальными файлами) и перемещать их после выполнения работы в конечный пункт назначения. Я думаю, что такие файловые операции должны быть "атомарными" из файловой системы. В противном случае в случае прерывания: перезапустите свою обработку с начала с чистых данных.