четвер, 25 лютого 2010 р.

Сервіс копіювання файлів конфігурації Kerio

На роботі стоїть сервер на якому встановлені Kerio Winroute Firewall і Kerio MailServer. Періодично продукти Kerio злітали і я почав робити ручне копіювання файлів конфігурації, а потім робити їх підміну – робота софта відновлювалась. В один прекрасний момент це мене задовбало.

Спочатку я написав програмку, яка виконувала резервне копіювання файлів конфігурації і помістив її в автозавантаження. Та ця програма виконувала свої функції лише тоді, коли користувач заходив в систему. Це мене не влаштовувало, адже не кожен день я заходив не цей сервер.
В результаті я вирішив написати сервіс для вирішення цієї проблеми.

Опис ділиться на декілька кроків


Крок 1. Створення сервісу

Спочатку я створював Service Application і з меню File-New-Other.

Далі присвоїв сервісу ім’я, як видно на скріні нижче copyconfig. Оскільки мій сервіс примітивний, який виконує всього лиш декілька дій – пошук в директорії файлів з маскою *.cfg і наступне їх копіювання в потрібну мені директорію, то я описував лише подію OnExecute.
OnExecute – служба стартує, виконує певні дії і завершує свою роботу.

Нижче приведений код всієї служби з коментарями.
unit Serv;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, SvcMgr, Dialogs,Inifiles;

type
  Tcopyconfig = class(TService)
    procedure ServiceExecute(Sender: TService);
  private
    { Private declarations }
  public
    function GetServiceController: TServiceController; override;
    { Public declarations }
  end;

var
  copyconfig: Tcopyconfig;
  listfile: array[0..20] of string; // масив для збереження імен файлів з потрібним нам розширенням
  i:integer;
implementation

{$R *.DFM}


{Процедура пошуку по директорії потрібних нам файлів
В процедуру передається 2 параметри:
mask – маска фалів, які ми шукатимемо
path – щлях до директорії в якій буде проводитися пошук файлів заданої маски }

procedure FileFind(mask:string;path:string);
var sr:Tsearchrec;// Опис структури, яку використовує для пошуку система
found:integer; // знайдено чи ні
attr:integer;

begin
i:=0;
attr:=0;
attr:=Faanyfile;
found:=FindFirst(path + mask,attr, sr); 
{по команді FindFirst програма створює структуру наступного типу
TsearchRec = record
Time: Integer; // час створення
Size: Integer; // розмір файлу
Attr: Integer;// атрибутиы
Name:TFileName // = TString; ім’я файлу
ExcludeAttr: Integer; знайдені атрибути
FindHandle: THandle; // !!! вказівник на структуру пошуку, яку створює ситсема, а не сервіс. В зв’язку з цим в кінці пошуку необхідно обов’язково писати FindClose – це звільняє паам’ять
FindData: TWin32FindData; // власне ця структура
end;}
while (found = 0) do // якщо хоч щось знайдено
begin
if  (sr.name <> '.') and (sr.name <> '..') then
begin // якщо не вказували на корневі каталоги, то щось знайшли

listfile[i]:=sr.Name;
inc(i);
end;
found:=findnext(sr); // шукаємо далі, якщо ще є файли і каталоги
end;
FindClose(sr); // пошук завершений – вивільняємо пам’ять
end;

{Наступні два методи згенеровані автоматично}

procedure ServiceController(CtrlCode: DWord); stdcall;
begin
  copyconfig.Controller(CtrlCode);
end;

function Tcopyconfig.GetServiceController: TServiceController;
begin
  Result := ServiceController;
end;


{В цьому методі описується тіло служби, ті дії які буде вона виконувати після запуску}
procedure Tcopyconfig.ServiceExecute(Sender: TService);
var
   mask,srcF,srcM,dstF,dstM:string //змінні для запису параметрів з ini файлу
   dat:string; // в цю змінну ми внесемо дату потрібного нам формату 
   j:integer // змінна, яка потрібна при копіюванні файлів 
   inst:integer; // для визначення, файли конфігурації якого з продуктів Kerio копіювати
   Ini:tinifile; // визначаємо змінну для файлу конфігурації сервісу
Begin
// Визначення ini файлу з якого будуть братися параметри
Ini:=Tinifile.Create('C:\BackupConfig.ini');
// зчитування параметрів з ini файлу
mask:=ini.ReadString('main','mask','*.cfg');
srcF:=ini.ReadString('source','firewall','C:\Program Files\Kerio\WinRoute FireWall');
srcM:=ini.ReadString('source','MailServer','C:\Program Files\Kerio\MailServer');
dstF:=ini.ReadString('dest','firewall','C:\Program Files\Kerio\WinRoute FireWall');
DstM:=ini.ReadString('dest','MailServer','C:\Program Files\Kerio\MailServer');
inst:=strtoint(ini.ReadString('main','inst','2'));
// після зчитування параметрів звільняємо ini файл
ini.Free;
// перетворюємо дату в потрібний формат
dateTimetostring(dat,'yyyy.mm.dd', Date);
// умовна конструкція, в якій визначається що і звідки копіювати
case inst of
// якщо 1, то відбувається копіювання файлів конфігурації тільки для MailServer
1:
 Begin
// перевірка існування директорії в яку будуть копіюватися файли
  if directoryexists(dstM+'\\Backup config MailServer') then
   begin
//якщо не має в директорії каталогу з датою то створюємо такий
    if not directoryexists (dstM+'\\Backup config MailServer\'+dat) then
    mkdir(dstM+'\\Backup config MailServer\'+dat);
   end
// якщо директорії немає, то створюємо таку
   else
   begin
    mkdir(dstM+'\\Backup config MailServer');
    mkdir(dstM+'\\Backup config MailServer\'+dat);
   end;
// пошук файлу
 Filefind(mask,srcM);
//копіювання файлу/файлів
for j:=0 to i do
 begin
  Copyfile(pchar(srcM+'\'+listfile[j]),pchar(dstM+'\Backup config MailServer\'+dat+'\'+listfile[j]),true);
 end;
end;
// якщо 0, то відбувається копіювання файлів конфігурації тільки для FireWall
0:
 begin
 if directoryexists(dstF+'\'+'\Backup config Firewall') then
  begin
   if not directoryexists (dstF+'\'+'\Backup config Firewall\'+dat) then
   mkdir(dstf+'\'+'\Backup config Firewall\'+dat);
  end
  else
  begin
   mkdir(dstF+'\'+'\Backup config Firewall');
   mkdir(dstf+'\'+'\Backup config Firewall\'+dat);
  end;
Filefind(mask,srcF);
  for j:=0 to i do
  begin
   Copyfile(pchar(srcF+'\'+listfile[j]),pchar(dstF+'\Backup config FireWall\'+dat+'\'+listfile[j]),true);
  end;
 end;
//якщо 2, то відбувається копіювання файлів конфігурації для MailServer і FireWall
 2:
 begin
 if directoryexists(dstF+'\'+'\Backup config Firewall') then
  begin
 if not directoryexists (dstF+'\'+'\Backup config Firewall\'+dat) then
   mkdir(dstf+'\'+'\Backup config Firewall\'+dat);
  end
  else
  begin
   //edit1.Text:='File not exists';
   mkdir(dstF+'\'+'\Backup config Firewall');
   mkdir(dstf+'\'+'\Backup config Firewall\'+dat);
  end;
  if directoryexists(dstM+'\'+'\Backup config MailServer') then
  begin
   if not directoryexists (dstM+'\'+'\Backup config MailServer\'+dat) then
   mkdir(dstM+'\'+'\Backup config MailServer\'+dat);
  end
  else
  begin
   mkdir(dstM+'\'+'\'+'Backup config MailServer');
   mkdir(dstM+'\'+'\Backup config MailServer\'+dat);
 end;
Filefind(mask,srcF);
for j:=0 to i do
 begin
  Copyfile(pchar(srcF+'\'+listfile[j]),pchar(dstF+'\Backup config FireWall\'+dat+'\'+listfile[j]),true);
 end;
Filefind(mask,srcM);
  for j:=0 to i do
  begin
   Copyfile(pchar(srcM+'\'+listfile[j]),pchar(dstM+'\Backup config MailServer\'+dat+'\'+listfile[j]),true);
  end;
 end;
end;
end;
end.
Для створення сервісу потрібно проект скомпілювати (Project-Compile або Ctrl+F9)

Файл конфігурації BackupConfig.ini такого змісту:
[Main]
' 0 - Firewall
' 1 - MailServer
' 2 - Firewall and Mailserver
inst=2
mask=*.cfg
[Source]
Firewall=C:\Program Files\Kerio\WinRoute FireWall\
Mailserver=C:\Program Files\Kerio\MailServer\
[Dest]
Firewall=g:\Kerio backup\
Mailserver=g:\Kerio backup\

Крок 2. Встановлення сервісу

Є 2 способи як встановити, так і деінсталювати сервіс:
А) Ручками
Перед інсталяцією таким способом потрібно обов’язково помістити файл конфігурації в корінь диску С.
Далі Пуск-Виконати або Win+R вписати шлях до exe файлу сервісу і задати параметр install.

 
Натиснути ОК  і в результаті ви отримаєте повідомлення про те, що сервіс встановлений. Щоб повідомлення не з’являлося потрібно дописати ще параметр /silent.
Для деінсталяції потрібно виконати ті ж самі дії тільки з параметром /uninstall.
Б) Програмно
Потрібно створити новий проект, кинути на форму дві кнопки, підписати їх і заповнити обробники подій цих кнопок, плюс дописати функції інсталяції і деінсталяції сервісу.
 

Нижче лістинг програми з коментарями.
unit ServInst;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls,shellapi,Winsvc;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button3Click(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}
{Функція інсталяції сервісу
Передаються два параметри:
ExecutablePath  - шлях до exe файлу сервісу
ServiceName  - ім’я сервісу, яке буде відображатись}

function CreateNTService(ExecutablePath,ServiceName: String):boolean;   
var  
 hNewService,hSCMgr: SC_HANDLE;
// Rights: DWORD;   
 FuncRetVal: Boolean;   
begin  
 FuncRetVal := False;
 hSCMgr := OpenSCManager(nil,nil,SC_MANAGER_CREATE_SERVICE);
 if (hSCMgr <> 0 ) then  
  begin  
   //Власні права доступу можуть описуватись тут
   //використовується GENERIC_EXECUTE, що є комбінацією   
   //STANDARD_RIGHTS_EXECUTE, SERVICE_START, SERVICE_STOP,   
   //SERVICE_PAUSE_CONTINUE, and SERVICE_USER_DEFINED_CONTROL   
   //Можна створювати власні права і використовувати їх як показано в закоментованих рядках   нижче 
    
   //Rights := STANDARD_RIGHTS_REQUIRED or SERVICE_START or SERVICE_STOP   
   // or SERVICE_QUERY_STATUS or SERVICE_PAUSE_CONTINUE or   
   // SERVICE_INTERROGATE;   
   
 // SERVICE_AUTO_START – відповідає за тип запуску сервісу Авто
   hNewService := CreateService(hSCMgr,PChar(ServiceName),PChar(ServiceName),   
                                STANDARD_RIGHTS_REQUIRED,SERVICE_WIN32_OWN_PROCESS,   
                                SERVICE_AUTO_START,SERVICE_ERROR_NORMAL,   
                                PChar(ExecutablePath),nil,nil,nil,nil,nil);   
  CloseServiceHandle(hSCMgr);   
   if (hNewService <> 0) then  
    FuncRetVal := true  
   else  
    FuncRetVal := false;   
  end;   
 CreateNTService := FuncRetVal;   
end;   
    
// ***   
    
{Функцыя для видалення сервісу
// ServiceName – ім’я сервісу для видалення
//Результат:   
// true – якщо операція завершена вдало
// false -  якщо видалення не було виконано}
    
function DeleteNTService(ServiceName: String):boolean;   
var  
 hServiceToDelete,hSCMgr: SC_HANDLE;   
 RetVal: LongBool;   
 FunctRetVal: Boolean;   
begin  
 FunctRetVal := false;   
 hSCMgr := OpenSCManager(nil,nil,SC_MANAGER_CREATE_SERVICE);   
 if (hSCMgr <> 0 ) then  
  begin  
   hServiceToDelete := OpenService(hSCMgr, PChar(ServiceName), SERVICE_ALL_ACCESS);   
   RetVal := DeleteService(hServiceToDelete);   
   CloseServiceHandle(hSCMgr);   
   FunctRetVal := RetVal;   
  end;   
   DeleteNTService := FunctRetVal;   
end;   

{Інсталяція сервісу}
procedure TForm1.Button1Click(Sender: TObject);
var 
tmps:string;
begin
// присвоєння імені сервісу
tmpS := 'CopyConfig';
// власне інсталяція з виводом повідомлення про успішність чи ні. 
   if (CreateNTService(GetCurrentdir+'\Service\Copycon.exe',tmpS)) then
     MessageDlg('Service '+tmpS+' has been successfully created!',mtInformation,[mbOK],0)
    else
     MessageDlg('Unable to create service '+tmpS+' Win32 Error code: '+IntToStr(GetLastError),mtWarning,[mbOK],0);
// копіювання фалу конфігурації сервісу в корінь диску С
CopyFile(pchar(GetCurrentdir+'\Service\BackupConfig.ini'),'C:\BackupConfig.ini',true);
end;

{Деінсталяція сервісу}
procedure TForm1.Button2Click(Sender: TObject);
var  
 tmpS: String;   
begin
 tmpS := 'CopyConfig';
// деінсталяція сервісу з виводом повідомлення про результат
 if (DeleteNTService(tmpS)) then  
     MessageDlg('Service '+tmpS+' has been successfully deleted!',mtInformation,[mbOK],0)   
 else  
     MessageDlg('Unable to delete service '+tmpS+' Win32 Error code: '+IntToStr(GetLastError),mtWarning,[mbOK],0);
//видалення файлу конфігурації сервісу з кореню диску С
 if fileexists('C:\BackupConfig.ini') then
 DeleteFile('C:\BackupConfig.ini');

end;

end.

Якщо використовувати мій код в тому вигляді в якому він є, то розміщення файлів має мати такий вигляд:

При обох варіантах інсталяції, після завершення сервіс буде не запущений. Для запуску потрібно або перезавантажитись, або через оснастку Service його запустити.
В принципі цей сервіс можна використовувати не тільки для копіювання файлів конфігурації Kerio, а й інших, потрібно просто задати відповідні настройки в BackupConfig.ini і при необхідності змінити розмірність масиву listfile, якщо файлів більше ніж 20.

При написанні сервісу я використовував матеріали розміщені на DelphiKingdom, HelloWorld.ru і в DRKB Explorer – домашня сторінка.

Початковий код, сервіс і його інсталятор можна взяти тут.

Немає коментарів:

Дописати коментар