1 /*
2    Copyright (c) 2003, 2021, Oracle and/or its affiliates.
3 
4 
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License, version 2.0,
7    as published by the Free Software Foundation.
8 
9    This program is also distributed with certain software (including
10    but not limited to OpenSSL) that is licensed under separate terms,
11    as designated in a particular file or component or in included license
12    documentation.  The authors of MySQL hereby grant you an additional
13    permission to link the program and your derivative works with the
14    separately licensed software that they have included with MySQL.
15 
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License, version 2.0, for more details.
20 
21    You should have received a copy of the GNU General Public License
22    along with this program; if not, write to the Free Software
23    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
24 */
25 
26 #include <ndb_global.h>
27 
28 #include <NdbOut.hpp>
29 #include <NdbTCP.h>
30 #include "CpcClient.hpp"
31 
32 #define CPC_CMD(name, value, desc) \
33  { (name), \
34    0, \
35    ParserRow_t::Cmd, \
36    ParserRow_t::String, \
37    ParserRow_t::Optional, \
38    ParserRow_t::IgnoreMinMax, \
39    0, 0, \
40    0, \
41    (desc), \
42    (value) }
43 
44 #define CPC_ARG(name, type, opt, desc) \
45  { (name), \
46    0, \
47    ParserRow_t::Arg, \
48    ParserRow_t::type, \
49    ParserRow_t::opt, \
50    ParserRow_t::IgnoreMinMax, \
51    0, 0, \
52    0, \
53   (desc), 0 }
54 
55 #define CPC_END() \
56  { 0, \
57    0, \
58    ParserRow_t::End, \
59    ParserRow_t::Int, \
60    ParserRow_t::Optional, \
61    ParserRow_t::IgnoreMinMax, \
62    0, 0, \
63    0, \
64    0, 0 }
65 
66 #ifdef DEBUG_PRINT_PROPERTIES
printprop(const Properties & p)67 static void printprop(const Properties &p) {
68   Properties::Iterator iter(&p);
69   const char *name;
70   while((name = iter.next()) != NULL) {
71     PropertiesType t;
72     Uint32 val_i;
73     BaseString val_s;
74 
75     p.getTypeOf(name, &t);
76     switch(t) {
77     case PropertiesType_Uint32:
78       p.get(name, &val_i);
79       ndbout << name << " (Uint32): " << val_i << endl;
80       break;
81     case PropertiesType_char:
82       p.get(name, val_s);
83       ndbout << name << " (string): " << val_s << endl;
84       break;
85     default:
86       ndbout << "Unknown type " << t << endl;
87       break;
88     }
89   }
90 }
91 #endif
92 
93 
94 int
stop_process(Uint32 id,Properties & reply)95 SimpleCpcClient::stop_process(Uint32 id, Properties& reply){
96   const ParserRow_t stop_reply[] = {
97     CPC_CMD("stop process", NULL, ""),
98     CPC_ARG("status", Int, Mandatory, ""),
99     CPC_ARG("id", Int, Optional, ""),
100     CPC_ARG("errormessage", String, Optional, ""),
101 
102     CPC_END()
103   };
104 
105   Properties args;
106   args.put("id", id);
107 
108   const Properties* ret = cpc_call("stop process", args, stop_reply);
109   if(ret == 0){
110     reply.put("status", (Uint32)0);
111     reply.put("errormessage", "unknown error");
112     return -1;
113   }
114 
115   Uint32 status = 0;
116   ret->get("status", &status);
117   reply.put("status", status);
118   if(status != 0) {
119     BaseString msg;
120     ret->get("errormessage", msg);
121     reply.put("errormessage", msg.c_str());
122   }
123   delete ret;
124   return status;
125 }
126 
127 
128 int
start_process(Uint32 id,Properties & reply)129 SimpleCpcClient::start_process(Uint32 id, Properties& reply){
130   const ParserRow_t start_reply[] = {
131     CPC_CMD("start process", NULL, ""),
132     CPC_ARG("status", Int, Mandatory, ""),
133     CPC_ARG("id", Int, Optional, ""),
134     CPC_ARG("errormessage", String, Optional, ""),
135 
136     CPC_END()
137   };
138 
139   Properties args;
140   args.put("id", id);
141 
142   const Properties* ret = cpc_call("start process", args, start_reply);
143   if(ret == 0){
144     reply.put("status", (Uint32)0);
145     reply.put("errormessage", "unknown error");
146     return -1;
147   }
148 
149   Uint32 status = 0;
150   ret->get("status", &status);
151   reply.put("status", status);
152   if(status != 0) {
153     BaseString msg;
154     ret->get("errormessage", msg);
155     reply.put("errormessage", msg.c_str());
156   }
157 
158   delete ret;
159 
160   return status;
161 }
162 
163 int
undefine_process(Uint32 id,Properties & reply)164 SimpleCpcClient::undefine_process(Uint32 id, Properties& reply){
165   const ParserRow_t stop_reply[] = {
166     CPC_CMD("undefine process", NULL, ""),
167     CPC_ARG("status", Int, Mandatory, ""),
168     CPC_ARG("id", Int, Optional, ""),
169     CPC_ARG("errormessage", String, Optional, ""),
170 
171     CPC_END()
172   };
173 
174   Properties args;
175   args.put("id", id);
176 
177   const Properties* ret = cpc_call("undefine process", args, stop_reply);
178   if(ret == 0){
179     reply.put("status", (Uint32)0);
180     reply.put("errormessage", "unknown error");
181     return -1;
182   }
183 
184   Uint32 status = 0;
185   ret->get("status", &status);
186   reply.put("status", status);
187   if(status != 0) {
188     BaseString msg;
189     ret->get("errormessage", msg);
190     reply.put("errormessage", msg.c_str());
191   }
192 
193   delete ret;
194 
195   return status;
196 }
197 
198 #if 0
199 static void
200 printproc(SimpleCpcClient::Process & p) {
201   ndbout.println("Name:                %s", p.m_name.c_str());
202   ndbout.println("Id:                  %d", p.m_id);
203   ndbout.println("Type:                %s", p.m_type.c_str());
204   ndbout.println("Group:               %s", p.m_group.c_str());
205   ndbout.println("Program path:        %s", p.m_path.c_str());
206   ndbout.println("Arguments:           %s", p.m_args.c_str());
207   ndbout.println("Environment:         %s", p.m_env.c_str());
208   ndbout.println("Working directory:   %s", p.m_cwd.c_str());
209   ndbout.println("Owner:               %s", p.m_owner.c_str());
210   ndbout.println("Runas:               %s", p.m_runas.c_str());
211   ndbout.println("Ulimit:              %s", p.m_ulimit.c_str());
212   ndbout.println("%s", "");
213 }
214 #endif
215 
216 static int
convert(const Properties & src,SimpleCpcClient::Process & dst)217 convert(const Properties & src, SimpleCpcClient::Process & dst){
218   bool b = true;
219   b &= src.get("id", (Uint32*)&dst.m_id);
220   b &= src.get("name",   dst.m_name);
221   b &= src.get("type",   dst.m_type);
222   b &= src.get("status", dst.m_status);
223   b &= src.get("owner",  dst.m_owner);
224   b &= src.get("group",  dst.m_group);
225   b &= src.get("path",   dst.m_path);
226   b &= src.get("args",   dst.m_args);
227   b &= src.get("env",    dst.m_env);
228   b &= src.get("cwd",    dst.m_cwd);
229   b &= src.get("runas",  dst.m_runas);
230 
231   b &= src.get("stdin",  dst.m_stdin);
232   b &= src.get("stdout", dst.m_stdout);
233   b &= src.get("stderr", dst.m_stderr);
234   b &= src.get("ulimit", dst.m_ulimit);
235   b &= src.get("shutdown", dst.m_shutdown_options);
236 
237   return b;
238 }
239 
240 static int
convert(const SimpleCpcClient::Process & src,Properties & dst)241 convert(const SimpleCpcClient::Process & src, Properties & dst ){
242   bool b = true;
243   //b &= dst.put("id",     (Uint32)src.m_id);
244   b &= dst.put("name",   src.m_name.c_str());
245   b &= dst.put("type",   src.m_type.c_str());
246   //b &= dst.put("status", src.m_status.c_str());
247   b &= dst.put("owner",  src.m_owner.c_str());
248   b &= dst.put("group",  src.m_group.c_str());
249   b &= dst.put("path",   src.m_path.c_str());
250   b &= dst.put("args",   src.m_args.c_str());
251   b &= dst.put("env",    src.m_env.c_str());
252   b &= dst.put("cwd",    src.m_cwd.c_str());
253   b &= dst.put("runas",  src.m_runas.c_str());
254 
255   b &= dst.put("stdin",  src.m_stdin.c_str());
256   b &= dst.put("stdout", src.m_stdout.c_str());
257   b &= dst.put("stderr", src.m_stderr.c_str());
258   b &= dst.put("ulimit", src.m_ulimit.c_str());
259   b &= dst.put("shutdown", src.m_shutdown_options.c_str());
260 
261   return b;
262 }
263 
264 int
define_process(Process & p,Properties & reply)265 SimpleCpcClient::define_process(Process & p, Properties& reply){
266   const ParserRow_t define_reply[] = {
267     CPC_CMD("define process", NULL, ""),
268     CPC_ARG("status", Int, Mandatory, ""),
269     CPC_ARG("id", Int, Optional, ""),
270     CPC_ARG("errormessage", String, Optional, ""),
271 
272     CPC_END()
273   };
274 
275   Properties args;
276   convert(p, args);
277 
278   const Properties* ret = cpc_call("define process", args, define_reply);
279   if(ret == 0){
280     reply.put("status", (Uint32)0);
281     reply.put("errormessage", "unknown error");
282     return -1;
283   }
284 
285   Uint32 status = 0;
286   ret->get("status", &status);
287   reply.put("status", status);
288   if(status != 0) {
289     BaseString msg;
290     ret->get("errormessage", msg);
291     reply.put("errormessage", msg.c_str());
292   }
293 
294   Uint32 id;
295   if(!ret->get("id", &id)){
296     return -1;
297   }
298 
299   p.m_id = id;
300   delete ret;
301   return status;
302 }
303 
304 int
list_processes(Vector<Process> & procs,Properties & reply)305 SimpleCpcClient::list_processes(Vector<Process> &procs, Properties& reply) {
306   int start = 0, end = 0, entry;
307   const ParserRow_t list_reply[] = {
308     CPC_CMD("start processes", &start, ""),
309     CPC_CMD("end processes", &end, ""),
310 
311     CPC_CMD("process", &entry, ""),
312     CPC_ARG("id",    Int,    Mandatory, "Id of process."),
313     CPC_ARG("name",  String, Mandatory, "Name of process"),
314     CPC_ARG("group", String, Mandatory, "Group of process"),
315     CPC_ARG("env",   String, Mandatory, "Environment variables for process"),
316     CPC_ARG("path",  String, Mandatory, "Path to binary"),
317     CPC_ARG("args",  String, Mandatory, "Arguments to process"),
318     CPC_ARG("type",  String, Mandatory, "Type of process"),
319     CPC_ARG("cwd",   String, Mandatory, "Working directory of process"),
320     CPC_ARG("owner", String, Mandatory, "Owner of process"),
321     CPC_ARG("status",String, Mandatory, "Status of process"),
322     CPC_ARG("runas", String, Mandatory, "Run as user"),
323     CPC_ARG("stdin", String, Mandatory, "Redirect stdin"),
324     CPC_ARG("stdout",String, Mandatory, "Redirect stdout"),
325     CPC_ARG("stderr",String, Mandatory, "Redirect stderr"),
326     CPC_ARG("ulimit",String, Mandatory, "ulimit"),
327     CPC_ARG("shutdown",String, Mandatory, "shutdown"),
328 
329     CPC_END()
330   };
331 
332   reply.clear();
333 
334   const Properties args;
335 
336   cpc_send("list processes", args);
337 
338   bool done = false;
339   while(!done) {
340     const Properties *proc;
341     void *p;
342     cpc_recv(list_reply, &proc, &p);
343 
344     end++;
345     if(p == &start)
346     {
347       start = 1;
348       /* do nothing */
349     }
350     else if(p == &end)
351     {
352       done = true;
353     }
354     else if(p == &entry)
355     {
356       if(proc != NULL)
357       {
358 	Process p;
359 	convert(* proc, p);
360 	procs.push_back(p);
361       }
362       else
363       {
364         ndbout_c("JONAS: start: %d loop: %d cnt: %u got p == &entry with proc == NULL",
365                  start, end, procs.size());
366       }
367     }
368     else
369     {
370       ndbout_c("internal error: %d", __LINE__);
371       return -1;
372     }
373     if (proc)
374     {
375       delete proc;
376     }
377   }
378   return 0;
379 }
380 
381 
SimpleCpcClient(const char * _host,int _port)382 SimpleCpcClient::SimpleCpcClient(const char *_host, int _port) {
383   host = strdup(_host);
384   port = _port;
385   my_socket_invalidate(&cpc_sock);
386 }
387 
~SimpleCpcClient()388 SimpleCpcClient::~SimpleCpcClient() {
389   if(host != NULL) {
390     free(host);
391     host = NULL;
392   }
393 
394   port = 0;
395 
396   if(my_socket_valid(cpc_sock)) {
397     my_socket_close(cpc_sock);
398     my_socket_invalidate(&cpc_sock);
399   }
400 }
401 
402 int
connect()403 SimpleCpcClient::connect() {
404   struct sockaddr_in sa;
405   struct hostent *hp;
406 
407   /* Create socket */
408   cpc_sock = my_socket_create(AF_INET, SOCK_STREAM, IPPROTO_TCP);
409   if(!my_socket_valid(cpc_sock))
410     return -1;
411 
412   /* Connect socket */
413   sa.sin_family = AF_INET;
414   hp = gethostbyname(host);
415   if(hp == NULL) {
416     errno = ENOENT;
417     return -1;
418   }
419 
420   memcpy(&sa.sin_addr, hp->h_addr, hp->h_length);
421   sa.sin_port = htons(port);
422   if (my_connect_inet(cpc_sock, &sa) < 0)
423     return -1;
424 
425   return 0;
426 }
427 
428 int
cpc_send(const char * cmd,const Properties & args)429 SimpleCpcClient::cpc_send(const char *cmd,
430 			  const Properties &args) {
431   SocketOutputStream cpc_out(cpc_sock);
432 
433   cpc_out.println("%s", cmd);
434 
435   Properties::Iterator iter(&args);
436   const char *name;
437   while((name = iter.next()) != NULL) {
438     PropertiesType t;
439     Uint32 val_i;
440     BaseString val_s;
441 
442     args.getTypeOf(name, &t);
443     switch(t) {
444     case PropertiesType_Uint32:
445       args.get(name, &val_i);
446       cpc_out.println("%s: %d", name, val_i);
447       break;
448     case PropertiesType_char:
449       args.get(name, val_s);
450       if (strlen(val_s.c_str()) > Parser_t::Context::MaxParseBytes)
451       {
452         ndbout << "Argument " << name << " at "
453                << strlen(val_s.c_str())
454                << " longer than max of "
455                << Parser_t::Context::MaxParseBytes
456                << endl;
457         abort();
458       }
459       cpc_out.println("%s: %s", name, val_s.c_str());
460       break;
461     default:
462       /* Silently ignore */
463       break;
464     }
465   }
466   cpc_out.println("%s", "");
467 
468   return 0;
469 }
470 
471 /**
472  * Receive a response from the CPCD. The argument reply will point
473  * to a Properties object describing the reply. Note that the caller
474  * is responsible for deleting the Properties object returned.
475  */
476 SimpleCpcClient::Parser_t::ParserStatus
cpc_recv(const ParserRow_t * syntax,const Properties ** reply,void ** user_value)477 SimpleCpcClient::cpc_recv(const ParserRow_t *syntax,
478 			  const Properties **reply,
479 			  void **user_value) {
480   SocketInputStream cpc_in(cpc_sock);
481 
482   Parser_t::Context ctx;
483   ParserDummy session(cpc_sock);
484   Parser_t parser(syntax, cpc_in);
485   *reply = parser.parse(ctx, session);
486   if(user_value != NULL)
487     *user_value = ctx.m_currentCmd->user_value;
488   return ctx.m_status;
489 }
490 
491 const Properties *
cpc_call(const char * cmd,const Properties & args,const ParserRow_t * reply_syntax)492 SimpleCpcClient::cpc_call(const char *cmd,
493 			  const Properties &args,
494 			  const ParserRow_t *reply_syntax) {
495   cpc_send(cmd, args);
496 
497   const Properties *ret;
498   cpc_recv(reply_syntax, &ret);
499   return ret;
500 }
501 
502 
ParserDummy(NDB_SOCKET_TYPE sock)503 SimpleCpcClient::ParserDummy::ParserDummy(NDB_SOCKET_TYPE sock)
504   : SocketServer::Session(sock) {
505 }
506 
507 template class Vector<SimpleCpcClient::Process>;
508 template class Vector<ParserRow<SimpleCpcClient::ParserDummy> const*>;
509