Perl для системного администрирования

       

Преобразование кода на С из utmp h в шаблон unpack( )



Таблица 9.1. Преобразование кода на С из utmp.h в шаблон unpack( )



Код на С Шаблон unpack() Буква шаблона/повтор
char ut_line[8]; A8 Строка ASCII (дополнена пробелами) длиной 8 байт
char ut_name[8]; A8 Строка ASCII (дополнена пробелами) длиной 8 байт
char ut_host[16]; A16 Строка ASCII (дополнена пробелами) длиной 16 байт
long ut_time; 1 «Длинное» целое значение со знаком (может и не совпадать с размером значения «long» на конкретной машине)

Шаблоны созданы, теперь используем их в настоящей программе:

шаблон, который мы собираемся передать unpack()

Stemplate = "А8 А8 А16 1";

ft используем pack(), чтобы определить размер (в байтах) каждой записи

Srecordsize = length(pack($template,()));

ft открываем файл

open(WTMP, "/var/adrc/wtmp") or die "Невозможно открыть wtT.D:$! \i":

# считываем его по одной записи

while (read(WTMP,SrecordSrecordsize)) {

# распаковываем, используя шаблон

($tty. $narne. $host $time)=unoack($temolate. Srecorc).

# специальным образом обрабатываем записи

if (Sname and substr($name. 0.1) г.е "\0"){

print "$tty:$name:$nobt : "

scalar localtime($time),"\n"; }

else <

print "$tty:(logout).(logout):",

scalar localtime(Stime),"\n";

i i

}

tt закрываем файл close(WTMP);

Вот как выглядит вывод этой маленькой программы:

":reboot::Мол Nov 17 15:24:30 1997

:0:dnb::0:Mon Nov 17 15:35:08 1997

ttyp8:user:host.mcs.anl.go:Mon Nov 17 18:09:49 1997

ttyp6:dnb:limbO-114.ccs.ne:Mon Nov 17 19:03:44 1997

ttyp6:(logout):(logout):Mon Nov 17 19:26:26 1997

ttyp1:dnb:traal-22.ccs.neu:Mon Nov 17 23:47:18 1997

ttyp1:(logout):(logout):Tue Nov 18 00:39:51 1997

Приведем пару комментариев:

В SunOS завершение работы с терминалов определенного типа отмечается символом с кодом 0 в первой позиции, поэтому:

if ($name and substr($name,1,1) ne "\0")

{

read() принимает в качестве третьего аргумента количество байт, которые нужно прочесть. Вместо того чтобы жестко определить размер записи как «32», мы воспользовались удобным свойством функции pack(). Если этой функции передать пустой список, то она возвращает пустую или заполненную пробелами строку размером, совпадающим с размером записи. Это позволяет передать функции pack() произвольный шаблон и узнать ее размер:

$recordsize = length(pack($template,()));

Вызов внешней программы

Работа с файлами wtmp - настолько распространенная задача, что в Unix есть специальная команда под названием last, предназначенная для вывода двоичных файлов в формате, удобном для человека. Вот образец ее вывода, показывающий примерно те же данные, что и в предыдущем примере:

dnb ttyp6 traal-22.ccs.neu Mon Nov 17 23:47 - 00:39 (00:52)

dnb ttypl traal-22.ccs.neu Mon Nov 17 23.47 - 00:39 (GO'52

dnb ttyps l:mbo-114.ccs.ne Mon Nov 17 19:03 - 19:26 (00'22)

user ttypS host.mcs.anl.go Mon Nov 17 18:09 - crash (27+11:50)

dnb '0 :0 Mo Nov 17 15:35 - 17:35 (4»P2 PC '

reboot " Mon Nov 17 15:24

Мы свободно можем вызывать программы, такие как last из Perl. Эта программа выводит все уникальные имена пользователей, найденные в текущем файле wtmp:

open( LAST. ' Slasrexec j"') or 02 "Невозможно Запустить

Sastexec :$!';:' while(<LAST>){

$user = (solit)[0]:

print "$user"."\n" unless exisfs $seen{$use"};

$seen{$user}='': } close(LAST) or die "Невозможно правильно закрыть канал:$!\п":

Так зачем же применять этот метод, если unраск() делает все, что нам нужно? Из-за переносимости. Мы уже продемонстрировали, что формат файла wtmp в различных операционных системах отличается. Ко всему прочему, производитель может изменить формат wtmp, а это приведет к тому, что шаблоном unpackQ в его существующем виде нельзя будет пользоваться.

Но вы можете рассчитывать на то, что команда last, читающая данный формат, будет присутствовать на вашей системе, независимо от каких-либо изменений формата. В случае применения метода unpack() придется создать и поддерживать различные строки шаблонов для каждого формата файла wtmp, который планируется использовать.

Самый большой недостаток такого метода по сравнению с unpack() -это увеличение сложности анализа полей, выполняемого в программе. В случае с unpack() все необходимые поля извлекаются автоматически. При использовании last можно столкнуться с данными, которые сложно разобрать при помощи split() или регулярных выражений:

user console Weo Oct 14 20:35 - 20:37 (00:01)

user pts/12 208.243,191.21 Wed Oct 14 09:19 - 18:12 (08:53)

user pts/17 208.243.191,21 Tue Oct 13 13:36 - 17:09 (03:33)

reboot system boot Tue Oct 6 14:13

На первый взгляд, не легко разобраться с полями, но любая программа, анализирующая подобный вывод, должна уметь правильно обрабатывать пропуски в первой и четвертой строках. Можно по-прежнему использовать unpack(), чтобы разделить эти данные, так как поля в нем имею риксированную ширину, но это не всегда возможно.

Использование API операционной системы для ведения журналов

Давайте перейдем к службе Event Log Service Windows NT/2000, чтобы рассмотреть этот подход. Как мы уже упоминали, в этом случае, к сожалению, журналы хранятся не в текстовых файлах. Самый лучший и единственный поддерживаемый способ, позволяющий добраться до этих данных, заключается в применении набора специальных API-вызовов. Большинство пользователей для получения этих данных полагаются на программу Event Viewer.

К счастью, существует модуль, написанный Джесси Доэрти (Jesse Dougherty) (и обновленный Мартином Поли (Martin Pauley) и Бретом Гиддингсом (Bret Giddings)), обеспечивающий простой доступ к API-вызовам Event Log. Вот простая программа, которая выводит список событий из журнала System в формате, подобном syslog. Позже мы подробно рассмотрим более сложную версию этой программы.

use Win32::EventLog;

П

у каждого события есть тип, вот как выглядят самые

и распространенные типы %type = (1 => "ERROR",

2 => "WARNING", 4 =<• "INFORMATION", 8 => "AUDIT_SUCCESS", 16 => "AUDIT_FAILURE");

# если это значение установлено, мы также получаем полный текст

# каждого сообщения при каждом вызове

Read() $Win32::EventLog::GetMessageText = 1;

fl открываем журнал событий

System Slog = new Win32::EventLog("System") or die

"Невозможно открыть системный журнал:$~Е\п";

# читаем его по одной записи, начиная с первой

while ($log->Read((EVENTLOG_SEQUENTIAL_READ|EVENTLOG_FORWARDS_READ),

1,$entry)){

print scalar localtime($entry->{TimeGenerated})." ";

print $entry->{Computer}."[".($entry->{EventID} &

Oxffff)."] ";

print Sentry->{Source}.":".$type{$entry->{EventType}}; print $entry->{Message};

}

В NT/2000 существуют также утилиты, работающие из командной строки, такие как last, выводящие события из журнала в текстовом виде. Позже мы посмотрим на эти утилиты в действии.



Содержание раздела