Исполнение команд операционной системы на сервере Caché
По мотивам статьи в Developer Connection написал свою реализациию класса для вызова команд операционной системы сервера, где установлена Caché или Ensemble.
Класс может пригодиться, когда нужно из Caché что-то сделать на хостовой ОС: распорядиться файлами бэкапов, настроить работу с системой контроля версий и другие полезные действия.
Класс позволяет выполнить команду на любой хостовой ОС и получить ответ в глобал или локальную переменную.
Код класса с комментариями
Class Server.Manage Extends %RegisteredObject
{
Parameter cmdlist = "dir,type,ver";
/// метод убирает из командной строки последовательности команд
/// оставляет только разрешённые команды
ClassMethod safeCmd(cmd) As %String
{
set cmd=$piece(cmd,"&&") // оставляем одну команду
set cmd=$piece(cmd,"|")
for i=1:1:$length(..#cmdlist,",") set safe($p(..#cmdlist,",",i))="" // список разрешённых команд
if '$data(safe($piece(cmd," "))) set cmd=""
quit cmd
}
/// метод выполняет команду cmd на сервере и складывает результат в глобал ad так, что:
/// @ad=количество строк
/// @ad@(номер строки)=значениеa
ClassMethod runCmd(cmd, ad As %String) As %Status
{
set sc=$$$OK
do $SYSTEM.Process.SetZEOF(1) // выключаем режим генерации ошибки на EOF
// делаем командную строку безопасной
set cmd(1)=..safeCmd(cmd)
quit:cmd(1)="" $$$ERROR($$$GeneralError,"Недопустимая команда "_cmd)
set cmd=cmd(1)
// очищаем присланный глобал
kill @ad
set count=0
try {
// открываем устройство с выполнением командной строки и чтением ответа
open cmd:"QR:K\CP866\" if '$test $$$ThrowStatus($$$ERROR($$$GeneralError,"Не удалось выполнить команду "_cmd))
Set IO=$IO // запоминаем текущее устройство
// вычитываем построчно результат команды пока не наступит конец файла и складываем его в глобал
for {
use cmd
read str quit:$zeof=-1
set @ad@($Increment(count))=str
}
set @ad=count
// закрываем устройство
#dim e as %Exception.AbstractException
} Catch e {
set sc=e.AsStatus()
//do e.Log() //
}
// закрываем устройство
close cmd
// возвращаем текущее устройство
If $data(IO) Use IO
quit sc
}
}
{
Parameter cmdlist = "dir,type,ver";
/// метод убирает из командной строки последовательности команд
/// оставляет только разрешённые команды
ClassMethod safeCmd(cmd) As %String
{
set cmd=$piece(cmd,"&&") // оставляем одну команду
set cmd=$piece(cmd,"|")
for i=1:1:$length(..#cmdlist,",") set safe($p(..#cmdlist,",",i))="" // список разрешённых команд
if '$data(safe($piece(cmd," "))) set cmd=""
quit cmd
}
/// метод выполняет команду cmd на сервере и складывает результат в глобал ad так, что:
/// @ad=количество строк
/// @ad@(номер строки)=значениеa
ClassMethod runCmd(cmd, ad As %String) As %Status
{
set sc=$$$OK
do $SYSTEM.Process.SetZEOF(1) // выключаем режим генерации ошибки на EOF
// делаем командную строку безопасной
set cmd(1)=..safeCmd(cmd)
quit:cmd(1)="" $$$ERROR($$$GeneralError,"Недопустимая команда "_cmd)
set cmd=cmd(1)
// очищаем присланный глобал
kill @ad
set count=0
try {
// открываем устройство с выполнением командной строки и чтением ответа
open cmd:"QR:K\CP866\" if '$test $$$ThrowStatus($$$ERROR($$$GeneralError,"Не удалось выполнить команду "_cmd))
Set IO=$IO // запоминаем текущее устройство
// вычитываем построчно результат команды пока не наступит конец файла и складываем его в глобал
for {
use cmd
read str quit:$zeof=-1
set @ad@($Increment(count))=str
}
set @ad=count
// закрываем устройство
#dim e as %Exception.AbstractException
} Catch e {
set sc=e.AsStatus()
//do e.Log() //
}
// закрываем устройство
close cmd
// возвращаем текущее устройство
If $data(IO) Use IO
quit sc
}
}
Параметр cmdlist содержит список разрешённых команд через запятую.
Метод safeCmd() причёсывает командную строку.
Метод runCmd() собственно выполняет команду cmd и кладёт результат в косвенный адрес ad.
Приведённый код работает на всех операционных системах, поддерживаемых Caché, исполняет команды, получает результат и обрабатывает ошибки. В отличие от реализации, описанной в статье, работа организована через устройство CommandPipe — режим «QR» для команды open. Подробнее можно почитать в документации.
Демо
Пример использования можно наблюдать здесь — небольшой проект, собранный на CSP+Twitter Bootstrap.
Сервер не отдан на заклание — разрешённые команды: type, ver,
Код класса и веб-проекта выложен на гитхабе.