1 /*
2    Copyright (c) 2003, 2021, Oracle and/or its affiliates.
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License, version 2.0,
6    as published by the Free Software Foundation.
7 
8    This program is also distributed with certain software (including
9    but not limited to OpenSSL) that is licensed under separate terms,
10    as designated in a particular file or component or in included license
11    documentation.  The authors of MySQL hereby grant you an additional
12    permission to link the program and your derivative works with the
13    separately licensed software that they have included with MySQL.
14 
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License, version 2.0, for more details.
19 
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
23 */
24 
25 #ifndef atrt_config_hpp
26 #define atrt_config_hpp
27 
28 #include <ndb_global.h>
29 #include <Vector.hpp>
30 #include <BaseString.hpp>
31 #include <NdbAutoPtr.hpp>
32 #include <Logger.hpp>
33 #include <mgmapi.h>
34 #include <CpcClient.hpp>
35 #include <Properties.hpp>
36 #include <mysql.h>
37 #include <my_sys.h>
38 #include <my_getopt.h>
39 #ifdef HAVE_MY_DEFAULT_H
40 #include <my_default.h>
41 #endif
42 #include <my_dir.h>
43 
44 enum ErrorCodes
45 {
46   ERR_OK = 0,
47   ERR_NDB_FAILED = 101,
48   ERR_SERVERS_FAILED = 102,
49   ERR_MAX_TIME_ELAPSED = 103,
50   ERR_COMMAND_FAILED = 104,
51   ERR_FAILED_TO_START = 105
52 };
53 
54 struct atrt_host
55 {
56   unsigned m_index;
57   BaseString m_user;
58   BaseString m_basedir;
59   BaseString m_hostname;
60   SimpleCpcClient * m_cpcd;
61   Vector<struct atrt_process*> m_processes;
62 };
63 
64 struct atrt_options
65 {
66   enum Feature {
67     AO_REPLICATION = 1,
68     AO_NDBCLUSTER = 2
69   };
70 
71   int m_features;
72   Properties m_loaded;
73   Properties m_generated;
74 };
75 
76 struct atrt_process
77 {
78   unsigned m_index;
79   struct atrt_host * m_host;
80   struct atrt_cluster * m_cluster;
81 
82   enum Type {
83     AP_ALL          = 255
84     ,AP_NDBD         = 1
85     ,AP_NDB_API      = 2
86     ,AP_NDB_MGMD     = 4
87     ,AP_MYSQLD       = 16
88     ,AP_CLIENT       = 32
89     ,AP_CLUSTER      = 256 // Used for options parsing for "cluster" options
90   } m_type;
91 
92   SimpleCpcClient::Process m_proc;
93 
94   NdbMgmHandle m_ndb_mgm_handle;   // if type == ndb_mgm
95   atrt_process * m_mysqld;         // if type == client
96   atrt_process * m_rep_src;        // if type == mysqld
97   Vector<atrt_process*> m_rep_dst; // if type == mysqld
98   MYSQL m_mysql;                   // if type == mysqld
99   atrt_options m_options;
100   uint m_nodeid;                   // if m_fix_nodeid
101 
102   struct {
103     bool m_saved;
104     SimpleCpcClient::Process m_proc;
105   } m_save;
106 
107 };
108 
109 struct atrt_cluster
110 {
111   BaseString m_name;
112   BaseString m_dir;
113   Vector<atrt_process*> m_processes;
114   atrt_options m_options;
115   uint m_next_nodeid;                   // if m_fix_nodeid
116 };
117 
118 struct atrt_config
119 {
120   bool m_generated;
121   BaseString m_key;
122   BaseString m_replication;
123   Vector<atrt_host*> m_hosts;
124   Vector<atrt_cluster*> m_clusters;
125   Vector<atrt_process*> m_processes;
126 };
127 
128 struct atrt_testcase
129 {
130   bool m_report;
131   bool m_run_all;
132   time_t m_max_time;
133   BaseString m_name;
134   BaseString m_mysqld_options;
135 
136   struct Command
137   {
138     atrt_process::Type m_cmd_type;
139     BaseString m_exe;
140     BaseString m_args;
141   } m_cmd; // Todo make array of these...
142 };
143 
144 extern Logger g_logger;
145 
146 bool parse_args(int argc, char** argv);
147 bool setup_config(atrt_config&, const char * mysqld);
148 bool configure(atrt_config&, int setup);
149 bool setup_directories(atrt_config&, int setup);
150 bool setup_files(atrt_config&, int setup, int sshx);
151 
152 bool deploy(int, atrt_config&);
153 bool sshx(atrt_config&, unsigned procmask);
154 bool start(atrt_config&, unsigned procmask);
155 
156 bool remove_dir(const char *, bool incl = true);
157 bool connect_hosts(atrt_config&);
158 bool connect_ndb_mgm(atrt_config&);
159 bool wait_ndb(atrt_config&, int ndb_mgm_node_status);
160 bool start_processes(atrt_config&, int);
161 bool stop_processes(atrt_config&, int);
162 bool update_status(atrt_config&, int);
163 int is_running(atrt_config&, int);
164 bool gather_result(atrt_config&, int * result);
165 
166 bool read_test_case(FILE *, atrt_testcase&, int& line);
167 bool setup_test_case(atrt_config&, const atrt_testcase&);
168 
169 bool setup_hosts(atrt_config&);
170 
171 bool do_command(atrt_config& config);
172 
173 bool start_process(atrt_process & proc);
174 bool stop_process(atrt_process & proc);
175 
176 bool connect_mysqld(atrt_process & proc);
177 bool disconnect_mysqld(atrt_process & proc);
178 
179 /**
180  * check configuration if any changes has been
181  *   done for the duration of the latest running test
182  *   if so, return true, and reset those changes
183  *   (true, indicates that a restart is needed to actually
184  *    reset the running processes)
185  */
186 bool reset_config(atrt_config&);
187 
188 NdbOut&
189 operator<<(NdbOut& out, const atrt_process& proc);
190 
191 /**
192  * SQL
193  */
194 bool setup_db(atrt_config&);
195 
196 /**
197  * Global variables...
198  */
199 extern Logger g_logger;
200 
201 extern const char * g_cwd;
202 extern const char * g_my_cnf;
203 extern const char * g_user;
204 extern const char * g_basedir;
205 extern const char * g_prefix;
206 extern const char * g_prefix1;
207 extern int          g_baseport;
208 extern int          g_fqpn;
209 extern int          g_fix_nodeid;
210 extern int          g_default_ports;
211 extern int          g_restart;
212 
213 extern const char * g_clusters;
214 
215 /**
216  * Since binaries move location between 5.1 and 5.5
217  *   we keep full path to them here
218  */
219 char * find_bin_path(const char * basename);
220 char * find_bin_path(const char * prefix, const char * basename);
221 extern const char * g_ndb_mgmd_bin_path;
222 extern const char * g_ndbd_bin_path;
223 extern const char * g_ndbmtd_bin_path;
224 extern const char * g_mysqld_bin_path;
225 extern const char * g_mysql_install_db_bin_path;
226 extern const char * g_libmysqlclient_so_path;
227 
228 extern const char * g_search_path[];
229 
230 #ifdef _WIN32
231 #include <direct.h>
232 
lstat(const char * name,struct stat * buf)233 inline int lstat(const char *name, struct stat *buf) {
234   return stat(name, buf);
235 }
236 
S_ISREG(int x)237 inline int S_ISREG(int x) {
238   return x & _S_IFREG;
239 }
240 
S_ISDIR(int x)241 inline int S_ISDIR(int x) {
242   return x & _S_IFDIR;
243 }
244 
245 #endif
246 
247 
248 /* in-place replace */
replace_chars(char * str,char from,char to)249 static inline char* replace_chars(char *str, char from, char to)
250 {
251   int i;
252 
253   for(i = 0; str[i]; i++) {
254     if(i && str[i]==from && str[i-1]!=' ') {
255       str[i]=to;
256     }
257   }
258   return str;
259 }
260 
replace_chars(BaseString & bs,char from,char to)261 static inline BaseString &replace_chars(BaseString &bs, char from, char to)
262 {
263   replace_chars((char*)bs.c_str(), from, to);
264   return bs;
265 }
to_native(BaseString & bs)266 static inline BaseString &to_native(BaseString &bs) {
267   return replace_chars(bs, DIR_SEPARATOR[0]=='/'?'\\':'/', DIR_SEPARATOR[0]);
268 }
to_fwd_slashes(BaseString & bs)269 static inline BaseString &to_fwd_slashes(BaseString &bs) {
270   return replace_chars(bs, '\\', '/');
271 }
to_fwd_slashes(char * bs)272 static inline char* to_fwd_slashes(char* bs) {
273   return replace_chars(bs, '\\', '/');
274 }
275 
276 //you must free() the result
replace_drive_letters(const char * path)277 static inline char* replace_drive_letters(const char* path) {
278 
279   int i, j;
280   int count; // number of ':'s in path
281   char *retval; // return value
282   const char cygdrive[] = "/cygdrive";
283   size_t cyglen = strlen(cygdrive), retval_len;
284 
285   for(i = 0, count = 0; path[i]; i++) {
286     count +=  path[i] == ':';
287   }
288   retval_len = strlen(path) + count * cyglen + 1;
289   retval = (char*)malloc(retval_len);
290 
291   for(i = j = 0; path[i]; i++) {
292     if(path[i] && path[i+1]) {
293       if( (!i || isspace(path[i-1]) || ispunct(path[i-1])) && path[i+1] == ':')
294 {
295         require(path[i+2] == '/');
296         j += BaseString::snprintf(retval + j, retval_len - 1, "%s/%c", cygdrive, path[i]);
297         i++;
298         continue;
299       }
300     }
301     retval[j++] = path[i];
302   }
303   retval[j] = 0;
304 
305   return retval;
306 }
307 
sh(const char * script)308 static inline int sh(const char *script){
309 
310 #ifdef _WIN32
311   g_logger.debug("sh('%s')", script);
312 
313   /*
314     Running sh script on Windows
315     1) Write the command to run into temporary file
316     2) Run the temporary file with 'sh <temp_file_name>'
317   */
318 
319   char tmp_path[MAX_PATH];
320   if (GetTempPath(sizeof(tmp_path), tmp_path) == 0)
321   {
322     g_logger.error( "GetTempPath failed, error: %d", GetLastError());
323     return -1;
324   }
325 
326   char tmp_file[MAX_PATH];
327   if (GetTempFileName(tmp_path, "sh_", 0, tmp_file) == 0)
328   {
329     g_logger.error( "GetTempFileName failed, error: %d", GetLastError());
330     return -1;
331   }
332 
333   FILE* fp = fopen(tmp_file, "w");
334   if (fp == NULL)
335   {
336     g_logger.error( "Cannot open file '%s', error: %d", tmp_file, errno);
337     return -1;
338   }
339 
340   // cygwin'ify the script and write it to temp file
341   {
342     char* cygwin_script = replace_drive_letters(script);
343     g_logger.debug(" - cygwin_script: '%s' ", cygwin_script);
344     fprintf(fp, "%s", cygwin_script);
345     free(cygwin_script);
346   }
347 
348   fclose(fp);
349 
350   // Run the temp file with "sh"
351   BaseString command;
352   command.assfmt("sh %s", tmp_file);
353   g_logger.debug(" - running '%s' ", command.c_str());
354 
355   int ret = system(command.c_str());
356   if (ret == 0)
357     g_logger.debug(" - OK!");
358   else
359     g_logger.warning("Running the command '%s' as '%s' failed, ret: %d",
360                      script, command.c_str(), ret);
361 
362   // Remove the temp file
363   unlink(tmp_file);
364 
365   return ret;
366 
367 #else
368 
369   return system(script);
370 
371 #endif
372 
373 }
374 #endif
375