1 /*
2    Copyright (c) 2003, 2013, 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 
26 #include <Parser.hpp>
27 #include <NdbOut.hpp>
28 #include <Properties.hpp>
29 #include <socket_io.h>
30 
31 #include "APIService.hpp"
32 #include "CPCD.hpp"
33 #include <NdbMutex.h>
34 #include <OutputStream.hpp>
35 
36 /**
37    const char * name;
38    const char * realName;
39    const Type type;
40    const ArgType argType;
41    const ArgRequired argRequired;
42    const ArgMinMax argMinMax;
43    const int minVal;
44    const int maxVal;
45    void (T::* function)(const class Properties & args);
46    const char * description;
47 */
48 
49 #define CPCD_CMD(name, fun, desc) \
50  { name, \
51    0, \
52    ParserRow<CPCDAPISession>::Cmd, \
53    ParserRow<CPCDAPISession>::String, \
54    ParserRow<CPCDAPISession>::Optional, \
55    ParserRow<CPCDAPISession>::IgnoreMinMax, \
56    0, 0, \
57    fun, \
58    desc, 0 }
59 
60 #define CPCD_ARG(name, type, opt, desc) \
61  { name, \
62    0, \
63    ParserRow<CPCDAPISession>::Arg, \
64    ParserRow<CPCDAPISession>::type, \
65    ParserRow<CPCDAPISession>::opt, \
66    ParserRow<CPCDAPISession>::IgnoreMinMax, \
67    0, 0, \
68    0, \
69    desc, 0 }
70 
71 #define CPCD_ARG2(name, type, opt, min, max, desc) \
72  { name, \
73    0, \
74    ParserRow<CPCDAPISession>::Arg, \
75    ParserRow<CPCDAPISession>::type, \
76    ParserRow<CPCDAPISession>::opt, \
77    ParserRow<CPCDAPISession>::IgnoreMinMax, \
78    min, max, \
79    0, \
80    desc, 0 }
81 
82 #define CPCD_END() \
83  { 0, \
84    0, \
85    ParserRow<CPCDAPISession>::End, \
86    ParserRow<CPCDAPISession>::Int, \
87    ParserRow<CPCDAPISession>::Optional, \
88    ParserRow<CPCDAPISession>::IgnoreMinMax, \
89    0, 0, \
90    0, \
91    0, 0 }
92 
93 #define CPCD_CMD_ALIAS(name, realName, fun) \
94  { name, \
95    realName, \
96    ParserRow<CPCDAPISession>::CmdAlias, \
97    ParserRow<CPCDAPISession>::Int, \
98    ParserRow<CPCDAPISession>::Optional, \
99    ParserRow<CPCDAPISession>::IgnoreMinMax, \
100    0, 0, \
101    0, \
102    0, 0 }
103 
104 #define CPCD_ARG_ALIAS(name, realName, fun) \
105  { name, \
106    realName, \
107    ParserRow<CPCDAPISession>::ArgAlias, \
108    ParserRow<CPCDAPISession>::Int, \
109    ParserRow<CPCDAPISession>::Optional, \
110    ParserRow<CPCDAPISession>::IgnoreMinMax, \
111    0, 0, \
112    0, \
113    0, 0 }
114 
115 const
116 ParserRow<CPCDAPISession> commands[] =
117 {
118   CPCD_CMD("define process" , &CPCDAPISession::defineProcess, ""),
119     CPCD_ARG("id",     Int,    Optional,  "Id of process."),
120     CPCD_ARG("name",   String, Mandatory, "Name of process"),
121     CPCD_ARG("group",  String, Mandatory, "Group of process"),
122     CPCD_ARG("env",    String, Optional,  "Environment variables for process"),
123     CPCD_ARG("path",   String, Mandatory, "Path to binary"),
124     CPCD_ARG("args",   String, Optional,  "Arguments to process"),
125     CPCD_ARG("type",   String, Mandatory, "Type of process"),
126     CPCD_ARG("cwd",    String, Mandatory, "Working directory of process"),
127     CPCD_ARG("owner",  String, Mandatory, "Owner of process"),
128     CPCD_ARG("runas",  String, Optional,  "Run as user"),
129     CPCD_ARG("stdout", String, Optional,  "Redirection of stdout"),
130     CPCD_ARG("stderr", String, Optional,  "Redirection of stderr"),
131     CPCD_ARG("stdin",  String, Optional,  "Redirection of stderr"),
132     CPCD_ARG("ulimit", String, Optional,  "ulimit"),
133     CPCD_ARG("shutdown", String, Optional,  "shutdown options"),
134 
135   CPCD_CMD("undefine process", &CPCDAPISession::undefineProcess, ""),
136     CPCD_CMD_ALIAS("undef", "undefine process", 0),
137     CPCD_ARG("id", Int, Mandatory, "Id of process"),
138     CPCD_ARG_ALIAS("i", "id", 0),
139 
140   CPCD_CMD("start process", &CPCDAPISession::startProcess, ""),
141     CPCD_ARG("id", Int, Mandatory, "Id of process"),
142 
143   CPCD_CMD("stop process", &CPCDAPISession::stopProcess, ""),
144     CPCD_ARG("id", Int, Mandatory, "Id of process"),
145 
146   CPCD_CMD("list processes", &CPCDAPISession::listProcesses, ""),
147 
148   CPCD_CMD("show version", &CPCDAPISession::showVersion, ""),
149 
150   CPCD_END()
151 };
CPCDAPISession(NDB_SOCKET_TYPE sock,CPCD & cpcd)152 CPCDAPISession::CPCDAPISession(NDB_SOCKET_TYPE sock,
153 			       CPCD & cpcd)
154   : SocketServer::Session(sock)
155   , m_cpcd(cpcd)
156 {
157   m_input = new SocketInputStream(sock, 7*24*60*60000);
158   m_output = new SocketOutputStream(sock);
159   m_parser = new Parser<CPCDAPISession>(commands, *m_input);
160 }
161 
CPCDAPISession(FILE * f,CPCD & cpcd)162 CPCDAPISession::CPCDAPISession(FILE * f, CPCD & cpcd)
163   : SocketServer::Session(my_socket_create_invalid())
164   , m_cpcd(cpcd)
165 {
166   m_input = new FileInputStream(f);
167   m_parser = new Parser<CPCDAPISession>(commands, *m_input);
168   m_output = 0;
169 }
170 
~CPCDAPISession()171 CPCDAPISession::~CPCDAPISession() {
172   delete m_input;
173   delete m_parser;
174   if (m_output)
175     delete m_output;
176 }
177 
178 void
runSession()179 CPCDAPISession::runSession(){
180   Parser_t::Context ctx;
181   while(!m_stop){
182     m_parser->run(ctx, * this);
183     if(ctx.m_currentToken == 0)
184       break;
185 
186     m_input->reset_timeout();
187     m_output->reset_timeout();
188 
189     switch(ctx.m_status){
190     case Parser_t::Ok:
191       for(unsigned i = 0; i<ctx.m_aliasUsed.size(); i++)
192 	ndbout_c("Used alias: %s -> %s",
193 		 ctx.m_aliasUsed[i]->name, ctx.m_aliasUsed[i]->realName);
194       break;
195     case Parser_t::NoLine:
196     case Parser_t::EmptyLine:
197       break;
198     default:
199       break;
200     }
201   }
202   NDB_CLOSE_SOCKET(m_socket);
203 }
204 
205 void
stopSession()206 CPCDAPISession::stopSession(){
207   CPCD::RequestStatus rs;
208   for(unsigned i = 0; i<m_temporaryProcesses.size(); i++){
209     Uint32 id = m_temporaryProcesses[i];
210     m_cpcd.undefineProcess(&rs, id);
211   }
212 }
213 
214 void
loadFile()215 CPCDAPISession::loadFile(){
216   Parser_t::Context ctx;
217   while(!m_stop){
218     m_parser->run(ctx, * this);
219     if(ctx.m_currentToken == 0)
220       break;
221 
222     switch(ctx.m_status){
223     case Parser_t::Ok:
224       for(unsigned i = 0; i<ctx.m_aliasUsed.size(); i++)
225 	ndbout_c("Used alias: %s -> %s",
226 		 ctx.m_aliasUsed[i]->name, ctx.m_aliasUsed[i]->realName);
227       break;
228     case Parser_t::NoLine:
229     case Parser_t::EmptyLine:
230       break;
231     default:
232       break;
233     }
234   }
235 }
236 
237 void
defineProcess(Parser_t::Context &,const class Properties & args)238 CPCDAPISession::defineProcess(Parser_t::Context & /* unused */,
239 			      const class Properties & args){
240 
241   CPCD::Process * p = new CPCD::Process(args, &m_cpcd);
242 
243   CPCD::RequestStatus rs;
244 
245   bool ret = m_cpcd.defineProcess(&rs, p);
246   if(!m_cpcd.loadingProcessList) {
247     m_output->println("define process");
248     m_output->println("status: %d", rs.getStatus());
249     if(ret == true){
250       m_output->println("id: %d", p->m_id);
251       if(p->m_processType == TEMPORARY){
252 	m_temporaryProcesses.push_back(p->m_id);
253       }
254     } else {
255       m_output->println("errormessage: %s", rs.getErrMsg());
256     }
257     m_output->println("%s", "");
258   }
259 }
260 
261 void
undefineProcess(Parser_t::Context &,const class Properties & args)262 CPCDAPISession::undefineProcess(Parser_t::Context & /* unused */,
263 				const class Properties & args){
264   Uint32 id;
265   CPCD::RequestStatus rs;
266 
267   args.get("id", &id);
268   bool ret = m_cpcd.undefineProcess(&rs, id);
269 
270   m_output->println("undefine process");
271   m_output->println("id: %d", id);
272   m_output->println("status: %d", rs.getStatus());
273   if(!ret)
274     m_output->println("errormessage: %s", rs.getErrMsg());
275 
276   m_output->println("%s", "");
277 }
278 
279 void
startProcess(Parser_t::Context &,const class Properties & args)280 CPCDAPISession::startProcess(Parser_t::Context & /* unused */,
281 			     const class Properties & args){
282   Uint32 id;
283   CPCD::RequestStatus rs;
284 
285   args.get("id", &id);
286   const int ret = m_cpcd.startProcess(&rs, id);
287 
288   if(!m_cpcd.loadingProcessList) {
289     m_output->println("start process");
290     m_output->println("id: %d", id);
291     m_output->println("status: %d", rs.getStatus());
292     if(!ret)
293       m_output->println("errormessage: %s", rs.getErrMsg());
294     m_output->println("%s", "");
295   }
296 }
297 
298 void
stopProcess(Parser_t::Context &,const class Properties & args)299 CPCDAPISession::stopProcess(Parser_t::Context & /* unused */,
300 			    const class Properties & args){
301   Uint32 id;
302   CPCD::RequestStatus rs;
303 
304   args.get("id", &id);
305   int ret = m_cpcd.stopProcess(&rs, id);
306 
307   m_output->println("stop process");
308   m_output->println("id: %d", id);
309   m_output->println("status: %d", rs.getStatus());
310   if(!ret)
311     m_output->println("errormessage: %s", rs.getErrMsg());
312 
313   m_output->println("%s", "");
314 }
315 
316 static const char *
propToString(Properties * prop,const char * key)317 propToString(Properties *prop, const char *key) {
318   static char buf[32];
319   const char *retval = NULL;
320   PropertiesType pt;
321 
322   prop->getTypeOf(key, &pt);
323   switch(pt) {
324   case PropertiesType_Uint32:
325     Uint32 val;
326     prop->get(key, &val);
327     BaseString::snprintf(buf, sizeof buf, "%d", val);
328     retval = buf;
329     break;
330   case PropertiesType_char:
331     const char *str;
332     prop->get(key, &str);
333     retval = str;
334     break;
335   default:
336     BaseString::snprintf(buf, sizeof buf, "(unknown)");
337     retval = buf;
338   }
339   return retval;
340 }
341 
342 void
printProperty(Properties * prop,const char * key)343 CPCDAPISession::printProperty(Properties *prop, const char *key) {
344   m_output->println("%s: %s", key, propToString(prop, key));
345 }
346 
347 void
listProcesses(Parser_t::Context &,const class Properties &)348 CPCDAPISession::listProcesses(Parser_t::Context & /* unused */,
349 			      const class Properties & /* unused */){
350   m_cpcd.m_processes.lock();
351   MutexVector<CPCD::Process *> *proclist = m_cpcd.getProcessList();
352 
353   m_output->println("start processes");
354   m_output->println("%s", "");
355 
356 
357   for(unsigned i = 0; i < proclist->size(); i++) {
358     CPCD::Process *p = (*proclist)[i];
359 
360     m_output->println("process");
361 
362     m_output->println("id: %d", p->m_id);
363     m_output->println("name: %s", p->m_name.c_str());
364     m_output->println("path: %s", p->m_path.c_str());
365     m_output->println("args: %s", p->m_args.c_str());
366     m_output->println("type: %s", p->m_type.c_str());
367     m_output->println("cwd: %s", p->m_cwd.c_str());
368     m_output->println("env: %s", p->m_env.c_str());
369     m_output->println("owner: %s", p->m_owner.c_str());
370     m_output->println("group: %s", p->m_group.c_str());
371     m_output->println("runas: %s", p->m_runas.c_str());
372     m_output->println("stdin: %s", p->m_stdin.c_str());
373     m_output->println("stdout: %s", p->m_stdout.c_str());
374     m_output->println("stderr: %s", p->m_stderr.c_str());
375     m_output->println("ulimit: %s", p->m_ulimit.c_str());
376     m_output->println("shutdown: %s", p->m_shutdown_options.c_str());
377     switch(p->m_status){
378     case STOPPED:
379       m_output->println("status: stopped");
380       break;
381     case STARTING:
382       m_output->println("status: starting");
383       break;
384     case RUNNING:
385       m_output->println("status: running");
386       break;
387     case STOPPING:
388       m_output->println("status: stopping");
389       break;
390     }
391 
392     m_output->println("%s", "");
393 
394   }
395 
396   m_output->println("end processes");
397   m_output->println("%s", "");
398 
399   m_cpcd.m_processes.unlock();
400 }
401 
402 void
showVersion(Parser_t::Context &,const class Properties & args)403 CPCDAPISession::showVersion(Parser_t::Context & /* unused */,
404                             const class Properties & args){
405   CPCD::RequestStatus rs;
406 
407   m_output->println("show version");
408   m_output->println("compile time: %s %s", __DATE__, __TIME__);
409 
410   m_output->println("%s", "");
411 }
412 
413 template class Vector<ParserRow<CPCDAPISession> const*>;
414