1 /*
2    Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
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 #include "atrt.hpp"
26 #include <portlib/NdbDir.hpp>
27 #include <portlib/NdbSleep.h>
28 
29 static bool create_directory(const char * path);
30 
31 bool
setup_directories(atrt_config & config,int setup)32 setup_directories(atrt_config& config, int setup)
33 {
34   /**
35    * 0 = validate
36    * 1 = setup
37    * 2 = setup+clean
38    */
39   for (size_t i = 0; i < config.m_clusters.size(); i++)
40   {
41     atrt_cluster& cluster = *config.m_clusters[i];
42     for (size_t j = 0; j<cluster.m_processes.size(); j++)
43     {
44       atrt_process& proc = *cluster.m_processes[j];
45       const char * dir = proc.m_proc.m_cwd.c_str();
46       struct stat sbuf;
47       int exists = 0;
48       if (lstat(dir, &sbuf) == 0)
49       {
50 	if (S_ISDIR(sbuf.st_mode))
51 	  exists = 1;
52 	else
53 	  exists = -1;
54       }
55 
56       switch(setup){
57       case 0:
58 	switch(exists){
59 	case 0:
60 	  g_logger.error("Could not find directory: %s", dir);
61 	  return false;
62 	case -1:
63 	  g_logger.error("%s is not a directory!", dir);
64 	  return false;
65 	}
66 	break;
67       case 1:
68 	if (exists == -1)
69 	{
70 	  g_logger.error("%s is not a directory!", dir);
71 	  return false;
72 	}
73 	break;
74       case 2:
75 	if (exists == 1)
76 	{
77 	  if (!remove_dir(dir))
78 	  {
79 	    g_logger.error("Failed to remove %s!", dir);
80 	    return false;
81 	  }
82 	  exists = 0;
83 	  break;
84 	}
85 	else if (exists == -1)
86 	{
87 	  if (!unlink(dir))
88 	  {
89 	    g_logger.error("Failed to remove %s!", dir);
90 	    return false;
91 	  }
92 	  exists = 0;
93 	}
94       }
95       if (exists != 1)
96       {
97 	if (!create_directory(dir))
98 	{
99 	  return false;
100 	}
101       }
102     }
103   }
104   return true;
105 }
106 
107 static
108 void
printfile(FILE * out,Properties & props,const char * section,...)109 printfile(FILE* out, Properties& props, const char * section, ...)
110 {
111   Properties::Iterator it (&props);
112   const char * name = it.first();
113   if (name)
114   {
115     va_list ap;
116     va_start(ap, section);
117     /* const int ret = */ vfprintf(out, section, ap);
118     va_end(ap);
119     fprintf(out, "\n");
120 
121     for (; name; name = it.next())
122     {
123       const char* val;
124       props.get(name, &val);
125       fprintf(out, "%s %s\n", name + 2, val);
126     }
127     fprintf(out, "\n");
128   }
129   fflush(out);
130 }
131 
132 bool
setup_files(atrt_config & config,int setup,int sshx)133 setup_files(atrt_config& config, int setup, int sshx)
134 {
135   /**
136    * 0 = validate
137    * 1 = setup
138    * 2 = setup+clean
139    */
140   BaseString mycnf;
141   mycnf.assfmt("%s/my.cnf", g_basedir);
142 
143   if (!create_directory(g_basedir))
144   {
145     return false;
146   }
147 
148   if (mycnf != g_my_cnf)
149   {
150     struct stat sbuf;
151     int ret = lstat(to_native(mycnf).c_str(), &sbuf);
152 
153     if (ret == 0)
154     {
155       if (unlink(to_native(mycnf).c_str()) != 0)
156       {
157 	g_logger.error("Failed to remove %s", mycnf.c_str());
158 	return false;
159       }
160     }
161 
162     BaseString cp;
163     cp.assfmt("cp %s %s", g_my_cnf, mycnf.c_str());
164     to_fwd_slashes(cp);
165     if (sh(cp.c_str()) != 0)
166     {
167       g_logger.error("Failed to '%s'", cp.c_str());
168       return false;
169     }
170   }
171 
172   if (setup == 2 || config.m_generated)
173   {
174     /**
175      * Do mysql_install_db
176      */
177     for (size_t i = 0; i < config.m_clusters.size(); i++)
178     {
179       atrt_cluster& cluster = *config.m_clusters[i];
180       for (size_t j = 0; j<cluster.m_processes.size(); j++)
181       {
182 	atrt_process& proc = *cluster.m_processes[j];
183 	if (proc.m_type == atrt_process::AP_MYSQLD)
184 #ifndef _WIN32
185 	{
186 	  const char * val;
187 	  require(proc.m_options.m_loaded.get("--datadir=", &val));
188 	  BaseString tmp;
189 	  tmp.assfmt("%s/bin/mysql_install_db --defaults-file=%s/my.cnf --datadir=%s > %s/mysql_install_db.log 2>&1",
190 		     g_prefix, g_basedir, val, proc.m_proc.m_cwd.c_str());
191 
192           to_fwd_slashes(tmp);
193 	  if (sh(tmp.c_str()) != 0)
194 	  {
195 	    g_logger.error("Failed to mysql_install_db for %s, cmd: '%s'",
196 			   proc.m_proc.m_cwd.c_str(),
197 			   tmp.c_str());
198 	  }
199 	  else
200 	  {
201 	    g_logger.info("mysql_install_db for %s",
202 			  proc.m_proc.m_cwd.c_str());
203 	  }
204         }
205 #else
206         {
207           g_logger.info("not running mysql_install_db for %s",
208                          proc.m_proc.m_cwd.c_str());
209         }
210 #endif
211       }
212     }
213   }
214 
215   FILE * out = NULL;
216   bool retval = true;
217   if (config.m_generated == false)
218   {
219     g_logger.info("Nothing configured...");
220   }
221   else
222   {
223     out = fopen(mycnf.c_str(), "a+");
224     if (out == 0)
225     {
226       g_logger.error("Failed to open %s for append", mycnf.c_str());
227       return false;
228     }
229     time_t now = time(0);
230     fprintf(out, "#\n# Generated by atrt\n");
231     fprintf(out, "# %s\n", ctime(&now));
232   }
233 
234   for (size_t i = 0; i < config.m_clusters.size(); i++)
235   {
236     atrt_cluster& cluster = *config.m_clusters[i];
237     if (out)
238     {
239       Properties::Iterator it(&cluster.m_options.m_generated);
240       printfile(out, cluster.m_options.m_generated,
241 		"[mysql_cluster%s]", cluster.m_name.c_str());
242     }
243 
244     for (size_t j = 0; j<cluster.m_processes.size(); j++)
245     {
246       atrt_process& proc = *cluster.m_processes[j];
247 
248       if (out)
249       {
250 	switch(proc.m_type){
251 	case atrt_process::AP_NDB_MGMD:
252 	  printfile(out, proc.m_options.m_generated,
253 		    "[cluster_config.ndb_mgmd.%d%s]",
254 		    proc.m_index, proc.m_cluster->m_name.c_str());
255 	  break;
256 	case atrt_process::AP_NDBD:
257 	  printfile(out, proc.m_options.m_generated,
258 		    "[cluster_config.ndbd.%d%s]",
259 		    proc.m_index, proc.m_cluster->m_name.c_str());
260 	  break;
261 	case atrt_process::AP_MYSQLD:
262 	  printfile(out, proc.m_options.m_generated,
263 		    "[mysqld.%d%s]",
264 		    proc.m_index, proc.m_cluster->m_name.c_str());
265 	  break;
266 	case atrt_process::AP_NDB_API:
267 	  break;
268 	case atrt_process::AP_CLIENT:
269 	  printfile(out, proc.m_options.m_generated,
270 		    "[client.%d%s]",
271 		    proc.m_index, proc.m_cluster->m_name.c_str());
272 	  break;
273 	case atrt_process::AP_ALL:
274 	case atrt_process::AP_CLUSTER:
275 	  abort();
276 	}
277       }
278 
279       /**
280        * Create env.sh
281        */
282       BaseString tmp;
283       tmp.assfmt("%s/env.sh", proc.m_proc.m_cwd.c_str());
284       to_native(tmp);
285       char **env = BaseString::argify(0, proc.m_proc.m_env.c_str());
286       if (env[0] || proc.m_proc.m_path.length())
287       {
288 	Vector<BaseString> keys;
289 	FILE *fenv = fopen(tmp.c_str(), "w+");
290 	if (fenv == 0)
291 	{
292 	  g_logger.error("Failed to open %s for writing", tmp.c_str());
293 	  retval = false;
294           goto end;
295 	}
296 	for (size_t k = 0; env[k]; k++)
297 	{
298 	  tmp = env[k];
299 	  ssize_t pos = tmp.indexOf('=');
300 	  require(pos > 0);
301 	  env[k][pos] = 0;
302 	  fprintf(fenv, "%s=\"%s\"\n", env[k], env[k]+pos+1);
303 	  keys.push_back(env[k]);
304 	  free(env[k]);
305 	}
306 	if (proc.m_proc.m_path.length())
307 	{
308 	  fprintf(fenv, "CMD=\"%s", proc.m_proc.m_path.c_str());
309 	  if (proc.m_proc.m_args.length())
310 	  {
311 	    fprintf(fenv, " %s", proc.m_proc.m_args.c_str());
312 	  }
313 	  fprintf(fenv, "\"\nexport CMD\n");
314 	}
315 
316 	fprintf(fenv, "PATH=%s/bin:%s/libexec:$PATH\n", g_prefix, g_prefix);
317 	keys.push_back("PATH");
318 	for (size_t k = 0; k<keys.size(); k++)
319 	  fprintf(fenv, "export %s\n", keys[k].c_str());
320 	fflush(fenv);
321 	fclose(fenv);
322       }
323       free(env);
324 
325       {
326         tmp.assfmt("%s/ssh-login.sh", proc.m_proc.m_cwd.c_str());
327         FILE* fenv = fopen(tmp.c_str(), "w+");
328         if (fenv == 0)
329         {
330           g_logger.error("Failed to open %s for writing", tmp.c_str());
331           retval = false;
332           goto end;
333         }
334         fprintf(fenv, "#!/bin/sh\n");
335         fprintf(fenv, "cd %s\n", proc.m_proc.m_cwd.c_str());
336         fprintf(fenv, "[ -f /etc/profile ] && . /etc/profile\n");
337         fprintf(fenv, ". ./env.sh\n");
338         fprintf(fenv, "ulimit -Sc unlimited\n");
339         fprintf(fenv, "bash -i");
340         fflush(fenv);
341         fclose(fenv);
342       }
343     }
344   }
345 
346 end:
347   if (out)
348   {
349     fclose(out);
350   }
351 
352   return retval;
353 }
354 
355 
356 static
357 bool
create_directory(const char * path)358 create_directory(const char * path)
359 {
360   BaseString native(path);
361   to_native(native);
362   BaseString tmp(path);
363   Vector<BaseString> list;
364 
365   if (tmp.split(list, "/") == 0)
366   {
367     g_logger.error("Failed to create directory: %s", tmp.c_str());
368     return false;
369   }
370 
371   BaseString cwd = IF_WIN("","/");
372   for (size_t i = 0; i < list.size(); i++)
373   {
374     cwd.append(list[i].c_str());
375     cwd.append("/");
376     NdbDir::create(cwd.c_str(),
377                    NdbDir::u_rwx() | NdbDir::g_r() | NdbDir::g_x(),
378                    true);
379   }
380 
381   struct stat sbuf;
382   if (lstat(native.c_str(), &sbuf) != 0 ||
383       !S_ISDIR(sbuf.st_mode))
384   {
385     g_logger.error("Failed to create directory: %s (%s)",
386 		   native.c_str(),
387 		   cwd.c_str());
388     return false;
389   }
390 
391   return true;
392 }
393 
394 bool
remove_dir(const char * path,bool inclusive)395 remove_dir(const char * path, bool inclusive)
396 {
397   if (access(path, 0))
398     return true;
399 
400   const int max_retries = 20;
401   int attempt = 0;
402 
403   while(true)
404   {
405     if (NdbDir::remove_recursive(path, !inclusive))
406       return true;
407 
408     attempt++;
409     if (attempt > max_retries)
410     {
411       g_logger.error("Failed to remove directory '%s'!", path);
412       return false;
413     }
414 
415     g_logger.warning(" - attempt %d to remove directory '%s' failed "
416                      ", retrying...", attempt, path);
417 
418     NdbSleep_MilliSleep(100);
419   }
420 
421   abort(); // Never reached
422   return false;
423 }
424