Uploaded by Алексей Быстров

My course work 3

advertisement
МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РФ
Федеральное государственное бюджетное образовательное
учреждение высшего образования
«Пензенский государственный университет»
(ФГБОУ ВО «Пензенский государственный университет»)
__________________________________________________________________
_
Кафедра «Математическое обеспечение и применение ЭВМ»
Зав. кафедрой
Макарычев П.П.
"___" _____________ 2019 г.
Система управления терминалами в компьютерной сети
Пояснительная записка к курсовому проекту по дисциплине
«Системное программирование»
ПГУ 09.03.02 – 4КП171.18 ПЗ
Автор работы
Суворов М.Д
Группа
17ВИ1
Специальность
09.03.02
Руководитель работы
Шашков Б.Д.
Работа сдана
“___”________2019 г.
Работа защищена
“___”________2019 г.
Оценка
________________
Пенза – 2019 г.
Реферат
Пояснительная
записка
содержит
43
листа,
16
рисунка,
5
использованных источников и 2 приложений.
ПОТОКИ, ИМЕНОВАННЫЕ КАНАЛЫ, СИГНАЛЫ, UNIX, LINUX,
UBUNTO, CLION, c++, PYTHON, QTDISIGNER, PYQT5, PYCHARM.
Объект разработки: база данных и приложение для финансового отдела
и отдела менеджмента, созданное с использованием средств, предоставляемых
современными СУБД реляционного типа.
Цель работы: разработка программного обеспечения для системы
удаленного управления терминалами в компьютерной сети. Средства
разработки: QTDesigner, PyCharm, CLion
Результаты работы: в процессе разработки проводилось изучение
основных средств, предоставляемых QTDesigner, PyCharm, CLion и на основе
их использования разработана и отлажена программа создания и управления
удаленными процессами.
Изм.
Лист
№ докум.
Подпись
ПГУ 09.03.02 – 4КП171.18 ПЗ
Дата
Разраб.
Суворов М.Д
Система
Провер.
Шашков Б.Д.
компьютерной
Реценз.
Н. Контр.
Утверд.
управления
Пояснительная записка.
терминалами
в
сети
Лит.
Лист
Листов
3
N
группа 17ВИ1
Содержание
Введение 4
1 Анализ инструментов .......................................................................................... 6
1.1 Сигналы .............................................................................................................. 6
1.2 Каналы ................................................................................................................ 8
1.3 Графический фреймворк PyQt ....................................................................... 10
2.Проектирование и разработка системы управления терминалами в
компьютерной сети ............................................................................................... 12
2.1 Постановка задачи........................................................................................... 12
2.2 Проектирование и разработка системы ........................................................ 12
2.3 Тестирование системы .................................................................................... 18
Заключение ............................................................................................................ 26
Приложение А ....................................................................................................... 28
Приложение Б ........................................................................................................ 38
3
Введение
Сегодня почти все программное обеспечение является многопоточном
или много процессным. Процессы отводятся для множества целей, например,
для разделения вычислительной нагрузки. Для удобного взаимодействия
между процессами и потоками в разных операционных системах были
придуманы различные Application Programming Interface (интерфейсы
программирования). При создании программного обеспечения (ПО) основное
внимание уделялось системе Ubunto Linux.
Linux — общее название UNIX-подобных операционных систем на
основе одноимённого ядра и собранных для него библиотек и системных
программ, разработанных в рамках проекта GNU.
GNU/Linux работает на PC-совместимых системах семейства Intel x86, а
также на IA-64, AMD64, PowerPC, ARM и многих других.
К операционной системе GNU/Linux также часто относят программы,
дополняющие эту операционную систему, и прикладные программы,
делающие её полноценной многофункциональной операционной средой. В
отличие от большинства других операционных систем, GNU/Linux не имеет
единой «официальной» комплектации. Вместо этого GNU/Linux поставляется
в большом количестве так называемых дистрибутивов, в которых программы
GNU соединяются с ядром Linux и другими программами.
Большинство пользователей для установки GNU/Linux используют
дистрибутивы. Дистрибутив — это не просто набор программ, а ряд решений
для разных задач пользователей, объединённых едиными системами
установки, управления и обновления пакетов, настройки и поддержки.
4
Реализация потоков в операционной системе Linux уникальна. Для ядра
Linux не существует отдельной концепции потоков. В ядре Linux потоки
реализованы так же, как и обычные процессы. В ОС Linux нет никакой
особенной семантики для планирования выполнения потоков или каких-либо
особенных структур данных для представления потоков. Поток— это просто
процесс, который использует некоторые ресурсы совместно с другими
процессами. Каждый поток имеет структуру task_struct и представляется для
ядра обычным процессом
5
1 Анализ инструментов
1.1 Сигналы
Сигналы способны в случайное время (асинхронно) прерывать процесс
для обработки какого-либо события. Процесс может быть прерван сигналом
по инициативе другого процесса или ядра. Ядро использует сигналы для
извещения процессов о различных событиях, например, о завершении
дочернего процесса.
Сигналы имеют определенный жизненный цикл. Вначале сигнал
создается – высылается процессом или генерируется ядром. Затем сигнал
ожидает доставки в процесс-приемник. Принято считать, что период
ожидания сигнала равен промежутку времени между созданием сигнала и
действием, которое этот сигнал высылает. В конце жизненного цикла сигнала
происходит его перехват (прием) процессом и выполнение связанных с
сигналом действий.
Существует деление сигналов на простые и надежные.
Изначально были разработаны и использовались простые (ненадежные)
сигналы. По своему принципу работы они похожи на канонический механизм
обработки аппаратных прерываний в процессоре. Если процесс хочет особым
образом обрабатывать некий сигнал, то он сообщает ядру об этом, указывая
специальную функцию – обработчик сигнала. При доставке сигнала процессу
ядро как можно скорее вызывает обработчик сигнала, прерывая работу
процесса. По завершении работы обработчика выполнение процесса
продолжается с того места, где он был прерван.Вместо написания своей
функции-обработчика можно просто указать ядру, что сигнал вызывает
действие, принятое для него по умолчанию, или что сигнал банально
игнорируется.Вся эта концепция работы с сигналами выглядит достаточно
хорошо, пока сигнал не приходит процессу в то время, когда он уже занят
обработкой другого сигнала. Здесь и проявляются проблемы – повторно
6
вызванный обработчик сигнала может испортить те разделяемые ресурсы
(общие структуры данных и переменные), которые он использует. Кроме того,
в случае прихода большого количества сигналов стек процесса может
неограниченно увеличиваться, что может привести к сбоям в работе
программы.В процессе решения проблемы были разработаны надежные
сигналы, которые стандартизованы в POSIX и используются по сей день.
Далее в статье рассматриваются именно надежные сигналы.
Посылка сигналов от одного процесса к другому обычно осуществляется
при помощи системного вызова kill(). Его первый параметр – PID процесса,
которому посылается сигнал; второй параметр – номер сигнала.
Все программы, подчиняющиеся стандарту POSIX, регистрируют свои
обработчики сигналов при помощи системного вызова sigaction(). Этот
системный вызов имеет три параметра: первый – int signum – номер
перехватываемого сигнала. Второй – struct sigaction * act – указатель на
структуру, описывающую правила установки обработчика. Третий параметр –
struct sigaction * oact – принимает уже установленные правила обработчика
сигнала. Либо второй, либо третий (но не оба) параметр можно установить в
NULL при необходимости.
В стандартной системе Unix определены два сигнала, SIGUSR1 (в Linux
– номер 10) и SIGUSR2 (номер 12), предназначенные для передачи
произвольной
информации,
но
использование
этих
сигналов
не
приветствуется. Одной из причин негативного отношения программистов
Unix к пользовательским сигналам является то, что сигналы, вообще говоря,
представляют собой ограниченный ресурс, совместное использование
которого
может
вызвать
конфликты
(например,
если
программист
задействовал эти сигналы в своей программе и при этом использует
стороннюю библиотеку, в которой эти сигналы также задействованы).
7
1.2 Каналы
Каналы позволяют передавать данные между процессами в порядке
поступления ("первым пришел - первым вышел"), а также синхронизировать
выполнение процессов. Их использование дает процессам возможность
взаимодействовать между собой, пусть даже не известно, какие процессы
находятся на другом конце канала. Традиционная реализация каналов
использует файловую систему для хранения данных. Различают два вида
каналов: поименованные каналы и, за отсутствием лучшего термина,
непоименованные каналы, которые идентичны между собой во всем, кроме
способа первоначального обращения к ним процессов. Для поименованных
каналов процессы используют системную функцию open, а системную
функцию pipe - для создания непоименованного канала. Впоследствии, при
работе с каналами процессы пользуются обычными системными функциями
для файлов, такими как read, write и close.
Поименованный канал - это файл, имеющий почти такую же семантику,
как и непоименованный канал, за исключением того, что этому файлу
соответствует запись в каталоге и обращение к нему производится по имени.
Процессы открывают поименованные каналы так же, как и обычные файлы, и,
следовательно, с помощью поименованных каналов могут взаимодействовать
между собой даже процессы, не имеющие друг к другу близкого отношения.
Поименованные каналы постоянно присутствуют в иерархии файловой
системы (из которой они удаляются с помощью системной функции unlink), а
непоименованные каналы являются временными: когда все процессы
заканчивают работу с каналом, ядро отбирает назад его индекс.
Алгоритм открытия поименованного канала идентичен алгоритму
открытия обычного файла. Однако, перед выходом из функции ядро
увеличивает значения тех счетчиков в индексе, которые показывают
количество процессов, открывших поименованный канал для чтения или
8
записи.
Процесс,
открывающий
поименованный
канал
для
чтения,
приостановит свое выполнение до тех пор, пока другой процесс не откроет
поименованный канал для записи, и наоборот. Не имеет смысла открывать
канал для чтения, если процесс не надеется получить данные; то же самое
касается записи. В зависимости от того, открывает ли процесс поименованный
канал для записи или для чтения, ядро возобновляет выполнение тех
процессов,
которые
были
приостановлены
в
ожидании
процесса,
записывающего в поименованный канал или считывающего данные из канала
(соответственно).
Канал следует рассматривать под таким углом зрения, что процессы
ведут запись на одном конце канала, а считывают данные на другом конце.
Как уже говорилось выше, процессы обращаются к данным в канале в порядке
их поступления в канал; это означает, что очередность, в которой данные
записываются в канал, совпадает с очередностью их выборки из канала.
Совпадение количества процессов, считывающих данные из канала, с
количеством процессов, ведущих запись в канал, совсем не обязательно; если
одно число отличается от другого более, чем на 1, процессы должны
координировать свои действия по использованию канала с помощью других
механизмов. Ядро обращается к данным в канале точно так же, как и к данным
в обычном файле: оно сохраняет данные на устройстве канала и назначает
каналу столько блоков, сколько нужно, во время выполнения функции write.
Различие в выделении памяти для канала и для обычного файла состоит в том,
что канал использует в индексе только блоки прямой адресации в целях
повышения эффективности работы, хотя это и накладывает определенные
ограничения на объем данных, одновременно помещающихся в канале.
При закрытии канала процесс выполняет ту же самую процедуру, что и
при закрытии обычного файла, за исключением того, что ядро, прежде чем
освободить индекс канала, выполняет специальную обработку. Оно
9
уменьшает количество процессов чтения из канала или записи в канал в
зависимости от типа файлового дескриптора. Если значение счетчика числа
записывающих в канал процессов становится равным 0 и имеются процессы,
приостановленные в ожидании чтения данных из канала, ядро возобновляет
выполнение последних и они завершают свои операции чтения без возврата
каких-либо данных. Если становится равным 0 значение счетчика числа
считывающих из канала процессов и имеются процессы, приостановленные в
ожидании возможности записи данных в канал, ядро возобновляет
выполнение последних и посылает им сигнал об ошибке. В обоих случаях не
имеет смысла продолжать держать процессы приостановленными, если нет
надежды на то, что состояние канала когда-нибудь изменится. Например, если
процесс ожидает возможности производить чтение из непоименованного
канала и в системе больше нет процессов, записывающих в этот канал, значит,
записывающий процесс никогда не появится. Несмотря на то, что если канал
поименованный, в принципе возможно появление нового считывающего или
записывающего процесса, ядро трактует эту ситуацию точно так же, как и для
непоименованных каналов. Если к каналу не обращается ни один
записывающий
или
считывающий
процесс,
ядро
освобождает
все
информационные блоки канала и переустанавливает индекс таким образом,
чтобы он указывал на то, что канал пуст. Когда ядро освобождает индекс
обычного канала, оно освобождает для переназначения и дисковую копию
этого индекса.
1.3 Графический фреймворк PyQt
PyQt5 – это набор Python-связей для фреймворка Qt5 от Digia. Набор
PyQt5 доступен для Python 2.x и 3.x. Это руководство рассматривает Python 3.
Библиотека Qt – это одна из самых мощных GUI-библиотек.
PyQt5 реализован как комплект Python-модулей. Он включает в себя
около 620 классов и 6000 функций и методов. Это мульти-платформенный
10
инструментарий, который запускается на большинстве операционных систем,
среди которых Unix, Windows и MacOS. PyQt5 реализован под двумя
лицензиями. Разработчики могут выбрать между GPL и коммерческой
лицензией.
PyQt также включает в себя Qt Designer (Qt Creator) — дизайнер
графического интерфейса пользователя. Программа pyuic генерирует Python
код из файлов, созданных в Qt Designer. Это делает PyQt очень полезным
инструментом для быстрого прототипирования. Кроме того, можно добавлять
новые графические элементы управления, написанные на Python, в Qt
Designer.
Раньше PyQt поставлялся вместе со средой разработки Eric, написанной
на PyQt. Eric имеет встроенный отладчик и может быть использована для
создания консольных программ. Теперь она доступна в качестве отдельного
проекта.
Qt Designer является кросс-платформенным компоновщиком макетов и
форм графического интерфейса пользователя. Он позволяет быстро
спроектировать виджеты и диалоги, используя экранные формы с
использованием тех же виджетов, которые будут использоваться в
приложении. Формы, созданные с Qt Designer, являются полностью
функциональными, а также могут быть просмотрены в режиме реального
времени.
11
2.Проектирование и разработка системы управления терминалами в
компьютерной сети
2.1 Постановка задачи
Требуется создать приложение для администрирование удаленных
терминалов. При этом приложение должно обеспечивать корректную работу
созданных процессов.
Под
терминалом
понимается
некое
устройство
(ЭВМ)
для
взаимодействия пользователя с вычислительной системной.
Администратор управляет работой терминалов и потенциально
пользователями. Контроль и управление терминалами осуществляется в
удаленном
режиме
с
автоматизированного
рабочего
места
(АРМ)
администратора.
Администратор может выполнять следящие действия:
 просмотреть состояние всех терминалов в сети;
 выбрать любой терминал в качестве текущего;
 определить состояние выбранного терминала;
 установить удалить любое ПО;
 просмотреть список пользователей;
 определить пользователя;
 удалить терминал;
В качестве согласования сообщений между процессами используется
именованные каналы и сигналы. Для АРМ администратора используется
графический интерфейс, построенный на платформе PyQt5 и написанный на
языке Python3.6.
2.2 Проектирование и разработка системы
При
проектировании
приложении
учитывалось
возможность
использования работы процесса администратора и процесса клиента в
12
отдельных потоках. На рисунке 1, представлена концептуальная блок-схема
работы системы.
13
Рисунок 1 – Концептуальная блок-схема работы системы
14
При проектировании учитывалось то, что прикладная программа
клиента не может быть запущенна, если не запущен процесс администратора.
После запуска программы администратора запускается функция,
которая записывает PID-администратора в канал для того, чтобы клиентский
терминал всегда мог обратиться к администратору по его PID.
Дальше
администратор
может
работать:
создавать
терминалы,
отправлять им команды, завершать их, менять их статус, менять их имя.
При создании терминала-клиента из администраторской программы
менеджеру предлагается ввести желаемое имя.
При этом все клиентские терминалы хранятся в хеш-таблице, где по
каждому PID клиента располагаются его данные.
Когда администратор отправляет команду, он указывает номер команды,
в графическом интерфейсе выделяет терминал, которому следует отправить
эту команду и отправляет ее. Сначала отправляется сигнал SIGUSR1, чтобы
клиентский терминал перешел в режим чтения из канала, после этого в
именованный канал записываются данные.
При этом учитывается возможность записи в канал несколькими
клиентскими терминалами. Для этого разработано послание специального
формата, которое позволяет отправлять несколько команд от разных
клиентских терминалов. Формат команды:
Msg:<тип_команды>,Status:
<номер_статуса>,name:<Имя_терминала>,data:<данные_команды >.
Где разделитель «.», разделяет отдельные команды. Первый параметр –
тип команды, второй – статус терминала, третий – имя терминала, четвертыйданные команды. Четвертый параметр используется для передачи системных
команд, например, ps –e.
Особым видом команды является завершение терминала. При
завершении администратор выделяет в графическом интерфейсе нужный
терминал и отправляет ему сигнал SIGQUIT.
15
При завершении программы администратора завершаются и все
процессы-клиенты.
В случае, если на момент запуска клиента, например, из консоли
программа администратора еще не запущена, клиент попадает в ожидание до
тех пор, пока администратор не включит программу.
Программа-клиент может быть запущенна как из терминала, так и из
программы администратора.
При запуске программы для клиента она считывает данные о PID
администратора из именованного канала и запоминает их.
После этого клиент может производить любые работы. Предусмотрена
ситуация, при которой терминал-клиент завершается при помощи комбинации
ctrl+c, что соответствует сигналу SIGINT. При этом клиент отправит
соответствующую команду приложению-администратору, которое удалит
терминал из списка активных терминалов.
Для программы администратора была разработана иерархия классов,
позволяющая расширять ее функциональные возможности (Рисунок 2).
16
Рисунок 2 – Диаграмма классов
Иерархия классов представлена следующим образом: MainWindow –
главная форма приложения. Класс связан композиционной связью с классом
PipeWorker, который ответственен за чтение и запись из именованного канала.
Этой связью также связан класс MessageConverter, ответственный за
конвертацию строки в класс Message. SendMsgDialog -этот класс является
частью графического интерфейса, позволяет вводить команду для терминала.
AddElementDialog служит для создания нового терминала. QCustomQwidget
является классом для обслуживания графического списка. В класс User
инкапсулированы все данные о пользователе.
В классе MainForm, содержится список userDict, который является
хеш-таблицей со всеми пользователями и их данными. По PID терминала
хранится объект класса User.
17
При поступлении сигнала SIGUSR1 данные читаются из канала
классом PipeWorker. После этого набор строк поступает на MessageConverter.
MessageConverter конвертирует строки в список объектов класса Message.
MainForm преобразует и добавляет обновленные данные в список.
MessageConverter валидирует строку. Если строка не подходит под заданный
формат команды, клиентскому терминалу отказывается во внесение
изменений, транзакция откатывается и именованный канал очищается.
Класс MainForm обеспечивает целостность данных.
2.3 Тестирование системы
Графический интерфейс приложения представляет собой форму с тремя
кнопками: «Удалить», «Добавить», «Отправить сообщение».
Рисунок 3 – главная форма приложения.
18
При нажатии на кнопку «Добавить» открывается диалог в который
можно внести имя нового пользователя (Рисунок 4).
Рисунок 4 – Диалог для ввода данных
Если существует хотя бы один активный терминал, то он отображается
в списке. В списке также отображаются : имя, статус, PID. (Рисунок 5)
Рисунок 5 – Отображение данных о терминале
19
Мы можем убедиться, что такой процесс существует с помощью
команды ps –e. (Рисунок 6)
Рисунок 6 – Отображение процесса
Администратор может управлять несколькими терминалами. Для этой
цели организован список (Рисунок 7).
20
Рисунок 7– Отображение всех запущенных процессов
Возможность запуска программы из терминала Linux c ее отображением
в программе администратора. (Рисунок 8).
Рисунок 8 –запуск приложения клиента из Linux-терминала
21
Программа
с
помощью
специального
диалога
позволяет
отправлять команды удалённому терминалу. (Рисунок 9)
Рисунок 9 – диалог для ввода команды
Результат отправки команды на смену имени терминалу.(Рисунок 10)
Рисунок 10 Результат выполнения смени имени терминалу.
Выполнение команды смены статуса. (Рисунок 11-12)
22
Рисунок 11 –Ввод команды на изменения статуса.
Рисунок 12 – Результат выполнения команды смена статуса.
Приложение администратора позволяет выполнять системные команды,
такие как ps –e. (Рисунок 13 -14)
23
Рисунок 13 – ввод команды ps –e
Рисунок 14 – Результат выполнения команды Ps-e
Демонстрация выполнения команды завершения терминала
представлена на рисунке 15.
24
Рисунок 15 – Результат выполнения завершения терминала
Если терминал завершает от имени клиента( ctrl+c). , то администратор
обновляет данные о терминале в своем списке.
Рисунок 16 – Результат выполнения ctrl +c
25
Заключение
При выполнении курсового проекта была создана система управления
удаленными терминалами. Создано приложение для администрирования
удаленных терминалов с графическим интерфейсом с использованием
фреймворка PyQt5. При создании приложение администратора использовался
язык Python 3.6.2. При создании клиента было использован язык С++ (14).
26
Список используемых источников
1.Техническая документация по PyQt5 [Электронный ресурс]. Режим
доступа: https://www.riverbankcomputing.com/static/Docs/PyQt5/
2.Техническая документация по Python3.6: [Электронный ресурс].
Режим доступа: https://docs.python.org/3.6/
3.Техническая документация по C++ (14): [Электронный ресурс]. Режим
доступа: https://en.cppreference.com/w/
4. К. Брайан .Unix. Программное окружение./К.Брайн, У. Пайк- СимволПлюс, 2003.
5.А.М Робачевский Операционная система UNIX / А.М Робачевский. –
БХВ-Петербург, 1997.
27
КОД ПРИЛОЖЕНИЯ АДМИНИСТРАТОРА
Приложение А
(обязательное)
28
Main.py
import os
import signal
import stat
import subprocess
import sys
from PyQt5 import QtWidgets
from PyQt5.QtCore import QTimer
from PyQt5.QtWidgets import QListWidget, QListWidgetItem
from ManagerSource.MessageConverter import MessageConverter
from ManagerSource.PipeWorker import PipeWorker
from UISource.PyUi.AddElementDialog import AddElementDialog
from UISource.PyUi.CustemQListWidget.QCustomQWidget import QCustomQWidget
from UISource.PyUi.MainForm import Ui_MainWindow
from UISource.PyUi.SendMsgDialog import SendMsgDialog
class MainWindow(QtWidgets.QMainWindow):
MainPipePath = ''
PipeName = "Pipe/GeneralPipe.p"
PidFile = "Pipe/PidFile.txt"
PipeMode = 0o666
pathToClientProgram = ""
dictUsers = {}
_pipeworker = PipeWorker()
_converter = MessageConverter()
_listCounter = 0
def __init__(self):
super(MainWindow, self).__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.listWidget = QListWidget(self)
self.MainPipePath = os.getcwd() + "/" + self.PipeName
try:
os.mkfifo(self.MainPipePath, stat.S_IFIFO) # stat.S_IWUSR | stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH)
os.chmod(self.MainPipePath, self.PipeMode)
print("Pipe created")
29
except IOError:
print("Pipe already exist")
# self.AddToDictonary()
file = open(self.PidFile, "w")
file.write(str(os.getpid()))
file.close()
self.ui.DelleteButton.clicked.connect(self.ButtonClicked)
self.ui.addElementButton.clicked.connect(self.ButtonAddClicked)
self.ui.sendMessageButton.clicked.connect(self.ButtonMsgClkicked)
self.InitList()
def ButtonMsgClkicked(self):
self.dealog = SendMsgDialog(self.CallBackSendMsgHandlet)
self.dealog.show()
def TryDistinctData(self, PID):
for key, value in self.dictUsers.items():
if value.PID == PID:
self.listWidget.takeItem(key)
def AddToUi(self, usrData):
for i in usrData:
self.TryDistinctData(i.USER.PID)
self._listCounter += 1
self.dictUsers[self._listCounter] = i.USER
myQCustomQWidget = QCustomQWidget()
print("PID added = ", i.USER.PID)
myQCustomQWidget.SetPidValue(i.USER.PID)
myQCustomQWidget.SetStatus(i.USER.STATUS)
myQCustomQWidget.SetName(i.USER.Name)
myQListWidgetItem = QListWidgetItem(self.listWidget)
# Set size hint
self.listWidget.setFixedHeight(500)
self.listWidget.setFixedWidth(950)
myQListWidgetItem.setSizeHint(myQCustomQWidget.sizeHint())
# Add QListWidgetItem into QListWidget
self.listWidget.addItem(myQListWidgetItem)
self.listWidget.setItemWidget(myQListWidgetItem, myQCustomQWidget)
30
def DeleteUserFromList(self, PID):
self.TryDistinctData(PID)
self._listCounter += 1
def AddToDictonary(self):
readedString = self._pipeworker.ReadPipe(self.MainPipePath)
self._converter.ParseToMessage(readedString)
def InitList(self):
myQListWidgetItem = QListWidgetItem(self.listWidget)
self.listWidget.setFixedHeight(500)
self.listWidget.setFixedWidth(950)
self.listWidget.addItem(myQListWidgetItem)
def ButtonClicked(self):
if self._listCounter == 0:
return
val = [x.row() for x in self.listWidget.selectedIndexes()]
listItems = self.listWidget.selectedItems()
if not listItems: return
for item in listItems:
self.listWidget.takeItem(self.listWidget.row(item))
self._listCounter -= 1
self.KillProcess(val[0])
print(val)
def KillProcess(self, index):
usrData = self.dictUsers[index]
os.kill(int(usrData.PID), 9)
def ButtonAddClicked(self):
self.dealog = AddElementDialog(self.CallBackHandler)
self.dealog.show()
def CallBackSendMsgHandlet(self, strTypeMsg, strComand):
val = [x.row() for x in self.listWidget.selectedIndexes()]
usrData = self.dictUsers[val[0]]
os.kill(int(usrData.PID), signal.SIGUSR1)
self._pipeworker.WriteToPipe(self.PipeName,
31
"messageType:" + strTypeMsg + ",PID:" + usrData.PID + ",status:" + usrData.STATUS +
",userName:" + usrData.Name + ",msg:" + strComand)
def CallBackHandler(self, str):
print("call back recived" + str + "\n")
self.pathToClientProgram = os.getcwd() + "/CClient/cmake-build-debug/CClient" # "cmake-build-debug" +
"/CClient &"
subprocess.Popen(
[self.pathToClientProgram, str, '1']) # API PYTHON для вызова processa - системно вызывает exec
# os.system(self.pathToClientProgram+" &")
# os.execl(self.pathToClientProgram, "sa", "1", "1")
def SignalUser1Handler(self, signum, stack):
print("signaled!\n")
readedString = self._pipeworker.ReadPipe(os.getcwd() + "/" + self.PipeName)
usrs = self._converter.ParseToMessage(readedString)
self.AddToUi(usrs)
def SigUserDieHandler(self, signum, stack):
readedString = self._pipeworker.ReadPipe(os.getcwd() + "/" + self.PipeName)
usrs = self._converter.ParseToMessage(readedString)
for usr in usrs:
self.DeleteUserFromList(usr.USER.PID)
def main():
os.chdir("..")
print(os.getpid())
app = QtWidgets.QApplication([])
application = MainWindow()
signal.signal(signal.SIGUSR1, application.SignalUser1Handler) # USR1 - for read from general named pipe
signal.signal(signal.SIGUSR2, application.SigUserDieHandler) # USR1 - for read from general named pipe
timer = QTimer()
timer.start(100)
timer.timeout.connect(lambda: None)
application.show()
sys.exit(app.exec())
if __name__ == '__main__':
main()
32
Message.Py
from ManagerSource.User import User
class Message():
MSG_TYPE = ""
USER = User("no", "no", "no")
MSG = ""
def __init__(self, User, type, msg):
self.USER = User
self.MSG_TYPE = type
self.MSG = msg
pass
MessageConverter.py
from ManagerSource.Message import Message
from ManagerSource.User import User
class MessageConverter():
def __init__(self):
pass
def CheckMessage(self, stringMessage):
return int(stringMessage.split(':')[0])
def ParseToMessage(self, stringMess):
if stringMess == "":
print("Can't parse string")
return
allMessages = stringMess.split(".")
print(allMessages)
msgList = []
for message in allMessages:
if message == "":
continue
res = []
for j in message.split(","):
if j == "":
continue
res.append(str(j.split(":")[1]))
print(res)
msg = Message(User(res[3], res[1], res[2]), res[0], res[4])
print(msg.USER.PID)
msgList.append(msg)
print("Count of element in lists = ", len(msgList))
return msgList
PipeWorker.py
33
import os
class PipeWorker():
def __init__(self):
pass
def ReadPipe(self, FIFO):
resultString = ""
print("LOG: FIFO opened")
with open(FIFO, "rb") as fifo:
while True:
data = fifo.read()
resultString += data.decode("ascii", "ignore")
if len(data) == 0:
print("LOG: Reader closed")
break
resultString.rstrip()
resultString.replace("\n", "")
print("LOG: Прочитанно =" + str(len(resultString)))
print(resultString)
print("end read")
return resultString
def WriteToPipe(self, FIFO, writedString):
ds = os.open(FIFO, os.O_WRONLY)
os.write(ds, writedString.encode())
os.close(ds)
User.py
class User():
Name = ""
PID = "-1"
STATUS = ""
MSG = ""
def __init__(self, name, pid, status):
self.Name = name
self.PID = pid
self.STATUS = status
pass
AddElementDialog.Py
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QFont
from PyQt5.QtWidgets import (QWidget, QPushButton, QLineEdit,
QInputDialog, QApplication, QGridLayout, QLabel)
class AddElementDialog(QWidget):
def __init__(self, callBack):
super().__init__()
self.initUI(callBack)
def initUI(self, callBack):
34
self.FormCallBack = callBack
self.gridLayout = QGridLayout(self)
self.btn = QPushButton('Ввод', self)
self.btn.clicked.connect(self.buttonClick)
font = QFont("Arial", 14)
self.label = QLabel()
self.label.setFont(font)
self.label.setAlignment(Qt.AlignCenter)
self.label.setText("Имя для терминала")
self.lineEdit = QLineEdit(self)
self.setGeometry(300, 300, 290, 150)
self.setWindowTitle('Input dialog')
self.gridLayout.addWidget(self.label, 0, 0)
self.gridLayout.addWidget(self.lineEdit, 1, 0)
self.gridLayout.addWidget(self.btn, 2, 0)
def buttonClick(self):
self.FormCallBack(self.lineEdit.text())
self.close()
SendMsgDialog.py
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QFont
from PyQt5.QtWidgets import (QWidget, QPushButton, QLineEdit,
QGridLayout, QLabel)
class SendMsgDialog(QWidget):
def __init__(self, callBack):
super().__init__()
self.initUI(callBack)
def initUI(self, callBack):
self.FormCallBack = callBack
self.gridLayout = QGridLayout(self)
self.btn = QPushButton('Ввод', self)
self.btn.clicked.connect(self.buttonClick)
font = QFont("Arial", 14)
self.label = QLabel()
self.label.setFont(font)
self.label.setAlignment(Qt.AlignCenter)
self.label.setText("Тип сообщения")
self.labelComand = QLabel()
self.labelComand.setFont(font)
self.labelComand.setAlignment(Qt.AlignCenter)
self.labelComand.setText("Данные сообщения")
self.lineEdit = QLineEdit(self)
self.lineEditComand = QLineEdit(self)
self.setGeometry(300, 300, 290, 150)
self.setWindowTitle('отправить')
self.gridLayout.addWidget(self.label, 0, 0)
self.gridLayout.addWidget(self.lineEdit, 1, 0)
self.gridLayout.addWidget(self.labelComand, 2, 0)
self.gridLayout.addWidget(self.lineEditComand, 3, 0)
35
self.gridLayout.addWidget(self.btn, 4, 0)
def buttonClick(self):
self.FormCallBack(self.lineEdit.text(), self.lineEditComand.text())
self.close()
QCustomQWidget.Py
from PyQt5.QtGui import QFont
from PyQt5.QtWidgets import QVBoxLayout, QLabel, QHBoxLayout, QWidget
class QCustomQWidget(QWidget):
def __init__(self, parent=None):
super(QCustomQWidget, self).__init__(parent)
font = QFont("Arial", 18)
self.firstColumn = QVBoxLayout()
self.secondColumn = QVBoxLayout()
self.thirdColumn = QVBoxLayout()
self.SetLabels()
# Устанавливаем шрифты
self.SetFonts(font)
self.firstColumn.addWidget(self.textUpQLabelPIDProcess)
self.firstColumn.addWidget(self.textDownQLabelValuePIDProcess)
self.secondColumn.addWidget(self.textQlabelStatus)
self.secondColumn.addWidget(self.textQlabeleStatusValue)
self.thirdColumn.addWidget(self.textQLabelNameProcess)
self.thirdColumn.addWidget(self.textValueNameProcess)
self.firstColumn.setContentsMargins(1, 0, 15, 0)
self.secondColumn.setContentsMargins(0, 0, 15, 0)
self.thirdColumn.setContentsMargins(0, 0, 15, 0)
self.allQHBoxLayout = QHBoxLayout()
self.allQHBoxLayout.addLayout(self.firstColumn, 0)
self.allQHBoxLayout.addLayout(self.secondColumn, 1)
self.allQHBoxLayout.addLayout(self.thirdColumn, 2)
self.setContentsMargins(15, 0, 15, 0)
self.setLayout(self.allQHBoxLayout)
# setStyleSheet
self.SetColors()
self.textQlabelStatus.setText("Статус")
self.textUpQLabelPIDProcess.setText("PID")
self.textQLabelNameProcess.setText("Имя")
def SetColors(self):
self.textUpQLabelPIDProcess.setStyleSheet('''
color: rgb(0, 0, 255);
''')
self.textDownQLabelValuePIDProcess.setStyleSheet('''
color: rgb(0, 0, 0);
''')
self.textQlabelStatus.setStyleSheet('''
color: rgb(0, 0, 255);
36
''')
self.textQLabelNameProcess.setStyleSheet('''
color: rgb(0, 0, 255);
''')
def SetLabels(self):
self.textUpQLabelPIDProcess = QLabel()
self.textDownQLabelValuePIDProcess = QLabel()
self.textQlabelStatus = QLabel()
self.textQlabeleStatusValue = QLabel()
self.textQLabelNameProcess = QLabel()
self.textValueNameProcess = QLabel()
def SetFonts(self, font):
self.textUpQLabelPIDProcess.setFont(font)
self.textDownQLabelValuePIDProcess.setFont(font)
self.textQlabelStatus.setFont(font)
self.textQlabeleStatusValue.setFont(font)
self.textQLabelNameProcess.setFont(font)
self.textValueNameProcess.setFont(font)
def SetPidValue(self, text):
self.textDownQLabelValuePIDProcess.setText(text)
def SetStatus(self, text):
self.textQlabeleStatusValue.setText(text)
def SetName(self, text):
self.textValueNameProcess.setText(text)
37
КОД ПРИЛОЖЕНИЯ УДАЛЕННОГО ТЕРМИНАЛА
Приложение Б
(обязательное)
38
#include <fcntl.h>
#include <iostream>
#include <sys/stat.h>
#include <unistd.h>
#include <string>
#include <fstream>
#include <signal.h>
#include <cstring>
#include "UsertData/UserData.h"
#include <vector>
#include <algorithm>
using namespace std;
int const STRING_LENGTH = 100;
string managerPID;
string ManagerPidPath;
int Ppid;
UserData userData;
string generalPipeName;
bool is_number(const std::string& s);
void InitPips(string &generalPipeName);
vector<string> splitStrings(string str, char dl);
void NotifyNotifyParent();
void InitSignals();
void ChooseTypeMsg(vector<string> msgData);
void SetNewName(vector<string> vector);
void SetStatus(vector<string> vector);
void ExecuteCommand(vector<string> vector)
void HandleUserSignal(int n){
cout.flush();
cout.clear();
cout<<"\nWork with SIGUSER1";
ifstream fifo {generalPipeName};
string line;
getline(fifo, line);
fifo.close();
cout<<"\nLOG_CLIENT: Recived\n" <<line;
char lineSpliter = ',';
vector<string> blocks = splitStrings(line, lineSpliter);
ChooseTypeMsg(blocks);
NotifyNotifyParent();}
39
void ChooseTypeMsg(vector<string> msgData) {
char lineSpliter = ':';
string msgType = splitStrings(msgData[0], lineSpliter)[1];
if (!is_number(msgType)){
cout<< "\nCLIENT_LOG: ERROR unavailable type msg\n ";
return;
}
int type = stoi(msgType);
cout << "\n msg type is" << type << "\n";
switch (type){
case 1:
SetNewName(msgData);
break;
case 2:
SetStatus(msgData);
break;
case 3:
ExecuteCommand(msgData);
break;
default:
cout<<"\nCLIENT_LOG: ERROR unavailable type msg \n";
}
}
void ExecuteCommand(vector<string> vector) {
char lineSplitter = ':';
string command = splitStrings(vector[4], lineSplitter)[1];
cout<<"\n\n\n\nResult of system command :\n\n\n\n\n";
system(command.c_str());
cout<<"\n\n\n\n\n";
}
void SetStatus(vector<string> vector) {
char lineSplitter = ':';
string newName = splitStrings(vector[4], lineSplitter)[1];
userData.setStatus(newName);
}
void SetNewName(vector<string> vector) {
char lineSplitter = ':';
string newName = splitStrings(vector[4], lineSplitter)[1];
40
userData.setName(newName);
}
void SignalQuitHandler(int n){
cout << "\nWork with SIGINT";
kill(Ppid, SIGUSR2);
int fdGeneralPipeName = open(generalPipeName.c_str(), O_WRONLY );
if(fdGeneralPipeName<0){
cout<<"\nLOG_CLIENT: ERROR HIRE\n";
perror("LOG: CAN'T write to pipe");
exit(0);
}
string
formatingMes
=
"messageType:9,PID:"+
userData.getPid()
+",status:"+
userData.getStatus()+",userName:"+userData.getName()+",msg:ps -e.";
int response = write(fdGeneralPipeName,formatingMes.c_str(), formatingMes.size());
close(fdGeneralPipeName);
exit(0);
}
int main(int argc, char *argv[]) {
cout<<"Process name = "<< argv[1];
setlocale(LC_CTYPE,"");
string processName(argv[1]);
userData = UserData(to_string((int)getpid()), processName, "1");
;
string targetPipeName;
__pid_t myPid = getpid();
cout<<"\nLOG_CLIENT: My PID is " <<userData.getPid() << endl;
if(argc==3){
Ppid = (int)getppid();
}else{
chdir("..");
chdir("..");
ManagerPidPath = get_current_dir_name() ;
ManagerPidPath += + "/Pipe/PidFile.txt";
ifstream rdl(ManagerPidPath);
getline(rdl, managerPID);
Ppid = stoi(managerPID);
}
InitPips(generalPipeName);
NotifyNotifyParent();
41
InitSignals();
while (true){
}
return 0;
}
void InitSignals() {
struct sigaction act, outI;
act.sa_handler = SignalQuitHandler;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_NODEFER;
sigaction(SIGINT, &act, &outI);
struct sigaction act2, outI1;
act.sa_handler = SignalQuitHandler;
sigemptyset(&act2.sa_mask);
act.sa_flags = SA_NODEFER;
sigaction(SIGQUIT, &act2, &outI1);
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_handler = HandleUserSignal;
sa.sa_flags = SA_NODEFER;
sigaction(SIGUSR1, &sa, NULL);
}
void NotifyNotifyParent() {
__pid_t parentPid = getppid();
kill(Ppid, SIGUSR1);
cout<<"\nLOG_CLIENT :PArent PID is "<< parentPid<<endl;
cout<< "\nLOG_CLIENT: start writing ..... \n";
cout<<generalPipeName;
int fdGeneralPipeName = open(generalPipeName.c_str(), O_WRONLY );
if(fdGeneralPipeName<0){
cout<<"\nLOG_CLIENT: ERROR HIRE\n";
perror("LOG: CAN'T write to pipe");
exit(0);
}
string
formatingMes
=
"messageType:1,PID:"+
userData.getPid()
+",status:"+
userData.getStatus()+",userName:"+userData.getName()+",msg:ps -e.";
int response = write(fdGeneralPipeName,formatingMes.c_str(), formatingMes.size());
close(fdGeneralPipeName);
42
cout<< "Writed = "<<response<<endl;
cout.flush();
}
void InitPips(string &generalPipeName) {
generalPipeName= get_current_dir_name();
generalPipeName += "/Pipe/GeneralPipe.p";
}
vector<string> splitStrings(string str, char dl)
{
string word = ""; int num = 0;
str = str + dl;
int l = str.size();
vector<string> substr_list;
for (int i = 0; i < l; i++) {
if (str[i] != dl)
word = word + str[i];
else {
if ((int)word.size() != 0)
substr_list.push_back(word);
word = "";
}
}
return substr_list;
}
bool is_number(const std::string& s)
{
return !s.empty() && std::find_if(s.begin(),
s.end(), [](char c) { return !std::isdigit(c); }) == s.end();
}
43
Download