klish3.ru.md 59 KB

Klish 3

Обзор

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

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

Примером встроенных систем может служить управляемое сетевое оборудование. Исторически в этом сегменте сложилось два основных подхода к организации интерфейса командной строки. Условно их можно назвать подход "Cisco" и подход "Juniper". И Cisco и Juniper имеют два режима работы - командный режим и режим конфигурирования. В командном режиме введенные команды немедленно исполняются, но не влияют на конфигурацию системы. Это команды просмотра состояния, команды управления устройством, но не изменения его конфигурации. В режиме конфигурирования идет настройка оборудования и сервисов. В Cisco команды конфигурирования также исполняются немедленно, меняя конфигурацию системы. В Juniper конфигурацию можно условно представить себе, как текстовый файл, изменения в котором производятся с помощью стандартных команд редактирования. При редактировании изменения не применяются в системе. И только по специальной команде весь накопленный комплекс изменений применяется разом, чем обеспечивается согласованность изменений. При Cisco подходе похожее поведение также можно эмулировать, проектируя команды определенным образом, но для Cisco это менее естественный способ.

Какой из подходов лучше и проще в конкретном случае - определяется задачей. Klish в первую очередь расчитан на подход Cisco, т.е. на немедленно выполняемые команды. Однако проект имеет систему плугинов, что позволяет расширять его возможности. Так плугин klish-plugin-sysrepo, реализованный отдельным проектом, работая на основе хранилища sysrepo, позволяет организовать Juniper подход.

Основные сведения

Основа проекта klish - библиотека libklish. На ее основе построены клиент klish и сервер klishd. Основную работу выполняет сервер klishd. Он загружает конфигурацию команд и ожидает запросов от клиентов. Взаимодействие между клиентами и сервером происходит по UNIX-сокетам с использованием специально разработанного для этой цели протокола KTP (Klish Transfer Protocol). Задача клиента - передача ввода от оператора на сервер и получение от него результата для показа оператору. Клиент не знает, какие команды существуют, как их выполнять. Все это делает серверная сторона. Так как клиент имеет относительно простой код, не трудно реализовать альтернативные программы - клиенты, например графический клиент или клиент для автоматизированного управления. Сейчас написан только текстовый клиент klish. Когда клиент соединяется с сервером, порождается отдельный процесс для обслуживания конкретного клиента. При завершении сессии, процесс также завершается. Таким образом типичное применение klish - это заранее запущенный в системе сервер klishd и клиенты, подключающиеся к нему по мере надобности.

Klish имеет два типа плугинов. Плугины для загрузки конфигурации команд (директория dbs/ в дереве исходных кодов) и плугины, реализующие действия для команд (директория plugins/ в дереве исходных кодов).

Для настройки параметров сервера используется конфигурационный файл /etc/klish/klishd.conf. Альтернативный конфигурационный файл можно указать при запуске сервера klishd.

Для настройки параметров клиента используется конфигурационный файл /etc/klish/klish.conf. Альтернативный конфигурационный файл можно указать при запуске клиента klish.

Загрузка конфигурации команд

В составе klish существуют следующие плугины dbs (DataBases) для загрузки конфигурации команд (схемы):

  • expat - Использует библиотеку expat для загрузки конфигурации из XML.
  • libxml2 - Использует библиотеку libxml2 для загрузки конфигурации из XML.
  • roxml - Использует библиотеку roxml для загрузки конфигурации из XML.
  • ischeme - Использует встроенную в C-код конфигурацию (Internal Scheme).

Существует внутреннее представление схемы, совпадающее с ischeme. Остальные плугины переводят внешние представление в ischeme, а klish обрабатывает ischeme внутренними механизмами.

Установленные плугины dbs находятся в /usr/lib/klish/dbs (если конфигурировать сборку с --prefix=/usr). Их имена kdb-<имя>.so, например /usr/lib/klish/dbs/kdb-libxml2.so.

Плугины исполняемых функций

Каждая команда klish выполняет какое-либо действие или несколько действий сразу. Эти действия надо как-то описывать. Если смотреть внутрь реализации, то klish может запускать только исполняемый откомпилированный код из плугина. Плугины содержат так называемые символы (symbol, sym), которые, по-сути, представляют собой функции с единым зафиксированным API. На эти символы могут ссылаться команды klish. В свою очередь символ может выполнять сложный код, например запускать интерпретатор shell со скриптом, определяемым при описании команды klish в конфигурационном файле. Или же другой символ может исполнять Lua скрипт.

Klish умеет получать символы только из плугинов. Стандартные символы реализованы в плугинах включенных в состав проекта klish. В состав klish входят следующие плугины:

  • klish - Базовый плугин. Навигация, типы данных (для параметров), вспомогательные функции.
  • lua - Исполнение скриптов Lua. Механизм встроен в плугин и не использует внешнюю программу для интерпретации.
  • script - Исполнение скриптов. Учитывает шебанг (shebang) для определения какой интерпретатор использовать. Таким образом могут выполняться не только shell скрипты, но и скрипты на других интерпретируемых языках. По умолчанию используется интерпретатор shell.

Пользователи могут писать свои плугины и использовать собственные символы в командах klish. Установленные плугины находятся в /usr/lib/klish/plugins (если конфигурировать сборку с --prefix=/usr). Их имена kplugin-<имя>.so, например /usr/lib/klish/plugins/kplugin-script.so.

Символы бывают "синхронные" и "асинхронные". Синхронные символы исполняются в адресном пространстве klishd, для асинхронных порождается отдельный процесс.

Структура XML конфигурации

Основным способом описания klish команд на сегодняшний день являются XML файлы. В данном разделе все примеры будут основаны на XML элементах.

Области видимости

Команды организованы в "области видимости", называемые VIEW. Т.е. каждая команда принадлежит какому-либо VIEW и в нем определена. При работе в klish всегда существует "текущий путь". По умолчанию, при запуске klish текущим путем назначается VIEW с именем "main". Текущий путь определяет какие команды в данный момент видны оператору. Текущий путь можно менять командами навигации. Например перейти в "соседний" VIEW, тогда текущим путем станет этот соседний VIEW, вытеснив старый. Другая возможность - "уйти вглубь", т.е. зайти во вложенный VIEW. Тогда текущий путь станет двух-уровневым, подобно тому, как можно зайти во вложенную директорию в файловой системе. Когда текущий путь имеет более одного уровня, оператору доступны команды самого "глубокого" VIEW, а также команды всех вышележащих уровней. С помощью команд навигации можно выходить из вложенных уровней. При смене текущего пути используемая команда навигации определяет будет ли VIEW текущего пути вытеснен на том же уровне вложенности другим VIEW, либо новый VIEW станет вложенным и в текущем пути появится еще один уровень. То, как VIEW определены в XML файлах не влияет на то, может ли VIEW быть вложенным.

При определении VIEW в XML файлах, одни VIEW могут быть вложены в другие VIEW. Не надо путать эту вложенность с вложенностью при формировании текущего пути. VIEW, определенный внутри другого VIEW добавляет свои команды к командам родительского VIEW, но при этом может адресоваться и отдельно от родительского. Кроме этого существуют ссылки на VIEW. Объявив такую ссылку внутри VIEW, мы добавляем команды того VIEW, на который указывает ссылка, к командам текущего VIEW. Можно определить VIEW со "стандартными" командами и включать ссылку на этот VIEW в другие VIEW, где требуются эти команды, не переопределяя их заново.

В XML файлах конфигурации для объявления VIEW используется тег VIEW.

Команды и параметры

Команды (тег COMMAND) могут иметь параметры (тег PARAM). Команда определяется внутри какого-либо VIEW и пренадлежит ему. Параметры определяются внутри команды и, в свою очередь, принадлежат ей. Команда может состоять только из одного слова, в отличие от команд в klish предыдущих версий. Если нужно определить многословную команду, то создаются вложенные команды. Т.е. внутри команды, идентифицируемой по первому слову многословной команды, создается команда, идентифицируемая по второму слову многословной команды и т.д.

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

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

Как и VIEW, команды и параметры могут быть ссылками. В этом случае ссылку можно рассматривать просто как подстановку того объекта, на который указывает ссылка.

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

Тип параметра

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

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

Действие

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

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

Фильтры

Фильтры представляют собой команды, которые обрабатывают вывод других команд. Фильтр может быть указан в командной строке после основной команды и знака |. Фильтр не может быть самостоятельной командой и использоваться без основной команды. Примером типичного фильтра может служить аналог UNIX утилиты grep. Фильтры могут использоваться один за другим, с разделителем |, как это делается в shell.

Если команда не является фильтром, то она не может использоваться после символа |.

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

Контейнеры параметров

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

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

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

Приглашения командной строки

Для формирования приглашения командной строки, которую видит оператор, используется конструкция PROMPT. Тег PROMPT должен быть вложен внутрь VIEW. Разные VIEW могут иметь разные приглашения. Т.е. в зависимости от текущего пути, оператор видит разное приглашение. Приглашение может быть динамическим и генерироваться действиями ACTION, заданными внутри PROMPT.

Автодополнение и подсказки

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

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

Условный элемент

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

Плугин

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

Горячие клавиши

Для удобства оператора в конфигурации команд klish могут быть заданы "горячие клавиши". Тег для задания горячих клавиш - HOTKEY. Список горячих клавиш передается сервером klishd клиенту. Клиент на свое усмотрение использует эту информацию или не использует. Например для клиента автоматизированного управления информация о горячих клавишах не имеет смысла.

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

Ссылки на элементы

Некоторые теги имеют атрибуты, которые являются ссылками на другие, определенные в файлах конфигурации, элементы. Например VIEW имеет атрибут ref, с помощью которого в текущем месте схемы создается "копия" стороннего VIEW. Или тег PARAM имеет атрибут ptype, с помощью которого ссылается на PTYPE, определяя тип параметра. Для организации ссылок в klish предусмотрен свой "язык". Можно сказать это сильно упрощенный аналог путей в файловой системе или XPath.

Путь до элемента в klish набирается, указывая все его родительские элементы с разделителем /. Именем элемента считается значение его атрибута name. Путь начинается также с символа /, обозначая корневой элемент.

Относительные пути в данный момент не поддерживаются

<KLISH>

<PTYPE name="PTYPE1">
	<ACTION sym="sym1"\>
</PTYPE>

<VIEW name="view1">

	<VIEW name="view1_2">
		<COMMAND name="cmd1">
			<PARAM name="par1" ptype="/PTYPE1"/>
		</COMMAND>
	</VIEW>

</VIEW>

<VIEW name="view2">

	<VIEW ref="/view1/view1_2"/>

</VIEW>

</KLISH>

Параметр "par1" ссылается на PTYPE, используя путь /PTYPE1. Имена типов принято обозначать большими буквами, чтобы проще отличать типы от других элементов. Здесь тип определен на самом верхнем уровне схемы. Базовые типы обычно так и определяются, но PTYPE не обязан находится на верхнем уровне и может быть вложен во VIEW или даже PARAM. В таком случае он будет иметь более сложный путь.

VIEW с именем "view2" импортирует команды из VIEW с именем "view1_2", используя путь /view1/view1_2.

Если, предположим, понадобится ссылка на параметр "par1", то путь будет выглядеть так - /view1/view1_2/cmd1/par1.

На следующие элементы нельзя ссылаться. Они не имеют пути:

  • KLISH
  • PLUGIN
  • HOTKEY
  • ACTION

Теги

KLISH

Любой XML файл с конфигурацией klish должен начинаться открывающим тегом KLISH и заканчиваться закрывающим тегом KLISH.

<?xml version="1.0" encoding="UTF-8"?>
<KLISH
	xmlns="https://klish.libcode.org/klish3"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="https://src.libcode.org/pkun/klish/src/master/klish.xsd">

<!-- Тут любая конфигурация для klish -->

</KLISH>

Заголовок вводит свое пространство имен XML по умолчанию "https://klish.libcode.org/klish3". Заголовок стардартный и чаще всего нет смысла его менять.

PLUGIN

Самостоятельно klish не загружает никаких плугинов исполняемых функций. Соответственно пользователю недоступны никакие символы для использования в действиях ACTION. Загрузку плугинов нужно в явном виде прописывать в файлах конфигурации. Для этого используется тег PLUGIN.

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

<PLUGIN name="klish"/>

Тег PLUGIN не может содержать других вложенных тегов.

Атрибут name

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

<ACTION sym="my_sym"\>

<ACTION sym="my_sym@my_plugin"\>

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

Если не указаны атрибуты id и file, то name используется также для формирования имени файла плугина. Плугин должен иметь имя kplugin-<name>.so и находиться в директории /usr/lib/klish/plugins (если klish был сконфигурирован с --prefix=/usr).

Атрибут id

Атрибут используется для формирования имени файла плугина, если не указан атрибут file. Плугин должен иметь имя kplugin-<id>.so и находиться в директории /usr/lib/klish/plugins (если klish был сконфигурирован с --prefix=/usr).

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

<PLUGIN name="alias_for_klish" id="klish"\>

<ACTION sym="nav@alias_for_klish"\>

Атрибут file

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

<PLUGIN name="my_plugin" file="/home/ttt/my_plugin.so"\>

<ACTION sym="my_sym@my_plugin"\>

Данные внутри тега

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

<PLUGIN name="sysrepo">
	JuniperLikeShow = y
	FirstKeyWithStatement = n
	MultiKeysWithStatement = y
	Colorize = y
</PLUGIN>

HOTKEY

Для более удобной работы в интерфейсе командной строки, для часто используемых команд могут быть заданы "горячие клавиши". Горячая клавиша определяется с помощью тега HOTKEY.

Для работы горячих клавиш нужна поддержка в клиентской программе, которая подключается к серверу klishd. Клиент "klish" имеет такую поддержку.

Тег HOTKEY не может иметь вложенных тегов. Какие либо дополнительные данные внутри тега не предусмотрены.

Атрибут key

Атрибут определяет, что именно должен нажать оператор для активации горячей клавиши. Поддерживаются комбинации с клавишей "Ctrl". Например ^Z означает, что должна быть нажата комбинация клавиш "Ctrl" и "Z".

<HOTKEY key="^Z" cmd="exit"\>

Атрибут cmd

Атрибут определяет какое именно действие будет выполнено при нажатии оператором горячей клавиши. Значение атрибута cmd разбирается по тем же правилам, как любая другая команда вручную введенная оператором.

<COMMAND name="exit" help="Exit view or shell">
	<ACTION sym="nav">pop</ACTION>
</COMMAND>

<HOTKEY key="^Z" cmd="exit"\>

Команда, используемая в качестве значения атрибута cmd должно быть определена в файлай конфигурации. Приведенный пример выполнит ранее определенную команду exit при нажатии сочетания клавиш "Ctrl^Z".

ACTION

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

Основным атрибутом ACTION является sym. Действие может выполнять только символы (функции), определенные в плугинах. Атрибут sym ссылается на такой символ.

Действия могут выполняться не только командой. Далее приведен список тегов, внутри которых может встречаться ACTION:

  • ENTRY - для чего будет использован ACTION, определяется параметрами ENTRY.
  • COMMAND - выполняется действие, определенное в ACTION, при вводе оператором соответствующей команды.
  • PARAM - тоже, что и для COMMAND.
  • PTYPE - ACTION определяет действия для проверки значения введенного оператором параметра, имеющего соответствующий тип.

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

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

<COMMAND name="cmd1">
	<ACTION sym="sym1"\>
	<SWITCH min="0">
		<COMMAND name="opt1">
			<ACTION sym="sym2"\>
		</COMMAND>
		<COMMAND name="opt2"\>
		<PARAM name="opt3" ptype="/STRING">
			<ACTION sym="sym3"\>
		</PARAM>
	</SWITCH>
</COMMAND>

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

Так если оператор ввел команду cmd1, то механизм разбора распознает команду с именем "cmd1" и будет искать ACTION непосредственно в этом элементе. Будет найден ACTION с символом "sym1".

Если оператор ввел команду cmd1 opt1, то строка "opt1" будет распознана, как параметр (он же подкоманда) с именем "opt1". Поиск идет с конца, поэтому сначала будет найден ACTION с символом "sym2". Как только блок действий найден, больше поиск действий производиться не будет и "sym1" найден не будет.

Если оператор ввел команду cmd1 opt2, то будет найдено действие с символом "sym1", так как элемент "opt2" не имеет собственных вложенных действий и поиск уходит наверх к родительским элементам.

Если оператор ввел команду cmd1 arbitrary_string, то будет найдено действие с символом "sym3".

Атрибут sym

Атрибут ссылается на символ (функцию) из плугина. Эта функция будет выполнена при выполнении ACTION. В качестве значения может быть указано имя символа, например sym="my_sym". В таком случае поиск символа будет происходить по всем загруженным плугинам. Если указан плугин, в котором надо искать символ, например sym="my_sym@my_plugin", то в других плугинах поиск производиться не будет.

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

Атрибут lock

Внимание: атрибут пока не реализован

Некоторые действия требуют атомарности и эксклюзивного доступа к ресурсу. При работе в klish это не обеспечивается автоматически. Два оператора могут независимо, но одновременно запустить на выполнение одну и ту-же команду. Для обеспечения атомарности или/и эксклюзивного доступа к ресурсу могут использоваться блокировки lock. Блокировки в klish являются именованными. Атрибут lock указывает, блокировку с каким именем захватит ACTION при выполнении. Например lock="my_lock", где "my_lock" - имя блокировки. Захват блокировки с одним именем никак не влияет на блокировки с другим именем. Таким образом в системе может быть реализована не одна глобальная блокировка, а несколько отдельных, основанных, например на том, какой именно ресурс защищает блокировка.

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

Атрибут interactive

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

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

Если действие объявлено интерактивным, то при его выполнении создается псевдо-терминал для обмена с пользователем. Не рекомендуется выставлять флаг интерактивности для не-интерактивных действий.

Атрибут permanent

Система klish может быть запущена в режиме "dry-run", когда все действия не будут в реальности выполняться, а их код возврата всегда будет иметь значение "успех". Такой режим может использоваться для тестирования, для проверки корректности входящих данных и т.д.

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

Флаг permanent может менять поведение действия в режиме "dry-run". Возможные значения атрибута - true и false. По умолчанию false, т.е. действие не является "постоянным" и будет отключено в режиме "dry-run".

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

Атрибут sync

Символ может выполняться "синхронно" или "асинхронно". В синхронном режиме код символа будет запущен прямо в контексте текущего процесса - сервера klishd. В асинхронном режиме для запуска кода символа будет порожден (fork()) отдельный процесс. Запуск символа в асинхронном режиме является более безопасным, так как ошибки в коде не повлияют на процесс klishd. Рекомендуется использовать асинхронный режим.

Возможные значения атрибута - true и false. По умолчанию false, т.е. символ будет выполняться асинхронно.

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

Атрибут update_retcode

В одной команде может содержаться несколько элементов ACTION. Это называется "блок действий". Каждое из действий имеет свой код возврата. Однако команда вцелом тоже должна иметь код возврата и этот код должен быть одним значением, а не массивом.

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

Иногда требуется, чтобы в независимости от кода возврата определенного действия выполнение блока продолжилось. Для этого может использоваться атрибут update_retcode. Атрибут может принимать значение true или false. По умолчанию используется true. Это означает, что код возврата текущего действия влияет на общий код возврата. На этом этапе общему коду возврата будет присвоено значение текущего кода возврата. Если значение флага установлено в значение false, то текущий код возврата игнорируется и никак не будет влиять на формирование общего кода возврата. Также и выполнение блока действий не будет прервано в случае ошибки на этапе выполнения текущего действия.

Атрибут exec_on

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

  • success - текущее действие будет отправлено на выполнение, если значение общего кода возврата на данный момент - "успех".
  • fail - текущее действие будет отправлено на выполнение, если значение общего кода возврата на данный момент - "ошибка".
  • always - текущее действие будет выполнено вне зависимости от общего кода возврата.
  • never - действие не будет выполняться ни при каких условиях.

По умолчанию используется значение success, т.е. действия выполняются, если до этого не было ошибок.

<ACTION sym="printl">1</ACTION>
<ACTION sym="printl" exec_on="never">2</ACTION>
<ACTION sym="printl">3</ACTION>
<ACTION sym="printl" exec_on="fail">4</ACTION>
<ACTION sym="script">/bin/false</ACTION>
<ACTION sym="printl">6</ACTION>
<ACTION sym="printl" exec_on="fail" update_retcode="false">7</ACTION>
<ACTION sym="printl" exec_on="always">8</ACTION>
<ACTION sym="printl" exec_on="fail">9</ACTION>

Данный пример выведет на экран:

1
3
7
8

Строка "1" выведется, потому что вначале выполнения блока действий общий код возврата принимается равным значению "успех", а так же значение exec_on по умолчанию равно success.

Строка "2" не выведется, потому что on_exec="never", т.е. не выполнять ни при каких условиях.

Строка "3" выполнится, потому что предыдущее действие (строка "1") выполнилось успешно.

Строка "4" не выполнится, потому что стоит условие on_exec="fail", а при этом предыдущее действие "3" выполнилось успешно и установило общий код возврата в значение "успех".

Строка "5" выполнится и переведет общий код возврата в значение "ошибка".

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

Строка "7" выведется, так как стоит условие on_exec="fail", текущий общий код возврата действительно равен "ошибке". Обратите внимание, что хотя само действие выполнится успешно, общий код возврата не будет изменен, так как использован атрибут update_retcode="false".

Строка "8" выведется, потому что стоит условие on_exec="always", что означает выполнить действие вне зависимости от текущего общего кода возврата.

Строка "9" не выведется, потому что строка "8" изменила общий код возврата на значение "успех".

Данные внутри тега

Данные внутри тега ACTION используются по усмотрению самого символа, указанного атрибутом sym. Как пример можно привести символ script из плугина script. Этот символ использует данные внутри тега, как код скрипта, который он должен выполнить.

<COMMAND name="ls" help="List root dir">
	<ACTION sym="script">
	ls /
	</ACTION>
</COMMAND>

<COMMAND name="pytest" help="Test for Phyton script">
	<ACTION sym="script">#!/usr/bin/python
	import os
	print('ENV', os.getenv("KLISH_COMMAND"))
	</ACTION>
</COMMAND>

Обратите внимание, что в команде "pytest" указан шебанг #!/usr/bin/python, который указывает с помощью какого интерпретатора нужно выполнять скрипт.

ENTRY

VIEW

COMMAND

FILTER

PARAM

SWITCH

SEQ

PTYPE

PROMPT

HELP

COMPL

COND