Студопедия
Случайная страница | ТОМ-1 | ТОМ-2 | ТОМ-3
АрхитектураБиологияГеографияДругоеИностранные языки
ИнформатикаИсторияКультураЛитератураМатематика
МедицинаМеханикаОбразованиеОхрана трудаПедагогика
ПолитикаПравоПрограммированиеПсихологияРелигия
СоциологияСпортСтроительствоФизикаФилософия
ФинансыХимияЭкологияЭкономикаЭлектроника

3.3 Взаимодействие службы и приложения



Содержание

 

Задание на выполнение

Введение

1. Анализ

2. Проектирование

3. Кодирование и отладка

3.1 Служба

3.2 Приложение

3.3 Взаимодействие службы и приложения

4. Тестирование

5. Сопровождение

Заключение

Список литературы

 


 

Задание на выполнение

 

Разработка службы Windows: «Контроль приложений», осуществляющей контроль набора выполняющихся приложений и, возможно, управление ими. Кроме разработки службы, необходимо разработать приложение, которое будет осуществлять управление этой службой.

 


 

Введение

 

Целью данного курсового проекта является развитие и закрепление навыков разработки системных приложений.

Объект проектирования в данной работе – служба Windows: «Контроль приложений».

Для более полного понимания объекта рассмотрим определения службы.

Служба Windows (англ. Windows Service, сервисы) – приложения, автоматически запускаемые системой при запуске Windows и выполняющиеся вне зависимости от статуса пользователя [1].

Служба – это Windows-приложение, содержащая дополнительную инфраструктуру, которая позволяет ServiceControlManager (SCM) – компоненту, работающему на всех windows-машинах, – управлять этим приложением [2].

Данный программный продукт разрабатывался на языке C++ с использованием среды разработки Builder C++ XE2, позволяющей оптимизировать процесс его создания.

 


 

1. Анализ

 

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

Для реализации данного проекта был выбран язык С++, так как в наличии имеется среда разработки, и соответствующие знания данного языка.

Реализация службы возможна в двух вариантах:

1) с использованием WIN API;

2) с использованием встроенного класса vcl TService в Builder C++ XE2.

Использование 2-го варианта наиболее оптимально, так как работа с сервисом значительно упрощается.

Рассмотрим необходимость интерактивности разрабатываемой службы. Интерактивные службы – службы, осуществляющие взаимодействие с пользователем. Службы предназначены для непрерывной работы в отсутствии пользователей, поэтому дожидаться, пока оператор (пользователь) нажмёт «OK», можно очень долго. Но, тем не менее, возможности существуют. Поэтому постараемся избежать интерактивности в разрабатываемой службе.



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

Существуют два варианта реализации приложения:

1) графическое приложение с использованием визуальных компонент;

2) консольное приложение, работающее с командной строкой.

В среде разработке Builder C++ XE2 возможна реализация обоих вариантов, но исходя из соображений, что обычный пользователь редко занимается установкой служб и их запуском самостоятельно, то выберем консольный вариант реализации.

Для удобства использования будет разработана система меню, удобная в использовании и включающая в себя справку.

 


 

2. Проектирование

 

Основной целью данного этапа является получение проектных решений.

На первом этапе проектирования рассмотрим саму службу.

Определим функционал разрабатываемого сервиса:

1) создание лог файла, в который будут заноситься сведенья о запушенных процессах. При запуске и завершении процесса добавляется соответствующая запись;

2) при бездействии операционной системы запускать дефрагментацию основного раздела жесткого диска.

Существуют два варианта подхода в проектировании службы.

Вариант первый: стартует служба, начинает что-то делать, время от времени уведомляя систему о своём состоянии, потом служба останавливается.

Вариант второй: стартует служба, создаёт рабочие потоки. По уведомлению от системы потоки останавливаются и служба выключается.

Обычно в службах используется второй вариант, поэтому остановимся именно на этом варианте.

Для формирования лога необходимо создать структуру, в полях которой будут храниться данные о процессе (время создания/завершения, идентификатор (pid), имя процесса, указатель процесса), передаваемую в качестве параметра в функцию записи в лог файл.

Сам процесс формирования лог файла происходит в несколько этапов:

1) служба получает список запушенных процессов в данный момент;

2) сверяет со списком запущенных процессов, полученным ранее, если существуют процессы, отсутствующие в данном списке, затем происходит заполнение структуры данных о процессе и добавление в этот список и вызов функции формирования записи в лог файле о запуске процесса;

 


3) поиск в списке, полученном во втором пункте, процессов, которые отсутствуют в списке из первого пункта и формирование записи о завершении данного процесса, удалении его из списка.

Сам процесс записи в лог файл будет осуществляться с использованием потоков.

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

На втором этапе рассмотрим управляющие приложение.

Данное приложение необходимо для установки, удаления службы и ее настройки во время установки.

Для управления сервисами в Windows существует Service Сontrol Manager (SCM). Для корректного управления службой при ее модификации или смене состояний необходимо каждый раз сначала получать указатель SCM и лишь затем указатель самой службы. Закрытие указателей происходит в обратном порядке.

Меню данного приложения можно реализовать в виде бесконечного цикла while c использованием условной конструкции switch предусматривающая возможность выхода из данного цикла. В данном меню необходимо будет реализовать 5 пунктов:

1) установка сервиса;

2) удаление сервиса;

3) запуск и завершение работы сервиса;

4) помощь по использованию приложения;

5) выход.

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

 


 

3. Кодирование и отладка

 

Основной целью данного этапа является реализация программного продукта.

 

3.1 Служба

 

При создании проекта службы были автоматически созданы два метода:

 

TServiceController __fastcall TControlProcess::GetServiceController(void)

{

return (TServiceController) ServiceController;

}

void __stdcall ServiceController(unsigned CtrlCode)

{

ControlProcess->Controller(CtrlCode);

}

 

Эти два метода никогда изменяться не будут, так как за ними скрывается процесс взаимодействия с SCM менеджером.

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

Для создания потока воспользуемся встроенным в Builder C++ XE2 классом TThread. Создадим потомка данного класса TSparkyThread.

 

class TSparkyThread: public TThread {

private:

protected:

void __fastcall Execute();/*главным методом объекта TThread. В теле метода должен содержаться код, который представляет собой программу потока*/

public:

fastcall TSparkyThread(bool CreateSuspended); /*конструктор созданого класса */ };

 

Win32 предоставляет несколько способов перечисления запущенных процессов:

с помощью библиотеки Process Status Helper (PSAPI);

с помощью ToolHelp32 API;

с помощью недокументированной функции ZwQuerySystemInformation;

с использованием интерфейсов Windows Management Instrumentation.

Был выбран первый вариант, т.к для перечисления процессов библиотека PSAPI предоставляет функцию EnumProcesses, которая возвращает массив идентификаторов запущенных процессов, и в отличии от других методов не извлекается избыточная информация о процессах, а необходимая информация извлекается с использованием указателей процессов.

 

BOOL WINAPI EnumProcesses(

__out DWORD *pProcessIds,//указатель на массив идентификаторов

__in DWORD cb,//размер pProcessIds

__out DWORD *pBytesReturned//число возвращенных байтов

);

 

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

struct ProcesInfo{

DWORD PID; //идентификатор процесса

HANDLE hProcess; //указатель процесса

TCHAR szProcessName[MAX_PATH]; //имя процесса

SYSTEMTIME ST[2]; // время создания и завершения

};

 

Для сохранения списка используем ассоциативный массив (map):

 

typedef map <DWORD,ProcesInfo> ProcessList;

 

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

Отслеживание запуска и завершение программы осуществляется согласно решению, полученному на этапе проектирования. Связи с этим, в потоке, создающемся при запуске службы, объявим два ассоциативных массива: ProcessList Old и ProcessList New.

Для заполнения структуры ProcesInfo создадим функцию:

 

void FillingProcesInfo(ProcesInfo &PrInfo,DWORD &aProcesses);

 

Параметрами, передаваемыми в данную функцию, являются сама структура и идентификатор процесса(PID), который будет позже сохранен в поле структуры. Указатель процесса может быть получен при помощи стандартной функции OpenProcess

 

HANDLE WINAPI OpenProcess(

__in DWORD dwDesiredAccess, // флаг доступа

__in BOOL bInheritHandle, //параметр дескриптора наследования

__in DWORD dwProcessId // идентификатор процесса

);

 

dwDesiredAccess – устанавливает уровень доступа к объекту процесса. Этот параметр может состоять из одного нескольких прав доступа к процессу. Из всех возможных были выбраны следующие параметры:

1) PROCESS_QUERY_INFORMATION – необходим, чтобы извлечь некоторую информацию о процессе;

2) PROCESS_VM_READ – необходим, чтобы читать память в процессе, используя функцию ReadProcessMemory.

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

EnumProcessModules перечисляет все модули процесса, а GetModuleBaseName просто возвращает имя модуля. Данные права доступа необходимы функции PSAPI EnumProcessModules, с помощью которой извлекается имя процесса.

 

BOOL WINAPI EnumProcessModules(

__in HANDLE hProcess, //дескриптор процесса

__out HMODULE *lphModule, // Массив, который получает список

//дескрипторов модуля.

__in DWORD cb, // Размер массива lphModule, в байтах.

__out LPDWORD lpcbNeeded /*Число байтов, требуемых сохранить

все дескрипторы модуля в массиве lphModule. количество байт*/

);

DWORD WINAPI GetModuleBaseName(

__in HANDLE hProcess,// дескриптор процесса

__in_opt HMODULE hModule, /* Дескриптор к модулю. Если этот параметр - NULL, эта функция возвращается, имя процесса*/

__out LPTSTR lpBaseName, /* Указатель на буфер, который получает базовое имя модуля*/

__in DWORD nSize // Размер буфера lpBaseName, в символах.

);

Время создания и завершения процесса узнается при помощи функции GetProcessTimes.

 

Время работы процессора можно вычислить по следующей формуле:

 

 

Для получения этих временных характеристик воспользуемся

 

NTSTATUS WINAPI NtQuerySystemInformation (

__ in SystemInformationClass SYSTEM_INFORMATION_CLASS,

/* Одно из значений SYSTEM_INFORMATION_CLASS, которые указывают на вид информации о системе, которая будет получена.*/

__ inout PVOID SystemInformation,/* Указатель на буфер, который получает требуемую информацию. Размер и структура этой информации изменяются в зависимости от значения параметра SystemInformationClass:*/

__ in SystemInformationLength ULONG,

__ out_opt PULONG ReturnLength

);

 

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

Для второго параметра определим структуру:


struct_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION {

LARGE_INTEGER IdleTime; /* количество времени, которое система была неактивна */

LARGE_INTEGER KernelTime;/* количество времени, которое система потратила выполнение в Привилегированном режиме (включая все потоки во всех процессах, на всех процессорах)*/

LARGE_INTEGER UserTime;/* количество времени, которое система потратила выполнение в Непривилегированном режиме (включая все потоки во всех процессах, на всех процессорах)*/

LARGE_INTEGER Reserved1 [2];

ULONG Reserved2;

};

 

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

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

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

 

3.2 Приложение

 

Для установки и удаления службы необходимо получать дескриптор SCM. Для этого воспользуемся OpenSCManager(

 

NULL, // локальная машина

NULL, // имя базы (ServicesActive)

SC_MANAGER_ALL_ACCESS); // полный доступ

 

Данная функция как раз и возвращает данный дескриптор.

Установка службы осуществляется вызовом функции CreateService, которая создаёт объект сервиса и добавляет его в базу данных менеджера управления сервисами (SCM).

 

SC_HANDLE CreateService(

SC_HANDLE hSCManager, /*Дескриптор базы данных менеджера управления сервисами (SCM)*/

LPCTSTR lpServiceName, /* Указатель на строку (завершающуюся нулём), содержащую имя создаваемого сервиса. Максимальная длина строки не должна превышать 256 символов. В имени сервиса нельзя использовать символы "/" и "\". */

LPCTSTR lpDisplayName, /* Указатель на строку (завершающуюся нулём), содержащую имя, которое будет отображаться в пользовательских приложениях */

DWORD dwDesiredAccess, /* Тип доступа к сервису. Перед тем, как разрешить запрашиваемый доступ, система проверит привелегии вызвавшего процесс*/

DWORD dwServiceType, /* Тип сервиса */

DWORD dwStartType, /* Тип запуска сервиса*/

DWORD dwErrorControl, /* Степень контроля ошибок, а так же действия, которые будут предприняты, в случае ошибки запуска сервиса. */

LPCTSTR lpBinaryPathName, /* Указатель на строку (заканчивающуюся нулём), которая содержит полный путь к исполняемому файлу сервиса. */

LPCTSTR lpLoadOrderGroup, /* Указатель на строку (заканчивающуюся нулём), которая содержит имя группы, членом которой является сервис. Если сервис не является членом группы, то можно указать NULL или пустую строку. */

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

LPCTSTR lpDependencies, /* Указатель на массив (завершающийся двумя нулями) имён (разделённых нулями) сервисов или групп сервисов, которые система должна запустить до запуска этого сервиса. Если сервис не зависит от других сервисов, то в этом параметре нужно указать NULL или пустую строку.*/

LPCTSTR lpServiceStartName, /* Указатель на строку (завершающуюся нулём), которая содержит имя аккаунта, с правами которого будет запущен сервис. */

LPCTSTR lpPassword/* Указатель на строку (заканчивающуюся нулём), которая содержит пароль к аккаунту, указанному в параметре lpServiceStartName. Если аккаунт не имеет пароля либо если сервис запускается с правами LocalService, NetworkService, или системы, то можно указать на пустую строку*/

);


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

Четвертым параметром передадим права доступа SERVICE_ALL_ACCESS, так как для работы со службой нужны права администратора.

Пятым параметром передадим значение SERVICE_WIN32_OWN_PROCESS, что означает что служба запускается в своем собственном процессе.

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

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

Восьмым параметром является путь расположения исполняемого файла службы - "C:\\PrControlProcess.exe".

Оставшимся параметрам присвоим значение NULL.

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

 

SC_HANDLE WINAPI OpenService(

__in SC_HANDLE hSCManager,//дескритор scm

__in LPCTSTR lpServiceName,// имя службы

__in DWORD dwDesiredAccess// права доступа

);

 

При разработке сервиса использовались следующие права доступа:

SERVICE_START – требует вызвать функцию StartService, чтобы запустить службу;

SERVICE_STOP – требует вызвать функцию ControlService, чтобы остановить службу;

3) DELETE – требует вызвать функцию ControlService для удаления службы.

 

3.3 Взаимодействие службы и приложения

служба windows контроль приложение

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

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

SCM хранит базу данных служб в ключе реестра:

 

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services

 

Удобно все параметры службы хранить в ключе:

 

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Имя-Службы(свойство Name)\Parameters.

 

Служба может считывать свои параметры оттуда при запуске, а приложение-конфигуратор — писать в этот ключ настройки [3].

Однако на этом не остановимся, и упростим работу службы, добавив в ней флаг mode. Именно его значение мы и будем записывать в реестр в ветку:


HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Имя-Службы(свойство Name)\mode.

 

Для этого воспользуемся следующими функциями: RegCreateKeyEx и RegSetValueEx. Первая функция предназначена для создания ветви реестра, если она отсутствует, а вторая для записи определенного значения.

 

LONG RegCreateKeyEx(

HKEY hKey, // дескриптор открытого ключа

LPCTSTR lpSubKey, // адрес имени подключа

DWORD Reserved, // зарезервировано

LPTSTR lpClass, // адрес строки класса

DWORD dwOptions, // флаг особых опций

REGSAM samDesired, // желаемый доступ безопасности

LPSECURITY_ATTRIBUTES lpSecurityAttributes, // адрес структуры ключа безопасности

PHKEY phkResult, // адрес буфера для открытого ключа

LPDWORD lpdwDisposition // адрес буфера характерного значения);

LONG RegSetValueEx(

HKEY hKey, // дескриптор ключа

LPCTSTR lpValueName,// адрес имени установливаемого значения

DWORD Reserved, // зарезервировано

DWORD dwType, // тип данных

CONST BYTE *lpData, // адрес данных для установки

DWORD cbData // размер данных

);

Считывание значение реестра службой осуществляется RegQueryValueExA.

LSTATUS RegQueryValueExA

(

HKEY hkey,// Дескриптор ключа.

LPCSTR name,//имя читаемого ключа

LPDWORD reserved, // зарезервировано

LPDWORD type, // тип данных

LPBYTE data,// адрес данных

LPDWORD count// размер данных

)

 

Возможны три режима работы службы:

создание лога и запуск дефрагментации;

создание лога;

запуск дефрагментации.

Выбор режима в службе осуществляется с помощью конструкции switch.

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

Описание функций были взяты с официального сервера корпорации майкрософт [4].

Кодом программы представлен в Приложениях А,Б,В.

 


 

4. Тестирование

 

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

Проведем планирование нашего тестирования.

На первом этапе проверим правильность работы приложения по установке и удалению службы. На втором этапе проверим ее функционирование. И на последнем проверим правильность обработки неправильного ввода пользователя.

Приступим к первому этапу тестирования:

Список служб до установки:

 

Рисунок 1. Список служб до установки службы

 

Попробуем установить службу при помощи разработанного приложения


Рисунок 2. Установка службы

 

Посмотрим вновь список установленных служб:

 

Рисунок 3. Список служб после установки сервиса

 

В перечне служб появилась новая служба ControlProcess- это и есть установленная служба, то есть в процессе установки ошибок обнаружено не было.

Теперь удалим нашу службу, используя консоль, и посмотрим список установленных сервисов:

 

Рисунок 4. Список служб после удаления


Службы ControlProcess больше нет в данном списке, что очень наглядно отображает правильность функционирования.

Установим сервис с ключом /l, что обеспечивает только запись в лог файл информации о процессах. И запустим ее при помощи консольного приложения:

 

Рисунок 5. Запуск службы из приложения

 

Проверим функционирование службы наличием лог файла в C:\WINDOWS\system32

 

Рисунок 6. Содержимое каталога C:\WINDOWS\system32

 

Просмотрим содержимое данного файла


Рисунок 7. Содержимое лог файла

 

Для проверки правильности работы запустим и закроем браузер internet explorer

 

Рисунок 8. Содержимое лог файла с записями о запуске и закрытии internet explorer-а

 

Как видно были добавлены соответствующие записи.

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


Рисунок 9. Содержимое лог файла с записями о запуске и завершении дефрагментатора

 

Из данного рисунка отчетливо видно запуск процесса дефрагментации и его завершение.

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

Отметим так же возможность пользователя запускать и завершать работу службы из SCM.

Проверим приложение на правильность ввода ключей

 

Рисунок 10. Проверка на ввод некорректных ключей

 

При вводе некорректных ключей выводиться соответствующее предупреждение.

 


 

5. Сопровождение

 

Системные требования:

Windows XP

Права администратора

На данный момент служба не корректно ведет себя в ОС Windows 7 и Windows Vista планируется устранить данный недостаток в следующей версии.

Инструкция по установке:

Скопируйте исполняемый файл PrControlProcess в корень диска С:\.

Запустите консольное приложение menu.exe с правами администратора.

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

Для запуска или завершения работы службы воспользуйтесь третьим пунктом меню или SCM.

Удаление службы происходит при выборе второго пункта. После успешного удаления службы удалите исполняемый файл из корня диска С:\.

 


Заключение

 

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

Недостаток разработанной службы заключается в том, что она рассчитана на работу в Windows XP и не работает в Windows 7 и Windows Vista. Это вызвано тем, что функции PSAPI не совсем корректно работают на новой платформе.

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

 

 


Дата добавления: 2015-09-28; просмотров: 18 | Нарушение авторских прав




<== предыдущая лекция | следующая лекция ==>
Биозавивка натуральных ресниц | Федеральные законы защиты населения и территории от ЧС природного и техногенного характера.

mybiblioteka.su - 2015-2024 год. (0.061 сек.)