Получить TTY, который отправил сигнал в Perl

У меня есть сценарий Perl, который работает как фоновый процесс на сервере электронной почты и пытается обнаружить скомпрометированные учетные записи электронной почты с помощью различных проверок очереди и файлов журнала. Я добавил обработчик сигнала USR1, который заставляет сценарий печатать некоторую информацию о себе во время выполнения. Это прекрасно работает, если я запускаю скрипт в фоновом режиме, а затем отправляю сигнал USR1, например:

./myscript.pl &
kill -USR1 (PID)

Проблема в том, что если я выйду из этого сеанса оболочки, а затем снова подключусь, я больше не смогу получить какие-либо выходные данные, когда я использую команду kill -USR1, потому что TTY, связанный с STDOUT для сценария, исчез.

Итак, мне интересно, есть ли способ получить TTY пользователя или процесса оболочки, который отправил сигнал в Perl, а затем направить вывод обратно в TTY вместо STDOUT. Я попытался использовать POSIX::ttyname(1) в обработчике сигнала, но он возвращает TTY STDOUT скрипта (в данном случае это пустое значение), а не TTY пользователя, отправившего сигнал USR1.

Я видел в Perldoc для POSIX, что POSIX::sigaction даст PID и UID процесса, сгенерировавшего сигнал, но я не знаю, есть ли хороший способ получить имя TTY из этой информации в Perl.

Любая помощь будет принята с благодарностью! Спасибо!


person Dave    schedule 28.06.2012    source источник
comment
Почему бы не использовать какой-нибудь лог-файл для захвата вывода сценария: ./myscript.pl > some.log &, а затем отслеживать его с помощью команды tail -f some.log с любого TTY, который вам нравится?   -  person nab    schedule 28.06.2012
comment
На самом деле это мой запасной план. Пусть обработчик сигнала записывает информацию в файл, а не в телетайп. Информация, которую я хочу получить от него, это такие вещи, как время безотказной работы, количество обработанных сообщений и т. д., поэтому было бы лучше, если бы я мог получать эту информацию по запросу, а не записывать ее в журнал периодически через определенный интервал. Думаю, мне может понадобиться написать сценарий оболочки для отправки сигнала USR1, а затем cat выходного файла перед его удалением.   -  person Dave    schedule 28.06.2012


Ответы (3)


Самое простое решение предложено nab в комментарии к вашему вопросу: просто запишите свой вывод в файл (возможно, в обработчик $SIG{USR1}), а затем отслеживайте это. Вы даже можете перезаписывать его каждый раз, когда получаете SIGUSR1.

Другое решение, которое у меня есть, — создать обработчик сокетов. Это становится немного более запутанным, если вы не хотите использовать кучу модулей. Мое решение — использовать AnyEvent + Коро. Я помещаю основную работу в один поток Coro, запускаю свой AnyEvent::Socket:: tcp_server(s) (номер сокета и/или порта tcp) и при подключении делать то, что мне нужно (в моем случае создать кучу потоков, в вашем случае просто вывести детали, как вы это делаете в $SIG обработчик {USR1} и закрыть соединение).

Реальный код:

AnyEvent::Socket::tcp_server "unix/", "/tmp/cbstats.sock", sub {
    my $fh = shift;
    $fh->binmode(':utf8');
    $fh = unblock $fh;

    $self->handle_connection($fh, @_);

};

А затем, поскольку мой интерактивный, я запускаю socat readline /tmp/cbstats.sock. В вашем случае вы можете просто сделать socat stdout /tmp/your.socket в любое время, когда захотите получить результат. (Используйте разрешения файловой системы, чтобы ограничить/разрешить, кто может видеть эти данные.)

Здесь нужно немного побеспокоиться — вам нужно прекратить использовать sleep() (или использовать версию Coro), чтобы вы могли получать запросы через сокет. Но, честно говоря, это было в основном минимально. Однако я переключил свой сервер на использование таймеров AnyEvent, поэтому мне даже не нужно беспокоиться о сне. После этого я объединил несколько серверов вместе по их собственным таймерам, и все это работало достаточно хорошо.

Удачи.

person Tanktalus    schedule 28.06.2012
comment
Я думаю, что решение для выходного файла - это то, что я в конечном итоге буду использовать. Я заставлю обработчик сигнала записать вывод в файл, а затем я просто напишу сценарий оболочки для отправки сигнала USR1 и выгрузки содержимого файла пользователю. Спасибо. - person Dave; 29.06.2012

Две основные проблемы с тем, что вы спрашиваете, заключаются в том, что

  1. Телетайп пропадает/удаляется после завершения работы вызывающей оболочки
    В моей системе Linux эффект аналогичен тому, что происходит, если вы открываете/перенаправляете STDOUT в файл, а затем разъединяете этот файл. Вы не вернете это устройство вывода, если не проделываете ужасные, ужасные трюки в /proc

  2. Сигналы не передают телетайп отправителя
    ни минимальным спецификацией, ни реализациями, расширяющими спецификация

Итак, я бы посоветовал вам запустить утилиту под терминальным мультиплексором, например tmux или экран GNU или перенаправить STDOUT на какой-либо другой вывод, который вы можете проверить с другого терминала... например, файл или системный журнал или база данных и т. д.

person pilcrow    schedule 28.06.2012
comment
Спасибо. Я надеялся избежать использования экрана и т. Д., Чтобы он вел себя как демон и казался немного более естественным (если это имеет смысл). Хотя, возможно, мне придется изучить этот вариант. - person Dave; 28.06.2012
comment
@Dave, демоны часто регистрируются в подсистемах ведения журнала, FWIW. - person pilcrow; 28.06.2012

Какая у вас ОС? ps(1) может выводить управляющий tty для любого идентификатора процесса. В Linux /proc/<pid>/fd/1 и /proc/<pid>/fd/2 являются символическими ссылками на терминал или файлы, к которым присоединяются потоки вывода процесса.

person mob    schedule 28.06.2012
comment
Извините, я должен был упомянуть, что ОС — Solaris 10 (SPARC). Я попытался использовать POSIX::sigaction с установленным флагом SA_SIGINFO для соответствующего объекта SigAction, но Solaris не передает pid и uid (или что-либо еще) обработчику, даже с установленным флагом SA_SIGINFO. - person Dave; 28.06.2012