1 /*
2 BAREOS® - Backup Archiving REcovery Open Sourced
3
4 Copyright (C) 2020-2020 Bareos GmbH & Co. KG
5
6 This program is Free Software; you can redistribute it and/or
7 modify it under the terms of version three of the GNU Affero General Public
8 License as published by the Free Software Foundation, which is
9 listed in the file LICENSE.
10
11 This program is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Affero General Public License for more details.
15
16 You should have received a copy of the GNU Affero General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301, USA.
20 */
21
22 #include "include/bareos.h"
23 #include "cats/cats.h"
24 #include "cats/cats_backends.h"
25 #include "cats/sql_pooling.h"
26 #include "dird/dbcopy/database_connection.h"
27 #include "dird/dbcopy/database_export.h"
28 #include "dird/dbcopy/database_export_postgresql.h"
29 #include "dird/dbcopy/database_import.h"
30 #include "dird/dird_conf.h"
31 #include "dird/dird_globals.h"
32 #include "dird/get_database_connection.h"
33 #include "dird/jcr_private.h"
34 #include "dird/job.h"
35 #include "include/make_unique.h"
36 #include "lib/parse_conf.h"
37 #include "lib/util.h"
38
39 #include <array>
40 #include <iostream>
41
42 #if !defined HAVE_DYNAMIC_CATS_BACKENDS
43 #error "NOT DEFINED: HAVE_DYNAMIC_CATS_BACKENDS"
44 #endif
45
46 namespace directordaemon {
DoReloadConfig()47 bool DoReloadConfig() { return false; }
48 } // namespace directordaemon
49
50 class DbCopy {
51 public:
DbCopy(int argc,char ** argv)52 explicit DbCopy(int argc, char** argv)
53 {
54 InitMsg(nullptr, nullptr);
55 SetWorkingDir();
56 cl.ParseCommandLine(argc, argv);
57
58 ParseConfig();
59 std::cout << "Copying tables from \"" << cl.source_db_resource_name
60 << "\" to \"" << cl.destination_db_resource_name << "\""
61 << std::endl;
62 ConnectToDatabases();
63 }
64
DoDatabaseCopy()65 void DoDatabaseCopy()
66 {
67 std::cout << "gathering information about source catalog \""
68 << cl.source_db_resource_name << "\"..." << std::endl;
69 std::unique_ptr<DatabaseImport> imp(
70 DatabaseImport::Create(*source_db_, cl.maximum_number_of_rows));
71
72 std::cout << "gathering information about destination catalog \""
73 << cl.destination_db_resource_name << "\"..." << std::endl;
74 std::unique_ptr<DatabaseExport> exp(
75 DatabaseExport::Create(*destination_db_,
76 cl.use_sql_insert_statements_instead_of_copy
77 ? DatabaseExport::InsertMode::kSqlInsert
78 : DatabaseExport::InsertMode::kSqlCopy,
79 cl.empty_destination_tables));
80
81 std::cout << "copying tables..." << std::endl;
82 imp->ExportTo(*exp);
83 }
84
85 private:
SetWorkingDir()86 void SetWorkingDir()
87 {
88 if (getcwd(current_working_directory_.data(),
89 current_working_directory_.size()) == nullptr) {
90 throw std::runtime_error(
91 "Could not determine current working directory.");
92 }
93 SetWorkingDirectory(current_working_directory_.data());
94 }
95
ParseConfig()96 void ParseConfig()
97 {
98 directordaemon::my_config =
99 directordaemon::InitDirConfig(cl.configpath_.c_str(), M_ERROR_TERM);
100
101 my_config_.reset(directordaemon::my_config);
102
103 if (!directordaemon::my_config->ParseConfig()) {
104 throw std::runtime_error("Error when loading config.");
105 }
106
107 directordaemon::me = dynamic_cast<directordaemon::DirectorResource*>(
108 my_config->GetNextRes(directordaemon::R_DIRECTOR, nullptr));
109
110 if (directordaemon::me == nullptr) {
111 throw std::runtime_error("Could not find director resource.");
112 }
113
114 DbSetBackendDirs(directordaemon::me->backend_directories);
115 }
116
ConnectToDatabases()117 void ConnectToDatabases()
118 {
119 try {
120 source_db_ = std::make_unique<DatabaseConnection>(
121 cl.source_db_resource_name, directordaemon::my_config);
122
123 destination_db_ = std::make_unique<DatabaseConnection>(
124 cl.destination_db_resource_name, directordaemon::my_config);
125
126 if (source_db_->db_type != DatabaseType::Enum::kMysql) {
127 throw std::runtime_error("Error: Source database is not mysql");
128 }
129
130 if (destination_db_->db_type != DatabaseType::Enum::kPostgresql) {
131 throw std::runtime_error(
132 "Error: Destination database is not postgresql");
133 }
134
135 } catch (const std::runtime_error& e) {
136 throw e;
137 }
138 }
139
140 class CommandLineParser {
141 friend class DbCopy;
142
143 public:
ParseCommandLine(int argc,char ** argv)144 void ParseCommandLine(int argc, char** argv)
145 {
146 int c{};
147 bool options_error{false};
148 int argument_count{};
149
150 while ((c = getopt(argc, argv, "ic:l:?")) != -1 && !options_error) {
151 switch (c) {
152 case 'c':
153 configpath_ = optarg;
154 argument_count += 2;
155 break;
156 case 'i':
157 use_sql_insert_statements_instead_of_copy = true;
158 argument_count += 1;
159 break;
160 #if 0
161 case 'd':
162 empty_destination_tables = true;
163 ++argument_count;
164 break;
165 #endif
166 case 'l':
167 try {
168 maximum_number_of_rows = std::stoul(optarg);
169 } catch (...) {
170 throw std::runtime_error("Wrong argument for 'l'");
171 }
172 argument_count += 2;
173 break;
174 case '?':
175 default:
176 options_error = true;
177 break;
178 }
179 }
180
181 ++argument_count; // program name
182 ++argument_count; // source catalog name
183 ++argument_count; // destination catalog name
184
185 if (options_error || argc != argument_count) {
186 usage();
187 throw std::runtime_error(std::string());
188 }
189 source_db_resource_name = argv[argument_count - 2];
190 destination_db_resource_name = argv[argument_count - 1];
191 }
192
193 private:
194 std::string configpath_{"/etc/bareos"};
195 std::string source_db_resource_name, destination_db_resource_name;
196 bool empty_destination_tables{false};
197 bool use_sql_insert_statements_instead_of_copy{false};
198 std::size_t maximum_number_of_rows{};
199 static constexpr std::size_t year_of_release = 2020;
200
usage()201 static void usage() noexcept
202 {
203 kBareosVersionStrings.PrintCopyright(stderr, year_of_release);
204
205 fprintf(
206 stderr,
207 _("Usage: bareos-dbcopy [options] Source Destination\n"
208 " -c <path> use <path> as configuration file or "
209 "directory\n"
210 " -i use SQL INSERT statements instead of COPY\n"
211 " -? print this message.\n"
212 "\n"));
213 }
214 }; // class CommandLineParser
215
216 public:
217 ~DbCopy() = default;
218 DbCopy(const DbCopy& other) = delete;
219 DbCopy(const DbCopy&& other) = delete;
220 DbCopy& operator=(const DbCopy& rhs) = delete;
221 DbCopy& operator=(const DbCopy&& rhs) = delete;
222
223 private:
224 CommandLineParser cl;
225 std::unique_ptr<DatabaseConnection> source_db_;
226 std::unique_ptr<DatabaseConnection> destination_db_;
227 std::unique_ptr<ConfigurationParser> my_config_;
228 static constexpr std::size_t sizeof_workingdir = 10 * 1024;
229 std::array<char, sizeof_workingdir> current_working_directory_{};
230 };
231
232 class Cleanup {
233 public:
234 Cleanup() = default;
~Cleanup()235 ~Cleanup()
236 {
237 DbSqlPoolDestroy();
238 DbFlushBackends();
239 }
240 };
241
main(int argc,char ** argv)242 int main(int argc, char** argv)
243 {
244 Cleanup c;
245
246 try {
247 DbCopy dbcopy(argc, argv);
248 dbcopy.DoDatabaseCopy();
249 } catch (const std::runtime_error& e) {
250 std::string errstring{e.what()};
251 if (!errstring.empty()) {
252 std::cerr << std::endl << std::endl << e.what() << std::endl;
253 }
254 return 1;
255 }
256 std::cout << "database copy completed successfully" << std::endl;
257 return 0;
258 }
259