• R/O
  • SSH
  • HTTPS

backup-svn: Commit


Commit MetaInfo

Revision8 (tree)
Time2020-01-18 21:40:23
Authorgottfried

Log Message

create dumps working now

Change Summary

Incremental Difference

--- trunk/backup-svn/BackupSVN.cpp (revision 7)
+++ trunk/backup-svn/BackupSVN.cpp (revision 8)
@@ -2,7 +2,10 @@
22 #include "BackupSVN.h"
33 #include "IniFile.h"
44 #include "StringTools.h"
5+#include "System.h"
56 #include <iostream>
7+#include <cstdio>
8+#include <cstdint>
69
710 #define WIN32_LEAN_AND_MEAN
811 #define NOMINMAX
@@ -87,7 +90,7 @@
8790 Configuration File: <repopath>\db\fsfs.conf
8891 */
8992
90- int result = 0;
93+ int errors = 0;
9194 for(vector<wstring>::const_iterator it = mRepolist.cbegin(); it != mRepolist.cend(); ++it)
9295 {
9396 const wstring &repo = *it;
@@ -96,88 +99,103 @@
9699 svnadmininfo += L" info ";
97100 svnadmininfo += L"\"" + repo + L"\"";
98101
99- string strResult = execCmd(svnadmininfo.c_str());
102+ string svnadminOutput;
103+ errors += cSystem::execCmd(svnadmininfo.c_str(), svnadminOutput);
100104
105+ size_t pos = svnadminOutput.find("Revisions");
106+ if(pos != string::npos)
107+ {
108+ int revision = atoi(svnadminOutput.substr(pos + 10).c_str());
101109
102- cout << strResult << endl;
110+ int storedRevision = cIniFile::getInt(L"REVISIONS", repo.c_str(), 0);
103111
104- // TODO: if version changed:
105- //dumpRepos(svnadmin.c_str(), repo.c_str());
112+ if( revision != storedRevision)
113+ {
114+ if(dumpRepos(svnadmin.c_str(), repo.c_str()))
115+ {
116+ // TODO: compress the dump file
117+
118+ // Write revisions, when dump created without error
119+ cIniFile::writeInt(L"REVISIONS", repo.c_str(), revision);
120+ }
121+ else
122+ {
123+ errors++;
124+ }
125+ }
126+ }
106127 }
107128
108- return result;
129+ return errors;
109130 }
110131
111-void cBackupSVN::dumpRepos(const wchar_t *svnadmin, const wchar_t *repo)
132+bool cBackupSVN::dumpRepos(const wchar_t *svnadmin, const wchar_t *repo)
112133 {
113-}
134+ wstring backupPath = cIniFile::getString(L"BACKUP", L"PATH", L"");
135+ if(backupPath.length())
136+ backupPath += L'/';
137+ backupPath += repo;
114138
139+ wstring svnAdminDump = L"\"" + wstring(svnadmin) + L"\"";
140+ svnAdminDump += L" dump ";
141+ svnAdminDump += L"\"" + wstring(repo) + L"\"";
115142
143+ wstring outputFile = backupPath + L".dump";
116144
117-string cBackupSVN::execCmd(const wchar_t *cmd)
118-{
119- string strResult;
120- HANDLE hPipeRead, hPipeWrite;
145+ // check if file exists
146+ // Open for read (will fail if file "crt_fopen_s.c" does not exist)
147+ FILE *fp;
148+ errno_t err = _wfopen_s(&fp, outputFile.c_str(), L"r");
149+ if(!err)
150+ {
151+ fclose(fp);
152+ fp = 0;
153+ rotateDump(outputFile.c_str());
154+ }
121155
122- SECURITY_ATTRIBUTES saAttr ={sizeof(SECURITY_ATTRIBUTES)};
123- saAttr.bInheritHandle = TRUE; // Pipe handles are inherited by child process.
124- saAttr.lpSecurityDescriptor = NULL;
156+ string svnadminErrorOutput;
157+ int result = cSystem::execCmd(svnAdminDump.c_str(), outputFile.c_str(), svnadminErrorOutput);
125158
126- // Create a pipe to get results from child's stdout.
127- if(!CreatePipe(&hPipeRead, &hPipeWrite, &saAttr, 0))
128- return strResult;
159+ return result == 0;
160+}
129161
130- STARTUPINFOW si ={sizeof(STARTUPINFOW)};
131- si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
132- si.hStdOutput = hPipeWrite;
133- si.hStdError = hPipeWrite;
134- si.wShowWindow = SW_HIDE; // Prevents cmd window from flashing.
135- // Requires STARTF_USESHOWWINDOW in dwFlags.
136162
137- PROCESS_INFORMATION pi ={0};
138-
139- BOOL fSuccess = CreateProcessW(NULL, (LPWSTR)cmd, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
140- if(! fSuccess)
163+void cBackupSVN::rotateDump(const wchar_t *dumpfile)
164+{
165+ int result;
166+
167+ int rotates = cIniFile::getInt(L"BACKUP", L"ROTATE", 0);
168+ if(rotates)
141169 {
142- CloseHandle(hPipeWrite);
143- CloseHandle(hPipeRead);
144- return strResult;
145- }
170+ string filepath;
171+ string oldname;
172+ string newname;
146173
147- bool bProcessEnded = false;
148- for(; !bProcessEnded;)
149- {
150- // Give some timeslice (50 ms), so we won't waste 100% CPU.
151- bProcessEnded = WaitForSingleObject(pi.hProcess, 50) == WAIT_OBJECT_0;
152-
153- // Even if process exited - we continue reading, if
154- // there is some data available over pipe.
155- for(;;)
174+ filepath = cStringTools::convertToString(dumpfile);
175+ for(int i = rotates; i >= 0; i--)
156176 {
157- char buf[1024];
158- DWORD dwRead = 0;
159- DWORD dwAvail = 0;
177+ oldname = filepath;
178+ if(i != 0)
179+ {
180+ char numbuf[20];
181+ sprintf_s(numbuf, ".%d", i);
182+ oldname += numbuf;
183+ }
160184
161- if(!::PeekNamedPipe(hPipeRead, NULL, 0, NULL, &dwAvail, NULL))
162- break;
185+ if(newname.length())
186+ {
187+ result = remove(newname.c_str());
188+ result = rename(oldname.c_str(), newname.c_str());
189+ }
163190
164- if(!dwAvail) // No data available, return
165- break;
166-
167- DWORD readlength = sizeof(buf) - 1;
168- if(readlength > dwAvail)
169- readlength = dwAvail;
170- if(!::ReadFile(hPipeRead, buf, readlength, &dwRead, NULL) || !dwRead)
171- break; // Error, the child process might ended
172-
173- buf[dwRead] = 0;
174- strResult += buf;
191+ newname = oldname;
175192 }
176- } //for
193+ }
194+ else
195+ {
196+ // no rotate - delete file only
197+ string file = cStringTools::convertToString(dumpfile);
198+ result = remove(file.c_str());
199+ }
200+}
177201
178- CloseHandle(hPipeWrite);
179- CloseHandle(hPipeRead);
180- CloseHandle(pi.hProcess);
181- CloseHandle(pi.hThread);
182- return strResult;
183-}
--- trunk/backup-svn/BackupSVN.h (revision 7)
+++ trunk/backup-svn/BackupSVN.h (revision 8)
@@ -18,8 +18,7 @@
1818
1919 void scanRepos();
2020 int checkRepos();
21- void dumpRepos(const wchar_t *svnadmin, const wchar_t *repo);
22-
23- std::string execCmd(const wchar_t *cmd);
21+ bool dumpRepos(const wchar_t *svnadmin, const wchar_t *repo);
22+ void rotateDump(const wchar_t *dumpfile);
2423 };
2524
--- trunk/backup-svn/IniFile.cpp (revision 7)
+++ trunk/backup-svn/IniFile.cpp (revision 8)
@@ -9,20 +9,20 @@
99
1010 using namespace std;
1111
12-std::string IniFile::mFile;
13-std::wstring IniFile::mwFile;
12+std::string cIniFile::mFile;
13+std::wstring cIniFile::mwFile;
1414
15-IniFile::IniFile()
15+cIniFile::cIniFile()
1616 {
1717 }
1818
1919
20-IniFile::~IniFile()
20+cIniFile::~cIniFile()
2121 {
2222 }
2323
2424
25-bool IniFile::Init(const char *filename)
25+bool cIniFile::Init(const char *filename)
2626 {
2727 // check if file exists
2828 #pragma warning( push )
@@ -43,7 +43,7 @@
4343 return false;
4444 }
4545
46-std::string IniFile::getString(const char *selection, const char *key, const char *strDefault)
46+std::string cIniFile::getString(const char *selection, const char *key, const char *strDefault)
4747 {
4848 if(!mFile.length())
4949 return strDefault;
@@ -54,7 +54,7 @@
5454 return result;
5555 }
5656
57-std::wstring IniFile::getString(const wchar_t *selection, const wchar_t *key, const wchar_t *strDefault)
57+std::wstring cIniFile::getString(const wchar_t *selection, const wchar_t *key, const wchar_t *strDefault)
5858 {
5959 if(!mwFile.length())
6060 return strDefault;
@@ -65,3 +65,18 @@
6565 //WritePrivateProfileStringW(selection, key, strDefault, mwFile.c_str());
6666 return result;
6767 }
68+
69+
70+int cIniFile::getInt(const wchar_t *selection, const wchar_t *key, int default)
71+{
72+ int result = GetPrivateProfileIntW(selection, key, default, mwFile.c_str());
73+
74+ return result;
75+}
76+
77+void cIniFile::writeInt(const wchar_t *selection, const wchar_t *key, int value)
78+{
79+ wchar_t buf[50];
80+ wsprintfW(buf, L"%d", value);
81+ WritePrivateProfileStringW(selection, key, buf, mwFile.c_str());
82+}
--- trunk/backup-svn/IniFile.h (revision 7)
+++ trunk/backup-svn/IniFile.h (revision 8)
@@ -2,17 +2,24 @@
22
33 #include <string>
44
5-class IniFile
5+class cIniFile
66 {
77 public:
8- IniFile();
9- ~IniFile();
8+ cIniFile();
9+ ~cIniFile();
1010
11+ /*
12+ * Use absolute path for ini file.
13+ * otherwise the ini file is stored in C:\Windows path, where we do not have rights to write file
14+ */
1115 static bool Init(const char *filename);
1216
13- static std::string getString(const char *selection, const char *key, const char *strDefault);
14- static std::wstring getString(const wchar_t *selection, const wchar_t *key, const wchar_t *strDefault);
17+ static std::string getString(const char *selection, const char *key, const char *strDefault = "");
18+ static std::wstring getString(const wchar_t *selection, const wchar_t *key, const wchar_t *strDefault = L"");
1519
20+ static int getInt(const wchar_t *selection, const wchar_t *key, int default = 0);
21+ static void writeInt(const wchar_t *selection, const wchar_t *key, int value);
22+
1623 private:
1724 static std::string mFile;
1825 static std::wstring mwFile;
--- trunk/backup-svn/System.cpp (nonexistent)
+++ trunk/backup-svn/System.cpp (revision 8)
@@ -0,0 +1,222 @@
1+#include "System.h"
2+
3+#define WIN32_LEAN_AND_MEAN
4+#define NOMINMAX
5+#include <Windows.h>
6+
7+#include <iostream>
8+#include <cstdint>
9+
10+using namespace std;
11+
12+
13+cSystem::cSystem()
14+{
15+}
16+
17+
18+cSystem::~cSystem()
19+{
20+}
21+
22+int cSystem::execCmd(const wchar_t *cmd, std::string &out)
23+{
24+ HANDLE hPipeRead, hPipeWrite;
25+
26+ out.clear();
27+ wcout << L"RUN> "<< cmd << endl;
28+
29+ SECURITY_ATTRIBUTES saAttr = {sizeof(SECURITY_ATTRIBUTES)};
30+ saAttr.bInheritHandle = TRUE; // Pipe handles are inherited by child process.
31+ saAttr.lpSecurityDescriptor = NULL;
32+
33+ // Create a pipe to get results from child's stdout.
34+ if(!CreatePipe(&hPipeRead, &hPipeWrite, &saAttr, 0))
35+ return -1;
36+
37+ STARTUPINFOW si = {sizeof(STARTUPINFOW)};
38+ si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
39+ si.hStdOutput = hPipeWrite;
40+ si.hStdError = hPipeWrite;
41+ si.wShowWindow = SW_HIDE; // Prevents cmd window from flashing.
42+ // Requires STARTF_USESHOWWINDOW in dwFlags.
43+
44+ PROCESS_INFORMATION pi = {0};
45+
46+ BOOL success = CreateProcessW(NULL, (LPWSTR)cmd, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
47+ if(!success)
48+ {
49+ CloseHandle(hPipeWrite);
50+ CloseHandle(hPipeRead);
51+ return -1;
52+ }
53+
54+ uint32_t bProcessEnded = 0;
55+ for(; !bProcessEnded;)
56+ {
57+ // Give some timeslice (50 ms), so we won't waste 100% CPU.
58+ bProcessEnded = WaitForSingleObject(pi.hProcess, 50) == WAIT_OBJECT_0;
59+
60+ // Even if process exited - we continue reading, if
61+ // there is some data available over pipe.
62+ for(;;)
63+ {
64+ char buf[1024];
65+ DWORD dwRead = 0;
66+ DWORD dwAvail = 0;
67+
68+ if(!PeekNamedPipe(hPipeRead, NULL, 0, NULL, &dwAvail, NULL))
69+ break;
70+
71+ // No data available?
72+ if(!dwAvail)
73+ break;
74+
75+ if(!ReadFile(hPipeRead, buf, sizeof(buf) - 1, &dwRead, NULL) || !dwRead)
76+ break; // Error, the child process might ended
77+
78+ string cmdOutput = string(buf, static_cast<size_t>(dwRead));
79+ cout << cmdOutput;
80+ out += cmdOutput;
81+ }
82+ }
83+
84+ DWORD exit_code = (DWORD)-1;
85+ BOOL result = GetExitCodeProcess(pi.hProcess, &exit_code);
86+ if(!result)
87+ std::cerr << "GetExitCodeProcess() failure: " << GetLastError() << "\n";
88+
89+ CloseHandle(hPipeWrite);
90+ CloseHandle(hPipeRead);
91+ CloseHandle(pi.hProcess);
92+ CloseHandle(pi.hThread);
93+ return exit_code;
94+}
95+
96+int cSystem::execCmd(const wchar_t *cmd, const wchar_t *stdout_file, std::string &outErr)
97+{
98+ HANDLE hPipeRead, hPipeWrite;
99+ HANDLE hStdOut;
100+
101+ outErr.clear();
102+ wcout << L"RUN> "<< cmd << endl;
103+
104+ SECURITY_ATTRIBUTES saAttr = {sizeof(SECURITY_ATTRIBUTES)};
105+ saAttr.bInheritHandle = TRUE; // Pipe handles are inherited by child process.
106+ saAttr.lpSecurityDescriptor = NULL;
107+
108+ // Create a pipe to get results from child's stderr.
109+ if(!CreatePipe(&hPipeRead, &hPipeWrite, &saAttr, 0))
110+ return -1;
111+
112+ SECURITY_ATTRIBUTES sa;
113+ sa.nLength = sizeof(sa);
114+ sa.bInheritHandle = TRUE;
115+ sa.lpSecurityDescriptor = NULL;
116+
117+ // Create a file to save results from childs stdout.
118+ hStdOut = CreateFileW(stdout_file, // name of the write
119+ GENERIC_WRITE, // open for writing
120+ FILE_SHARE_WRITE|FILE_SHARE_READ, // share mode
121+ &sa, // default security
122+ CREATE_NEW, // create new file
123+ FILE_ATTRIBUTE_NORMAL, // normal file
124+ NULL); // no attr. template
125+ if(hStdOut == INVALID_HANDLE_VALUE)
126+ {
127+ std::wcerr << L"CreateFile() failure: " << outputError() << endl;
128+ CloseHandle(hPipeWrite);
129+ CloseHandle(hPipeRead);
130+ return -1;
131+ }
132+
133+ STARTUPINFOW si = {sizeof(STARTUPINFOW)};
134+ si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
135+ si.hStdOutput = hStdOut;
136+ si.hStdError = hPipeWrite;
137+ si.wShowWindow = SW_HIDE; // Prevents cmd window from flashing.
138+ // Requires STARTF_USESHOWWINDOW in dwFlags.
139+
140+ PROCESS_INFORMATION pi = {0};
141+
142+ BOOL success = CreateProcessW(NULL, (LPWSTR)cmd, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
143+ if(!success)
144+ {
145+ CloseHandle(hStdOut);
146+ CloseHandle(hPipeWrite);
147+ CloseHandle(hPipeRead);
148+ return -1;
149+ }
150+
151+ uint32_t bProcessEnded = 0;
152+ for(; !bProcessEnded;)
153+ {
154+ // Give some timeslice (50 ms), so we won't waste 100% CPU.
155+ bProcessEnded = WaitForSingleObject(pi.hProcess, 50) == WAIT_OBJECT_0;
156+
157+ // Even if process exited - we continue reading, if
158+ // there is some data available over pipe.
159+ for(;;)
160+ {
161+ char buf[1024];
162+ DWORD dwRead = 0;
163+ DWORD dwAvail = 0;
164+
165+ if(!PeekNamedPipe(hPipeRead, NULL, 0, NULL, &dwAvail, NULL))
166+ break;
167+
168+ // No data available?
169+ if(!dwAvail)
170+ break;
171+
172+ if(!ReadFile(hPipeRead, buf, sizeof(buf) - 1, &dwRead, NULL) || !dwRead)
173+ break; // Error, the child process might ended
174+
175+ string cmdOutput = string(buf, static_cast<size_t>(dwRead));
176+ cout << cmdOutput;
177+ outErr += cmdOutput;
178+ }
179+ }
180+
181+ DWORD exit_code = (DWORD)-1;
182+ BOOL result = GetExitCodeProcess(pi.hProcess, &exit_code);
183+ if(!result)
184+ std::wcerr << L"GetExitCodeProcess() failure: " << outputError() << endl;
185+
186+ CloseHandle(hStdOut);
187+ CloseHandle(hPipeWrite);
188+ CloseHandle(hPipeRead);
189+ CloseHandle(pi.hProcess);
190+ CloseHandle(pi.hThread);
191+ return exit_code;
192+}
193+
194+
195+std::wstring cSystem::outputError()
196+{
197+ wstring output;
198+ LPVOID lpMsgBuf;
199+ DWORD last_error = GetLastError();
200+
201+ FormatMessageW(
202+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
203+ FORMAT_MESSAGE_FROM_SYSTEM |
204+ FORMAT_MESSAGE_IGNORE_INSERTS,
205+ NULL,
206+ last_error,
207+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
208+ (LPTSTR)&lpMsgBuf,
209+ 0, NULL);
210+
211+ // Display the error message and exit the process
212+ wchar_t numbuf[50];
213+ wsprintfW(numbuf, L"%d: ", last_error);
214+ output = numbuf;
215+ output += static_cast<wchar_t*>(lpMsgBuf);
216+
217+ LocalFree(lpMsgBuf);
218+
219+ //MessageBoxW(NULL, output.c_str(), TEXT("Error"), MB_OK);
220+
221+ return output;
222+}
--- trunk/backup-svn/System.h (nonexistent)
+++ trunk/backup-svn/System.h (revision 8)
@@ -0,0 +1,25 @@
1+#pragma once
2+
3+#include <string>
4+
5+
6+class cSystem
7+{
8+public:
9+ cSystem();
10+ ~cSystem();
11+
12+ /**
13+ * execute command and store output (sdtout, stderr) to [out]
14+ */
15+ static int execCmd(const wchar_t *cmd, std::string &out);
16+ /**
17+ * execute command
18+ * store stdout to a file, write stderr to outErr
19+ */
20+ static int execCmd(const wchar_t *cmd, const wchar_t *stdout_file, std::string &outErr);
21+
22+private:
23+ static std::wstring outputError();
24+};
25+
--- trunk/backup-svn/backup-svn.ini (revision 7)
+++ trunk/backup-svn/backup-svn.ini (revision 8)
@@ -10,3 +10,17 @@
1010 #SVNADMIN_PATH=C:\cygwin\bin
1111 #SVNADMIN_PATH=C:\Program Files (x86)\VisualSVN Server\bin
1212 SVNADMIN_PATH=C:\Program Files\TortoiseSVN\bin
13+
14+
15+[BACKUP]
16+# Path where the backup files created
17+# When no path is given, the backup stored in current working directory
18+PATH=
19+
20+# rotate to keep old backups
21+ROTATE=3
22+
23+
24+# here the revisions stored
25+# when revision isn't changed, we do not create a dump
26+[REVISIONS]
--- trunk/backup-svn/main.cpp (revision 7)
+++ trunk/backup-svn/main.cpp (revision 8)
@@ -29,19 +29,22 @@
2929 }
3030
3131 // check if we run program from full path
32- std::string filepath = argv[0];
33- size_t pos = filepath.find_last_of("/\\");
34- if(pos != std::string::npos)
35- app_path = std::string(filepath.c_str(), pos);
36- else
32+ if(argc > 0)
33+ {
34+ std::string filepath = argv[0];
35+ size_t pos = filepath.find_last_of("/\\");
36+ if(pos != std::string::npos)
37+ app_path = std::string(filepath.c_str(), pos);
38+ }
39+ if(!app_path.length())
3740 app_path = curr_path;
3841
3942
4043 ini_file = app_path + "/" + "backup-svn.ini";
41- if(!IniFile::Init(ini_file.c_str()))
44+ if(!cIniFile::Init(ini_file.c_str()))
4245 {
4346 ini_file = curr_path + "/" + "backup-svn.ini";
44- if(!IniFile::Init(ini_file.c_str()))
47+ if(!cIniFile::Init(ini_file.c_str()))
4548 cerr << "No INI file found!" << endl;
4649 }
4750
@@ -49,8 +52,8 @@
4952 wstring wcurr_path = cStringTools::convertToWString(curr_path.c_str());
5053
5154 //Get path for svnadmin
52- std::wstring program_wpath = IniFile::getString(L"FILES", L"SVNADMIN_PATH", L"");
53- //std::string program_path = IniFile::getString( "FILES", "SVNADMIN_PATH", "");
55+ std::wstring program_wpath = cIniFile::getString(L"FILES", L"SVNADMIN_PATH", L"");
56+ //std::string program_path = cIniFile::getString( "FILES", "SVNADMIN_PATH", "");
5457
5558 cBackupSVN backup(program_wpath.c_str(), wcurr_path.c_str());
5659 int result = backup.run();
Show on old repository browser