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