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