EYED

Eyed предназначен для захвата и инициализации SHM, запуска pluginов а также для их перезапуска при изменениях конфигурации и останова. Кроме того eyed декрементирует счетчики в статусный ячейках плагинов. Информацию о начальных установках и запускаемых pluginах eyed получает из структур, создаваемых библиотекой разбора parse. Данная библиотека достает информацию из базы данных, в виде структур, удобных для использования демоном и плагинами. Стоить отметить, что eyed интересуют в основном количество структур, в то время как плагинам интересно их содержимое. Необходимую информацию eyed помещает в Shared Memory и затем запускает pluginы. Каждый plugin определяет свою область в SHM и, в зависимости от задачи, записывает или считывает данные. Кроме того, eyed обеспечивает корректную остановку с высвобождением SHM памяти.

Не останавливаясь сейчас подробно на библиотеке разбора конфигурации Parse я лишь опишу создаваемые ей структуры. Это необходимо для более полного понимания работы демноа eyed.

Parse

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

Большинство структур, создаваемых Parse представляют собой односвязные списки. Поля структур состоят из указателей на слудующую структуру или указателей на char (поле данных). Поля данных представленны именно как character по двум причинам:

Функция OpenCfgFile(char *name_cfg, int flag) с параметрами NULL, FL_OPENMAINFILE разбирает все конфигурационные таблицы плагинов и создает в памяти экземпляра программы необходимы структуры с данными. Функция GetFirstPlugin() возвращает указатель на первую из свазанного списка структуру Plugin.

Структура Plugin имеет следующий вид:


typedef struct __Plugin
{
  char *NameOfExec;
  char *PathOfExec;
  char *NameOfCfg;
  char *PathOfCfg;

  Command *p_cmd;

  PutSection * ps_next;

  struct __Plugin * p_next;
} Plugin;

Где Хотя NameOfCfg и NameOfExec зачастую одинаковы, необходимо отметить, что один и тот же исполняемый файл может быть несколько раз запущен демоном eyed с разными параметрами NameOfCfg.

Поле p_cmd структуры Plugin указывает на связанный список вот следующих структур:


typedef struct __Command
{
    char *c_cmd;
    char *c_value;

    struct __Command *c_next;
} Command;

Где Набор параметров плагина и их значений может выгладеть например так (в терминах базы данных):

eye=# select * from parametrs  where nameofcfg = 'smtp';
 nameofcfg |   parametr   |         value         
-----------+--------------+-----------------------
 smtp      | LogFile      | /var/log/eye/smtp.err
 smtp      | SendTime     | 15
 smtp      | ChaildNumber | 10
 smtp      | StartCounter | 30
(4 rows)

eye=#

Поле ps_next структуры Plugin указывает на связанный список структур типа PutSection в которых находится информация о сервисах, проверяемых плагином. Структура PutSection имеет следующий вид:


typedef struct __PutSection
{
  char * ps_name;
  Host * h_next;

  struct __PutSection *ps_next;
} PutSection;

Где

Поле h_next структуры PutSection указывает на связанный список структур типа Host. Именно в этих структурах находится информация о проверяемых сервисах. Структура Host имеет следующий вид:


typedef struct __Host
{
  int h_key;

  int h_flag;
  char *_h_addr;

  int h_addcount;
  char ** h_adddata;
  struct __Host * h_next;
} Host;

Где

Плагином используется вызов функции OpenCfgFile() со следующими параметрами: имя_конфигурационной_таблицы,0. Что приводит к восстановлению в памяти только информации по данному плагину, т.е. его параметров и его записей о проверяемых сервисах.

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

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

SHM и работа с ней

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

Идентификатор IPC определяется переменной типа key_t которую возвращает ftok(). Он может использоваться не только для уникальной идентификации взаимодействия через shared memory, но и для идентификации взаимодействий через семафоры и очереди сообщений.ftok() это функция от имени файла и переменной типа char возвращает переменную key_t размером в 4 байта.

Сегмент Shared Memory создается функцией shmget(). Это функция от ключа key_t, требуемого размера памяти и прав доступа.shmget() возвращает положительный целый идентификатор сегмента SHM типа int или -1 в случае неудачи.

Cозданный и идентифицированный функцией shmget() сегмент Shared Memory присоединяется к адресному пространству программы с помощью функции shmat(). В качестве параметров shmat() принимает целый идентификатор сегмента, желаемый адрес присоединения (типа void* ) или 0 и флаг, задающий округление адреса присоединения до ближайшего значения кратного SHMLBA.

Информацию и сегменте SHM можно получить с помощью функции shmctl(). Это функция, естественно, целого идентификатора SHM, команды и указателя на структуру shmid_ds. Структура shmid_ds содержит размер сегмента в байтах, PID процесса,который последним изменял содержимое shm, PID процесса создавшего этот сегмент, число процессов, присоединенных на текущий момент к этому сегменту, время последнего присоединения к сегменту и отсоединения, время последнего изменения этой информации с помощью shmctl() и некоторую служебную информацию. Кроме того, shmctl() позволяет менять владельца сегмента, группу и атрибуты доступа при наличии, конечно, соответствующих прав. Также shmctl() позволяет пометить сегмент SHM как удаленный. Реально сегмент не удаляется пока к нему присоединен хоть один процесс (shmat() ). Но такая пометка препятствует присоединению новых процессов.

Функция shmdt() отделяет сегмент SHM от адресного пространства процесса. В качестве параметра в shmdt() передается указатель на адрес присоединенного сегмента в адресном пространстве процесса.

Структуры лежащие в SHM

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

Первое что лежит в SHM - это ее длина. Таким образом мы избавились от передачи этого параметра в plugin каким-либо другим путем. Plugin просто берет первые 4 байта SHM, смотрит лежащее в них значение и затем берет всю Shared memory. Здесь лежит код который проводит эту операцию SHMGET.C Количество pluginов используется библиотекой работы с SHM в служебных целях. Именно с ее помощью plugin получает указатель на принадлежащий ему участок SHM.

Опять первой лежит длина области принадлежащей pluginу. Но теперь это значение используется в служебных целях. Как, впрочем, и длина имени области pluginа. Имя области совпадает с именем таблицы конфигурации pluginа, что должно обеспечить достаточную уникальность. Демон eyed берет это имя из поля NameOfCfg соответствующей структуры Plugin. Ячейка статуса предназначена для наблюдения за состоянием pluginа. Например в случае ошибки в конфигурационном файле или невозможности его открыть plugin записывает в эту ячейку статус ошибки и прекращает работу. В случае когда ping не может открыть raw socket (для этого требуются права суперпользователя) он также помещает сообщение об ошибке в статусную ячейку и выходит. Количество put секций является служебной информацией. Данные в put секции в целом похожи на данные в plugin секции. За отсутствием статусной ячейки, которая относится к pluginу в целом. Аналогично имя put секции записываемое после собственной длины берется из поля ps_name соответствующей структуры PutSection. Число ячеек (Number Of Cells) также является служебной информацией. Таким образом в Shared Memory ячейку можно однозначно определить тремя параметрами:

  • Именем CFG секции
  • Именем Put секции
  • Номером ячейки в пределах Put секции.
Таким образом устанавливается соответствие между параметрами запрашиваемого сервиса и статусной информацией разделяемой между демоном и плагинами. Различная методология доступа к ячейкам будет рассмотрена ниже. Собственно в ячейке CELL_M лежит нисходящий счетчик, который eyed декрементирует каждую секунду и, при достижении 0, включает восходящий счетчик. Кроме того в статус, лежащий в CELL_M plugin может записать информацию конкретизирующую ошибку. Например 'error in resolve' для pingа.

Идеология взаимодействия демона и pluginа в пределах ячейки

Каждая ячейка CELL_M, вне зависимости от того является ли она ячейкой статуса pluginа или ячейкой мониторинга, состоит из нисходящего счетчика cell, восходящего счетчика rate и переменной статуса status.


typedef long int CELL;

typedef struct cell_m
    {
    CELL rate;
    int status;
    CELL cell;
} CELL_M;

Если сервис работает (хост pingуется) то plugin записывает в нисходящий счетчик cell значение StartCounter и 0 в переменную статуса status. Главный демон каждую секунду вычитает 1 из cell и при достижении 0 заменяет status на 1 (состояние DOWN). Если status отличен от нуля, то прибавляется 1 к rate, а cell eyed не трогает. Eyed изменяет status только с 0 на 1. И только в этом случае он обнуляет rate. При всех остальных изменениях статуса plugin должен сам заботиться об обнулении счетчика rate. Явного обнуления счетчика cell не происходит поскольку в случае стабильно работающего сервиса его значение не должно сильно отклоняться от значения StartCounter. А при неработающем сервисе счетчик через некоторое время обнулится из-за ежесекундного декрементирования главным демоном. Некоторая статистическая исследовательская задача заключается в подборе начального значения StartCounter и периода проверки SendTime. Значение StartCounter должно быть достаточно большим, для того чтобы plugin смог обработать возможные ошибки. Т.е. чтобы переключение из состояния UP status=0 в состояние со статусом отличным от 0 не проходило через состояние со status=1. SendTime должно подбираться таким образом, чтобы случайная ошибка (потеря пакета в сети, обрыв сессии) при рабочем состоянии сервиса не приводила к переходу в состояние DOWN.

Механика взаимодействия демона и pluginа в пределах ячейки

Вышеописанная идеология относилась к собирающему информацию pluginу. Механика одинакова как для собирающих так и для раздающих pluginов. С той лишь разницей, что раздающие plugiны не изменяют данные в ячейках. Хотя ничто этому не препятствует. Будем считать, что plugin имеет указатель на начало сегмента Shared Memory mem. Процедура получения указателя на ячейку во многом сходна с процедурой получения записи Host из структуры Plugin. Сначала с помощью функции GetCfgByName() получаем указатель на количество Put секций. Этот указатель используется в качестве параметра в функции GetPutByName() , которая возвращает указатель на число ячеек CELL_M. В свою очередь этот указатель используется как параметр в функции GetCELL_M() , которая последовательно возвращает указатели на CELL_M. Т.е. при каждом следующем вызове мы будем получать указатель на следующую ячейку. Это удобно, поскольку pluginы обычно последовательно просматривают/модифицируют содержимое ячейки. При таком способе обращения необходимо знать имя конфигурационного файла plugina которое является идентификатором области pluginа в SHM, имя put секции, номер ячейки и указатель на начало сегмента Shared Memory. указатель на начало сегмента Shared Memory передается как параметр в функцию GetCfgByName().

Указатель на ячейку можно получить также зная ее имя в структуре Host->_h_addr с помощью функции GetCELL_MByName(). Кроме имени конфигурационного файла, имени put секции и _h_addr в GetCELL_MByName() необходимо передать указатель на начало связанного списка структур Host. Поэтому такой способ неудобен для обращения к нескольким последовательным ячейкам. Имя ,лежащее в Host->_h_addr - это обычно IP или domain адрес. GetCELL_MByName() возвращает указатель на ячейку соответствующую Host->_h_addr.

Функция GetStatusByName() возвращает указатель на статусную ячейку pluginа. Естественно, ей требуется его идентификатор (имя конфигурационного файла).

Контроль и управление

Контроль и управление осуществляется с консоли UNIX. Остановка демона и перечитывание конфигурационных файлов осуществляется с помощью общепринятых в UNIX сигналов. Перезапуск демона с перечитыванием конфигурации осуществляется сигналом SIGHUP. Остановка всей системы с закрытием всех плагинов и освобождением Shared Memory осуществляется по сигналу SIGTERM. Перезапуск плагинов осуществляется по сигналу SIGINT. При смене конфигурации только перезапуск плагинов может привести к некорректному использованию ими SHM. Правка конфигурации и запуск демона происходит в командном режиме. Конфигурационные таблицы делятся на две основные части. Это put секции с данными о сервисах и некоторые параметры, которые plugin может использовать, а может и установить по умолчанию при их отсутствии. В общем случае одинаковые параметры могут означать различные для разных pluginov команды. Параметры лежат в структурах Command а содержимое put секций конфигурационных таблиц и памяти идентично. Об этом заботится eyed, который при запуске или перезапуске переносит данные в SHM. При включенном ключе DEBUG мониторинг может осуществляться просмотром выдачи отладочной информации в ASCII кодах. При этом eyed не становится демоном. Т.е. не становится лидером сеанса, не закрывает stdin, stdout, stderr, не игнорирует сигналы SIGTTOU, SIGTTIN, SIGTSTP. Кроме того, с помошью программы dump может быть представлен в ASCII кодах и записан в файл мгновенный "слепок" состояния SHM. В качестве основного средства раздачи информации применяются HTML страницы.

Приложение 1: Библиотеки constr и view.