1 /*
2    Copyright (c) 2007, 2019, 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 <ndb_global.h>
26 #include <util/ndb_opts.h>
27 #include <map>
28 #include <string>
29 #include <util/BaseString.hpp>
30 #include <util/File.hpp>
31 #include <util/NdbOut.hpp>
32 #include "atrt.hpp"
33 
34 extern int g_mt;
35 extern int g_mt_rr;
36 
37 static atrt_host* find(const char* hostname, Vector<atrt_host*>&);
38 static bool load_process(atrt_config&, atrt_cluster&, BaseString,
39                          atrt_process::Type, unsigned idx,
40                          const char* hostname);
41 static bool load_options(int argc, char** argv, int type, atrt_options&);
42 bool load_custom_processes(atrt_config& config, atrt_cluster& cluster);
43 bool load_deployment_options_for_process(atrt_cluster& cluster,
44                                          atrt_process& proc);
45 bool matches_custom_process_option(char* arg, BaseString& proc_name,
46                                    BaseString& hosts);
47 BaseString getProcGroupName(atrt_process::Type type);
48 
49 enum {
50   PO_NDB = atrt_options::AO_NDBCLUSTER,
51   PO_REP_SLAVE = 256,
52   PO_REP_MASTER = 512,
53   PO_REP = (atrt_options::AO_REPLICATION | PO_REP_SLAVE | PO_REP_MASTER)
54 };
55 
56 struct proc_option {
57   const char* name;
58   int type;
59   int options;
60 };
61 
62 static struct proc_option f_options[] = {
63     {"--FileSystemPath=", atrt_process::AP_NDBD, 0},
64     {"--PortNumber=", atrt_process::AP_NDB_MGMD, 0},
65     {"--datadir=", atrt_process::AP_MYSQLD, 0},
66     {"--socket=", atrt_process::AP_MYSQLD | atrt_process::AP_CLIENT, 0},
67     {"--port=",
68      atrt_process::AP_MYSQLD | atrt_process::AP_CLIENT |
69          atrt_process::AP_CUSTOM,
70      0},
71     {"--host=", atrt_process::AP_CLIENT, 0},
72     {"--server-id=", atrt_process::AP_MYSQLD, PO_REP},
73     {"--log-bin", atrt_process::AP_MYSQLD, PO_REP_MASTER},
74     {"--ndb-connectstring=", atrt_process::AP_MYSQLD | atrt_process::AP_CLUSTER,
75      PO_NDB},
76     {"--ndbcluster", atrt_process::AP_MYSQLD, PO_NDB},
77     {0, 0, 0}};
78 const char* ndbcs = "--ndb-connectstring=";
79 
setup_config(atrt_config & config,const char * atrt_mysqld)80 bool setup_config(atrt_config& config, const char* atrt_mysqld) {
81   config.m_site = g_site;
82 
83   BaseString tmp(g_clusters);
84 
85   if (atrt_mysqld) {
86     tmp.appfmt(",.atrt");
87   }
88   Vector<BaseString> clusters;
89   tmp.split(clusters, ",");
90 
91   bool fqpn = clusters.size() > 1 || g_fqpn;
92 
93   size_t j;
94   for (unsigned i = 0; i < clusters.size(); i++) {
95     struct atrt_cluster* cluster = new atrt_cluster;
96     config.m_clusters.push_back(cluster);
97 
98     cluster->m_name = clusters[i];
99     cluster->m_options.m_features = 0;
100     if (fqpn) {
101       cluster->m_dir.assfmt("cluster%s/", cluster->m_name.c_str());
102     } else {
103       cluster->m_dir = "";
104     }
105     cluster->m_next_nodeid = 1;
106 
107     int argc = 1;
108     const char* argv[] = {"atrt", 0, 0};
109 
110     BaseString buf;
111     buf.assfmt("--defaults-group-suffix=%s", clusters[i].c_str());
112     argv[argc++] = buf.c_str();
113     char** tmp = (char**)argv;
114     const char* groups[] = {"cluster_config", 0};
115     MEM_ROOT* alloc = new MEM_ROOT{PSI_NOT_INSTRUMENTED, 512}; // LEAK
116     int ret = load_defaults(g_my_cnf, groups, &argc, &tmp, alloc);
117     if (ret) {
118       g_logger.error("Unable to load defaults for cluster: %s",
119                      clusters[i].c_str());
120       return false;
121     }
122 
123     struct {
124       atrt_process::Type type;
125       const char* name;
126       const char* value;
127     } proc_args[] = {{atrt_process::AP_NDB_MGMD, "--ndb_mgmd=", 0},
128                      {atrt_process::AP_NDBD, "--ndbd=", 0},
129                      {atrt_process::AP_NDB_API, "--ndbapi=", 0},
130                      {atrt_process::AP_NDB_API, "--api=", 0},
131                      {atrt_process::AP_MYSQLD, "--mysqld=", 0},
132                      {atrt_process::AP_ALL, 0, 0}};
133 
134     /**
135      * Find all processes...
136      */
137     for (j = 0; j < (size_t)argc; j++) {
138       if (my_getopt_is_args_separator(tmp[j])) /* skip arguments separator */
139         continue;
140       for (unsigned k = 0; proc_args[k].name; k++) {
141         if (!strncmp(tmp[j], proc_args[k].name, strlen(proc_args[k].name))) {
142           proc_args[k].value = tmp[j] + strlen(proc_args[k].name);
143           break;
144         }
145       }
146     }
147 
148     if (strcmp(clusters[i].c_str(), ".atrt") == 0) {
149       /**
150        * Only use a mysqld...
151        */
152       proc_args[0].value = 0;
153       proc_args[1].value = 0;
154       proc_args[2].value = 0;
155       proc_args[3].value = 0;
156       proc_args[4].value = atrt_mysqld;
157     }
158 
159     /**
160      * Load each process
161      */
162     BaseString name;
163     for (j = 0; proc_args[j].name; j++) {
164       if (proc_args[j].value) {
165         BaseString tmp(proc_args[j].value);
166         Vector<BaseString> list;
167         tmp.split(list, ",");
168         for (unsigned k = 0; k < list.size(); k++)
169           if (!load_process(config, *cluster, name, proc_args[j].type, k + 1,
170                             list[k].c_str()))
171             return false;
172       }
173     }
174 
175     /**
176      * Load custom processes
177      */
178     if (!load_custom_processes(config, *cluster)) return false;
179 
180     {
181       /**
182        * Load cluster options
183        */
184       int argc = 1;
185       const char* argv[] = {"atrt", 0, 0};
186       argv[argc++] = buf.c_str();
187       const char* groups[] = {"mysql_cluster", 0};
188       char** tmp = (char**)argv;
189       MEM_ROOT* alloc = new MEM_ROOT{PSI_NOT_INSTRUMENTED, 512}; // LEAK
190       ret = load_defaults(g_my_cnf, groups, &argc, &tmp, alloc);
191 
192       if (ret) {
193         g_logger.error("Unable to load defaults for cluster: %s",
194                        clusters[i].c_str());
195         return false;
196       }
197 
198       load_options(argc, tmp, atrt_process::AP_CLUSTER, cluster->m_options);
199     }
200   }
201   return true;
202 }
203 
load_custom_processes(atrt_config & config,atrt_cluster & cluster)204 bool load_custom_processes(atrt_config& config, atrt_cluster& cluster) {
205   int argc = 1;
206   const char* argv[] = {"atrt", 0, 0};
207 
208   BaseString buf;
209   buf.assfmt("--defaults-group-suffix=%s", cluster.m_name.c_str());
210   argv[argc++] = buf.c_str();
211   char** tmp = (char**)argv;
212   const char* groups[] = {"cluster_deployment", 0};
213 
214   MEM_ROOT* alloc = new MEM_ROOT{PSI_NOT_INSTRUMENTED, 512}; // LEAK
215   int ret = load_defaults(g_my_cnf, groups, &argc, &tmp, alloc);
216   if (ret != 0) {
217     g_logger.error("Failure to '%s' group for cluster %s", groups[0],
218                    cluster.m_name.c_str());
219     return false;
220   }
221 
222   for (int i = 1; i < argc; i++) {
223     BaseString proc_name;
224     BaseString hosts;
225     if (!matches_custom_process_option(tmp[i], proc_name, hosts)) continue;
226 
227     Vector<BaseString> host_list;
228     hosts.split(host_list, ",");
229     for (unsigned int j = 0; j < host_list.size(); j++) {
230       bool ok =
231           load_process(config, cluster, proc_name, atrt_process::AP_CUSTOM,
232                        j + 1, host_list[j].c_str());
233       if (!ok) return false;
234     }
235   }
236 
237   return true;
238 }
239 
matches_custom_process_option(char * arg,BaseString & proc_name,BaseString & hosts)240 bool matches_custom_process_option(char* arg, BaseString& proc_name,
241                                    BaseString& hosts) {
242   const char* opt_prefix = "--proc:";
243 
244   if (strncmp(arg, opt_prefix, strlen(opt_prefix)) != 0) return false;
245 
246   arg += strlen(opt_prefix);  // advance prefix
247 
248   char* list = strstr(arg, "=");
249   if (list == NULL) return false;
250 
251   proc_name.assign(arg, list - arg);
252 
253   list++;  // advance "="
254   hosts.assign(list);
255 
256   return true;
257 }
258 
find(const char * hostname,Vector<atrt_host * > & hosts)259 static atrt_host* find(const char* hostname, Vector<atrt_host*>& hosts) {
260   for (unsigned i = 0; i < hosts.size(); i++) {
261     if (hosts[i]->m_hostname == hostname) {
262       return hosts[i];
263     }
264   }
265 
266   atrt_host* host = new atrt_host;
267   host->m_index = hosts.size();
268   host->m_cpcd = new SimpleCpcClient(hostname, 1234);
269   host->m_basedir = g_basedir;
270   host->m_user = g_user;
271   host->m_hostname = hostname;
272   hosts.push_back(host);
273   return host;
274 }
275 
load_deployment_options_for_process(atrt_cluster & cluster,atrt_process & proc)276 bool load_deployment_options_for_process(atrt_cluster& cluster,
277                                          atrt_process& proc) {
278   if (proc.m_name.empty()) {
279     g_logger.debug("Skipping deployment_options loading for process type %d",
280                    proc.m_type);
281     return true;
282   }
283 
284   BaseString suffix;
285   suffix.assfmt("--defaults-group-suffix=%s", cluster.m_name.c_str());
286 
287   const char* argv[] = {"atrt", suffix.c_str(), 0};
288   int argc = 2;
289   char** tmp = (char**)argv;
290 
291   BaseString buf[2];
292   buf[0].assfmt("cluster_deployment.%s", proc.m_name.c_str());
293   buf[1].assfmt("cluster_deployment.%s.%u", proc.m_name.c_str(), proc.m_index);
294   const char* groups[] = {buf[0].c_str(), buf[1].c_str(), 0};
295 
296   MEM_ROOT* alloc = new MEM_ROOT{PSI_NOT_INSTRUMENTED, 512}; // LEAK
297   int ret = load_defaults(g_my_cnf, groups, &argc, &tmp, alloc);
298   if (ret != 0) {
299     g_logger.error("Failed to load defaults for cluster %s's process %s",
300                    cluster.m_name.c_str(), proc.m_name.c_str());
301     return false;
302   }
303 
304   char* cmd = NULL;
305   char* args = NULL;
306   bool generate_port = false;
307   char* cpuset = NULL;
308 
309   struct my_option options[] = {
310       {"cmd", 0, "Executable name", &cmd, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0,
311        0},
312       {"args", 0, "Arguments passed to process", &args, 0, 0, GET_STR, OPT_ARG,
313        0, 0, 0, 0, 0, 0},
314       {"port-generate", 0, "Flag to generate --port=N", &generate_port, 0, 0,
315        GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0},
316       {"cpuset", 0, "Process's CPU affinity", &cpuset, 0, 0, GET_STR, OPT_ARG,
317        0, 0, 0, 0, 0, 0},
318       {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}};
319 
320   int status = handle_options(&argc, &tmp, options, NULL);
321   if (status != 0) {
322     g_logger.error("Failed to handle options for cluster %s' process %d",
323                    cluster.m_name.c_str(), proc.m_type);
324 
325     return false;
326   }
327 
328   proc.m_proc.m_cpuset = BaseString(cpuset);
329 
330   if (proc.m_type != atrt_process::AP_CUSTOM) {
331     // Prevent overwriting settings of non custom processes
332     return true;
333   }
334 
335   if (!cmd) {
336     g_logger.error("Cluster's %s process %s must define 'cmd'",
337                    cluster.m_name.c_str(), proc.m_name.c_str());
338     return false;
339   }
340 
341   char* bin_path = find_bin_path(cmd);
342   if (!bin_path) {
343     g_logger.error("Cluster's %s custom process %s binary could not be found",
344                    cluster.m_name.c_str(), proc.m_name.c_str());
345     return false;
346   }
347 
348   proc.m_proc.m_path.assign(bin_path);
349 
350   if (args) {
351     proc.m_proc.m_args.appfmt(" %s", args);
352   }
353 
354   const char* port_arg = "--port=";
355   const char* val;
356   if (generate_port && !proc.m_options.m_loaded.get(port_arg, &val)) {
357     BaseString portno;
358     portno.assfmt("%d", g_baseport + proc.m_procno);
359 
360     proc.m_proc.m_args.appfmt(" %s%s", port_arg, portno.c_str());
361     proc.m_options.m_generated.put(port_arg, portno.c_str());
362     proc.m_options.m_loaded.put(port_arg, portno.c_str());
363   }
364 
365   return true;
366 }
367 
getProcGroupName(atrt_process::Type type)368 BaseString getProcGroupName(atrt_process::Type type) {
369   BaseString name;
370   switch (type) {
371     case atrt_process::AP_CLIENT:
372       name.assign("client");
373       break;
374     case atrt_process::AP_MYSQLD:
375       name.assign("mysqld");
376       break;
377     case atrt_process::AP_NDB_API:
378       name.assign("ndb_api");
379       break;
380     case atrt_process::AP_NDB_MGMD:
381       name.assign("ndb_mgmd");
382       break;
383     case atrt_process::AP_NDBD:
384       name.assign("ndbd");
385       break;
386     case atrt_process::AP_CUSTOM:
387     case atrt_process::AP_ALL:
388     case atrt_process::AP_CLUSTER:
389       // No group name in my.cnf skipping
390       break;
391   }
392 
393   return name;
394 }
395 
load_process(atrt_config & config,atrt_cluster & cluster,BaseString name,atrt_process::Type type,unsigned idx,const char * hostname)396 static bool load_process(atrt_config& config, atrt_cluster& cluster,
397                          BaseString name, atrt_process::Type type, unsigned idx,
398                          const char* hostname) {
399   atrt_host* host_ptr = find(hostname, config.m_hosts);
400   atrt_process* proc_ptr = new atrt_process;
401 
402   const unsigned proc_no = (unsigned)config.m_processes.size();
403   config.m_processes.push_back(proc_ptr);
404   host_ptr->m_processes.push_back(proc_ptr);
405   cluster.m_processes.push_back(proc_ptr);
406 
407   atrt_process& proc = *proc_ptr;
408 
409   proc.m_index = idx;
410   proc.m_type = type;
411 
412   proc.m_name = name.empty() ? getProcGroupName(type) : name;
413   proc.m_procno = proc_no;
414   proc.m_host = host_ptr;
415   proc.m_save.m_saved = false;
416   proc.m_nodeid = -1;
417   proc.m_cluster = &cluster;
418   proc.m_options.m_features = 0;
419   proc.m_rep_src = 0;
420   proc.m_proc.m_id = -1;
421   proc.m_proc.m_type = "temporary";
422   proc.m_proc.m_owner = "atrt";
423   if (config.m_site.length() == 0) {
424     proc.m_proc.m_group.assfmt("%s", cluster.m_name.c_str());
425   } else {
426     proc.m_proc.m_group.assfmt("%s-%s", config.m_site.c_str(),
427                                cluster.m_name.c_str());
428   }
429   proc.m_proc.m_stdout = "log.out";
430   proc.m_proc.m_stderr = "2>&1";
431   proc.m_proc.m_runas = proc.m_host->m_user;
432   proc.m_proc.m_ulimit = "c:unlimited";
433   proc.m_proc.m_env.assfmt("MYSQL_BASE_DIR=%s", g_prefix0);
434 
435   BaseString mysql_home(" MYSQL_HOME=");
436   mysql_home.append(g_basedir);
437   if (mysql_home.c_str()[mysql_home.length() - 1] != '/') {
438     mysql_home.append("/");
439   }
440   proc.m_proc.m_env.append(mysql_home);
441 
442   proc.m_proc.m_env.appfmt(" ATRT_PID=%u", (unsigned)proc_no);
443   proc.m_proc.m_shutdown_options = "";
444 
445   {
446     /**
447      * In 5.5...binaries aren't compiled with rpath
448      * So we need an explicit LD_LIBRARY_PATH
449      *
450      * Use path from libmysqlclient.so
451      */
452 #if defined(__MACH__)
453     const char* libname = g_resources.LIBMYSQLCLIENT_DYLIB;
454     BaseString libdir = g_resources.getLibraryDirectory(libname).c_str();
455     proc.m_proc.m_env.appfmt(" DYLD_LIBRARY_PATH=%s", libdir.c_str());
456 #else
457     const char* libname = g_resources.LIBMYSQLCLIENT_SO;
458     BaseString libdir = g_resources.getLibraryDirectory(libname).c_str();
459     proc.m_proc.m_env.appfmt(" LD_LIBRARY_PATH=%s", libdir.c_str());
460 #endif
461   }
462 
463   int argc = 1;
464   const char* argv[] = {"atrt", 0, 0};
465 
466   BaseString buf[10];
467   char** tmp = (char**)argv;
468   const char* groups[] = {0, 0, 0, 0};
469   switch (type) {
470     case atrt_process::AP_NDB_MGMD:
471       proc.m_nodeid = cluster.m_next_nodeid++;  // always specify node-id
472 
473       groups[0] = "cluster_config";
474       buf[1].assfmt("cluster_config.ndb_mgmd.%u", idx);
475       groups[1] = buf[1].c_str();
476       buf[0].assfmt("--defaults-group-suffix=%s", cluster.m_name.c_str());
477       argv[argc++] = buf[0].c_str();
478       break;
479     case atrt_process::AP_NDBD:
480       if (g_fix_nodeid) proc.m_nodeid = cluster.m_next_nodeid++;
481 
482       groups[0] = "cluster_config";
483       buf[1].assfmt("cluster_config.ndbd.%u", idx);
484       groups[1] = buf[1].c_str();
485       buf[0].assfmt("--defaults-group-suffix=%s", cluster.m_name.c_str());
486       argv[argc++] = buf[0].c_str();
487       break;
488     case atrt_process::AP_MYSQLD:
489       if (g_fix_nodeid) proc.m_nodeid = cluster.m_next_nodeid++;
490 
491       groups[0] = "mysqld";
492       groups[1] = "mysql_cluster";
493       buf[0].assfmt("--defaults-group-suffix=.%u%s", idx,
494                     cluster.m_name.c_str());
495       argv[argc++] = buf[0].c_str();
496       break;
497     case atrt_process::AP_CLIENT:
498       buf[0].assfmt("client.%u%s", idx, cluster.m_name.c_str());
499       groups[0] = buf[0].c_str();
500       break;
501     case atrt_process::AP_NDB_API:
502       if (g_fix_nodeid) proc.m_nodeid = cluster.m_next_nodeid++;
503       break;
504     case atrt_process::AP_CUSTOM:
505       buf[0].assfmt("%s%s", proc.m_name.c_str(), cluster.m_name.c_str());
506       groups[0] = buf[0].c_str();
507 
508       buf[1].assfmt("%s.%u%s", proc.m_name.c_str(), idx,
509                     cluster.m_name.c_str());
510       groups[1] = buf[1].c_str();
511       break;
512     default:
513       g_logger.critical("Unhandled process type: %d", type);
514       return false;
515   }
516 
517   MEM_ROOT* alloc = new MEM_ROOT{PSI_NOT_INSTRUMENTED, 512}; // LEAK
518   int ret = load_defaults(g_my_cnf, groups, &argc, &tmp, alloc);
519   if (ret) {
520     g_logger.error("Unable to load defaults for cluster: %s",
521                    cluster.m_name.c_str());
522     return false;
523   }
524 
525   load_options(argc, tmp, type, proc.m_options);
526 
527   BaseString dir;
528   dir.assfmt("%s/%s", proc.m_host->m_basedir.c_str(), cluster.m_dir.c_str());
529 
530   switch (type) {
531     case atrt_process::AP_NDB_MGMD: {
532       proc.m_proc.m_name.assfmt("%u-%s", proc_no, "ndb_mgmd");
533       proc.m_proc.m_cwd.assfmt("%sndb_mgmd.%u", dir.c_str(), proc.m_index);
534 
535       BaseString ndb_mgmd_bin_path =
536           g_resources.getExecutableFullPath(g_resources.NDB_MGMD).c_str();
537       proc.m_proc.m_path.assign(ndb_mgmd_bin_path);
538       proc.m_proc.m_env.appfmt(" MYSQL_GROUP_SUFFIX=%s",
539                                cluster.m_name.c_str());
540       proc.m_proc.m_args.assfmt("--defaults-file=%s/my.cnf",
541                                 proc.m_host->m_basedir.c_str());
542       proc.m_proc.m_args.appfmt(" --defaults-group-suffix=%s",
543                                 cluster.m_name.c_str());
544 
545       switch (config.m_config_type) {
546         case atrt_config::CNF: {
547           proc.m_proc.m_args.append(" --mycnf");
548           break;
549         }
550         case atrt_config::INI: {
551           proc.m_proc.m_args.assfmt("--config-file=%s/config%s.ini",
552                                     proc.m_host->m_basedir.c_str(),
553                                     cluster.m_name.c_str());
554           break;
555         }
556       }
557       proc.m_proc.m_args.append(" --nodaemon");
558       proc.m_proc.m_args.appfmt(" --ndb-nodeid=%u", proc.m_nodeid);
559       proc.m_proc.m_args.appfmt(" --configdir=%s", proc.m_proc.m_cwd.c_str());
560       break;
561     }
562     case atrt_process::AP_NDBD: {
563       proc.m_proc.m_name.assfmt("%u-%s", proc_no, "ndbd");
564       proc.m_proc.m_cwd.assfmt("%sndbd.%u", dir.c_str(), proc.m_index);
565 
566       if (g_mt == 0 || (g_mt == 1 && ((g_mt_rr++) & 1) == 0)) {
567         BaseString ndbd_bin_path =
568             g_resources.getExecutableFullPath(g_resources.NDBD).c_str();
569         proc.m_proc.m_path.assign(ndbd_bin_path);
570       } else {
571         BaseString ndbmtd_bin_path =
572             g_resources.getExecutableFullPath(g_resources.NDBMTD).c_str();
573         proc.m_proc.m_path.assign(ndbmtd_bin_path);
574       }
575 
576       proc.m_proc.m_env.appfmt(" MYSQL_GROUP_SUFFIX=%s",
577                                cluster.m_name.c_str());
578 
579       proc.m_proc.m_args.assfmt("--defaults-file=%s/my.cnf",
580                                 proc.m_host->m_basedir.c_str());
581       proc.m_proc.m_args.appfmt(" --defaults-group-suffix=%s",
582                                 cluster.m_name.c_str());
583       proc.m_proc.m_args.append(" --nodaemon -n");
584 
585       if (!g_restart) proc.m_proc.m_args.append(" --initial");
586       if (g_fix_nodeid)
587         proc.m_proc.m_args.appfmt(" --ndb-nodeid=%u", proc.m_nodeid);
588       break;
589     }
590     case atrt_process::AP_MYSQLD: {
591       proc.m_proc.m_name.assfmt("%u-%s", proc_no, "mysqld");
592 
593       BaseString mysqld_bin_path =
594           g_resources.getExecutableFullPath(g_resources.MYSQLD).c_str();
595       proc.m_proc.m_path.assign(mysqld_bin_path);
596 
597       proc.m_proc.m_args.assfmt("--defaults-file=%s/my.cnf",
598                                 proc.m_host->m_basedir.c_str());
599       proc.m_proc.m_args.appfmt(" --defaults-group-suffix=.%d%s", proc.m_index,
600                                 cluster.m_name.c_str());
601       proc.m_proc.m_args.append(" --core-file");
602       if (g_fix_nodeid)
603         proc.m_proc.m_args.appfmt(" --ndb-nodeid=%d", proc.m_nodeid);
604 
605       // Add ndb connect string
606       const char* val;
607       if (cluster.m_options.m_loaded.get(ndbcs, &val)) {
608         proc.m_proc.m_args.appfmt(" %s=%s", ndbcs, val);
609       }
610 
611       proc.m_proc.m_cwd.appfmt("%smysqld.%u", dir.c_str(), proc.m_index);
612       proc.m_proc.m_shutdown_options = "SIGKILL";  // not nice
613       proc.m_proc.m_env.appfmt(" MYSQL_GROUP_SUFFIX=.%u%s", proc.m_index,
614                                cluster.m_name.c_str());
615       break;
616     }
617     case atrt_process::AP_NDB_API: {
618       proc.m_proc.m_name.assfmt("%u-%s", proc_no, "ndb_api");
619       proc.m_proc.m_path = "";
620       proc.m_proc.m_args = "";
621       proc.m_proc.m_cwd.appfmt("%sndb_api.%u", dir.c_str(), proc.m_index);
622       proc.m_proc.m_env.appfmt(" MYSQL_GROUP_SUFFIX=%s",
623                                cluster.m_name.c_str());
624       break;
625     }
626     case atrt_process::AP_CLIENT: {
627       proc.m_proc.m_name.assfmt("%u-%s", proc_no, "mysql");
628       proc.m_proc.m_path = "";
629       proc.m_proc.m_args = "";
630       proc.m_proc.m_cwd.appfmt("%s/client.%u", dir.c_str(), proc.m_index);
631       proc.m_proc.m_env.appfmt(" MYSQL_GROUP_SUFFIX=.%d%s", proc.m_index,
632                                cluster.m_name.c_str());
633       break;
634     }
635     case atrt_process::AP_CUSTOM: {
636       proc.m_proc.m_name.assfmt("%u-%s", proc_no, proc.m_name.c_str());
637       proc.m_proc.m_cwd.assfmt("%s%s.%u", dir.c_str(), proc.m_name.c_str(),
638                                proc.m_index);
639 
640       const char* port_arg = "--port=";
641       const char* val;
642       if (proc.m_options.m_loaded.get(port_arg, &val)) {
643         proc.m_proc.m_args.assfmt("%s%s", port_arg, val);
644       }
645       break;
646     }
647     case atrt_process::AP_ALL:
648     case atrt_process::AP_CLUSTER:
649       g_logger.critical("Unhandled process type: %d", proc.m_type);
650       return false;
651   }
652 
653   if (type == atrt_process::AP_MYSQLD) {
654     /**
655      * Add a client for each mysqld
656      */
657     BaseString name;
658     if (!load_process(config, cluster, name, atrt_process::AP_CLIENT, idx,
659                       hostname)) {
660       return false;
661     }
662   }
663 
664   if (type == atrt_process::AP_CLIENT) {
665     proc.m_mysqld = cluster.m_processes[cluster.m_processes.size() - 2];
666   }
667 
668   bool status = load_deployment_options_for_process(cluster, proc);
669   return status;
670 }
671 
load_options(int argc,char ** argv,int type,atrt_options & opts)672 static bool load_options(int argc, char** argv, int type, atrt_options& opts) {
673   for (size_t i = 0; i < (size_t)argc; i++) {
674     if (ndb_is_load_default_arg_separator(argv[i])) continue;
675     for (size_t j = 0; f_options[j].name; j++) {
676       const char* name = f_options[j].name;
677       const size_t len = strlen(name);
678 
679       if ((f_options[j].type & type) && strncmp(argv[i], name, len) == 0) {
680         opts.m_loaded.put(name, argv[i] + len, true);
681         break;
682       }
683     }
684   }
685   return true;
686 }
687 
688 struct proc_rule_ctx {
689   int m_setup;
690   atrt_config* m_config;
691   atrt_host* m_host;
692   atrt_cluster* m_cluster;
693   atrt_process* m_process;
694 };
695 
696 struct proc_rule {
697   int type;
698   bool (*func)(Properties& prop, proc_rule_ctx&, int extra);
699   int extra;
700 };
701 
702 static bool pr_check_replication(Properties&, proc_rule_ctx&, int);
703 static bool pr_check_features(Properties&, proc_rule_ctx&, int);
704 static bool pr_fix_client(Properties&, proc_rule_ctx&, int);
705 static bool pr_proc_options(Properties&, proc_rule_ctx&, int);
706 static bool pr_fix_ndb_connectstring(Properties&, proc_rule_ctx&, int);
707 static bool pr_set_ndb_connectstring(Properties&, proc_rule_ctx&, int);
708 static bool pr_check_proc(Properties&, proc_rule_ctx&, int);
709 static bool pr_set_customprocs_connectstring(Properties&, proc_rule_ctx&, int);
710 
711 static proc_rule f_rules[] = {
712     {atrt_process::AP_CLUSTER, pr_check_features, 0},
713     {atrt_process::AP_MYSQLD, pr_check_replication, 0},
714     {(atrt_process::AP_ALL & ~atrt_process::AP_CLIENT &
715       ~atrt_process::AP_CUSTOM),
716      pr_proc_options, ~(PO_REP | PO_NDB)},
717     {(atrt_process::AP_ALL & ~atrt_process::AP_CLIENT &
718       ~atrt_process::AP_CUSTOM),
719      pr_proc_options, PO_REP},
720     {atrt_process::AP_CLIENT, pr_fix_client, 0},
721     {atrt_process::AP_CLUSTER, pr_fix_ndb_connectstring, 0},
722     {atrt_process::AP_MYSQLD, pr_set_ndb_connectstring, 0},
723     {atrt_process::AP_ALL & ~atrt_process::AP_CUSTOM, pr_check_proc, 0},
724     {atrt_process::AP_CLUSTER, pr_set_customprocs_connectstring, 0},
725     {0, 0, 0}};
726 
configure(atrt_config & config,int setup)727 bool configure(atrt_config& config, int setup) {
728   Properties props;
729 
730   for (size_t i = 0; f_rules[i].func; i++) {
731     bool ok = true;
732     proc_rule_ctx ctx;
733     memset(&ctx, 0, sizeof(ctx));
734     ctx.m_setup = setup;
735     ctx.m_config = &config;
736 
737     for (unsigned j = 0; j < config.m_clusters.size(); j++) {
738       ctx.m_cluster = config.m_clusters[j];
739 
740       if (f_rules[i].type & atrt_process::AP_CLUSTER) {
741         g_logger.debug("applying rule %u to cluster %s", (unsigned)i,
742                        ctx.m_cluster->m_name.c_str());
743         if (!(*f_rules[i].func)(props, ctx, f_rules[i].extra)) ok = false;
744       } else {
745         atrt_cluster& cluster = *config.m_clusters[j];
746         for (unsigned k = 0; k < cluster.m_processes.size(); k++) {
747           atrt_process& proc = *cluster.m_processes[k];
748           ctx.m_process = cluster.m_processes[k];
749           if (proc.m_type & f_rules[i].type) {
750             g_logger.debug("applying rule %u to %s", (unsigned)i,
751                            proc.m_proc.m_cwd.c_str());
752             if (!(*f_rules[i].func)(props, ctx, f_rules[i].extra)) ok = false;
753           }
754         }
755       }
756     }
757 
758     if (!ok) {
759       return false;
760     }
761   }
762 
763   return true;
764 }
765 
find(atrt_config & config,int type,const char * name)766 static atrt_process* find(atrt_config& config, int type, const char* name) {
767   BaseString tmp(name);
768   Vector<BaseString> src;
769   Vector<BaseString> dst;
770   tmp.split(src, ".");
771 
772   if (src.size() != 2) {
773     return 0;
774   }
775   atrt_cluster* cluster = 0;
776   BaseString cl;
777   cl.appfmt(".%s", src[1].c_str());
778   for (unsigned i = 0; i < config.m_clusters.size(); i++) {
779     if (config.m_clusters[i]->m_name == cl) {
780       cluster = config.m_clusters[i];
781       break;
782     }
783   }
784 
785   if (cluster == 0) {
786     return 0;
787   }
788 
789   int idx = atoi(src[0].c_str()) - 1;
790   for (unsigned i = 0; i < cluster->m_processes.size(); i++) {
791     if (cluster->m_processes[i]->m_type & type) {
792       if (idx == 0)
793         return cluster->m_processes[i];
794       else
795         idx--;
796     }
797   }
798 
799   return 0;
800 }
801 
pr_check_replication(Properties & props,proc_rule_ctx & ctx,int)802 static bool pr_check_replication(Properties& props, proc_rule_ctx& ctx, int) {
803   if (!(ctx.m_config->m_replication == "")) {
804     Vector<BaseString> list;
805     ctx.m_config->m_replication.split(list, ";");
806     atrt_config& config = *ctx.m_config;
807 
808     ctx.m_config->m_replication = "";
809 
810     const char* msg = "Invalid replication specification";
811     for (unsigned i = 0; i < list.size(); i++) {
812       Vector<BaseString> rep;
813       list[i].split(rep, ":");
814       if (rep.size() != 2) {
815         g_logger.error("%s: %s (split: %d)", msg, list[i].c_str(), rep.size());
816         return false;
817       }
818 
819       atrt_process* src = find(config, atrt_process::AP_MYSQLD, rep[0].c_str());
820       atrt_process* dst = find(config, atrt_process::AP_MYSQLD, rep[1].c_str());
821 
822       if (src == 0 || dst == 0) {
823         g_logger.error("%s: %s (%d %d)", msg, list[i].c_str(), src != 0,
824                        dst != 0);
825         return false;
826       }
827 
828       if (dst->m_rep_src != 0) {
829         g_logger.error("%s: %s : %s already has replication src (%s)", msg,
830                        list[i].c_str(), dst->m_proc.m_cwd.c_str(),
831                        dst->m_rep_src->m_proc.m_cwd.c_str());
832         return false;
833       }
834 
835       dst->m_rep_src = src;
836       src->m_rep_dst.push_back(dst);
837 
838       src->m_options.m_features |= PO_REP_MASTER;
839       dst->m_options.m_features |= PO_REP_SLAVE;
840     }
841   }
842   return true;
843 }
844 
pr_check_features(Properties & props,proc_rule_ctx & ctx,int)845 static bool pr_check_features(Properties& props, proc_rule_ctx& ctx, int) {
846   atrt_cluster& cluster = *ctx.m_cluster;
847   if (cluster.m_name == ".atrt") {
848     // skip cluster and replication features for atrt
849     return true;
850   }
851 
852   int features = 0;
853   for (unsigned i = 0; i < cluster.m_processes.size(); i++) {
854     if (cluster.m_processes[i]->m_type == atrt_process::AP_NDB_MGMD ||
855         cluster.m_processes[i]->m_type == atrt_process::AP_NDB_API ||
856         cluster.m_processes[i]->m_type == atrt_process::AP_NDBD) {
857       features |= atrt_options::AO_NDBCLUSTER;
858       break;
859     }
860   }
861 
862   if (features) {
863     cluster.m_options.m_features |= features;
864     for (unsigned i = 0; i < cluster.m_processes.size(); i++) {
865       cluster.m_processes[i]->m_options.m_features |= features;
866     }
867   }
868   return true;
869 }
870 
pr_fix_client(Properties & props,proc_rule_ctx & ctx,int)871 static bool pr_fix_client(Properties& props, proc_rule_ctx& ctx, int) {
872   atrt_process& proc = *ctx.m_process;
873   const char *val, *name = "--host=";
874   if (!proc.m_options.m_loaded.get(name, &val)) {
875     val = proc.m_mysqld->m_host->m_hostname.c_str();
876     proc.m_options.m_loaded.put(name, val);
877     proc.m_options.m_generated.put(name, val);
878   }
879 
880   for (size_t i = 0; f_options[i].name; i++) {
881     proc_option& opt = f_options[i];
882     const char* name = opt.name;
883     if (opt.type & atrt_process::AP_CLIENT) {
884       const char* val;
885       if (!proc.m_options.m_loaded.get(name, &val)) {
886         require(proc.m_mysqld->m_options.m_loaded.get(name, &val));
887         proc.m_options.m_loaded.put(name, val);
888         proc.m_options.m_generated.put(name, val);
889       }
890     }
891   }
892 
893   return true;
894 }
895 
try_default_port(atrt_process & proc,const char * name)896 static Uint32 try_default_port(atrt_process& proc, const char* name) {
897   Uint32 port = strcmp(name, "--port=") == 0
898                     ? 3306
899                     : strcmp(name, "--PortNumber=") == 0 ? 1186 : 0;
900 
901   atrt_host* host = proc.m_host;
902   for (unsigned i = 0; i < host->m_processes.size(); i++) {
903     const char* val;
904     if (host->m_processes[i]->m_options.m_loaded.get(name, &val)) {
905       if ((Uint32)atoi(val) == port) return 0;
906     }
907   }
908   return port;
909 }
910 
generate(atrt_process & proc,const char * name,Properties & props)911 static bool generate(atrt_process& proc, const char* name, Properties& props) {
912   atrt_options& opts = proc.m_options;
913   if (strcmp(name, "--port=") == 0 || strcmp(name, "--PortNumber=") == 0) {
914     Uint32 val;
915     if (g_default_ports == 0 || (val = try_default_port(proc, name)) == 0) {
916       val = g_baseport;
917       props.get("--PortNumber=", &val);
918       props.put("--PortNumber=", (val + 1), true);
919     }
920 
921     char buf[255];
922     BaseString::snprintf(buf, sizeof(buf), "%u", val);
923     opts.m_loaded.put(name, buf);
924     opts.m_generated.put(name, buf);
925     return true;
926   } else if (strcmp(name, "--datadir=") == 0) {
927     BaseString datadir(proc.m_proc.m_cwd);
928     datadir.append("/data");
929     opts.m_loaded.put(name, datadir.c_str());
930     opts.m_generated.put(name, datadir.c_str());
931     return true;
932   } else if (strcmp(name, "--FileSystemPath=") == 0) {
933     opts.m_loaded.put(name, proc.m_proc.m_cwd.c_str());
934     opts.m_generated.put(name, proc.m_proc.m_cwd.c_str());
935     return true;
936   } else if (strcmp(name, "--socket=") == 0) {
937     const char* sock = 0;
938     if (g_default_ports) {
939       sock = "/tmp/mysql.sock";
940       atrt_host* host = proc.m_host;
941       for (unsigned i = 0; i < host->m_processes.size(); i++) {
942         const char* val;
943         if (host->m_processes[i]->m_options.m_loaded.get(name, &val)) {
944           if (strcmp(sock, val) == 0) {
945             sock = 0;
946             break;
947           }
948         }
949       }
950     }
951 
952     BaseString tmp;
953     if (sock == 0) {
954       tmp.assfmt("%s/mysql.sock", proc.m_proc.m_cwd.c_str());
955       sock = tmp.c_str();
956     }
957 
958     opts.m_loaded.put(name, sock);
959     opts.m_generated.put(name, sock);
960     return true;
961   } else if (strcmp(name, "--server-id=") == 0) {
962     Uint32 val = 1;
963     props.get(name, &val);
964     char buf[255];
965     BaseString::snprintf(buf, sizeof(buf), "%u", val);
966     opts.m_loaded.put(name, buf);
967     opts.m_generated.put(name, buf);
968     props.put(name, (val + 1), true);
969     return true;
970   } else if (strcmp(name, "--log-bin") == 0) {
971     opts.m_loaded.put(name, "");
972     opts.m_generated.put(name, "");
973     return true;
974   }
975 
976   g_logger.warning("Unknown parameter: %s", name);
977   return true;
978 }
979 
pr_proc_options(Properties & props,proc_rule_ctx & ctx,int extra)980 static bool pr_proc_options(Properties& props, proc_rule_ctx& ctx, int extra) {
981   for (size_t i = 0; f_options[i].name; i++) {
982     proc_option& opt = f_options[i];
983     atrt_process& proc = *ctx.m_process;
984     const char* name = opt.name;
985     if (opt.type & proc.m_type) {
986       if (opt.options == 0 ||
987           (opt.options & extra & proc.m_options.m_features)) {
988         const char* val;
989         if (!proc.m_options.m_loaded.get(name, &val)) {
990           generate(proc, name, props);
991         }
992       }
993     }
994   }
995   return true;
996 }
997 
pr_fix_ndb_connectstring(Properties & props,proc_rule_ctx & ctx,int)998 static bool pr_fix_ndb_connectstring(Properties& props, proc_rule_ctx& ctx,
999                                      int) {
1000   const char* val;
1001   atrt_cluster& cluster = *ctx.m_cluster;
1002 
1003   if (cluster.m_options.m_features & atrt_options::AO_NDBCLUSTER) {
1004     if (!cluster.m_options.m_loaded.get(ndbcs, &val)) {
1005       /**
1006        * Construct connect string for this cluster
1007        */
1008       BaseString str;
1009       for (unsigned i = 0; i < cluster.m_processes.size(); i++) {
1010         atrt_process* tmp = cluster.m_processes[i];
1011         if (tmp->m_type == atrt_process::AP_NDB_MGMD) {
1012           if (str.length()) {
1013             str.append(";");
1014           }
1015           const char* port;
1016           require(tmp->m_options.m_loaded.get("--PortNumber=", &port));
1017           str.appfmt("%s:%s", tmp->m_host->m_hostname.c_str(), port);
1018         }
1019       }
1020       cluster.m_options.m_loaded.put(ndbcs, str.c_str());
1021       cluster.m_options.m_generated.put(ndbcs, str.c_str());
1022       cluster.m_options.m_loaded.get(ndbcs, &val);
1023     }
1024 
1025     for (unsigned i = 0; i < cluster.m_processes.size(); i++) {
1026       cluster.m_processes[i]->m_proc.m_env.appfmt(" NDB_CONNECTSTRING=%s", val);
1027     }
1028   }
1029   return true;
1030 }
1031 
pr_set_ndb_connectstring(Properties & props,proc_rule_ctx & ctx,int)1032 static bool pr_set_ndb_connectstring(Properties& props, proc_rule_ctx& ctx,
1033                                      int) {
1034   const char* val;
1035 
1036   atrt_process& proc = *ctx.m_process;
1037   if (proc.m_options.m_features & atrt_options::AO_NDBCLUSTER) {
1038     if (!proc.m_options.m_loaded.get(ndbcs, &val)) {
1039       require(proc.m_cluster->m_options.m_loaded.get(ndbcs, &val));
1040       proc.m_options.m_loaded.put(ndbcs, val);
1041       proc.m_options.m_generated.put(ndbcs, val);
1042     }
1043 
1044     if (!proc.m_options.m_loaded.get("--ndbcluster", &val)) {
1045       proc.m_options.m_loaded.put("--ndbcluster", "");
1046       proc.m_options.m_generated.put("--ndbcluster", "");
1047     }
1048   }
1049   return true;
1050 }
1051 
pr_check_proc(Properties & props,proc_rule_ctx & ctx,int)1052 static bool pr_check_proc(Properties& props, proc_rule_ctx& ctx, int) {
1053   bool ok = true;
1054   bool generated = false;
1055   const int setup = ctx.m_setup;
1056   atrt_process& proc = *ctx.m_process;
1057   for (size_t i = 0; f_options[i].name; i++) {
1058     proc_option& opt = f_options[i];
1059     const char* name = opt.name;
1060     if ((ctx.m_process->m_type & opt.type) &&
1061         (opt.options == 0 ||
1062          (ctx.m_process->m_options.m_features & opt.options))) {
1063       const char* val;
1064       if (!proc.m_options.m_loaded.get(name, &val)) {
1065         ok = false;
1066         g_logger.warning("Missing parameter: %s for %s", name,
1067                          proc.m_proc.m_cwd.c_str());
1068       } else if (proc.m_options.m_generated.get(name, &val)) {
1069         if (setup == 0) {
1070           ok = false;
1071           g_logger.warning("Missing parameter: %s for %s", name,
1072                            proc.m_proc.m_cwd.c_str());
1073         } else {
1074           generated = true;
1075         }
1076       }
1077     }
1078   }
1079 
1080   if (generated) {
1081     ctx.m_config->m_generated = true;
1082   }
1083 
1084   // ndbout << proc << endl;
1085 
1086   return ok;
1087 }
1088 
pr_set_customprocs_connectstring(Properties & props,proc_rule_ctx & ctx,int)1089 static bool pr_set_customprocs_connectstring(Properties& props,
1090                                              proc_rule_ctx& ctx, int) {
1091   atrt_cluster& cluster = *ctx.m_cluster;
1092 
1093   std::map<BaseString, BaseString> connectstrings;
1094   std::map<BaseString, BaseString>::iterator it;
1095   for (unsigned i = 0; i < cluster.m_processes.size(); i++) {
1096     atrt_process* proc = cluster.m_processes[i];
1097     if (proc->m_type != atrt_process::AP_CUSTOM) continue;
1098 
1099     BaseString host_list;
1100 
1101     it = connectstrings.find(proc->m_name);
1102     if (it != connectstrings.end()) {
1103       host_list = it->second;
1104       host_list.appfmt(",%s", proc->m_host->m_hostname.c_str());
1105     } else {
1106       host_list.assign(proc->m_host->m_hostname);
1107     }
1108 
1109     const char* port_arg = "--port=";
1110     const char* portno;
1111     if (proc->m_options.m_loaded.get(port_arg, &portno) ||
1112         proc->m_options.m_generated.get(port_arg, &portno)) {
1113       host_list.appfmt(":%s", portno);
1114     }
1115 
1116     connectstrings[proc->m_name] = host_list;
1117   }
1118 
1119   for (unsigned i = 0; i < cluster.m_processes.size(); i++) {
1120     atrt_process* proc = cluster.m_processes[i];
1121 
1122     bool client_or_api =
1123         proc->m_type & (atrt_process::AP_CLIENT | atrt_process::AP_NDB_API);
1124     if (!client_or_api) continue;
1125 
1126     for (it = connectstrings.begin(); it != connectstrings.end(); ++it) {
1127       BaseString connstr(it->first);
1128       connstr.ndb_toupper();
1129       connstr.append("_CONNECTSTRING");
1130 
1131       if (!proc->m_proc.m_env.empty()) proc->m_proc.m_env.append(" ");
1132       proc->m_proc.m_env.appfmt("%s=%s", connstr.c_str(), it->second.c_str());
1133     }
1134   }
1135 
1136   return true;
1137 }
1138 
operator <<(NdbOut & out,const atrt_process & proc)1139 NdbOut& operator<<(NdbOut& out, const atrt_process& proc) {
1140   out << "[ atrt_process: ";
1141   switch (proc.m_type) {
1142     case atrt_process::AP_NDB_MGMD:
1143     case atrt_process::AP_NDBD:
1144     case atrt_process::AP_MYSQLD:
1145     case atrt_process::AP_NDB_API:
1146     case atrt_process::AP_CLIENT:
1147       out << proc.m_name.c_str();
1148       break;
1149     case atrt_process::AP_CUSTOM:
1150       out << "custom:" << proc.m_name.c_str() << ": ";
1151       break;
1152     default:
1153       out << "<unknown: " << (int)proc.m_type << " >";
1154   }
1155 
1156   out << " cluster: " << proc.m_cluster->m_name.c_str()
1157       << " host: " << proc.m_host->m_hostname.c_str() << endl
1158       << " cwd: " << proc.m_proc.m_cwd.c_str() << endl
1159       << " path: " << proc.m_proc.m_path.c_str() << endl
1160       << " args: " << proc.m_proc.m_args.c_str() << endl
1161       << " env: " << proc.m_proc.m_env.c_str() << endl;
1162 
1163   proc.m_options.m_generated.print(stdout, "generated: ");
1164 
1165   out << " ]";
1166 
1167 #if 0
1168   proc.m_index = 0; //idx;
1169   proc.m_host = host_ptr;
1170   proc.m_cluster = cluster;
1171   proc.m_proc.m_id = -1;
1172   proc.m_proc.m_type = "temporary";
1173   proc.m_proc.m_owner = "atrt";
1174   proc.m_proc.m_group = cluster->m_name.c_str();
1175   proc.m_proc.m_cwd.assign(dir).append("/atrt/").append(cluster->m_dir);
1176   proc.m_proc.m_stdout = "log.out";
1177   proc.m_proc.m_stderr = "2>&1";
1178   proc.m_proc.m_runas = proc.m_host->m_user;
1179   proc.m_proc.m_ulimit = "c:unlimited";
1180   proc.m_proc.m_env.assfmt("MYSQL_BASE_DIR=%s", dir);
1181   proc.m_proc.m_shutdown_options = "";
1182 #endif
1183 
1184   return out;
1185 }
1186 
find_bin_path(const char * exe)1187 char* find_bin_path(const char* exe) { return find_bin_path(g_prefix0, exe); }
1188 
find_bin_path(const char * prefix,const char * exe)1189 char* find_bin_path(const char* prefix, const char* exe) {
1190   if (exe == 0) return 0;
1191 
1192   if (exe[0] == '/') {
1193     /**
1194      * Trust that path is correct...
1195      */
1196     return strdup(exe);
1197   }
1198 
1199   for (int i = 0; g_search_path[i] != 0; i++) {
1200     BaseString p;
1201     p.assfmt("%s/%s/%s", prefix, g_search_path[i], exe);
1202     if (File_class::exists(p.c_str())) {
1203       return strdup(p.c_str());
1204     }
1205   }
1206   return 0;
1207 }
1208