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

         

Действия пользователей Управление



Управление процессами в MacOS

«Управление» - слишком громко сказано для функциональных возможностей, предоставляемых MacOS, поскольку последняя является не многопользовательской, а просто многозадачной операционной системой. Используя модуль Mac::Processes, можно взаимодействовать с менеджером процессов Macintosh (Macintosh Process Manager) при помощи MacOS Toolbox API для управления процессами. В случае частого применения этого модуля, стоит поискать руководство «Inside Ма-cintosh:Processes>> о работе с менеджером процессов.

При загрузке Mac: :Processes с помощью стандартной директивы i.sc Mac::Processes, инициализируется специальный хэш %Process. Этот хэш - магический, в нем всегда отображается состояние текущего процесса при помощи возможности Perl, именуемой «связанные переменные». Каждый раз при обращении к содержимому кэша возвращается информация о процессах, запущенных в настоящий момент в системе. Чтобы просмотреть список серийных номеров теку щих процессов (Process Serial Number , PSN - так в MacOS называются идентификаторы процессов), надо просто запросить список ключей этого хэша:

use Mac: Processes:

print map{"$_\n"} keys %Process:

Подробную информацию по любому процессу можно получить, пора ботав со значениями, возвращаемыми для каждого ключа. Каждая запись в хэше содержит объект, представляющий структуру. Чтобы получить поля этой структуры, необходимо вызвать соответствующие методы объекта. Более подробно о том, что представляет собой каждое поле, можно узнать в книге «Inside Macintosh:Processes». В настоящее время существуют методы processNumber (), processType(), processSignatureO, processSize(), procbssMoae(), processLocat.: -on(), processLauncher(),processLaunchDate(), processActiveTlmef)и processAppSpec().

Нетрудно вывести список запущенных процессов с их именами:

use Mac::Processes;

while(($psn, $psi) = each (%Process)){

$name = $psi->processName():

write; }

format STDOUT_TOP =

Process Serial Number Process Name

format STDOUT =

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

$psn, $name

Результат таков:

Process Serial Number Process Name

8192 FaxMonitor

8193 Queue Watcher

8194 Finder



8195 Serial Port Monitor 8198 MacPerl

Теперь, когда вы знаете, какие процессы у вас запущенны, совершенно естественно попытаться ими управлять. К сожалению, здесь практически ничего нельзя сделать. Самое большое, на что мы способны, - это перевести процесс в интерактивный режим при помощи SetFrontProcess($psn). У нас даже нет возможности напрямую его завершить (Perl-функция kill() не реализована). Лучшее, что можно сделать, послать выполняющемуся приложению событие AppleEvent, чтобы сообщить, что процесс должен быть завершен. Самый простой способ сделать это - применить модуль Mac: :Apps: : Launch Криса Нандора (Chris Nandor). В нем есть функция QuitApps(), которая позволяет завершить работу приложения, располагая его ID. В Мае:: Apes: . Launcn есть еще несколько полезных функций для запуска приложений и перевода их из/в интерактивный режим. Делается это так же, как и при использовании Мае: :Processes. Теперь наступает время операционной системы, в которой управление процессами менее ограничено.



Информация о модулях из этой главы


Получение и установка модуля Win32::1Ргос происходят несколько сложнее, чем бывает с другими модулями. Сам модуль вместе с остальными модулями Рамдэна можно найти на http://www.generation.net, -aminer/Perl/. Чтобы использовать Win32: : IProc, вам также понадобится загрузить еще два модуля: Win32: :ISync Рамдэна и Win32.: API Алдо Калпини (Aldo Calpini). Первый можно найти на сайте Рамдэна, второй в репозитории модулей ActiveState или на http://dada.perl.it/.

Некоторые из модулей Рамдэна устанавливаются вручную без помощи команды ррт и требуют небольших изменений исходного кода. Вот полный рецепт для установки. Я считаю, что вы распаковали дистрибутивы и собираетесь устанавливать их в Perl, скомпилированный ActiveState и установленный в каталоге C:\Perl:

ррт install Win32-API md c:\Perl\site\lib\auto\Win32\Sync и C:\Perl\site\lib\auto\Win32\ Iproc Скопируйте timer.dll и sync.dll в c:\Perl\site\lib\auto\Win32\Sync Скопируйте iprocnt.dll, psapi.dll и iproc.dll в C:\Perl\site\lib\auto\ Win32\Iproc Скопируйте iproc.pm, iipc.pm и isync.pm в C:\Perl\site\lib\Win32\ Измените строки DLLPath в iproc.pm на следующие:

my($DLLPath) ="C:\\Perl\\site\\lib\\auto\\Win32\\Iproc\\IProc,dll":

my($DLLPath1)="C:\\Perl\\site\\lib\\auto\\Win32\\Iproc\\IprocNT.dll";

my($DLLPath2)="C:\\Perl\\site\\lib\\auto\\Win32\\Sync\\Sync.dll";

Установка Win32::Setupsup

Если вы хотите установить модуль Win32: :Setunsi:p вручную и/или изучить его исходный код, вы можете найти ZIP-архив модуля на ftp: ftp.roth.net/pub/NTPerl/Others/SetupSup/. Если же вы предпочитаете установить его простым способом в существующий ActiveState, то можете соединиться с архивом модулей Йенды Крыницки (Jenda Kry-nicky) и установить его, используя обычный способ ррт. Инструкции о том, как это сделать, можно найти на сайте http:/ /Jenda.Krynicky.c:.

Сложность в том, что документация в формате pod неверно форматируется, если вызывать ее при помощи perldoc или устанавливать в HTML. Документация в конце setupsup.pm (вероятнее всего, вы найдете ее в <ваш каталог Perl \site\lib\Win32\) гораздо более верная. Если вы попытаетесь узнать, как использовать этот модуль, я советую открыть сам файл в обычном текстовом редакторе и просмотреть те части, которые являются документацией.



Использование файловой системы /ргос



Использование файловой системы /ргос

В большинстве современных вариантов Unix существует интересное добавление - файловая система /ргос. Эта загадочная файловая система не имеет ничего общего с хранением данных. Она обеспечивает «файлоподобный» интерфейс к таблице запущенных процессов. Для каждого из них в этой файловой системе существует «каталог», название которого совпадает с идентификатором процесса. В этом каталоге есть целый ряд «файлов», предоставляющих информацию о данном процессе. В один из этих файлов разрешена запись, что и позволяет управлять самим процессом.

Это действительно мудрая идея и это замечательно. Плохо то, что каждый производитель/команда разработчиков, поддержав эту мудрую концепцию, разбежались каждый в своем направлении. В результате файлы, которые можно найти в каталогах /ргос, часто специфичны для различных вариантов операционной системы, отличаясь как по именам, так и по формату. Описание того, какие файлы доступны и что в них хранится, вам придется искать на страницах руководства (обычно в разделах 4, 5 или 8) по ргосfs или mount jjrocfs.

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

opendir(PROC, "/proc") or die

"Невозможно открыть /proc:$!\n";

while (defined($_= readdir(PROC))){

next if ($_ eq " . " or $_ eq "..");

next unless /"\d+$/;

# отфильтровываем все случайные файлы, названия

# которых могут являться идентификаторами процессов

print "$_\t". getpwjid((lstat "/Ргос/$_" )[4] ) , "\n";

> closedir(PROC);

Для того чтобы получить подробную информацию о процессе, следует открыть нужный двоичный файл из каталогов в /ргос и воспользоваться функцией unpack(). Обычно это файл status или psinfo. На страницах только что упомянутых руководств есть подробная информация о С-структуре, которую можно найти в этом файле, или, по крайней мере, есть ссылка на включаемый (include) файл, в котором эта структура документирована. Поскольку эти форматы зависят от операционной системы (и версии ОС), вы снова столкнетесь с проблемами переносимости программы.

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



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



Использование инструментария управления окнами (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. Теперь перейдем к последней операционной системе.



Использование модуля Proc ProcessTable



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

Дэниел Дж. Урист (Daniel J. Urist) (с помощью нескольких добровольцев) написал модуль Proc: :ProcessTable, предоставляющий единый интерфейс к таблице процессов для всех основных вариантов операционной системы Unix. Он скрывает от вас причуды различных реализаций /ргос и kmem, позволяя писать более переносимые программы.

Просто загрузите модуль, создайте объект Ргос: :ProcessTable: :Proces-и используйте методы этого объекта:

use Proc: :ProcessTable;

$tobj = new Proc: : ProcessTable;

Этот объект использует механизм связанных переменных (tied variable) для представления текущего состояния системы. Для обновления! этого объекта не требуется вызывать специальную функцию - он перечитывает таблицу процессов при каждом обращении к нему. Это похоже на хэш %Process, знакомый нам по обсуждению модуля Мае : Р ses ранее в этой главе.

Чтобы получить нужную информацию, следует вызвать метод

: : Sproctable = $tobj->table();

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

use Proc::ProcessTable:

Stobj = new Proc::ProcessTable:

Sproctable = $tobj->table(); for (ia>$proctable){

print $_->pid."\t". getpwuid($_->uid)."\n";

}

Список методов, доступных в вашей операционной системе, можно получить при помощи метода fields() объекта Proc: : ProcessTable (т.е. $tobj).

В Proc:: ProcessTable также есть три дополнительных метода у каждого объекта процесса: kill(), priorityO и pgrp(), которые являются всего лишь интерфейсом к встроенным функциям, упомянутым в начале этого раздела.

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

Первый пример из документации по Proc: : ProcessTable: use Proc::ProcessTable;

$t = new Proc::ProcessTable; foreach $p ((5>{$t->table}){

if ($p->pctmem > 95){ $p->kill(9);

}

}

Эта программа «отстреливает» все процессы, занимающие 95% памяти в тех вариантах операционной системы Unix, где поддерживается метод pctmem() (а он поддерживается в большинстве случаев). В таком виде пример, вероятно, слишком «безжалостен», чтобы использовать его в реальной жизни. Было бы благоразумно добавить перед командой kill() что-то подобное:

print "собираемся убрят;. " Sn-^pid. "\t". get. owuid($p->uid).

"Vi": print "выполнять9 (yes'''i'0 " chomp($ans = о):

next unless (Sans eq "yes"):

Здесь может возникнуть состояние перехвата: не исключено, что во время задержки, вызванной вопросом к пользователю, состояние системы изменится. Учитывая, что мы в данном случае работаем только с «большими» процессами, которые вряд ли меняют свое состояние в течение короткого времени, такой вариант, скорее всего, пройдет нормально. Если вы хотите подойти к этому вопросу более педантично, вам, наверное, стоит получить сначала список процессов, которые вы хотите завершить, спросить пользователя, а затем проверить еще раз состояние таблицы процессов и только потом их завершать.

Бывают случаи, когда завершение процесса - это слишком легкая расплата. Иногда важно засечь, что процесс действительно работает, чтобы предпринять необходимые меры (скажем, поставить пользователя на место). Например, политика нашего сайта запрещает применять IRC-роботы. Роботы - это процессы-демоны, которые соединяются с IRC-серверами и выполняют автоматические действия. И хотя роботы могут использоваться в благих целях, в настоящее время они, в основном, играют асоциальную роль в IRC. Кроме того, мы обращали внимание на взлом системы безопасности из-за того, что первое (и часто единственное), что делал взломщик, — это запускал IRC-робота. Короче говоря, нам важно заметить присутствие таких процессов, а не завершать их работу.

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

use Proc::ProcessTable;

open(LOG, "»$logf ile") or die

"Невозможно открыть журнал для дозаписи:

$t = new Proc::ProcessTable; foreach $p (@{$t->table})

{ if ($p->fname() =" /eggdrop/i){ print LOG time."\t".

getpwuid($p->uid).

"\t".$p->fname()."\n": >

}

close(LOG);

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

А пока рассмотрим еще один пример, в котором Perl помогает управлять процессами пользователей. До сих пор все наши примеры касались отрицательных явлений. Рассмотренные программы имели дело со злонамеренными или жадными к ресурсам процессами. Теперь посмотрим на что-нибудь более жизнерадостное.

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

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

Если точность не важна и достаточно знать только приблизительную оценку набора работающих программ, можно применить Ргос::Рго-cessTable. Ниже приведена программа, которая активизируется каждые пять минут и проверяет состояние текущих процессов. Она просто ведет учет всех найденных имен процессов, причем те процессы, которые встречались в предыдущий раз, она во второй раз не учитывает. Ежечасно программа записывает результаты и начинает подсчет заново. Пятиминутное ожидание объясняется тем, что обход таблицы процессов является ресурсоемкой операцией (обычно), а мы хотим, чтобы эта программа как можно меньше загружала систему:

use Proc::ProcessTable;

Sinterval = 600;

5 минут перерыва

Spartofhour = 0; (f отмечаем позицию часа, в которой мы находимся

Stop] = new Proc: : ProcessTabie;

создаем човый объект

tt вечный цикл, сбор данных каждые Sintervai секунд

№ и сброс этих данных один раз в час

while(1){

ucollectstats;

&dumpandreset if (Spartofhour >= 3600);

sleep($interval), }

сбор статистики по пролессу sub collectsrars ( my($process);

foreach Sprocess (@{$tobj->table}){

мы должны игнорировать себя next if ($process->pid() == $$);

сохраняем информацию о процессе для следующего запуска

push(@last,Sprocess->pid(),$process->fname());

игнорируем процесс, если мы его уже видели

next if ($last{$process->pid()} eq $process->fname());

если не видели, запоминаем его

$collection{$process->fname()}++; }

и устанавливаем хэш %last, используя текущую таблицу %last = ©last;

Spartofhour += $interval; }

выводим результаты и сбрасываем значения счетчиков

sub dumpandreset{

print scalar localtime(time). C'-"x50),"\n";

for (sort reverse_value_sort keys %collection){ write;

undef %collection; Spartofhour = 0; }

(обратная) сортировка no значениям хэша %collection и по

именам ключей

sub reverse_value_sort{

return $collection{$b} <=> $collection{$a} || $a cmp $b;

}

format STDOUT = @««««« »»

$_, $collection{$._}

format STDOUT_TOP = Name Count

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



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



Использование модуля 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

Если упоминание о манипуляции окнами процесса, приведенное в конце предыдущего раздела, возбудило ваше любопытство, вам понравится наш следующий подход. На этот раз мы рассмотрим модуль 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, что очень полезно во множестве задач системного администрирования.



Изучение структуры ядра



Изучение структуры ядра

Я упомянул эту возможность только для полноты картины. Можно написать программу, которая будет открывать устройство, подобное /dev/ kmem, и обращаться к структурам памяти ядра. Таким образом можно добраться до текущей таблицы процессов и прочитать ее. Учитывая, что сделать это трудно (разобраться вручную в сложной двоичной структуре), а полученный результат не будет обладать абсолютно никакой переносимостью (любое изменение даже версии операционной системы сделает, скорее всего, вашу программу неработоспособной), я настоятельно рекомендую не пользоваться такой возможностью.

Тем, кто все же не прислушается к этому совету, придется вспомнить документацию по функциям pack(), unpack() и заголовочным файлам для вашего ядра. Откройте файл памяти ядра (часто это /dev/kmem), затем выполняйте read() и unpack(). Вам может понадобиться изучить исходники таких программ, как top (ищите на ftp://ftp.groupsys.com/ pub/top), выполняющих эти же задачи, используя большое количество кода на С. В следующем разделе мы рассмотрим слегка улучшенную версию этого метода.



Отслеживание операций с файлами


Попытка найти файлы, открытые другими пользователями, вернее всего сработает, если применять программу, работающую в командной строке, - nthandle Марка Руссиновича (Mark Russinovich), ее можно найти на http://www.sysinternals.com. Она позволяет показать все открытые дескрипторы на определенной системе. Вот как выглядит ее вывод:

System pid: 2

10: File C:\WINNT\SYSTEM32\CONFIG\SECURITY

84: File C:\WINNT\SYSTEM32\CONFIG\SAM.LOG

cc: File C:\WINNT\SYSTEM32\CONFIG\SYSrEM

dO: File C:\WINNT\SYSTEM32\CONFIG\SECURITY.LOG

d4: File C:\WINNT\SYSTEM32\CONFIG\DEFAULT

e8: File C:\WINNT\SYSTEM32\CONFIG\SYSTEM.ALT

fc: File C:\WINNT\SYSTEM32\CONFIG4SOFTWARE.LOG

118: File C:\WINNT\SYSTEM32\CONFIG\SAM

128: File C:\pagefile.sys

134: File C:\WINNT\SYSTEM32\CONFIG\DEFAULT. LOG

154: File С:\WNNT\3YSTEM32\CON'FIG;'SOFTWARE

1bO: File \3evice\NafiedPipe\

294: File C:\WINNT\PROFILES\Adnirustrator\ntLSer.aa-.; OG

2a4: File C:\WINNT\PROFILES\AdminisTrator\NTUSEH.DAT

SMSS.EXE pid: 27 (NT AUTHORITY:SYSTEM)

4: Section С:\WINNT\SYSTEM32\5MSS.EXE

c: File С'\WINNT

28: File C:\WINNT\SYSTEM

Можно также запросить информацию по конкретным файлам и каталогам:

> nthandle c:\temp

Handle V1.11

Copyright (С) 1997 Mark Russinovich

http://www.sysinternals.com

WINWORD.EXE pid: 652

C:\TEMP\~DFF2B3.tmp WINWORD.EXE pid: 652

C:\TEMP\~DFA773.tmp WINWORD.EXE pid: 652

C:\TEMP\~DF9l3E.tmp

Программа nthandle позволяет получить эту информацию по конкретному процессу при помощи ключа -р.

Использовать ее из Perl очень просто, поэтому не будем приводить примеры. Вместо этого посмотрим на подобную, но более интересную операцию - аудит.

NT/2000 позволяет эффективно отслеживать изменения в файлах, каталогах или иерархии каталогов. Вы могли бы учитывать постоянное повторение операции stat() над нужным объектом, но это потребовало бы слишком больших затрат процессорного времени. В NT/2000 отслеживание изменений можно поручить операционной системе.

Относительно безболезненно эту работу выполняют два модуля: Win32: :ChangeNotify Кристофера Мадсена (Christopher J. Madsen) и Win32: :AdvNotify Амина Мюлей Рамдана (Amine Moulay Ramdane). В примерах этого раздела будет использоваться второй, т. к. он более гибкий.

Работа с модулем Win32: : AdvNotify- это многошаговый процесс.

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

Sthread = $aob]->StartThread(Directory => 'C:\terr.c'.

Filter => All, WatchSubtree -> 0) or die "Невозможно начать поток\п":

Первый параметр этого метода говорит сам за себя; рассмотрим остальные.

Установив Filter в одно из приведенных значений (табл. 4.1) или в их комбинацию (SETTING 1 | SETTING2 | SETTINGS. ..), можно следить за различными типами изменений.



Отслеживание операций в Unix



Отслеживание операций в Unix

Для отслеживания операций с файлами и сетью в Unix можно использовать один и тот же подход. Это один из тех редких случаев, когда вызов внешней программы намного предпочтительней. Вик Абель (Vic Abell) преподнес чудесный подарок системным администраторам, написав программу Isof (LiSt Open Files), которую можно найти на ftp:// vic.cc.purdue.edu/pub/tools/unix/lsof. Isof позволяет отобразить подробную информацию об открытых в настоящий момент файлах и сетевых соединениях на Unix-машине. По-настоящему удивительной эту программу делает ее переносимость. Последняя версия программы (на момент написания этой книги) работает по крайней мере на 18 видах Unix и поддерживает различные версии этих операционных систем.

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

COMMAND PID USER FO TYPE DEVICE SIZE/OFF NODE NAME

netscape 21065 dno cwd VOIR 172,289"; 8192 12129 /-OTie

netscape 21065 dnb txt VREG 172,1246 1438236Д 656749

/net/ arch-solans (fileserver-./vol/systems/arch-solaris)

netscape 21065 dnb txt VREG 32,6 54656 35172

/usr (,/dev/ dsk/cOtOdOs6)

netscape 21065 dnb txt VREG 32;6 146740 6321

/ubr/lro/ libelf.so.1

netscape 21065 dnb txt VREG 32,6 69292 102611

/usr (/dev/ dsk/cOtOdOs6)

netscape 21065 dnb txt VREG 32,6 21376 79751

/usr/iib/ locale/en_US/en_US.so.1

netscape 21065 dnb txt VREG 32,6 19304 5804

/usrЛib/ libmp.so.2

netscape 21065 dnb txt VREG 32,6 98284 22860

usr/onenwi:' lib/libICE.so.6

netscape 21065 dnb txt VREG 32,6 46576 22891

/usr/opftrwiv lib/libSM.so.6

netscape 21065 drib txt VREG 32.6 1014020 5810

/!jsr/::.t; libc.so.1

netscape 21065 dnb txt VREG 32.6 105788 5849

/usr/ln; libm.so.1

netscape 21065 dnb txt VREG 32,6 721924 5806

netscape 21065 ar.b txt VREG 32 6 156196 5774

netscape 21065 ri^t 0. VCHP 2-1.3 Ot73 5853 > .

pseudo/bls@C:3-> ttcoirpat ->lcter"b>:neii->pis netscape 21065 dnb 3u VCHR 13,12 oto 5821

/devices/ pseudo/mm@0:zero

netscape 21065 dnb 7u FIFO Ox6034d264 C;1 47151 PIPE-> Ox6034d1eO

netscape 21065 dnb 8u met Ox6084cb68 Oxfb210er, TCP host. cos.

ne^.. edu:46575->host2.ccs,neu. edu:6000 (ESTABLISHED)

netscape 21065 dnb 29u met 0x60642848 Ot215868 TCP nost, ccs. re.. edu:46758->

www.mind-bright.se:80 (CLOSE_ WAIT)

Из этого примера можно понять, насколько мощна эта команда. Мы можем увидеть текущий рабочий каталог (VDIR), обычные файлы (VREG), символьные устройства (VCHR), каналы (FIFO) и сетевые соединения (inet), открытые этим процессом.

Самый простой способ применить программу Isof из Perl - вызвать ее в специальном режиме «field» (-F). В этом режиме вывод программы делится на специальным образом отмеченные и разделенные поля, вместо использования колонок в стиле ps. Это позволяет надежно проанализировать и распознать вывод.

У этого способа вывода результатов есть одна особенность. Вывод организован в виде «наборов процессов» (process sets) и «наборов файлов» (file sets), как их называет автор. Набор процессов - это набор полей, относящихся к одному процессу; набор файлов - это подобный же набор для файла. Все приобретет больший смысл, если включить режим разбивки на поля с параметром 0. В этом случае поля будут разделены символом NUL (ASCII 0), а наборы - символом NL (ASCII 12). Вот как будет выглядеть предыдущий вариант вывода команды, если использовать режим разбивки на поля (NUL представлен в виде символов ~@):

p21065~@cnetscape~@u6700"@Ldnb~@

fcwd"@a ~@1 "@tVDIR"@DOx2bOOb4b"@s8192"@i12129"@n/home/dnb"@

ftxt"@a ~@1 >tVREG"@DOx2b004de"@s14382364"@i656749"@n/net/arch-solaris (fileserver:/vol/systems/arch-solaris)"@

ftxt~@a "@1 ~@tVREG"(5)DOx800006~@s54656~@i35172~@n/usr (/dev/dsk/cOtOdOs6)"@ ftxt"@a "@1 "@tVREG"@OOx800006"@s146740"@i6321"@n/usr/lib/libelf.so.1"@ ftxt"@a "@1 "@tVREG"@DOx800006"@s40184"@i6089"ian/usr (/dev/dsk/cOtOdOs6)"@ ftxt"(5>a "@1 "@tVREG"@DOx800006"@s69292"@i102611"@n/usr (/dev/dsk/cOtOdOs6)"@

ftxt"@a ~@1 •@tVREG"@DOx800006"@s21376"@i79751"@n/usr/lib/locale/en_US/ en_US.so.1"@

ftxt"@a ~@1 "@tVREG"№Ox800006"@s19304"@i5804"@n/usr/lib/libmp. so. 2"@

ftxt"@a "@1 ~@tVREG"@DOx800006"@s98284"@i22860"@n/L>sr/openwin/lib/ libICE.so.6"@ ftxt~@a ~@1 "@tVREG"@DOx800006"@s46576"@i22891"@n/usr/openwin/lib/ libSM.so.6"<°>

ftxt"@a ~@1 "@tVREG"@DOx800006"@s1014020"@i5810"@n/usr/lib/libc.so.1"@ ftxt~@a ~@1 ~@tVREG~№Ox800006~@s105788~@i5849~<a>n/usr/lib/libm. so. 1"@ ftxt"@a "@1 "§tVREG"@DOx800006'@s721924"@i5806"@n/usr/lib/libnsl.so.Г@

ftxt"@a "@1 "@tVREG"@DOx800006"§s166196"@i5774"@n/usr/lib/ld.so.Г@ fO"@au~@l "@tVCHR"@DOx600003"iao73~(ai5863"(an/devices/pseudo/ pts@0:3->ttcompat->ldterm->ptem->pts"@

f3"@au"@l "@tVCHR"@DOx34000c"@oO"§i5821"@n/devices/pseudo/mm(s)0:zero"(8i f7"@au"@l "@tFIFO"@dOx6034d264"@o1"@i47151"@nPIPE->Ox6034d1eO"@

f8"@au"@l "@tinet"@dOx6084cb68"@o270380692"(9>PTCP''@nhost.ccs.neu.edu:46575-> host2.cos.neu.edu: 6000"@TST=ESTABLISHED~@

f29"@au"@l "@tinet"@dOx60642848"@o215868~@PTCP"@nhost.ccs.neu.edu:46758-> www.mindbright.se: 80"(a>TST=CLOSE_WAIT~@

Давайте разберемся с этими данными. Первая строка - это набор процессов (это можно понять по первому символу р):

p21065~(acnetscape~@u6700~@Ldnb~(a

Каждое поле начинается с буквы, определяющей его содержимое (о -идентификатор процесса (pid), с - команда, и - идентификатор пользователя (uid) и L - имя пользователя (login)), и заканчивается символом разделителя. Все поля в строке создают набор процесса. Все последующие строки вплоть до очередного набора процесса описывают открытые файлы/сетевые соединения процесса, описываемого своим набором.

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

use Text::Wrap;

Slsofexec = "/usr/local/Din/lsof"; 8 путь к Isof

8 режим (F)ield, разделитель NUL (0), показывать (L)ogin,

8 тип файла (t)ype и имя файла (n)ame

$lsofflag = "-FLOtn";

open(LSOF,"$lsofexec $lsofflag[") or

die "Невозможно запустить $lsofexec:$!\n";

while(<LSOF>){

8 работаем с набором процесса if (substr($_,0,1) eq "p"){

($pid,$login) = split(/\0/);

$pid = substr($pid,1,length(Spid)); }

# работаем с набором файла.

# Замечание: мы интересуемся только обычными файлами

if (substr($_,0,5) eq "tVREG"){

($type,$pathname) = split(/\0/);

# процесс может дважды открыть один и тот же файл,

# поэтому мы должны убедиться, что запишем его

# только один раз

next if ($seen{$pathname} eq $pid); $seen{$pathname} = $pid;

Spathname = substr($pathname,1,length($pathname));

push(@{$paths{$pathname}},$pid);

}

close(LSOF);

for (sort keys %paths){

print "$_:\n";

print wrap("\t","\t",join(" ",@{$paths{$_}})),"\n";

}

В этом случае Isof будет показывать только некоторые из полей. Можно обойти в цикле весь вывод, собирая имена файлов и идентификаторы процессов в хэш списков. Когда будут обработаны все выведенные данные, следует ввести имена файлов в виде отформатированного списка процессов (спасибо Дэвиду Шарноффу (David Muir Sharnof f) за модуль Text: :Wrap):

/usr (/dev/dsk/cOtOdOs6):

115 117 128 145 150 152 167 171 184 191 200 222 232 238

247 251 276 285 286 292 293 296 297 298 4244 4709 4991

4993 14697 20946 21065 24530 25080 27266 27603

/usr/bin/tcsh:

4246 4249 5159 14699 20949

/usr/bin/zsh:

24532 25082 27292 27564

/usr/dt/lib/libXm.so.S: 21065 21080

/usr/lib/ld.so.1:

115 117 128 145 150 152 167 171 184 191 200 222 232 238

247 251 267 276 285 286 292 293 296 297 298 4244 4246 4249 4709 4991

4993 5159 14697 14699 20946 20949 21065

21080 24530 24532 25080 25082 25947 27266 27273 27291

27292 27306 27307 27308 27563 27564 27603

/usr/lib/libc.so.1:

267 4244 4246 4249 4991 4993 5159 14697 14699 20949

21065 21080 24530 24532 25080 25082 25947 27273 27291

27292 27306 27307 27308 27563 27564 27603

Чтобы показать последний код, относящийся к отслеживанию операций с файлами и сетью в Unix, вернемся к поиску запущенных IRC-po-ботов из приведенного ранее примера. Существует более надежный способ найти такие процессы-демоны, чем изучение таблицы процессов. Пользователь может скрыть имя робота, переименовав исполняемый файл, но для того чтобы спрятать сетевое соединение, ему придется очень хорошо потрудиться. В большинстве случаев это соединение с сервером на портах 6660-7000. Программа Isof позволяет без труда отыскивать такие процессы:

Slsofexec = "/usr/local/bin/lsof" Slsofflag = "-FLOc -iTCP: 6660-7000";

ft это срез хэша. используемый для предварительной загрузки

и таблицы хэшей, существование этих ключей мы будем проверять

и позже. Обычно это записывается так:

ft %approvedclients = ("ircll" => undef, "xirc" => undef, ...);

ft (но такой вариант - отличная идея, воплощенная Марком-

ft Джейсоном Доминусом(МагК-иа50п Dominus))

@>approvedclients{"ircH" , "xirc" , "pirc"} = ();

open(LSOF, "$lsofexec $lsofflag|") or die "Невозможно запустить $lsofexec:$! \n" ;

while(<LSOF>){

($pid,$command,$login) = /p(\d+)\000

c(.+)\000 L(\w+)\000/x;

warn "$login использует неразрешенный клиент с именем

$conimand (pid $pid)l\n" unless (exisTs $approvedclients{$command});

close(LSOF);

Это самая простая проверка из всех возможных. Она позволяет отловить тех, кто догадается переименовать eggdrop б pine или -tcsh, и тем более тех, кто даже не попытается спрятать своего робота. Тем не менее, она подвержена тому же недостатку, что и предыдущий вариант тестирования. Если пользователь достаточно умен, он может переименовать робота во что-то из списка «разрешенных клиентов». Чтобы продолжить игру в кошки-мышки, можно предпринять еще как минимум два шага:

Запустить Isof для проверки того, что файл, открытый для данной исполняемой программы, известен как тот, который мог быть открыт этой программой, а не произвольный файл из файловой системы. Применить методы управления процессами для проверки того, что пользователь запускает программу из существующего интерпретатора. Если это единственный процесс, запущенный пользователем (т. е. если пользователь оставил его, а сам завершил работу), он, вероятно, является демоном, а значит и роботом.

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



Рекомендуемая дополнительная литература



Рекомендуемая дополнительная литература

http://pudget.net/macperl -

домашняя страница модулей Криса Нан-дора (Chris Nandor). Нандор один из самых активных разработчиков модулей MacPerl (и соавтор книги, ссылка на которую приведена ниже).

http://www.activestate.com/ support /mailing_lists.htm.

Здесь находятся списки рассылки Perl-Win32 Admin и Perl-Win32-Users. Оба списка и их архивы - просто бесценные ресурсы для программистов Win32.

http://www.microsoft.com/management

- домашняя страница всех технологий управления Microsoft, включая WMI.

http://www.sysinternals.com -

домашняя страница программы nthan-dle (на этом сайте она называется просто Handle) и многих других полезных утилит для NT/2000. На родственном сайте http:// www.winternals.com продаются отличные коммерческие утилиты.

«MacPerl:Power and Ease»,

Vicki Brown, Chris Nandor (Prime Time Freeware, 1998) - лучшая книга о модулях для MacPerl. Стоит также обратить внимание на веб-сайт издателя http://www.macperl.com.

http://www.dmtf.org-

домашняя страница Distributed Management Task Force и просто хороший источник информации по WBEM.

http://www.mspress.com -

издатели Microsoft NT Resource Kit. Можно зарегистрироваться и получить доступ к последним утилитам из RK.

Параметры Filter в Win32 AdvNotifу



Таблица 4.1. Параметры Filter в Win32::AdvNotifу

Параметр Отмечает
FILE_NAME Создание, удаление, переименование файла(ов)
DIR_NAME Создание или удаление каталога(ов)
ATTRIBUTES Изменение атрибутов любого каталога
SIZE Изменение размера любого файла
LAST_ WRITE Изменение даты модификации файла(ов)
CREATION Изменение даты создания файла(ов)
SECURITY Изменение информации безопасности (ACL и пр.) файла(ов)

Значение АИ из приведенного примера- это всего лишь постоянная, объединяющая все варианты выбора. Если не указать параметр Filter при вызове метода, то по умолчанию будет использоваться АИ. Параметр WatchSubtree определяет, необходимо ли следить только за указанным каталогом или за каталогом и всеми его подкаталогами.

StartThreadO создает поток, но проверка начинается только после того, как поступает распоряжение об этом:

$thread->EnableWatch() or die

"Невозможно начать наблюдение\п";

Существует также функция OisableWatch(), которую необходимо использовать в программе для завершения проверки.

Мы следим за нужным объектом, но как узнать, изменилось ли что-нибудь? Надо придумать что-то, что позволило бы потоку сообщить нам об изменениях, за которыми мы наблюдаем. Здесь тот же подход, что и в главе 9 «Журналы» при обсуждении сетевых сокетов. Обычно следует вызывать функции, которые заблокированы до тех пор, пока ничего не происходит:

while($thread->Wait(INFINITE)){

print "Что-то изменилось1\п":

last if ($changes++ == 5):

}

Этот цикл while() вызовет метод Wait() для нашего потока. До тех пор пока потоку нечего сообщить, вызов будет заблокирован. Обычно Wair() принимает в качестве параметра число миллисекунд, равное времени ожидания. Мы же передаем специальное значение, которое соответствует «бесконечному ожиданию». Когда Wait() возвращает значение, следует вывести сообщение и ждать дальше, если только уже не были замечены пять других изменений. Теперь можно закончить:

$thread->Terminnte() undef $aobj;

Наша программа пока еще не очень полезна. Нам известно, что что-то изменилось, но мы не знаем ни что изменилось, ни как это произошло. Чтобы исправить эту ситуацию, изменим тело цикла wniie() и добавим определение формата:

while($thread->Wait(INFINITE)){

while ($thread->Read(\@status)){ foreach Sevent (@status){

Sfilename = $event->{FileName); $time = $event->{DateTime};

Saction = $ActionName{$event->{Action}}; write; } } }

format STDOUT =

@««««««««« @««««««««« @«««««««««

Sfilename,$time,Saction

format STDOUT_TOP =

File Name Date Action

Основное изменение- это добавление метода Read(). Он получает информацию об изменении и заполняет элементы списка ©status ссылками на хэш. Каждая ссылка указывает на анонимный хэш, который выглядит примерно так:

{'FileName' => "GLF2425.TMP',

'DateTime' => '11/08/1999 06:23:25р',

'Directory' => 'C:\temp', 'Action' => 3 }

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

File Name Date Acti01

"DF400E.tmp 11/08/1999 07:29:56p

FILE_.ACTION_PEMO\,'F.D

"DF6C5C. fnp 11/08/1999 07'29'56p

F1LF_AC1ION_ADDED

~OF6E66.tmp 11/08/1999 07:29:56р

FILE_ACTION_ADOED

~DF6E5C.tmp 11/08/1999 07:29:56р

FILE_ACriON_REMOVEO

К сожалению, отслеживание операций с сетью в NT/2000 впечатляет намного меньше. В идеале, как администратор вы хотели бы знать, какой процесс (а, следовательно, какой пользователь) открыл сетевой порт. Печально, но я не знаю ни одного модуля и ни одного инструмента, которые могли бы предоставить такую информацию. Существует один коммерческий инструмент, работающий в командной строке, под названием TCPVstat, который может показать связь процессов с использованием сети. TCPVstat можно найти в пакете TCPView Professional Edition, который доступен на http://www.winternals.com.

Если использовать только некоммерческие инструменты, то придется иметь дело лишь со списком сетевых портов, открытых в настоящее время. Для этого следует применять другой модуль Рамдана -Win32: :1рНе1р. Вот как выглядит код, печатающий нужную информацию:

use Win32: -.IpHelp;

# замечание: в данном случае регистр "IpHelp"

имеет значение' my $iobj = new Win32::IpHelp;

# заполняем список хэшем хэшей $iobj->GetTcpTable(\(a>table,1);

foreach Sentry (@table){

print $entry->{LocalIP}-><Value( . ":" .

$entry->{LocalPort}->{ValLie}. " -> ";

print $entry->{fiemoteIP}->{Value} . ":" .

Sentry->{RemotePort}->{Value}."\n";

}

Посмотрим, как можно сделать то же самое в Unix.



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



Управление процессами в NT/2000

Мы вкратце рассмотрим четыре различных способа работы с процессами в 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-функций - он завершит работу процесса с особенной предвзятостью.

Управление процессами в Unix Стратегии


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

Еще более неприятная проблема - это сложность анализа вывода (который тоже отличается в различных версиях). Вот как выглядит вывод команды ps на машине с SunOS:

USER PID %CPU %MEM SZ RSS TT STAT START TIME COMMAND

dnb 385 0.0 0.0 268 0 p4 IW Jul 2 0:00 /bin/zsh

drib 24103 0.0 2.610504 1092 p3 S Aug 10 35:49 emacs

dnb 389 0.0 2.5 3604 1044 p4 S Jul 2 60:16 emacs

remy 15396 0.0 0.0 252 0 p9 IW Jul 7 0:01 -zsn (zsh)

sys 393 0.0 0.0 28 0 7 IW Jul 2 0: 02 in, ider.td

dnb 29488 0.0 0.0 68 0 p5 IW 20:15 0:00 scree:

dnb 29544 0.0 0.4 24 148 p7 R 20:39 0:00 less

dnb 5707 0.0 0.0 260 0 p6 IW Jul 24 0,00 -)

root 28766 0,0 0.0 244 0 "> IW 13:20 0:00 - 0 xd

Обратите внимание на третью строку. Два столбца в ней слились вместе и при анализе вывода разобраться в этом будет непросто. Нет, это возможно, но просто действует на нервы. В некоторых вариантах Unixj дела обстоят лучше, но это обстоятельство следует учитывать. Программа на Perl, применяемая в этом случае, прямолинейна: в ней используются орем() для запуска ps, whilei<KH.-0{...} для чтения вывода и split(), unpackO или substrQ для анализа данного вывода. Совет по этому поводу можно найти в книге Тома Кристиансена (Tom Christiansen) и Натана Торкингтона (Nathan Torkington) «Perl: Библиотека программиста», изд-во «Питер», 2000 г. («Perl Cookbook», O'Reilly).