msd 5 Posted October 27 Hello. I found a few Firebird/Interbase managers have the option to backup databases from a remote server (for example, a Linux server) locally in some directory. When I set all properties to FireDAC Firebid Backup Component, everything is working fine. If I choose a remote instance, I don't have any errors, but there is no locally backup file. Do I miss something? Thanks for the assistance in advance... Share this post Link to post
gkobler 38 Posted October 27 You will find the backup file at the FB-Server PC! 1 Share this post Link to post
corneliusdavid 220 Posted October 28 7 hours ago, msd said: If I choose a remote instance, I don't have any errors, but there is no locally backup file. You need to have the client tools installed on the remote machine, then use gbak like this: gbak -b -user SYSDBA -password masterkey server:/path/to/database.fdb client:/path/to/backup.fbk 1 Share this post Link to post
msd 5 Posted October 28 Hello, This is nice advice, but I need to solve it over my Delphi app, so I need to make some class that will make backups from the remote server to the local machine, zip it, and copy it to the FTP backup location. Share this post Link to post
tgbs 16 Posted October 28 (edited) I'm not sure about the latest version of Firebird, but as with Interbase, only gbak utility can make archives on a remote disk. Therefore, CreateProcess and Waitforsingleobject for Gbak.exe can be used in Delphi Edited October 28 by tgbs 1 Share this post Link to post
msd 5 Posted October 28 I give a sample with FireDAC; it is not mandatory to be with FireDAC. I have UniDAC, FibPlus, and UIB, all full-source x32/x64. If there is some experience about this subject, it will be nice 🙂 Share this post Link to post
tgbs 16 Posted October 28 function RunProcess(CommandLine: String; Var ErrMessage : String; WaitForProcess : Boolean; CreationFlags : Word): Boolean; Var ResultCode : LongWord; StartInfo: TStartUpInfo; ProcInfo: TProcessInformation; begin Result := False; ResultCode := 1; FillChar(StartInfo, SizeOf(TStartUpInfo), #0); FillChar(ProcInfo, SizeOf(TProcessInformation), #0); StartInfo.cb := SizeOf(TStartUpInfo); StartInfo.dwFlags := STARTF_USESHOWWINDOW; // StartInfo.wShowWindow := SW_SHOWMINIMIZED; UniqueString(CommandLine); try try IF NOT CreateProcess(nil, PChar(CommandLine), nil, nil, FALSE, CreationFlags, nil, nil, StartInfo, ProcInfo) THEN BEGIN ErrMessage := SysErrorMessage(GetLastError); Exit; END; except on e : exception do begin ErrMessage := e.Message + #13#10 + SysErrorMessage(GetLastError); Exit; end; end; if not WaitForProcess then ResultCode := 0 else begin WaitForSingleObject(ProcInfo.hProcess, INFINITE); GetExitCodeProcess(ProcInfo.hProcess, ResultCode); end; finally Result := (ResultCode = 0); //-------- CloseHandle(ProcInfo.hProcess); CloseHandle(ProcInfo.hThread); end; end; Set CommandLine to somehing like @corneliusdavid comment 1 Share this post Link to post
Frickler 11 Posted October 28 3 hours ago, msd said: I give a sample with FireDAC; it is not mandatory to be with FireDAC. I have UniDAC, FibPlus, and UIB, all full-source x32/x64. IBX2 for Lazarus can do that. DevArt IBDAC can't. 1 Share this post Link to post
msd 5 Posted October 28 13 hours ago, tgbs said: function RunProcess(CommandLine: String; Var ErrMessage : String; WaitForProcess : Boolean; CreationFlags : Word): Boolean; Var ResultCode : LongWord; StartInfo: TStartUpInfo; ProcInfo: TProcessInformation; begin Result := False; ResultCode := 1; FillChar(StartInfo, SizeOf(TStartUpInfo), #0); FillChar(ProcInfo, SizeOf(TProcessInformation), #0); StartInfo.cb := SizeOf(TStartUpInfo); StartInfo.dwFlags := STARTF_USESHOWWINDOW; // StartInfo.wShowWindow := SW_SHOWMINIMIZED; UniqueString(CommandLine); try try IF NOT CreateProcess(nil, PChar(CommandLine), nil, nil, FALSE, CreationFlags, nil, nil, StartInfo, ProcInfo) THEN BEGIN ErrMessage := SysErrorMessage(GetLastError); Exit; END; except on e : exception do begin ErrMessage := e.Message + #13#10 + SysErrorMessage(GetLastError); Exit; end; end; if not WaitForProcess then ResultCode := 0 else begin WaitForSingleObject(ProcInfo.hProcess, INFINITE); GetExitCodeProcess(ProcInfo.hProcess, ResultCode); end; finally Result := (ResultCode = 0); //-------- CloseHandle(ProcInfo.hProcess); CloseHandle(ProcInfo.hThread); end; end; Set CommandLine to somehing like @corneliusdavid comment This code is OK; I made some modifications, but I have one small problem. When I call a command from Windows cmd everything is working fine, but over this function there are no executions; just blink the app, and nothing happens. What can be wrong? P.S. I chanced working folder to Firebird execution and run as administrator but nothing over the app; over Windows cmd works fine with the same command. Share this post Link to post
Serge_G 87 Posted October 29 Hello, On 10/28/2024 at 4:39 AM, corneliusdavid said: You need to have the client tools installed on the remote machine, then use gbak like this: gbak -b -user SYSDBA -password masterkey server:/path/to/database.fdb client:/path/to/backup.fbk There is a -service option you have to use Remote Backup & Restore Share this post Link to post
msd 5 Posted October 29 Hello, I have client tools on the server and client side, and the backup command is working fine when I call it from Windows cmd. When I call the over function from the Delphi app, I just get cmd blink, and there are no backup files or any kind of errors. Command is working fine from cmd but fails from Delphi. Share this post Link to post
msd 5 Posted October 29 // FileName - full path to executable // Params - command line parameters or use empty string // Folder - working folder for called program - if empty path will be extracted from FileName // WaitUntilTerminated - if true function will wait for process to finish execution // WaitUntilIdle - if true function will call WaitForInputIdle function and wait until the specified process has finished processing its initial input and until there is no user input pending // RunMinimized - if true process will be run minimized // ErrorCode - if function fails this will contain encountered Windows Error Code function ExecuteProcess(const FileName, Params: string; Folder: string; WaitUntilTerminated, WaitUntilIdle, RunMinimized: boolean; var ErrorCode: integer): boolean; var CmdLine: string; WorkingDirP: PChar; StartupInfo: TStartupInfo; ProcessInfo: TProcessInformation; begin Result := true; CmdLine := '"' + FileName + '" ' + Params; if Folder = '' then Folder := ExcludeTrailingPathDelimiter(ExtractFilePath(FileName)); ZeroMemory(@StartupInfo, SizeOf(StartupInfo)); StartupInfo.cb := SizeOf(StartupInfo); if RunMinimized then begin StartupInfo.dwFlags := STARTF_USESHOWWINDOW; StartupInfo.wShowWindow := SW_SHOWMINIMIZED; end; if Folder <> '' then WorkingDirP := PChar(Folder) else WorkingDirP := nil; if not CreateProcess(nil, PChar(CmdLine), nil, nil, false, 0, nil, WorkingDirP, StartupInfo, ProcessInfo) then begin Result := false; ErrorCode := GetLastError; exit; end; with ProcessInfo do begin CloseHandle(hThread); if WaitUntilIdle then WaitForInputIdle(hProcess, INFINITE); if WaitUntilTerminated then repeat Application.ProcessMessages; until MsgWaitForMultipleObjects(1, hProcess, false, INFINITE, QS_ALLINPUT) <> WAIT_OBJECT_0 + 1; CloseHandle(hProcess); end; end; FileName := Server.libPath + 'gbak.exe'; WorkingFolder := Server.libPath; Parameters := PChar(' -b -par 9 -se ' + PChar(Server.dbHost + ':gds_db') + ' -user SYSDBA -pass masterkey ' + PChar(db.fdbPath + db.fdbFile) + ' stdout > ' + PChar(db.fbkFile)); OK := ExecuteProcess(FileName, Parameters, WorkingFolder, true, true, true, Error); Share this post Link to post
tgbs 16 Posted October 29 UniqueString(CmdLine); Before CreateProcess ? 1 Share this post Link to post
Olli73 5 Posted October 29 Since the params parameter in the ExecuteProcess function is string, I would not use PChar casting in Parameters variable; use strings. And for testing I would try it without "stdout > ...". And I would try to use the path to the DB insterad of "gds_db". 1 Share this post Link to post
msd 5 Posted October 29 With UniqueString(CmdLine); + removing PChar, everything is working fine, but without a service call... Share this post Link to post
Thijs van Dien 9 Posted October 29 (edited) I've been doing this for a very long time, so surely I can share some experience. Yes, you can use a backup component that employs the built-in service manager. In my case that's TIBOBackupService from IBObjects. The key is that you need to name the output file 'stdout' and have the option to capture that. This component does; I haven't looked into others. Most have similar methods, though. Keep in mind that it produces binary, so I wrote a subclass to handle that: type TIBOBinaryCapableBackupService = class(TIBOBackupService) public function GetNextBinaryChunk: TBytes; end; implementation { TIBOBinaryCapableBackupService } function TIBOBinaryCapableBackupService.GetNextBinaryChunk: TBytes; var LLength: Integer; begin Result := nil; if EOF then Exit; GetNextChunk; // Fill buffer LLength := IB_Session.isc_vax_integer(Pointer(OutputBuffer + 1), 2); SetLength(Result, LLength); Move(OutputBuffer[3], Pointer(Result)^, LLength); end; The alternative is indeed having gbak on the client. Eventually I switched to that, because I wanted to be able to use a newer version (3.0) than the server itself (2.5) – that way I could exclude certain tables. Much of the code was inspired by @David Heffernan in my question here. Edited October 29 by Thijs van Dien Share this post Link to post