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

         

Мы вкратце рассмотрим четыре различных


Мы вкратце рассмотрим четыре различных способа работы с процессами в NT/2000, поскольку каждый из них открывает перед нами двери к увлекательным возможностям, лежащим за пределами нашего обсуждения. Сначала мы остановимся на двух задачах: поиске всех запущенных процессов и завершении работы некоторых из них.



Используем Microsoft Resource Kit



В главе 3 «Учетные записи пользователей» упоминалось, что NT Resource Kit является отличным источником для сценариев и информации. Из этого пакета будут использоваться две программы: pulist.exe и kill.exe. Первая выводит список процессов, вторая- «убивает» их. В этом пакете есть еще одна утилита tlist.exe, похожая на pulist.exe, которая может вывести все процессы в списке, удобном для чтения, но ей не достает некоторых возможностей pulist.exe. Например, pulist.exe может вывести список процессов не только текущего, но и другого компьютера.

Вот фрагмент из вывода командыpulist:

Process PID User

TAPISRV.EXE 119 NT AUTHORITY\SYSTEM

TpChrSrv.exe 125 NT AUTHORITY\SYSTEM

RASMAN.EXE 131 NT AUTHORITY\SYSTEM

mstask.exe 137 NT AUTHORITY\SYSTEM

mxserver.exe 147 NT AUTHORITY\SYSTEM

PSTORES.EXE 154 NT AUTHORITY\SYSTEM

NDDEAGNT.EXE 46 OMPHALOSKEPSIS\Administrator

explorer.exe 179 OMPHAlOSKEPSIS\Administrator





SYSTRAY.EXE 74 OMPHALOSKEPSIS\Administrator

cardview.exe 184 OMPHAlOSKEPSIS\Administrator

ltmsg.exe 167 OMPHALOSKEPSIS\Administrator

daemon.exe 185 OMPHALOSKEPSIS\Adrdinistrator

Применять pulist.exe из Perl очень просто. Вот один из способов:

$pulistexe = "\\bin\\PULIST.EXE":

местоположение программы open

(PULIST. "$pulistexe|")

"Невозможно выполнить Spulistexe.S! ''

scalar <PULIST>: и удаляем первую строку заголовка

while(defined($_=<PULIST>)){

print "$pranie.$pid:$puser\n":

close(PULIST):

Вторая упомянутая программа - это kill.exe. Ее тоже просто использовать. В качестве аргумента она Принимает либо идентификатор процесса, либо часть имени задачи. В целях безопасности я рекомендую использовать идентификаторы процессов, потому что иначе очень легко убить не тот процесс, который нужно.



Программа kill. exe использует два различных способа завершения работы процессов. Один из них - это так называемая «вежливая смерть»: kill.exe <process id> попросит подтверждения на завершение работы процесса. Но если добавить к командной строке ключ //, действия kill.exe /f <process id> будут скорее напоминать манеру истинных Perl-функций - он завершит работу процесса с особенной предвзятостью.



Использование модуля Win32::IProc



Второй подход - применять модуль Win32: : IProc Амина Мюлэй Рамдэна (Amine Moulay Ramdane). И хотя название подсказывает, казалось бы, очевидный выбор, но Win32: : Iproc, в действительности, гораздо полезнее для нас, чем Win32: :Process. У Win32: : Process есть один значительный недостаток, который тут же выводит модуль из борьбы: он создан для работы с процессами, которые были запущены им самим. В то время как нас больше интересуют процессы, запущенные другими пользователями. Если вам не удается установить модуль Win32: :IProc, загляните в раздел «Информация о модулях из этой главы» .

Сначала необходимо создать объект процесса подобным образом:

use Win32::IProc;

и обратите внимание на регистр.

Обязательно должно быть "IProc"

$pobj = new Win32::IProc or die

"Невозможно создать объект proccess: $!\n";

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

$pobj-> EnumProccesses(\@processlist) or

die "Невозможно получить список процессов:$!\n";

@processlist - это массив ссылок на анонимные хэши. В каждом анонимном хэше есть два ключа: ProcessName и Processld с их значениями. Такой код позволяет аккуратно вывести нужную информацию:

use Win32::IProc;

$pobj=new Win32::IProc or die

$pobj->EnumPrecesses(VSprocessiis и or

die "Невозможно получитэ список процессор:$г\п';

foreach Sprocess (@processlist){

$pid = $process-x{ProcessId};

Snair.e - $cessNama}; write:

}

format STDOUT_TCP =



Process ID Process Name

format STDOUT =

@<««« @««« ««««««««««««

$pid, $name

Получаем результат:

Process ID Process Name

======= ===========

0 System-Idle

2 System

25 smss.exe

39 winlogon.exe

41 services.exe

48 lsass.exe

78 spoolss.exe

82 OKSERVICE.EXE

Отличие от действий pulist.exe заключается в том, что Win32: : IP roc не может сообщить вам пользовательский контекст для процесса. Если эта информация для вас важна, то следует применять pulist.exe.

pulist.exe может вывести только один тип информации, а что может Win32: - сейчас будет ясно. Допустим, вам хочется узнать не только о запущенных процессах, но и о том, какие исполняемые программы и динамически загружаемые библиотеки (.сШ) использует каждый процесс. Получить эту информацию просто:

импортируем постоянную FULLPATH.

чтоб показывать пути к

библиотекам, может быть и NOPATH use

Win32:.IProc "FULLPATH": Spobj = пел Win32::IProc:

$pobj ->EnuTiProcesses(\5processlist) or die

"Невозможно список процессов : $;

foreach Sp^ocess (?processlisr){ print "\n".

$p^ocess->(D''ocessNaTie!

"\n".(' = ' x length($orocess->{ProcessNa~e})!. "-n":

Vswodules,FULLPATH); print join("\n", map {lc $_->

{Modul'eName}} ^modules), "\n":

}

GetProcessModules() получает идентификатор процесса, ссылку на массив и флаг, говорящий о том, возвращать ли полный путь к библиотеке. Элементами массива, на который мы ссылаемся, являются ссылки на анонимные хэши, содержащие информацию о каждой библиотеке, используемой этим процессом. В нашем примере собираются имена всех библиотек. тар() используется для того, чтобы обойти весь массив ссылок, разыменовывать каждый анонимный хэш и получить значение ключа ModuleName.

Вот отрывок полученных данных:

smss.exe

\systemroot\system32\smss.exe c:\winnt\system32\ntdll.dll

winlogon.exe

\??\с:\winnt\system32\winlogon.exe

c:\winnt\system32\ntdll.dll

c:\winnt\system32\msvcrt.dll

c:\winnt\system32\kernel32.dll



c:\winnt\system32\advapi32.dll

c:\winnt\system32\user32.dll

c:\winnt\systein32\gdi32.dll

c:\winnt\system32\rpcrt4.dll

c:\winnt\system32\userenv.dll

c:\winnt\system32\shell32.dll

c:\winnt\system32\shlwapi.dll

с:\winnt\system32\comctl32.dll

c:\winnt\system32\netapi32.dll

С:\winnt\system32\netrap. dll

c:\winnt\system32\samlib.dll

c:\winnt\system32\winmm. dll

с:\winnt\system32\cwcmmsys.dll

c:\winnt\system32\cwcfm3.dll

c:\winnt\system32\msgina.dll

c:\winnt\system32\rpclts1.dll

c:\winnt\system32\rpcltcl. all. . .

Но давайте пойдем еще дальше. Совсем немного усилий следует приложить, чтобы больше узнать о запущенных процессах. Для получения необходимой информации сначала нужно определить дескриптор этого процесса.

Дескриптор процесса можно рассматривать как открытое соединение с данным процессом. Чтобы выяснить разницу между дескриптором процесса и его идентификатором, проведем аналогию со стоянкой прицепов. Если каждый прицеп на стоянке считать процессом, то адрес прицепа можно считать идентификатором процесса. Это способ найти нужный прицеп. Дескриптор процесса - это что-то наподобие телефонных линий, труб и проводов, подведенных к прицепу. Когда прицеп подсоединен к этим коммуникациям, вы можете не только найти нужный прицеп, но и передавать в него информацию.

Для получения дескриптора процесса, если у нас есть его идентификатор, используем метод Ореп() из модуля Win32: : IProc. Ореп() принимает идентификатор процесса, флаг доступа, флаг наследования и ссылку на скалярное значение, в котором хранится дескриптор. В следующем примере запроса мы будем использовать флаги доступа, которых достаточно для получения информации о процессе. Подробную информацию об этих флагах можно найти в документации по Win32: :IProc и в разделе «Processes and Threads» документации Win32 SDK по основным службам, которую можно найти на http://msdn.microsoft.com. Дескрипторы процессов, открытые при помощи Ореп(), необходимо закрыть при помощи CloseHandle().

Зная дескриптор процесса, можно использовать метод Kill() для завершения его работы:



завершить процесс и заставить его вернуть именно этот код

$pobj->Kill($handle.$exitcode);

Но дескрипторы процессов следует применять не только для того, чтобы завершать работу процесса. Например, можно использовать такие методы, как GetStatusQ, чтобы больше узнать о процессе. Вот пример кода, который выводит информацию о времени для заданного идентификатора процесса:

use Win32::IProc qw(

PROCESS_QUERY_INFORMATION INHERITED DIGITAL);

$pobj = new Win32::IProc;

$pobj->Open($ARGV[0],PROCESS_QUERY_INFORMATION, INHERITED. \$handle) or warn

"Невозможно получить дескриптор:".$pobj->LastError()."\n";

ft DIGITAL = время в понятном формате $pouj->

GetStatus($handle,\$statusinfo.DIGITAL):

$pobj->CloseHandle($handle):

while ((Sprocname,$vaiue)=eacn %$statusinfo){

print "$procname: $value\n":

}

В результате получается что-то приблизительно следующее:

Kernelrii?L-: 00;00:22:442:270 Fxituate:

ExitTime:

CreationDate: 29/7/1999 CreationTime:

17:09:28:100

UserTime:

00:00:11:566:632

Теперь известно, когда процесс был запущен и сколько системного времени он занимает. Поля ExitDate и ExitTime пусты, поскольку процесс все еще активен. Вы могли бы спросить, как эти поля, в принципе, могут оказаться не пустыми, если для получения дескриптора нужно использовать идентификатор работающего процесса? На этот вопрос есть два ответа. Во-первых, можно получить дескриптор для работающего процесса, а затем заставить этот процесс завершиться до того, как вы закроете дескриптор. GetStatusO в таком случае вернет информацию о завершении работы для умершего процесса. Вторая возможность получить эту информацию- использовать метод Сгеate(), о котором мы пока еще не знаем.

Create О

позволяет запускать процессы из Win32: так же, как и в случае с Win32::Process. Если запустить процесс при помощи модуля, то объект процесса ($pobj), который до сих пор не обсуждался, будет содержать информацию о самом процессе и потоках. Обладая этой информацией, вы сможете делать любопытные вещи, например, манипулировать приоритетами потоков и окнами этого процесса. Мы не собираемся рассматривать эти возможности, но упомянуть о них следует, чтобы спокойно перейти к следующему модулю.





Использование модуля Win32::Setupsup



Если упоминание о манипуляции окнами процесса, приведенное в конце предыдущего раздела, возбудило ваше любопытство, вам понравится наш следующий подход. На этот раз мы рассмотрим модуль Win32: :Setupsup Йена Хелберга (Jens Helberg). Он называется «Setup-sup», потому что первоначально был разработан для использования при установке программного обеспечения (при частом применении программы Setup.exe).

Некоторые инсталляторы можно запускать в так называемом «тихом режиме» для полной автоматизации установки. В этом режиме они не задают никаких вопросов и не просят нажимать кнопки «ОК>>, освобождая администратора от необходимости сидеть нянькой при инсталляторе. Если такой режим не поддерживается механизмом установки (а подобных случаев очень много), это сильно усложняет жизнь системного администратора. Win32: .Setupsup помогает справиться с такими трудностями. Он позволяет найти информацию о работающих процессах и работать с ними (либо завершить эти процессы, если вы того пожелаете).

Обратитесь к разделу «Информация о модулях из этой главы», чтобы узнать, как получить и установить модуль Win32: Используя Win32: :Setupsup, получить список выполняемых процессов очень просто. Вот слегка измененная версия примера, который можно увидеть в последнем разделе:

use Win32::Setupsup:

$machine = "";

получаем список на текущей ма^/не

Win32::Setupsup::GetProcessList

($machine, \@processlist. \@threaalist i a-die

"Ошибка получения списка процессов:

". Win32 :Serupsjjp: :GetLa;:: Ем^г(). ", '

pop(@processlist);

# удалить фальшивую запись, всегда

добавляемую к списку foreach Sprocesslist ((aprocesslist){

$pid = $processlist->{pid}:

$name = $processlist->{name};

write; }

format STDOUT_TOP =

Process ID Process Name

format STDOUT =

@<««« @««««««««««««««

$pid, $name

Завершение процессов тоже очень просто:

KillProcess($pid, Sexitvalule, Ssystemprocessflag) or die

"Невозможно завершить процесс:



".Win32: :Setupsup: ,GetLast.Error()

Два последних аргумента необязательны. Первый завершает процесс и, соответственно, устанавливает его код завершения (по умолчанию это 0). Второй аргумент позволяет вам завершать системные процессы (при условии, что у вас есть право Debug Prog rans).

Но все это очень скучно. Мы можем перейти на другой уровень манипулирования процессами, взаимодействуя с окнами, которые откры ты запущенным процессом. Чтобы вывести список окон, доступных на рабочем столе, применим:

Win32: :Setuosup: : EnuTiWiPdows( \awinduwiisi;) or die

Win32: :Setuosp: :GetUstError( @windowlist

теперь содержит список дескрипторов окон, которые выглядят как обычные числа, если их напечатать. Чтобы узнать больше о каждом окне, можно использовать несколько различных функций. Например, чтобы прочитать заголовки всех окон, воспользуемся функцией GetWindowText():

use Win32::Setupsup;

Win32::Setupsup::EnumWindOws(\@windowlist) or die

"Ошибка получения списка процессов:

".Win32::Setupsup::GetLastError()."\n"

foreach Swhandle (@windowlist){

if (Win32::Setupsup::GetWindowText($whandle,\$text)){

print "$whandle: $text","\n"; }

else { warn

"Невозможно получить текст для Swhandle" .

Win32::Setupsup::Get LastError()."\n"; >

}

Вот небольшой отрывок получаемых данных:

66130: chapter02 - Microsoft Word

66184: Style

194905150:

66634: setupsup - WordPad

65716: Fuel

328754: DDE Server Window

66652:

66646:

66632: OleMainThreadWndName

Как видите, у некоторых окон есть заголовки, а у некоторых их нет. Внимательные читатели могли заметить в этом отрывке еще кое-что любопытное. Окно 66130 принадлежит сеансу Microsoft Word, запущенному в настоящий момент (в нем набиралась эта глава). Окно 66184 смутно напоминает название еще одного окна, связанного с Microsoft Word. Как мы можем узнать, действительно ли эти окна взаимосвязаны?

В Win32: :Setupsup также есть функция EnumChildWindows(), которая позволяет вывести список дочерних окон для любого окна. Используем ее для вывода иерархии текущего окна:

use Win32::Setupsup:



# получаем список

Win32: : Setupsup: : FnunWindows(^.®windowlist) or

die "Ошибка получения процессов:

".Win32: 'Setiipsup: : Gf;: LastError(). "\n":

превращаем список дескрипторов окон в хэш

ЗАМЕЧАНИЕ: в результате преобразования

элементами хэиа становятся обычные числа,

а не дескрипторы окон. Некоторые функции,

например GetWindowProperties

(которую мы скоро рассмотрим),

не могут использовать эти преобразованные v/c.ra.

Будьте осторожны,

for (is>windowlist){$windowlist{$_}++;

}

и проверяем наличие дочерних окон для каждого окна

foreach $whandle (@windowlist){

(Win32: :Setupsup: : EnumChildWindows($whandIe. \ichildren)){

сохраняем отсортированный список дочерних окон

$children{$whandle} = [sort {$a <=>$b} ©children]:

tt удаляем все дочерние окна из главного хзша,

в результате всех итераций %windowlist будет

содержать только родительские окна без

соответствующих дочерних

foreach $child (@children){

delete $wir.dewlist{$child};

}

}

}

обходим в цикле список родительских окон

и тех окон, у которых нет дочерних,

и рекурсивно печатаем дескриптор каждого

окна и его дочерние окна (если они есть)

foreach my $window (sort {$a <=> $b} keys %windowlist){ &printfamily($window,0);

}

выводим дескриптор заданного окна и его дочерних окон

(рекурсивно) sub printfamily {

начальное окно, насколько глубоко мы ушли по дереву?

my($startwindow,Sievel) = @_;

выводим дескриптор окна с соответствующим отступом

print((" " х Slevel)."$startwindow\n").

return unless (exists $children{$startwindow>):

к дочерних окон не дело сделано

противном случае мы должны

roreach Schildwir.dow (@{

}

Есть еще одна функция, о которой надо сказать, перед тем как двигаться дальше: GetWindowPropertiesO. Функция GetWindowPropertiesO вмещает в себя остальные свойства окон. Например, используя GetWindowPropertiesO, можно получить идентификатор процесса, создавшего конкретное окно. Это разумно совместить с некоторыми из только что рассмотренных возможностей модуля Win32: : IProc.



В документации к модулю Win32: :Setupsup есть список свойств, и к ним можно обратиться. Используем одно из них для написания очень простой программы, которая выведет размеры окна на экране. GetWindowPropertiesO принимает три аргумента: дескриптор окна, ссылку на массив, содержащий имена запрашиваемых свойств, и ссылку на хэш, где будут храниться результаты запроса. Вот какой код мы применим для этого:

Win32: :Setupsup: :GetW:

ndowProperties($ARGV[0], ["reef, "id"], \%info);

print "\t" . $info{rect}{top} . "\n";

print $info{rect}{left} . " -" . $ARGV[0] .

"- " . $info{rect}{right} . "\n";

print "\t" , $info{rect}{bottom} . "\n";

Вывод получается несколько вычурным. Вот как выглядит вывод размеров (координат верхнего, левого, правого и нижнего края) окна с дескриптором 66180:

154

272 -66180- 903

595

GetWindowPropertiesO возвращает специальную структуру данных только для одного свойства rect. Все остальные будут представлены в хэше в виде обычных ключей и значений. Если вы не уверены в свойствах, возвращаемых Perl для конкретного окна, воспользуйтесь утилитой windowse, которую можно найти на http://greatis.virtualave.net/ products.htm.

Разве теперь, когда мы знаем, как определить различные свойства окон, не было бы логично научиться изменять некоторые из этих свойств? Например, было бы полезно изменять заголовок окна. С такими возможностями мы могли бы создавать сценарии, использующие заголовок окна в качестве индикатора состояния:

"Prestidigitation In Progress ... 32% complete" Чтобы внести эти изменения, достаточно одного вызова функции:

Win32::Setupsup:SetWindow

Text($handle,Stext);

Свойство rect тоже можно установить таким образом. Следующие строки заставляют указанное окно переместиться в заданную позицию экрана:

use Win32::Setupsup;

$info{rect}{left} = 0;

$info{rect}{nght} = 600;

$info{rect}{top} = 10;

$info{rect}{bottom}= 500;

Win32::Setupsup::SetWindow



Properties($ARGV[0], \%info);

Самую впечатляющую функцию я приберег напоследок. При помощи SendKeysO можно послать произвольные комбинации клавиш любому окну на рабочем столе. Например:

use Win32::Setupsup;

$texttosend = "\\DN\\Low in trie gums";

Win32::Setupsup::SendKeys($ARGV[0],Stexttosend, '',0);

В результате, в указанное окно будет послан текст, предваряемый символом «курсор вниз». Аргументы SendKeysO очень просты: дескриптор окна, посылаемый текст, флаг, определяющий, нужно ли активизировать окно для каждого сочетания клавиш, и необязательный интервал между сочетаниями клавиш. Коды специальных символов, таких как «курсор вниз», окружаются обратными слэшами. Список допустимых кодов можно найти в документации к модулю.

С помощью этого модуля мы попадаем на иной уровень управления процессами. Теперь мы можем удаленно управлять приложениями (и частями операционной системы), не взаимодействуя явно с этими приложениями. Нам не нужна поддержка командной строки или специальных API. У нас есть возможность писать сценарии для GUI, что очень полезно во множестве задач системного администрирования.



Использование инструментария управления окнами (Window Management Instrumentation, WMI)



Перед тем как перейти еще к одной операционной системе, рассмотрим последний подход к управлению процессами в NT/2000. Этот подход следовало бы назвать «Страной будущего», поскольку в нем используется пока еще не очень распространенная, но уже пробивающаяся технология. Инструментарий управления окнами (WMI) доступен в Windows 2000 (и в NT4.0 с установленным SP4+). Со временем, когда Windows 2000 широко распространится, WMI вполне может стать важной частью администрирования NT/2000.

К сожалению, WMI относится к числу технологий не для слабонервных, потому что очень быстро становится чересчур сложной. Она основана на объектно-ориентированной модели, которая позволяет представить не только данные, но и отношения между объектами. Например, можно создать связь между веб-сервером и дисковым массивом RAID, в котором хранятся данные с этого сервера, обеспечивающую, в случае неисправности массива RAID, сообщение и о проблеме с вебсервером. Не желая вдаваться во все эти сложности, мы дадим здесь лишь поверхностный обзор WMI в небольшом и простом введении, сопровождаемом примерами.



Если вы хотите познакомиться с этой технологией подробнее, я рекомендую загрузить документацию по WMI, обучающее руководство «LearnWBM» и WMI SDK из раздела «WMI» сайта http://msdn.microsoft.com /developer/sdk. Также взгляните на информацию, представленную на веб-сайте Distributed Management Task Force на http:// www.dtmf.org. Тем временем, начнем с краткого экскурса.

WMI - это реализация и расширение (от Microsoft) неудачно названной инициативы Web-Based Enterprise Management или WBEM. И хотя такое название вызывает в воображении что-то связанное с броузерами, эта технология не имеет практически ничего общего с World Wide Web. Компании, входившие в состав Distributed Management Task Force (DMTF), хотели придумать что-то, что могло бы упростить выполнение задач управления при помощи броузеров. Забыв про название, можно сказать, что WBEM определяет модель данных для информации управления. WBEM обеспечивает спецификацию для организации, доступа и перемещения этих данных. WBEM также предлагает связующий интерфейс для работы с данными, доступными из других протоколов, например, Simple Network Management Protocol (SNMP) (о котором мы будем говорить в главе 10 «Безопасность и наблюдение за сетью») и Common Management Information Protocol (CMIP).

Данные в WBEM организованы при помощи Общей информационной модели

(Common Information Model, CIM). CIM - источник силы и сложности в WBEM/WMI. Она предоставляет расширяемые модели данных, содержащие объекты и классы объектов для любой физической или логической сущности, которой можно захотеть управлять. Например, в ней есть классы объектов для целых сетей и объекты для отдельного слота машины. Существуют объекты для настроек как аппаратного обеспечения, так и приложений программного обеспечения. Помимо этого CIM позволяет определять классы объектов, описывающие связи между другими объектами.

Модель данных документирована в двух частях: в спецификации CIM и схеме CIM. Первая описывает, как (как данные определяются, их связь с другими стандартами управления и т. д.); вторая - что (т. е. сами объекты). Это деление может напомнить о связи SNMP SMI и МШ (подробный материал в главе 10).



На практике вы будете чаще обращаться к схеме CIM, чем к спецификации, когда вы освоитесь с тем, как представляются данные. Формат схемы, названный форматом управляемых объектов (Managed Object Format, MOF), довольно просто читать.

Схема CIM состоит из двух слоев:

  • Центральная модель (core model) для объектов и классов, полезна для всех типов взаимодействия WBEM.


  • Общая модель (common model) для объектов, которые не зависят от создателя и операционной системы. Внутри общей модели в настоящее время определены пять областей: системы, устройства, приложения, сети и физический уровень.


  • На вершине этих двух слоев может быть любое число расширенных схем (Extension schema), определяющих объекты и классы для информации, зависящей от создателя и информационной системы.

    Самая важная часть WMI, которая отличает ее от обычных реализаций WBEM, - это схема Win32, расширенная схема для информации, специфичной для Win32, построенная на центральной и общей модели. WMI также добавляется к общей структуре WBEM, обеспечивая механизмы доступа к данным CIM, специфичные для Win32. Используя это расширение схемы и набор методов доступа к данным, мы можем выяснить, как управлять процессами из Perl средствами WMI.

    Два из этих методов доступа: ODBC (открытый интерфейс взаимодействия с базами данных) и COM/DCOM (модель составных компонентов распределенная модель составных компонентов) будут более полно

    рассмотрены в других главах этой книги. В примерах будет использоваться модель COM/DCOM, поскольку ODBC разрешает лишь запрашивать информацию у WMI (хотя и в простой, похожей на присущую базам данных манере). COM/DCOM позволяет и запрашивать информацию, и взаимодействовать с ней, что очень важно для «управляющей» части контроля над процессами.

    Приведенные далее примеры программ на Perl не выглядят такими уж трудными, и вас могут удивить слова «очень быстро становится чересчур сложным». Приведенный ниже код выглядит простым, потому что:

  • Мы касаемся только поверхности WMI. Мы даже не затрагиваем таких понятий, как «ассоциации» (т. е. связи между объектами и классами объектов).




  • Мы выполняем только простые операции управления. Управление процессами в таком контексте состоит из опроса исполняемых процессов и возможности их завершения. Эти операции легко осуществляются в WMI при использовании расширения схемы Win32.


  • В наших примерах спрятана вся сложность перевода документации WMI и примеров программ с VBscript/JScript на Perl.


  • В примерах скрыта неясность процессов отладки. Когда код на Perl, имеющий отношений к WMI, завершается с ошибками, выдается очень мало информации, которая могла бы помочь найти их причину. Да, вы получите сообщения об ошибках, но в них никогда не будет сказано ОШИБКА: ПРОБЛЕМА ЗАКЛЮЧАЕТСЯ В СЛЕДУЮЩЕМ. .. Скорее всего, вы получите что-нибудь подобное wbemErrFailed 0x8004100 или вообще пустую структуру данных. Справедливости ради надо сказать, что большая часть такой неясности возникает благодаря участию Perl в этом процессе. Он действует в качестве интерфейса к целому ряду довольно сложных многоуровневых операций, которые не утруждают себя передачей содержательных сообщений в случае возникновения проблем.


  • Это звучит довольно мрачно. Поэтому позвольте предложить совет, воспользоваться которым стоит перед тем, как рассматривать сами примеры:

  • Изучите любые примеры, использующие модуль Win32. :OLE, которые сможете найти. Список рассылки Win32-Users на ActiveState и его архивы на http://www.activestate.com - хороший источник подобной информации. Если сравнить их с эквивалентными примерами на VBscript, то станут понятны необходимые идиомы трансляции. Кроме того, вам может помочь раздел «ADSI (Интерфейсы активных служб каталогов)» главы 6.


  • Подружитесь с отладчиком Perl и используйте его для тщательной проверки фрагментов кода в качестве части процесса обучения. Другой способ тестирования на платформе Win32 отрывков кода на Perl - применение программы TurboPerl Вильяма Смита (William P. Smith), ее можно найти на http://users.erols.com/turboperL/, совместно с модулями dumpvar.pl или Data: :Dumper. В ней бывают сбои (я советую чаще сохранять исправления), но обычно она может помочь в создании заготовок кода на Perl. Другие инструменты интегрированной среды разработки также могут обладать подобными возможностями.




  • Всегда держите под рукой копию WMI SDK. Его документация и примеры кода на VBscript очень полезны.


  • Чаще используйте броузер объектов WMI в WMI SDK. Он поможет вам разобраться со структурой.


  • Теперь перейдем к Perl. Первоначальная наша задача - определить, какую информацию о процессах в Win32 можно получить и как ее использовать.

    Сначала нужно установить соединение с пространством имен (names расе) WMI. Пространство имен определяется в WMI SDK как «единица для группировки классов и экземпляров для управления их областью действия и видимостью». Нам необходимо соединение с корнем стандартного пространства имен cimv2, в котором содержатся все интересующие нас данные.

    Кроме того, потребуется установить соединение с соответствующим уровнем привилегий. Программа должна иметь право отлаживать процесс и представлять нас; другими словами, она должна выполняться от имени пользователя, запустившего сценарий. Установленное соединение позволит получить объект Win32_Procoss (как это определяется в схеме Win32).

    Существуют как простой, так и сложный способы создать это соединение. В первом примере будут приведены оба способа, так что читатель сможет решить, чего стоит каждый из них. Вот сложный способ с объяснениями.

    use Win32::OLE('in1);

    Sserver = ''; 8 соединение с локальной машиной

    Я получаем объект SWbemLocator

    $lob] = Win32: :OLE->new

    ( 'WbemScripting, SWoe:riLocat:or')

    or die "Невозможно создать объект локатор: ' .Win32: :CLE->LasTf.-"rori

    и определяем, что сценарий выполняется с празам!'

    # используем зю для пслуче^'р объекта bWbe;rServ:c»^ Ssobj = $iol". ->

    or oie M^acjMcvhc создать ооье^.т сеов-' ".Win32 :OLt-.>LastErrcr (). \:V ,

    и получаем объект схемы

    Sprocschm = $sobj->Get('Win32_Process');

    Сложный способ включает в себя:

  • Получение объекта локатора, используемого для нахождения соединения с объектом-сервером


  • Установку прав, т. е. программа будет выполняться с нашими привилегиями


  • Использование этого объекта для получения соединения с ci/w2-пространством имен WMI




  • Применение этого соединения для получения объекта Win32_Process


  • Все это можно сделать за один шаг, если использовать COM moniker's display name. В соответствии с WMI SDK, «в модели составных объектов (СОМ) моникер - это стандартный механизм для инкапсуляции местоположения другого СОМ-объекта и связи с ним. Текстовое представление моникера называется отображаемым именем». Вот и простой способ в действии:

    usa Win32::OLECirT);

    Sprocschm = Win32::OLE->GetObject(

    'winmgmts: {impersonationLevel=impersonate}! Wiri32_Process ')

    or die "Невозможно создать объект сервера: ".Win32: :OLE->LastError()."\n":

    Теперь, когда у нас есть объект Win32_Process, можно с его помощью получить нужные части схемы, представляющие собой процессы в Win32. В их число входят все доступные свойства и методы Win32_Pro-cess, которые годятся к употреблению. Применяемая программа довольно проста; единственно, что не вполне очевидно, - это использование оператора in в Win32: :01_Е. Чтобы объяснить это, нам придется немного отклониться от темы.

    Объект $procschm имеет два специальных свойства: Properties и Methods. В каждом из них хранится специальный дочерний объект, известный как collection object в терминологии СОМ. Объект collection object является родительским контейнером для других объектов; в этом случае в них хранятся объекты описания свойств (Properties_) и методов (Methods) схемы. Оператор in возвращает массив ссылок на каждый дочерний объект контейнера. Располагая таким массивом, можно обойти все его элементы в цикле, возвращая на каждой итерации свойство Name каждого дочернего объекта. О других известных применениях in можно узнать из раздела «ADSI (Интерфейсы активных служб каталогов)» главы 6. Вот как выглядит сама программа:

    use Win32::OLE('in'):

    соединяемся с пространством имен, даем указание действовать

    с правами пользователя и получаем объект Win32_process,

    просто используя отображаемое имя

    Sprocschm = Win32 -OLE->GetObject(

    'winmgmtr, : {impersor,ationl_evel = impersonate} ' win32._Process )



    or die "Невозможно создать объект сервера: " .i/nn32' .OLE- > Last etc :().

    print "--- Prope-ties ---\n";

    print join("\n" , map {$_->{Name}

    }

    (in $procschm->{Properties_} )}:

    print "\n--- Methods ---\n";

    printoin("\n",map {$_->{Name}

    }

    (in $procschm->{Methods_l ;}:

    Вывод (на машине с NT4.0) выглядит примерно так:

    — Properties —

    Caption

    CreationCiassName

    CreationDate

    CSCreationClassName

    CSName

    Description

    ExecutablePath

    ExecutionState

    Handle

    InstallDate

    KernelModeTime

    MaximumWorkingSetSize

    MinimumWorkingSetSize

    Name

    OSCreationClassName

    OSNarne

    PageFaults

    PageFilellsage

    PeakPageFileUsage

    PeakWorkingSetSize

    Priority

    Processld

    QuotaNonPagedPoolUsage

    QuotaPagedPoolUsage

    QuotaPeakNonPagedPoolUsage

    QuotaPeakPagedPoolUsage

    Status

    TerminationDate

    User'ModeTime

    WindowsVersion

    WorkingSetSize

    --- Methods ---

    Create

    Terminate

    GetOwner

    GetOwrierSui

    Рассмотрим это подробнее. Чтобы получить список запущенных процессов, нужно запросить все экземпляры объектов Win32_Process:

    use Win32::OLE('in'):

    it выполняем все первоначальные шаги в одном цикле

    $sob] = Win32::OLE->GetOcject(

    'winmgmts:{impersonationLeveI=inpersorate}') or die

    "Невозможно создать объект сервера: ".Win32: :OLE->LastError() "V"

    foreach Sprocess (in $sobj->InstancesOf("Win32_Process")){

    print $process->{Name)." имеет pid #".$process->{Process!d}, "\n"; }

    Первоначальное отображаемое имя не включает путь к определенному объекту (т. е. мы отбросили ! Win32_Process). Итак, получен объект связи с сервером. В результате вызова метод InstancesOf () возвращает объект-коллекцию (collection object), который содержит все экземпляры конкретного объекта. Наш код обращается к каждому объекту и выводит его свойства Name и Processld. В итоге, у нас есть список всех запущенных процессов.

    Чуть менее великодушный подход к обойденным в цикле процессам позволил бы использовать один из методов, указанных в приведенном выше списке:

    foreach $process (in $sobj->InstancesOf ("Win32__Process")){

    $process->Terminate(1); }

    В результате, все работающие процессы будут завершены. Я не рекомендую вам запускать эту программу в таком виде; подправьте ее в соответствии с вашими нуждами, сделав более конкретной.

    Теперь у вас есть необходимые знания, чтобы начать использовать WMI для управления процессами. В WMI есть \Уш32-расширения для многих других частей операционной системы, включая реестр и журнал событий.

    Вот и все, что мы хотели сказать об управлении процессами в WinNT и 2000. Теперь перейдем к последней операционной системе.






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