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