1 /*
2 Copyright (c) 2003, 2021, Oracle and/or its affiliates.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License, version 2.0,
6 as published by the Free Software Foundation.
7
8 This program is also distributed with certain software (including
9 but not limited to OpenSSL) that is licensed under separate terms,
10 as designated in a particular file or component or in included license
11 documentation. The authors of MySQL hereby grant you an additional
12 permission to link the program and your derivative works with the
13 separately licensed software that they have included with MySQL.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License, version 2.0, for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 */
24
25
26 #include <ndb_global.h>
27 #include <getarg.h>
28 #include "CpcClient.hpp"
29
30 #include <portlib/NdbEnv.h>
31 #include <util/NdbOut.hpp>
32
33 #define DEFAULT_PORT 1234
34 #define ENV_HOSTS "NDB_CPCC_HOSTS"
35
36 struct settings {
37 int m_longl;
38 short m_port;
39 } g_settings = { 0 , DEFAULT_PORT };
40
41 Vector<SimpleCpcClient*> g_hosts;
42 int connect(Vector<SimpleCpcClient*>&);
43
44 class Expression {
45 public:
~Expression()46 virtual ~Expression() {}
47 virtual bool evaluate(SimpleCpcClient*, const SimpleCpcClient::Process &)= 0;
48 };
49
50 int for_each(Vector<SimpleCpcClient*>& list, Expression &);
51 int start_stop(const char * cmd, Vector<SimpleCpcClient*>& list,
52 Vector<Vector<Uint32> >& procs);
53
54 class True : public Expression {
55 public:
~True()56 virtual ~True() {}
evaluate(SimpleCpcClient *,const SimpleCpcClient::Process & p)57 virtual bool evaluate(SimpleCpcClient*, const SimpleCpcClient::Process & p){
58 return true;
59 }
60 };
61
62 class FieldEQ : public Expression {
63 BaseString m_field;
64 BaseString m_value;
65 public:
FieldEQ(const BaseString & field,const BaseString & value)66 FieldEQ(const BaseString & field, const BaseString & value){
67 m_field = field;
68 m_value = value;
69 }
~FieldEQ()70 virtual ~FieldEQ(){}
71
evaluate(SimpleCpcClient *,const SimpleCpcClient::Process & p)72 virtual bool evaluate(SimpleCpcClient*, const SimpleCpcClient::Process & p){
73 BaseString v;
74 if(m_field == "name") v = p.m_name;
75
76 if(m_field == "type") v = p.m_type;
77 if(m_field == "status") v = p.m_status;
78 if(m_field == "owner") v = p.m_owner;
79 if(m_field == "group") v = p.m_group;
80 if(m_field == "path") v = p.m_path;
81 if(m_field == "args") v = p.m_args;
82 if(m_field == "env") v = p.m_env;
83 if(m_field == "cwd") v = p.m_cwd;
84
85 if(m_field == "stdin") v = p.m_stdin;
86 if(m_field == "stdout") v = p.m_stdout;
87 if(m_field == "stderr") v = p.m_stderr;
88
89 return v == m_value;
90 }
91 };
92
93 class Match : public Expression {
94 Expression & m_cond;
95 Expression & m_apply;
96 public:
Match(Expression & condition,Expression & rule)97 Match(Expression& condition, Expression & rule)
98 : m_cond(condition), m_apply(rule) {
99 }
~Match()100 virtual ~Match(){}
101
evaluate(SimpleCpcClient * c,const SimpleCpcClient::Process & p)102 virtual bool evaluate(SimpleCpcClient* c,const SimpleCpcClient::Process & p){
103 if(m_cond.evaluate(c, p))
104 return m_apply.evaluate(c, p);
105 return false;
106 }
107 };
108
109 class Operate : public Expression {
110 const char * cmd;
111 SimpleCpcClient * host;
112 settings & sets;
113 public:
Operate(const char * c,settings & s)114 Operate(const char * c, settings & s) : sets(s) {
115 cmd = c;
116 host = 0;
117 }
~Operate()118 virtual ~Operate() {}
119
120 virtual bool evaluate(SimpleCpcClient*, const SimpleCpcClient::Process & p);
121 };
122
123 class ProcEQ : public Expression {
124 SimpleCpcClient * host;
125 Uint32 id;
126 public:
ProcEQ(SimpleCpcClient * h,Uint32 i)127 ProcEQ(SimpleCpcClient* h, Uint32 i){
128 host = h; id = i;
129 }
~ProcEQ()130 virtual ~ProcEQ() {}
evaluate(SimpleCpcClient * c,const SimpleCpcClient::Process & p)131 virtual bool evaluate(SimpleCpcClient* c,const SimpleCpcClient::Process & p){
132 return p.m_id == (int)id && c == host;
133 }
134 };
135
136 class OrExpr : public Expression {
137 Expression * m_rule;
138 Vector<Expression *> m_cond;
139 bool on_empty;
140 public:
OrExpr(Expression * rule,bool onEmp=true)141 OrExpr(Expression * rule, bool onEmp = true){
142 m_rule = rule;
143 on_empty = onEmp;
144 }
145
~OrExpr()146 virtual ~OrExpr(){}
147
evaluate(SimpleCpcClient * c,const SimpleCpcClient::Process & p)148 virtual bool evaluate(SimpleCpcClient* c, const SimpleCpcClient::Process & p){
149 bool run = on_empty;
150 for(unsigned i = 0; i<m_cond.size(); i++){
151 if(m_cond[i]->evaluate(c, p)){
152 run = true;
153 break;
154 }
155 }
156 if(run)
157 return m_rule->evaluate(c, p);
158 return false;
159 }
160
push_back(Expression * expr)161 void push_back(Expression * expr){
162 m_cond.push_back(expr);
163 }
164 };
165
166 void
add_host(Vector<SimpleCpcClient * > & hosts,BaseString tmp)167 add_host(Vector<SimpleCpcClient*> & hosts, BaseString tmp){
168 Vector<BaseString> split;
169 tmp.split(split, ":");
170
171 short port = g_settings.m_port;
172 if(split.size() > 1)
173 port = atoi(split[1].c_str());
174
175 hosts.push_back(new SimpleCpcClient(split[0].c_str(), port));
176 }
177
178 void
add_hosts(Vector<SimpleCpcClient * > & hosts,BaseString list)179 add_hosts(Vector<SimpleCpcClient*> & hosts, BaseString list){
180 Vector<BaseString> split;
181 list.split(split);
182 for(unsigned i = 0; i<split.size(); i++){
183 add_host(hosts, split[i]);
184 }
185 }
186
187 int
main(int argc,const char ** argv)188 main(int argc, const char** argv){
189 ndb_init();
190 int help = 0;
191 const char *cmd=0, *name=0, *group=0, *owner=0;
192 int list = 0, start = 0, stop = 0, rm = 0;
193 struct getargs args[] = {
194 { "cmd", 'c', arg_string, &cmd, "command", "command to run (default ls)" }
195 ,{ "name", 'n', arg_string, &name,
196 "apply command for all processes with name", "" }
197 ,{ "group", 'g', arg_string, &group,
198 "apply command for all processes in group", "" }
199 ,{ "owner", 'g', arg_string, &owner,
200 "apply command for all processes with owner", "" }
201 ,{ "long", 'l', arg_flag, &g_settings.m_longl, "long", "long listing"}
202 ,{ "usage", '?', arg_flag, &help, "Print help", "" }
203 ,{ "ls", 0, arg_flag, &list, "-c list", "list process(es)" }
204 ,{ "start", 0, arg_flag, &start, "-c start", "start process(es)" }
205 ,{ "stop", 0, arg_flag, &stop, "-c stop", "stop process(es)" }
206 ,{ "rm", 0, arg_flag, &rm, "-c rm", "undefine process(es)" }
207 };
208 const int num_args = 10;
209 int i;
210 int optind = 0;
211 char desc[] = "[host:[port]]\n";
212
213 if(getarg(args, num_args, argc, argv, &optind) || help) {
214 arg_printusage(args, num_args, argv[0], desc);
215 return 1;
216 }
217
218 if(list + start + stop + rm > 1){
219 ndbout_c("Can only specify one command");
220 arg_printusage(args, num_args, argv[0], desc);
221 return 1;
222 }
223
224 if(list) cmd = "list";
225 if(start) cmd = "start";
226 if(stop) cmd = "stop";
227 if(rm) cmd = "rm";
228 if(!cmd) cmd = "list";
229
230 Expression * m_expr = 0;
231
232 for(i = optind; i<argc; i++){
233 add_host(g_hosts, argv[i]);
234 }
235
236 OrExpr * orE = new OrExpr(new Operate(cmd, g_settings), true);
237 m_expr = orE;
238 for(i = optind; i<argc; i++){
239 BaseString tmp(argv[i]);
240 Vector<BaseString> split;
241 tmp.split(split, ":");
242
243 if(split.size() > 2){
244 Uint32 id = atoi(split[2].c_str());
245 orE->push_back(new ProcEQ(g_hosts[i-optind], id));
246 }
247 }
248
249 if(g_hosts.size() == 0){
250 char buf[1024];
251 if(NdbEnv_GetEnv(ENV_HOSTS, buf, sizeof(buf))){
252 add_hosts(g_hosts, BaseString(buf));
253 }
254 }
255
256 if(g_hosts.size() == 0){
257 g_hosts.push_back(new SimpleCpcClient("localhost", g_settings.m_port));
258 }
259
260 if(group != 0){
261 Expression * tmp = new FieldEQ("group", group);
262 m_expr = new Match(* tmp, * m_expr);
263 }
264
265 if(name != 0){
266 Expression * tmp = new FieldEQ("name", name);
267 m_expr = new Match(* tmp, * m_expr);
268 }
269
270 if(owner != 0){
271 Expression * tmp = new FieldEQ("owner", owner);
272 m_expr = new Match(* tmp, * m_expr);
273 }
274
275 connect(g_hosts);
276 for_each(g_hosts, * m_expr);
277
278 return 0;
279 }
280
281 int
connect(Vector<SimpleCpcClient * > & list)282 connect(Vector<SimpleCpcClient*>& list){
283 for(unsigned i = 0; i<list.size(); i++){
284 if(list[i]->connect() != 0){
285 ndbout_c("Failed to connect to %s:%d",
286 list[i]->getHost(), list[i]->getPort());
287 delete list[i]; list[i] = 0;
288 }
289 }
290 return 0;
291 }
292
293 int
for_each(Vector<SimpleCpcClient * > & list,Expression & expr)294 for_each(Vector<SimpleCpcClient*>& list, Expression & expr){
295 for(unsigned i = 0; i<list.size(); i++){
296 if(list[i] == 0)
297 continue;
298 Properties p;
299 Vector<SimpleCpcClient::Process> procs;
300 if(list[i]->list_processes(procs, p) != 0){
301 ndbout << "Failed to list processes on "
302 << list[i]->getHost() << ":" << list[i]->getPort() << endl;
303 }
304 for(unsigned j = 0; j<procs.size(); j++)
305 expr.evaluate(list[i], procs[j]);
306 }
307 return 0;
308 }
309
310 bool
evaluate(SimpleCpcClient * c,const SimpleCpcClient::Process & pp)311 Operate::evaluate(SimpleCpcClient* c, const SimpleCpcClient::Process & pp){
312 Uint32 id = pp.m_id;
313 Properties p;
314 int res;
315
316 if(strcasecmp(cmd, "start") == 0)
317 res = c->start_process(id, p);
318 else if(strcasecmp(cmd, "stop") == 0)
319 res = c->stop_process(id, p);
320 else if(strcasecmp(cmd, "rm") == 0)
321 res = c->undefine_process(id, p);
322 else if(strcasecmp(cmd, "list") == 0){
323 if(!sets.m_longl){
324 if(host != c){
325 ndbout_c("--- %s:%d", c->getHost(), c->getPort());
326 host = c;
327 }
328 }
329
330 char s = 0;
331 const char * status = pp.m_status.c_str();
332 if(strcmp(status, "stopped") == 0) s = '-';
333 if(strcmp(status, "starting") == 0) s = 's';
334 if(strcmp(status, "running") == 0) s = 'r';
335 if(strcmp(status, "stopping") == 0) s = 'k';
336 if(s == 0) s = '?';
337
338 if(!sets.m_longl){
339 ndbout_c("%c%c\t%d\t%s\t%s\t%s(%s)",
340 s,
341 pp.m_type.c_str()[0], id, pp.m_owner.c_str(),
342 pp.m_group.c_str(), pp.m_name.c_str(), pp.m_path.c_str());
343 } else {
344 ndbout_c("%c%c %s:%d:%d %s %s %s(%s)",
345 s, pp.m_type.c_str()[0], c->getHost(), c->getPort(),
346 id, pp.m_owner.c_str(), pp.m_group.c_str(),
347 pp.m_name.c_str(), pp.m_path.c_str());
348 }
349 return true;
350 }
351 else
352 {
353 ndbout_c("No such command supported: cmd = %s", cmd);
354 return false;
355 }
356
357 if(res != 0){
358 BaseString msg;
359 p.get("errormessage", msg);
360 ndbout_c("Failed to %s %d on %s:%d - %s",
361 cmd, id,
362 c->getHost(), c->getPort(), msg.c_str());
363 return false;
364 }
365
366 return true;
367 }
368
369 template class Vector<Expression*>;
370 template class Vector<SimpleCpcClient*>;
371